docs: examples
This commit is contained in:
1 parent
818043e5e4
commit
d9e6d0925b
33 files changed
+732
-152
No files matched your search
@@ -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
@@ -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 |
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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}" }))}
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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" }
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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!"}),
|
||||
}}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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()
|
||||
})
|
||||
}))
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
@@ -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)() }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod iterator;
|
||||
mod memo;
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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() }
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
File renamed without changes.
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
static Example: FC<()> = |cx| {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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>,
|
||||
}
|
||||
@@ -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
@@ -1,26 +1,31 @@
|
||||
//! A Showcase of all the useful examples
|
||||
//!
|
||||
//!
|
||||
//! Example: Reference Showcase
|
||||
//! ---------------------------
|
||||
//!
|
||||
//! This example provides a cute interface for browsing the reference examples.
|
||||
|
||||
fn main() {
|
||||
use_css_consumer(&cx, "mystyle");
|
||||
|
||||
// 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() {}
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
// ==============================
|
||||
|
||||
Reference in new issue
Block a user