Browse Source

Feat: some docs

Jonathan Kelley 4 years ago
parent
commit
1919f88

+ 2 - 0
.vscode/spellright.dict

@@ -43,3 +43,5 @@ virtualdom
 namespaced
 namespaced
 namespacing
 namespacing
 impl
 impl
+destructured
+linting

+ 2 - 2
docs/posts/01-hello-world.md

@@ -9,8 +9,8 @@ struct MyProps {
 }
 }
 
 
 fn Example(ctx: Context<MyProps>) -> VNode {
 fn Example(ctx: Context<MyProps>) -> VNode {
-    ctx.render(html! { 
-        <div> "Hello {ctx.props().name}!" </div> 
+    ctx.render(html! {
+        <div> "Hello {ctx.name}!" </div>
     })
     })
 }
 }
 ```
 ```

+ 1 - 1
docs/posts/02-utilites.md

@@ -35,7 +35,7 @@ struct ExampleProps {
 fn Example(ctx: &mut Context<ExampleProps>) -> VNode {
 fn Example(ctx: &mut Context<ExampleProps>) -> VNode {
     let ExampleProps {
     let ExampleProps {
         name, pending, count
         name, pending, count
-    } = ctx.props();
+    } = ctx.props;
 
 
     rsx! {
     rsx! {
         <div>
         <div>

+ 63 - 9
docs/posts/03-vnode-macros.md

@@ -4,10 +4,12 @@ Dioxus comes preloaded with two macros for creating VNodes.
 
 
 ## html! macro
 ## html! macro
 
 
-The html! macro supports the html standard. This macro will happily accept a copy-paste from something like tailwind builder. Writing this one by hand is a bit tedious and doesn't come with much help from Rust IDE tools.
+The html! macro supports a limited subset of the html standard. This macro will happily accept a copy-paste from something like tailwind builder. However, writing HTML by hand is a bit tedious - IDE tools for Rust don't support linting/autocomplete/syntax highlighting. RSX is much more natural for Rust programs and _does_ integrate well with Rust IDE tools.
 
 
 There is also limited support for dynamic handlers, but it will function similarly to JSX.
 There is also limited support for dynamic handlers, but it will function similarly to JSX.
 
 
+You'll want to write RSX where you can, and in a future release we'll have a tool that automatically converts HTML to RSX.
+
 ```rust
 ```rust
 #[fc]
 #[fc]
 fn Example(ctx: Context, name: &str, pending: bool, count: i32 ) -> VNode {
 fn Example(ctx: Context, name: &str, pending: bool, count: i32 ) -> VNode {
@@ -23,9 +25,9 @@ fn Example(ctx: Context, name: &str, pending: bool, count: i32 ) -> VNode {
 
 
 ## rsx! macro
 ## rsx! macro
 
 
-The rsx! macro is a VNode builder macro designed especially for Rust. Writing these should feel very natural, much like assembling a struct. VSCode also supports these with code folding, bracket-tabbing, bracket highlighting, and section selecting.
+The rsx! macro is a VNode builder macro designed especially for Rust programs. Writing these should feel very natural, much like assembling a struct. VSCode also supports these with code folding, bracket-tabbing, bracket highlighting, and section selecting.
 
 
-The Dioxus VSCode extension provides a function to convert a selection of html! template and turn it into rsx!, so you'll never need to transcribe templates by hand.
+The Dioxus VSCode extension will eventually provide a macro to convert a selection of html! template and turn it into rsx!, so you'll never need to transcribe templates by hand.
 
 
 It's also a bit easier on the eyes 🙂.
 It's also a bit easier on the eyes 🙂.
 
 
@@ -51,30 +53,55 @@ Each element takes a comma-separated list of expressions to build the node. Roug
 - `CustomTag {}` adds a new child component
 - `CustomTag {}` adds a new child component
 - `{expr}` pastes the `expr` tokens literally. They must be IntoCtx<Vnode> to work properly
 - `{expr}` pastes the `expr` tokens literally. They must be IntoCtx<Vnode> to work properly
 
 
-Lists must include commas, much like how struct definitions work.
+Commas are entirely optional, but might be useful to delineate between elements and attributes.
+
+The `render` function provides an **extremely efficient** allocator for VNodes and text, so try not to use the `format!` macro in your components. Rust's default `ToString` methods pass through the global allocator, but all text in components is allocated inside a manually-managed Bump arena.
 
 
 ```rust
 ```rust
 static Example: FC<()> = |ctx| {
 static Example: FC<()> = |ctx| {
 
 
+    let text = "example";
+
     ctx.render(rsx!{
     ctx.render(rsx!{
         div {
         div {
             h1 { "Example" },
             h1 { "Example" },
+
+            // fstring interpolation
+            "{text}"
+
             p {
             p {
-                // Props
+                // Attributes
                 tag: "type",
                 tag: "type",
+
+                // Anything that implements display can be an attribute
                 abc: 123,
                 abc: 123,
                 enabled: true,
                 enabled: true,
-                class: "big small wide short",
+
+                // attributes also supports interpolation
+                // `class` is not a restricted keyword unlike JS and ClassName
+                class: "big small wide short {text}",
+
+                // bool-based classnames
+                classes: [("big", true), ("small", false)]
+
+                // Bool-based props
+                // *must* be in the tuple form, cannot enter as a variable
+                tag: ("type", false)
+
+                tag: {"these tokens are placed directly"}
 
 
                 // Children
                 // Children
                 a { "abcder" },
                 a { "abcder" },
 
 
-                // Children with props
-                h2 { "whatsup", class: "abc-123" },
+                // Children with attributes
+                h2 { "hello", class: "abc-123" },
 
 
                 // Child components
                 // Child components
                 CustomComponent { a: 123, b: 456, key: "1" },
                 CustomComponent { a: 123, b: 456, key: "1" },
 
 
+                // Child components with paths
+                crate::components::CustomComponent { a: 123, b: 456, key: "1" },
+
                 // Iterators
                 // Iterators
                 { 0..3.map(|i| rsx!{ h1 {"{:i}"} }) },
                 { 0..3.map(|i| rsx!{ h1 {"{:i}"} }) },
 
 
@@ -82,7 +109,34 @@ static Example: FC<()> = |ctx| {
                 { rsx! { div { } } },
                 { rsx! { div { } } },
                 { html! { <div> </div> } },
                 { html! { <div> </div> } },
 
 
-                // Any expression that is Into<VNode>
+                // Matching
+                // Requires rendering the nodes first.
+                // rsx! is lazy, and the underlying closures cannot have the same type
+                // Rendering produces the VNode type
+                {match rand::gen_range::<i32>(1..3) {
+                    1 => rsx!(in ctx, h1 { "big" })
+                    2 => rsx!(in ctx, h2 { "medium" })
+                    _ => rsx!(in ctx, h3 { "small" })
+                }}
+
+                // Optionals
+                {true.and_then(|f| rsx!{ h1 {"Conditional Rendering"} })}
+
+                // Bool options
+                {(rsx!{ h1 {"Conditional Rendering"}, true)}
+
+                // Child nodes
+                // Returns &[VNode]
+                {ctx.children()}
+
+                // Duplicating nodes
+                // Clones the nodes by reference, so they are literally identical
+                {{
+                    let node = rsx!(in ctx, h1{ "TopNode" });
+                    (0..10).map(|_| node.clone())
+                }}
+
+                // Any expression that is `IntoVNode`
                 {expr}
                 {expr}
             }
             }
         }
         }

+ 56 - 0
docs/website/homepage/ex1.md

@@ -0,0 +1,56 @@
+# A Simple Component
+
+```rust
+#[derive(PartialEq, Properties)]
+struct Props { name: &'static str }
+
+static HelloMessage: FC<Props> = |ctx| {
+    ctx.render(rsx!{
+        div { "Hello {ctx.props.name}" }
+    })
+}
+```
+
+# Any syntax you like
+
+Choose from a close-to-html syntax or the standard rsx! syntax
+
+```rust
+static HelloMessage: FC<()> = |ctx| {
+    ctx.render(html!{
+        <div> Hello World! </div>
+    })
+}
+```
+
+# A Stateful Component
+
+Store state with hooks!
+
+```rust
+enum LightState {
+    Green
+    Yellow,
+    Red,
+}
+static HelloMessage: FC<()> = |ctx| {
+    let (color, set_color) = use_state(ctx, || LightState::Green);
+
+    let title = match color {
+        Green => "Green means go",
+        Yellow => "Yellow means slow down",
+        Red => "Red means stop",
+    };
+
+    ctx.render(rsx!{
+        h1 {"{title}"}
+        button { "tick"
+            onclick: move |_| set_color(match color {
+                Green => Yellow,
+                Yellow => Red,
+                Red => Green,
+            })
+        }
+    })
+}
+```

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

@@ -22,6 +22,7 @@ static Example: FC<()> = |ctx| {
     //
     //
     rsx! { in ctx,
     rsx! { in ctx,
         div {
         div {
+            h1 {"these are children nodes: "}
             {nodes}
             {nodes}
         }
         }
     }
     }

+ 5 - 126
packages/core/examples/nested.rs

@@ -14,7 +14,11 @@ static Header: FC<()> = |ctx| {
 
 
     ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
     ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
         builder::ElementBuilder::new(nodectx, "div")
         builder::ElementBuilder::new(nodectx, "div")
-            .child(VNode::Component(VComponent::new(Bottom, (), None)))
+            .child(VNode::Component(nodectx.bump().alloc(VComponent::new(
+                Bottom,
+                (),
+                None,
+            ))))
             .finish()
             .finish()
     }))
     }))
 };
 };
@@ -36,128 +40,3 @@ fn Top(ctx: Context<()>) -> VNode {
         </div>
         </div>
     })
     })
 }
 }
-
-struct Callback<T>(Box<T>);
-
-// impl<O, T: Fn() -> O> From<T> for Callback<T> {
-//     fn from(_: T) -> Self {
-//         todo!()
-//     }
-// }
-
-impl<O, A> From<&dyn Fn(A) -> O> for Callback<&dyn Fn(A) -> O> {
-    fn from(_: &dyn Fn(A) -> O) -> Self {
-        todo!()
-    }
-}
-
-impl<O, A, B> From<&dyn Fn(A, B) -> O> for Callback<&dyn Fn(A, B) -> O> {
-    fn from(_: &dyn Fn(A, B) -> O) -> Self {
-        todo!()
-    }
-}
-
-// compile time reordering of arguments
-// Allows for transparently calling
-#[derive(Default)]
-pub struct Args<A, B, C> {
-    pub a: CuOpt<A>,
-    pub b: CuOpt<B>,
-    pub c: CuOpt<C>,
-}
-
-pub enum CuOpt<T> {
-    Some(T),
-    None,
-}
-impl<T> Default for CuOpt<T> {
-    fn default() -> Self {
-        CuOpt::None
-    }
-}
-
-impl<T> CuOpt<T> {
-    fn unwrap(self) -> T {
-        match self {
-            CuOpt::Some(t) => t,
-            CuOpt::None => panic!(""),
-        }
-    }
-}
-
-trait IsMemo {
-    fn memo(&self, other: &Self) -> bool {
-        false
-    }
-}
-
-impl<T: PartialEq> IsMemo for CuOpt<T> {
-    fn memo(&self, other: &Self) -> bool {
-        self == other
-    }
-}
-
-impl<T: PartialEq> PartialEq for CuOpt<T> {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (CuOpt::Some(a), CuOpt::Some(b)) => a == b,
-            (CuOpt::Some(_), CuOpt::None) => false,
-            (CuOpt::None, CuOpt::Some(_)) => false,
-            (CuOpt::None, CuOpt::None) => true,
-        }
-    }
-}
-
-impl<T> IsMemo for &CuOpt<T> {
-    fn memo(&self, other: &Self) -> bool {
-        false
-    }
-}
-
-// #[test]
-#[test]
-fn try_test() {
-    // test_poc()
-}
-
-// fn test_poc(ctx: Context) {
-//     let b = Bump::new();
-
-//     let h = Args {
-//         a: CuOpt::Some("ASD"),
-//         b: CuOpt::Some(123),
-//         c: CuOpt::Some(|| {}),
-//         // c: CuOpt::Some(b.alloc(|| {})),
-//         // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
-//     };
-
-//     let h2 = Args {
-//         a: CuOpt::Some("ASD"),
-//         b: CuOpt::Some(123),
-//         c: CuOpt::Some(|| {}),
-//         // c: CuOpt::Some(b.alloc(|| {})),
-//         // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
-//         // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
-//         // c: CuOpt::Some(Box::new(|| {}) as Box<dyn Fn()>),
-//     };
-
-//     // dbg!((&h.a).memo((&&h2.a)));
-//     // dbg!((&h.b).memo((&&h2.b)));
-//     // dbg!((&h.c).memo((&&h2.c)));
-//     //
-//     // ctx: Context
-//     Top(ctx, &h.a.unwrap(), &h.b.unwrap(), &h.c.unwrap());
-// }
-
-// fn test_realzies() {
-//     let h = Args {
-//         a: CuOpt::Some("ASD"),
-//         b: CuOpt::Some(123),
-//         c: CuOpt::Some(|| {}),
-//     };
-
-//     let g = |ctx: Context| {
-//         //
-//         Top(ctx, &h.a.unwrap(), &h.b.unwrap(), &h.c.unwrap())
-//     };
-// }

+ 5 - 12
packages/core/src/nodebuilder.rs

@@ -486,14 +486,6 @@ where
     }
     }
 }
 }
 
 
