Răsfoiți Sursa

docs: examples

Jonathan Kelley 4 ani în urmă
părinte
comite
d9e6d09

+ 1 - 0
Cargo.toml

@@ -30,6 +30,7 @@ split-debuginfo = "unpacked"
 
 [dev-dependencies]
 futures = "0.3.15"
+log = "0.4.14"
 # For the tide ssr examples
 # async-std = { version="1.9.0", features=["attributes"] }
 # tide = { version="0.16.0" }

+ 46 - 25
examples/README.md

@@ -1,29 +1,50 @@
 # Examples
 
-Most of these examples are run through webview so you don't need the dioxus cli installed to preview the functionality. Anything labeled `_web` will need to be built with the Dioxus CLI to preview features that only a native bundle can handle.
+Most of these examples are run through webview so you don't need the dioxus cli installed to preview the functionality.
 
-List of examples:
+These examples are fully-fledged micro apps. They can be ran with the `cargo run --example XYZ`
 
-| Example                                 | What it does |
-| --------------------------------------- | ------------ |
-| [The basics](./basics.rs)               | this does    |
-| [fine grained reactivity](./signals.rs) | this does    |
-| Global State Management                 | this does    |
-| Virtual Refs                            | this does    |
-| Inline Styles                           | this does    |
-| Conditional Rendering                   | this does    |
-| Maps/Iterators                          | this does    |
-| Render To string                        | this does    |
-| Component Children                      | this does    |
-| Function Driven children                | this does    |
-| Memoization                             | this does    |
-| Borrowed Data                           | this does    |
-| Fragments                               | this does    |
-| Null/None Components                    | this does    |
-| Spread Pattern for props                | this does    |
-| Controlled Inputs                       | this does    |
-| Custom Elements                         | this does    |
-| Testing And debugging                   | this does    |
-| Asynchronous Data                       | this does    |
-| Fiber/Scheduled Rendering               | this does    |
-| CSS Compiled Styles                     | this does    |
+| Example                                             | What it does                                | Status |
+| --------------------------------------------------- | ------------------------------------------- | ------ |
+| [The basics](./basics.rs)                           | A few basic examples to preview Dioxus      | 🛠      |
+| [fine grained reactivity](./signals.rs)             | Escape `diffing` by writing values directly | 🛠      |
+| [Global State Management](./statemanagement.rs)     | Share state between components              | 🛠      |
+| [Virtual Refs]()                                    | Cross-platform imperative elements          | 🛠      |
+| [Inline Styles](./inline-styles.rs)                 | Define styles for elements inline           | 🛠      |
+| [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals       | ✅     |
+
+These examples are not necessarily meant to be run, but rather serve as a reference for the given functionality.
+
+| Example                                             | What it does                                    | Status |
+| --------------------------------------------------- | ----------------------------------------------- | ------ |
+| [The basics](./basics.rs)                           | A few basic examples to preview Dioxus          | 🛠      |
+| [fine grained reactivity](./signals.rs)             | Escape `diffing` by writing values directly     | 🛠      |
+| [Global State Management](./statemanagement.rs)     | Share state between components                  | 🛠      |
+| [Virtual Refs]()                                    | Cross-platform imperative elements              | 🛠      |
+| [Inline Styles](./inline-styles.rs)                 | Define styles for elements inline               | 🛠      |
+| [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals           | ✅     |
+| [Maps/Iterators](./iterators.rs)                    | Use iterators in the rsx! macro                 | 🛠      |
+| [Render To string](./tostring.rs)                   | Render a mounted virtualdom to a string         | 🛠      |
+| [Component Children](./children.rs)                 | Pass children into child components             | 🛠      |
+| [Function Driven children]()                        | Pass functions to make VNodes                   | 🛠      |
+| [Memoization & Borrowed Data](./memo.rs)            | Suppress renders, borrow from parents           | ✅     |
+| [Fragments](./fragments.rs)                         | Support root-less element groups                | ✅     |
+| [Null/None Components](./empty.rs)                  | Return nothing!                                 | 🛠      |
+| [Spread Pattern for props](./spreadpattern.rs)      | Manually specify and override props             | ✅     |
+| [Controlled Inputs](./controlled-inputs.rs)         | this does                                       | 🛠      |
+| [Custom Elements]()                                 | Define custom elements                          | 🛠      |
+| [Web Components]()                                  | Custom elements to interface with WebComponents | 🛠      |
+| [Testing And debugging]()                           | this does                                       | 🛠      |
+| [Asynchronous Data]()                               | Using suspense to wait for data                 | 🛠      |
+| [Fiber/Scheduled Rendering]()                       | this does                                       | 🛠      |
+| [CSS Compiled Styles]()                             | this does                                       | 🛠      |
+| [Anti-patterns](./antipatterns.rs)                  | A collection of discouraged patterns            | ✅     |
+| [Complete rsx reference](./rsx_usage.rs)            | A complete reference for all rsx! usage         | ✅     |
+| [Event Listeners](./listener.rs)                    | Attach closures to events on elements           | ✅     |
+
+These web-specific examples must be run with `dioxus-cli` using `dioxus develop --example XYZ`
+
+| Example | What it does |
+| ------- | ------------ |
+| asd     | this does    |
+| asd     | this does    |

+ 0 - 24
examples/listener.rs

@@ -1,24 +0,0 @@
-#![allow(unused, non_upper_case_globals)]
-
-use dioxus_core::prelude::*;
-
-fn main() {}
-
-static Example: FC<()> = |cx| {
-    let (name, set_name) = use_state(&cx, || "...?");
-
-    cx.render(rsx!(
-        div {
-            h1 { "Hello, {name}" }
-            // look ma - we can rsx! and html! together
-            {["jack", "jill"].iter().map(|f| html!(<button onclick={move |_| set_name(f)}> "{f}" </button>))}
-        }
-    ))
-};
-
-pub fn render<'src, 'a, F: for<'b> FnOnce(&'b NodeFactory<'src>) -> VNode<'src> + 'src + 'a, P>(
-    cx: &'a Context<'src, P>,
-    lazy_nodes: LazyNodes<'src, F>,
-) -> VNode<'src> {
-    cx.render(lazy_nodes)
-}

+ 3 - 3
examples/antipatterns.rs → examples/reference/antipatterns.rs

@@ -21,10 +21,10 @@ fn main() {}
 /// Antipattern: Iterators without keys
 /// -----------------------------------
 ///
-/// This is considered an anti-pattern for performance reasons. Dioxus must diff your current and old layout and must
+/// This is considered an anti-pattern for performance reasons. Dioxus will diff your current and old layout and must
 /// take a slower path if it can't correlate old elements with new elements. Lists are particularly susceptible to the
 /// "slow" path, so you're strongly encouraged to provide a unique ID stable between renders. Additionally, providing
-/// the *wrong* keys is even worse. Keys should be:
+/// the *wrong* keys is even worse - props might be assigned to the wrong components! Keys should be:
 /// - Unique
 /// - Stable
 /// - Predictable
@@ -39,7 +39,7 @@ static AntipatternNoKeys: FC<NoKeysProps> = |cx| {
     rsx!(in cx, ul {
         {cx.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
     });
-    // Like this:
+    // RIGHT: Like this:
     rsx!(in cx, ul {
         {cx.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
     })

+ 10 - 0
examples/reference/async.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/basics.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 45 - 0
examples/reference/children.rs

@@ -0,0 +1,45 @@
+//! Example: Children
+//! -----------------
+//!
+//! Dioxus supports passing children in from the parent. These children are allocated in the parent and just forced
+//! into the child. Components that pass in children may not be safely memoized, though in practice its rare for a
+//! change in a parent to not result in a different set of children.
+//!
+//! In Dioxus, children can *only be a list*. Unlike React, you cannot pass in functions or arbitrary data. This is
+//! partially a limitaiton in having static types, but is rather intentional to encourage the use of attributes where
+//! arbitrary child data might normally be used. Check out the `function driven children` example for how to adopt your
+//! React pattern for Dioxus' semantics.
+//!
+//! Dioxus will let you use the `children` method more than once - and it's semantically *okay* - but you'll likely
+//! ruin your page if you try to clone elements in this way. Under the hood, Dioxus shares a "mounted ID" for each node,
+//! and mounting the same VNode in two places will overwrite the first mounted ID. This will likely lead to dead elements.
+//!
+//! In the future, this might become a runtime error, so consider it an error today.
+
+use dioxus::prelude::*;
+fn main() {}
+
+static App: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+            Banner {
+                p { "Some Content1" }
+            }
+            Banner {
+                p { "Some Content2" }
+            }
+        }
+    })
+};
+
+static Banner: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+            h1 { "This is a great banner!" }
+            div { class: "content"
+                {cx.children()}
+            }
+            footer { "Wow, what a great footer" }
+        }
+    })
+};

