Browse Source

Merge pull request #51 from DioxusLabs/jk/arbitrary_expressions

Jk/arbitrary expressions
Jonathan Kelley 3 years ago
parent
commit
7d41a16208
65 changed files with 1011 additions and 1405 deletions
  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"]
 
 [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-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-ssr = { path = "./packages/ssr", 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]
 default = ["macro", "hooks", "html"]
@@ -31,11 +32,13 @@ macro = ["dioxus-core-macro"]
 hooks = ["dioxus-hooks"]
 html = ["dioxus-html"]
 router = ["dioxus-router"]
-liveview = ["dioxus-liveview"]
 ssr = ["dioxus-ssr"]
 web = ["dioxus-web", "dioxus-router/web"]
 desktop = ["dioxus-desktop", "dioxus-router/desktop"]
-mobile = ["dioxus-mobile"]
+
+
+# mobile = ["dioxus-mobile"]
+# liveview = ["dioxus-liveview"]
 
 
 [workspace]
@@ -60,7 +63,6 @@ im-rc = "15.0.0"
 fxhash = "0.2.1"
 anyhow = "1.0.51"
 serde_json = "1.0.73"
-simple_logger = "1.16.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
 argh = "0.1.7"

+ 7 - 1
README.md

@@ -79,7 +79,9 @@ If you know React, then you already know Dioxus.
     <tr>
 </table>
 
-## Examples:
+
+
+## Examples Projects:
 
 | 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.
 
+## 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?
 
 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         | ✅      |
 | [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!
 
@@ -64,7 +56,7 @@ Here's what a few common tasks look like in Dioxus:
 
 Nested components with children and internal state:
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   cx.render(rsx!( Toggle { "Toggle me" } ))
 }
 
@@ -75,9 +67,9 @@ fn Toggle(cx: Scope<ToggleProps>) -> Element {
   let mut toggled = use_state(&cx, || false);
   cx.render(rsx!{
     div {
-      {&cx.props.children}
+      &cx.props.children
       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:
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let value = use_state(&cx, String::new);
   cx.render(rsx!( 
     input {
@@ -100,7 +92,7 @@ fn App(cx: Scope<()>) -> Element {
 
 Lists and Conditional rendering:
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let list = (0..10).map(|i| {
     rsx!(li { key: "{i}", "Value: {i}" })
   });
@@ -112,8 +104,8 @@ fn App(cx: Scope<()>) -> Element {
 
   if should_show {
     cx.render(rsx!( 
-      {title}
-      ul { {list} } 
+      title,
+      ul { list } 
     ))
   } else {
     None
@@ -128,7 +120,7 @@ static App: Component = |cx, _| rsx!(cx, div {"hello world!"});
 
 Borrowed prop contents:
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let name = use_state(&cx, || String::from("example"));
   rsx!(cx, Child { title: name.as_str() })
 }
@@ -145,12 +137,12 @@ Global State
 ```rust
 struct GlobalState { name: String }
 
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   use_provide_shared_state(cx, || GlobalState { name: String::from("Toby") })
   rsx!(cx, Leaf {})
 }
 
-fn Leaf(cx: Scope<()>) -> Element {
+fn Leaf(cx: Scope) -> Element {
   let state = use_consume_shared_state::<GlobalState>(cx)?;
   rsx!(cx, "Hello {state.name}")
 }
@@ -166,20 +158,20 @@ enum Route {
   Post(id)
 }
 
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let route = use_router(cx, Route::parse);
   cx.render(rsx!(div {
-    {match route {
+    match route {
       Route::Home => rsx!( Home {} ),
       Route::Post(id) => rsx!( Post { id: id })
-    }}
+    }
   }))  
 }
 ```
 
 Suspense 
 ```rust
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
   let doggo = use_suspense(cx,
     || async { reqwest::get("https://dog.ceo/api/breeds/image/random").await.unwrap().json::<Response>().await.unwrap() },
     |response| cx.render(rsx!( img { src: "{response.message}" }))
@@ -187,8 +179,8 @@ fn App(cx: Scope<()>) -> Element {
   
   cx.render(rsx!{
     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]
 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 mut direction = use_state(&cx, || 1);
+    let direction = use_state(&cx, || 1);
 
     let (async_count, dir) = (count.for_async(), *direction);
 
     let task = use_coroutine(&cx, move || async move {
         loop {
             TimeoutFuture::new(250).await;
-            *async_count.get_mut() += dir;
+            *async_count.modify() += dir;
         }
     });
 
     rsx!(cx, div {
         h1 {"count is {count}"}
-        button {
+        button { onclick: move |_| task.stop(),
             "Stop counting"
-            onclick: move |_| task.stop()
         }
-        button {
+        button { onclick: move |_| task.resume(),
             "Start counting"
-            onclick: move |_| task.resume()
         }
         button {
-            "Switch counting direcion"
             onclick: move |_| {
-                direction *= -1;
+                *direction.modify() *= -1;
                 task.restart();
-            }
+            },
+            "Switch counting direcion"
         }
     })
-};
+}

+ 2 - 2
examples/borrowed.rs

@@ -20,8 +20,8 @@ fn main() {
     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();
 

+ 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 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;
 
 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 operator = use_state(&cx, || None as Option<&'static str>);
     let display_value = use_state(&cx, || String::from(""));
@@ -21,109 +23,138 @@ static APP: Component = |cx| {
     let toggle_percent = move |_| todo!();
     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,
     onclick: &'a dyn Fn(Arc<MouseEvent>),
     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);
 }
 
-fn App((cx, props): Scope<()>) -> Element {
+fn App((cx, props): Scope) -> Element {
     let mut rng = SmallRng::from_entropy();
     let rows = (0..10_000_usize).map(|f| {
         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)
                 "ID: {f}"
                 ul {
-                    {(0..10).map(|k| rsx!{
+                    (0..10).map(|k| rsx!{
                         li {
                             "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::*;
 
 fn main() {
-    dioxus::web::launch(App);
+    dioxus::desktop::launch(app);
 }
 enum Scene {
     ClientsList,
@@ -19,88 +19,101 @@ pub struct Client {
     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 {
             "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_hooks::*;
 use dioxus_html as dioxus_elements;
-use simple_logger::SimpleLogger;
 
 use std::collections::HashMap;
 
 fn main() {
-    if cfg!(debug_assertions) {
-        SimpleLogger::new().init().unwrap();
-    }
-
     dioxus_desktop::launch(App)
 }
 
@@ -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"} }
                         ul { class: "filters"
                             li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
                             li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
                             li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
                         }
-                        {(show_clear_completed).then(|| rsx!(
+                        (show_clear_completed).then(|| rsx!(
                             button { class: "clear-completed", onclick: move |_| clear_completed(),
                                 "Clear completed"
                             }
-                        ))}
+                        ))
                     }
-                ))}
+                ))
             }
         }
         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 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::*;
 
 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| {
-            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!(
-        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 {
     path_stack: Vec<String>,
@@ -70,29 +64,21 @@ impl Files {
     }
 
     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,
             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();
                 return;
             }
         };
-        let collected = paths.collect::<Vec<_>>();
-        log::info!("Path list reloaded {:#?}", collected);
 
         // clear the current state
         self.clear_err();
         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) {

+ 15 - 15
examples/framework_benchmark.rs

@@ -2,8 +2,8 @@ use dioxus::prelude::*;
 use rand::prelude::*;
 
 fn main() {
-    dioxus::web::launch(App);
-    // dioxus::desktop::launch(App);
+    // dioxus::web::launch(App);
+    dioxus::desktop::launch(App);
 }
 
 #[derive(Clone, PartialEq)]
@@ -31,16 +31,16 @@ impl Label {
 }
 
 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! {
-        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"
-                        div { class: "row"
+                    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)),
                             }
@@ -67,15 +67,15 @@ static App: Component = |cx| {
                 tbody {
                     {items.read().iter().enumerate().map(|(id, item)| {
                         let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
-                        rsx!(tr { class: "{is_in_danger}"
+                        rsx!(tr { class: "{is_in_danger}",
                             td { class:"col-md-1" }
                             td { class:"col-md-1", "{item.key}" }
                             td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
-                                a { class: "lbl", {item.labels} }
+                                a { class: "lbl", item.labels }
                             }
-                            td { class: "col-md-1"
+                            td { class: "col-md-1",
                                 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" }
@@ -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 {
-    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)(),
             "{cx.props.name}"
         }

+ 1 - 1
examples/hello_world.rs

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

+ 4 - 4
examples/hydration.rs

@@ -16,18 +16,18 @@ fn main() {
     let vdom = VirtualDom::new(App);
     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| {
-    let mut val = use_state(&cx, || 0);
+    let val = use_state(&cx, || 0);
 
     cx.render(rsx! {
         div {
-            h1 {"hello world. Count: {val}"}
+            h1 { "hello world. Count: {val}" }
             button {
+                onclick: move |_| *val.modify() += 1,
                 "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");
 fn main() {
     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 clear_display = state.read().display_value.eq("0");
     let clear_text = if clear_display { "C" } else { "AC" };
     let formatted = state.read().formatted_display();
 
-    rsx!(cx, div { id: "wrapper"
+    rsx!(cx, div { id: "wrapper",
         div { class: "app", style { "{STYLE}" }
             div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
                 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-sign", onclick: move |_| state.write().toggle_sign(), "±"}
                             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-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}" }
-                            })}
+                            })
                         }
                     }
-                    div { class: "operator-keys"
+                    div { class: "operator-keys",
                         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-subtract", onclick: move |_| state.write().set_operator(Operator::Sub), "−" }
@@ -70,7 +70,7 @@ static App: Component = |cx| {
             }
         }
     })
-};
+}
 
 #[derive(Props)]
 struct CalculatorKeyProps<'a> {
@@ -82,9 +82,9 @@ struct CalculatorKeyProps<'a> {
 fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element {
     cx.render(rsx! {
         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| {
     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 {

+ 1 - 0
examples/readme.rs

@@ -3,6 +3,7 @@
 //! The example from the README.md.
 
 use dioxus::prelude::*;
+
 fn main() {
     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 with {formatting}"}
             h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"}
+            h1 {"Formatting without interpolation " [formatting_tuple.0] "and" [formatting_tuple.1] }
             h2 {
                 "Multiple"
                 "Text"
@@ -72,7 +73,7 @@ pub static EXAMPLE: Component = |cx| {
                 h3 {"elements"}
             }
             div {
-                class: "my special div"
+                class: "my special div",
                 h1 {"Headers and attributes!"}
             }
             div {
@@ -88,42 +89,51 @@ pub static EXAMPLE: Component = |cx| {
             }
 
             // Expressions can be used in element position too:
-            {rsx!(p { "More templating!" })}
-            // {html!(<p>"Even HTML templating!!"</p>)}
+            rsx!(p { "More templating!" }),
 
             // 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();
                 // Iterators *should* have keys when you can provide them.
                 // Keys make your app run faster. Make sure your keys are stable, unique, and predictable.
                 // Using an "ID" associated with your data is a good idea.
-                data.into_iter().map(|(k, v)| rsx!(li { key: "{k}" "{v}" }))
-            }}
+                data.into_iter().map(|(k, v)| rsx!(li { key: "{k}", "{v}" }))
+            }
 
             // Matching
-            {match true {
+            match true {
                 true => rsx!( h1 {"Top text"}),
                 false => rsx!( h1 {"Bottom text"})
-            }}
+            }
 
             // Conditional rendering
             // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals.
             // You can convert a bool condition to rsx! with .then and .or
-            {true.then(|| rsx!(div {}))}
+            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 {
-                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
-            {NONE_ELEMENT}
+            NONE_ELEMENT,
 
             // can also just use empty fragments
             Fragment {}
@@ -137,9 +147,8 @@ pub static EXAMPLE: Component = |cx| {
                 Fragment {
                     "D"
                     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" }
 
             // Can pass in props directly as an expression
-            {{
+            {
                 let props = TallerProps {a: "hello", children: Default::default()};
                 rsx!(Taller { ..props })
-            }}
+            }
 
             // Spreading can also be overridden manually
             Taller {
-                ..TallerProps { a: "ballin!", children: Default::default() }
+                ..TallerProps { a: "ballin!", children: Default::default() },
                 a: "not ballin!"
             }
 
             // Can take children too!
             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(&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)]
     pub struct BallerProps {}
 
+    #[allow(non_snake_case)]
     /// This component totally balls
     pub fn Baller(_: Scope<BallerProps>) -> Element {
         todo!()
@@ -195,12 +212,20 @@ mod baller {
 
 #[derive(Props)]
 pub struct TallerProps<'a> {
+    /// Fields are documented and accessible in rsx!
     a: &'static str,
     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| {
     cx.render(rsx!(
-        div { class: "overflow-hidden"
+        div { class: "overflow-hidden",
         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 {}
             Entry {}
             Hero {}
@@ -33,20 +33,20 @@ pub static App: Component = |cx| {
 pub static Header: Component = |cx| {
     cx.render(rsx! {
         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 {}
-                        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 {
-                        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"
                         RightArrowIcon {}
                     }
@@ -59,34 +59,34 @@ pub static Header: Component = |cx| {
 pub static Hero: Component = |cx| {
     //
     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" }
                         "Dioxus Sneak Peek"
                     }
                     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
                         technologies! It is functional, fast, and portable. Dioxus can run on the web, on the desktop, and
                         on mobile and embedded platforms."
 
                     }
-                    div { class: "flex justify-center"
+                    div { class: "flex justify-center",
                         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"
                         }
                         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"
                         }
                     }
                 }
-                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"
                 }
                 }
@@ -97,8 +97,8 @@ pub static Hero: Component = |cx| {
 pub static Entry: Component = |cx| {
     //
     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 {
 
                 }
@@ -111,13 +111,13 @@ pub static StacksIcon: Component = |cx| {
     cx.render(rsx!(
         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"}
         }
     ))
@@ -125,13 +125,13 @@ pub static StacksIcon: Component = |cx| {
 pub static RightArrowIcon: Component = |cx| {
     cx.render(rsx!(
         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"}
         }
     ))

+ 12 - 7
examples/tasks.rs

@@ -2,26 +2,31 @@
 //!
 //! The example from the README.md.
 
+use dioxus::prelude::*;
 use std::time::Duration;
 
-use dioxus::prelude::*;
 fn main() {
     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! {
         div {
             h1 { "High-Five counter: {count}" }
             button {
-                onclick: move |_| count +=1 ,
+                onclick: move |_| *count.modify() += 1,
                 "Click me!"
             }
         }

+ 21 - 18
examples/todomvc.rs

@@ -29,7 +29,7 @@ const App: Component = |cx| {
 
     let todolist = todos
         .iter()
-        .filter(|(id, item)| match *filter {
+        .filter(|(_id, item)| match *filter {
             FilterState::All => true,
             FilterState::Active => !item.checked,
             FilterState::Completed => item.checked,
@@ -48,34 +48,37 @@ const App: Component = |cx| {
         _ => "items",
     };
 
-    rsx!(cx, div { id: "app"
+    rsx!(cx, div { id: "app",
         style {"{STYLE}"}
         div {
-            header { class: "header"
+            header { class: "header",
                 h1 {"todos"}
                 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 {
-                    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: "Active", a { href: "active", onclick: move |_| filter.set(FilterState::Active), "Active" }}
                         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 { "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 {
         "{todo.id}"
         input {
-            class: "toggle"
-            r#type: "checkbox"
+            class: "toggle",
+            r#type: "checkbox",
             "{todo.checked}"
         }
        {is_editing.then(|| rsx!{
             input {
-                value: "{contents}"
+                value: "{contents}",
                 oninput: move |evt| contents.set(evt.value.clone())
             }
         })}

+ 9 - 18
examples/web_tick.rs

@@ -13,28 +13,19 @@
 use dioxus::prelude::*;
 
 fn main() {
-    #[cfg(target_arch = "wasm32")]
-    intern_strings();
-
-    dioxus::web::launch(App);
+    dioxus::desktop::launch(App);
 }
 
 static App: Component = |cx| {
     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! {
         table {
             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! {
         tr {
             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}" }
             }
-            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" }

+ 3 - 4
examples/webview_web.rs

@@ -1,4 +1,3 @@
-#![allow(non_upper_case_globals, non_snake_case)]
 //! Example: Webview Renderer
 //! -------------------------
 //!
@@ -13,10 +12,10 @@
 use dioxus::prelude::*;
 
 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);
 
     cx.render(rsx! {
@@ -26,4 +25,4 @@ static App: Component = |cx| {
             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]
 name = "dioxus-core-macro"
-version = "0.1.2"
+version = "0.1.3"
 authors = ["Jonathan Kelley"]
 edition = "2021"
 description = "Core macro for Dioxus Virtual DOM"
@@ -16,6 +16,7 @@ proc-macro = true
 
 [dependencies]
 once_cell = "1.8"
+proc-macro-error = "1.0.4"
 proc-macro2 = { version = "1.0.6" }
 quote = "1.0"
 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! {
+            #[allow(non_camel_case)]
             #modifiers
             #vis struct #struct_name #generics {
                 #(#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 {}
 ///
 ///     /// This component totally balls
-///     pub fn Baller(cx: Scope<()>) -> DomTree {
+///     pub fn Baller(cx: Scope) -> DomTree {
 ///         todo!()
 ///     }
 /// }
@@ -177,11 +177,12 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
 ///     todo!()
 /// }
 /// ```
+#[proc_macro_error::proc_macro_error]
 #[proc_macro]
 pub fn rsx(s: TokenStream) -> TokenStream {
     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::{
     ext::IdentExt,
     parse::{Parse, ParseBuffer, ParseStream},
-    token, Error, Expr, ExprClosure, Ident, Result, Token,
+    token, Expr, ExprClosure, Ident, Result, Token,
 };
 
 pub struct Component {
-    // accept any path-like argument
     name: syn::Path,
     body: Vec<ComponentField>,
     children: Vec<BodyNode>,
@@ -32,12 +31,8 @@ pub struct Component {
 
 impl Parse for Component {
     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)?;
 
-        // parse the guts
         let content: ParseBuffer;
 
         // if we see a `{` then we have a block
@@ -48,91 +43,32 @@ impl Parse for Component {
             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 children = Vec::new();
         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 !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![..]>()?;
                 manual_props = Some(content.parse::<Expr>()?);
             } 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>()?);
             } else {
-                if !self.allow_children {
-                    return Err(Error::new(
-                        content.span(),
-                        "This item is not allowed to accept children.",
-                    ));
-                }
                 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![,]) {
                 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 {
     ManExpr(Expr),
-    OnHandler(ExprClosure),
 
-    // A handler was provided in {} tokens
     OnHandlerRaw(Expr),
 }
 
@@ -229,9 +163,6 @@ impl ToTokens for ContentField {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         match self {
             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! {
                 __cx.bump().alloc(#e)
             }),
@@ -246,13 +177,7 @@ impl Parse for ComponentField {
 
         let name_str = name.to_string();
         let content = if name_str.starts_with("on") {
-            if input.peek(token::Brace) {
-                let content;
-                syn::braced!(content in input);
-                ContentField::OnHandlerRaw(content.parse()?)
-            } else {
-                ContentField::OnHandler(input.parse()?)
-            }
+            ContentField::OnHandlerRaw(input.parse()?)
         } else {
             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 syn::{
     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 _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![:]) {
                 let name = content.parse::<Ident>()?;
+                let ident = name.clone();
+
                 let name_str = name.to_string();
                 content.parse::<Token![:]>()?;
 
                 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 {
                     match name_str.as_str() {
                         "key" => {
                             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" => {
                             _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
             // we don't actually care if there *are* commas after elements/text
             if content.peek(Token![,]) {
@@ -138,7 +191,7 @@ impl Parse for Element {
 impl ToTokens for Element {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let name = &self.name;
-        let childs = &self.children;
+        let children = &self.children;
 
         let listeners = &self.listeners;
         let attr = &self.attributes;
@@ -153,7 +206,7 @@ impl ToTokens for Element {
                 dioxus_elements::#name,
                 [ #(#listeners),* ],
                 [ #(#attr),* ],
-                [ #(#childs),* ],
+                [ #(#children),* ],
                 #key,
             )
         });
@@ -173,8 +226,8 @@ enum ElementAttr {
     // "attribute": true,
     CustomAttrExpression { name: LitStr, value: Expr },
 
-    // onclick: move |_| {}
-    EventClosure { name: Ident, closure: ExprClosure },
+    // // onclick: move |_| {}
+    // EventClosure { name: Ident, closure: ExprClosure },
 
     // onclick: {}
     EventTokens { name: Ident, tokens: Expr },
@@ -189,7 +242,7 @@ impl ToTokens for ElementAttrNamed {
     fn to_tokens(&self, tokens: &mut TokenStream2) {
         let ElementAttrNamed { el_name, attr } = self;
 
-        let toks = match attr {
+        tokens.append_all(match attr {
             ElementAttr::AttrText { name, value } => {
                 quote! {
                     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)
                 }
             }
-
             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 } => {
-                quote! { __cx.attr( #name, format_args_f!(#value), None, false ) }
-            }
-
-            ElementAttr::EventClosure { name, closure } => {
                 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 } => {
                 quote! {
                     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.
 
-mod ambiguous;
-mod body;
 mod component;
 mod element;
-mod fragment;
 mod node;
 
 // Re-export the namespaces into each other
-pub use ambiguous::*;
-pub use body::*;
 pub use component::*;
 pub use element::*;
-pub use fragment::*;
 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 syn::{
     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 {
-    Element(AmbiguousElement),
-    Text(TextNode),
+    Element(Element),
+    Component(Component),
+    Text(LitStr),
     RawExpr(Expr),
 }
 
 impl Parse for BodyNode {
     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) {
         match &self {
             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! {
                  __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" }
 fern = { version = "0.6.0", features = ["colored"] }
 rand = { version = "0.8.4", features = ["small_rng"] }
-simple_logger = "1.13.0"
 dioxus-core-macro = { path = "../core-macro", version = "^0.1.2" }
 criterion = "0.3.5"
 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
-fn app(cx: Scope<()>) -> Element {
+fn app(cx: Scope) -> Element {
     rsx!(cx, div { "hello world" })
 }
 

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

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

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

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

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

@@ -84,7 +84,7 @@ pub enum VNode<'src> {
     /// # Example
     ///
     /// ```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 {
-        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()
     }
 }
+
+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
     ///
     /// ```rust, ignore
-    /// fn App(cx: Scope<()>) -> Element {
+    /// fn App(cx: Scope) -> Element {
     ///     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.
 ///
 /// ```rust, ignore
-/// fn App(cx: Scope<()>) -> Element {
+/// fn App(cx: Scope) -> Element {
 ///     cx.render(rsx!{
 ///         div { "Hello World" }
 ///     })
@@ -127,7 +127,7 @@ impl VirtualDom {
     ///
     /// # Example
     /// ```rust, ignore
-    /// fn Example(cx: Scope<()>) -> Element  {
+    /// fn Example(cx: Scope) -> Element  {
     ///     cx.render(rsx!( div { "hello world" } ))
     /// }
     ///
@@ -388,7 +388,7 @@ impl VirtualDom {
     /// # Example
     ///
     /// ```rust, ignore
-    /// fn App(cx: Scope<()>) -> Element {
+    /// fn App(cx: Scope) -> Element {
     ///     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.
     ///
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     /// }
     ///
@@ -564,7 +564,7 @@ impl VirtualDom {
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     ///
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     /// }
     ///
@@ -586,7 +586,7 @@ impl VirtualDom {
     ///
     ///
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     /// }
     ///
@@ -609,7 +609,7 @@ impl VirtualDom {
     ///
     ///
     /// ```rust
-    /// fn Base(cx: Scope<()>) -> Element {
+    /// fn Base(cx: Scope) -> Element {
     ///     rsx!(cx, div {})
     /// }
     ///
@@ -753,7 +753,7 @@ pub enum SchedulerMsg {
 ///
 /// # Example
 /// ```rust
-/// fn App(cx: Scope<()>) -> Element {
+/// fn App(cx: Scope) -> Element {
 ///     rsx!(cx, div {
 ///         onclick: move |_| println!("Clicked!")
 ///     })
@@ -871,7 +871,7 @@ impl<'a, const A: bool> FragmentBuilder<'a, A> {
 /// ## Example
 ///
 /// ```rust, ignore
-/// fn App(cx: Scope<()>) -> Element {
+/// fn App(cx: Scope) -> Element {
 ///     cx.render(rsx!{
 ///         CustomCard {
 ///             h1 {}2

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

@@ -10,7 +10,7 @@ fn test_borrowed_state() {
     let _ = VirtualDom::new(Parent);
 }
 
-fn Parent(cx: Scope<()>) -> Element {
+fn Parent(cx: Scope) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| &*f);
 
     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.
 #[test]
 fn test_memory_leak() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         let val = cx.use_hook(|_| 0, |f| f);
 
         *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" })
     }
 
@@ -85,7 +85,7 @@ fn test_memory_leak() {
 
 #[test]
 fn memo_works_properly() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         let val = cx.use_hook(|_| 0, |f| f);
 
         *val += 1;
@@ -164,7 +164,7 @@ fn free_works_on_root_props() {
 
 #[test]
 fn free_works_on_borrowed() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         cx.render(rsx! {
             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);
         rsx!(cx, div { "{name.0}" })
     }
@@ -214,7 +214,7 @@ fn free_works_on_root_hooks() {
 
 #[test]
 fn old_props_arent_stale() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         dbg!("rendering parent");
         let cnt = cx.use_hook(|_| 0, |f| f);
         *cnt += 1;
@@ -261,7 +261,7 @@ fn old_props_arent_stale() {
 
 #[test]
 fn basic() {
-    fn app(cx: Scope<()>) -> Element {
+    fn app(cx: Scope) -> Element {
         rsx!(cx, div {
             child(a: "abcdef".to_string())
         })

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

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

+ 0 - 1
packages/desktop/Cargo.toml

@@ -37,4 +37,3 @@ tokio_runtime = ["tokio"]
 
 [dev-dependencies]
 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);
 }
 
-fn app(cx: Scope<()>) -> Element {
+fn app(cx: Scope) -> Element {
     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! {
         div {

+ 4 - 43
packages/hooks/README.md

@@ -4,58 +4,19 @@ This crate includes some basic useful hooks for dioxus:
 
 - use_state
 - use_ref
-- use_collection
-- use_task
-- use_signal
+- use_future
+- use_coroutine
 
 ## use_state
 
-The king daddy of state hooks.
+The primary mechanism of stored state.
 
 You can always use it "normally" with the `split` method:
 
 ```rust
-// Normal usage:
+// Rusty-smart-pointer usage:
 let value = use_state(&cx, || 10);
 
 // "Classic" usage:
 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;
-pub use usestate::{use_state, AsyncUseState, UseState};
+pub use usestate::{use_state, UseState};
 
 mod useref;
 pub use useref::*;
@@ -16,5 +16,45 @@ pub use usefuture::*;
 mod 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::{
     cell::{Cell, Ref, RefCell, RefMut},
     fmt::{Debug, Display},
-    ops::Not,
     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!
 ///
 /// ## Dioxus equivalent of useState, designed for Rust
@@ -109,6 +98,7 @@ where
         UseState { inner: self.inner }
     }
 }
+
 impl<T: Debug> Debug for UseState<'_, T> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         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> {
-    /// 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.
     ///
@@ -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};
 
 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));
     }
 }
-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"
 repository = "https://github.com/DioxusLabs/dioxus/"
 homepage = "https://dioxuslabs.com"
-documentation = "https://dioxuslabs.com"
+documentation = "https://docs.rs/dioxus"
 keywords = ["dom", "ui", "gui", "react", "wasm"]
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
 dioxus-core = { path = "../core", version = "^0.1.4" }
 serde = { version = "1", features = ["derive"], optional = true }
 serde_repr = { version = "0.1", optional = true }
 
-
 [features]
 default = []
 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;    
 }
 
-fn App(cx: Scope<()>) -> Element {
+fn App(cx: Scope) -> Element {
     let mut count = use_state(&cx, || 0);
     cx.render(rsx!(
         button { onclick: move |_| count += 1, "Incr" }

+ 1 - 4
packages/mobile/Cargo.toml

@@ -12,7 +12,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 [dependencies]
 anyhow = "1.0"
 # 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"
 serde = "1.0.126"
 serde_json = "1.0.64"
@@ -23,6 +23,3 @@ wry = "0.12.2"
 android_logger = "0.9.0"
 log = "0.4.11"
 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;
 use dom::*;
 
-fn init_logging() {
-    simple_logger::SimpleLogger::new().init().unwrap();
-}
-
 static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html");
 
 pub fn launch(root: Component, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
@@ -187,7 +183,6 @@ impl<T: 'static + Send> WebviewRenderer<T> {
 }
 
 fn main() {
-    init_logging();
     let event_loop = EventLoop::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 {
             href: "#",
             class: format_args!("{}", cx.props.class.unwrap_or("")),
-            {&cx.props.children}
             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| {
         cx.render(rsx! {
-            div {
-                title: "About W3Schools"
-                {(0..20).map(|f| rsx!{
+            div { title: "About W3Schools",
+                (0..20).map(|f| rsx!{
                     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 {
-                            title: "About W3Schools"
+                            title: "About W3Schools",
                             "Hello world!: {f}"
                         }
                     }
-                })}
+                })
             }
         })
     };

+ 17 - 29
packages/web/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "dioxus-web"
-version = "0.0.0"
+version = "0.0.1"
 authors = ["Jonathan Kelley"]
 edition = "2018"
 description = "Dioxus VirtualDOM renderer for the web browser using websys"
@@ -9,20 +9,12 @@ repository = "https://github.com/DioxusLabs/dioxus/"
 homepage = "https://dioxuslabs.com"
 documentation = "https://dioxuslabs.com"
 keywords = ["dom", "ui", "gui", "react", "wasm"]
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [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"
-# wasm-bindgen-shared = { path = "../../../Tinkering/wasm-bindgen/crates/shared" }
-# wasm-bindgen-macro-support = { path = "../../../Tinkering/wasm-bindgen/crates/macro-support" }
-# wasm-bindgen = { features = [
-
-
 wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
-# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
-# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
 lazy_static = "1.4.0"
 wasm-bindgen-futures = "0.4.20"
 log = { 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);
 //! }
 //!
-//! fn app(cx: Scope<()>) -> Element {
+//! fn app(cx: Scope) -> Element {
 //!     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!
 //!
 //! ```rust
-//! fn App(cx: Scope<()>) -> Element {
+//! fn App(cx: Scope) -> Element {
 //!     cx.render(rsx!(
 //!         Header {
 //!             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.
 //!
 //! ```rust
-//! fn app(cx: Scope<()>) -> Element {
+//! fn app(cx: Scope) -> Element {
 //!     let name = use_state(&cx, || "world");
 //!
 //!     rsx!(cx, "hello {name}!")
@@ -137,7 +137,7 @@
 //!     dioxus::desktop::launch(App);
 //! }
 //!
-//! fn App(cx: Scope<()>) -> Element {
+//! fn App(cx: Scope) -> Element {
 //!     let mut count = use_state(&cx, || 0);
 //!
 //!     cx.render(rsx!(
@@ -187,20 +187,20 @@ pub use dioxus_core as core;
 #[cfg(feature = "hooks")]
 pub use dioxus_hooks as hooks;
 
+#[cfg(feature = "router")]
+pub use dioxus_router as router;
+
 #[cfg(feature = "ssr")]
 pub use dioxus_ssr as ssr;
 
 #[cfg(feature = "web")]
 pub use dioxus_web as web;
 
-#[cfg(feature = "mobile")]
-pub use dioxus_mobile as mobile;
-
 #[cfg(feature = "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 {
     #[cfg(feature = "html")]