Quellcode durchsuchen

Feat: building large apps, revamp macro

Jonathan Kelley vor 4 Jahren
Ursprung
Commit
9f7f43b

+ 1 - 0
Cargo.toml

@@ -6,6 +6,7 @@ members = [
     "packages/web",
     "packages/liveview",
     "packages/3d",
+    "packages/docsite",
 ]
 
 # Built-in

+ 2 - 2
packages/core-macro/src/htm.rs

@@ -58,11 +58,11 @@ impl ToTokens for HtmlRender {
 
         // create a lazy tree that accepts a bump allocator
         let final_tokens = quote! {
-            move |ctx| {
+            dioxus::prelude::LazyNodes::new(move |ctx| {
                 let bump = ctx.bump;
 
                 #new_toks
-            }
+            })
         };
 
         final_tokens.to_tokens(out_tokens);

+ 66 - 25
packages/core-macro/src/rsxt.rs

@@ -30,6 +30,10 @@ ctx.render(rsx!{
 })
 ```
 
+optionally, include the allocator directly
+
+rsx!(ctx, div { "hello"} )
+
 each element is given by tag { [Attr] }
 
 */
@@ -50,18 +54,31 @@ use {
 // Parse any stream coming from the rsx! macro
 // ==============================================
 pub struct RsxRender {
+    custom_context: Option<Ident>,
     el: Element,
 }
 
 impl Parse for RsxRender {
     fn parse(input: ParseStream) -> Result<Self> {
+        let fork = input.fork();
+
+        let custom_context = fork
+            .parse::<Ident>()
+            .and_then(|ident| {
+                fork.parse::<Token![,]>().map(|_| {
+                    input.advance_to(&fork);
+                    ident
+                })
+            })
+            .ok();
+
         // cannot accept multiple elements
         // can only accept one root per component
         // fragments can be used as
         // todo
         // enable fragements by autocoerrcing into list
         let el: Element = input.parse()?;
-        Ok(Self { el })
+        Ok(Self { el, custom_context })
     }
 }
 
@@ -71,11 +88,19 @@ impl ToTokens for RsxRender {
         // let new_toks = ToToksCtx::new(&self.kind).to_token_stream();
 
         // create a lazy tree that accepts a bump allocator
-        let final_tokens = quote! {
-            move |ctx: &dioxus::prelude::NodeCtx<'_>| -> dioxus::prelude::VNode<'_>{
-                let bump = ctx.bump;
-                #new_toks
-            }
+        let final_tokens = match &self.custom_context {
+            Some(ident) => quote! {
+                #ident.render(dioxus::prelude::LazyNodes::new(move |ctx|{
+                    let bump = ctx.bump;
+                    #new_toks
+                }))
+            },
+            None => quote! {
+                dioxus::prelude::LazyNodes::new(move |ctx|{
+                    let bump = ctx.bump;
+                    #new_toks
+                })
+            },
         };
 
         final_tokens.to_tokens(out_tokens);
@@ -102,29 +127,32 @@ impl ToTokens for ToToksCtx<&Node> {
 }
 
 impl Parse for Node {
-    fn parse(s: ParseStream) -> Result<Self> {
-        let fork1 = s.fork();
-        let fork2 = s.fork();
+    fn parse(stream: ParseStream) -> Result<Self> {
+        let fork1 = stream.fork();
+        let fork2 = stream.fork();
 
         // todo: map issues onto the second fork if any arise
         // it's unlikely that textnodes or components would fail?
 
         let ret = if let Ok(text) = fork1.parse::<TextNode>() {
-            s.advance_to(&fork1);
+            stream.advance_to(&fork1);
             Self::Text(text)
         } else if let Ok(element) = fork2.parse::<Element>() {
-            s.advance_to(&fork2);
+            stream.advance_to(&fork2);
             Self::Element(element)
-        } else if let Ok(comp) = s.parse::<Component>() {
+        } else if let Ok(comp) = stream.parse::<Component>() {
             Self::Component(comp)
         } else {
-            return Err(Error::new(s.span(), "Failed to parse as a valid child"));
+            return Err(Error::new(
+                stream.span(),
+                "Failed to parse as a valid child",
+            ));
         };
 
         // consume comma if it exists
         // we don't actually care if there *are* commas after elements/text
-        if s.peek(Token![,]) {
-            let _ = s.parse::<Token![,]>();
+        if stream.peek(Token![,]) {
+            let _ = stream.parse::<Token![,]>();
         }
         Ok(ret)
     }
@@ -301,7 +329,7 @@ impl ToTokens for ToToksCtx<&Element> {
         }
         match &self.inner.children {
             MaybeExpr::Expr(expr) => tokens.append_all(quote! {
-                .children(#expr)
+                .iter_child(#expr)
             }),
             MaybeExpr::Literal(nodes) => {
                 let mut children = nodes.iter();
@@ -340,7 +368,7 @@ impl Parse for Element {
 
         let mut attrs: Vec<Attr> = vec![];
         let mut children: Vec<Node> = vec![];
-        parse_element_content(content, &mut attrs, &mut children);
+        parse_element_content(content, &mut attrs, &mut children)?;
 
         let children = MaybeExpr::Literal(children);
 
@@ -353,7 +381,11 @@ impl Parse for Element {
 }
 
 // used by both vcomponet and velement to parse the contents of the elements into attras and children
-fn parse_element_content(content: ParseBuffer, attrs: &mut Vec<Attr>, children: &mut Vec<Node>) {
+fn parse_element_content(
+    stream: ParseBuffer,
+    attrs: &mut Vec<Attr>,
+    children: &mut Vec<Node>,
+) -> Result<()> {
     'parsing: loop {
         // todo move this around into a more functional style
         // [1] Break if empty
@@ -363,38 +395,47 @@ fn parse_element_content(content: ParseBuffer, attrs: &mut Vec<Attr>, children:
         // [last] Fail if none worked
 
         // [1] Break if empty
-        if content.is_empty() {
+        if stream.is_empty() {
             break 'parsing;
         }
 
         // [2] Try to consume an attr
-        let fork = content.fork();
+        let fork = stream.fork();
         if let Ok(attr) = fork.parse::<Attr>() {
             // make sure to advance or your computer will become a spaceheater :)
-            content.advance_to(&fork);
+            stream.advance_to(&fork);
             attrs.push(attr);
             continue 'parsing;
         }
 
         // [3] Try to consume a child node
-        let fork = content.fork();
+        let fork = stream.fork();
         if let Ok(node) = fork.parse::<Node>() {
             // make sure to advance or your computer will become a spaceheater :)
-            content.advance_to(&fork);
+            stream.advance_to(&fork);
             children.push(node);
             continue 'parsing;
         }
 
         // [4] TODO: Parsing brackets
-        // let fork = content.fork();
+
+        if stream.peek(token::Brace) {
+            // this can fail (mismatched brackets)
+            let content;
+            syn::braced!(content in &stream);
+            continue 'parsing;
+            // let fork = content.fork();
+            // stream.advance_to(fork)
+        }
         // if let Ok(el) = fork.parse() {
         //     children.push(el);
         //     continue 'parsing;
         // }
 
         // todo: pass a descriptive error onto the offending tokens
-        panic!("Entry is not an attr or element\n {}", content)
+        panic!("Entry is not an attr or element\n {}", stream)
     }
+    Ok(())
 }
 
 /// =======================================

+ 15 - 91
packages/core/examples/alternative.rs

@@ -1,93 +1,17 @@
-// //! An alternative function syntax
-// //!
-
-// use bumpalo::Bump;
-// use dioxus_core::prelude::VNode;
-
 fn main() {}
 
-// struct Context2<'a, P> {
-//     _props: &'a P, // _p: PhantomData<&'a ()>,
-//     rops: &'a P,   // _p: PhantomData<&'a ()>,
-// }
-// impl<'a, P> Context2<'a, P> {
-//     fn render(self, _f: impl FnOnce(&'a Bump) -> VNode<'a>) -> DTree {
-//         DTree {}
-//     }
-
-//     fn props(&self) -> &'a P {
-//         todo!()
-//     }
-
-//     pub fn use_hook<'scope, InternalHookState: 'static, Output: 'a>(
-//         &'scope self,
-//         _initializer: impl FnOnce() -> InternalHookState,
-//         _runner: impl FnOnce(&'a mut InternalHookState) -> Output,
-//         _cleanup: impl FnOnce(InternalHookState),
-//     ) -> Output {
-//         todo!()
-//     }
-// }
-
-// trait Properties {}
-
-// struct DTree;
-// // type FC2<'a, T: 'a> = fn(Context2<T>) -> DTree;
-// fn virtual_child<'a, T: 'a>(_bump: &'a Bump, _props: T, _f: FC2<T>) -> VNode<'a> {
-//     todo!()
-// }
-
-// struct Props {
-//     c: String,
-// }
-
-// fn Example(ctx: Context2<Props>) -> DTree {
-//     let val = use_state(&ctx, || String::from("asd"));
-//     let props = ctx.props();
-
-//     ctx.render(move |b| {
-//         dioxus_core::nodebuilder::div(b)
-//             .child(dioxus_core::nodebuilder::text(props.c.as_str()))
-//             .child(virtual_child(b, Props2 { a: val }, AltChild))
-//             .finish()
-//     })
-// }
-
-// // #[fc]
-// fn Example2(ctx: Context2<()>, name: &str, _blah: &str) -> DTree {
-//     let val = use_state(&ctx, || String::from("asd"));
-
-//     ctx.render(move |ctx| {
-//         dioxus_core::builder::ElementBuilder::new(b, "div")
-//             .child(dioxus_core::nodebuilder::text(name))
-//             .child(virtual_child(b, Props2 { a: val }, AltChild))
-//             .finish()
-//     })
-// }
-
-// type FC2<'a, T> = fn(Context2<T>) -> DTree;
-
-// // still works if you don't take any references in your props (ie, something copy or cloneable)
-// static CHILD: FC2<Props2> = |_ctx: Context2<Props2>| {
-//     //
-//     todo!()
-// };
-
-// struct Props2<'a> {
-//     a: &'a String,
-// }
-// impl Properties for Props2<'_> {}
-
-// fn AltChild(ctx: Context2<Props2>) -> DTree {
-//     ctx.render(|_b| {
-//         //
-//         todo!()
-//     })
-// }
-
-// fn use_state<'a, 'c, P, T: 'static, F: FnOnce() -> T>(
-//     _ctx: &'_ Context2<'a, P>,
-//     _initial_state_fn: F,
-// ) -> &'a T {
-//     todo!()
-// }
+use dioxus_core::prelude::*;
+
+static Example: FC<()> = |ctx, props| {
+    ctx.render(dioxus_core::prelude::LazyNodes::new(move |ctx| {
+        let bump = ctx.bump;
+        dioxus::builder::ElementBuilder::new(ctx, "h1")
+            .children([{
+                use bumpalo::core_alloc::fmt::Write;
+                let mut s = bumpalo::collections::String::new_in(bump);
+                write!(s, "hello");
+                dioxus::builder::text2(s)
+            }])
+            .finish()
+    }))
+};

+ 2 - 2
packages/core/examples/borrowed.rs

@@ -22,7 +22,7 @@ struct ListItem {
 fn app(ctx: Context, props: &Props) -> DomTree {
     let (_f, setter) = use_state(&ctx, || 0);
 
-    ctx.render(move |c| {
+    ctx.render(dioxus::prelude::LazyNodes::new(move |c| {
         let mut root = builder::ElementBuilder::new(c, "div");
         for child in &props.items {
             // notice that the child directly borrows from our vec
@@ -39,7 +39,7 @@ fn app(ctx: Context, props: &Props) -> DomTree {
             ));
         }
         root.finish()
-    })
+    }))
 }
 
 type StateSetter<T> = dyn Fn(T);

+ 2 - 2
packages/core/examples/contextapi.rs

@@ -13,7 +13,7 @@ struct Props {
 static Example: FC<Props> = |ctx, props| {
     let value = ctx.use_context(|c: &SomeContext| c.items.last().unwrap());
 
-    ctx.render(move |bump| {
+    ctx.render(LazyNodes::new(move |bump| {
         builder::ElementBuilder::new(bump, "button")
             .on("click", move |_| {
                 println!("Value is {}", props.name);
@@ -24,7 +24,7 @@ static Example: FC<Props> = |ctx, props| {
                 println!("Value is {}", props.name);
             })
             .finish()
-    })
+    }))
     // ctx.render(html! {
     //     <div>
     //         <button onclick={move |_| println!("Value is {}", value)} />

+ 47 - 1
packages/core/examples/fc.rs

@@ -18,8 +18,54 @@ pub struct ExampleProps {
 }
 
 static SomeComponent: FC<ExampleProps> = |ctx, _props| {
+    let blah = rsx! {
+        div {}
+    };
+
+    let data = match 1 {
+        1 => ctx.render(rsx! (
+            div {
+                h1 {}
+                h3 {}
+            }
+        )),
+        1 => ctx.render(rsx!( div { "abc" } )),
+        2 => ctx.render(rsx!( div { "abc" } )),
+        3 => ctx.render(rsx!( div { "abc" } )),
+        _ => todo!(),
+    };
+
+    let data = match 1 {
+        1 => ctx.render(rsx! (
+            div {
+                h1 {}
+                h3 {}
+            }
+        )),
+        1 => ctx.render(rsx!(
+            div { "abc" }
+        )),
+        2 => ctx.render(rsx!(
+            div { "abc" }
+        )),
+        3 => ctx.render(rsx!(
+            div { "abc" }
+        )),
+        _ => todo!(),
+    };
+
+    let i = (0..10).map(|v| {
+        rsx! {
+            div {
+                "{v}"
+            }
+        }
+    });
+
     ctx.render(rsx! {
-        div { }
+        div {
+            ""
+         }
     })
 };
 

+ 2 - 2
packages/core/examples/nested.rs

@@ -12,7 +12,7 @@ static Header: FC<()> = |ctx, props| {
 
     let handler1 = move || println!("Value is {}", inner.current());
 
-    ctx.render(|c| {
+    ctx.render(dioxus::prelude::LazyNodes::new(|c| {
         builder::ElementBuilder::new(c, "div")
             .child(VNode::Component(VComponent::new(
                 Bottom,
@@ -20,7 +20,7 @@ static Header: FC<()> = |ctx, props| {
                 c.bump.alloc(()),
             )))
             .finish()
-    })
+    }))
 };
 
 static Bottom: FC<()> = |ctx, props| {

+ 0 - 1
packages/core/examples/rsx_usage.rs

@@ -1,4 +1,3 @@
-
 use dioxus_core::prelude::*;
 
 fn main() {}

+ 7 - 4
packages/core/src/context.rs

@@ -1,5 +1,5 @@
-use crate::nodes::VNode;
-use crate::prelude::*;
+use crate::{nodebuilder::IntoDomTree, prelude::*};
+use crate::{nodebuilder::LazyNodes, nodes::VNode};
 use bumpalo::Bump;
 use hooks::Hook;
 use std::{cell::RefCell, future::Future, ops::Deref, rc::Rc, sync::atomic::AtomicUsize};
@@ -95,7 +95,10 @@ impl<'a> Context<'a> {
     ///     ctx.render(lazy_tree)
     /// }
     ///```
-    pub fn render(&self, lazy_nodes: impl FnOnce(&'_ NodeCtx<'a>) -> VNode<'a> + 'a) -> DomTree {
+    pub fn render<F: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a>(
+        &self,
+        lazy_nodes: LazyNodes<'a, F>,
+    ) -> DomTree {
         let ctx = NodeCtx {
             bump: self.bump,
             scope: self.scope,
@@ -103,7 +106,7 @@ impl<'a> Context<'a> {
             listeners: self.listeners,
         };
 
-        let safe_nodes = lazy_nodes(&ctx);
+        let safe_nodes = lazy_nodes.into_vnode(&ctx);
         let root: VNode<'static> = unsafe { std::mem::transmute(safe_nodes) };
         DomTree { root }
     }

+ 7 - 6
packages/core/src/events.rs

@@ -160,12 +160,7 @@ pub mod on {
 
     #[derive(Debug)]
     pub struct MouseEvent(pub Box<RawMouseEvent>);
-    impl Deref for MouseEvent {
-        type Target = RawMouseEvent;
-        fn deref(&self) -> &Self::Target {
-            self.0.as_ref()
-        }
-    }
+
     #[derive(Debug)]
     pub struct RawMouseEvent {
         pub alt_key: bool,
@@ -183,6 +178,12 @@ pub mod on {
         pub get_modifier_state: GetModifierKey,
         // relatedTarget: DOMEventTarget,
     }
+    impl Deref for MouseEvent {
+        type Target = RawMouseEvent;
+        fn deref(&self) -> &Self::Target {
+            self.0.as_ref()
+        }
+    }
     event_builder! {
         MouseEvent;
         click contextmenu doubleclick drag dragend dragenter dragexit

+ 2 - 0
packages/core/src/lib.rs

@@ -138,6 +138,8 @@ pub mod prelude {
     use crate::nodes;
     pub use nodes::*;
 
+    pub use crate::nodebuilder::LazyNodes;
+
     pub use crate::context::NodeCtx;
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.

+ 115 - 149
packages/core/src/nodebuilder.rs

@@ -16,14 +16,13 @@ use crate::{
 /// function for building `<div>` elements or the `button` function for building
 /// `<button>` elements.
 #[derive(Debug)]
-pub struct ElementBuilder<'a, Listeners, Attributes, Children>
+pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Attributes: 'a + AsRef<[Attribute<'a>]>,
     Children: 'a + AsRef<[VNode<'a>]>,
 {
-    ctx: NodeCtx<'a>,
-    // ctx: &'b NodeCtx<'a>,
+    ctx: &'b NodeCtx<'a>,
     key: NodeKey,
     tag_name: &'a str,
     listeners: Listeners,
@@ -32,9 +31,10 @@ where
     namespace: Option<&'a str>,
 }
 
-impl<'a>
+impl<'a, 'b>
     ElementBuilder<
         'a,
+        'b,
         bumpalo::collections::Vec<'a, Listener<'a>>,
         bumpalo::collections::Vec<'a, Attribute<'a>>,
         bumpalo::collections::Vec<'a, VNode<'a>>,
@@ -63,24 +63,22 @@ impl<'a>
     /// let my_element_builder = ElementBuilder::new(&b, tag_name);
     /// # fn flip_coin() -> bool { true }
     /// ```
-    pub fn new(ctx: &NodeCtx<'a>, tag_name: &'static str) -> Self {
-        // pub fn new(ctx: &'b NodeCtx<'a>, tag_name: &'static str) -> Self {
-        // pub fn new<B>(ctx: &'a mut NodeCtx<'a>, tag_name: &'a str) -> Self {
+    pub fn new(ctx: &'b NodeCtx<'a>, tag_name: &'static str) -> Self {
         let bump = ctx.bump;
-        todo!()
-        // ElementBuilder {
-        //     ctx,
-        //     key: NodeKey::NONE,
-        //     tag_name,
-        //     listeners: bumpalo::collections::Vec::new_in(bump),
-        //     attributes: bumpalo::collections::Vec::new_in(bump),
-        //     children: bumpalo::collections::Vec::new_in(bump),
-        //     namespace: None,
-        // }
+        ElementBuilder {
+            ctx,
+            key: NodeKey::NONE,
+            tag_name,
+            listeners: bumpalo::collections::Vec::new_in(bump),
+            attributes: bumpalo::collections::Vec::new_in(bump),
+            children: bumpalo::collections::Vec::new_in(bump),
+            namespace: None,
+        }
     }
 }
 
-impl<'a, Listeners, Attributes, Children> ElementBuilder<'a, Listeners, Attributes, Children>
+impl<'a, 'b, Listeners, Attributes, Children>
+    ElementBuilder<'a, 'b, Listeners, Attributes, Children>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Attributes: 'a + AsRef<[Attribute<'a>]>,
@@ -114,7 +112,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, L, Attributes, Children>
+    pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
     where
         L: 'a + AsRef<[Listener<'a>]>,
     {
@@ -153,7 +151,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, Listeners, A, Children>
+    pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
     where
         A: 'a + AsRef<[Attribute<'a>]>,
     {
@@ -192,7 +190,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn children<C>(self, children: C) -> ElementBuilder<'a, Listeners, Attributes, C>
+    pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
     where
         C: 'a + AsRef<[VNode<'a>]>,
     {
@@ -311,8 +309,8 @@ where
     }
 }
 
-impl<'a, Attributes, Children>
-    ElementBuilder<'a, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
+impl<'a, 'b, Attributes, Children>
+    ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
 where
     Attributes: 'a + AsRef<[Attribute<'a>]>,
     Children: 'a + AsRef<[VNode<'a>]>,
@@ -365,8 +363,8 @@ where
     }
 }
 