+ 92 - 0
examples/reference/conditional_rendering.rs

@@ -0,0 +1,92 @@
+//! Example: Conditional Rendering
+//! ------------------------------
+//!
+//! This example shows how to hide or show elements using conditional rendering.
+//!
+//! Often times you might want to display a different UI given some sort of condition. This is called "conditonal rendering".
+//! In Dioxus, you can perform conditional rendering with optionals or matching.
+//!
+//! The rsx! and html! macro accepts anything that is `IntoIter<Item = impl IntoVnode>`. Options and Results both implement
+//! IntoIter for impl VNode, so you can use option/result for conditional rendering.
+
+use dioxus::prelude::*;
+
+fn main() {}
+
+// Convert a boolean conditional into a hide/show
+#[derive(PartialEq, Props)]
+struct MyProps {
+    should_show: bool,
+}
+static Example: FC<MyProps> = |cx| {
+    cx.render(rsx! {
+        div {
+            {cx.should_show.then(|| rsx!{
+                h1 { "showing the title!" }
+            })}
+        }
+    })
+};
+
+// Convert a boolean conditional into an either/or
+// Because rsx! is lazy (produces a closure), we cannot use it in two branch arms. To use it in matching/branching, we
+// must render it.
+//
+// Dioxus will let you render any `LazyNodes` into a `VNode` with `cx.render`. `rsx!` also supports the `in cx` syntax
+// which will do essentially the same thing as `cx.render`.
+//
+// In short:
+// `rsx!(in cx, ...)` is shorthand for `cx.render(rsx!(...))`
+#[derive(PartialEq, Props)]
+struct MyProps1 {
+    should_show: bool,
+}
+static Example1: FC<MyProps1> = |cx| {
+    cx.render(rsx! {
+        div {
+            // With matching
+            {match cx.should_show {
+                true => cx.render(rsx!(div {"it is true!"})),
+                false => rsx!(in cx, div {"it is false!"}),
+            }}
+
+            // or with just regular conditions
+            {if cx.should_show {
+                rsx!(in cx, div {"it is true!"})
+            } else {
+                rsx!(in cx, div {"it is false!"})
+            }}
+
+            // or with optional chaining
+            {
+                cx.should_show
+                .then(|| rsx!(in cx, div {"it is false!"}))
+                .unwrap_or_else(|| rsx!(in cx, div {"it is false!"}))
+            }
+        }
+    })
+};
+
+/// Matching can be expanded
+
+#[derive(PartialEq)]
+enum Color {
+    Green,
+    Yellow,
+    Red,
+}
+#[derive(PartialEq, Props)]
+struct MyProps2 {
+    color: Color,
+}
+static Example2: FC<MyProps2> = |cx| {
+    cx.render(rsx! {
+        div {
+            {match cx.color {
+                Color::Green => rsx!(in cx, div {"it is Green!"}),
+                Color::Yellow => rsx!(in cx, div {"it is Yellow!"}),
+                Color::Red => rsx!(in cx, div {"it is Red!"}),
+            }}
+        }
+    })
+};

