From ddfa2bac3f598523e8d5508e7d99a7ff6fa11726 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 13:28:53 -0500 Subject: [PATCH 1/8] publish: bump core version --- packages/core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 21724e005..cd96fca04 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-core" -version = "0.1.3" +version = "0.1.4" authors = ["Jonathan Kelley"] edition = "2018" description = "Core functionality for Dioxus - a concurrent renderer-agnostic Virtual DOM for interactive user experiences" From 18f1fa463780885ec7281bf4a2256d3fecc146ea Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 13:30:07 -0500 Subject: [PATCH 2/8] publish: bump html crate --- packages/html/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 6083203df..672199a1e 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-html" -version = "0.1.0" +version = "0.1.1" authors = ["Jonathan Kelley"] edition = "2018" description = "HTML Element pack for Dioxus - a concurrent renderer-agnostic Virtual DOM for interactive user experiences" @@ -12,9 +12,9 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version = "^0.1.3" } +dioxus-core = { path = "../core", version = "^0.1.4" } serde = { version = "1", features = ["derive"], optional = true } -serde_repr = { version = "0.1.7", optional = true } +serde_repr = { version = "0.1", optional = true } [features] From 42c6d1772b71b061aed1987819df1eaf5e951bde Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 13:30:48 -0500 Subject: [PATCH 3/8] publish: fix documentation page --- packages/html/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 672199a1e..b00ae38f4 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -7,7 +7,7 @@ description = "HTML Element pack for Dioxus - a concurrent renderer-agnostic Vir license = "MIT/Apache-2.0" repository = "https://github.com/DioxusLabs/dioxus/" homepage = "https://dioxuslabs.com" -documentation = "https://dioxuslabs.com" +documentation = "https://docs.rs/dioxus" keywords = ["dom", "ui", "gui", "react", "wasm"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 639f9f53224a1a8a6699d6bbb5f46f328f645031 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 13:31:52 -0500 Subject: [PATCH 4/8] publish: bump macro crate --- packages/core-macro/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index dee8b0a36..e3179510e 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-core-macro" -version = "0.1.2" +version = "0.1.3" authors = ["Jonathan Kelley"] edition = "2021" description = "Core macro for Dioxus Virtual DOM" From 2d58d380bef83f799c81738a0a7b012e45cdb9b7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 13:37:25 -0500 Subject: [PATCH 5/8] publish: get web ready gto publish --- packages/web/Cargo.toml | 46 +++---- packages/web/examples/async.rs | 29 ---- packages/web/examples/js_bench.rs | 221 ------------------------------ packages/web/examples/simple.rs | 44 ------ packages/web/examples/suspense.rs | 41 ------ 5 files changed, 17 insertions(+), 364 deletions(-) delete mode 100644 packages/web/examples/async.rs delete mode 100644 packages/web/examples/js_bench.rs delete mode 100644 packages/web/examples/simple.rs delete mode 100644 packages/web/examples/suspense.rs diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index cb540dae5..87521e35e 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dioxus-web" -version = "0.0.0" +version = "0.0.1" authors = ["Jonathan Kelley"] edition = "2018" description = "Dioxus VirtualDOM renderer for the web browser using websys" @@ -9,20 +9,12 @@ repository = "https://github.com/DioxusLabs/dioxus/" homepage = "https://dioxuslabs.com" documentation = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react", "wasm"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version ="^0.1.3"} -dioxus-html = { path = "../html" } +dioxus-core = { path = "../core", version = "^0.1.4" } +dioxus-html = { path = "../html", version = "^0.1.1" } js-sys = "0.3" -# wasm-bindgen-shared = { path = "../../../Tinkering/wasm-bindgen/crates/shared" } -# wasm-bindgen-macro-support = { path = "../../../Tinkering/wasm-bindgen/crates/macro-support" } -# wasm-bindgen = { features = [ - - wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] } -# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] } -# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] } lazy_static = "1.4.0" wasm-bindgen-futures = "0.4.20" log = { version = "0.4.14", features = ["release_max_level_off"] } @@ -78,23 +70,19 @@ features = [ ] -[lib] -crate-type = ["cdylib", "rlib"] +# [lib] +# crate-type = ["cdylib", "rlib"] -[dev-dependencies] -im-rc = "15.0.0" -separator = "0.4.1" -uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] } -serde = { version = "1.0.126", features = ["derive"] } -reqwest = { version = "0.11", features = ["json"] } -dioxus-hooks = { path = "../hooks" } -dioxus-core-macro = { path = "../core-macro" } -rand = { version = "0.8.4", features = ["small_rng"] } +# [dev-dependencies] +# im-rc = "15.0.0" +# separator = "0.4.1" +# uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] } +# serde = { version = "1.0.126", features = ["derive"] } +# reqwest = { version = "0.11", features = ["json"] } +# dioxus-hooks = { path = "../hooks" } +# dioxus-core-macro = { path = "../core-macro" } +# rand = { version = "0.8.4", features = ["small_rng"] } -[dev-dependencies.getrandom] -version = "0.2" -features = ["js"] - -# surf = { version = "2.3.1", default-features = false, features = [ -# "wasm-client", -# ] } +# [dev-dependencies.getrandom] +# version = "0.2" +# features = ["js"] diff --git a/packages/web/examples/async.rs b/packages/web/examples/async.rs deleted file mode 100644 index c045ca5e4..000000000 --- a/packages/web/examples/async.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Example: README.md showcase -//! -//! The example from the README.md. - -use dioxus::prelude::*; -use dioxus_core as dioxus; -use dioxus_core_macro::*; -use dioxus_hooks::use_state; -use dioxus_html as dioxus_elements; -use dioxus_web; -use gloo_timers::future::TimeoutFuture; - -fn main() { - dioxus_web::launch(App); -} - -static App: Component = |cx| { - let mut count = use_state(&cx, || 0); - - cx.push_future(|| async move { - TimeoutFuture::new(100).await; - count += 1; - }); - - rsx!(cx, div { - h3 { "High-Five counter: {count}" } - button { onclick: move |_| count.set(0), "Reset!" } - }) -}; diff --git a/packages/web/examples/js_bench.rs b/packages/web/examples/js_bench.rs deleted file mode 100644 index d9ddc7e1c..000000000 --- a/packages/web/examples/js_bench.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::cell::Cell; - -use dioxus::prelude::*; -use dioxus_core as dioxus; -use dioxus_core_macro::*; -use dioxus_hooks::{use_ref, use_state}; -use dioxus_html as dioxus_elements; -use dioxus_web; -use rand::prelude::*; - -fn main() { - console_error_panic_hook::set_once(); - if cfg!(debug_assertions) { - wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - log::debug!("hello world"); - } - - for a in ADJECTIVES { - wasm_bindgen::intern(*a); - } - for a in COLOURS { - wasm_bindgen::intern(*a); - } - for a in NOUNS { - wasm_bindgen::intern(*a); - } - for a in [ - "container", - "jumbotron", - "row", - "Dioxus", - "col-md-6", - "col-md-1", - "Create 1,000 rows", - "run", - "Create 10,000 rows", - "runlots", - "Append 1,000 rows", - "add", - "Update every 10th row", - "update", - "Clear", - "clear", - "Swap rows", - "swaprows", - "preloadicon glyphicon glyphicon-remove", // - "aria-hidden", - "onclick", - "true", - "false", - "danger", - "type", - "id", - "class", - "glyphicon glyphicon-remove remove", - "dioxus-id", - "dioxus-event-click", - "dioxus", - "click", - "1.10", - "lbl", - "remove", - "dioxus-event", - "col-sm-6 smallpad", - "btn btn-primary btn-block", - "", - " ", - ] { - wasm_bindgen::intern(a); - } - for x in 0..100_000 { - wasm_bindgen::intern(&x.to_string()); - } - - dioxus_web::launch(App); -} - -#[derive(Clone, PartialEq, Copy)] -struct Label { - key: usize, - labels: [&'static str; 3], -} - -static mut Counter: Cell = Cell::new(1); - -impl Label { - fn new_list(num: usize) -> Vec { - let mut rng = SmallRng::from_entropy(); - let mut labels = Vec::with_capacity(num); - - let offset = unsafe { Counter.get() }; - unsafe { Counter.set(offset + num) }; - - for k in offset..(offset + num) { - labels.push(Label { - key: k, - labels: [ - ADJECTIVES.choose(&mut rng).unwrap(), - COLOURS.choose(&mut rng).unwrap(), - NOUNS.choose(&mut rng).unwrap(), - ], - }); - } - - labels - } -} - -static App: Component = |cx| { - let mut items = use_ref(&cx, || vec![]); - let mut selected = use_state(&cx, || None); - - cx.render(rsx! { - div { class: "container" - div { class: "jumbotron" - div { class: "row" - div { class: "col-md-6", h1 { "Dioxus" } } - div { class: "col-md-6" - div { class: "row" - ActionButton { name: "Create 1,000 rows", id: "run", - onclick: move || items.set(Label::new_list(1_000)), - } - ActionButton { name: "Create 10,000 rows", id: "runlots", - onclick: move || items.set(Label::new_list(10_000)), - } - ActionButton { name: "Append 1,000 rows", id: "add", - onclick: move || items.write().extend(Label::new_list(1_000)), - } - ActionButton { name: "Update every 10th row", id: "update", - onclick: move || items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"), - } - ActionButton { name: "Clear", id: "clear", - onclick: move || items.write().clear(), - } - ActionButton { name: "Swap Rows", id: "swaprows", - onclick: move || items.write().swap(0, 998), - } - } - } - } - } - table { class: "table table-hover table-striped test-data" - tbody { id: "tbody" - {items.read().iter().enumerate().map(|(id, item)| { - let [adj, col, noun] = item.labels; - let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""}; - rsx!(tr { - class: "{is_in_danger}", - key: "{id}", - td { class:"col-md-1" } - td { class:"col-md-1", "{item.key}" } - td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), - a { class: "lbl", "{adj} {col} {noun}" } - } - td { class: "col-md-1" - a { class: "remove", onclick: move |_| { items.write().remove(id); }, - span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } - } - } - td { class: "col-md-6" } - }) - })} - } - } - span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" } - } - }) -}; - -#[derive(Props)] -struct ActionButtonProps<'a> { - name: &'static str, - id: &'static str, - onclick: &'a dyn Fn(), -} - -fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element { - rsx!(cx, div { class: "col-sm-6 smallpad" - button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (cx.props.onclick)(), - "{cx.props.name}" - } - }) -} - -static ADJECTIVES: &[&str] = &[ - "pretty", - "large", - "big", - "small", - "tall", - "short", - "long", - "handsome", - "plain", - "quaint", - "clean", - "elegant", - "easy", - "angry", - "crazy", - "helpful", - "mushy", - "odd", - "unsightly", - "adorable", - "important", - "inexpensive", - "cheap", - "expensive", - "fancy", -]; - -static COLOURS: &[&str] = &[ - "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", - "orange", -]; - -static NOUNS: &[&str] = &[ - "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", - "pizza", "mouse", "keyboard", -]; diff --git a/packages/web/examples/simple.rs b/packages/web/examples/simple.rs deleted file mode 100644 index d3f2f078b..000000000 --- a/packages/web/examples/simple.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Example: README.md showcase -//! -//! The example from the README.md. - -use dioxus::prelude::*; -use dioxus_core as dioxus; -use dioxus_core_macro::*; -use dioxus_hooks::use_state; -use dioxus_html as dioxus_elements; - -fn main() { - wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - dioxus_web::launch(App); -} - -static App: Component = |cx| { - let show = use_state(&cx, || true); - - let inner = match *show { - true => { - rsx!( div { - "hello world" - }) - } - false => { - rsx!( div { - // h1 { - "bello world" - // } - }) - } - }; - - rsx!(cx, div { - button { - "toggle" - onclick: move |_| { - let cur = *show; - show.set(!cur); - } - } - {inner} - }) -}; diff --git a/packages/web/examples/suspense.rs b/packages/web/examples/suspense.rs deleted file mode 100644 index 30a15a123..000000000 --- a/packages/web/examples/suspense.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(non_upper_case_globals)] - -//! Example: README.md showcase -//! -//! The example from the README.md. - -use dioxus::prelude::*; -use dioxus_core as dioxus; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; -use dioxus_web; - -fn main() { - dioxus_web::launch(App); -} - -static App: Component = |cx| { - todo!("suspense is broken") - // let doggo = suspend(|| async move { - // #[derive(serde::Deserialize)] - // struct Doggo { - // message: String, - // } - - // let src = reqwest::get("https://dog.ceo/api/breeds/image/random") - // .await - // .expect("Failed to fetch doggo") - // .json::() - // .await - // .expect("Failed to parse doggo") - // .message; - - // rsx!(cx, img { src: "{src}" }) - // }); - - // rsx!(cx, div { - // h1 {"One doggo coming right up"} - // button { onclick: move |_| cx.needs_update(), "Get a new doggo" } - // {doggo} - // }) -}; From 14961023f927b3a8bde83cfc7883aa8bfcca9e85 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 29 Dec 2021 21:28:28 -0500 Subject: [PATCH 6/8] wip: polish some more things --- Cargo.toml | 22 ++- examples/README.md | 16 +- examples/async.rs | 12 +- examples/borrowed.rs | 2 +- examples/calculator.rs | 194 ++++++++++--------- examples/core/jsframework.rs | 2 +- examples/desktop/todomvc.rs | 5 - examples/file_explorer.rs | 1 - examples/hello_world.rs | 2 +- examples/readme.rs | 7 +- examples/tasks.rs | 18 +- examples/webview_web.rs | 13 +- packages/core-macro/src/lib.rs | 2 +- packages/core/Cargo.toml | 1 - packages/core/README.md | 2 +- packages/core/examples/component_children.rs | 2 +- packages/core/examples/works.rs | 2 +- packages/core/src/nodes.rs | 12 +- packages/core/src/scopes.rs | 2 +- packages/core/src/virtual_dom.rs | 18 +- packages/core/tests/borrowedstate.rs | 2 +- packages/core/tests/miri_stress.rs | 14 +- packages/desktop/Cargo.toml | 1 - packages/desktop/examples/async.rs | 24 +-- packages/hooks/README.md | 47 +---- packages/hooks/src/lib.rs | 46 ++++- packages/hooks/src/usestate.rs | 134 ++----------- packages/html/Cargo.toml | 2 - packages/liveview/README.md | 2 +- packages/mobile/Cargo.toml | 5 +- packages/mobile/src/lib.rs | 5 - src/lib.rs | 18 +- 32 files changed, 265 insertions(+), 370 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 44750d463..01d8c0370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,18 +11,19 @@ documentation = "https://dioxuslabs.com" keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] -dioxus-core = { path = "./packages/core", version = "^0.1.3" } -dioxus-html = { path = "./packages/html", optional = true } -dioxus-core-macro = { path = "./packages/core-macro", optional = true } +dioxus-core = { path = "./packages/core", version = "^0.1.4" } +dioxus-html = { path = "./packages/html", version = "^0.1.1", optional = true } +dioxus-core-macro = { path = "./packages/core-macro", version = "^0.1.3", optional = true } dioxus-hooks = { path = "./packages/hooks", optional = true } -dioxus-ssr = { path = "./packages/ssr", optional = true } -dioxus-web = { path = "./packages/web", optional = true } +dioxus-web = { path = "./packages/web", version = "^0.0.1", optional = true } dioxus-desktop = { path = "./packages/desktop", optional = true } +dioxus-ssr = { path = "./packages/ssr", optional = true } + dioxus-router = { path = "./packages/router", optional = true } -dioxus-mobile = { path = "./packages/mobile", optional = true } -dioxus-liveview = { path = "./packages/liveview", optional = true } +# dioxus-mobile = { path = "./packages/mobile", optional = true } +# dioxus-liveview = { path = "./packages/liveview", optional = true } [features] default = ["macro", "hooks", "html"] @@ -31,11 +32,13 @@ macro = ["dioxus-core-macro"] hooks = ["dioxus-hooks"] html = ["dioxus-html"] router = ["dioxus-router"] -liveview = ["dioxus-liveview"] ssr = ["dioxus-ssr"] web = ["dioxus-web", "dioxus-router/web"] desktop = ["dioxus-desktop", "dioxus-router/desktop"] -mobile = ["dioxus-mobile"] + + +# mobile = ["dioxus-mobile"] +# liveview = ["dioxus-liveview"] [workspace] @@ -60,7 +63,6 @@ im-rc = "15.0.0" fxhash = "0.2.1" anyhow = "1.0.51" serde_json = "1.0.73" -simple_logger = "1.16.0" [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] argh = "0.1.7" diff --git a/examples/README.md b/examples/README.md index eabb0f57d..eb0037683 100644 --- a/examples/README.md +++ b/examples/README.md @@ -64,7 +64,7 @@ Here's what a few common tasks look like in Dioxus: Nested components with children and internal state: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { cx.render(rsx!( Toggle { "Toggle me" } )) } @@ -86,7 +86,7 @@ fn Toggle(cx: Scope) -> Element { Controlled inputs: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let value = use_state(&cx, String::new); cx.render(rsx!( input { @@ -100,7 +100,7 @@ fn App(cx: Scope<()>) -> Element { Lists and Conditional rendering: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let list = (0..10).map(|i| { rsx!(li { key: "{i}", "Value: {i}" }) }); @@ -128,7 +128,7 @@ static App: Component = |cx, _| rsx!(cx, div {"hello world!"}); Borrowed prop contents: ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let name = use_state(&cx, || String::from("example")); rsx!(cx, Child { title: name.as_str() }) } @@ -145,12 +145,12 @@ Global State ```rust struct GlobalState { name: String } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { use_provide_shared_state(cx, || GlobalState { name: String::from("Toby") }) rsx!(cx, Leaf {}) } -fn Leaf(cx: Scope<()>) -> Element { +fn Leaf(cx: Scope) -> Element { let state = use_consume_shared_state::(cx)?; rsx!(cx, "Hello {state.name}") } @@ -166,7 +166,7 @@ enum Route { Post(id) } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let route = use_router(cx, Route::parse); cx.render(rsx!(div { {match route { @@ -179,7 +179,7 @@ fn App(cx: Scope<()>) -> Element { Suspense ```rust -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let doggo = use_suspense(cx, || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::().await.unwrap() }, |response| cx.render(rsx!( img { src: "{response.message}" })) diff --git a/examples/async.rs b/examples/async.rs index c473ead72..e399ea016 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -8,19 +8,19 @@ use gloo_timers::future::TimeoutFuture; #[tokio::main] async fn main() { - dioxus::desktop::launch(App); + dioxus::desktop::launch(app); } -pub static App: Component = |cx| { +fn app(cx: Scope) -> Element { let count = use_state(&cx, || 0); - let mut direction = use_state(&cx, || 1); + let direction = use_state(&cx, || 1); let (async_count, dir) = (count.for_async(), *direction); let task = use_coroutine(&cx, move || async move { loop { TimeoutFuture::new(250).await; - *async_count.get_mut() += dir; + *async_count.modify() += dir; } }); @@ -37,9 +37,9 @@ pub static App: Component = |cx| { button { "Switch counting direcion" onclick: move |_| { - direction *= -1; + *direction.modify() *= -1; task.restart(); } } }) -}; +} diff --git a/examples/borrowed.rs b/examples/borrowed.rs index 3b05b6f9f..29c502e13 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -20,7 +20,7 @@ fn main() { dioxus::desktop::launch(App); } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let text: &mut Vec = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); let first = text.get_mut(0).unwrap(); diff --git a/examples/calculator.rs b/examples/calculator.rs index c7490a1b4..c3814c088 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + /* This example is a simple iOS-style calculator. This particular example can run any platform - Web, Mobile, Desktop. This calculator version uses React-style state management. All state is held as individual use_states. @@ -10,10 +12,10 @@ use dioxus::prelude::*; use separator::Separatable; fn main() { - dioxus::desktop::launch(APP); + dioxus::desktop::launch(app); } -static APP: Component = |cx| { +fn app(cx: Scope) -> Element { let cur_val = use_state(&cx, || 0.0_f64); let operator = use_state(&cx, || None as Option<&'static str>); let display_value = use_state(&cx, || String::from("")); @@ -21,109 +23,111 @@ static APP: Component = |cx| { let toggle_percent = move |_| todo!(); let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str()); - rsx!(cx, div { - class: "calculator", - onkeydown: move |evt| match evt.key_code { - KeyCode::Add => operator.set(Some("+")), - KeyCode::Subtract => operator.set(Some("-")), - KeyCode::Divide => operator.set(Some("/")), - KeyCode::Multiply => operator.set(Some("*")), - KeyCode::Num0 => input_digit(0), - KeyCode::Num1 => input_digit(1), - KeyCode::Num2 => input_digit(2), - KeyCode::Num3 => input_digit(3), - KeyCode::Num4 => input_digit(4), - KeyCode::Num5 => input_digit(5), - KeyCode::Num6 => input_digit(6), - KeyCode::Num7 => input_digit(7), - KeyCode::Num8 => input_digit(8), - KeyCode::Num9 => input_digit(9), - KeyCode::Backspace => { - if !display_value.as_str().eq("0") { - display_value.modify().pop(); - } - } - _ => {} - } - div { class: "calculator-display", {[format_args!("{}", cur_val.separated_string())]} } - div { class: "input-keys" - div { class: "function-keys" - CalculatorKey { - {[if display_value == "0" { "C" } else { "AC" }]} - name: "key-clear", - onclick: move |_| { - display_value.set("0".to_string()); - if display_value != "0" { - operator.set(None); - cur_val.set(0.0); - } + cx.render(rsx!( + div { class: "calculator", + onkeydown: move |evt| match evt.key_code { + KeyCode::Add => operator.set(Some("+")), + KeyCode::Subtract => operator.set(Some("-")), + KeyCode::Divide => operator.set(Some("/")), + KeyCode::Multiply => operator.set(Some("*")), + KeyCode::Num0 => input_digit(0), + KeyCode::Num1 => input_digit(1), + KeyCode::Num2 => input_digit(2), + KeyCode::Num3 => input_digit(3), + KeyCode::Num4 => input_digit(4), + KeyCode::Num5 => input_digit(5), + KeyCode::Num6 => input_digit(6), + KeyCode::Num7 => input_digit(7), + KeyCode::Num8 => input_digit(8), + KeyCode::Num9 => input_digit(9), + KeyCode::Backspace => { + if !display_value.as_str().eq("0") { + display_value.modify().pop(); } } - CalculatorKey { - "±" - name: "key-sign", - onclick: move |_| { - if display_value.starts_with("-") { - display_value.set(display_value.trim_start_matches("-").to_string()) - } else { - display_value.set(format!("-{}", *display_value)) - } - }, - } - CalculatorKey { - "%" - onclick: {toggle_percent} - name: "key-percent", - } + _ => {} } - div { class: "digit-keys" - CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" } - CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "●" } + div { class: "calculator-display", {[cur_val.separated_string()]} } + div { class: "input-keys" + div { class: "function-keys" + CalculatorKey { + {[if display_value == "0" { "C" } else { "AC" }]} + name: "key-clear", + onclick: move |_| { + display_value.set("0".to_string()); + if display_value != "0" { + operator.set(None); + cur_val.set(0.0); + } + } + } + CalculatorKey { + "±" + name: "key-sign", + onclick: move |_| { + if display_value.starts_with("-") { + display_value.set(display_value.trim_start_matches("-").to_string()) + } else { + display_value.set(format!("-{}", *display_value)) + } + }, + } + CalculatorKey { + "%" + onclick: {toggle_percent} + name: "key-percent", + } + } + div { class: "digit-keys" + CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" } + CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "●" } - {(1..9).map(|k| rsx!{ - CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } - })} - } - div { class: "operator-keys" - CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some("/")) "÷" } - CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "×" } - CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "−" } - CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" } - CalculatorKey { - "=" - name: "key-equals", - onclick: move |_| { - if let Some(op) = operator.as_ref() { - let rhs = display_value.parse::().unwrap(); - let new_val = match *op { - "+" => *cur_val + rhs, - "-" => *cur_val - rhs, - "*" => *cur_val * rhs, - "/" => *cur_val / rhs, - _ => unreachable!(), - }; - cur_val.set(new_val); - display_value.set(new_val.to_string()); - operator.set(None); - } - }, + {(1..9).map(|k| rsx!{ + CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } + })} + } + div { class: "operator-keys" + CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some("/")) "÷" } + CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "×" } + CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "−" } + CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" } + CalculatorKey { + "=" + name: "key-equals", + onclick: move |_| { + if let Some(op) = operator.as_ref() { + let rhs = display_value.parse::().unwrap(); + let new_val = match *op { + "+" => *cur_val + rhs, + "-" => *cur_val - rhs, + "*" => *cur_val * rhs, + "/" => *cur_val / rhs, + _ => unreachable!(), + }; + cur_val.set(new_val); + display_value.set(new_val.to_string()); + operator.set(None); + } + }, + } } } } - }) -}; + )) +} -#[derive(Props)] -struct CalculatorKeyProps<'a> { +#[inline_props] +fn CalculatorKey<'a>( + cx: Scope, name: &'static str, onclick: &'a dyn Fn(Arc), children: Element<'a>, -} - -fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element { - rsx!(cx, button { - class: "calculator-key {cx.props.name}" - onclick: {cx.props.onclick} - {&cx.props.children} +) -> Element { + cx.render(rsx! { + button { + class: "calculator-key {name}" + onclick: {onclick} + {children} + } }) } diff --git a/examples/core/jsframework.rs b/examples/core/jsframework.rs index 15c25ec8d..f9fa9c431 100644 --- a/examples/core/jsframework.rs +++ b/examples/core/jsframework.rs @@ -15,7 +15,7 @@ fn main() { assert!(g.edits.len() > 1); } -fn App((cx, props): Scope<()>) -> Element { +fn App((cx, props): Scope) -> Element { let mut rng = SmallRng::from_entropy(); let rows = (0..10_000_usize).map(|f| { let label = Label::new(&mut rng); diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs index ff982a585..e70ed1806 100644 --- a/examples/desktop/todomvc.rs +++ b/examples/desktop/todomvc.rs @@ -5,15 +5,10 @@ use dioxus_core::prelude::*; use dioxus_core_macro::*; use dioxus_hooks::*; use dioxus_html as dioxus_elements; -use simple_logger::SimpleLogger; use std::collections::HashMap; fn main() { - if cfg!(debug_assertions) { - SimpleLogger::new().init().unwrap(); - } - dioxus_desktop::launch(App) } diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index a2f168d6b..05f5c4ca7 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -8,7 +8,6 @@ use dioxus::prelude::*; fn main() { - // simple_logger::init_with_level(log::Level::Debug); dioxus::desktop::launch_cfg(App, |c| { c.with_window(|w| { w.with_resizable(true).with_inner_size( diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 24f54883a..ab7bfffa3 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -4,7 +4,7 @@ fn main() { dioxus::desktop::launch(app); } -fn app(cx: Scope<()>) -> Element { +fn app(cx: Scope) -> Element { cx.render(rsx! ( div { "Hello, world!" } )) diff --git a/examples/readme.rs b/examples/readme.rs index c3f0a3de7..9f0a288e3 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -3,18 +3,19 @@ //! The example from the README.md. use dioxus::prelude::*; + fn main() { dioxus::desktop::launch(App); } static App: Component = |cx| { - let mut count = use_state(&cx, || 0); + let count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } + button { onclick: move |_| *count.modify() += 1, "Up high!" } + button { onclick: move |_| *count.modify() -= 1, "Down low!" } } }) }; diff --git a/examples/tasks.rs b/examples/tasks.rs index 0db459786..a378c24a6 100644 --- a/examples/tasks.rs +++ b/examples/tasks.rs @@ -2,26 +2,30 @@ //! //! The example from the README.md. +use dioxus::prelude::*; use std::time::Duration; -use dioxus::prelude::*; fn main() { dioxus::desktop::launch(app); } -fn app(cx: Scope<()>) -> Element { - let mut count = use_state(&cx, || 0); +fn app(cx: Scope) -> Element { + let count = use_state(&cx, || 0); - cx.push_future(|| async move { - tokio::time::sleep(Duration::from_millis(100)).await; - count += 1; + use_future(&cx, || { + for_async![count]; + async move { + while let _ = tokio::time::sleep(Duration::from_millis(1000)).await { + *count.modify() += 1; + } + } }); cx.render(rsx! { div { h1 { "High-Five counter: {count}" } button { - onclick: move |_| count +=1 , + onclick: move |_| *count.modify() += 1, "Click me!" } } diff --git a/examples/webview_web.rs b/examples/webview_web.rs index 367094b2c..5a7df845d 100644 --- a/examples/webview_web.rs +++ b/examples/webview_web.rs @@ -1,4 +1,3 @@ -#![allow(non_upper_case_globals, non_snake_case)] //! Example: Webview Renderer //! ------------------------- //! @@ -13,17 +12,17 @@ use dioxus::prelude::*; fn main() { - dioxus::web::launch(App); + dioxus::desktop::launch(app); } -static App: Component = |cx| { - let mut count = use_state(&cx, || 0); +fn app(cx: Scope) -> Element { + let count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "Hifive counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } + button { onclick: move |_| *count.modify() += 1, "Up high!" } + button { onclick: move |_| *count.modify() -= 1, "Down low!" } } }) -}; +} diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index a196441b8..2d17f4bbc 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -161,7 +161,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// pub struct BallerProps {} /// /// /// This component totally balls -/// pub fn Baller(cx: Scope<()>) -> DomTree { +/// pub fn Baller(cx: Scope) -> DomTree { /// todo!() /// } /// } diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index cd96fca04..53d169760 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -50,7 +50,6 @@ anyhow = "1.0.42" dioxus-html = { path = "../html" } fern = { version = "0.6.0", features = ["colored"] } rand = { version = "0.8.4", features = ["small_rng"] } -simple_logger = "1.13.0" dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" } criterion = "0.3.5" thiserror = "1.0.30" diff --git a/packages/core/README.md b/packages/core/README.md index 672a1dc78..87d724415 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -6,7 +6,7 @@ To build new apps with Dioxus or to extend the ecosystem with new hooks or compo ```rust -fn app(cx: Scope<()>) -> Element { +fn app(cx: Scope) -> Element { rsx!(cx, div { "hello world" }) } diff --git a/packages/core/examples/component_children.rs b/packages/core/examples/component_children.rs index b6bbf8a86..73d0750a6 100644 --- a/packages/core/examples/component_children.rs +++ b/packages/core/examples/component_children.rs @@ -11,7 +11,7 @@ fn main() { dbg!(edits); } -fn parent(cx: Scope<()>) -> Element { +fn parent(cx: Scope) -> Element { let value = cx.use_hook(|_| String::new(), |f| f); cx.render(rsx! { diff --git a/packages/core/examples/works.rs b/packages/core/examples/works.rs index f521dcc42..d108b6d07 100644 --- a/packages/core/examples/works.rs +++ b/packages/core/examples/works.rs @@ -9,7 +9,7 @@ fn main() { let _ = VirtualDom::new(parent); } -fn parent(cx: Scope<()>) -> Element { +fn parent(cx: Scope) -> Element { let value = cx.use_hook(|_| String::new(), |f| f); cx.render(rsx! { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index fc0e38f59..2ebd1e250 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -84,7 +84,7 @@ pub enum VNode<'src> { /// # Example /// /// ```rust, ignore - /// fn Example(cx: Scope<()>) -> Element { + /// fn Example(cx: Scope) -> Element { /// ... /// } /// @@ -740,6 +740,12 @@ impl IntoVNode<'_> for &'static str { } } +impl IntoVNode<'_> for String { + fn into_vnode(self, cx: NodeFactory) -> VNode { + cx.text(format_args!("{}", self)) + } +} + impl IntoVNode<'_> for Arguments<'_> { fn into_vnode(self, cx: NodeFactory) -> VNode { cx.text(self) @@ -758,3 +764,7 @@ impl<'a> IntoVNode<'a> for &VNode<'a> { self.decouple() } } + +trait IntoAcceptedVnode<'a> { + fn into_accepted_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>; +} diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 42e16d223..5d35b298d 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -490,7 +490,7 @@ impl ScopeState { /// # Example /// /// ```rust, ignore - /// fn App(cx: Scope<()>) -> Element { + /// fn App(cx: Scope) -> Element { /// rsx!(cx, div { "Subtree {id}"}) /// }; /// ``` diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 09df37bb8..e96527e95 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -80,7 +80,7 @@ use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::A /// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above. /// /// ```rust, ignore -/// fn App(cx: Scope<()>) -> Element { +/// fn App(cx: Scope) -> Element { /// cx.render(rsx!{ /// div { "Hello World" } /// }) @@ -127,7 +127,7 @@ impl VirtualDom { /// /// # Example /// ```rust, ignore - /// fn Example(cx: Scope<()>) -> Element { + /// fn Example(cx: Scope) -> Element { /// cx.render(rsx!( div { "hello world" } )) /// } /// @@ -388,7 +388,7 @@ impl VirtualDom { /// # Example /// /// ```rust, ignore - /// fn App(cx: Scope<()>) -> Element { + /// fn App(cx: Scope) -> Element { /// cx.render(rsx!( div {"hello"} )) /// } /// @@ -544,7 +544,7 @@ impl VirtualDom { /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -564,7 +564,7 @@ impl VirtualDom { /// Useful when needing to render nodes from outside the VirtualDom, such as in a test. /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -586,7 +586,7 @@ impl VirtualDom { /// /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -609,7 +609,7 @@ impl VirtualDom { /// /// /// ```rust - /// fn Base(cx: Scope<()>) -> Element { + /// fn Base(cx: Scope) -> Element { /// rsx!(cx, div {}) /// } /// @@ -753,7 +753,7 @@ pub enum SchedulerMsg { /// /// # Example /// ```rust -/// fn App(cx: Scope<()>) -> Element { +/// fn App(cx: Scope) -> Element { /// rsx!(cx, div { /// onclick: move |_| println!("Clicked!") /// }) @@ -871,7 +871,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> { /// ## Example /// /// ```rust, ignore -/// fn App(cx: Scope<()>) -> Element { +/// fn App(cx: Scope) -> Element { /// cx.render(rsx!{ /// CustomCard { /// h1 {}2 diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index f58e97635..c143c2fe1 100644 --- a/packages/core/tests/borrowedstate.rs +++ b/packages/core/tests/borrowedstate.rs @@ -10,7 +10,7 @@ fn test_borrowed_state() { let _ = VirtualDom::new(Parent); } -fn Parent(cx: Scope<()>) -> Element { +fn Parent(cx: Scope) -> Element { let value = cx.use_hook(|_| String::new(), |f| &*f); cx.render(rsx! { diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index 2091a8e3a..ebf38c7c1 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -28,7 +28,7 @@ fn new_dom(app: Component

