Przeglądaj źródła

wip: pre vnodes instead of vnode

Jonathan Kelley 4 lat temu
rodzic
commit
fe6938c

+ 3 - 3
Cargo.toml

@@ -5,11 +5,11 @@ members = [
     "packages/core",
     "packages/web",
     "packages/dioxus",
-    "packages/recoil",
-    "packages/docsite",
     "packages/ssr",
-    "packages/webview",
 ]
+# "packages/recoil",
+# "packages/docsite",
+# "packages/webview",
 
 # "packages/cli",
 # "packages/webview",

+ 4 - 6
README.md

@@ -8,15 +8,13 @@
 Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
 
 ```rust
-fn Example(ctx: Context<()>) -> VNode {
+fn Example(ctx: Context<()>) -> VNodes {
     let (selection, set_selection) = use_state(&ctx, move || "..?");
 
     ctx.render(rsx! {
-        div {
-            h1 { "Hello, {selection}" }
-            button { "?", onclick: move |_| set_selection("world!")}
-            button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
-        }
+        h1 { "Hello, {selection}" }
+        button { "?", onclick: move |_| set_selection("world!")}
+        button { "?", onclick: move |_| set_selection("Dioxus 🎉")}
     })
 };
 ```

+ 35 - 35
notes/Parity.md

@@ -2,41 +2,41 @@
 
 Sorted by priority
 
-| Feature                | Dioxus | React | Notes                                |
-| ---------------------- | ------ | ----- | ------------------------------------ |
-| ----- Phase 1 -----    | -----  | ----- | -----                                |
-| Conditional Rendering  | ✅     | ✅    | if/then to hide/show component       |
-| Map, Iterator          | ✅     | ✅    | map/filter/reduce rsx!               |
-| Keyed Components       | ✅     | ✅    | advanced diffing with keys           |
-| Web                    | ✅     | ✅    | renderer for web browser             |
-| Desktop (webview)      | ✅     | ✅    | renderer for desktop                 |
-| Context                | ✅     | ✅    | share state through the tree         |
-| Hook                   | ✅     | ✅    | memory cells in components           |
-| SSR                    | ✅     | ✅    | render directly to string            |
-| Runs natively          | ✅     | 👀    | runs as a sharable binary            |
-| Null components        | 👀     | ✅    | allow returning None                 |
-| Fragments              | 👀     | ✅    | no requirement on main root          |
-| Component Children     | 👀     | ✅    | ctx.children() as a list of nodes    |
-| NodeRef                | 👀     | ✅    | gain direct access to nodes          |
-| Controlled Inputs      | 👀     | ✅    | stateful wrappers around inputs      |
-| No-div components      | 👀     | ✅    | components that render components    |
-| CSS/Inline Styles      | 🛠      | ✅    | syntax for inline/conditional styles |
-| 1st class global state | 🛠      | ✅    | redux/recoil/mobx on top of context  |
-| ----- Phase 2 -----    | -----  | ----- | -----                                |
-| 1st class router       | 👀     | ✅    | Hook built on top of history         |
-| Assets                 | 👀     | ✅    | include css/svg/img url statically   |
-| Integrated classnames  | 🛠      | 👀    | built-in `classnames`                |
-| Suspense               | 👀     | 👀    | schedule future render from future   |
-| Transition             | 👀     | 👀    | High-level control over suspense     |
-| Animation              | 👀     | ✅    | Spring-style animations              |
-| Mobile                 | 👀     | ✅    | Render with cacao                    |
-| Desktop (native)       | 👀     | ✅    | Render with native desktop           |
-| 3D Renderer            | 👀     | ✅    | react-three-fiber                    |
-| ----- Phase 3 -----    | -----  | ----- | -----                                |
-| Portal                 | 👀     | ✅    | cast elements through tree           |
-| Error/Panic boundary   | 👀     | ✅    | catch panics and display BSOD        |
-| Code-splitting         | 👀     | ✅    | Make bundle smaller/lazy             |
-| LiveView               | 👀     | 👀    | Example for SSR + WASM apps          |
+| Feature                | Dioxus | React | Notes                                            |
+| ---------------------- | ------ | ----- | ------------------------------------------------ |
+| ----- Phase 1 -----    | -----  | ----- | -----                                            |
+| Conditional Rendering  | ✅     | ✅    | if/then to hide/show component                   |
+| Map, Iterator          | ✅     | ✅    | map/filter/reduce rsx!                           |
+| Keyed Components       | ✅     | ✅    | advanced diffing with keys                       |
+| Web                    | ✅     | ✅    | renderer for web browser                         |
+| Desktop (webview)      | ✅     | ✅    | renderer for desktop                             |
+| Context                | ✅     | ✅    | share state through the tree                     |
+| Hook                   | ✅     | ✅    | memory cells in components                       |
+| SSR                    | ✅     | ✅    | render directly to string                        |
+| Runs natively          | ✅     | 👀    | runs as a sharable binary                        |
+| Component Children     | ✅     | ✅    | ctx.children() as a list of nodes                |
+| Null components        | ✅     | ✅    | allow returning no components                    |
+| No-div components      | ✅     | ✅    | components that render components                |
+| Fragments              | ✅     | ✅    | rsx! can return multiple elements without a root |
+| NodeRef                | 👀     | ✅    | gain direct access to nodes                      |
+| Controlled Inputs      | 👀     | ✅    | stateful wrappers around inputs                  |
+| CSS/Inline Styles      | 🛠      | ✅    | syntax for inline/conditional styles             |
+| 1st class global state | 🛠      | ✅    | redux/recoil/mobx on top of context              |
+| ----- Phase 2 -----    | -----  | ----- | -----                                            |
+| 1st class router       | 👀     | ✅    | Hook built on top of history                     |
+| Assets                 | 👀     | ✅    | include css/svg/img url statically               |
+| Integrated classnames  | 🛠      | 👀    | built-in `classnames`                            |
+| Suspense               | 👀     | 👀    | schedule future render from future               |
+| Transition             | 👀     | 👀    | High-level control over suspense                 |
+| Animation              | 👀     | ✅    | Spring-style animations                          |
+| Mobile                 | 👀     | ✅    | Render with cacao                                |
+| Desktop (native)       | 👀     | ✅    | Render with native desktop                       |
+| 3D Renderer            | 👀     | ✅    | react-three-fiber                                |
+| ----- Phase 3 -----    | -----  | ----- | -----                                            |
+| Portal                 | 👀     | ✅    | cast elements through tree                       |
+| Error/Panic boundary   | 👀     | ✅    | catch panics and display BSOD                    |
+| Code-splitting         | 👀     | ✅    | Make bundle smaller/lazy                         |
+| LiveView               | 👀     | 👀    | Example for SSR + WASM apps                      |
 
 ## Required services:
 