+ 10 - 0
examples/reference/controlled_inputs.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/custom_elements.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 15 - 0
examples/reference/empty.rs

@@ -0,0 +1,15 @@
+//! Example: Null/None Children
+//! ---------------------------
+//!
+//! This is a simple pattern that allows you to return no elements!
+
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/fiber.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 45 - 0
examples/reference/fragments.rs

@@ -0,0 +1,45 @@
+//! Example: Fragments
+//! ------------------
+//!
+//! Dioxus can return multiple elements without a container through the use of the VNode called a "Fragment". Fragments do not
+//! have a mounted root and are inserted inline with their siblings. There are three ways of creating fragments as outlined
+//! in the examples below:
+//! - By returning multiple elements in Rsx!
+//! - By using the `Fragment` component
+//! - By using the fragment() method on the node factory
+
+use dioxus::prelude::*;
+fn main() {}
+
+// Returning multiple elements with rsx! or html!
+static Example1: FC<()> = |cx| {
+    cx.render(rsx! {
+        h1 { }
+        h2 { }
+        h3 { }
+        // {}
+        // "hello world"
+    })
+};
+
+// Using the Fragment component
+static Example2: FC<()> = |cx| {
+    cx.render(rsx! {
+        Fragment {
+            div {}
+            div {}
+            "asd"
+        }
+    })
+};
+
+// Using the `fragment` method on the NodeFactory
+static Example3: FC<()> = |cx| {
+    cx.render(LazyNodes::new(move |fac: &NodeFactory| {
+        fac.fragment_builder(None, |list| {
+            list.add_child(fac.text(format_args!("A")))
+                .add_child(fac.text(format_args!("B")))
+                .finish()
+        })
+    }))
+};

+ 10 - 0
examples/reference/global_css.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/inline_styles.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/iterators.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 50 - 0
examples/reference/listener.rs