, props: P) -> VirtualDom { /// In debug, this should also toss a warning. #[test] fn test_memory_leak() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { let val = cx.use_hook(|_| 0, |f| f); *val += 1; @@ -68,7 +68,7 @@ fn test_memory_leak() { }) } - fn child(cx: Scope<()>) -> Element { + fn child(cx: Scope) -> Element { rsx!(cx, div { "goodbye world" }) } @@ -85,7 +85,7 @@ fn test_memory_leak() { #[test] fn memo_works_properly() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { let val = cx.use_hook(|_| 0, |f| f); *val += 1; @@ -164,7 +164,7 @@ fn free_works_on_root_props() { #[test] fn free_works_on_borrowed() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { cx.render(rsx! { child(a: "alpha", b: "asd".to_string()) }) @@ -203,7 +203,7 @@ fn free_works_on_root_hooks() { } } - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { let name = cx.use_hook(|_| Droppable(String::from("asd")), |f| f); rsx!(cx, div { "{name.0}" }) } @@ -214,7 +214,7 @@ fn free_works_on_root_hooks() { #[test] fn old_props_arent_stale() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { dbg!("rendering parent"); let cnt = cx.use_hook(|_| 0, |f| f); *cnt += 1; @@ -261,7 +261,7 @@ fn old_props_arent_stale() { #[test] fn basic() { - fn app(cx: Scope<()>) -> Element { + fn app(cx: Scope) -> Element { rsx!(cx, div { child(a: "abcdef".to_string()) }) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index a132bc513..d088aa04c 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -37,4 +37,3 @@ tokio_runtime = ["tokio"] [dev-dependencies] dioxus-hooks = { path = "../hooks" } -simple_logger = "1.13.0" diff --git a/packages/desktop/examples/async.rs b/packages/desktop/examples/async.rs index 2c6901f85..056e7b591 100644 --- a/packages/desktop/examples/async.rs +++ b/packages/desktop/examples/async.rs @@ -9,22 +9,18 @@ fn main() { dioxus_desktop::launch(app); } -fn app(cx: Scope<()>) -> Element { +fn app(cx: Scope) -> Element { let count = use_state(&cx, || 0); - // push the futureo on initialization - cx.use_hook( - |_| { - cx.push_future({ - let count = count.for_async(); - async move { - tokio::time::sleep(Duration::from_millis(1000)).await; - *count.get_mut() += 1; - } - }); - }, - |_| {}, - ); + use_future(&cx, || { + let count = count.for_async(); + async move { + loop { + tokio::time::sleep(Duration::from_millis(1000)).await; + *count.modify() += 1; + } + } + }); cx.render(rsx! { div { diff --git a/packages/hooks/README.md b/packages/hooks/README.md index 11e008c44..18133e40b 100644 --- a/packages/hooks/README.md +++ b/packages/hooks/README.md @@ -4,58 +4,19 @@ This crate includes some basic useful hooks for dioxus: - use_state - use_ref -- use_collection -- use_task -- use_signal +- use_future +- use_coroutine ## use_state -The king daddy of state hooks. +The primary mechanism of stored state. You can always use it "normally" with the `split` method: ```rust -// Normal usage: +// Rusty-smart-pointer usage: let value = use_state(&cx, || 10); // "Classic" usage: let (value, set_value) = use_state(&cx, || 0).split(); ``` - -## use_ref - - -## use_rwlock -A multithreaded form of RwLock for use in tasks -```rust -let val = use_rwlock(cx, || 10); -use_task((), || async loop { - *val.write().unwrap() += 1; - async_std::task::delay(Duration::from_ms(1000)).await; -}); -use_task((), || async loop { - *val.write().unwrap() -= 1; - async_std::task::delay(Duration::from_ms(500)).await; -}); -``` - -## use_hashmap -Store a memoized collection with similar semantics to use_state. Comes with a bunch of utility methods to make working with collections easier. Is essentially a wrapper over the immutable hashmap in im-rc. - -```rust -let todos = use_hashmap(cx, |map| map.insert("bob", "bill")); -cx.render(rsx!( - button { onclick: move |_| todos.insert("bob", "bill") - "add random todo" - } -) - -``` - -## use_task - -use_task submits a task to the dioxus task queue to be progressed during Dioxus's async event loop. The task must not return anything - - -## use_signal - diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index 1a38ed172..0050d1779 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -1,5 +1,5 @@ mod usestate; -pub use usestate::{use_state, AsyncUseState, UseState}; +pub use usestate::{use_state, UseState}; mod useref; pub use useref::*; @@ -16,5 +16,45 @@ pub use usefuture::*; mod usesuspense; pub use usesuspense::*; -// mod usemodel; -// pub use usemodel::*; +#[macro_export] +macro_rules! to_owned { + ($($es:ident),+) => {$( + #[allow(unused_mut)] + let mut $es = $es.to_owned(); + )*} +} + +/// Calls `for_async` on the series of paramters. +/// +/// If the type is Clone, then it will be cloned. However, if the type is not `clone` +/// then it must have a `for_async` method for Rust to lower down into. +/// +/// See: how use_state implements `for_async` but *not* through the trait. +#[macro_export] +macro_rules! for_async { + ($($es:ident),+) => {$( + #[allow(unused_mut)] + let mut $es = $es.for_async(); + )*} +} + +/// This is a marker trait that uses decoherence. +/// +/// It is *not* meant for hooks to actually implement, but rather defer to their +/// underlying implementation if they *don't* implement the trait. +/// +/// +pub trait AsyncHook { + type Output; + fn for_async(self) -> Self::Output; +} + +impl AsyncHook for T +where + T: ToOwned, +{ + type Output = T; + fn for_async(self) -> Self::Output { + self + } +} diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 88caea82d..6ef67efcb 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -2,20 +2,9 @@ use dioxus_core::prelude::*; use std::{ cell::{Cell, Ref, RefCell, RefMut}, fmt::{Debug, Display}, - ops::Not, rc::Rc, }; -pub trait UseStateA<'a, T> { - fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T>; -} - -impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> { - fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T> { - use_state(self.scope, initial_state_fn) - } -} - /// Store state between component renders! /// /// ## Dioxus equivalent of useState, designed for Rust @@ -109,6 +98,7 @@ where UseState { inner: self.inner } } } + impl Debug for UseState<'_, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.inner.current_val) @@ -157,21 +147,21 @@ impl<'a, T: 'static> UseState<'a, T> { }) } - pub fn for_async(&self) -> AsyncUseState { - AsyncUseState { - re_render: self.inner.update_callback.clone(), - wip: self.inner.wip.clone(), - inner: self.inner.current_val.clone(), - } + pub fn for_async(self) -> UseState<'static, T> { + todo!() } - pub fn split_for_async(&'a self) -> (&'a Self, AsyncUseState) { - (self, self.for_async()) + pub fn wtih(self, f: impl FnOnce(&mut T)) { + let mut val = self.inner.wip.borrow_mut(); + + if let Some(inner) = val.as_mut() { + f(inner); + } } } impl<'a, T: 'static + ToOwned> UseState<'a, T> { - /// Gain mutable access to the new value via RefMut. + /// Gain mutable access to the new value via [`RefMut`]. /// /// If `modify` is called, then the component will re-render. /// @@ -207,57 +197,10 @@ impl<'a, T> std::ops::Deref for UseState<'a, T> { } } -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -impl<'a, T: Copy + Add> Add for UseState<'a, T> { - type Output = T; - - fn add(self, rhs: T) -> Self::Output { - self.inner.current_val.add(rhs) - } -} -impl<'a, T: Copy + Add> AddAssign for UseState<'a, T> { - fn add_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.add(rhs)); - } -} -impl<'a, T: Copy + Sub> Sub for UseState<'a, T> { - type Output = T; - - fn sub(self, rhs: T) -> Self::Output { - self.inner.current_val.sub(rhs) - } -} -impl<'a, T: Copy + Sub> SubAssign for UseState<'a, T> { - fn sub_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.sub(rhs)); - } -} - -/// MUL -impl<'a, T: Copy + Mul> Mul for UseState<'a, T> { - type Output = T; - - fn mul(self, rhs: T) -> Self::Output { - self.inner.current_val.mul(rhs) - } -} -impl<'a, T: Copy + Mul> MulAssign for UseState<'a, T> { - fn mul_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.mul(rhs)); - } -} -/// DIV -impl<'a, T: Copy + Div> Div for UseState<'a, T> { - type Output = T; - - fn div(self, rhs: T) -> Self::Output { - self.inner.current_val.div(rhs) - } -} -impl<'a, T: Copy + Div> DivAssign for UseState<'a, T> { - fn div_assign(&mut self, rhs: T) { - self.set(self.inner.current_val.div(rhs)); +// enable displaty for the handle +impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.inner.current_val) } } impl<'a, V, T: PartialEq> PartialEq for UseState<'a, T> { @@ -265,57 +208,10 @@ impl<'a, V, T: PartialEq> PartialEq for UseState<'a, T> { self.get() == other } } -impl<'a, O, T: Not + Copy> Not for UseState<'a, T> { +impl<'a, O, T: std::ops::Not + Copy> std::ops::Not for UseState<'a, T> { type Output = O; fn not(self) -> Self::Output { !*self.get() } } - -// enable displaty for the handle -impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.inner.current_val) - } -} - -/// A less ergonmic but still capable form of use_state that's valid for `static lifetime -pub struct AsyncUseState { - inner: Rc, - re_render: Rc, - wip: Rc>>, -} - -impl> AsyncUseState { - pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> { - // make sure we get processed - { - let mut wip = self.wip.borrow_mut(); - if wip.is_none() { - *wip = Some(self.inner.as_ref().to_owned()); - } - (self.re_render)(); - } - // Bring out the new value, cloning if it we need to - // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this - RefMut::map(self.wip.borrow_mut(), |slot| { - // - slot.as_mut().unwrap() - }) - } -} -impl AsyncUseState { - pub fn set(&mut self, val: T) { - (self.re_render)(); - *self.wip.borrow_mut() = Some(val); - } - - // pub fn get(&self) -> Ref<'_, T> { - // self.wip.borrow - // } - - pub fn get_rc(&self) -> &Rc { - &self.inner - } -} diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index b00ae38f4..755508d58 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -9,14 +9,12 @@ repository = "https://github.com/DioxusLabs/dioxus/" homepage = "https://dioxuslabs.com" documentation = "https://docs.rs/dioxus" keywords = ["dom", "ui", "gui", "react", "wasm"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] dioxus-core = { path = "../core", version = "^0.1.4" } serde = { version = "1", features = ["derive"], optional = true } serde_repr = { version = "0.1", optional = true } - [features] default = [] serialize = ["serde", "serde_repr"] diff --git a/packages/liveview/README.md b/packages/liveview/README.md index 5bbd8b75a..1d708eab2 100644 --- a/packages/liveview/README.md +++ b/packages/liveview/README.md @@ -39,7 +39,7 @@ async fn order_shoes(mut req: WebsocketRequest) -> Response { dioxus::liveview::launch(App, stream).await; } -fn App(cx: Scope<()>) -> Element { +fn App(cx: Scope) -> Element { let mut count = use_state(&cx, || 0); cx.render(rsx!( button { onclick: move |_| count += 1, "Incr" } diff --git a/packages/mobile/Cargo.toml b/packages/mobile/Cargo.toml index ef05f2eee..22aefeac2 100644 --- a/packages/mobile/Cargo.toml +++ b/packages/mobile/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] anyhow = "1.0" # cacao = { git = "https://github.com/ryanmcgrath/cacao" } -dioxus-core = { path = "../core", version ="^0.1.3"} +dioxus-core = { path = "../core", version = "^0.1.3" } log = "0.4.14" serde = "1.0.126" serde_json = "1.0.64" @@ -23,6 +23,3 @@ wry = "0.12.2" android_logger = "0.9.0" log = "0.4.11" ndk-glue = "0.2.1" - -[target.'cfg(not(target_os = "android"))'.dependencies] -simple_logger = "1.11.0" diff --git a/packages/mobile/src/lib.rs b/packages/mobile/src/lib.rs index 938d4e957..685722718 100644 --- a/packages/mobile/src/lib.rs +++ b/packages/mobile/src/lib.rs @@ -20,10 +20,6 @@ use wry::{ mod dom; use dom::*; -fn init_logging() { - simple_logger::SimpleLogger::new().init().unwrap(); -} - static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html"); pub fn launch(root: Component, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> { @@ -187,7 +183,6 @@ impl WebviewRenderer { } fn main() { - init_logging(); let event_loop = EventLoop::new(); let mut weviews = HashMap::new(); diff --git a/src/lib.rs b/src/lib.rs index a5e89bddb..5259377b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ //! dioxus::desktop::launch(app); //! } //! -//! fn app(cx: Scope<()>) -> Element { +//! fn app(cx: Scope) -> Element { //! cx.render(rsx!("hello world!")) //! } //! ``` @@ -57,7 +57,7 @@ //! For components with no explicit properties, we can use the `()` type. In Dioxus, all properties are memoized by default! //! //! ```rust -//! fn App(cx: Scope<()>) -> Element { +//! fn App(cx: Scope) -> Element { //! cx.render(rsx!( //! Header { //! title: "My App", @@ -91,7 +91,7 @@ //! While components are reusable forms of UI elements, hooks are reusable forms of logic. All hooks start with `use_`. We can use hooks to declare state. //! //! ```rust -//! fn app(cx: Scope<()>) -> Element { +//! fn app(cx: Scope) -> Element { //! let name = use_state(&cx, || "world"); //! //! rsx!(cx, "hello {name}!") @@ -137,7 +137,7 @@ //! dioxus::desktop::launch(App); //! } //! -//! fn App(cx: Scope<()>) -> Element { +//! fn App(cx: Scope) -> Element { //! let mut count = use_state(&cx, || 0); //! //! cx.render(rsx!( @@ -187,20 +187,20 @@ pub use dioxus_core as core; #[cfg(feature = "hooks")] pub use dioxus_hooks as hooks; +#[cfg(feature = "router")] +pub use dioxus_router as router; + #[cfg(feature = "ssr")] pub use dioxus_ssr as ssr; #[cfg(feature = "web")] pub use dioxus_web as web; -#[cfg(feature = "mobile")] -pub use dioxus_mobile as mobile; - #[cfg(feature = "desktop")] pub use dioxus_desktop as desktop; -#[cfg(feature = "router")] -pub use dioxus_router as router; +// #[cfg(feature = "mobile")] +// pub use dioxus_mobile as mobile; pub mod events { #[cfg(feature = "html")] From 4c85bcfdc84184b4fd0fb9317ba31fe569884890 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 30 Dec 2021 03:14:47 -0500 Subject: [PATCH 7/8] awesome: arbitrary expressions excepted without braces --- README.md | 8 +- examples/README.md | 24 +-- examples/async.rs | 10 +- examples/borrowed.rs | 2 +- examples/calculator.rs | 79 ++++++---- examples/core_reference/iterators.rs | 4 +- examples/crm.rs | 177 +++++++++++----------- examples/desktop/demo.rs | 2 +- examples/desktop/kitchensink.rs | 8 - examples/desktop/tauri.rs | 1 - examples/desktop/todomvc.rs | 14 +- examples/file_explorer.rs | 73 ++++----- examples/framework_benchmark.rs | 30 ++-- examples/hydration.rs | 8 +- examples/pattern_model.rs | 40 ++--- examples/pattern_reducer.rs | 24 ++- examples/readme.rs | 6 +- examples/rsx_compile_fail.rs | 61 ++++++++ examples/rsx_usage.rs | 83 +++++++---- examples/tailwind.rs | 76 +++++----- examples/tasks.rs | 5 +- examples/todomvc.rs | 39 ++--- examples/web_tick.rs | 27 ++-- examples/webview_web.rs | 6 +- examples/xss_safety.rs | 31 ++++ packages/core-macro/Cargo.toml | 1 + packages/core-macro/src/inlineprops.rs | 1 + packages/core-macro/src/lib.rs | 5 +- packages/core-macro/src/rsx/ambiguous.rs | 64 -------- packages/core-macro/src/rsx/body.rs | 66 --------- packages/core-macro/src/rsx/component.rs | 115 +++------------ packages/core-macro/src/rsx/element.rs | 179 +++++++++++++++-------- packages/core-macro/src/rsx/fragment.rs | 63 -------- packages/core-macro/src/rsx/mod.rs | 80 +++++++++- packages/core-macro/src/rsx/node.rs | 93 +++++++----- packages/core/src/nodes.rs | 4 +- packages/core/tests/vdom_rebuild.rs | 2 +- packages/hooks/src/usestate.rs | 64 ++++++++ packages/router/src/link.rs | 2 +- packages/ssr/src/lib.rs | 15 +- 40 files changed, 825 insertions(+), 767 deletions(-) delete mode 100644 examples/desktop/kitchensink.rs delete mode 100644 examples/desktop/tauri.rs create mode 100644 examples/rsx_compile_fail.rs create mode 100644 examples/xss_safety.rs delete mode 100644 packages/core-macro/src/rsx/ambiguous.rs delete mode 100644 packages/core-macro/src/rsx/body.rs delete mode 100644 packages/core-macro/src/rsx/fragment.rs diff --git a/README.md b/README.md index ffbfba8a6..0573fa040 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,9 @@ If you know React, then you already know Dioxus. -## Examples: + + +## Examples Projects: | File Navigator (Desktop) | WiFi scanner (Desktop) | TodoMVC (All platforms) | Ecommerce w/ Tailwind (Liveview) | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -88,6 +90,10 @@ If you know React, then you already know Dioxus. See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem. +## Running examples locally + +All local examples are built for the desktop renderer. This means you can simply clone this repo and call `cargo run --example EXAMPLE_NAME`. To run non-desktop examples, checkout the example projects shown above. + ## Why Dioxus and why Rust? TypeScript is a fantastic addition to JavaScript, but it's still fundamentally JavaScript. TS code runs slightly slower, has tons of configuration options, and not every package is properly typed. diff --git a/examples/README.md b/examples/README.md index eb0037683..2e4959e7f 100644 --- a/examples/README.md +++ b/examples/README.md @@ -42,14 +42,6 @@ These examples are not necessarily meant to be run, but rather serve as a refere | [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 | - - ## Show me some examples! @@ -75,9 +67,9 @@ fn Toggle(cx: Scope) -> Element { let mut toggled = use_state(&cx, || false); cx.render(rsx!{ div { - {&cx.props.children} + &cx.props.children button { onclick: move |_| toggled.set(true), - {toggled.and_then(|| "On").or_else(|| "Off")} + toggled.and_then(|| "On").or_else(|| "Off") } } }) @@ -112,8 +104,8 @@ fn App(cx: Scope) -> Element { if should_show { cx.render(rsx!( - {title} - ul { {list} } + title, + ul { list } )) } else { None @@ -169,10 +161,10 @@ enum Route { fn App(cx: Scope) -> Element { let route = use_router(cx, Route::parse); cx.render(rsx!(div { - {match route { + match route { Route::Home => rsx!( Home {} ), Route::Post(id) => rsx!( Post { id: id }) - }} + } })) } ``` @@ -187,8 +179,8 @@ fn App(cx: Scope) -> Element { cx.render(rsx!{ div { - "One doggo coming right up:" - {doggo} + "One doggo coming right up:", + doggo } }) } diff --git a/examples/async.rs b/examples/async.rs index e399ea016..4d292779e 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -26,20 +26,18 @@ fn app(cx: Scope) -> Element { rsx!(cx, div { h1 {"count is {count}"} - button { + button { onclick: move |_| task.stop(), "Stop counting" - onclick: move |_| task.stop() } - button { + button { onclick: move |_| task.resume(), "Start counting" - onclick: move |_| task.resume() } button { - "Switch counting direcion" onclick: move |_| { *direction.modify() *= -1; task.restart(); - } + }, + "Switch counting direcion" } }) } diff --git a/examples/borrowed.rs b/examples/borrowed.rs index 29c502e13..6533a21a7 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -21,7 +21,7 @@ fn main() { } fn App(cx: Scope) -> Element { - let text: &mut Vec = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); + let text = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); let first = text.get_mut(0).unwrap(); diff --git a/examples/calculator.rs b/examples/calculator.rs index c3814c088..e43323a21 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -24,7 +24,9 @@ fn app(cx: Scope) -> Element { let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str()); cx.render(rsx!( - div { class: "calculator", + style { [include_str!("./assets/calculator.css")] } + div { + class: "calculator", onkeydown: move |evt| match evt.key_code { KeyCode::Add => operator.set(Some("+")), KeyCode::Subtract => operator.set(Some("-")), @@ -46,12 +48,11 @@ fn app(cx: Scope) -> Element { } } _ => {} - } - div { class: "calculator-display", {[cur_val.separated_string()]} } - div { class: "input-keys" - div { class: "function-keys" + }, + div { class: "calculator-display", [cur_val.separated_string()] } + div { class: "input-keys", + div { class: "function-keys", CalculatorKey { - {[if display_value == "0" { "C" } else { "AC" }]} name: "key-clear", onclick: move |_| { display_value.set("0".to_string()); @@ -59,10 +60,10 @@ fn app(cx: Scope) -> Element { operator.set(None); cur_val.set(0.0); } - } + }, + [if display_value == "0" { "C" } else { "AC" }] } CalculatorKey { - "±" name: "key-sign", onclick: move |_| { if display_value.starts_with("-") { @@ -71,28 +72,53 @@ fn app(cx: Scope) -> Element { display_value.set(format!("-{}", *display_value)) } }, + "±" } CalculatorKey { - "%" - onclick: {toggle_percent} + onclick: toggle_percent, name: "key-percent", + "%" } } - div { class: "digit-keys" - CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" } - CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "●" } - - {(1..9).map(|k| rsx!{ - CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } - })} + div { class: "digit-keys", + CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), + "0" + } + CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), + "●" + } + (1..9).map(|k| rsx!{ + CalculatorKey { + key: "{k}", + name: "key-{k}", + onclick: move |_| input_digit(k), + "{k}" + } + }), } - div { class: "operator-keys" - CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some("/")) "÷" } - CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "×" } - CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "−" } - CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" } + + div { class: "operator-keys", + CalculatorKey { + name: "key-divide", + onclick: move |_| operator.set(Some("/")), + "÷" + } + CalculatorKey { + name: "key-multiply", + onclick: move |_| operator.set(Some("*")), + "×" + } + CalculatorKey { + name: "key-subtract", + onclick: move |_| operator.set(Some("-")), + "−" + } + CalculatorKey { + name: "key-add", + onclick: move |_| operator.set(Some("+")), + "+" + } CalculatorKey { - "=" name: "key-equals", onclick: move |_| { if let Some(op) = operator.as_ref() { @@ -109,6 +135,7 @@ fn app(cx: Scope) -> Element { operator.set(None); } }, + "=" } } } @@ -125,9 +152,9 @@ fn CalculatorKey<'a>( ) -> Element { cx.render(rsx! { button { - class: "calculator-key {name}" - onclick: {onclick} - {children} + class: "calculator-key {name}", + onclick: onclick, + children } }) } diff --git a/examples/core_reference/iterators.rs b/examples/core_reference/iterators.rs index 07452921c..ad376a1bd 100644 --- a/examples/core_reference/iterators.rs +++ b/examples/core_reference/iterators.rs @@ -20,11 +20,11 @@ pub static Example: Component = |cx| { li { onclick: move |_| example_data.set(f) "ID: {f}" ul { - {(0..10).map(|k| rsx!{ + (0..10).map(|k| rsx!{ li { "Sub iterator: {f}.{k}" } - })} + }) } } } diff --git a/examples/crm.rs b/examples/crm.rs index a8eeb456b..ca1ae19cf 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -4,7 +4,7 @@ Tiny CRM: A port of the Yew CRM example to Dioxus. use dioxus::prelude::*; fn main() { - dioxus::web::launch(App); + dioxus::desktop::launch(app); } enum Scene { ClientsList, @@ -19,88 +19,101 @@ pub struct Client { pub description: String, } -static App: Component = |cx| { - let mut clients = use_ref(&cx, || vec![] as Vec); - let mut scene = use_state(&cx, || Scene::ClientsList); +fn app(cx: Scope) -> Element { + let clients = use_ref(&cx, || vec![] as Vec); - let mut firstname = use_state(&cx, || String::new()); - let mut lastname = use_state(&cx, || String::new()); - let mut description = use_state(&cx, || String::new()); - - let scene = match *scene { - Scene::ClientsList => { - rsx!(cx, div { class: "crm" - h2 { "List of clients" margin_bottom: "10px" } - div { class: "clients" margin_left: "10px" - {clients.read().iter().map(|client| rsx!( - div { class: "client" style: "margin-bottom: 50px" - p { "First Name: {client.first_name}" } - p { "Last Name: {client.last_name}" } - p {"Description: {client.description}"} - }) - )} - } - button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" } - button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" } - }) - } - Scene::NewClientForm => { - let add_new = move |_| { - clients.write().push(Client { - description: (*description).clone(), - first_name: (*firstname).clone(), - last_name: (*lastname).clone(), - }); - description.set(String::new()); - firstname.set(String::new()); - lastname.set(String::new()); - }; - rsx!(cx, div { class: "crm" - h2 {"Add new client" margin_bottom: "10px" } - form { class: "pure-form" - input { class: "new-client firstname" placeholder: "First name" value: "{firstname}" - oninput: move |e| firstname.set(e.value.clone()) - } - input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}" - oninput: move |e| lastname.set(e.value.clone()) - } - textarea { class: "new-client description" placeholder: "Description" value: "{description}" - oninput: move |e| description.set(e.value.clone()) - } - } - button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" } - button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" } - }) - } - Scene::Settings => { - rsx!(cx, div { - h2 {"Settings" margin_bottom: "10px" } - button { - background: "rgb(202, 60, 60)" - class: "pure-button pure-button-primary" - onclick: move |_| clients.write().clear(), - "Remove all clients" - } - button { - class: "pure-button pure-button-primary" - onclick: move |_| scene.set(Scene::ClientsList), - "Go Back" - } - }) - } - }; + let scene = use_state(&cx, || Scene::ClientsList); + let firstname = use_state(&cx, String::new); + let lastname = use_state(&cx, String::new); + let description = use_state(&cx, String::new); cx.render(rsx!( - body { - link { - rel: "stylesheet" - href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css" - integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5" - crossorigin: "anonymous" - } - margin_left: "35%" - h1 {"Dioxus CRM Example"} - {scene} - } + body { margin_left: "35%", + link { + rel: "stylesheet", + href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css", + integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", + crossorigin: "anonymous", + } + + h1 {"Dioxus CRM Example"} + + match *scene { + Scene::ClientsList => rsx!( + div { class: "crm", + h2 { margin_bottom: "10px", "List of clients" } + div { class: "clients", margin_left: "10px", + clients.read().iter().map(|client| rsx!( + div { class: "client", style: "margin-bottom: 50px", + p { "First Name: {client.first_name}" } + p { "Last Name: {client.last_name}" } + p {"Description: {client.description}"} + }) + ) + } + button { class: "pure-button pure-button-primary", onclick: move |_| scene.set(Scene::NewClientForm), "Add New" } + button { class: "pure-button", onclick: move |_| scene.set(Scene::Settings), "Settings" } + } + ), + Scene::NewClientForm => rsx!( + div { class: "crm", + h2 { margin_bottom: "10px", "Add new client" } + form { class: "pure-form", + input { + class: "new-client firstname", + placeholder: "First name", + value: "{firstname}", + oninput: move |e| firstname.set(e.value.clone()) + } + input { + class: "new-client lastname", + placeholder: "Last name", + value: "{lastname}", + oninput: move |e| lastname.set(e.value.clone()) + } + textarea { + class: "new-client description", + placeholder: "Description", + value: "{description}", + oninput: move |e| description.set(e.value.clone()) + } + } + button { + class: "pure-button pure-button-primary", + onclick: move |_| { + clients.write().push(Client { + description: (*description).clone(), + first_name: (*firstname).clone(), + last_name: (*lastname).clone(), + }); + description.set(String::new()); + firstname.set(String::new()); + lastname.set(String::new()); + }, + "Add New" + } + button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), + "Go Back" + } + } + ), + Scene::Settings => rsx!( + div { + h2 { margin_bottom: "10px", "Settings" } + button { + background: "rgb(202, 60, 60)", + class: "pure-button pure-button-primary", + onclick: move |_| clients.write().clear(), + "Remove all clients" + } + button { + class: "pure-button pure-button-primary", + onclick: move |_| scene.set(Scene::ClientsList), + "Go Back" + } + } + ) + } + } )) -}; +} diff --git a/examples/desktop/demo.rs b/examples/desktop/demo.rs index 1782d8cb0..d8dc51697 100644 --- a/examples/desktop/demo.rs +++ b/examples/desktop/demo.rs @@ -13,6 +13,6 @@ static App: Component = |cx| { div { "hello world!" } - {(0..10).map(|f| rsx!( div {"abc {f}"}))} + (0..10).map(|f| rsx!( div {"abc {f}"})) )) }; diff --git a/examples/desktop/kitchensink.rs b/examples/desktop/kitchensink.rs deleted file mode 100644 index 5ec049ee9..000000000 --- a/examples/desktop/kitchensink.rs +++ /dev/null @@ -1,8 +0,0 @@ -use dioxus_core as dioxus; -use dioxus_core::prelude::*; -use dioxus_core_macro::*; -use dioxus_hooks::*; - -use dioxus_html as dioxus_elements; - -fn main() {} diff --git a/examples/desktop/tauri.rs b/examples/desktop/tauri.rs deleted file mode 100644 index f328e4d9d..000000000 --- a/examples/desktop/tauri.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs index e70ed1806..46ee5ec1d 100644 --- a/examples/desktop/todomvc.rs +++ b/examples/desktop/todomvc.rs @@ -98,24 +98,24 @@ pub static App: Component = |cx| { } } } - ul { class: "todo-list" - {filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))} + ul { class: "todo-list", + filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id })) } - {(!todos.read().is_empty()).then(|| rsx!( - footer { class: "footer" + (!todos.read().is_empty()).then(|| rsx!( + footer { class: "footer", span { class: "todo-count" strong {"{items_left} "} span {"{item_text} left"} } ul { class: "filters" li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }} li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }} li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }} } - {(show_clear_completed).then(|| rsx!( + (show_clear_completed).then(|| rsx!( button { class: "clear-completed", onclick: move |_| clear_completed(), "Clear completed" } - ))} + )) } - ))} + )) } } footer { class: "info" diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 05f5c4ca7..f27e018a9 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -4,50 +4,45 @@ //! This is a fun little desktop application that lets you explore the file system. //! //! This example is interesting because it's mixing filesystem operations and GUI, which is typically hard for UI to do. +//! +//! It also uses `use_ref` to maintain a model, rather than `use_state`. That way, +//! we dont need to clutter our code with `read` commands. use dioxus::prelude::*; fn main() { - dioxus::desktop::launch_cfg(App, |c| { + dioxus::desktop::launch_cfg(app, |c| { c.with_window(|w| { - w.with_resizable(true).with_inner_size( - dioxus::desktop::wry::application::dpi::LogicalSize::new(400.0, 800.0), - ) + w.with_resizable(true) + .with_inner_size(dioxus::desktop::tao::dpi::LogicalSize::new(400.0, 800.0)) }) }); } -static App: Component = |cx| { - let file_manager = use_ref(&cx, Files::new); - let files = file_manager.read(); - - let file_list = files.path_names.iter().enumerate().map(|(dir_id, path)| { - rsx! ( - li { a {"{path}", onclick: move |_| file_manager.write().enter_dir(dir_id), href: "#"} } - ) - }); - - let err_disp = files.err.as_ref().map(|err| { - rsx! ( - div { - code {"{err}"} - button {"x", onclick: move |_| file_manager.write().clear_err() } - } - ) - }); - - let current_dir = files.current(); +fn app(cx: Scope) -> Element { + let files = use_ref(&cx, Files::new); cx.render(rsx!( - div { - h1 {"Files: "} - h3 {"Cur dir: {current_dir}"} - button { "go up", onclick: move |_| file_manager.write().go_up() } - ol { {file_list} } - {err_disp} + h1 { "Files: " } + h3 { "Cur dir: " [files.read().current()] } + button { onclick: move |_| files.write().go_up(), "go up" } + ol { + files.read().path_names.iter().enumerate().map(|(dir_id, path)| rsx!( + li { key: "{path}", + a { href: "#", onclick: move |_| files.write().enter_dir(dir_id), + "{path}", + } + } + )) } + files.read().err.as_ref().map(|err| rsx!( + div { + code { "{err}" } + button { onclick: move |_| files.write().clear_err(), "x" } + } + )) )) -}; +} struct Files { path_stack: Vec, @@ -69,29 +64,21 @@ impl Files { } fn reload_path_list(&mut self) { - let cur_path = self.path_stack.last().unwrap(); - log::info!("Reloading path list for {:?}", cur_path); - let paths = match std::fs::read_dir(cur_path) { + let paths = match std::fs::read_dir(self.path_stack.last().unwrap()) { Ok(e) => e, Err(err) => { - let err = format!("An error occured: {:?}", err); - self.err = Some(err); + self.err = Some(format!("An error occured: {:?}", err)); self.path_stack.pop(); return; } }; - let collected = paths.collect::>(); - log::info!("Path list reloaded {:#?}", collected); // clear the current state self.clear_err(); self.path_names.clear(); - for path in collected { - self.path_names - .push(path.unwrap().path().display().to_string()); - } - log::info!("path namees are {:#?}", self.path_names); + self.path_names + .extend(paths.map(|path| path.unwrap().path().display().to_string())); } fn go_up(&mut self) { diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index 100169679..f09693f1b 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -2,8 +2,8 @@ use dioxus::prelude::*; use rand::prelude::*; fn main() { - dioxus::web::launch(App); - // dioxus::desktop::launch(App); + // dioxus::web::launch(App); + dioxus::desktop::launch(App); } #[derive(Clone, PartialEq)] @@ -31,16 +31,16 @@ impl Label { } static App: Component = |cx| { - let mut items = use_ref(&cx, || vec![]); - let mut selected = use_state(&cx, || None); + let items = use_ref(&cx, || vec![]); + let selected = use_state(&cx, || None); cx.render(rsx! { - div { class: "container" - div { class: "jumbotron" - div { class: "row" + div { class: "container", + div { class: "jumbotron", + div { class: "row", div { class: "col-md-6", h1 { "Dioxus" } } - div { class: "col-md-6" - div { class: "row" + div { class: "col-md-6", + div { class: "row", ActionButton { name: "Create 1,000 rows", id: "run", onclick: move || items.set(Label::new_list(1_000)), } @@ -67,15 +67,15 @@ static App: Component = |cx| { tbody { {items.read().iter().enumerate().map(|(id, item)| { let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""}; - rsx!(tr { class: "{is_in_danger}" + rsx!(tr { class: "{is_in_danger}", td { class:"col-md-1" } td { class:"col-md-1", "{item.key}" } td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), - a { class: "lbl", {item.labels} } + a { class: "lbl", item.labels } } - td { class: "col-md-1" + td { class: "col-md-1", a { class: "remove", onclick: move |_| { items.write().remove(id); }, - span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" } } } td { class: "col-md-6" } @@ -83,7 +83,7 @@ static App: Component = |cx| { })} } } - // span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" } + span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" } } }) }; @@ -96,7 +96,7 @@ struct ActionButtonProps<'a> { } fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element { - rsx!(cx, div { class: "col-sm-6 smallpad" + rsx!(cx, div { class: "col-sm-6 smallpad", button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}", onclick: move |_| (cx.props.onclick)(), "{cx.props.name}" } diff --git a/examples/hydration.rs b/examples/hydration.rs index 3c4faa55d..1f769e799 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -16,18 +16,18 @@ fn main() { let vdom = VirtualDom::new(App); let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true)); - dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content)); + // dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content)); } static App: Component = |cx| { - let mut val = use_state(&cx, || 0); + let val = use_state(&cx, || 0); cx.render(rsx! { div { - h1 {"hello world. Count: {val}"} + h1 { "hello world. Count: {val}" } button { + onclick: move |_| *val.modify() += 1, "click to increment" - onclick: move |_| val += 1 } } }) diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index 252a000c9..45b0ccf4e 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -24,42 +24,42 @@ use dioxus::prelude::*; const STYLE: &str = include_str!("./assets/calculator.css"); fn main() { env_logger::init(); - dioxus::desktop::launch_cfg(App, |cfg| { - cfg.with_window(|w| { - w.with_title("Calculator Demo") - .with_resizable(false) - .with_inner_size(LogicalSize::new(320.0, 530.0)) - }) - }); + // dioxus::desktop::launch_cfg(App, |cfg| { + // cfg.with_window(|w| { + // w.with_title("Calculator Demo") + // .with_resizable(false) + // .with_inner_size(LogicalSize::new(320.0, 530.0)) + // }) + // }); } -static App: Component = |cx| { +fn app(cx: Scope) -> Element { let state = use_ref(&cx, || Calculator::new()); let clear_display = state.read().display_value.eq("0"); let clear_text = if clear_display { "C" } else { "AC" }; let formatted = state.read().formatted_display(); - rsx!(cx, div { id: "wrapper" + rsx!(cx, div { id: "wrapper", div { class: "app", style { "{STYLE}" } div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt), div { class: "calculator-display", "{formatted}"} - div { class: "calculator-keypad" - div { class: "input-keys" - div { class: "function-keys" + div { class: "calculator-keypad", + div { class: "input-keys", + div { class: "function-keys", CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" } CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"} CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"} } - div { class: "digit-keys" + div { class: "digit-keys", CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" } CalculatorKey { name: "key-dot", onclick: move |_| state.write().input_dot(), "●" } - {(1..10).map(move |k| rsx!{ + (1..10).map(move |k| rsx!{ CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| state.write().input_digit(k), "{k}" } - })} + }) } } - div { class: "operator-keys" + div { class: "operator-keys", CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" } CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" } CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" } @@ -70,7 +70,7 @@ static App: Component = |cx| { } } }) -}; +} #[derive(Props)] struct CalculatorKeyProps<'a> { @@ -82,9 +82,9 @@ struct CalculatorKeyProps<'a> { fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element { cx.render(rsx! { button { - class: "calculator-key {cx.props.name}" - onclick: move |e| (cx.props.onclick)(e) - {&cx.props.children} + class: "calculator-key {cx.props.name}", + onclick: move |e| (cx.props.onclick)(e), + &cx.props.children } }) } diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs index cd14460f6..26bf3acff 100644 --- a/examples/pattern_reducer.rs +++ b/examples/pattern_reducer.rs @@ -14,20 +14,18 @@ fn main() { pub static App: Component = |cx| { let state = use_state(&cx, PlayerState::new); - let is_playing = state.is_playing(); - - rsx!(cx, div { - h1 {"Select an option"} - h3 {"The radio is... {is_playing}!"} - button { - "Pause" - onclick: move |_| state.modify().reduce(PlayerAction::Pause) + cx.render(rsx!( + div { + h1 {"Select an option"} + h3 { "The radio is... " [state.is_playing()], "!" } + button { onclick: move |_| state.modify().reduce(PlayerAction::Pause), + "Pause" + } + button { onclick: move |_| state.modify().reduce(PlayerAction::Play), + "Play" + } } - button { - "Play" - onclick: move |_| state.modify().reduce(PlayerAction::Play) - } - }) + )) }; enum PlayerAction { diff --git a/examples/readme.rs b/examples/readme.rs index 9f0a288e3..ae17c1cd9 100644 --- a/examples/readme.rs +++ b/examples/readme.rs @@ -9,13 +9,13 @@ fn main() { } static App: Component = |cx| { - let count = use_state(&cx, || 0); + let mut count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "High-Five counter: {count}" } - button { onclick: move |_| *count.modify() += 1, "Up high!" } - button { onclick: move |_| *count.modify() -= 1, "Down low!" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } } }) }; diff --git a/examples/rsx_compile_fail.rs b/examples/rsx_compile_fail.rs new file mode 100644 index 000000000..c95f3d7fe --- /dev/null +++ b/examples/rsx_compile_fail.rs @@ -0,0 +1,61 @@ +use dioxus::prelude::*; + +fn main() { + let mut vdom = VirtualDom::new(example); + vdom.rebuild(); + + let out = dioxus::ssr::render_vdom_cfg(&vdom, |c| c.newline(true).indent(true)); + println!("{}", out); +} + +fn example(cx: Scope) -> Element { + let items = use_state(&cx, || { + vec![Thing { + a: "asd".to_string(), + b: 10, + }] + }); + + let things = use_ref(&cx, || { + vec![Thing { + a: "asd".to_string(), + b: 10, + }] + }); + let things_list = things.read(); + + let mything = use_ref(&cx, || Some(String::from("asd"))); + let mything_read = mything.read(); + + cx.render(rsx!( + div { + div { + id: "asd", + "your neighborhood spiderman" + + items.iter().cycle().take(5).map(|f| rsx!{ + div { + "{f.a}" + } + }) + + things_list.iter().map(|f| rsx!{ + div { + "{f.a}" + } + }) + + mything_read.as_ref().map(|f| rsx!{ + div { + "{f}" + } + }) + } + } + )) +} + +struct Thing { + a: String, + b: u32, +} diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 195edac00..04afb7407 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -60,6 +60,7 @@ pub static EXAMPLE: Component = |cx| { h1 {"Some text"} h1 {"Some text with {formatting}"} h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"} + h1 {"Formatting without interpolation " [formatting_tuple.0] "and" [formatting_tuple.1] } h2 { "Multiple" "Text" @@ -72,7 +73,7 @@ pub static EXAMPLE: Component = |cx| { h3 {"elements"} } div { - class: "my special div" + class: "my special div", h1 {"Headers and attributes!"} } div { @@ -88,42 +89,51 @@ pub static EXAMPLE: Component = |cx| { } // Expressions can be used in element position too: - {rsx!(p { "More templating!" })} - // {html!(

"Even HTML templating!!"

)} + rsx!(p { "More templating!" }), // Iterators - {(0..10).map(|i| rsx!(li { "{i}" }))} - {{ + (0..10).map(|i| rsx!(li { "{i}" })), + + // Iterators within expressions + { let data = std::collections::HashMap::<&'static str, &'static str>::new(); // Iterators *should* have keys when you can provide them. // Keys make your app run faster. Make sure your keys are stable, unique, and predictable. // Using an "ID" associated with your data is a good idea. - data.into_iter().map(|(k, v)| rsx!(li { key: "{k}" "{v}" })) - }} + data.into_iter().map(|(k, v)| rsx!(li { key: "{k}", "{v}" })) + } // Matching - {match true { + match true { true => rsx!( h1 {"Top text"}), false => rsx!( h1 {"Bottom text"}) - }} + } // Conditional rendering // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals. // You can convert a bool condition to rsx! with .then and .or - {true.then(|| rsx!(div {}))} + true.then(|| rsx!(div {})), - // True conditions need to be rendered (same reasons as matching) - {if true { - rsx!(cx, h1 {"Top text"}) + // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element + if false { + rsx!(h1 {"Top text"}) } else { - rsx!(cx, h1 {"Bottom text"}) - }} + rsx!(h1 {"Bottom text"}) + } - // returning "None" is a bit noisy... but rare in practice - {None as Option<()>} + // Using optionals for diverging branches + if true { + Some(rsx!(h1 {"Top text"})) + } else { + None + } + + + // returning "None" without a diverging branch is a bit noisy... but rare in practice + None as Option<()>, // Use the Dioxus type-alias for less noise - {NONE_ELEMENT} + NONE_ELEMENT, // can also just use empty fragments Fragment {} @@ -137,9 +147,8 @@ pub static EXAMPLE: Component = |cx| { Fragment { "D" Fragment { - "heavily nested fragments is an antipattern" - "they cause Dioxus to do unnecessary work" - "don't use them carelessly if you can help it" + "E" + "F" } } } @@ -158,22 +167,29 @@ pub static EXAMPLE: Component = |cx| { Taller { a: "asd" } // Can pass in props directly as an expression - {{ + { let props = TallerProps {a: "hello", children: Default::default()}; rsx!(Taller { ..props }) - }} + } // Spreading can also be overridden manually Taller { - ..TallerProps { a: "ballin!", children: Default::default() } + ..TallerProps { a: "ballin!", children: Default::default() }, a: "not ballin!" } // Can take children too! Taller { a: "asd", div {"hello world!"} } + // Components can be used with the `call` syntax + // This component's props are defined *inline* with the `inline_props` macro + with_inline( + text: "using functionc all syntax" + ) + // helper functions - {helper(&cx, "hello world!")} + // Single values must be wrapped in braces or `Some` to satisfy `IntoIterator` + [helper(&cx, "hello world!")] } }) }; @@ -187,6 +203,7 @@ mod baller { #[derive(Props, PartialEq)] pub struct BallerProps {} + #[allow(non_snake_case)] /// This component totally balls pub fn Baller(_: Scope) -> Element { todo!() @@ -195,12 +212,20 @@ mod baller { #[derive(Props)] pub struct TallerProps<'a> { + /// Fields are documented and accessible in rsx! a: &'static str, children: Element<'a>, } -/// This component is taller than most :) -pub fn Taller<'a>(_: Scope<'a, TallerProps<'a>>) -> Element { - let b = true; - todo!() +/// Documention for this component is visible within the rsx macro +#[allow(non_snake_case)] +pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element { + cx.render(rsx! { + &cx.props.children + }) +} + +#[inline_props] +fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element { + rsx!(cx, p { "{text}" }) } diff --git a/examples/tailwind.rs b/examples/tailwind.rs index 49da99ee8..9bd87c15e 100644 --- a/examples/tailwind.rs +++ b/examples/tailwind.rs @@ -16,9 +16,9 @@ const STYLE: &str = "body {overflow:hidden;}"; pub static App: Component = |cx| { cx.render(rsx!( - div { class: "overflow-hidden" + div { class: "overflow-hidden", style { "{STYLE}" } - link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" } + link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" } Header {} Entry {} Hero {} @@ -33,20 +33,20 @@ pub static App: Component = |cx| { pub static Header: Component = |cx| { cx.render(rsx! { div { - header { class: "text-gray-400 bg-gray-900 body-font" - div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center" - a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0" + header { class: "text-gray-400 bg-gray-900 body-font", + div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center", + a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0", StacksIcon {} - span { class: "ml-3 text-xl" "Hello Dioxus!"} + span { class: "ml-3 text-xl", "Hello Dioxus!"} } - nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center" - a { class: "mr-5 hover:text-white" "First Link"} - a { class: "mr-5 hover:text-white" "Second Link"} - a { class: "mr-5 hover:text-white" "Third Link"} - a { class: "mr-5 hover:text-white" "Fourth Link"} + nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center", + a { class: "mr-5 hover:text-white", "First Link"} + a { class: "mr-5 hover:text-white", "Second Link"} + a { class: "mr-5 hover:text-white", "Third Link"} + a { class: "mr-5 hover:text-white", "Fourth Link"} } button { - class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0" + class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", "Button" RightArrowIcon {} } @@ -59,34 +59,34 @@ pub static Header: Component = |cx| { pub static Hero: Component = |cx| { // cx.render(rsx! { - section{ class: "text-gray-400 bg-gray-900 body-font" - div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center" - div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center" - h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white" + section{ class: "text-gray-400 bg-gray-900 body-font", + div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", + div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center", + h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white", br { class: "hidden lg:inline-block" } "Dioxus Sneak Peek" } p { - class: "mb-8 leading-relaxed" + class: "mb-8 leading-relaxed", "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and on mobile and embedded platforms." } - div { class: "flex justify-center" + div { class: "flex justify-center", button { - class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg" + class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg", "Learn more" } button { - class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg" + class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg", "Build an app" } } } - div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6" - img { class: "object-cover object-center rounded" alt: "hero" src: "https://i.imgur.com/oK6BLtw.png" + div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6", + img { class: "object-cover object-center rounded", alt: "hero", src: "https://i.imgur.com/oK6BLtw.png", referrerpolicy:"no-referrer" } } @@ -97,8 +97,8 @@ pub static Hero: Component = |cx| { pub static Entry: Component = |cx| { // cx.render(rsx! { - section{ class: "text-gray-400 bg-gray-900 body-font" - div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center" + section{ class: "text-gray-400 bg-gray-900 body-font", + div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center", textarea { } @@ -111,13 +111,13 @@ pub static StacksIcon: Component = |cx| { cx.render(rsx!( svg { // xmlns: "http://www.w3.org/2000/svg" - fill: "none" - stroke: "currentColor" - stroke_linecap: "round" - stroke_linejoin: "round" - stroke_width: "2" - class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full" - view_box: "0 0 24 24" + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full", + view_box: "0 0 24 24", path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"} } )) @@ -125,13 +125,13 @@ pub static StacksIcon: Component = |cx| { pub static RightArrowIcon: Component = |cx| { cx.render(rsx!( svg { - fill: "none" - stroke: "currentColor" - stroke_linecap: "round" - stroke_linejoin: "round" - stroke_width: "2" - class: "w-4 h-4 ml-1" - view_box: "0 0 24 24" + fill: "none", + stroke: "currentColor", + stroke_linecap: "round", + stroke_linejoin: "round", + stroke_width: "2", + class: "w-4 h-4 ml-1", + view_box: "0 0 24 24", path { d: "M5 12h14M12 5l7 7-7 7"} } )) diff --git a/examples/tasks.rs b/examples/tasks.rs index a378c24a6..0838c1fa2 100644 --- a/examples/tasks.rs +++ b/examples/tasks.rs @@ -15,8 +15,9 @@ fn app(cx: Scope) -> Element { use_future(&cx, || { for_async![count]; async move { - while let _ = tokio::time::sleep(Duration::from_millis(1000)).await { - *count.modify() += 1; + loop { + tokio::time::sleep(Duration::from_millis(1000)).await; + count += 1; } } }); diff --git a/examples/todomvc.rs b/examples/todomvc.rs index a41f78d5e..8a62e0ddd 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -29,7 +29,7 @@ const App: Component = |cx| { let todolist = todos .iter() - .filter(|(id, item)| match *filter { + .filter(|(_id, item)| match *filter { FilterState::All => true, FilterState::Active => !item.checked, FilterState::Completed => item.checked, @@ -48,34 +48,37 @@ const App: Component = |cx| { _ => "items", }; - rsx!(cx, div { id: "app" + rsx!(cx, div { id: "app", style {"{STYLE}"} div { - header { class: "header" + header { class: "header", h1 {"todos"} input { - class: "new-todo" - placeholder: "What needs to be done?" - value: "{draft}" - oninput: move |evt| draft.set(evt.value.clone()) + class: "new-todo", + placeholder: "What needs to be done?", + value: "{draft}", + oninput: move |evt| draft.set(evt.value.clone()), } } - {todolist} - {(!todos.is_empty()).then(|| rsx!( + todolist, + (!todos.is_empty()).then(|| rsx!( footer { - span { strong {"{items_left}"} span {"{item_text} left"} } - ul { class: "filters" + span { + strong {"{items_left}"} + span {"{item_text} left"} + } + ul { class: "filters", li { class: "All", a { href: "", onclick: move |_| filter.set(FilterState::All), "All" }} li { class: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }} li { class: "Completed", a { href: "completed", onclick: move |_| filter.set(FilterState::Completed), "Completed" }} } } - ))} + )) } - footer { class: "info" + footer { class: "info", p {"Double-click to edit a todo"} - p { "Created by ", a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }} - p { "Part of ", a { "TodoMVC", href: "http://todomvc.com" }} + p { "Created by ", a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }} + p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }} } }) }; @@ -93,13 +96,13 @@ pub fn TodoEntry(cx: Scope) -> Element { rsx!(cx, li { "{todo.id}" input { - class: "toggle" - r#type: "checkbox" + class: "toggle", + r#type: "checkbox", "{todo.checked}" } {is_editing.then(|| rsx!{ input { - value: "{contents}" + value: "{contents}", oninput: move |evt| contents.set(evt.value.clone()) } })} diff --git a/examples/web_tick.rs b/examples/web_tick.rs index 447bd1121..74cc1897f 100644 --- a/examples/web_tick.rs +++ b/examples/web_tick.rs @@ -13,28 +13,19 @@ use dioxus::prelude::*; fn main() { - #[cfg(target_arch = "wasm32")] - intern_strings(); - - dioxus::web::launch(App); + dioxus::desktop::launch(App); } static App: Component = |cx| { let mut rng = SmallRng::from_entropy(); - let rows = (0..1_000).map(|f| { - let label = Label::new(&mut rng); - rsx! { - Row { - row_id: f, - label: label - } - } - }); cx.render(rsx! { table { tbody { - {rows} + (0..1_000).map(|f| { + let label = Label::new(&mut rng); + rsx! (Row { row_id: f, label: label }) + }) } } }) @@ -50,12 +41,12 @@ fn Row(cx: Scope) -> Element { cx.render(rsx! { tr { td { class:"col-md-1", "{cx.props.row_id}" } - td { class:"col-md-1", onclick: move |_| { /* run onselect */ } + td { class:"col-md-1", onclick: move |_| { /* run onselect */ }, a { class: "lbl", "{adj}" "{col}" "{noun}" } } - td { class: "col-md-1" - a { class: "remove", onclick: move |_| {/* remove */} - span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + td { class: "col-md-1", + a { class: "remove", onclick: move |_| {/* remove */}, + span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" } } } td { class: "col-md-6" } diff --git a/examples/webview_web.rs b/examples/webview_web.rs index 5a7df845d..aa762cb88 100644 --- a/examples/webview_web.rs +++ b/examples/webview_web.rs @@ -16,13 +16,13 @@ fn main() { } fn app(cx: Scope) -> Element { - let count = use_state(&cx, || 0); + let mut count = use_state(&cx, || 0); cx.render(rsx! { div { h1 { "Hifive counter: {count}" } - button { onclick: move |_| *count.modify() += 1, "Up high!" } - button { onclick: move |_| *count.modify() -= 1, "Down low!" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } } }) } diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs new file mode 100644 index 000000000..cdb0705ca --- /dev/null +++ b/examples/xss_safety.rs @@ -0,0 +1,31 @@ +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + let contents = use_state(&cx, || String::from("")); + + cx.render(rsx! { + div { + "hello world!" + + h1 { + "{contents}" + } + h3 { + [contents.as_str()] + } + + input { + value: "{contents}", + oninput: move |e| { + contents.set(e.value.clone()); + eprintln!("asd"); + }, + "type": "text", + } + } + }) +} diff --git a/packages/core-macro/Cargo.toml b/packages/core-macro/Cargo.toml index e3179510e..9af9f3126 100644 --- a/packages/core-macro/Cargo.toml +++ b/packages/core-macro/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true [dependencies] once_cell = "1.8" +proc-macro-error = "1.0.4" proc-macro2 = { version = "1.0.6" } quote = "1.0" syn = { version = "1.0.11", features = ["full", "extra-traits"] } diff --git a/packages/core-macro/src/inlineprops.rs b/packages/core-macro/src/inlineprops.rs index 64dab1ee3..acbf778d3 100644 --- a/packages/core-macro/src/inlineprops.rs +++ b/packages/core-macro/src/inlineprops.rs @@ -99,6 +99,7 @@ impl ToTokens for InlinePropsBody { }; out_tokens.append_all(quote! { + #[allow(non_camel_case)] #modifiers #vis struct #struct_name #generics { #(#fields),* diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index 2d17f4bbc..8f603bbb7 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -177,11 +177,12 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// todo!() /// } /// ``` +#[proc_macro_error::proc_macro_error] #[proc_macro] pub fn rsx(s: TokenStream) -> TokenStream { match syn::parse::(s) { - Err(e) => e.to_compile_error().into(), - Ok(s) => s.to_token_stream().into(), + Err(err) => err.to_compile_error().into(), + Ok(stream) => stream.to_token_stream().into(), } } diff --git a/packages/core-macro/src/rsx/ambiguous.rs b/packages/core-macro/src/rsx/ambiguous.rs deleted file mode 100644 index 16cb2c4e5..000000000 --- a/packages/core-macro/src/rsx/ambiguous.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! Parse anything that has a pattern of < Ident, Bracket > -//! ======================================================== -//! -//! Whenever a `name {}` pattern emerges, we need to parse it into an element, a component, or a fragment. -//! This feature must support: -//! - Namepsaced/pathed components -//! - Differentiating between built-in and custom elements - -use super::*; - -use proc_macro2::TokenStream as TokenStream2; -use quote::ToTokens; -use syn::{ - parse::{Parse, ParseStream}, - Error, Ident, Result, Token, -}; - -#[allow(clippy::large_enum_variant)] -pub enum AmbiguousElement { - Element(Element), - Component(Component), -} - -impl Parse for AmbiguousElement { - fn parse(input: ParseStream) -> Result { - // Try to parse as an absolute path and immediately defer to the componetn - if input.peek(Token![::]) { - return input.parse::().map(AmbiguousElement::Component); - } - - // If not an absolute path, then parse the ident and check if it's a valid tag - if let Ok(pat) = input.fork().parse::() { - if pat.segments.len() > 1 { - return input.parse::().map(AmbiguousElement::Component); - } - } - - use syn::ext::IdentExt; - if let Ok(name) = input.fork().call(Ident::parse_any) { - let name_str = name.to_string(); - - let first_char = name_str.chars().next().unwrap(); - if first_char.is_ascii_uppercase() { - input.parse::().map(AmbiguousElement::Component) - } else { - if input.peek2(syn::token::Paren) { - input.parse::().map(AmbiguousElement::Component) - } else { - input.parse::().map(AmbiguousElement::Element) - } - } - } else { - Err(Error::new(input.span(), "Not a valid Html tag")) - } - } -} -impl ToTokens for AmbiguousElement { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - AmbiguousElement::Element(el) => el.to_tokens(tokens), - AmbiguousElement::Component(comp) => comp.to_tokens(tokens), - } - } -} diff --git a/packages/core-macro/src/rsx/body.rs b/packages/core-macro/src/rsx/body.rs deleted file mode 100644 index 83d2886e6..000000000 --- a/packages/core-macro/src/rsx/body.rs +++ /dev/null @@ -1,66 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::{quote, ToTokens, TokenStreamExt}; -use syn::{ - parse::{Parse, ParseStream}, - Ident, Result, Token, -}; - -use super::*; - -pub struct CallBody { - custom_context: Option, - roots: Vec, -} - -/// The custom rusty variant of parsing rsx! -impl Parse for CallBody { - fn parse(input: ParseStream) -> Result { - let custom_context = try_parse_custom_context(input)?; - let (_, roots, _) = BodyConfig::new_call_body().parse_component_body(input)?; - Ok(Self { - custom_context, - roots, - }) - } -} - -fn try_parse_custom_context(input: ParseStream) -> Result> { - let res = if input.peek(Ident) && input.peek2(Token![,]) { - let name = input.parse::()?; - input.parse::()?; - Some(name) - } else { - None - }; - Ok(res) -} - -/// Serialize the same way, regardless of flavor -impl ToTokens for CallBody { - fn to_tokens(&self, out_tokens: &mut TokenStream2) { - let inner = if self.roots.len() == 1 { - let inner = &self.roots[0]; - quote! {#inner} - } else { - let childs = &self.roots; - quote! { __cx.fragment_root([ #(#childs),* ]) } - }; - - match &self.custom_context { - // The `in cx` pattern allows directly rendering - Some(ident) => out_tokens.append_all(quote! { - #ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode { - use dioxus_elements::{GlobalAttributes, SvgAttributes}; - #inner - })) - }), - // Otherwise we just build the LazyNode wrapper - None => out_tokens.append_all(quote! { - LazyNodes::new_some(move |__cx: NodeFactory| -> VNode { - use dioxus_elements::{GlobalAttributes, SvgAttributes}; - #inner - }) - }), - }; - } -} diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index 9c51cb748..a19f55bac 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -19,11 +19,10 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ ext::IdentExt, parse::{Parse, ParseBuffer, ParseStream}, - token, Error, Expr, ExprClosure, Ident, Result, Token, + token, Expr, ExprClosure, Ident, Result, Token, }; pub struct Component { - // accept any path-like argument name: syn::Path, body: Vec, children: Vec, @@ -32,12 +31,8 @@ pub struct Component { impl Parse for Component { fn parse(stream: ParseStream) -> Result { - // let name = s.parse::()?; - // todo: look into somehow getting the crate/super/etc - let name = syn::Path::parse_mod_style(stream)?; - // parse the guts let content: ParseBuffer; // if we see a `{` then we have a block @@ -48,13 +43,25 @@ impl Parse for Component { syn::parenthesized!(content in stream); } - let cfg: BodyConfig = BodyConfig { - allow_children: true, - allow_fields: true, - allow_manual_props: true, - }; + let mut body = Vec::new(); + let mut children = Vec::new(); + let mut manual_props = None; - let (body, children, manual_props) = cfg.parse_component_body(&content)?; + while !content.is_empty() { + // if we splat into a component then we're merging properties + if content.peek(Token![..]) { + content.parse::()?; + manual_props = Some(content.parse::()?); + } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { + body.push(content.parse::()?); + } else { + children.push(content.parse::()?); + } + + if content.peek(Token![,]) { + let _ = content.parse::(); + } + } Ok(Self { name, @@ -65,77 +72,6 @@ impl Parse for Component { } } -pub struct BodyConfig { - pub allow_fields: bool, - pub allow_children: bool, - pub allow_manual_props: bool, -} - -impl BodyConfig { - /// The configuration to parse the root - pub fn new_call_body() -> Self { - Self { - allow_children: true, - allow_fields: false, - allow_manual_props: false, - } - } -} - -impl BodyConfig { - // todo: unify this body parsing for both elements and components - // both are style rather ad-hoc, though components are currently more configured - pub fn parse_component_body( - &self, - content: &ParseBuffer, - ) -> Result<(Vec, Vec, Option)> { - let mut body = Vec::new(); - let mut children = Vec::new(); - let mut manual_props = None; - - 'parsing: loop { - // [1] Break if empty - if content.is_empty() { - break 'parsing; - } - - if content.peek(Token![..]) { - if !self.allow_manual_props { - return Err(Error::new( - content.span(), - "Props spread syntax is not allowed in this context. \nMake to only use the elipsis `..` in Components.", - )); - } - content.parse::()?; - manual_props = Some(content.parse::()?); - } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { - if !self.allow_fields { - return Err(Error::new( - content.span(), - "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.", - )); - } - body.push(content.parse::()?); - } else { - if !self.allow_children { - return Err(Error::new( - content.span(), - "This item is not allowed to accept children.", - )); - } - children.push(content.parse::()?); - } - - // consume comma if it exists - // we don't actually care if there *are* commas between attrs - if content.peek(Token![,]) { - let _ = content.parse::(); - } - } - Ok((body, children, manual_props)) - } -} - impl ToTokens for Component { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; @@ -219,9 +155,7 @@ pub struct ComponentField { enum ContentField { ManExpr(Expr), - OnHandler(ExprClosure), - // A handler was provided in {} tokens OnHandlerRaw(Expr), } @@ -229,9 +163,6 @@ impl ToTokens for ContentField { fn to_tokens(&self, tokens: &mut TokenStream2) { match self { ContentField::ManExpr(e) => e.to_tokens(tokens), - ContentField::OnHandler(e) => tokens.append_all(quote! { - __cx.bump().alloc(#e) - }), ContentField::OnHandlerRaw(e) => tokens.append_all(quote! { __cx.bump().alloc(#e) }), @@ -246,13 +177,7 @@ impl Parse for ComponentField { let name_str = name.to_string(); let content = if name_str.starts_with("on") { - if input.peek(token::Brace) { - let content; - syn::braced!(content in input); - ContentField::OnHandlerRaw(content.parse()?) - } else { - ContentField::OnHandler(input.parse()?) - } + ContentField::OnHandlerRaw(input.parse()?) } else { ContentField::ManExpr(input.parse::()?) }; diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index babaee825..cb0b7bdaa 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseBuffer, ParseStream}, - token, Expr, ExprClosure, Ident, LitStr, Result, Token, + Expr, Ident, LitStr, Result, Token, }; // ======================================= @@ -33,45 +33,69 @@ impl Parse for Element { let mut key = None; let mut _el_ref = None; - // todo: more descriptive error handling - while !content.is_empty() { + // parse fields with commas + // break when we don't get this pattern anymore + // start parsing bodynodes + // "def": 456, + // abc: 123, + loop { + // Parse the raw literal fields + if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) { + let name = content.parse::()?; + let ident = name.clone(); + + content.parse::()?; + + if content.peek(LitStr) { + let value = content.parse::()?; + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::CustomAttrText { name, value }, + }); + } else { + let value = content.parse::()?; + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::CustomAttrExpression { name, value }, + }); + } + + if content.is_empty() { + break; + } + + // todo: add a message saying you need to include commas between fields + if content.parse::().is_err() { + proc_macro_error::emit_error!( + ident, + "This attribute is misisng a trailing comma" + ) + } + continue; + } + if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { let name = content.parse::()?; + let ident = name.clone(); + let name_str = name.to_string(); content.parse::()?; if name_str.starts_with("on") { - if content.peek(token::Brace) { - let mycontent; - syn::braced!(mycontent in content); - - listeners.push(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr::EventTokens { - name, - tokens: mycontent.parse()?, - }, - }); - } else { - listeners.push(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr::EventClosure { - name, - closure: content.parse()?, - }, - }); - }; + listeners.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::EventTokens { + name, + tokens: content.parse()?, + }, + }); } else { match name_str.as_str() { "key" => { key = Some(content.parse()?); } - "classes" => { - todo!("custom class list not supported") - } - "namespace" => { - todo!("custom namespace not supported") - } + "classes" => todo!("custom class list not supported yet"), + // "namespace" => todo!("custom namespace not supported yet"), "node_ref" => { _el_ref = Some(content.parse::()?); } @@ -96,27 +120,56 @@ impl Parse for Element { } } } - } else if content.peek(LitStr) && content.peek2(Token![:]) { - let name = content.parse::()?; - content.parse::()?; - if content.peek(LitStr) { - let value = content.parse::()?; - attributes.push(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr::CustomAttrText { name, value }, - }); - } else { - let value = content.parse::()?; - attributes.push(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr::CustomAttrExpression { name, value }, - }); + if content.is_empty() { + break; } - } else { - children.push(content.parse::()?); + + // todo: add a message saying you need to include commas between fields + if content.parse::().is_err() { + proc_macro_error::emit_error!( + ident, + "This attribute is misisng a trailing comma" + ) + } + continue; } + break; + } + + while !content.is_empty() { + if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) { + let ident = content.parse::().unwrap(); + let name = ident.value(); + proc_macro_error::emit_error!( + ident, "This attribute `{}` is in the wrong place.", name; + help = +"All attribute fields must be placed above children elements. + + div { + attr: \"...\", <---- attribute is above children + div { } <---- children are below attributes + }"; + ) + } + + if (content.peek(Ident) && content.peek2(Token![:])) && !content.peek3(Token![:]) { + let ident = content.parse::().unwrap(); + let name = ident.to_string(); + proc_macro_error::emit_error!( + ident, "This attribute `{}` is in the wrong place.", name; + help = +"All attribute fields must be placed above children elements. + + div { + attr: \"...\", <---- attribute is above children + div { } <---- children are below attributes + }"; + ) + } + + children.push(content.parse::()?); // consume comma if it exists // we don't actually care if there *are* commas after elements/text if content.peek(Token![,]) { @@ -138,7 +191,7 @@ impl Parse for Element { impl ToTokens for Element { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; - let childs = &self.children; + let children = &self.children; let listeners = &self.listeners; let attr = &self.attributes; @@ -153,7 +206,7 @@ impl ToTokens for Element { dioxus_elements::#name, [ #(#listeners),* ], [ #(#attr),* ], - [ #(#childs),* ], + [ #(#children),* ], #key, ) }); @@ -173,8 +226,8 @@ enum ElementAttr { // "attribute": true, CustomAttrExpression { name: LitStr, value: Expr }, - // onclick: move |_| {} - EventClosure { name: Ident, closure: ExprClosure }, + // // onclick: move |_| {} + // EventClosure { name: Ident, closure: ExprClosure }, // onclick: {} EventTokens { name: Ident, tokens: Expr }, @@ -189,7 +242,7 @@ impl ToTokens for ElementAttrNamed { fn to_tokens(&self, tokens: &mut TokenStream2) { let ElementAttrNamed { el_name, attr } = self; - let toks = match attr { + tokens.append_all(match attr { ElementAttr::AttrText { name, value } => { quote! { dioxus_elements::#el_name.#name(__cx, format_args_f!(#value)) @@ -200,26 +253,26 @@ impl ToTokens for ElementAttrNamed { dioxus_elements::#el_name.#name(__cx, #value) } } - ElementAttr::CustomAttrText { name, value } => { - quote! { __cx.attr( #name, format_args_f!(#value), None, false ) } - } - ElementAttr::CustomAttrExpression { name, value } => { - quote! { __cx.attr( #name, format_args_f!(#value), None, false ) } - } - - ElementAttr::EventClosure { name, closure } => { quote! { - dioxus_elements::on::#name(__cx, #closure) + __cx.attr( #name, format_args_f!(#value), None, false ) } } + ElementAttr::CustomAttrExpression { name, value } => { + quote! { + __cx.attr( #name, format_args_f!(#value), None, false ) + } + } + // ElementAttr::EventClosure { name, closure } => { + // quote! { + // dioxus_elements::on::#name(__cx, #closure) + // } + // } ElementAttr::EventTokens { name, tokens } => { quote! { dioxus_elements::on::#name(__cx, #tokens) } } - }; - - tokens.append_all(toks); + }); } } diff --git a/packages/core-macro/src/rsx/fragment.rs b/packages/core-macro/src/rsx/fragment.rs deleted file mode 100644 index 9743b1501..000000000 --- a/packages/core-macro/src/rsx/fragment.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Parse `Fragments` into the Fragment VNode -//! ========================================== -//! -//! This parsing path emerges from [`AmbiguousElement`] which supports validation of the Fragment format. -//! We can be reasonably sure that whatever enters this parsing path is in the right format. -//! This feature must support: -//! - [x] Optional commas -//! - [ ] Children -//! - [ ] Keys - -use super::AmbiguousElement; -use syn::parse::ParseBuffer; -use { - proc_macro2::TokenStream as TokenStream2, - quote::{quote, ToTokens, TokenStreamExt}, - syn::{ - parse::{Parse, ParseStream}, - Ident, Result, Token, - }, -}; - -pub struct Fragment { - children: Vec, -} - -impl Parse for Fragment { - fn parse(input: ParseStream) -> Result { - input.parse::()?; - - let children = Vec::new(); - - // parse the guts - let content: ParseBuffer; - syn::braced!(content in input); - while !content.is_empty() { - content.parse::()?; - - if content.peek(Token![,]) { - let _ = content.parse::(); - } - } - Ok(Self { children }) - } -} - -impl ToTokens for Fragment { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let childs = &self.children; - let children = quote! { - ChildrenList::new(__cx) - #( .add_child(#childs) )* - .finish() - }; - tokens.append_all(quote! { - // #key_token, - dioxus::builder::vfragment( - __cx, - None, - #children - ) - }) - } -} diff --git a/packages/core-macro/src/rsx/mod.rs b/packages/core-macro/src/rsx/mod.rs index 5a0592b36..5ff5d85f7 100644 --- a/packages/core-macro/src/rsx/mod.rs +++ b/packages/core-macro/src/rsx/mod.rs @@ -11,17 +11,85 @@ //! //! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful. -mod ambiguous; -mod body; mod component; mod element; -mod fragment; mod node; // Re-export the namespaces into each other -pub use ambiguous::*; -pub use body::*; pub use component::*; pub use element::*; -pub use fragment::*; pub use node::*; + +// imports +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::{ + parse::{Parse, ParseStream}, + Ident, Result, Token, +}; + +pub struct CallBody { + custom_context: Option, + roots: Vec, +} + +impl Parse for CallBody { + fn parse(input: ParseStream) -> Result { + let custom_context = if input.peek(Ident) && input.peek2(Token![,]) { + let name = input.parse::()?; + input.parse::()?; + + Some(name) + } else { + None + }; + + let mut roots = Vec::new(); + + while !input.is_empty() { + let node = input.parse::()?; + + if input.peek(Token![,]) { + let _ = input.parse::(); + } + + roots.push(node); + } + + Ok(Self { + custom_context, + roots, + }) + } +} + +/// Serialize the same way, regardless of flavor +impl ToTokens for CallBody { + fn to_tokens(&self, out_tokens: &mut TokenStream2) { + let inner = if self.roots.len() == 1 { + let inner = &self.roots[0]; + quote! { #inner } + } else { + let childs = &self.roots; + quote! { __cx.fragment_root([ #(#childs),* ]) } + }; + + match &self.custom_context { + // The `in cx` pattern allows directly rendering + Some(ident) => out_tokens.append_all(quote! { + #ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode { + use dioxus_elements::{GlobalAttributes, SvgAttributes}; + #inner + })) + }), + + // Otherwise we just build the LazyNode wrapper + None => out_tokens.append_all(quote! { + LazyNodes::new_some(move |__cx: NodeFactory| -> VNode { + use dioxus_elements::{GlobalAttributes, SvgAttributes}; + #inner + }) + }), + }; + } +} diff --git a/packages/core-macro/src/rsx/node.rs b/packages/core-macro/src/rsx/node.rs index e027711e6..d5d701767 100644 --- a/packages/core-macro/src/rsx/node.rs +++ b/packages/core-macro/src/rsx/node.rs @@ -4,34 +4,67 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, - token, Expr, LitStr, Result, + token, Expr, LitStr, Result, Token, }; -// ============================================== -// Parse any div {} as a VElement -// ============================================== +/* +Parse +-> div {} +-> Component {} +-> component() +-> "text {with_args}" +-> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr +*/ pub enum BodyNode { - Element(AmbiguousElement), - Text(TextNode), + Element(Element), + Component(Component), + Text(LitStr), RawExpr(Expr), } impl Parse for BodyNode { fn parse(stream: ParseStream) -> Result { - // Supposedly this approach is discouraged due to inability to return proper errors - // TODO: Rework this to provide more informative errors - - if stream.peek(token::Brace) { - let content; - syn::braced!(content in stream); - return Ok(BodyNode::RawExpr(content.parse::()?)); - } - if stream.peek(LitStr) { - return Ok(BodyNode::Text(stream.parse::()?)); + return Ok(BodyNode::Text(stream.parse()?)); } - Ok(BodyNode::Element(stream.parse::()?)) + // div {} -> el + // Div {} -> comp + if stream.peek(syn::Ident) && stream.peek2(token::Brace) { + if stream + .fork() + .parse::()? + .to_string() + .chars() + .next() + .unwrap() + .is_ascii_uppercase() + { + return Ok(BodyNode::Component(stream.parse()?)); + } else { + return Ok(BodyNode::Element(stream.parse::()?)); + } + } + + // component() -> comp + // ::component {} -> comp + // ::component () -> comp + if (stream.peek(syn::Ident) && stream.peek2(token::Paren)) + || (stream.peek(Token![::])) + || (stream.peek(Token![:]) && stream.peek2(Token![:])) + { + return Ok(BodyNode::Component(stream.parse::()?)); + } + + // crate::component{} -> comp + // crate::component() -> comp + if let Ok(pat) = stream.fork().parse::() { + if pat.segments.len() > 1 { + return Ok(BodyNode::Component(stream.parse::()?)); + } + } + + Ok(BodyNode::RawExpr(stream.parse::()?)) } } @@ -39,31 +72,13 @@ impl ToTokens for BodyNode { fn to_tokens(&self, tokens: &mut TokenStream2) { match &self { BodyNode::Element(el) => el.to_tokens(tokens), - BodyNode::Text(txt) => txt.to_tokens(tokens), + BodyNode::Component(comp) => comp.to_tokens(tokens), + BodyNode::Text(txt) => tokens.append_all(quote! { + __cx.text(format_args_f!(#txt)) + }), BodyNode::RawExpr(exp) => tokens.append_all(quote! { __cx.fragment_from_iter(#exp) }), } } } - -// ======================================= -// Parse just plain text -// ======================================= -pub struct TextNode(LitStr); - -impl Parse for TextNode { - fn parse(s: ParseStream) -> Result { - Ok(Self(s.parse()?)) - } -} - -impl ToTokens for TextNode { - fn to_tokens(&self, tokens: &mut TokenStream2) { - // todo: use heuristics to see if we can promote to &static str - let token_stream = &self.0.to_token_stream(); - tokens.append_all(quote! { - __cx.text(format_args_f!(#token_stream)) - }); - } -} diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 2ebd1e250..36cca7f0c 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -734,9 +734,9 @@ impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> { } } -impl IntoVNode<'_> for &'static str { +impl<'b> IntoVNode<'_> for &'b str { fn into_vnode(self, cx: NodeFactory) -> VNode { - cx.static_text(self) + cx.text(format_args!("{}", self)) } } diff --git a/packages/core/tests/vdom_rebuild.rs b/packages/core/tests/vdom_rebuild.rs index 21619d9c2..5d1dbcdf8 100644 --- a/packages/core/tests/vdom_rebuild.rs +++ b/packages/core/tests/vdom_rebuild.rs @@ -44,7 +44,7 @@ fn lists_work() { static App: Component = |cx| { cx.render(rsx!( h1 {"hello"} - {(0..6).map(|f| rsx!(span{ "{f}" }))} + (0..6).map(|f| rsx!(span{ "{f}" })) )) }; let mut vdom = VirtualDom::new(App); diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 6ef67efcb..9a02d6bcb 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -215,3 +215,67 @@ impl<'a, O, T: std::ops::Not + Copy> std::ops::Not for UseState<'a, !*self.get() } } + +/* + +Convenience methods for UseState. + +Note! + +This is not comprehensive. +This is *just* meant to make common operations easier. +*/ + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +impl<'a, T: Copy + Add> Add for UseState<'a, T> { + type Output = T; + + fn add(self, rhs: T) -> Self::Output { + self.inner.current_val.add(rhs) + } +} +impl<'a, T: Copy + Add> AddAssign for UseState<'a, T> { + fn add_assign(&mut self, rhs: T) { + self.set(self.inner.current_val.add(rhs)); + } +} +impl<'a, T: Copy + Sub> Sub for UseState<'a, T> { + type Output = T; + + fn sub(self, rhs: T) -> Self::Output { + self.inner.current_val.sub(rhs) + } +} +impl<'a, T: Copy + Sub> SubAssign for UseState<'a, T> { + fn sub_assign(&mut self, rhs: T) { + self.set(self.inner.current_val.sub(rhs)); + } +} + +/// MUL +impl<'a, T: Copy + Mul> Mul for UseState<'a, T> { + type Output = T; + + fn mul(self, rhs: T) -> Self::Output { + self.inner.current_val.mul(rhs) + } +} +impl<'a, T: Copy + Mul> MulAssign for UseState<'a, T> { + fn mul_assign(&mut self, rhs: T) { + self.set(self.inner.current_val.mul(rhs)); + } +} +/// DIV +impl<'a, T: Copy + Div> Div for UseState<'a, T> { + type Output = T; + + fn div(self, rhs: T) -> Self::Output { + self.inner.current_val.div(rhs) + } +} +impl<'a, T: Copy + Div> DivAssign for UseState<'a, T> { + fn div_assign(&mut self, rhs: T) { + self.set(self.inner.current_val.div(rhs)); + } +} diff --git a/packages/router/src/link.rs b/packages/router/src/link.rs index 818d2d283..dd8e683d9 100644 --- a/packages/router/src/link.rs +++ b/packages/router/src/link.rs @@ -41,8 +41,8 @@ pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element { a { href: "#", class: format_args!("{}", cx.props.class.unwrap_or("")), - {&cx.props.children} onclick: move |_| service.push_route(cx.props.to.clone()), + {&cx.props.children} } }) } diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 23e93084f..aefd2062e 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -309,19 +309,18 @@ mod tests { static SLIGHTLY_MORE_COMPLEX: Component = |cx| { cx.render(rsx! { - div { - title: "About W3Schools" - {(0..20).map(|f| rsx!{ + div { title: "About W3Schools", + (0..20).map(|f| rsx!{ div { - title: "About W3Schools" - style: "color:blue;text-align:center" - class: "About W3Schools" + title: "About W3Schools", + style: "color:blue;text-align:center", + class: "About W3Schools", p { - title: "About W3Schools" + title: "About W3Schools", "Hello world!: {f}" } } - })} + }) } }) }; From d1560450bac55f9566e00e00ea405bd1c70b57e5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 2 Jan 2022 02:15:04 -0500 Subject: [PATCH 8/8] wip: remove runner on hook and then update docs --- .vscode/spellright.dict | 1 + README.md | 10 +- docs/guide/src/README.md | 21 +-- docs/guide/src/SUMMARY.md | 111 +++--------- .../{concepts => advanced-guides}/00-index.md | 0 .../06-subscription-api.md | 0 .../10-concurrent-mode.md | 0 .../11-arena-memo.md | 0 .../12-signals.md | 0 .../13-subtrees.md | 0 .../src/{concepts => advanced-guides}/rsx.md | 0 .../rsx_in_depth.md | 0 .../{concepts => advanced-guides}/testing.md | 2 +- docs/guide/src/async/asynctasks.md | 90 ++++++++++ docs/guide/src/async/index.md | 19 ++ docs/guide/src/concepts/async.md | 1 - docs/guide/src/concepts/asynccallbacks.md | 1 - docs/guide/src/concepts/asynctasks.md | 1 - docs/guide/src/concepts/bundline.md | 1 - docs/guide/src/concepts/custom_elements.md | 1 - docs/guide/src/concepts/custom_renderer.md | 1 - docs/guide/src/concepts/effects.md | 1 - docs/guide/src/concepts/event_handlers.md | 11 -- docs/guide/src/concepts/managing_state.md | 1 - docs/guide/src/concepts/memoization.md | 1 - .../src/concepts/server_side_components.md | 1 - docs/guide/src/concepts/suspense.md | 54 ------ docs/guide/src/concepts/usestate.md | 1 - .../component_children.md | 44 ++--- .../src/{concepts => elements}/components.md | 40 ++++- .../conditional_rendering.md | 25 +-- .../exporting_components.md | 32 ++-- docs/guide/src/elements/index.md | 73 ++++++++ .../guide/src/{concepts => elements}/lists.md | 0 .../src/{concepts => elements}/vnodes.md | 28 ++- docs/guide/src/final.md | 49 ++++++ docs/guide/src/final/index.md | 10 -- docs/guide/src/hello_world.md | 10 +- docs/guide/src/images/publish.png | Bin 0 -> 278470 bytes .../guide/src/interactivity/event_handlers.md | 1 + .../src/{concepts => interactivity}/hooks.md | 20 ++- .../index.md} | 2 +- .../{concepts => interactivity}/lifecycles.md | 0 .../{concepts => interactivity}/user_input.md | 0 docs/guide/src/setup.md | 2 +- .../src/{concepts => state}/errorhandling.md | 0 docs/guide/src/state/index.md | 8 + docs/guide/src/state/liftingstate.md | 1 + docs/guide/src/state/localstate.md | 1 + .../src/{concepts => state}/sharedstate.md | 0 docs/guide/src/tutorial/publishing.md | 64 +++++++ docs/reference/src/SUMMARY.md | 61 +++++++ examples/borrowed.rs | 2 +- examples/calculator.rs | 2 +- examples/crm.rs | 3 +- examples/desktop/todomvc.rs | 2 +- examples/framework_benchmark.rs | 1 - examples/hydration.rs | 3 +- examples/inputs.rs | 150 ++++++++++++++++ examples/pattern_model.rs | 14 +- examples/router.rs | 4 +- examples/rsx_compile_fail.rs | 1 + examples/weather_app.rs | 16 +- examples/xss_safety.rs | 9 +- notes/SUSPENSE.md | 6 +- packages/core-macro/src/rsx/component.rs | 2 +- packages/core/benches/jsframework.rs | 24 +-- packages/core/examples/component_children.rs | 6 +- packages/core/examples/works.rs | 2 +- packages/core/src/diff.rs | 31 ++-- packages/core/src/lib.rs | 8 +- packages/core/src/scopes.rs | 30 ++-- packages/core/src/virtual_dom.rs | 104 +++++++++++ packages/core/tests/borrowedstate.rs | 2 +- packages/core/tests/earlyabort.rs | 2 +- packages/core/tests/lifecycle.rs | 6 +- packages/core/tests/miri_stress.rs | 12 +- packages/desktop/src/desktop_context.rs | 9 +- packages/desktop/src/index.html | 13 +- packages/desktop/src/index.js | 27 ++- packages/hooks/src/use_shared_state.rs | 73 ++++---- packages/hooks/src/usecoroutine.rs | 83 +++++---- packages/hooks/src/usefuture.rs | 62 ++++--- packages/hooks/src/usemodel.rs | 40 ++--- packages/hooks/src/useref.rs | 19 +- packages/hooks/src/usestate.rs | 44 +++-- packages/hooks/src/usesuspense.rs | 47 +++-- packages/html/src/elements.rs | 5 +- packages/html/src/events.rs | 12 +- packages/html/src/global_attributes.rs | 5 +- packages/router/src/platform/web.rs | 23 ++- packages/router/src/userouter.rs | 41 ++--- packages/ssr/README.md | 81 ++++++++- packages/ssr/src/lib.rs | 22 +-- src/lib.rs | 165 ++++++++++++++++-- 95 files changed, 1339 insertions(+), 675 deletions(-) rename docs/guide/src/{concepts => advanced-guides}/00-index.md (100%) rename docs/guide/src/{concepts => advanced-guides}/06-subscription-api.md (100%) rename docs/guide/src/{concepts => advanced-guides}/10-concurrent-mode.md (100%) rename docs/guide/src/{concepts => advanced-guides}/11-arena-memo.md (100%) rename docs/guide/src/{concepts => advanced-guides}/12-signals.md (100%) rename docs/guide/src/{concepts => advanced-guides}/13-subtrees.md (100%) rename docs/guide/src/{concepts => advanced-guides}/rsx.md (100%) rename docs/guide/src/{concepts => advanced-guides}/rsx_in_depth.md (100%) rename docs/guide/src/{concepts => advanced-guides}/testing.md (98%) create mode 100644 docs/guide/src/async/asynctasks.md create mode 100644 docs/guide/src/async/index.md delete mode 100644 docs/guide/src/concepts/async.md delete mode 100644 docs/guide/src/concepts/asynccallbacks.md delete mode 100644 docs/guide/src/concepts/asynctasks.md delete mode 100644 docs/guide/src/concepts/bundline.md delete mode 100644 docs/guide/src/concepts/custom_elements.md delete mode 100644 docs/guide/src/concepts/custom_renderer.md delete mode 100644 docs/guide/src/concepts/effects.md delete mode 100644 docs/guide/src/concepts/event_handlers.md delete mode 100644 docs/guide/src/concepts/managing_state.md delete mode 100644 docs/guide/src/concepts/memoization.md delete mode 100644 docs/guide/src/concepts/server_side_components.md delete mode 100644 docs/guide/src/concepts/suspense.md delete mode 100644 docs/guide/src/concepts/usestate.md rename docs/guide/src/{concepts => elements}/component_children.md (86%) rename docs/guide/src/{concepts => elements}/components.md (87%) rename docs/guide/src/{concepts => elements}/conditional_rendering.md (88%) rename docs/guide/src/{concepts => elements}/exporting_components.md (86%) create mode 100644 docs/guide/src/elements/index.md rename docs/guide/src/{concepts => elements}/lists.md (100%) rename docs/guide/src/{concepts => elements}/vnodes.md (79%) create mode 100644 docs/guide/src/final.md delete mode 100644 docs/guide/src/final/index.md create mode 100644 docs/guide/src/images/publish.png create mode 100644 docs/guide/src/interactivity/event_handlers.md rename docs/guide/src/{concepts => interactivity}/hooks.md (89%) rename docs/guide/src/{concepts/interactivity.md => interactivity/index.md} (99%) rename docs/guide/src/{concepts => interactivity}/lifecycles.md (100%) rename docs/guide/src/{concepts => interactivity}/user_input.md (100%) rename docs/guide/src/{concepts => state}/errorhandling.md (100%) create mode 100644 docs/guide/src/state/index.md create mode 100644 docs/guide/src/state/liftingstate.md create mode 100644 docs/guide/src/state/localstate.md rename docs/guide/src/{concepts => state}/sharedstate.md (100%) create mode 100644 examples/inputs.rs diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index 8f587cea4..22e58619e 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -67,3 +67,4 @@ SegVec contentful Jank noderef +reborrow diff --git a/README.md b/README.md index 0573fa040..7c171a686 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count -= 1, "Down low!" } )) -}; +} ``` Dioxus can be used to deliver webapps, desktop apps, static sites, liveview apps, mobile apps (WIP), and more. At its core, Dioxus is entirely renderer agnostic and has great documentation for creating new renderers for any platform. @@ -73,8 +73,8 @@ If you know React, then you already know Dioxus. Web Desktop Mobile + TUI State - Docs Tools @@ -83,9 +83,9 @@ If you know React, then you already know Dioxus. ## Examples Projects: -| File Navigator (Desktop) | WiFi scanner (Desktop) | TodoMVC (All platforms) | Ecommerce w/ Tailwind (Liveview) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [![File Explorer](https://github.com/DioxusLabs/example-projects/raw/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer) | [![Wifi Scanner Demo](https://github.com/DioxusLabs/example-projects/raw/master/wifi-scanner/demo_small.png)](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner) | [![TodoMVC example](https://github.com/DioxusLabs/example-projects/raw/master/todomvc/example.png)](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) | [![Ecommerce Example](https://github.com/DioxusLabs/example-projects/raw/master/ecommerce-site/demo.png)](https://github.com/DioxusLabs/example-projects/blob/master/ecommerce-site) | +| File Navigator (Desktop) | WiFi scanner (Desktop) | TodoMVC (All platforms) | E-commerce w/ Tailwind (Liveview) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [![File Explorer](https://github.com/DioxusLabs/example-projects/raw/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer) | [![Wifi Scanner Demo](https://github.com/DioxusLabs/example-projects/raw/master/wifi-scanner/demo_small.png)](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner) | [![TodoMVC example](https://github.com/DioxusLabs/example-projects/raw/master/todomvc/example.png)](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) | [![E-commerce Example](https://github.com/DioxusLabs/example-projects/raw/master/ecommerce-site/demo.png)](https://github.com/DioxusLabs/example-projects/blob/master/ecommerce-site) | See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem. diff --git a/docs/guide/src/README.md b/docs/guide/src/README.md index ab2cc0403..c91f9f1bb 100644 --- a/docs/guide/src/README.md +++ b/docs/guide/src/README.md @@ -18,7 +18,7 @@ fn App(cx: Scope) -> Element { In general, Dioxus and React share many functional similarities. If this guide is lacking in any general concept or an error message is confusing, React's documentation might be more helpful. We are dedicated to providing a *familiar* toolkit for UI in Rust, so we've chosen to follow in the footsteps of popular UI frameworks (React, Redux, etc). If you know React, then you already know Dioxus. If you don't know either, this guide will still help you! -> This is introduction book! For advanced topics, check out the [Reference](https://dioxuslabs.com/reference) instead. +> This is an introduction book! For advanced topics, check out the [Reference](https://dioxuslabs.com/reference) instead. ## Multiplatform @@ -29,6 +29,7 @@ Right now, we have several 1st-party renderers: - Tao/Tokio (for Desktop apps) - Tao/Tokio (for Mobile apps) - SSR (for generating static markup) +- TUI/Rink (for terminal-based apps) ### Web Support --- @@ -40,12 +41,10 @@ Because the web is a fairly mature platform, we expect there to be very little A [Jump to the getting started guide for the web.]() Examples: -- [TodoMVC](https://github.com/dioxusLabs/todomvc/) -- [ECommerce]() -- [Photo Editor]() - -[![TODOMVC](https://github.com/DioxusLabs/todomvc/raw/master/example.png)](https://github.com/dioxusLabs/todomvc/) +- [TodoMVC](https://github.com/DioxusLabs/example-projects/tree/master/todomvc) +- [ECommerce](https://github.com/DioxusLabs/example-projects/tree/master/ecommerce-site) +[![TodoMVC example](https://github.com/DioxusLabs/example-projects/raw/master/todomvc/example.png)](https://github.com/DioxusLabs/example-projects/blob/master/todomvc) ### SSR Support --- Dioxus supports server-side rendering! @@ -59,7 +58,7 @@ let contents = dioxus::ssr::render_vdom(&dom); [Jump to the getting started guide for SSR.]() Examples: -- [Example DocSite]() +- [Example DocSite](https://github.com/dioxusLabs/docsite) - [Tide WebServer]() - [Markdown to fancy HTML generator]() @@ -72,9 +71,8 @@ Desktop APIs will likely be in flux as we figure out better patterns than our El [Jump to the getting started guide for Desktop.]() Examples: -- [File explorer]() -- [Bluetooth scanner]() -- [Device Viewer]() +- [File explorer](https://github.com/dioxusLabs/file-explorer/) +- [WiFi scanner](https://github.com/DioxusLabs/example-projects/blob/master/wifi-scanner) [![File ExplorerExample](https://github.com/DioxusLabs/file-explorer-example/raw/master/image.png)](https://github.com/dioxusLabs/file-explorer/) @@ -87,8 +85,7 @@ Mobile support is currently best suited for CRUD-style apps, ideally for interna [Jump to the getting started guide for Mobile.]() Examples: -- [Todo App]() -- [Chat App]() +- [Todo App](https://github.com/DioxusLabs/example-projects/blob/master/ios_demo) ### LiveView / Server Component Support --- diff --git a/docs/guide/src/SUMMARY.md b/docs/guide/src/SUMMARY.md index 4149008c8..8f9183dc2 100644 --- a/docs/guide/src/SUMMARY.md +++ b/docs/guide/src/SUMMARY.md @@ -3,96 +3,39 @@ - [Introduction](README.md) - [Getting Setup](setup.md) - [Hello, World!](hello_world.md) -- [Describing the UI](concepts/00-index.md) - - [Intro to Elements](concepts/vnodes.md) - - [Intro to Components](concepts/components.md) - - [Reusing, Importing, and Exporting Components](concepts/exporting_components.md) - - [Passing children and attributes](concepts/component_children.md) - - [Conditional Rendering](concepts/conditional_rendering.md) - - [Lists](concepts/lists.md) -- [Adding Interactivity](concepts/interactivity.md) - - [Hooks and Internal State](concepts/hooks.md) - - [Event handlers](concepts/event_handlers.md) - - [User Input and Controlled Components](concepts/user_input.md) - - [Lifecycle, updates, and effects](concepts/lifecycles.md) - - - - -- [Managing State](concepts/managing_state.md) - - [Global State](concepts/sharedstate.md) - - [Error handling](concepts/errorhandling.md) - - [Effects](concepts/effects.md) -- [Working with Async](concepts/async.md) - - [Tasks](concepts/asynctasks.md) - - [Suspense](concepts/suspense.md) - - [Async Callbacks](concepts/asynccallbacks.md) -- [Putting it all together](tutorial/index.md) +- [Describing the UI](elements/index.md) + - [Intro to Elements](elements/vnodes.md) + - [Intro to Components](elements/components.md) + - [Reusing, Importing, and Exporting Components](elements/exporting_components.md) + - [Passing children and attributes](elements/component_children.md) + - [Conditional Rendering](elements/conditional_rendering.md) + - [Lists](elements/lists.md) +- [Adding Interactivity](interactivity/index.md) + - [Hooks and Internal State](interactivity/hooks.md) + - [Event handlers](interactivity/event_handlers.md) + - [User Input and Controlled Components](interactivity/user_input.md) + - [Lifecycle, updates, and effects](interactivity/lifecycles.md) +- [Managing State](state/index.md) + - [Local State](state/localstate.md) + - [Lifting State](state/liftingstate.md) + - [Global State](state/sharedstate.md) + - [Error handling](state/errorhandling.md) +- [Working with Async](async/index.md) + - [Tasks](async/asynctasks.md) +- [Putting it all together: Dog Search Engine](tutorial/index.md) - [New app](tutorial/new_app.md) - [Structuring our app](tutorial/structure.md) - [Defining State](tutorial/state.md) - [Defining Components](tutorial/components.md) - [Styling](tutorial/styling.md) - - [Publishing](tutorial/publishing.md) -- [Next Steps and Advanced Topics](final/index.md) - + - [Bundling](tutorial/publishing.md) +- [Next Steps and Advanced Topics](final.md) + ----------- [Contributors](misc/contributors.md) + + + + diff --git a/docs/guide/src/concepts/00-index.md b/docs/guide/src/advanced-guides/00-index.md similarity index 100% rename from docs/guide/src/concepts/00-index.md rename to docs/guide/src/advanced-guides/00-index.md diff --git a/docs/guide/src/concepts/06-subscription-api.md b/docs/guide/src/advanced-guides/06-subscription-api.md similarity index 100% rename from docs/guide/src/concepts/06-subscription-api.md rename to docs/guide/src/advanced-guides/06-subscription-api.md diff --git a/docs/guide/src/concepts/10-concurrent-mode.md b/docs/guide/src/advanced-guides/10-concurrent-mode.md similarity index 100% rename from docs/guide/src/concepts/10-concurrent-mode.md rename to docs/guide/src/advanced-guides/10-concurrent-mode.md diff --git a/docs/guide/src/concepts/11-arena-memo.md b/docs/guide/src/advanced-guides/11-arena-memo.md similarity index 100% rename from docs/guide/src/concepts/11-arena-memo.md rename to docs/guide/src/advanced-guides/11-arena-memo.md diff --git a/docs/guide/src/concepts/12-signals.md b/docs/guide/src/advanced-guides/12-signals.md similarity index 100% rename from docs/guide/src/concepts/12-signals.md rename to docs/guide/src/advanced-guides/12-signals.md diff --git a/docs/guide/src/concepts/13-subtrees.md b/docs/guide/src/advanced-guides/13-subtrees.md similarity index 100% rename from docs/guide/src/concepts/13-subtrees.md rename to docs/guide/src/advanced-guides/13-subtrees.md diff --git a/docs/guide/src/concepts/rsx.md b/docs/guide/src/advanced-guides/rsx.md similarity index 100% rename from docs/guide/src/concepts/rsx.md rename to docs/guide/src/advanced-guides/rsx.md diff --git a/docs/guide/src/concepts/rsx_in_depth.md b/docs/guide/src/advanced-guides/rsx_in_depth.md similarity index 100% rename from docs/guide/src/concepts/rsx_in_depth.md rename to docs/guide/src/advanced-guides/rsx_in_depth.md diff --git a/docs/guide/src/concepts/testing.md b/docs/guide/src/advanced-guides/testing.md similarity index 98% rename from docs/guide/src/concepts/testing.md rename to docs/guide/src/advanced-guides/testing.md index 42f9af1d8..dc53feafd 100644 --- a/docs/guide/src/concepts/testing.md +++ b/docs/guide/src/advanced-guides/testing.md @@ -20,7 +20,7 @@ fn runs_in_browser() { Then, when you run -``` +```console $ dioxus test --chrome ``` diff --git a/docs/guide/src/async/asynctasks.md b/docs/guide/src/async/asynctasks.md new file mode 100644 index 000000000..bcb93633d --- /dev/null +++ b/docs/guide/src/async/asynctasks.md @@ -0,0 +1,90 @@ +# Tasks + +All async code in Dioxus must be explicit and handled through Dioxus' task system. + +In this chapter, we'll learn how to spawn new tasks through our `Scope`. + +## Spawning a task + +You can push any `'static` future into the Dioxus future queue by simply calling `cx.spawn` to spawn a task. Pushing a future returns a `TaskId` which can then be used to cancel the + +```rust +fn App(cx: Scope) -> Element { + cx.spawn(async { + let mut count = 0; + loop { + tokio::time::delay(std::instant::Duration::from_millis(500)).await; + count += 1; + println!("Current count is {}", count); + } + }); + + None +} +``` + +The future must be `'static` - so any values captured by the task must not carry any references to `cx`. All the Dioxus hooks have a method called `for_async` which will create a slightly more limited handle to the hook for you to use in your async code. + +```rust +fn App(cx: Scope) -> Element { + let mut count = use_state(&cx, || 0); + + let taskid = cx.spawn({ + let mut count = count.for_async(); + async { + loop { + tokio::time::delay(std::instant::Duration::from_millis(500)).await; + count += 1; + println!("Current count is {}", count); + } + } + }); +} +``` + +The task will run in the background until it is completed. + +> Note: `spawn` will always spawn a *new* future. You probably want to call it from a hook initializer instead of the main body of your component. + +When bringing lots of values into your task, we provide the `for_async!` macro which will can `for_async` on all values passed in. For types that implement `ToOwned`, `for_async!` will simply call `ToOwned` for that value. + +```rust +fn App(cx: Scope) -> Element { + let mut age = use_state(&cx, || 0); + let mut name = use_state(&cx, || "Bob"); + let mut description = use_state(&cx, || "asd".to_string()); + + let taskid = cx.spawn({ + for_async![count, name, description] + async { /* code that uses count/name/description */ } + }); +} +``` + +## Details of Tasks + +Calling `spawn` is *not* a hook and will *always* generate a new task. Make sure to only spawn tasks when you want to. You should *probably* not call `spawn` in the main body of your component, since a new task will be spawned on every render. + +## Spawning Tokio Tasks (for multithreaded use cases) + +Sometimes, you might want to spawn a background task that needs multiple threads or talk to hardware that might block your app code. In these cases, we can can directly spawn a `tokio task` from our future. For Dioxus-Desktop, your task will be spawned onto Tokio's Multithreaded runtime: + +```rust +cx.spawn({ + tokio::spawn(async { + // some multithreaded work + }).await; + + tokio::spawn_blocking(|| { + // some extremely blocking work + }).await; + + tokio::spawn_local(|| { + // some !Send work + }).await; +}) +``` + +> Note: Tokio tasks must be `Send`. Most hooks are `Send` compatible, but if they aren't, then you can use `spawn_local` to spawn onto Dioxus-Desktop's `localset`. + + diff --git a/docs/guide/src/async/index.md b/docs/guide/src/async/index.md new file mode 100644 index 000000000..41a8b9231 --- /dev/null +++ b/docs/guide/src/async/index.md @@ -0,0 +1,19 @@ +# Working with Async + +Not all apps you'll build can be self-contained with synchronous code. You'll often need to interact with file systems, network interfaces, hardware, or timers. + +So far, we've only talked about building apps with synchronous code, so this chapter will focus integrating asynchronous code into your app. + + +## The Runtime + +By default, Dioxus-Desktop ships with the `Tokio` runtime and automatically sets everything up for you. + + + +## Send/Sync +Writing apps that deal with Send/Sync can be frustrating at times. Under the hood, Dioxus is not currently thread-safe, so any async code you write does *not* need to be `Send/Sync`. That means Cell/Rc/RefCell are all fair game. + + + +All async code in your app is polled on a `LocalSet`, so any async code we w diff --git a/docs/guide/src/concepts/async.md b/docs/guide/src/concepts/async.md deleted file mode 100644 index f6c3d103d..000000000 --- a/docs/guide/src/concepts/async.md +++ /dev/null @@ -1 +0,0 @@ -# Working with Async diff --git a/docs/guide/src/concepts/asynccallbacks.md b/docs/guide/src/concepts/asynccallbacks.md deleted file mode 100644 index 6fcf65b66..000000000 --- a/docs/guide/src/concepts/asynccallbacks.md +++ /dev/null @@ -1 +0,0 @@ -# Async Callbacks diff --git a/docs/guide/src/concepts/asynctasks.md b/docs/guide/src/concepts/asynctasks.md deleted file mode 100644 index a26beca9a..000000000 --- a/docs/guide/src/concepts/asynctasks.md +++ /dev/null @@ -1 +0,0 @@ -# Tasks diff --git a/docs/guide/src/concepts/bundline.md b/docs/guide/src/concepts/bundline.md deleted file mode 100644 index 963cf5490..000000000 --- a/docs/guide/src/concepts/bundline.md +++ /dev/null @@ -1 +0,0 @@ -# Bundling and Distributing diff --git a/docs/guide/src/concepts/custom_elements.md b/docs/guide/src/concepts/custom_elements.md deleted file mode 100644 index 3b24ad9fe..000000000 --- a/docs/guide/src/concepts/custom_elements.md +++ /dev/null @@ -1 +0,0 @@ -# Custom Elements diff --git a/docs/guide/src/concepts/custom_renderer.md b/docs/guide/src/concepts/custom_renderer.md deleted file mode 100644 index 577b4f5d3..000000000 --- a/docs/guide/src/concepts/custom_renderer.md +++ /dev/null @@ -1 +0,0 @@ -# Custom Renderer diff --git a/docs/guide/src/concepts/effects.md b/docs/guide/src/concepts/effects.md deleted file mode 100644 index 7b8ba3e3f..000000000 --- a/docs/guide/src/concepts/effects.md +++ /dev/null @@ -1 +0,0 @@ -# Effects diff --git a/docs/guide/src/concepts/event_handlers.md b/docs/guide/src/concepts/event_handlers.md deleted file mode 100644 index d10fddd09..000000000 --- a/docs/guide/src/concepts/event_handlers.md +++ /dev/null @@ -1,11 +0,0 @@ -# Handling Events - -In the overview for this section, we mentioned how we can modify the state of our component by responding to user-generated events inside of event listeners. - -In this section, we'll talk more about event listeners: -- What events are available -- Handling event data -- Mutability in event listeners - -## Event Listeners - diff --git a/docs/guide/src/concepts/managing_state.md b/docs/guide/src/concepts/managing_state.md deleted file mode 100644 index 931d66c2f..000000000 --- a/docs/guide/src/concepts/managing_state.md +++ /dev/null @@ -1 +0,0 @@ -# Managing State diff --git a/docs/guide/src/concepts/memoization.md b/docs/guide/src/concepts/memoization.md deleted file mode 100644 index 3ed477f68..000000000 --- a/docs/guide/src/concepts/memoization.md +++ /dev/null @@ -1 +0,0 @@ -# Memoization diff --git a/docs/guide/src/concepts/server_side_components.md b/docs/guide/src/concepts/server_side_components.md deleted file mode 100644 index a8c4f6182..000000000 --- a/docs/guide/src/concepts/server_side_components.md +++ /dev/null @@ -1 +0,0 @@ -# Server-side components diff --git a/docs/guide/src/concepts/suspense.md b/docs/guide/src/concepts/suspense.md deleted file mode 100644 index 6c81fa992..000000000 --- a/docs/guide/src/concepts/suspense.md +++ /dev/null @@ -1,54 +0,0 @@ -# Suspense - -Suspense in Dioxus is enabled through placeholder nodes. - -just a component that renders nodes into the placeholder after the future is finished? - -in react, suspense is just completely pausing diffing while a - -```rust -let n = use_suspense(cx || { - cx.render(rsx!{ - Suspense { - prom: fut, - callback: || {} - } - }) -}) - -suspense () { - let value = use_state(); - if first_render { - push_task({ - value.set(fut.await); - }); - } else { - callback(value) - } -} - - -let name = fetch_name().await; - - -function ProfileDetails() { - // Try to read user info, although it might not have loaded yet - const user = resource.read(); - return

