From 601078f9cf78a58d7502a377676ac94f3cf037bf Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 22 Oct 2021 01:16:39 -0400 Subject: [PATCH 1/9] fix: desktop and mobile --- packages/core/Cargo.toml | 4 +- packages/core/examples/async.rs | 4 +- packages/core/tests/borrowedstate.rs | 20 +- packages/core/tests/create_dom.rs | 2 + packages/core/tests/diffing.rs | 1 + packages/core/tests/display_vdom.rs | 7 +- packages/core/tests/eventsystem.rs | 19 -- packages/core/tests/hooks.rs | 26 -- packages/core/tests/lifecycle.rs | 4 +- packages/core/tests/sharedstate.rs | 10 +- packages/core/tests/test_logging.rs | 3 +- packages/core/tests/vdom_rebuild.rs | 2 + packages/desktop/.vscode/settings.json | 3 - packages/desktop/.vscode/spellright.dict | 1 - packages/desktop/Cargo.toml | 1 + packages/desktop/examples/crm.rs | 16 +- packages/desktop/examples/todomvc.css | 379 +++++++++++++++++++++++ packages/desktop/examples/todomvc.rs | 171 ++++++++++ packages/desktop/src/events.rs | 1 - packages/desktop/src/index.js | 92 +++--- packages/desktop/src/lib.rs | 22 +- packages/hooks/src/usestate.rs | 2 +- packages/html/src/lib.rs | 12 +- 23 files changed, 670 insertions(+), 132 deletions(-) delete mode 100644 packages/core/tests/eventsystem.rs delete mode 100644 packages/core/tests/hooks.rs delete mode 100644 packages/desktop/.vscode/settings.json delete mode 100644 packages/desktop/.vscode/spellright.dict create mode 100644 packages/desktop/examples/todomvc.css create mode 100644 packages/desktop/examples/todomvc.rs diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 4d320ea7c..9c19720cf 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -42,13 +42,13 @@ serde_repr = { version = "0.1.7", optional = true } [dev-dependencies] anyhow = "1.0.42" -async-std = { version = "1.9.0", features = ["attributes"] } -criterion = "0.3.5" 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" } +async-std = { version = "1.9.0", features = ["attributes"] } +criterion = "0.3.5" [features] default = [] diff --git a/packages/core/examples/async.rs b/packages/core/examples/async.rs index 9a1ff4a2c..24a4f34f7 100644 --- a/packages/core/examples/async.rs +++ b/packages/core/examples/async.rs @@ -7,9 +7,9 @@ fn main() { const App: FC<()> = |(cx, props)| { let id = cx.scope_id(); - cx.submit_task(Box::pin(async move { id })); + // cx.submit_task(Box::pin(async move { id })); - let (handle, contents) = use_task(cx, || async { "hello world".to_string() }); + // let (handle, contents) = use_task(cx, || async { "hello world".to_string() }); todo!() }; diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs index 60cc0d3f2..b7d2e3f31 100644 --- a/packages/core/tests/borrowedstate.rs +++ b/packages/core/tests/borrowedstate.rs @@ -1,9 +1,16 @@ -use dioxus::{nodes::VSuspended, prelude::*, DomEdit, TestDom}; +#![allow(non_snake_case)] + +use dioxus::prelude::*; use dioxus_core as dioxus; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; -static Parent: FC<()> = |(cx, props)| { +#[test] +fn test_borrowed_state() { + let _ = VirtualDom::new(Parent); +} + +fn Parent((cx, _): Component<()>) -> DomTree { let value = cx.use_hook(|_| String::new(), |f| &*f, |_| {}); cx.render(rsx! { @@ -14,11 +21,11 @@ static Parent: FC<()> = |(cx, props)| { Child { name: value } } }) -}; +} #[derive(Props)] struct ChildProps<'a> { - name: &'a String, + name: &'a str, } fn Child<'a>((cx, props): Component<'a, ChildProps>) -> DomTree<'a> { @@ -32,7 +39,7 @@ fn Child<'a>((cx, props): Component<'a, ChildProps>) -> DomTree<'a> { #[derive(Props)] struct Grandchild<'a> { - name: &'a String, + name: &'a str, } fn Child2<'a>((cx, props): Component<'a, Grandchild>) -> DomTree<'a> { @@ -40,6 +47,3 @@ fn Child2<'a>((cx, props): Component<'a, Grandchild>) -> DomTree<'a> { div { "Hello {props.name}!" } }) } - -#[test] -fn test_borrowed_state() {} diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs index c26c8bed1..d8a79162d 100644 --- a/packages/core/tests/create_dom.rs +++ b/packages/core/tests/create_dom.rs @@ -1,3 +1,5 @@ +#![allow(unused, non_upper_case_globals)] + //! Prove that the dom works normally through virtualdom methods. //! //! This methods all use "rebuild" which completely bypasses the scheduler. diff --git a/packages/core/tests/diffing.rs b/packages/core/tests/diffing.rs index 7ab0731a6..bfb33b4d6 100644 --- a/packages/core/tests/diffing.rs +++ b/packages/core/tests/diffing.rs @@ -1,3 +1,4 @@ +#![allow(unused, non_upper_case_globals)] //! Diffing Tests //! //! These tests only verify that the diffing algorithm works properly for single components. diff --git a/packages/core/tests/display_vdom.rs b/packages/core/tests/display_vdom.rs index 107a2a1c9..ec81ac52c 100644 --- a/packages/core/tests/display_vdom.rs +++ b/packages/core/tests/display_vdom.rs @@ -1,19 +1,16 @@ +#![allow(unused, non_upper_case_globals)] + //! test that we can display the virtualdom properly //! //! //! -use std::{cell::RefCell, rc::Rc}; - -use anyhow::{Context, Result}; use dioxus::prelude::*; use dioxus_core as dioxus; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; mod test_logging; -const IS_LOGGING_ENABLED: bool = true; - #[test] fn please_work() { static App: FC<()> = |(cx, props)| { diff --git a/packages/core/tests/eventsystem.rs b/packages/core/tests/eventsystem.rs deleted file mode 100644 index 7b0380efe..000000000 --- a/packages/core/tests/eventsystem.rs +++ /dev/null @@ -1,19 +0,0 @@ -use bumpalo::Bump; - -use anyhow::{Context, Result}; -use dioxus::{prelude::*, DomEdit}; -use dioxus_core as dioxus; -use dioxus_core_macro::*; -use dioxus_html as dioxus_elements; - -#[async_std::test] -async fn event_queue_works() { - static App: FC<()> = |(cx, props)| { - cx.render(rsx! { - div { "hello world" } - }) - }; - - let mut dom = VirtualDom::new(App); - let edits = dom.rebuild(); -} diff --git a/packages/core/tests/hooks.rs b/packages/core/tests/hooks.rs deleted file mode 100644 index 664355fdf..000000000 --- a/packages/core/tests/hooks.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::{cell::RefCell, rc::Rc}; - -use anyhow::{Context, Result}; -use dioxus::prelude::*; -use dioxus_core as dioxus; -use dioxus_html as dioxus_elements; - -type Shared = Rc>; - -#[test] -fn sample_refs() { - - // static App: FC<()> = |(cx, props)|{ - // let div_ref = use_node_ref::(cx); - - // cx.render(rsx! { - // div { - // style: { color: "red" }, - // node_ref: div_ref, - // onmouseover: move |_| { - // div_ref.borrow_mut().focus(); - // }, - // }, - // }) - // }; -} diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 5e3d69a97..3b9c05cca 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -1,6 +1,6 @@ -//! Tests for the lifecycle of components. +#![allow(unused, non_upper_case_globals)] -use anyhow::{Context, Result}; +//! Tests for the lifecycle of components. use dioxus::prelude::*; use dioxus_core as dioxus; use dioxus_core_macro::*; diff --git a/packages/core/tests/sharedstate.rs b/packages/core/tests/sharedstate.rs index f5b730016..1983ec8c7 100644 --- a/packages/core/tests/sharedstate.rs +++ b/packages/core/tests/sharedstate.rs @@ -1,4 +1,6 @@ -use dioxus::{nodes::VSuspended, prelude::*, DomEdit, TestDom}; +#![allow(unused, non_upper_case_globals)] + +use dioxus::{prelude::*, DomEdit, TestDom}; use dioxus_core as dioxus; use dioxus_core_macro::*; use dioxus_html as dioxus_elements; @@ -7,12 +9,6 @@ use DomEdit::*; mod test_logging; -fn new_dom() -> TestDom { - const IS_LOGGING_ENABLED: bool = false; - test_logging::set_up_logging(IS_LOGGING_ENABLED); - TestDom::new() -} - #[test] fn shared_state_test() { struct MySharedState(&'static str); diff --git a/packages/core/tests/test_logging.rs b/packages/core/tests/test_logging.rs index 8f696be89..54b5982ef 100644 --- a/packages/core/tests/test_logging.rs +++ b/packages/core/tests/test_logging.rs @@ -1,6 +1,5 @@ pub fn set_up_logging(enabled: bool) { use fern::colors::{Color, ColoredLevelConfig}; - if !enabled { return; @@ -19,7 +18,7 @@ pub fn set_up_logging(enabled: bool) { // configure colors for the name of the level. // since almost all of them are the same as the color for the whole line, we // just clone `colors_line` and overwrite our changes - let colors_level = colors_line.clone().info(Color::Green); + let colors_level = colors_line.info(Color::Green); // here we set up our fern Dispatch // when running tests in batch, the logger is re-used, so ignore the logger error diff --git a/packages/core/tests/vdom_rebuild.rs b/packages/core/tests/vdom_rebuild.rs index 12fa26d4f..9ea872a17 100644 --- a/packages/core/tests/vdom_rebuild.rs +++ b/packages/core/tests/vdom_rebuild.rs @@ -1,3 +1,5 @@ +#![allow(unused, non_upper_case_globals)] + //! Rebuilding tests //! ---------------- //! diff --git a/packages/desktop/.vscode/settings.json b/packages/desktop/.vscode/settings.json deleted file mode 100644 index b3bde867c..000000000 --- a/packages/desktop/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rust-analyzer.cargo.allFeatures": true -} diff --git a/packages/desktop/.vscode/spellright.dict b/packages/desktop/.vscode/spellright.dict deleted file mode 100644 index 59b2711ce..000000000 --- a/packages/desktop/.vscode/spellright.dict +++ /dev/null @@ -1 +0,0 @@ -WebviewWindow diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 18b925646..948ffcc0b 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -33,6 +33,7 @@ tokio_runtime = ["tokio"] [dev-dependencies] dioxus-html = { path = "../html" } dioxus-hooks = { path = "../hooks" } +simple_logger = "1.13.0" [build-dependencies] diff --git a/packages/desktop/examples/crm.rs b/packages/desktop/examples/crm.rs index 3971a4ab0..efb7efed2 100644 --- a/packages/desktop/examples/crm.rs +++ b/packages/desktop/examples/crm.rs @@ -22,14 +22,14 @@ pub struct Client { } static App: FC<()> = |(cx, _)| { - let scene = use_state(cx, || Scene::ClientsList); + let mut scene = use_state(cx, || Scene::ClientsList); 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()); + 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 { + let scene = match scene.get() { Scene::ClientsList => { rsx!(cx, div { class: "crm" h2 { "List of clients" margin_bottom: "10px" } @@ -61,13 +61,13 @@ static App: FC<()> = |(cx, _)| { h2 {"Add new client" margin_bottom: "10px" } form { class: "pure-form" input { class: "new-client firstname" placeholder: "First name" value: "{firstname}" - oninput: move |evt| firstname.set(evt.value.clone()) + oninput: move |evt| firstname.set(evt.value) } input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}" - oninput: move |evt| lastname.set(evt.value.clone()) + oninput: move |evt| lastname.set(evt.value) } textarea { class: "new-client description" placeholder: "Description" value: "{description}" - oninput: move |evt| description.set(evt.value.clone()) + oninput: move |evt| description.set(evt.value) } } button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" } diff --git a/packages/desktop/examples/todomvc.css b/packages/desktop/examples/todomvc.css new file mode 100644 index 000000000..ab9dbd7a9 --- /dev/null +++ b/packages/desktop/examples/todomvc.css @@ -0,0 +1,379 @@ +html, +body { + margin: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; + border: 0; + background: none; + font-size: 100%; + vertical-align: baseline; + font-family: inherit; + font-weight: inherit; + color: inherit; + -webkit-appearance: none; + appearance: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; + line-height: 1.4em; + background: #f5f5f5; + color: #4d4d4d; + min-width: 230px; + max-width: 550px; + margin: 0 auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-weight: 300; +} + +:focus { + outline: 0; +} + +.hidden { + display: none; +} + +.todoapp { + background: #fff; + margin: 130px 0 40px 0; + position: relative; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), + 0 25px 50px 0 rgba(0, 0, 0, 0.1); +} + +.todoapp input::-webkit-input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::-moz-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp input::input-placeholder { + font-style: italic; + font-weight: 300; + color: #e6e6e6; +} + +.todoapp h1 { + position: absolute; + top: -155px; + width: 100%; + font-size: 100px; + font-weight: 100; + text-align: center; + color: rgba(175, 47, 47, 0.15); + -webkit-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + text-rendering: optimizeLegibility; +} + +.new-todo, +.edit { + position: relative; + margin: 0; + width: 100%; + font-size: 24px; + font-family: inherit; + font-weight: inherit; + line-height: 1.4em; + border: 0; + color: inherit; + padding: 6px; + border: 1px solid #999; + box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); + box-sizing: border-box; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.new-todo { + padding: 16px 16px 16px 60px; + border: none; + background: rgba(0, 0, 0, 0.003); + box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03); +} + +.main { + position: relative; + z-index: 2; + border-top: 1px solid #e6e6e6; +} + +.toggle-all { + text-align: center; + border: none; + /* Mobile Safari */ + opacity: 0; + position: absolute; +} + +.toggle-all+label { + width: 60px; + height: 34px; + font-size: 0; + position: absolute; + top: -52px; + left: -13px; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.toggle-all+label:before { + content: '❯'; + font-size: 22px; + color: #e6e6e6; + padding: 10px 27px 10px 27px; +} + +.toggle-all:checked+label:before { + color: #737373; +} + +.todo-list { + margin: 0; + padding: 0; + list-style: none; +} + +.todo-list li { + position: relative; + font-size: 24px; + border-bottom: 1px solid #ededed; +} + +.todo-list li:last-child { + border-bottom: none; +} + +.todo-list li.editing { + border-bottom: none; + padding: 0; +} + +.todo-list li.editing .edit { + display: block; + width: 506px; + padding: 12px 16px; + margin: 0 0 0 43px; +} + +.todo-list li.editing .view { + display: none; +} + +.todo-list li .toggle { + text-align: center; + width: 40px; + /* auto, since non-WebKit browsers doesn't support input styling */ + height: auto; + position: absolute; + top: 0; + bottom: 0; + margin: auto 0; + border: none; + /* Mobile Safari */ + -webkit-appearance: none; + appearance: none; +} + +.todo-list li .toggle { + opacity: 0; +} + +.todo-list li .toggle+label { + /* + Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 + IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ + */ + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); + background-repeat: no-repeat; + background-position: center left; +} + +.todo-list li .toggle:checked+label { + background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); +} + +.todo-list li label { + word-break: break-all; + padding: 15px 15px 15px 60px; + display: block; + line-height: 1.2; + transition: color 0.4s; +} + +.todo-list li.completed label { + color: #d9d9d9; + text-decoration: line-through; +} + +.todo-list li .destroy { + display: none; + position: absolute; + top: 0; + right: 10px; + bottom: 0; + width: 40px; + height: 40px; + margin: auto 0; + font-size: 30px; + color: #cc9a9a; + margin-bottom: 11px; + transition: color 0.2s ease-out; +} + +.todo-list li .destroy:hover { + color: #af5b5e; +} + +.todo-list li .destroy:after { + content: '×'; +} + +.todo-list li:hover .destroy { + display: block; +} + +.todo-list li .edit { + display: none; +} + +.todo-list li.editing:last-child { + margin-bottom: -1px; +} + +.footer { + color: #777; + padding: 10px 15px; + height: 20px; + text-align: center; + border-top: 1px solid #e6e6e6; +} + +.footer:before { + content: ''; + position: absolute; + right: 0; + bottom: 0; + left: 0; + height: 50px; + overflow: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), + 0 8px 0 -3px #f6f6f6, + 0 9px 1px -3px rgba(0, 0, 0, 0.2), + 0 16px 0 -6px #f6f6f6, + 0 17px 2px -6px rgba(0, 0, 0, 0.2); +} + +.todo-count { + float: left; + text-align: left; +} + +.todo-count strong { + font-weight: 300; +} + +.filters { + margin: 0; + padding: 0; + list-style: none; + position: absolute; + right: 0; + left: 0; +} + +.filters li { + display: inline; +} + +.filters li a { + color: inherit; + margin: 3px; + padding: 3px 7px; + text-decoration: none; + border: 1px solid transparent; + border-radius: 3px; +} + +.filters li a:hover { + border-color: rgba(175, 47, 47, 0.1); +} + +.filters li a.selected { + border-color: rgba(175, 47, 47, 0.2); +} + +.clear-completed, +html .clear-completed:active { + float: right; + position: relative; + line-height: 20px; + text-decoration: none; + cursor: pointer; +} + +.clear-completed:hover { + text-decoration: underline; +} + +.info { + margin: 65px auto 0; + color: #bfbfbf; + font-size: 10px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-align: center; +} + +.info p { + line-height: 1; +} + +.info a { + color: inherit; + text-decoration: none; + font-weight: 400; +} + +.info a:hover { + text-decoration: underline; +} + +/* + Hack to remove background from Mobile Safari. + Can't use it globally since it destroys checkboxes in Firefox +*/ +@media screen and (-webkit-min-device-pixel-ratio:0) { + + .toggle-all, + .todo-list li .toggle { + background: none; + } + + .todo-list li .toggle { + height: 40px; + } +} + +@media (max-width: 430px) { + .footer { + height: 50px; + } + + .filters { + bottom: 10px; + } +} diff --git a/packages/desktop/examples/todomvc.rs b/packages/desktop/examples/todomvc.rs new file mode 100644 index 000000000..fa2eae08e --- /dev/null +++ b/packages/desktop/examples/todomvc.rs @@ -0,0 +1,171 @@ +#![allow(non_upper_case_globals, non_snake_case)] + +use dioxus_core as dioxus; +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, |c| c) +} + +#[derive(PartialEq)] +pub enum FilterState { + All, + Active, + Completed, +} + +#[derive(Debug, PartialEq)] +pub struct TodoItem { + pub id: u32, + pub checked: bool, + pub contents: String, +} +pub type Todos = HashMap; + +pub static App: FC<()> = |(cx, _)| { + // Share our TodoList to the todos themselves + use_provide_state(cx, || Todos::new()); + + // Save state for the draft, filter + let mut draft = use_state(cx, || "".to_string()); + let mut filter = use_state(cx, || FilterState::All); + let mut todo_id = use_state(cx, || 0); + + // Consume the todos + let todos = use_shared_state::(cx)?; + + // Filter the todos based on the filter state + let mut filtered_todos = todos + .read() + .iter() + .filter(|(_, item)| match *filter { + FilterState::All => true, + FilterState::Active => !item.checked, + FilterState::Completed => item.checked, + }) + .map(|f| *f.0) + .collect::>(); + filtered_todos.sort_unstable(); + + // Define the actions to manage the todolist + let mut submit_todo = move || { + if !draft.is_empty() { + todos.write().insert( + *todo_id, + TodoItem { + id: *todo_id, + checked: false, + contents: draft.get().clone(), + }, + ); + todo_id += 1; + draft.set("".to_string()); + } + }; + let clear_completed = move || { + todos.write().retain(|_, todo| todo.checked == false); + }; + + // Some assists in actually rendering the content + let show_clear_completed = todos.read().values().any(|todo| todo.checked); + let items_left = filtered_todos.len(); + let item_text = match items_left { + 1 => "item", + _ => "items", + }; + + cx.render(rsx!{ + section { class: "todoapp" + style { {[include_str!("./todomvc.css")]} } + div { + header { class: "header" + h1 {"todos"} + input { + class: "new-todo" + placeholder: "What needs to be done?" + value: "{draft}" + autofocus: "true" + oninput: move |evt| draft.set(evt.value.clone()) + onkeydown: move |evt| { + if evt.key == "Enter" { + submit_todo(); + } + } + } + } + ul { class: "todo-list" + {filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))} + } + {(!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!( + button { class: "clear-completed", onclick: move |_| clear_completed(), + "Clear completed" + } + ))} + } + ))} + } + } + 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" }} + } + }) +}; + +#[derive(PartialEq, Props)] +pub struct TodoEntryProps { + id: u32, +} + +pub fn TodoEntry((cx, props): Component) -> DomTree { + let todos = use_shared_state::(cx)?; + + let _todos = todos.read(); + let todo = _todos.get(&props.id)?; + + let is_editing = use_state(cx, || false); + let completed = if todo.checked { "completed" } else { "" }; + + cx.render(rsx!{ + li { class: "{completed}" + div { class: "view" + input { class: "toggle" r#type: "checkbox" id: "cbg-{todo.id}" checked: "{todo.checked}" + onchange: move |evt| { + todos.write().get_mut(&props.id).map(|todo| todo.checked = evt.value.parse().unwrap()); + } + } + + label { r#for: "cbg-{todo.id}" pointer_events: "none" + "{todo.contents}" + } + + {is_editing.then(|| rsx!{ + input { value: "{todo.contents}" + oninput: move |evt| { + todos.write().get_mut(&props.id).map(|todo| todo.contents = evt.value.clone()); + }, + } + })} + } + } + }) +} diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index f1e6d165e..82a698f05 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -49,7 +49,6 @@ fn make_synthetic_event(name: &str, val: serde_json::Value) -> Box(val).unwrap()) } "keydown" | "keypress" | "keyup" => { - dbg!(&val); let evt = serde_json::from_value::(val).unwrap(); Box::new(evt) } diff --git a/packages/desktop/src/index.js b/packages/desktop/src/index.js index 222509629..2caad4dc2 100644 --- a/packages/desktop/src/index.js +++ b/packages/desktop/src/index.js @@ -39,12 +39,12 @@ class Interpreter { } ReplaceWith(edit) { - console.log(edit); + // console.log(edit); let root = this.nodes[edit.root]; let els = this.stack.splice(this.stack.length - edit.m); - console.log(root); - console.log(els); + // console.log(root); + // console.log(els); root.replaceWith(...els); @@ -108,7 +108,7 @@ class Interpreter { this.listeners[event_name] = "bla"; this.root.addEventListener(event_name, (event) => { - console.log("CLICKED"); + // console.log("CLICKED"); const target = event.target; const val = target.getAttribute(`dioxus-event-${event_name}`); if (val == null) { @@ -119,30 +119,31 @@ class Interpreter { 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(`parsed event with scope_id ${scope_id} and real_id ${real_id}`); - console.log("message fired"); + // console.log("message fired"); let contents = serialize_event(event); - rpc.call('user_event', { + let evt = { event: event_name, scope: scope_id, mounted_dom_id: real_id, contents: contents, - }).then((reply) => { - console.log("reply received"); + }; + // console.log(evt); + rpc.call('user_event', evt).then((reply) => { + // console.log("reply received", reply); - // console.log(reply); this.stack.push(this.root); - let edits = reply.edits; + reply.map((reply) => { + let edits = reply.edits; + for (let x = 0; x < edits.length; x++) { + let edit = edits[x]; + let f = this[edit.type]; + f.call(this, edit); + } + }); - for (let x = 0; x < edits.length; x++) { - let edit = edits[x]; - let f = this[edit.type]; - f.call(this, edit); - } - - // console.log("initiated"); }) }); } @@ -153,25 +154,35 @@ class Interpreter { } SetAttribute(edit) { + // console.log("setting attr", edit); const name = edit.field; const value = edit.value; const ns = edit.ns; const node = this.nodes[edit.root] + if (ns == "style") { node.style[name] = value; - } else if (ns !== undefined) { + } else if ((ns != null) || (ns != undefined)) { node.setAttributeNS(ns, name, value); } else { - node.setAttribute(name, value); - } - if (name === "value") { - node.value = value; - } - if (name === "checked") { - node.checked = true; - } - if (name === "selected") { - node.selected = true; + switch (name) { + case "value": + node.value = value; + break; + case "checked": + // console.log("setting checked"); + node.checked = (value === "true"); + break; + case "selected": + node.selected = (value === "true"); + break; + case "dangerous_inner_html": + node.innerHTML = value; + break; + default: + // console.log("setting attr directly ", name, value); + node.setAttribute(name, value); + } } } RemoveAttribute(edit) { @@ -209,14 +220,14 @@ async function initialize() { } function apply_edits(edits, interpreter) { - console.log(edits); + // console.log(edits); for (let x = 0; x < edits.length; x++) { let edit = edits[x]; let f = interpreter[edit.type]; f.call(interpreter, edit); } - // console.log("stack completed: ", interpreter.stack); + // // console.log("stack completed: ", interpreter.stack); } function serialize_event(event) { @@ -244,8 +255,8 @@ const SerializeMap = { "focus": serialize_focus, "blur": serialize_focus, - "change": serialize_form, - // "change": serialize_change, + // "change": serialize_form, + "change": serialize_change, "input": serialize_form, "invalid": serialize_form, @@ -353,8 +364,19 @@ function serialize_keyboard(event) { function serialize_focus(_event) { return {} } -function serialize_change(_event) { - return {} + +function serialize_change(event) { + let target = event.target; + let value; + if (target.type === "checkbox" || target.type === "radio") { + value = target.checked ? "true" : "false"; + } else { + value = target.value ?? target.textContent; + } + + return { + value: value + } } function serialize_form(event) { let target = event.target; diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 49fedf515..ce119e9bc 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -95,13 +95,13 @@ pub fn run( match event { Event::NewEvents(StartCause::Init) => { - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let window = WindowBuilder::new().build(event_loop).unwrap(); let window_id = window.id(); let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); let my_props = props_shared.take().unwrap(); - let sender = launch_vdom_with_tokio(root, my_props, event_tx.clone()); + let sender = launch_vdom_with_tokio(root, my_props, event_tx); let locked_receiver = Rc::new(RefCell::new(event_rx)); @@ -121,10 +121,15 @@ pub fn run( } "user_event" => { let event = events::trigger_from_serialized(req.params.unwrap()); + log::debug!("User event: {:?}", event); + sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap(); + if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() { + log::info!("bridge received message {:?}", edits); Some(RpcResponse::new_result(req.id.take(), Some(edits))) } else { + log::info!("none received message"); None } } @@ -224,13 +229,20 @@ pub(crate) fn launch_vdom_with_tokio( let mut muts = vir.run_with_deadline(|| false); + log::debug!("finished running with deadline"); + + let mut edits = vec![]; + while let Some(edit) = muts.pop() { + log::debug!("sending message on channel with edit {:?}", edit); let edit_string = serde_json::to_value(Evt { edits: edit.edits }) .expect("serializing edits should never fail"); - event_tx - .send(BridgeEvent::Update(edit_string)) - .expect("Sending should not fail"); + edits.push(edit_string); } + + event_tx + .send(BridgeEvent::Update(serde_json::Value::Array(edits))) + .expect("Sending should not fail"); } }) }); diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 76a72ea75..79402f22d 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -102,7 +102,7 @@ impl<'a, T: 'static> UseState<'a, T> { } } - pub fn set(&self, new_val: T) { + pub fn set(&mut self, new_val: T) { *self.inner.wip.borrow_mut() = Some(new_val); self.needs_update(); } diff --git a/packages/html/src/lib.rs b/packages/html/src/lib.rs index 9fe166997..0eac59265 100644 --- a/packages/html/src/lib.rs +++ b/packages/html/src/lib.rs @@ -1076,7 +1076,9 @@ builder_constructors! { /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code) /// element. - code {}; + code { + language: String, + }; /// Build a /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data) @@ -1973,8 +1975,8 @@ pub trait SvgAttributes { vert_adv_y: "vert-adv-y", vert_origin_x: "vert-origin-x", vert_origin_y: "vert-origin-y", - viewBox: "viewBox", - viewTarget: "viewTarget", + view_box: "viewBox", + view_target: "viewTarget", visibility: "visibility", width: "width", widths: "widths", @@ -1985,11 +1987,11 @@ pub trait SvgAttributes { x1: "x1", x2: "x2", xmlns: "xmlns", - xChannelSelector: "xChannelSelector", + x_channel_selector: "xChannelSelector", y: "y", y1: "y1", y2: "y2", - yChannelSelector: "yChannelSelector", + y_channel_selector: "yChannelSelector", z: "z", zoomAndPan: "zoomAndPan", } From fda2ebc2a22965845e015384f39f34ce7cb3e428 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 25 Oct 2021 15:05:17 -0400 Subject: [PATCH 2/9] feat: improve safety --- docs/src/README.md | 10 ++--- docs/src/SUMMARY.md | 4 +- packages/core/examples/jsframework.rs | 11 +++-- packages/core/src/diff.rs | 58 +++++++++++++++++--------- packages/core/src/events.rs | 2 + packages/core/src/hooklist.rs | 1 + packages/core/src/nodes.rs | 4 ++ packages/core/src/scheduler.rs | 2 - packages/core/src/scope.rs | 6 +++ packages/desktop/src/index.html | 11 ++++- packages/desktop/src/lib.rs | 38 ++++++++++++++++- packages/hooks/src/use_shared_state.rs | 2 +- 12 files changed, 113 insertions(+), 36 deletions(-) diff --git a/docs/src/README.md b/docs/src/README.md index 5c02265aa..da1f68a97 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -4,9 +4,9 @@ **Dioxus** is a framework and ecosystem for building fast, scalable, and robust user interfaces with the Rust programming language. This guide will help you get up-and-running with Dioxus running on the Web, Desktop, Mobile, and more. -```rust, ignore +```rust // An example Dioxus app - closely resembles React -static App: FC<()> = |(cx, props)| { +fn App(cx: Component<()>) -> Element { let mut count = use_state(cx, || 0); cx.render(rsx!( @@ -94,8 +94,8 @@ The internal architecture of Dioxus was designed from day one to support the `Li ### Multithreaded Support --- -The Dioxus VirtualDom, sadly, is not `Send.` This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc. Currently, your two options include: -- Actix: Actix supports `!Send` handlers -- VirtualDomPool: A thread-per-core VirtualDom pool that uses message passing and serialization +The Dioxus VirtualDom, sadly, is not currently `Send.` Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc. + +To solve this, you'll want to spawn a VirtualDom on its own thread and communicate with it via channels. When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String - but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a VirtualDomPool which solves the `Send` problem. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 2718e5b2d..6c00723fe 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -4,8 +4,8 @@ - [Getting Setup](setup.md) - [Hello, World!](hello_world.md) - [Core Topics](concepts/00-index.md) - - [Intro to VNodes](concepts/vnodes.md) - - [VNodes with RSX, HTML, and NodeFactory](concepts/rsx.md) + - [Intro to Elements](concepts/vnodes.md) + - [Elements with RSX and NodeFactory](concepts/rsx.md) - [RSX in Depth](concepts/rsx_in_depth.md) - [Components and Props](concepts/components.md) - [Memoization](concepts/memoization.md) diff --git a/packages/core/examples/jsframework.rs b/packages/core/examples/jsframework.rs index c05bd06f6..3a9d73681 100644 --- a/packages/core/examples/jsframework.rs +++ b/packages/core/examples/jsframework.rs @@ -1,3 +1,5 @@ +#![allow(non_snake_case)] + use dioxus::component::Component; use dioxus::events::on::MouseEvent; use dioxus_core as dioxus; @@ -13,7 +15,7 @@ fn main() { assert!(g.edits.len() > 1); } -static App: FC<()> = |(cx, props)| { +fn App((cx, props): Component<()>) -> DomTree { let mut rng = SmallRng::from_entropy(); let rows = (0..10_000_usize).map(|f| { let label = Label::new(&mut rng); @@ -31,13 +33,14 @@ static App: FC<()> = |(cx, props)| { } } }) -}; +} #[derive(PartialEq, Props)] struct RowProps { row_id: usize, label: Label, } + fn Row((cx, props): Component) -> DomTree { let handler = move |evt: MouseEvent| { let g = evt.button; @@ -45,11 +48,11 @@ fn Row((cx, props): Component) -> DomTree { cx.render(rsx! { tr { td { class:"col-md-1", "{props.row_id}" } - td { class:"col-md-1", onclick: move |_| { /* run onselect */ } + td { class:"col-md-1", on_click: move |_| { /* run onselect */ } a { class: "lbl", "{props.label}" } } td { class: "col-md-1" - a { class: "remove", onclick: {handler} + a { class: "remove", on_click: {handler} span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } } } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index d73b0b128..25e2d71e7 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -221,12 +221,12 @@ impl<'bump> DiffMachine<'bump> { MountType::Replace { old } => { if let Some(old_id) = old.try_mounted_id() { self.mutations.replace_with(old_id, nodes_created as u32); - self.remove_nodes(Some(old)); + self.remove_nodes(Some(old), true); } else { if let Some(id) = self.find_first_element_id(old) { self.mutations.replace_with(id, nodes_created as u32); } - self.remove_nodes(Some(old)); + self.remove_nodes(Some(old), true); } } @@ -369,7 +369,11 @@ impl<'bump> DiffMachine<'bump> { let new_component = self.vdom.get_scope_mut(new_idx).unwrap(); - log::debug!("initializing component {:?}", new_idx); + log::debug!( + "initializing component {:?} with height {:?}", + new_idx, + parent_scope.height + 1 + ); // Run the scope for one iteration to initialize it if new_component.run_scope(self.vdom) { @@ -614,7 +618,7 @@ impl<'bump> DiffMachine<'bump> { self.stack.create_children(new, MountType::Append); } (_, []) => { - self.remove_nodes(old); + self.remove_nodes(old, true); } ([VNode::Anchor(old_anchor)], [VNode::Anchor(new_anchor)]) => { old_anchor.dom_id.set(new_anchor.dom_id.get()); @@ -667,7 +671,7 @@ impl<'bump> DiffMachine<'bump> { use std::cmp::Ordering; match old.len().cmp(&new.len()) { - Ordering::Greater => self.remove_nodes(&old[new.len()..]), + Ordering::Greater => self.remove_nodes(&old[new.len()..], true), Ordering::Less => { self.stack.create_children( &new[old.len()..], @@ -750,7 +754,7 @@ impl<'bump> DiffMachine<'bump> { ); if new_middle.is_empty() { // remove the old elements - self.remove_nodes(old_middle); + self.remove_nodes(old_middle, true); } else if old_middle.is_empty() { // there were no old elements, so just create the new elements // we need to find the right "foothold" though - we shouldnt use the "append" at all @@ -825,7 +829,7 @@ impl<'bump> DiffMachine<'bump> { // And if that was all of the new children, then remove all of the remaining // old children and we're finished. if left_offset == new.len() { - self.remove_nodes(&old[left_offset..]); + self.remove_nodes(&old[left_offset..], true); return None; } @@ -1070,7 +1074,7 @@ impl<'bump> DiffMachine<'bump> { new: &'bump VNode<'bump>, ) { if let Some(first_old) = old.get(0) { - self.remove_nodes(&old[1..]); + self.remove_nodes(&old[1..], true); self.stack .create_node(new, MountType::Replace { old: first_old }); } else { @@ -1080,42 +1084,58 @@ impl<'bump> DiffMachine<'bump> { /// schedules nodes for garbage collection and pushes "remove" to the mutation stack /// remove can happen whenever - fn remove_nodes(&mut self, nodes: impl IntoIterator>) { + fn remove_nodes( + &mut self, + nodes: impl IntoIterator>, + gen_muts: bool, + ) { // or cache the vec on the diff machine for node in nodes { match node { VNode::Text(t) => { - if let Some(id) = t.dom_id.get() { + let id = t.dom_id.get().unwrap(); + self.vdom.collect_garbage(id); + + if gen_muts { self.mutations.remove(id.as_u64()); - self.vdom.collect_garbage(id); } } VNode::Suspended(s) => { - if let Some(id) = s.dom_id.get() { + let id = s.dom_id.get().unwrap(); + self.vdom.collect_garbage(id); + + if gen_muts { self.mutations.remove(id.as_u64()); - self.vdom.collect_garbage(id); } } VNode::Anchor(a) => { - if let Some(id) = a.dom_id.get() { + let id = a.dom_id.get().unwrap(); + self.vdom.collect_garbage(id); + + if gen_muts { self.mutations.remove(id.as_u64()); - self.vdom.collect_garbage(id); } } VNode::Element(e) => { - if let Some(id) = e.dom_id.get() { + let id = e.dom_id.get().unwrap(); + + if gen_muts { self.mutations.remove(id.as_u64()); } + + self.remove_nodes(e.children, false); } VNode::Fragment(f) => { - self.remove_nodes(f.children); + self.remove_nodes(f.children, gen_muts); } VNode::Component(c) => { let scope_id = c.associated_scope.get().unwrap(); let scope = self.vdom.get_scope_mut(scope_id).unwrap(); let root = scope.root_node(); - self.remove_nodes(Some(root)); + self.remove_nodes(Some(root), gen_muts); + + log::debug!("Destroying scope {:?}", scope_id); let mut s = self.vdom.try_remove(scope_id).unwrap(); s.hooks.clear_hooks(); } @@ -1132,7 +1152,7 @@ impl<'bump> DiffMachine<'bump> { new: &'bump [VNode<'bump>], ) { if let Some(first_old) = old.get(0) { - self.remove_nodes(&old[1..]); + self.remove_nodes(&old[1..], true); self.stack .create_children(new, MountType::Replace { old: first_old }) } else { diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 728b71e78..dd87e343b 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -137,6 +137,7 @@ pub mod on { } // The Dioxus Synthetic event system + // todo: move these into the html event system. dioxus accepts *any* event, so having these here doesn't make sense. event_directory! { ClipboardEvent: [ /// Called when "copy" @@ -1031,6 +1032,7 @@ pub enum KeyCode { // kanji, = 244 // unlock trackpad (Chrome/Edge), = 251 // toggle touchpad, = 255 + #[cfg_attr(feature = "serialize", serde(other))] Unknown, } diff --git a/packages/core/src/hooklist.rs b/packages/core/src/hooklist.rs index 45b07d9cc..5e80bf11b 100644 --- a/packages/core/src/hooklist.rs +++ b/packages/core/src/hooklist.rs @@ -57,6 +57,7 @@ impl HookList { } pub fn clear_hooks(&mut self) { + log::debug!("clearing hooks..."); self.vals .borrow_mut() .drain(..) diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index 917b7aca3..1a21f21d8 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -487,6 +487,8 @@ impl<'a> NodeFactory<'a> { props.memoize(real_other) }; + log::debug!("comparing props..."); + // It's only okay to memoize if there are no children and the props can be memoized // Implementing memoize is unsafe and done automatically with the props trait matches!((props_memoized, children.is_empty()), (true, true)) @@ -502,6 +504,7 @@ impl<'a> NodeFactory<'a> { let drop_props: &mut dyn FnMut() = bump.alloc_with(|| { move || unsafe { + log::debug!("dropping props!"); if !has_dropped { let real_other = raw_props as *mut _ as *mut P; let b = BumpBox::from_raw(real_other); @@ -525,6 +528,7 @@ impl<'a> NodeFactory<'a> { let caller: &'a mut dyn for<'b> Fn(&'b Scope) -> DomTree<'b> = bump.alloc(move |scope: &Scope| -> DomTree { + log::debug!("calling component renderr {:?}", scope.our_arena_idx); let props: &'_ P = unsafe { &*(raw_props as *const P) }; let res = component((Context { scope }, props)); unsafe { std::mem::transmute(res) } diff --git a/packages/core/src/scheduler.rs b/packages/core/src/scheduler.rs index 20005a33e..fab67e211 100644 --- a/packages/core/src/scheduler.rs +++ b/packages/core/src/scheduler.rs @@ -407,8 +407,6 @@ impl Scheduler { let work_completed = machine.work(deadline_reached); - log::debug!("Working finished? {:?}", work_completed); - // log::debug!("raw edits {:?}", machine.mutations.edits); let mut machine: DiffMachine<'static> = unsafe { std::mem::transmute(machine) }; diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 850bab8a2..df23feea1 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -43,6 +43,8 @@ pub struct Scope { // State pub(crate) hooks: HookList, + + // todo: move this into a centralized place - is more memory efficient pub(crate) shared_contexts: RefCell>>, // whenever set_state is called, we fire off a message to the scheduler @@ -345,11 +347,15 @@ impl Scope { debug_assert!(self.suspended_nodes.borrow().is_empty()); debug_assert!(self.borrowed_props.borrow().is_empty()); + log::debug!("Borrowed stuff is successfully cleared"); + // Cast the caller ptr from static to one with our own reference let render: &dyn for<'b> Fn(&'b Scope) -> DomTree<'b> = unsafe { &*self.caller }; // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. if let Some(new_head) = render(self) { + log::debug!("Render is successful"); + // the user's component succeeded. We can safely cycle to the next frame self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) }; self.frames.cycle_frame(); diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index 868e7fa4a..ebddd60d5 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -4,8 +4,15 @@ - + + + diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index ce119e9bc..f64ac82b0 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -19,11 +19,15 @@ use serde::{Deserialize, Serialize}; pub use wry; +use wry::application::accelerator::{Accelerator, SysMods}; use wry::application::event::{Event, StartCause, WindowEvent}; use wry::application::event_loop::{self, ControlFlow, EventLoop}; +use wry::application::keyboard::KeyCode; +use wry::application::menu::{MenuBar, MenuItem, MenuItemAttributes}; use wry::application::window::Fullscreen; use wry::webview::{WebView, WebViewBuilder}; use wry::{ + application::menu, application::window::{Window, WindowBuilder}, webview::{RpcRequest, RpcResponse}, }; @@ -95,7 +99,39 @@ pub fn run( match event { Event::NewEvents(StartCause::Init) => { - let window = WindowBuilder::new().build(event_loop).unwrap(); + // create main menubar menu + let mut menu_bar_menu = MenuBar::new(); + + // create `first_menu` + let mut first_menu = MenuBar::new(); + + first_menu.add_native_item(MenuItem::About("Todos".to_string())); + first_menu.add_native_item(MenuItem::Services); + first_menu.add_native_item(MenuItem::Separator); + first_menu.add_native_item(MenuItem::Hide); + first_menu.add_native_item(MenuItem::HideOthers); + first_menu.add_native_item(MenuItem::ShowAll); + + let quit_item = first_menu.add_item( + MenuItemAttributes::new("Quit") + .with_accelerators(&Accelerator::new(SysMods::Cmd, KeyCode::KeyQ)), + ); + + // create second menu + let mut second_menu = MenuBar::new(); + + // second_menu.add_submenu("Sub menu", true, my_sub_menu); + second_menu.add_native_item(MenuItem::Copy); + second_menu.add_native_item(MenuItem::Paste); + second_menu.add_native_item(MenuItem::SelectAll); + + menu_bar_menu.add_submenu("First menu", true, first_menu); + menu_bar_menu.add_submenu("Second menu", true, second_menu); + + let window = WindowBuilder::new() + .with_menu(menu_bar_menu) + .build(event_loop) + .unwrap(); let window_id = window.id(); let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel(); diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs index b1febb498..593cf3922 100644 --- a/packages/hooks/src/use_shared_state.rs +++ b/packages/hooks/src/use_shared_state.rs @@ -126,7 +126,7 @@ impl<'a, T: 'static> UseSharedState<'a, T> { /// /// /// TODO: We prevent unncessary notifications only in the hook, but we should figure out some more global lock - pub fn write(&self) -> RefMut<'_, T> { + pub fn write(&mut self) -> RefMut<'_, T> { self.cx.needs_update(); self.notify_consumers(); self.value.borrow_mut() From 9839f5235554a3bbd5c7eca12d9a9e160e609979 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 27 Oct 2021 14:33:16 -0400 Subject: [PATCH 3/9] docs: clean up setup --- docs/blog/01-release.md | 15 --------------- docs/src/setup.md | 16 ++++++++++------ 2 files changed, 10 insertions(+), 21 deletions(-) delete mode 100644 docs/blog/01-release.md diff --git a/docs/blog/01-release.md b/docs/blog/01-release.md deleted file mode 100644 index 545e3ee12..000000000 --- a/docs/blog/01-release.md +++ /dev/null @@ -1,15 +0,0 @@ - -- Rust's smart pointers make use_state extremely easy and fun to work with. -- Multiple flavors (raw, html, rsx) of templating to pick-and-choose for the type of component. -- Extensible DSL through traits and impls plus support for WebComponents. -- Simple-to-implement trait for new reconcilers. -- Fast retained-mode server-side-renderer that works with buffered writers, files, and strings. -- Compile-time correct inline CSS as well as global styling. -- Integrated "signal" system allows for near-instant updates by skipping the diff algorithm entirely. -- Built-in cooperative scheduler (IE React's fiber mechanism). -- Built-in asynchronous scheduler for coroutines and tasks within components. -- Built-in suspense scheduler for Rust's futures. -- Extremely fast diffing algorithm for the most complex of apps. -- Runs natively on mobile and on desktop with no need for a 3rd party JS engine. -- Drastically fewer runtime errors and crashes with first-class error handling. -- Extremely powerful iterator and optional chaining integration for fast and robust apps. diff --git a/docs/src/setup.md b/docs/src/setup.md index 861d5a73f..233415685 100644 --- a/docs/src/setup.md +++ b/docs/src/setup.md @@ -2,7 +2,7 @@ Dioxus aims to provide a fast, friendly, and portable toolkit for building user interfaces with Rust. -This guide assumes you'll be building a SPA (single page application) for the web. The process for building desktop apps, server-rendered apps, static sites, and mobile apps, is more-or-less the same. You can check out the [Platform Specific Guides](../platforms/00-index.md) for more information on setting up Dioxus for any of the various targets you are building for. +This Getting Setup guide assumes you'll be building a small desktop application. The process for building web-apps, desktop apps, server-rendered apps, static sites, and mobile apps, is more-or-less the same. You can check out the [Platform Specific Guides](../platforms/00-index.md) for more information on setting up Dioxus for any of the various targets you are building for. # Setting up Dioxus @@ -10,10 +10,12 @@ Dioxus requires a few main things to get up and running: - The [Rust compiler](https://www.rust-lang.org) and associated build tooling -- An editor of your choice, with the [Rust-Analyzer LSP plugin](https://rust-analyzer.github.io) +- An editor of your choice, ideally configured with the [Rust-Analyzer LSP plugin](https://rust-analyzer.github.io) Dioxus integrates very well with the Rust-Analyzer IDE plugin which will provide appropriate syntax highlighting, code navigation, folding, and more. +### Dioxus-CLI for dev server, bundling, etc. + We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. It'll be our one-stop-shop for anything related to building and sharing our Dioxus Apps. To install the CLI, you'll need cargo (should be automatically installed with Rust): ``` @@ -28,15 +30,17 @@ $ cargo install --force dioxus-cli If your version of the CLI is out of date, it'll remind you to update whenever a new version is uploaded to Rust's package manager [crates.io](https://crates.io). We use a dedicated 1st-party CLI to save you from having to run potentially untrusted code every time you add a crate to your project - as is standard in the NPM ecosystem. You can vet the source of the Dioxus-CLI yourself at its [GitHub repo](https://github.com/jkelleyrtp/dioxus/tree/master/packages/cli). +### Suggested extensions + If you want to keep your traditional `npm install XXX` workflow for adding packages, you might want to install `cargo-edit` and a few other fun `cargo` extensions: - [cargo edit](https://github.com/killercup/cargo-edit) for adding dependencies from the CLI - [cargo-expand](https://github.com/dtolnay/cargo-expand) for expanding macro calls -- `cargo tree` - an integrated cargo command that lets you inspect your dependency tree +- [cargo tree](https://doc.rust-lang.org/cargo/commands/cargo-tree.html) - an integrated cargo command that lets you inspect your dependency tree -That's it! We won't need to touch NPM/WebPack/Babel/Parcel, etc. +That's it! We won't need to touch NPM/WebPack/Babel/Parcel, etc. However, you _can_ configure your app to use WebPack with [traditional WASM-pack tooling](https://rustwasm.github.io/wasm-pack/book/tutorials/hybrid-applications-with-webpack/using-your-library.html). -# Important tools +## 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: @@ -45,7 +49,7 @@ With Rust, things like benchmarking, testing, and documentation are included in - Closures - Macros -We've put a lot of care into making Dioxus syntax familiar and easy to understand, so you won't need knowledge on async, lifetimes, and smart pointers until you really start building complex Dioxus apps. +We've put a lot of care into making Dioxus syntax familiar and easy to understand, so you won't need deep knowledge on async, lifetimes, or smart pointers until you really start building complex Dioxus apps. We strongly encourage exploring the guides for more information on how to work with the integrated tooling: From a42711a324215b87f607093a57b204be4154f30e Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 27 Oct 2021 14:54:49 -0400 Subject: [PATCH 4/9] wip: docs --- docs/src/hello_world.md | 62 +++++++++++++++------------------ docs/src/images/helloworld.png | Bin 0 -> 31650 bytes examples/hello_world.rs | 11 ++++++ packages/desktop/src/cfg.rs | 5 +-- packages/desktop/src/lib.rs | 1 + 5 files changed, 43 insertions(+), 36 deletions(-) create mode 100644 docs/src/images/helloworld.png create mode 100644 examples/hello_world.rs diff --git a/docs/src/hello_world.md b/docs/src/hello_world.md index b06027ea2..a2274eea6 100644 --- a/docs/src/hello_world.md +++ b/docs/src/hello_world.md @@ -4,6 +4,8 @@ Let's put together a simple "hello world" to get acquainted with Dioxus. The Dio This demo will build a simple desktop app. Check out the platform-specific setup guides on how to port your app to different targets. +### A new project with Cargo + First, let's start a new project. Rust has the concept of executables and libraries. Executables have a `main.rs` and libraries have `lib.rs`. A project may have both. Our `hello world` will be an executable - we expect our app to launch when we run it! Cargo provides this for us: ```shell @@ -65,6 +67,8 @@ edition = "2018" ``` +### Adding Dioxus as a dependency + To use the Dioxus library, we'll want to add the most recent version of `Dioxus` to our crate. If you have `cargo edit` installed, simply call: ```shell @@ -75,24 +79,30 @@ It's very important to add `dioxus` with the `desktop` feature for this example. If you plan to develop extensions for the `Dioxus` ecosystem, please use the `dioxus` crate with the `core` feature to limit the amount of dependencies your project brings in. +### Our first app + Now, let's edit our `main.rs` file: ```rust -use diouxs::prelude::*; +use dioxus::prelude::*; fn main() { dioxus::desktop::start(App, |c| c); } -static App: FC<()> = |(cx, props)| { +fn App(cx: Component<()>) -> Element { cx.render(rsx! ( div { "Hello, world!" } )) -}; +} ``` -Let's dissect our example a bit. +At this point, you could call `cargo run` and be greeted with a simple `Hello, World!` screen: + +![hello world](images/helloworld.png) + +### Dissecting our example This bit of code imports everything from the the `prelude` module. This brings into scope the right traits, types, and macros needed for working with Dioxus. @@ -111,45 +121,29 @@ fn main() { Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Option`. ```rust -static App: FC<()> = |(cx, props)| { - cx.render(rsx! { - div { "Hello, world!" } - }) -}; -``` - -The closure `FC<()>` syntax is identical to the function syntax, but with lifetimes managed for you. In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax: - -```rust -fn App<'a>(cx: Context<'a>, props: &'a ()) -> DomTree<'a> { +fn App(cx: Component<()>) -> DomTree { cx.render(rsx! { div { "Hello, world!" } }) } ``` +The closure `FC<()>` syntax is identical to the function syntax, but with lifetimes managed for you. In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax: + +```rust +fn App<'a>(cx: Component<'a, ()>) -> DomTree<'a> { + cx.render(rsx! { + div { "Hello, world!" } + }) +} +``` + +### The `Context` object In React, you'll save 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 `rsx!` macro + Next, we're greeted with the `rsx!` macro. This lets us add a custom DSL for declaratively building the structure of our app. The semantics of this macro are similar to that of JSX and HTML, though with a familiar Rust-y interface. The `html!` macro is also available for writing components with a JSX/HTML syntax. The `rsx!` macro is lazy: it does not immediately produce elements or allocates, but rather builds a closure which can be rendered with `cx.render`. - -Now, let's launch our app: - -```shell -$ cargo run -``` - -Huzzah! We have a simple app. - -![Hello world](images/01-setup-helloworld.png) - -If we wanted to golf a bit, we can shrink our hello-world even smaller: - -```rust -fn main() { - static App: FC<()> = |(cx, props)| rsx!(cx, div {"hello world!"}); - dioxus::web::start(App, |c| c); -} -``` diff --git a/docs/src/images/helloworld.png b/docs/src/images/helloworld.png new file mode 100644 index 0000000000000000000000000000000000000000..c10ebdf46ee879f8f50994ce3918c1038ec40cba GIT binary patch literal 31650 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 C`XB@V literal 0 HcmV?d00001 diff --git a/examples/hello_world.rs b/examples/hello_world.rs new file mode 100644 index 000000000..db9e22899 --- /dev/null +++ b/examples/hello_world.rs @@ -0,0 +1,11 @@ +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(App, |c| c); +} + +fn App((cx, props): Component<()>) -> DomTree { + cx.render(rsx! ( + div { "Hello, world!" } + )) +} diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index 0bfdbab36..9b74a048a 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -22,9 +22,10 @@ impl<'a> DesktopConfig<'a> { /// Initializes a new `WindowBuilder` with default values. #[inline] pub fn new() -> Self { + let mut window = WindowBuilder::new().with_title("Dioxus app"); Self { event_handler: None, - window: Default::default(), + window, pre_rendered: None, manual_edits: None, } @@ -43,7 +44,7 @@ impl<'a> DesktopConfig<'a> { pub fn with_window(&mut self, f: impl FnOnce(WindowBuilder) -> WindowBuilder) -> &mut Self { // gots to do a swap because the window builder only takes itself as muy self // I wish more people knew about returning &mut Self - let mut builder = WindowBuilder::default(); + let mut builder = WindowBuilder::default().with_title("Dioxus App"); std::mem::swap(&mut self.window, &mut builder); builder = f(builder); std::mem::swap(&mut self.window, &mut builder); diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index f64ac82b0..3f521c9ab 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -130,6 +130,7 @@ pub fn run( let window = WindowBuilder::new() .with_menu(menu_bar_menu) + .with_title("Dioxus App") .build(event_loop) .unwrap(); let window_id = window.id(); From 9f7eb0f6002156d3e6e14ea2cb24829133b531c5 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 27 Oct 2021 16:41:00 -0400 Subject: [PATCH 5/9] wip: remove HTML macro and add custom fields --- docs/src/concepts/old | 32 ++ docs/src/concepts/vnodes.md | 129 ++++--- packages/core-macro/src/lib.rs | 153 +-------- packages/core-macro/src/rsx/ambiguous.rs | 51 +-- packages/core-macro/src/rsx/body.rs | 24 +- packages/core-macro/src/rsx/component.rs | 179 +--------- packages/core-macro/src/rsx/element.rs | 418 ++++++++--------------- packages/core-macro/src/rsx/fragment.rs | 30 +- packages/core-macro/src/rsx/node.rs | 32 +- 9 files changed, 290 insertions(+), 758 deletions(-) create mode 100644 docs/src/concepts/old diff --git a/docs/src/concepts/old b/docs/src/concepts/old new file mode 100644 index 000000000..c43f7b4c3 --- /dev/null +++ b/docs/src/concepts/old @@ -0,0 +1,32 @@ + +## All the VNode types + +VNodes can be any of: +- **Element**: a container with a tag name, namespace, attributes, children, and event listeners +- **Text**: bump allocated text derived from string formatting +- **Fragments**: a container of elements with no parent +- **Suspended**: a container for nodes that aren't yet ready to be rendered +- **Anchor**: a special type of node that is only available when fragments have no children + +In practice, only elements and text can be initialized directly while other node types can only be created through hooks or NodeFactory methods. + +## Bump Arena Allocation + +To speed up the process of building our elements and text, Dioxus uses a special type of memory allocator tuned for large batches of small allocations called a Bump Arena. We use the `bumpalo` allocator which was initially developed for Dioxus' spiritual predecessor: `Dodrio.` + +- Bumpalo: [https://github.com/fitzgen/bumpalo](https://github.com/fitzgen/bumpalo) +- Dodrio: [https://github.com/fitzgen/dodrio](https://github.com/fitzgen/dodrio) + +In other frontend frameworks for Rust, nearly every string is allocated using the global allocator. This means that strings in Rust do not benefit from the immutable string interning optimizations that JavaScript engines employ. By using a smaller, faster, more limited allocator, we can increase framework performance, bypassing even the naive WasmBindgen benchmarks for very quick renders. + +It's important to note that VNodes are not `'static` - the VNode definition has a lifetime attached to it: + +```rust, ignore +enum VNode<'bump> { + VElement { tag: &'static str, children: &'bump [VNode<'bump>] }, + VText { content: &'bump str }, + // other VNodes .... +} +``` + +Because VNodes use a bump allocator as their memory backing, they can only be created through the `NodeFactory` API - which we'll cover in the next chapter. This particular detail is important to understand because "rendering" VNodes produces a lifetime attached to the bump arena - which must be explicitly declared when dealing with components that borrow data from their parents. diff --git a/docs/src/concepts/vnodes.md b/docs/src/concepts/vnodes.md index df8e8d391..c80c93853 100644 --- a/docs/src/concepts/vnodes.md +++ b/docs/src/concepts/vnodes.md @@ -1,64 +1,99 @@ -# VNodes and Elements +# Declaring your first UI with Elements -At the heart of Dioxus is the concept of an "element" - a container that can have children, properties, event handlers, and other important attributes. Dioxus only knows how to render the `VNode` datastructure - an Enum variant of an Element, Text, Components, Fragments, and Anchors. +Every user interface you've ever used is just a symphony of tiny widgets working together to abstract over larger complex functions. In Dioxus, we call these tiny widgets "Elements." Using Components, you can easily compose Elements into larger groups to form even larger structures: Apps. -Because Dioxus is meant for the Web and uses WebView as a desktop and mobile renderer, almost all elements in Dioxus share properties with their HTML counterpart. When we declare our elements, we'll do so using HTML semantics: +Because Dioxus is mostly used with HTML/CSS renderers, the default Element "collection" is HTML. Provided the `html` feature is not disabled, we can declare Elements using the `rsx!` macro: + +```rust +#use dioxus::prelude::*; +rsx!( + div {} +) +``` +As you might expect, we can render this call using Dioxus-SSR to produce valid HTML: + +```rust +#use dioxus::prelude::*; +dioxus::ssr::render_lazy(rsx!( + div {} +)) +``` +Produces: +```html +
+``` + +## Composing Elements + +Every element has a set of properties that can be rendered in different ways. In particular, each Element may contain other Elements. To achieve this, we can simply declare new Elements within the parent: + +```rust +#use dioxus::prelude::*; +rsx!( + div { + h1 {} + h2 {} + p {} + } +) +``` +With the default configuration, any Element defined within the `dioxus-html` crate can be declared in this way. To create your own new elements, see the `Custom Elements` Advanced Guide. + +## Text Elements + +Dioxus also supports a special type of Element: Text. Text Elements do not accept children, but rather just text denoted with double quotes. + +```rust +rsx! ( + "hello world" +) +``` +Text Elements can be composed within other Elements: +```rust +rsx! ( + div { + h1 { "hello world" } + p { "Some body content" } + } +) +``` +Text can also be formatted with any value that implements `Display`. We use f-string formatting - a "coming soon" feature for stable Rust that is familiar for Python and JavaScript users: + +```rust +let name = "Bob"; +rsx! ( "hello {name}" ) +``` + +## Attributes + +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 simply use the familiar struct-style syntax that Rust provides us. Commas are optional: ```rust rsx!( div { - "hello world" + hidden: true, + background_color: "blue", + class: "card color-{mycolor}" } ) ``` -As you would expect, this snippet would generate a simple hello-world div. In fact, we can render these nodes directly with the SSR crate: +Each field is defined as a method on the element in the `dioxus-html` crate. This prevents you from misspelling a field name and lets us provide inline documentation. When you need to use a field not defined as a method, you have two options: +1) file an issue if the attribute _should_ be enabled +2) add a custom attribute on-the-fly + +To use custom attributes, simply put the attribute name in quotes followed by a colon: ```rust -dioxus::ssr::render_lazy(rsx!( +rsx!( div { - "hello world" + "custom_attr": "important data here" } -)) +) ``` -And produce the corresponding html structure: -```html -
hello world
-``` +## Listeners -Our structure declared above is made of two variants of the `VNode` datastructure: -- A VElement with a tagname of `div` -- A VText with contents of `"hello world"` - -## All the VNode types - -VNodes can be any of: -- **Element**: a container with a tag name, namespace, attributes, children, and event listeners -- **Text**: bump allocated text derived from string formatting -- **Fragments**: a container of elements with no parent -- **Suspended**: a container for nodes that aren't yet ready to be rendered -- **Anchor**: a special type of node that is only available when fragments have no children - -In practice, only elements and text can be initialized directly while other node types can only be created through hooks or NodeFactory methods. - -## Bump Arena Allocation - -To speed up the process of building our elements and text, Dioxus uses a special type of memory allocator tuned for large batches of small allocations called a Bump Arena. We use the `bumpalo` allocator which was initially developed for Dioxus' spiritual predecessor: `Dodrio.` - -- Bumpalo: [https://github.com/fitzgen/bumpalo](https://github.com/fitzgen/bumpalo) -- Dodrio: [https://github.com/fitzgen/dodrio](https://github.com/fitzgen/dodrio) - -In other frontend frameworks for Rust, nearly every string is allocated using the global allocator. This means that strings in Rust do not benefit from the immutable string interning optimizations that JavaScript engines employ. By using a smaller, faster, more limited allocator, we can increase framework performance, bypassing even the naive WasmBindgen benchmarks for very quick renders. - -It's important to note that VNodes are not `'static` - the VNode definition has a lifetime attached to it: - -```rust, ignore -enum VNode<'bump> { - VElement { tag: &'static str, children: &'bump [VNode<'bump>] }, - VText { content: &'bump str }, - // other VNodes .... -} -``` - -Because VNodes use a bump allocator as their memory backing, they can only be created through the `NodeFactory` API - which we'll cover in the next chapter. This particular detail is important to understand because "rendering" VNodes produces a lifetime attached to the bump arena - which must be explicitly declared when dealing with components that borrow data from their parents. +## Arbitrary Tokens diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index d01a27f35..dab35d6fb 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -182,158 +182,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// ``` #[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(), - } -} - -/// The html! macro makes it easy for developers to write jsx-style markup in their components. -/// -/// ## Complete Reference Guide: -/// ``` -/// const Example: FC<()> = |(cx, props)|{ -/// let formatting = "formatting!"; -/// let formatting_tuple = ("a", "b"); -/// let lazy_fmt = format_args!("lazily formatted text"); -/// cx.render(html! { -///
-///
-///
-///

"Some text"

-///

"Some text with {formatting}"

-///

"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"

-///

-/// "Multiple" -/// "Text" -/// "Blocks" -/// "Use comments as separators in html" -///

-///
-///

"multiple"

-///

"nested"

-///

"elements"

-///
-///
-///

"Headers and attributes!"

-///
-///
-///
-///
-/// {rsx!(p { "More templating!" })} -/// {html!(

"Even HTML templating!!"

)} -/// {(0..10).map(|i| html!(
  • "{i}"
  • ))} -/// {{ -/// 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!(
  • "{v}"
  • )) -/// }} - -/// // Matching -/// // Matching will throw a Rust error about "no two closures are the same type" -/// // To fix this, call "render" method or use the "in" syntax to produce VNodes. -/// // There's nothing we can do about it, sorry :/ (unless you want *really* unhygenic macros) -/// {match true { -/// true => rsx!(cx,

    "Top text"

    ), -/// false => rsx!(cx,

    "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(|| html!(
    ))} -/// -/// // True conditions need to be rendered (same reasons as matching) -/// {if true { -/// html!(cx,

    "Top text"

    ) -/// } else { -/// html!(cx,

    "Bottom text"

    ) -/// }} -/// -/// // returning "None" is a bit noisy... but rare in practice -/// {None as Option<()>} -/// -/// // Use the Dioxus type-alias for less noise -/// {NONE_ELEMENT} -/// -/// // can also just use empty fragments -/// -/// -/// // Fragments let you insert groups of nodes without a parent. -/// // This lets you make components that insert elements as siblings without a container. -///
    "A"
    -/// -///
    "B"
    -///
    "C"
    -/// -/// "D" -/// -/// "heavily nested fragments is an antipattern" -/// "they cause Dioxus to do unnecessary work" -/// "don't use them carelessly if you can help it" -/// -/// -/// -/// // Components -/// // Can accept any paths -/// // Notice how you still get syntax highlighting and IDE support :) -/// -/// -/// -/// -/// // Can take properties -/// -/// -/// // Can take optional properties -/// -/// -/// // Can pass in props directly as an expression -/// {{ -/// let props = TallerProps {a: "hello"}; -/// html!() -/// }} -/// -/// // Spreading can also be overridden manually -/// -/// -/// // Can take children too! -/// -///
    "hello world!"
    -///
    -/// } -/// }) -/// }; -/// -/// mod baller { -/// use super::*; -/// pub struct BallerProps {} -/// -/// /// This component totally balls -/// pub fn Baller(cx: Context<()>) -> DomTree { -/// todo!() -/// } -/// } -/// -/// #[derive(Debug, PartialEq, Props)] -/// pub struct TallerProps { -/// a: &'static str, -/// } -/// -/// /// This component is taller than most :) -/// pub fn Taller(cx: Context) -> DomTree { -/// let b = true; -/// todo!() -/// } -/// ``` -#[proc_macro] -pub fn html(s: TokenStream) -> TokenStream { - match syn::parse::>(s) { + match syn::parse::(s) { Err(e) => e.to_compile_error().into(), Ok(s) => s.to_token_stream().into(), } diff --git a/packages/core-macro/src/rsx/ambiguous.rs b/packages/core-macro/src/rsx/ambiguous.rs index f1ed461e8..71b3a2c4c 100644 --- a/packages/core-macro/src/rsx/ambiguous.rs +++ b/packages/core-macro/src/rsx/ambiguous.rs @@ -16,26 +16,22 @@ use syn::{ }; #[allow(clippy::large_enum_variant)] -pub enum AmbiguousElement { - Element(Element), - Component(Component), +pub enum AmbiguousElement { + Element(Element), + Component(Component), } -impl Parse for AmbiguousElement { +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); + 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); + return input.parse::().map(AmbiguousElement::Component); } } @@ -45,45 +41,16 @@ impl Parse for AmbiguousElement { let first_char = name_str.chars().next().unwrap(); if first_char.is_ascii_uppercase() { - input - .parse::>() - .map(AmbiguousElement::Component) + input.parse::().map(AmbiguousElement::Component) } else { - input - .parse::>() - .map(AmbiguousElement::Element) + input.parse::().map(AmbiguousElement::Element) } } else { Err(Error::new(input.span(), "Not a valid Html tag")) } } } - -impl Parse for AmbiguousElement { - fn parse(input: ParseStream) -> Result { - if input.peek(Token![<]) { - let forked = input.fork(); - forked.parse::().unwrap(); - let tag = forked.parse::()?; - let name_str = tag.to_string(); - - let first_char = name_str.chars().next().unwrap(); - if first_char.is_ascii_uppercase() { - 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 { +impl ToTokens for AmbiguousElement { fn to_tokens(&self, tokens: &mut TokenStream2) { match self { AmbiguousElement::Element(el) => el.to_tokens(tokens), diff --git a/packages/core-macro/src/rsx/body.rs b/packages/core-macro/src/rsx/body.rs index 375e0344a..b5b487459 100644 --- a/packages/core-macro/src/rsx/body.rs +++ b/packages/core-macro/src/rsx/body.rs @@ -7,30 +7,16 @@ use syn::{ use super::*; -pub struct CallBody { +pub struct CallBody { custom_context: Option, - roots: Vec>, + roots: Vec, } /// The custom rusty variant of parsing rsx! -impl Parse for CallBody { +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, - }) - } -} - -/// The HTML variant of parsing rsx! -impl Parse for CallBody { - fn parse(input: ParseStream) -> Result { - let custom_context = try_parse_custom_context(input)?; - - // parsing the contents is almost like parsing the inner of any element, but with no props - let (_, roots, _) = BodyConfig::::new_call_body().parse_component_body(input)?; + let (_, roots, _) = BodyConfig::new_call_body().parse_component_body(input)?; Ok(Self { custom_context, roots, @@ -50,7 +36,7 @@ fn try_parse_custom_context(input: ParseStream) -> Result> { } /// Serialize the same way, regardless of flavor -impl ToTokens for CallBody { +impl ToTokens for CallBody { fn to_tokens(&self, out_tokens: &mut TokenStream2) { let inner = if self.roots.len() == 1 { let inner = &self.roots[0]; diff --git a/packages/core-macro/src/rsx/component.rs b/packages/core-macro/src/rsx/component.rs index f28130e6b..35963b8b5 100644 --- a/packages/core-macro/src/rsx/component.rs +++ b/packages/core-macro/src/rsx/component.rs @@ -22,15 +22,15 @@ use syn::{ token, Error, Expr, ExprClosure, Ident, Result, Token, }; -pub struct Component { +pub struct Component { // accept any path-like argument name: syn::Path, - body: Vec>, - children: Vec>, + body: Vec, + children: Vec, manual_props: Option, } -impl Parse for Component { +impl Parse for Component { fn parse(stream: ParseStream) -> Result { // let name = s.parse::()?; // todo: look into somehow getting the crate/super/etc @@ -41,7 +41,7 @@ impl Parse for Component { let content: ParseBuffer; syn::braced!(content in stream); - let cfg: BodyConfig = BodyConfig { + let cfg: BodyConfig = BodyConfig { allow_children: true, allow_fields: true, allow_manual_props: true, @@ -57,80 +57,14 @@ impl Parse for Component { }) } } -impl Parse for Component { - fn parse(stream: ParseStream) -> Result { - let _l_tok = stream.parse::()?; - let name = syn::Path::parse_mod_style(stream)?; - let mut manual_props = None; - - let mut body: Vec> = vec![]; - let mut children: Vec> = vec![]; - - if stream.peek(Token![..]) { - stream.parse::()?; - manual_props = Some(stream.parse::()?); - } - - while !stream.peek(Token![>]) { - // self-closing - if stream.peek(Token![/]) { - stream.parse::()?; - stream.parse::]>()?; - - return Ok(Self { - name, - manual_props, - body, - children, - }); - } - body.push(stream.parse::>()?); - } - - stream.parse::]>()?; - - 'parsing: loop { - if stream.peek(Token![<]) && stream.peek2(Token![/]) { - break 'parsing; - } - - // [1] Break if empty - if stream.is_empty() { - break 'parsing; - } - - children.push(stream.parse::>()?); - } - - // closing element - stream.parse::()?; - stream.parse::()?; - let close = syn::Path::parse_mod_style(stream)?; - if close != name { - return Err(Error::new_spanned( - close, - "closing element does not match opening", - )); - } - stream.parse::]>()?; - - Ok(Self { - name, - body, - children, - manual_props, - }) - } -} - -pub struct BodyConfig { +pub struct BodyConfig { pub allow_fields: bool, pub allow_children: bool, pub allow_manual_props: bool, } -impl BodyConfig { +impl BodyConfig { /// The configuration to parse the root pub fn new_call_body() -> Self { Self { @@ -141,17 +75,13 @@ impl BodyConfig { } } -impl BodyConfig { +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, - )> { + ) -> Result<(Vec, Vec, Option)> { let mut body = Vec::new(); let mut children = Vec::new(); let mut manual_props = None; @@ -178,7 +108,7 @@ impl BodyConfig { "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.", )); } - body.push(content.parse::>()?); + body.push(content.parse::()?); } else { if !self.allow_children { return Err(Error::new( @@ -186,65 +116,7 @@ impl BodyConfig { "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 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::>()?); + children.push(content.parse::()?); } // consume comma if it exists @@ -257,7 +129,7 @@ impl BodyConfig { } } -impl ToTokens for Component { +impl ToTokens for Component { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; @@ -327,7 +199,7 @@ impl ToTokens for Component { } // the struct's fields info -pub struct ComponentField { +pub struct ComponentField { name: Ident, content: ContentField, } @@ -354,28 +226,7 @@ impl ToTokens for ContentField { } } -impl Parse for ComponentField { - fn parse(input: ParseStream) -> Result { - let name = Ident::parse_any(input)?; - input.parse::()?; - - 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()?) - } - } else { - ContentField::ManExpr(input.parse::()?) - }; - - Ok(Self { name, content }) - } -} -impl Parse for ComponentField { +impl Parse for ComponentField { fn parse(input: ParseStream) -> Result { let name = Ident::parse_any(input)?; input.parse::()?; @@ -397,7 +248,7 @@ impl Parse for ComponentField { } } -impl ToTokens for ComponentField { +impl ToTokens for ComponentField { fn to_tokens(&self, tokens: &mut TokenStream2) { let ComponentField { name, content, .. } = self; tokens.append_all(quote! { diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index 865b8e814..493dc7887 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -11,16 +11,16 @@ use syn::{ // ======================================= // Parse the VNode::Element type // ======================================= -pub struct Element { +pub struct Element { name: Ident, key: Option, - attributes: Vec>, - listeners: Vec>, - children: Vec>, + attributes: Vec, + listeners: Vec, + children: Vec, _is_static: bool, } -impl Parse for Element { +impl Parse for Element { fn parse(stream: ParseStream) -> Result { let name = Ident::parse(stream)?; @@ -28,29 +28,75 @@ impl Parse for Element { let content: ParseBuffer; syn::braced!(content in stream); - let mut attributes: Vec> = vec![]; - let mut listeners: Vec> = vec![]; - let mut children: Vec> = vec![]; + let mut attributes: Vec = vec![]; + let mut listeners: Vec = vec![]; + let mut children: Vec = vec![]; let mut key = None; let mut el_ref = None; - 'parsing: loop { - // [1] Break if empty - if content.is_empty() { - break 'parsing; - } - + while !content.is_empty() { if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { - parse_rsx_element_field( - &content, - &mut attributes, - &mut listeners, - &mut key, - &mut el_ref, - name.clone(), - )?; + let name = Ident::parse_any(stream)?; + let name_str = name.to_string(); + stream.parse::()?; + + if name_str.starts_with("on") { + if stream.peek(token::Brace) { + let content; + syn::braced!(content in stream); + + listeners.push(ElementAttr::EventTokens { + name, + tokens: content.parse()?, + }); + } else { + listeners.push(ElementAttr::EventClosure { + name, + closure: content.parse()?, + }); + }; + } else { + match name_str.as_str() { + "key" => { + key = Some(stream.parse()?); + } + "classes" => { + todo!("custom class list not supported") + } + "namespace" => { + todo!("custom namespace not supported") + } + "node_ref" => { + el_ref = Some(stream.parse::()?); + } + _ => { + if stream.peek(LitStr) { + listeners.push(ElementAttr::AttrText { + name, + value: content.parse()?, + }); + } else { + listeners.push(ElementAttr::AttrExpression { + name, + value: content.parse()?, + }); + } + } + } + } + } else if content.peek(LitStr) && content.peek2(Token![:]) { + let name = content.parse::()?; + content.parse::()?; + + if content.peek(LitStr) { + let value = content.parse::()?; + attributes.push(ElementAttr::CustomAttrText { name, value }); + } else { + let value = content.parse::()?; + attributes.push(ElementAttr::CustomAttrExpression { name, value }); + } } else { - children.push(content.parse::>()?); + children.push(content.parse::()?); } // consume comma if it exists @@ -71,118 +117,17 @@ impl Parse for Element { } } -impl Parse for Element { - fn parse(stream: ParseStream) -> Result { - let _l_tok = stream.parse::()?; - let el_name = Ident::parse(stream)?; - - let mut attributes: Vec> = vec![]; - let mut listeners: Vec> = vec![]; - let mut children: Vec> = vec![]; - let key = None; - - while !stream.peek(Token![>]) { - // self-closing - if stream.peek(Token![/]) { - stream.parse::()?; - stream.parse::]>()?; - - return Ok(Self { - name: el_name, - key: None, - attributes, - _is_static: false, - listeners, - children, - }); - } - - let name = Ident::parse_any(stream)?; - let name_str = name.to_string(); - stream.parse::()?; - if name_str.starts_with("on") { - let inner; - syn::braced!(inner in stream); - let toks = inner.parse::()?; - let ty = AttrType::EventTokens(toks); - listeners.push(ElementAttr { - element_name: el_name.clone(), - name, - value: ty, - namespace: None, - }) - } else { - match name_str.as_str() { - "style" => {} - "key" => {} - _ => { - // "classes" | "namespace" | "ref" | _ => { - let ty = if stream.peek(LitStr) { - let rawtext = stream.parse::().unwrap(); - AttrType::BumpText(rawtext) - } else { - // like JSX, we expect raw expressions - let inner; - syn::braced!(inner in stream); - let toks = inner.parse::()?; - AttrType::FieldTokens(toks) - }; - attributes.push(ElementAttr { - element_name: el_name.clone(), - name, - value: ty, - namespace: None, - }) - } - } - }; - } - - stream.parse::]>()?; - - 'parsing: loop { - if stream.peek(Token![<]) && stream.peek2(Token![/]) { - break 'parsing; - } - - // [1] Break if empty - if stream.is_empty() { - break 'parsing; - } - - children.push(stream.parse::>()?); - } - - // closing element - stream.parse::()?; - stream.parse::()?; - - let close = Ident::parse_any(stream)?; - if close != el_name { - return Err(Error::new_spanned( - close, - "closing element does not match opening", - )); - } - stream.parse::]>()?; - - Ok(Self { - key, - name: el_name, - attributes, - children, - listeners, - _is_static: false, - }) - } -} - -impl ToTokens for Element { +impl ToTokens for Element { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; - let attr = &self.attributes; let childs = &self.children; let listeners = &self.listeners; + + let attr = self.attributes.iter().map(|x| ElementAttrNamed { + attr: x, + el_name: name, + }); + let key = match &self.key { Some(ty) => quote! { Some(format_args_f!(#ty)) }, None => quote! { None }, @@ -200,164 +145,73 @@ impl ToTokens for Element { } } -/// ======================================= -/// Parse a VElement's Attributes -/// ======================================= -struct ElementAttr { - element_name: Ident, - name: Ident, - value: AttrType, - namespace: Option, +enum ElementAttr { + // attribute: "valuee {}" + AttrText { name: Ident, value: LitStr }, + + // attribute: true, + AttrExpression { name: Ident, value: Expr }, + + // "attribute": "value {}" + CustomAttrText { name: LitStr, value: LitStr }, + + // "attribute": true, + CustomAttrExpression { name: LitStr, value: Expr }, + + // onclick: move |_| {} + EventClosure { name: Ident, closure: ExprClosure }, + + // onclick: {} + EventTokens { name: Ident, tokens: Expr }, } -enum AttrType { - BumpText(LitStr), - FieldTokens(Expr), - EventTokens(Expr), - Event(ExprClosure), -} - -// We parse attributes and dump them into the attribute vec -// This is because some tags might be namespaced (IE style) -// These dedicated tags produce multiple name-spaced attributes -fn parse_rsx_element_field( - stream: ParseStream, - attrs: &mut Vec>, - listeners: &mut Vec>, - key: &mut Option, - el_ref: &mut Option, - element_name: Ident, -) -> Result<()> { - let name = Ident::parse_any(stream)?; - let name_str = name.to_string(); - stream.parse::()?; - - // Return early if the field is a listener - if name_str.starts_with("on") { - // remove the "on" bit - let ty = if stream.peek(token::Brace) { - let content; - syn::braced!(content in stream); - - // Try to parse directly as a closure - let fork = content.fork(); - if let Ok(event) = fork.parse::() { - content.advance_to(&fork); - AttrType::Event(event) - } else { - AttrType::EventTokens(content.parse()?) - } - } else { - AttrType::Event(stream.parse()?) - }; - listeners.push(ElementAttr { - name, - value: ty, - namespace: None, - element_name, - }); - return Ok(()); - } - - let ty: AttrType = match name_str.as_str() { - // short circuit early if style is using the special syntax - "style" if stream.peek(token::Brace) => { - let inner; - syn::braced!(inner in stream); - - while !inner.is_empty() { - let name = Ident::parse_any(&inner)?; - inner.parse::()?; - let ty = if inner.peek(LitStr) { - let rawtext = inner.parse::().unwrap(); - AttrType::BumpText(rawtext) - } else { - let toks = inner.parse::()?; - AttrType::FieldTokens(toks) - }; - if inner.peek(Token![,]) { - let _ = inner.parse::(); - } - attrs.push(ElementAttr { - name, - value: ty, - namespace: Some("style".to_string()), - element_name: element_name.clone(), - }); - } - - return Ok(()); - } - "key" => { - *key = Some(stream.parse::()?); - return Ok(()); - } - "classes" => { - todo!("custom class list not supported") - } - "namespace" => { - todo!("custom namespace not supported") - } - "node_ref" => { - *el_ref = Some(stream.parse::()?); - return Ok(()); - } - - // Fall through - _ => { - if stream.peek(LitStr) { - let rawtext = stream.parse::().unwrap(); - AttrType::BumpText(rawtext) - } else { - let toks = stream.parse::()?; - AttrType::FieldTokens(toks) - } - } - }; - - // consume comma if it exists - // we don't actually care if there *are* commas between attrs - if stream.peek(Token![,]) { - let _ = stream.parse::(); - } - - attrs.push(ElementAttr { - name, - value: ty, - namespace: None, - element_name, - }); - Ok(()) -} - -impl ToTokens for ElementAttr { +impl ToTokens for ElementAttr { fn to_tokens(&self, tokens: &mut TokenStream2) { - let el_name = &self.element_name; - let nameident = &self.name; + // weird requirment + todo!() + } +} - // TODO: wire up namespace - let _name_str = self.name.to_string(); - let _namespace = match &self.namespace { - Some(t) => quote! { Some(#t) }, - None => quote! { None }, - }; +struct ElementAttrNamed<'a> { + el_name: &'a Ident, + attr: &'a ElementAttr, +} + +impl ToTokens for ElementAttrNamed<'_> { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let ElementAttrNamed { el_name, attr } = *self; - match &self.value { - AttrType::BumpText(value) => tokens.append_all(quote! { - dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value)) - }), + let toks = match attr { + ElementAttr::AttrText { name, value } => { + quote! { + dioxus_elements::#el_name.#name(__cx, format_args_f!(#value)) + } + } + ElementAttr::AttrExpression { name, value } => { + quote! { + dioxus_elements::#el_name.#name(__cx, #value) + } + } - AttrType::FieldTokens(exp) => tokens.append_all(quote! { - dioxus_elements::#el_name.#nameident(__cx, #exp) - }), + 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 ) } + } - AttrType::Event(event) => tokens.append_all(quote! { - dioxus::events::on::#nameident(__cx, #event) - }), + ElementAttr::EventClosure { name, closure } => { + quote! { + dioxus::events::on::#name(__cx, #closure) + } + } + ElementAttr::EventTokens { name, tokens } => { + quote! { + dioxus::events::on::#name(__cx, #tokens) + } + } + }; - AttrType::EventTokens(event) => tokens.append_all(quote! { - dioxus::events::on::#nameident(__cx, #event) - }), - } + tokens.append_all(toks); } } diff --git a/packages/core-macro/src/rsx/fragment.rs b/packages/core-macro/src/rsx/fragment.rs index 91141538e..8964f8841 100644 --- a/packages/core-macro/src/rsx/fragment.rs +++ b/packages/core-macro/src/rsx/fragment.rs @@ -19,11 +19,11 @@ use { }, }; -pub struct Fragment { - children: Vec>, +pub struct Fragment { + children: Vec, } -impl Parse for Fragment { +impl Parse for Fragment { fn parse(input: ParseStream) -> Result { input.parse::()?; @@ -33,7 +33,7 @@ impl Parse for Fragment { let content: ParseBuffer; syn::braced!(content in input); while !content.is_empty() { - content.parse::>()?; + content.parse::()?; if content.peek(Token![,]) { let _ = content.parse::(); @@ -43,27 +43,7 @@ impl Parse for Fragment { } } -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 { +impl ToTokens for Fragment { fn to_tokens(&self, tokens: &mut TokenStream2) { let childs = &self.children; let children = quote! { diff --git a/packages/core-macro/src/rsx/node.rs b/packages/core-macro/src/rsx/node.rs index 9d0475bb7..e027711e6 100644 --- a/packages/core-macro/src/rsx/node.rs +++ b/packages/core-macro/src/rsx/node.rs @@ -10,13 +10,13 @@ use syn::{ // ============================================== // Parse any div {} as a VElement // ============================================== -pub enum BodyNode { - Element(AmbiguousElement), +pub enum BodyNode { + Element(AmbiguousElement), Text(TextNode), RawExpr(Expr), } -impl Parse for BodyNode { +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 @@ -31,33 +31,11 @@ impl Parse for BodyNode { return Ok(BodyNode::Text(stream.parse::()?)); } - Ok(BodyNode::Element( - stream.parse::>()?, - )) - } -} -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::()?)); - } - - Ok(BodyNode::Element( - stream.parse::>()?, - )) + Ok(BodyNode::Element(stream.parse::()?)) } } -impl ToTokens for BodyNode { +impl ToTokens for BodyNode { fn to_tokens(&self, tokens: &mut TokenStream2) { match &self { BodyNode::Element(el) => el.to_tokens(tokens), From 339e450027b4a5d2e1317e13863cd1b2e7ab5853 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 27 Oct 2021 16:58:20 -0400 Subject: [PATCH 6/9] wip: fix some bugs around the rsx macro --- packages/core-macro/src/rsx/element.rs | 98 ++++++++++++++------------ packages/core/examples/expand.rs | 1 + packages/core/examples/jsframework.rs | 4 +- packages/core/examples/syntax.rs | 23 +----- 4 files changed, 57 insertions(+), 69 deletions(-) create mode 100644 packages/core/examples/expand.rs diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index 493dc7887..4c37b0b21 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -14,51 +14,57 @@ use syn::{ pub struct Element { name: Ident, key: Option, - attributes: Vec, - listeners: Vec, + attributes: Vec, + listeners: Vec, children: Vec, _is_static: bool, } impl Parse for Element { fn parse(stream: ParseStream) -> Result { - let name = Ident::parse(stream)?; + let el_name = Ident::parse(stream)?; // parse the guts let content: ParseBuffer; syn::braced!(content in stream); - let mut attributes: Vec = vec![]; - let mut listeners: Vec = vec![]; + let mut attributes: Vec = vec![]; + let mut listeners: Vec = vec![]; let mut children: Vec = vec![]; let mut key = None; let mut el_ref = None; while !content.is_empty() { if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { - let name = Ident::parse_any(stream)?; + let name = content.parse::()?; let name_str = name.to_string(); - stream.parse::()?; + content.parse::()?; if name_str.starts_with("on") { - if stream.peek(token::Brace) { - let content; - syn::braced!(content in stream); + if content.peek(token::Brace) { + let mycontent; + syn::braced!(mycontent in content); - listeners.push(ElementAttr::EventTokens { - name, - tokens: content.parse()?, + listeners.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::EventTokens { + name, + tokens: mycontent.parse()?, + }, }); } else { - listeners.push(ElementAttr::EventClosure { - name, - closure: content.parse()?, + listeners.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::EventClosure { + name, + closure: content.parse()?, + }, }); }; } else { match name_str.as_str() { "key" => { - key = Some(stream.parse()?); + key = Some(content.parse()?); } "classes" => { todo!("custom class list not supported") @@ -67,18 +73,24 @@ impl Parse for Element { todo!("custom namespace not supported") } "node_ref" => { - el_ref = Some(stream.parse::()?); + el_ref = Some(content.parse::()?); } _ => { - if stream.peek(LitStr) { - listeners.push(ElementAttr::AttrText { - name, - value: content.parse()?, + if content.peek(LitStr) { + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::AttrText { + name, + value: content.parse()?, + }, }); } else { - listeners.push(ElementAttr::AttrExpression { - name, - value: content.parse()?, + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::AttrExpression { + name, + value: content.parse()?, + }, }); } } @@ -90,10 +102,16 @@ impl Parse for Element { if content.peek(LitStr) { let value = content.parse::()?; - attributes.push(ElementAttr::CustomAttrText { name, value }); + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::CustomAttrText { name, value }, + }); } else { let value = content.parse::()?; - attributes.push(ElementAttr::CustomAttrExpression { name, value }); + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr::CustomAttrExpression { name, value }, + }); } } else { children.push(content.parse::()?); @@ -108,7 +126,7 @@ impl Parse for Element { Ok(Self { key, - name, + name: el_name, attributes, children, listeners, @@ -121,12 +139,9 @@ impl ToTokens for Element { fn to_tokens(&self, tokens: &mut TokenStream2) { let name = &self.name; let childs = &self.children; - let listeners = &self.listeners; - let attr = self.attributes.iter().map(|x| ElementAttrNamed { - attr: x, - el_name: name, - }); + let listeners = &self.listeners; + let attr = &self.attributes; let key = match &self.key { Some(ty) => quote! { Some(format_args_f!(#ty)) }, @@ -165,21 +180,14 @@ enum ElementAttr { EventTokens { name: Ident, tokens: Expr }, } -impl ToTokens for ElementAttr { - fn to_tokens(&self, tokens: &mut TokenStream2) { - // weird requirment - todo!() - } +struct ElementAttrNamed { + el_name: Ident, + attr: ElementAttr, } -struct ElementAttrNamed<'a> { - el_name: &'a Ident, - attr: &'a ElementAttr, -} - -impl ToTokens for ElementAttrNamed<'_> { +impl ToTokens for ElementAttrNamed { fn to_tokens(&self, tokens: &mut TokenStream2) { - let ElementAttrNamed { el_name, attr } = *self; + let ElementAttrNamed { el_name, attr } = self; let toks = match attr { ElementAttr::AttrText { name, value } => { diff --git a/packages/core/examples/expand.rs b/packages/core/examples/expand.rs new file mode 100644 index 000000000..1b041b70c --- /dev/null +++ b/packages/core/examples/expand.rs @@ -0,0 +1 @@ +fn main( ) {} diff --git a/packages/core/examples/jsframework.rs b/packages/core/examples/jsframework.rs index 3a9d73681..2b2939a15 100644 --- a/packages/core/examples/jsframework.rs +++ b/packages/core/examples/jsframework.rs @@ -48,11 +48,11 @@ fn Row((cx, props): Component) -> DomTree { cx.render(rsx! { tr { td { class:"col-md-1", "{props.row_id}" } - td { class:"col-md-1", on_click: move |_| { /* run onselect */ } + td { class:"col-md-1", onclick: move |_| { /* run onselect */ } a { class: "lbl", "{props.label}" } } td { class: "col-md-1" - a { class: "remove", on_click: {handler} + a { class: "remove", onclick: {handler} span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } } } diff --git a/packages/core/examples/syntax.rs b/packages/core/examples/syntax.rs index 0c1674c8d..a54a3c218 100644 --- a/packages/core/examples/syntax.rs +++ b/packages/core/examples/syntax.rs @@ -8,31 +8,10 @@ use dioxus_html as dioxus_elements; fn main() {} fn html_usage() { - let r = html! { -
    - "hello world" -
    -
    -
    - "hello world" - - -
    - }; let r = rsx! { div { + onclick: move |_| {} "hello world" } }; } - -fn rsx_uage() { - // let r = html! { - // - // "hello world" - // "hello world" - // "hello world" - // "hello world" - // - // }; -} From e1c858dda5c937a56f402bfb3e8b90baf34b84f1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 27 Oct 2021 17:09:22 -0400 Subject: [PATCH 7/9] chore: clippy happy on macro --- packages/core-macro/src/lib.rs | 1 - packages/core-macro/src/props/mod.rs | 6 +++--- packages/core-macro/src/rsx/element.rs | 10 +++++----- packages/core-macro/src/rsx/fragment.rs | 2 +- packages/core-macro/src/rsx/mod.rs | 4 ---- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index dab35d6fb..a132148c8 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -1,6 +1,5 @@ use proc_macro::TokenStream; use quote::ToTokens; -use rsx::{AS_HTML, AS_RSX}; use syn::parse_macro_input; pub(crate) mod htm; diff --git a/packages/core-macro/src/props/mod.rs b/packages/core-macro/src/props/mod.rs index cdfe7e8b6..c13f1b174 100644 --- a/packages/core-macro/src/props/mod.rs +++ b/packages/core-macro/src/props/mod.rs @@ -17,7 +17,7 @@ pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result { let data = match &ast.data { syn::Data::Struct(data) => match &data.fields { syn::Fields::Named(fields) => { - let struct_info = struct_info::StructInfo::new(&ast, fields.named.iter())?; + let struct_info = struct_info::StructInfo::new(ast, fields.named.iter())?; let builder_creation = struct_info.builder_creation_impl()?; let conversion_helper = struct_info.conversion_helper_impl()?; let fields = struct_info @@ -196,7 +196,7 @@ mod field_info { if let Some(ref name) = field.ident { Ok(FieldInfo { ordinal, - name: &name, + name, generic_ident: syn::Ident::new( &format!("__{}", strip_raw_ident_prefix(name.to_string())), proc_macro2::Span::call_site(), @@ -585,7 +585,7 @@ mod struct_info { // we use the heuristic: are there *any* generic parameters? // If so, then they might have non-static lifetimes and we can't compare two generic things that *might borrow* // Therefore, we will generate code that shortcircuits the "comparison" in memoization - let are_there_generics = self.generics.params.len() > 0; + let are_there_generics = !self.generics.params.is_empty(); let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); let all_fields_param = syn::GenericParam::Type( diff --git a/packages/core-macro/src/rsx/element.rs b/packages/core-macro/src/rsx/element.rs index 4c37b0b21..0a13b1bcd 100644 --- a/packages/core-macro/src/rsx/element.rs +++ b/packages/core-macro/src/rsx/element.rs @@ -3,9 +3,8 @@ use super::*; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ - ext::IdentExt, - parse::{discouraged::Speculative, Parse, ParseBuffer, ParseStream}, - token, Error, Expr, ExprClosure, Ident, LitStr, Result, Token, + parse::{Parse, ParseBuffer, ParseStream}, + token, Expr, ExprClosure, Ident, LitStr, Result, Token, }; // ======================================= @@ -32,8 +31,9 @@ impl Parse for Element { let mut listeners: Vec = vec![]; let mut children: Vec = vec![]; let mut key = None; - let mut el_ref = None; + let mut _el_ref = None; + // todo: more descriptive error handling while !content.is_empty() { if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { let name = content.parse::()?; @@ -73,7 +73,7 @@ impl Parse for Element { todo!("custom namespace not supported") } "node_ref" => { - el_ref = Some(content.parse::()?); + _el_ref = Some(content.parse::()?); } _ => { if content.peek(LitStr) { diff --git a/packages/core-macro/src/rsx/fragment.rs b/packages/core-macro/src/rsx/fragment.rs index 8964f8841..9743b1501 100644 --- a/packages/core-macro/src/rsx/fragment.rs +++ b/packages/core-macro/src/rsx/fragment.rs @@ -8,7 +8,7 @@ //! - [ ] Children //! - [ ] Keys -use super::{AmbiguousElement, HtmlOrRsx, AS_HTML, AS_RSX}; +use super::AmbiguousElement; use syn::parse::ParseBuffer; use { proc_macro2::TokenStream as TokenStream2, diff --git a/packages/core-macro/src/rsx/mod.rs b/packages/core-macro/src/rsx/mod.rs index 57f3d9bf7..5a0592b36 100644 --- a/packages/core-macro/src/rsx/mod.rs +++ b/packages/core-macro/src/rsx/mod.rs @@ -25,7 +25,3 @@ pub use component::*; pub use element::*; pub use fragment::*; pub use node::*; - -pub(crate) type HtmlOrRsx = bool; -pub const AS_HTML: bool = true; -pub const AS_RSX: bool = false; From 9b5f82af7dce18b75a781b86b188bc713860bb99 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 27 Oct 2021 22:32:12 -0400 Subject: [PATCH 8/9] docs: start on components --- docs/src/SUMMARY.md | 2 +- docs/src/concepts/components.md | 17 +++++++++-- docs/src/concepts/vnodes.md | 47 +++++++++++++++++++++++++++---- docs/src/images/reddit.png | Bin 0 -> 166746 bytes packages/core/examples/syntax.rs | 3 ++ 5 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 docs/src/images/reddit.png diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 6c00723fe..5ef73694e 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -5,9 +5,9 @@ - [Hello, World!](hello_world.md) - [Core Topics](concepts/00-index.md) - [Intro to Elements](concepts/vnodes.md) + - [Intro to Components](concepts/components.md) - [Elements with RSX and NodeFactory](concepts/rsx.md) - [RSX in Depth](concepts/rsx_in_depth.md) - - [Components and Props](concepts/components.md) - [Memoization](concepts/memoization.md) - [Hooks and Internal State](concepts/hooks.md) - [Event handlers](concepts/event_handlers.md) diff --git a/docs/src/concepts/components.md b/docs/src/concepts/components.md index 62d0eaf81..e54cc2066 100644 --- a/docs/src/concepts/components.md +++ b/docs/src/concepts/components.md @@ -1,4 +1,17 @@ -# Components and Props +# Introduction to Components + +In the previous chapter, we learned about Elements and how they can be composed to create a basic User Interface. In this chapter, we'll learn how to group Elements together to form Components. + +## What is a component? + +In short, a component is a special function that takes an input and outputs a group of Elements. Typically, Components serve a single purpose: group functionality of a User Interface. Much like a function encapsulates some specific computation task, a Component encapsulates some specific rendering task. + +Let's take the r/rust subreddit page and see if we can identify some parts of the page that are encapsulated as a single rendering task. + + + + + Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API. @@ -9,7 +22,7 @@ struct MyProps { } fn Example(cx: Context) -> DomTree { - html! {
    "Hello {cx.cx.name}!"
    } + html! {
    "Hello {cx.props.name}!"
    } } ``` diff --git a/docs/src/concepts/vnodes.md b/docs/src/concepts/vnodes.md index c80c93853..28e0c35a5 100644 --- a/docs/src/concepts/vnodes.md +++ b/docs/src/concepts/vnodes.md @@ -25,7 +25,7 @@ Produces: ## Composing Elements -Every element has a set of properties that can be rendered in different ways. In particular, each Element may contain other Elements. To achieve this, we can simply declare new Elements within the parent: +Every element has a set of properties that can be rendered in different ways. In particular, each Element may contain other Elements. To achieve this, we can simply declare new Elements contained within the parent's curly braces: ```rust #use dioxus::prelude::*; @@ -37,17 +37,27 @@ rsx!( } ) ``` +As you might expect, the generated HTML for this structure would look like: +```html +
    +

    +

    +

    +
    +``` + With the default configuration, any Element defined within the `dioxus-html` crate can be declared in this way. To create your own new elements, see the `Custom Elements` Advanced Guide. ## Text Elements -Dioxus also supports a special type of Element: Text. Text Elements do not accept children, but rather just text denoted with double quotes. +Dioxus also supports a special type of Element: Text. Text Elements do not accept children, but rather just string literals denoted with double quotes. ```rust rsx! ( "hello world" ) ``` + Text Elements can be composed within other Elements: ```rust rsx! ( @@ -57,7 +67,8 @@ rsx! ( } ) ``` -Text can also be formatted with any value that implements `Display`. We use f-string formatting - a "coming soon" feature for stable Rust that is familiar for Python and JavaScript users: + +Text can also be formatted with any value that implements `Display`. We use [f-string formatting](https://docs.rs/fstrings/0.2.3/fstrings/) - a "coming soon" feature for stable Rust that is familiar for Python and JavaScript users: ```rust let name = "Bob"; @@ -81,6 +92,7 @@ rsx!( ``` Each field is defined as a method on the element in the `dioxus-html` crate. This prevents you from misspelling a field name and lets us provide inline documentation. When you need to use a field not defined as a method, you have two options: + 1) file an issue if the attribute _should_ be enabled 2) add a custom attribute on-the-fly @@ -89,11 +101,36 @@ To use custom attributes, simply put the attribute name in quotes followed by a ```rust rsx!( div { - "custom_attr": "important data here" + "customAttr": "important data here" } ) ``` +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. + ## Listeners -## Arbitrary Tokens +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. + +```rust +rsx!( + div { + onclick: move |_| {} + onmouseover: {handler}, + } +) +``` + +## Moving On + +This chapter just scratches the surface on how Elements can be defined. + +We learned: +- Elements are the basic building blocks of User Interfaces +- Elements can contain other elements +- Elements can either be a named container or text +- Some Elements have properties that the renderer can use to draw the UI to the screen + +Next, we'll compose Elements together to form components. diff --git a/docs/src/images/reddit.png b/docs/src/images/reddit.png new file mode 100644 index 0000000000000000000000000000000000000000..2277db264584fc54029578817d0d1cd8eddb0ce3 GIT binary patch literal 166746 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

    C#V%Ncemtgqm;qXCGJWzZofLB^^X0#1GWR*uT!f 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 literal 0 HcmV?d00001 diff --git a/packages/core/examples/syntax.rs b/packages/core/examples/syntax.rs index a54a3c218..39453c0cc 100644 --- a/packages/core/examples/syntax.rs +++ b/packages/core/examples/syntax.rs @@ -8,9 +8,12 @@ use dioxus_html as dioxus_elements; fn main() {} fn html_usage() { + let mo = move |_| {}; let r = rsx! { div { onclick: move |_| {} + onmouseover: {mo} + "type": "bar", "hello world" } }; From 78007445f944f259170307d840e0f16242b7b4b6 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 28 Oct 2021 17:07:16 -0400 Subject: [PATCH 9/9] docs: improve docs --- README.md | 2 +- docs/src/README.md | 18 +- docs/src/SUMMARY.md | 104 ++++--- .../rsx-in-depth.md} | 0 docs/src/cli/README.md | 1 - docs/src/cli/build.md | 1 - docs/src/cli/clean.md | 1 - docs/src/cli/init.md | 1 - docs/src/cli/serve.md | 1 - docs/src/cli/test.md | 1 - docs/src/cli/watch.md | 1 - docs/src/concepts/04-hooks.md | 16 - docs/src/concepts/05-context-api.md | 45 --- docs/src/concepts/09-interactivity.md | 0 docs/src/concepts/9901-hello-world.md | 18 -- docs/src/concepts/async.md | 1 + docs/src/concepts/asynccallbacks.md | 1 + docs/src/concepts/asynctasks.md | 1 + docs/src/concepts/bundline.md | 1 + docs/src/concepts/components.md | 174 ++++++++++- docs/src/concepts/conditional_rendering.md | 1 + docs/src/concepts/custom_elements.md | 1 + docs/src/concepts/custom_renderer.md | 1 + docs/src/concepts/effects.md | 1 + docs/src/concepts/exporting_components.md | 291 ++++++++++++++++++ docs/src/concepts/interactivity.md | 1 + docs/src/concepts/lifecycles.md | 1 + docs/src/concepts/managing_state.md | 1 + docs/src/concepts/old | 32 -- docs/src/concepts/server_side_components.md | 1 + docs/src/concepts/suspense.md | 1 + docs/src/concepts/vnodes.md | 21 +- docs/src/depth/components.md | 1 + docs/src/depth/memoization.md | 1 + docs/src/depth/performance.md | 1 + docs/src/depth/props.md | 1 + docs/src/depth/rsx.md | 1 + docs/src/depth/testing.md | 1 + docs/src/depth/topics.md | 1 + docs/src/format/README.md | 1 - docs/src/format/configuration/README.md | 1 - .../configuration/environment-variables.md | 1 - docs/src/format/configuration/general.md | 1 - .../src/format/configuration/preprocessors.md | 1 - docs/src/format/configuration/renderers.md | 1 - docs/src/format/mathjax.md | 1 - docs/src/format/mdbook.md | 1 - docs/src/format/summary.md | 1 - docs/src/format/theme/README.md | 1 - docs/src/format/theme/editor.md | 1 - docs/src/format/theme/index-hbs.md | 1 - docs/src/format/theme/syntax-highlighting.md | 1 - docs/src/gettingstarted/fromjs.md | 37 --- docs/src/gettingstarted/webapps.md | 26 -- docs/src/hello_world.md | 28 +- docs/src/images/reddit_post.png | Bin 0 -> 26297 bytes docs/src/images/reddit_post_components.png | Bin 0 -> 26651 bytes docs/src/platforms/00-index.md | 16 - docs/src/platforms/01-ssr.md | 3 - docs/src/platforms/02-wasm.md | 3 - docs/src/platforms/03-desktop.md | 3 - docs/src/platforms/04-concurrency.md | 21 -- docs/src/platforms/05-liveview.md | 20 -- docs/src/platforms/06-components.md | 31 -- docs/src/setup.md | 4 +- docs/src/tutorial/advanced_guides.md | 1 + docs/src/tutorial/components.md | 1 + docs/src/tutorial/index.md | 13 + docs/src/tutorial/new_app.md | 1 + docs/src/tutorial/publishing.md | 1 + docs/src/tutorial/state.md | 1 + docs/src/tutorial/structure.md | 1 + docs/src/tutorial/styling.md | 1 + src/lib.rs | 2 +- 74 files changed, 605 insertions(+), 370 deletions(-) rename docs/src/{concepts/07-state-management.md => advanced-guides/rsx-in-depth.md} (100%) delete mode 100644 docs/src/cli/README.md delete mode 100644 docs/src/cli/build.md delete mode 100644 docs/src/cli/clean.md delete mode 100644 docs/src/cli/init.md delete mode 100644 docs/src/cli/serve.md delete mode 100644 docs/src/cli/test.md delete mode 100644 docs/src/cli/watch.md delete mode 100644 docs/src/concepts/04-hooks.md delete mode 100644 docs/src/concepts/05-context-api.md delete mode 100644 docs/src/concepts/09-interactivity.md delete mode 100644 docs/src/concepts/9901-hello-world.md create mode 100644 docs/src/concepts/async.md create mode 100644 docs/src/concepts/asynccallbacks.md create mode 100644 docs/src/concepts/asynctasks.md create mode 100644 docs/src/concepts/bundline.md create mode 100644 docs/src/concepts/conditional_rendering.md create mode 100644 docs/src/concepts/custom_elements.md create mode 100644 docs/src/concepts/custom_renderer.md create mode 100644 docs/src/concepts/effects.md create mode 100644 docs/src/concepts/exporting_components.md create mode 100644 docs/src/concepts/interactivity.md create mode 100644 docs/src/concepts/lifecycles.md create mode 100644 docs/src/concepts/managing_state.md delete mode 100644 docs/src/concepts/old create mode 100644 docs/src/concepts/server_side_components.md create mode 100644 docs/src/concepts/suspense.md create mode 100644 docs/src/depth/components.md create mode 100644 docs/src/depth/memoization.md create mode 100644 docs/src/depth/performance.md create mode 100644 docs/src/depth/props.md create mode 100644 docs/src/depth/rsx.md create mode 100644 docs/src/depth/testing.md create mode 100644 docs/src/depth/topics.md delete mode 100644 docs/src/format/README.md delete mode 100644 docs/src/format/configuration/README.md delete mode 100644 docs/src/format/configuration/environment-variables.md delete mode 100644 docs/src/format/configuration/general.md delete mode 100644 docs/src/format/configuration/preprocessors.md delete mode 100644 docs/src/format/configuration/renderers.md delete mode 100644 docs/src/format/mathjax.md delete mode 100644 docs/src/format/mdbook.md delete mode 100644 docs/src/format/summary.md delete mode 100644 docs/src/format/theme/README.md delete mode 100644 docs/src/format/theme/editor.md delete mode 100644 docs/src/format/theme/index-hbs.md delete mode 100644 docs/src/format/theme/syntax-highlighting.md delete mode 100644 docs/src/gettingstarted/fromjs.md delete mode 100644 docs/src/gettingstarted/webapps.md create mode 100644 docs/src/images/reddit_post.png create mode 100644 docs/src/images/reddit_post_components.png delete mode 100644 docs/src/platforms/00-index.md delete mode 100644 docs/src/platforms/01-ssr.md delete mode 100644 docs/src/platforms/02-wasm.md delete mode 100644 docs/src/platforms/03-desktop.md delete mode 100644 docs/src/platforms/04-concurrency.md delete mode 100644 docs/src/platforms/05-liveview.md delete mode 100644 docs/src/platforms/06-components.md create mode 100644 docs/src/tutorial/advanced_guides.md create mode 100644 docs/src/tutorial/components.md create mode 100644 docs/src/tutorial/index.md create mode 100644 docs/src/tutorial/new_app.md create mode 100644 docs/src/tutorial/publishing.md create mode 100644 docs/src/tutorial/state.md create mode 100644 docs/src/tutorial/structure.md create mode 100644 docs/src/tutorial/styling.md diff --git a/README.md b/README.md index bb7e60cd6..bb02e6420 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust. ```rust -static App: FC<()> = |(cx, props)| { +fn App((cx, props): Component<()>) -> Element { let mut count = use_state(cx, || 0); cx.render(rsx!( diff --git a/docs/src/README.md b/docs/src/README.md index da1f68a97..f700448fc 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -5,8 +5,7 @@ **Dioxus** is a framework and ecosystem for building fast, scalable, and robust user interfaces with the Rust programming language. This guide will help you get up-and-running with Dioxus running on the Web, Desktop, Mobile, and more. ```rust -// An example Dioxus app - closely resembles React -fn App(cx: Component<()>) -> Element { +fn App((cx, props): Component<()>) -> Element { let mut count = use_state(cx, || 0); cx.render(rsx!( @@ -20,7 +19,6 @@ fn App(cx: Component<()>) -> Element { The Dioxus API and patterns closely resemble React - if this guide is lacking in any general concept or an error message is confusing, we recommend substituting "React" for "Dioxus" in your web search terms. A major goal of Dioxus is to provide 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! - ### Web Support --- @@ -79,23 +77,27 @@ Examples: --- Mobile is currently the least-supported renderer target for Dioxus. Mobile apps are rendered with the platform's WebView, meaning that animations, transparency, and native widgets are not currently achievable. In addition, iOS is the only supported Mobile Platform. It is possible to get Dioxus running on Android and rendered with WebView, but the Rust windowing library that Dioxus uses - tao - does not currently supported Android. +Mobile support is currently best suited for CRUD-style apps, ideally for internal teams who need to develop quickly but don't care much about animations or native widgets. + [Jump to the getting started guide for Mobile.]() Examples: - [Todo App]() - [Chat App]() -### LiveView Support +### LiveView / Server Component Support --- -The internal architecture of Dioxus was designed from day one to support the `LiveView` use-case, where a web server hosts a running app for each connected user. As of today, there is no out-of-the-box LiveView support - you'll need to wire this up yourself. While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between WASM and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA. - +The internal architecture of Dioxus was designed from day one to support the `LiveView` use-case, where a web server hosts a running app for each connected user. As of today, there is no first-class LiveView support - you'll need to wire this up yourself. +While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between Wasm and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA. ### Multithreaded Support --- -The Dioxus VirtualDom, sadly, is not currently `Send.` Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc. +The Dioxus VirtualDom, sadly, is not currently `Send`. Internally, we use quite a bit of interior mutability which is not thread-safe. This means you can't easily use Dioxus with most web frameworks like Tide, Rocket, Axum, etc. To solve this, you'll want to spawn a VirtualDom on its own thread and communicate with it via channels. -When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String - but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a VirtualDomPool which solves the `Send` problem. +When working with web frameworks that require `Send`, it is possible to render a VirtualDom immediately to a String - but you cannot hold the VirtualDom across an await point. For retained-state SSR (essentially LiveView), you'll need to create a pool of VirtualDoms. + +Ultimately, you can always wrap the VirtualDom with a `Send` type and manually uphold the `Send` guarantees yourself. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 5ef73694e..9255f443d 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -3,22 +3,47 @@ - [Introduction](README.md) - [Getting Setup](setup.md) - [Hello, World!](hello_world.md) -- [Core Topics](concepts/00-index.md) - - [Intro to Elements](concepts/vnodes.md) - - [Intro to Components](concepts/components.md) - - [Elements with RSX and NodeFactory](concepts/rsx.md) - - [RSX in Depth](concepts/rsx_in_depth.md) - - [Memoization](concepts/memoization.md) - - [Hooks and Internal State](concepts/hooks.md) - - [Event handlers](concepts/event_handlers.md) - - [Global State](concepts/sharedstate.md) - - [User Input and Controlled Components](concepts/errorhandling.md) - - [Error handling](concepts/errorhandling.md) -- [Reference Guide]() -- [Advanced Guides]() - - [Custom Renderer]() - - [LiveView Support]() - - [Bundling and Distributing]() +- [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) + - [Conditional Rendering](concepts/conditional_rendering.md) + - [Lists](concepts/lists.md) +- [Adding Interactivity](concepts/interactivity.md) + - [Event handlers](concepts/event_handlers.md) + - [User Input and Controlled Components](concepts/errorhandling.md) + - [Lifecycle, updates, and effects](concepts/lifecycles.md) +- [Managing State](concepts/managing_state.md) + - [Hooks and Internal State](concepts/hooks.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) + - [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) +- [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) - [Web]() - [Getting Started]() - [Down-casting Nodes]() @@ -29,33 +54,26 @@ - [Wrapping Web APIs]() - [Mobile]() - [Wrapping Web APIs]() - - - +- [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/docs/src/concepts/07-state-management.md b/docs/src/advanced-guides/rsx-in-depth.md similarity index 100% rename from docs/src/concepts/07-state-management.md rename to docs/src/advanced-guides/rsx-in-depth.md diff --git a/docs/src/cli/README.md b/docs/src/cli/README.md deleted file mode 100644 index bcb71988f..000000000 --- a/docs/src/cli/README.md +++ /dev/null @@ -1 +0,0 @@ -# Command Line Tool diff --git a/docs/src/cli/build.md b/docs/src/cli/build.md deleted file mode 100644 index 6a5f7cb4e..000000000 --- a/docs/src/cli/build.md +++ /dev/null @@ -1 +0,0 @@ -# build diff --git a/docs/src/cli/clean.md b/docs/src/cli/clean.md deleted file mode 100644 index 0eceb144a..000000000 --- a/docs/src/cli/clean.md +++ /dev/null @@ -1 +0,0 @@ -# clean diff --git a/docs/src/cli/init.md b/docs/src/cli/init.md deleted file mode 100644 index a6131c10e..000000000 --- a/docs/src/cli/init.md +++ /dev/null @@ -1 +0,0 @@ -# init diff --git a/docs/src/cli/serve.md b/docs/src/cli/serve.md deleted file mode 100644 index 5293e3f35..000000000 --- a/docs/src/cli/serve.md +++ /dev/null @@ -1 +0,0 @@ -# serve diff --git a/docs/src/cli/test.md b/docs/src/cli/test.md deleted file mode 100644 index 83c831f0b..000000000 --- a/docs/src/cli/test.md +++ /dev/null @@ -1 +0,0 @@ -# test diff --git a/docs/src/cli/watch.md b/docs/src/cli/watch.md deleted file mode 100644 index 12ec1aa74..000000000 --- a/docs/src/cli/watch.md +++ /dev/null @@ -1 +0,0 @@ -# watch diff --git a/docs/src/concepts/04-hooks.md b/docs/src/concepts/04-hooks.md deleted file mode 100644 index 7979ccc6e..000000000 --- a/docs/src/concepts/04-hooks.md +++ /dev/null @@ -1,16 +0,0 @@ -```rust -fn Example<'a>(cx: Context<'a>, props: &()) -> DomTree<'a> { - let service = use_combubulator(cx); - let Status { name, pending, count } = service.info(); - html! { -
    -

    "Hello, {name}!"

    -

    "Status: {pending}!"

    -

    "Count {count}!"

    - -
    - } -} -``` diff --git a/docs/src/concepts/05-context-api.md b/docs/src/concepts/05-context-api.md deleted file mode 100644 index 319c4f8ee..000000000 --- a/docs/src/concepts/05-context-api.md +++ /dev/null @@ -1,45 +0,0 @@ -# Context API - -```rust -// Create contexts available to children -// Only one context can be associated with any given component -// This is known as "exposed state". Children can access this context, -// but will not be automatically subscribed. -fn ContextCreate(cx: &mut Context<()>) -> DomTree { - let context = cx.set_context(|| CustomContext::new()); - html! { <> {cx.children()} } -} - -fn ContextRead(cx: &mut Context<()>) -> DomTree { - // Panics if context is not available - let some_cx = cx.get_context::(); - let text = some_cx.select("some_selector"); - html! {
    "{text}"
    } -} - -fn Subscription(cx: &mut Context<()>) -> DomTree { - // Open a "port" on the component for actions to trigger a re-evaluation - let subscription = cx.new_subscription(); - - // A looping timer - the effect is re-called on every re-evaluation - use_async_effect(cx, move || async { - timer::new(2000).await; - subscription.call(); - }, None); - - // A one-shot timer, the deps don't change so the effect only happens once - use_async_effect_deps(cx, move || async { - timer::new(2000).await; - subscription.call(); - }, ()); -} - -// Mix subscriptions and context to make a simple Redux -fn use_global_state(cx: &mut Context<()>) -> T { - let some_cx = cx.get_context::(); - let component_subscription = cx.new_subscription(); - some_cx.subscribe_component(component_subscription); - some_cx -} - -``` diff --git a/docs/src/concepts/09-interactivity.md b/docs/src/concepts/09-interactivity.md deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/src/concepts/9901-hello-world.md b/docs/src/concepts/9901-hello-world.md deleted file mode 100644 index f61ca4f09..000000000 --- a/docs/src/concepts/9901-hello-world.md +++ /dev/null @@ -1,18 +0,0 @@ -# Hello, World! - -Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API. - -```rust -#[derive(Properties, PartialEq)] -struct MyProps { - name: String -} - -fn Example(cx: Context) -> DomTree { - cx.render(html! { -
    "Hello {cx.name}!"
    - }) -} -``` - -For functions to be valid components, they must take the `Context` object which is generic over some properties. The properties parameter must implement the `Properties` trait, which can be automatically derived. Whenever the input properties of a component changes, the function component will be re-ran and a new set of VNodes will be generated. diff --git a/docs/src/concepts/async.md b/docs/src/concepts/async.md new file mode 100644 index 000000000..f6c3d103d --- /dev/null +++ b/docs/src/concepts/async.md @@ -0,0 +1 @@ +# Working with Async diff --git a/docs/src/concepts/asynccallbacks.md b/docs/src/concepts/asynccallbacks.md new file mode 100644 index 000000000..6fcf65b66 --- /dev/null +++ b/docs/src/concepts/asynccallbacks.md @@ -0,0 +1 @@ +# Async Callbacks diff --git a/docs/src/concepts/asynctasks.md b/docs/src/concepts/asynctasks.md new file mode 100644 index 000000000..a26beca9a --- /dev/null +++ b/docs/src/concepts/asynctasks.md @@ -0,0 +1 @@ +# Tasks diff --git a/docs/src/concepts/bundline.md b/docs/src/concepts/bundline.md new file mode 100644 index 000000000..963cf5490 --- /dev/null +++ b/docs/src/concepts/bundline.md @@ -0,0 +1 @@ +# Bundling and Distributing diff --git a/docs/src/concepts/components.md b/docs/src/concepts/components.md index e54cc2066..81406ff36 100644 --- a/docs/src/concepts/components.md +++ b/docs/src/concepts/components.md @@ -6,26 +6,176 @@ In the previous chapter, we learned about Elements and how they can be composed In short, a component is a special function that takes an input and outputs a group of Elements. Typically, Components serve a single purpose: group functionality of a User Interface. Much like a function encapsulates some specific computation task, a Component encapsulates some specific rendering task. -Let's take the r/rust subreddit page and see if we can identify some parts of the page that are encapsulated as a single rendering task. +### Learning through prior art +Let's take a look at a post on r/rust and see if we can sketch out a component representation. - +![Reddit Post](../images/reddit_post.png) +This component has a bunch of important information: +- The score +- The number of comments +- How long ago it was posted +- The url short address +- The title +- The username of the original poster -Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API. +If we wanted to sketch out these requirements in Rust, we would start with a struct: ```rust -#[derive(Properties, PartialEq)] -struct MyProps { - name: String -} - -fn Example(cx: Context) -> DomTree { - html! {
    "Hello {cx.props.name}!"
    } +struct Post { + score: i32, + comment_count: u32, + post_time: Instant, + url: String, + title: String, + original_poster_name: String } ``` -Here, the `Context` object is used to access hook state, create subscriptions, and interact with the built-in context API. Props, children, and component APIs are accessible via the `Context` object. The functional component macro makes life more productive by inlining props directly as function arguments, similar to how Rocket parses URIs. +If we look at the layout of the component, we notice quite a few buttons and functionality: + +- Upvote/Downvote +- View comments +- Share +- Save +- Hide +- Give award +- Report +- Crosspost +- Filter by site +- View article +- Visit user + +If we included all this functionality in one `rsx!` call, it would be huge! Instead, let's break the post down into some core pieces: + +![Post as Component](../images/reddit_post_components.png) + +- **VoteButtons**: Upvote/Downvote +- **TitleCard**: Title, Filter-By-Url +- **MetaCard**: Original Poster, Time Submitted +- **ActionCard**: View comments, Share, Save, Hide, Give award, Report, Crosspost + +### Modeling with Dioxus + +When designing these components, we can start by sketching out the hierarchy using Dioxus. In general, our "Post" component will simply be comprised of our four sub-components. We would start the process by defining our "Post" component. Our component will take in all of the important data we listed above as part of its input. + +Unlike normal functions, Dioxus components must explicitly define a single struct to contain all the inputs. These are commonly called "Properties" (props). Our component will be a combination of these properties and a function to render them. + +Our props must implement the `Properties` trait and - if the component does not borrow any data - `PartialEq`. Both of these can be done automatically through derive macros: +```rust +#[derive(Properties, PartialEq)] +struct PostProps { + id: Uuid, + score: i32, + comment_count: u32, + post_time: Instant, + url: String, + title: String, + original_poster: String +} +``` + +And our render function: +```rust +fn Post((cx, props): Component) -> Element { + cx.render(rsx!{ + div { class: "post-container" + VoteButtons { + score: props.score, + } + TitleCard { + title: props.title, + url: props.url, + } + MetaCard { + original_poster: props.original_poster, + post_time: props.post_time, + } + ActionCard { + post_id: props.id + } + } + }) +} +``` + +When we render components, we use the traditional Rust struct syntax to declare their properties. Dioxus will automatically call "into" on the property fields, cloning when necessary. Notice how our `Post` component is simply a collection of important smaller components wrapped together in a single container. + +Let's take a look at the `VoteButtons` component. For now, we won't include any interactivity - just the rendering the vote buttons and score to the screen. + +Most of your Components will look exactly like this: a Props struct and a render function. 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`. Every component must take a tuple of `Context` and `&Props` and return an `Element`. +```rust + +#[derive(PartialEq, Props)] +struct VoteButtonsProps { + score: i32 +} + +fn VoteButtons((cx, props): Component) -> Element { + cx.render(rsx!{ + div { class: "votebuttons" + div { class: "arrow up" } + div { class: "score", "{props.score}"} + div { class: "arrow down" } + } + }) +} +``` + +## Borrowing + +You can avoid clones using borrowed component syntax. For example, let's say we passed the TitleCard title as an `&str` instead of `String`. In JavaScript, the string would simply be copied by reference - none of the contents would be copied, but rather the reference to the string's contents are copied. In Rust, this would be similar to calling `clone` on `Rc`. + +Because we're working in Rust, we can choose to either use `Rc`, clone `Title` on every re-render of `Post`, or simply borrow it. In most cases, you'll just want to let `Title` be cloned. + +To enable borrowed values for your component, we need to add a lifetime to let the Rust compiler know that the output `Element` borrows from the component's props. + +```rust +#[derive(Properties)] +struct TitleCardProps<'a> { + title: &'a str, +} + +fn TitleCard<'a>((cx, props): Component<'a, TitleCardProps>) -> Element<'a> { + cx.render(rsx!{ + h1 { "{props.title}" } + }) +} +``` + +## The `Context` object + +Though very similar with React, Dioxus is different in a few ways. Most notably, React components will not have a `Context` parameter in the component declaration. + +Have you ever wondered how the `useState()` call works in React without a `this` object to actually store the state? + +React uses global variables to store this information which must be carefully managed, especially in environments with multiple React roots - like the server. + +```javascript +function Component({}) { + let [state, set_state] = useState(10); +} +``` + + +Because Dioxus needs to work with the rules of Rust, we need to provide a way for the component to do some internal bookkeeping. That's what the `Context` object is: a place for the component to store state, manage listeners, and allocate elements. Advanced users of Dioxus will want to learn how to properly leverage the `Context` object to build robust, performant extensions for Dioxus. + +```rust +fn Post((cx /* <-- our Context object*/, props): Component) -> Element { + cx.render(rsx!{ }) +} +``` + +## Moving forward + +Next chapter, we'll talk about composing Elements and Components across files to build a larger Dioxus App. + +For more references on components, make sure to check out: + +- [Components in depth]() +- [Lifecycles]() +- [The Context object]() +- [Optional Prop fields]() -The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly. diff --git a/docs/src/concepts/conditional_rendering.md b/docs/src/concepts/conditional_rendering.md new file mode 100644 index 000000000..b6cea82dd --- /dev/null +++ b/docs/src/concepts/conditional_rendering.md @@ -0,0 +1 @@ +# Conditional Rendering diff --git a/docs/src/concepts/custom_elements.md b/docs/src/concepts/custom_elements.md new file mode 100644 index 000000000..3b24ad9fe --- /dev/null +++ b/docs/src/concepts/custom_elements.md @@ -0,0 +1 @@ +# Custom Elements diff --git a/docs/src/concepts/custom_renderer.md b/docs/src/concepts/custom_renderer.md new file mode 100644 index 000000000..577b4f5d3 --- /dev/null +++ b/docs/src/concepts/custom_renderer.md @@ -0,0 +1 @@ +# Custom Renderer diff --git a/docs/src/concepts/effects.md b/docs/src/concepts/effects.md new file mode 100644 index 000000000..7b8ba3e3f --- /dev/null +++ b/docs/src/concepts/effects.md @@ -0,0 +1 @@ +# Effects diff --git a/docs/src/concepts/exporting_components.md b/docs/src/concepts/exporting_components.md new file mode 100644 index 000000000..4dac4a339 --- /dev/null +++ b/docs/src/concepts/exporting_components.md @@ -0,0 +1,291 @@ + +# Reusing, Importing, and Exporting Components + +As your application grows in size, you'll want to start breaking your UI into components and, eventually, different files. This is a great idea to encapsulate functionality of your UI and scale your team. + +Let's say our app looks something like this: + +```shell +├── Cargo.toml +└── src + └── main.rs +``` + +```rust +// main.rs +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(App, |c| c); +} + +fn App((cx, props): Component<()>) -> Element {} + +#[derive(PartialEq, Props)] +struct PostProps{} +fn Post((cx, props): Component) -> Element {} + +#[derive(PartialEq, Props)] +struct VoteButtonsProps {} +fn VoteButtons((cx, props): Component) -> Element {} + +#[derive(PartialEq, Props)] +struct TitleCardProps {} +fn TitleCard((cx, props): Component) -> Element {} + +#[derive(PartialEq, Props)] +struct MetaCardProps {} +fn MetaCard((cx, props): Component) -> Element {} + +#[derive(PartialEq, Props)] +struct ActionCardProps {} +fn ActionCard((cx, props): Component) -> 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. + +## Breaking into different files + +Fortunately, Rust has a built-in module system that's much cleaner than what you might be used to in JavaScript. Because `VoteButtons`, `TitleCard`, `MetaCard`, and `ActionCard` all belong to the `Post` component, let's put them all in a folder together called "post". We'll make a file for each component and move the props and render function. + +```rust +// src/post/action.rs + +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +struct ActionCardProps {} +fn ActionCard((cx, props): Component) -> 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. + +```shell +├── Cargo.toml +└── src + ├── main.rs + └── post + ├── vote.rs + ├── title.rs + ├── meta.rs + ├── action.rs + └── mod.rs +``` + + + +In our `main.rs`, we'll want to declare the `post` module so we can access our `Post` component. + +```rust +// main.rs +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(App, |c| c); +} + +mod post; + +fn App((cx, props): Component<()>) -> Element { + cx.render(rsx!{ + post::Post { + id: Uuid::new_v4(), + score: 10, + comment_count: 10, + post_time: std::Instant::now(), + url: "example".to_string(), + title: "Title".to_string(), + original_poster: "me".to_string() + } + }) +} +``` + +If you tried to build this app right now, you'll get an error message saying that `Post is private, trying changing it to public`. This is because we haven't properly exported our component! To fix this, we need to make sure both the Props and Component are declared as "public": + +```rust +// src/post/mod.rs + +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +pub struct PostProps {} +pub fn Post((cx, props): Component) -> Element {} +``` + +While we're here, we also need to make sure each of our subcomponents are included as modules and exported. + +Our "post/mod.rs" file will eventually look like this: + +```rust +use dioxus::prelude::*; + +mod vote; +mod title; +mod meta; +mod action; + +#[derive(Properties, PartialEq)] +pub struct PostProps { + id: uuid::Uuid, + score: i32, + comment_count: u32, + post_time: std::time::Instant, + url: String, + title: String, + original_poster: String +} + +pub fn Post((cx, props): Component) -> Element { + cx.render(rsx!{ + div { class: "post-container" + vote::VoteButtons { + score: props.score, + } + title::TitleCard { + title: props.title, + url: props.url, + } + meta::MetaCard { + original_poster: props.original_poster, + post_time: props.post_time, + } + action::ActionCard { + post_id: props.id + } + } + }) +} +``` + + +Ultimately, including and exporting components is governed by Rust's module system. [The Rust book is a great resource to learn about these concepts in greater detail.](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) + +## Final structure: + +```shell +├── Cargo.toml +└── src + ├── main.rs + └── post + ├── vote.rs + ├── title.rs + ├── meta.rs + ├── action.rs + └── mod.rs +``` + +```rust +// main.rs: +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(App, |c| c); +} + +mod post; + +fn App((cx, props): Component<()>) -> Element { + cx.render(rsx!{ + post::Post { + id: Uuid::new_v4(), + score: 10, + comment_count: 10, + post_time: std::Instant::now(), + url: "example".to_string(), + title: "Title".to_string(), + original_poster: "me".to_string() + } + }) +} +``` + + +```rust +// src/post/mod.rs +use dioxus::prelude::*; + +mod vote; +mod title; +mod meta; +mod action; + +#[derive(Properties, PartialEq)] +pub struct PostProps { + id: uuid::Uuid, + score: i32, + comment_count: u32, + post_time: std::time::Instant, + url: String, + title: String, + original_poster: String +} + +pub fn Post((cx, props): Component) -> Element { + cx.render(rsx!{ + div { class: "post-container" + vote::VoteButtons { + score: props.score, + } + title::TitleCard { + title: props.title, + url: props.url, + } + meta::MetaCard { + original_poster: props.original_poster, + post_time: props.post_time, + } + action::ActionCard { + post_id: props.id + } + } + }) +} +``` + +```rust +// src/post/vote.rs +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +pub struct VoteButtonsProps {} +pub fn VoteButtons((cx, props): Component) -> Element {} +``` + +```rust +// src/post/title.rs +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +pub struct TitleCardProps {} +pub fn TitleCard((cx, props): Component) -> Element {} +``` + +```rust +// src/post/meta.rs +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +pub struct MetaCardProps {} +pub fn MetaCard((cx, props): Component) -> Element {} +``` + +```rust +// src/post/action.rs +use dioxus::prelude::*; + +#[derive(PartialEq, Props)] +pub struct ActionCardProps {} +pub fn ActionCard((cx, props): Component) -> Element {} +``` + +## Moving forward + +Next chapter, we'll start to add use code to hide and show Elements with conditional rendering. + +For more reading on components: + +- [Components in depth]() +- [Lifecycles]() +- [The Context object]() +- [Optional Prop fields]() diff --git a/docs/src/concepts/interactivity.md b/docs/src/concepts/interactivity.md new file mode 100644 index 000000000..5e99d9853 --- /dev/null +++ b/docs/src/concepts/interactivity.md @@ -0,0 +1 @@ +# Adding Interactivity diff --git a/docs/src/concepts/lifecycles.md b/docs/src/concepts/lifecycles.md new file mode 100644 index 000000000..07a02f4ea --- /dev/null +++ b/docs/src/concepts/lifecycles.md @@ -0,0 +1 @@ +# Lifecycle, updates, and effects diff --git a/docs/src/concepts/managing_state.md b/docs/src/concepts/managing_state.md new file mode 100644 index 000000000..931d66c2f --- /dev/null +++ b/docs/src/concepts/managing_state.md @@ -0,0 +1 @@ +# Managing State diff --git a/docs/src/concepts/old b/docs/src/concepts/old deleted file mode 100644 index c43f7b4c3..000000000 --- a/docs/src/concepts/old +++ /dev/null @@ -1,32 +0,0 @@ - -## All the VNode types - -VNodes can be any of: -- **Element**: a container with a tag name, namespace, attributes, children, and event listeners -- **Text**: bump allocated text derived from string formatting -- **Fragments**: a container of elements with no parent -- **Suspended**: a container for nodes that aren't yet ready to be rendered -- **Anchor**: a special type of node that is only available when fragments have no children - -In practice, only elements and text can be initialized directly while other node types can only be created through hooks or NodeFactory methods. - -## Bump Arena Allocation - -To speed up the process of building our elements and text, Dioxus uses a special type of memory allocator tuned for large batches of small allocations called a Bump Arena. We use the `bumpalo` allocator which was initially developed for Dioxus' spiritual predecessor: `Dodrio.` - -- Bumpalo: [https://github.com/fitzgen/bumpalo](https://github.com/fitzgen/bumpalo) -- Dodrio: [https://github.com/fitzgen/dodrio](https://github.com/fitzgen/dodrio) - -In other frontend frameworks for Rust, nearly every string is allocated using the global allocator. This means that strings in Rust do not benefit from the immutable string interning optimizations that JavaScript engines employ. By using a smaller, faster, more limited allocator, we can increase framework performance, bypassing even the naive WasmBindgen benchmarks for very quick renders. - -It's important to note that VNodes are not `'static` - the VNode definition has a lifetime attached to it: - -```rust, ignore -enum VNode<'bump> { - VElement { tag: &'static str, children: &'bump [VNode<'bump>] }, - VText { content: &'bump str }, - // other VNodes .... -} -``` - -Because VNodes use a bump allocator as their memory backing, they can only be created through the `NodeFactory` API - which we'll cover in the next chapter. This particular detail is important to understand because "rendering" VNodes produces a lifetime attached to the bump arena - which must be explicitly declared when dealing with components that borrow data from their parents. diff --git a/docs/src/concepts/server_side_components.md b/docs/src/concepts/server_side_components.md new file mode 100644 index 000000000..a8c4f6182 --- /dev/null +++ b/docs/src/concepts/server_side_components.md @@ -0,0 +1 @@ +# Server-side components diff --git a/docs/src/concepts/suspense.md b/docs/src/concepts/suspense.md new file mode 100644 index 000000000..f6d8c7e98 --- /dev/null +++ b/docs/src/concepts/suspense.md @@ -0,0 +1 @@ +# Suspense diff --git a/docs/src/concepts/vnodes.md b/docs/src/concepts/vnodes.md index 28e0c35a5..23092cfd6 100644 --- a/docs/src/concepts/vnodes.md +++ b/docs/src/concepts/vnodes.md @@ -18,14 +18,16 @@ dioxus::ssr::render_lazy(rsx!( div {} )) ``` + Produces: ```html
    ``` +We can construct any valid HTML tag with the `tag {}` pattern and expect the resulting HTML structure to resemble our declaration. ## Composing Elements -Every element has a set of properties that can be rendered in different ways. In particular, each Element may contain other Elements. To achieve this, we can simply declare new Elements contained within the parent's curly braces: +Of course, we need more complex structures to make our apps actually useful! Just like HTML, the `rsx!` macro lets us nest Elements inside of each other. ```rust #use dioxus::prelude::*; @@ -75,11 +77,26 @@ let name = "Bob"; rsx! ( "hello {name}" ) ``` +Unfortunately, you cannot drop in arbitrary expressions directly into the string literal. In the cases where we need to compute a complex value, we'll want to use `format_args!` directly. Due to specifics of how the `rsx!` macro (we'll cover later), our call to `format_args` must be contained within curly braces *and* square braces. + +```rust +rsx!( {[format_args!("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: + +```rust +let name = if enabled { "Jack" } else { "Bob" }; +rsx! ( "hello {name}" ) +``` + ## Attributes 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 simply use the familiar struct-style syntax that Rust provides us. Commas are optional: +To do this, we use the familiar struct-style syntax that Rust provides. Commas are optional: ```rust rsx!( diff --git a/docs/src/depth/components.md b/docs/src/depth/components.md new file mode 100644 index 000000000..0eac4eb52 --- /dev/null +++ b/docs/src/depth/components.md @@ -0,0 +1 @@ +# Components diff --git a/docs/src/depth/memoization.md b/docs/src/depth/memoization.md new file mode 100644 index 000000000..3ed477f68 --- /dev/null +++ b/docs/src/depth/memoization.md @@ -0,0 +1 @@ +# Memoization diff --git a/docs/src/depth/performance.md b/docs/src/depth/performance.md new file mode 100644 index 000000000..6babb314a --- /dev/null +++ b/docs/src/depth/performance.md @@ -0,0 +1 @@ +# Performance diff --git a/docs/src/depth/props.md b/docs/src/depth/props.md new file mode 100644 index 000000000..d4b69bda7 --- /dev/null +++ b/docs/src/depth/props.md @@ -0,0 +1 @@ +# Props diff --git a/docs/src/depth/rsx.md b/docs/src/depth/rsx.md new file mode 100644 index 000000000..1f349a7ef --- /dev/null +++ b/docs/src/depth/rsx.md @@ -0,0 +1 @@ +# RSX diff --git a/docs/src/depth/testing.md b/docs/src/depth/testing.md new file mode 100644 index 000000000..f00b526a9 --- /dev/null +++ b/docs/src/depth/testing.md @@ -0,0 +1 @@ +# Testing diff --git a/docs/src/depth/topics.md b/docs/src/depth/topics.md new file mode 100644 index 000000000..0cf65091f --- /dev/null +++ b/docs/src/depth/topics.md @@ -0,0 +1 @@ +# Topics in Depth diff --git a/docs/src/format/README.md b/docs/src/format/README.md deleted file mode 100644 index a095177ba..000000000 --- a/docs/src/format/README.md +++ /dev/null @@ -1 +0,0 @@ -# Format diff --git a/docs/src/format/configuration/README.md b/docs/src/format/configuration/README.md deleted file mode 100644 index a025a48b1..000000000 --- a/docs/src/format/configuration/README.md +++ /dev/null @@ -1 +0,0 @@ -# Configuration diff --git a/docs/src/format/configuration/environment-variables.md b/docs/src/format/configuration/environment-variables.md deleted file mode 100644 index 1d535f22b..000000000 --- a/docs/src/format/configuration/environment-variables.md +++ /dev/null @@ -1 +0,0 @@ -# Environment Variables diff --git a/docs/src/format/configuration/general.md b/docs/src/format/configuration/general.md deleted file mode 100644 index 23d342b49..000000000 --- a/docs/src/format/configuration/general.md +++ /dev/null @@ -1 +0,0 @@ -# General diff --git a/docs/src/format/configuration/preprocessors.md b/docs/src/format/configuration/preprocessors.md deleted file mode 100644 index eafd89490..000000000 --- a/docs/src/format/configuration/preprocessors.md +++ /dev/null @@ -1 +0,0 @@ -# Preprocessors diff --git a/docs/src/format/configuration/renderers.md b/docs/src/format/configuration/renderers.md deleted file mode 100644 index 3d174b89a..000000000 --- a/docs/src/format/configuration/renderers.md +++ /dev/null @@ -1 +0,0 @@ -# Renderers diff --git a/docs/src/format/mathjax.md b/docs/src/format/mathjax.md deleted file mode 100644 index 41c57bff1..000000000 --- a/docs/src/format/mathjax.md +++ /dev/null @@ -1 +0,0 @@ -# MathJax Support diff --git a/docs/src/format/mdbook.md b/docs/src/format/mdbook.md deleted file mode 100644 index 13bd6bc23..000000000 --- a/docs/src/format/mdbook.md +++ /dev/null @@ -1 +0,0 @@ -# mdBook-specific features diff --git a/docs/src/format/summary.md b/docs/src/format/summary.md deleted file mode 100644 index 746e7ad06..000000000 --- a/docs/src/format/summary.md +++ /dev/null @@ -1 +0,0 @@ -# SUMMARY.md diff --git a/docs/src/format/theme/README.md b/docs/src/format/theme/README.md deleted file mode 100644 index dbe490157..000000000 --- a/docs/src/format/theme/README.md +++ /dev/null @@ -1 +0,0 @@ -# Theme diff --git a/docs/src/format/theme/editor.md b/docs/src/format/theme/editor.md deleted file mode 100644 index 068f68b3c..000000000 --- a/docs/src/format/theme/editor.md +++ /dev/null @@ -1 +0,0 @@ -# Editor diff --git a/docs/src/format/theme/index-hbs.md b/docs/src/format/theme/index-hbs.md deleted file mode 100644 index 21b912727..000000000 --- a/docs/src/format/theme/index-hbs.md +++ /dev/null @@ -1 +0,0 @@ -# index.hbs diff --git a/docs/src/format/theme/syntax-highlighting.md b/docs/src/format/theme/syntax-highlighting.md deleted file mode 100644 index 5a52c1080..000000000 --- a/docs/src/format/theme/syntax-highlighting.md +++ /dev/null @@ -1 +0,0 @@ -# Syntax highlighting diff --git a/docs/src/gettingstarted/fromjs.md b/docs/src/gettingstarted/fromjs.md deleted file mode 100644 index 015a0671a..000000000 --- a/docs/src/gettingstarted/fromjs.md +++ /dev/null @@ -1,37 +0,0 @@ -### Immutability by default? - ---- - -Rust, like JS and TS, supports both mutable and immutable data. With JS, `const` would be used to signify immutable data, while in rust, the absence of `mut` signifies immutable data. - -Mutability: - -```rust -let mut val = 10; // rust -let val = 10; // js -``` - -Immutability - -```rust -let val = 10; // rust -const val = 10; // js -``` - -However, `const` in JS does not prohibit you from modify the value itself only disallowing assignment. In Rust, immutable **is immutable**. You _never_ have to work about accidentally mutating data; mutating immutable data in Rust requires deliberate advanced datastructures that you won't find in your typical frontend code. - -## How do strings work? - ---- - -In rust, we have `&str`, `&'static str` `String`, and `Rc`. It's a lot, yes, and it might be confusing at first. But it's actually not too bad. - -In Rust, UTF-8 is supported natively, allowing for emoji and extended character sets (like Chinese and Arabic!) instead of the typical ASCII. The primitive `str` can be seen as a couple of UTF-8 code points squished together with a dynamic size. Because this size is variable (not known at compile time for any single character), we reference an array of UTF-8 code points as `&str`. Essentially, we're referencing (the & symbol) some dynamic `str` (a collection of UTF-8 points). - -For text encoded directly in your code, this collection of UTF-8 code points is given the `'static` reference lifetime - essentially meaning the text can be safely referenced for the entire runtime of your program. Contrast this with JS, where a string will only exist for as long as code references it before it gets cleaned up by the garbage collector. - -For text that needs to have characters added, removed, sorted, uppercased, formatted, accessed for mutation, etc, Rust has the `String` type, which is essentially just a dynamically sized `str`. In JS, if you add a character to your string, you actually create an entirely new string (completely cloning the old one first). In Rust, you can safely added characters to strings _without_ having to clone first, making string manipulation in Rust very efficient. - -Finally, we have `Rc`. This is essentially Rust's version of JavaScript's `string`. In JS, whenever you pass a `string` around (and don't mutate it), you don't actually clone it, but rather just increment a counter that says "this code is using this string." This counter prevents the garbage collector from deleting the string before your code is done using it. Only when all parts of your code are done with the string, will the string be deleted. `Rc` works exactly the same way in Rust, but requires a deliberate `.clone()` to get the same behavior. In most instances, Dioxus will automatically do this for you, saving the trouble of having to `clone` when you pass an `Rc` into child components. `Rc` is typically better than `String` for Rust - it allows cheap sharing of strings, and through `make_mut` you can always produce your own mutable copy for modifying. You might not see `Rc` in other Rust libraries as much, but you will see it in Dioxus due to Dioxus' aggressive memoization and focus on efficiency and performance. - -If you run into issues with `&str`, `String`, `Rc`, just try cloning and `to_string` first. For the vast majority of apps, the slight performance hit will be unnoticeable. Once you get better with Strings, it's very easy to go back and remove all the clones for more efficient alternatives, but you will likely never need to. diff --git a/docs/src/gettingstarted/webapps.md b/docs/src/gettingstarted/webapps.md deleted file mode 100644 index 5474fc3b9..000000000 --- a/docs/src/gettingstarted/webapps.md +++ /dev/null @@ -1,26 +0,0 @@ -# Choose your architecture type: -- Static Pages -- Server-side-rendering -- Pure SPA -- Hybrid SPA (liveview) - - -## Static Pages -You'll want the TextRenderer crate. -Use it imperatively by passing props and rendering to string. - -## Server-side-rendering -You'll want the TextRenderer crate. -Serve it with a dedicated middleware crate. - -## Pure SPA -You'll want the WebSys Crate. - -## Hybrid SPA (hydration) -You'll want the WebSys Crate -You'll want the TextRenderer crate - - -## Hybrid SPA (liveview) -You'll want the WebSys crate. -You'll want the Liveview crate. diff --git a/docs/src/hello_world.md b/docs/src/hello_world.md index a2274eea6..6d16375c5 100644 --- a/docs/src/hello_world.md +++ b/docs/src/hello_world.md @@ -91,7 +91,7 @@ fn main() { dioxus::desktop::start(App, |c| c); } -fn App(cx: Component<()>) -> Element { +fn App((cx, props): Component<()>) -> Element { cx.render(rsx! ( div { "Hello, world!" } )) @@ -110,7 +110,7 @@ This bit of code imports everything from the the `prelude` module. This brings i use diouxs::prelude::*; ``` -This initialization code launches a Tokio runtime on a helper thread - where your code will run, and then the WebView on the main-thread. Due to platform requirements, the main thread is blocked by this call. +This initialization code launches a Tokio runtime on a helper thread where your code will run. Then, the WebView renderer will be launched on the main-thread. Due to platform requirements, the main thread is blocked by your app's event loop. ```rust fn main() { @@ -118,29 +118,37 @@ fn main() { } ``` -Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Option`. +Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Element`. ```rust -fn App(cx: Component<()>) -> DomTree { +fn App((cx, props): Component<()>) -> Element { + cx.render(rsx! { + div { "Hello, world!" } + }) +} +``` +In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax: + +```rust +fn App<'a>(cx: Component<'a, ()>) -> Element<'a> { cx.render(rsx! { div { "Hello, world!" } }) } ``` -The closure `FC<()>` syntax is identical to the function syntax, but with lifetimes managed for you. In cases where props need to borrow from their parent, you will need to specify lifetimes using the function syntax: - +Writing `fn App((cx, props): Component<()>) -> Element {` might become tedious. Rust will also let you write functions as static closures, but these types of Components cannot have props that borrow data. ```rust -fn App<'a>(cx: Component<'a, ()>) -> DomTree<'a> { +static App: Fc<()> = |(cx, props)| { cx.render(rsx! { div { "Hello, world!" } - }) -} + }) +}; ``` ### The `Context` object -In React, you'll save 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. +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 `rsx!` macro diff --git a/docs/src/images/reddit_post.png b/docs/src/images/reddit_post.png new file mode 100644 index 0000000000000000000000000000000000000000..6eccf39b23e5cda9d56376bf8cf6ef0cd188178e GIT binary patch literal 26297 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 z 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 Q{RtjdRn$_ble3QcKSib-`Tzg` literal 0 HcmV?d00001 diff --git a/docs/src/platforms/00-index.md b/docs/src/platforms/00-index.md deleted file mode 100644 index 551aa3523..000000000 --- a/docs/src/platforms/00-index.md +++ /dev/null @@ -1,16 +0,0 @@ -# Welcome to Dioxus! - -## Running Examples - -We use the dedicated `dioxus-cli` to build and test dioxus web-apps. This can run examples, tests, build web workers, launch development servers, bundle, and more. It's general purpose, but currently very tailored to Dioxus for liveview and bundling. If you've not used it before, `cargo install --path pacakages/dioxus-cli` will get it installed. This CLI tool should feel like using `cargo` but with 1st party support for assets, bundling, and other important dioxus-specific features. - -Alternatively, `trunk` works but can't run examples. - -- tide_ssr: Handle an HTTP request and return an html body using the html! macro. `cargo run --example tide_ssr` -- doc_generator: Use dioxus SSR to generate the website and docs. `cargo run --example doc_generator` -- fc_macro: Use the functional component macro to build terse components. `cargo run --example fc_macro` -- hello_web: Start a simple wasm app. Requires a web packer like dioxus-cli or trunk `cargo run --example hello` -- router: `cargo run --example router` -- tide_ssr: `cargo run --example tide_ssr` -- webview: Use liveview to bridge into a webview context for a simple desktop application. `cargo run --example webview` -- twitter-clone: A full-featured Twitter clone showcasing dioxus-liveview, state management patterns, and hooks. `cargo run --example twitter` diff --git a/docs/src/platforms/01-ssr.md b/docs/src/platforms/01-ssr.md deleted file mode 100644 index 4880fd321..000000000 --- a/docs/src/platforms/01-ssr.md +++ /dev/null @@ -1,3 +0,0 @@ -# The Server-Side-Rendering Guide - -This guide will help you build your first app that leverages server-side-rendering to display the user interface. diff --git a/docs/src/platforms/02-wasm.md b/docs/src/platforms/02-wasm.md deleted file mode 100644 index bf677cbd3..000000000 --- a/docs/src/platforms/02-wasm.md +++ /dev/null @@ -1,3 +0,0 @@ -# The WASM Guide - -This guide will help you build your first app that leverages WASM in the browser to display the user interface. diff --git a/docs/src/platforms/03-desktop.md b/docs/src/platforms/03-desktop.md deleted file mode 100644 index 39ac86d97..000000000 --- a/docs/src/platforms/03-desktop.md +++ /dev/null @@ -1,3 +0,0 @@ -# The Desktop Guide - -This guide will help you build your first app that leverages webview for desktop to display the user interface. diff --git a/docs/src/platforms/04-concurrency.md b/docs/src/platforms/04-concurrency.md deleted file mode 100644 index c1b2fb12d..000000000 --- a/docs/src/platforms/04-concurrency.md +++ /dev/null @@ -1,21 +0,0 @@ -## Concurrency - -In Dioxus, VNodes are asynchronous and can their rendering can be paused at any time by awaiting a future. Hooks can combine this functionality with the Context and Subscription APIs to craft dynamic and efficient user experiences. - -```rust -fn user_data(cx: Context<()>) -> DomTree { - // Register this future as a task - use_suspense(cx, async { - // Continue on with the component as usual, waiting for data to arrive - let Profile { name, birthday, .. } = fetch_data().await; - html! { -
    - {"Hello, {name}!"} - {if birthday === std::Instant::now() {html! {"Happy birthday!"}}} -
    - } - }) -} -``` - -Asynchronous components are powerful but can also be easy to misuse as they pause rendering for the component and its children. Refer to the concurrent guide for information on how to best use async components. diff --git a/docs/src/platforms/05-liveview.md b/docs/src/platforms/05-liveview.md deleted file mode 100644 index 547d5f91a..000000000 --- a/docs/src/platforms/05-liveview.md +++ /dev/null @@ -1,20 +0,0 @@ -## Liveview - -With the Context, Subscription, and Asynchronous APIs, we've built Dioxus Liveview: a coupling of frontend and backend to deliver user experiences that do not require dedicated API development. Instead of building and maintaining frontend-specific API endpoints, components can directly access databases, server caches, and other services directly from the component. - -These set of features are still experimental. Currently, we're still working on making these components more ergonomic - -```rust -fn live_component(cx: &Context<()>) -> DomTree { - use_live_component( - cx, - // Rendered via the client - #[cfg(target_arch = "wasm32")] - || html! {
    {"Loading data from server..."}
    }, - - // Renderered on the server - #[cfg(not(target_arch = "wasm32"))] - || html! {
    {"Server Data Loaded!"}
    }, - ) -} -``` diff --git a/docs/src/platforms/06-components.md b/docs/src/platforms/06-components.md deleted file mode 100644 index b6b02c0fb..000000000 --- a/docs/src/platforms/06-components.md +++ /dev/null @@ -1,31 +0,0 @@ -## Components - -Dioxus should look and feel just like writing functional React components. In Dioxus, there are no class components with lifecycles. All state management is done via hooks. This encourages logic reusability and lessens the burden on Dioxus to maintain a non-breaking lifecycle API. - -```rust -#[derive(Properties, PartialEq)] -struct MyProps { - name: String -} - -fn Example(cx: Context) -> DomTree { - html! {
    "Hello {cx.cx.name}!"
    } -} -``` - -Here, the `Context` object is used to access hook state, create subscriptions, and interact with the built-in context API. Props, children, and component APIs are accessible via the `Context` object. The functional component macro makes life more productive by inlining props directly as function arguments, similar to how Rocket parses URIs. - -```rust -// A very terse component! -#[fc] -fn Example(cx: Context, name: String) -> DomTree { - html! {
    "Hello {name}!"
    } -} - -// or - -#[functional_component] -pub static Example: FC = |cx, name: String| html! {
    "Hello {name}!"
    }; -``` - -The final output of components must be a tree of VNodes. We provide an html macro for using JSX-style syntax to write these, though, you could use any macro, DSL, templating engine, or the constructors directly. diff --git a/docs/src/setup.md b/docs/src/setup.md index 233415685..c8b2ba756 100644 --- a/docs/src/setup.md +++ b/docs/src/setup.md @@ -2,7 +2,7 @@ Dioxus aims to provide a fast, friendly, and portable toolkit for building user interfaces with Rust. -This Getting Setup guide assumes you'll be building a small desktop application. The process for building web-apps, desktop apps, server-rendered apps, static sites, and mobile apps, is more-or-less the same. You can check out the [Platform Specific Guides](../platforms/00-index.md) for more information on setting up Dioxus for any of the various targets you are building for. +This Getting Setup guide assumes you'll be building a small desktop application. You can check out the [Platform Specific Guides](../platforms/00-index.md) for more information on setting up Dioxus for any of the various supported platforms. # Setting up Dioxus @@ -16,7 +16,7 @@ Dioxus integrates very well with the Rust-Analyzer IDE plugin which will provide ### Dioxus-CLI for dev server, bundling, etc. -We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. It'll be our one-stop-shop for anything related to building and sharing our Dioxus Apps. To install the CLI, you'll need cargo (should be automatically installed with Rust): +We also recommend installing the Dioxus CLI. The Dioxus CLI automates building and packaging for various targets and integrates with simulators, development servers, and app deployment. To install the CLI, you'll need cargo (should be automatically installed with Rust): ``` $ cargo install dioxus-cli diff --git a/docs/src/tutorial/advanced_guides.md b/docs/src/tutorial/advanced_guides.md new file mode 100644 index 000000000..53d2cb54f --- /dev/null +++ b/docs/src/tutorial/advanced_guides.md @@ -0,0 +1 @@ +# Advanced Guides diff --git a/docs/src/tutorial/components.md b/docs/src/tutorial/components.md new file mode 100644 index 000000000..bfbb760b3 --- /dev/null +++ b/docs/src/tutorial/components.md @@ -0,0 +1 @@ +# Defining Components diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md new file mode 100644 index 000000000..48b7e493c --- /dev/null +++ b/docs/src/tutorial/index.md @@ -0,0 +1,13 @@ +# Putting it all together + +So far, we've covered the basics of Dioxus. We've talked about: + +- Elements +- Components +- Interactivity +- State Management +- Async Actions +- Styling + +In this chapter, we'll build a real-world weather app that combines everything we've learned into a cute application that you can run locally. It'll let us monitor different locations simultaneously and periodically check for updates. + diff --git a/docs/src/tutorial/new_app.md b/docs/src/tutorial/new_app.md new file mode 100644 index 000000000..6c644c0bf --- /dev/null +++ b/docs/src/tutorial/new_app.md @@ -0,0 +1 @@ +# New app diff --git a/docs/src/tutorial/publishing.md b/docs/src/tutorial/publishing.md new file mode 100644 index 000000000..96670f1c8 --- /dev/null +++ b/docs/src/tutorial/publishing.md @@ -0,0 +1 @@ +# Publishing diff --git a/docs/src/tutorial/state.md b/docs/src/tutorial/state.md new file mode 100644 index 000000000..35df690d6 --- /dev/null +++ b/docs/src/tutorial/state.md @@ -0,0 +1 @@ +# Defining State diff --git a/docs/src/tutorial/structure.md b/docs/src/tutorial/structure.md new file mode 100644 index 000000000..1cbb47579 --- /dev/null +++ b/docs/src/tutorial/structure.md @@ -0,0 +1 @@ +# Structuring our app diff --git a/docs/src/tutorial/styling.md b/docs/src/tutorial/styling.md new file mode 100644 index 000000000..3e90761b5 --- /dev/null +++ b/docs/src/tutorial/styling.md @@ -0,0 +1 @@ +# Styling diff --git a/src/lib.rs b/src/lib.rs index c2871b5ec..64c5865a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,7 +193,7 @@ pub mod debug {} pub mod prelude { //! A glob import that includes helper types like FC, rsx!, html!, and required traits pub use dioxus_core::prelude::*; - pub use dioxus_core_macro::{format_args_f, html, rsx, Props}; + pub use dioxus_core_macro::{format_args_f, rsx, Props}; pub use dioxus_elements::{GlobalAttributes, SvgAttributes}; pub use dioxus_hooks::*; pub use dioxus_html as dioxus_elements;