@@ -0,0 +1,50 @@
+//! Example: Listeners
+//! ------------------
+//!
+//! This example demonstrates the various ways listeners may be used in Dioxus.
+//! Listeners may be at most `FnMut` - but not `FnOnce`.
+//! Listeners may borrow the lifetime of cx (children and hooks), but not of local (stack) data.
+
+use dioxus::prelude::*;
+
+fn main() {}
+
+/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
+static ButtonList: FC<()> = |cx| {
+    let (name, set_name) = use_state(&cx, || "...?");
+
+    let names = ["jack", "jill", "john", "jane"]
+        .iter()
+        .map(move |n| rsx!(button { onclick: move |_| set_name(n), "{n}" }));
+
+    cx.render(rsx!(
+        div {
+            h1 { "Hello, {name}" }
+            {names}
+        }
+    ))
+};
+
+/// This shows how listeners may be without a visible change in the display.
+/// Check the console.
+static NonUpdatingEvents: FC<()> = |cx| {
+    rsx!(in cx, div {
+        button {
+            onclick: move |_| log::info!("Did not cause any updates!")
+            "Click me to log!"
+        }
+    })
+};
+
+static DisablePropogation: FC<()> = |cx| {
+    rsx!(in cx,
+        div {
+            onclick: move |_| log::info!("event propogated to the div!")
+            button {
+                onclick: move |evt| {
+                    log::info!("Button will allow propogation");
+                }
+            }
+        }
+    )
+};

+ 85 - 0
examples/reference/memo.rs

@@ -0,0 +1,85 @@
+//! Example: Memoization
+//! --------------------
+//!
+//! This example showcases how memoization works in Dioxus.
+//!
+//! Memoization is the process in which Dioxus skips diffing child components if their props don't change.
+//! In React, components are never memoized unless wrapped in `memo` or configured with `shouldComponentUpdate`.
+//!
+//! Due to the safety guarantees of Rust, we can automatically memoize components in some circumstances. Whenever a
+//! component's properties are valid for the `'static` lifetime, Dioxus will automatically compare the props before
+//! diffing the component. If the props don't change (according to PartialEq), the component will not be re-rendered.
+//!
+//! However, if the props use some generics or borrow from their parent, then Dioxus can't safely supress updates,
+//! and is forced to render the child. If you think that this behavior is wrong for your usecase, you can implement
+//! the memo method yourself, but beware, doing so is UNSAFE and may cause issues if you do it wrong.
+//!
+//! If you want to gain that little bit extra performance, consider using global state management, signals, or
+//! memoized collections like im-rc which are designed for this use case.
+
+use dioxus::prelude::*;
+fn main() {}
+
+// By default, components with no props are always memoized.
+// A props of () is considered empty.
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div { "100% memoized!" }
+    })
+};
+
+// These props do not borrow any content, and therefore can be safely memoized.
+// However, the parent *must* create a new string on every render.
+// Notice how these props implement PartialEq - this is required for 'static props
+#[derive(PartialEq, Props)]
+struct MyProps1 {
+    name: String,
+}
+
+static Example1: FC<MyProps1> = |cx| {
+    cx.render(rsx! {
+        div { "100% memoized! {cx.name}" }
+    })
+};
+
+// These props do not borrow any content, and therefore can be safely memoized.
+// In contrast with the `String` example, these props use `Rc<str>` which operates similar to strings in JavaScript.
+// These strings cannot be modified, but may be cheaply shared in many places without issue.
+#[derive(PartialEq, Props)]
+struct MyProps2 {
+    name: std::rc::Rc<str>,
+}
+
+static Example2: FC<MyProps2> = |cx| {
+    cx.render(rsx! {
+        div { "100% memoized! {cx.name}" }
+    })
+};
+
+// These props *do* borrow any content, and therefore cannot be safely memoized!.
+#[derive(PartialEq, Props)]
+struct MyProps3<'a> {
+    name: &'a str,
+}
+// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
+// Using the `static Example: FC<()>` pattern _will_ specify a lifetime, but that lifetime will be static which might
+// not exactly be what you want
+fn Example3<'a>(cx: Context<'a, MyProps3<'a>>) -> VNode {
+    cx.render(rsx! {
+        div { "Not memoized! {cx.name}" }
+    })
+}
+
+// These props *do* borrow any content, and therefore cannot be safely memoized!.
+// However, they cannot be compared, so we don't need the PartialEq flag.
+#[derive(Props)]
+struct MyProps4<'a> {
+    onhandle: &'a dyn Fn(),
+}
+
+// We need to manually specify a lifetime that ensures props and scope (the component's state) share the same lifetime.
+fn Example4<'a>(cx: Context<'a, MyProps4<'a>>) -> VNode {
+    cx.render(rsx! {
+        div { "Not memoized!", onclick: move |_| (cx.onhandle)() }
+    })
+}

