docs: examples

This commit is contained in:
Jonathan Kelley committed 2021-07-02 01:30:52 -04:00
1 parent 818043e5e4
commit d9e6d0925b
33 files changed
+732 -152

No files matched your search

+1
View File
@@ -30,6 +30,7 @@ split-debuginfo = "unpacked"
[dev-dependencies] [dev-dependencies]
futures = "0.3.15" futures = "0.3.15"
log = "0.4.14"
# For the tide ssr examples # For the tide ssr examples
# async-std = { version="1.9.0", features=["attributes"] } # async-std = { version="1.9.0", features=["attributes"] }
# tide = { version="0.16.0" } # tide = { version="0.16.0" }
+46 -25
View File
@@ -1,29 +1,50 @@
# Examples # 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 | | Example | What it does | Status |
| --------------------------------------- | ------------ | | --------------------------------------------------- | ------------------------------------------- | ------ |
| [The basics](./basics.rs) | this does | | [The basics](./basics.rs) | A few basic examples to preview Dioxus | 🛠 |
| [fine grained reactivity](./signals.rs) | this does | | [fine grained reactivity](./signals.rs) | Escape `diffing` by writing values directly | 🛠 |
| Global State Management | this does | | [Global State Management](./statemanagement.rs) | Share state between components | 🛠 |
| Virtual Refs | this does | | [Virtual Refs]() | Cross-platform imperative elements | 🛠 |
| Inline Styles | this does | | [Inline Styles](./inline-styles.rs) | Define styles for elements inline | 🛠 |
| Conditional Rendering | this does | | [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals | ✅ |
| Maps/Iterators | this does |
| Render To string | this does | These examples are not necessarily meant to be run, but rather serve as a reference for the given functionality.
| Component Children | this does |
| Function Driven children | this does | | Example | What it does | Status |
| Memoization | this does | | --------------------------------------------------- | ----------------------------------------------- | ------ |
| Borrowed Data | this does | | [The basics](./basics.rs) | A few basic examples to preview Dioxus | 🛠 |
| Fragments | this does | | [fine grained reactivity](./signals.rs) | Escape `diffing` by writing values directly | 🛠 |
| Null/None Components | this does | | [Global State Management](./statemanagement.rs) | Share state between components | 🛠 |
| Spread Pattern for props | this does | | [Virtual Refs]() | Cross-platform imperative elements | 🛠 |
| Controlled Inputs | this does | | [Inline Styles](./inline-styles.rs) | Define styles for elements inline | 🛠 |
| Custom Elements | this does | | [Conditional Rendering](./conditional-rendering.rs) | Hide/Show elements using conditionals | ✅ |
| Testing And debugging | this does | | [Maps/Iterators](./iterators.rs) | Use iterators in the rsx! macro | 🛠 |
| Asynchronous Data | this does | | [Render To string](./tostring.rs) | Render a mounted virtualdom to a string | 🛠 |
| Fiber/Scheduled Rendering | this does | | [Component Children](./children.rs) | Pass children into child components | 🛠 |
| CSS Compiled Styles | this does | | [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 |
-24
View File
@@ -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 /// 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 /// 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 /// "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 /// - Unique
/// - Stable /// - Stable
/// - Predictable /// - Predictable
@@ -39,7 +39,7 @@ static AntipatternNoKeys: FC<NoKeysProps> = |cx| {
rsx!(in cx, ul { rsx!(in cx, ul {
{cx.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))} {cx.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
}); });
// Like this: // RIGHT: Like this:
rsx!(in cx, ul { rsx!(in cx, ul {
{cx.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))} {cx.data.iter().map(|(k, v)| rsx!(li { key: "{k}", "List item: {v}" }))}
}) })
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+45
View File
@@ -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!"}),
}}
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+15
View File
@@ -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
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+45
View File
@@ -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
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+50
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,2 @@
mod iterator;
mod memo;
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+40
View File
@@ -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
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
File renamed without changes.
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+10
View File
@@ -0,0 +1,10 @@
use dioxus::prelude::*;
fn main() {}
static Example: FC<()> = |cx| {
cx.render(rsx! {
div {
}
})
};
+21
View File
@@ -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
View File
@@ -120,7 +120,7 @@ static Example: FC<()> = |cx| {
{if true { {if true {
rsx!(in cx, h1 {"Top text"}) rsx!(in cx, h1 {"Top text"})
} else { } else {
cx.render(rsx!( h1 {"Bottom text"})) rsx!(in cx, h1 {"Bottom text"})
}} }}
// returning "None" is a bit noisy... but rare in practice // returning "None" is a bit noisy... but rare in practice
@@ -168,7 +168,13 @@ static Example: FC<()> = |cx| {
rsx!(Taller { ..props }) 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!"} } Taller { a: "asd", div {"hello world!"} }
} }
}) })
+28 -23
View File
@@ -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() { mod reference {
use_css_consumer(&cx, "mystyle"); mod antipatterns;
mod basics;
// at the global head of the app mod children;
use_css_provider(&cx, |cfg| {}); mod conditional_rendering;
use_recoil_provider(&cx, |cfg| {}); mod controlled_inputs;
mod custom_elements;
let recoil = use_recoil_api(&cx, |_| {}); mod empty;
use_websocket_connection(&cx, move |cfg| { mod fiber;
cfg.on_receive(move |event| match event.data::<Msg>() { mod fragments;
Ok(msg) => match msg { mod global_css;
a => recoil.set(&ATOM, 10), mod inline_styles;
c => recoil.set(&ATOM, 20), mod iterators;
_ => {} mod listener;
}, mod memo;
Err(e) => {} mod noderefs;
}); mod signals;
cfg.on_close(move |event| {}); mod spreadpattern;
cfg.on_open(move |event| {}); mod statemanagement;
}); mod suspense;
mod testing;
mod tostring;
mod webcomponents;
} }
fn main() {}
+36 -23
View File
@@ -80,33 +80,46 @@ impl ToTokens for Component {
fn to_tokens(&self, tokens: &mut TokenStream2) { fn to_tokens(&self, tokens: &mut TokenStream2) {
let name = &self.name; 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; let mut has_key = None;
for field in &self.body { let builder = match &self.manual_props {
if field.name.to_string() == "key" { Some(manual_props) => {
has_key = Some(field); let mut toks = quote! {
} else { let mut __manual_props = #manual_props;
match using_manual_override { };
true => panic!("Currently we don't support manual props and prop fields. Choose either manual props or prop fields"), for field in &self.body {
false => builder.append_all(quote! {#field}), 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
}}
} }
} None => {
let mut toks = quote! { fc_to_builder(#name) };
if !using_manual_override { for field in &self.body {
builder.append_all(quote! { if field.name.to_string() == "key" {
.build() has_key = Some(field);
}); } else {
} toks.append_all(quote! {#field})
}
}
toks.append_all(quote! {
.build()
});
toks
}
};
let key_token = match has_key { let key_token = match has_key {
Some(field) => { Some(field) => {
+61 -52
View File
@@ -4,7 +4,7 @@
//! 3rd party renderers are responsible for converting their native events into these virtual event types. Events might //! 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. //! 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}; use crate::{innerlude::ScopeIdx, virtual_dom::RealDomNode};
@@ -28,21 +28,21 @@ impl EventTrigger {
#[derive(Debug)] #[derive(Debug)]
pub enum VirtualEvent { pub enum VirtualEvent {
// Real events // Real events
ClipboardEvent(Rc<dyn on::ClipboardEvent>), ClipboardEvent(on::ClipboardEvent),
CompositionEvent(Rc<dyn on::CompositionEvent>), CompositionEvent(on::CompositionEvent),
KeyboardEvent(Rc<dyn on::KeyboardEvent>), KeyboardEvent(on::KeyboardEvent),
FocusEvent(Rc<dyn on::FocusEvent>), FocusEvent(on::FocusEvent),
FormEvent(Rc<dyn on::FormEvent>), FormEvent(on::FormEvent),
SelectionEvent(Rc<dyn on::SelectionEvent>), SelectionEvent(on::SelectionEvent),
TouchEvent(Rc<dyn on::TouchEvent>), TouchEvent(on::TouchEvent),
UIEvent(Rc<dyn on::UIEvent>), UIEvent(on::UIEvent),
WheelEvent(Rc<dyn on::WheelEvent>), WheelEvent(on::WheelEvent),
MediaEvent(Rc<dyn on::MediaEvent>), MediaEvent(on::MediaEvent),
AnimationEvent(Rc<dyn on::AnimationEvent>), AnimationEvent(on::AnimationEvent),
TransitionEvent(Rc<dyn on::TransitionEvent>), TransitionEvent(on::TransitionEvent),
ToggleEvent(Rc<dyn on::ToggleEvent>), ToggleEvent(on::ToggleEvent),
MouseEvent(Rc<dyn on::MouseEvent>), MouseEvent(on::MouseEvent),
PointerEvent(Rc<dyn on::PointerEvent>), PointerEvent(on::PointerEvent),
// Whenever a task is ready (complete) Dioxus produces this "FiberEvent" // Whenever a task is ready (complete) Dioxus produces this "FiberEvent"
FiberEvent { task_id: u16 }, FiberEvent { task_id: u16 },
@@ -75,12 +75,21 @@ pub mod on {
use super::VirtualEvent; use super::VirtualEvent;
macro_rules! event_directory { 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>( pub fn $name<'a>(
c: &'_ NodeFactory<'a>, c: &'_ NodeFactory<'a>,
callback: impl Fn(Rc<dyn $eventdata>) + 'a, callback: impl Fn($wrapper) + 'a,
) -> Listener<'a> { ) -> Listener<'a> {
let bump = &c.bump(); let bump = &c.bump();
Listener { Listener {
@@ -88,7 +97,7 @@ pub mod on {
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())), mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
scope: c.scope_ref.arena_idx, scope: c.scope_ref.arena_idx,
callback: bump.alloc(move |evt: VirtualEvent| match evt { 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!") _ => unreachable!("Downcasted VirtualEvent to wrong event type - this is an internal bug!")
}), }),
} }
@@ -99,36 +108,36 @@ pub mod on {
} }
event_directory! { event_directory! {
ClipboardEvent: [copy cut paste]; ClipboardEventInner(ClipboardEvent): [copy cut paste];
CompositionEvent: [compositionend compositionstart compositionupdate]; CompositionEventInner(CompositionEvent): [compositionend compositionstart compositionupdate];
KeyboardEvent: [keydown keypress keyup]; KeyboardEventInner(KeyboardEvent): [keydown keypress keyup];
FocusEvent: [focus blur]; FocusEventInner(FocusEvent): [focus blur];
FormEvent: [change input invalid reset submit]; FormEventInner(FormEvent): [change input invalid reset submit];
MouseEvent: [ MouseEventInner(MouseEvent): [
click contextmenu doubleclick drag dragend dragenter dragexit click contextmenu doubleclick drag dragend dragenter dragexit
dragleave dragover dragstart drop mousedown mouseenter mouseleave dragleave dragover dragstart drop mousedown mouseenter mouseleave
mousemove mouseout mouseover mouseup mousemove mouseout mouseover mouseup
]; ];
PointerEvent: [ PointerEventInner(PointerEvent): [
pointerdown pointermove pointerup pointercancel gotpointercapture pointerdown pointermove pointerup pointercancel gotpointercapture
lostpointercapture pointerenter pointerleave pointerover pointerout lostpointercapture pointerenter pointerleave pointerover pointerout
]; ];
SelectionEvent: [select]; SelectionEventInner(SelectionEvent): [select];
TouchEvent: [touchcancel touchend touchmove touchstart]; TouchEventInner(TouchEvent): [touchcancel touchend touchmove touchstart];
UIEvent: [scroll]; UIEventInner(UIEvent): [scroll];
WheelEvent: [wheel]; WheelEventInner(WheelEvent): [wheel];
MediaEvent: [ MediaEventInner(MediaEvent): [
abort canplay canplaythrough durationchange emptied encrypted abort canplay canplaythrough durationchange emptied encrypted
ended error loadeddata loadedmetadata loadstart pause play ended error loadeddata loadedmetadata loadstart pause play
playing progress ratechange seeked seeking stalled suspend playing progress ratechange seeked seeking stalled suspend
timeupdate volumechange waiting timeupdate volumechange waiting
]; ];
AnimationEvent: [animationstart animationend animationiteration]; AnimationEventInner(AnimationEvent): [animationstart animationend animationiteration];
TransitionEvent: [transitionend]; TransitionEventInner(TransitionEvent): [transitionend];
ToggleEvent: [toggle]; ToggleEventInner(ToggleEvent): [toggle];
} }
trait GenericEvent { pub trait GenericEventInner {
/// Returns whether or not a specific event is a bubbling event /// Returns whether or not a specific event is a bubbling event
fn bubbles(&self) -> bool; fn bubbles(&self) -> bool;
/// Sets or returns whether the event should propagate up the hierarchy or not /// 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; fn time_stamp(&self) -> usize;
} }
pub trait ClipboardEvent: Debug { pub trait ClipboardEventInner: Debug + GenericEventInner {
// DOMDataTransfer clipboardData // DOMDataTransfer clipboardData
} }
pub trait CompositionEvent: Debug { pub trait CompositionEventInner: Debug {
fn data(&self) -> String; fn data(&self) -> String;
} }
pub trait KeyboardEvent: Debug { pub trait KeyboardEventInner: Debug {
fn char_code(&self) -> usize; fn char_code(&self) -> usize;
fn ctrl_key(&self) -> bool; fn ctrl_key(&self) -> bool;
fn key(&self) -> String; fn key(&self) -> String;
@@ -181,15 +190,15 @@ pub mod on {
fn get_modifier_state(&self, key_code: usize) -> bool; fn get_modifier_state(&self, key_code: usize) -> bool;
} }
pub trait FocusEvent: Debug { pub trait FocusEventInner: Debug {
/* DOMEventTarget relatedTarget */ /* DOMEventInnerTarget relatedTarget */
} }
pub trait FormEvent: Debug { pub trait FormEventInner: Debug {
fn value(&self) -> String; fn value(&self) -> String;
} }
pub trait MouseEvent: Debug { pub trait MouseEventInner: Debug {
fn alt_key(&self) -> bool; fn alt_key(&self) -> bool;
fn button(&self) -> i16; fn button(&self) -> i16;
fn buttons(&self) -> u16; fn buttons(&self) -> u16;
@@ -205,7 +214,7 @@ pub mod on {
fn get_modifier_state(&self, key_code: &str) -> bool; fn get_modifier_state(&self, key_code: &str) -> bool;
} }
pub trait PointerEvent: Debug { pub trait PointerEventInner: Debug {
// Mouse only // Mouse only
fn alt_key(&self) -> bool; fn alt_key(&self) -> bool;
fn button(&self) -> usize; fn button(&self) -> usize;
@@ -232,9 +241,9 @@ pub mod on {
fn is_primary(&self) -> bool; 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 alt_key(&self) -> bool;
fn ctrl_key(&self) -> bool; fn ctrl_key(&self) -> bool;
fn meta_key(&self) -> bool; fn meta_key(&self) -> bool;
@@ -245,37 +254,37 @@ pub mod on {
// touches: DOMTouchList, // touches: DOMTouchList,
} }
pub trait UIEvent: Debug { pub trait UIEventInner: Debug {
// DOMAbstractView view // DOMAbstractView view
fn detail(&self) -> i32; fn detail(&self) -> i32;
} }
pub trait WheelEvent: Debug { pub trait WheelEventInner: Debug {
fn delta_mode(&self) -> i32; fn delta_mode(&self) -> i32;
fn delta_x(&self) -> i32; fn delta_x(&self) -> i32;
fn delta_y(&self) -> i32; fn delta_y(&self) -> i32;
fn delta_z(&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 // load error
} }
pub trait AnimationEvent: Debug { pub trait AnimationEventInner: Debug {
fn animation_name(&self) -> String; fn animation_name(&self) -> String;
fn pseudo_element(&self) -> String; fn pseudo_element(&self) -> String;
fn elapsed_time(&self) -> f32; fn elapsed_time(&self) -> f32;
} }
pub trait TransitionEvent: Debug { pub trait TransitionEventInner: Debug {
fn property_name(&self) -> String; fn property_name(&self) -> String;
fn pseudo_element(&self) -> String; fn pseudo_element(&self) -> String;
fn elapsed_time(&self) -> f32; fn elapsed_time(&self) -> f32;
} }
pub trait ToggleEvent: Debug {} pub trait ToggleEventInner: Debug {}
} }
mod tests { mod tests {
+19
View File
@@ -768,6 +768,25 @@ impl<'a> NodeFactory<'a> {
> { > {
ElementBuilder::new(self, tag_name) 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; use std::fmt::Debug;
+5
View File
@@ -297,6 +297,11 @@ impl<'a> NodeKey<'a> {
pub fn new(key: &'a str) -> Self { pub fn new(key: &'a str) -> Self {
NodeKey(Some(key)) NodeKey(Some(key))
} }
#[inline]
pub fn new_opt(key: Option<&'a str>) -> Self {
NodeKey(key)
}
} }
// ============================== // ==============================