1
0
Эх сурвалжийг харах

Merge pull request #51 from DioxusLabs/jk/arbitrary_expressions

Jk/arbitrary expressions
Jonathan Kelley 3 жил өмнө
parent
commit
7d41a16208
65 өөрчлөгдсөн 1011 нэмэгдсэн , 1405 устгасан
  1. 12 10
      Cargo.toml
  2. 7 1
      README.md
  3. 16 24
      examples/README.md
  4. 10 12
      examples/async.rs
  5. 2 2
      examples/borrowed.rs
  6. 124 93
      examples/calculator.rs
  7. 1 1
      examples/core/jsframework.rs
  8. 2 2
      examples/core_reference/iterators.rs
  9. 92 79
      examples/crm.rs
  10. 1 1
      examples/desktop/demo.rs
  11. 0 8
      examples/desktop/kitchensink.rs
  12. 0 1
      examples/desktop/tauri.rs
  13. 7 12
      examples/desktop/todomvc.rs
  14. 30 44
      examples/file_explorer.rs
  15. 15 15
      examples/framework_benchmark.rs
  16. 1 1
      examples/hello_world.rs
  17. 4 4
      examples/hydration.rs
  18. 20 20
      examples/pattern_model.rs
  19. 11 13
      examples/pattern_reducer.rs
  20. 1 0
      examples/readme.rs
  21. 61 0
      examples/rsx_compile_fail.rs
  22. 54 29
      examples/rsx_usage.rs
  23. 38 38
      examples/tailwind.rs
  24. 12 7
      examples/tasks.rs
  25. 21 18
      examples/todomvc.rs
  26. 9 18
      examples/web_tick.rs
  27. 3 4
      examples/webview_web.rs
  28. 31 0
      examples/xss_safety.rs
  29. 2 1
      packages/core-macro/Cargo.toml
  30. 1 0
      packages/core-macro/src/inlineprops.rs
  31. 4 3
      packages/core-macro/src/lib.rs
  32. 0 64
      packages/core-macro/src/rsx/ambiguous.rs
  33. 0 66
      packages/core-macro/src/rsx/body.rs
  34. 11 86
      packages/core-macro/src/rsx/component.rs
  35. 114 61
      packages/core-macro/src/rsx/element.rs
  36. 0 63
      packages/core-macro/src/rsx/fragment.rs
  37. 74 6
      packages/core-macro/src/rsx/mod.rs
  38. 52 37
      packages/core-macro/src/rsx/node.rs
  39. 0 1
      packages/core/Cargo.toml
  40. 1 1
      packages/core/README.md
  41. 1 1
      packages/core/examples/component_children.rs
  42. 1 1
      packages/core/examples/works.rs
  43. 13 3
      packages/core/src/nodes.rs
  44. 1 1
      packages/core/src/scopes.rs
  45. 9 9
      packages/core/src/virtual_dom.rs
  46. 1 1
      packages/core/tests/borrowedstate.rs
  47. 7 7
      packages/core/tests/miri_stress.rs
  48. 1 1
      packages/core/tests/vdom_rebuild.rs
  49. 0 1
      packages/desktop/Cargo.toml
  50. 10 14
      packages/desktop/examples/async.rs
  51. 4 43
      packages/hooks/README.md
  52. 43 3
      packages/hooks/src/lib.rs
  53. 39 79
      packages/hooks/src/usestate.rs
  54. 1 3
      packages/html/Cargo.toml
  55. 1 1
      packages/liveview/README.md
  56. 1 4
      packages/mobile/Cargo.toml
  57. 0 5
      packages/mobile/src/lib.rs
  58. 1 1
      packages/router/src/link.rs
  59. 7 8
      packages/ssr/src/lib.rs
  60. 17 29
      packages/web/Cargo.toml
  61. 0 29
      packages/web/examples/async.rs
  62. 0 221
      packages/web/examples/js_bench.rs
  63. 0 44
      packages/web/examples/simple.rs
  64. 0 41
      packages/web/examples/suspense.rs
  65. 9 9
      src/lib.rs

+ 12 - 10
Cargo.toml

@@ -11,18 +11,19 @@ documentation = "https://dioxuslabs.com"
 keywords = ["dom", "ui", "gui", "react", "wasm"]
 keywords = ["dom", "ui", "gui", "react", "wasm"]
 
 
 [dependencies]
 [dependencies]
-dioxus-core = { path = "./packages/core", version = "^0.1.3" }
-dioxus-html = { path = "./packages/html", optional = true }
-dioxus-core-macro = { path = "./packages/core-macro", optional = true }
+dioxus-core = { path = "./packages/core", version = "^0.1.4" }
+dioxus-html = { path = "./packages/html", version = "^0.1.1", optional = true }
+dioxus-core-macro = { path = "./packages/core-macro", version = "^0.1.3", optional = true }
 dioxus-hooks = { path = "./packages/hooks", optional = true }
 dioxus-hooks = { path = "./packages/hooks", optional = true }
 
 
-dioxus-ssr = { path = "./packages/ssr", optional = true }
-dioxus-web = { path = "./packages/web", optional = true }
+dioxus-web = { path = "./packages/web", version = "^0.0.1", optional = true }
 dioxus-desktop = { path = "./packages/desktop", optional = true }
 dioxus-desktop = { path = "./packages/desktop", optional = true }
+dioxus-ssr = { path = "./packages/ssr", optional = true }
+
 dioxus-router = { path = "./packages/router", optional = true }
 dioxus-router = { path = "./packages/router", optional = true }
 
 
-dioxus-mobile = { path = "./packages/mobile", optional = true }
-dioxus-liveview = { path = "./packages/liveview", optional = true }
+# dioxus-mobile = { path = "./packages/mobile", optional = true }
+# dioxus-liveview = { path = "./packages/liveview", optional = true }
 
 
 [features]
 [features]
 default = ["macro", "hooks", "html"]
 default = ["macro", "hooks", "html"]
@@ -31,11 +32,13 @@ macro = ["dioxus-core-macro"]
 hooks = ["dioxus-hooks"]
 hooks = ["dioxus-hooks"]
 html = ["dioxus-html"]
 html = ["dioxus-html"]
 router = ["dioxus-router"]
 router = ["dioxus-router"]
-liveview = ["dioxus-liveview"]
 ssr = ["dioxus-ssr"]
 ssr = ["dioxus-ssr"]
 web = ["dioxus-web", "dioxus-router/web"]
 web = ["dioxus-web", "dioxus-router/web"]
 desktop = ["dioxus-desktop", "dioxus-router/desktop"]
 desktop = ["dioxus-desktop", "dioxus-router/desktop"]
-mobile = ["dioxus-mobile"]
+
+
+# mobile = ["dioxus-mobile"]
+# liveview = ["dioxus-liveview"]
 
 
 
 
 [workspace]
 [workspace]
@@ -60,7 +63,6 @@ im-rc = "15.0.0"
 fxhash = "0.2.1"
 fxhash = "0.2.1"
 anyhow = "1.0.51"
 anyhow = "1.0.51"
 serde_json = "1.0.73"
 serde_json = "1.0.73"
-simple_logger = "1.16.0"
 
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
 argh = "0.1.7"
 argh = "0.1.7"

+ 7 - 1
README.md

@@ -79,7 +79,9 @@ If you know React, then you already know Dioxus.
     <tr>
     <tr>
 </table>
 </table>
 
 
-## Examples:
+
+
+## Examples Projects:
 
 
 | File Navigator (Desktop)                                                                                                                                                        | WiFi scanner (Desktop)                                                                                                                                                                 | TodoMVC (All platforms)                                                                                                                                                 | Ecommerce w/ Tailwind (Liveview)                                                                                                                                                     |
 | File Navigator (Desktop)                                                                                                                                                        | WiFi scanner (Desktop)                                                                                                                                                                 | TodoMVC (All platforms)                                                                                                                                                 | Ecommerce w/ Tailwind (Liveview)                                                                                                                                                     |
 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -88,6 +90,10 @@ If you know React, then you already know Dioxus.
 
 
 See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem.
 See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem.
 
 
+## Running examples locally
+
+All local examples are built for the desktop renderer. This means you can simply clone this repo and call `cargo run --example EXAMPLE_NAME`. To run non-desktop examples, checkout the example projects shown above.
+
 ## Why Dioxus and why Rust?
 ## Why Dioxus and why Rust?
 
 
 TypeScript is a fantastic addition to JavaScript, but it's still fundamentally JavaScript. TS code runs slightly slower, has tons of configuration options, and not every package is properly typed. 
 TypeScript is a fantastic addition to JavaScript, but it's still fundamentally JavaScript. TS code runs slightly slower, has tons of configuration options, and not every package is properly typed. 

+ 16 - 24
examples/README.md

@@ -42,14 +42,6 @@ These examples are not necessarily meant to be run, but rather serve as a refere
 | [Complete rsx reference](./rsx_usage.rs)            | A complete reference for all rsx! usage         | ✅      |
 | [Complete rsx reference](./rsx_usage.rs)            | A complete reference for all rsx! usage         | ✅      |
 | [Event Listeners](./listener.rs)                    | Attach closures to events on elements           | ✅      |
 | [Event Listeners](./listener.rs)                    | Attach closures to events on elements           | ✅      |
 
 
-These web-specific examples must be run with `dioxus-cli` using `dioxus develop --example XYZ`
-
-| Example | What it does |
-| ------- | ------------ |
-| asd     | this does    |
-| asd     | this does    |
-
-
 
 
 ## Show me some examples!
 ## Show me some examples!
 
 