+ 2 - 0
examples/reference/mod.rs

@@ -0,0 +1,2 @@
+mod iterator;
+mod memo;

+ 10 - 0
examples/reference/noderefs.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/signals.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 40 - 0
examples/reference/spreadpattern.rs

@@ -0,0 +1,40 @@
+//! Example: Spread pattern for Components
+//! --------------------------------------
+//!
+//! Dioxus supports  the "spread" pattern for manually building a components properties. This is useful when props
+//! are passed down from a parent, or it's more ergonomic to construct props from outside the rsx! macro.
+//!
+//! To use the spread pattern, simply pass ".." followed by a Rust epxression. This pattern also supports overriding
+//! values, using the manual props as the base and then modifying fields specified with non-spread attributes.
+
+use dioxus::prelude::*;
+fn main() {}
+
+static App: FC<()> = |cx| {
+    let props = MyProps {
+        count: 0,
+        live: true,
+        name: "Dioxus",
+    };
+    cx.render(rsx! {
+        Example { ..props, count: 10, div {"child"} }
+    })
+};
+
+#[derive(PartialEq, Props)]
+struct MyProps {
+    count: u32,
+    live: bool,
+    name: &'static str,
+}
+
+static Example: FC<MyProps> = |cx| {
+    cx.render(rsx! {
+        div {
+            h1 { "Hello, {cx.name}"}
+            h3 {"Are we alive? {cx.live}"}
+            p {"Count is {cx.count}"}
+            { cx.children() }
+        }
+    })
+};

+ 10 - 0
examples/reference/statemanagement.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 0 - 0
examples/suspense.rs → examples/reference/suspense.rs


+ 10 - 0
examples/reference/testing.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 10 - 0
examples/reference/tostring.rs

@@ -0,0 +1,10 @@
+use dioxus::prelude::*;
+fn main() {}
+
+static Example: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+
+        }
+    })
+};

+ 21 - 0
examples/reference/webcomponents.rs

@@ -0,0 +1,21 @@
+//! Example: Web Components
+//! -----------------------
+//!
+//! Web components are a flavor of html elements that can be user-defined.
+//! See: https://www.webcomponents.org for more information.
+//!
+//! Users who use webcomponents typically don't use Dioxus. However, if you would like to use webcomponents in Dioxus,
+//! you can easily create a new custom element with compile-time correct wrappers around the webcomponent.
+//!
+//! We don't support building new webcomponents with Dioxus, however.
+//!
+
+use dioxus::{builder::ElementBuilder, prelude::NodeFactory};
+
+fn main() {}
+
+// TODO
+struct MyEle<'a, 'b> {
+    el: ElementBuilder<'a, 'b>,
+    fac: &'b NodeFactory<'a>,
+}

+ 8 - 2
examples/rsx_usage.rs

@@ -120,7 +120,7 @@ static Example: FC<()> = |cx| {
             {if true {
                 rsx!(in cx, h1 {"Top text"})
             } else {
-                cx.render(rsx!( h1 {"Bottom text"}))
+                rsx!(in cx, h1 {"Bottom text"})
             }}
 
             // returning "None" is a bit noisy... but rare in practice
@@ -168,7 +168,13 @@ static Example: FC<()> = |cx| {
                 rsx!(Taller { ..props })
             }}
 
-            // Can take children
+            // Spreading can also be overridden manually
+            Taller {
+                ..TallerProps { a: "ballin!" }
+                a: "not ballin!"
+            }
+
+            // Can take children too!
             Taller { a: "asd", div {"hello world!"} }
         }
     })

+ 28 - 23
examples/showcase.rs

@@ -1,26 +1,31 @@
-//! A Showcase of all the useful examples
+//! Example: Reference Showcase
+//! ---------------------------
 //!