+ 3 - 9
packages/core-macro/src/rsx/ambiguous.rs

@@ -50,15 +50,9 @@ impl Parse for AmbiguousElement {
                 false => {
                     let first_char = name_str.chars().next().unwrap();
                     if first_char.is_ascii_uppercase() {
-                        if name_str == "Fragment" {
-                            input
-                                .parse::<Fragment>()
-                                .map(|c| AmbiguousElement::Fragment(c))
-                        } else {
-                            input
-                                .parse::<Component>()
-                                .map(|c| AmbiguousElement::Component(c))
-                        }
+                        input
+                            .parse::<Component>()
+                            .map(|c| AmbiguousElement::Component(c))
                     } else {
                         let name = input.parse::<Ident>().unwrap();
                         Err(Error::new(

+ 35 - 11
packages/core-macro/src/rsx/component.rs

@@ -26,7 +26,7 @@ pub struct Component {
     // accept any path-like argument
     name: syn::Path,
     body: Vec<ComponentField>,
-    _children: Vec<Node>,
+    children: Vec<Node>,
 }
 
 impl Parse for Component {
@@ -41,7 +41,7 @@ impl Parse for Component {
         syn::braced!(content in s);
 
         let mut body: Vec<ComponentField> = Vec::new();
-        let _children: Vec<Node> = Vec::new();
+        let mut children: Vec<Node> = Vec::new();
 
         'parsing: loop {
             // [1] Break if empty
@@ -49,7 +49,7 @@ impl Parse for Component {
                 break 'parsing;
             }
 
-            if content.peek(token::Brace) {
+            if content.peek(token::Brace) && content.peek2(Token![...]) {
                 let inner: ParseBuffer;
                 syn::braced!(inner in content);
                 if inner.peek(Token![...]) {
@@ -57,7 +57,11 @@ impl Parse for Component {
                 }
             }
 
-            body.push(content.parse::<ComponentField>()?);
+            if content.peek(Ident) && content.peek2(Token![:]) {
+                body.push(content.parse::<ComponentField>()?);
+            } else {
+                children.push(content.parse::<Node>()?);
+            }
 
             // consume comma if it exists
             // we don't actually care if there *are* commas between attrs
@@ -66,13 +70,10 @@ impl Parse for Component {
             }
         }
 
-        // todo: add support for children
-        let children: Vec<Node> = vec![];
-
         Ok(Self {
             name,
             body,
-            _children: children,
+            children,
         })
     }
 }
@@ -109,9 +110,32 @@ impl ToTokens for Component {
             None => quote! {None},
         };
 
-        let _toks = tokens.append_all(quote! {
-            dioxus::builder::virtual_child(__ctx, #name, #builder, #key_token)
-        });
+        let childs = &self.children;
+        let children = quote! {
+            ChildrenList::new(__ctx)
+                #( .add_child(#childs) )*
+                .finish()
+        };
+        let name_str = name.segments.last().unwrap().ident.to_string();
+        match name_str.as_str() {
+            "Fragment" => tokens.append_all(quote! {
+                dioxus::builder::vfragment(
+                    __ctx,
+                    #key_token,
+                    #children
+                )
+            }),
+            _ => tokens.append_all(quote! {
+                dioxus::builder::virtual_child(
+                    __ctx,
+                    #name,
+                    #builder,
+                    #key_token,
+                    #children
+                )
+            }),
+        }
+        // }
     }
 }
 

+ 20 - 8
packages/core-macro/src/rsx/element.rs

@@ -133,6 +133,10 @@ impl Parse for ElementAttr {
             }
         } else {
             match name_str.as_str() {
+                "key" => {
+                    // todo: better error here
+                    AttrType::BumpText(s.parse::<LitStr>()?)
+                }
                 "style" => {
                     //
                     todo!("inline style not yet supported")
@@ -196,10 +200,23 @@ impl ToTokens for ElementAttr {
         let name = self.name.to_string();
         let nameident = &self.name;
         let _attr_stream = TokenStream2::new();
+
         match &self.ty {
-            AttrType::BumpText(value) => {
+            AttrType::BumpText(value) => match name.as_str() {
+                "key" => {
+                    tokens.append_all(quote! {
+                        .key2(format_args_f!(#value))
+                    });
+                }
+                _ => {
+                    tokens.append_all(quote! {
+                        .attr(#name, format_args_f!(#value))
+                    });
+                }
+            },
+            AttrType::FieldTokens(exp) => {
                 tokens.append_all(quote! {
-                    .attr(#name, format_args_f!(#value))
+                    .attr(#name, #exp)
                 });
             }
             AttrType::Event(event) => {
@@ -207,16 +224,11 @@ impl ToTokens for ElementAttr {
                     .add_listener(dioxus::events::on::#nameident(__ctx, #event))
                 });
             }
-            AttrType::FieldTokens(exp) => {
-                tokens.append_all(quote! {
-                    .attr(#name, #exp)
-                });
-            }
             AttrType::EventTokens(event) => {
                 //
                 tokens.append_all(quote! {
                     .add_listener(dioxus::events::on::#nameident(__ctx, #event))
-                })
+                });
             }
         }
     }

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

@@ -15,6 +15,7 @@ static Header: FC<()> = |ctx| {
     ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
         builder::ElementBuilder::new(nodectx, "div")
             .child(VNode::Component(nodectx.bump().alloc(VComponent::new(
+                nodectx,
                 Bottom,
                 (),
                 None,

+ 8 - 154
packages/core/src/component.rs

@@ -7,21 +7,12 @@
 
 use crate::innerlude::FC;
 
-pub type ScopeIdx = generational_arena::Index;
-
 pub unsafe trait Properties: PartialEq + Sized {
     type Builder;
     const CAN_BE_MEMOIZED: bool;
     fn builder() -> Self::Builder;
 }
 
-pub struct EmptyBuilder;
-impl EmptyBuilder {
-    pub fn build(self) -> () {
-        ()
-    }
-}
-
 unsafe impl Properties for () {
     const CAN_BE_MEMOIZED: bool = true;
     type Builder = EmptyBuilder;
@@ -31,151 +22,14 @@ unsafe impl Properties for () {
     }
 }
 
-pub fn fc_to_builder<T: Properties>(_f: FC<T>) -> T::Builder {
-    T::builder()
-}
-
-mod testing {
-    use std::{any::Any, ops::Deref};
-
-    use crate::innerlude::VNode;
-
-    // trait PossibleProps {
-    //     type POut: PartialEq;
-    //     fn as_partial_eq(&self) -> Option<&Self::POut> {
-    //         None
-    //     }
-    // }
-
-    // impl<T: PartialEq> PossibleProps for T {
-    //     type POut = Self;
-    // }
-
-    // struct SomeProps2<'a> {
-    //     inner: &'a str,
-    // }
-
-    // Fallback trait for to all types to default to `false`.
-    trait NotEq {
-        const IS_EQ: bool = false;
-    }
-    impl<T> NotEq for T {}
-
-    // Concrete wrapper type where `IS_COPY` becomes `true` if `T: Copy`.
-    struct IsEq<G, T>(std::marker::PhantomData<(G, T)>);
-
-    impl<G: PartialEq, T: PartialEq<G>> IsEq<G, T> {
-        // Because this is implemented directly on `IsCopy`, it has priority over
-        // the `NotCopy` trait impl.
-        //
-        // Note: this is a *totally different* associated constant from that in
-        // `NotCopy`. This does not specialize the `NotCopy` trait impl on `IsCopy`.
-        const IS_EQ: bool = true;
-    }
-
-    #[derive(PartialEq)]
-    struct SomeProps {
-        inner: &'static str,
-    }
-
-    struct SomeProps2 {
-        inner: &'static str,
-    }
-
-    #[test]
-    fn test() {
-        let g = IsEq::<SomeProps, SomeProps>::IS_EQ;
-
-        // let g = IsEq::<Vec<u32>>::IS_COPY;
-        // let g = IsEq::<u32>::IS_COPY;
-        // dbg!(g);
-
-        // let props = SomeProps { inner: "asd" };
-
-        // let intermediate: Box<dyn PartialEq<SomeProps>> = Box::new(props);
-        // let as_any: Box<dyn Any> = Box::new(intermediate);
-
-        // let as_partialeq = as_any
-        //     .downcast_ref::<Box<dyn PartialEq<SomeProps>>>()
-        //     .unwrap();
-    }
-
-    // struct blah {}
-    // #[reorder_args]
-    pub fn blah(a: i32, b: &str, c: &str) {}
-
-    // pub mod blah {
-    //     pub const a: u8 = 0;
-    //     pub const b: u8 = 1;
-    // }
-
-    trait Eat {}
-    impl Eat for fn() {}
-    impl<T> Eat for fn(T) {}
-    impl<T, K> Eat for fn(T, K) {}
-
-    mod other {
-        use super::blah;
-        fn test2() {
-            // rsx!{
-            //     div {
-            //         Ele {
-            //             a: 10,
-            //             b: "asd"
-            //             c: impl Fn() -> ()
-            //         }
-            //     }
-            // }
-
-            // becomes
-
-            // const reorder: fn(_, _) = |a, b| {};
-            // blah::META;
-            // let a = 10;
-            // let b = "asd";
-            // let g = [10, 10.0];
-            // let c = g.a;
-
-            // blah(10, "asd");
-        }
-    }
-
-    struct Inner<'a> {
-        a: String,
-        b: i32,
-        c: &'a str,
-    }
-
-    struct Custom<'a, P: 'a> {
-        inner: &'a P,
-        // inner: *const (),
-        _p: std::marker::PhantomData<&'a P>,
-    }
-
-    impl<'a, P> Custom<'a, P> {
-        fn props(&self) -> &P {
-            todo!()
-        }
-    }
-
-    // impl<P> Deref for Custom<P> {
-    //     type Target = Inner;
-
-    //     fn deref(&self) -> &Self::Target {
-    //         unsafe { &*self.inner }
-    //     }
-    // }
-
-    fn test2<'a>(a: Custom<'a, Inner<'a>>) -> VNode {
-        let r = a.inner;
-
-        todo!()
-        // let g = a.props();
-        // todo!()
-        // let g = &a.a;
+pub struct EmptyBuilder;
+impl EmptyBuilder {
+    #[inline]
+    pub fn build(self) -> () {
+        ()
     }
-
-    fn is_comp() {}
 }
 
-mod style {}
+pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
+    T::builder()
+}

+ 28 - 20
packages/core/src/diff.rs

@@ -201,11 +201,13 @@ impl<'a> DiffMachine<'a> {
                 todo!("Suspended components not currently available")
             }
 
-            // (VNode::Fragment(_), VNode::Fragment(_)) => {
-            //     todo!("Fragments not currently supported in diffing")
-            // }
             // Fragments are special
-            (VNode::Fragment(_), _) | (_, VNode::Fragment(_)) => {
+            // we actually have to remove a bunch of nodes
+            (VNode::Fragment(_), _) => {
+                todo!("Fragments not currently supported in diffing")
+            }
+
+            (_, VNode::Fragment(_)) => {
                 todo!("Fragments not currently supported in diffing")
             }
         }
@@ -227,7 +229,7 @@ impl<'a> DiffMachine<'a> {
                 self.change_list.create_text_node(text);
             }
             VNode::Element(&VElement {
-                key: _,
+                key,
                 tag_name,
                 listeners,
                 attributes,
@@ -266,7 +268,12 @@ impl<'a> DiffMachine<'a> {
 
                 for child in children {
                     self.create(child);
-                    self.change_list.append_child();
+                    if let VNode::Fragment(_) = child {
+                        // do nothing
+                        // fragments append themselves
+                    } else {
+                        self.change_list.append_child();
+                    }
                 }
             }
 
@@ -284,22 +291,23 @@ impl<'a> DiffMachine<'a> {
                 // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
                 // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
 
+                let parent_idx = self.cur_idx;
+
                 // Insert a new scope into our component list
                 let idx = self
                     .components
                     .with(|components| {
                         components.insert_with(|new_idx| {
-                            let cur_idx = self.cur_idx;
-                            let cur_scope = self.components.try_get(cur_idx).unwrap();
-                            let height = cur_scope.height + 1;
+                            let parent_scope = self.components.try_get(parent_idx).unwrap();
+                            let height = parent_scope.height + 1;
                             Scope::new(
                                 caller,
                                 new_idx,
-                                Some(cur_idx),
+                                Some(parent_idx),
                                 height,
                                 self.event_queue.new_channel(height, new_idx),
                                 self.components.clone(),
-                                &[],
+                                component.children,
                             )
                         })
                     })
@@ -323,24 +331,24 @@ impl<'a> DiffMachine<'a> {
                 // Run the scope for one iteration to initialize it
                 new_component.run_scope().unwrap();
 
-                // // Navigate the diff machine to the right point in the output dom
-                // self.change_list.load_known_root(id);
-                // let root_id = component.stable_addr
-
                 // And then run the diff algorithm
                 self.diff_node(new_component.old_frame(), new_component.next_frame());
 
                 // Finally, insert this node as a seen node.
                 self.seen_nodes.insert(idx);
             }
-            VNode::Suspended => {
-                todo!("Creation of VNode::Suspended not yet supported")
-            }
 
             // we go the the "known root" but only operate on a sibling basis
             VNode::Fragment(frag) => {
-                //
-                todo!("Cannot current create fragments")
+                // create the children directly in the space
+                for child in frag.children {
+                    self.create(child);
+                    self.change_list.append_child();
+                }
+            }
+
+            VNode::Suspended => {
+                todo!("Creation of VNode::Suspended not yet supported")
             }
         }
     }

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

@@ -87,8 +87,6 @@ pub mod builder {
     pub use super::nodebuilder::*;
 }
 
-pub mod support;
-
 // types used internally that are important
 pub(crate) mod innerlude {
     pub use crate::component::*;
@@ -122,14 +120,12 @@ pub mod prelude {
 
     pub use crate::nodebuilder::LazyNodes;
 
+    pub use crate::nodebuilder::ChildrenList;
     pub use crate::virtual_dom::NodeCtx;
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
     pub use crate::innerlude::FC;
 
-    // TODO @Jon, fix this
-    // hack the VNode type until VirtualNode is fixed in the macro crate
-
     // expose our bumpalo type
     pub use bumpalo;
     pub use bumpalo::Bump;
@@ -141,19 +137,9 @@ pub mod prelude {
 
     pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
 
-    pub use crate::component::ScopeIdx;
     pub use crate::diff::DiffMachine;
+    pub use crate::virtual_dom::ScopeIdx;
 
     pub use crate::debug_renderer::DebugRenderer;
-    pub use crate::dioxus_main;
     pub use crate::hooks::*;
 }
-
-#[macro_export]
-macro_rules! dioxus_main {
-    ($i:ident) => {
-        fn main() {
-            todo!("this macro is a placeholder for launching a dioxus app on different platforms. \nYou probably don't want to use this, but it's okay for small apps.")
-        }
-    };
-}

+ 52 - 3
packages/core/src/nodebuilder.rs

@@ -1,12 +1,12 @@
 //! Helpers for building virtual DOM VNodes.
 
-use std::{any::Any, borrow::BorrowMut, intrinsics::transmute, u128};
+use std::{any::Any, borrow::BorrowMut, fmt::Arguments, intrinsics::transmute, u128};
 
 use crate::{
     events::VirtualEvent,
     innerlude::{Properties, VComponent, FC},
     nodes::{Attribute, Listener, NodeKey, VNode},
-    prelude::VElement,
+    prelude::{VElement, VFragment},
     virtual_dom::NodeCtx,
 };
 
@@ -269,6 +269,20 @@ where
         self
     }
 
+    pub fn key2(mut self, args: Arguments) -> Self {
+        let key = match args.as_str() {
+            Some(static_str) => static_str,
+            None => {
+                use bumpalo::core_alloc::fmt::Write;
+                let mut s = bumpalo::collections::String::new_in(self.ctx.bump());
+                s.write_fmt(args).unwrap();
+                s.into_bump_str()
+            }
+        };
+        self.key = NodeKey(Some(key));
+        self
+    }
+
     /// Create the virtual DOM VNode described by this builder.
     ///
     /// # Example
@@ -655,14 +669,49 @@ pub fn virtual_child<'a, T: Properties + 'a>(
     f: FC<T>,
     props: T,
     key: Option<&'a str>, // key: NodeKey<'a>,
+    children: &'a [VNode<'a>],
 ) -> VNode<'a> {
     // currently concerned about if props have a custom drop implementation
     // might override it with the props macro
     // todo!()
     VNode::Component(
         ctx.bump()
-            .alloc(crate::nodes::VComponent::new(ctx.bump(), f, props, key)),
+            .alloc(crate::nodes::VComponent::new(ctx, f, props, key, children)),
         // ctx.bump()
         //     .alloc(crate::nodes::VComponent::new(f, props, key)),
     )
 }
+
+pub fn vfragment<'a>(
+    ctx: &NodeCtx<'a>,
+    key: Option<&'a str>, // key: NodeKey<'a>,
+    children: &'a [VNode<'a>],
+) -> VNode<'a> {
+    VNode::Fragment(ctx.bump().alloc(VFragment::new(key, children)))
+}
+
+pub struct ChildrenList<'a, 'b> {
+    ctx: &'b NodeCtx<'a>,
+    children: bumpalo::collections::Vec<'a, VNode<'a>>,
+}
+
+impl<'a, 'b> ChildrenList<'a, 'b> {
+    pub fn new(ctx: &'b NodeCtx<'a>) -> Self {
+        Self {
+            ctx,
+            children: bumpalo::collections::Vec::new_in(ctx.bump()),
+        }
+    }
+
+    pub fn add_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
+        for item in nodes {
+            let child = item.into_vnode(&self.ctx);
+            self.children.push(child);
+        }
+        self
+    }
+
+    pub fn finish(self) -> &'a [VNode<'a>] {
+        self.children.into_bump_slice()
+    }
+}

+ 18 - 4
packages/core/src/nodes.rs

@@ -7,6 +7,8 @@ use crate::{
     events::VirtualEvent,
     innerlude::{Context, Properties, Scope, ScopeIdx, FC},
     nodebuilder::text3,
+    virtual_dom::NodeCtx,
+    // support::NodeCtx,
 };
 use bumpalo::Bump;
 use std::{
@@ -238,7 +240,6 @@ pub struct VComponent<'src> {
 
     // a pointer to the raw fn typ
     pub user_fc: *const (),
-    _p: PhantomData<&'src ()>,
 }
 
 impl<'a> VComponent<'a> {
@@ -249,14 +250,17 @@ impl<'a> VComponent<'a> {
     // we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
 
     pub fn new<P: Properties + 'a>(
-        bump: &'a Bump,
+        // bump: &'a Bump,
+        ctx: &NodeCtx<'a>,
         component: FC<P>,
         // props: bumpalo::boxed::Box<'a, P>,
         props: P,
         key: Option<&'a str>,
+        children: &'a [VNode<'a>],
     ) -> Self {
         // pub fn new<P: Properties + 'a>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
         // let bad_props = unsafe { transmogrify(props) };
+        let bump = ctx.bump();
         let caller_ref = component as *const ();
         let props = bump.alloc(props);
 
@@ -319,8 +323,7 @@ impl<'a> VComponent<'a> {
             user_fc: caller_ref,
             comparator,
             raw_props,
-            _p: PhantomData,
-            children: &[],
+            children,
             caller,
             stable_addr: RefCell::new(None),
         }
@@ -355,3 +358,14 @@ pub struct VFragment<'src> {
     pub key: NodeKey<'src>,
     pub children: &'src [VNode<'src>],
 }
+
+impl<'a> VFragment<'a> {
+    pub fn new(key: Option<&'a str>, children: &'a [VNode<'a>]) -> Self {
+        let key = match key {
+            Some(key) => NodeKey::new(key),
+            None => NodeKey(None),
+        };
+
+        Self { key, children }
+    }
+}

+ 0 - 165
packages/core/src/support.rs

@@ -1,165 +0,0 @@
-use crate::{arena::ScopeArena, innerlude::*};
-use bumpalo::Bump;
-use generational_arena::Arena;
-use std::{
-    any::{Any, TypeId},
-    cell::RefCell,
-    collections::{HashMap, HashSet, VecDeque},
-    fmt::Debug,
-    future::Future,
-    ops::Deref,
-    pin::Pin,
-    rc::{Rc, Weak},
-};
-// We actually allocate the properties for components in their parent's properties
-// We then expose a handle to use those props for render in the form of "OpaqueComponent"
-pub(crate) type OpaqueComponent<'e> = dyn Fn(&'e Scope) -> VNode<'e> + 'e;
-// pub(crate) type OpaqueComponent<'e> = dyn for<'b> Fn(&'b Scope) -> VNode<'b> + 'e;
-
-#[derive(PartialEq, Debug, Clone, Default)]
-pub(crate) struct EventQueue(pub Rc<RefCell<Vec<HeightMarker>>>);
-
-impl EventQueue {
-    pub fn new_channel(&self, height: u32, idx: ScopeIdx) -> Rc<dyn Fn()> {
-        let inner = self.clone();
-        let marker = HeightMarker { height, idx };
-        Rc::new(move || inner.0.as_ref().borrow_mut().push(marker))
-    }
-}
-
-/// A helper type that lets scopes be ordered by their height
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub(crate) struct HeightMarker {
-    pub idx: ScopeIdx,
-    pub height: u32,
-}
-
-impl Ord for HeightMarker {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.height.cmp(&other.height)
-    }
-}
-
-impl PartialOrd for HeightMarker {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-// NodeCtx is used to build VNodes in the component's memory space.
-// This struct adds metadata to the final VNode about listeners, attributes, and children
-#[derive(Clone)]
-pub struct NodeCtx<'a> {
-    pub scope_ref: &'a Scope,
-    pub listener_id: RefCell<usize>,
-}
-
-impl<'a> NodeCtx<'a> {
-    pub fn bump(&self) -> &'a Bump {
-        &self.scope_ref.cur_frame().bump
-    }
-}
-
-impl Debug for NodeCtx<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        Ok(())
-    }
-}
-
-#[derive(Debug, PartialEq, Hash)]
-pub struct ContextId {
-    // Which component is the scope in
-    original: ScopeIdx,
-
-    // What's the height of the scope
-    height: u32,
-
-    // Which scope is it (in order)
-    id: u32,
-}
-
-pub struct ActiveFrame {
-    // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken
-    pub generation: RefCell<usize>,
-
-    // The double-buffering situation that we will use
-    pub frames: [BumpFrame; 2],
-}
-
-pub struct BumpFrame {
-    pub bump: Bump,
-    pub head_node: VNode<'static>,
-}
-
-impl ActiveFrame {
-    pub fn new() -> Self {
-        Self::from_frames(
-            BumpFrame {
-                bump: Bump::new(),
-                head_node: VNode::text(""),
-            },
-            BumpFrame {
-                bump: Bump::new(),
-                head_node: VNode::text(""),
-            },
-        )
-    }
-
-    pub(crate) fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
-        Self {
-            generation: 0.into(),
-            frames: [a, b],
-        }
-    }
-
-    pub(crate) fn cur_frame(&self) -> &BumpFrame {
-        match *self.generation.borrow() & 1 == 0 {
-            true => &self.frames[0],
-            false => &self.frames[1],
-        }
-    }
-    pub(crate) fn cur_frame_mut(&mut self) -> &mut BumpFrame {
-        match *self.generation.borrow() & 1 == 0 {
-            true => &mut self.frames[0],
-            false => &mut self.frames[1],
-        }
-    }
-
-    pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let raw_node = match *self.generation.borrow() & 1 == 0 {
-            true => &self.frames[0],
-            false => &self.frames[1],
-        };
-
-        // Give out our self-referential item with our own borrowed lifetime
-        unsafe {
-            let unsafe_head = &raw_node.head_node;
-            let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
-            safe_node
-        }
-    }
-
-    pub fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let raw_node = match *self.generation.borrow() & 1 != 0 {
-            true => &self.frames[0],
-            false => &self.frames[1],
-        };
-
-        // Give out our self-referential item with our own borrowed lifetime
-        unsafe {
-            let unsafe_head = &raw_node.head_node;
-            let safe_node = std::mem::transmute::<&VNode<'static>, &VNode<'b>>(unsafe_head);
-            safe_node
-        }
-    }
-
-    pub(crate) fn next(&mut self) -> &mut BumpFrame {
-        *self.generation.borrow_mut() += 1;
-
-        if *self.generation.borrow() % 2 == 0 {
-            &mut self.frames[0]
-        } else {
-            &mut self.frames[1]
-        }
-    }
-}

+ 4 - 1
packages/core/src/virtual_dom.rs

@@ -32,6 +32,7 @@ use std::{
     pin::Pin,
     rc::{Rc, Weak},
 };
+pub type ScopeIdx = generational_arena::Index;
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 /// Differences are converted into patches which a renderer can use to draw the UI.
@@ -420,8 +421,10 @@ impl Scope {
             >(caller)
         };
 
+        let child_nodes = unsafe { std::mem::transmute(child_nodes) };
+
         Self {
-            child_nodes: &[],
+            child_nodes: child_nodes,
             caller,
             parent,
             arena_idx,

+ 1 - 1
packages/hooks/src/lib.rs

@@ -84,7 +84,7 @@ fn use_map() {}
 // Elements are received as Rc<T> in case the underlying collection is shuffled around
 // Setters/getters can be generated
 fn use_collection<'a, T: Collection>(
-    ctx: &Context<'a>,
+    ctx: &impl Scoped<'a>,
     f: impl Fn() -> T,
 ) -> CollectionHandle<'a, T> {
     ctx.use_hook(

+ 1 - 1
packages/recoil/examples/callback.rs

@@ -20,7 +20,7 @@ fn update_title(api: &RecoilApi) {
 }
 
 static App: FC<()> = |ctx| {
-    let title = use_read(ctx, &TITLE);
+    let title = use_read(&ctx, &TITLE);
     let next_light = use_recoil_api(ctx, |api| move |_| update_title(&api));
 
     rsx! { in ctx,

+ 36 - 0
packages/web/examples/children.rs

@@ -0,0 +1,36 @@
+//! Basic example that renders a simple VNode to the browser.
+
+use dioxus_core::prelude::*;
+use dioxus_web::*;
+
+fn main() {
+    // Setup logging
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    console_error_panic_hook::set_once();
+
+    // Run the app
+    wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
+}
+
+static App: FC<()> = |ctx| {
+    ctx.render(rsx! {
+        Calcier {
+            h2 {"abc 1"}
+            h2 {"abc 2"}
+            h2 {"abc 3"}
+            h2 {"abc 4"}
+            h2 {"abc 5"}
+        }
+    })
+};
+
+static Calcier: FC<()> = |ctx| {
+    ctx.render(rsx! {
+        div {
+            h1 {
+                "abc 0"
+            }
+            {ctx.children()}
+        }
+    })
+};

+ 1 - 1
packages/web/examples/context.rs

@@ -52,7 +52,7 @@ struct ButtonProps {
     id: u8,
 }
 
-fn CustomButton(ctx: Context, props: &ButtonProps) -> VNode {
+fn CustomButton(ctx: Context<ButtonProps>) -> VNode {
     let names = ctx.use_context::<CustomContext>();
     let name = names.0[ctx.id as usize];
 

+ 27 - 0
packages/web/examples/fragment.rs

@@ -0,0 +1,27 @@
+//! Basic example that renders a simple VNode to the browser.
+
+use dioxus_core::prelude::*;
+use dioxus_web::*;
+
+fn main() {
+    // Setup logging
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    console_error_panic_hook::set_once();
+
+    // Run the app
+    wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
+}
+
+static App: FC<()> = |ctx| {
+    ctx.render(rsx! {
+        h2 { "abc 1" }
+        h2 { "abc 1" }
+        h2 { "abc 1" }
+        h2 { "abc 1" }
+        h2 { "abc 1" }
+        h2 { "abc 1" }
+        div {
+            "hello world!"
+        }
+    })
+};

+ 21 - 0
packages/web/examples/many.rs

@@ -0,0 +1,21 @@
+use dioxus_core::prelude::*;
+use dioxus_web::WebsysRenderer;
+
+fn main() {
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    console_error_panic_hook::set_once();
+
+    log::info!("hello world");
+    wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
+}
+
+static Example: FC<()> = |ctx| {
+    ctx.render(rsx! {
+        div {
+            span {
+                class: "px-2 py-1 flex w-36 mt-4 items-center text-xs rounded-md font-semibold text-yellow-500 bg-yellow-100"
+                "DUE DATE : 18 JUN"
+            }
+        }
+    })
+};

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

@@ -53,7 +53,7 @@ struct ButtonProps<'src, F: Fn(MouseEvent)> {
     handler: F
 }
 
-fn CustomButton<'b, 'a, F: Fn(MouseEvent)>(ctx: Context<'a>, props: &'b ButtonProps<'b, F>) -> VNode {
+fn CustomButton<'a, F: Fn(MouseEvent)>(ctx: Context<'a, ButtonProps<'a, F>>) -> VNode {
     ctx.render(rsx!{
         button {  
             class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
@@ -74,7 +74,7 @@ impl<F: Fn(MouseEvent)> PartialEq for ButtonProps<'_, F> {
 struct PlaceholderProps {
     val: &'static str
 }
-fn Placeholder(ctx: Context, props: &PlaceholderProps) -> VNode {
+fn Placeholder(ctx: Context<PlaceholderProps>) -> VNode {
     ctx.render(rsx!{
         div {
             "child: {ctx.val}"