@@ -64,7 +56,7 @@ Here's what a few common tasks look like in Dioxus:
 
 
 Nested components with children and internal state:
 Nested components with children and internal state:
 ```rust
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   cx.render(rsx!( Toggle { "Toggle me" } ))
   cx.render(rsx!( Toggle { "Toggle me" } ))
 }
 }
 
 
@@ -75,9 +67,9 @@ fn Toggle(cx: Scope<ToggleProps>) -> Element {
   let mut toggled = use_state(&cx, || false);
   let mut toggled = use_state(&cx, || false);
   cx.render(rsx!{
   cx.render(rsx!{
     div {
     div {
-      {&cx.props.children}
+      &cx.props.children
       button { onclick: move |_| toggled.set(true),
       button { onclick: move |_| toggled.set(true),
-        {toggled.and_then(|| "On").or_else(|| "Off")}
+        toggled.and_then(|| "On").or_else(|| "Off")
       }
       }
     }
     }
   })
   })
@@ -86,7 +78,7 @@ fn Toggle(cx: Scope<ToggleProps>) -> Element {
 
 
 Controlled inputs:
 Controlled inputs:
 ```rust
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let value = use_state(&cx, String::new);
   let value = use_state(&cx, String::new);
   cx.render(rsx!( 
   cx.render(rsx!( 
     input {
     input {
@@ -100,7 +92,7 @@ fn App(cx: Scope<()>) -> Element {
 
 
 Lists and Conditional rendering:
 Lists and Conditional rendering:
 ```rust
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let list = (0..10).map(|i| {
   let list = (0..10).map(|i| {
     rsx!(li { key: "{i}", "Value: {i}" })
     rsx!(li { key: "{i}", "Value: {i}" })
   });
   });
@@ -112,8 +104,8 @@ fn App(cx: Scope<()>) -> Element {
 
 
   if should_show {
   if should_show {
     cx.render(rsx!( 
     cx.render(rsx!( 
-      {title}
-      ul { {list} } 
+      title,
+      ul { list } 
     ))
     ))
   } else {
   } else {
     None
     None
@@ -128,7 +120,7 @@ static App: Component = |cx, _| rsx!(cx, div {"hello world!"});
 
 
 Borrowed prop contents:
 Borrowed prop contents:
 ```rust
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let name = use_state(&cx, || String::from("example"));
   let name = use_state(&cx, || String::from("example"));
   rsx!(cx, Child { title: name.as_str() })
   rsx!(cx, Child { title: name.as_str() })
 }
 }
@@ -145,12 +137,12 @@ Global State
 ```rust
 ```rust
 struct GlobalState { name: String }
 struct GlobalState { name: String }
 
 
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   use_provide_shared_state(cx, || GlobalState { name: String::from("Toby") })
   use_provide_shared_state(cx, || GlobalState { name: String::from("Toby") })
   rsx!(cx, Leaf {})
   rsx!(cx, Leaf {})
 }
 }
 
 
-fn Leaf(cx: Scope<()>) -> Element {
+fn Leaf(cx: Scope) -> Element {
   let state = use_consume_shared_state::<GlobalState>(cx)?;
   let state = use_consume_shared_state::<GlobalState>(cx)?;
   rsx!(cx, "Hello {state.name}")
   rsx!(cx, "Hello {state.name}")
 }
 }
@@ -166,20 +158,20 @@ enum Route {
   Post(id)
   Post(id)
 }
 }
 
 
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let route = use_router(cx, Route::parse);
   let route = use_router(cx, Route::parse);
   cx.render(rsx!(div {
   cx.render(rsx!(div {
-    {match route {
+    match route {
       Route::Home => rsx!( Home {} ),
       Route::Home => rsx!( Home {} ),
       Route::Post(id) => rsx!( Post { id: id })
       Route::Post(id) => rsx!( Post { id: id })
-    }}
+    }
   }))  
   }))  
 }
 }
 ```
 ```
 
 
 Suspense 
 Suspense 
 ```rust
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let doggo = use_suspense(cx,
   let doggo = use_suspense(cx,
     || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::<Response>().await.unwrap() },
     || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::<Response>().await.unwrap() },
     |response| cx.render(rsx!( img { src: "{response.message}" }))
     |response| cx.render(rsx!( img { src: "{response.message}" }))
@@ -187,8 +179,8 @@ fn App(cx: Scope<()>) -> Element {
   
   
   cx.render(rsx!{
   cx.render(rsx!{
     div {
     div {
-      "One doggo coming right up:"
-      {doggo}
+      "One doggo coming right up:",
+      doggo
     }
     }
   })
   })
 }
 }

+ 10 - 12
examples/async.rs

@@ -8,38 +8,36 @@ use gloo_timers::future::TimeoutFuture;
 
 
 #[tokio::main]
 #[tokio::main]
 async fn main() {
 async fn main() {
-    dioxus::desktop::launch(App);
+    dioxus::desktop::launch(app);
 }
 }
 
 
-pub static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let count = use_state(&cx, || 0);
     let count = use_state(&cx, || 0);
-    let mut direction = use_state(&cx, || 1);
+    let direction = use_state(&cx, || 1);
 
 
     let (async_count, dir) = (count.for_async(), *direction);
     let (async_count, dir) = (count.for_async(), *direction);
 
 
     let task = use_coroutine(&cx, move || async move {
     let task = use_coroutine(&cx, move || async move {
         loop {
         loop {
             TimeoutFuture::new(250).await;
             TimeoutFuture::new(250).await;
-            *async_count.get_mut() += dir;
+            *async_count.modify() += dir;
         }
         }
     });
     });
 
 
     rsx!(cx, div {
     rsx!(cx, div {
         h1 {"count is {count}"}
         h1 {"count is {count}"}
-        button {
+        button { onclick: move |_| task.stop(),
             "Stop counting"
             "Stop counting"
-            onclick: move |_| task.stop()
         }
         }
-        button {
+        button { onclick: move |_| task.resume(),
             "Start counting"
             "Start counting"
-            onclick: move |_| task.resume()
         }
         }
         button {
         button {
-            "Switch counting direcion"
             onclick: move |_| {
             onclick: move |_| {
-                direction *= -1;
+                *direction.modify() *= -1;
                 task.restart();
                 task.restart();
-            }
+            },
+            "Switch counting direcion"
         }
         }
     })
     })
-};
+}

+ 2 - 2
examples/borrowed.rs

@@ -20,8 +20,8 @@ fn main() {
     dioxus::desktop::launch(App);
     dioxus::desktop::launch(App);
 }
 }
 
 
-fn App(cx: Scope<()>) -> Element {
-    let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f);
+fn App(cx: Scope) -> Element {
+    let text = cx.use_hook(|_| vec![String::from("abc=def")], |f| f);
 
 
     let first = text.get_mut(0).unwrap();
     let first = text.get_mut(0).unwrap();
 
 

+ 124 - 93
examples/calculator.rs

@@ -1,3 +1,5 @@
+#![allow(non_snake_case)]
+
 /*
 /*
 This example is a simple iOS-style calculator. This particular example can run any platform - Web, Mobile, Desktop.
 This example is a simple iOS-style calculator. This particular example can run any platform - Web, Mobile, Desktop.
 This calculator version uses React-style state management. All state is held as individual use_states.
 This calculator version uses React-style state management. All state is held as individual use_states.
@@ -10,10 +12,10 @@ use dioxus::prelude::*;
 use separator::Separatable;
 use separator::Separatable;
 
 
 fn main() {
 fn main() {
-    dioxus::desktop::launch(APP);
+    dioxus::desktop::launch(app);
 }
 }
 
 
-static APP: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let cur_val = use_state(&cx, || 0.0_f64);
     let cur_val = use_state(&cx, || 0.0_f64);
     let operator = use_state(&cx, || None as Option<&'static str>);
     let operator = use_state(&cx, || None as Option<&'static str>);
     let display_value = use_state(&cx, || String::from(""));
     let display_value = use_state(&cx, || String::from(""));
@@ -21,109 +23,138 @@ static APP: Component = |cx| {
     let toggle_percent = move |_| todo!();
     let toggle_percent = move |_| todo!();
     let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str());
     let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str());
 
 
-    rsx!(cx, div {
-        class: "calculator",
-        onkeydown: move |evt| match evt.key_code {
-            KeyCode::Add => operator.set(Some("+")),
-            KeyCode::Subtract => operator.set(Some("-")),
-            KeyCode::Divide => operator.set(Some("/")),
-            KeyCode::Multiply => operator.set(Some("*")),
-            KeyCode::Num0 => input_digit(0),
-            KeyCode::Num1 => input_digit(1),
-            KeyCode::Num2 => input_digit(2),
-            KeyCode::Num3 => input_digit(3),
-            KeyCode::Num4 => input_digit(4),
-            KeyCode::Num5 => input_digit(5),
-            KeyCode::Num6 => input_digit(6),
-            KeyCode::Num7 => input_digit(7),
-            KeyCode::Num8 => input_digit(8),
-            KeyCode::Num9 => input_digit(9),
-            KeyCode::Backspace => {
-                if !display_value.as_str().eq("0") {
-                    display_value.modify().pop();
+    cx.render(rsx!(
+        style { [include_str!("./assets/calculator.css")] }
+        div {
+            class: "calculator",
+            onkeydown: move |evt| match evt.key_code {
+                KeyCode::Add => operator.set(Some("+")),
+                KeyCode::Subtract => operator.set(Some("-")),
+                KeyCode::Divide => operator.set(Some("/")),
+                KeyCode::Multiply => operator.set(Some("*")),
+                KeyCode::Num0 => input_digit(0),
+                KeyCode::Num1 => input_digit(1),
+                KeyCode::Num2 => input_digit(2),
+                KeyCode::Num3 => input_digit(3),
+                KeyCode::Num4 => input_digit(4),
+                KeyCode::Num5 => input_digit(5),
+                KeyCode::Num6 => input_digit(6),
+                KeyCode::Num7 => input_digit(7),
+                KeyCode::Num8 => input_digit(8),
+                KeyCode::Num9 => input_digit(9),
+                KeyCode::Backspace => {
+                    if !display_value.as_str().eq("0") {
+                        display_value.modify().pop();
+                    }
                 }
                 }
-            }
-            _ => {}
-        }
-        div { class: "calculator-display", {[format_args!("{}", cur_val.separated_string())]} }
-        div { class: "input-keys"
-            div { class: "function-keys"
-                CalculatorKey {
-                    {[if display_value == "0" { "C" } else { "AC" }]}
-                    name: "key-clear",
-                    onclick: move |_| {
-                        display_value.set("0".to_string());
-                        if display_value != "0" {
-                            operator.set(None);
-                            cur_val.set(0.0);
-                        }
+                _ => {}
+            },
+            div { class: "calculator-display", [cur_val.separated_string()] }
+            div { class: "input-keys",
+                div { class: "function-keys",
+                    CalculatorKey {
+                        name: "key-clear",
+                        onclick: move |_| {
+                            display_value.set("0".to_string());
+                            if display_value != "0" {
+                                operator.set(None);
+                                cur_val.set(0.0);
+                            }
+                        },
+                        [if display_value == "0" { "C" } else { "AC" }]
+                    }
+                    CalculatorKey {
+                        name: "key-sign",
+                        onclick: move |_| {
+                            if display_value.starts_with("-") {
+                                display_value.set(display_value.trim_start_matches("-").to_string())
+                            } else {
+                                display_value.set(format!("-{}", *display_value))
+                            }
+                        },
+                        "±"
+                    }
+                    CalculatorKey {
+                        onclick: toggle_percent,
+                        name: "key-percent",
+                        "%"
                     }
                     }
                 }
                 }
-                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))
+                div { class: "digit-keys",
+                    CalculatorKey { name: "key-0", onclick: move |_| input_digit(0),
+                        "0"
+                    }
+                    CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."),
+                        "●"
+                    }
+                    (1..9).map(|k| rsx!{
+                        CalculatorKey {
+                            key: "{k}",
+                            name: "key-{k}",
+                            onclick: move |_| input_digit(k),
+                            "{k}"
                         }
                         }
-                    },
+                    }),
                 }
                 }
-                CalculatorKey {
-                    "%"
-                    onclick: {toggle_percent}
-                    name: "key-percent",
-                }
-            }
-            div { class: "digit-keys"
-                CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" }
-                CalculatorKey { name: "key-dot", onclick: move |_| display_value.modify().push_str("."), "●" }
 
 
-                {(1..9).map(|k| rsx!{
-                    CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" }
-                })}
-            }
-            div { class: "operator-keys"
-                CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some("/")) "÷" }
-                CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some("*")) "×" }
-                CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some("-")) "−" }
-                CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some("+")) "+" }
-                CalculatorKey {
-                    "="
-                    name: "key-equals",
-                    onclick: move |_| {
-                        if let Some(op) = operator.as_ref() {
-                            let rhs = display_value.parse::<f64>().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);
-                        }
-                    },
+                div { class: "operator-keys",
+                    CalculatorKey {
+                        name: "key-divide",
+                        onclick: move |_| operator.set(Some("/")),
+                        "÷"
+                    }
+                    CalculatorKey {
+                        name: "key-multiply",
+                        onclick: move |_| operator.set(Some("*")),
+                        "×"
+                    }
+                    CalculatorKey {
+                        name: "key-subtract",
+                        onclick: move |_| operator.set(Some("-")),
+                        "−"
+                    }
+                    CalculatorKey {
+                        name: "key-add",
+                        onclick: move |_| operator.set(Some("+")),
+                        "+"
+                    }
+                    CalculatorKey {
+                        name: "key-equals",
+                        onclick: move |_| {
+                            if let Some(op) = operator.as_ref() {
+                                let rhs = display_value.parse::<f64>().unwrap();
+                                let new_val = match *op {
+                                    "+" => *cur_val + rhs,
+                                    "-" => *cur_val - rhs,
+                                    "*" => *cur_val * rhs,
+                                    "/" => *cur_val / rhs,
+                                    _ => unreachable!(),
+                                };
+                                cur_val.set(new_val);
+                                display_value.set(new_val.to_string());
+                                operator.set(None);
+                            }
+                        },
+                        "="
+                    }
                 }
                 }
             }
             }
         }
         }
-    })
-};
+    ))
+}
 
 
-#[derive(Props)]
-struct CalculatorKeyProps<'a> {
+#[inline_props]
+fn CalculatorKey<'a>(
+    cx: Scope,
     name: &'static str,
     name: &'static str,
     onclick: &'a dyn Fn(Arc<MouseEvent>),
     onclick: &'a dyn Fn(Arc<MouseEvent>),
     children: Element<'a>,
     children: Element<'a>,
-}
-
-fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
-    rsx!(cx, button {
-        class: "calculator-key {cx.props.name}"
-        onclick: {cx.props.onclick}
-        {&cx.props.children}
+) -> Element {
+    cx.render(rsx! {
+        button {
+            class: "calculator-key {name}",
+            onclick: onclick,
+            children
+        }
     })
     })
 }
 }

+ 1 - 1
examples/core/jsframework.rs

@@ -15,7 +15,7 @@ fn main() {
     assert!(g.edits.len() > 1);
     assert!(g.edits.len() > 1);
 }
 }
 
 
-fn App((cx, props): Scope<()>) -> Element {
+fn App((cx, props): Scope) -> Element {
     let mut rng = SmallRng::from_entropy();
     let mut rng = SmallRng::from_entropy();
     let rows = (0..10_000_usize).map(|f| {
     let rows = (0..10_000_usize).map(|f| {
         let label = Label::new(&mut rng);
         let label = Label::new(&mut rng);

+ 2 - 2
examples/core_reference/iterators.rs

@@ -20,11 +20,11 @@ pub static Example: Component = |cx| {
             li { onclick: move |_| example_data.set(f)
             li { onclick: move |_| example_data.set(f)
                 "ID: {f}"
                 "ID: {f}"
                 ul {
                 ul {
-                    {(0..10).map(|k| rsx!{
+                    (0..10).map(|k| rsx!{
                         li {
                         li {
                             "Sub iterator: {f}.{k}"
                             "Sub iterator: {f}.{k}"
                         }
                         }
-                    })}
+                    })
                 }
                 }
             }
             }
         }
         }

+ 92 - 79
examples/crm.rs

@@ -4,7 +4,7 @@ Tiny CRM: A port of the Yew CRM example to Dioxus.
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 
 
 fn main() {
 fn main() {
-    dioxus::web::launch(App);
+    dioxus::desktop::launch(app);
 }
 }
 enum Scene {
 enum Scene {
     ClientsList,
     ClientsList,
@@ -19,88 +19,101 @@ pub struct Client {
     pub description: String,
     pub description: String,
 }
 }
 
 
-static App: Component = |cx| {
-    let mut clients = use_ref(&cx, || vec![] as Vec<Client>);
-    let mut scene = use_state(&cx, || Scene::ClientsList);
+fn app(cx: Scope) -> Element {
+    let clients = use_ref(&cx, || vec![] as Vec<Client>);
 
 
-    let mut firstname = use_state(&cx, || String::new());
-    let mut lastname = use_state(&cx, || String::new());
-    let mut description = use_state(&cx, || String::new());
+    let scene = use_state(&cx, || Scene::ClientsList);
+    let firstname = use_state(&cx, String::new);
+    let lastname = use_state(&cx, String::new);
+    let description = use_state(&cx, String::new);
 
 
-    let scene = match *scene {
-        Scene::ClientsList => {
-            rsx!(cx, div { class: "crm"
-                h2 { "List of clients" margin_bottom: "10px" }
-                div { class: "clients" margin_left: "10px"
-                    {clients.read().iter().map(|client| rsx!(
-                        div { class: "client" style: "margin-bottom: 50px"
-                            p { "First Name: {client.first_name}" }
-                            p { "Last Name: {client.last_name}" }
-                            p {"Description: {client.description}"}
-                        })
-                    )}
-                }
-                button { class: "pure-button pure-button-primary" onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
-                button { class: "pure-button" onclick: move |_| scene.set(Scene::Settings), "Settings" }
-            })
-        }
-        Scene::NewClientForm => {
-            let add_new = move |_| {
-                clients.write().push(Client {
-                    description: (*description).clone(),
-                    first_name: (*firstname).clone(),
-                    last_name: (*lastname).clone(),
-                });
-                description.set(String::new());
-                firstname.set(String::new());
-                lastname.set(String::new());
-            };
-            rsx!(cx, div { class: "crm"
-                h2 {"Add new client" margin_bottom: "10px" }
-                form { class: "pure-form"
-                    input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
-                        oninput: move |e| firstname.set(e.value.clone())
+    cx.render(rsx!(
+        body { margin_left: "35%",
+            link {
+                rel: "stylesheet",
+                href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css",
+                integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5",
+                crossorigin: "anonymous",
+            }
+
+            h1 {"Dioxus CRM Example"}
+
+            match *scene {
+                Scene::ClientsList => rsx!(
+                    div { class: "crm",
+                        h2 { margin_bottom: "10px", "List of clients" }
+                        div { class: "clients", margin_left: "10px",
+                            clients.read().iter().map(|client| rsx!(
+                                div { class: "client", style: "margin-bottom: 50px",
+                                    p { "First Name: {client.first_name}" }
+                                    p { "Last Name: {client.last_name}" }
+                                    p {"Description: {client.description}"}
+                                })
+                            )
+                        }
+                        button { class: "pure-button pure-button-primary", onclick: move |_| scene.set(Scene::NewClientForm), "Add New" }
+                        button { class: "pure-button", onclick: move |_| scene.set(Scene::Settings), "Settings" }
                     }
                     }
-                    input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
-                        oninput: move |e| lastname.set(e.value.clone())
+                ),
+                Scene::NewClientForm => rsx!(
+                    div { class: "crm",
+                        h2 { margin_bottom: "10px", "Add new client" }
+                        form { class: "pure-form",
+                            input {
+                                class: "new-client firstname",
+                                placeholder: "First name",
+                                value: "{firstname}",
+                                oninput: move |e| firstname.set(e.value.clone())
+                            }
+                            input {
+                                class: "new-client lastname",
+                                placeholder: "Last name",
+                                value: "{lastname}",
+                                oninput: move |e| lastname.set(e.value.clone())
+                            }
+                            textarea {
+                                class: "new-client description",
+                                placeholder: "Description",
+                                value: "{description}",
+                                oninput: move |e| description.set(e.value.clone())
+                            }
+                        }
+                        button {
+                            class: "pure-button pure-button-primary",
+                            onclick: move |_| {
+                                clients.write().push(Client {
+                                    description: (*description).clone(),
+                                    first_name: (*firstname).clone(),
+                                    last_name: (*lastname).clone(),
+                                });
+                                description.set(String::new());
+                                firstname.set(String::new());
+                                lastname.set(String::new());
+                            },
+                            "Add New"
+                        }
+                        button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList),
+                            "Go Back"
+                        }
                     }
                     }
-                    textarea { class: "new-client description" placeholder: "Description" value: "{description}"
-                        oninput: move |e| description.set(e.value.clone())
+                ),
+                Scene::Settings => rsx!(
+                    div {
+                        h2 { margin_bottom: "10px", "Settings" }
+                        button {
+                            background: "rgb(202, 60, 60)",
+                            class: "pure-button pure-button-primary",
+                            onclick: move |_| clients.write().clear(),
+                            "Remove all clients"
+                        }
+                        button {
+                            class: "pure-button pure-button-primary",
+                            onclick: move |_| scene.set(Scene::ClientsList),
+                            "Go Back"
+                        }
                     }
                     }
-                }
-                button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }
-                button { class: "pure-button", onclick: move |_| scene.set(Scene::ClientsList), "Go Back" }
-            })
+                )
+            }
         }
         }
-        Scene::Settings => {
-            rsx!(cx, div {
-                h2 {"Settings" margin_bottom: "10px" }
-                button {
-                    background: "rgb(202, 60, 60)"
-                    class: "pure-button pure-button-primary"
-                    onclick: move |_| clients.write().clear(),
-                    "Remove all clients"
-                }
-                button {
-                    class: "pure-button pure-button-primary"
-                    onclick: move |_| scene.set(Scene::ClientsList),
-                    "Go Back"
-                }
-            })
-        }
-    };
-
-    cx.render(rsx!(
-        body {
-           link {
-               rel: "stylesheet"
-               href: "https://unpkg.com/purecss@2.0.6/build/pure-min.css"
-               integrity: "sha384-Uu6IeWbM+gzNVXJcM9XV3SohHtmWE+3VGi496jvgX1jyvDTXfdK+rfZc8C1Aehk5"
-               crossorigin: "anonymous"
-           }
-           margin_left: "35%"
-           h1 {"Dioxus CRM Example"}
-           {scene}
-       }
     ))
     ))
-};
+}

+ 1 - 1
examples/desktop/demo.rs

@@ -13,6 +13,6 @@ static App: Component = |cx| {
         div {
         div {
             "hello world!"
             "hello world!"
         }
         }
-        {(0..10).map(|f| rsx!( div {"abc {f}"}))}
+        (0..10).map(|f| rsx!( div {"abc {f}"}))
     ))
     ))
 };
 };

+ 0 - 8
examples/desktop/kitchensink.rs

@@ -1,8 +0,0 @@
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-
-use dioxus_html as dioxus_elements;
-
-fn main() {}

+ 0 - 1
examples/desktop/tauri.rs

@@ -1 +0,0 @@
-fn main() {}

+ 7 - 12
examples/desktop/todomvc.rs

@@ -5,15 +5,10 @@ use dioxus_core::prelude::*;
 use dioxus_core_macro::*;
 use dioxus_core_macro::*;
 use dioxus_hooks::*;
 use dioxus_hooks::*;
 use dioxus_html as dioxus_elements;
 use dioxus_html as dioxus_elements;
-use simple_logger::SimpleLogger;
 
 
 use std::collections::HashMap;
 use std::collections::HashMap;
 
 
 fn main() {
 fn main() {
-    if cfg!(debug_assertions) {
-        SimpleLogger::new().init().unwrap();
-    }
-
     dioxus_desktop::launch(App)
     dioxus_desktop::launch(App)
 }
 }
 
 
@@ -103,24 +98,24 @@ pub static App: Component = |cx| {
                         }
                         }
                     }
                     }
                 }
                 }
-                ul { class: "todo-list"
-                    {filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))}
+                ul { class: "todo-list",
+                    filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id }))
                 }
                 }
-                {(!todos.read().is_empty()).then(|| rsx!(
-                    footer { class: "footer"
+                (!todos.read().is_empty()).then(|| rsx!(
+                    footer { class: "footer",
                         span { class: "todo-count" strong {"{items_left} "} span {"{item_text} left"} }
                         span { class: "todo-count" strong {"{items_left} "} span {"{item_text} left"} }
                         ul { class: "filters"
                         ul { class: "filters"
                             li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
                             li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
                             li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
                             li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
                             li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
                             li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
                         }
                         }
-                        {(show_clear_completed).then(|| rsx!(
+                        (show_clear_completed).then(|| rsx!(
                             button { class: "clear-completed", onclick: move |_| clear_completed(),
                             button { class: "clear-completed", onclick: move |_| clear_completed(),
                                 "Clear completed"
                                 "Clear completed"
                             }
                             }
-                        ))}
+                        ))
                     }
                     }
-                ))}
+                ))
             }
             }
         }
         }
         footer { class: "info"
         footer { class: "info"

+ 30 - 44
examples/file_explorer.rs

@@ -4,51 +4,45 @@
 //! This is a fun little desktop application that lets you explore the file system.
 //! This is a fun little desktop application that lets you explore the file system.
 //!
 //!
 //! This example is interesting because it's mixing filesystem operations and GUI, which is typically hard for UI to do.
 //! This example is interesting because it's mixing filesystem operations and GUI, which is typically hard for UI to do.
+//!
+//! It also uses `use_ref` to maintain a model, rather than `use_state`. That way,
+//! we dont need to clutter our code with `read` commands.
 
 
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 
 
 fn main() {
 fn main() {
-    // simple_logger::init_with_level(log::Level::Debug);
-    dioxus::desktop::launch_cfg(App, |c| {
+    dioxus::desktop::launch_cfg(app, |c| {
         c.with_window(|w| {
         c.with_window(|w| {
-            w.with_resizable(true).with_inner_size(
-                dioxus::desktop::wry::application::dpi::LogicalSize::new(400.0, 800.0),
-            )
+            w.with_resizable(true)
+                .with_inner_size(dioxus::desktop::tao::dpi::LogicalSize::new(400.0, 800.0))
         })
         })
     });
     });
 }
 }
 
 
-static App: Component = |cx| {
-    let file_manager = use_ref(&cx, Files::new);
-    let files = file_manager.read();
-
-    let file_list = files.path_names.iter().enumerate().map(|(dir_id, path)| {
-        rsx! (
-            li { a {"{path}", onclick: move |_| file_manager.write().enter_dir(dir_id), href: "#"} }
-        )
-    });
-
-    let err_disp = files.err.as_ref().map(|err| {
-        rsx! (
-            div {
-                code {"{err}"}
-                button {"x", onclick: move |_| file_manager.write().clear_err() }
-            }
-        )
-    });
-
-    let current_dir = files.current();
+fn app(cx: Scope) -> Element {
+    let files = use_ref(&cx, Files::new);
 
 
     cx.render(rsx!(
     cx.render(rsx!(
-        div {
-            h1 {"Files: "}
-            h3 {"Cur dir: {current_dir}"}
-            button { "go up", onclick: move |_| file_manager.write().go_up() }
-            ol { {file_list} }
-            {err_disp}
+        h1 { "Files: " }
+        h3 { "Cur dir: " [files.read().current()] }
+        button { onclick: move |_| files.write().go_up(), "go up" }
+        ol {
+            files.read().path_names.iter().enumerate().map(|(dir_id, path)| rsx!(
+                li { key: "{path}",
+                    a { href: "#", onclick: move |_| files.write().enter_dir(dir_id),
+                        "{path}",
+                    }
+                }
+            ))
         }
         }
+        files.read().err.as_ref().map(|err| rsx!(
+            div {
+                code { "{err}" }
+                button { onclick: move |_| files.write().clear_err(), "x" }
+            }
+        ))
     ))
     ))
-};
+}
 
 
 struct Files {
 struct Files {
     path_stack: Vec<String>,
     path_stack: Vec<String>,
@@ -70,29 +64,21 @@ impl Files {
     }
     }
 
 
     fn reload_path_list(&mut self) {
     fn reload_path_list(&mut self) {
-        let cur_path = self.path_stack.last().unwrap();
-        log::info!("Reloading path list for {:?}", cur_path);
-        let paths = match std::fs::read_dir(cur_path) {
+        let paths = match std::fs::read_dir(self.path_stack.last().unwrap()) {
             Ok(e) => e,
             Ok(e) => e,
             Err(err) => {
             Err(err) => {
-                let err = format!("An error occured: {:?}", err);
-                self.err = Some(err);
+                self.err = Some(format!("An error occured: {:?}", err));
                 self.path_stack.pop();
                 self.path_stack.pop();
                 return;
                 return;
             }
             }
         };
         };
-        let collected = paths.collect::<Vec<_>>();
-        log::info!("Path list reloaded {:#?}", collected);
 
 
         // clear the current state
         // clear the current state
         self.clear_err();
         self.clear_err();
         self.path_names.clear();
         self.path_names.clear();
 
 
-        for path in collected {
-            self.path_names
-                .push(path.unwrap().path().display().to_string());
-        }
-        log::info!("path namees are {:#?}", self.path_names);
+        self.path_names
+            .extend(paths.map(|path| path.unwrap().path().display().to_string()));
     }
     }
 
 
     fn go_up(&mut self) {
     fn go_up(&mut self) {

+ 15 - 15
examples/framework_benchmark.rs

@@ -2,8 +2,8 @@ use dioxus::prelude::*;
 use rand::prelude::*;
 use rand::prelude::*;
 
 
 fn main() {
 fn main() {
-    dioxus::web::launch(App);
-    // dioxus::desktop::launch(App);
+    // dioxus::web::launch(App);
+    dioxus::desktop::launch(App);
 }
 }
 
 
 #[derive(Clone, PartialEq)]
 #[derive(Clone, PartialEq)]
@@ -31,16 +31,16 @@ impl Label {
 }
 }
 
 
 static App: Component = |cx| {
 static App: Component = |cx| {
-    let mut items = use_ref(&cx, || vec![]);
-    let mut selected = use_state(&cx, || None);
+    let items = use_ref(&cx, || vec![]);
+    let selected = use_state(&cx, || None);
 
 
     cx.render(rsx! {
     cx.render(rsx! {
-        div { class: "container"
-            div { class: "jumbotron"
-                div { class: "row"
+        div { class: "container",
+            div { class: "jumbotron",
+                div { class: "row",
                     div { class: "col-md-6", h1 { "Dioxus" } }
                     div { class: "col-md-6", h1 { "Dioxus" } }
-                    div { class: "col-md-6"
-                        div { class: "row"
+                    div { class: "col-md-6",
+                        div { class: "row",
                             ActionButton { name: "Create 1,000 rows", id: "run",
                             ActionButton { name: "Create 1,000 rows", id: "run",
                                 onclick: move || items.set(Label::new_list(1_000)),
                                 onclick: move || items.set(Label::new_list(1_000)),
                             }
                             }
@@ -67,15 +67,15 @@ static App: Component = |cx| {
                 tbody {
                 tbody {
                     {items.read().iter().enumerate().map(|(id, item)| {
                     {items.read().iter().enumerate().map(|(id, item)| {
                         let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
                         let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
-                        rsx!(tr { class: "{is_in_danger}"
+                        rsx!(tr { class: "{is_in_danger}",
                             td { class:"col-md-1" }
                             td { class:"col-md-1" }
                             td { class:"col-md-1", "{item.key}" }
                             td { class:"col-md-1", "{item.key}" }
                             td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
                             td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
-                                a { class: "lbl", {item.labels} }
+                                a { class: "lbl", item.labels }
                             }
                             }
-                            td { class: "col-md-1"
+                            td { class: "col-md-1",
                                 a { class: "remove", onclick: move |_| { items.write().remove(id); },
                                 a { class: "remove", onclick: move |_| { items.write().remove(id); },
-                                    span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
+                                    span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" }
                                 }
                                 }
                             }
                             }
                             td { class: "col-md-6" }
                             td { class: "col-md-6" }
@@ -83,7 +83,7 @@ static App: Component = |cx| {
                     })}
                     })}
                 }
                 }
              }
              }
-            // span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" }
+            span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" }
         }
         }
     })
     })
 };
 };
@@ -96,7 +96,7 @@ struct ActionButtonProps<'a> {
 }
 }
 
 
 fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
 fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
-    rsx!(cx, div { class: "col-sm-6 smallpad"
+    rsx!(cx, div { class: "col-sm-6 smallpad",
         button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}",  onclick: move |_| (cx.props.onclick)(),
         button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}",  onclick: move |_| (cx.props.onclick)(),
             "{cx.props.name}"
             "{cx.props.name}"
         }
         }

+ 1 - 1
examples/hello_world.rs

@@ -4,7 +4,7 @@ fn main() {
     dioxus::desktop::launch(app);
     dioxus::desktop::launch(app);
 }
 }
 
 
-fn app(cx: Scope<()>) -> Element {
+fn app(cx: Scope) -> Element {
     cx.render(rsx! (
     cx.render(rsx! (
         div { "Hello, world!" }
         div { "Hello, world!" }
     ))
     ))

+ 4 - 4
examples/hydration.rs

@@ -16,18 +16,18 @@ fn main() {
     let vdom = VirtualDom::new(App);
     let vdom = VirtualDom::new(App);
     let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true));
     let content = ssr::render_vdom_cfg(&vdom, |f| f.pre_render(true));
 
 
-    dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content));
+    // dioxus::desktop::launch_cfg(App, |c| c.with_prerendered(content));
 }
 }
 
 
 static App: Component = |cx| {
 static App: Component = |cx| {
-    let mut val = use_state(&cx, || 0);
+    let val = use_state(&cx, || 0);
 
 
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
-            h1 {"hello world. Count: {val}"}
+            h1 { "hello world. Count: {val}" }
             button {
             button {
+                onclick: move |_| *val.modify() += 1,
                 "click to increment"
                 "click to increment"
-                onclick: move |_| val += 1
             }
             }
         }
         }
     })
     })

+ 20 - 20
examples/pattern_model.rs

@@ -24,42 +24,42 @@ use dioxus::prelude::*;
 const STYLE: &str = include_str!("./assets/calculator.css");
 const STYLE: &str = include_str!("./assets/calculator.css");
 fn main() {
 fn main() {
     env_logger::init();
     env_logger::init();
-    dioxus::desktop::launch_cfg(App, |cfg| {
-        cfg.with_window(|w| {
-            w.with_title("Calculator Demo")
-                .with_resizable(false)
-                .with_inner_size(LogicalSize::new(320.0, 530.0))
-        })
-    });
+    // dioxus::desktop::launch_cfg(App, |cfg| {
+    //     cfg.with_window(|w| {
+    //         w.with_title("Calculator Demo")
+    //             .with_resizable(false)
+    //             .with_inner_size(LogicalSize::new(320.0, 530.0))
+    //     })
+    // });
 }
 }
 
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let state = use_ref(&cx, || Calculator::new());
     let state = use_ref(&cx, || Calculator::new());
 
 
     let clear_display = state.read().display_value.eq("0");
     let clear_display = state.read().display_value.eq("0");
     let clear_text = if clear_display { "C" } else { "AC" };
     let clear_text = if clear_display { "C" } else { "AC" };
     let formatted = state.read().formatted_display();
     let formatted = state.read().formatted_display();
 
 
-    rsx!(cx, div { id: "wrapper"
+    rsx!(cx, div { id: "wrapper",
         div { class: "app", style { "{STYLE}" }
         div { class: "app", style { "{STYLE}" }
             div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
             div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
                 div { class: "calculator-display", "{formatted}"}
                 div { class: "calculator-display", "{formatted}"}
-                div { class: "calculator-keypad"
-                    div { class: "input-keys"
-                        div { class: "function-keys"
+                div { class: "calculator-keypad",
+                    div { class: "input-keys",
+                        div { class: "function-keys",
                             CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" }
                             CalculatorKey { name: "key-clear", onclick: move |_| state.write().clear_display(), "{clear_text}" }
                             CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"}
                             CalculatorKey { name: "key-sign", onclick: move |_| state.write().toggle_sign(), "±"}
                             CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"}
                             CalculatorKey { name: "key-percent", onclick: move |_| state.write().toggle_percent(), "%"}
                         }
                         }
-                        div { class: "digit-keys"
+                        div { class: "digit-keys",
                             CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
                             CalculatorKey { name: "key-0", onclick: move |_| state.write().input_digit(0), "0" }
                             CalculatorKey { name: "key-dot", onclick: move |_|  state.write().input_dot(), "●" }
                             CalculatorKey { name: "key-dot", onclick: move |_|  state.write().input_dot(), "●" }
-                            {(1..10).map(move |k| rsx!{
+                            (1..10).map(move |k| rsx!{
                                 CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_|  state.write().input_digit(k), "{k}" }
                                 CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_|  state.write().input_digit(k), "{k}" }
-                            })}
+                            })
                         }
                         }
                     }
                     }
-                    div { class: "operator-keys"
+                    div { class: "operator-keys",
                         CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" }
                         CalculatorKey { name:"key-divide", onclick: move |_| state.write().set_operator(Operator::Div), "÷" }
                         CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" }
                         CalculatorKey { name:"key-multiply", onclick: move |_| state.write().set_operator(Operator::Mul), "×" }
                         CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
                         CalculatorKey { name:"key-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
@@ -70,7 +70,7 @@ static App: Component = |cx| {
             }
             }
         }
         }
     })
     })
-};
+}
 
 
 #[derive(Props)]
 #[derive(Props)]
 struct CalculatorKeyProps<'a> {
 struct CalculatorKeyProps<'a> {
@@ -82,9 +82,9 @@ struct CalculatorKeyProps<'a> {
 fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
 fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
     cx.render(rsx! {
     cx.render(rsx! {
         button {
         button {
-            class: "calculator-key {cx.props.name}"
-            onclick: move |e| (cx.props.onclick)(e)
-            {&cx.props.children}
+            class: "calculator-key {cx.props.name}",
+            onclick: move |e| (cx.props.onclick)(e),
+            &cx.props.children
         }
         }
     })
     })
 }
 }

+ 11 - 13
examples/pattern_reducer.rs

@@ -14,20 +14,18 @@ fn main() {
 pub static App: Component = |cx| {
 pub static App: Component = |cx| {
     let state = use_state(&cx, PlayerState::new);
     let state = use_state(&cx, PlayerState::new);
 
 
-    let is_playing = state.is_playing();
-
-    rsx!(cx, div {
-        h1 {"Select an option"}
-        h3 {"The radio is... {is_playing}!"}
-        button {
-            "Pause"
-            onclick: move |_| state.modify().reduce(PlayerAction::Pause)
-        }
-        button {
-            "Play"
-            onclick: move |_| state.modify().reduce(PlayerAction::Play)
+    cx.render(rsx!(
+        div {
+            h1 {"Select an option"}
+            h3 { "The radio is... " [state.is_playing()], "!" }
+            button { onclick: move |_| state.modify().reduce(PlayerAction::Pause),
+                "Pause"
+            }
+            button { onclick: move |_| state.modify().reduce(PlayerAction::Play),
+                "Play"
+            }
         }
         }
-    })
+    ))
 };
 };
 
 
 enum PlayerAction {
 enum PlayerAction {

+ 1 - 0
examples/readme.rs

@@ -3,6 +3,7 @@
 //! The example from the README.md.
 //! The example from the README.md.
 
 
 use dioxus::prelude::*;
 use dioxus::prelude::*;
+
 fn main() {
 fn main() {
     dioxus::desktop::launch(App);
     dioxus::desktop::launch(App);
 }
 }

+ 61 - 0
examples/rsx_compile_fail.rs

@@ -0,0 +1,61 @@
+use dioxus::prelude::*;
+
+fn main() {
+    let mut vdom = VirtualDom::new(example);
+    vdom.rebuild();
+
+    let out = dioxus::ssr::render_vdom_cfg(&vdom, |c| c.newline(true).indent(true));
+    println!("{}", out);
+}
+
+fn example(cx: Scope) -> Element {
+    let items = use_state(&cx, || {
+        vec![Thing {
+            a: "asd".to_string(),
+            b: 10,
+        }]
+    });
+
+    let things = use_ref(&cx, || {
+        vec![Thing {
+            a: "asd".to_string(),
+            b: 10,
+        }]
+    });
+    let things_list = things.read();
+
+    let mything = use_ref(&cx, || Some(String::from("asd")));
+    let mything_read = mything.read();
+
+    cx.render(rsx!(
+        div {
+            div {
+                id: "asd",
+                "your neighborhood spiderman"
+
+                items.iter().cycle().take(5).map(|f| rsx!{
+                    div {
+                        "{f.a}"
+                    }
+                })
+
+                things_list.iter().map(|f| rsx!{
+                    div {
+                        "{f.a}"
+                    }
+                })
+
+                mything_read.as_ref().map(|f| rsx!{
+                    div {
+                       "{f}"
+                    }
+                })
+            }
+        }
+    ))
+}
+
+struct Thing {
+    a: String,
+    b: u32,
+}

+ 54 - 29
examples/rsx_usage.rs

@@ -60,6 +60,7 @@ pub static EXAMPLE: Component = |cx| {
             h1 {"Some text"}
             h1 {"Some text"}
             h1 {"Some text with {formatting}"}
             h1 {"Some text with {formatting}"}
             h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
             h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
+            h1 {"Formatting without interpolation " [formatting_tuple.0] "and" [formatting_tuple.1] }
             h2 {
             h2 {
                 "Multiple"
                 "Multiple"
                 "Text"
                 "Text"
@@ -72,7 +73,7 @@ pub static EXAMPLE: Component = |cx| {
                 h3 {"elements"}
                 h3 {"elements"}
             }
             }
             div {
             div {
-                class: "my special div"
+                class: "my special div",
                 h1 {"Headers and attributes!"}
                 h1 {"Headers and attributes!"}
             }
             }
             div {
             div {
@@ -88,42 +89,51 @@ pub static EXAMPLE: Component = |cx| {
             }
             }
 
 
             // Expressions can be used in element position too:
             // Expressions can be used in element position too:
-            {rsx!(p { "More templating!" })}
-            // {html!(<p>"Even HTML templating!!"</p>)}
+            rsx!(p { "More templating!" }),
 
 
             // Iterators
             // Iterators
-            {(0..10).map(|i| rsx!(li { "{i}" }))}
-            {{
+            (0..10).map(|i| rsx!(li { "{i}" })),
+
+            // Iterators within expressions
+            {
                 let data = std::collections::HashMap::<&'static str, &'static str>::new();
                 let data = std::collections::HashMap::<&'static str, &'static str>::new();
                 // Iterators *should* have keys when you can provide them.
                 // Iterators *should* have keys when you can provide them.
                 // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
                 // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
                 // Using an "ID" associated with your data is a good idea.
                 // Using an "ID" associated with your data is a good idea.
-                data.into_iter().map(|(k, v)| rsx!(li { key: "{k}" "{v}" }))
-            }}
+                data.into_iter().map(|(k, v)| rsx!(li { key: "{k}", "{v}" }))
+            }
 
 
             // Matching
             // Matching
-            {match true {
+            match true {
                 true => rsx!( h1 {"Top text"}),
                 true => rsx!( h1 {"Top text"}),
                 false => rsx!( h1 {"Bottom text"})
                 false => rsx!( h1 {"Bottom text"})
-            }}
+            }
 
 
             // Conditional rendering
             // Conditional rendering
             // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
             // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
             // You can convert a bool condition to rsx! with .then and .or
             // You can convert a bool condition to rsx! with .then and .or
-            {true.then(|| rsx!(div {}))}
+            true.then(|| rsx!(div {})),
+
+            // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element
+            if false {
+                rsx!(h1 {"Top text"})
+            } else {
+                rsx!(h1 {"Bottom text"})
+            }
 
 
-            // True conditions need to be rendered (same reasons as matching)
-            {if true {
-                rsx!(cx, h1 {"Top text"})
+            // Using optionals for diverging branches
+            if true {
+                Some(rsx!(h1 {"Top text"}))
             } else {
             } else {
-                rsx!(cx, h1 {"Bottom text"})
-            }}
+                None
+            }
+
 
 
-            // returning "None" is a bit noisy... but rare in practice
-            {None as Option<()>}
+            // returning "None" without a diverging branch is a bit noisy... but rare in practice
+            None as Option<()>,
 
 
             // Use the Dioxus type-alias for less noise
             // Use the Dioxus type-alias for less noise
-            {NONE_ELEMENT}
+            NONE_ELEMENT,
 
 
             // can also just use empty fragments
             // can also just use empty fragments
             Fragment {}
             Fragment {}
@@ -137,9 +147,8 @@ pub static EXAMPLE: Component = |cx| {
                 Fragment {
                 Fragment {
                     "D"
                     "D"
                     Fragment {
                     Fragment {
-                        "heavily nested fragments is an antipattern"
-                        "they cause Dioxus to do unnecessary work"
-                        "don't use them carelessly if you can help it"
+                        "E"
+                        "F"
                     }
                     }
                 }
                 }
             }
             }
@@ -158,22 +167,29 @@ pub static EXAMPLE: Component = |cx| {
             Taller { a: "asd" }
             Taller { a: "asd" }
 
 
             // Can pass in props directly as an expression
             // Can pass in props directly as an expression
-            {{
+            {
                 let props = TallerProps {a: "hello", children: Default::default()};
                 let props = TallerProps {a: "hello", children: Default::default()};
                 rsx!(Taller { ..props })
                 rsx!(Taller { ..props })
-            }}
+            }
 
 
             // Spreading can also be overridden manually
             // Spreading can also be overridden manually
             Taller {
             Taller {
-                ..TallerProps { a: "ballin!", children: Default::default() }
+                ..TallerProps { a: "ballin!", children: Default::default() },
                 a: "not ballin!"
                 a: "not ballin!"
             }
             }
 
 
             // Can take children too!
             // Can take children too!
             Taller { a: "asd", div {"hello world!"} }
             Taller { a: "asd", div {"hello world!"} }
 
 
+            // Components can be used with the `call` syntax
+            // This component's props are defined *inline* with the `inline_props` macro
+            with_inline(
+                text: "using functionc all syntax"
+            )
+
             // helper functions
             // helper functions
-            {helper(&cx, "hello world!")}
+            // Single values must be wrapped in braces or `Some` to satisfy `IntoIterator`
+            [helper(&cx, "hello world!")]
         }
         }
     })
     })
 };
 };
@@ -187,6 +203,7 @@ mod baller {
     #[derive(Props, PartialEq)]
     #[derive(Props, PartialEq)]
     pub struct BallerProps {}
     pub struct BallerProps {}
 
 
+    #[allow(non_snake_case)]
     /// This component totally balls
     /// This component totally balls
     pub fn Baller(_: Scope<BallerProps>) -> Element {
     pub fn Baller(_: Scope<BallerProps>) -> Element {
         todo!()
         todo!()
@@ -195,12 +212,20 @@ mod baller {
 
 
 #[derive(Props)]
 #[derive(Props)]
 pub struct TallerProps<'a> {
 pub struct TallerProps<'a> {
+    /// Fields are documented and accessible in rsx!
     a: &'static str,
     a: &'static str,
     children: Element<'a>,
     children: Element<'a>,
 }
 }
 
 
-/// This component is taller than most :)
-pub fn Taller<'a>(_: Scope<'a, TallerProps<'a>>) -> Element {
-    let b = true;
-    todo!()
+/// Documention for this component is visible within the rsx macro
+#[allow(non_snake_case)]
+pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element {
+    cx.render(rsx! {
+        &cx.props.children
+    })
+}
+
+#[inline_props]
+fn with_inline<'a>(cx: Scope<'a>, text: &'a str) -> Element {
+    rsx!(cx, p { "{text}" })
 }
 }

+ 38 - 38
examples/tailwind.rs

@@ -16,9 +16,9 @@ const STYLE: &str = "body {overflow:hidden;}";
 
 
 pub static App: Component = |cx| {
 pub static App: Component = |cx| {
     cx.render(rsx!(
     cx.render(rsx!(
-        div { class: "overflow-hidden"
+        div { class: "overflow-hidden",
         style { "{STYLE}" }
         style { "{STYLE}" }
-            link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
+            link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
             Header {}
             Header {}
             Entry {}
             Entry {}
             Hero {}
             Hero {}
@@ -33,20 +33,20 @@ pub static App: Component = |cx| {
 pub static Header: Component = |cx| {
 pub static Header: Component = |cx| {
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
-            header { class: "text-gray-400 bg-gray-900 body-font"
-                div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center"
-                    a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0"
+            header { class: "text-gray-400 bg-gray-900 body-font",
+                div { class: "container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center",
+                    a { class: "flex title-font font-medium items-center text-white mb-4 md:mb-0",
                         StacksIcon {}
                         StacksIcon {}
-                        span { class: "ml-3 text-xl" "Hello Dioxus!"}
+                        span { class: "ml-3 text-xl", "Hello Dioxus!"}
                     }
                     }
-                    nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center"
-                        a { class: "mr-5 hover:text-white" "First Link"}
-                        a { class: "mr-5 hover:text-white" "Second Link"}
-                        a { class: "mr-5 hover:text-white" "Third Link"}
-                        a { class: "mr-5 hover:text-white" "Fourth Link"}
+                    nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center",
+                        a { class: "mr-5 hover:text-white", "First Link"}
+                        a { class: "mr-5 hover:text-white", "Second Link"}
+                        a { class: "mr-5 hover:text-white", "Third Link"}
+                        a { class: "mr-5 hover:text-white", "Fourth Link"}
                     }
                     }
                     button {
                     button {
-                        class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0"
+                        class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
                         "Button"
                         "Button"
                         RightArrowIcon {}
                         RightArrowIcon {}
                     }
                     }
@@ -59,34 +59,34 @@ pub static Header: Component = |cx| {
 pub static Hero: Component = |cx| {
 pub static Hero: Component = |cx| {
     //
     //
     cx.render(rsx! {
     cx.render(rsx! {
-        section{ class: "text-gray-400 bg-gray-900 body-font"
-            div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
-                div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center"
-                    h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white"
+        section{ class: "text-gray-400 bg-gray-900 body-font",
+            div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center",
+                div { class: "lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center",
+                    h1 { class: "title-font sm:text-4xl text-3xl mb-4 font-medium text-white",
                         br { class: "hidden lg:inline-block" }
                         br { class: "hidden lg:inline-block" }
                         "Dioxus Sneak Peek"
                         "Dioxus Sneak Peek"
                     }
                     }
                     p {
                     p {
-                        class: "mb-8 leading-relaxed"
+                        class: "mb-8 leading-relaxed",
 
 
                         "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
                         "Dioxus is a new UI framework that makes it easy and simple to write cross-platform apps using web
                         technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
                         technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
                         on mobile and embedded platforms."
                         on mobile and embedded platforms."
 
 
                     }
                     }
-                    div { class: "flex justify-center"
+                    div { class: "flex justify-center",
                         button {
                         button {
-                            class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg"
+                            class: "inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg",
                             "Learn more"
                             "Learn more"
                         }
                         }
                         button {
                         button {
-                            class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg" 
+                            class: "ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg",
                             "Build an app"
                             "Build an app"
                         }
                         }
                     }
                     }
                 }
                 }
-                div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6"
-                    img { class: "object-cover object-center rounded" alt: "hero" src: "https://i.imgur.com/oK6BLtw.png" 
+                div { class: "lg:max-w-lg lg:w-full md:w-1/2 w-5/6",
+                    img { class: "object-cover object-center rounded", alt: "hero", src: "https://i.imgur.com/oK6BLtw.png",
                     referrerpolicy:"no-referrer"
                     referrerpolicy:"no-referrer"
                 }
                 }
                 }
                 }
@@ -97,8 +97,8 @@ pub static Hero: Component = |cx| {
 pub static Entry: Component = |cx| {
 pub static Entry: Component = |cx| {
     //
     //
     cx.render(rsx! {
     cx.render(rsx! {
-        section{ class: "text-gray-400 bg-gray-900 body-font"
-            div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center"
+        section{ class: "text-gray-400 bg-gray-900 body-font",
+            div { class: "container mx-auto flex px-5 py-24 md:flex-row flex-col items-center",
                 textarea {
                 textarea {
 
 
                 }
                 }
@@ -111,13 +111,13 @@ pub static StacksIcon: Component = |cx| {
     cx.render(rsx!(
     cx.render(rsx!(
         svg {
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
             // xmlns: "http://www.w3.org/2000/svg"
-            fill: "none"
-            stroke: "currentColor"
-            stroke_linecap: "round"
-            stroke_linejoin: "round"
-            stroke_width: "2"
-            class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full"
-            view_box: "0 0 24 24"
+            fill: "none",
+            stroke: "currentColor",
+            stroke_linecap: "round",
+            stroke_linejoin: "round",
+            stroke_width: "2",
+            class: "w-10 h-10 text-white p-2 bg-indigo-500 rounded-full",
+            view_box: "0 0 24 24",
             path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
             path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
         }
         }
     ))
     ))
@@ -125,13 +125,13 @@ pub static StacksIcon: Component = |cx| {
 pub static RightArrowIcon: Component = |cx| {
 pub static RightArrowIcon: Component = |cx| {
     cx.render(rsx!(
     cx.render(rsx!(
         svg {
         svg {
-            fill: "none"
-            stroke: "currentColor"
-            stroke_linecap: "round"
-            stroke_linejoin: "round"
-            stroke_width: "2"
-            class: "w-4 h-4 ml-1"
-            view_box: "0 0 24 24"
+            fill: "none",
+            stroke: "currentColor",
+            stroke_linecap: "round",
+            stroke_linejoin: "round",
+            stroke_width: "2",
+            class: "w-4 h-4 ml-1",
+            view_box: "0 0 24 24",
             path { d: "M5 12h14M12 5l7 7-7 7"}
             path { d: "M5 12h14M12 5l7 7-7 7"}
         }
         }
     ))
     ))

+ 12 - 7
examples/tasks.rs

@@ -2,26 +2,31 @@
 //!
 //!
 //! The example from the README.md.
 //! The example from the README.md.
 
 
+use dioxus::prelude::*;
 use std::time::Duration;
 use std::time::Duration;
 
 
-use dioxus::prelude::*;
 fn main() {
 fn main() {
     dioxus::desktop::launch(app);
     dioxus::desktop::launch(app);
 }
 }
 
 
-fn app(cx: Scope<()>) -> Element {
-    let mut count = use_state(&cx, || 0);
+fn app(cx: Scope) -> Element {
+    let count = use_state(&cx, || 0);
 
 
-    cx.push_future(|| async move {
-        tokio::time::sleep(Duration::from_millis(100)).await;
-        count += 1;
+    use_future(&cx, || {
+        for_async![count];
+        async move {
+            loop {
+                tokio::time::sleep(Duration::from_millis(1000)).await;
+                count += 1;
+            }
+        }
     });
     });
 
 
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {
             h1 { "High-Five counter: {count}" }
             h1 { "High-Five counter: {count}" }
             button {
             button {
-                onclick: move |_| count +=1 ,
+                onclick: move |_| *count.modify() += 1,
                 "Click me!"
                 "Click me!"
             }
             }
         }
         }

+ 21 - 18
examples/todomvc.rs

@@ -29,7 +29,7 @@ const App: Component = |cx| {
 
 
     let todolist = todos
     let todolist = todos
         .iter()
         .iter()
-        .filter(|(id, item)| match *filter {
+        .filter(|(_id, item)| match *filter {
             FilterState::All => true,
             FilterState::All => true,
             FilterState::Active => !item.checked,
             FilterState::Active => !item.checked,
             FilterState::Completed => item.checked,
             FilterState::Completed => item.checked,
@@ -48,34 +48,37 @@ const App: Component = |cx| {
         _ => "items",
         _ => "items",
     };
     };
 
 
-    rsx!(cx, div { id: "app"
+    rsx!(cx, div { id: "app",
         style {"{STYLE}"}
         style {"{STYLE}"}
         div {
         div {
-            header { class: "header"
+            header { class: "header",
                 h1 {"todos"}
                 h1 {"todos"}
                 input {
                 input {
-                    class: "new-todo"
-                    placeholder: "What needs to be done?"
-                    value: "{draft}"
-                    oninput: move |evt| draft.set(evt.value.clone())
+                    class: "new-todo",
+                    placeholder: "What needs to be done?",
+                    value: "{draft}",
+                    oninput: move |evt| draft.set(evt.value.clone()),
                 }
                 }
             }
             }
-            {todolist}
-            {(!todos.is_empty()).then(|| rsx!(
+            todolist,
+            (!todos.is_empty()).then(|| rsx!(
                 footer {
                 footer {
-                    span { strong {"{items_left}"} span {"{item_text} left"} }
-                    ul { class: "filters"
+                    span {
+                        strong {"{items_left}"}
+                        span {"{item_text} left"}
+                    }
+                    ul { class: "filters",
                         li { class: "All", a { href: "", onclick: move |_| filter.set(FilterState::All), "All" }}
                         li { class: "All", a { href: "", onclick: move |_| filter.set(FilterState::All), "All" }}
                         li { class: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }}
                         li { class: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }}
                         li { class: "Completed", a { href: "completed", onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
                         li { class: "Completed", a { href: "completed", onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
                     }
                     }
                 }
                 }
-            ))}
+            ))
         }
         }
-        footer { class: "info"
+        footer { class: "info",
             p {"Double-click to edit a todo"}
             p {"Double-click to edit a todo"}
-            p { "Created by ", a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }}
-            p { "Part of ", a { "TodoMVC", href: "http://todomvc.com" }}
+            p { "Created by ", a {  href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
+            p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
         }
         }
     })
     })
 };
 };
@@ -93,13 +96,13 @@ pub fn TodoEntry(cx: Scope<TodoEntryProps>) -> Element {
     rsx!(cx, li {
     rsx!(cx, li {
         "{todo.id}"
         "{todo.id}"
         input {
         input {
-            class: "toggle"
-            r#type: "checkbox"
+            class: "toggle",
+            r#type: "checkbox",
             "{todo.checked}"
             "{todo.checked}"
         }
         }
        {is_editing.then(|| rsx!{
        {is_editing.then(|| rsx!{
             input {
             input {
-                value: "{contents}"
+                value: "{contents}",
                 oninput: move |evt| contents.set(evt.value.clone())
                 oninput: move |evt| contents.set(evt.value.clone())
             }
             }
         })}
         })}

+ 9 - 18
examples/web_tick.rs

@@ -13,28 +13,19 @@
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 
 
 fn main() {
 fn main() {
-    #[cfg(target_arch = "wasm32")]
-    intern_strings();
-
-    dioxus::web::launch(App);
+    dioxus::desktop::launch(App);
 }
 }
 
 
 static App: Component = |cx| {
 static App: Component = |cx| {
     let mut rng = SmallRng::from_entropy();
     let mut rng = SmallRng::from_entropy();
-    let rows = (0..1_000).map(|f| {
-        let label = Label::new(&mut rng);
-        rsx! {
-            Row {
-                row_id: f,
-                label: label
-            }
-        }
-    });
 
 
     cx.render(rsx! {
     cx.render(rsx! {
         table {
         table {
             tbody {
             tbody {
-                {rows}
+                (0..1_000).map(|f| {
+                    let label = Label::new(&mut rng);
+                    rsx! (Row { row_id: f, label: label })
+                })
             }
             }
         }
         }
     })
     })
@@ -50,12 +41,12 @@ fn Row(cx: Scope<RowProps>) -> Element {
     cx.render(rsx! {
     cx.render(rsx! {
         tr {
         tr {
             td { class:"col-md-1", "{cx.props.row_id}" }
             td { class:"col-md-1", "{cx.props.row_id}" }
-            td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
+            td { class:"col-md-1", onclick: move |_| { /* run onselect */ },
                 a { class: "lbl", "{adj}" "{col}" "{noun}" }
                 a { class: "lbl", "{adj}" "{col}" "{noun}" }
             }
             }
-            td { class: "col-md-1"
-                a { class: "remove", onclick: move |_| {/* remove */}
-                    span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
+            td { class: "col-md-1",
+                a { class: "remove", onclick: move |_| {/* remove */},
+                    span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" }
                 }
                 }
             }
             }
             td { class: "col-md-6" }
             td { class: "col-md-6" }

+ 3 - 4
examples/webview_web.rs

@@ -1,4 +1,3 @@
-#![allow(non_upper_case_globals, non_snake_case)]
 //! Example: Webview Renderer
 //! Example: Webview Renderer
 //! -------------------------
 //! -------------------------
 //!
 //!
@@ -13,10 +12,10 @@
 use dioxus::prelude::*;
 use dioxus::prelude::*;
 
 
 fn main() {
 fn main() {
-    dioxus::web::launch(App);
+    dioxus::desktop::launch(app);
 }
 }
 
 
-static App: Component = |cx| {
+fn app(cx: Scope) -> Element {
     let mut count = use_state(&cx, || 0);
     let mut count = use_state(&cx, || 0);
 
 
     cx.render(rsx! {
     cx.render(rsx! {
@@ -26,4 +25,4 @@ static App: Component = |cx| {
             button { onclick: move |_| count -= 1, "Down low!" }
             button { onclick: move |_| count -= 1, "Down low!" }
         }
         }
     })
     })
-};
+}

+ 31 - 0
examples/xss_safety.rs

@@ -0,0 +1,31 @@
+use dioxus::prelude::*;
+
+fn main() {
+    dioxus::desktop::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+    let contents = use_state(&cx, || String::from("<script>alert(123)</script>"));
+
+    cx.render(rsx! {
+        div {
+            "hello world!"
+
+            h1 {
+                "{contents}"
+            }
+            h3 {
+                [contents.as_str()]
+            }
+
+            input {
+                value: "{contents}",
+                oninput: move |e| {
+                    contents.set(e.value.clone());
+                    eprintln!("asd");
+                },
+                "type": "text",
+            }
+        }
+    })
+}

+ 2 - 1
packages/core-macro/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 [package]
 name = "dioxus-core-macro"
 name = "dioxus-core-macro"
-version = "0.1.2"
+version = "0.1.3"
 authors = ["Jonathan Kelley"]
 authors = ["Jonathan Kelley"]
 edition = "2021"
 edition = "2021"
 description = "Core macro for Dioxus Virtual DOM"
 description = "Core macro for Dioxus Virtual DOM"
@@ -16,6 +16,7 @@ proc-macro = true
 
 
 [dependencies]
 [dependencies]
 once_cell = "1.8"
 once_cell = "1.8"
+proc-macro-error = "1.0.4"
 proc-macro2 = { version = "1.0.6" }
 proc-macro2 = { version = "1.0.6" }
 quote = "1.0"
 quote = "1.0"
 syn = { version = "1.0.11", features = ["full", "extra-traits"] }
 syn = { version = "1.0.11", features = ["full", "extra-traits"] }

+ 1 - 0
packages/core-macro/src/inlineprops.rs

@@ -99,6 +99,7 @@ impl ToTokens for InlinePropsBody {
         };
         };
 
 
         out_tokens.append_all(quote! {
         out_tokens.append_all(quote! {
+            #[allow(non_camel_case)]
             #modifiers
             #modifiers
             #vis struct #struct_name #generics {
             #vis struct #struct_name #generics {
                 #(#fields),*
                 #(#fields),*

+ 4 - 3
packages/core-macro/src/lib.rs

@@ -161,7 +161,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
 ///     pub struct BallerProps {}
 ///     pub struct BallerProps {}
 ///
 ///
 ///     /// This component totally balls
 ///     /// This component totally balls
-///     pub fn Baller(cx: Scope<()>) -> DomTree {
+///     pub fn Baller(cx: Scope) -> DomTree {
 ///         todo!()
 ///         todo!()
 ///     }
 ///     }
 /// }
 /// }
@@ -177,11 +177,12 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
 ///     todo!()
 ///     todo!()
 /// }
 /// }
 /// ```
 /// ```