-//!
-//!
-
-fn main() {
-    use_css_consumer(&cx, "mystyle");
+//! This example provides a cute interface for browsing the reference examples.
 
-    // at the global head of the app
-    use_css_provider(&cx, |cfg| {});
-    use_recoil_provider(&cx, |cfg| {});
-
-    let recoil = use_recoil_api(&cx, |_| {});
-    use_websocket_connection(&cx, move |cfg| {
-        cfg.on_receive(move |event| match event.data::<Msg>() {
-            Ok(msg) => match msg {
-                a => recoil.set(&ATOM, 10),
-                c => recoil.set(&ATOM, 20),
-                _ => {}
-            },
-            Err(e) => {}
-        });
-        cfg.on_close(move |event| {});
-        cfg.on_open(move |event| {});
-    });
+mod reference {
+    mod antipatterns;
+    mod basics;
+    mod children;
+    mod conditional_rendering;
+    mod controlled_inputs;
+    mod custom_elements;
+    mod empty;
+    mod fiber;
+    mod fragments;
+    mod global_css;
+    mod inline_styles;
+    mod iterators;
+    mod listener;
+    mod memo;
+    mod noderefs;
+    mod signals;
+    mod spreadpattern;
+    mod statemanagement;
+    mod suspense;
+    mod testing;
+    mod tostring;
+    mod webcomponents;
 }
+
+fn main() {}

+ 36 - 23
packages/core-macro/src/rsx/component.rs

@@ -80,33 +80,46 @@ impl ToTokens for Component {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let name = &self.name;
 
-        let using_manual_override = self.manual_props.is_some();
-
-        let mut builder = {
-            match &self.manual_props {
-                Some(manual_props) => quote! { #manual_props },
-                None => quote! { fc_to_builder(#name) },
-            }
-        };
-
         let mut has_key = None;
 
-        for field in &self.body {
-            if field.name.to_string() == "key" {
-                has_key = Some(field);
-            } else {
-                match using_manual_override {
-                    true => panic!("Currently we don't support manual props and prop fields. Choose either manual props or prop fields"),
-                    false => builder.append_all(quote! {#field}),
+        let builder = match &self.manual_props {
+            Some(manual_props) => {
+                let mut toks = quote! {
+                    let mut __manual_props = #manual_props;
+                };
+                for field in &self.body {
+                    if field.name.to_string() == "key" {
+                        has_key = Some(field);
+                    } else {
+                        let name = &field.name;
+                        let val = &field.content;
+                        toks.append_all(quote! {
+                            __manual_props.#name = #val;
+                        });
+                    }
                 }
+                toks.append_all(quote! {
+                    __manual_props
+                });
+                quote! {{
+                    #toks
+                }}
             }
-        }
-
-        if !using_manual_override {
-            builder.append_all(quote! {
-                .build()
-            });
-        }
+            None => {
+                let mut toks = quote! { fc_to_builder(#name) };
+                for field in &self.body {
+                    if field.name.to_string() == "key" {
+                        has_key = Some(field);
+                    } else {
+                        toks.append_all(quote! {#field})
+                    }
+                }
+                toks.append_all(quote! {
+                    .build()
+                });
+                toks
+            }
+        };
 
         let key_token = match has_key {
             Some(field) => {

+ 61 - 52
packages/core/src/events.rs

@@ -4,7 +4,7 @@
 //! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might
 //! be heavy or need to interact through FFI, so the events themselves are designed to be lazy.
 
-use std::rc::Rc;
+use std::{ops::Deref, rc::Rc};
 
 use crate::{innerlude::ScopeIdx, virtual_dom::RealDomNode};
 
@@ -28,21 +28,21 @@ impl EventTrigger {
 #[derive(Debug)]
 pub enum VirtualEvent {
     // Real events
-    ClipboardEvent(Rc<dyn on::ClipboardEvent>),
-    CompositionEvent(Rc<dyn on::CompositionEvent>),
-    KeyboardEvent(Rc<dyn on::KeyboardEvent>),
-    FocusEvent(Rc<dyn on::FocusEvent>),
-    FormEvent(Rc<dyn on::FormEvent>),
-    SelectionEvent(Rc<dyn on::SelectionEvent>),
-    TouchEvent(Rc<dyn on::TouchEvent>),
-    UIEvent(Rc<dyn on::UIEvent>),
-    WheelEvent(Rc<dyn on::WheelEvent>),
-    MediaEvent(Rc<dyn on::MediaEvent>),
-    AnimationEvent(Rc<dyn on::AnimationEvent>),
-    TransitionEvent(Rc<dyn on::TransitionEvent>),
-    ToggleEvent(Rc<dyn on::ToggleEvent>),
-    MouseEvent(Rc<dyn on::MouseEvent>),
-    PointerEvent(Rc<dyn on::PointerEvent>),
+    ClipboardEvent(on::ClipboardEvent),
+    CompositionEvent(on::CompositionEvent),
+    KeyboardEvent(on::KeyboardEvent),
+    FocusEvent(on::FocusEvent),
+    FormEvent(on::FormEvent),
+    SelectionEvent(on::SelectionEvent),
+    TouchEvent(on::TouchEvent),
+    UIEvent(on::UIEvent),
+    WheelEvent(on::WheelEvent),
+    MediaEvent(on::MediaEvent),
+    AnimationEvent(on::AnimationEvent),
+    TransitionEvent(on::TransitionEvent),
+    ToggleEvent(on::ToggleEvent),
+    MouseEvent(on::MouseEvent),
+    PointerEvent(on::PointerEvent),
 
     // Whenever a task is ready (complete) Dioxus produces this "FiberEvent"
     FiberEvent { task_id: u16 },
@@ -75,12 +75,21 @@ pub mod on {
     use super::VirtualEvent;
 
     macro_rules! event_directory {
-        ( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
+        ( $( $eventdata:ident($wrapper:ident): [ $( $name:ident )* ]; )* ) => {
             $(
+                #[derive(Debug)]
+                pub struct $wrapper(Rc<dyn $eventdata>);
+                impl Deref for $wrapper {
+                    type Target = Rc<dyn $eventdata>;
+                    fn deref(&self) -> &Self::Target {
+                        &self.0
+                    }
+                }
+
                 $(
                     pub fn $name<'a>(
                         c: &'_ NodeFactory<'a>,
-                        callback: impl Fn(Rc<dyn $eventdata>) + 'a,
+                        callback: impl Fn($wrapper) + 'a,
                     ) -> Listener<'a> {
                         let bump = &c.bump();
                         Listener {
@@ -88,7 +97,7 @@ pub mod on {
                             mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
                             scope: c.scope_ref.arena_idx,
                             callback: bump.alloc(move |evt: VirtualEvent| match evt {
-                                VirtualEvent::$eventdata(event) => callback(event),
+                                VirtualEvent::$wrapper(event) => callback(event),
                                 _ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
                             }),
                         }
@@ -99,36 +108,36 @@ pub mod on {
     }
 
     event_directory! {
-        ClipboardEvent: [copy cut paste];
-        CompositionEvent: [compositionend compositionstart compositionupdate];
-        KeyboardEvent: [keydown keypress keyup];
-        FocusEvent: [focus blur];
-        FormEvent: [change input invalid reset submit];
-        MouseEvent: [
+        ClipboardEventInner(ClipboardEvent): [copy cut paste];
+        CompositionEventInner(CompositionEvent): [compositionend compositionstart compositionupdate];
+        KeyboardEventInner(KeyboardEvent): [keydown keypress keyup];
+        FocusEventInner(FocusEvent): [focus blur];
+        FormEventInner(FormEvent): [change input invalid reset submit];
+        MouseEventInner(MouseEvent): [
             click contextmenu doubleclick drag dragend dragenter dragexit
             dragleave dragover dragstart drop mousedown mouseenter mouseleave
             mousemove mouseout mouseover mouseup
         ];
-        PointerEvent: [
+        PointerEventInner(PointerEvent): [
             pointerdown pointermove pointerup pointercancel gotpointercapture
             lostpointercapture pointerenter pointerleave pointerover pointerout
         ];
-        SelectionEvent: [select];
-        TouchEvent: [touchcancel touchend touchmove touchstart];
-        UIEvent: [scroll];
-        WheelEvent: [wheel];
-        MediaEvent: [
+        SelectionEventInner(SelectionEvent): [select];
+        TouchEventInner(TouchEvent): [touchcancel touchend touchmove touchstart];
+        UIEventInner(UIEvent): [scroll];
+        WheelEventInner(WheelEvent): [wheel];
+        MediaEventInner(MediaEvent): [
             abort canplay canplaythrough durationchange emptied encrypted
             ended error loadeddata loadedmetadata loadstart pause play
             playing progress ratechange seeked seeking stalled suspend
             timeupdate volumechange waiting
         ];
-        AnimationEvent: [animationstart animationend animationiteration];
-        TransitionEvent: [transitionend];
-        ToggleEvent: [toggle];
+        AnimationEventInner(AnimationEvent): [animationstart animationend animationiteration];
+        TransitionEventInner(TransitionEvent): [transitionend];
+        ToggleEventInner(ToggleEvent): [toggle];
     }
 
-    trait GenericEvent {
+    pub trait GenericEventInner {
         /// Returns whether or not a specific event is a bubbling event
         fn bubbles(&self) -> bool;
         /// Sets or returns whether the event should propagate up the hierarchy or not
@@ -159,15 +168,15 @@ pub mod on {
         fn time_stamp(&self) -> usize;
     }
 
-    pub trait ClipboardEvent: Debug {
+    pub trait ClipboardEventInner: Debug + GenericEventInner {
         // DOMDataTransfer clipboardData
     }
 
-    pub trait CompositionEvent: Debug {
+    pub trait CompositionEventInner: Debug {
         fn data(&self) -> String;
     }
 
-    pub trait KeyboardEvent: Debug {
+    pub trait KeyboardEventInner: Debug {
         fn char_code(&self) -> usize;
         fn ctrl_key(&self) -> bool;
         fn key(&self) -> String;
@@ -181,15 +190,15 @@ pub mod on {
         fn get_modifier_state(&self, key_code: usize) -> bool;
     }
 
-    pub trait FocusEvent: Debug {
-        /* DOMEventTarget relatedTarget */
+    pub trait FocusEventInner: Debug {
+        /* DOMEventInnerTarget relatedTarget */
     }
 
-    pub trait FormEvent: Debug {
+    pub trait FormEventInner: Debug {
         fn value(&self) -> String;
     }
 
-    pub trait MouseEvent: Debug {
+    pub trait MouseEventInner: Debug {
         fn alt_key(&self) -> bool;
         fn button(&self) -> i16;
         fn buttons(&self) -> u16;
@@ -205,7 +214,7 @@ pub mod on {
         fn get_modifier_state(&self, key_code: &str) -> bool;
     }
 
-    pub trait PointerEvent: Debug {
+    pub trait PointerEventInner: Debug {
         // Mouse only
         fn alt_key(&self) -> bool;
         fn button(&self) -> usize;
@@ -232,9 +241,9 @@ pub mod on {
         fn is_primary(&self) -> bool;
     }
 
-    pub trait SelectionEvent: Debug {}
+    pub trait SelectionEventInner: Debug {}
 
-    pub trait TouchEvent: Debug {
+    pub trait TouchEventInner: Debug {
         fn alt_key(&self) -> bool;
         fn ctrl_key(&self) -> bool;
         fn meta_key(&self) -> bool;
@@ -245,37 +254,37 @@ pub mod on {
         // touches: DOMTouchList,
     }
 
-    pub trait UIEvent: Debug {
+    pub trait UIEventInner: Debug {
         // DOMAbstractView view
         fn detail(&self) -> i32;
     }
 
-    pub trait WheelEvent: Debug {
+    pub trait WheelEventInner: Debug {
         fn delta_mode(&self) -> i32;
         fn delta_x(&self) -> i32;
         fn delta_y(&self) -> i32;
         fn delta_z(&self) -> i32;
     }
 
-    pub trait MediaEvent: Debug {}
+    pub trait MediaEventInner: Debug {}
 
-    pub trait ImageEvent: Debug {
+    pub trait ImageEventInner: Debug {
         //     load error
     }
 
-    pub trait AnimationEvent: Debug {
+    pub trait AnimationEventInner: Debug {
         fn animation_name(&self) -> String;
         fn pseudo_element(&self) -> String;
         fn elapsed_time(&self) -> f32;
     }
 
-    pub trait TransitionEvent: Debug {
+    pub trait TransitionEventInner: Debug {
         fn property_name(&self) -> String;
         fn pseudo_element(&self) -> String;
         fn elapsed_time(&self) -> f32;
     }
 
-    pub trait ToggleEvent: Debug {}
+    pub trait ToggleEventInner: Debug {}
 }
 
 mod tests {

+ 19 - 0
packages/core/src/nodebuilder.rs

@@ -768,6 +768,25 @@ impl<'a> NodeFactory<'a> {
     > {
         ElementBuilder::new(self, tag_name)
     }
+
+    pub fn child_list(&self) -> ChildrenList {
+        ChildrenList::new(&self)
+    }
+
+    pub fn fragment_builder<'b>(
+        &'b self,
+        key: Option<&'a str>,
+        builder: impl FnOnce(ChildrenList<'a, 'b>) -> &'a [VNode<'a>],
+    ) -> VNode<'a> {
+        self.fragment(builder(ChildrenList::new(&self)), key)
+    }
+
+    pub fn fragment(&self, children: &'a [VNode<'a>], key: Option<&'a str>) -> VNode<'a> {
+        VNode::Fragment(self.bump().alloc(VFragment {
+            children,
+            key: NodeKey::new_opt(key),
+        }))
+    }
 }
 
 use std::fmt::Debug;

+ 5 - 0
packages/core/src/nodes.rs

@@ -297,6 +297,11 @@ impl<'a> NodeKey<'a> {
     pub fn new(key: &'a str) -> Self {
         NodeKey(Some(key))
     }
+
+    #[inline]
+    pub fn new_opt(key: Option<&'a str>) -> Self {
+        NodeKey(key)
+    }
 }
 
 // ==============================