Parcourir la source

Feat: some docs

Jonathan Kelley il y a 4 ans
Parent
commit
1919f88

+ 2 - 0
.vscode/spellright.dict

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

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

@@ -9,8 +9,8 @@ struct MyProps {
 }
 
 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 {
     let ExampleProps {
         name, pending, count
-    } = ctx.props();
+    } = ctx.props;
 
     rsx! {
         <div>

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

@@ -4,10 +4,12 @@ Dioxus comes preloaded with two macros for creating VNodes.
 
 ## 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.
 
+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
 #[fc]
 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
 
-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 🙂.
 
@@ -51,30 +53,55 @@ Each element takes a comma-separated list of expressions to build the node. Roug
 - `CustomTag {}` adds a new child component
 - `{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
 static Example: FC<()> = |ctx| {
 
+    let text = "example";
+
     ctx.render(rsx!{
         div {
             h1 { "Example" },
+
+            // fstring interpolation
+            "{text}"
+
             p {
-                // Props
+                // Attributes
                 tag: "type",
+
+                // Anything that implements display can be an attribute
                 abc: 123,
                 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
                 a { "abcder" },
 
-                // Children with props
-                h2 { "whatsup", class: "abc-123" },
+                // Children with attributes
+                h2 { "hello", class: "abc-123" },
 
                 // Child components
                 CustomComponent { a: 123, b: 456, key: "1" },
 
+                // Child components with paths
+                crate::components::CustomComponent { a: 123, b: 456, key: "1" },
+
                 // Iterators
                 { 0..3.map(|i| rsx!{ h1 {"{:i}"} }) },
 
@@ -82,7 +109,34 @@ static Example: FC<()> = |ctx| {
                 { rsx! { 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}
             }
         }

+ 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,
         div {
+            h1 {"these are children nodes: "}
             {nodes}
         }
     }

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

@@ -14,7 +14,11 @@ static Header: FC<()> = |ctx| {
 
     ctx.render(dioxus::prelude::LazyNodes::new(|nodectx| {
         builder::ElementBuilder::new(nodectx, "div")
-            .child(VNode::Component(VComponent::new(Bottom, (), None)))
+            .child(VNode::Component(nodectx.bump().alloc(VComponent::new(
+                Bottom,
+                (),
+                None,
+            ))))
             .finish()
     }))
 };
@@ -36,128 +40,3 @@ fn Top(ctx: Context<()>) -> VNode {
         </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> {
     type Item = VNode<'a>;
     type IntoIter = std::iter::Once<Self::Item>;
@@ -665,8 +657,9 @@ pub fn virtual_child<'a, T: Properties>(
 ) -> VNode<'a> {
     // currently concerned about if props have a custom drop implementation
     // 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>],
 
     // a pointer into the bump arena (given by the 'src lifetime)
-    raw_props: Box<dyn Any>,
+    // raw_props: Box<dyn Any>,
     // raw_props: *const (),
 
     // a pointer to the raw fn typ
@@ -243,13 +243,24 @@ pub struct VComponent<'src> {
     _p: PhantomData<&'src ()>,
 }
 
+unsafe fn transmogrify<'a>(p: impl Properties + 'a) -> impl Properties + 'static {
+    todo!()
+}
 impl<'a> VComponent<'a> {
     // 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:
     // - perform comparisons when diffing (memoization)
     // TODO: lift the requirement that props need to be static
     // 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 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 key = match key {
@@ -283,7 +306,7 @@ impl<'a> VComponent<'a> {
             key,
             ass_scope: Rc::new(RefCell::new(None)),
             user_fc: caller_ref,
-            raw_props: Box::new(props),
+            // raw_props: Box::new(props),
             _p: PhantomData,
             children: &[],
             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
         // 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
@@ -618,18 +617,23 @@ impl Scope {
             .ok_or(Error::FatalInternal("Failed to get caller"))?;
 
         // 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(())
     }
 
+    // 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
     // calling listeners will invalidate the list of listeners
     // The listener list will be completely drained because the next frame will write over previous listeners