+#[proc_macro_error::proc_macro_error]
 #[proc_macro]
 #[proc_macro]
 pub fn rsx(s: TokenStream) -> TokenStream {
 pub fn rsx(s: TokenStream) -> TokenStream {
     match syn::parse::<rsx::CallBody>(s) {
     match syn::parse::<rsx::CallBody>(s) {
-        Err(e) => e.to_compile_error().into(),
-        Ok(s) => s.to_token_stream().into(),
+        Err(err) => err.to_compile_error().into(),
+        Ok(stream) => stream.to_token_stream().into(),
     }
     }
 }
 }
 
 

+ 0 - 64
packages/core-macro/src/rsx/ambiguous.rs

@@ -1,64 +0,0 @@
-//! Parse anything that has a pattern of < Ident, Bracket >
-//! ========================================================
-//!
-//! Whenever a `name {}` pattern emerges, we need to parse it into an element, a component, or a fragment.
-//! This feature must support:
-//! - Namepsaced/pathed components
-//! - Differentiating between built-in and custom elements
-
-use super::*;
-
-use proc_macro2::TokenStream as TokenStream2;
-use quote::ToTokens;
-use syn::{
-    parse::{Parse, ParseStream},
-    Error, Ident, Result, Token,
-};
-
-#[allow(clippy::large_enum_variant)]
-pub enum AmbiguousElement {
-    Element(Element),
-    Component(Component),
-}
-
-impl Parse for AmbiguousElement {
-    fn parse(input: ParseStream) -> Result<Self> {
-        // Try to parse as an absolute path and immediately defer to the componetn
-        if input.peek(Token![::]) {
-            return input.parse::<Component>().map(AmbiguousElement::Component);
-        }
-
-        // If not an absolute path, then parse the ident and check if it's a valid tag
-        if let Ok(pat) = input.fork().parse::<syn::Path>() {
-            if pat.segments.len() > 1 {
-                return input.parse::<Component>().map(AmbiguousElement::Component);
-            }
-        }
-
-        use syn::ext::IdentExt;
-        if let Ok(name) = input.fork().call(Ident::parse_any) {
-            let name_str = name.to_string();
-
-            let first_char = name_str.chars().next().unwrap();
-            if first_char.is_ascii_uppercase() {
-                input.parse::<Component>().map(AmbiguousElement::Component)
-            } else {
-                if input.peek2(syn::token::Paren) {
-                    input.parse::<Component>().map(AmbiguousElement::Component)
-                } else {
-                    input.parse::<Element>().map(AmbiguousElement::Element)
-                }
-            }
-        } else {
-            Err(Error::new(input.span(), "Not a valid Html tag"))
-        }
-    }
-}
-impl ToTokens for AmbiguousElement {
-    fn to_tokens(&self, tokens: &mut TokenStream2) {
-        match self {
-            AmbiguousElement::Element(el) => el.to_tokens(tokens),
-            AmbiguousElement::Component(comp) => comp.to_tokens(tokens),
-        }
-    }
-}

