From fe12b1062f53adecc02cf87791b9b4b6d6062a23 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 15 Jan 2024 14:40:56 -0800 Subject: [PATCH] Clean up more examples --- examples/calculator.rs | 2 +- examples/compose.rs | 50 ++-- examples/counter.rs | 4 +- examples/crm.rs | 5 +- examples/dog_app.rs | 4 +- examples/drops.rs | 36 --- examples/file_explorer.rs | 2 +- examples/generic_component.rs | 2 +- examples/rsx_usage.rs | 513 +++++++++++++++++----------------- 9 files changed, 285 insertions(+), 333 deletions(-) delete mode 100644 examples/drops.rs diff --git a/examples/calculator.rs b/examples/calculator.rs index af71984a9..23cd1284a 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -22,7 +22,7 @@ fn app() -> Element { let val = use_signal(|| String::from("0")); let input_digit = move |num: u8| { - if val.get() == "0" { + if *val() == "0" { val.set(String::new()); } diff --git a/examples/compose.rs b/examples/compose.rs index dc947c208..2410e973f 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -2,52 +2,44 @@ use dioxus::prelude::*; use futures_util::StreamExt; +use tokio::sync::mpsc::UnboundedSender; fn main() { dioxus_desktop::launch(app); } fn app() -> Element { - let emails_sent = use_signal(Vec::new); + let emails_sent = use_signal(|| Vec::new() as Vec); - let tx = use_coroutine(|mut rx: UnboundedReceiver| { - to_owned![emails_sent]; - async move { - while let Some(message) = rx.next().await { - emails_sent.write().push(message); - } + // Wait for responses to the compose channel, and then push them to the emails_sent signal. + let tx = use_coroutine(|mut rx: UnboundedReceiver| async move { + while let Some(message) = rx.next().await { + emails_sent.write().push(message); } }); + let open_compose_window = move |evt: MouseEvent| { + dioxus_desktop::window().new_window( + VirtualDom::new_with_props(compose, tx.clone()), + Default::default(), + ) + }; + rsx! { - div { - h1 { "This is your email" } - - button { - onclick: move |_| { - let dom = VirtualDom::new_with_props(compose, ComposeProps { app_tx: tx.clone() }); - dioxus_desktop::window().new_window(dom, Default::default()); - }, - "Click to compose a new email" - } - - ul { - for message in emails_sent.read().iter() { - li { - h3 { "email" } - span {"{message}"} - } + h1 { "This is your email" } + button { onclick: open_compose_window, "Click to compose a new email" } + ul { + for message in emails_sent.read().iter() { + li { + h3 { "email" } + span { "{message}" } } } } } } -struct ComposeProps { - app_tx: Coroutine, -} - -fn compose(cx: Scope) -> Element { +fn compose(receiver: UnboundedSender) -> Element { let user_input = use_signal(String::new); rsx! { diff --git a/examples/counter.rs b/examples/counter.rs index c766e45ed..0be35938a 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -9,14 +9,14 @@ fn main() { fn app() -> Element { let counters = use_signal(|| vec![0, 0, 0]); - let sum: usize = counters.iter().copied().sum(); + let sum = use_selector(move || counters.read().iter().copied().sum() as usize); render! { div { button { onclick: move |_| counters.write().push(0), "Add counter" } button { onclick: move |_| { counters.write().pop(); }, "Remove counter" } p { "Total: {sum}" } - for (i, counter) in counters.iter().enumerate() { + for (i, counter) in counters.read().iter().enumerate() { li { button { onclick: move |_| counters.write()[i] -= 1, "-1" } input { diff --git a/examples/crm.rs b/examples/crm.rs index 536e0aeef..0ab034a02 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -145,10 +145,7 @@ fn Settings() -> Element { button { class: "pure-button pure-button-primary red", - onclick: move |_| { - let mut clients = clients.write(); - clients.clear(); - }, + onclick: move |_| clients.write().clear(), "Remove all Clients" } diff --git a/examples/dog_app.rs b/examples/dog_app.rs index 8d90dfb5e..91ff0f36d 100644 --- a/examples/dog_app.rs +++ b/examples/dog_app.rs @@ -36,7 +36,7 @@ fn app() -> Element { } } } - div { flex: "50%", BreedPic { breed: breed.to_string() } } + div { flex: "50%", BreedPic { breed } } } } }, @@ -50,7 +50,7 @@ struct DogApi { } #[component] -fn BreedPic(breed: String) -> Element { +fn BreedPic(breed: Signal) -> Element { let fut = use_future(|breed| async move { reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random")) .await diff --git a/examples/drops.rs b/examples/drops.rs deleted file mode 100644 index 9f4a5bb35..000000000 --- a/examples/drops.rs +++ /dev/null @@ -1,36 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - dioxus_desktop::launch(app); -} - -fn app() -> Element { - let count = if cx.generation() % 2 == 0 { 10 } else { 0 }; - - println!("Generation: {}", cx.generation()); - - if cx.generation() < 10 { - cx.needs_update(); - } - - render! { - for _ in 0..count { - drop_child {} - } - } -} - -fn drop_child() -> Element { - cx.use_hook(|| Drops); - render! { - div{} - } -} - -struct Drops; - -impl Drop for Drops { - fn drop(&mut self) { - println!("Dropped!"); - } -} diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 92a900676..8436eb7a4 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -19,7 +19,7 @@ fn main() { const _STYLE: &str = manganis::mg!(file("./examples/assets/fileexplorer.css")); -fn app(_props: ()) -> Element { +fn app() -> Element { let files = use_signal(Files::new); rsx! { diff --git a/examples/generic_component.rs b/examples/generic_component.rs index d5e5e1daa..632a69a9a 100644 --- a/examples/generic_component.rs +++ b/examples/generic_component.rs @@ -19,6 +19,6 @@ struct GenericChildProps { fn generic_child(props: GenericChildProps) -> Element { render! { - div { "{&props.data}" } + div { "{props.data}" } } } diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 778e0dc10..656a178ed 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -39,264 +39,263 @@ //! - Allow top-level fragments fn main() { - dioxus_desktop::launch(App); -} - -use core::{fmt, str::FromStr}; -use std::fmt::Display; - -use baller::Baller; -use dioxus::prelude::*; - -#[component] -fn App() -> Element { - let formatting = "formatting!"; - let formatting_tuple = ("a", "b"); - let lazy_fmt = format_args!("lazily formatted text"); - let asd = 123; - rsx! { - div { - // Elements - div {} - 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" - "Blocks" - "Use comments as separators in html" - } - div { - h1 {"multiple"} - h2 {"nested"} - h3 {"elements"} - } - div { - class: "my special div", - h1 {"Headers and attributes!"} - } - div { - // pass simple rust expressions in - class: lazy_fmt, - id: format_args!("attributes can be passed lazily with std::fmt::Arguments"), - class: "asd", - class: "{asd}", - // if statements can be used to conditionally render attributes - class: if formatting.contains("form") { "{asd}" }, - div { - class: { - const WORD: &str = "expressions"; - format_args!("Arguments can be passed in through curly braces for complex {WORD}") - } - } - } - - // Expressions can be used in element position too: - {rsx!(p { "More templating!" })}, - - // Iterators - {(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}" })) - } - - // Matching - 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 {}))}, - - // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element - if false { - h1 {"Top text"} - } else { - h1 {"Bottom text"} - } - - // Using optionals for diverging branches - // Note that since this is wrapped in curlies, it's interpreted as an expression - {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<()>}, - - // can also just use empty fragments - Fragment {} - - // Fragments let you insert groups of nodes without a parent. - // This lets you make components that insert elements as siblings without a container. - div {"A"} - Fragment { - div {"B"} - div {"C"} - Fragment { - "D" - Fragment { - "E" - "F" - } - } - } - - // Components - // Can accept any paths - // Notice how you still get syntax highlighting and IDE support :) - Baller {} - baller::Baller {} - crate::baller::Baller {} - - // Can take properties - Taller { a: "asd" } - - // Can take optional properties - Taller { a: "asd" } - - // Can pass in props directly as an expression - { - let props = TallerProps {a: "hello", children: None }; - rsx!(Taller { ..props }) - } - - // Spreading can also be overridden manually - Taller { - ..TallerProps { a: "ballin!", children: None }, - a: "not ballin!" - } - - // Can take children too! - Taller { a: "asd", div {"hello world!"} } - - // This component's props are defined *inline* with the `inline_props` macro - WithInline { text: "using functionc all syntax" } - - // Components can be generic too - // This component takes i32 type to give you typed input - TypedInput:: {} - - // Type inference can be used too - TypedInput { initial: 10.0 } - - // geneircs with the `inline_props` macro - Label { text: "hello geneirc world!" } - Label { text: 99.9 } - - // Lowercase components work too, as long as they are access using a path - baller::lowercase_component {} - - // For in-scope lowercase components, use the `self` keyword - self::lowercase_helper {} - - // helper functions - // Anything that implements IntoVnode can be dropped directly into Rsx - {helper("hello world!")} - - // Strings can be supplied directly - {String::from("Hello world!")} - - // So can format_args - {format_args!("Hello {}!", "world")} - - // Or we can shell out to a helper function - {format_dollars(10, 50)} - } - } -} - -fn format_dollars(dollars: u32, cents: u32) -> String { - format!("${dollars}.{cents:02}") -} - -fn helper<'a>(cx: &'a ScopeState, text: &'a str) -> Element { - rsx! { - p { "{text}" } - } -} - -// no_case_check disables PascalCase checking if you *really* want a snake_case component. -// This will likely be deprecated/removed in a future update that will introduce a more polished linting system, -// something like Clippy. -#[component(no_case_check)] -fn lowercase_helper() -> Element { - rsx! { - "asd" - } -} - -mod baller { - use super::*; - - #[component] - /// This component totally balls - pub fn Baller() -> Element { - todo!() - } - - // no_case_check disables PascalCase checking if you *really* want a snake_case component. - // This will likely be deprecated/removed in a future update that will introduce a more polished linting system, - // something like Clippy. - #[component(no_case_check)] - pub fn lowercase_component() -> Element { - rsx! { "look ma, no uppercase" } - } -} - -/// Documention for this component is visible within the rsx macro -#[component] -pub fn Taller( - /// Fields are documented and accessible in rsx! - a: &'static str, - children: Element, -) -> Element { - rsx! { - {&cx.props.children} - } -} - -#[derive(Props, PartialEq, Eq)] -pub struct TypedInputProps { - #[props(optional, default)] - initial: Option, -} - -#[allow(non_snake_case)] -pub fn TypedInput(_: Scope>) -> Element -where - T: FromStr + fmt::Display, - ::Err: std::fmt::Display, -{ todo!() + // dioxus_desktop::launch(App); } -#[component] -fn WithInline<'a>(cx: Scope<'a>, text: &'a str) -> Element { - rsx! { - p { "{text}" } - } -} +// use core::{fmt, str::FromStr}; +// use std::fmt::Display; -#[component] -fn Label(text: T) -> Element -where - T: Display, -{ - rsx! { - p { "{text}" } - } -} +// use baller::Baller; +// use dioxus::prelude::*; + +// #[component] +// fn App() -> Element { +// let formatting = "formatting!"; +// let formatting_tuple = ("a", "b"); +// let lazy_fmt = format_args!("lazily formatted text"); +// let asd = 123; +// rsx! { +// div { +// // Elements +// div {} +// 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" +// "Blocks" +// "Use comments as separators in html" +// } +// div { +// h1 {"multiple"} +// h2 {"nested"} +// h3 {"elements"} +// } +// div { +// class: "my special div", +// h1 {"Headers and attributes!"} +// } +// div { +// // pass simple rust expressions in +// class: lazy_fmt, +// id: format_args!("attributes can be passed lazily with std::fmt::Arguments"), +// class: "asd", +// class: "{asd}", +// // if statements can be used to conditionally render attributes +// class: if formatting.contains("form") { "{asd}" }, +// div { +// class: { +// const WORD: &str = "expressions"; +// format_args!("Arguments can be passed in through curly braces for complex {WORD}") +// } +// } +// } + +// // Expressions can be used in element position too: +// {rsx!(p { "More templating!" })}, + +// // Iterators +// {(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}" })) +// } + +// // Matching +// 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 {}))}, + +// // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element +// if false { +// h1 {"Top text"} +// } else { +// h1 {"Bottom text"} +// } + +// // Using optionals for diverging branches +// // Note that since this is wrapped in curlies, it's interpreted as an expression +// {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<()>}, + +// // can also just use empty fragments +// Fragment {} + +// // Fragments let you insert groups of nodes without a parent. +// // This lets you make components that insert elements as siblings without a container. +// div {"A"} +// Fragment { +// div {"B"} +// div {"C"} +// Fragment { +// "D" +// Fragment { +// "E" +// "F" +// } +// } +// } + +// // Components +// // Can accept any paths +// // Notice how you still get syntax highlighting and IDE support :) +// Baller {} +// baller::Baller {} +// crate::baller::Baller {} + +// // Can take properties +// Taller { a: "asd" } + +// // Can take optional properties +// Taller { a: "asd" } + +// // Can pass in props directly as an expression +// { +// let props = TallerProps {a: "hello", children: None }; +// rsx!(Taller { ..props }) +// } + +// // Spreading can also be overridden manually +// Taller { +// ..TallerProps { a: "ballin!", children: None }, +// a: "not ballin!" +// } + +// // Can take children too! +// Taller { a: "asd", div {"hello world!"} } + +// // This component's props are defined *inline* with the `inline_props` macro +// WithInline { text: "using functionc all syntax" } + +// // Components can be generic too +// // This component takes i32 type to give you typed input +// TypedInput:: {} + +// // Type inference can be used too +// TypedInput { initial: 10.0 } + +// // geneircs with the `inline_props` macro +// Label { text: "hello geneirc world!" } +// Label { text: 99.9 } + +// // Lowercase components work too, as long as they are access using a path +// baller::lowercase_component {} + +// // For in-scope lowercase components, use the `self` keyword +// self::lowercase_helper {} + +// // helper functions +// // Anything that implements IntoVnode can be dropped directly into Rsx +// {helper("hello world!")} + +// // Strings can be supplied directly +// {String::from("Hello world!")} + +// // So can format_args +// {format_args!("Hello {}!", "world")} + +// // Or we can shell out to a helper function +// {format_dollars(10, 50)} +// } +// } +// } + +// fn format_dollars(dollars: u32, cents: u32) -> String { +// format!("${dollars}.{cents:02}") +// } + +// fn helper<'a>(cx: &'a ScopeState, text: &'a str) -> Element { +// rsx! { +// p { "{text}" } +// } +// } + +// // no_case_check disables PascalCase checking if you *really* want a snake_case component. +// // This will likely be deprecated/removed in a future update that will introduce a more polished linting system, +// // something like Clippy. +// #[component(no_case_check)] +// fn lowercase_helper() -> Element { +// rsx! { +// "asd" +// } +// } + +// mod baller { +// use super::*; + +// #[component] +// /// This component totally balls +// pub fn Baller() -> Element { +// todo!() +// } + +// // no_case_check disables PascalCase checking if you *really* want a snake_case component. +// // This will likely be deprecated/removed in a future update that will introduce a more polished linting system, +// // something like Clippy. +// #[component(no_case_check)] +// pub fn lowercase_component() -> Element { +// rsx! { "look ma, no uppercase" } +// } +// } + +// /// Documention for this component is visible within the rsx macro +// #[component] +// pub fn Taller( +// /// Fields are documented and accessible in rsx! +// a: &'static str, +// children: Element, +// ) -> Element { +// rsx! { {&children} } +// } + +// #[derive(Props, PartialEq, Eq)] +// pub struct TypedInputProps { +// #[props(optional, default)] +// initial: Option, +// } + +// #[allow(non_snake_case)] +// pub fn TypedInput(_: Scope>) -> Element +// where +// T: FromStr + fmt::Display, +// ::Err: std::fmt::Display, +// { +// todo!() +// } + +// #[component] +// fn WithInline(cx: Scope<'a>, text: &'a str) -> Element { +// rsx! { +// p { "{text}" } +// } +// } + +// #[component] +// fn Label(text: T) -> Element +// where +// T: Display, +// { +// rsx! { +// p { "{text}" } +// } +// }