-// impl IntoIterator for VNode {
-//     type Item = VNode;
-//     type IntoIter = std::iter::Once<Self::Item>;
-//     fn into_iter(self) -> Self::IntoIter {
-//         std::iter::once(self)
-//     }
-// }
-
 impl<'a> IntoIterator for VNode<'a> {
 impl<'a> IntoIterator for VNode<'a> {
     type Item = VNode<'a>;
     type Item = VNode<'a>;
     type IntoIter = std::iter::Once<Self::Item>;
     type IntoIter = std::iter::Once<Self::Item>;
@@ -665,8 +657,9 @@ pub fn virtual_child<'a, T: Properties>(
 ) -> VNode<'a> {
 ) -> VNode<'a> {
     // currently concerned about if props have a custom drop implementation
     // currently concerned about if props have a custom drop implementation
     // might override it with the props macro
     // might override it with the props macro
-    VNode::Component(
-        ctx.bump()
-            .alloc(crate::nodes::VComponent::new(f, props, key)),
-    )
+    todo!()
+    // VNode::Component(
+    //     ctx.bump()
+    //         .alloc(crate::nodes::VComponent::new(f, props, key)),
+    // )
 }
 }

+ 27 - 19
packages/core/src/nodes.rs

@@ -235,7 +235,7 @@ pub struct VComponent<'src> {
     pub children: &'src [VNode<'src>],
     pub children: &'src [VNode<'src>],
 
 
     // a pointer into the bump arena (given by the 'src lifetime)
     // a pointer into the bump arena (given by the 'src lifetime)