{user.name}

; -} - - -fn ProfileDteails() { - let user = resource.suspend(cx, |l| rsx!("{l}")); - - // waits for the resource to be ready and updates the placeholder with the tree - let name = resource.suspend_with(cx, |val| rsx!( div { "hello" "{user}" } )); - - cx.render(rsx!( - div { - {user} - {name} - } - )) -} -``` diff --git a/docs/guide/src/concepts/usestate.md b/docs/guide/src/concepts/usestate.md deleted file mode 100644 index 86a6b2efc..000000000 --- a/docs/guide/src/concepts/usestate.md +++ /dev/null @@ -1 +0,0 @@ -# Fundamental Hooks and use_hook diff --git a/docs/guide/src/concepts/component_children.md b/docs/guide/src/elements/component_children.md similarity index 86% rename from docs/guide/src/concepts/component_children.md rename to docs/guide/src/elements/component_children.md index ed482b007..d8469241a 100644 --- a/docs/guide/src/concepts/component_children.md +++ b/docs/guide/src/elements/component_children.md @@ -30,7 +30,7 @@ struct ClickableProps<'a> { title: &'a str } -fn clickable(cx: Scope) -> Element { +fn Clickable(cx: Scope) -> Element { cx.render(rsx!( a { href: "{cx.props.href}" @@ -44,10 +44,10 @@ And then use it in our code like so: ```rust rsx!( - clickable( + Clickable { href: "https://google.com" title: "Link to Google" - ) + } ) ``` @@ -64,11 +64,11 @@ struct ClickableProps<'a> { body: Element<'a> } -fn clickable(cx: Scope) -> Element { +fn Clickable(cx: Scope) -> Element { cx.render(rsx!( a { - href: "{cx.props.href}" - {&cx.props.body} + href: "{cx.props.href}", + &cx.props.body } )) } @@ -78,15 +78,17 @@ Then, at the call site, we can render some nodes and pass them in: ```rust rsx!( - clickable( + Clickable { href: "https://google.com" body: cx.render(rsx!( - img { src: "https://www.google.com/logos/doodles/2021/seasonal-holidays-2021-6753651837109324-6752733080595603-cst.gif" } + img { src: "https://www.google.com/logos/doodles/..." } )) - ) + } ) ``` +## Auto Conversion of the `Children` field + This pattern can become tedious in some instances, so Dioxus actually performs an implicit conversion of any `rsx` calls inside components into `Elements` at the `children` field. This means you must explicitly declare if a component can take children. ```rust @@ -99,26 +101,26 @@ struct ClickableProps<'a> { fn clickable(cx: Scope) -> Element { cx.render(rsx!( a { - href: "{cx.props.href}" - {&cx.props.children} + href: "{cx.props.href}", + &cx.props.children } )) } ``` -And to call `clickable`: +Now, whenever we use `Clickable` in another component, we don't need to call `render` on child nodes - it will happen automatically! ```rust rsx!( - clickable( + Clickable { href: "https://google.com" - img { src: "https://www.google.com/logos/doodles/2021/seasonal-holidays-2021-6753651837109324-6752733080595603-cst.gif" } - ) + img { src: "https://www.google.com/logos/doodles/...." } + } ) ``` > Note: Passing children into components will break any memoization due to the associated lifetime. -While technically allowed, it's an antipattern to pass children more than once in a component and will probably break your app significantly. +While technically allowed, it's an antipattern to pass children more than once in a component and will probably cause your app to crash. However, because the `Element` is transparently a `VNode`, we can actually match on it to extract the nodes themselves, in case we are expecting a specific format: @@ -142,10 +144,10 @@ In the cases where you need to pass arbitrary element properties into a componen ```rust rsx!( - clickable( + Clickable { "class": "blue-button", "style": "background: red;" - ) + } ) ``` @@ -161,7 +163,7 @@ struct ClickableProps<'a> { fn clickable(cx: Scope) -> Element { cx.render(rsx!( a { - ..{cx.props.attributes}, + ..cx.props.attributes, "Any link, anywhere" } )) @@ -195,9 +197,9 @@ Then, we can attach a listener at the call site: ```rust rsx!( - clickable( + Clickable { onclick: move |_| log::info!("Clicked"), - ) + } ) ``` diff --git a/docs/guide/src/concepts/components.md b/docs/guide/src/elements/components.md similarity index 87% rename from docs/guide/src/concepts/components.md rename to docs/guide/src/elements/components.md index a396d4b4b..8567e9270 100644 --- a/docs/guide/src/concepts/components.md +++ b/docs/guide/src/elements/components.md @@ -110,7 +110,7 @@ When declaring a component in `rsx!`, we can pass in properties using the tradit Let's take a look at the `VoteButton` component. For now, we won't include any interactivity - just the rendering the score and buttons to the screen. -Most of your Components will look exactly like this: a Props struct and a render function. Every component must take a tuple of `Scope` and `&Props` and return an `Element`. +Most of your Components will look exactly like this: a Props struct and a render function. Every component must take a `Scope` generic over some `Props` and return an `Element`. As covered before, we'll build our User Interface with the `rsx!` macro and HTML tags. However, with components, we must actually "render" our HTML markup. Calling `cx.render` converts our "lazy" `rsx!` structure into an `Element`. @@ -145,7 +145,7 @@ struct TitleCardProps<'a> { title: &'a str, } -fn TitleCard(cx: Scope) -> Element { +fn TitleCard<'a>(cx: Scope<'a, TitleCardProps<'a>>) -> Element { cx.render(rsx!{ h1 { "{cx.props.title}" } }) @@ -156,6 +156,42 @@ For users of React: Dioxus knows *not* to memoize components that borrow propert This means that during the render process, a newer version of `TitleCardProps` will never be compared with a previous version, saving some clock cycles. +## The inline_props macro + +Yes - *another* macro! However, this one is entirely optional. + +For internal components, we provide the `inline_props` macro, which will let you embed your `Props` definition right into the function arguments of your component. + +Our title card above would be transformed from: + +```rust +#[derive(Props, PartialEq)] +struct TitleCardProps { + title: String, +} + +fn TitleCard(cx: Scope) -> Element { + cx.render(rsx!{ + h1 { "{cx.props.title}" } + }) +} +``` + +to: + +```rust +#[inline_props] +fn TitleCard(cx: Scope, title: String) -> Element { + cx.render(rsx!{ + h1 { "{title}" } + }) +} +``` + +Again, this macro is optional and should not be used by library authors since you have less fine-grained control over documentation and optionality. + +However, it's great for quickly throwing together an app without dealing with *any* extra boilerplate. + ## The `Scope` object Though very similar to React, Dioxus is different in a few ways. Most notably, React components will not have a `Scope` parameter in the component declaration. diff --git a/docs/guide/src/concepts/conditional_rendering.md b/docs/guide/src/elements/conditional_rendering.md similarity index 88% rename from docs/guide/src/concepts/conditional_rendering.md rename to docs/guide/src/elements/conditional_rendering.md index 5bb75c490..b23df6000 100644 --- a/docs/guide/src/concepts/conditional_rendering.md +++ b/docs/guide/src/elements/conditional_rendering.md @@ -101,14 +101,14 @@ fn App(cx: Scope)-> Element { ## Nesting RSX -By looking at other examples, you might have noticed that it's possible to include `rsx!` calls inside other `rsx!` calls. With the curly-brace syntax, we can include anything in our `rsx!` that implements `IntoVnodeList`: a marker trait for iterators that produce Elements. `rsx!` itself implements this trait, so we can include it directly: +By looking at other examples, you might have noticed that it's possible to include `rsx!` calls inside other `rsx!` calls. We can include anything in our `rsx!` that implements `IntoVnodeList`: a marker trait for iterators that produce Elements. `rsx!` itself implements this trait, so we can include it directly: ```rust rsx!( div { - {rsx!( + rsx!( "more rsx!" - )} + ) } ) ``` @@ -120,7 +120,7 @@ let title = rsx!( "more rsx!" ); rsx!( div { - {title} + title } ) ``` @@ -136,7 +136,7 @@ let screen = match logged_in { cx.render(rsx!{ Navbar {} - {screen} + screen, Footer {} }) ``` @@ -152,9 +152,9 @@ By default, Rust lets you convert any `boolean` into any other type by calling ` let show_title = true; rsx!( div { - {show_title.and_then(|| rsx!{ + show_title.and_then(|| rsx!{ "This is the title" - })} + }) } ) ``` @@ -164,20 +164,23 @@ We can use this pattern for many things, including options: let user_name = Some("bob"); rsx!( div { - {user_name.map(|name| rsx!("Hello {name}"))} + user_name.map(|name| rsx!("Hello {name}")) } ) ``` ## Rendering Nothing -Sometimes, you don't want your component to return anything at all. In these cases, we can pass "None" into our bracket. However, Rust is not able to infer that our `None` corresponds to `Element` so we need to cast it appropriately: +Sometimes, you don't want your component to return anything at all. Under the hood, the `Element` type is just an alias for `Option`, so you can simply return `None`. + +This can be helpful in certain patterns where you need to perform some logical side-effects but don't want to render anything. ```rust -rsx!(cx, {None as Element} ) +fn demo(cx: Scope) -> Element { + None +} ``` - ## Moving Forward: In this chapter, we learned how to render different Elements from a Component depending on a condition. This is a very powerful building block to assemble complex User Interfaces! diff --git a/docs/guide/src/concepts/exporting_components.md b/docs/guide/src/elements/exporting_components.md similarity index 86% rename from docs/guide/src/concepts/exporting_components.md rename to docs/guide/src/elements/exporting_components.md index 59fea7046..16d6f1002 100644 --- a/docs/guide/src/concepts/exporting_components.md +++ b/docs/guide/src/elements/exporting_components.md @@ -25,27 +25,27 @@ fn main() { dioxus::desktop::launch(App); } -fn App((cx, props): Component) -> Element {} +fn App(Scope) -> Element {} #[derive(PartialEq, Props)] struct PostProps{} -fn Post((cx, props): Component) -> Element {} +fn Post(Scope) -> Element {} #[derive(PartialEq, Props)] struct VoteButtonsProps {} -fn VoteButtons((cx, props): Component) -> Element {} +fn VoteButtons(Scope) -> Element {} #[derive(PartialEq, Props)] struct TitleCardProps {} -fn TitleCard((cx, props): Component) -> Element {} +fn TitleCard(Scope) -> Element {} #[derive(PartialEq, Props)] struct MetaCardProps {} -fn MetaCard((cx, props): Component) -> Element {} +fn MetaCard(Scope) -> Element {} #[derive(PartialEq, Props)] struct ActionCardProps {} -fn ActionCard((cx, props): Component) -> Element {} +fn ActionCard(Scope) -> Element {} ``` That's a lot of components for one file! We've successfully refactored our app into components, but we should probably start breaking it up into a file for each component. @@ -61,7 +61,7 @@ use dioxus::prelude::*; #[derive(PartialEq, Props)] struct ActionCardProps {} -fn ActionCard((cx, props): Component) -> Element {} +fn ActionCard(Scope) -> Element {} ``` We should also create a `mod.rs` file in the `post` folder so we can use it from our `main.rs`. Our `Post` component and its props will go into this file. @@ -92,7 +92,7 @@ fn main() { mod post; -fn App((cx, props): Component) -> Element { +fn App(Scope) -> Element { cx.render(rsx!{ post::Post { id: Uuid::new_v4(), @@ -116,7 +116,7 @@ use dioxus::prelude::*; #[derive(PartialEq, Props)] pub struct PostProps {} -pub fn Post((cx, props): Component) -> Element {} +pub fn Post(Scope) -> Element {} ``` While we're here, we also need to make sure each of our subcomponents are included as modules and exported. @@ -142,7 +142,7 @@ pub struct PostProps { original_poster: String } -pub fn Post((cx, props): Component) -> Element { +pub fn Post(Scope) -> Element { cx.render(rsx!{ div { class: "post-container" vote::VoteButtons { @@ -191,7 +191,7 @@ fn main() { mod post; -fn App((cx, props): Component) -> Element { +fn App(Scope) -> Element { cx.render(rsx!{ post::Post { id: Uuid::new_v4(), @@ -227,7 +227,7 @@ pub struct PostProps { original_poster: String } -pub fn Post((cx, props): Component) -> Element { +pub fn Post(Scope) -> Element { cx.render(rsx!{ div { class: "post-container" vote::VoteButtons { @@ -255,7 +255,7 @@ use dioxus::prelude::*; #[derive(PartialEq, Props)] pub struct VoteButtonsProps {} -pub fn VoteButtons((cx, props): Component) -> Element {} +pub fn VoteButtons(Scope) -> Element {} ``` ```rust @@ -264,7 +264,7 @@ use dioxus::prelude::*; #[derive(PartialEq, Props)] pub struct TitleCardProps {} -pub fn TitleCard((cx, props): Component) -> Element {} +pub fn TitleCard(Scope) -> Element {} ``` ```rust @@ -273,7 +273,7 @@ use dioxus::prelude::*; #[derive(PartialEq, Props)] pub struct MetaCardProps {} -pub fn MetaCard((cx, props): Component) -> Element {} +pub fn MetaCard(Scope) -> Element {} ``` ```rust @@ -282,7 +282,7 @@ use dioxus::prelude::*; #[derive(PartialEq, Props)] pub struct ActionCardProps {} -pub fn ActionCard((cx, props): Component) -> Element {} +pub fn ActionCard(Scope) -> Element {} ``` ## Moving forward diff --git a/docs/guide/src/elements/index.md b/docs/guide/src/elements/index.md new file mode 100644 index 000000000..13a61a868 --- /dev/null +++ b/docs/guide/src/elements/index.md @@ -0,0 +1,73 @@ +# Core Topics + +In this chapter, we'll cover some core topics on how Dioxus works and how to best leverage the features to build a beautiful, reactive app. + +At a very high level, Dioxus is simply a Rust framework for _declaring_ user interfaces and _reacting_ to changes. + +1) We declare what we want our user interface to look like given a state using Rust-based logic and control flow. +2) We declare how we want our state to change when the user triggers an event. + +## Declarative UI + +Dioxus is a *declarative* framework. This means that instead of manually writing calls to "create element" and "set element background to red," we simply *declare* what we want the element to look like and let Dioxus handle the differences. + +Let's pretend that we have a stoplight we need to control - it has a color state with red, yellow, and green as options. + + +Using an imperative approach, we would have to manually declare each element and then handlers for advancing the stoplight. + +```rust +let container = Container::new(); + +let green_light = Light::new().color("green").enabled(true); +let yellow_light = Light::new().color("yellow").enabled(false); +let red_light = Light::new().color("red").enabled(false); +container.push(green_light); +container.push(yellow_light); +container.push(red_light); + +container.set_onclick(move |_| { + if red_light.enabled() { + red_light.set_enabled(false); + green_light.set_enabled(true); + } else if yellow_light.enabled() { + yellow_light.set_enabled(false); + red_light.set_enabled(true); + } else if green_light.enabled() { + green_light.set_enabled(false); + yellow_light.set_enabled(true); + } +}); +``` + +As the UI grows in scale, our logic to keep each element in the proper state would grow exponentially. This can become very unwieldy and lead to out-of-sync UIs that harm user experience. + +Instead, with Dioxus, we *declare* what we want our UI to look like: + +```rust +let mut state = use_state(&cx, || "red"); + +cx.render(rsx!( + Container { + Light { color: "red", enabled: state == "red", } + Light { color: "yellow", enabled: state == "yellow", } + Light { color: "green", enabled: state == "green", } + + onclick: move |_| { + state.set(match *state { + "green" => "yellow", + "yellow" => "red", + "red" => "green", + }) + } + } +)) +``` + +Remember: this concept is not new! Many frameworks are declarative - with React being the most popular. Declarative frameworks tend to be much more enjoyable to work with than imperative frameworks. + +Here's some reading about declaring UI in React: + +- [https://stackoverflow.com/questions/33655534/difference-between-declarative-and-imperative-in-react-js](https://stackoverflow.com/questions/33655534/difference-between-declarative-and-imperative-in-react-js) + +- [https://medium.com/@myung.kim287/declarative-vs-imperative-251ce99c6c44](https://medium.com/@myung.kim287/declarative-vs-imperative-251ce99c6c44) diff --git a/docs/guide/src/concepts/lists.md b/docs/guide/src/elements/lists.md similarity index 100% rename from docs/guide/src/concepts/lists.md rename to docs/guide/src/elements/lists.md diff --git a/docs/guide/src/concepts/vnodes.md b/docs/guide/src/elements/vnodes.md similarity index 79% rename from docs/guide/src/concepts/vnodes.md rename to docs/guide/src/elements/vnodes.md index 0a98c89d9..b407ed02f 100644 --- a/docs/guide/src/concepts/vnodes.md +++ b/docs/guide/src/elements/vnodes.md @@ -87,9 +87,15 @@ Unfortunately, you cannot drop in arbitrary expressions directly into the string rsx!( {[format_args!("Hello {}", if enabled { "Jack" } else { "Bob" } )]} ) ``` +Alternatively, `&str` can be included directly, though it must be inside of an array: + +```rust +rsx!( "Hello ", [if enabled { "Jack" } else { "Bob" }] ) +``` + This is different from React's way of generating arbitrary markup but fits within idiomatic Rust. -Typically, with Dioxus, you'll just want to compute your substrings outside of the `rsx!` call: +Typically, with Dioxus, you'll just want to compute your substrings outside of the `rsx!` call and leverage the f-string formatting: ```rust let name = if enabled { "Jack" } else { "Bob" }; @@ -100,7 +106,7 @@ rsx! ( "hello {name}" ) Every Element in your User Interface will have some sort of properties that the renderer will use when drawing to the screen. These might inform the renderer if the component should be hidden, what its background color should be, or to give it a specific name or ID. -To do this, we use the familiar struct-style syntax that Rust provides. Commas are optional: +To do this, we use the familiar struct-style syntax that Rust provides: ```rust rsx!( @@ -127,19 +133,29 @@ rsx!( ) ``` -Note: the name of the custom attribute must match exactly what you want the renderer to output. All attributes defined as methods in `dioxus-html` follow the snake_case naming convention. However, they internally translate their snake_case convention to HTML's camelCase convention. When using custom attributes, make sure the name of the attribute exactly matches what the renderer is expecting. +> Note: the name of the custom attribute must match exactly what you want the renderer to output. All attributes defined as methods in `dioxus-html` follow the snake_case naming convention. However, they internally translate their snake_case convention to HTML's camelCase convention. When using custom attributes, make sure the name of the attribute **exactly** matches what the renderer is expecting. + +All element attributes must occur *before* child elements. The `rsx!` macro will throw an error if your child elements come before any of your attributes. If you don't see the error, try editing your Rust-Analyzer IDE setting to ignore macro-errors. This is a temporary workaround because Rust-Analyzer currently throws *two* errors instead of just the one we care about. + +```rust +// settings.json +{ + "rust-analyzer.diagnostics.disabled": [ + "macro-error" + ], +} +``` ## Listeners Listeners are a special type of Attribute that only accept functions. Listeners let us attach functionality to our Elements by running a provided closure whenever the specified Listener is triggered. -We'll cover listeners in more depth in the Listeners chapter, but for now, just know that every listener must start with the `on` keyword and can accept either a closure or an expression wrapped in curly braces. +We'll cover listeners in more depth in the Listeners chapter, but for now, just know that every listener must start with the `on` keyword and accepts closures. ```rust rsx!( div { - onclick: move |_| {} - onmouseover: {handler}, + onclick: move |_| log::debug!("div clicked!"), } ) ``` diff --git a/docs/guide/src/final.md b/docs/guide/src/final.md new file mode 100644 index 000000000..68706f1aa --- /dev/null +++ b/docs/guide/src/final.md @@ -0,0 +1,49 @@ +# Congrats! + +Congrats! You've made it through the `learning Dioxus` book. Throughout this tutorial, you've learned a ton: + +- How to build User Interfaces with Elements +- How to compose Element groups together as Components +- How to handle user input with event listeners +- How to manage local and global state +- How to work with async using tasks, coroutines, and suspense +- How to build custom hooks and handlers + +With any luck, you followed through the "Putting it All Together" mini guide and have your very own dog search engine app! + +# Next Steps and Advanced Topics + +Continuing on your journey with Dioxus, you can try a number of things: + +- Build a simple TUI app +- Publish your search engine app +- Deploy a WASM app to GitHub +- Design a custom hook +- Contribute to the ecosystem! + +There are a number of advanced topics we glossed over: + +- The underlying NodeFactory API +- Static elements and templates +- Anti-patterns +- Bundling/distribution +- Working with wasm apps + +# Contributing to the ecosystem + +Dioxus is still quite young and could use your help! + +The core team is actively working on: + +- Declarative window management (via Tauri) for Desktop apps +- Portals for Dioxus Core +- Mobile support +- Integration with 3D renderers +- Better async story (suspense, error handling) +- Global state management +- Web development server +- LiveView +- Broader platform support (iOS/Android/TV/embedded) + +If there's something specifically interesting to you, don't be afraid to jump in! + diff --git a/docs/guide/src/final/index.md b/docs/guide/src/final/index.md deleted file mode 100644 index 32961beed..000000000 --- a/docs/guide/src/final/index.md +++ /dev/null @@ -1,10 +0,0 @@ -# Next Steps and Advanced Topics - - -Congrats! You've made it through the `learning Dioxus` book. Throughout this tutorial, you've learned a ton: - -- How to build User Interfaces with Elements -- How to compose Element groups together as Components -- How to handle user input with event listeners -- How to manage global and local state -- How to work with async using tasks, coroutines, and suspense diff --git a/docs/guide/src/hello_world.md b/docs/guide/src/hello_world.md index fd5e90aa5..fbe6153f5 100644 --- a/docs/guide/src/hello_world.md +++ b/docs/guide/src/hello_world.md @@ -1,4 +1,4 @@ -# Hello, World desktop app +# "Hello, World" desktop app Let's put together a simple "hello world" desktop application to get acquainted with Dioxus. @@ -138,11 +138,13 @@ Writing `fn App(cx: Scope) -> Element {` might become tedious. Rust will also le static App: Component = |cx| cx.render(rsx!(div { "Hello, world!" })); ``` -### What is this `Context` object? +### What is this `Scope` object? -Coming from React, the `Context` object might be confusing. In React, you'll want to store data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. +Coming from React, the `Scope` object might be confusing. In React, you'll want to store data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. -In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data. The `Context` object provides a handful of useful APIs for features like suspense, rendering, and more. +In Dioxus, you are given an explicit `Scope` object to control how the component renders and stores data. The `Scope` object provides a handful of useful APIs for features like suspense, rendering, and more. + +For now, just know that `Scope` lets you store state with hooks and render elements with `cx.render`. ## Moving on diff --git a/docs/guide/src/images/publish.png b/docs/guide/src/images/publish.png new file mode 100644 index 0000000000000000000000000000000000000000..b17d3bb06eba38e7cad385d3aed87786cf2466d9 GIT binary patch literal 278470 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
##0PXrvufB0wJ`%=YsvNItF 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

sW@yfIK5!0r3C!jXOp{;!Mh)wCw-TYJeHj?h7 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%0AT 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 KTJSHwum215)xFXH literal 0 HcmV?d00001 diff --git a/docs/guide/src/interactivity/event_handlers.md b/docs/guide/src/interactivity/event_handlers.md new file mode 100644 index 000000000..ee4560b1e --- /dev/null +++ b/docs/guide/src/interactivity/event_handlers.md @@ -0,0 +1 @@ +# Event handlers diff --git a/docs/guide/src/concepts/hooks.md b/docs/guide/src/interactivity/hooks.md similarity index 89% rename from docs/guide/src/concepts/hooks.md rename to docs/guide/src/interactivity/hooks.md index 28f027db1..029e2db15 100644 --- a/docs/guide/src/concepts/hooks.md +++ b/docs/guide/src/interactivity/hooks.md @@ -19,16 +19,16 @@ Broadly, there are two types of GUI structures: Typically, immediate-mode GUIs are simpler to write but can slow down as more features, like styling, are added. -Many GUIs today - including Dioxus - are written in *Retained mode* - your code changes the data of the user interface but the renderer is responsible for actually drawing to the screen. In these cases, our GUI's state sticks around as the UI is rendered. +Many GUIs today are written in *Retained mode* - your code changes the data of the user interface but the renderer is responsible for actually drawing to the screen. In these cases, our GUI's state sticks around as the UI is rendered. To help accommodate retained mode GUIs, like the web browser, Dioxus provides a mechanism to keep state around. -Dioxus, following in the footsteps of React, provides a "Reactive" model for you to design your UI. This model emphasizes one-way data flow and encapsulation. Essentially, your UI code should be as predictable as possible. +> Note: Even though hooks are accessible, you should still prefer to one-way data flow and encapsulation. Your UI code should be as predictable as possible. Dioxus is plenty fast, even for the largest apps. ## Mechanics of Hooks In order to have state stick around between renders, Dioxus provides the `hook` through the `use_hook` API. This gives us a mutable reference to data returned from the initialization function. ```rust fn example(cx: Scope) -> Element { - let name: &mut String = cx.use_hook(|| "John Doe".to_string(), |hook| hook); + let name: &mut String = cx.use_hook(|| "John Doe".to_string()); // } @@ -38,7 +38,7 @@ We can even modify this value directly from an event handler: ```rust fn example(cx: Scope) -> Element { - let name: &mut String = cx.use_hook(|| "John Doe".to_string(), |hook| hook); + let name: &mut String = cx.use_hook(|| "John Doe".to_string()); cx.render(rsx!( button { @@ -52,9 +52,9 @@ Mechanically, each call to `use_hook` provides us with `&mut T` for a new value. ```rust fn example(cx: Scope) -> Element { - let name: &mut String = cx.use_hook(|| "John Doe".to_string(), |hook| hook); - let age: &mut u32 = cx.use_hook(|| 10, |hook| hook); - let friends: &mut Vec = cx.use_hook(|| vec!["Jane Doe".to_string()], |hook| hook); + let name: &mut String = cx.use_hook(|| "John Doe".to_string()); + let age: &mut u32 = cx.use_hook(|| 10); + let friends: &mut Vec = cx.use_hook(|| vec!["Jane Doe".to_string()]); // } @@ -84,7 +84,7 @@ Consider when we try to pass our `&mut String` into two different handlers: ```rust fn example(cx: Scope) -> Element { - let name: &mut String = cx.use_hook(|| "John Doe".to_string(), |hook| hook); + let name: &mut String = cx.use_hook(|| "John Doe".to_string()); cx.render(rsx!( button { onclick: move |_| name.push_str("yes"), } @@ -112,7 +112,7 @@ This example uses the `Cell` type to let us replace the value through interior m ```rust fn example(cx: Scope) -> Element { - let name: &Cell<&'static str> = cx.use_hook(|| "John Doe", |hook| hook); + let name: &Cell<&'static str> = cx.use_hook(|| Cell::new("John Doe")); cx.render(rsx!( button { onclick: move |_| name.set("John"), } @@ -180,6 +180,8 @@ By default, we bundle a handful of hooks in the Dioxus-Hooks package. Feel free - [use_callback](https://docs.rs/dioxus_hooks/use_callback) - store a callback that implements PartialEq for memoization - [use_provide_context](https://docs.rs/dioxus_hooks/use_provide_context) - expose state to descendent components - [use_context](https://docs.rs/dioxus_hooks/use_context) - consume state provided by `use_provide_context` + +For a more in-depth guide to building new hooks, checkout out the advanced hook building guide in the reference. ## Wrapping up diff --git a/docs/guide/src/concepts/interactivity.md b/docs/guide/src/interactivity/index.md similarity index 99% rename from docs/guide/src/concepts/interactivity.md rename to docs/guide/src/interactivity/index.md index 26720cc1e..9e23862f1 100644 --- a/docs/guide/src/concepts/interactivity.md +++ b/docs/guide/src/interactivity/index.md @@ -139,7 +139,7 @@ fn App(cx: Scope)-> Element { let mut sec_elapsed = use_state(&cx, || 0); use_future(&cx, || { - let mut sec_elapsed = sec_elapsed.to_owned(); + let mut sec_elapsed = sec_elapsed.for_async(); async move { loop { TimeoutFuture::from_ms(1000).await; diff --git a/docs/guide/src/concepts/lifecycles.md b/docs/guide/src/interactivity/lifecycles.md similarity index 100% rename from docs/guide/src/concepts/lifecycles.md rename to docs/guide/src/interactivity/lifecycles.md diff --git a/docs/guide/src/concepts/user_input.md b/docs/guide/src/interactivity/user_input.md similarity index 100% rename from docs/guide/src/concepts/user_input.md rename to docs/guide/src/interactivity/user_input.md diff --git a/docs/guide/src/setup.md b/docs/guide/src/setup.md index c654179c9..11855d487 100644 --- a/docs/guide/src/setup.md +++ b/docs/guide/src/setup.md @@ -58,7 +58,7 @@ That's it! We won't need to touch NPM/WebPack/Babel/Parcel, etc. However, you _c ## Rust Knowledge -With Rust, things like benchmarking, testing, and documentation are included in the language. We strongly recommend going through the official Rust book _completely_. However, our hope is that a Dioxus app can serve as a great first project. With Dioxus you'll learn about: +With Rust, things like benchmarking, testing, and documentation are included in the language. We strongly recommend going through the official Rust book _completely_. However, our hope is that a Dioxus app can serve as a great first Rust project. With Dioxus you'll learn about: - Error handling - Structs, Functions, Enums diff --git a/docs/guide/src/concepts/errorhandling.md b/docs/guide/src/state/errorhandling.md similarity index 100% rename from docs/guide/src/concepts/errorhandling.md rename to docs/guide/src/state/errorhandling.md diff --git a/docs/guide/src/state/index.md b/docs/guide/src/state/index.md new file mode 100644 index 000000000..9b3e054fc --- /dev/null +++ b/docs/guide/src/state/index.md @@ -0,0 +1,8 @@ +# Managing State + +Every app you'll build with Dioxus will have some sort of state that needs to be maintained and updated as your users interact with it. However, managing state can be particular challenging at times, and is frequently the source of bugs in many GUI frameworks. + +In this chapter, we'll cover the various ways to manage state, the appropriate terminology, various patterns, and then take an overview + + +## Terminology diff --git a/docs/guide/src/state/liftingstate.md b/docs/guide/src/state/liftingstate.md new file mode 100644 index 000000000..3e906a0ca --- /dev/null +++ b/docs/guide/src/state/liftingstate.md @@ -0,0 +1 @@ +# Lifting State diff --git a/docs/guide/src/state/localstate.md b/docs/guide/src/state/localstate.md new file mode 100644 index 000000000..29085ac45 --- /dev/null +++ b/docs/guide/src/state/localstate.md @@ -0,0 +1 @@ +# Local State diff --git a/docs/guide/src/concepts/sharedstate.md b/docs/guide/src/state/sharedstate.md similarity index 100% rename from docs/guide/src/concepts/sharedstate.md rename to docs/guide/src/state/sharedstate.md diff --git a/docs/guide/src/tutorial/publishing.md b/docs/guide/src/tutorial/publishing.md index 96670f1c8..89bf39edf 100644 --- a/docs/guide/src/tutorial/publishing.md +++ b/docs/guide/src/tutorial/publishing.md @@ -1 +1,65 @@ # Publishing + +Congrats! You've made your first Dioxus app that actually does some pretty cool stuff. This app uses your operating system's WebView library, so it's portable to be distributed for other platforms. + +In this section, we'll cover how to bundle your app for macOS, Windows, and Linux. + + + +## Install `cargo-bundle` + + +The first thing we'll do is install [`cargo-bundle`](https://github.com/burtonageo/cargo-bundle). This extension to cargo will make it very easy to package our app for the various platforms. + +According to the `cargo-bundle` github page, + + + +*"cargo-bundle is a tool used to generate installers or app bundles for GUI executables built with cargo. It can create .app bundles for Mac OS X and iOS, .deb packages for Linux, and .msi installers for Windows (note however that iOS and Windows support is still experimental). Support for creating .rpm packages (for Linux) and .apk packages (for Android) is still pending."* + + +To install, simply run + + +`cargo install cargo-bundle` + +## Setting up your project + + +To get a project setup for bundling, we need to add some flags to our `Cargo.toml` file. + + +```toml +[package] +name = "example" +# ...other fields... + +[package.metadata.bundle] +name = "DogSearch" +identifier = "com.dogs.dogsearch" +version = "1.0.0" +copyright = "Copyright (c) Jane Doe 2016. All rights reserved." +category = "Developer Tool" +short_description = "Easily search for Dog photos" +long_description = """ +This app makes it quick and easy to browse photos of dogs from over 200 bree +""" +``` + + +## Building + +Following cargo-bundle's instructions, we simply `cargo-bundle --release` to produce a final app with all the optimizations and assets builtin. + +Once you've ran `cargo-bundle --release`, your app should be accessible in + +`target/release/bundle//`. + +For example, a macOS app would look like this: + +![Published App](../images/publish.png) + +Nice! And it's only 4.8 Mb - extremely lean!! Because Dioxus leverages your platform's native WebView, Dioxus apps are extremely memory efficient and won't waste your battery. + +> Note: not all CSS works the same on all platforms. Make sure to view your app's CSS on each platform - or web browser (Firefox, Chrome, Safari) before publishing. + diff --git a/docs/reference/src/SUMMARY.md b/docs/reference/src/SUMMARY.md index e69de29bb..a83f3c476 100644 --- a/docs/reference/src/SUMMARY.md +++ b/docs/reference/src/SUMMARY.md @@ -0,0 +1,61 @@ +# Summary + +- [Introduction](README.md) + +- [Web](web/intro.md) + - [Getting Started](web/setup.md) + - [Events](concepts/errorhandling.md) + +- [Desktop](desktop/intro.md) + - [Getting Started](desktop/setup.md) + - [events](desktop/.md) + +- [Mobile](concepts/managing_state.md) + - [Getting Started](concepts/sharedstate.md) + - [Specifics](concepts/errorhandling.md) + +- [Working with Async](concepts/async.md) + +- [Code Reference]() + - [Code Reference]() + - [Code Reference]() + - [Code Reference]() + - [Code Reference]() + +- [Topics in Depth](depth/topics.md) + - [RSX](depth/rsx.md) + - [Components](depth/components.md) + - [Props](depth/props.md) + - [Memoization](depth/memoization.md) + - [Performance](depth/performance.md) + - [Testing](depth/testing.md) + +- [Advanced Guides](tutorial/advanced_guides.md) + - [Memoization](concepts/memoization.md) + - [RSX in Depth](concepts/rsx_in_depth.md) + - [Building Elements with NodeFactory](concepts/rsx.md) + - [Custom Elements](concepts/custom_elements.md) + - [Custom Renderer](concepts/custom_renderer.md) + - [Server-side components](concepts/server_side_components.md) + - [Bundling and Distributing](concepts/bundline.md) + +- [Reference Guide]() + - [Anti-patterns]() + - [Children]() + - [Conditional Rendering]() + - [Controlled Inputs]() + - [Custom Elements]() + - [Empty Components]() + - [Error Handling]() + - [Fragments]() + - [Global CSS]() + - [Inline Styles]() + - [Iterators]() + - [Listeners]() + - [Memoization]() + - [Node Refs]() + - [Spread Pattern]() + - [State Management]() + - [Suspense]() + - [task]() + - [Testing]() diff --git a/examples/borrowed.rs b/examples/borrowed.rs index 6533a21a7..82920029b 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -21,7 +21,7 @@ fn main() { } fn App(cx: Scope) -> Element { - let text = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); + let text = cx.use_hook(|_| vec![String::from("abc=def")]); let first = text.get_mut(0).unwrap(); diff --git a/examples/calculator.rs b/examples/calculator.rs index e43323a21..c09f1b2aa 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,7 +12,7 @@ use dioxus::prelude::*; use separator::Separatable; fn main() { - dioxus::desktop::launch(app); + // dioxus::desktop::launch(app); } fn app(cx: Scope) -> Element { diff --git a/examples/crm.rs b/examples/crm.rs index ca1ae19cf..11acb4346 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -28,7 +28,8 @@ fn app(cx: Scope) -> Element { let description = use_state(&cx, String::new); cx.render(rsx!( - body { margin_left: "35%", + body { + margin_left: "35%", link { rel: "stylesheet", href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css", diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs index 46ee5ec1d..64bfb2876 100644 --- a/examples/desktop/todomvc.rs +++ b/examples/desktop/todomvc.rs @@ -109,7 +109,7 @@ pub static App: Component = |cx| { li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }} li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }} } - (show_clear_completed).then(|| rsx!( + show_clear_completed.then(|| rsx!( button { class: "clear-completed", onclick: move |_| clear_completed(), "Clear completed" } diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index f09693f1b..73a82ea2c 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -2,7 +2,6 @@ use dioxus::prelude::*; use rand::prelude::*; fn main() { - // dioxus::web::launch(App); dioxus::desktop::launch(App); } diff --git a/examples/hydration.rs b/examples/hydration.rs index 1f769e799..e0a23b46b 100644 --- a/examples/hydration.rs +++ b/examples/hydration.rs @@ -15,8 +15,7 @@ use dioxus::ssr; fn main() { let vdom = VirtualDom::new(App); let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true)); - - // dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content)); + dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content)); } static App: Component = |cx| { diff --git a/examples/inputs.rs b/examples/inputs.rs new file mode 100644 index 000000000..18edc9e8f --- /dev/null +++ b/examples/inputs.rs @@ -0,0 +1,150 @@ +//! This example roughly shows how events are serialized into Rust from JavaScript. +//! +//! There is some conversion happening when input types are checkbox/radio/select/textarea etc. + +use std::sync::Arc; + +use dioxus::{events::FormEvent, prelude::*}; + +fn main() { + dioxus::desktop::launch(App); +} + +const FIELDS: &[(&str, &str)] = &[ + ("button", "Click me!"), + ("checkbox", "CHECKBOX"), + ("color", ""), + ("date", ""), + ("datetime-local", ""), + ("email", ""), + ("file", ""), + ("image", ""), + ("number", ""), + ("password", ""), + ("radio", ""), + ("range", ""), + ("reset", ""), + ("search", ""), + ("submit", ""), + ("tel", ""), + ("text", ""), + ("time", ""), + ("url", ""), + // + // less supported things + ("hidden", ""), + ("month", ""), // degrades to text most of the time, but works properly as "value'" + ("week", ""), // degrades to text most of the time +]; + +static App: Component = |cx| { + cx.render(rsx! { + div { margin_left: "30px", + + // radio group + + + + + div { + // handling inputs on divs will catch all input events below + // so the value of our input event will be either huey, dewey, louie, or true/false (because of the checkboxe) + // be mindful in grouping inputs together, as they will all be handled by the same event handler + oninput: move |evt| { + println!("{:?}", evt); + }, + div { + input { + id: "huey", + r#type: "radio", + value: "huey", + checked: "", + name: "drone", + } + label { + r#for: "huey", + "Huey" + } + } + div { + input { + id: "dewey", + r#type: "radio", + value: "dewey", + name: "drone", + } + label { + r#for: "dewey", + "Dewey" + } + } + div { + input { + id: "louie", + value: "louie", + r#type: "radio", + name: "drone", + } + label { + r#for: "louie", + "Louie" + } + } + div { + input { + id: "groovy", + value: "groovy", + r#type: "checkbox", + name: "drone", + } + label { + r#for: "groovy", + "groovy" + } + } + } + + // elements with driven values will preventdefault automatically. + // you can disable this override with preventdefault: false + + div { + input { + id: "pdf", + value: "pdf", + name: "pdf", + r#type: "checkbox", + oninput: move |evt| { + println!("{:?}", evt); + }, + } + label { + r#for: "pdf", + "pdf" + } + } + + + + FIELDS.iter().map(|(field, value)| rsx!( + div { + input { + id: "{field}", + name: "{field}", + r#type: "{field}", + value: "{value}", + // checked: "false", + oninput: move |evt: Arc| { + println!("{:?}", evt); + }, + } + label { + r#for: "{field}", + "{field} element" + } + br {} + } + )) + + } + }) +}; diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index 45b0ccf4e..d9d2eb47e 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -24,13 +24,13 @@ use dioxus::prelude::*; const STYLE: &str = include_str!("./assets/calculator.css"); fn main() { env_logger::init(); - // dioxus::desktop::launch_cfg(App, |cfg| { - // cfg.with_window(|w| { - // w.with_title("Calculator Demo") - // .with_resizable(false) - // .with_inner_size(LogicalSize::new(320.0, 530.0)) - // }) - // }); + dioxus::desktop::launch_cfg(app, |cfg| { + cfg.with_window(|w| { + w.with_title("Calculator Demo") + .with_resizable(false) + .with_inner_size(LogicalSize::new(320.0, 530.0)) + }) + }); } fn app(cx: Scope) -> Element { diff --git a/examples/router.rs b/examples/router.rs index 47350ad16..c28853185 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -32,14 +32,14 @@ static App: Component = |cx| { Link { to: Route::AllUsers { page: 0 }, "List all users" } Link { to: Route::BlogList { page: 0 }, "Blog posts" } } - {match route { + match route { Route::Home => rsx!("Home"), Route::AllUsers { page } => rsx!("All users - page {page}"), Route::User { id } => rsx!("User - id: {id}"), Route::BlogList { page } => rsx!("Blog posts - page {page}"), Route::BlogPost { post_id } => rsx!("Blog post - post {post_id}"), Route::NotFound => rsx!("Not found"), - }} + } footer {} }) }; diff --git a/examples/rsx_compile_fail.rs b/examples/rsx_compile_fail.rs index c95f3d7fe..cdc69c21e 100644 --- a/examples/rsx_compile_fail.rs +++ b/examples/rsx_compile_fail.rs @@ -42,6 +42,7 @@ fn example(cx: Scope) -> Element { things_list.iter().map(|f| rsx!{ div { "{f.a}" + "{f.b}" } }) diff --git a/examples/weather_app.rs b/examples/weather_app.rs index 3317a1774..66e90dbb7 100644 --- a/examples/weather_app.rs +++ b/examples/weather_app.rs @@ -43,21 +43,21 @@ struct WeatherProps {} static WeatherDisplay: Component = |cx| { // cx.render(rsx!( - div { class: "flex items-center justify-center flex-col" - div { class: "flex items-center justify-center" - div { class: "flex flex-col bg-white rounded p-4 w-full max-w-xs" - div{ class: "font-bold text-xl" + div { class: "flex items-center justify-center flex-col", + div { class: "flex items-center justify-center", + div { class: "flex flex-col bg-white rounded p-4 w-full max-w-xs", + div{ class: "font-bold text-xl", "Jon's awesome site!!" } - div{ class: "text-sm text-gray-500" + div{ class: "text-sm text-gray-500", "He worked so hard on it :)" } - div { class: "flex flex-row items-center justify-center mt-6" - div { class: "font-medium text-6xl" + div { class: "flex flex-row items-center justify-center mt-6", + div { class: "font-medium text-6xl", "1337" } } - div { class: "flex flex-row justify-between mt-6" + div { class: "flex flex-row justify-between mt-6", "Legit made my own React" } } diff --git a/examples/xss_safety.rs b/examples/xss_safety.rs index cdb0705ca..c694d646f 100644 --- a/examples/xss_safety.rs +++ b/examples/xss_safety.rs @@ -11,12 +11,9 @@ fn app(cx: Scope) -> Element { div { "hello world!" - h1 { - "{contents}" - } - h3 { - [contents.as_str()] - } + h1 { "{contents}" } + + h3 { [contents.as_str()] } input { value: "{contents}", diff --git a/notes/SUSPENSE.md b/notes/SUSPENSE.md index b377f1021..a3cb442cb 100644 --- a/notes/SUSPENSE.md +++ b/notes/SUSPENSE.md @@ -17,11 +17,7 @@ for more general tasks, we need some way of submitting a future or task into som ```rust -let task = use_hook( - || { /* */ }, - || { /* update the future if it needs to be updated */ }, - || {} -); +let task = use_hook(|| { /* */ }); cx.poll_future() // let recoil_event_loop = cx.use_task(move |_| async move { // loop { diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index a19f55bac..fe8475131 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -19,7 +19,7 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ ext::IdentExt, parse::{Parse, ParseBuffer, ParseStream}, - token, Expr, ExprClosure, Ident, Result, Token, + token, Expr, Ident, Result, Token, }; pub struct Component { diff --git a/packages/core/benches/jsframework.rs b/packages/core/benches/jsframework.rs index 30a37249d..8a6fcaf30 100644 --- a/packages/core/benches/jsframework.rs +++ b/packages/core/benches/jsframework.rs @@ -25,16 +25,16 @@ criterion_main!(mbenches); fn create_rows(c: &mut Criterion) { static App: Component = |cx| { let mut rng = SmallRng::from_entropy(); - let rows = (0..10_000_usize).map(|f| { - let label = Label::new(&mut rng); - rsx!(Row { - row_id: f, - label: label - }) - }); + rsx!(cx, table { tbody { - {rows} + (0..10_000_usize).map(|f| { + let label = Label::new(&mut rng); + rsx!(Row { + row_id: f, + label: label + }) + }) } }) }; @@ -58,12 +58,12 @@ fn Row(cx: Scope) -> Element { cx.render(rsx! { tr { td { class:"col-md-1", "{cx.props.row_id}" } - td { class:"col-md-1", onclick: move |_| { /* run onselect */ } + td { class:"col-md-1", onclick: move |_| { /* run onselect */ }, a { class: "lbl", "{adj}" "{col}" "{noun}" } } - td { class: "col-md-1" - a { class: "remove", onclick: move |_| {/* remove */} - span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + td { class: "col-md-1", + a { class: "remove", onclick: move |_| {/* remove */}, + span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" } } } td { class: "col-md-6" } diff --git a/packages/core/examples/component_children.rs b/packages/core/examples/component_children.rs index 73d0750a6..ac76740b8 100644 --- a/packages/core/examples/component_children.rs +++ b/packages/core/examples/component_children.rs @@ -12,7 +12,7 @@ fn main() { } fn parent(cx: Scope) -> Element { - let value = cx.use_hook(|_| String::new(), |f| f); + let value = cx.use_hook(|_| String::new()); cx.render(rsx! { div { @@ -33,8 +33,8 @@ struct ChildProps<'a> { fn child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element { cx.render(rsx! { div { - "it's nested {cx.props.name}" - {&cx.props.children} + "it's nested {cx.props.name}", + &cx.props.children } }) } diff --git a/packages/core/examples/works.rs b/packages/core/examples/works.rs index d108b6d07..0de94b5c3 100644 --- a/packages/core/examples/works.rs +++ b/packages/core/examples/works.rs @@ -10,7 +10,7 @@ fn main() { } fn parent(cx: Scope) -> Element { - let value = cx.use_hook(|_| String::new(), |f| f); + let value = cx.use_hook(|_| String::new()); cx.render(rsx! { div { diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 7f852a6d0..4f8af6601 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -473,9 +473,16 @@ impl<'bump> DiffState<'bump> { // Check the most common cases first // these are *actual* elements, not wrappers around lists (Text(old), Text(new)) => { - self.diff_text_nodes(old, new, old_node, new_node); + if let Some(root) = old.id.get() { + if old.text != new.text { + self.mutations.set_text(new.text, root.as_u64()); + } + self.scopes.update_node(new_node, root); + + new.id.set(Some(root)); + } } - (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node), + (Placeholder(old), Placeholder(new)) => { if let Some(root) = old.id.get() { self.scopes.update_node(new_node, root); @@ -483,10 +490,13 @@ impl<'bump> DiffState<'bump> { } } + (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node), + // These two sets are pointers to nodes but are not actually nodes themselves (Component(old), Component(new)) => { self.diff_component_nodes(old_node, new_node, *old, *new) } + (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new), // The normal pathway still works, but generates slightly weird instructions @@ -506,23 +516,6 @@ impl<'bump> DiffState<'bump> { } } - fn diff_text_nodes( - &mut self, - old: &'bump VText<'bump>, - new: &'bump VText<'bump>, - _old_node: &'bump VNode<'bump>, - new_node: &'bump VNode<'bump>, - ) { - if let Some(root) = old.id.get() { - if old.text != new.text { - self.mutations.set_text(new.text, root.as_u64()); - } - self.scopes.update_node(new_node, root); - - new.id.set(Some(root)); - } - } - fn diff_element_nodes( &mut self, old: &'bump VElement<'bump>, diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index ad00536d2..61fb2c264 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -65,10 +65,10 @@ pub(crate) mod innerlude { } pub use crate::innerlude::{ - Attribute, Component, DioxusElement, DomEdit, Element, ElementId, EventHandler, EventPriority, - IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, Properties, SchedulerMsg, Scope, - ScopeId, ScopeState, TaskId, UserEvent, VComponent, VElement, VFragment, VNode, VPlaceholder, - VText, VirtualDom, + Attribute, Component, DioxusElement, DomEdit, Element, ElementId, ElementIdIterator, Event, + EventHandler, EventPriority, IntoVNode, LazyNodes, Listener, Mutations, NodeFactory, + Properties, SchedulerMsg, Scope, ScopeId, ScopeState, TaskId, UserEvent, VComponent, VElement, + VFragment, VNode, VPlaceholder, VText, VirtualDom, }; pub mod prelude { diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 5d35b298d..61c60ec02 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -320,6 +320,18 @@ impl ScopeArena { pub fn root_node(&self, id: ScopeId) -> &VNode { self.fin_head(id) } + + // this is totally okay since all our nodes are always in a valid state + pub fn get_element(&self, id: ElementId) -> Option<&VNode> { + let ptr = self.nodes.borrow().get(id.0).cloned(); + match ptr { + Some(ptr) => { + let node = unsafe { &*ptr }; + Some(unsafe { extend_vnode(node) }) + } + None => None, + } + } } /// Components in Dioxus use the "Context" object to interact with their lifecycle. @@ -620,7 +632,7 @@ impl ScopeState { /// struct SharedState(&'static str); /// /// static App: Component = |cx| { - /// cx.use_hook(|_| cx.provide_context(SharedState("world")), |_| {}, |_| {}); + /// cx.use_hook(|_| cx.provide_context(SharedState("world"))); /// rsx!(cx, Child {}) /// } /// @@ -711,17 +723,13 @@ impl ScopeState { /// ```ignore /// // use_ref is the simplest way of storing a value between renders /// fn use_ref(initial_value: impl FnOnce() -> T) -> &RefCell { - /// use_hook( - /// || Rc::new(RefCell::new(initial_value())), - /// |state| state, - /// ) + /// use_hook(|| Rc::new(RefCell::new(initial_value()))) /// } /// ``` - pub fn use_hook<'src, State: 'static, Output: 'src>( + pub fn use_hook<'src, State: 'static>( &'src self, initializer: impl FnOnce(usize) -> State, - runner: impl FnOnce(&'src mut State) -> Output, - ) -> Output { + ) -> &'src mut State { let mut vals = self.hook_vals.borrow_mut(); let hook_len = vals.len(); @@ -731,7 +739,7 @@ impl ScopeState { vals.push(self.hook_arena.alloc(initializer(hook_len))); } - let state = vals + vals .get(cur_idx) .and_then(|inn| { self.hook_idx.set(cur_idx + 1); @@ -746,9 +754,7 @@ impl ScopeState { You likely used the hook in a conditional. Hooks rely on consistent ordering between renders. Functions prefixed with "use" should never be called conditionally. "###, - ); - - runner(state) + ) } /// The "work in progress frame" represents the frame that is currently being worked on. diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index e96527e95..2d16bbdc2 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -260,6 +260,10 @@ impl VirtualDom { self.channel.0.clone() } + pub fn get_element(&self, id: ElementId) -> Option<&VNode> { + self.scopes.get_element(id) + } + /// Add a new message to the scheduler queue directly. /// /// @@ -1003,3 +1007,103 @@ impl EmptyBuilder { pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder { T::builder() } + +pub struct Event { + data: Arc, + _cancel: std::rc::Rc>, +} + +impl std::ops::Deref for Event { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.data.as_ref() + } +} + +impl Event { + pub fn cancel(&self) {} + pub fn timestamp(&self) {} + pub fn triggered_element(&self) -> Option { + None + } +} + +pub struct ElementIdIterator<'a> { + vdom: &'a VirtualDom, + + // Heuristcally we should never bleed into 5 completely nested fragments/components + // Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane + stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>, +} + +impl<'a> ElementIdIterator<'a> { + pub fn new(vdom: &'a VirtualDom, node: &'a VNode<'a>) -> Self { + Self { + vdom, + stack: smallvec::smallvec![(0, node)], + } + } +} + +impl<'a> Iterator for ElementIdIterator<'a> { + type Item = &'a VNode<'a>; + + fn next(&mut self) -> Option<&'a VNode<'a>> { + let mut should_pop = false; + let mut returned_node = None; + let mut should_push = None; + + while returned_node.is_none() { + if let Some((count, node)) = self.stack.last_mut() { + match node { + // We can only exit our looping when we get "real" nodes + VNode::Element(_) | VNode::Text(_) | VNode::Placeholder(_) => { + // We've recursed INTO an element/text + // We need to recurse *out* of it and move forward to the next + // println!("Found element! Returning it!"); + should_pop = true; + returned_node = Some(&**node); + } + + // If we get a fragment we push the next child + VNode::Fragment(frag) => { + let _count = *count as usize; + if _count >= frag.children.len() { + should_pop = true; + } else { + should_push = Some(&frag.children[_count]); + } + } + + // For components, we load their root and push them onto the stack + VNode::Component(sc) => { + // todo!(); + let scope = self.vdom.get_scope(sc.scope.get().unwrap()).unwrap(); + + // Simply swap the current node on the stack with the root of the component + *node = scope.root_node(); + } + } + } else { + // If there's no more items on the stack, we're done! + return None; + } + + if should_pop { + self.stack.pop(); + if let Some((id, _)) = self.stack.last_mut() { + *id += 1; + } + should_pop = false; + } + + if let Some(push) = should_push { + self.stack.push((0, push)); + should_push = None; + } + } + + returned_node + } +} diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index c143c2fe1..b1583dc85 100644 --- a/packages/core/tests/borrowedstate.rs +++ b/packages/core/tests/borrowedstate.rs @@ -11,7 +11,7 @@ fn test_borrowed_state() { } fn Parent(cx: Scope) -> Element { - let value = cx.use_hook(|_| String::new(), |f| &*f); + let value = cx.use_hook(|_| String::new()); cx.render(rsx! { div { diff --git a/packages/core/tests/earlyabort.rs b/packages/core/tests/earlyabort.rs index 9689aba5d..7dc2ae4d4 100644 --- a/packages/core/tests/earlyabort.rs +++ b/packages/core/tests/earlyabort.rs @@ -25,7 +25,7 @@ fn new_dom(app: Component

