From a5f05d73acc0e47b05cff64a373482519414bc7c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 19 Nov 2021 00:49:04 -0500 Subject: [PATCH 1/7] wip: docs and router --- Cargo.toml | 4 + docs/guide/src/concepts/10-concurrent-mode.md | 4 +- docs/guide/src/concepts/11-arena-memo.md | 6 +- docs/guide/src/concepts/12-signals.md | 6 +- .../src/concepts/conditional_rendering.md | 2 +- docs/guide/src/hello_world.md | 2 +- examples/async.rs | 2 +- examples/calculator.rs | 6 +- examples/core/alternative.rs | 2 +- examples/core/syntax.rs | 2 +- examples/crm.rs | 2 +- examples/desktop/crm.rs | 2 +- examples/desktop/todomvc.rs | 2 +- examples/framework_benchmark.rs | 180 ++++---- examples/pattern_reducer.rs | 2 +- examples/web/basic.rs | 4 +- examples/web/crm2.rs | 2 +- notes/Parity.md | 2 +- notes/SOLVEDPROBLEMS.md | 10 +- packages/core-macro/src/lib.rs | 2 +- packages/core-macro/src/rsxtemplate.rs | 2 +- packages/core/Cargo.toml | 11 +- packages/core/benches/jsframework.rs | 19 +- packages/core/examples/rows.rs | 120 +++++ packages/core/flamegraph.svg | 412 ++++++++++++++++++ packages/core/src/diff.rs | 73 +++- packages/core/src/scope.rs | 2 +- packages/core/src/scopearena.rs | 16 +- packages/core/src/virtual_dom.rs | 2 +- packages/desktop/README.md | 14 +- packages/desktop/src/index.html | 5 +- packages/desktop/src/lib.rs | 6 + packages/hooks/src/usecollection.rs | 2 +- packages/hooks/src/useref.rs | 5 + packages/hooks/src/usestate.rs | 2 +- packages/html/src/elements.rs | 21 +- packages/router/Cargo.toml | 6 + packages/router/README.md | 40 ++ packages/router/examples/simple.rs | 45 ++ packages/router/src/lib.rs | 36 +- packages/ssr/README.md | 2 +- packages/ssr/src/lib.rs | 2 +- packages/web/Cargo.toml | 22 +- packages/web/examples/js_bench.rs | 243 +++++++++++ packages/web/examples/simple.rs | 46 ++ packages/web/src/cache.rs | 5 + packages/web/src/cfg.rs | 2 +- packages/web/src/dom.rs | 68 ++- packages/web/src/lib.rs | 15 +- src/lib.rs | 9 +- 50 files changed, 1263 insertions(+), 234 deletions(-) create mode 100644 packages/core/examples/rows.rs create mode 100644 packages/core/flamegraph.svg create mode 100644 packages/router/README.md create mode 100644 packages/router/examples/simple.rs create mode 100644 packages/web/examples/js_bench.rs create mode 100644 packages/web/examples/simple.rs diff --git a/Cargo.toml b/Cargo.toml index 4c1863dc8..bbf4243e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,3 +91,7 @@ path = "./examples/webview.rs" required-features = ["desktop"] name = "tailwind" path = "./examples/tailwind.rs" + + +[patch.crates-io] +wasm-bindgen = { path = "../Tinkering/wasm-bindgen" } diff --git a/docs/guide/src/concepts/10-concurrent-mode.md b/docs/guide/src/concepts/10-concurrent-mode.md index 821f94e12..76e443ab9 100644 --- a/docs/guide/src/concepts/10-concurrent-mode.md +++ b/docs/guide/src/concepts/10-concurrent-mode.md @@ -50,8 +50,8 @@ async fn ExampleLoader(cx: Context<()>) -> Vnode { This API stores the result on the Context object, so the loaded data is taken as reference. */ let name: &Result = use_fetch_data("http://example.com/json", ()) - .place_holder(|(cx, props)|rsx!{
"loading..."
}) - .delayed_place_holder(1000, |(cx, props)|rsx!{
"still loading..."
}) + .place_holder(|cx, props|rsx!{
"loading..."
}) + .delayed_place_holder(1000, |cx, props|rsx!{
"still loading..."
}) .await; match name { diff --git a/docs/guide/src/concepts/11-arena-memo.md b/docs/guide/src/concepts/11-arena-memo.md index dce655bc4..28eccda24 100644 --- a/docs/guide/src/concepts/11-arena-memo.md +++ b/docs/guide/src/concepts/11-arena-memo.md @@ -21,9 +21,9 @@ fn test() -> DomTree { } } -static TestComponent: FC<()> = |(cx, props)|html!{
"Hello world"
}; +static TestComponent: FC<()> = |cx, props|html!{
"Hello world"
}; -static TestComponent: FC<()> = |(cx, props)|{ +static TestComponent: FC<()> = |cx, props|{ let g = "BLAH"; html! {
"Hello world"
@@ -31,7 +31,7 @@ static TestComponent: FC<()> = |(cx, props)|{ }; #[functional_component] -static TestComponent: FC<{ name: String }> = |(cx, props)|html! {
"Hello {name}"
}; +static TestComponent: FC<{ name: String }> = |cx, props|html! {
"Hello {name}"
}; ``` ## Why this behavior? diff --git a/docs/guide/src/concepts/12-signals.md b/docs/guide/src/concepts/12-signals.md index d64a7617e..5000faebc 100644 --- a/docs/guide/src/concepts/12-signals.md +++ b/docs/guide/src/concepts/12-signals.md @@ -96,7 +96,7 @@ Sometimes you want a signal to propagate across your app, either through far-awa ```rust const TITLE: Atom = || "".to_string(); -const Provider: FC<()> = |(cx, props)|{ +const Provider: FC<()> = |cx, props|{ let title = use_signal(&cx, &TITLE); rsx!(cx, input { value: title }) }; @@ -105,7 +105,7 @@ const Provider: FC<()> = |(cx, props)|{ If we use the `TITLE` atom in another component, we can cause updates to flow between components without calling render or diffing either component trees: ```rust -const Receiver: FC<()> = |(cx, props)|{ +const Receiver: FC<()> = |cx, props|{ let title = use_signal(&cx, &TITLE); log::info!("This will only be called once!"); rsx!(cx, @@ -132,7 +132,7 @@ Dioxus automatically understands how to use your signals when mixed with iterato ```rust const DICT: AtomFamily = |_| {}; -const List: FC<()> = |(cx, props)|{ +const List: FC<()> = |cx, props|{ let dict = use_signal(&cx, &DICT); cx.render(rsx!( ul { diff --git a/docs/guide/src/concepts/conditional_rendering.md b/docs/guide/src/concepts/conditional_rendering.md index 977f66597..cfbb8add9 100644 --- a/docs/guide/src/concepts/conditional_rendering.md +++ b/docs/guide/src/concepts/conditional_rendering.md @@ -83,7 +83,7 @@ fn App((cx, props): Scope<()>) -> Element { This syntax even enables us to write a one-line component: ```rust -static App: Fc<()> = |(cx, props)| rsx!(cx, "hello world!"); +static App: Fc<()> = |cx, props| rsx!(cx, "hello world!"); ``` Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`: diff --git a/docs/guide/src/hello_world.md b/docs/guide/src/hello_world.md index 7c703b034..89c785a4b 100644 --- a/docs/guide/src/hello_world.md +++ b/docs/guide/src/hello_world.md @@ -143,7 +143,7 @@ fn App<'a>(cx: Component<'a, ()>) -> Element<'a> { 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 -static App: Fc<()> = |(cx, props)| { +static App: Fc<()> = |cx, props| { cx.render(rsx! { div { "Hello, world!" } }) diff --git a/examples/async.rs b/examples/async.rs index b0ea0a7b4..bb05669e4 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -11,7 +11,7 @@ async fn main() { dioxus::desktop::launch(App, |c| c); } -pub static App: FC<()> = |(cx, _)| { +pub static App: FC<()> = |cx, _| { let count = use_state(cx, || 0); let mut direction = use_state(cx, || 1); diff --git a/examples/calculator.rs b/examples/calculator.rs index 3fc110b64..0b1b4f1e3 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -10,7 +10,7 @@ fn main() { dioxus::desktop::launch(APP, |cfg| cfg); } -const APP: FC<()> = |(cx, _)| { +const APP: FC<()> = |cx, _| { let cur_val = use_state(cx, || 0.0_f64); let operator = use_state(cx, || None as Option<&'static str>); let display_value = use_state(cx, || String::from("")); @@ -114,10 +114,10 @@ const APP: FC<()> = |(cx, _)| { struct CalculatorKeyProps<'a> { name: &'static str, onclick: &'a dyn Fn(MouseEvent), - children: ScopeChildren<'a>, + children: Element, } -fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps<'a>>) -> Element { +fn CalculatorKey<'a>(cx: Context, props: &CalculatorKeyProps) -> Element { rsx!(cx, button { class: "calculator-key {props.name}" onclick: {props.onclick} diff --git a/examples/core/alternative.rs b/examples/core/alternative.rs index 3f2fa9790..6c20e2adf 100644 --- a/examples/core/alternative.rs +++ b/examples/core/alternative.rs @@ -9,7 +9,7 @@ fn main() { println!("{}", dom); } -pub static EXAMPLE: FC<()> = |(cx, _)| { +pub static EXAMPLE: FC<()> = |cx, _| { let list = (0..10).map(|_f| { rsx! { "{_f}" diff --git a/examples/core/syntax.rs b/examples/core/syntax.rs index 5eec9d181..9a21bc7e6 100644 --- a/examples/core/syntax.rs +++ b/examples/core/syntax.rs @@ -31,7 +31,7 @@ fn html_usage() { // let p = rsx!(div { {f} }); } -static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!")); +static App2: FC<()> = |cx, _| cx.render(rsx!("hello world!")); static App: FC<()> = |cx, props| { let name = cx.use_state(|| 0); diff --git a/examples/crm.rs b/examples/crm.rs index e0c873221..3de5b7e3a 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -19,7 +19,7 @@ pub struct Client { pub description: String, } -static App: FC<()> = |(cx, _)| { +static App: FC<()> = |cx, _| { let mut clients = use_ref(cx, || vec![] as Vec); let mut scene = use_state(cx, || Scene::ClientsList); diff --git a/examples/desktop/crm.rs b/examples/desktop/crm.rs index efb7efed2..8cc5b2988 100644 --- a/examples/desktop/crm.rs +++ b/examples/desktop/crm.rs @@ -21,7 +21,7 @@ pub struct Client { pub description: String, } -static App: FC<()> = |(cx, _)| { +static App: FC<()> = |cx, _| { let mut scene = use_state(cx, || Scene::ClientsList); let clients = use_ref(cx, || vec![] as Vec); diff --git a/examples/desktop/todomvc.rs b/examples/desktop/todomvc.rs index 6e85b87c6..d8edf40e4 100644 --- a/examples/desktop/todomvc.rs +++ b/examples/desktop/todomvc.rs @@ -32,7 +32,7 @@ pub struct TodoItem { } pub type Todos = HashMap; -pub static App: FC<()> = |(cx, _)| { +pub static App: FC<()> = |cx, _| { // Share our TodoList to the todos themselves use_provide_state(cx, Todos::new); diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index 6dd9a9073..782081c41 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -1,50 +1,38 @@ -use dioxus::{events::MouseEvent, prelude::*}; -use fxhash::FxBuildHasher; -use std::rc::Rc; +use dioxus::prelude::*; +use rand::prelude::*; fn main() { - dioxus::desktop::launch(App, |c| c); + dioxus::web::launch(App, |c| c); + // dioxus::desktop::launch(App, |c| c); } -// We use a special immutable hashmap to make hashmap operations efficient -type RowList = im_rc::HashMap, FxBuildHasher>; +#[derive(Clone, PartialEq)] +struct Label { + key: usize, + labels: [&'static str; 3], +} -static App: FC<()> = |(cx, _props)| { - let mut items = use_state(cx, || RowList::default()); - - let create_rendered_rows = move |from, num| move |_| items.set(create_row_list(from, num)); - - let mut append_1_000_rows = - move |_| items.set(create_row_list(items.len(), 1000).union((*items).clone())); - - let update_every_10th_row = move |_| { - let mut new_items = (*items).clone(); - let mut small_rng = SmallRng::from_entropy(); - new_items.iter_mut().step_by(10).for_each(|(_, val)| { - *val = create_new_row_label(&mut String::with_capacity(30), &mut small_rng) - }); - items.set(new_items); - }; - let clear_rows = move |_| items.set(RowList::default()); - - let swap_rows = move |_| { - // this looks a bit ugly because we're using a hashmap instead of a vec - if items.len() > 998 { - let mut new_items = (*items).clone(); - let a = new_items.get(&0).unwrap().clone(); - *new_items.get_mut(&0).unwrap() = new_items.get(&998).unwrap().clone(); - *new_items.get_mut(&998).unwrap() = a; - items.set(new_items); +impl Label { + fn new_list(num: usize) -> Vec { + let mut rng = SmallRng::from_entropy(); + let mut labels = Vec::with_capacity(num); + for _ in 0..num { + labels.push(Label { + key: 0, + labels: [ + ADJECTIVES.choose(&mut rng).unwrap(), + COLOURS.choose(&mut rng).unwrap(), + NOUNS.choose(&mut rng).unwrap(), + ], + }); } - }; + labels + } +} - let rows = items.iter().map(|(key, value)| { - rsx!(Row { - key: "{key}", - row_id: *key as usize, - label: value.clone(), - }) - }); +static App: FC<()> = |cx, _props| { + let mut items = use_ref(cx, || vec![]); + let mut selected = use_state(cx, || None); cx.render(rsx! { div { class: "container" @@ -53,22 +41,49 @@ static App: FC<()> = |(cx, _props)| { div { class: "col-md-6", h1 { "Dioxus" } } div { class: "col-md-6" div { class: "row" - ActionButton { name: "Create 1,000 rows", id: "run", onclick: {create_rendered_rows(0, 1_000)} } - ActionButton { name: "Create 10,000 rows", id: "runlots", onclick: {create_rendered_rows(0, 10_000)} } - ActionButton { name: "Append 1,000 rows", id: "add", onclick: {append_1_000_rows} } - ActionButton { name: "Update every 10th row", id: "update", onclick: {update_every_10th_row} } - ActionButton { name: "Clear", id: "clear", onclick: {clear_rows} } - ActionButton { name: "Swap rows", id: "swaprows", onclick: {swap_rows} } + ActionButton { name: "Create 1,000 rows", id: "run", + onclick: move || items.set(Label::new_list(1_000)), + } + ActionButton { name: "Create 10,000 rows", id: "runlots", + onclick: move || items.set(Label::new_list(10_000)), + } + ActionButton { name: "Append 1,000 rows", id: "add", + onclick: move || items.write().extend(Label::new_list(1_000)), + } + ActionButton { name: "Update every 10th row", id: "update", + onclick: move || items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"), + } + ActionButton { name: "Clear", id: "clear", + onclick: move || items.write().clear(), + } + ActionButton { name: "Swap rows", id: "swaprows", + onclick: move || items.write().swap(0, 998), + } } } } } table { tbody { - {rows} + {items.read().iter().enumerate().map(|(id, item)| { + let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""}; + rsx!(tr { class: "{is_in_danger}" + td { class:"col-md-1" } + td { class:"col-md-1", "{item.key}" } + td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), + a { class: "lbl", {item.labels} } + } + td { class: "col-md-1" + a { class: "remove", onclick: move |_| { items.write().remove(id); }, + span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + } + } + td { class: "col-md-6" } + }) + })} } } - span {} + // span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" } } }) }; @@ -77,59 +92,17 @@ static App: FC<()> = |(cx, _props)| { struct ActionButtonProps<'a> { name: &'static str, id: &'static str, - onclick: &'a dyn Fn(MouseEvent), + onclick: &'a dyn Fn(), } -fn ActionButton<'a>((cx, props): Scope<'a, ActionButtonProps>) -> Element<'a> { +fn ActionButton(cx: Context, props: &ActionButtonProps) -> Element { rsx!(cx, div { class: "col-sm-6 smallpad" - button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: {props.onclick}, + button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(), "{props.name}" } }) } -#[derive(PartialEq, Props)] -struct RowProps { - row_id: usize, - label: Rc, -} -fn Row((cx, props): Scope) -> Element { - rsx!(cx, tr { - td { class:"col-md-1", "{props.row_id}" } - td { class:"col-md-1", onclick: move |_| { /* run onselect */ } - a { class: "lbl", "{props.label}" } - } - td { class: "col-md-1" - a { class: "remove", onclick: move |_| {/* remove */} - span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } - } - } - td { class: "col-md-6" } - }) -} - -use rand::prelude::*; -fn create_new_row_label(label: &mut String, rng: &mut SmallRng) -> Rc { - label.push_str(ADJECTIVES.choose(rng).unwrap()); - label.push(' '); - label.push_str(COLOURS.choose(rng).unwrap()); - label.push(' '); - label.push_str(NOUNS.choose(rng).unwrap()); - Rc::from(label.as_ref()) -} - -fn create_row_list(from: usize, num: usize) -> RowList { - let mut small_rng = SmallRng::from_entropy(); - let mut buf = String::with_capacity(35); - (from..num + from) - .map(|f| { - let o = (f, create_new_row_label(&mut buf, &mut small_rng)); - buf.clear(); - o - }) - .collect::() -} - static ADJECTIVES: &[&str] = &[ "pretty", "large", @@ -167,3 +140,24 @@ static NOUNS: &[&str] = &[ "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", "pizza", "mouse", "keyboard", ]; + +// #[derive(PartialEq, Props)] +// struct RowProps<'a> { +// row_id: usize, +// label: &'a Label, +// } + +// fn Row(cx: Context, props: &RowProps) -> Element { +// rsx!(cx, tr { +// td { class:"col-md-1", "{props.row_id}" } +// td { class:"col-md-1", onclick: move |_| { /* run onselect */ } +// a { class: "lbl", {props.label.labels} } +// } +// td { class: "col-md-1" +// a { class: "remove", onclick: move |_| {/* remove */} +// span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } +// } +// } +// td { class: "col-md-6" } +// }) +// } diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs index df28fdcf1..221e8eb0d 100644 --- a/examples/pattern_reducer.rs +++ b/examples/pattern_reducer.rs @@ -11,7 +11,7 @@ fn main() { dioxus::desktop::launch(App, |c| c); } -pub static App: FC<()> = |(cx, _)| { +pub static App: FC<()> = |cx, _| { let state = use_state(cx, PlayerState::new); let is_playing = state.is_playing(); diff --git a/examples/web/basic.rs b/examples/web/basic.rs index 699305df2..4c4d52ce5 100644 --- a/examples/web/basic.rs +++ b/examples/web/basic.rs @@ -14,7 +14,7 @@ fn main() { dioxus_web::launch(APP, |c| c) } -static APP: FC<()> = |(cx, _)| { +static APP: FC<()> = |cx, _| { let mut count = use_state(cx, || 3); let content = use_state(cx, || String::from("h1")); let text_content = use_state(cx, || String::from("Hello, world!")); @@ -86,4 +86,4 @@ fn render_list(cx: Context, count: usize) -> Element { rsx!(cx, ul { {items} }) } -static CHILD: FC<()> = |(cx, _)| rsx!(cx, div {"hello child"}); +static CHILD: FC<()> = |cx, _| rsx!(cx, div {"hello child"}); diff --git a/examples/web/crm2.rs b/examples/web/crm2.rs index 0645f05fc..4dfe9db34 100644 --- a/examples/web/crm2.rs +++ b/examples/web/crm2.rs @@ -28,7 +28,7 @@ pub struct Client { pub description: String, } -static App: FC<()> = |(cx, _)| { +static App: FC<()> = |cx, _| { let scene = use_state(cx, || Scene::ClientsList); let clients = use_ref(cx, || vec![] as Vec); diff --git a/notes/Parity.md b/notes/Parity.md index 9842b7963..5cc0851c9 100644 --- a/notes/Parity.md +++ b/notes/Parity.md @@ -12,7 +12,7 @@ https://github.com/rustwasm/gloo For example, resize observer would function like this: ```rust -pub static Example: FC<()> = |(cx, props)|{ +pub static Example: FC<()> = |cx, props|{ let observer = use_resize_observer(); cx.render(rsx!( diff --git a/notes/SOLVEDPROBLEMS.md b/notes/SOLVEDPROBLEMS.md index d7bc60344..3c42094ee 100644 --- a/notes/SOLVEDPROBLEMS.md +++ b/notes/SOLVEDPROBLEMS.md @@ -153,13 +153,13 @@ Notice that LiveComponent receivers (the client-side interpretation of a LiveCom The `VNodeTree` type is a very special type that allows VNodes to be created using a pluggable allocator. The html! macro creates something that looks like: ```rust -pub static Example: FC<()> = |(cx, props)|{ +pub static Example: FC<()> = |cx, props|{ html! {
"blah"
} }; // expands to... -pub static Example: FC<()> = |(cx, props)|{ +pub static Example: FC<()> = |cx, props|{ // This function converts a Fn(allocator) -> DomTree closure to a VNode struct that will later be evaluated. html_macro_to_vnodetree(move |allocator| { let mut node0 = allocator.alloc(VElement::div); @@ -313,7 +313,7 @@ Here's how react does it: Any "dirty" node causes an entire subtree render. Calling "setState" at the very top will cascade all the way down. This is particularly bad for this component design: ```rust -static APP: FC<()> = |(cx, props)|{ +static APP: FC<()> = |cx, props|{ let title = use_context(Title); cx.render(html!{
@@ -334,7 +334,7 @@ static APP: FC<()> = |(cx, props)|{
}) }; -static HEAVY_LIST: FC<()> = |(cx, props)|{ +static HEAVY_LIST: FC<()> = |cx, props|{ cx.render({ {0.100.map(i => )} }) @@ -378,7 +378,7 @@ struct Props { } -static Component: FC = |(cx, props)|{ +static Component: FC = |cx, props|{ } ``` diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index a218ccc4d..6e754a163 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -30,7 +30,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token /// /// ## Complete Reference Guide: /// ``` -/// const Example: FC<()> = |(cx, props)|{ +/// const Example: FC<()> = |cx, props|{ /// let formatting = "formatting!"; /// let formatting_tuple = ("a", "b"); /// let lazy_fmt = format_args!("lazily formatted text"); diff --git a/packages/core-macro/src/rsxtemplate.rs b/packages/core-macro/src/rsxtemplate.rs index e228929cf..1f712997d 100644 --- a/packages/core-macro/src/rsxtemplate.rs +++ b/packages/core-macro/src/rsxtemplate.rs @@ -72,7 +72,7 @@ impl ToTokens for RsxTemplate { // // create a lazy tree that accepts a bump allocator // let final_tokens = quote! { - // dioxus::prelude::LazyNodes::new(move |(cx, props)|{ + // dioxus::prelude::LazyNodes::new(move |cx, props|{ // let bump = &cx.bump(); // #new_toks diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 8619c7e71..7ea455d3c 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -49,8 +49,7 @@ rand = { version = "0.8.4", features = ["small_rng"] } simple_logger = "1.13.0" dioxus-core-macro = { path = "../core-macro", version = "0.1.2" } dioxus-hooks = { path = "../hooks" } -# async-std = { version = "1.9.0", features = ["attributes"] } -# criterion = "0.3.5" +criterion = "0.3.5" [features] default = [] @@ -64,3 +63,11 @@ harness = false [[bench]] name = "jsframework" harness = false + +[[example]] +name = "rows" +path = "./examples/rows.rs" + + +[profile.release] +debug = true diff --git a/packages/core/benches/jsframework.rs b/packages/core/benches/jsframework.rs index a36d4a1e5..4f09f0202 100644 --- a/packages/core/benches/jsframework.rs +++ b/packages/core/benches/jsframework.rs @@ -19,7 +19,6 @@ use dioxus_core_macro::*; use dioxus_html as dioxus_elements; use rand::prelude::*; -fn main() {} criterion_group!(mbenches, create_rows); criterion_main!(mbenches); @@ -28,18 +27,14 @@ fn create_rows(c: &mut Criterion) { let mut rng = SmallRng::from_entropy(); let rows = (0..10_000_usize).map(|f| { let label = Label::new(&mut rng); - rsx! { - Row { - row_id: f, - label: label - } - } + rsx!(Row { + row_id: f, + label: label + }) }); - cx.render(rsx! { - table { - tbody { - {rows} - } + rsx!(cx, table { + tbody { + {rows} } }) }; diff --git a/packages/core/examples/rows.rs b/packages/core/examples/rows.rs new file mode 100644 index 000000000..7506c418f --- /dev/null +++ b/packages/core/examples/rows.rs @@ -0,0 +1,120 @@ +#![allow(non_snake_case, non_upper_case_globals)] +//! This benchmark tests just the overhead of Dioxus itself. +//! +//! For the JS Framework Benchmark, both the framework and the browser is benchmarked together. Dioxus prepares changes +//! to be made, but the change application phase will be just as performant as the vanilla wasm_bindgen code. In essence, +//! we are measuring the overhead of Dioxus, not the performance of the "apply" phase. +//! +//! On my MBP 2019: +//! - Dioxus takes 3ms to create 1_000 rows +//! - Dioxus takes 30ms to create 10_000 rows +//! +//! As pure "overhead", these are amazing good numbers, mostly slowed down by hitting the global allocator. +//! These numbers don't represent Dioxus with the heuristic engine installed, so I assume it'll be even faster. + +use criterion::{criterion_group, criterion_main, Criterion}; +use dioxus_core as dioxus; +use dioxus_core::prelude::*; +use dioxus_core_macro::*; +use dioxus_html as dioxus_elements; +use rand::prelude::*; + +fn main() { + static App: FC<()> = |cx, _| { + let mut rng = SmallRng::from_entropy(); + let rows = (0..10_000_usize).map(|f| { + let label = Label::new(&mut rng); + rsx! { + Row { + row_id: f, + label: label + } + } + }); + cx.render(rsx! { + table { + tbody { + {rows} + } + } + }) + }; + + let mut dom = VirtualDom::new(App); + let g = dom.rebuild(); + assert!(g.edits.len() > 1); +} + +#[derive(PartialEq, Props)] +struct RowProps { + row_id: usize, + label: Label, +} +fn Row(cx: Context, props: &RowProps) -> Element { + let [adj, col, noun] = props.label.0; + cx.render(rsx! { + tr { + td { class:"col-md-1", "{props.row_id}" } + td { class:"col-md-1", onclick: move |_| { /* run onselect */ } + a { class: "lbl", "{adj}" "{col}" "{noun}" } + } + td { class: "col-md-1" + a { class: "remove", onclick: move |_| {/* remove */} + span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + } + } + td { class: "col-md-6" } + } + }) +} + +#[derive(PartialEq)] +struct Label([&'static str; 3]); + +impl Label { + fn new(rng: &mut SmallRng) -> Self { + Label([ + ADJECTIVES.choose(rng).unwrap(), + COLOURS.choose(rng).unwrap(), + NOUNS.choose(rng).unwrap(), + ]) + } +} + +static ADJECTIVES: &[&str] = &[ + "pretty", + "large", + "big", + "small", + "tall", + "short", + "long", + "handsome", + "plain", + "quaint", + "clean", + "elegant", + "easy", + "angry", + "crazy", + "helpful", + "mushy", + "odd", + "unsightly", + "adorable", + "important", + "inexpensive", + "cheap", + "expensive", + "fancy", +]; + +static COLOURS: &[&str] = &[ + "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", + "orange", +]; + +static NOUNS: &[&str] = &[ + "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", + "pizza", "mouse", "keyboard", +]; diff --git a/packages/core/flamegraph.svg b/packages/core/flamegraph.svg new file mode 100644 index 000000000..0699a198c --- /dev/null +++ b/packages/core/flamegraph.svg @@ -0,0 +1,412 @@ +Flame Graph Reset ZoomSearch jsframework-a8f4acf5955e8e7f`core::ptr::drop_in_place<dioxus_core::virtual_dom::VirtualDom> (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`core::ptr::drop_in_place<alloc::boxed::Box<dioxus_core::scopearena::ScopeArena>> (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::ops::drop::Drop>::drop (1 samples, 1.82%)j..libsystem_malloc.dylib`free_medium (1 samples, 1.82%)l..libsystem_kernel.dylib`madvise (1 samples, 1.82%)l..libsystem_malloc.dylib`_malloc_zone_malloc (2 samples, 3.64%)libs..libsystem_malloc.dylib`szone_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`tiny_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`tiny_malloc_from_free_list (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_free_list_add_ptr (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_element_node (7 samples, 12.73%)jsframework-a8f4acf..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (3 samples, 5.45%)jsframe..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::finish_grow (3 samples, 5.45%)jsframe..libsystem_malloc.dylib`realloc (1 samples, 1.82%)l..libsystem_malloc.dylib`malloc_zone_realloc (1 samples, 1.82%)l..libsystem_malloc.dylib`szone_realloc (1 samples, 1.82%)l..libsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::fin_head (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::default::Default>::default (4 samples, 7.27%)jsframewor..jsframework-a8f4acf5955e8e7f`<bumpalo::Bump as core::ops::drop::Drop>::drop (1 samples, 1.82%)j..libsystem_malloc.dylib`free_tiny (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_free_no_lock (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_free_list_add_ptr (1 samples, 1.82%)l..libsystem_malloc.dylib`malloc_zone_realloc (1 samples, 1.82%)l..libsystem_platform.dylib`DYLD-STUB$$_platform_memmove (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::RawVec<T,A>::reserve::do_reserve_and_handle (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`alloc::raw_vec::finish_grow (2 samples, 3.64%)jsfr..libsystem_malloc.dylib`realloc (2 samples, 3.64%)libs..libsystem_malloc.dylib`szone_size (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::with_capacity (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::with_capacity (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::scope::BumpFrame::new (3 samples, 5.45%)jsframe..libsystem_malloc.dylib`_malloc_zone_malloc (1 samples, 1.82%)l..libsystem_malloc.dylib`szone_malloc_should_clear (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_malloc_should_clear (1 samples, 1.82%)l..libsystem_malloc.dylib`tiny_malloc_from_free_list (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::new_with_key (14 samples, 25.45%)jsframework-a8f4acf5955e8e7f`dioxus_core:..jsframework-a8f4acf5955e8e7f`hashbrown::raw::RawTable<T,A>::insert (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::element (6 samples, 10.91%)jsframework-a8f4..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::alloc_layout_slow (6 samples, 10.91%)jsframework-a8f4..libsystem_malloc.dylib`_malloc_zone_malloc (2 samples, 3.64%)libs..libsystem_malloc.dylib`szone_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`tiny_malloc_should_clear (2 samples, 3.64%)libs..libsystem_malloc.dylib`set_tiny_meta_header_in_use (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::raw_element (12 samples, 21.82%)jsframework-a8f4acf5955e8e7f`dioxu..jsframework-a8f4acf5955e8e7f`bumpalo::Bump::alloc_layout_slow (12 samples, 21.82%)jsframework-a8f4acf5955e8e7f`bumpa..libsystem_malloc.dylib`_malloc_zone_malloc (1 samples, 1.82%)l..libsystem_malloc.dylib`szone_malloc_should_clear (1 samples, 1.82%)l..libsystem_malloc.dylib`small_malloc_should_clear (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`<&T as core::fmt::Display>::fmt (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`bumpalo::collections::string::String::into_bump_str (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::text (3 samples, 5.45%)jsframe..jsframework-a8f4acf5955e8e7f`core::fmt::write (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<&mut W as core::fmt::Write>::write_str (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::call (22 samples, 40.00%)jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::c..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::new::_{{closure}} (22 samples, 40.00%)jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::n..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::empty_cell (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::scope::Scope::render (23 samples, 41.82%)jsframework-a8f4acf5955e8e7f`dioxus_core::scope::Scope::renderlibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_node (40 samples, 72.73%)jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::create_nodejsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::run_scope (24 samples, 43.64%)jsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::run_s..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::component::_{{closure}} (24 samples, 43.64%)jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::component..jsframework-a8f4acf5955e8e7f`jsframework::Row (24 samples, 43.64%)jsframework-a8f4acf5955e8e7f`jsframework::Rowlibsystem_platform.dylib`_platform_memmove$VARIANT$Haswell (1 samples, 1.82%)l..jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::work (48 samples, 87.27%)jsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::workjsframework-a8f4acf5955e8e7f`dioxus_core::diff::DiffState::mount (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`<core::option::Option<dioxus_core::lazynodes::LazyNodes> as dioxus_core::nodes::IntoVNode>::into_vnode (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::call (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::new::_{{closure}} (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::component (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`main (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`mainjsframework-a8f4acf5955e8e7f`std::rt::lang_start_internal (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`std::rt::lang_start_internaljsframework-a8f4acf5955e8e7f`std::rt::lang_start::_{{closure}} (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`std::rt::lang_start::_{{closure}}jsframework-a8f4acf5955e8e7f`std::sys_common::backtrace::__rust_begin_short_backtrace (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`std::sys_common::backtrace::__rust_begin_short_backtracejsframework-a8f4acf5955e8e7f`jsframework::main (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`jsframework::mainjsframework-a8f4acf5955e8e7f`criterion::Criterion<M>::bench_function (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::Criterion<M>::bench_functionjsframework-a8f4acf5955e8e7f`criterion::benchmark_group::BenchmarkGroup<M>::bench_function (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::benchmark_group::BenchmarkGroup<M>::bench_functionjsframework-a8f4acf5955e8e7f`criterion::routine::Routine::test (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::routine::Routine::testjsframework-a8f4acf5955e8e7f`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`<alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iterjsframework-a8f4acf5955e8e7f`criterion::bencher::Bencher<M>::iter (51 samples, 92.73%)jsframework-a8f4acf5955e8e7f`criterion::bencher::Bencher<M>::iterjsframework-a8f4acf5955e8e7f`dioxus_core::virtual_dom::VirtualDom::rebuild (50 samples, 90.91%)jsframework-a8f4acf5955e8e7f`dioxus_core::virtual_dom::VirtualDom::rebuildjsframework-a8f4acf5955e8e7f`dioxus_core::scopearena::ScopeArena::run_scope (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::virtual_dom::VirtualDom::new_with_props_and_scheduler::_{{closure}} (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`core::ops::function::FnOnce::call_once (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::scope::Scope::render (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::call (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::lazynodes::LazyNodes::new::_{{closure}} (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`dioxus_core::nodes::NodeFactory::fragment_from_iter (2 samples, 3.64%)jsfr..jsframework-a8f4acf5955e8e7f`core::ops::function::impls::_<impl core::ops::function::FnOnce<A> for &mut F>::call_once (1 samples, 1.82%)j..jsframework-a8f4acf5955e8e7f`rand::rng::Rng::gen_range (1 samples, 1.82%)j..all (55 samples, 100%)0x1 (55 samples, 100.00%)0x1libdyld.dylib`start (55 samples, 100.00%)libdyld.dylib`startlibsystem_kernel.dylib`__exit (4 samples, 7.27%)libsystem_.. \ No newline at end of file diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 8c6831ca3..f4bebc8da 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -434,8 +434,15 @@ impl<'bump> DiffState<'bump> { } if !children.is_empty() { - self.stack.create_children(children, MountType::Append); + if children.len() == 1 { + if let VNode::Text(vtext) = children[0] { + self.mutations.set_text(vtext.text, real_id.as_u64()); + return; + } + } } + + self.stack.create_children(children, MountType::Append); } fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) { @@ -645,17 +652,59 @@ impl<'bump> DiffState<'bump> { } } - if old.children.is_empty() && !new.children.is_empty() { - self.mutations.edits.push(PushRoot { - root: root.as_u64(), - }); - self.stack.element_stack.push(root); - self.stack.instructions.push(DiffInstruction::PopElement); - self.stack.create_children(new.children, MountType::Append); - } else { - self.stack.element_stack.push(root); - self.stack.instructions.push(DiffInstruction::PopElement); - self.diff_children(old.children, new.children); + match (old.children.len(), new.children.len()) { + (0, 0) => {} + (1, 1) => { + let old1 = &old.children[0]; + let new1 = &new.children[0]; + + match (old1, new1) { + (VNode::Text(old_text), VNode::Text(new_text)) => { + if old_text.text != new_text.text { + self.mutations.set_text(new_text.text, root.as_u64()); + } + } + (VNode::Text(old_text), _) => { + self.stack.element_stack.push(root); + self.stack.instructions.push(DiffInstruction::PopElement); + self.stack.create_node(new1, MountType::Append); + } + (_, VNode::Text(new_text)) => { + self.remove_nodes([old1], false); + self.mutations.set_text(new_text.text, root.as_u64()); + } + _ => { + self.stack.element_stack.push(root); + self.stack.instructions.push(DiffInstruction::PopElement); + self.diff_children(old.children, new.children); + } + } + } + (0, 1) => { + if let VNode::Text(text) = &new.children[0] { + self.mutations.set_text(text.text, root.as_u64()); + } else { + self.stack.element_stack.push(root); + self.stack.instructions.push(DiffInstruction::PopElement); + } + } + (0, _) => { + self.mutations.edits.push(PushRoot { + root: root.as_u64(), + }); + self.stack.element_stack.push(root); + self.stack.instructions.push(DiffInstruction::PopElement); + self.stack.create_children(new.children, MountType::Append); + } + (_, 0) => { + self.remove_nodes(old.children, false); + self.mutations.set_text("", root.as_u64()); + } + (_, _) => { + self.stack.element_stack.push(root); + self.stack.instructions.push(DiffInstruction::PopElement); + self.diff_children(old.children, new.children); + } } } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 4a2eabd68..398c8f203 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -196,7 +196,7 @@ impl Scope { let chan = self.sender.clone(); let id = self.scope_id(); Rc::new(move || { - log::debug!("set on channel an update for scope {:?}", id); + // log::debug!("set on channel an update for scope {:?}", id); let _ = chan.unbounded_send(SchedulerMsg::Immediate(id)); }) } diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index dd1f555cd..ea6f0c5be 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -92,15 +92,15 @@ impl ScopeArena { let new_scope_id = ScopeId(self.scope_counter.get()); self.scope_counter.set(self.scope_counter.get() + 1); - log::debug!("new scope {:?} with parent {:?}", new_scope_id, container); + // log::debug!("new scope {:?} with parent {:?}", new_scope_id, container); if let Some(old_scope) = self.free_scopes.borrow_mut().pop() { let scope = unsafe { &mut *old_scope }; - log::debug!( - "reusing scope {:?} as {:?}", - scope.our_arena_idx, - new_scope_id - ); + // log::debug!( + // "reusing scope {:?} as {:?}", + // scope.our_arena_idx, + // new_scope_id + // ); scope.caller = caller; scope.parent_scope = parent_scope; @@ -202,7 +202,7 @@ impl ScopeArena { pub fn try_remove(&self, id: &ScopeId) -> Option<()> { self.ensure_drop_safety(id); - log::debug!("removing scope {:?}", id); + // log::debug!("removing scope {:?}", id); // Safety: // - ensure_drop_safety ensures that no references to this scope are in use @@ -311,7 +311,7 @@ impl ScopeArena { let scope = unsafe { &mut *self.get_scope_mut(id).expect("could not find scope") }; - log::debug!("found scope, about to run: {:?}", id); + // log::debug!("found scope, about to run: {:?}", id); // Safety: // - We dropped the listeners, so no more &mut T can be used while these are held diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index fe1fcac23..fc85ef0a0 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -352,7 +352,7 @@ impl VirtualDom { let mut committed_mutations = vec![]; while !self.dirty_scopes.is_empty() { - log::debug!("working with deadline"); + // log::debug!("working with deadline"); let scopes = &self.scopes; let mut diff_state = DiffState::new(scopes); diff --git a/packages/desktop/README.md b/packages/desktop/README.md index 2888ae223..0c1663488 100644 --- a/packages/desktop/README.md +++ b/packages/desktop/README.md @@ -7,7 +7,7 @@ fn main() { dioxus::desktop::launch(App, |c| c) } -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { let (count, set_count) = use_state(cx, || 0); cx.render(rsx!( @@ -34,7 +34,7 @@ Window management, system trays, notifications, and other desktop-related functi Managing windows is done by simply rendering content into a `WebviewWindow` component. ```rust -static App: FC<()> = |(cx, props)| { +static App: FC<()> = |cx, props| { rsx!(cx, WebviewWindow { "hello world" } ) } ``` @@ -46,7 +46,7 @@ Notifications also use a declarative approach. Sending a notification has never The api has been somewhat modeled after https://github.com/mikaelbr/node-notifier ```rust -static Notifications: FC<()> = |(cx, props)| { +static Notifications: FC<()> = |cx, props| { cx.render(rsx!( Notification { title: "title" @@ -78,7 +78,7 @@ static Notifications: FC<()> = |(cx, props)| { Dioxus Desktop supports app trays, which can be built with native menu groups or with a custom window. ```rust -static Tray: FC<()> = |(cx, props)| { +static Tray: FC<()> = |cx, props| { cx.render(rsx!( GlobalTray { MenuGroup { @@ -90,7 +90,7 @@ static Tray: FC<()> = |(cx, props)| { }; // using a builder -static Tray: FC<()> = |(cx, props)| { +static Tray: FC<()> = |cx, props| { let menu = MenuGroup::builder(cx) .with_items([ MenuGroupItem::builder() @@ -107,7 +107,7 @@ static Tray: FC<()> = |(cx, props)| { } // or with a custom window -static Tray: FC<()> = |(cx, props)| { +static Tray: FC<()> = |cx, props| { rsx!(cx, GlobalTray { div { "custom buttons here" } }) }; ``` @@ -116,7 +116,7 @@ static Tray: FC<()> = |(cx, props)| { Declaring menus is convenient and cross-platform. ```rust -static Menu: FC<()> = |(cx, props)| { +static Menu: FC<()> = |cx, props| { cx.render(rsx!( MenuBarMajorItem { title: "File" MenuGroup { diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index 428a3b8d8..df3bd6436 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -19,10 +19,9 @@ -
+
- + diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 817e20499..1e4290590 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -90,6 +90,8 @@ pub fn run( let edit_queue = Arc::new(RwLock::new(VecDeque::new())); let is_ready: Arc = Default::default(); + let mut frame = 0; + event_loop.run(move |window_event, event_loop, control_flow| { *control_flow = ControlFlow::Wait; @@ -145,6 +147,9 @@ pub fn run( view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit)) .unwrap(); } + } else { + println!("waiting for onload {:?}", frame); + frame += 1; } } Event::Resumed => {} @@ -258,6 +263,7 @@ fn create_webview( // always driven through eval None }) + // .with_initialization_script(include_str!("./index.js")) // Any content that that uses the `wry://` scheme will be shuttled through this handler as a "special case" // For now, we only serve two pieces of content which get included as bytes into the final binary. .with_custom_protocol("wry".into(), move |request| { diff --git a/packages/hooks/src/usecollection.rs b/packages/hooks/src/usecollection.rs index c82277291..d0b3b71e7 100644 --- a/packages/hooks/src/usecollection.rs +++ b/packages/hooks/src/usecollection.rs @@ -36,7 +36,7 @@ uses the same memoization on top of the use_context API. Here's a fully-functional todo app using the use_map API: ```rust -static TodoList: FC<()> = |(cx, props)|{ +static TodoList: FC<()> = |cx, props|{ let todos = use_map(cx, || HashMap::new()); let input = use_ref(|| None); diff --git a/packages/hooks/src/useref.rs b/packages/hooks/src/useref.rs index 75ae9c82b..9ebfbfebf 100644 --- a/packages/hooks/src/useref.rs +++ b/packages/hooks/src/useref.rs @@ -33,6 +33,11 @@ impl<'a, T> UseRef<'a, T> { self.inner.value.borrow() } + pub fn set(&self, new: T) { + *self.inner.value.borrow_mut() = new; + self.needs_update(); + } + pub fn read_write(&self) -> (Ref<'_, T>, &Self) { (self.read(), self) } diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 83db764ee..fb8d3e4af 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -35,7 +35,7 @@ use std::{ /// /// Usage: /// ```ignore -/// const Example: FC<()> = |(cx, props)|{ +/// const Example: FC<()> = |cx, props|{ /// let counter = use_state(cx, || 0); /// let increment = |_| counter += 1; /// let decrement = |_| counter += 1; diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index 38797ed6d..db4b3442c 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -717,8 +717,7 @@ builder_constructors! { nonce: Nonce, src: Uri, text: String, - r#async: Bool, - r#type: String, // TODO could be an enum + }; @@ -823,7 +822,6 @@ builder_constructors! { formnovalidate: Bool, formtarget: Target, name: Id, - r#type: ButtonType, value: String, }; @@ -1064,6 +1062,23 @@ impl input { volatile attributes */ +impl script { + // r#async: Bool, + // r#type: String, // TODO could be an enum + pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr("type", val, None, false) + } + pub fn r#script<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr("script", val, None, false) + } +} + +impl button { + pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr("type", val, None, false) + } +} + impl select { pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { cx.attr("value", val, None, true) diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 93a811a76..3f6992374 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -37,3 +37,9 @@ default = ["web"] web = ["web-sys"] desktop = [] mobile = [] + +[dev-dependencies] +console_error_panic_hook = "0.1.7" +dioxus-web = { path = "../web" } +log = "0.4.14" +wasm-logger = "0.2.0" diff --git a/packages/router/README.md b/packages/router/README.md new file mode 100644 index 000000000..902bfdb17 --- /dev/null +++ b/packages/router/README.md @@ -0,0 +1,40 @@ +# Router hook for Dioxus apps + +Dioxus-router provides a use_router hook that returns a different value depending on the route. +The router is generic over any value, however it makes sense to return a different set of VNodes +and feed them into the App's return VNodes. + +Using the router should feel similar to tide's routing framework where an "address" book is assembled at the head of the app. + +Here's an example of how to use the router hook: + +```rust +#[derive(Clone, Routable)] +enum AppRoute { + Home, + Posts, + NotFound +} + +static App: FC<()> = |cx, props| { + let route = use_router(cx, AppRoute::parse); + + match route { + AppRoute::Home => rsx!(cx, Home {}) + AppRoute::Posts => rsx!(cx, Posts {}) + AppRoute::Notfound => rsx!(cx, Notfound {}) + } +}; +``` + +Adding links into your app: + +```rust +static Leaf: FC<()> = |cx, props| { + rsx!(cx, div { + Link { to: AppRoute::Home } + }) +} +``` + +Currently, the router is only supported in a web environment, but we plan to add 1st-party support via the context API when new renderers are available. diff --git a/packages/router/examples/simple.rs b/packages/router/examples/simple.rs new file mode 100644 index 000000000..43ddaa2d2 --- /dev/null +++ b/packages/router/examples/simple.rs @@ -0,0 +1,45 @@ +use dioxus_core::prelude::*; +use dioxus_core_macro::*; +use dioxus_html as dioxus_elements; +use dioxus_router::*; + +fn main() { + console_error_panic_hook::set_once(); + dioxus_web::launch(App, |c| c); +} + +#[derive(Clone, Debug, PartialEq)] +enum Route { + Home, + About, + NotFound, +} + +static App: FC<()> = |cx, props| { + let route = use_router(cx, Route::parse); + + match route { + Route::Home => rsx!(cx, div { "Home" }), + Route::About => rsx!(cx, div { "About" }), + Route::NotFound => rsx!(cx, div { "NotFound" }), + } +}; + +impl ToString for Route { + fn to_string(&self) -> String { + match self { + Route::Home => "/".to_string(), + Route::About => "/about".to_string(), + Route::NotFound => "/404".to_string(), + } + } +} +impl Route { + fn parse(s: &str) -> Self { + match s { + "/" => Route::Home, + "/about" => Route::About, + _ => Route::NotFound, + } + } +} diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 3aee5a684..948cb4eff 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -11,14 +11,18 @@ use web_sys::Event; use crate::utils::fetch_base_url; +pub trait Routable: 'static + Send + Clone + ToString + PartialEq {} +impl Routable for T where T: 'static + Send + Clone + ToString + PartialEq {} + pub struct RouterService { - history: RefCell>, + historic_routes: RefCell>, + history_service: web_sys::History, base_ur: RefCell>, } impl RouterService { fn push_route(&self, r: R) { - self.history.borrow_mut().push(r); + self.historic_routes.borrow_mut().push(r); } fn get_current_route(&self) -> &str { @@ -61,7 +65,7 @@ impl RouterService { /// This hould only be used once per app /// /// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine -pub fn use_router(cx: Context, cfg: impl FnOnce(&str) -> R) -> Option<&R> { +pub fn use_router(cx: Context, parse: impl FnMut(&str) -> R) -> &R { // for the web, attach to the history api cx.use_hook( |f| { @@ -71,10 +75,13 @@ pub fn use_router(cx: Context, cfg: impl FnOnce(&str) -> R) -> Opti let base_url = fetch_base_url(); let service: RouterService = RouterService { - history: RefCell::new(vec![]), + historic_routes: RefCell::new(vec![]), + history_service: web_sys::window().unwrap().history().expect("no history"), base_ur: RefCell::new(base_url), }; + // service.history_service.push_state(data, title); + cx.provide_state(service); let regenerate = cx.schedule_update(); @@ -105,30 +112,13 @@ pub struct LinkProps { children: Element, } -pub fn Link<'a, R: Routable>(cx: Context, props: &LinkProps) -> Element { +pub fn Link(cx: Context, props: &LinkProps) -> Element { let service = use_router_service::(cx)?; cx.render(rsx! { a { - href: format_args!("{}", props.to.to_path()), + href: format_args!("{}", props.to.to_string()), onclick: move |_| service.push_route(props.to.clone()), {&props.children}, } }) } - -pub trait Routable: Sized + Clone + 'static { - /// Converts path to an instance of the routes enum. - fn from_path(path: &str, params: &HashMap<&str, &str>) -> Option; - - /// Converts the route to a string that can passed to the history API. - fn to_path(&self) -> String; - - /// Lists all the available routes - fn routes() -> Vec<&'static str>; - - /// The route to redirect to on 404 - fn not_found_route() -> Option; - - /// Match a route based on the path - fn recognize(pathname: &str) -> Option; -} diff --git a/packages/ssr/README.md b/packages/ssr/README.md index 7efb5ca92..4d47b426d 100644 --- a/packages/ssr/README.md +++ b/packages/ssr/README.md @@ -5,7 +5,7 @@ Render a Dioxus VirtualDOM to a string. ```rust // Our app: -const App: FC<()> = |(cx, props)| rsx!(cx, div {"hello world!"}); +const App: FC<()> = |cx, props| rsx!(cx, div {"hello world!"}); // Build the virtualdom from our app let mut vdom = VirtualDOM::new(App); diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index 4fc696c1a..a04271bbc 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -110,7 +110,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option { /// /// ## Example /// ```ignore -/// static App: FC<()> = |(cx, props)|cx.render(rsx!(div { "hello world" })); +/// static App: FC<()> = |cx, props|cx.render(rsx!(div { "hello world" })); /// let mut vdom = VirtualDom::new(App); /// vdom.rebuild(); /// diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 05c8ce4e3..043e4e7d8 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -11,10 +11,17 @@ license = "MIT/Apache-2.0" dioxus-core = { path = "../core", version = "0.1.2" } dioxus-html = { path = "../html" } js-sys = "0.3" +# wasm-bindgen-shared = { path = "../../../Tinkering/wasm-bindgen/crates/shared" } +# wasm-bindgen-macro-support = { path = "../../../Tinkering/wasm-bindgen/crates/macro-support" } +# wasm-bindgen = { features = [ + + wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] } +# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] } +# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] } lazy_static = "1.4.0" wasm-bindgen-futures = "0.4.20" -log = "0.4.14" +log = { version = "0.4.14", features = ["release_max_level_off"] } fxhash = "0.2.1" wasm-logger = "0.2.0" console_error_panic_hook = "0.1.6" @@ -24,6 +31,11 @@ async-channel = "1.6.1" anyhow = "1.0" gloo-timers = { version = "0.2.1", features = ["futures"] } futures-util = "0.3.15" +smallstr = "0.2.0" + +[patch.crates-io] +wasm-bindgen = { path = "../../../Tinkering/wasm-bindgen/" } + [dependencies.web-sys] version = "0.3.51" @@ -65,6 +77,7 @@ features = [ "IdleDeadline", ] + [lib] crate-type = ["cdylib", "rlib"] @@ -76,7 +89,12 @@ serde = { version = "1.0.126", features = ["derive"] } reqwest = { version = "0.11", features = ["json"] } dioxus-hooks = { path = "../hooks" } dioxus-core-macro = { path = "../core-macro" } -# rand = { version="0.8.4", features=["small_rng"] } +rand = { version = "0.8.4", features = ["small_rng"] } + +[dev-dependencies.getrandom] +version = "0.2" +features = ["js"] + # surf = { version = "2.3.1", default-features = false, features = [ # "wasm-client", # ] } diff --git a/packages/web/examples/js_bench.rs b/packages/web/examples/js_bench.rs new file mode 100644 index 000000000..9ce90d841 --- /dev/null +++ b/packages/web/examples/js_bench.rs @@ -0,0 +1,243 @@ +use std::cell::Cell; + +use dioxus::prelude::*; +use dioxus_core as dioxus; +use dioxus_core_macro::*; +use dioxus_hooks::{use_ref, use_state}; +use dioxus_html as dioxus_elements; +use dioxus_web; +use gloo_timers::future::TimeoutFuture; +use rand::prelude::*; + +fn main() { + console_error_panic_hook::set_once(); + if cfg!(debug_assertions) { + wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); + log::debug!("hello world"); + } + + for a in ADJECTIVES { + wasm_bindgen::intern(*a); + } + for a in COLOURS { + wasm_bindgen::intern(*a); + } + for a in NOUNS { + wasm_bindgen::intern(*a); + } + for a in [ + "container", + "jumbotron", + "row", + "Dioxus", + "col-md-6", + "col-md-1", + "Create 1,000 rows", + "run", + "Create 10,000 rows", + "runlots", + "Append 1,000 rows", + "add", + "Update every 10th row", + "update", + "Clear", + "clear", + "Swap rows", + "swaprows", + "preloadicon glyphicon glyphicon-remove", // + "aria-hidden", + "onclick", + "true", + "false", + "danger", + "type", + "id", + "class", + "glyphicon glyphicon-remove remove", + "dioxus-id", + "dioxus-event-click", + "dioxus", + "click", + "1.10", + "lbl", + "remove", + "dioxus-event", + "col-sm-6 smallpad", + "btn btn-primary btn-block", + "", + " ", + ] { + wasm_bindgen::intern(a); + } + for x in 0..100_000 { + wasm_bindgen::intern(&x.to_string()); + } + + dioxus_web::launch(App, |c| c.rootname("main")); +} + +#[derive(Clone, PartialEq, Copy)] +struct Label { + key: usize, + labels: [&'static str; 3], +} + +static mut Counter: Cell = Cell::new(1); + +impl Label { + fn new_list(num: usize) -> Vec { + let mut rng = SmallRng::from_entropy(); + let mut labels = Vec::with_capacity(num); + + let offset = unsafe { Counter.get() }; + unsafe { Counter.set(offset + num) }; + + for k in offset..(offset + num) { + labels.push(Label { + key: k, + labels: [ + ADJECTIVES.choose(&mut rng).unwrap(), + COLOURS.choose(&mut rng).unwrap(), + NOUNS.choose(&mut rng).unwrap(), + ], + }); + } + + labels + } +} + +static App: FC<()> = |cx, _props| { + let mut items = use_ref(cx, || vec![]); + let mut selected = use_state(cx, || None); + + cx.render(rsx! { + div { class: "container" + div { class: "jumbotron" + div { class: "row" + div { class: "col-md-6", h1 { "Dioxus" } } + div { class: "col-md-6" + div { class: "row" + ActionButton { name: "Create 1,000 rows", id: "run", + onclick: move || items.set(Label::new_list(1_000)), + } + ActionButton { name: "Create 10,000 rows", id: "runlots", + onclick: move || items.set(Label::new_list(10_000)), + } + ActionButton { name: "Append 1,000 rows", id: "add", + onclick: move || items.write().extend(Label::new_list(1_000)), + } + ActionButton { name: "Update every 10th row", id: "update", + onclick: move || items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"), + } + ActionButton { name: "Clear", id: "clear", + onclick: move || items.write().clear(), + } + ActionButton { name: "Swap Rows", id: "swaprows", + onclick: move || items.write().swap(0, 998), + } + } + } + } + } + table { class: "table table-hover table-striped test-data" + tbody { id: "tbody" + {items.read().iter().enumerate().map(|(id, item)| { + let [adj, col, noun] = item.labels; + let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""}; + rsx!(tr { + class: "{is_in_danger}", + key: "{id}", + td { class:"col-md-1" } + td { class:"col-md-1", "{item.key}" } + td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), + a { class: "lbl", "{adj} {col} {noun}" } + } + td { class: "col-md-1" + a { class: "remove", onclick: move |_| { items.write().remove(id); }, + span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } + } + } + td { class: "col-md-6" } + }) + })} + } + } + span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" } + } + }) +}; + +#[derive(Props)] +struct ActionButtonProps<'a> { + name: &'static str, + id: &'static str, + onclick: &'a dyn Fn(), +} + +fn ActionButton(cx: Context, props: &ActionButtonProps) -> Element { + rsx!(cx, div { class: "col-sm-6 smallpad" + button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(), + "{props.name}" + } + }) +} + +static ADJECTIVES: &[&str] = &[ + "pretty", + "large", + "big", + "small", + "tall", + "short", + "long", + "handsome", + "plain", + "quaint", + "clean", + "elegant", + "easy", + "angry", + "crazy", + "helpful", + "mushy", + "odd", + "unsightly", + "adorable", + "important", + "inexpensive", + "cheap", + "expensive", + "fancy", +]; + +static COLOURS: &[&str] = &[ + "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black", + "orange", +]; + +static NOUNS: &[&str] = &[ + "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger", + "pizza", "mouse", "keyboard", +]; + +// #[derive(PartialEq, Props)] +// struct RowProps<'a> { +// row_id: usize, +// label: &'a Label, +// } + +// fn Row(cx: Context, props: &RowProps) -> Element { +// rsx!(cx, tr { +// td { class:"col-md-1", "{props.row_id}" } +// td { class:"col-md-1", onclick: move |_| { /* run onselect */ } +// a { class: "lbl", {props.label.labels} } +// } +// td { class: "col-md-1" +// a { class: "remove", onclick: move |_| {/* remove */} +// span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" } +// } +// } +// td { class: "col-md-6" } +// }) +// } diff --git a/packages/web/examples/simple.rs b/packages/web/examples/simple.rs new file mode 100644 index 000000000..f6a397e00 --- /dev/null +++ b/packages/web/examples/simple.rs @@ -0,0 +1,46 @@ +//! Example: README.md showcase +//! +//! The example from the README.md. + +use dioxus::prelude::*; +use dioxus_core as dioxus; +use dioxus_core_macro::*; +use dioxus_hooks::use_state; +use dioxus_html as dioxus_elements; +use dioxus_web; +use gloo_timers::future::TimeoutFuture; + +fn main() { + wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); + dioxus_web::launch(App, |c| c); +} + +static App: FC<()> = |cx, props| { + let show = use_state(cx, || true); + + let inner = match *show { + true => { + rsx!( div { + "hello world" + }) + } + false => { + rsx!( div { + // h1 { + "bello world" + // } + }) + } + }; + + rsx!(cx, div { + button { + "toggle" + onclick: move |_| { + let cur = *show; + show.set(!cur); + } + } + {inner} + }) +}; diff --git a/packages/web/src/cache.rs b/packages/web/src/cache.rs index 1cdba7958..61339f918 100644 --- a/packages/web/src/cache.rs +++ b/packages/web/src/cache.rs @@ -7,6 +7,11 @@ /// Eventually we might want to procedurally generate these strings for common words, phrases, and values. pub(crate) fn intern_cached_strings() { let cached_words = [ + // Important tags to dioxus + "dioxus-id", + "dioxus", + "dioxus-event-click", // todo: more events + "click", // All the HTML Tags "a", "abbr", diff --git a/packages/web/src/cfg.rs b/packages/web/src/cfg.rs index 287e81850..09c488022 100644 --- a/packages/web/src/cfg.rs +++ b/packages/web/src/cfg.rs @@ -17,7 +17,7 @@ impl Default for WebConfig { fn default() -> Self { Self { hydrate: false, - rootname: "dioxusroot".to_string(), + rootname: "main".to_string(), } } } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 630cd2b36..c92dd3400 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -213,7 +213,7 @@ impl WebsysDom { fn create_placeholder(&mut self, id: u64) { self.create_element("pre", None, id); - // self.set_attribute("hidden", "", None); + self.set_attribute("hidden", "", None, id); } fn create_text_node(&mut self, text: &str, id: u64) { @@ -246,8 +246,15 @@ impl WebsysDom { .unwrap(), }; + use smallstr; + use smallstr::SmallString; + use std::fmt::Write; + + let mut s: SmallString<[u8; 8]> = smallstr::SmallString::new(); + write!(s, "{}", id).unwrap(); + let el2 = el.dyn_ref::().unwrap(); - el2.set_attribute("dioxus-id", &format!("{}", id)).unwrap(); + el2.set_attribute("dioxus-id", s.as_str()).unwrap(); self.stack.push(el.clone()); self.nodes[(id as usize)] = Some(el); @@ -263,18 +270,21 @@ impl WebsysDom { let el = self.stack.top(); - let el = el - .dyn_ref::() - .expect(&format!("not an element: {:?}", el)); + let el = el.dyn_ref::().unwrap(); + // let el = el.dyn_ref::().unwrap(); + // .expect(&format!("not an element: {:?}", el)); // let scope_id = scope.data().as_ffi(); - let scope_id = scope.0 as u64; + // let scope_id = scope.0 as u64; + // "dioxus-event-click", + // "1.10" + // &format!("", scope_id, real_id), + // &format!("dioxus-event-{}", event), + // &format!("{}.{}", scope_id, real_id), + // &format!("dioxus-event-{}", event), + // &format!("{}.{}", scope_id, real_id), - el.set_attribute( - &format!("dioxus-event-{}", event), - &format!("{}.{}", scope_id, real_id), - ) - .unwrap(); + el.set_attribute("dioxus-event", event).unwrap(); // el.set_attribute(&format!("dioxus-event"), &format!("{}", event)) // .unwrap(); @@ -488,6 +498,8 @@ unsafe impl Sync for DioxusWebsysEvent {} fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc { use dioxus_html::on::*; use dioxus_html::KeyCode; + // event.prevent_default(); + // use dioxus_core::events::on::*; match event.type_().as_str() { "copy" | "cut" | "paste" => Arc::new(ClipboardEvent {}), @@ -682,30 +694,40 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { use anyhow::Context; + let element_id = target + .get_attribute("dioxus-id") + .context("Could not find element id on event target")? + .parse()?; + // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system let val: String = target - .get_attribute(&format!("dioxus-event-{}", typ)) + .get_attribute("dioxus-event") .context(format!("wrong format - received {:#?}", typ))?; + // .get_attribute(&format!("dioxus-event-{}", typ)) + // .context(format!("wrong format - received {:#?}", typ))?; let mut fields = val.splitn(3, "."); - let gi_id = fields - .next() - .and_then(|f| f.parse::().ok()) - .context("failed to parse gi id")?; + // let gi_id = fields + // .next() + // .and_then(|f| f.parse::().ok()) + // .context("failed to parse gi id")?; - let real_id = fields - .next() - .and_then(|raw_id| raw_id.parse::().ok()) - .context("failed to parse real id")?; + // let real_id = fields + // .next() + // .and_then(|raw_id| raw_id.parse::().ok()) + // .context("failed to parse real id")?; - let triggered_scope = gi_id; + // let triggered_scope = gi_id; Ok(UserEvent { name: event_name_from_typ(&typ), data: virtual_event_from_websys_event(event.clone()), - element: Some(ElementId(real_id as usize)), - scope_id: Some(ScopeId(triggered_scope as usize)), + element: Some(ElementId(element_id)), + scope_id: None, + // scope_id: Some(ScopeId(triggered_scope as usize)), + // element: Some(ElementId(real_id as usize)), + // scope_id: Some(ScopeId(triggered_scope as usize)), priority: dioxus_core::EventPriority::Medium, }) } diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 4e5f5fefc..d9e14b6c2 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -85,7 +85,7 @@ mod ric_raf; /// dioxus_web::launch(App, |c| c); /// } /// -/// static App: FC<()> = |(cx, props)| { +/// static App: FC<()> = |cx, props| { /// rsx!(cx, div {"hello world"}) /// } /// ``` @@ -109,7 +109,7 @@ pub fn launch(root_component: FC<()>, configuration: impl FnOnce(WebConfig) -> W /// name: String /// } /// -/// static App: FC = |(cx, props)| { +/// static App: FC = |cx, props| { /// rsx!(cx, div {"hello {props.name}"}) /// } /// ``` @@ -155,7 +155,7 @@ pub async fn run_with_props(root: FC, root_props: T, cfg: // hydrating is simply running the dom for a single render. If the page is already written, then the corresponding // ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory if !should_hydrate { - log::info!("Applying rebuild edits..., {:?}", mutations); + // log::info!("Applying rebuild edits..., {:?}", mutations); websys_dom.process_edits(&mut mutations.edits); } @@ -166,17 +166,20 @@ pub async fn run_with_props(root: FC, root_props: T, cfg: // if there is work then this future resolves immediately. dom.wait_for_work().await; - // wait for the mainthread to schedule us in - let mut deadline = work_loop.wait_for_idle_time().await; + // // wait for the mainthread to schedule us in + // let mut deadline = work_loop.wait_for_idle_time().await; // run the virtualdom work phase until the frame deadline is reached - let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some()); + let mutations = dom.work_with_deadline(|| false); + // // run the virtualdom work phase until the frame deadline is reached + // let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some()); // wait for the animation frame to fire so we can apply our changes work_loop.wait_for_raf().await; for mut edit in mutations { // actually apply our changes during the animation frame + // log::info!("Applying change edits..., {:?}", edit); websys_dom.process_edits(&mut edit.edits); } } diff --git a/src/lib.rs b/src/lib.rs index 93c7d33b1..999fff41a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ //! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component. //! //! ``` -//! pub pub static Example: FC<()> = |(cx, props)|{ +//! pub pub static Example: FC<()> = |cx, props|{ //! let (val, set_val) = use_state(cx, || 0); //! cx.render(rsx!( //! button { onclick: move |_| set_val(val + 1) } @@ -156,7 +156,7 @@ //! dioxus::web::launch(Example); //! } //! -//! pub pub static Example: FC<()> = |(cx, props)|{ +//! pub pub static Example: FC<()> = |cx, props|{ //! cx.render(rsx! { //! div { "Hello World!" } //! }) @@ -190,6 +190,11 @@ pub use dioxus_router as router; pub mod debug {} +pub mod events { + #[cfg(feature = "html")] + pub use dioxus_html::{on::*, KeyCode}; +} + pub mod prelude { //! A glob import that includes helper types like FC, rsx!, html!, and required traits pub use dioxus_core::prelude::*; From bab21a0aa1cbf8e6bd95f823e49f53c082e8d6cc Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 22 Nov 2021 15:22:42 -0500 Subject: [PATCH 2/7] wip: updates to router --- examples/calculator.rs | 149 +++++++++++++++-------------- packages/core-macro/src/router.rs | 66 ++++++------- packages/core/src/scope.rs | 26 ----- packages/core/src/scopearena.rs | 5 - packages/desktop/src/index.js | 2 +- packages/router/examples/simple.rs | 58 +++++------ packages/router/src/lib.rs | 94 +++++++++++++----- packages/router/src/utils.rs | 25 ----- 8 files changed, 208 insertions(+), 217 deletions(-) diff --git a/examples/calculator.rs b/examples/calculator.rs index 0b1b4f1e3..1a918879c 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -3,8 +3,11 @@ This example is a simple iOS-style calculator. This particular example can run a This calculator version uses React-style state management. All state is held as individual use_states. */ +use std::sync::Arc; + use dioxus::events::*; use dioxus::prelude::*; +use separator::Separatable; fn main() { dioxus::desktop::launch(APP, |cfg| cfg); @@ -15,85 +18,67 @@ const APP: FC<()> = |cx, _| { let operator = use_state(cx, || None as Option<&'static str>); let display_value = use_state(cx, || String::from("")); - let clear_display = display_value == "0"; - let clear_text = if clear_display { "C" } else { "AC" }; - + let toggle_percent = move |_| todo!(); let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str()); - let input_dot = move || display_value.modify().push_str("."); - - let perform_operation = move || { - if let Some(op) = operator.as_ref() { - let rhs = display_value.parse::().unwrap(); - let new_val = match *op { - "+" => *cur_val + rhs, - "-" => *cur_val - rhs, - "*" => *cur_val * rhs, - "/" => *cur_val / rhs, - _ => unreachable!(), - }; - cur_val.set(new_val); - display_value.set(new_val.to_string()); - operator.set(None); - } - }; - - let toggle_sign = move |_| { - if display_value.starts_with("-") { - display_value.set(display_value.trim_start_matches("-").to_string()) - } else { - display_value.set(format!("-{}", *display_value)) - } - }; - - let toggle_percent = move |_| todo!(); - - let clear_key = move |_| { - display_value.set("0".to_string()); - if !clear_display { - operator.set(None); - cur_val.set(0.0); - } - }; - - let keydownhandler = move |evt: KeyboardEvent| match evt.key_code { - KeyCode::Add => operator.set(Some("+")), - KeyCode::Subtract => operator.set(Some("-")), - KeyCode::Divide => operator.set(Some("/")), - KeyCode::Multiply => operator.set(Some("*")), - KeyCode::Num0 => input_digit(0), - KeyCode::Num1 => input_digit(1), - KeyCode::Num2 => input_digit(2), - KeyCode::Num3 => input_digit(3), - KeyCode::Num4 => input_digit(4), - KeyCode::Num5 => input_digit(5), - KeyCode::Num6 => input_digit(6), - KeyCode::Num7 => input_digit(7), - KeyCode::Num8 => input_digit(8), - KeyCode::Num9 => input_digit(9), - KeyCode::Backspace => { - if !display_value.as_str().eq("0") { - display_value.modify().pop(); - } - } - _ => {} - }; - - use separator::Separatable; - let formatted_display = cur_val.separated_string(); - rsx!(cx, div { - class: "calculator", onkeydown: {keydownhandler} - div { class: "calculator-display", "{formatted_display}" } + class: "calculator", + onkeydown: move |evt| match evt.key_code { + KeyCode::Add => operator.set(Some("+")), + KeyCode::Subtract => operator.set(Some("-")), + KeyCode::Divide => operator.set(Some("/")), + KeyCode::Multiply => operator.set(Some("*")), + KeyCode::Num0 => input_digit(0), + KeyCode::Num1 => input_digit(1), + KeyCode::Num2 => input_digit(2), + KeyCode::Num3 => input_digit(3), + KeyCode::Num4 => input_digit(4), + KeyCode::Num5 => input_digit(5), + KeyCode::Num6 => input_digit(6), + KeyCode::Num7 => input_digit(7), + KeyCode::Num8 => input_digit(8), + KeyCode::Num9 => input_digit(9), + KeyCode::Backspace => { + if !display_value.as_str().eq("0") { + display_value.modify().pop(); + } + } + _ => {} + } + div { class: "calculator-display", {[format_args!("{}", cur_val.separated_string())]} } div { class: "input-keys" div { class: "function-keys" - CalculatorKey { name: "key-clear", onclick: {clear_key} "{clear_text}" } - CalculatorKey { name: "key-sign", onclick: {toggle_sign}, "ยฑ"} - CalculatorKey { name: "key-percent", onclick: {toggle_percent} "%"} + CalculatorKey { + {[if display_value == "0" { "C" } else { "AC" }]} + name: "key-clear", + onclick: move |_| { + display_value.set("0".to_string()); + if display_value != "0" { + operator.set(None); + cur_val.set(0.0); + } + } + } + CalculatorKey { + "ยฑ" + name: "key-sign", + onclick: move |_| { + if display_value.starts_with("-") { + display_value.set(display_value.trim_start_matches("-").to_string()) + } else { + display_value.set(format!("-{}", *display_value)) + } + }, + } + CalculatorKey { + "%" + onclick: {toggle_percent} + name: "key-percent", + } } div { class: "digit-keys" CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" } - CalculatorKey { name: "key-dot", onclick: move |_| input_dot(), "โ—" } + CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "โ—" } {(1..9).map(|k| rsx!{ CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } @@ -104,7 +89,25 @@ const APP: FC<()> = |cx, _| { CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "ร—" } CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "โˆ’" } CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" } - CalculatorKey { name: "key-equals", onclick: move |_| perform_operation() "=" } + CalculatorKey { + "=" + name: "key-equals", + onclick: move |_| { + if let Some(op) = operator.as_ref() { + let rhs = display_value.parse::().unwrap(); + let new_val = match *op { + "+" => *cur_val + rhs, + "-" => *cur_val - rhs, + "*" => *cur_val * rhs, + "/" => *cur_val / rhs, + _ => unreachable!(), + }; + cur_val.set(new_val); + display_value.set(new_val.to_string()); + operator.set(None); + } + }, + } } } }) @@ -113,7 +116,7 @@ const APP: FC<()> = |cx, _| { #[derive(Props)] struct CalculatorKeyProps<'a> { name: &'static str, - onclick: &'a dyn Fn(MouseEvent), + onclick: &'a dyn Fn(Arc), children: Element, } diff --git a/packages/core-macro/src/router.rs b/packages/core-macro/src/router.rs index 2ab9f3a17..e28a9e9a6 100644 --- a/packages/core-macro/src/router.rs +++ b/packages/core-macro/src/router.rs @@ -206,43 +206,43 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream { // static #cache_thread_local_ident: ::std::cell::RefCell<::std::option::Option<#ident>> = ::std::cell::RefCell::new(::std::option::Option::None); // } - #[automatically_derived] - impl ::dioxus::router::Routable for #ident { - #from_path - #to_path + // #[automatically_derived] + // impl ::dioxus::router::Routable for #ident { + // #from_path + // #to_path - fn routes() -> ::std::vec::Vec<&'static str> { - ::std::vec![#(#ats),*] - } + // // fn routes() -> ::std::vec::Vec<&'static str> { + // // ::std::vec![#(#ats),*] + // // } - fn not_found_route() -> ::std::option::Option { - #not_found_route - } + // fn not_found_route() -> ::std::option::Option { + // #not_found_route + // } - // fn current_route() -> ::std::option::Option { - // #cache_thread_local_ident.with(|val| ::std::clone::Clone::clone(&*val.borrow())) - // } + // // fn current_route() -> ::std::option::Option { + // // #cache_thread_local_ident.with(|val| ::std::clone::Clone::clone(&*val.borrow())) + // // } - fn recognize(pathname: &str) -> ::std::option::Option { - todo!() - // ::std::thread_local! { - // static ROUTER: ::dioxus::router::__macro::Router = ::dioxus::router::__macro::build_router::<#ident>(); - // } - // let route = ROUTER.with(|router| ::dioxus::router::__macro::recognize_with_router(router, pathname)); - // { - // let route = ::std::clone::Clone::clone(&route); - // #cache_thread_local_ident.with(move |val| { - // *val.borrow_mut() = route; - // }); - // } - // route - } + // fn recognize(pathname: &str) -> ::std::option::Option { + // todo!() + // // ::std::thread_local! { + // // static ROUTER: ::dioxus::router::__macro::Router = ::dioxus::router::__macro::build_router::<#ident>(); + // // } + // // let route = ROUTER.with(|router| ::dioxus::router::__macro::recognize_with_router(router, pathname)); + // // { + // // let route = ::std::clone::Clone::clone(&route); + // // #cache_thread_local_ident.with(move |val| { + // // *val.borrow_mut() = route; + // // }); + // // } + // // route + // } - // fn cleanup() { - // #cache_thread_local_ident.with(move |val| { - // *val.borrow_mut() = ::std::option::Option::None; - // }); - // } - } + // // fn cleanup() { + // // #cache_thread_local_ident.with(move |val| { + // // *val.borrow_mut() = ::std::option::Option::None; + // // }); + // // } + // } } } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 398c8f203..3eaf44f1c 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -74,7 +74,6 @@ pub struct SelfReferentialItems<'a> { pub(crate) borrowed_props: Vec<&'a VComponent<'a>>, pub(crate) suspended_nodes: FxHashMap, pub(crate) tasks: Vec>>, - pub(crate) pending_effects: Vec>, } /// A component's unique identifier. @@ -284,24 +283,6 @@ impl Scope { } } - /// Push an effect to be ran after the component has been successfully mounted to the dom - /// Returns the effect's position in the stack - pub fn push_effect<'src>(&'src self, effect: impl FnOnce() + 'src) -> usize { - // this is some tricker to get around not being able to actually call fnonces - let mut slot = Some(effect); - let fut: &mut dyn FnMut() = self.bump().alloc(move || slot.take().unwrap()()); - - // wrap it in a type that will actually drop the contents - let boxed_fut = unsafe { BumpBox::from_raw(fut) }; - - // erase the 'src lifetime for self-referential storage - let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) }; - - let mut items = self.items.borrow_mut(); - items.pending_effects.push(self_ref_fut); - items.pending_effects.len() - 1 - } - /// Pushes the future onto the poll queue to be polled /// The future is forcibly dropped if the component is not ready by the next render pub fn push_task<'src, F: Future>( @@ -483,13 +464,6 @@ impl Scope { } } - // run the list of effects - pub(crate) fn run_effects(&mut self) { - for mut effect in self.items.get_mut().pending_effects.drain(..) { - effect(); - } - } - pub fn root_node(&self) -> &VNode { let node = *self.wip_frame().nodes.borrow().get(0).unwrap(); unsafe { std::mem::transmute(&*node) } diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index ea6f0c5be..e577d8d1e 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -188,7 +188,6 @@ impl ScopeArena { borrowed_props: Default::default(), suspended_nodes: Default::default(), tasks: Default::default(), - pending_effects: Default::default(), }), }); @@ -226,14 +225,12 @@ impl ScopeArena { let SelfReferentialItems { borrowed_props, listeners, - pending_effects, suspended_nodes, tasks, } = scope.items.get_mut(); borrowed_props.clear(); listeners.clear(); - pending_effects.clear(); suspended_nodes.clear(); tasks.clear(); @@ -328,14 +325,12 @@ impl ScopeArena { // just forget about our suspended nodes while we're at it items.suspended_nodes.clear(); items.tasks.clear(); - items.pending_effects.clear(); // guarantee that we haven't screwed up - there should be no latent references anywhere debug_assert!(items.listeners.is_empty()); debug_assert!(items.borrowed_props.is_empty()); debug_assert!(items.suspended_nodes.is_empty()); debug_assert!(items.tasks.is_empty()); - debug_assert!(items.pending_effects.is_empty()); // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. scope.wip_frame().nodes.borrow_mut().clear(); diff --git a/packages/desktop/src/index.js b/packages/desktop/src/index.js index aca3d3139..5d93052c7 100644 --- a/packages/desktop/src/index.js +++ b/packages/desktop/src/index.js @@ -409,7 +409,7 @@ class Interpreter { } function main() { - let root = window.document.getElementById("_dioxusroot"); + let root = window.document.getElementById("main"); window.interpreter = new Interpreter(root); console.log(window.interpreter); diff --git a/packages/router/examples/simple.rs b/packages/router/examples/simple.rs index 43ddaa2d2..4a317dd5c 100644 --- a/packages/router/examples/simple.rs +++ b/packages/router/examples/simple.rs @@ -5,41 +5,35 @@ use dioxus_router::*; fn main() { console_error_panic_hook::set_once(); + wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); dioxus_web::launch(App, |c| c); } -#[derive(Clone, Debug, PartialEq)] -enum Route { - Home, - About, - NotFound, -} - static App: FC<()> = |cx, props| { - let route = use_router(cx, Route::parse); - - match route { - Route::Home => rsx!(cx, div { "Home" }), - Route::About => rsx!(cx, div { "About" }), - Route::NotFound => rsx!(cx, div { "NotFound" }), + #[derive(Clone, Debug, PartialEq)] + enum Route { + Home, + About, + NotFound, } + + let route = use_router(cx, |s| match s { + "/" => Route::Home, + "/about" => Route::About, + _ => Route::NotFound, + }); + + cx.render(rsx! { + div { + {match route { + Route::Home => rsx!(h1 { "Home" }), + Route::About => rsx!(h1 { "About" }), + Route::NotFound => rsx!(h1 { "NotFound" }), + }} + nav { + Link { to: Route::Home, href: |_| "/".to_string() } + Link { to: Route::About, href: |_| "/about".to_string() } + } + } + }) }; - -impl ToString for Route { - fn to_string(&self) -> String { - match self { - Route::Home => "/".to_string(), - Route::About => "/about".to_string(), - Route::NotFound => "/404".to_string(), - } - } -} -impl Route { - fn parse(s: &str) -> Self { - match s { - "/" => Route::Home, - "/about" => Route::About, - _ => Route::NotFound, - } - } -} diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 948cb4eff..24daf8359 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -6,23 +6,24 @@ use dioxus_core as dioxus; use dioxus_core::prelude::*; use dioxus_core_macro::{format_args_f, rsx, Props}; use dioxus_html as dioxus_elements; -use wasm_bindgen::JsValue; -use web_sys::Event; +use wasm_bindgen::{JsCast, JsValue}; +use web_sys::{window, Event}; -use crate::utils::fetch_base_url; +use crate::utils::strip_slash_suffix; -pub trait Routable: 'static + Send + Clone + ToString + PartialEq {} -impl Routable for T where T: 'static + Send + Clone + ToString + PartialEq {} +pub trait Routable: 'static + Send + Clone + PartialEq {} +impl Routable for T where T: 'static + Send + Clone + PartialEq {} pub struct RouterService { - historic_routes: RefCell>, - history_service: web_sys::History, + historic_routes: Vec, + history_service: RefCell, base_ur: RefCell>, } impl RouterService { fn push_route(&self, r: R) { - self.historic_routes.borrow_mut().push(r); + todo!() + // self.historic_routes.borrow_mut().push(r); } fn get_current_route(&self) -> &str { @@ -65,41 +66,73 @@ impl RouterService { /// This hould only be used once per app /// /// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine -pub fn use_router(cx: Context, parse: impl FnMut(&str) -> R) -> &R { +pub fn use_router(cx: Context, mut parse: impl FnMut(&str) -> R + 'static) -> &R { // for the web, attach to the history api cx.use_hook( |f| { // use gloo::events::EventListener; - let base_url = fetch_base_url(); + let base = window() + .unwrap() + .document() + .unwrap() + .query_selector("base[href]") + .ok() + .flatten() + .and_then(|base| { + let base = JsCast::unchecked_into::(base).href(); + let url = web_sys::Url::new(&base).unwrap(); + + if url.pathname() != "/" { + Some(strip_slash_suffix(&base).to_string()) + } else { + None + } + }); + + let location = window().unwrap().location(); + let pathname = location.pathname().unwrap(); + let initial_route = parse(&pathname); let service: RouterService = RouterService { - historic_routes: RefCell::new(vec![]), - history_service: web_sys::window().unwrap().history().expect("no history"), - base_ur: RefCell::new(base_url), + historic_routes: vec![initial_route], + history_service: RefCell::new( + web_sys::window().unwrap().history().expect("no history"), + ), + base_ur: RefCell::new(base), }; + // let base = base_url(); + // let url = route.to_path(); + // pending_routes: RefCell::new(vec![]), // service.history_service.push_state(data, title); - cx.provide_state(service); + // cx.provide_state(service); let regenerate = cx.schedule_update(); - // when "back" is called by the user, we want to to re-render the component + // // when "back" is called by the user, we want to to re-render the component let listener = EventListener::new(&web_sys::window().unwrap(), "popstate", move |_| { // regenerate(); }); + + service }, - |f| { + |state| { + let base = state.base_ur.borrow(); + if let Some(base) = base.as_ref() { + // + let path = format!("{}{}", base, state.get_current_route()); + } + let history = state.history_service.borrow(); + + todo!() + // history.state.historic_routes.last().unwrap() // }, - ); - - todo!() - // let router = use_router_service::(cx)?; - // Some(cfg(router.get_current_route())) + ) } pub fn use_router_service(cx: Context) -> Option<&Rc>> { @@ -109,6 +142,23 @@ pub fn use_router_service(cx: Context) -> Option<&Rc { to: R, + + /// The url that gets pushed to the history stack + /// + /// You can either put it your own inline method or just autoderive the route using `derive(Routable)` + /// + /// ```rust + /// + /// Link { to: Route::Home, href: |_| "home".to_string() } + /// + /// // or + /// + /// Link { to: Route::Home, href: Route::as_url } + /// + /// ``` + href: fn(&R) -> String, + + #[builder(default)] children: Element, } @@ -116,7 +166,7 @@ pub fn Link(cx: Context, props: &LinkProps) -> Element { let service = use_router_service::(cx)?; cx.render(rsx! { a { - href: format_args!("{}", props.to.to_string()), + href: format_args!("{}", (props.href)(&props.to)), onclick: move |_| service.push_route(props.to.clone()), {&props.children}, } diff --git a/packages/router/src/utils.rs b/packages/router/src/utils.rs index 46764ebab..a8e9a2c35 100644 --- a/packages/router/src/utils.rs +++ b/packages/router/src/utils.rs @@ -1,31 +1,6 @@ use wasm_bindgen::JsCast; use web_sys::window; -pub fn fetch_base_url() -> Option { - match window() - .unwrap() - .document() - .unwrap() - .query_selector("base[href]") - { - Ok(Some(base)) => { - let base = JsCast::unchecked_into::(base).href(); - - let url = web_sys::Url::new(&base).unwrap(); - let base = url.pathname(); - - let base = if base != "/" { - strip_slash_suffix(&base) - } else { - return None; - }; - - Some(base.to_string()) - } - _ => None, - } -} - pub(crate) fn strip_slash_suffix(path: &str) -> &str { path.strip_suffix('/').unwrap_or(path) } From 93d4b8ca7c1b133e5dba2a8dc9a310dbe1357001 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 23 Nov 2021 15:53:57 -0500 Subject: [PATCH 3/7] wip: some docs and suspense --- Cargo.toml | 4 +- docs/guide/src/README.md | 23 ++--- docs/guide/src/concepts/00-index.md | 21 ++--- docs/guide/src/concepts/components.md | 10 +-- .../src/concepts/conditional_rendering.md | 14 ++-- docs/guide/src/concepts/interactivity.md | 14 ++-- docs/guide/src/concepts/lists.md | 2 +- docs/guide/src/concepts/suspense.md | 53 ++++++++++++ docs/guide/src/concepts/vnodes.md | 2 +- docs/guide/src/hello_world.md | 40 ++++----- docs/guide/src/setup.md | 10 +++ packages/core/Cargo.toml | 4 - packages/core/README.md | 20 +++++ packages/core/src/diff.rs | 84 ++++--------------- packages/core/src/lib.rs | 2 +- packages/core/src/nodes.rs | 37 +++----- packages/core/src/scope.rs | 43 +--------- packages/core/src/scopearena.rs | 5 -- packages/core/src/virtual_dom.rs | 3 - packages/ssr/src/lib.rs | 5 +- packages/web/Cargo.toml | 4 - src/lib.rs | 62 +++++++------- 22 files changed, 210 insertions(+), 252 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bbf4243e8..6d78e7182 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,5 +93,5 @@ name = "tailwind" path = "./examples/tailwind.rs" -[patch.crates-io] -wasm-bindgen = { path = "../Tinkering/wasm-bindgen" } +# [patch.crates-io] +# wasm-bindgen = { path = "../Tinkering/wasm-bindgen" } diff --git a/docs/guide/src/README.md b/docs/guide/src/README.md index 4aaf4d22c..faf58e309 100644 --- a/docs/guide/src/README.md +++ b/docs/guide/src/README.md @@ -5,7 +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 -fn App((cx, props): Component<()>) -> Element { +fn App(cx: Context, props: &()) -> Element { let mut count = use_state(cx, || 0); cx.render(rsx!( @@ -16,11 +16,20 @@ fn App((cx, props): 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! - +In general, Dioxus and React share many functional similarities. If this guide is lacking in any general concept or an error message is confusing, React's documentation might be more helpful. We are dedicated to providing a *familiar* toolkit for UI in Rust, so we've chosen to follow in the footsteps of popular UI frameworks (React, Redux, etc). If you know React, then you already know Dioxus. If you don't know either, this guide will still help you! > This is introduction book! For advanced topics, check out the [Reference Guide]() instead. +## Multiplatform + +Dioxus is a *portable* toolkit, meaning the Core implementation can run anywhere with no platform-dependent linking. Unlike many other Rust frontend toolkits, Dioxus is not intrinsically linked to Web-Sys. In fact, every element and event listener can be swapped out at compile time. By default, Dioxus ships with the `Html` feature enabled which can be disabled depending on your target renderer. + +Right now, we have several 1st-party renderers: +- WebSys (for WASM) +- Tao/Tokio (for Desktop apps) +- Tao/Tokio (for Mobile apps) +- SSR (for generating static markup) + ### Web Support --- @@ -39,12 +48,7 @@ Examples: ### SSR Support --- -Dioxus supports server-side rendering! In a pinch, you can literally "debug" the VirtualDom: - -```rust -let dom = VirtualDom::new(App); -println!("{:?}, dom"); -``` +Dioxus supports server-side rendering! For rendering statically to an `.html` file or from a WebServer, then you'll want to make sure the `ssr` feature is enabled in the `dioxus` crate and use the `dioxus::ssr` API. We don't expect the SSR API to change drastically in the future. @@ -52,7 +56,6 @@ For rendering statically to an `.html` file or from a WebServer, then you'll wan let contents = dioxus::ssr::render_vdom(&dom, |c| c); ``` - [Jump to the getting started guide for SSR.]() Examples: diff --git a/docs/guide/src/concepts/00-index.md b/docs/guide/src/concepts/00-index.md index 30c137c45..715c3d69b 100644 --- a/docs/guide/src/concepts/00-index.md +++ b/docs/guide/src/concepts/00-index.md @@ -26,7 +26,7 @@ container.push(green_light); container.push(yellow_light); container.push(red_light); -container.onclick(move |_| { +container.set_onclick(move |_| { if red_light.enabled() { red_light.set_enabled(false); green_light.set_enabled(true); @@ -45,22 +45,23 @@ As the UI grows in scale, our logic to keep each element in the proper state wou Instead, with Dioxus, we *declare* what we want our UI to look like: ```rust -let state = "red"; +let mut state = use_state(cx, || "red"); -rsx!( +cx.render(rsx!( Container { - Light { color: "red", enabled: {state == "red"} } - Light { color: "yellow", enabled: {state == "yellow"} } - Light { color: "green", enabled: {state == "green"} } - onclick: |_| { - state = match state { + Light { color: "red", enabled: format_args!("{}", state == "red") } + Light { color: "yellow", enabled: format_args!("{}", state == "yellow") } + Light { color: "green", enabled: format_args!("{}", state == "green") } + + onclick: move |_| { + state.set(match *state { "green" => "yellow", "yellow" => "red", "red" => "green", - } + }) } } -) +)) ``` Remember: this concept is not new! Many frameworks are declarative - with React being the most popular. Declarative frameworks tend to be much more enjoyable to work with than imperative frameworks. diff --git a/docs/guide/src/concepts/components.md b/docs/guide/src/concepts/components.md index a5b824817..ef750f87a 100644 --- a/docs/guide/src/concepts/components.md +++ b/docs/guide/src/concepts/components.md @@ -84,7 +84,7 @@ struct PostProps { And our render function: ```rust -fn Post((cx, props): Scope) -> Element { +fn Post(cx: Context, props: &PostProps) -> Element { cx.render(rsx!{ div { class: "post-container" VoteButton { @@ -120,7 +120,7 @@ struct VoteButtonProps { score: i32 } -fn VoteButton((cx, props): Scope) -> Element { +fn VoteButton(cx: Context, props: &VoteButtonProps) -> Element { cx.render(rsx!{ div { class: "votebutton" div { class: "arrow up" } @@ -145,7 +145,7 @@ struct TitleCardProps<'a> { title: &'a str, } -fn TitleCard<'a>((cx, props): Scope<'a, TitleCardProps>) -> Element<'a> { +fn TitleCard(cx: Context, props: &TitleCardProps) -> Element { cx.render(rsx!{ h1 { "{props.title}" } }) @@ -174,8 +174,8 @@ function Component(props) { Because Dioxus needs to work with the rules of Rust it uses the `Context` object to maintain 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 and performant extensions for Dioxus. ```rust -fn Post((cx /* <-- our Context object*/, props): Scope) -> Element { - cx.render(rsx!{ }) +fn Post(cx: Context, props: &PostProps) -> Element { + cx.render(rsx!("hello")) } ``` ## Moving forward diff --git a/docs/guide/src/concepts/conditional_rendering.md b/docs/guide/src/concepts/conditional_rendering.md index cfbb8add9..e35fce36d 100644 --- a/docs/guide/src/concepts/conditional_rendering.md +++ b/docs/guide/src/concepts/conditional_rendering.md @@ -26,7 +26,7 @@ struct AppProps { Now that we have a "logged_in" flag accessible in our props, we can render two different screens: ```rust -fn App((cx, props): Scope) -> Element { +fn App(cx: Context, props: &AppProps) -> Element { if props.logged_in { cx.render(rsx!{ DashboardScreen {} @@ -48,7 +48,7 @@ Rust provides us algebraic datatypes: enums that can contain values. Using the ` For instance, we could run a function that returns a Result: ```rust -fn App((cx, props): Scope<()>) -> Element { +fn App(cx: Context, props: &())-> Element { match get_name() { Ok(name) => cx.render(rsx!( "Hello, {name}!" )), Err(err) => cx.render(rsx!( "Sorry, I don't know your name, because an error occurred: {err}" )), @@ -58,7 +58,7 @@ fn App((cx, props): Scope<()>) -> Element { We can even match against values: ```rust -fn App((cx, props): Scope<()>) -> Element { +fn App(cx: Context, props: &())-> Element { match get_name() { "jack" => cx.render(rsx!( "Hey Jack, how's Diane?" )), "diane" => cx.render(rsx!( "Hey Diana, how's Jack?" )), @@ -67,12 +67,12 @@ fn App((cx, props): Scope<()>) -> Element { } ``` -Do note: the `rsx!` macro returns a `Closure`, an anonymous function that has a unique type. +Do note: the `rsx!` macro returns a `Closure`, an anonymous function that has a unique type. To turn our `rsx!` into Elements, we need to call `cx.render`. To make patterns like these less verbose, the `rsx!` macro accepts an optional first argument on which it will call `render`. Our previous component can be shortened with this alternative syntax: ```rust -fn App((cx, props): Scope<()>) -> Element { +fn App(cx: Context, props: &())-> Element { match get_name() { "jack" => rsx!(cx, "Hey Jack, how's Diane?" ), "diane" => rsx!(cx, "Hey Diana, how's Jack?" ), @@ -83,13 +83,13 @@ fn App((cx, props): Scope<()>) -> Element { This syntax even enables us to write a one-line component: ```rust -static App: Fc<()> = |cx, props| rsx!(cx, "hello world!"); +static App: FC<()> = |cx, props| rsx!(cx, "hello world!"); ``` Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`: ```rust -fn App((cx, props): Scope<()>) -> Element { +fn App(cx: Context, props: &())-> Element { let greeting = match get_name() { "jack" => rsx!("Hey Jack, how's Diane?" ), "diane" => rsx!("Hey Diana, how's Jack?" ), diff --git a/docs/guide/src/concepts/interactivity.md b/docs/guide/src/concepts/interactivity.md index a7403933c..bd8ff14aa 100644 --- a/docs/guide/src/concepts/interactivity.md +++ b/docs/guide/src/concepts/interactivity.md @@ -35,7 +35,7 @@ fn main() { When Dioxus renders your app, it will pass an immutable reference of `PostProps` to your `Post` component. Here, you can pass the state down into children. ```rust -fn App((cx, props): Scope) -> Element { +fn App(cx: Context, props: &PostProps) -> Element { cx.render(rsx!{ Title { title: &props.title } Score { score: &props.score } @@ -63,8 +63,8 @@ Instead, you'll want to store state internally in your components and let *that* The most common hook you'll use for storing state is `use_state`. `use_state` provides a slot for some data that allows you to read and update the value without accidentally mutating it. ```rust -fn App((cx, props): Scope<()>) -> Element { - let post = use_state(|| { +fn App(cx: Context, props: &())-> Element { + let post = use_state(cx, || { PostData { id: Uuid::new_v4(), score: 10, @@ -111,8 +111,8 @@ When responding to user-triggered events, we'll want to "listen" for an event on For example, let's say we provide a button to generate a new post. Whenever the user clicks the button, they get a new post. To achieve this functionality, we'll want to attach a function to the `on_click` method of `button`. Whenever the button is clicked, our function will run, and we'll get new Post data to work with. ```rust -fn App((cx, props): Scope<()>) -> Element { - let post = use_state(|| PostData::new()); +fn App(cx: Context, props: &())-> Element { + let post = use_state(cx, || PostData::new()); cx.render(rsx!{ button { @@ -134,8 +134,8 @@ We can use tasks in our components to build a tiny stopwatch that ticks every se ```rust -fn App((cx, props): Scope<()>) -> Element { - let mut sec_elapsed = use_state(|| 0); +fn App(cx: Context, props: &())-> Element { + let mut sec_elapsed = use_state(cx, || 0); cx.spawn_task(async move { TimeoutFuture::from_ms(1000).await; diff --git a/docs/guide/src/concepts/lists.md b/docs/guide/src/concepts/lists.md index 2f9efa6ba..ebe12821d 100644 --- a/docs/guide/src/concepts/lists.md +++ b/docs/guide/src/concepts/lists.md @@ -74,7 +74,7 @@ struct PostListProps<'a> { Next, we're going to define our component: ```rust -fn App((cx, props): Component) -> Element { +fn App(cx: Context, props: &PostList) -> Element { // First, we create a new iterator by mapping the post array let posts = props.posts.iter().map(|post| rsx!{ Post { diff --git a/docs/guide/src/concepts/suspense.md b/docs/guide/src/concepts/suspense.md index f6d8c7e98..6c81fa992 100644 --- a/docs/guide/src/concepts/suspense.md +++ b/docs/guide/src/concepts/suspense.md @@ -1 +1,54 @@ # Suspense + +Suspense in Dioxus is enabled through placeholder nodes. + +just a component that renders nodes into the placeholder after the future is finished? + +in react, suspense is just completely pausing diffing while a + +```rust +let n = use_suspense(cx || { + cx.render(rsx!{ + Suspense { + prom: fut, + callback: || {} + } + }) +}) + +suspense () { + let value = use_state(); + if first_render { + push_task({ + value.set(fut.await); + }); + } else { + callback(value) + } +} + + +let name = fetch_name().await; + + +function ProfileDetails() { + // Try to read user info, although it might not have loaded yet + const user = resource.read(); + return

{user.name}

; +} + + +fn ProfileDteails() { + let user = resource.suspend(cx, |l| rsx!("{l}")); + + // waits for the resource to be ready and updates the placeholder with the tree + let name = resource.suspend_with(cx, |val| rsx!( div { "hello" "{user}" } )); + + cx.render(rsx!( + div { + {user} + {name} + } + )) +} +``` diff --git a/docs/guide/src/concepts/vnodes.md b/docs/guide/src/concepts/vnodes.md index 774b1bfba..e6f1b43d0 100644 --- a/docs/guide/src/concepts/vnodes.md +++ b/docs/guide/src/concepts/vnodes.md @@ -107,7 +107,7 @@ To do this, we use the familiar struct-style syntax that Rust provides. Commas a ```rust rsx!( div { - hidden: true, + hidden: "true", background_color: "blue", class: "card color-{mycolor}" } diff --git a/docs/guide/src/hello_world.md b/docs/guide/src/hello_world.md index 89c785a4b..2520c286a 100644 --- a/docs/guide/src/hello_world.md +++ b/docs/guide/src/hello_world.md @@ -1,4 +1,4 @@ -# Hello World +# Hello, World desktop app Let's put together a simple "hello world" desktop application to get acquainted with Dioxus. @@ -92,10 +92,10 @@ use dioxus::prelude::*; fn main() { - dioxus::desktop::start(App, |c| c); + dioxus::desktop::launch(App, |c| c); } -fn App((cx, props): Component<()>) -> Element { +fn App(cx: Context, props: &()) -> Element { cx.render(rsx! ( div { "Hello, world!" } )) @@ -108,7 +108,7 @@ At this point, you could call `cargo run` and be greeted with a simple `Hello, W ### 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. +The `use` statement at the top of our app imports everything from the the `prelude` module. `use`-ing the prelude imports the right traits, types, and macros needed for working with Dioxus. ```rust use dioxus::prelude::*; @@ -118,44 +118,34 @@ This initialization code launches a Tokio runtime on a helper thread where your ```rust fn main() { - dioxus::desktop::start(App, |c| c); + dioxus::desktop::launch(App, |c| c); } ``` Finally, our app. Every component in Dioxus is a function that takes in `Context` and `Props` and returns an `Element`. ```rust -fn App((cx, props): Component<()>) -> Element { +fn App(cx: Context, props: &()) -> 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: + +Writing `fn App(cx: Context, props: &()) -> 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, ()>) -> Element<'a> { - cx.render(rsx! { - div { "Hello, world!" } - }) -} +static App: FC<()> = |cx, props| cx.render(rsx!(div { "Hello, world!" })); ``` -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 -static App: Fc<()> = |cx, props| { - cx.render(rsx! { - div { "Hello, world!" } - }) -}; -``` +### What is this `Context` object? -### The `Context` object +Coming from React, the `Context` object might be confusing. In React, you'll want to store data between renders with hooks. However, hooks rely on global variables which make them difficult to integrate in multi-tenant systems like server-rendering. -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. +In Dioxus, you are given an explicit `Context` object to control how the component renders and stores data. The `Context` object provides a handful of useful APIs for features like suspense, rendering, and more. -### The `rsx!` macro +## Moving on + +Congrats! You've built your first desktop application with Dioxus. Next, we're going to learn about the basics of building interactive user interfaces. -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`. diff --git a/docs/guide/src/setup.md b/docs/guide/src/setup.md index 497762902..c654179c9 100644 --- a/docs/guide/src/setup.md +++ b/docs/guide/src/setup.md @@ -20,6 +20,16 @@ Dioxus requires a few main things to get up and running: Dioxus integrates very well with the Rust-Analyzer IDE plugin which will provide appropriate syntax highlighting, code navigation, folding, and more. +### Installing Rust + +Head over to [https://rust-lang.org](http://rust-lang.org) and install the Rust compiler. + +Once installed, make sure to install wasm32-unknown-unknown as a target if you're planning on deploying your app to the web. + +``` +rustup target add wasm32-unknown-uknown +``` + ### 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. To install the CLI, you'll need cargo (should be automatically installed with Rust): diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 7ea455d3c..af7fa4807 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -67,7 +67,3 @@ harness = false [[example]] name = "rows" path = "./examples/rows.rs" - - -[profile.release] -debug = true diff --git a/packages/core/README.md b/packages/core/README.md index 45059cd89..ce2602003 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -56,3 +56,23 @@ There's a few invariants that are very important: - References to `ScopeInner` and `Props` passed into components are *always* valid for as long as the component exists. Even if the scope backing is resized to fit more scopes, the scope has to stay the same place in memory. + + +## Suspense + +Suspense is done through combinators on values. + +```rust +let name = get_name(cx).suspend(); + +rsx!( + div { + {name} + div { + div { + + } + } + } +) +``` diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index f4bebc8da..e057ae1c2 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -284,7 +284,7 @@ impl<'bump> DiffState<'bump> { // recursively push all the nodes of a tree onto the stack and return how many are there fn push_all_nodes(&mut self, node: &'bump VNode<'bump>) -> usize { match node { - VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => { + VNode::Text(_) | VNode::Placeholder(_) => { self.mutations.push_root(node.mounted_id()); 1 } @@ -354,8 +354,7 @@ impl<'bump> DiffState<'bump> { fn create_node(&mut self, node: &'bump VNode<'bump>) { match node { VNode::Text(vtext) => self.create_text_node(vtext, node), - VNode::Suspended(suspended) => self.create_suspended_node(suspended, node), - VNode::Anchor(anchor) => self.create_anchor_node(anchor, node), + VNode::Placeholder(anchor) => self.create_anchor_node(anchor, node), VNode::Element(element) => self.create_element_node(element, node), VNode::Fragment(frag) => self.create_fragment_node(frag), VNode::Component(component) => self.create_component_node(component), @@ -371,17 +370,7 @@ impl<'bump> DiffState<'bump> { self.stack.add_child_count(1); } - fn create_suspended_node(&mut self, suspended: &'bump VSuspended, node: &'bump VNode<'bump>) { - let real_id = self.scopes.reserve_node(node); - self.mutations.create_placeholder(real_id); - - suspended.dom_id.set(Some(real_id)); - self.stack.add_child_count(1); - - self.attach_suspended_node_to_scope(suspended); - } - - fn create_anchor_node(&mut self, anchor: &'bump VAnchor, node: &'bump VNode<'bump>) { + fn create_anchor_node(&mut self, anchor: &'bump VPlaceholder, node: &'bump VNode<'bump>) { let real_id = self.scopes.reserve_node(node); self.mutations.create_placeholder(real_id); @@ -433,12 +422,10 @@ impl<'bump> DiffState<'bump> { self.mutations.set_attribute(attr, real_id.as_u64()); } - if !children.is_empty() { - if children.len() == 1 { - if let VNode::Text(vtext) = children[0] { - self.mutations.set_text(vtext.text, real_id.as_u64()); - return; - } + if !children.is_empty() && children.len() == 1 { + if let VNode::Text(vtext) = children[0] { + self.mutations.set_text(vtext.text, real_id.as_u64()); + return; } } @@ -529,17 +516,14 @@ impl<'bump> DiffState<'bump> { self.diff_component_nodes(old_node, new_node, old, new) } (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new), - (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()), - (Suspended(old), Suspended(new)) => self.diff_suspended_nodes(old, new), + (Placeholder(old), Placeholder(new)) => new.dom_id.set(old.dom_id.get()), (Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node), (Linked(old), Linked(new)) => self.diff_linked_nodes(old, new), // Anything else is just a basic replace and create ( - Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) - | Suspended(_), - Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) - | Suspended(_), + Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_), + Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_), ) => self .stack .create_node(new_node, MountType::Replace { old: old_node }), @@ -664,7 +648,7 @@ impl<'bump> DiffState<'bump> { self.mutations.set_text(new_text.text, root.as_u64()); } } - (VNode::Text(old_text), _) => { + (VNode::Text(_old_text), _) => { self.stack.element_stack.push(root); self.stack.instructions.push(DiffInstruction::PopElement); self.stack.create_node(new1, MountType::Append); @@ -770,11 +754,6 @@ impl<'bump> DiffState<'bump> { self.diff_children(old.children, new.children); } - fn diff_suspended_nodes(&mut self, old: &'bump VSuspended, new: &'bump VSuspended) { - new.dom_id.set(old.dom_id.get()); - self.attach_suspended_node_to_scope(new); - } - fn diff_linked_nodes(&mut self, old: &'bump NodeLink, new: &'bump NodeLink) { if !std::ptr::eq(old.node, new.node) { // if the ptrs are the same then theyr're the same @@ -818,14 +797,14 @@ impl<'bump> DiffState<'bump> { (_, []) => { self.remove_nodes(old, true); } - ([VNode::Anchor(old_anchor)], [VNode::Anchor(new_anchor)]) => { + ([VNode::Placeholder(old_anchor)], [VNode::Placeholder(new_anchor)]) => { old_anchor.dom_id.set(new_anchor.dom_id.get()); } - ([VNode::Anchor(_)], _) => { + ([VNode::Placeholder(_)], _) => { self.stack .create_children(new, MountType::Replace { old: &old[0] }); } - (_, [VNode::Anchor(_)]) => { + (_, [VNode::Placeholder(_)]) => { let new: &'bump VNode<'bump> = &new[0]; if let Some(first_old) = old.get(0) { self.remove_nodes(&old[1..], true); @@ -1227,8 +1206,7 @@ impl<'bump> DiffState<'bump> { match &search_node.take().unwrap() { VNode::Text(t) => break t.dom_id.get(), VNode::Element(t) => break t.dom_id.get(), - VNode::Suspended(t) => break t.dom_id.get(), - VNode::Anchor(t) => break t.dom_id.get(), + VNode::Placeholder(t) => break t.dom_id.get(), VNode::Linked(l) => { let node: &VNode = unsafe { std::mem::transmute(&*l.node) }; self.find_last_element(node); @@ -1263,8 +1241,7 @@ impl<'bump> DiffState<'bump> { } VNode::Text(t) => break t.dom_id.get(), VNode::Element(t) => break t.dom_id.get(), - VNode::Suspended(t) => break t.dom_id.get(), - VNode::Anchor(t) => break t.dom_id.get(), + VNode::Placeholder(t) => break t.dom_id.get(), } } } @@ -1282,7 +1259,7 @@ impl<'bump> DiffState<'bump> { self.remove_nodes(el.children, false); } - VNode::Text(_) | VNode::Anchor(_) | VNode::Suspended(_) => { + VNode::Text(_) | VNode::Placeholder(_) => { let id = old .try_mounted_id() .unwrap_or_else(|| panic!("broke on {:?}", old)); @@ -1331,15 +1308,7 @@ impl<'bump> DiffState<'bump> { } } } - VNode::Suspended(s) => { - let id = s.dom_id.get().unwrap(); - self.scopes.collect_garbage(id); - - if gen_muts { - self.mutations.remove(id.as_u64()); - } - } - VNode::Anchor(a) => { + VNode::Placeholder(a) => { let id = a.dom_id.get().unwrap(); self.scopes.collect_garbage(id); @@ -1386,21 +1355,4 @@ impl<'bump> DiffState<'bump> { let long_listener = unsafe { std::mem::transmute(listener) }; scope.items.borrow_mut().listeners.push(long_listener) } - - fn attach_suspended_node_to_scope(&mut self, suspended: &'bump VSuspended) { - if let Some(scope) = self - .stack - .current_scope() - .and_then(|id| self.scopes.get_scope(&id)) - { - todo!() - // // safety: this lifetime is managed by the logic on scope - // let extended = unsafe { std::mem::transmute(suspended) }; - // scope - // .items - // .borrow_mut() - // .suspended_nodes - // .insert(suspended.task_id, extended); - } - } } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index a4ea08399..0d248c246 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -38,7 +38,7 @@ pub(crate) mod innerlude { pub use crate::innerlude::{ Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, IntoVNode, LazyNodes, Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg, ScopeId, - UserEvent, VAnchor, VElement, VFragment, VNode, VSuspended, VirtualDom, FC, + UserEvent, VElement, VFragment, VNode, VPlaceholder, VSuspended, VirtualDom, FC, }; pub mod prelude { diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index bba4ad1f0..e2b8487d9 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -97,19 +97,9 @@ pub enum VNode<'src> { /// ``` Component(&'src VComponent<'src>), - /// Suspended VNodes represent chunks of the UI tree that are not yet ready to be displayed. + /// Placeholders are a type of placeholder VNode used when fragments don't contain any children. /// - /// # Example - /// - /// ```rust, ignore - /// - /// - /// ``` - Suspended(&'src VSuspended), - - /// Anchors are a type of placeholder VNode used when fragments don't contain any children. - /// - /// Anchors cannot be directly constructed via public APIs. + /// Placeholders cannot be directly constructed via public APIs. /// /// # Example /// @@ -123,7 +113,7 @@ pub enum VNode<'src> { /// assert_eq!(root, VNode::Anchor); /// } /// ``` - Anchor(&'src VAnchor), + Placeholder(&'src VPlaceholder), /// A VNode that is actually a pointer to some nodes rather than the nodes directly. Useful when rendering portals /// or eliding lifetimes on VNodes through runtime checks. @@ -151,8 +141,7 @@ impl<'src> VNode<'src> { VNode::Fragment(f) => f.key, VNode::Text(_t) => None, - VNode::Suspended(_s) => None, - VNode::Anchor(_f) => None, + VNode::Placeholder(_f) => None, VNode::Linked(_c) => None, } } @@ -171,8 +160,7 @@ impl<'src> VNode<'src> { match &self { VNode::Text(el) => el.dom_id.get(), VNode::Element(el) => el.dom_id.get(), - VNode::Anchor(el) => el.dom_id.get(), - VNode::Suspended(el) => el.dom_id.get(), + VNode::Placeholder(el) => el.dom_id.get(), VNode::Linked(_) => None, VNode::Fragment(_) => None, @@ -194,8 +182,7 @@ impl<'src> VNode<'src> { VNode::Text(t) => VNode::Text(*t), VNode::Element(e) => VNode::Element(*e), VNode::Component(c) => VNode::Component(*c), - VNode::Suspended(s) => VNode::Suspended(*s), - VNode::Anchor(a) => VNode::Anchor(*a), + VNode::Placeholder(a) => VNode::Placeholder(*a), VNode::Fragment(f) => VNode::Fragment(VFragment { children: f.children, key: f.key, @@ -219,12 +206,11 @@ impl Debug for VNode<'_> { .finish(), VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text), - VNode::Anchor(_) => write!(s, "VNode::VAnchor"), + VNode::Placeholder(_) => write!(s, "VNode::VAnchor"), VNode::Fragment(frag) => { write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children) } - VNode::Suspended { .. } => write!(s, "VNode::VSuspended"), VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc), VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()), } @@ -254,7 +240,7 @@ fn empty_cell() -> Cell> { } /// A placeholder node only generated when Fragments don't have any children. -pub struct VAnchor { +pub struct VPlaceholder { pub dom_id: Cell>, } @@ -357,6 +343,7 @@ pub struct Listener<'bump> { pub event: &'static str, /// The actual callback that the user specified + #[allow(clippy::type_complexity)] pub(crate) callback: RefCell) + 'bump>>>, } @@ -655,7 +642,7 @@ impl<'a> NodeFactory<'a> { } if nodes.is_empty() { - nodes.push(VNode::Anchor(bump.alloc(VAnchor { + nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder { dom_id: empty_cell(), }))); } @@ -678,7 +665,7 @@ impl<'a> NodeFactory<'a> { } if nodes.is_empty() { - nodes.push(VNode::Anchor(bump.alloc(VAnchor { + nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder { dom_id: empty_cell(), }))); } @@ -722,7 +709,7 @@ impl<'a> NodeFactory<'a> { } if nodes.is_empty() { - nodes.push(VNode::Anchor(bump.alloc(VAnchor { + nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder { dom_id: empty_cell(), }))); } diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 3eaf44f1c..7a0c93256 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -1,7 +1,6 @@ use crate::innerlude::*; use futures_channel::mpsc::UnboundedSender; -use fxhash::FxHashMap; use smallvec::SmallVec; use std::{ any::{Any, TypeId}, @@ -72,7 +71,6 @@ pub struct Scope { pub struct SelfReferentialItems<'a> { pub(crate) listeners: Vec<&'a Listener<'a>>, pub(crate) borrowed_props: Vec<&'a VComponent<'a>>, - pub(crate) suspended_nodes: FxHashMap, pub(crate) tasks: Vec>>, } @@ -347,31 +345,6 @@ impl Scope { Some(link) } - pub fn suspend<'src, F: Future + 'src>( - &'src self, - mut fut: impl FnMut() -> F, - ) -> Option { - let channel = self.sender.clone(); - let node_fut = fut(); - - let scope = self.scope_id(); - - // self.push_task(move || { - // - // async move { - // // - // let r = node_fut.await; - // if let Some(node) = r { - // channel - // .unbounded_send(SchedulerMsg::Suspended { node, scope }) - // .unwrap(); - // } - // } - // }); - - todo!() - } - /// Store a value between renders /// /// This is *the* foundational hook for all other hooks. @@ -452,18 +425,6 @@ impl Scope { self.generation.set(self.generation.get() + 1); } - // General strategy here is to load up the appropriate suspended task and then run it. - // Suspended nodes cannot be called repeatedly. - pub(crate) fn call_suspended_node<'a>(&'a mut self, task_id: u64) { - let mut nodes = &mut self.items.get_mut().suspended_nodes; - - if let Some(suspended) = nodes.remove(&task_id) { - let sus: &'a VSuspended = suspended; - // let mut boxed = sus.callback.borrow_mut().take().unwrap(); - // let new_node: Element = boxed(); - } - } - pub fn root_node(&self) -> &VNode { let node = *self.wip_frame().nodes.borrow().get(0).unwrap(); unsafe { std::mem::transmute(&*node) } @@ -488,7 +449,7 @@ impl BumpFrame { Self { bump, nodes } } - pub fn allocated_bytes(&self) -> usize { + pub fn _allocated_bytes(&self) -> usize { self.bump.allocated_bytes() } @@ -569,7 +530,7 @@ impl HookList { /// Get the ammount of memory a hooklist uses /// Used in heuristics - pub fn get_hook_arena_size(&self) -> usize { + pub fn _get_hook_arena_size(&self) -> usize { self.arena.allocated_bytes() } } diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index e577d8d1e..b92e23857 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -186,7 +186,6 @@ impl ScopeArena { items: RefCell::new(SelfReferentialItems { listeners: Default::default(), borrowed_props: Default::default(), - suspended_nodes: Default::default(), tasks: Default::default(), }), }); @@ -225,13 +224,11 @@ impl ScopeArena { let SelfReferentialItems { borrowed_props, listeners, - suspended_nodes, tasks, } = scope.items.get_mut(); borrowed_props.clear(); listeners.clear(); - suspended_nodes.clear(); tasks.clear(); self.free_scopes.borrow_mut().push(scope); @@ -323,13 +320,11 @@ impl ScopeArena { let mut items = scope.items.borrow_mut(); // just forget about our suspended nodes while we're at it - items.suspended_nodes.clear(); items.tasks.clear(); // guarantee that we haven't screwed up - there should be no latent references anywhere debug_assert!(items.listeners.is_empty()); debug_assert!(items.borrowed_props.is_empty()); - debug_assert!(items.suspended_nodes.is_empty()); debug_assert!(items.tasks.is_empty()); // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index fc85ef0a0..3cefe444d 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -297,7 +297,6 @@ impl VirtualDom { SchedulerMsg::Immediate(s) => { self.dirty_scopes.insert(s); } - SchedulerMsg::Suspended { scope } => todo!(), } } } @@ -548,8 +547,6 @@ pub enum SchedulerMsg { // an async task pushed from an event handler (or just spawned) TaskPushed(ScopeId), - - Suspended { scope: ScopeId }, } /// User Events are events that are shuttled from the renderer into the VirtualDom trhough the scheduler channel. diff --git a/packages/ssr/src/lib.rs b/packages/ssr/src/lib.rs index a04271bbc..071894c17 100644 --- a/packages/ssr/src/lib.rs +++ b/packages/ssr/src/lib.rs @@ -149,7 +149,7 @@ impl<'a> TextRenderer<'a, '_> { } write!(f, "{}", text.text)? } - VNode::Anchor(_anchor) => { + VNode::Placeholder(_anchor) => { // if self.cfg.indent { for _ in 0..il { @@ -252,9 +252,6 @@ impl<'a> TextRenderer<'a, '_> { } else { } } - VNode::Suspended { .. } => { - // we can't do anything with suspended nodes - } } Ok(()) } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 043e4e7d8..a45bd7e31 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -33,10 +33,6 @@ gloo-timers = { version = "0.2.1", features = ["futures"] } futures-util = "0.3.15" smallstr = "0.2.0" -[patch.crates-io] -wasm-bindgen = { path = "../../../Tinkering/wasm-bindgen/" } - - [dependencies.web-sys] version = "0.3.51" features = [ diff --git a/src/lib.rs b/src/lib.rs index 999fff41a..c1062033e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,8 +32,10 @@ //! ``` //! use dioxus::prelude::*; //! -//! fn Example(cx: Context<()>) -> DomTree { -//! html! {
"Hello, world!"
} +//! fn App(cx: Context, props: &()) -> Element { +//! cx.render(rsx!( +//! div {"hello world"} +//! )) //! } //! ``` //! Components need to take a "Context" parameter which is generic over some properties. This defines how the component can be used @@ -41,11 +43,15 @@ //! `()`, and components with properties must declare their properties as a struct: //! //! ``` -//! #[derive(Props)] -//! struct Props { name: String } +//! #[derive(Props, PartialEq)] +//! struct AppProps { +//! name: String +//! } //! -//! fn Example(cx: Context) -> DomTree { -//! html! {
"Hello {cx.props.name}!"
} +//! fn App(cx: Context, props: &AppProps) -> Element { +//! cx.render(rsx!( +//! div { "Hello {props.name}!" } +//! )) //! } //! ``` //! @@ -57,22 +63,20 @@ //! //! ``` //! #[derive(Props)] -//! struct Props<'a> { name: &'a str } +//! struct Props<'a> { +//! name: &'a str +//! } //! -//! fn Example<'a>(cx: Context<'a, Props<'a>>) -> DomTree { -//! html! {
"Hello {cx.props.name}!"
} +//! fn Example(cx: Context, props: &AppProps) -> Element { +//! cx.render(rsx!( +//! div { "Hello {props.name}!" } +//! )) //! } //! ``` //! -//! -//! -//! The lifetimes might look a little messy, but are crucially important for Dioxus's efficiency and overall ergonimics. -//! Components can also be crafted as pub static closures, enabling type inference without all the type signature noise. However, -//! closure-style components cannot work with borrowed data due to limitations in Rust's lifetime system. -//! //! To use custom properties for components, you'll need to derive the `Props` trait for your properties. This trait -//! exposes a compile-time correct builder pattern (similar to typed-builder) that can be used in the `rsx!` and `html!` -//! macros to build components. Component props may have default fields notated by the `Default` attribute: +//! exposes a compile-time correct builder pattern (similar to typed-builder) that can be used in the `rsx!` macro to +//! build components. Component props may have default fields notated by the `Default` attribute: //! //! ``` //! #[derive(Props)] @@ -82,7 +86,7 @@ //! #[props(default = false)] //! checked: bool, //! -//! #[props(default, setter(strip_option, into))] +//! #[props(default, into))] //! title: Option //! } //! ``` @@ -94,34 +98,30 @@ //! //! ``` //! pub pub static Example: FC<()> = |cx, props|{ -//! let (val, set_val) = use_state(cx, || 0); +//! let mut val = use_state(cx, || 0); //! cx.render(rsx!( -//! button { onclick: move |_| set_val(val + 1) } +//! button { onclick: move |_| val += 1 } //! )) //! } //! ```` //! -//! Instead of using a single struct to represent a component and its state, hooks use the "use_hook" building block -//! which allows the persistence of data between function component renders. This primitive is exposed directly through -//! the `Context` item: +//! As a building block for hooks, Dioxus provides the `use_hook` method on Context that stores a provided value in a +//! list of other values. Whenever `use_hook` is called, the next hook value in the list is returned. //! ``` -//! fn my_hook<'a>(cx: &impl Scoped<'a>) -> &'a String { +//! fn my_hook(cx: Context) -> &String { //! cx.use_hook( //! // Initializer stores a value //! |hook_idx| String::new("stored_data"), //! //! // Runner returns the hook value every time the component is rendered //! |hook| &*hook, -//! -//! // Cleanup runs after the component is unmounted -//! |hook| log::debug!("cleaning up hook with value {:#?}", hook) //! ) //! } //! ``` -//! Under the hood, hooks store their data in a series of "memory cells". The first render defines the layout of these -//! memory cells, and on each subsequent render, each `use_hook` call accesses its corresponding memory cell. If a hook -//! accesses the wrong memory cell, `use_hook` will panic, and your app will crash. You can always use `try_use_hook` but -//! these types of errors can be easily mitigated by following the rules of hooks: +//! Under the hood, hooks store their data in a list of `Box`. The first render defines the layout of these +//! list, and on each subsequent render, each `use_hook` call accesses its corresponding list item. If a hook +//! accesses the wrong index, `use_hook` will panic when trying to downcast `Any` to your type, and your app will crash. +//! These types of errors can be easily mitigated by following the rules of hooks: //! //! - Donโ€™t call Hooks inside loops, conditions, or nested functions //! - Don't call hooks in changing order between renders From 36d89beb34821694cb0afb546d3b0cb4e01aaae1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sun, 28 Nov 2021 16:25:42 -0500 Subject: [PATCH 4/7] wip: rename --- examples/borrowed.rs | 12 +- packages/core-macro/src/router.rs | 36 - packages/core-macro/styles/calc.rs | 264 --- packages/core-macro/styles/codegen.rs | 1150 ---------- packages/core-macro/styles/color.rs | 854 ------- packages/core-macro/styles/mod.rs | 2044 ----------------- packages/core-macro/styles/string/lexer.rs | 502 ---- packages/core-macro/styles/string/mod.rs | 1 - packages/core-macro/styles/syn_parse.rs | 2411 -------------------- packages/core/Cargo.toml | 6 +- packages/core/architecture.md | 5 + packages/core/src/component.rs | 4 +- packages/core/src/diff.rs | 203 +- packages/core/src/lib.rs | 4 +- packages/core/src/nodes.rs | 110 +- packages/core/src/scope.rs | 147 +- packages/core/src/scopearena.rs | 22 +- packages/core/src/virtual_dom.rs | 13 +- packages/core/tests/diffing.rs | 2 +- packages/ssr/src/lib.rs | 2 +- 20 files changed, 243 insertions(+), 7549 deletions(-) delete mode 100644 packages/core-macro/styles/calc.rs delete mode 100644 packages/core-macro/styles/codegen.rs delete mode 100644 packages/core-macro/styles/color.rs delete mode 100644 packages/core-macro/styles/mod.rs delete mode 100644 packages/core-macro/styles/string/lexer.rs delete mode 100644 packages/core-macro/styles/string/mod.rs delete mode 100644 packages/core-macro/styles/syn_parse.rs diff --git a/examples/borrowed.rs b/examples/borrowed.rs index 786de9da9..bf779f3b5 100644 --- a/examples/borrowed.rs +++ b/examples/borrowed.rs @@ -20,7 +20,7 @@ fn main() { dioxus::desktop::launch(App, |c| c); } -fn App((cx, props): Scope<()>) -> Element { +fn App(cx: Context, props: &()) -> Element { let text: &mut Vec = cx.use_hook(|_| vec![String::from("abc=def")], |f| f); let first = text.get_mut(0).unwrap(); @@ -39,11 +39,7 @@ struct C1Props<'a> { text: &'a mut String, } -impl<'a> Drop for C1Props<'a> { - fn drop(&mut self) {} -} - -fn Child1<'a>((cx, props): Scope<'a, C1Props>) -> Element<'a> { +fn Child1(cx: Context, props: &C1Props) -> Element { let (left, right) = props.text.split_once("=").unwrap(); cx.render(rsx! { @@ -59,7 +55,7 @@ struct C2Props<'a> { text: &'a str, } -fn Child2<'a>((cx, props): Scope<'a, C2Props>) -> Element<'a> { +fn Child2(cx: Context, props: &C2Props) -> Element { cx.render(rsx! { Child3 { text: props.text @@ -72,7 +68,7 @@ struct C3Props<'a> { text: &'a str, } -fn Child3<'a>((cx, props): Scope<'a, C3Props>) -> Element<'a> { +fn Child3(cx: Context, props: &C3Props) -> Element { cx.render(rsx! { div { "{props.text}"} }) diff --git a/packages/core-macro/src/router.rs b/packages/core-macro/src/router.rs index e28a9e9a6..d64fe2aea 100644 --- a/packages/core-macro/src/router.rs +++ b/packages/core-macro/src/router.rs @@ -189,39 +189,9 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream { let from_path = input.build_from_path(); let to_path = input.build_to_path(); - let not_found_route = match not_found_route { - Some(route) => quote! { ::std::option::Option::Some(Self::#route) }, - None => quote! { ::std::option::Option::None }, - }; - - let cache_thread_local_ident = Ident::new( - &format!("__{}_ROUTER_CURRENT_ROUTE_CACHE", ident), - ident.span(), - ); - quote! { - // ::std::thread_local! { - // #[doc(hidden)] - // #[allow(non_upper_case_globals)] - // static #cache_thread_local_ident: ::std::cell::RefCell<::std::option::Option<#ident>> = ::std::cell::RefCell::new(::std::option::Option::None); - // } - // #[automatically_derived] // impl ::dioxus::router::Routable for #ident { - // #from_path - // #to_path - - // // fn routes() -> ::std::vec::Vec<&'static str> { - // // ::std::vec![#(#ats),*] - // // } - - // fn not_found_route() -> ::std::option::Option { - // #not_found_route - // } - - // // fn current_route() -> ::std::option::Option { - // // #cache_thread_local_ident.with(|val| ::std::clone::Clone::clone(&*val.borrow())) - // // } // fn recognize(pathname: &str) -> ::std::option::Option { // todo!() @@ -237,12 +207,6 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream { // // } // // route // } - - // // fn cleanup() { - // // #cache_thread_local_ident.with(move |val| { - // // *val.borrow_mut() = ::std::option::Option::None; - // // }); - // // } // } } } diff --git a/packages/core-macro/styles/calc.rs b/packages/core-macro/styles/calc.rs deleted file mode 100644 index f52b3eda3..000000000 --- a/packages/core-macro/styles/calc.rs +++ /dev/null @@ -1,264 +0,0 @@ -//! The `calc` functionality. -use crate::LengthPercentage; -use ::{ - proc_macro2::TokenStream, - quote::{quote, ToTokens}, - std::fmt, - syn::{ - custom_keyword, parenthesized, - parse::{Parse, ParseStream}, - Token, - }, -}; - -/// Values that can be a calculaion (currently restricted to length & percentages) -#[derive(Debug, Clone, PartialEq)] -pub enum Calc { - Calculated(CalcSum), - Normal(LengthPercentage), -} - -impl fmt::Display for Calc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Calc::Calculated(inner) => write!(f, "calc({})", inner), - Calc::Normal(inner) => write!(f, "{}", inner), - } - } -} - -impl Parse for Calc { - fn parse(s: ParseStream) -> syn::Result { - custom_keyword!(calc); - if s.peek(calc) { - s.parse::()?; - let content; - parenthesized!(content in s); - Ok(Calc::Calculated(content.parse()?)) - } else { - Ok(Calc::Normal(s.parse()?)) - } - } -} - -impl ToTokens for Calc { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Calc::Calculated(inner) => quote!(style::Calc::Calculated(#inner)), - Calc::Normal(inner) => quote!(style::Calc::Normal(#inner)), - }); - } -} - -#[test] -fn test_calc() { - for (input, output) in vec![ - ("calc(10% - 20\"em\")", "calc(10% - 20em)"), - ("calc(100% + 5px)", "calc(100% + 5px)"), - ("calc(100% - 60px)", "calc(100% - 60px)"), - ] { - assert_eq!(&syn::parse_str::(input).unwrap().to_string(), output); - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct CalcSum { - pub first: CalcProduct, - pub rest: Vec, -} - -impl fmt::Display for CalcSum { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.first)?; - for op in self.rest.iter() { - write!(f, "{}", op)?; - } - Ok(()) - } -} - -impl Parse for CalcSum { - fn parse(s: ParseStream) -> syn::Result { - let first: CalcProduct = s.parse()?; - let mut rest: Vec = vec![]; - while SumOp::peek(s) { - rest.push(s.parse()?); - } - Ok(CalcSum { first, rest }) - } -} - -impl ToTokens for CalcSum { - fn to_tokens(&self, tokens: &mut TokenStream) { - let first = &self.first; - let rest = self.rest.iter(); - tokens.extend(quote! { - style::calc::CalcSum { - first: #first, - rest: vec![#(#rest,)*] - } - }); - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum SumOp { - Add(CalcProduct), - Sub(CalcProduct), -} - -impl SumOp { - fn peek(s: ParseStream) -> bool { - s.peek(Token![+]) || s.peek(Token![-]) - } -} - -impl fmt::Display for SumOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - SumOp::Add(inner) => write!(f, " + {}", inner), - SumOp::Sub(inner) => write!(f, " - {}", inner), - } - } -} - -impl Parse for SumOp { - fn parse(s: ParseStream) -> syn::Result { - let lookahead = s.lookahead1(); - if lookahead.peek(Token![+]) { - s.parse::()?; - Ok(SumOp::Add(s.parse()?)) - } else if lookahead.peek(Token![-]) { - s.parse::()?; - Ok(SumOp::Sub(s.parse()?)) - } else { - Err(lookahead.error()) - } - } -} - -impl ToTokens for SumOp { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - SumOp::Add(inner) => quote!(style::SumOp::Add(#inner)), - SumOp::Sub(inner) => quote!(style::SumOp::Sub(#inner)), - }); - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct CalcProduct { - pub first: CalcValue, - pub rest: Vec, -} - -impl fmt::Display for CalcProduct { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.first)?; - for op in self.rest.iter() { - write!(f, "{}", op)?; - } - Ok(()) - } -} - -impl Parse for CalcProduct { - fn parse(s: ParseStream) -> syn::Result { - let first: CalcValue = s.parse()?; - let mut rest: Vec = vec![]; - while ProductOp::peek(s) { - rest.push(s.parse()?); - } - Ok(CalcProduct { first, rest }) - } -} - -impl ToTokens for CalcProduct { - fn to_tokens(&self, tokens: &mut TokenStream) { - let first = &self.first; - let rest = self.rest.iter(); - tokens.extend(quote! { - style::calc::CalcProduct { - first: #first, - rest: vec![#(#rest,)*] - } - }); - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum ProductOp { - Mul(CalcValue), - // todo Div(Number), -} - -impl ProductOp { - pub fn peek(s: ParseStream) -> bool { - s.peek(Token![*]) // || s.peek(Token[/]) - } -} - -impl fmt::Display for ProductOp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ProductOp::Mul(inner) => write!(f, "*{}", inner), - //ProductOp::Div(inner) => write!(f, "/{}", inner), - } - } -} - -impl Parse for ProductOp { - fn parse(s: ParseStream) -> syn::Result { - let lookahead = s.lookahead1(); - if lookahead.peek(Token![*]) { - s.parse::()?; - Ok(ProductOp::Mul(s.parse()?)) - /* - } else if lookahead.peek(Token![/]) { - s.parse::()?; - Ok(ProductOp::Div(s.parse()?)) - */ - } else { - Err(lookahead.error()) - } - } -} - -impl ToTokens for ProductOp { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - ProductOp::Mul(inner) => quote!(style::ProductOp::Mul(#inner)), - //ProductOp::Div(inner) => quote!(style::ProductOp::Div(#inner)), - }); - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum CalcValue { - LengthPercentage(LengthPercentage), - // todo more variants -} - -impl Parse for CalcValue { - fn parse(s: ParseStream) -> syn::Result { - Ok(CalcValue::LengthPercentage(s.parse()?)) - } -} - -impl fmt::Display for CalcValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - CalcValue::LengthPercentage(inner) => write!(f, "{}", inner), - } - } -} - -impl ToTokens for CalcValue { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - CalcValue::LengthPercentage(inner) => { - quote!(style::CalcValue::LengthPercentage(#inner)) - } - }); - } -} diff --git a/packages/core-macro/styles/codegen.rs b/packages/core-macro/styles/codegen.rs deleted file mode 100644 index d4a399452..000000000 --- a/packages/core-macro/styles/codegen.rs +++ /dev/null @@ -1,1150 +0,0 @@ -use crate::*; -use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; - -macro_rules! path { - ($($t:tt)+) => { - ::quote::quote!(::style:: $($t)+) - }; -} - -impl ToTokens for DynamicStyles { - fn to_tokens(&self, tokens: &mut TokenStream) { - let parts = self - .rules - .iter() - .filter(|style| !style.is_dummy()) - .map(|style| style.to_token_stream()); - tokens.extend(quote! { - { - let mut styles = style::Styles::new(); - #(styles.push(#parts);)* - styles - } - }) - } -} - -impl ToTokens for DynamicStyle { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - DynamicStyle::Dynamic(block) => quote!(#block), - DynamicStyle::Literal(lit) => quote!(#lit), - }) - } -} - -impl ToTokens for Style { - fn to_tokens(&self, tokens: &mut TokenStream) { - let path = quote!(::style::Style::); - tokens.extend(match self { - Style::Dummy => quote!(#path Dummy), - Style::Unchecked(v) => quote!(#path Unchecked(String::from(#v))), - - Style::AlignContent(v) => quote!(#path AlignContent(#v)), - Style::AlignItems(v) => quote!(#path AlignItems(#v)), - Style::AlignSelf(v) => quote!(#path AlignSelf(#v)), - // all - // background - Style::BackgroundAttachment(v) => quote!(#path BackgroundAttachment(#v)), - Style::BackgroundBlendMode(v) => quote!(#path BackgroundBlendMode(#v)), - Style::BackgroundClip(v) => quote!(#path BackgroundClip(#v)), - Style::BackgroundColor(v) => quote!(#path BackgroundColor(#v)), - Style::BackgroundImage(v) => quote!(#path BackgroundImage(#v)), - Style::BackgroundOrigin(v) => quote!(#path BackgroundOrigin(#v)), - Style::BackgroundPosition(v) => quote!(#path BackgroundPosition(#v)), - Style::BackgroundRepeat(v) => quote!(#path BackgroundRepeat(#v)), - Style::BackgroundSize(v) => quote!(#path BackgroundSize(#v)), - Style::Border(v) => quote!(#path Border(#v)), - Style::BorderBottom(v) => quote!(#path BorderBottom(#v)), - Style::BorderBottomColor(v) => quote!(#path BorderBottomColor(#v)), - Style::BorderBottomLeftRadius(v) => quote!(#path BorderBottomLeftRadius(#v)), - Style::BorderBottomRightRadius(v) => quote!(#path BorderBottomRightRadius(#v)), - Style::BorderBottomStyle(v) => quote!(#path BorderBottomStyle(#v)), - Style::BorderBottomWidth(v) => quote!(#path BorderBottomWidth(#v)), - Style::BorderCollapse(v) => quote!(#path BorderCollapse(#v)), - Style::BorderColor(v) => quote!(#path BorderColor(#v)), - // border-image - // border-image-outset - // border-image-repeat - // border-image-slice - // border-image-source - // border-image-width - Style::BorderLeft(v) => quote!(#path BorderLeft(#v)), - Style::BorderLeftColor(v) => quote!(#path BorderLeftColor(#v)), - Style::BorderLeftStyle(v) => quote!(#path BorderLeftStyle(#v)), - Style::BorderLeftWidth(v) => quote!(#path BorderLeftWidth(#v)), - Style::BorderRadius(v) => quote!(#path BorderRadius(#v)), - Style::BorderRight(v) => quote!(#path BorderRight(#v)), - Style::BorderRightColor(v) => quote!(#path BorderRightColor(#v)), - Style::BorderRightStyle(v) => quote!(#path BorderRightStyle(#v)), - Style::BorderRightWidth(v) => quote!(#path BorderRightWidth(#v)), - // border-spacing - Style::BorderStyle(v) => quote!(#path BorderStyle(#v)), - Style::BorderTop(v) => quote!(#path BorderTop(#v)), - Style::BorderTopColor(v) => quote!(#path BorderTopColor(#v)), - Style::BorderTopLeftRadius(v) => quote!(#path BorderTopLeftRadius(#v)), - Style::BorderTopRightRadius(v) => quote!(#path BorderTopRightRadius(#v)), - Style::BorderTopStyle(v) => quote!(#path BorderTopStyle(#v)), - Style::BorderTopWidth(v) => quote!(#path BorderTopWidth(#v)), - Style::BorderWidth(v) => quote!(#path BorderWidth(#v)), - Style::Bottom(v) => quote!(#path Bottom(#v)), - // box-decoration-break - Style::BoxShadow(v) => quote!(#path BoxShadow(#v)), - Style::BoxSizing(v) => quote!(#path BoxSizing(#v)), - // break-after - // break-before - // break-inside - // caption-side - // caret-color - Style::Clear(v) => quote!(#path Clear(#v)), - // clip - // clip-path - // clip-rule - Style::ColumnCount(v) => quote!(#path ColumnCount(#v)), - Style::Color(v) => quote!(#path Color(#v)), - // contain - // content - // counter-increment - // counter-reset - // cue - // cue-after - // cue-before - Style::Cursor(v) => quote!(#path Cursor(#v)), - // direction - Style::Display(v) => quote!(#path Display(#v)), - // elevation - // empty-cells - // flex - Style::FlexBasis(v) => quote!(#path FlexBasis(#v)), - Style::FlexDirection(v) => quote!(#path FlexDirection(#v)), - // flex-flow - Style::FlexGrow(v) => quote!(#path FlexGrow(#v)), - Style::FlexShrink(v) => quote!(#path FlexShrink(#v)), - Style::FlexWrap(v) => quote!(#path FlexWrap(#v)), - Style::Float(v) => quote!(#path Float(#v)), - // font - Style::FontFamily(v) => quote!(#path FontFamily(#v)), - // font-feature-settings - // font-kerning - Style::FontSize(v) => quote!(#path FontSize(#v)), - // font-size-adjust - // font-stretch - Style::FontStyle(v) => quote!(#path FontStyle(#v)), - // font-synthesis - // font-variant - // font-variant-caps - // font-variant-east-asian - // font-variant-ligatures - // font-variant-numeric - // font-variant-position - Style::FontWeight(v) => quote!(#path FontWeight(#v)), - // glyph-orientation-vertical - // grid - // grid-area - // grid-auto-columns - // grid-auto-flow - // grid-auto-rows - // grid-column - // grid-column-end - // grid-column-start - // grid-row - // grid-row-end - // grid-row-start - // grid-template - // grid-template-areas - // grid-template-columns - // grid-template-rows - Style::Height(v) => quote!(#path Height(#v)), - // image-orientation - // image-rendering - // isolation - Style::JustifyContent(v) => quote!(#path JustifyContent(#v)), - Style::Left(v) => quote!(#path Left(#v)), - // letter-spacing - Style::LineHeight(v) => quote!(#path LineHeight(#v)), - // list-style - // list-style-image - // list-style-position - Style::ListStyleType(v) => quote!(#path ListStyleType(#v)), - Style::Margin(v) => quote!(#path Margin(#v)), - Style::MarginBottom(v) => quote!(#path MarginBottom(#v)), - Style::MarginLeft(v) => quote!(#path MarginLeft(#v)), - Style::MarginRight(v) => quote!(#path MarginRight(#v)), - Style::MarginTop(v) => quote!(#path MarginTop(#v)), - // mask - // mask-border - // mask-border-mode - // mask-border-outset - // mask-border-repeat - // mask-border-slice - // mask-border-source - // mask-border-width - // mask-clip - // mask-composite - // mask-image - // mask-mode - // mask-origin - // mask-position - // mask-repeat - // mask-size - // mask-type - Style::MaxHeight(v) => quote!(#path MaxHeight(#v)), - Style::MaxWidth(v) => quote!(#path MaxWidth(#v)), - Style::MinHeight(v) => quote!(#path MinHeight(#v)), - Style::MinWidth(v) => quote!(#path MinWidth(#v)), - // mix-blend-mode - Style::ObjectFit(v) => quote!(#path ObjectFit(#v)), - // object-position - // opacity - // order - // orphans - // outline - // outline-color - // outline-offset - // outline-style - // outline-width - Style::Overflow(v) => quote!(#path Overflow(#v)), - Style::OverflowX(v) => quote!(#path OverflowX(#v)), - Style::OverflowY(v) => quote!(#path OverflowY(#v)), - Style::Padding(v) => quote!(#path Padding(#v)), - Style::PaddingBottom(v) => quote!(#path PaddingBottom(#v)), - Style::PaddingLeft(v) => quote!(#path PaddingLeft(#v)), - Style::PaddingRight(v) => quote!(#path PaddingRight(#v)), - Style::PaddingTop(v) => quote!(#path PaddingTop(#v)), - // page-break-after - // page-break-before - // page-break-inside - // pause - // pause-after - // pause-before - // pitch - // pitch-range - // play-during - Style::Position(v) => quote!(#path Position(#v)), - // quotes - Style::Resize(v) => quote!(#path Resize(#v)), - // richness - Style::Right(v) => quote!(#path Right(#v)), - // scroll-margin - // scroll-margin-block - // scroll-margin-block-end - // scroll-margin-block-start - // scroll-margin-bottom - // scroll-margin-inline - // scroll-margin-inline-end - // scroll-margin-inline-start - // scroll-margin-left - // scroll-margin-right - // scroll-margin-top - // scroll-padding - // scroll-padding-block - // scroll-padding-block-end - // scroll-padding-block-start - // scroll-padding-bottom - // scroll-padding-inline - // scroll-padding-inline-end - // scroll-padding-inline-start - // scroll-padding-left - // scroll-padding-right - // scroll-padding-top - // scroll-snap-align - // scroll-snap-stop - // scroll-snap-type - // shape-image-threshold - // shape-margin - // shape-outside - // speak - // speak-header - // speak-numeral - // speak-punctuation - // speech-rate - // stress - // table-layout - Style::TextAlign(v) => quote!(#path TextAlign(#v)), - // text-combine-upright - // text-decoration - // text-decoration-color - // text-decoration-line - // text-decoration-style - // text-emphasis - // text-emphasis-color - // text-emphasis-position - // text-emphasis-style - // text-indent - // text-orientation - // text-overflow - // text-shadow - // text-transform - // text-underline-position - Style::Top(v) => quote!(#path Top(#v)), - // transform - // transform-box - // transform-origin - // unicode-bidi - // vertical-align - // visibility - // voice-family - // volume - // white-space - Style::WhiteSpace(v) => quote!(#path WhiteSpace(#v)), - Style::Widows(v) => quote!(#path Widows(#v)), - Style::Width(v) => quote!(#path Width(#v)), - // will-change - // word-spacing - // writing-mode - // z-index - }); - } -} - -impl ToTokens for AlignContent { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - AlignContent::FlexStart => path!(AlignContent::FlexStart), - AlignContent::Center => path!(AlignContent::Center), - AlignContent::FlexEnd => path!(style::AlignContent::FlexEnd), - AlignContent::SpaceAround => path!(AlignContent::SpaceAround), - AlignContent::SpaceBetween => path!(AlignContent::SpaceBetween), - AlignContent::Stretch => path!(AlignContent::Stretch), - }); - } -} - -impl ToTokens for Cursor { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Cursor::Auto => path!(Cursor::Auto), - Cursor::Default => path!(Cursor::Default), - Cursor::None => path!(Cursor::None), - Cursor::ContextMenu => path!(Cursor::ContextMenu), - Cursor::Help => path!(Cursor::Help), - Cursor::Pointer => path!(Cursor::Pointer), - Cursor::Progress => path!(Cursor::Progress), - Cursor::Wait => path!(Cursor::Wait), - Cursor::Cell => path!(Cursor::Cell), - Cursor::Crosshair => path!(Cursor::Crosshair), - Cursor::Text => path!(Cursor::Text), - Cursor::VerticalText => path!(Cursor::VerticalText), - Cursor::Alias => path!(Cursor::Alias), - Cursor::Copy => path!(Cursor::Copy), - Cursor::Move => path!(Cursor::Move), - Cursor::NoDrop => path!(Cursor::NoDrop), - Cursor::NotAllowed => path!(Cursor::NotAllowed), - Cursor::Grab => path!(Cursor::Grab), - Cursor::Grabbing => path!(Cursor::Grabbing), - Cursor::EResize => path!(Cursor::EResize), - Cursor::NResize => path!(Cursor::NResize), - Cursor::NEResize => path!(Cursor::NEResize), - Cursor::NWResize => path!(Cursor::NWResize), - Cursor::SResize => path!(Cursor::SResize), - Cursor::SEResize => path!(Cursor::SEResize), - Cursor::SWResize => path!(Cursor::SWResize), - Cursor::WResize => path!(Cursor::WResize), - Cursor::EWResize => path!(Cursor::EWResize), - Cursor::NSResize => path!(Cursor::NSResize), - Cursor::NESWResize => path!(Cursor::NESWResize), - Cursor::NWSEResize => path!(Cursor::NWSEResize), - Cursor::ColResize => path!(Cursor::ColResize), - Cursor::RowResize => path!(Cursor::RowResize), - Cursor::AllScroll => path!(Cursor::AllScroll), - Cursor::ZoomIn => path!(Cursor::ZoomIn), - Cursor::ZoomOut => path!(Cursor::ZoomOut), - }) - } -} - -impl ToTokens for Display { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Display::Block => path!(Display::Block), - Display::Flex => path!(Display::Flex), - Display::Inline => path!(Display::Inline), - }); - } -} - -impl ToTokens for FlexBasis { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - FlexBasis::Width(v) => path!(FlexBasis::Width(#v)), - FlexBasis::Content => path!(FlexBasis::Content), - }); - } -} - -impl ToTokens for FlexDirection { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - FlexDirection::Row => path!(FlexDirection::Row), - FlexDirection::Column => path!(FlexDirection::Column), - }); - } -} - -impl ToTokens for FlexWrap { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - FlexWrap::Wrap => path!(FlexWrap::Wrap), - FlexWrap::Nowrap => path!(FlexWrap::Nowrap), - }); - } -} - -impl ToTokens for AlignItems { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - AlignItems::Normal => path!(AlignItems::Normal), - AlignItems::Stretch => path!(AlignItems::Stretch), - AlignItems::Center => path!(AlignItems::Center), - AlignItems::Start => path!(AlignItems::Start), - AlignItems::End => path!(AlignItems::End), - AlignItems::FlexStart => path!(AlignItems::FlexStart), - AlignItems::FlexEnd => path!(AlignItems::FlexEnd), - AlignItems::Baseline => path!(AlignItems::Baseline), - AlignItems::FirstBaseline => path!(AlignItems::FirstBaseline), - AlignItems::LastBaseline => path!(AlignItems::LastBaseline), - AlignItems::SafeCenter => path!(AlignItems::SafeCenter), - AlignItems::UnsafeCenter => path!(AlignItems::UnsafeCenter), - }); - } -} - -impl ToTokens for AlignSelf { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - AlignSelf::Auto => path!(AlignSelf::Auto), - AlignSelf::Normal => path!(AlignSelf::Normal), - AlignSelf::Center => path!(AlignSelf::Center), - AlignSelf::Start => path!(AlignSelf::Start), - AlignSelf::End => path!(AlignSelf::End), - AlignSelf::SelfStart => path!(AlignSelf::SelfStart), - AlignSelf::SelfEnd => path!(AlignSelf::SelfEnd), - AlignSelf::FlexStart => path!(AlignSelf::FlexStart), - AlignSelf::FlexEnd => path!(AlignSelf::FlexEnd), - AlignSelf::Baseline => path!(AlignSelf::Baseline), - AlignSelf::FirstBaseline => path!(AlignSelf::FirstBaseline), - AlignSelf::LastBaseline => path!(AlignSelf::LastBaseline), - AlignSelf::Stretch => path!(AlignSelf::Stretch), - AlignSelf::SafeCenter => path!(AlignSelf::SafeCenter), - AlignSelf::UnsafeCenter => path!(AlignSelf::UnsafeCenter), - }); - } -} - -impl ToTokens for BackgroundAttachment { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BackgroundAttachment::Scroll => path!(BackgroundAttachment::Scroll), - BackgroundAttachment::Fixed => path!(BackgroundAttachment::Fixed), - BackgroundAttachment::Local => path!(BackgroundAttachment::Local), - }) - } -} - -impl ToTokens for BlendMode { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BlendMode::Normal => path!(BlendMode::Normal), - BlendMode::Multiply => path!(BlendMode::Multiply), - BlendMode::Screen => path!(BlendMode::Screen), - BlendMode::Overlay => path!(BlendMode::Overlay), - BlendMode::Darken => path!(BlendMode::Darken), - BlendMode::Lighten => path!(BlendMode::Lighten), - BlendMode::ColorDodge => path!(BlendMode::ColorDodge), - BlendMode::ColorBurn => path!(BlendMode::ColorBurn), - BlendMode::HardLight => path!(BlendMode::HardLight), - BlendMode::SoftLight => path!(BlendMode::SoftLight), - BlendMode::Difference => path!(BlendMode::Difference), - BlendMode::Exclusion => path!(BlendMode::Exclusion), - BlendMode::Hue => path!(BlendMode::Hue), - BlendMode::Saturation => path!(BlendMode::Saturation), - BlendMode::Color => path!(BlendMode::Color), - BlendMode::Luminosity => path!(BlendMode::Luminosity), - }) - } -} - -impl ToTokens for BackgroundBox { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BackgroundBox::BorderBox => path!(BackgroundBox::BorderBox), - BackgroundBox::PaddingBox => path!(BackgroundBox::PaddingBox), - BackgroundBox::ContentBox => path!(BackgroundBox::ContentBox), - }) - } -} - -impl ToTokens for BackgroundImage { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BackgroundImage::None => path!(BackgroundImage::None), - BackgroundImage::Url(url) => path!(BackgroundImage::Url(#url)), - }) - } -} - -impl ToTokens for BackgroundPosition { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BackgroundPosition::Top => path!(BackgroundPosition::Top), - BackgroundPosition::Bottom => path!(BackgroundPosition::Bottom), - BackgroundPosition::Left => path!(BackgroundPosition::Left), - BackgroundPosition::Right => path!(BackgroundPosition::Right), - BackgroundPosition::Center => path!(BackgroundPosition::Center), - }) - } -} - -impl ToTokens for BackgroundRepeat { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BackgroundRepeat::RepeatX => path!(BackgroundRepeat::RepeatX), - BackgroundRepeat::RepeatY => path!(BackgroundRepeat::RepeatY), - BackgroundRepeat::SingleOrDouble(v) => path!(BackgroundRepeat::SingleOrDouble(#v)), - }) - } -} - -impl ToTokens for BgRepeatPart { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BgRepeatPart::Repeat => path!(BgRepeatPart::Repeat), - BgRepeatPart::Space => path!(BgRepeatPart::Space), - BgRepeatPart::Round => path!(BgRepeatPart::Round), - BgRepeatPart::NoRepeat => path!(BgRepeatPart::NoRepeat), - }) - } -} - -impl ToTokens for BackgroundSize { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BackgroundSize::Cover => path!(BackgroundSize::Cover), - BackgroundSize::Contain => path!(BackgroundSize::Contain), - BackgroundSize::SingleOrDouble(v) => path!(BackgroundSize::SingleOrDouble(#v)), - }) - } -} - -impl ToTokens for BorderCollapse { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BorderCollapse::Collapse => path!(BorderCollapse::Collapse), - BorderCollapse::Separate => path!(BorderCollapse::Separate), - }) - } -} - -impl ToTokens for JustifyContent { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - JustifyContent::FlexStart => path!(JustifyContent::FlexStart), - JustifyContent::Center => path!(JustifyContent::Center), - JustifyContent::FlexEnd => path!(JustifyContent::FlexEnd), - JustifyContent::SpaceAround => path!(JustifyContent::SpaceAround), - JustifyContent::SpaceBetween => path!(JustifyContent::SpaceBetween), - }); - } -} - -impl ToTokens for Float { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Float::None => path!(Float::None), - Float::Left => path!(Float::Left), - Float::Right => path!(Float::Right), - Float::InlineStart => path!(Float::InlineStart), - Float::InlineEnd => path!(Float::InlineEnd), - }) - } -} - -impl ToTokens for FontWeight { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - FontWeight::Normal => path!(FontWeight::Normal), - FontWeight::Bold => path!(FontWeight::Bold), - FontWeight::Lighter => path!(FontWeight::Lighter), - FontWeight::Bolder => path!(FontWeight::Bolder), - FontWeight::Number(v) => path!(FontWeight::Number(#v)), - }); - } -} - -impl ToTokens for Font { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Font::Named(inner) => path!(Font::Named(String::from(#inner))), - Font::Serif => path!(Font::Serif), - Font::SansSerif => path!(Font::SansSerif), - Font::Cursive => path!(Font::Cursive), - Font::Fantasy => path!(Font::Fantasy), - Font::Monospace => path!(Font::Monospace), - }) - } -} - -impl ToTokens for FontSize { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - FontSize::XXSmall => path!(FontSize::XXSmall), - FontSize::XSmall => path!(FontSize::XSmall), - FontSize::Small => path!(FontSize::Small), - FontSize::Medium => path!(FontSize::Medium), - FontSize::Large => path!(FontSize::Large), - FontSize::XLarge => path!(FontSize::XLarge), - FontSize::XXLarge => path!(FontSize::XXLarge), - FontSize::XXXLarge => path!(FontSize::XXXLarge), - FontSize::Larger => path!(FontSize::Larger), - FontSize::Smaller => path!(FontSize::Smaller), - FontSize::LengthPercentage(v) => path!(FontSize::LengthPercentage(#v)), - }); - } -} - -impl ToTokens for FontStyle { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - FontStyle::Normal => path!(FontStyle::Normal), - FontStyle::Italic => path!(FontStyle::Italic), - FontStyle::Oblique => path!(FontStyle::Oblique), - }); - } -} - -impl ToTokens for Border { - fn to_tokens(&self, tokens: &mut TokenStream) { - let line_width = match self.line_width { - Some(line_width) => quote!(Some(#line_width)), - None => quote!(None), - }; - let line_style = match self.line_style { - Some(line_style) => quote!(Some(#line_style)), - None => quote!(None), - }; - let color = match self.color { - Some(color) => quote!(Some(#color)), - None => quote!(None), - }; - tokens.extend(quote!( - style::Border { - line_width: #line_width, - line_style: #line_style, - color: #color, - } - )) - } -} - -impl ToTokens for BoxShadow { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BoxShadow::None => path!(BoxShadow::None), - BoxShadow::Shadows(list) => path!(BoxShadow::Shadows(#list)), - }); - } -} - -impl ToTokens for BoxSizing { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - BoxSizing::BorderBox => path!(BoxSizing::BorderBox), - BoxSizing::ContentBox => path!(BoxSizing::ContentBox), - }); - } -} - -impl ToTokens for Clear { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Clear::None => path!(Clear::None), - Clear::Left => path!(Clear::Left), - Clear::Right => path!(Clear::Right), - Clear::Both => path!(Clear::Both), - Clear::InlineStart => path!(Clear::InlineStart), - Clear::InlineEnd => path!(Clear::InlineEnd), - }) - } -} - -impl ToTokens for ColumnCount { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - ColumnCount::Auto => path!(ColumnCount::Auto), - ColumnCount::Fixed(v) => path!(ColumnCount::Fixed(#v)), - }) - } -} - -impl ToTokens for Overflow { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Overflow::Both(v) => path!(Overflow::Both(#v)), - Overflow::XY(x, y) => path!(Overflow::XY(#x, #y)), - }) - } -} - -impl ToTokens for OverflowXY { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - OverflowXY::Visible => path!(OverflowXY::Visible), - OverflowXY::Hidden => path!(OverflowXY::Hidden), - OverflowXY::Clip => path!(OverflowXY::Clip), - OverflowXY::Scroll => path!(OverflowXY::Scroll), - OverflowXY::Auto => path!(OverflowXY::Auto), - }) - } -} - -impl ToTokens for ObjectFit { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - ObjectFit::Fill => path!(ObjectFit::Fill), - ObjectFit::None => path!(ObjectFit::None), - ObjectFit::Contain { scale_down } => { - path!(ObjectFit::Contain { scale_down: #scale_down }) - } - ObjectFit::Cover { scale_down } => path!(ObjectFit::Cover { scale_down: #scale_down }), - }) - } -} - -impl ToTokens for Rect -where - T: ToTokens, -{ - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Rect::All(v) => path!(Rect::All(#v)), - Rect::VerticalHorizontal(v, h) => path!(Rect::VerticalHorizontal(#v, #h)), - Rect::TopHorizontalBottom(t, h, b) => path!(Rect::TopHorizontalBottom(#t, #h, #b)), - Rect::TopRightBottomLeft(t, r, b, l) => path!(Rect::TopRightBottomLeft(#t, #r, #b, #l)), - }); - } -} - -impl ToTokens for LengthPercentage { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - LengthPercentage::Length(v) => path!(LengthPercentage::Length(#v)), - LengthPercentage::Percentage(v) => path!(LengthPercentage::Percentage(#v)), - }); - } -} - -impl ToTokens for AutoLengthPercentage { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - AutoLengthPercentage::LengthPercentage(v) => { - path!(AutoLengthPercentage::LengthPercentage(#v)) - } - AutoLengthPercentage::Auto => path!(AutoLengthPercentage::Auto), - }); - } -} - -impl ToTokens for LineStyle { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - LineStyle::None => path!(LineStyle::None), - LineStyle::Hidden => path!(LineStyle::Hidden), - LineStyle::Dotted => path!(LineStyle::Dotted), - LineStyle::Dashed => path!(LineStyle::Dashed), - LineStyle::Solid => path!(LineStyle::Solid), - LineStyle::Double => path!(LineStyle::Double), - LineStyle::Groove => path!(LineStyle::Groove), - LineStyle::Ridge => path!(LineStyle::Ridge), - LineStyle::Inset => path!(LineStyle::Inset), - LineStyle::Outset => path!(LineStyle::Outset), - }) - } -} - -impl ToTokens for LineWidth { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - LineWidth::Length(length) => path!(LineWidth::Length(#length)), - LineWidth::Thin => path!(LineWidth::Thin), - LineWidth::Medium => path!(LineWidth::Medium), - LineWidth::Thick => path!(LineWidth::Thick), - }) - } -} - -impl ToTokens for LineHeight { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.0.to_tokens(tokens) - } -} - -impl ToTokens for ListStyleType { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - ListStyleType::Disc => path!(ListStyleType::Disc), - ListStyleType::Circle => path!(ListStyleType::Circle), - ListStyleType::Square => path!(ListStyleType::Square), - ListStyleType::Decimal => path!(ListStyleType::Decimal), - ListStyleType::DecimalLeadingZero => path!(ListStyleType::DecimalLeadingZero), - ListStyleType::LowerRoman => path!(ListStyleType::LowerRoman), - ListStyleType::UpperRoman => path!(ListStyleType::UpperRoman), - ListStyleType::LowerGreek => path!(ListStyleType::LowerGreek), - ListStyleType::UpperGreek => path!(ListStyleType::UpperGreek), - ListStyleType::LowerLatin => path!(ListStyleType::LowerLatin), - ListStyleType::UpperLatin => path!(ListStyleType::UpperLatin), - ListStyleType::Armenian => path!(ListStyleType::Armenian), - ListStyleType::Georgian => path!(ListStyleType::Georgian), - ListStyleType::LowerAlpha => path!(ListStyleType::LowerAlpha), - ListStyleType::UpperAlpha => path!(ListStyleType::UpperAlpha), - ListStyleType::None => path!(ListStyleType::None), - }) - } -} - -impl ToTokens for Position { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Position::Static => path!(Position::Static), - Position::Relative => path!(Position::Relative), - Position::Absolute => path!(Position::Absolute), - Position::Fixed => path!(Position::Fixed), - }) - } -} - -impl ToTokens for Resize { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Resize::None => path!(Resize::None), - Resize::Both => path!(Resize::Both), - Resize::Horizontal => path!(Resize::Horizontal), - Resize::Vertical => path!(Resize::Vertical), - }) - } -} - -impl ToTokens for WhiteSpace { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - WhiteSpace::Normal => path!(WhiteSpace::Normal), - WhiteSpace::Pre => path!(WhiteSpace::Pre), - WhiteSpace::Nowrap => path!(WhiteSpace::Nowrap), - WhiteSpace::PreWrap => path!(WhiteSpace::PreWrap), - WhiteSpace::PreLine => path!(WhiteSpace::PreLine), - }) - } -} - -impl ToTokens for WidthHeight { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - WidthHeight::Auto => path!(WidthHeight::Auto), - WidthHeight::LengthPercentage(v) => path!(WidthHeight::LengthPercentage(#v)), - WidthHeight::MinContent => path!(WidthHeight::MinContent), - WidthHeight::MaxContent => path!(WidthHeight::MaxContent), - WidthHeight::FitContent(v) => path!(WidthHeight::FitContent(#v)), - }) - } -} - -impl ToTokens for MaxWidthHeight { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - MaxWidthHeight::None => path!(MaxWidthHeight::None), - MaxWidthHeight::LengthPercentage(v) => path!(MaxWidthHeight::LengthPercentage(#v)), - MaxWidthHeight::MinContent => path!(MaxWidthHeight::MinContent), - MaxWidthHeight::MaxContent => path!(MaxWidthHeight::MaxContent), - MaxWidthHeight::FitContent(v) => path!(MaxWidthHeight::FitContent(#v)), - }) - } -} - -impl ToTokens for Width21 { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Width21::Auto => path!(Width21::Auto), - Width21::LengthPercentage(v) => path!(Width21::LengthPercentage(#v)), - }) - } -} - -impl ToTokens for Shadow { - fn to_tokens(&self, tokens: &mut TokenStream) { - let color = match self.color.as_ref() { - Some(color) => quote!(Some(#color)), - None => quote!(None), - }; - let length = &self.length; - let inset = &self.inset; - tokens.extend(quote! { - style::Shadow { - color: #color, - length: #length, - inset: #inset, - } - }) - } -} - -impl ToTokens for ShadowLength { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - ShadowLength::Offsets { - vertical, - horizontal, - } => path!(ShadowLength::Offsets { - vertical: #vertical, - horizontal: #horizontal, - }), - ShadowLength::OffsetsBlur { - vertical, - horizontal, - blur, - } => path!(ShadowLength::OffsetsBlur { - vertical: #vertical, - horizontal: #horizontal, - blur: #blur, - }), - ShadowLength::OffsetsBlurSpread { - vertical, - horizontal, - blur, - spread, - } => path!(ShadowLength::Offsets { - vertical: #vertical, - horizontal: #horizontal, - blur: #blur, - spread: #spread, - }), - }) - } -} - -impl ToTokens for TextAlign { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - TextAlign::Left => path!(TextAlign::Left), - TextAlign::Right => path!(TextAlign::Right), - TextAlign::Center => path!(TextAlign::Center), - TextAlign::Justify => path!(TextAlign::Justify), - }); - } -} - -impl ToTokens for Length { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Length::Em(v) => path!(Length::Em(#v)), - Length::Ex(v) => path!(Length::Ex(#v)), - Length::In(v) => path!(Length::In(#v)), - Length::Cm(v) => path!(Length::Cm(#v)), - Length::Mm(v) => path!(Length::Mm(#v)), - Length::Pt(v) => path!(Length::Pt(#v)), - Length::Pc(v) => path!(Length::Pc(#v)), - Length::Px(v) => path!(Length::Px(#v)), - Length::Zero => path!(Length::Zero), - }) - } -} - -impl ToTokens for Percentage { - fn to_tokens(&self, tokens: &mut TokenStream) { - let val = self.0; - tokens.extend(path!(Percentage(#val))); - } -} - -impl ToTokens for DynamicColor { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - DynamicColor::Dynamic(block) => path!(DynamicColor::Literal(#block)), - DynamicColor::Literal(color) => path!(DynamicColor::Literal(#color)), - }) - } -} - -impl ToTokens for Color { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - Color::HexRGB(r, g, b) => path!(Color::HexRGB(#r, #g, #b)), - Color::HexRGBA(r, g, b, a) => path!(Color::HexRGB(#r, #g, #b, #a)), - Color::HSL(h, s, l) => path!(Color::HSL(#h, #s, #l)), - Color::HSLA(h, s, l, a) => path!(Color::HSLA(#h, #s, #l, #a)), - Color::IndianRed => path!(Color::IndianRed), - Color::LightCoral => path!(Color::LightCoral), - Color::Salmon => path!(Color::Salmon), - Color::DarkSalmon => path!(Color::DarkSalmon), - Color::LightSalmon => path!(Color::LightSalmon), - Color::Crimson => path!(Color::Crimson), - Color::Red => path!(Color::Red), - Color::FireBrick => path!(Color::FireBrick), - Color::DarkRed => path!(Color::DarkRed), - Color::Pink => path!(Color::Pink), - Color::LightPink => path!(Color::LightPink), - Color::HotPink => path!(Color::HotPink), - Color::DeepPink => path!(Color::DeepPink), - Color::MediumVioletRed => path!(Color::MediumVioletRed), - Color::PaleVioletRed => path!(Color::PaleVioletRed), - Color::Coral => path!(Color::Coral), - Color::Tomato => path!(Color::Tomato), - Color::OrangeRed => path!(Color::OrangeRed), - Color::DarkOrange => path!(Color::DarkOrange), - Color::Orange => path!(Color::Orange), - Color::Gold => path!(Color::Gold), - Color::Yellow => path!(Color::Yellow), - Color::LightYellow => path!(Color::LightYellow), - Color::LemonChiffon => path!(Color::LemonChiffon), - Color::LightGoldenrodYellow => path!(Color::LightGoldenrodYellow), - Color::PapayaWhip => path!(Color::PapayaWhip), - Color::Moccasin => path!(Color::Moccasin), - Color::PeachPuff => path!(Color::PeachPuff), - Color::PaleGoldenrod => path!(Color::PaleGoldenrod), - Color::Khaki => path!(Color::Khaki), - Color::DarkKhaki => path!(Color::DarkKhaki), - Color::Lavender => path!(Color::Lavender), - Color::Thistle => path!(Color::Thistle), - Color::Plum => path!(Color::Plum), - Color::Violet => path!(Color::Violet), - Color::Orchid => path!(Color::Orchid), - Color::Fuchsia => path!(Color::Fuchsia), - Color::Magenta => path!(Color::Magenta), - Color::MediumOrchid => path!(Color::MediumOrchid), - Color::MediumPurple => path!(Color::MediumPurple), - Color::RebeccaPurple => path!(Color::RebeccaPurple), - Color::BlueViolet => path!(Color::BlueViolet), - Color::DarkViolet => path!(Color::DarkViolet), - Color::DarkOrchid => path!(Color::DarkOrchid), - Color::DarkMagenta => path!(Color::DarkMagenta), - Color::Purple => path!(Color::Purple), - Color::Indigo => path!(Color::Indigo), - Color::SlateBlue => path!(Color::SlateBlue), - Color::DarkSlateBlue => path!(Color::DarkSlateBlue), - Color::MediumSlateBlue => path!(Color::MediumSlateBlue), - Color::GreenYellow => path!(Color::GreenYellow), - Color::Chartreuse => path!(Color::Chartreuse), - Color::LawnGreen => path!(Color::LawnGreen), - Color::Lime => path!(Color::Lime), - Color::LimeGreen => path!(Color::LimeGreen), - Color::PaleGreen => path!(Color::PaleGreen), - Color::LightGreen => path!(Color::LightGreen), - Color::MediumSpringGreen => path!(Color::MediumSpringGreen), - Color::SpringGreen => path!(Color::SpringGreen), - Color::MediumSeaGreen => path!(Color::MediumSeaGreen), - Color::SeaGreen => path!(Color::SeaGreen), - Color::ForestGreen => path!(Color::ForestGreen), - Color::Green => path!(Color::Green), - Color::DarkGreen => path!(Color::DarkGreen), - Color::YellowGreen => path!(Color::YellowGreen), - Color::OliveDrab => path!(Color::OliveDrab), - Color::Olive => path!(Color::Olive), - Color::DarkOliveGreen => path!(Color::DarkOliveGreen), - Color::MediumAquamarine => path!(Color::MediumAquamarine), - Color::DarkSeaGreen => path!(Color::DarkSeaGreen), - Color::LightSeaGreen => path!(Color::LightSeaGreen), - Color::DarkCyan => path!(Color::DarkCyan), - Color::Teal => path!(Color::Teal), - Color::Aqua => path!(Color::Aqua), - Color::Cyan => path!(Color::Cyan), - Color::LightCyan => path!(Color::LightCyan), - Color::PaleTurquoise => path!(Color::PaleTurquoise), - Color::Aquamarine => path!(Color::Aquamarine), - Color::Turquoise => path!(Color::Turquoise), - Color::MediumTurquoise => path!(Color::MediumTurquoise), - Color::DarkTurquoise => path!(Color::DarkTurquoise), - Color::CadetBlue => path!(Color::CadetBlue), - Color::SteelBlue => path!(Color::SteelBlue), - Color::LightSteelBlue => path!(Color::LightSteelBlue), - Color::PowderBlue => path!(Color::PowderBlue), - Color::LightBlue => path!(Color::LightBlue), - Color::SkyBlue => path!(Color::SkyBlue), - Color::LightSkyBlue => path!(Color::LightSkyBlue), - Color::DeepSkyBlue => path!(Color::DeepSkyBlue), - Color::DodgerBlue => path!(Color::DodgerBlue), - Color::CornflowerBlue => path!(Color::CornflowerBlue), - Color::RoyalBlue => path!(Color::RoyalBlue), - Color::Blue => path!(Color::Blue), - Color::MediumBlue => path!(Color::MediumBlue), - Color::DarkBlue => path!(Color::DarkBlue), - Color::Navy => path!(Color::Navy), - Color::MidnightBlue => path!(Color::MidnightBlue), - Color::Cornsilk => path!(Color::Cornsilk), - Color::BlanchedAlmond => path!(Color::BlanchedAlmond), - Color::Bisque => path!(Color::Bisque), - Color::NavajoWhite => path!(Color::NavajoWhite), - Color::Wheat => path!(Color::Wheat), - Color::BurlyWood => path!(Color::BurlyWood), - Color::Tan => path!(Color::Tan), - Color::RosyBrown => path!(Color::RosyBrown), - Color::SandyBrown => path!(Color::SandyBrown), - Color::Goldenrod => path!(Color::Goldenrod), - Color::DarkGoldenrod => path!(Color::DarkGoldenrod), - Color::Peru => path!(Color::Peru), - Color::Chocolate => path!(Color::Chocolate), - Color::SaddleBrown => path!(Color::SaddleBrown), - Color::Sienna => path!(Color::Sienna), - Color::Brown => path!(Color::Brown), - Color::Maroon => path!(Color::Maroon), - Color::White => path!(Color::White), - Color::Snow => path!(Color::Snow), - Color::HoneyDew => path!(Color::HoneyDew), - Color::MintCream => path!(Color::MintCream), - Color::Azure => path!(Color::Azure), - Color::AliceBlue => path!(Color::AliceBlue), - Color::GhostWhite => path!(Color::GhostWhite), - Color::WhiteSmoke => path!(Color::WhiteSmoke), - Color::SeaShell => path!(Color::SeaShell), - Color::Beige => path!(Color::Beige), - Color::OldLace => path!(Color::OldLace), - Color::FloralWhite => path!(Color::FloralWhite), - Color::Ivory => path!(Color::Ivory), - Color::AntiqueWhite => path!(Color::AntiqueWhite), - Color::Linen => path!(Color::Linen), - Color::LavenderBlush => path!(Color::LavenderBlush), - Color::MistyRose => path!(Color::MistyRose), - Color::Gainsboro => path!(Color::Gainsboro), - Color::LightGray => path!(Color::LightGray), - Color::Silver => path!(Color::Silver), - Color::DarkGray => path!(Color::DarkGray), - Color::Gray => path!(Color::Gray), - Color::DimGray => path!(Color::DimGray), - Color::LightSlateGray => path!(Color::LightSlateGray), - Color::SlateGray => path!(Color::SlateGray), - Color::DarkSlateGray => path!(Color::DarkSlateGray), - Color::Black => path!(Color::Black), - }) - } -} - -// Generic containers - -impl ToTokens for NonemptyCommaList -where - T: ToTokens, -{ - fn to_tokens(&self, tokens: &mut TokenStream) { - let first = &self.first; - let rest = &self.rest; - tokens.extend(path! { - NonemptyCommaList { - first: #first, - rest: vec![#(#rest),*], - } - }) - } -} - -impl ToTokens for SingleOrDouble -where - T: ToTokens, -{ - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend(match self { - SingleOrDouble::Single(t) => path!(SingleOrDouble::Single(#t)), - SingleOrDouble::Double { vert, horiz } => path!(SingleOrDouble::Double { - vert: #vert, - horiz: #horiz, - }), - }) - } -} diff --git a/packages/core-macro/styles/color.rs b/packages/core-macro/styles/color.rs deleted file mode 100644 index 15bc7cfb5..000000000 --- a/packages/core-macro/styles/color.rs +++ /dev/null @@ -1,854 +0,0 @@ -use std::fmt; - -/// A color that possibly is possibly code, rather than a literal -#[derive(Debug, Clone, PartialEq)] -pub enum DynamicColor { - Literal(Color), - /// The type of the block is not checked here (it is checked by typeck). - Dynamic(syn::Block), -} - -impl DynamicColor { - pub fn is_dynamic(&self) -> bool { - match self { - DynamicColor::Dynamic(_) => true, - DynamicColor::Literal(_) => false, - } - } -} - -impl fmt::Display for DynamicColor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - DynamicColor::Dynamic(_) => Ok(()), - DynamicColor::Literal(color) => color.fmt(f), - } - } -} - -// TODO other color variants. -#[derive(Debug, Clone, Copy, PartialEq)] -#[non_exhaustive] -pub enum Color { - HexRGB(u8, u8, u8), - HexRGBA(u8, u8, u8, u8), - // Invariants: `0 <= .0 < 360`, `0 <= .1 < 100`, `0 <= .2 < 100`. - HSL(f64, f64, f64), - // Invariants: `0 <= .0 < 360`, `0 <= .1 < 100`, `0 <= .2 < 100`, `0 <= .3 < 1`. - HSLA(f64, f64, f64, f64), - - // Red HTML Color Names - /// rgb(205, 92, 92) - IndianRed, - /// rgb(240, 128, 128) - LightCoral, - /// rgb(250, 128, 114) - Salmon, - /// rgb(233, 150, 122) - DarkSalmon, - /// rgb(255, 160, 122) - LightSalmon, - /// rgb(220, 20, 60) - Crimson, - /// rgb(255, 0, 0) - Red, - /// rgb(178, 34, 34) - FireBrick, - /// rgb(139, 0, 0) - DarkRed, - // Pink HTML Color Names - /// rgb(255, 192, 203) - Pink, - /// rgb(255, 182, 193) - LightPink, - /// rgb(255, 105, 180) - HotPink, - /// rgb(255, 20, 147) - DeepPink, - /// rgb(199, 21, 133) - MediumVioletRed, - /// rgb(219, 112, 147) - PaleVioletRed, - //Orange HTML Color Names - // /// rgb(255, 160, 122) redefined - // LightSalmon, - /// rgb(255, 127, 80) - Coral, - /// rgb(255, 99, 71) - Tomato, - /// rgb(255, 69, 0) - OrangeRed, - /// rgb(255, 140, 0) - DarkOrange, - /// rgb(255, 165, 0) - Orange, - // Yellow HTML Color Names - /// rgb(255, 215, 0) - Gold, - /// rgb(255, 255, 0) - Yellow, - /// rgb(255, 255, 224) - LightYellow, - /// rgb(255, 250, 205) - LemonChiffon, - /// rgb(250, 250, 210) - LightGoldenrodYellow, - /// rgb(255, 239, 213) - PapayaWhip, - /// rgb(255, 228, 181) - Moccasin, - /// rgb(255, 218, 185) - PeachPuff, - /// rgb(238, 232, 170) - PaleGoldenrod, - /// rgb(240, 230, 140) - Khaki, - /// rgb(189, 183, 107) - DarkKhaki, - // Purple HTML Color Names - /// rgb(230, 230, 250) - Lavender, - /// rgb(216, 191, 216) - Thistle, - /// rgb(221, 160, 221) - Plum, - /// rgb(238, 130, 238) - Violet, - /// rgb(218, 112, 214) - Orchid, - /// rgb(255, 0, 255) - Fuchsia, - /// rgb(255, 0, 255) - Magenta, - /// rgb(186, 85, 211) - MediumOrchid, - /// rgb(147, 112, 219) - MediumPurple, - /// rgb(102, 51, 153) - RebeccaPurple, - /// rgb(138, 43, 226) - BlueViolet, - /// rgb(148, 0, 211) - DarkViolet, - /// rgb(153, 50, 204) - DarkOrchid, - /// rgb(139, 0, 139) - DarkMagenta, - /// rgb(128, 0, 128) - Purple, - /// rgb(75, 0, 130) - Indigo, - /// rgb(106, 90, 205) - SlateBlue, - /// rgb(72, 61, 139) - DarkSlateBlue, - /// rgb(123, 104, 238) - MediumSlateBlue, - // Green HTML Color Names - /// rgb(173, 255, 47) - GreenYellow, - /// rgb(127, 255, 0) - Chartreuse, - /// rgb(124, 252, 0) - LawnGreen, - /// rgb(0, 255, 0) - Lime, - /// rgb(50, 205, 50) - LimeGreen, - /// rgb(152, 251, 152) - PaleGreen, - /// rgb(144, 238, 144) - LightGreen, - /// rgb(0, 250, 154) - MediumSpringGreen, - /// rgb(0, 255, 127) - SpringGreen, - /// rgb(60, 179, 113) - MediumSeaGreen, - /// rgb(46, 139, 87) - SeaGreen, - /// rgb(34, 139, 34) - ForestGreen, - /// rgb(0, 128, 0) - Green, - /// rgb(0, 100, 0) - DarkGreen, - /// rgb(154, 205, 50) - YellowGreen, - /// rgb(107, 142, 35) - OliveDrab, - /// rgb(128, 128, 0) - Olive, - /// rgb(85, 107, 47) - DarkOliveGreen, - /// rgb(102, 205, 170) - MediumAquamarine, - /// rgb(143, 188, 139) - DarkSeaGreen, - /// rgb(32, 178, 170) - LightSeaGreen, - /// rgb(0, 139, 139) - DarkCyan, - /// rgb(0, 128, 128) - Teal, - // Blue HTML Color Names - /// rgb(0, 255, 255) - Aqua, - /// rgb(0, 255, 255) - Cyan, - /// rgb(224, 255, 255) - LightCyan, - /// rgb(175, 238, 238) - PaleTurquoise, - /// rgb(127, 255, 212) - Aquamarine, - /// rgb(64, 224, 208) - Turquoise, - /// rgb(72, 209, 204) - MediumTurquoise, - /// rgb(0, 206, 209) - DarkTurquoise, - /// rgb(95, 158, 160) - CadetBlue, - /// rgb(70, 130, 180) - SteelBlue, - /// rgb(176, 196, 222) - LightSteelBlue, - /// rgb(176, 224, 230) - PowderBlue, - /// rgb(173, 216, 230) - LightBlue, - /// rgb(135, 206, 235) - SkyBlue, - /// rgb(135, 206, 250) - LightSkyBlue, - /// rgb(0, 191, 255) - DeepSkyBlue, - /// rgb(30, 144, 255) - DodgerBlue, - /// rgb(100, 149, 237) - CornflowerBlue, - // /// rgb(123, 104, 238) duplicate - //MediumSlateBlue, - /// rgb(65, 105, 225) - RoyalBlue, - /// rgb(0, 0, 255) - Blue, - /// rgb(0, 0, 205) - MediumBlue, - /// rgb(0, 0, 139) - DarkBlue, - /// rgb(0, 0, 128) - Navy, - /// rgb(25, 25, 112) - MidnightBlue, - // Brown HTML Color Names - /// rgb(255, 248, 220) - Cornsilk, - /// rgb(255, 235, 205) - BlanchedAlmond, - /// rgb(255, 228, 196) - Bisque, - /// rgb(255, 222, 173) - NavajoWhite, - /// rgb(245, 222, 179) - Wheat, - /// rgb(222, 184, 135) - BurlyWood, - /// rgb(210, 180, 140) - Tan, - /// rgb(188, 143, 143) - RosyBrown, - /// rgb(244, 164, 96) - SandyBrown, - /// rgb(218, 165, 32) - Goldenrod, - /// rgb(184, 134, 11) - DarkGoldenrod, - /// rgb(205, 133, 63) - Peru, - /// rgb(210, 105, 30) - Chocolate, - /// rgb(139, 69, 19) - SaddleBrown, - /// rgb(160, 82, 45) - Sienna, - /// rgb(165, 42, 42) - Brown, - /// rgb(128, 0, 0) - Maroon, - // White HTML Color Names - /// rgb(255, 255, 255) - White, - /// rgb(255, 250, 250) - Snow, - /// rgb(240, 255, 240) - HoneyDew, - /// rgb(245, 255, 250) - MintCream, - /// rgb(240, 255, 255) - Azure, - /// rgb(240, 248, 255) - AliceBlue, - /// rgb(248, 248, 255) - GhostWhite, - /// rgb(245, 245, 245) - WhiteSmoke, - /// rgb(255, 245, 238) - SeaShell, - /// rgb(245, 245, 220) - Beige, - /// rgb(253, 245, 230) - OldLace, - /// rgb(255, 250, 240) - FloralWhite, - /// rgb(255, 255, 240) - Ivory, - /// rgb(250, 235, 215) - AntiqueWhite, - /// rgb(250, 240, 230) - Linen, - /// rgb(255, 240, 245) - LavenderBlush, - /// rgb(255, 228, 225) - MistyRose, - // Gray HTML Color Names - /// rgb(220, 220, 220) - Gainsboro, - /// rgb(211, 211, 211) - LightGray, - /// rgb(192, 192, 192) - Silver, - /// rgb(169, 169, 169) - DarkGray, - /// rgb(128, 128, 128) - Gray, - /// rgb(105, 105, 105) - DimGray, - /// rgb(119, 136, 153) - LightSlateGray, - /// rgb(112, 128, 144) - SlateGray, - /// rgb(47, 79, 79) - DarkSlateGray, - /// rgb(0, 0, 0) - Black, -} - -impl Color { - // todo similar for others - pub fn to_rgb(self) -> Color { - use Color::*; - match self { - HexRGB(r, g, b) => HexRGB(r, g, b), - HexRGBA(r, g, b, _) => HexRGB(r, g, b), - HSL(h, s, l) => { - let s = s * 0.01; // percent conversion - let l = l * 0.01; // percent conversion - let (r, g, b) = hsl_to_rgb(h, s, l); - HexRGB((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8) - } - HSLA(h, s, l, _) => Color::to_rgb(HSL(h, s, l)), - IndianRed => HexRGB(205, 92, 92), - LightCoral => HexRGB(240, 128, 128), - Salmon => HexRGB(250, 128, 114), - DarkSalmon => HexRGB(233, 150, 122), - LightSalmon => HexRGB(255, 160, 122), - Crimson => HexRGB(220, 20, 60), - Red => HexRGB(255, 0, 0), - FireBrick => HexRGB(178, 34, 34), - DarkRed => HexRGB(139, 0, 0), - Pink => HexRGB(255, 192, 203), - LightPink => HexRGB(255, 182, 193), - HotPink => HexRGB(255, 105, 180), - DeepPink => HexRGB(255, 20, 147), - MediumVioletRed => HexRGB(199, 21, 133), - PaleVioletRed => HexRGB(219, 112, 147), - Coral => HexRGB(255, 127, 80), - Tomato => HexRGB(255, 99, 71), - OrangeRed => HexRGB(255, 69, 0), - DarkOrange => HexRGB(255, 140, 0), - Orange => HexRGB(255, 165, 0), - Gold => HexRGB(255, 215, 0), - Yellow => HexRGB(255, 255, 0), - LightYellow => HexRGB(255, 255, 224), - LemonChiffon => HexRGB(255, 250, 205), - LightGoldenrodYellow => HexRGB(250, 250, 210), - PapayaWhip => HexRGB(255, 239, 213), - Moccasin => HexRGB(255, 228, 181), - PeachPuff => HexRGB(255, 218, 185), - PaleGoldenrod => HexRGB(238, 232, 170), - Khaki => HexRGB(240, 230, 140), - DarkKhaki => HexRGB(189, 183, 107), - Lavender => HexRGB(230, 230, 250), - Thistle => HexRGB(216, 191, 216), - Plum => HexRGB(221, 160, 221), - Violet => HexRGB(238, 130, 238), - Orchid => HexRGB(218, 112, 214), - Fuchsia => HexRGB(255, 0, 255), - Magenta => HexRGB(255, 0, 255), - MediumOrchid => HexRGB(186, 85, 211), - MediumPurple => HexRGB(147, 112, 219), - RebeccaPurple => HexRGB(102, 51, 153), - BlueViolet => HexRGB(138, 43, 226), - DarkViolet => HexRGB(148, 0, 211), - DarkOrchid => HexRGB(153, 50, 204), - DarkMagenta => HexRGB(139, 0, 139), - Purple => HexRGB(128, 0, 128), - Indigo => HexRGB(75, 0, 130), - SlateBlue => HexRGB(106, 90, 205), - DarkSlateBlue => HexRGB(72, 61, 139), - MediumSlateBlue => HexRGB(123, 104, 238), - GreenYellow => HexRGB(173, 255, 47), - Chartreuse => HexRGB(127, 255, 0), - LawnGreen => HexRGB(124, 252, 0), - Lime => HexRGB(0, 255, 0), - LimeGreen => HexRGB(50, 205, 50), - PaleGreen => HexRGB(152, 251, 152), - LightGreen => HexRGB(144, 238, 144), - MediumSpringGreen => HexRGB(0, 250, 154), - SpringGreen => HexRGB(0, 255, 127), - MediumSeaGreen => HexRGB(60, 179, 113), - SeaGreen => HexRGB(46, 139, 87), - ForestGreen => HexRGB(34, 139, 34), - Green => HexRGB(0, 128, 0), - DarkGreen => HexRGB(0, 100, 0), - YellowGreen => HexRGB(154, 205, 50), - OliveDrab => HexRGB(107, 142, 35), - Olive => HexRGB(128, 128, 0), - DarkOliveGreen => HexRGB(85, 107, 47), - MediumAquamarine => HexRGB(102, 205, 170), - DarkSeaGreen => HexRGB(143, 188, 139), - LightSeaGreen => HexRGB(32, 178, 170), - DarkCyan => HexRGB(0, 139, 139), - Teal => HexRGB(0, 128, 128), - Aqua => HexRGB(0, 255, 255), - Cyan => HexRGB(0, 255, 255), - LightCyan => HexRGB(224, 255, 255), - PaleTurquoise => HexRGB(175, 238, 238), - Aquamarine => HexRGB(127, 255, 212), - Turquoise => HexRGB(64, 224, 208), - MediumTurquoise => HexRGB(72, 209, 204), - DarkTurquoise => HexRGB(0, 206, 209), - CadetBlue => HexRGB(95, 158, 160), - SteelBlue => HexRGB(70, 130, 180), - LightSteelBlue => HexRGB(176, 196, 222), - PowderBlue => HexRGB(176, 224, 230), - LightBlue => HexRGB(173, 216, 230), - SkyBlue => HexRGB(135, 206, 235), - LightSkyBlue => HexRGB(135, 206, 250), - DeepSkyBlue => HexRGB(0, 191, 255), - DodgerBlue => HexRGB(30, 144, 255), - CornflowerBlue => HexRGB(100, 149, 237), - RoyalBlue => HexRGB(65, 105, 225), - Blue => HexRGB(0, 0, 255), - MediumBlue => HexRGB(0, 0, 205), - DarkBlue => HexRGB(0, 0, 139), - Navy => HexRGB(0, 0, 128), - MidnightBlue => HexRGB(25, 25, 112), - Cornsilk => HexRGB(255, 248, 220), - BlanchedAlmond => HexRGB(255, 235, 205), - Bisque => HexRGB(255, 228, 196), - NavajoWhite => HexRGB(255, 222, 173), - Wheat => HexRGB(245, 222, 179), - BurlyWood => HexRGB(222, 184, 135), - Tan => HexRGB(210, 180, 140), - RosyBrown => HexRGB(188, 143, 143), - SandyBrown => HexRGB(244, 164, 96), - Goldenrod => HexRGB(218, 165, 32), - DarkGoldenrod => HexRGB(184, 134, 11), - Peru => HexRGB(205, 133, 63), - Chocolate => HexRGB(210, 105, 30), - SaddleBrown => HexRGB(139, 69, 19), - Sienna => HexRGB(160, 82, 45), - Brown => HexRGB(165, 42, 42), - Maroon => HexRGB(128, 0, 0), - White => HexRGB(255, 255, 255), - Snow => HexRGB(255, 250, 250), - HoneyDew => HexRGB(240, 255, 240), - MintCream => HexRGB(245, 255, 250), - Azure => HexRGB(240, 255, 255), - AliceBlue => HexRGB(240, 248, 255), - GhostWhite => HexRGB(248, 248, 255), - WhiteSmoke => HexRGB(245, 245, 245), - SeaShell => HexRGB(255, 245, 238), - Beige => HexRGB(245, 245, 220), - OldLace => HexRGB(253, 245, 230), - FloralWhite => HexRGB(255, 250, 240), - Ivory => HexRGB(255, 255, 240), - AntiqueWhite => HexRGB(250, 235, 215), - Linen => HexRGB(250, 240, 230), - LavenderBlush => HexRGB(255, 240, 245), - MistyRose => HexRGB(255, 228, 225), - Gainsboro => HexRGB(220, 220, 220), - LightGray => HexRGB(211, 211, 211), - Silver => HexRGB(192, 192, 192), - DarkGray => HexRGB(169, 169, 169), - Gray => HexRGB(128, 128, 128), - DimGray => HexRGB(105, 105, 105), - LightSlateGray => HexRGB(119, 136, 153), - SlateGray => HexRGB(112, 128, 144), - DarkSlateGray => HexRGB(47, 79, 79), - Black => HexRGB(0, 0, 0), - } - } - - pub fn from_named(name: &str) -> Option { - // todo use a faster search (e.g. hashmap, aho-corasick) - use Color::*; - Some(match name { - "indianred" => IndianRed, - "lightcoral" => LightCoral, - "salmon" => Salmon, - "darksalmon" => DarkSalmon, - "lightsalmon" => LightSalmon, - "crimson" => Crimson, - "red" => Red, - "firebrick" => FireBrick, - "darkred" => DarkRed, - "pink" => Pink, - "lightpink" => LightPink, - "hotpink" => HotPink, - "deeppink" => DeepPink, - "mediumvioletred" => MediumVioletRed, - "palevioletred" => PaleVioletRed, - "coral" => Coral, - "tomato" => Tomato, - "orangered" => OrangeRed, - "darkorange" => DarkOrange, - "orange" => Orange, - "gold" => Gold, - "yellow" => Yellow, - "lightyellow" => LightYellow, - "lemonchiffon" => LemonChiffon, - "lightgoldenrodyellow" => LightGoldenrodYellow, - "papayawhip" => PapayaWhip, - "Moccasin" => Moccasin, - "Peachpuff" => PeachPuff, - "palegoldenrod" => PaleGoldenrod, - "khaki" => Khaki, - "darkkhaki" => DarkKhaki, - "lavender" => Lavender, - "thistle" => Thistle, - "plum" => Plum, - "violet" => Violet, - "orchid" => Orchid, - "fuchsia" => Fuchsia, - "magenta" => Magenta, - "mediumorchid" => MediumOrchid, - "mediumpurple" => MediumPurple, - "rebeccapurple" => RebeccaPurple, - "blueviolet" => BlueViolet, - "darkviolet" => DarkViolet, - "darkorchid" => DarkOrchid, - "darkmagenta" => DarkMagenta, - "purple" => Purple, - "indigo" => Indigo, - "slateblue" => SlateBlue, - "darkslateblue" => DarkSlateBlue, - "mediumslateblue" => MediumSlateBlue, - "greenyellow" => GreenYellow, - "chartreuse" => Chartreuse, - "lawngreen" => LawnGreen, - "lime" => Lime, - "limegreen" => LimeGreen, - "palegreen" => PaleGreen, - "lightgreen" => LightGreen, - "mediumspringgreen" => MediumSpringGreen, - "springgreen" => SpringGreen, - "mediumseagreen" => MediumSeaGreen, - "seagreen" => SeaGreen, - "forestgreen" => ForestGreen, - "green" => Green, - "darkgreen" => DarkGreen, - "yellowgreen" => YellowGreen, - "olivedrab" => OliveDrab, - "olive" => Olive, - "darkolivegreen" => DarkOliveGreen, - "mediumaquamarine" => MediumAquamarine, - "darkseagreen" => DarkSeaGreen, - "lightseagreen" => LightSeaGreen, - "darkcyan" => DarkCyan, - "teal" => Teal, - "aqua" => Aqua, - "cyan" => Cyan, - "lightcyan" => LightCyan, - "paleturquoise" => PaleTurquoise, - "aquamarine" => Aquamarine, - "turquoise" => Turquoise, - "mediumturquoise" => MediumTurquoise, - "darkturquoise" => DarkTurquoise, - "cadetblue" => CadetBlue, - "steelblue" => SteelBlue, - "lightsteelblue" => LightSteelBlue, - "powderblue" => PowderBlue, - "lightblue" => LightBlue, - "skyblue" => SkyBlue, - "lightskyblue" => LightSkyBlue, - "deepskyblue" => DeepSkyBlue, - "dodgerblue" => DodgerBlue, - "cornflowerblue" => CornflowerBlue, - "royalblue" => RoyalBlue, - "blue" => Blue, - "mediumblue" => MediumBlue, - "darkblue" => DarkBlue, - "navy" => Navy, - "midnightblue" => MidnightBlue, - "cornsilk" => Cornsilk, - "blanchedalmond" => BlanchedAlmond, - "bisque" => Bisque, - "navajowhite" => NavajoWhite, - "wheat" => Wheat, - "burlywood" => BurlyWood, - "tan" => Tan, - "rosybrown" => RosyBrown, - "sandybrown" => SandyBrown, - "goldenrod" => Goldenrod, - "darkgoldenrod" => DarkGoldenrod, - "peru" => Peru, - "chocolate" => Chocolate, - "saddlebrown" => SaddleBrown, - "sienna" => Sienna, - "brown" => Brown, - "maroon" => Maroon, - "white" => White, - "snow" => Snow, - "honeydew" => HoneyDew, - "mintcream" => MintCream, - "azure" => Azure, - "aliceblue" => AliceBlue, - "ghostwhite" => GhostWhite, - "whitesmoke" => WhiteSmoke, - "seashell" => SeaShell, - "beige" => Beige, - "oldlace" => OldLace, - "floralwhite" => FloralWhite, - "ivory" => Ivory, - "antiquewhite" => AntiqueWhite, - "linen" => Linen, - "lavenderblush" => LavenderBlush, - "mistyrose" => MistyRose, - "gainsboro" => Gainsboro, - "lightgray" => LightGray, - "silver" => Silver, - "darkgray" => DarkGray, - "gray" => Gray, - "dimgray" => DimGray, - "lightslategray" => LightSlateGray, - "slategray" => SlateGray, - "darkslategray" => DarkSlateGray, - "black" => Black, - _ => return None, - }) - } -} - -impl fmt::Display for Color { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Color::*; - match self { - HexRGB(r, g, b) => write!(f, "#{:02x}{:02x}{:02x}", r, g, b), - HexRGBA(r, g, b, a) => write!(f, "#{:02x}{:02x}{:02x}{:02x}", r, g, b, a), - HSL(h, s, l) => write!(f, "hsl({}, {}%, {}%)", h, s, l), - HSLA(h, s, l, a) => write!(f, "hsla({}, {}%, {}%, {})", h, s, l, a), - IndianRed => write!(f, "indianred"), - LightCoral => write!(f, "lightcoral"), - Salmon => write!(f, "salmon"), - DarkSalmon => write!(f, "darksalmon"), - LightSalmon => write!(f, "lightsalmon"), - Crimson => write!(f, "crimson"), - Red => write!(f, "red"), - FireBrick => write!(f, "firebrick"), - DarkRed => write!(f, "darkred"), - Pink => write!(f, "pink"), - LightPink => write!(f, "lightpink"), - HotPink => write!(f, "hotpink"), - DeepPink => write!(f, "deeppink"), - MediumVioletRed => write!(f, "mediumvioletred"), - PaleVioletRed => write!(f, "palevioletred"), - Coral => write!(f, "coral"), - Tomato => write!(f, "tomato"), - OrangeRed => write!(f, "orangered"), - DarkOrange => write!(f, "darkorange"), - Orange => write!(f, "orange"), - Gold => write!(f, "gold"), - Yellow => write!(f, "yellow"), - LightYellow => write!(f, "lightyellow"), - LemonChiffon => write!(f, "lemonchiffon"), - LightGoldenrodYellow => write!(f, "lightgoldenrodyellow"), - PapayaWhip => write!(f, "papayawhip"), - Moccasin => write!(f, "Moccasin"), - PeachPuff => write!(f, "Peachpuff"), - PaleGoldenrod => write!(f, "palegoldenrod"), - Khaki => write!(f, "khaki"), - DarkKhaki => write!(f, "darkkhaki"), - Lavender => write!(f, "lavender"), - Thistle => write!(f, "thistle"), - Plum => write!(f, "plum"), - Violet => write!(f, "violet"), - Orchid => write!(f, "orchid"), - Fuchsia => write!(f, "fuchsia"), - Magenta => write!(f, "magenta"), - MediumOrchid => write!(f, "mediumorchid"), - MediumPurple => write!(f, "mediumpurple"), - RebeccaPurple => write!(f, "rebeccapurple"), - BlueViolet => write!(f, "blueviolet"), - DarkViolet => write!(f, "darkviolet"), - DarkOrchid => write!(f, "darkorchid"), - DarkMagenta => write!(f, "darkmagenta"), - Purple => write!(f, "purple"), - Indigo => write!(f, "indigo"), - SlateBlue => write!(f, "slateblue"), - DarkSlateBlue => write!(f, "darkslateblue"), - MediumSlateBlue => write!(f, "mediumslateblue"), - GreenYellow => write!(f, "greenyellow"), - Chartreuse => write!(f, "chartreuse"), - LawnGreen => write!(f, "lawngreen"), - Lime => write!(f, "lime"), - LimeGreen => write!(f, "limegreen"), - PaleGreen => write!(f, "palegreen"), - LightGreen => write!(f, "lightgreen"), - MediumSpringGreen => write!(f, "mediumspringgreen"), - SpringGreen => write!(f, "springgreen"), - MediumSeaGreen => write!(f, "mediumseagreen"), - SeaGreen => write!(f, "seagreen"), - ForestGreen => write!(f, "forestgreen"), - Green => write!(f, "green"), - DarkGreen => write!(f, "darkgreen"), - YellowGreen => write!(f, "yellowgreen"), - OliveDrab => write!(f, "olivedrab"), - Olive => write!(f, "olive"), - DarkOliveGreen => write!(f, "darkolivegreen"), - MediumAquamarine => write!(f, "mediumaquamarine"), - DarkSeaGreen => write!(f, "darkseagreen"), - LightSeaGreen => write!(f, "lightseagreen"), - DarkCyan => write!(f, "darkcyan"), - Teal => write!(f, "teal"), - Aqua => write!(f, "aqua"), - Cyan => write!(f, "cyan"), - LightCyan => write!(f, "lightcyan"), - PaleTurquoise => write!(f, "paleturquoise"), - Aquamarine => write!(f, "aquamarine"), - Turquoise => write!(f, "turquoise"), - MediumTurquoise => write!(f, "mediumturquoise"), - DarkTurquoise => write!(f, "darkturquoise"), - CadetBlue => write!(f, "cadetblue"), - SteelBlue => write!(f, "steelblue"), - LightSteelBlue => write!(f, "lightsteelblue"), - PowderBlue => write!(f, "powderblue"), - LightBlue => write!(f, "lightblue"), - SkyBlue => write!(f, "skyblue"), - LightSkyBlue => write!(f, "lightskyblue"), - DeepSkyBlue => write!(f, "deepskyblue"), - DodgerBlue => write!(f, "dodgerblue"), - CornflowerBlue => write!(f, "cornflowerblue"), - RoyalBlue => write!(f, "royalblue"), - Blue => write!(f, "blue"), - MediumBlue => write!(f, "mediumblue"), - DarkBlue => write!(f, "darkblue"), - Navy => write!(f, "navy"), - MidnightBlue => write!(f, "midnightblue"), - Cornsilk => write!(f, "cornsilk"), - BlanchedAlmond => write!(f, "blanchedalmond"), - Bisque => write!(f, "bisque"), - NavajoWhite => write!(f, "navajowhite"), - Wheat => write!(f, "wheat"), - BurlyWood => write!(f, "burlywood"), - Tan => write!(f, "tan"), - RosyBrown => write!(f, "rosybrown"), - SandyBrown => write!(f, "sandybrown"), - Goldenrod => write!(f, "goldenrod"), - DarkGoldenrod => write!(f, "darkgoldenrod"), - Peru => write!(f, "peru"), - Chocolate => write!(f, "chocolate"), - SaddleBrown => write!(f, "saddlebrown"), - Sienna => write!(f, "sienna"), - Brown => write!(f, "brown"), - Maroon => write!(f, "maroon"), - White => write!(f, "white"), - Snow => write!(f, "snow"), - HoneyDew => write!(f, "honeydew"), - MintCream => write!(f, "mintcream"), - Azure => write!(f, "azure"), - AliceBlue => write!(f, "aliceblue"), - GhostWhite => write!(f, "ghostwhite"), - WhiteSmoke => write!(f, "whitesmoke"), - SeaShell => write!(f, "seashell"), - Beige => write!(f, "beige"), - OldLace => write!(f, "oldlace"), - FloralWhite => write!(f, "floralwhite"), - Ivory => write!(f, "ivory"), - AntiqueWhite => write!(f, "antiquewhite"), - Linen => write!(f, "linen"), - LavenderBlush => write!(f, "lavenderblush"), - MistyRose => write!(f, "mistyrose"), - Gainsboro => write!(f, "gainsboro"), - LightGray => write!(f, "lightgray"), - Silver => write!(f, "silver"), - DarkGray => write!(f, "darkgray"), - Gray => write!(f, "gray"), - DimGray => write!(f, "dimgray"), - LightSlateGray => write!(f, "lightslategray"), - SlateGray => write!(f, "slategray"), - DarkSlateGray => write!(f, "darkslategray"), - Black => write!(f, "black"), - } - } -} - -fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (f64, f64, f64) { - debug_assert!(h >= 0.0 && h < 360.0); - debug_assert!(s >= 0.0 && s <= 1.0); - debug_assert!(l >= 0.0 && l <= 1.0); - let c = (1.0 - (2.0 * l - 1.0).abs()) * s; - let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs()); - let m = l - c * 0.5; - let (rp, gp, bp) = if h < 60.0 { - (c, x, 0.0) - } else if h < 120.0 { - (x, c, 0.0) - } else if h < 180.0 { - (0.0, c, x) - } else if h < 240.0 { - (0.0, x, c) - } else if h < 300.0 { - (x, 0.0, c) - } else { - (c, 0.0, x) - }; - (rp + m, gp + m, bp + m) -} - -pub fn parse_hex(hex: &str) -> Option { - match hex.len() { - 3 => { - let r = u8::from_str_radix(hex.get(0..1)?, 16).ok()?; - let g = u8::from_str_radix(hex.get(1..2)?, 16).ok()?; - let b = u8::from_str_radix(hex.get(2..3)?, 16).ok()?; - // #fff is equivalent to #ffffff - Some(Color::HexRGB(r << 4 | r, g << 4 | g, b << 4 | b)) - } - 6 => { - let r = u8::from_str_radix(hex.get(0..2)?, 16).ok()?; - let g = u8::from_str_radix(hex.get(2..4)?, 16).ok()?; - let b = u8::from_str_radix(hex.get(4..6)?, 16).ok()?; - Some(Color::HexRGB(r, g, b)) - } - 8 => { - let r = u8::from_str_radix(hex.get(0..2)?, 16).ok()?; - let g = u8::from_str_radix(hex.get(2..4)?, 16).ok()?; - let b = u8::from_str_radix(hex.get(4..6)?, 16).ok()?; - let a = u8::from_str_radix(hex.get(6..8)?, 16).ok()?; - Some(Color::HexRGBA(r, g, b, a)) - } - _ => None, - } -} - -#[test] -fn test_color_convert() { - let color = Color::HSL(60.0, 0.0, 100.0); - assert_eq!(color.to_rgb(), Color::HexRGB(255, 255, 255)); - let color = Color::HSL(0.0, 100.0, 50.0); - assert_eq!(color.to_rgb(), Color::HexRGB(255, 0, 0)); -} diff --git a/packages/core-macro/styles/mod.rs b/packages/core-macro/styles/mod.rs deleted file mode 100644 index 47c8259c0..000000000 --- a/packages/core-macro/styles/mod.rs +++ /dev/null @@ -1,2044 +0,0 @@ -//! A module to type styles. -// TODO most stuff here is on the stack, but there are a few heap-allocs here and there. It would -// be good if we could just to allocate them in the bump arena when using bumpalo. -mod calc; -mod codegen; -mod color; -pub mod string; -mod syn_parse; - -use std::{ - fmt, - ops::{Deref, DerefMut}, -}; - -pub use calc::*; -pub use color::{Color, DynamicColor}; -// pub use crate::{ -// calc::*, -// color::{Color, DynamicColor}, -// }; - -pub struct DynamicStyles { - pub rules: Vec, -} - -impl From> for DynamicStyles { - fn from(v: Vec) -> Self { - Self { rules: v } - } -} - -impl fmt::Display for DynamicStyles { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for style in self - .rules - .iter() - .filter(|style| !(style.is_dynamic() || style.is_dummy())) - { - write!(f, "{};", style)?; - } - Ok(()) - } -} - -// TODO make container generic over heap (e.g. support bumpalo) -#[derive(Debug, Clone, PartialEq)] -pub struct Styles { - pub rules: Vec