+ 0 - 66
packages/core-macro/src/rsx/body.rs

@@ -1,66 +0,0 @@
-use proc_macro2::TokenStream as TokenStream2;
-use quote::{quote, ToTokens, TokenStreamExt};
-use syn::{
-    parse::{Parse, ParseStream},
-    Ident, Result, Token,
-};
-
-use super::*;
-
-pub struct CallBody {
-    custom_context: Option<Ident>,
-    roots: Vec<BodyNode>,
-}
-
-/// The custom rusty variant of parsing rsx!
-impl Parse for CallBody {
-    fn parse(input: ParseStream) -> Result<Self> {
-        let custom_context = try_parse_custom_context(input)?;
-        let (_, roots, _) = BodyConfig::new_call_body().parse_component_body(input)?;
-        Ok(Self {
-            custom_context,
-            roots,
-        })
-    }
-}
-
-fn try_parse_custom_context(input: ParseStream) -> Result<Option<Ident>> {
-    let res = if input.peek(Ident) && input.peek2(Token![,]) {
-        let name = input.parse::<Ident>()?;
-        input.parse::<Token![,]>()?;
-        Some(name)
-    } else {
-        None
-    };
-    Ok(res)
-}
-
-/// Serialize the same way, regardless of flavor
-impl ToTokens for CallBody {
-    fn to_tokens(&self, out_tokens: &mut TokenStream2) {
-        let inner = if self.roots.len() == 1 {
-            let inner = &self.roots[0];
-            quote! {#inner}
-        } else {
-            let childs = &self.roots;
-            quote! { __cx.fragment_root([ #(#childs),* ]) }
-        };
-
-        match &self.custom_context {
-            // The `in cx` pattern allows directly rendering
-            Some(ident) => out_tokens.append_all(quote! {
-                #ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
-                    use dioxus_elements::{GlobalAttributes, SvgAttributes};
-                    #inner
-                }))
-            }),
-            // Otherwise we just build the LazyNode wrapper
-            None => out_tokens.append_all(quote! {
-                LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
-                    use dioxus_elements::{GlobalAttributes, SvgAttributes};
-                    #inner
-                })
-            }),
-        };
-    }
-}