, props: P) -> VirtualDom { #[test] fn test_early_abort() { const app: Component = |cx| { - let val = cx.use_hook(|_| 0, |f| f); + let val = cx.use_hook(|_| 0); *val += 1; diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index f87a403c7..6c0df4e1e 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -46,7 +46,7 @@ fn manual_diffing() { #[test] fn events_generate() { static App: Component = |cx| { - let count = cx.use_hook(|_| 0, |f| f); + let count = cx.use_hook(|_| 0); let inner = match *count { 0 => { @@ -105,7 +105,7 @@ fn events_generate() { #[test] fn components_generate() { static App: Component = |cx| { - let render_phase = cx.use_hook(|_| 0, |f| f); + let render_phase = cx.use_hook(|_| 0); *render_phase += 1; cx.render(match *render_phase { @@ -221,7 +221,7 @@ fn components_generate() { #[test] fn component_swap() { static App: Component = |cx| { - let render_phase = cx.use_hook(|_| 0, |f| f); + let render_phase = cx.use_hook(|_| 0); *render_phase += 1; cx.render(match *render_phase { diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index ebf38c7c1..4541d593a 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -29,7 +29,7 @@ fn new_dom(app: Component

, props: P) -> VirtualDom { #[test] fn test_memory_leak() { fn app(cx: Scope) -> Element { - let val = cx.use_hook(|_| 0, |f| f); + let val = cx.use_hook(|_| 0); *val += 1; @@ -37,7 +37,7 @@ fn test_memory_leak() { return None; } - let name = cx.use_hook(|_| String::from("asd"), |f| f); + let name = cx.use_hook(|_| String::from("asd")); cx.render(rsx!( div { "Hello, world!" } @@ -86,7 +86,7 @@ fn test_memory_leak() { #[test] fn memo_works_properly() { fn app(cx: Scope) -> Element { - let val = cx.use_hook(|_| 0, |f| f); + let val = cx.use_hook(|_| 0); *val += 1; @@ -94,7 +94,7 @@ fn memo_works_properly() { return None; } - let name = cx.use_hook(|_| String::from("asd"), |f| f); + let name = cx.use_hook(|_| String::from("asd")); cx.render(rsx!( div { "Hello, world!" } @@ -204,7 +204,7 @@ fn free_works_on_root_hooks() { } fn app(cx: Scope) -> Element { - let name = cx.use_hook(|_| Droppable(String::from("asd")), |f| f); + let name = cx.use_hook(|_| Droppable(String::from("asd"))); rsx!(cx, div { "{name.0}" }) } @@ -216,7 +216,7 @@ fn free_works_on_root_hooks() { fn old_props_arent_stale() { fn app(cx: Scope) -> Element { dbg!("rendering parent"); - let cnt = cx.use_hook(|_| 0, |f| f); + let cnt = cx.use_hook(|_| 0); *cnt += 1; if *cnt == 1 { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index c1eae3613..48c82f5a8 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -59,14 +59,7 @@ pub struct WebviewWindowProps<'a> { pub fn WebviewWindow(cx: Scope) -> Element { let dtcx = cx.consume_state::>()?; - cx.use_hook( - |_| { - // - }, - |state| { - // - }, - ); + cx.use_hook(|_| {}); // render the children directly todo!() diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index df3bd6436..e03b993f7 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -4,17 +4,8 @@ - - - - - + + diff --git a/packages/desktop/src/index.js b/packages/desktop/src/index.js index 5d93052c7..2ef907f78 100644 --- a/packages/desktop/src/index.js +++ b/packages/desktop/src/index.js @@ -54,6 +54,11 @@ function serialize_event(event) { case "submit": { let target = event.target; let value = target.value ?? target.textContent; + + if (target.type == "checkbox") { + value = target.checked ? "true" : "false"; + } + return { value: value }; @@ -310,32 +315,24 @@ class Interpreter { element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`); if (this.listeners[event_name] === undefined) { - this.listeners[event_name] = "bla"; + this.listeners[event_name] = true; this.root.addEventListener(event_name, (event) => { - // console.log("CLICKED"); + console.log("handling event", event); + const target = event.target; const real_id = target.getAttribute(`dioxus-id`); if (real_id == null) { + alert("no id"); return; } - // const fields = val.split("."); - // const scope_id = parseInt(fields[0]); - // const real_id = parseInt(fields[1]); - - // // console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`); - - // console.log("message fired"); - let contents = serialize_event(event); - let evt = { + rpc.call('user_event', { event: event_name, - // scope: scope_id, mounted_dom_id: parseInt(real_id), - contents: contents, - }; + contents: serialize_event(event), + }); - rpc.call('user_event', evt); }); } } diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs index 82bf2bda4..ac02c0e72 100644 --- a/packages/hooks/src/use_shared_state.rs +++ b/packages/hooks/src/use_shared_state.rs @@ -60,37 +60,33 @@ impl ProvidedStateInner { /// /// pub fn use_context<'a, T: 'static>(cx: &'a ScopeState) -> Option> { - cx.use_hook( - |_| { - let scope_id = cx.scope_id(); - let root = cx.consume_context::>(); + let state = cx.use_hook(|_| { + let scope_id = cx.scope_id(); + let root = cx.consume_context::>(); - if let Some(root) = root.as_ref() { - root.borrow_mut().consumers.insert(scope_id); - } + if let Some(root) = root.as_ref() { + root.borrow_mut().consumers.insert(scope_id); + } - let value = root.as_ref().map(|f| f.borrow().value.clone()); - SharedStateInner { - root, - value, - scope_id, - needs_notification: Cell::new(false), - } - }, - |f| { - // - f.needs_notification.set(false); - match (&f.value, &f.root) { - (Some(value), Some(root)) => Some(UseSharedState { - cx, - value, - root, - needs_notification: &f.needs_notification, - }), - _ => None, - } - }, - ) + let value = root.as_ref().map(|f| f.borrow().value.clone()); + SharedStateInner { + root, + value, + scope_id, + needs_notification: Cell::new(false), + } + }); + + state.needs_notification.set(false); + match (&state.value, &state.root) { + (Some(value), Some(root)) => Some(UseSharedState { + cx, + value, + root, + needs_notification: &state.needs_notification, + }), + _ => None, + } } struct SharedStateInner { @@ -176,15 +172,12 @@ where /// /// pub fn use_context_provider<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) { - cx.use_hook( - |_| { - let state: ProvidedState = RefCell::new(ProvidedStateInner { - value: Rc::new(RefCell::new(f())), - notify_any: cx.schedule_update_any(), - consumers: HashSet::new(), - }); - cx.provide_context(state) - }, - |_inner| {}, - ) + cx.use_hook(|_| { + let state: ProvidedState = RefCell::new(ProvidedStateInner { + value: Rc::new(RefCell::new(f())), + notify_any: cx.schedule_update_any(), + consumers: HashSet::new(), + }); + cx.provide_context(state) + }); } diff --git a/packages/hooks/src/usecoroutine.rs b/packages/hooks/src/usecoroutine.rs index b01326345..737f3c2a5 100644 --- a/packages/hooks/src/usecoroutine.rs +++ b/packages/hooks/src/usecoroutine.rs @@ -1,6 +1,6 @@ use dioxus_core::{ScopeState, TaskId}; use std::future::Future; -use std::{cell::Cell, pin::Pin, rc::Rc}; +use std::{cell::Cell, rc::Rc}; /* @@ -22,61 +22,58 @@ pub fn use_coroutine<'a, F>( where F: Future + 'static, { - cx.use_hook( - move |_| { - let f = create_future(); - let id = cx.push_future(f); - State { + let state = cx.use_hook(move |_| { + let f = create_future(); + let id = cx.push_future(f); + State { running: Default::default(), - id + _id: id // pending_fut: Default::default(), // running_fut: Default::default(), } - }, - |state| { - // state.pending_fut.set(Some(Box::pin(f))); + }); - // if let Some(fut) = state.running_fut.as_mut() { - // cx.push_future(fut); - // } + // state.pending_fut.set(Some(Box::pin(f))); - // if let Some(fut) = state.running_fut.take() { - // state.running.set(true); - // fut.resume(); - // } + // if let Some(fut) = state.running_fut.as_mut() { + // cx.push_future(fut); + // } - // let submit: Box = Box::new(move || { - // let g = async move { - // running.set(true); - // create_future().await; - // running.set(false); - // }; - // let p: Pin>> = Box::pin(g); - // fut_slot - // .borrow_mut() - // .replace(unsafe { std::mem::transmute(p) }); - // }); + // if let Some(fut) = state.running_fut.take() { + // state.running.set(true); + // fut.resume(); + // } - // let submit = unsafe { std::mem::transmute(submit) }; - // state.submit.get_mut().replace(submit); + // let submit: Box = Box::new(move || { + // let g = async move { + // running.set(true); + // create_future().await; + // running.set(false); + // }; + // let p: Pin>> = Box::pin(g); + // fut_slot + // .borrow_mut() + // .replace(unsafe { std::mem::transmute(p) }); + // }); - // if state.running.get() { - // // let mut fut = state.fut.borrow_mut(); - // // cx.push_task(|| fut.as_mut().unwrap().as_mut()); - // } else { - // // make sure to drop the old future - // if let Some(fut) = state.fut.borrow_mut().take() { - // drop(fut); - // } - // } - CoroutineHandle { cx, inner: state } - }, - ) + // let submit = unsafe { std::mem::transmute(submit) }; + // state.submit.get_mut().replace(submit); + + // if state.running.get() { + // // let mut fut = state.fut.borrow_mut(); + // // cx.push_task(|| fut.as_mut().unwrap().as_mut()); + // } else { + // // make sure to drop the old future + // if let Some(fut) = state.fut.borrow_mut().take() { + // drop(fut); + // } + // } + CoroutineHandle { cx, inner: state } } struct State { running: Rc>, - id: TaskId, + _id: TaskId, // the way this is structure, you can toggle the coroutine without re-rendering the comppnent // this means every render *generates* the future, which is a bit of a waste // todo: allocate pending futures in the bump allocator and then have a true promotion diff --git a/packages/hooks/src/usefuture.rs b/packages/hooks/src/usefuture.rs index 8ca014e6b..f6e026af9 100644 --- a/packages/hooks/src/usefuture.rs +++ b/packages/hooks/src/usefuture.rs @@ -5,40 +5,38 @@ pub fn use_future<'a, T: 'static, F: Future + 'static>( cx: &'a ScopeState, f: impl FnOnce() -> F, ) -> (Option<&T>, FutureHandle<'a, T>) { - cx.use_hook( - |_| { - // - let fut = f(); - let slot = Rc::new(Cell::new(None)); - let updater = cx.schedule_update(); + let state = cx.use_hook(|_| { + // + let fut = f(); + let slot = Rc::new(Cell::new(None)); + let updater = cx.schedule_update(); - let _slot = slot.clone(); - let new_fut = async move { - let res = fut.await; - _slot.set(Some(res)); - updater(); - }; - let task = cx.push_future(new_fut); + let _slot = slot.clone(); + let new_fut = async move { + let res = fut.await; + _slot.set(Some(res)); + updater(); + }; + let task = cx.push_future(new_fut); - UseFutureInner { - needs_regen: true, - slot, - value: None, - task: Some(task), - } - }, - |state| { - if let Some(value) = state.slot.take() { - state.value = Some(value); - state.task = None; - } - ( - state.value.as_ref(), - FutureHandle { - cx, - value: Cell::new(None), - }, - ) + UseFutureInner { + needs_regen: true, + slot, + value: None, + task: Some(task), + } + }); + + if let Some(value) = state.slot.take() { + state.value = Some(value); + state.task = None; + } + + ( + state.value.as_ref(), + FutureHandle { + cx, + value: Cell::new(None), }, ) } diff --git a/packages/hooks/src/usemodel.rs b/packages/hooks/src/usemodel.rs index 2cc532810..301774f94 100644 --- a/packages/hooks/src/usemodel.rs +++ b/packages/hooks/src/usemodel.rs @@ -14,18 +14,15 @@ use std::{ }; pub fn use_model<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) -> UseModel<'a, T> { - cx.use_hook( - |_| UseModelInner { - update_scheduled: Cell::new(false), - update_callback: cx.schedule_update(), - value: RefCell::new(f()), - // tasks: RefCell::new(Vec::new()), - }, - |inner| { - inner.update_scheduled.set(false); - UseModel { inner } - }, - ) + let inner = cx.use_hook(|_| UseModelInner { + update_scheduled: Cell::new(false), + update_callback: cx.schedule_update(), + value: RefCell::new(f()), + // tasks: RefCell::new(Vec::new()), + }); + + inner.update_scheduled.set(false); + UseModel { inner } } pub struct UseModel<'a, T> { @@ -81,21 +78,10 @@ pub fn use_model_coroutine<'a, T, F: Future + 'static>( _model: UseModel, _f: impl FnOnce(AppModels) -> F, ) -> UseModelCoroutine { - cx.use_hook( - |_| { - // - UseModelTaskInner { - task: Default::default(), - } - }, - |inner| { - // if let Some(task) = inner.task.get_mut() { - // cx.push_task(|| task); - // } - // - todo!() - }, - ) + cx.use_hook(|_| UseModelTaskInner { + task: Default::default(), + }); + todo!() } impl Copy for UseModel<'_, T> {} diff --git a/packages/hooks/src/useref.rs b/packages/hooks/src/useref.rs index f80572293..614fd86fa 100644 --- a/packages/hooks/src/useref.rs +++ b/packages/hooks/src/useref.rs @@ -6,17 +6,14 @@ use std::{ use dioxus_core::ScopeState; pub fn use_ref<'a, T: 'static>(cx: &'a ScopeState, f: impl FnOnce() -> T) -> UseRef<'a, T> { - cx.use_hook( - |_| UseRefInner { - update_scheduled: Cell::new(false), - update_callback: cx.schedule_update(), - value: RefCell::new(f()), - }, - |inner| { - inner.update_scheduled.set(false); - UseRef { inner } - }, - ) + let inner = cx.use_hook(|_| UseRefInner { + update_scheduled: Cell::new(false), + update_callback: cx.schedule_update(), + value: RefCell::new(f()), + }); + + inner.update_scheduled.set(false); + UseRef { inner } } pub struct UseRef<'a, T> { diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 9a02d6bcb..03f15a58b 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -51,32 +51,28 @@ pub fn use_state<'a, T: 'static>( cx: &'a ScopeState, initial_state_fn: impl FnOnce() -> T, ) -> UseState<'a, T> { - cx.use_hook( - move |_| { - let first_val = initial_state_fn(); - UseStateInner { - current_val: Rc::new(first_val), - update_callback: cx.schedule_update(), - wip: Rc::new(RefCell::new(None)), - update_scheuled: Cell::new(false), - } - }, - move |hook| { - hook.update_scheuled.set(false); + let hook = cx.use_hook(move |_| { + let first_val = initial_state_fn(); + UseStateInner { + current_val: Rc::new(first_val), + update_callback: cx.schedule_update(), + wip: Rc::new(RefCell::new(None)), + update_scheuled: Cell::new(false), + } + }); - let mut new_val = hook.wip.borrow_mut(); - if new_val.is_some() { - // if there's only one reference (weak or otherwise), we can just swap the values - if let Some(val) = Rc::get_mut(&mut hook.current_val) { - *val = new_val.take().unwrap(); - } else { - hook.current_val = Rc::new(new_val.take().unwrap()); - } - } + hook.update_scheuled.set(false); + let mut new_val = hook.wip.borrow_mut(); + if new_val.is_some() { + // if there's only one reference (weak or otherwise), we can just swap the values + if let Some(val) = Rc::get_mut(&mut hook.current_val) { + *val = new_val.take().unwrap(); + } else { + hook.current_val = Rc::new(new_val.take().unwrap()); + } + } - UseState { inner: &*hook } - }, - ) + UseState { inner: &*hook } } struct UseStateInner { current_val: Rc, diff --git a/packages/hooks/src/usesuspense.rs b/packages/hooks/src/usesuspense.rs index e40246b64..5b08ac04d 100644 --- a/packages/hooks/src/usesuspense.rs +++ b/packages/hooks/src/usesuspense.rs @@ -7,34 +7,31 @@ pub fn use_suspense + 'static>( create_future: impl FnOnce() -> F, render: impl FnOnce(&R) -> Element, ) -> Element { - cx.use_hook( - |_| { - let fut = create_future(); + let sus = cx.use_hook(|_| { + let fut = create_future(); - let wip_value: Rc>> = Default::default(); + let wip_value: Rc>> = Default::default(); - let wip = wip_value.clone(); - let new_fut = async move { - let val = fut.await; - wip.set(Some(val)); - }; + let wip = wip_value.clone(); + let new_fut = async move { + let val = fut.await; + wip.set(Some(val)); + }; - let task = cx.push_future(new_fut); - SuspenseInner { - task, - value: None, - wip_value, - } - }, - |sus| { - if let Some(value) = sus.value.as_ref() { - render(&value) - } else { - // generate a placeholder node if the future isnt ready - None - } - }, - ) + let task = cx.push_future(new_fut); + SuspenseInner { + task, + value: None, + wip_value, + } + }); + + if let Some(value) = sus.value.as_ref() { + render(&value) + } else { + // generate a placeholder node if the future isnt ready + None + } } struct SuspenseInner { diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index f79fb8b70..02b0046ad 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -281,8 +281,8 @@ builder_constructors! { /// ``` /// /// ## References: - /// - https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div - /// - https://www.w3schools.com/tags/tag_div.asp + /// - + /// - div {}; /// Build a @@ -883,7 +883,6 @@ builder_constructors! { src: Uri, step: String, tabindex: usize, - width: isize, // Manual implementations below... diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 9292f8e64..c0b432cbe 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -21,12 +21,12 @@ pub mod on { $( $(#[$method_attr])* pub fn $name<'a, F>( - c: NodeFactory<'a>, + factory: NodeFactory<'a>, mut callback: F, ) -> Listener<'a> where F: FnMut(Arc<$wrapper>) + 'a { - let bump = &c.bump(); + let bump = &factory.bump(); // we can't allocate unsized in bumpalo's box, so we need to craft the box manually // safety: this is essentially the same as calling Box::new() but manually @@ -48,7 +48,7 @@ pub mod on { callback: bump.alloc(std::cell::RefCell::new(Some(callback))), }; - c.listener(shortname, handler) + factory.listener(shortname, handler) } )* )* @@ -179,8 +179,8 @@ pub mod on { /// ``` /// /// ## Reference - /// - https://www.w3schools.com/tags/ev_onclick.asp - /// - https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event + /// - + /// - onclick /// oncontextmenu @@ -430,7 +430,7 @@ pub mod on { /// Get the key code as an enum Variant. /// /// This is intended for things like arrow keys, escape keys, function keys, and other non-international keys. - /// To match on unicode sequences, use the [`key`] method - this will return a string identifier instead of a limited enum. + /// To match on unicode sequences, use the [`KeyboardEvent::key`] method - this will return a string identifier instead of a limited enum. /// /// /// ## Example diff --git a/packages/html/src/global_attributes.rs b/packages/html/src/global_attributes.rs index a0bd4a679..4cde4b341 100644 --- a/packages/html/src/global_attributes.rs +++ b/packages/html/src/global_attributes.rs @@ -399,7 +399,7 @@ pub trait GlobalAttributes { height: "height", /// Specifies how flex items are aligned along the main axis of the flex container after any flexible lengths and auto margins have been resolved. - justify_content: "auto margins have been resolved.", + justify_content: "justify-content", /// Specify the location of the left edge of the positioned element. left: "left", @@ -535,6 +535,7 @@ pub trait GlobalAttributes { /// Sets the horizontal alignment of inline content. text_align: "text-align", + /// Specifies how the last line of a block or a line right before a forced line break is aligned when is justify.", text_align_last: "text-align-last", @@ -614,7 +615,7 @@ pub trait GlobalAttributes { word_wrap: "word-wrap", /// Specifies a layering or stacking order for positioned elements. - z_index : "z-index ", + z_index : "z-index", } aria_trait_methods! { diff --git a/packages/router/src/platform/web.rs b/packages/router/src/platform/web.rs index 3630fec85..5b2673273 100644 --- a/packages/router/src/platform/web.rs +++ b/packages/router/src/platform/web.rs @@ -49,7 +49,8 @@ impl RouterService { } pub fn use_router_service(cx: &ScopeState) -> Option<&Rc>> { - cx.use_hook(|_| cx.consume_state::>(), |f| f.as_ref()) + cx.use_hook(|_| cx.consume_state::>()) + .as_ref() } /// This hould only be used once per app @@ -57,7 +58,7 @@ pub fn use_router_service(cx: &ScopeState) -> Option<&Rc(cx: &ScopeState, mut parse: impl FnMut(&str) -> R + 'static) -> &R { // for the web, attach to the history api - cx.use_hook( + let state = cx.use_hook( #[cfg(not(feature = "web"))] |_| {}, #[cfg(feature = "web")] @@ -112,15 +113,13 @@ pub fn use_router(cx: &ScopeState, mut parse: impl FnMut(&str) -> R service }, - |state| { - let base = state.base_ur.borrow(); - if let Some(base) = base.as_ref() { - // - let path = format!("{}{}", base, state.get_current_route()); - } - let history = state.history_service.borrow(); + ); - state.historic_routes.last().unwrap() - }, - ) + let base = state.base_ur.borrow(); + if let Some(base) = base.as_ref() { + let path = format!("{}{}", base, state.get_current_route()); + } + + let history = state.history_service.borrow(); + state.historic_routes.last().unwrap() } diff --git a/packages/router/src/userouter.rs b/packages/router/src/userouter.rs index fad22a335..4736b634e 100644 --- a/packages/router/src/userouter.rs +++ b/packages/router/src/userouter.rs @@ -4,30 +4,27 @@ use crate::{Routable, RouterCfg, RouterService}; use dioxus_core::ScopeState; /// Initialize the app's router service and provide access to `Link` components -pub fn use_router<'a, R: Routable>(cx: &'a ScopeState, cfg: impl FnOnce(&mut RouterCfg)) -> &'a R { - cx.use_hook( - |_| { - let svc: RouterService = RouterService { - regen_route: cx.schedule_update(), - pending_routes: RefCell::new(Vec::new()), - }; - let first_path = R::default(); - cx.provide_context(svc); - UseRouterInner { - svc: cx.consume_context::>().unwrap(), - history: vec![first_path], - } - }, - |f| { - let mut pending_routes = f.svc.pending_routes.borrow_mut(); +pub fn use_router<'a, R: Routable>(cx: &'a ScopeState, _cfg: impl FnOnce(&mut RouterCfg)) -> &'a R { + let router = cx.use_hook(|_| { + let svc: RouterService = RouterService { + regen_route: cx.schedule_update(), + pending_routes: RefCell::new(Vec::new()), + }; + let first_path = R::default(); + cx.provide_context(svc); + UseRouterInner { + svc: cx.consume_context::>().unwrap(), + history: vec![first_path], + } + }); - for route in pending_routes.drain(..) { - f.history.push(route); - } + let mut pending_routes = router.svc.pending_routes.borrow_mut(); - f.history.last().unwrap() - }, - ) + for route in pending_routes.drain(..) { + router.history.push(route); + } + + router.history.last().unwrap() } struct UseRouterInner { diff --git a/packages/ssr/README.md b/packages/ssr/README.md index 59bf28baa..2ae2afffe 100644 --- a/packages/ssr/README.md +++ b/packages/ssr/README.md @@ -1,24 +1,87 @@ -# Dioxus SSR +

+

Dioxus Server-Side Rendering (SSR)

+

+ Render Dioxus to valid html. +

+
-Render a Dioxus VirtualDOM to a string. +## Resources +This crate is a part of the broader Dioxus ecosystem. For more resources about Dioxus, check out: +- [Getting Started](https://dioxuslabs.com/getting-started) +- [Book](https://dioxuslabs.com/book) +- [Reference](https://dioxuslabs.com/reference) +- [Community Examples](https://github.com/DioxusLabs/community-examples) + +## Overview + +Dioxus SSR provides utilities to render Dioxus components to valid HTML. Once rendered, the HTML can be rehydrated client side or served from your web-server of choice. ```rust -// Our app: -const App: Component = |cx| rsx!(cx, div {"hello world!"}); +let app: Component = |cx| cx.render(rsx!(div {"hello world!"})); -// Build the virtualdom from our app -let mut vdom = VirtualDOM::new(App); - -// This runs components, lifecycles, etc. without needing a physical dom. Some features (like noderef) won't work. +let mut vdom = VirtualDom::new(app); let _ = vdom.rebuild(); -// Render the entire virtualdom from the root let text = dioxus::ssr::render_vdom(&vdom); assert_eq!(text, "
hello world!
") ``` +## Basic Usage + +The simplest example is to simply render some `rsx!` nodes to html. This can be done with the [`render_lazy`] api. + +```rust +let content = dioxus::ssr::render(rsx!{ + div { + (0..5).map(|i| rsx!( + "Number: {i}" + )) + } +}); +``` + +## Rendering a VirtualDom + +```rust +let mut dom = VirtualDom::new(app); +let _ = dom.rebuild(); + +let content = dioxus::ssr::render_vdom(&dom); +``` + +## Configuring output +It's possible to configure the output of the generated HTML. + +```rust +let content = dioxus::ssr::render_vdom(&dom, |config| config.pretty(true).prerender(true)); +``` + +## Usage as a writer + +We provide the basic `SsrFormatter` object that implements `Display`, so you can integrate SSR into an existing string, or write directly to a file. + +```rust +use std::fmt::{Error, Write}; + +let mut buf = String::new(); + +let dom = VirtualDom::new(app); +let _ = dom.rebuild(); + +let args = dioxus::ssr::formatter(dom, |config| config); +buf.write_fmt!(format_args!("{}", args)); +``` + +## Configuration + + + + + + + ## Usage in pre-rendering This crate is particularly useful in pre-generating pages server-side and then selectively loading dioxus client-side to pick up the reactive elements. diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index aefd2062e..5902be660 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -1,14 +1,4 @@ -//! -//! -//! -//! -//! This crate demonstrates how to implement a custom renderer for Dioxus VNodes via the `TextRenderer` renderer. -//! The `TextRenderer` consumes a Dioxus Virtual DOM, progresses its event queue, and renders the VNodes to a String. -//! -//! While `VNode` supports "to_string" directly, it renders child components as the RSX! macro tokens. For custom components, -//! an external renderer is needed to progress the component lifecycles. The `TextRenderer` shows how to use the Virtual DOM -//! API to progress these lifecycle events to generate a fully-mounted Virtual DOM instance which can be renderer in the -//! `render` method. +#![doc = include_str!("../README.md")] use std::fmt::{Display, Formatter}; @@ -17,7 +7,6 @@ use dioxus_core::exports::bumpalo::Bump; use dioxus_core::IntoVNode; use dioxus_core::*; -/// A memory pool for rendering pub struct SsrRenderer { inner: bumpalo::Bump, cfg: SsrConfig, @@ -34,6 +23,7 @@ impl SsrRenderer { pub fn render_lazy<'a>(&'a mut self, f: LazyNodes<'a, '_>) -> String { let bump = &mut self.inner as *mut _; let s = self.render_inner(f); + // reuse the bump's memory unsafe { (&mut *bump as &mut bumpalo::Bump).reset() }; s @@ -63,13 +53,12 @@ pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String { // regular component usage. The <'a> lifetime is used to enforce that all calls of IntoVnode use the same allocator. // // When LazyNodes are provided, they are FnOnce, but do not come with a allocator selected to borrow from. The <'a> - // lifetime is therefore longer than the lifetime of the allocator which doesn't exist yet. + // lifetime is therefore longer than the lifetime of the allocator which doesn't exist... yet. // // Therefore, we cast our local bump alloactor into right lifetime. This is okay because our usage of the bump arena - // is *definitely* shorter than the <'a> lifetime, and we return owned data - not borrowed data. - // Therefore, we know that no references are leaking. - let _b: &'a Bump = unsafe { std::mem::transmute(borrowed) }; + // is *definitely* shorter than the <'a> lifetime, and we return *owned* data - not borrowed data. + let _b = unsafe { std::mem::transmute::<&Bump, &'a Bump>(borrowed) }; let root = f.into_vnode(NodeFactory::new(_b)); format!( @@ -85,6 +74,7 @@ pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String { pub fn render_vdom(dom: &VirtualDom) -> String { format!("{:}", TextRenderer::from_vdom(dom, SsrConfig::default())) } + pub fn render_vdom_cfg(dom: &VirtualDom, cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> String { format!( "{:}", diff --git a/src/lib.rs b/src/lib.rs index 5259377b0..ce5239411 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ //!
-//!

🌗🚀 📦 Dioxus

+//!

🌗🚀 Dioxus

//!

//! A concurrent, functional, virtual DOM for Rust //!

@@ -37,7 +37,7 @@ //! All Dioxus apps are built by composing functions that take in a `Scope` which is generic over some `Properties` and return an `Element`. //! A `Scope` holds relevant state data for the the currently-rendered component. //! -//! To launch an app, we use the `launch` method for the specific renderer we want to use. In the launch function, was pass the app's `Component`. +//! To launch an app, we use the `launch` method for the specific renderer we want to use. In the launch function, we pass the app's `Component`. //! //! ```rust //! use dioxus::prelude::*; @@ -51,10 +51,89 @@ //! } //! ``` //! +//! ## Elements & your first component +//! +//! To assemble UI trees with Diouxs, you need to use the `render` function on +//! something called `LazyNodes`. To produce `LazyNodes`, you can use the `rsx!` +//! macro or the NodeFactory API. For the most part, you want to use the `rsx!` +//! macro. +//! +//! Any element in `rsx!` can have attributes, listeners, and children. For +//! consistency, we force all attributes and listeners to be listed *before* +//! children. +//! +//! ```rust +//! let value = "123"; +//! +//! rsx!( +//! div { +//! class: "my-class {value}", // <--- attribute +//! onclick: move |_| log::info!("clicked!"), // <--- listener +//! h1 { "hello world" }, // <--- child +//! } +//! ) +//! ``` +//! +//! The rsx macro accepts attributes in "struct form" and then will parse the rest +//! of the body as child elements and rust expressions. Any rust expression that +//! implements `IntoIterator` will be parsed as a child. +//! +//! ```rust +//! rsx!( +//! div { +//! (0..10).map(|_| rsx!(span { "hello world" })) +//! } +//! ) +//! +//! ``` +//! +//! Used within components, the rsx! macro must be rendered into an `Element` with +//! the `render` function on Scope. +//! +//! If we want to omit the boilerplate of `cx.render`, we can simply pass in +//! `cx` as the first argument of rsx. This is sometimes useful when we need to +//! render nodes in match statements. +//! +//! ```rust +//! fn example(cx: Scope) -> Element { +//! +//! // both of these are equivalent +//! cx.render(rsx!("hello world")) +//! +//! rsx!(cx, "hello world!") +//! } +//! ``` +//! +//! Putting everything together, we can write a simple component that a list of +//! elements: +//! +//! ```rust +//! fn app(cx: Scope) -> Element { +//! let name = "dave"; +//! cx.render(rsx!( +//! h1 { "Hello, {name}!" } +//! div { +//! class: "my-class", +//! id: "my-id", +//! +//! (0..5).map(|i| rsx!( +//! div { key: "{i}" +//! "FizzBuzz: {i}" +//! } +//! )) +//! +//! } +//! )) +//! } +//! ``` +//! //! ## Components //! -//! We can compose these function components to build a complex app. Each new component we design must take some Properties. -//! For components with no explicit properties, we can use the `()` type. In Dioxus, all properties are memoized by default! +//! We can compose these function components to build a complex app. Each new +//! component we design must take some Properties. For components with no explicit +//! properties, we can use the `()` type or simply omit the type altogether. +//! +//! In Dioxus, all properties are memoized by default! //! //! ```rust //! fn App(cx: Scope) -> Element { @@ -67,7 +146,9 @@ //! } //! ``` //! -//! Our `Header` component takes in a `title` and a `color` property, which we delcare on an explicit `HeaderProps` struct. +//! Our `Header` component takes in a `title` and a `color` property, which we +//! delcare on an explicit `HeaderProps` struct. +//! //! ``` //! // The `Props` derive macro lets us add additional functionality to how props are interpreted. //! #[derive(Props, PartialEq)] @@ -86,9 +167,68 @@ //! } //! ``` //! +//! Components may use the `inline_props` macro to completely inline the props +//! definition into the function arguments. +//! +//! ```rust +//! #[inline_props] +//! fn Header(cx: Scope, title: String, color: String) -> Element { +//! cx.render(rsx!( +//! div { +//! background_color: "{color}" +//! h1 { "{title}" } +//! } +//! )) +//! } +//! ``` +//! +//! Components may also borrow data from their parent component. We just need to +//! attach some lifetimes to the props struct. +//! > Note: we don't need to derive `PartialEq` for borrowed props since they cannot be memoized. +//! +//! ```rust +//! #[derive(Props)] +//! struct HeaderProps<'a> { +//! title: &'a str, +//! color: &'a str, +//! } +//! +//! fn Header<'a>(cx: Scope<'a, HeaderProps<'a>>) -> Element { +//! cx.render(rsx!( +//! div { +//! background_color: "{cx.props.color}" +//! h1 { "{cx.props.title}" } +//! } +//! )) +//! } +//! ``` +//! +//! Components that beging with an uppercase letter may be called through +//! traditional curly-brace syntax like so: +//! +//! ```rust +//! rsx!( +//! Header { title: "My App" } +//! ) +//! ``` +//! +//! Alternatively, if your components begin with a lowercase letter, you can use +//! the function call syntax: +//! +//! ```rust +//! rsx!( +//! header( title: "My App" ) +//! ) +//! ``` +//! //! ## Hooks //! -//! While components are reusable forms of UI elements, hooks are reusable forms of logic. All hooks start with `use_`. We can use hooks to declare state. +//! While components are reusable forms of UI elements, hooks are reusable forms +//! of logic. Hooks provide us a way of retrieving state from `Scope` and using +//! it to render UI elements. +//! +//! By convention, all hooks are functions that should start with `use_`. We can +//! use hooks to define state and modify it from within listeners. //! //! ```rust //! fn app(cx: Scope) -> Element { @@ -98,10 +238,11 @@ //! } //! ``` //! -//! Hooks are sensitive to how they are used. To use hooks, you must abide by the ["rules of hooks" (borrowed from react)](https://reactjs.org/docs/hooks-rules.html): -//! - Hooks should not be called in callbacks -//! - Hooks should not be called in out of order -//! - Hooks should not be called in loops or conditionals +//! Hooks are sensitive to how they are used. To use hooks, you must abide by the +//! ["rules of hooks" (borrowed from react)](https://reactjs.org/docs/hooks-rules.html): +//! - Functions with "use_" should not be called in callbacks +//! - Functions with "use_" should not be called out of order +//! - Functions with "use_" should not be called in loops or conditionals //! //! In a sense, hooks let us add a field of state to our component without declaring //! an explicit struct. However, this means we need to "load" the struct in the right @@ -112,7 +253,7 @@ //! ```rust //! fn use_username(cx: &ScopeState, id: Uuid) -> bool { //! let users = use_context::(cx); -//! users.get(&id).is_some().map(|user| user.logged_in).ok_or(false) +//! users.get(&id).map(|user| user.logged_in).ok_or(false) //! } //! ``` //! @@ -120,7 +261,7 @@ //! //! ```rust //! fn use_mut_string(cx: &ScopeState) -> &mut String { -//! cx.use_hook(|_| "Hello".to_string(), |hook| hook) +//! cx.use_hook(|_| "Hello".to_string()) //! } //! ``` //!