diff --git a/examples/crm.rs b/examples/crm.rs index 724010177..c55f23f78 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -1,11 +1,13 @@ -/* -Tiny CRM: A port of the Yew CRM example to Dioxus. -*/ +//! Tiny CRM: A port of the Yew CRM example to Dioxus. +#![allow(non_snake_case)] + +use std::sync::{Arc, Mutex}; + use dioxus::prelude::*; -use dioxus_router::{Link, Route, Router}; +use dioxus_router::prelude::*; fn main() { - dioxus_desktop::launch(app); + dioxus_desktop::launch(App); } #[derive(Clone, Debug, Default)] @@ -15,92 +17,185 @@ pub struct Client { pub description: String, } -fn app(cx: Scope) -> Element { - let clients = use_ref(cx, || vec![] as Vec); - let firstname = use_state(cx, String::new); - let lastname = use_state(cx, String::new); - let description = use_state(cx, String::new); +type ClientContext = Arc>>; - cx.render(rsx!( - 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"} - Router { - Route { to: "/", - 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}"} - }) - ) - } - Link { to: "/new", class: "pure-button pure-button-primary", "Add New" } - Link { to: "/new", class: "pure-button", "Settings" } - } - } - Route { to: "/new", - 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.to_string(), - first_name: firstname.to_string(), - last_name: lastname.to_string(), - }); - description.set(String::new()); - firstname.set(String::new()); - lastname.set(String::new()); - }, - "Add New" - } - Link { to: "/", class: "pure-button", "Go Back" } - } - } - Route { to: "/settings", - 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" - } - Link { to: "/", class: "pure-button pure-button-primary", "Go Back" } - } - } - } +fn App(cx: Scope) -> Element { + use_router(cx, &|| RouterConfiguration::default(), &|| { + Segment::content(comp(ClientList)) + .fixed( + "new", + Route::content(comp(ClientAdd)).name::(), + ) + .fixed( + "settings", + Route::content(comp(Settings)).name::(), + ) + }); + + use_context_provider::(&cx, Default::default); + + render! { + link { + rel: "stylesheet", + href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css", + integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5", + crossorigin: "anonymous", } - )) + + style { " + .red {{ + background-color: rgb(202, 60, 60) !important; + }} + " } + + h1 {"Dioxus CRM Example"} + + Outlet { } + } +} + +fn ClientList(cx: Scope) -> Element { + let clients = use_context::(cx).unwrap(); + + cx.render(rsx! { + h2 { "List of Clients" } + + Link { + target: named::(), + class: "pure-button pure-button-primary", + "Add Client" + } + Link { + target: named::(), + class: "pure-button", + "Settings" + } + + clients.lock().unwrap().iter().map(|client| rsx! { + div { + class: "client", + style: "margin-bottom: 50px", + + p { "Name: {client.first_name} {client.last_name}" } + p { "Description: {client.description}" } + } + }) + }) +} + +struct ClientAddName; +fn ClientAdd(cx: Scope) -> Element { + let clients = use_context::(cx).unwrap(); + let first_name = use_state(&cx, String::new); + let last_name = use_state(&cx, String::new); + let description = use_state(&cx, String::new); + + let navigator = use_navigate(&cx).unwrap(); + + cx.render(rsx! { + h2 { "Add new Client" } + + form { + class: "pure-form pure-form-aligned", + onsubmit: move |_| { + let mut clients = clients.lock().unwrap(); + + clients.push(Client { + first_name: first_name.to_string(), + last_name: last_name.to_string(), + description: description.to_string(), + }); + + navigator.push(named::()); + }, + + fieldset { + div { + class: "pure-control-group", + label { + "for": "first_name", + "First Name" + } + input { + id: "first_name", + "type": "text", + placeholder: "First Name…", + required: "", + value: "{first_name}", + oninput: move |e| first_name.set(e.value.clone()) + } + } + + div { + class: "pure-control-group", + label { + "for": "last_name", + "Last Name" + } + input { + id: "last_name", + "type": "text", + placeholder: "Last Name…", + required: "", + value: "{last_name}", + oninput: move |e| last_name.set(e.value.clone()) + } + } + + div { + class: "pure-control-group", + label { + "for": "description", + "Description" + } + textarea { + id: "description", + placeholder: "Description…", + value: "{description}", + oninput: move |e| description.set(e.value.clone()) + } + } + + div { + class: "pure-controls", + button { + "type": "submit", + class: "pure-button pure-button-primary", + "Save" + } + Link { + target: named::(), + class: "pure-button pure-button-primary red", + "Cancel" + } + } + } + + + } + }) +} + +struct SettingsName; +fn Settings(cx: Scope) -> Element { + let clients = use_context::(&cx).unwrap(); + + cx.render(rsx! { + h2 { "Settings" } + + button { + class: "pure-button pure-button-primary red", + onclick: move |_| { + let mut clients = clients.lock().unwrap(); + clients.clear(); + }, + "Remove all Clients" + } + + Link { + target: named::(), + class: "pure-button", + "Go back" + } + }) } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 752b58760..4a184edc3 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -1,6 +1,8 @@ +#![allow(non_snake_case)] + use dioxus::prelude::*; use dioxus_desktop::{tao::dpi::LogicalSize, Config, WindowBuilder}; -use dioxus_router::{Link, Route, Router}; +use dioxus_router::prelude::*; fn main() { env_logger::init(); @@ -16,24 +18,43 @@ fn main() { } fn app(cx: Scope) -> Element { - cx.render(rsx! { - Router { - Route { to: "/", "Home" } - Route { to: "/games", "Games" } - Route { to: "/play", "Play" } - Route { to: "/settings", "Settings" } + use_router(cx, &|| RouterConfiguration::default(), &|| { + Segment::content(comp(Home)) + .fixed("games", comp(Games)) + .fixed("play", comp(Play)) + .fixed("settings", comp(Settings)) + }); - p { - "----" - } - nav { - ul { - Link { to: "/", li { "Home" } } - Link { to: "/games", li { "Games" } } - Link { to: "/play", li { "Play" } } - Link { to: "/settings", li { "Settings" } } - } + render! { + Outlet { } + + p { + "----" + } + + nav { + ul { + li { Link { target: "/", "Home" } } + li { Link { target: "/games", "Games" } } + li { Link { target: "/play", "Play" } } + li { Link { target: "/settings", "Settings" } } } } - }) + } +} + +fn Home(cx: Scope) -> Element { + render!("Home") +} + +fn Games(cx: Scope) -> Element { + render!("Games") +} + +fn Play(cx: Scope) -> Element { + render!("Play") +} + +fn Settings(cx: Scope) -> Element { + render!("Settings") } diff --git a/examples/link.rs b/examples/link.rs index afef99cf2..b25ee11c3 100644 --- a/examples/link.rs +++ b/examples/link.rs @@ -1,11 +1,17 @@ +#![allow(non_snake_case)] + use dioxus::prelude::*; -use dioxus_router::{Link, Route, Router}; +use dioxus_router::prelude::*; fn main() { dioxus_desktop::launch(app); } fn app(cx: Scope) -> Element { + use_router(cx, &|| RouterConfiguration::default(), &|| { + Segment::content(comp(Home)).fixed("settings", comp(Settings)) + }); + cx.render(rsx! ( div { p { @@ -21,15 +27,20 @@ fn app(cx: Scope) -> Element { } } div { - Router { - Route { to: "/", h1 { "Home" } }, - Route { to: "/settings", h1 { "settings" } }, - p { "----"} - ul { - Link { to: "/", li { "Router link to home" } }, - Link { to: "/settings", li { "Router link to settings" } }, - } + Outlet { } + p { "----"} + ul { + Link { target: "/", li { "Router link to home" } }, + Link { target: "/settings", li { "Router link to settings" } }, } } )) } + +fn Home(cx: Scope) -> Element { + render!(h1 { "Home" }) +} + +fn Settings(cx: Scope) -> Element { + render!(h1 { "Settings" }) +} diff --git a/examples/router.rs b/examples/router.rs index 5766b8e1c..026260b7f 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -1,36 +1,58 @@ #![allow(non_snake_case)] use dioxus::prelude::*; -use dioxus_router::{Link, Route, Router}; -use serde::Deserialize; +use dioxus_router::prelude::*; fn main() { dioxus_desktop::launch(app); } fn app(cx: Scope) -> Element { - cx.render(rsx! { - Router { - ul { - Link { to: "/", li { "Go home!" } } - Link { to: "/users", li { "List all users" } } - Link { to: "/blog", li { "Blog posts" } } + use_router(cx, &|| RouterConfiguration::default(), &|| { + Segment::content(comp(Home)) + .fixed( + "users", + Route::empty() + .nested(Segment::content(comp(UserList)).catch_all((comp(User), UserId { }))), + ) + .fixed( + "blog", + Route::empty().nested( + Segment::content(comp(BlogList)).catch_all((comp(BlogPost), PostId { })), + ), + ) + .fallback(comp(E404)) + }); - Link { to: "/users/bill", li { "List all users" } } - Link { to: "/blog/5", li { "Blog post 5" } } - } - Route { to: "/", "Home" } - Route { to: "/users", "User list" } - Route { to: "/users/:name", User {} } - Route { to: "/blog", "Blog list" } - Route { to: "/blog/:post", BlogPost {} } - Route { to: "", "Err 404 Route Not Found" } + cx.render(rsx! { + ul { + li { Link { target: "/", "Go home!" } } + li { Link { target: "/users", "List all users" } } + li { Link { target: "/blog", "Blog posts" }} } + + Outlet { } }) } +fn Home(cx: Scope) -> Element { + render!(h1 { "Home" }) +} + +fn BlogList(cx: Scope) -> Element { + render! { + h1 { "Blog Posts" } + ul { + li { Link { target: "/blog/1", "First blog post" } } + li { Link { target: "/blog/2", "Second blog post" } } + li { Link { target: "/blog/3", "Third blog post" } } + } + } +} + +struct PostId; fn BlogPost(cx: Scope) -> Element { - let post = dioxus_router::use_route(cx).last_segment().unwrap(); + let post = use_route(cx)?.parameter::().unwrap(); cx.render(rsx! { div { @@ -40,24 +62,31 @@ fn BlogPost(cx: Scope) -> Element { }) } -#[derive(Deserialize)] -struct Query { - bold: bool, +fn UserList(cx: Scope) -> Element { + render! { + h1 { "Users" } + ul { + li { Link { target: "/users/bill", "Bill" } } + li { Link { target: "/users/jeremy", "Jeremy" } } + li { Link { target: "/users/adrian", "Adrian" } } + } + } } +struct UserId; fn User(cx: Scope) -> Element { - let post = dioxus_router::use_route(cx).last_segment().unwrap(); + let state = use_route(cx)?; - let query = dioxus_router::use_route(cx) - .query::() - .unwrap_or(Query { bold: false }); + let user = state.parameter::().unwrap(); + + let query = state.query.as_ref().map(|q| q.clone()).unwrap_or_default(); + let bold = query.contains("bold") && !query.contains("bold=false"); cx.render(rsx! { div { - h1 { "Reading blog post: {post}" } - p { "example blog post" } + h1 { "Showing user: {user}" } - if query.bold { + if bold { rsx!{ b { "bold" } } } else { rsx!{ i { "italic" } } @@ -65,3 +94,7 @@ fn User(cx: Scope) -> Element { } }) } + +fn E404(cx: Scope) -> Element { + render!(h1 { "Error 404 - Page not Found" }) +} diff --git a/examples/simple_desktop.rs b/examples/simple_desktop.rs index d8c00afac..35d101d83 100644 --- a/examples/simple_desktop.rs +++ b/examples/simple_desktop.rs @@ -1,5 +1,7 @@ +#![allow(non_snake_case)] + use dioxus::prelude::*; -use dioxus_router::*; +use dioxus_router::prelude::*; fn main() { simple_logger::SimpleLogger::new() @@ -12,49 +14,58 @@ fn main() { } fn app(cx: Scope) -> Element { - cx.render(rsx! { - Router { - h1 { "Your app here" } - ul { - Link { to: "/", li { "home" } } - Link { to: "/blog", li { "blog" } } - Link { to: "/blog/tim", li { "tims' blog" } } - Link { to: "/blog/bill", li { "bills' blog" } } - Link { to: "/blog/james", - li { "james amazing' blog" } - } - Link { to: "/apples", li { "go to apples" } } - } - Route { to: "/", Home {} } - Route { to: "/blog/", BlogList {} } - Route { to: "/blog/:id/", BlogPost {} } - Route { to: "/oranges", "Oranges are not apples!" } - Redirect { from: "/apples", to: "/oranges" } + use_router(cx, &|| RouterConfiguration::default(), &|| { + Segment::content(comp(Home)) + .fixed( + "blog", + Route::empty().nested( + Segment::content(comp(BlogList)).catch_all((comp(BlogPost), PostId {})), + ), + ) + .fixed("oranges", comp(Oranges)) + .fixed("apples", "/oranges") + }); + + render! { + h1 { "Your app here" } + ul { + li { Link { target: "/", "home" } } + li { Link { target: "/blog", "blog" } } + li { Link { target: "/blog/tim", "tims' blog" } } + li { Link { target: "/blog/bill", "bills' blog" } } + li { Link { target: "/blog/james", "james amazing' blog" } } + li { Link { target: "/apples", "go to apples" } } } - }) + Outlet { } + } } fn Home(cx: Scope) -> Element { log::debug!("rendering home {:?}", cx.scope_id()); - cx.render(rsx! { h1 { "Home" } }) + render! { h1 { "Home" } } } fn BlogList(cx: Scope) -> Element { log::debug!("rendering blog list {:?}", cx.scope_id()); - cx.render(rsx! { div { "Blog List" } }) + render! { div { "Blog List" } } } +struct PostId; fn BlogPost(cx: Scope) -> Element { - let Some(id) = use_route(cx).segment("id") else { - return cx.render(rsx! { div { "No blog post id" } }) + let Some(id) = use_route(cx)?.parameter::() else { + return render!(div { "No blog post id" }); }; log::debug!("rendering blog post {}", id); - cx.render(rsx! { + render! { div { h3 { "blog post: {id:?}" } - Link { to: "/blog/", "back to blog list" } + Link { target: "/blog/", "back to blog list" } } - }) + } +} + +fn Oranges(cx: Scope) -> Element { + render!("Oranges are not apples!") }