+ 11 - 86
packages/core-macro/src/rsx/component.rs

@@ -19,11 +19,10 @@ use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
 use syn::{
     ext::IdentExt,
     ext::IdentExt,
     parse::{Parse, ParseBuffer, ParseStream},
     parse::{Parse, ParseBuffer, ParseStream},
-    token, Error, Expr, ExprClosure, Ident, Result, Token,
+    token, Expr, ExprClosure, Ident, Result, Token,
 };
 };
 
 
 pub struct Component {
 pub struct Component {
-    // accept any path-like argument
     name: syn::Path,
     name: syn::Path,
     body: Vec<ComponentField>,
     body: Vec<ComponentField>,
     children: Vec<BodyNode>,
     children: Vec<BodyNode>,
@@ -32,12 +31,8 @@ pub struct Component {
 
 
 impl Parse for Component {
 impl Parse for Component {
     fn parse(stream: ParseStream) -> Result<Self> {
     fn parse(stream: ParseStream) -> Result<Self> {
-        // let name = s.parse::<syn::ExprPath>()?;
-        // todo: look into somehow getting the crate/super/etc
-
         let name = syn::Path::parse_mod_style(stream)?;
         let name = syn::Path::parse_mod_style(stream)?;
 
 
-        // parse the guts
         let content: ParseBuffer;
         let content: ParseBuffer;
 
 
         // if we see a `{` then we have a block
         // if we see a `{` then we have a block
@@ -48,91 +43,32 @@ impl Parse for Component {
             syn::parenthesized!(content in stream);
             syn::parenthesized!(content in stream);
         }
         }
 
 
-        let cfg: BodyConfig = BodyConfig {
-            allow_children: true,
-            allow_fields: true,
-            allow_manual_props: true,
-        };
-
-        let (body, children, manual_props) = cfg.parse_component_body(&content)?;
-
-        Ok(Self {
-            name,
-            body,
-            children,
-            manual_props,
-        })
-    }
-}
-
-pub struct BodyConfig {
-    pub allow_fields: bool,
-    pub allow_children: bool,
-    pub allow_manual_props: bool,
-}
-
-impl BodyConfig {
-    /// The configuration to parse the root
-    pub fn new_call_body() -> Self {
-        Self {
-            allow_children: true,
-            allow_fields: false,
-            allow_manual_props: false,
-        }
-    }
-}
-
-impl BodyConfig {
-    // todo: unify this body parsing for both elements and components
-    // both are style rather ad-hoc, though components are currently more configured
-    pub fn parse_component_body(
-        &self,
-        content: &ParseBuffer,
-    ) -> Result<(Vec<ComponentField>, Vec<BodyNode>, Option<Expr>)> {
         let mut body = Vec::new();
         let mut body = Vec::new();
         let mut children = Vec::new();
         let mut children = Vec::new();
         let mut manual_props = None;
         let mut manual_props = None;
 
 
-        'parsing: loop {
-            // [1] Break if empty
-            if content.is_empty() {
-                break 'parsing;
-            }
-
+        while !content.is_empty() {
+            // if we splat into a component then we're merging properties
             if content.peek(Token![..]) {
             if content.peek(Token![..]) {
-                if !self.allow_manual_props {
-                    return Err(Error::new(
-                        content.span(),
-                        "Props spread syntax is not allowed in this context. \nMake to only use the elipsis `..` in Components.",
-                    ));
-                }
                 content.parse::<Token![..]>()?;
                 content.parse::<Token![..]>()?;
                 manual_props = Some(content.parse::<Expr>()?);
                 manual_props = Some(content.parse::<Expr>()?);
             } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
             } else if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
-                if !self.allow_fields {
-                    return Err(Error::new(
-                        content.span(),
-                        "Property fields is not allowed in this context. \nMake to only use fields in Components or Elements.",
-                    ));
-                }
                 body.push(content.parse::<ComponentField>()?);
                 body.push(content.parse::<ComponentField>()?);
             } else {
             } else {
-                if !self.allow_children {
-                    return Err(Error::new(
-                        content.span(),
-                        "This item is not allowed to accept children.",
-                    ));
-                }
                 children.push(content.parse::<BodyNode>()?);
                 children.push(content.parse::<BodyNode>()?);
             }
             }
 
 
-            // consume comma if it exists
-            // we don't actually care if there *are* commas between attrs
             if content.peek(Token![,]) {
             if content.peek(Token![,]) {
                 let _ = content.parse::<Token![,]>();
                 let _ = content.parse::<Token![,]>();
             }
             }
         }
         }
-        Ok((body, children, manual_props))
+
+        Ok(Self {
+            name,
+            body,
+            children,
+            manual_props,
+        })
     }
     }
 }
 }
 
 
