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]
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
View File
@@ -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 |
-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
/// -----------------------------------
///
/// 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
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 {
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
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() {
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() {}
+36 -23
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
// ==============================