-    raw_props: Box<dyn Any>,
+    // raw_props: Box<dyn Any>,
     // raw_props: *const (),
     // raw_props: *const (),
 
 
     // a pointer to the raw fn typ
     // a pointer to the raw fn typ
@@ -243,13 +243,24 @@ pub struct VComponent<'src> {
     _p: PhantomData<&'src ()>,
     _p: PhantomData<&'src ()>,
 }
 }
 
 
+unsafe fn transmogrify<'a>(p: impl Properties + 'a) -> impl Properties + 'static {
+    todo!()
+}
 impl<'a> VComponent<'a> {
 impl<'a> VComponent<'a> {
     // use the type parameter on props creation and move it into a portable context
     // use the type parameter on props creation and move it into a portable context
     // this lets us keep scope generic *and* downcast its props when we need to:
     // this lets us keep scope generic *and* downcast its props when we need to:
     // - perform comparisons when diffing (memoization)
     // - perform comparisons when diffing (memoization)
     // TODO: lift the requirement that props need to be static
     // TODO: lift the requirement that props need to be static
     // we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
     // we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
-    pub fn new<P: Properties>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
+
+    pub fn new<P: Properties>(
+        component: FC<P>,
+        // props: bumpalo::boxed::Box<'a, P>,
+        props: P,
+        key: Option<&'a str>,
+    ) -> Self {
+        // pub fn new<P: Properties + 'a>(component: FC<P>, props: P, key: Option<&'a str>) -> Self {
+        // let bad_props = unsafe { transmogrify(props) };
         let caller_ref = component as *const ();
         let caller_ref = component as *const ();
 
 
         // let raw_props = props as *const P as *const ();
         // let raw_props = props as *const P as *const ();
@@ -271,7 +282,19 @@ impl<'a> VComponent<'a> {
         //     }
         //     }
         // };
         // };
 
 
-        let caller: Rc<dyn Fn(&Scope) -> VNode + 'a> = Rc::new(move |scp| todo!());
+        // let prref: &'a P = props.as_ref();
+
+        let caller: Rc<dyn Fn(&Scope) -> VNode> = Rc::new(move |scope| {
+            //
+            // let props2 = bad_props;
+            // props.as_ref()
+            // let ctx = Context {
+            //     props: prref,
+            //     scope,
+            // };
+            todo!()
+            // component(ctx)
+        });
         // let caller = Rc::new(create_closure(component, raw_props));
         // let caller = Rc::new(create_closure(component, raw_props));
 
 
         let key = match key {
         let key = match key {
@@ -283,7 +306,7 @@ impl<'a> VComponent<'a> {
             key,
             key,
             ass_scope: Rc::new(RefCell::new(None)),
             ass_scope: Rc::new(RefCell::new(None)),
             user_fc: caller_ref,
             user_fc: caller_ref,
-            raw_props: Box::new(props),
+            // raw_props: Box::new(props),
             _p: PhantomData,
             _p: PhantomData,
             children: &[],
             children: &[],
             caller,
             caller,
@@ -292,18 +315,3 @@ impl<'a> VComponent<'a> {
         }
         }
     }
     }
 }
 }
-
-// fn create_closure<P: Properties>(
-//     component: FC<P>,
-//     raw_props: *const (),
-// ) -> impl for<'r> Fn(&'r Scope) -> VNode<'r> {
-//     move |ctx| -> VNode {
-//         // cast back into the right lifetime
-//         let safe_props: &P = unsafe { &*(raw_props as *const P) };
-//         component(Context {
-//             props: safe_props,
-//             scope: ctx,
-//         })
-//         // component(ctx, safe_props)
-//     }
-// }

+ 17 - 13
packages/core/src/virtual_dom.rs

@@ -137,12 +137,11 @@ impl VirtualDom {
 
 
         // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
         // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
         // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
         // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope.
-        let _root_caller: Rc<OpaqueComponent> = Rc::new(move |ctx| {
-            todo!()
-            // root(Context {
-            //     props: &root_props,
-            //     scope: ctx,
-            // })
+        let _root_caller: Rc<OpaqueComponent<'static>> = Rc::new(move |scope| {
+            // the lifetime of this closure is just as long as the lifetime on the scope reference
+            // this closure moves root props (which is static) into this closure
+            let props = unsafe { &*(&root_props as *const _) };
+            root(Context { props, scope })
         });
         });
 
 
         // Create a weak reference to the OpaqueComponent for the root scope to use as its render function
         // Create a weak reference to the OpaqueComponent for the root scope to use as its render function
@@ -618,18 +617,23 @@ impl Scope {
             .ok_or(Error::FatalInternal("Failed to get caller"))?;
             .ok_or(Error::FatalInternal("Failed to get caller"))?;
 
 
         // Cast the caller ptr from static to one with our own reference
         // Cast the caller ptr from static to one with our own reference
-        let new_head = unsafe {
-            std::mem::transmute::<&OpaqueComponent<'static>, &OpaqueComponent<'sel>>(
-                caller.as_ref(),
-            )
-        }(&self);
+        let c2: &OpaqueComponent<'static> = caller.as_ref();
+        let c3: &OpaqueComponent<'sel> = unsafe { std::mem::transmute(c2) };
+
+        let unsafe_head = unsafe { self.own_vnodes(c3) };
 
 
-        todo!();
-        // self.frames.cur_frame_mut().head_node = new_head;
+        self.frames.cur_frame_mut().head_node = unsafe_head;
 
 
         Ok(())
         Ok(())
     }
     }
 
 
+    // this is its own function so we can preciesly control how lifetimes flow
+    unsafe fn own_vnodes<'a>(&'a self, f: &OpaqueComponent<'a>) -> VNode<'static> {
+        let new_head: VNode<'a> = f(self);
+        let out: VNode<'static> = std::mem::transmute(new_head);
+        out
+    }
+
     // A safe wrapper around calling listeners
     // A safe wrapper around calling listeners
     // calling listeners will invalidate the list of listeners
     // calling listeners will invalidate the list of listeners
     // The listener list will be completely drained because the next frame will write over previous listeners
     // The listener list will be completely drained because the next frame will write over previous listeners