Rsx children
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use osui::prelude::*;
|
||||
|
||||
pub fn main() {
|
||||
let engine = Console::new();
|
||||
engine.run(App {}).expect("Failed to run engine");
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App(cx: &Arc<Context>) -> View {
|
||||
rsx! {
|
||||
MyComponent { "Hello world!" }
|
||||
}
|
||||
.view(cx.clone())
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn MyComponent(cx: &Arc<Context>, children: &Rsx) -> View {
|
||||
children.view(cx.clone())
|
||||
}
|
||||
+38
-5
@@ -3,7 +3,11 @@ use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub fn emit_rsx(root: RsxRoot) -> TokenStream {
|
||||
let nodes = root.nodes.iter().map(emit_node_scope);
|
||||
emit_rsx_vec(&root.nodes)
|
||||
}
|
||||
|
||||
pub fn emit_rsx_vec(nodes: &Vec<RsxNode>) -> TokenStream {
|
||||
let nodes = nodes.iter().map(emit_node_scope);
|
||||
|
||||
quote! {{
|
||||
let mut r = osui::frontend::Rsx::new();
|
||||
@@ -55,7 +59,7 @@ fn emit_node_scope(node: &RsxNode) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
RsxNode::Component(_) => {
|
||||
RsxNode::Component { .. } => {
|
||||
let emit = emit_node(node);
|
||||
quote! {
|
||||
r.static_scope(move |scope| {#emit});
|
||||
@@ -125,9 +129,38 @@ fn emit_node(node: &RsxNode) -> TokenStream {
|
||||
}));
|
||||
},
|
||||
|
||||
RsxNode::Component(expr) => quote! {
|
||||
scope.child(#expr, None);
|
||||
},
|
||||
RsxNode::Component {
|
||||
path,
|
||||
props,
|
||||
children,
|
||||
} => {
|
||||
let prop_inits = props.iter().map(|p| {
|
||||
let name = &p.name;
|
||||
let value = &p.value;
|
||||
quote! { #name: #value }
|
||||
});
|
||||
|
||||
let emit_children = emit_rsx_vec(children);
|
||||
|
||||
let component_expr = if children.len() > 0 {
|
||||
quote! {
|
||||
#path {
|
||||
#(#prop_inits,)*
|
||||
children: #emit_children
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#path {
|
||||
#(#prop_inits,)*
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
scope.child(#component_expr, None);
|
||||
}
|
||||
}
|
||||
|
||||
RsxNode::Mount(m) => quote! {
|
||||
#m.mount();
|
||||
|
||||
+85
-4
@@ -1,7 +1,8 @@
|
||||
use syn::braced;
|
||||
use syn::{
|
||||
Expr, Ident, LitStr, Pat, Result, Token,
|
||||
Expr, Ident, LitStr, Pat, Path, Result, Token,
|
||||
parse::{Parse, ParseStream},
|
||||
token::Brace,
|
||||
};
|
||||
|
||||
pub struct RsxRoot {
|
||||
@@ -18,9 +19,20 @@ impl Parse for RsxRoot {
|
||||
}
|
||||
}
|
||||
|
||||
/// A single prop: `ident: expr`.
|
||||
pub struct RsxProp {
|
||||
pub name: Ident,
|
||||
pub value: Expr,
|
||||
}
|
||||
|
||||
pub enum RsxNode {
|
||||
Text(LitStr),
|
||||
Component(Expr),
|
||||
/// Component with optional props and children: `Path { prop: expr, "child", ... }` or bare `Path`.
|
||||
Component {
|
||||
path: Path,
|
||||
props: Vec<RsxProp>,
|
||||
children: Vec<RsxNode>,
|
||||
},
|
||||
Mount(Ident),
|
||||
If {
|
||||
deps: Vec<Dep>,
|
||||
@@ -116,11 +128,80 @@ impl Parse for RsxNode {
|
||||
return Ok(RsxNode::Text(input.parse()?));
|
||||
}
|
||||
|
||||
// Component: path (e.g. HelloWorld) or struct init (e.g. HelloWorld { t: 10 })
|
||||
Ok(RsxNode::Component(input.parse()?))
|
||||
// Component: Path or Path { props, children }
|
||||
parse_component_invocation(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses Path or Path { ... } into RsxNode::Component.
|
||||
fn parse_component_invocation(input: ParseStream) -> Result<RsxNode> {
|
||||
let path: Path = input.parse()?;
|
||||
|
||||
if !input.peek(Brace) {
|
||||
return Ok(RsxNode::Component {
|
||||
path,
|
||||
props: Vec::new(),
|
||||
children: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let content;
|
||||
braced!(content in input);
|
||||
|
||||
let (props, children) = parse_props_and_children(&content)?;
|
||||
|
||||
Ok(RsxNode::Component {
|
||||
path,
|
||||
props,
|
||||
children,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses comma-separated "ident: expr" (props) and RsxNode (children) inside braces.
|
||||
fn parse_props_and_children(input: ParseStream) -> Result<(Vec<RsxProp>, Vec<RsxNode>)> {
|
||||
let mut props = Vec::new();
|
||||
let mut children = Vec::new();
|
||||
|
||||
while !input.is_empty() {
|
||||
if input.peek(LitStr) {
|
||||
children.push(input.parse()?);
|
||||
} else {
|
||||
let path: Path = input.parse()?;
|
||||
|
||||
if input.peek(Token![:]) {
|
||||
let name = path
|
||||
.get_ident()
|
||||
.cloned()
|
||||
.ok_or_else(|| input.error("prop name must be a single identifier"))?;
|
||||
input.parse::<Token![:]>()?;
|
||||
let value: Expr = input.parse()?;
|
||||
props.push(RsxProp { name, value });
|
||||
} else if input.peek(Brace) {
|
||||
let nested_content;
|
||||
braced!(nested_content in input);
|
||||
let (nested_props, nested_children) = parse_props_and_children(&nested_content)?;
|
||||
children.push(RsxNode::Component {
|
||||
path,
|
||||
props: nested_props,
|
||||
children: nested_children,
|
||||
});
|
||||
} else {
|
||||
children.push(RsxNode::Component {
|
||||
path,
|
||||
props: Vec::new(),
|
||||
children: Vec::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !input.is_empty() {
|
||||
input.parse::<Token![,]>()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((props, children))
|
||||
}
|
||||
|
||||
fn parse_children(input: ParseStream) -> Result<Vec<RsxNode>> {
|
||||
let mut nodes = Vec::new();
|
||||
while !input.is_empty() {
|
||||
|
||||
@@ -11,6 +11,7 @@ pub mod state;
|
||||
pub mod prelude {
|
||||
pub use crate::component::*;
|
||||
pub use crate::engine::*;
|
||||
pub use crate::frontend::*;
|
||||
pub use crate::render::*;
|
||||
pub use crate::state::*;
|
||||
pub use crate::View;
|
||||
|
||||
+2
-2
@@ -115,11 +115,11 @@ impl<T> DerefMut for Inner<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HookDependency {
|
||||
pub trait HookDependency: Send + Sync {
|
||||
fn on_update(&self, hook: HookEffect);
|
||||
}
|
||||
|
||||
impl<T> HookDependency for State<T> {
|
||||
impl<T: Send + Sync> HookDependency for State<T> {
|
||||
fn on_update(&self, hook: HookEffect) {
|
||||
self.dependents.lock().unwrap().push(hook);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user