@@ -219,9 +155,7 @@ pub struct ComponentField {
 
 
 enum ContentField {
 enum ContentField {
     ManExpr(Expr),
     ManExpr(Expr),
-    OnHandler(ExprClosure),
 
 
-    // A handler was provided in {} tokens
     OnHandlerRaw(Expr),
     OnHandlerRaw(Expr),
 }
 }
 
 
@@ -229,9 +163,6 @@ impl ToTokens for ContentField {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         match self {
         match self {
             ContentField::ManExpr(e) => e.to_tokens(tokens),
             ContentField::ManExpr(e) => e.to_tokens(tokens),
-            ContentField::OnHandler(e) => tokens.append_all(quote! {
-                __cx.bump().alloc(#e)
-            }),
             ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
             ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
                 __cx.bump().alloc(#e)
                 __cx.bump().alloc(#e)
             }),
             }),
@@ -246,13 +177,7 @@ impl Parse for ComponentField {
 
 
         let name_str = name.to_string();
         let name_str = name.to_string();
         let content = if name_str.starts_with("on") {
         let content = if name_str.starts_with("on") {
-            if input.peek(token::Brace) {
-                let content;
-                syn::braced!(content in input);
-                ContentField::OnHandlerRaw(content.parse()?)
-            } else {
-                ContentField::OnHandler(input.parse()?)
-            }
+            ContentField::OnHandlerRaw(input.parse()?)
         } else {
         } else {
             ContentField::ManExpr(input.parse::<Expr>()?)
             ContentField::ManExpr(input.parse::<Expr>()?)
         };
         };

+ 114 - 61
packages/core-macro/src/rsx/element.rs

@@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2;
 use quote::{quote, ToTokens, TokenStreamExt};
 use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
 use syn::{
     parse::{Parse, ParseBuffer, ParseStream},
     parse::{Parse, ParseBuffer, ParseStream},
-    token, Expr, ExprClosure, Ident, LitStr, Result, Token,
+    Expr, Ident, LitStr, Result, Token,
 };
 };
 
 
 // =======================================
 // =======================================
@@ -33,45 +33,69 @@ impl Parse for Element {
         let mut key = None;
         let mut key = None;
         let mut _el_ref = None;
         let mut _el_ref = None;
 
 
-        // todo: more descriptive error handling
-        while !content.is_empty() {
+        // parse fields with commas
+        // break when we don't get this pattern anymore
+        // start parsing bodynodes
+        // "def": 456,
+        // abc: 123,
+        loop {
+            // Parse the raw literal fields
+            if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
+                let name = content.parse::<LitStr>()?;
+                let ident = name.clone();
+
+                content.parse::<Token![:]>()?;
+
+                if content.peek(LitStr) {
+                    let value = content.parse::<LitStr>()?;
+                    attributes.push(ElementAttrNamed {
+                        el_name: el_name.clone(),
+                        attr: ElementAttr::CustomAttrText { name, value },
+                    });
+                } else {
+                    let value = content.parse::<Expr>()?;
+                    attributes.push(ElementAttrNamed {
+                        el_name: el_name.clone(),
+                        attr: ElementAttr::CustomAttrExpression { name, value },
+                    });
+                }
+
+                if content.is_empty() {
+                    break;
+                }
+
+                // todo: add a message saying you need to include commas between fields
+                if content.parse::<Token![,]>().is_err() {
+                    proc_macro_error::emit_error!(
+                        ident,
+                        "This attribute is misisng a trailing comma"
+                    )
+                }
+                continue;
+            }
+
             if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
             if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
                 let name = content.parse::<Ident>()?;
                 let name = content.parse::<Ident>()?;
+                let ident = name.clone();
+
                 let name_str = name.to_string();
                 let name_str = name.to_string();
                 content.parse::<Token![:]>()?;
                 content.parse::<Token![:]>()?;
 
 
                 if name_str.starts_with("on") {
                 if name_str.starts_with("on") {
-                    if content.peek(token::Brace) {
-                        let mycontent;
-                        syn::braced!(mycontent in content);
-
-                        listeners.push(ElementAttrNamed {
-                            el_name: el_name.clone(),
-                            attr: ElementAttr::EventTokens {
-                                name,
-                                tokens: mycontent.parse()?,
-                            },
-                        });
-                    } else {
-                        listeners.push(ElementAttrNamed {
-                            el_name: el_name.clone(),
-                            attr: ElementAttr::EventClosure {
-                                name,
-                                closure: content.parse()?,
-                            },
-                        });
-                    };
+                    listeners.push(ElementAttrNamed {
+                        el_name: el_name.clone(),
+                        attr: ElementAttr::EventTokens {
+                            name,
+                            tokens: content.parse()?,
+                        },
+                    });
                 } else {
                 } else {
                     match name_str.as_str() {
                     match name_str.as_str() {
                         "key" => {
                         "key" => {
                             key = Some(content.parse()?);
                             key = Some(content.parse()?);
                         }
                         }
-                        "classes" => {
-                            todo!("custom class list not supported")
-                        }
-                        "namespace" => {
-                            todo!("custom namespace not supported")
-                        }
+                        "classes" => todo!("custom class list not supported yet"),
+                        // "namespace" => todo!("custom namespace not supported yet"),
                         "node_ref" => {
                         "node_ref" => {
                             _el_ref = Some(content.parse::<Expr>()?);
                             _el_ref = Some(content.parse::<Expr>()?);
                         }
                         }
@@ -96,27 +120,56 @@ impl Parse for Element {
                         }
                         }
                     }
                     }
                 }
                 }
-            } else if content.peek(LitStr) && content.peek2(Token![:]) {
-                let name = content.parse::<LitStr>()?;
-                content.parse::<Token![:]>()?;
 
 
-                if content.peek(LitStr) {
-                    let value = content.parse::<LitStr>()?;
-                    attributes.push(ElementAttrNamed {
-                        el_name: el_name.clone(),
-                        attr: ElementAttr::CustomAttrText { name, value },
-                    });
-                } else {
-                    let value = content.parse::<Expr>()?;
-                    attributes.push(ElementAttrNamed {
-                        el_name: el_name.clone(),
-                        attr: ElementAttr::CustomAttrExpression { name, value },
-                    });
+                if content.is_empty() {
+                    break;
                 }
                 }
-            } else {
-                children.push(content.parse::<BodyNode>()?);
+
+                // todo: add a message saying you need to include commas between fields
+                if content.parse::<Token![,]>().is_err() {
+                    proc_macro_error::emit_error!(
+                        ident,
+                        "This attribute is misisng a trailing comma"
+                    )
+                }
+                continue;
             }
             }
 
 
+            break;
+        }
+
+        while !content.is_empty() {
+            if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
+                let ident = content.parse::<LitStr>().unwrap();
+                let name = ident.value();
+                proc_macro_error::emit_error!(
+                    ident, "This attribute `{}` is in the wrong place.", name;
+                    help =
+"All attribute fields must be placed above children elements.
+
+                div {
+                   attr: \"...\",  <---- attribute is above children
+                   div { }       <---- children are below attributes
+                }";
+                )
+            }
+
+            if (content.peek(Ident) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
+                let ident = content.parse::<Ident>().unwrap();
+                let name = ident.to_string();
+                proc_macro_error::emit_error!(
+                    ident, "This attribute `{}` is in the wrong place.", name;
+                    help =
+"All attribute fields must be placed above children elements.
+
+                div {
+                   attr: \"...\",  <---- attribute is above children
+                   div { }       <---- children are below attributes
+                }";
+                )
+            }
+
+            children.push(content.parse::<BodyNode>()?);
             // consume comma if it exists
             // consume comma if it exists
             // we don't actually care if there *are* commas after elements/text
             // we don't actually care if there *are* commas after elements/text
             if content.peek(Token![,]) {
             if content.peek(Token![,]) {
@@ -138,7 +191,7 @@ impl Parse for Element {
 impl ToTokens for Element {
 impl ToTokens for Element {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let name = &self.name;
         let name = &self.name;
-        let childs = &self.children;
+        let children = &self.children;
 
 
         let listeners = &self.listeners;
         let listeners = &self.listeners;
         let attr = &self.attributes;
         let attr = &self.attributes;
@@ -153,7 +206,7 @@ impl ToTokens for Element {
                 dioxus_elements::#name,
                 dioxus_elements::#name,
                 [ #(#listeners),* ],
                 [ #(#listeners),* ],
                 [ #(#attr),* ],
                 [ #(#attr),* ],
-                [ #(#childs),* ],
+                [ #(#children),* ],
                 #key,
                 #key,
             )
             )
         });
         });
@@ -173,8 +226,8 @@ enum ElementAttr {
     // "attribute": true,
     // "attribute": true,
     CustomAttrExpression { name: LitStr, value: Expr },
     CustomAttrExpression { name: LitStr, value: Expr },
 
 
-    // onclick: move |_| {}
-    EventClosure { name: Ident, closure: ExprClosure },
+    // // onclick: move |_| {}
+    // EventClosure { name: Ident, closure: ExprClosure },
 
 
     // onclick: {}
     // onclick: {}
     EventTokens { name: Ident, tokens: Expr },
     EventTokens { name: Ident, tokens: Expr },
@@ -189,7 +242,7 @@ impl ToTokens for ElementAttrNamed {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let ElementAttrNamed { el_name, attr } = self;
         let ElementAttrNamed { el_name, attr } = self;
 
 
-        let toks = match attr {
+        tokens.append_all(match attr {
             ElementAttr::AttrText { name, value } => {
             ElementAttr::AttrText { name, value } => {
                 quote! {
                 quote! {
                     dioxus_elements::#el_name.#name(__cx, format_args_f!(#value))
                     dioxus_elements::#el_name.#name(__cx, format_args_f!(#value))
@@ -200,26 +253,26 @@ impl ToTokens for ElementAttrNamed {
                     dioxus_elements::#el_name.#name(__cx, #value)
                     dioxus_elements::#el_name.#name(__cx, #value)
                 }
                 }
             }
             }
-
             ElementAttr::CustomAttrText { name, value } => {
             ElementAttr::CustomAttrText { name, value } => {
-                quote! { __cx.attr( #name, format_args_f!(#value), None, false ) }
+                quote! {
+                    __cx.attr( #name, format_args_f!(#value), None, false )
+                }
             }
             }
             ElementAttr::CustomAttrExpression { name, value } => {
             ElementAttr::CustomAttrExpression { name, value } => {
-                quote! { __cx.attr( #name, format_args_f!(#value), None, false ) }
-            }
-
-            ElementAttr::EventClosure { name, closure } => {
                 quote! {
                 quote! {
-                    dioxus_elements::on::#name(__cx, #closure)
+                    __cx.attr( #name, format_args_f!(#value), None, false )
                 }
                 }
             }
             }
+            // ElementAttr::EventClosure { name, closure } => {
+            //     quote! {
+            //         dioxus_elements::on::#name(__cx, #closure)
+            //     }
+            // }
             ElementAttr::EventTokens { name, tokens } => {
             ElementAttr::EventTokens { name, tokens } => {
                 quote! {
                 quote! {
                     dioxus_elements::on::#name(__cx, #tokens)
                     dioxus_elements::on::#name(__cx, #tokens)
                 }
                 }
             }
             }
-        };
-
-        tokens.append_all(toks);
+        });
     }
     }
 }
 }

+ 0 - 63
packages/core-macro/src/rsx/fragment.rs

@@ -1,63 +0,0 @@
-//! Parse `Fragments` into the Fragment VNode
-//! ==========================================
-//!
-//! This parsing path emerges from [`AmbiguousElement`] which supports validation of the Fragment format.
-//! We can be reasonably sure that whatever enters this parsing path is in the right format.
-//! This feature must support:
-//! - [x] Optional commas
-//! - [ ] Children
-//! - [ ] Keys
-
-use super::AmbiguousElement;
-use syn::parse::ParseBuffer;
-use {
-    proc_macro2::TokenStream as TokenStream2,
-    quote::{quote, ToTokens, TokenStreamExt},
-    syn::{
-        parse::{Parse, ParseStream},
-        Ident, Result, Token,
-    },
-};
-
-pub struct Fragment {
-    children: Vec<AmbiguousElement>,
-}
-
-impl Parse for Fragment {
-    fn parse(input: ParseStream) -> Result<Self> {
-        input.parse::<Ident>()?;
-
-        let children = Vec::new();
-
-        // parse the guts
-        let content: ParseBuffer;
-        syn::braced!(content in input);
-        while !content.is_empty() {
-            content.parse::<AmbiguousElement>()?;
-
-            if content.peek(Token![,]) {
-                let _ = content.parse::<Token![,]>();
-            }
-        }
-        Ok(Self { children })
-    }
-}
-
-impl ToTokens for Fragment {
-    fn to_tokens(&self, tokens: &mut TokenStream2) {
-        let childs = &self.children;
-        let children = quote! {
-            ChildrenList::new(__cx)
-                #( .add_child(#childs) )*
-                .finish()
-        };
-        tokens.append_all(quote! {
-            // #key_token,
-            dioxus::builder::vfragment(
-                __cx,
-                None,
-                #children
-            )
-        })
-    }
-}

+ 74 - 6
packages/core-macro/src/rsx/mod.rs

@@ -11,17 +11,85 @@
 //!
 //!
 //! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
 //! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
 
 
-mod ambiguous;
-mod body;
 mod component;
 mod component;
 mod element;
 mod element;
-mod fragment;
 mod node;
 mod node;
 
 
 // Re-export the namespaces into each other
 // Re-export the namespaces into each other
-pub use ambiguous::*;
-pub use body::*;
 pub use component::*;
 pub use component::*;
 pub use element::*;
 pub use element::*;
-pub use fragment::*;
 pub use node::*;
 pub use node::*;
+
+// imports
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{quote, ToTokens, TokenStreamExt};
+use syn::{
+    parse::{Parse, ParseStream},
+    Ident, Result, Token,
+};
+
+pub struct CallBody {
+    custom_context: Option<Ident>,
+    roots: Vec<BodyNode>,
+}
+
+impl Parse for CallBody {
+    fn parse(input: ParseStream) -> Result<Self> {
+        let custom_context = if input.peek(Ident) && input.peek2(Token![,]) {
+            let name = input.parse::<Ident>()?;
+            input.parse::<Token![,]>()?;
+
+            Some(name)
+        } else {
+            None
+        };
+
+        let mut roots = Vec::new();
+
+        while !input.is_empty() {
+            let node = input.parse::<BodyNode>()?;
+
+            if input.peek(Token![,]) {
+                let _ = input.parse::<Token![,]>();
+            }
+
+            roots.push(node);
+        }
+
+        Ok(Self {
+            custom_context,
+            roots,
+        })
+    }
+}
+
+/// Serialize the same way, regardless of flavor
+impl ToTokens for CallBody {
+    fn to_tokens(&self, out_tokens: &mut TokenStream2) {
+        let inner = if self.roots.len() == 1 {
+            let inner = &self.roots[0];
+            quote! { #inner }
+        } else {
+            let childs = &self.roots;
+            quote! { __cx.fragment_root([ #(#childs),* ]) }
+        };
+
+        match &self.custom_context {
+            // The `in cx` pattern allows directly rendering
+            Some(ident) => out_tokens.append_all(quote! {
+                #ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
+                    use dioxus_elements::{GlobalAttributes, SvgAttributes};
+                    #inner
+                }))
+            }),
+
+            // Otherwise we just build the LazyNode wrapper
+            None => out_tokens.append_all(quote! {
+                LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
+                    use dioxus_elements::{GlobalAttributes, SvgAttributes};
+                    #inner
+                })
+            }),
+        };
+    }
+}

+ 52 - 37
packages/core-macro/src/rsx/node.rs

@@ -4,34 +4,67 @@ use proc_macro2::TokenStream as TokenStream2;
 use quote::{quote, ToTokens, TokenStreamExt};
 use quote::{quote, ToTokens, TokenStreamExt};
 use syn::{
 use syn::{
     parse::{Parse, ParseStream},
     parse::{Parse, ParseStream},
-    token, Expr, LitStr, Result,
+    token, Expr, LitStr, Result, Token,
 };
 };
 
 
-// ==============================================
-// Parse any div {} as a VElement
-// ==============================================
+/*
+Parse
+-> div {}
+-> Component {}
+-> component()
+-> "text {with_args}"
+-> (0..10).map(|f| rsx!("asd")),  // <--- notice the comma - must be a complete expr
+*/
 pub enum BodyNode {
 pub enum BodyNode {
-    Element(AmbiguousElement),
-    Text(TextNode),
+    Element(Element),
+    Component(Component),
+    Text(LitStr),
     RawExpr(Expr),
     RawExpr(Expr),
 }
 }
 
 
 impl Parse for BodyNode {
 impl Parse for BodyNode {
     fn parse(stream: ParseStream) -> Result<Self> {
     fn parse(stream: ParseStream) -> Result<Self> {
-        // Supposedly this approach is discouraged due to inability to return proper errors
-        // TODO: Rework this to provide more informative errors
+        if stream.peek(LitStr) {
+            return Ok(BodyNode::Text(stream.parse()?));
+        }
 
 
-        if stream.peek(token::Brace) {
-            let content;
-            syn::braced!(content in stream);
-            return Ok(BodyNode::RawExpr(content.parse::<Expr>()?));
+        // div {} -> el
+        // Div {} -> comp
+        if stream.peek(syn::Ident) && stream.peek2(token::Brace) {
+            if stream
+                .fork()
+                .parse::<Ident>()?
+                .to_string()
+                .chars()
+                .next()
+                .unwrap()
+                .is_ascii_uppercase()
+            {
+                return Ok(BodyNode::Component(stream.parse()?));
+            } else {
+                return Ok(BodyNode::Element(stream.parse::<Element>()?));
+            }
         }
         }
 
 
-        if stream.peek(LitStr) {
-            return Ok(BodyNode::Text(stream.parse::<TextNode>()?));
+        // component() -> comp
+        // ::component {} -> comp
+        // ::component () -> comp
+        if (stream.peek(syn::Ident) && stream.peek2(token::Paren))
+            || (stream.peek(Token![::]))
+            || (stream.peek(Token![:]) && stream.peek2(Token![:]))
+        {
+            return Ok(BodyNode::Component(stream.parse::<Component>()?));
         }
         }
 
 
-        Ok(BodyNode::Element(stream.parse::<AmbiguousElement>()?))
+        // crate::component{} -> comp
+        // crate::component() -> comp
+        if let Ok(pat) = stream.fork().parse::<syn::Path>() {
+            if pat.segments.len() > 1 {
+                return Ok(BodyNode::Component(stream.parse::<Component>()?));
+            }
+        }
+
+        Ok(BodyNode::RawExpr(stream.parse::<Expr>()?))
     }
     }
 }
 }
 
 
@@ -39,31 +72,13 @@ impl ToTokens for BodyNode {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         match &self {
         match &self {
             BodyNode::Element(el) => el.to_tokens(tokens),
             BodyNode::Element(el) => el.to_tokens(tokens),
-            BodyNode::Text(txt) => txt.to_tokens(tokens),
+            BodyNode::Component(comp) => comp.to_tokens(tokens),
+            BodyNode::Text(txt) => tokens.append_all(quote! {
+                __cx.text(format_args_f!(#txt))
+            }),
             BodyNode::RawExpr(exp) => tokens.append_all(quote! {
             BodyNode::RawExpr(exp) => tokens.append_all(quote! {
                  __cx.fragment_from_iter(#exp)
                  __cx.fragment_from_iter(#exp)
             }),
             }),
         }
         }
     }
     }
 }
 }
-
-// =======================================
-// Parse just plain text
-// =======================================
-pub struct TextNode(LitStr);
-
-impl Parse for TextNode {
-    fn parse(s: ParseStream) -> Result<Self> {
-        Ok(Self(s.parse()?))
-    }
-}
-
-impl ToTokens for TextNode {
-    fn to_tokens(&self, tokens: &mut TokenStream2) {
-        // todo: use heuristics to see if we can promote to &static str
-        let token_stream = &self.0.to_token_stream();
-        tokens.append_all(quote! {
-            __cx.text(format_args_f!(#token_stream))
-        });
-    }
-}

+ 0 - 1
packages/core/Cargo.toml

@@ -50,7 +50,6 @@ anyhow = "1.0.42"
 dioxus-html = { path = "../html" }
 dioxus-html = { path = "../html" }
 fern = { version = "0.6.0", features = ["colored"] }
 fern = { version = "0.6.0", features = ["colored"] }
 rand = { version = "0.8.4", features = ["small_rng"] }
 rand = { version = "0.8.4", features = ["small_rng"] }
-simple_logger = "1.13.0"
 dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" }
 dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" }
 criterion = "0.3.5"
 criterion = "0.3.5"
 thiserror = "1.0.30"
 thiserror = "1.0.30"

+ 1 - 1
packages/core/README.md

@@ -6,7 +6,7 @@ To build new apps with Dioxus or to extend the ecosystem with new hooks or compo
 
 
 
 
 ```rust
 ```rust
-fn app(cx: Scope<()>) -> Element {
+fn app(cx: Scope) -> Element {
     rsx!(cx, div { "hello world" })
     rsx!(cx, div { "hello world" })
 }
 }
 
 

+ 1 - 1
packages/core/examples/component_children.rs

@@ -11,7 +11,7 @@ fn main() {
     dbg!(edits);
     dbg!(edits);
 }
 }
 
 
-fn parent(cx: Scope<()>) -> Element {
+fn parent(cx: Scope) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| f);
     let value = cx.use_hook(|_| String::new(), |f| f);
 
 
     cx.render(rsx! {
     cx.render(rsx! {

+ 1 - 1
packages/core/examples/works.rs

@@ -9,7 +9,7 @@ fn main() {
     let _ = VirtualDom::new(parent);
     let _ = VirtualDom::new(parent);
 }
 }
 
 
-fn parent(cx: Scope<()>) -> Element {
+fn parent(cx: Scope) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| f);
     let value = cx.use_hook(|_| String::new(), |f| f);
 
 
     cx.render(rsx! {
     cx.render(rsx! {

+ 13 - 3
packages/core/src/nodes.rs

@@ -84,7 +84,7 @@ pub enum VNode<'src> {
     /// # Example
     /// # Example
     ///
     ///
     /// ```rust, ignore
     /// ```rust, ignore
-    /// fn Example(cx: Scope<()>) -> Element {
+    /// fn Example(cx: Scope) -> Element {
     ///     ...
     ///     ...
     /// }
     /// }
     ///
     ///
@@ -734,9 +734,15 @@ impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
     }
     }
 }
 }
 
 
-impl IntoVNode<'_> for &'static str {
+impl<'b> IntoVNode<'_> for &'b str {
     fn into_vnode(self, cx: NodeFactory) -> VNode {
     fn into_vnode(self, cx: NodeFactory) -> VNode {
-        cx.static_text(self)
+        cx.text(format_args!("{}", self))
+    }
+}
+
+impl IntoVNode<'_> for String {
+    fn into_vnode(self, cx: NodeFactory) -> VNode {
+        cx.text(format_args!("{}", self))
     }
     }
 }
 }
 
 
@@ -758,3 +764,7 @@ impl<'a> IntoVNode<'a> for &VNode<'a> {
         self.decouple()
         self.decouple()
     }
     }
 }
 }
+
+trait IntoAcceptedVnode<'a> {
+    fn into_accepted_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
+}

+ 1 - 1
packages/core/src/scopes.rs

@@ -490,7 +490,7 @@ impl ScopeState {
     /// # Example
     /// # Example
     ///
     ///
     /// ```rust, ignore
     /// ```rust, ignore
-    /// fn App(cx: Scope<()>) -> Element {
+    /// fn App(cx: Scope) -> Element {
     ///     rsx!(cx, div { "Subtree {id}"})
     ///     rsx!(cx, div { "Subtree {id}"})
     /// };
     /// };
     /// ```
     /// ```

+ 9 - 9
packages/core/src/virtual_dom.rs

@@ -80,7 +80,7 @@ use std::{any::Any, collections::VecDeque, iter::FromIterator, pin::Pin, sync::A
 /// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
 /// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
 ///
 ///
 /// ```rust, ignore
 /// ```rust, ignore
-/// fn App(cx: Scope<()>) -> Element {
+/// fn App(cx: Scope) -> Element {
 ///     cx.render(rsx!{
 ///     cx.render(rsx!{
 ///         div { "Hello World" }
 ///         div { "Hello World" }
 ///     })
 ///     })
@@ -127,7 +127,7 @@ impl VirtualDom {
     ///
     ///
     /// # Example
     /// # Example
     /// ```rust, ignore
     /// ```rust, ignore
-    /// fn Example(cx: Scope<()>) -> Element  {
+    /// fn Example(cx: Scope) -> Element  {
     ///     cx.render(rsx!( div { "hello world" } ))
     ///     cx.render(rsx!( div { "hello world" } ))
     /// }
     /// }
     ///
     ///
@@ -388,7 +388,7 @@ impl VirtualDom {
     /// # Example
     /// # Example
     ///
     ///
     /// ```rust, ignore
     /// ```rust, ignore
-    /// fn App(cx: Scope<()>) -> Element {
+    /// fn App(cx: Scope) -> Element {
     ///     cx.render(rsx!( div {"hello"} ))
     ///     cx.render(rsx!( div {"hello"} ))
     /// }
     /// }
     ///
     ///
@@ -544,7 +544,7 @@ impl VirtualDom {
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     ///
     ///
     /// ```rust
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     ///     rsx!(cx, div {})
     /// }
     /// }
     ///
     ///
@@ -564,7 +564,7 @@ impl VirtualDom {
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     ///
     ///
     /// ```rust
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     ///     rsx!(cx, div {})
     /// }
     /// }
     ///
     ///
@@ -586,7 +586,7 @@ impl VirtualDom {
     ///
     ///
     ///
     ///
     /// ```rust
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     ///     rsx!(cx, div {})
     /// }
     /// }
     ///
     ///
@@ -609,7 +609,7 @@ impl VirtualDom {
     ///
     ///
     ///
     ///
     /// ```rust
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     ///     rsx!(cx, div {})
     /// }
     /// }
     ///
     ///
@@ -753,7 +753,7 @@ pub enum SchedulerMsg {
 ///
 ///
 /// # Example
 /// # Example
 /// ```rust
 /// ```rust
-/// fn App(cx: Scope<()>) -> Element {
+/// fn App(cx: Scope) -> Element {
 ///     rsx!(cx, div {
 ///     rsx!(cx, div {
 ///         onclick: move |_| println!("Clicked!")
 ///         onclick: move |_| println!("Clicked!")
 ///     })
 ///     })
@@ -871,7 +871,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> {
 /// ## Example
 /// ## Example
 ///
 ///
 /// ```rust, ignore
 /// ```rust, ignore
-/// fn App(cx: Scope<()>) -> Element {
+/// fn App(cx: Scope) -> Element {
 ///     cx.render(rsx!{
 ///     cx.render(rsx!{
 ///         CustomCard {
 ///         CustomCard {
 ///             h1 {}2
 ///             h1 {}2

+ 1 - 1
packages/core/tests/borrowedstate.rs

@@ -10,7 +10,7 @@ fn test_borrowed_state() {
     let _ = VirtualDom::new(Parent);
     let _ = VirtualDom::new(Parent);
 }
 }
 
 
-fn Parent(cx: Scope<()>) -> Element {
+fn Parent(cx: Scope) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| &*f);
     let value = cx.use_hook(|_| String::new(), |f| &*f);
 
 
     cx.render(rsx! {
     cx.render(rsx! {

+ 7 - 7
packages/core/tests/miri_stress.rs

@@ -28,7 +28,7 @@ fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
 /// In debug, this should also toss a warning.
 /// In debug, this should also toss a warning.
 #[test]
 #[test]
 fn test_memory_leak() {
 fn test_memory_leak() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         let val = cx.use_hook(|_| 0, |f| f);
         let val = cx.use_hook(|_| 0, |f| f);
 
 
         *val += 1;
         *val += 1;
@@ -68,7 +68,7 @@ fn test_memory_leak() {
         })
         })
     }
     }
 
 
-    fn child(cx: Scope<()>) -> Element {
+    fn child(cx: Scope) -> Element {
         rsx!(cx, div { "goodbye world" })
         rsx!(cx, div { "goodbye world" })
     }
     }
 
 
@@ -85,7 +85,7 @@ fn test_memory_leak() {
 
 
 #[test]
 #[test]
 fn memo_works_properly() {
 fn memo_works_properly() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         let val = cx.use_hook(|_| 0, |f| f);
         let val = cx.use_hook(|_| 0, |f| f);
 
 
         *val += 1;
         *val += 1;
@@ -164,7 +164,7 @@ fn free_works_on_root_props() {
 
 
 #[test]
 #[test]
 fn free_works_on_borrowed() {
 fn free_works_on_borrowed() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         cx.render(rsx! {
         cx.render(rsx! {
             child(a: "alpha", b: "asd".to_string())
             child(a: "alpha", b: "asd".to_string())
         })
         })
@@ -203,7 +203,7 @@ fn free_works_on_root_hooks() {
         }
         }
     }
     }
 
 
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         let name = cx.use_hook(|_| Droppable(String::from("asd")), |f| f);
         let name = cx.use_hook(|_| Droppable(String::from("asd")), |f| f);
         rsx!(cx, div { "{name.0}" })
         rsx!(cx, div { "{name.0}" })
     }
     }
@@ -214,7 +214,7 @@ fn free_works_on_root_hooks() {
 
 
 #[test]
 #[test]
 fn old_props_arent_stale() {
 fn old_props_arent_stale() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         dbg!("rendering parent");
         dbg!("rendering parent");
         let cnt = cx.use_hook(|_| 0, |f| f);
         let cnt = cx.use_hook(|_| 0, |f| f);
         *cnt += 1;
         *cnt += 1;
@@ -261,7 +261,7 @@ fn old_props_arent_stale() {
 
 
 #[test]
 #[test]
 fn basic() {
 fn basic() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         rsx!(cx, div {
         rsx!(cx, div {
             child(a: "abcdef".to_string())
             child(a: "abcdef".to_string())
         })
         })

+ 1 - 1
packages/core/tests/vdom_rebuild.rs

@@ -44,7 +44,7 @@ fn lists_work() {
     static App: Component = |cx| {
     static App: Component = |cx| {
         cx.render(rsx!(
         cx.render(rsx!(
             h1 {"hello"}
             h1 {"hello"}
-            {(0..6).map(|f| rsx!(span{ "{f}" }))}
+            (0..6).map(|f| rsx!(span{ "{f}" }))
         ))
         ))
     };
     };
     let mut vdom = VirtualDom::new(App);
     let mut vdom = VirtualDom::new(App);

+ 0 - 1
packages/desktop/Cargo.toml

@@ -37,4 +37,3 @@ tokio_runtime = ["tokio"]
 
 
 [dev-dependencies]
 [dev-dependencies]
 dioxus-hooks = { path = "../hooks" }
 dioxus-hooks = { path = "../hooks" }
-simple_logger = "1.13.0"

+ 10 - 14
packages/desktop/examples/async.rs

@@ -9,22 +9,18 @@ fn main() {
     dioxus_desktop::launch(app);
     dioxus_desktop::launch(app);
 }
 }
 
 
-fn app(cx: Scope<()>) -> Element {
+fn app(cx: Scope) -> Element {
     let count = use_state(&cx, || 0);
     let count = use_state(&cx, || 0);
 
 
-    // push the futureo on initialization
-    cx.use_hook(
-        |_| {
-            cx.push_future({
-                let count = count.for_async();
-                async move {
-                    tokio::time::sleep(Duration::from_millis(1000)).await;
-                    *count.get_mut() += 1;
-                }
-            });
-        },
-        |_| {},
-    );
+    use_future(&cx, || {
+        let count = count.for_async();
+        async move {
+            loop {
+                tokio::time::sleep(Duration::from_millis(1000)).await;
+                *count.modify() += 1;
+            }
+        }
+    });
 
 
     cx.render(rsx! {
     cx.render(rsx! {
         div {
         div {

+ 4 - 43
packages/hooks/README.md

@@ -4,58 +4,19 @@ This crate includes some basic useful hooks for dioxus:
 
 
 - use_state
 - use_state
 - use_ref
 - use_ref
-- use_collection
-- use_task
-- use_signal
+- use_future
+- use_coroutine
 
 
 ## use_state
 ## use_state
 
 
-The king daddy of state hooks.
+The primary mechanism of stored state.
 
 
 You can always use it "normally" with the `split` method:
 You can always use it "normally" with the `split` method:
 
 
 ```rust
 ```rust
-// Normal usage:
+// Rusty-smart-pointer usage:
 let value = use_state(&cx, || 10);
 let value = use_state(&cx, || 10);
 
 
 // "Classic" usage:
 // "Classic" usage:
 let (value, set_value) = use_state(&cx, || 0).split();
 let (value, set_value) = use_state(&cx, || 0).split();
 ```
 ```
-
-## use_ref
-
-
-## use_rwlock
-A multithreaded form of RwLock for use in tasks
-```rust
-let val = use_rwlock(cx, || 10);
-use_task((), || async loop {
-    *val.write().unwrap() += 1;
-    async_std::task::delay(Duration::from_ms(1000)).await;
-});
-use_task((), || async loop {
-    *val.write().unwrap() -= 1;
-    async_std::task::delay(Duration::from_ms(500)).await;
-});
-```
-
-## use_hashmap
-Store a memoized collection with similar semantics to use_state. Comes with a bunch of utility methods to make working with collections easier. Is essentially a wrapper over the immutable hashmap in im-rc.
-
-```rust
-let todos = use_hashmap(cx, |map| map.insert("bob", "bill"));
-cx.render(rsx!(
-    button { onclick: move |_| todos.insert("bob", "bill")
-        "add random todo"
-    }
-)
-
-```
-
-## use_task
-
-use_task submits a task to the dioxus task queue to be progressed during Dioxus's async event loop. The task must not return anything
-
-
-## use_signal
-

+ 43 - 3
packages/hooks/src/lib.rs

@@ -1,5 +1,5 @@
 mod usestate;
 mod usestate;
-pub use usestate::{use_state, AsyncUseState, UseState};
+pub use usestate::{use_state, UseState};
 
 
 mod useref;
 mod useref;
 pub use useref::*;
 pub use useref::*;
@@ -16,5 +16,45 @@ pub use usefuture::*;
 mod usesuspense;
 mod usesuspense;
 pub use usesuspense::*;
 pub use usesuspense::*;
 
 
-// mod usemodel;
-// pub use usemodel::*;
+#[macro_export]
+macro_rules! to_owned {
+    ($($es:ident),+) => {$(
+        #[allow(unused_mut)]
+        let mut $es = $es.to_owned();
+    )*}
+}
+
+/// Calls `for_async` on the series of paramters.
+///
+/// If the type is Clone, then it will be cloned. However, if the type is not `clone`
+/// then it must have a `for_async` method for Rust to lower down into.
+///
+/// See: how use_state implements `for_async` but *not* through the trait.
+#[macro_export]
+macro_rules! for_async {
+    ($($es:ident),+) => {$(
+        #[allow(unused_mut)]
+        let mut $es = $es.for_async();
+    )*}
+}
+
+/// This is a marker trait that uses decoherence.
+///
+/// It is *not* meant for hooks to actually implement, but rather defer to their
+/// underlying implementation if they *don't* implement the trait.
+///
+///
+pub trait AsyncHook {
+    type Output;
+    fn for_async(self) -> Self::Output;
+}
+
+impl<T> AsyncHook for T
+where
+    T: ToOwned<Owned = T>,
+{
+    type Output = T;
+    fn for_async(self) -> Self::Output {
+        self
+    }
+}

+ 39 - 79
packages/hooks/src/usestate.rs

@@ -2,20 +2,9 @@ use dioxus_core::prelude::*;
 use std::{
 use std::{
     cell::{Cell, Ref, RefCell, RefMut},
     cell::{Cell, Ref, RefCell, RefMut},
     fmt::{Debug, Display},
     fmt::{Debug, Display},
-    ops::Not,
     rc::Rc,
     rc::Rc,
 };
 };
 
 
-pub trait UseStateA<'a, T> {
-    fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T>;
-}
-
-impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> {
-    fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T> {
-        use_state(self.scope, initial_state_fn)
-    }
-}
-
 /// Store state between component renders!
 /// Store state between component renders!
 ///
 ///
 /// ## Dioxus equivalent of useState, designed for Rust
 /// ## Dioxus equivalent of useState, designed for Rust
@@ -109,6 +98,7 @@ where
         UseState { inner: self.inner }
         UseState { inner: self.inner }
     }
     }
 }
 }
+
 impl<T: Debug> Debug for UseState<'_, T> {
 impl<T: Debug> Debug for UseState<'_, T> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(f, "{:?}", self.inner.current_val)
         write!(f, "{:?}", self.inner.current_val)
@@ -157,21 +147,21 @@ impl<'a, T: 'static> UseState<'a, T> {
         })
         })
     }
     }
 
 
-    pub fn for_async(&self) -> AsyncUseState<T> {
-        AsyncUseState {
-            re_render: self.inner.update_callback.clone(),
-            wip: self.inner.wip.clone(),
-            inner: self.inner.current_val.clone(),
-        }
+    pub fn for_async(self) -> UseState<'static, T> {
+        todo!()
     }
     }
 
 
-    pub fn split_for_async(&'a self) -> (&'a Self, AsyncUseState<T>) {
-        (self, self.for_async())
+    pub fn wtih(self, f: impl FnOnce(&mut T)) {
+        let mut val = self.inner.wip.borrow_mut();
+
+        if let Some(inner) = val.as_mut() {
+            f(inner);
+        }
     }
     }
 }
 }
 
 
 impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
 impl<'a, T: 'static + ToOwned<Owned = T>> UseState<'a, T> {
-    /// Gain mutable access to the new value via RefMut.
+    /// Gain mutable access to the new value via [`RefMut`].
     ///
     ///
     /// If `modify` is called, then the component will re-render.
     /// If `modify` is called, then the component will re-render.
     ///
     ///
@@ -207,6 +197,35 @@ impl<'a, T> std::ops::Deref for UseState<'a, T> {
     }
     }
 }
 }
 
 
+// enable displaty for the handle
+impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.inner.current_val)
+    }
+}
+impl<'a, V, T: PartialEq<V>> PartialEq<V> for UseState<'a, T> {
+    fn eq(&self, other: &V) -> bool {
+        self.get() == other
+    }
+}
+impl<'a, O, T: std::ops::Not<Output = O> + Copy> std::ops::Not for UseState<'a, T> {
+    type Output = O;
+
+    fn not(self) -> Self::Output {
+        !*self.get()
+    }
+}
+
+/*
+
+Convenience methods for UseState.
+
+Note!
+
+This is not comprehensive.
+This is *just* meant to make common operations easier.
+*/
+
 use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
 
 
 impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
 impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
@@ -260,62 +279,3 @@ impl<'a, T: Copy + Div<T, Output = T>> DivAssign<T> for UseState<'a, T> {
         self.set(self.inner.current_val.div(rhs));
         self.set(self.inner.current_val.div(rhs));
     }
     }
 }
 }
-impl<'a, V, T: PartialEq<V>> PartialEq<V> for UseState<'a, T> {
-    fn eq(&self, other: &V) -> bool {
-        self.get() == other
-    }
-}
-impl<'a, O, T: Not<Output = O> + Copy> Not for UseState<'a, T> {
-    type Output = O;
-
-    fn not(self) -> Self::Output {
-        !*self.get()
-    }
-}
-
-// enable displaty for the handle
-impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.inner.current_val)
-    }
-}
-
-/// A less ergonmic but still capable form of use_state that's valid for `static lifetime
-pub struct AsyncUseState<T: 'static> {
-    inner: Rc<T>,
-    re_render: Rc<dyn Fn()>,
-    wip: Rc<RefCell<Option<T>>>,
-}
-
-impl<T: ToOwned<Owned = T>> AsyncUseState<T> {
-    pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> {
-        // make sure we get processed
-        {
-            let mut wip = self.wip.borrow_mut();
-            if wip.is_none() {
-                *wip = Some(self.inner.as_ref().to_owned());
-            }
-            (self.re_render)();
-        }
-        // Bring out the new value, cloning if it we need to
-        // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
-        RefMut::map(self.wip.borrow_mut(), |slot| {
-            //
-            slot.as_mut().unwrap()
-        })
-    }
-}
-impl<T> AsyncUseState<T> {
-    pub fn set(&mut self, val: T) {
-        (self.re_render)();
-        *self.wip.borrow_mut() = Some(val);
-    }
-
-    // pub fn get(&self) -> Ref<'_, T> {
-    //     self.wip.borrow
-    // }
-
-    pub fn get_rc(&self) -> &Rc<T> {
-        &self.inner
-    }
-}

+ 1 - 3
packages/html/Cargo.toml

@@ -7,16 +7,14 @@ description = "HTML Element pack for Dioxus - a concurrent renderer-agnostic Vir
 license = "MIT/Apache-2.0"
 license = "MIT/Apache-2.0"
 repository = "https://github.com/DioxusLabs/dioxus/"
 repository = "https://github.com/DioxusLabs/dioxus/"
 homepage = "https://dioxuslabs.com"
 homepage = "https://dioxuslabs.com"
-documentation = "https://dioxuslabs.com"
+documentation = "https://docs.rs/dioxus"
 keywords = ["dom", "ui", "gui", "react", "wasm"]
 keywords = ["dom", "ui", "gui", "react", "wasm"]
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 
 [dependencies]
 [dependencies]
 dioxus-core = { path = "../core", version = "^0.1.4" }
 dioxus-core = { path = "../core", version = "^0.1.4" }
 serde = { version = "1", features = ["derive"], optional = true }
 serde = { version = "1", features = ["derive"], optional = true }
 serde_repr = { version = "0.1", optional = true }
 serde_repr = { version = "0.1", optional = true }
 
 
-
 [features]
 [features]
 default = []
 default = []
 serialize = ["serde", "serde_repr"]
 serialize = ["serde", "serde_repr"]

+ 1 - 1
packages/liveview/README.md

@@ -39,7 +39,7 @@ async fn order_shoes(mut req: WebsocketRequest) -> Response {
     dioxus::liveview::launch(App, stream).await;    
     dioxus::liveview::launch(App, stream).await;    
 }
 }
 
 
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
     let mut count = use_state(&cx, || 0);
     let mut count = use_state(&cx, || 0);
     cx.render(rsx!(
     cx.render(rsx!(
         button { onclick: move |_| count += 1, "Incr" }
         button { onclick: move |_| count += 1, "Incr" }

+ 1 - 4
packages/mobile/Cargo.toml

@@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 [dependencies]
 [dependencies]
 anyhow = "1.0"
 anyhow = "1.0"
 # cacao = { git = "https://github.com/ryanmcgrath/cacao" }
 # cacao = { git = "https://github.com/ryanmcgrath/cacao" }
-dioxus-core = { path = "../core", version ="^0.1.3"}
+dioxus-core = { path = "../core", version = "^0.1.3" }
 log = "0.4.14"
 log = "0.4.14"
 serde = "1.0.126"
 serde = "1.0.126"
 serde_json = "1.0.64"
 serde_json = "1.0.64"
@@ -23,6 +23,3 @@ wry = "0.12.2"
 android_logger = "0.9.0"
 android_logger = "0.9.0"
 log = "0.4.11"
 log = "0.4.11"
 ndk-glue = "0.2.1"
 ndk-glue = "0.2.1"
-
-[target.'cfg(not(target_os = "android"))'.dependencies]
-simple_logger = "1.11.0"

+ 0 - 5
packages/mobile/src/lib.rs

@@ -20,10 +20,6 @@ use wry::{
 mod dom;
 mod dom;
 use dom::*;
 use dom::*;
 
 
-fn init_logging() {
-    simple_logger::SimpleLogger::new().init().unwrap();
-}
-
 static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html");
 static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html");
 
 
 pub fn launch(root: Component, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
 pub fn launch(root: Component, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
@@ -187,7 +183,6 @@ impl<T: 'static + Send> WebviewRenderer<T> {
 }
 }
 
 
 fn main() {
 fn main() {
-    init_logging();
     let event_loop = EventLoop::new();
     let event_loop = EventLoop::new();
 
 
     let mut weviews = HashMap::new();
     let mut weviews = HashMap::new();

+ 1 - 1
packages/router/src/link.rs

@@ -41,8 +41,8 @@ pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
         a {
         a {
             href: "#",
             href: "#",
             class: format_args!("{}", cx.props.class.unwrap_or("")),
             class: format_args!("{}", cx.props.class.unwrap_or("")),
-            {&cx.props.children}
             onclick: move |_| service.push_route(cx.props.to.clone()),
             onclick: move |_| service.push_route(cx.props.to.clone()),
+            {&cx.props.children}
         }
         }
     })
     })
 }
 }

+ 7 - 8
packages/ssr/src/lib.rs

@@ -309,19 +309,18 @@ mod tests {
 
 
     static SLIGHTLY_MORE_COMPLEX: Component = |cx| {
     static SLIGHTLY_MORE_COMPLEX: Component = |cx| {
         cx.render(rsx! {
         cx.render(rsx! {
-            div {
-                title: "About W3Schools"
-                {(0..20).map(|f| rsx!{
+            div { title: "About W3Schools",
+                (0..20).map(|f| rsx!{
                     div {
                     div {
-                        title: "About W3Schools"
-                        style: "color:blue;text-align:center"
-                        class: "About W3Schools"
+                        title: "About W3Schools",
+                        style: "color:blue;text-align:center",
+                        class: "About W3Schools",
                         p {
                         p {
-                            title: "About W3Schools"
+                            title: "About W3Schools",
                             "Hello world!: {f}"
                             "Hello world!: {f}"
                         }
                         }
                     }
                     }
-                })}
+                })
             }
             }
         })
         })
     };
     };

+ 17 - 29
packages/web/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 [package]
 name = "dioxus-web"
 name = "dioxus-web"
-version = "0.0.0"
+version = "0.0.1"
 authors = ["Jonathan Kelley"]
 authors = ["Jonathan Kelley"]
 edition = "2018"
 edition = "2018"
 description = "Dioxus VirtualDOM renderer for the web browser using websys"
 description = "Dioxus VirtualDOM renderer for the web browser using websys"
@@ -9,20 +9,12 @@ repository = "https://github.com/DioxusLabs/dioxus/"
 homepage = "https://dioxuslabs.com"
 homepage = "https://dioxuslabs.com"
 documentation = "https://dioxuslabs.com"
 documentation = "https://dioxuslabs.com"
 keywords = ["dom", "ui", "gui", "react", "wasm"]
 keywords = ["dom", "ui", "gui", "react", "wasm"]
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 
 [dependencies]
 [dependencies]
-dioxus-core = { path = "../core", version ="^0.1.3"}
-dioxus-html = { path = "../html" }
+dioxus-core = { path = "../core", version = "^0.1.4" }
+dioxus-html = { path = "../html", version = "^0.1.1" }
 js-sys = "0.3"
 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"] }
-# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
 lazy_static = "1.4.0"
 lazy_static = "1.4.0"
 wasm-bindgen-futures = "0.4.20"
 wasm-bindgen-futures = "0.4.20"
 log = { version = "0.4.14", features = ["release_max_level_off"] }
 log = { version = "0.4.14", features = ["release_max_level_off"] }
@@ -78,23 +70,19 @@ features = [
 ]
 ]
 
 
 
 
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[dev-dependencies]
-im-rc = "15.0.0"
-separator = "0.4.1"
-uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
-serde = { version = "1.0.126", features = ["derive"] }
-reqwest = { version = "0.11", features = ["json"] }
-dioxus-hooks = { path = "../hooks" }
-dioxus-core-macro = { path = "../core-macro" }
-rand = { version = "0.8.4", features = ["small_rng"] }
+# [lib]
+# crate-type = ["cdylib", "rlib"]
 
 
-[dev-dependencies.getrandom]
-version = "0.2"
-features = ["js"]
+# [dev-dependencies]
+# im-rc = "15.0.0"
+# separator = "0.4.1"
+# uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
+# serde = { version = "1.0.126", features = ["derive"] }
+# reqwest = { version = "0.11", features = ["json"] }
+# dioxus-hooks = { path = "../hooks" }
+# dioxus-core-macro = { path = "../core-macro" }
+# rand = { version = "0.8.4", features = ["small_rng"] }
 
 
-# surf = { version = "2.3.1", default-features = false, features = [
-#     "wasm-client",
-# ] }
+# [dev-dependencies.getrandom]
+# version = "0.2"
+# features = ["js"]

+ 0 - 29
packages/web/examples/async.rs

@@ -1,29 +0,0 @@
-//! Example: README.md showcase
-//!
-//! The example from the README.md.
-
-use dioxus::prelude::*;
-use dioxus_core as dioxus;
-use dioxus_core_macro::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-use dioxus_web;
-use gloo_timers::future::TimeoutFuture;
-
-fn main() {
-    dioxus_web::launch(App);
-}
-
-static App: Component = |cx| {
-    let mut count = use_state(&cx, || 0);
-
-    cx.push_future(|| async move {
-        TimeoutFuture::new(100).await;
-        count += 1;
-    });
-
-    rsx!(cx, div {
-        h3 { "High-Five counter: {count}" }
-        button { onclick: move |_| count.set(0), "Reset!" }
-    })
-};

+ 0 - 221
packages/web/examples/js_bench.rs

@@ -1,221 +0,0 @@
-use std::cell::Cell;
-
-use dioxus::prelude::*;
-use dioxus_core as dioxus;
-use dioxus_core_macro::*;
-use dioxus_hooks::{use_ref, use_state};
-use dioxus_html as dioxus_elements;
-use dioxus_web;
-use rand::prelude::*;
-
-fn main() {
-    console_error_panic_hook::set_once();
-    if cfg!(debug_assertions) {
-        wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-        log::debug!("hello world");
-    }
-
-    for a in ADJECTIVES {
-        wasm_bindgen::intern(*a);
-    }
-    for a in COLOURS {
-        wasm_bindgen::intern(*a);
-    }
-    for a in NOUNS {
-        wasm_bindgen::intern(*a);
-    }
-    for a in [
-        "container",
-        "jumbotron",
-        "row",
-        "Dioxus",
-        "col-md-6",
-        "col-md-1",
-        "Create 1,000 rows",
-        "run",
-        "Create 10,000 rows",
-        "runlots",
-        "Append 1,000 rows",
-        "add",
-        "Update every 10th row",
-        "update",
-        "Clear",
-        "clear",
-        "Swap rows",
-        "swaprows",
-        "preloadicon glyphicon glyphicon-remove", //
-        "aria-hidden",
-        "onclick",
-        "true",
-        "false",
-        "danger",
-        "type",
-        "id",
-        "class",
-        "glyphicon glyphicon-remove remove",
-        "dioxus-id",
-        "dioxus-event-click",
-        "dioxus",
-        "click",
-        "1.10",
-        "lbl",
-        "remove",
-        "dioxus-event",
-        "col-sm-6 smallpad",
-        "btn btn-primary btn-block",
-        "",
-        " ",
-    ] {
-        wasm_bindgen::intern(a);
-    }
-    for x in 0..100_000 {
-        wasm_bindgen::intern(&x.to_string());
-    }
-
-    dioxus_web::launch(App);
-}
-
-#[derive(Clone, PartialEq, Copy)]
-struct Label {
-    key: usize,
-    labels: [&'static str; 3],
-}
-
-static mut Counter: Cell<usize> = Cell::new(1);
-
-impl Label {
-    fn new_list(num: usize) -> Vec<Self> {
-        let mut rng = SmallRng::from_entropy();
-        let mut labels = Vec::with_capacity(num);
-
-        let offset = unsafe { Counter.get() };
-        unsafe { Counter.set(offset + num) };
-
-        for k in offset..(offset + num) {
-            labels.push(Label {
-                key: k,
-                labels: [
-                    ADJECTIVES.choose(&mut rng).unwrap(),
-                    COLOURS.choose(&mut rng).unwrap(),
-                    NOUNS.choose(&mut rng).unwrap(),
-                ],
-            });
-        }
-
-        labels
-    }
-}
-
-static App: Component = |cx| {
-    let mut items = use_ref(&cx, || vec![]);
-    let mut selected = use_state(&cx, || None);
-
-    cx.render(rsx! {
-        div { class: "container"
-            div { class: "jumbotron"
-                div { class: "row"
-                    div { class: "col-md-6", h1 { "Dioxus" } }
-                    div { class: "col-md-6"
-                        div { class: "row"
-                            ActionButton { name: "Create 1,000 rows", id: "run",
-                                onclick: move || items.set(Label::new_list(1_000)),
-                            }
-                            ActionButton { name: "Create 10,000 rows", id: "runlots",
-                                onclick: move || items.set(Label::new_list(10_000)),
-                            }
-                            ActionButton { name: "Append 1,000 rows", id: "add",
-                                onclick: move || items.write().extend(Label::new_list(1_000)),
-                            }
-                            ActionButton { name: "Update every 10th row", id: "update",
-                                onclick: move || items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"),
-                            }
-                            ActionButton { name: "Clear", id: "clear",
-                                onclick: move || items.write().clear(),
-                            }
-                            ActionButton { name: "Swap Rows", id: "swaprows",
-                                onclick: move || items.write().swap(0, 998),
-                            }
-                        }
-                    }
-                }
-            }
-            table { class: "table table-hover table-striped test-data"
-                tbody { id: "tbody"
-                    {items.read().iter().enumerate().map(|(id, item)| {
-                        let [adj, col, noun] = item.labels;
-                        let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
-                        rsx!(tr {
-                            class: "{is_in_danger}",
-                            key: "{id}",
-                            td { class:"col-md-1" }
-                            td { class:"col-md-1", "{item.key}" }
-                            td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
-                                a { class: "lbl", "{adj} {col} {noun}" }
-                            }
-                            td { class: "col-md-1"
-                                a { class: "remove", onclick: move |_| { items.write().remove(id); },
-                                    span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
-                                }
-                            }
-                            td { class: "col-md-6" }
-                        })
-                    })}
-                }
-             }
-            span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" }
-        }
-    })
-};
-
-#[derive(Props)]
-struct ActionButtonProps<'a> {
-    name: &'static str,
-    id: &'static str,
-    onclick: &'a dyn Fn(),
-}
-
-fn ActionButton<'a>(cx: Scope<'a, ActionButtonProps<'a>>) -> Element {
-    rsx!(cx, div { class: "col-sm-6 smallpad"
-        button { class:"btn btn-primary btn-block", r#type: "button", id: "{cx.props.id}",  onclick: move |_| (cx.props.onclick)(),
-            "{cx.props.name}"
-        }
-    })
-}
-
-static ADJECTIVES: &[&str] = &[
-    "pretty",
-    "large",
-    "big",
-    "small",
-    "tall",
-    "short",
-    "long",
-    "handsome",
-    "plain",
-    "quaint",
-    "clean",
-    "elegant",
-    "easy",
-    "angry",
-    "crazy",
-    "helpful",
-    "mushy",
-    "odd",
-    "unsightly",
-    "adorable",
-    "important",
-    "inexpensive",
-    "cheap",
-    "expensive",
-    "fancy",
-];
-
-static COLOURS: &[&str] = &[
-    "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
-    "orange",
-];
-
-static NOUNS: &[&str] = &[
-    "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
-    "pizza", "mouse", "keyboard",
-];