-impl<'a, Listeners, Children>
-    ElementBuilder<'a, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
+impl<'a, 'b, Listeners, Children>
+    ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Children: 'a + AsRef<[VNode<'a>]>,
@@ -424,8 +422,8 @@ where
     }
 }
 
-impl<'a, Listeners, Attributes>
-    ElementBuilder<'a, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
+impl<'a, 'b, Listeners, Attributes>
+    ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
 where
     Listeners: 'a + AsRef<[Listener<'a>]>,
     Attributes: 'a + AsRef<[Attribute<'a>]>,
@@ -451,16 +449,26 @@ where
         self
     }
 
-    pub fn iter_child(mut self, nodes: impl IntoIterator<Item = DomTree>) -> Self {
-        for item in nodes.into_iter() {
-            self.children.push(item.root);
+    /// Add multiple children to this element from an iterator.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use dioxus::{builder::*, bumpalo::Bump};
+    ///
+    /// let b = Bump::new();
+    ///
+    /// let my_div = p(&b)
+    ///     .iter_child((0..10).map(|f| span(&b).finish())
+    ///     .finish();
+    /// ```    
+    pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoDomTree<'a>>) -> Self {
+        for item in nodes {
+            let child = item.into_vnode(&self.ctx);
+            self.children.push(child);
         }
         self
     }
-
-    pub fn into_tomdtree(mut self, nodes: impl IntoDomTree<'a>) -> Self {
-        self
-    }
 }
 
 impl IntoIterator for DomTree {
@@ -472,166 +480,124 @@ impl IntoIterator for DomTree {
 }
 
 pub trait IntoDomTree<'a> {
-    type NodeIter: IntoIterator<Item = DomTree>;
-    fn into_domtree(self, ctx: &NodeCtx<'a>) -> Self::NodeIter;
+    fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a>;
 }
 
-impl IntoDomTree<'_> for DomTree {
-    type NodeIter = std::iter::Once<DomTree>;
-    fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
-        std::iter::once(self)
+// Cover the cases where nodes are pre-rendered.
+// Likely used by enums.
+// ----
+//  let nodes = ctx.render(rsx!{ ... };
+//  rsx! { {nodes } }
+impl<'a> IntoDomTree<'a> for DomTree {
+    fn into_vnode(self, _ctx: &NodeCtx<'a>) -> VNode<'a> {
+        self.root
     }
 }
 
-impl<'a, G> IntoDomTree<'a> for NodeWrapper<'a, G>
+// Wrap the the node-builder closure in a concrete type.
+// ---
+// This is a bit of a hack to implement the IntoDomTree trait for closure types.
+pub struct LazyNodes<'a, G>
 where
-    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
+    G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a,
 {
-    type NodeIter = std::iter::Once<DomTree>;
-
-    fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
-        let p: VNode<'_> = (self.inner)(ctx);
-        let root: VNode<'static> = unsafe { std::mem::transmute(p) };
-        std::iter::once(DomTree { root })
-    }
+    inner: G,
+    _p: std::marker::PhantomData<&'a ()>,
 }
 
-impl<'a, G, I> IntoDomTree<'a> for I
+impl<'a, G> LazyNodes<'a, G>
 where
-    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
-    I: Iterator<Item = NodeWrapper<'a, G>>,
-    // O: Iterator<Item = DomTree>,
+    G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a,
 {
-    type NodeIter = std::iter::Map<I, O>;
-
-    fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
-        self.map(|f| {
-            //
-            let caller = (f.inner);
-            let r = caller(ctx);
-        })
-        // todo!()
-        // let p: VNode<'_> = (self.inner)(ctx);
-        // let root: VNode<'static> = unsafe { std::mem::transmute(p) };
-        // std::iter::once(DomTree { root })
+    pub fn new(f: G) -> Self {
+        Self {
+            inner: f,
+            _p: std::default::Default::default(),
+        }
     }
 }
 
-/*
-all possible impls
-
-rsx!{ }
-ctx.render(rsx!)
-
-map(rsx!)
-map(ctx.render(rsx!))
-*/
-
-pub struct NodeWrapper<'a, G>
+// Cover the cases where nodes are used by macro.
+// Likely used directly.
+// ---
+//  let nodes = rsx!{ ... };
+//  rsx! { {nodes } }
+impl<'a, G> IntoDomTree<'a> for LazyNodes<'a, G>
 where
-    G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
+    G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a,
 {
-    inner: G,
-    _p: std::marker::PhantomData<&'a ()>,
+    fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a> {
+        (self.inner)(ctx)
+    }
 }
 
-fn new_wrapper<'a, G>(f: G) -> NodeWrapper<'a, G>
+// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
+impl<'a, G> IntoIterator for LazyNodes<'a, G>
 where
     G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
 {
-    NodeWrapper {
-        inner: f,
-        _p: std::default::Default::default(),
+    type Item = Self;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
     }
 }
 
 #[test]
 fn test_iterator_of_nodes<'b>() {
-    let p = (0..10).map(|f| {
-        //
-        new_wrapper(rsx! {
-            div {
-                "aaa {f}"
-            }
-        })
-    });
-
-    let g = p.into_iter();
-    for f in g {}
+    use crate::prelude::*;
 
     static Example: FC<()> = |ctx, props| {
-        ctx.render(|c| {
-            //
-            ElementBuilder::new(c, "div")
-                .into_tomdtree({
-                    // rsx!
-                    new_wrapper(move |n: &NodeCtx| -> VNode {
-                        //
-                        ElementBuilder::new(n, "div").finish()
-                    })
-                    // use the macro to be "blind" to the type
-                    // everything gets interpreted as
-                    /*
-
-
-
-                    for node in {expr} {
-                        ctx.push_alloc_node(node)
-                    }
-
-
-                    so.... just make sure whatever enters {} is iterable (domtree and nodewrapper need impls, )
-
-
-                    */
-                    //
-                })
-                .into_tomdtree({
-                    // render to wrapper -> tree
-                    ctx.render(rsx! {
-                        div {}
-                    })
-                })
-                .into_tomdtree({
-                    // map rsx!
-                    (0..10).map(|f| {
-                        new_wrapper(move |n: &NodeCtx| -> VNode {
-                            //
-                            ElementBuilder::new(n, "div").finish()
-                        })
-                    })
-                })
-                .finish()
+        let g: LazyNodes<_> = rsx! {
+            div {}
+        };
+
+        ctx.render(rsx! {
+            div {
+                h1 {}
+                {}
+            }
         })
     };
 
-    use crate::prelude::*;
-
-    // static Example: FC<()> = |ctx, props| {
+    // let p = (0..10).map(|f| {
     //     //
-    //     let list = (0..10).map(|f| {
-    //         ctx.render(rsx! {
-    //             div {}
-    //         })
-    //     });
+    //     LazyNodes::new(rsx! {
+    //         div {
+    //             "aaa {f}"
+    //         }
+    //     })
+    // });
+
+    // let g = p.into_iter();
+    // for f in g {}
 
+    // static Example: FC<()> = |ctx, props| {
     //     ctx.render(|c| {
+    //         //
     //         ElementBuilder::new(c, "div")
     //             .iter_child({
-    //                 //
+    //                 // rsx!
+    //                 LazyNodes::new(move |n: &NodeCtx| -> VNode {
+    //                     //
+    //                     ElementBuilder::new(n, "div").finish()
+    //                 })
+    //             })
+    //             .iter_child({
+    //                 // render to wrapper -> tree
     //                 ctx.render(rsx! {
     //                     div {}
     //                 })
     //             })
     //             .iter_child({
-    //                 //
+    //                 // map rsx!
     //                 (0..10).map(|f| {
-    //                     ctx.render(rsx! {
-    //                         div {}
+    //                     LazyNodes::new(move |n: &NodeCtx| -> VNode {
+    //                         //
+    //                         ElementBuilder::new(n, "div").finish()
     //                     })
     //                 })
     //             })
-    //             .iter_child(list)
     //             .finish()
     //     })
     // };

+ 1 - 1
packages/core/src/nodes.rs

@@ -5,7 +5,7 @@
 
 use crate::{
     events::VirtualEvent,
-    innerlude::{Context, Properties, ScopeIdx, FC},
+    innerlude::{Context, NodeCtx, Properties, ScopeIdx, FC},
 };
 
 use bumpalo::Bump;

+ 10 - 0
packages/docsite/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "dioxus-docs-site"
+version = "0.0.0"
+authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+dioxus-ssr = { path = "../ssr" }

+ 10 - 0
packages/docsite/README.md

@@ -0,0 +1,10 @@
+# Dioxus docs site
+
+Generate the liveview site powering diouxslabs.com.
+
+We use the dioxus SSR crate and the use_router hook to generate a 
+
+
+
+
+

+ 0 - 0
packages/docsite/src/components/app.rs


+ 82 - 0
packages/docsite/src/main.rs

@@ -0,0 +1,82 @@
+use dioxus_ssr::{prelude::*, TextRenderer};
+
+fn main() {
+    TextRenderer::new(App);
+}
+
+#[derive(Debug, PartialEq)]
+enum Routes {
+    Homepage,
+    ExampleList,
+}
+
+#[derive(Debug, PartialEq, Props)]
+struct AppProps {
+    route: Routes,
+}
+
+trait Blah {}
+impl<'a, G> Blah for LazyNodes<'a, G> where G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a {}
+
+static App: FC<AppProps> = |ctx, props| {
+    //
+    let body = match props.route {
+        Routes::Homepage => ctx.render(rsx!(
+            div {
+                Homepage {}
+            }
+        )),
+        Routes::ExampleList => ctx.render(rsx!(
+        div {
+            ExampleList {}
+            }
+        )),
+    };
+
+    ctx.render(rsx!(
+        div {
+            Header {}
+            {body}
+            {
+            }
+            Footer {}
+        }
+    ))
+};
+
+#[derive(Debug, PartialEq, Props)]
+struct HeaderProp {
+    selected: Routes,
+}
+
+static Header: FC<()> = |ctx, _| {
+    ctx.render(rsx! {
+        div {
+
+        }
+    })
+};
+
+static Footer: FC<()> = |ctx, _| {
+    ctx.render(rsx! {
+        div {
+
+        }
+    })
+};
+
+static Homepage: FC<()> = |ctx, _| {
+    ctx.render(rsx! {
+        div {
+
+        }
+    })
+};
+
+static ExampleList: FC<()> = |ctx, _| {
+    ctx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 4 - 0
packages/ssr/src/lib.rs

@@ -21,6 +21,10 @@
 
 use dioxus_core::prelude::{VNode, FC};
 
+pub mod prelude {
+    pub use dioxus_core::prelude::*;
+}
+
 /// The `TextRenderer` provides a way of rendering a Dioxus Virtual DOM to a String.
 ///
 ///

+ 6 - 2
packages/web/examples/rsxt.rs

@@ -1,6 +1,6 @@
 #![allow(non_snake_case)]
 use dioxus_core as dioxus;
-use dioxus::prelude::*;
+use dioxus::{events::on::MouseEvent, prelude::*};
 use dioxus_web::WebsysRenderer;
 
 fn main() {
@@ -16,6 +16,9 @@ fn main() {
     });
 }
 
+
+
+
 #[derive(PartialEq, Props)]
 struct ExampleProps {
     initial_name: &'static str,
@@ -43,13 +46,14 @@ static Example: FC<ExampleProps> = |ctx, props| {
     })
 };
 
+
+
 #[derive(Props)]
 struct ButtonProps<'src> {
     name: &'src str,
     set_name: &'src dyn Fn(String)
 }
 
-/// this is an awesome component
 fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree {
     ctx.render(rsx!{
         button {