+ 0 - 44
packages/web/examples/simple.rs

@@ -1,44 +0,0 @@
-//! Example: README.md showcase
-//!
-//! The example from the README.md.
-
-use dioxus::prelude::*;
-use dioxus_core as dioxus;
-use dioxus_core_macro::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    dioxus_web::launch(App);
-}
-
-static App: Component = |cx| {
-    let show = use_state(&cx, || true);
-
-    let inner = match *show {
-        true => {
-            rsx!( div {
-                "hello world"
-            })
-        }
-        false => {
-            rsx!( div {
-                // h1 {
-                    "bello world"
-                // }
-            })
-        }
-    };
-
-    rsx!(cx, div {
-        button {
-            "toggle"
-            onclick: move |_| {
-                let cur = *show;
-                show.set(!cur);
-            }
-        }
-        {inner}
-    })
-};

+ 0 - 41
packages/web/examples/suspense.rs

@@ -1,41 +0,0 @@
-#![allow(non_upper_case_globals)]
-
-//! Example: README.md showcase
-//!
-//! The example from the README.md.
-
-use dioxus::prelude::*;
-use dioxus_core as dioxus;
-use dioxus_core_macro::*;
-use dioxus_html as dioxus_elements;
-use dioxus_web;
-
-fn main() {
-    dioxus_web::launch(App);
-}
-
-static App: Component = |cx| {
-    todo!("suspense is broken")
-    // let doggo = suspend(|| async move {
-    //     #[derive(serde::Deserialize)]
-    //     struct Doggo {
-    //         message: String,
-    //     }
-
-    //     let src = reqwest::get("https://dog.ceo/api/breeds/image/random")
-    //         .await
-    //         .expect("Failed to fetch doggo")
-    //         .json::<Doggo>()
-    //         .await
-    //         .expect("Failed to parse doggo")
-    //         .message;
-
-    //     rsx!(cx, img { src: "{src}" })
-    // });
-
-    // rsx!(cx, div {
-    //     h1 {"One doggo coming right up"}
-    //     button { onclick: move |_| cx.needs_update(), "Get a new doggo" }
-    //     {doggo}
-    // })
-};

+ 9 - 9
src/lib.rs

@@ -46,7 +46,7 @@
 //!     dioxus::desktop::launch(app);
 //!     dioxus::desktop::launch(app);
 //! }
 //! }
 //!
 //!
-//! fn app(cx: Scope<()>) -> Element {
+//! fn app(cx: Scope) -> Element {
 //!     cx.render(rsx!("hello world!"))
 //!     cx.render(rsx!("hello world!"))
 //! }
 //! }
 //! ```
 //! ```
@@ -57,7 +57,7 @@
 //! For components with no explicit properties, we can use the `()` type. In Dioxus, all properties are memoized by default!
 //! For components with no explicit properties, we can use the `()` type. In Dioxus, all properties are memoized by default!
 //!
 //!
 //! ```rust
 //! ```rust
-//! fn App(cx: Scope<()>) -> Element {
+//! fn App(cx: Scope) -> Element {
 //!     cx.render(rsx!(
 //!     cx.render(rsx!(
 //!         Header {
 //!         Header {
 //!             title: "My App",
 //!             title: "My App",
@@ -91,7 +91,7 @@
 //! While components are reusable forms of UI elements, hooks are reusable forms of logic. All hooks start with `use_`. We can use hooks to declare state.
 //! While components are reusable forms of UI elements, hooks are reusable forms of logic. All hooks start with `use_`. We can use hooks to declare state.
 //!
 //!
 //! ```rust
 //! ```rust
-//! fn app(cx: Scope<()>) -> Element {
+//! fn app(cx: Scope) -> Element {
 //!     let name = use_state(&cx, || "world");
 //!     let name = use_state(&cx, || "world");
 //!
 //!
 //!     rsx!(cx, "hello {name}!")
 //!     rsx!(cx, "hello {name}!")
@@ -137,7 +137,7 @@
 //!     dioxus::desktop::launch(App);
 //!     dioxus::desktop::launch(App);
 //! }
 //! }
 //!
 //!
-//! fn App(cx: Scope<()>) -> Element {
+//! fn App(cx: Scope) -> Element {
 //!     let mut count = use_state(&cx, || 0);
 //!     let mut count = use_state(&cx, || 0);
 //!
 //!
 //!     cx.render(rsx!(
 //!     cx.render(rsx!(
@@ -187,20 +187,20 @@ pub use dioxus_core as core;
 #[cfg(feature = "hooks")]
 #[cfg(feature = "hooks")]
 pub use dioxus_hooks as hooks;
 pub use dioxus_hooks as hooks;
 
 
+#[cfg(feature = "router")]
+pub use dioxus_router as router;
+
 #[cfg(feature = "ssr")]
 #[cfg(feature = "ssr")]
 pub use dioxus_ssr as ssr;
 pub use dioxus_ssr as ssr;
 
 
 #[cfg(feature = "web")]
 #[cfg(feature = "web")]
 pub use dioxus_web as web;
 pub use dioxus_web as web;
 
 
-#[cfg(feature = "mobile")]
-pub use dioxus_mobile as mobile;
-
 #[cfg(feature = "desktop")]
 #[cfg(feature = "desktop")]
 pub use dioxus_desktop as desktop;
 pub use dioxus_desktop as desktop;
 
 
-#[cfg(feature = "router")]
-pub use dioxus_router as router;
+// #[cfg(feature = "mobile")]
+// pub use dioxus_mobile as mobile;
 
 
 pub mod events {
 pub mod events {
     #[cfg(feature = "html")]
     #[cfg(feature = "html")]