Просмотр исходного кода

feat: overhaul examples and clean things up

Jonathan Kelley 3 лет назад
Родитель
Сommit
420a30e5d4

+ 2 - 18
Cargo.toml

@@ -60,24 +60,8 @@ num-format = "0.4.0"
 separator = "0.4.1"
 serde = { version = "1.0.131", features = ["derive"] }
 im-rc = "15.0.0"
-fxhash = "0.2.1"
 anyhow = "1.0.51"
 serde_json = "1.0.73"
-
-[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
-argh = "0.1.7"
-env_logger = "0.9.0"
-tokio = { version = "1.14.0", features = ["full"] }
-rand = { version = "0.8.4", features = ["small_rng"] }
-gloo-timers = "0.2.2"
-
-[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
-gloo-timers = "0.2.2"
-wasm-logger = "0.2.0"
-console_error_panic_hook = "0.1.7"
 rand = { version = "0.8.4", features = ["small_rng"] }
-wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
-
-[dev-dependencies.getrandom]
-version = "0.2.3"
-features = ["js"]
+tokio = { version = "1.14.0", features = ["full"] }
+reqwest = { version = "0.11.8", features = ["json"] }

+ 180 - 0
examples/assets/fileexplorer.css

@@ -0,0 +1,180 @@
+* {
+    margin: 0;
+    padding: 0;
+    font-family: 'Roboto', sans-serif;
+    user-select: none;
+    transition: .2s all;
+}
+
+body {
+    padding-top: 77px;
+}
+
+
+/* header {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 10;
+  padding: 20px;
+  background-color: #2196F3;
+  color: white;
+}
+header h1 {
+  float: left;
+  font-size: 20px;
+  font-weight: 400;
+}
+header .material-icons {
+  float: right;
+  cursor: pointer;
+}
+header .icon-menu {
+  float: left;
+  margin-right: 20px;
+} */
+
+main {
+    padding: 20px 50px;
+}
+
+.folder * {
+    width: 100px;
+}
+
+.folder {
+    float: left;
+    width: 100px;
+    height: 152px;
+    /* //padding: 20px; */
+    margin-right: 50px;
+    margin-bottom: 70px;
+    border-radius: 2px;
+    /* //overflow: hidden; */
+    cursor: pointer;
+}
+
+.folder:hover h1 {
+    display: none;
+}
+
+.folder:hover p.cooltip {
+    opacity: 1;
+    top: 0;
+}
+
+.folder * {
+    text-align: center;
+}
+
+.folder i {
+    margin: 0;
+    font-size: 100px;
+    color: #607D8B;
+}
+
+.folder h1 {
+    position: relative;
+    display: block;
+    top: -37px;
+    font-size: 20px;
+    font-weight: 400;
+}
+
+.folder p.cooltip {
+    position: relative;
+    top: 5px;
+    left: -50%;
+    margin-left: 35px;
+    background: #212121;
+    font-size: 15px;
+    color: white;
+    border-radius: 4px;
+    padding: 10px 20px;
+    padding-right: 30px;
+    width: 100px;
+    opacity: 0;
+}
+
+.folder p.cooltip:before {
+    content: '';
+    position: absolute;
+    display: block;
+    top: -4px;
+    left: 50%;
+    margin-left: -5px;
+    height: 10px;
+    width: 10px;
+    border-radius: 2px;
+    background-color: #212121;
+    transform: rotate(45deg);
+}
+
+div.properties {
+    position: fixed;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 10;
+    width: 300px;
+    background-color: white;
+}
+
+div.properties:before {
+    content: '';
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 300px;
+    bottom: 0;
+    background-color: #212121;
+    opacity: .5;
+    overflow: hidden;
+}
+
+div.properties img {
+    position: relative;
+    top: -1px;
+    left: -1px;
+    width: 110%;
+    height: 200px;
+    filter: blur(2px);
+}
+
+div.properties h1 {
+    position: relative;
+    width: 100%;
+    text-align: left;
+    margin-left: 20px;
+    color: white;
+}
+
+header {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    padding: 20px;
+    background-color: #2196F3;
+    color: white;
+    display: flex;
+    align-items: center;
+}
+
+header h1 {
+    font-weight: 400;
+}
+
+header span {
+    flex: 1;
+}
+
+header i {
+    margin: 0 10px;
+    cursor: pointer;
+}
+
+header i:nth-child(1) {
+    margin: 0 20px;
+}

+ 373 - 0
examples/assets/todomvc.css

@@ -0,0 +1,373 @@
+html,
+body {
+    margin: 0;
+    padding: 0;
+}
+
+button {
+    margin: 0;
+    padding: 0;
+    border: 0;
+    background: none;
+    font-size: 100%;
+    vertical-align: baseline;
+    font-family: inherit;
+    font-weight: inherit;
+    color: inherit;
+    -webkit-appearance: none;
+    appearance: none;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+    font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
+    line-height: 1.4em;
+    background: #f5f5f5;
+    color: #4d4d4d;
+    min-width: 230px;
+    max-width: 550px;
+    margin: 0 auto;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+    font-weight: 300;
+}
+
+:focus {
+    outline: 0;
+}
+
+.hidden {
+    display: none;
+}
+
+.todoapp {
+    background: #fff;
+    margin: 130px 0 40px 0;
+    position: relative;
+    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
+}
+
+.todoapp input::-webkit-input-placeholder {
+    font-style: italic;
+    font-weight: 300;
+    color: #e6e6e6;
+}
+
+.todoapp input::-moz-placeholder {
+    font-style: italic;
+    font-weight: 300;
+    color: #e6e6e6;
+}
+
+.todoapp input::input-placeholder {
+    font-style: italic;
+    font-weight: 300;
+    color: #e6e6e6;
+}
+
+.todoapp h1 {
+    position: absolute;
+    top: -155px;
+    width: 100%;
+    font-size: 100px;
+    font-weight: 100;
+    text-align: center;
+    color: rgba(175, 47, 47, 0.15);
+    -webkit-text-rendering: optimizeLegibility;
+    -moz-text-rendering: optimizeLegibility;
+    text-rendering: optimizeLegibility;
+}
+
+.new-todo,
+.edit {
+    position: relative;
+    margin: 0;
+    width: 100%;
+    font-size: 24px;
+    font-family: inherit;
+    font-weight: inherit;
+    line-height: 1.4em;
+    border: 0;
+    color: inherit;
+    padding: 6px;
+    border: 1px solid #999;
+    box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
+    box-sizing: border-box;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+}
+
+.new-todo {
+    padding: 16px 16px 16px 60px;
+    border: none;
+    background: rgba(0, 0, 0, 0.003);
+    box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
+}
+
+.main {
+    position: relative;
+    z-index: 2;
+    border-top: 1px solid #e6e6e6;
+}
+
+.toggle-all {
+    text-align: center;
+    border: none;
+    /* Mobile Safari */
+    opacity: 0;
+    position: absolute;
+}
+
+.toggle-all+label {
+    width: 60px;
+    height: 34px;
+    font-size: 0;
+    position: absolute;
+    top: -52px;
+    left: -13px;
+    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
+}
+
+.toggle-all+label:before {
+    content: '❯';
+    font-size: 22px;
+    color: #e6e6e6;
+    padding: 10px 27px 10px 27px;
+}
+
+.toggle-all:checked+label:before {
+    color: #737373;
+}
+
+.todo-list {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+}
+
+.todo-list li {
+    position: relative;
+    font-size: 24px;
+    border-bottom: 1px solid #ededed;
+}
+
+.todo-list li:last-child {
+    border-bottom: none;
+}
+
+.todo-list li.editing {
+    border-bottom: none;
+    padding: 0;
+}
+
+.todo-list li.editing .edit {
+    display: block;
+    width: 506px;
+    padding: 12px 16px;
+    margin: 0 0 0 43px;
+}
+
+.todo-list li.editing .view {
+    display: none;
+}
+
+.todo-list li .toggle {
+    text-align: center;
+    width: 40px;
+    /* auto, since non-WebKit browsers doesn't support input styling */
+    height: auto;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    margin: auto 0;
+    border: none;
+    /* Mobile Safari */
+    -webkit-appearance: none;
+    appearance: none;
+}
+
+.todo-list li .toggle {
+    opacity: 0;
+}
+
+.todo-list li .toggle+label {
+    /*
+		Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
+		IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
+	*/
+    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
+    background-repeat: no-repeat;
+    background-position: center left;
+}
+
+.todo-list li .toggle:checked+label {
+    background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
+}
+
+.todo-list li label {
+    word-break: break-all;
+    padding: 15px 15px 15px 60px;
+    display: block;
+    line-height: 1.2;
+    transition: color 0.4s;
+}
+
+.todo-list li.completed label {
+    color: #d9d9d9;
+    text-decoration: line-through;
+}
+
+.todo-list li .destroy {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 10px;
+    bottom: 0;
+    width: 40px;
+    height: 40px;
+    margin: auto 0;
+    font-size: 30px;
+    color: #cc9a9a;
+    margin-bottom: 11px;
+    transition: color 0.2s ease-out;
+}
+
+.todo-list li .destroy:hover {
+    color: #af5b5e;
+}
+
+.todo-list li .destroy:after {
+    content: '×';
+}
+
+.todo-list li:hover .destroy {
+    display: block;
+}
+
+.todo-list li .edit {
+    display: none;
+}
+
+.todo-list li.editing:last-child {
+    margin-bottom: -1px;
+}
+
+.footer {
+    color: #777;
+    padding: 10px 15px;
+    height: 20px;
+    text-align: center;
+    border-top: 1px solid #e6e6e6;
+}
+
+.footer:before {
+    content: '';
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    height: 50px;
+    overflow: hidden;
+    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
+}
+
+.todo-count {
+    float: left;
+    text-align: left;
+}
+
+.todo-count strong {
+    font-weight: 300;
+}
+
+.filters {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    position: absolute;
+    right: 0;
+    left: 0;
+}
+
+.filters li {
+    display: inline;
+}
+
+.filters li a {
+    color: inherit;
+    margin: 3px;
+    padding: 3px 7px;
+    text-decoration: none;
+    border: 1px solid transparent;
+    border-radius: 3px;
+}
+
+.filters li a:hover {
+    border-color: rgba(175, 47, 47, 0.1);
+}
+
+.filters li a.selected {
+    border-color: rgba(175, 47, 47, 0.2);
+}
+
+.clear-completed,
+html .clear-completed:active {
+    float: right;
+    position: relative;
+    line-height: 20px;
+    text-decoration: none;
+    cursor: pointer;
+}
+
+.clear-completed:hover {
+    text-decoration: underline;
+}
+
+.info {
+    margin: 65px auto 0;
+    color: #bfbfbf;
+    font-size: 10px;
+    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+    text-align: center;
+}
+
+.info p {
+    line-height: 1;
+}
+
+.info a {
+    color: inherit;
+    text-decoration: none;
+    font-weight: 400;
+}
+
+.info a:hover {
+    text-decoration: underline;
+}
+
+
+/*
+	Hack to remove background from Mobile Safari.
+	Can't use it globally since it destroys checkboxes in Firefox
+*/
+
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+    .toggle-all,
+    .todo-list li .toggle {
+        background: none;
+    }
+    .todo-list li .toggle {
+        height: 40px;
+    }
+}
+
+@media (max-width: 430px) {
+    .footer {
+        height: 50px;
+    }
+    .filters {
+        bottom: 10px;
+    }
+}

+ 0 - 48
examples/async.rs

@@ -1,48 +0,0 @@
-/*
-This example shows how to use async and loops to implement a coroutine in a component. Coroutines can be controlled via
-the `TaskHandle` object.
-*/
-
-use dioxus::prelude::*;
-use gloo_timers::future::TimeoutFuture;
-
-#[tokio::main]
-async fn main() {
-    dioxus::desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let count = use_state(&cx, || 0);
-    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.modify() += dir;
-            }
-        }
-    });
-
-    cx.render(rsx! {
-        div {
-            h1 {"count is {count}"}
-            button { onclick: move |_| task.stop(),
-                "Stop counting"
-            }
-            button { onclick: move |_| task.resume(),
-                "Start counting"
-            }
-            button {
-                onclick: move |_| {
-                    *direction.modify() *= -1;
-                    task.restart();
-                },
-                "Switch counting direcion"
-            }
-        }
-    })
-}

+ 120 - 131
examples/calculator.rs

@@ -1,160 +1,149 @@
-#![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.
 */
 
-use std::sync::Arc;
-
 use dioxus::events::*;
 use dioxus::prelude::*;
 use separator::Separatable;
 
 fn main() {
-    dioxus::desktop::launch(app);
+    use dioxus::desktop::tao::dpi::LogicalSize;
+    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))
+        })
+    });
 }
 
 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(""));
+    let operator = use_state(&cx, Option::default);
+    let display_value = use_state(&cx, String::new);
 
-    let toggle_percent = move |_| todo!();
     let input_digit = move |num: u8| display_value.modify().push_str(num.to_string().as_str());
 
     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", [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);
+        div { id: "wrapper",
+            div { class: "app",
+                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();
                             }
-                        },
-                        [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))
+                        }
+                        _ => {}
+                    },
+                    div { class: "calculator-display", [cur_val.separated_string()] }
+                    div { class: "calculator-keypad",
+                        div { class: "input-keys",
+                            div { class: "function-keys",
+                                button {
+                                    class: "calculator-key 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" }]
+                                }
+                                button {
+                                    class: "calculator-key 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))
+                                        }
+                                    },
+                                    "±"
+                                }
+                                button {
+                                    class: "calculator-key key-percent",
+                                    onclick: move |_| {
+                                        let pct = (display_value.parse::<f64>().unwrap() / 100.0).to_string();
+                                        display_value.set(pct);
+                                    },
+                                    "%"
+                                }
+                            }
+                            div { class: "digit-keys",
+                                button { class: "calculator-key key-0", onclick: move |_| input_digit(0),
+                                    "0"
+                                }
+                                button { class: "calculator-key key-dot", onclick: move |_| display_value.modify().push_str("."),
+                                    "●"
+                                }
+                                (1..10).map(|k| rsx!{
+                                    button {
+                                        class: "calculator-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",
+                            button { class: "calculator-key key-divide",
+                                onclick: move |_| operator.set(Some("/")),
+                                "÷"
+                            }
+                            button { class: "calculator-key key-multiply",
+                                onclick: move |_| operator.set(Some("*")),
+                                "×"
+                            }
+                            button { class: "calculator-key key-subtract",
+                                onclick: move |_| operator.set(Some("-")),
+                                "−"
+                            }
+                            button { class: "calculator-key key-add",
+                                onclick: move |_| operator.set(Some("+")),
+                                "+"
                             }
-                        },
-                        "="
+                            button { class: "calculator-key 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);
+                                    }
+                                },
+                                "="
+                            }
+                        }
                     }
                 }
             }
         }
-    ))
-}
 
-#[inline_props]
-fn CalculatorKey<'a>(
-    cx: Scope,
-    name: &'static str,
-    onclick: &'a dyn Fn(Arc<MouseEvent>),
-    children: Element<'a>,
-) -> Element {
-    cx.render(rsx! {
-        button {
-            class: "calculator-key {name}",
-            onclick: onclick,
-            children
-        }
-    })
+    ))
 }

+ 0 - 73
examples/coroutine.rs

@@ -1,73 +0,0 @@
-//! Example: Coroutines!
-//! --------------------
-//!
-//! Coroutines are an awesome way to write concurrent code. Dioxus heavily leverages coroutines to make sense of complex
-//! ongoing asynchronous tasks. The async scheduler of Dioxus supports both single-threaded and multi-threaded coroutines,
-//! so you can drop in code to run across multiple threads without blocking the main thread.
-//!
-//! Dioxus cannot simply abstract away the threading model for the web, unfortunately. If you want to use "web threads"
-//! you either need to limit support for Chrome, or you need to use a Web Workers and message passing. This is easy enough
-//! to do in your own code, and doesn't require 1st-party support from Dioxus itself.
-//!
-//! UseState and friends work fine with coroutines, but coroutines might be easier to use with the Dirac global state
-//! management API. This lets you easily drive global state from a coroutine without having to subscribe to the state.
-//!
-//! For now, this example shows how to use coroutines used with use_state.
-//!
-//!
-//! ## What is a Coroutine?
-//!
-//! A coroutine is a function that can be paused and resumed. It can be paused internally through "await" or externally
-//! using the `TaskHandle` API. Within a coroutine, you may execute asynchronous code, that modifies values captured when
-//! the coroutine was initiated. `use_state` always returns the same setter, so you don't need to worry about
-
-fn main() {
-    dioxus::desktop::launch(app);
-}
-
-use dioxus::prelude::*;
-
-fn app(cx: Scope) -> Element {
-    let p1 = use_state(&cx, || 0);
-    let p2 = use_state(&cx, || 0);
-
-    let (mut p1_async, mut p2_async) = (p1.for_async(), p2.for_async());
-    let (p1_handle, _) = use_coroutine(cx, || async move {
-        loop {
-            *p1_async.get_mut() += 1;
-            async_std::task::sleep(std::time::Duration::from_millis(75)).await;
-        }
-    });
-    let (p2_handle, _) = use_coroutine(cx, || async move {
-        loop {
-            *p2_async.get_mut() += 1;
-            async_std::task::sleep(std::time::Duration::from_millis(100)).await;
-        }
-    });
-
-    cx.render(rsx! {
-        div {
-            width: "400px", height: "400px", position: "relative", background: "yellow"
-            button { "reset", onclick: move |_| {} }
-            Horsey { pos: *p1, "horsey 1" }
-            Horsey { pos: *p2, "horsey 2" }
-        }
-    })
-}
-
-#[derive(Props)]
-struct HorseyProps<'a> {
-    pos: i32,
-    children: Element<'a>,
-}
-
-fn Horsey<'a>(cx: Scope<'a, HorseyProps<'a>>) -> Element {
-    cx.render(rsx! {
-        div {
-            button { "pause" }
-            div {
-                {&cx.props.children}
-            }
-        }
-    })
-}

+ 1 - 1
examples/crm.rs

@@ -21,10 +21,10 @@ pub struct Client {
 
 fn app(cx: Scope) -> Element {
     let scene = use_state(&cx, || Scene::ClientsList);
+    let clients = use_ref(&cx, || vec![] as Vec<Client>);
     let firstname = use_state(&cx, String::new);
     let lastname = use_state(&cx, String::new);
     let description = use_state(&cx, String::new);
-    let clients = use_ref(&cx, || vec![] as Vec<Client>);
 
     cx.render(rsx!(
         body { 

+ 50 - 29
examples/file_explorer.rs

@@ -11,37 +11,53 @@
 use dioxus::prelude::*;
 
 fn main() {
-    dioxus::desktop::launch_cfg(app, |c| {
-        c.with_window(|w| {
-            w.with_resizable(true)
-                .with_inner_size(dioxus::desktop::tao::dpi::LogicalSize::new(400.0, 800.0))
-        })
-    });
+    dioxus::desktop::launch_cfg(app, |c| c.with_window(|w| w.with_resizable(true)));
 }
 
 fn app(cx: Scope) -> Element {
-    let files = use_ref(&cx, Files::new);
+    let files = use_ref(&cx, || Files::new());
 
-    cx.render(rsx!(
-        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}",
+    rsx!(cx, div {
+        link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", }
+        style { [include_str!("./assets/fileexplorer.css")] }
+        header {
+            i { class: "material-icons icon-menu", "menu" }
+            h1 { "Files: " [files.read().current()] }
+            span { }
+            i { class: "material-icons", onclick: move |_| files.write().go_up(), "logout" }
+        }
+        main {
+            files.read().path_names.iter().enumerate().map(|(dir_id, path)| {
+                let path_end = path.split('/').last().unwrap_or(path.as_str());
+                let icon_type = if path_end.contains(".") {
+                    "description"
+                } else {
+                    "folder"
+                };
+                rsx! (
+                    div {
+                        class: "folder",
+                        key: "{path}",
+                        i { class: "material-icons",
+                            onclick: move |_| files.write().enter_dir(dir_id),
+                            "{icon_type}"
+                            p { class: "cooltip", "0 folders / 0 files" }
+                        }
+                        h1 { "{path_end}" }
+                    }
+                )
+            }),
+            files.read().err.as_ref().map(|err| {
+                rsx! (
+                    div {
+                        code { "{err}" }
+                        button { onclick: move |_| files.write().clear_err(), "x" }
                     }
-                }
-            ))
+                )
+            })
         }
-        files.read().err.as_ref().map(|err| rsx!(
-            div {
-                code { "{err}" }
-                button { onclick: move |_| files.write().clear_err(), "x" }
-            }
-        ))
-    ))
+
+    })
 }
 
 struct Files {
@@ -64,21 +80,26 @@ impl Files {
     }
 
     fn reload_path_list(&mut self) {
-        let paths = match std::fs::read_dir(self.path_stack.last().unwrap()) {
+        let cur_path = self.path_stack.last().unwrap();
+        let paths = match std::fs::read_dir(cur_path) {
             Ok(e) => e,
             Err(err) => {
-                self.err = Some(format!("An error occured: {:?}", err));
+                let err = format!("An error occured: {:?}", err);
+                self.err = Some(err);
                 self.path_stack.pop();
                 return;
             }
         };
+        let collected = paths.collect::<Vec<_>>();
 
         // clear the current state
         self.clear_err();
         self.path_names.clear();
 
-        self.path_names
-            .extend(paths.map(|path| path.unwrap().path().display().to_string()));
+        for path in collected {
+            self.path_names
+                .push(path.unwrap().path().display().to_string());
+        }
     }
 
     fn go_up(&mut self) {

+ 60 - 22
examples/pattern_model.rs

@@ -22,7 +22,6 @@ use dioxus::events::*;
 use dioxus::prelude::*;
 
 fn main() {
-    env_logger::init();
     dioxus::desktop::launch_cfg(app, |cfg| {
         cfg.with_window(|w| {
             w.with_title("Calculator Demo")
@@ -35,38 +34,78 @@ fn main() {
 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();
-
-    cx.render(rsx!{
+    cx.render(rsx! {
+        style { [include_str!("./assets/calculator.css")] }
         div { id: "wrapper",
-            div { 
-                class: "app", 
-                style { [include_str!("./assets/calculator.css")] }
+            div { class: "app",
                 div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt),
-                    div { class: "calculator-display", "{formatted}"}
+                    div { class: "calculator-display", [state.read().formatted_display()]}
                     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(), "%"}
+                                CalculatorKey {
+                                    name: "key-clear",
+                                    onclick: move |_| state.write().clear_display(),
+                                    [if state.read().display_value == "0" { "C" } else { "AC" }]
+                                }
+                                CalculatorKey {
+                                    name: "key-sign",
+                                    onclick: move |_| state.write().toggle_sign(),
+                                    "±"
+                                }
+                                CalculatorKey {
+                                    name: "key-percent",
+                                    onclick: move |_| state.write().toggle_percent(),
+                                    "%"
+                                }
                             }
                             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(), "●" }
+                                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!{
-                                    CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_|  state.write().input_digit(k), "{k}" }
+                                    CalculatorKey {
+                                        key: "{k}",
+                                        name: "key-{k}",
+                                        onclick: move |_| state.write().input_digit(k),
+                                        "{k}"
+                                    }
                                 })
                             }
                         }
                         div { class: "operator-keys",
-                            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), "−" }
-                            CalculatorKey { name:"key-add", onclick: move |_| state.write().set_operator(Operator::Add), "+" }
-                            CalculatorKey { name:"key-equals", onclick: move |_| state.write().perform_operation(), "=" }
+                            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),
+                                "−"
+                            }
+                            CalculatorKey {
+                                name: "key-add",
+                                onclick: move |_| state.write().set_operator(Operator::Add),
+                                "+"
+                            }
+                            CalculatorKey {
+                                name: "key-equals",
+                                onclick: move |_| state.write().perform_operation(),
+                                "="
+                            }
                         }
                     }
                 }
@@ -105,7 +144,6 @@ enum Operator {
     Mul,
     Div,
 }
-
 impl Calculator {
     fn new() -> Self {
         Calculator {

+ 5 - 2
examples/pattern_reducer.rs

@@ -4,10 +4,13 @@
 //! This example shows how to encapsulate state in dioxus components with the reducer pattern.
 //! This pattern is very useful when a single component can handle many types of input that can
 //! be represented by an enum.
+//!
+//! Currently we don't have a reducer pattern hook. If you'd like to add it,
+//! feel free to make a PR.
 
 use dioxus::prelude::*;
+
 fn main() {
-    env_logger::init();
     dioxus::desktop::launch(app);
 }
 
@@ -17,7 +20,7 @@ fn app(cx: Scope) -> Element {
     cx.render(rsx!(
         div {
             h1 {"Select an option"}
-            h3 { "The radio is... " [state.is_playing()], "!" }
+            h3 { "The radio is... " [state.is_playing()] "!" }
             button { onclick: move |_| state.modify().reduce(PlayerAction::Pause),
                 "Pause"
             }

+ 32 - 2
examples/ssr.rs

@@ -1,10 +1,40 @@
+//! Example: SSR
+//!
+//! This example shows how we can render the Dioxus Virtualdom using SSR.
+
+use std::fmt::Write;
+
 use dioxus::prelude::*;
-use dioxus::ssr;
 
 fn main() {
+    // We can render VirtualDoms
     let mut vdom = VirtualDom::new(app);
     let _ = vdom.rebuild();
-    println!("{}", ssr::render_vdom(&vdom));
+    println!("{}", dioxus::ssr::render_vdom(&vdom));
+
+    // Or we can render rsx! calls themselves
+    println!(
+        "{}",
+        dioxus::ssr::render_lazy(rsx! {
+            div {
+                h1 { "Hello, world!" }
+            }
+        })
+    );
+
+    // We can configure the SSR rendering to add ids for rehydration
+    println!(
+        "{}",
+        dioxus::ssr::render_vdom_cfg(&vdom, |c| c.pre_render(true))
+    );
+
+    // We can even render as a writer
+    let mut file = String::new();
+    let _ = file.write_fmt(format_args!(
+        "{}",
+        dioxus::ssr::TextRenderer::from_vdom(&vdom, Default::default())
+    ));
+    println!("{}", file);
 }
 
 fn app(cx: Scope) -> Element {

+ 0 - 26
examples/ssr/basic.rs

@@ -1,26 +0,0 @@
-use dioxus::virtual_dom::VirtualDom;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    let mut dom = VirtualDom::new(app);
-    dom.rebuild();
-    println!(
-        "{}",
-        dioxus_ssr::render_vdom(&dom, |c| c.newline(true).indent(true))
-    )
-}
-
-fn app(cx: Scope) -> Element {
-    cx.render(rsx!(
-        div {
-            class: "overflow-hidden"
-            ul {
-                (0..10).map(|i| rsx!{ li { class: "flex flex-col", "entry: {i}"}})
-            }
-            "hello world!"
-        }
-    ))
-}

+ 0 - 149
examples/ssr/tofile.rs

@@ -1,149 +0,0 @@
-use dioxus::virtual_dom::VirtualDom;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::use_state;
-use dioxus_html as dioxus_elements;
-use dioxus_html::{GlobalAttributes, SvgAttributes};
-use dioxus_ssr::TextRenderer;
-
-fn main() {
-    use std::fs::File;
-    use std::io::Write;
-
-    let mut file = File::create("example.html").unwrap();
-
-    let mut dom = VirtualDom::new(App);
-
-    dom.rebuild();
-
-    file.write_fmt(format_args!(
-        "{}",
-        TextRenderer::from_vdom(&dom, Default::default())
-    ))
-    .unwrap();
-}
-
-pub fn App(cx: Scope) -> Element {
-    cx.render(rsx!(
-        div { class: "overflow-hidden"
-            link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
-            Header {}
-            Entry {}
-            Hero {}
-            Hero {}
-            Hero {}
-            Hero {}
-            Hero {}
-        }
-    ))
-}
-
-pub fn Header(cx: Scope) -> Element {
-    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"
-                        StacksIcon {}
-                        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"}
-                    }
-                    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"
-                        "Button"
-                        RightArrowIcon {}
-                    }
-                }
-            }
-        }
-    })
-}
-
-pub fn Hero(cx: Scope) -> Element {
-    //
-    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"
-                        br { class: "hidden lg:inline-block" }
-                        "Dioxus Sneak Peek"
-                    }
-                    p {
-                        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"
-                        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"
-                            "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" 
-                            "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" 
-                    referrerpolicy:"no-referrer"
-                }
-                }
-            }
-        }
-    })
-}
-
-pub fn Entry(cx: Scope) -> Element {
-    //
-    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"
-                textarea {
-
-                }
-            }
-        }
-    })
-}
-
-pub fn StacksIcon(cx: Scope) -> Element {
-    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"
-            path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
-        }
-    ))
-}
-
-pub fn RightArrowIcon(cx: Scope) -> Element {
-    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"
-            path { d: "M5 12h14M12 5l7 7-7 7"}
-        }
-    ))
-}

+ 72 - 0
examples/suspense.rs

@@ -0,0 +1,72 @@
+//! Suspense in Dioxus
+//!
+//! Currently, `rsx!` does not accept futures as values. To achieve the functionality
+//! of suspense, we need to make a new component that performs its own suspense
+//! handling.
+//!
+//! In this example, we render the `Doggo` component which starts a future that
+//! will cause it to fetch a random dog image from the Dog API. Since the data
+//! is not ready immediately, we render some loading text.
+//!
+//! We can achieve the majoirty of suspense functionality by composing "suspenseful"
+//! primitives in our own custom components.
+
+use dioxus::prelude::*;
+
+fn main() {
+    dioxus::desktop::launch(app)
+}
+
+#[derive(serde::Deserialize)]
+struct DogApi {
+    message: String,
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            h1 {"Dogs are very important"}
+            p {
+                "The dog or domestic dog (Canis familiaris[4][5] or Canis lupus familiaris[5])" 
+                "is a domesticated descendant of the wolf which is characterized by an upturning tail." 
+                "The dog derived from an ancient, extinct wolf,[6][7] and the modern grey wolf is the"
+                "dog's nearest living relative.[8] The dog was the first species to be domesticated,[9][8]"
+                 "by hunter–gatherers over 15,000 years ago,[7] before the development of agriculture.[1]"
+            }
+
+            h3 { "Illustrious Dog Photo" }
+            Doggo { }
+        }
+    })
+}
+
+/// This component will re-render when the future has finished
+/// Suspense is achieved my moving the future into only the component that
+/// actually renders the data.
+fn Doggo(cx: Scope) -> Element {
+    let fut = use_future(&cx, || async move {
+        reqwest::get("https://dog.ceo/api/breeds/image/random/")
+            .await
+            .unwrap()
+            .json::<DogApi>()
+            .await
+    });
+
+    cx.render(match fut.value() {
+        Some(Ok(resp)) => rsx! {
+            button {
+                onclick: move |_| fut.restart(),
+                "Click to fetch another dog"
+            }
+            div {
+                img {
+                    max_width: "500px",
+                    max_height: "500px",
+                    src: "{resp.message}",
+                }
+            }
+        },
+        Some(Err(_)) => rsx! { div { "loading dogs failed" } },
+        None => rsx! { div { "loading dogs..." } },
+    })
+}

+ 47 - 66
examples/tailwind.rs

@@ -1,8 +1,18 @@
+//! Example: Basic Tailwind usage
+//! 
+//! This example shows how an app might be styled with TailwindCSS.
+//! 
+//! To minify your tailwind bundle, currently you need to use npm. Follow these instructions:
+//! 
+//!     https://dev.to/arctic_hen7/how-to-set-up-tailwind-css-with-yew-and-trunk-il9
+
+
 use dioxus::prelude::*;
 
 fn main() {
-    use dioxus::desktop::wry::application::platform::macos::*;
-    dioxus::desktop::launch_cfg(App, |c| {
+    dioxus::desktop::launch_cfg(app, |c| {
+        // we can configure our primary window through the Tauri Config
+        use dioxus::desktop::tao::platform::macos::*;
         c.with_window(|w| {
             w.with_fullsize_content_view(true)
                 .with_titlebar_buttons_hidden(false)
@@ -12,26 +22,9 @@ fn main() {
     });
 }
 
-const STYLE: &str = "body {overflow:hidden;}";
-
-pub fn App(cx: Scope) -> Element {
+pub fn app(cx: Scope) -> Element {
     cx.render(rsx!(
-        div { class: "overflow-hidden",
-        style { "{STYLE}" }
-            link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
-            Header {}
-            Entry {}
-            Hero {}
-            Hero {}
-            Hero {}
-            Hero {}
-            Hero {}
-        }
-    ))
-}
-
-pub fn Header(cx: Scope) -> Element {
-    cx.render(rsx! {
+        link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", rel:"stylesheet" }
         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",
@@ -51,66 +44,53 @@ pub fn Header(cx: Scope) -> Element {
                         RightArrowIcon {}
                     }
                 }
-            }
-        }
-    })
-}
+            }            
 
-pub fn Hero(cx: Scope) -> Element {
-    //
-    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",
-                        br { class: "hidden lg:inline-block" }
-                        "Dioxus Sneak Peek"
-                    }
-                    p {
-                        class: "mb-8 leading-relaxed",
+            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",
 
-                        "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."
+                            "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",
-                        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",
-                            "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",
-                            "Build an app"
+                        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",
+                                "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",
+                                "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", 
+                            src: "https://i.imgur.com/oK6BLtw.png",
+                            referrerpolicy:"no-referrer",
+                            alt: "hero", 
                         }
                     }
-                }
-                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"
-                }
                 }
             }
         }
-    })
+    ))
 }
-pub fn Entry(cx: Scope) -> Element {
-    //
-    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",
-                textarea {
 
-                }
-            }
-        }
-    })
-}
+
 
 pub fn StacksIcon(cx: Scope) -> Element {
     cx.render(rsx!(
         svg {
-            // xmlns: "http://www.w3.org/2000/svg"
             fill: "none",
             stroke: "currentColor",
             stroke_linecap: "round",
@@ -122,6 +102,7 @@ pub fn StacksIcon(cx: Scope) -> Element {
         }
     ))
 }
+
 pub fn RightArrowIcon(cx: Scope) -> Element {
     cx.render(rsx!(
         svg {

+ 3 - 3
examples/tasks.rs

@@ -25,10 +25,10 @@ fn app(cx: Scope) -> Element {
 
     cx.render(rsx! {
         div {
-            h1 { "High-Five counter: {count}" }
+            h1 { "Current count: {count}" }
             button {
-                onclick: move |_| *count.modify() += 1,
-                "Click me!"
+                onclick: move |_| count.set(0),
+                "Reset the count"
             }
         }
     })

+ 75 - 51
examples/todomvc.rs

@@ -1,7 +1,5 @@
-#![allow(non_upper_case_globals, non_snake_case)]
 use dioxus::prelude::*;
-use im_rc::HashMap;
-use std::rc::Rc;
+
 
 fn main() {
     dioxus::desktop::launch(app);
@@ -14,99 +12,125 @@ pub enum FilterState {
     Completed,
 }
 
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Clone)]
 pub struct TodoItem {
     pub id: u32,
     pub checked: bool,
     pub contents: String,
 }
 
-fn app(cx: Scope) -> Element {
-    let draft = use_state(&cx, || "".to_string());
-    let todos = use_state(&cx, || HashMap::<u32, Rc<TodoItem>>::new());
+
+pub fn app(cx: Scope<()>) -> Element {
+    let todos = use_state(&cx, || im_rc::HashMap::<u32, TodoItem>::default());
     let filter = use_state(&cx, || FilterState::All);
+    let draft = use_state(&cx, || "".to_string());
+    let mut todo_id = use_state(&cx, || 0);
 
-    let todolist = todos
+    // Filter the todos based on the filter state
+    let mut filtered_todos = todos
         .iter()
-        .filter(|(_id, item)| match *filter {
+        .filter(|(_, item)| match *filter {
             FilterState::All => true,
             FilterState::Active => !item.checked,
             FilterState::Completed => item.checked,
         })
-        .map(|(id, todo)| {
-            rsx!(TodoEntry {
-                key: "{id}",
-                todo: todo.clone()
-            })
-        })
+        .map(|f| *f.0)
         .collect::<Vec<_>>();
+    filtered_todos.sort_unstable();
 
-    let items_left = todolist.len();
+    let show_clear_completed = todos.values().any(|todo| todo.checked);
+    let items_left = filtered_todos.len();
     let item_text = match items_left {
         1 => "item",
         _ => "items",
     };
 
-    cx.render(rsx!(
-        div { id: "app",
+    cx.render(rsx!{
+        section { class: "todoapp",
             style { [include_str!("./assets/todomvc.css")] }
             div {
-                header {
-                    class: "header",
-                    h1 { "todos" }
+                header { class: "header",
+                    h1 {"todos"}
                     input {
                         class: "new-todo",
                         placeholder: "What needs to be done?",
                         value: "{draft}",
+                        autofocus: "true",
                         oninput: move |evt| draft.set(evt.value.clone()),
+                        onkeydown: move |evt| {
+                            if evt.key == "Enter" {
+                                if !draft.is_empty() {
+                                    todos.modify().insert(
+                                        *todo_id,
+                                        TodoItem {
+                                            id: *todo_id,
+                                            checked: false,
+                                            contents: draft.get().clone(),
+                                        },
+                                    );
+                                    todo_id += 1;
+                                    draft.set("".to_string());
+                                }
+                            }
+                        }
                     }
                 }
-                todolist,
+                ul { class: "todo-list",
+                    filtered_todos.iter().map(|id| rsx!(todo_entry( key: "{id}", id: *id, todos: todos  )))
+                }
                 (!todos.is_empty()).then(|| rsx!(
-                    footer {
-                        span {
-                            strong {"{items_left}"}
-                            span {"{item_text} left"}
+                    footer { class: "footer",
+                        span { class: "todo-count", 
+                            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" }}
+                            li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
+                            li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
+                            li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
                         }
+                        (show_clear_completed).then(|| rsx!(
+                            button { 
+                                class: "clear-completed", 
+                                onclick: move |_| todos.modify().retain(|_, todo| todo.checked == false),
+                                "Clear completed"
+                            }
+                        ))
                     }
                 ))
             }
-            footer { class: "info",
-                p {"Double-click to edit a todo"}
-                p { "Created by ", a {  href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
-                p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
-            }
         }
-    ))
+        footer { class: "info",
+            p {"Double-click to edit a todo"}
+            p { "Created by ", a {  href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
+            p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
+        }
+    })
 }
 
-#[derive(PartialEq, Props)]
-pub struct TodoEntryProps {
-    todo: Rc<TodoItem>,
+#[derive(Props)]
+pub struct TodoEntryProps<'a> {
+    todos: UseState<'a, im_rc::HashMap<u32, TodoItem>>,
+    id: u32,
 }
 
-pub fn TodoEntry(cx: Scope<TodoEntryProps>) -> Element {
+pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
+    let todo = &cx.props.todos[&cx.props.id];
     let is_editing = use_state(&cx, || false);
-    let contents = use_state(&cx, || String::from(""));
-    let todo = &cx.props.todo;
+    let completed = if todo.checked { "completed" } else { "" };
 
-    cx.render(rsx! {
-        li {
-            "{todo.id}"
-            input {
-                class: "toggle",
-                r#type: "checkbox",
-                "{todo.checked}"
+    rsx!(cx, li { class: "{completed}",
+        div { class: "view",
+            input { class: "toggle", r#type: "checkbox", id: "cbg-{todo.id}", checked: "{todo.checked}",
+                onchange: move |evt| {
+                    cx.props.todos.modify()[&cx.props.id].checked = evt.value.parse().unwrap();
+                }
             }
-           is_editing.then(|| rsx!{
+            label { r#for: "cbg-{todo.id}", pointer_events: "none", "{todo.contents}" }
+            is_editing.then(|| rsx!{
                 input {
-                    value: "{contents}",
-                    oninput: move |evt| contents.set(evt.value.clone())
+                    value: "{todo.contents}",
+                    oninput: move |evt| cx.props.todos.modify()[&cx.props.id].contents = evt.value.clone(),
                 }
             })
         }

+ 0 - 67
examples/weather_app.rs

@@ -1,67 +0,0 @@
-//! Example: Weather App
-//! --------------------
-//!
-//!
-//!
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus::desktop::launch(app);
-}
-
-const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather";
-
-fn app(cx: Scope) -> Element {
-    //
-    let body = use_suspense(
-        &cx,
-        || async {
-            todo!()
-            // let content = reqwest::get(ENDPOINT)
-            //     .await
-            //     .unwrap()
-            //     .json::<serde_json::Value>()
-            //     .await
-            //     .unwrap();
-        },
-        |props| {
-            //
-            cx.render(rsx!(WeatherDisplay {}))
-        },
-    );
-
-    cx.render(rsx! {
-        div {
-            {body}
-        }
-    })
-}
-
-#[derive(PartialEq, Props)]
-struct WeatherProps {}
-
-fn WeatherDisplay(cx: Scope<WeatherProps>) -> Element {
-    cx.render(rsx!(
-        div { class: "flex items-center justify-center flex-col",
-            div { class: "flex items-center justify-center",
-                div { class: "flex flex-col bg-white rounded p-4 w-full max-w-xs",
-                    div{ class: "font-bold text-xl",
-                        "Jon's awesome site!!"
-                    }
-                    div{ class: "text-sm text-gray-500",
-                        "He worked so hard on it :)"
-                    }
-                    div { class: "flex flex-row items-center justify-center mt-6",
-                        div { class: "font-medium text-6xl",
-                            "1337"
-                        }
-                    }
-                    div { class: "flex flex-row justify-between mt-6",
-                        "Legit made my own React"
-                    }
-                }
-            }
-        }
-    ))
-}

+ 0 - 53
examples/web/async_web.rs

@@ -1,53 +0,0 @@
-//! Basic example that renders a simple VNode to the browser.
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    console_error_panic_hook::set_once();
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    dioxus_web::launch(APP)
-}
-
-#[derive(serde::Deserialize)]
-struct DogApi {
-    message: String,
-}
-
-static APP: Component = |(cx, _props)| {
-    let state = use_state(&cx, || 0);
-
-    const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/";
-    let doggo = use_suspense(
-        cx,
-        || async { reqwest::get(ENDPOINT).await.unwrap().json::<DogApi>().await },
-        |cx, res| match res {
-            Ok(res) => rsx!(
-                cx,
-                img {
-                    src: "{res.message}"
-                }
-            ),
-            Err(_err) => rsx!(cx, div { "No doggos for you :(" }),
-        },
-    );
-
-    rsx!(cx, div { align_items: "center"
-        section { class: "py-12 px-4 text-center"
-            div { class: "w-full max-w-2xl mx-auto"
-                span { class: "text-sm font-semibold"
-                    "count: {state}"
-                }
-                div {
-                    button { onclick: move |_| state.set(state + 1), "incr" }
-                    button { onclick: move |_| state.set(state - 1), "decr" }
-                }
-                div {
-                    {doggo}
-                }
-            }
-        }
-    })
-};

+ 0 - 89
examples/web/basic.rs

@@ -1,89 +0,0 @@
-//! Basic example that renders a simple VNode to the browser.
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-fn main() {
-    // Setup logging
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    console_error_panic_hook::set_once();
-
-    // Run the app
-    dioxus_web::launch(APP)
-}
-
-static APP: Component = |cx| {
-    let mut count = use_state(&cx, || 3);
-    let content = use_state(&cx, || String::from("h1"));
-    let text_content = use_state(&cx, || String::from("Hello, world!"));
-
-    cx.render(rsx! {
-        div {
-            h1 { "content val is {content}" }
-
-            input {
-                r#type: "text",
-                value: "{text_content}"
-                oninput: move |e| text_content.set(e.value.clone())
-            }
-
-            {(0..10).map(|_| {
-                rsx!(
-                    button {
-                        onclick: move |_| count += 1,
-                        "Click to add."
-                        "Current count: {count}"
-                    }
-                    br {}
-                )
-            })}
-
-            select {
-                name: "cars"
-                id: "cars"
-                value: "{content}"
-                oninput: move |ev| {
-                    content.set(ev.value.clone());
-                    match ev.value.as_str() {
-                        "h1" => count.set(0),
-                        "h2" => count.set(5),
-                        "h3" => count.set(10),
-                        _ => {}
-                    }
-                },
-
-                option { value: "h1", "h1" }
-                option { value: "h2", "h2" }
-                option { value: "h3", "h3" }
-            }
-
-            {render_list(cx, *count)}
-
-            {render_bullets(cx)}
-
-            CHILD {}
-        }
-    })
-};
-
-fn render_bullets(cx: Scope) -> Element {
-    rsx!(cx, div {
-        "bite me"
-    })
-}
-
-fn render_list(cx: Scope, count: usize) -> Element {
-    let items = (0..count).map(|f| {
-        rsx! {
-            li { "a - {f}" }
-            li { "b - {f}" }
-            li { "c - {f}" }
-        }
-    });
-
-    rsx!(cx, ul { {items} })
-}
-
-static CHILD: Component = |cx, _| rsx!(cx, div {"hello child"});

+ 0 - 55
examples/web/blah.rs

@@ -1,55 +0,0 @@
-//! Basic example that renders a simple VNode to the browser.
-
-use dioxus::events::on::MouseEvent;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-use std::future::Future;
-
-use std::{pin::Pin, time::Duration};
-
-use dioxus::prelude::*;
-
-use dioxus_web::*;
-
-fn main() {
-    // Setup logging
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    console_error_panic_hook::set_once();
-
-    // Run the app
-    dioxus_web::launch(App)
-}
-
-static App: Component = |cx| {
-    let mut state = use_state(&cx, || 0);
-    cx.render(rsx! {
-        div {
-            style: {
-                align_items: "center"
-            }
-            section { class: "py-12 px-4 text-center"
-                div { class: "w-full max-w-2xl mx-auto"
-                    span { class: "text-sm font-semibold"
-                        "static subtree"
-                    }
-                }
-            }
-            section { class: "py-12 px-4 text-center"
-                div { class: "w-full max-w-2xl mx-auto"
-                    span { class: "text-sm font-semibold"
-                        "dynamic subtree {state}"
-                    }
-                    div {
-                        button { onclick: move |e| state+=1, "incr" }
-                        br {}
-                        button { onclick: move |e| state-=1, "decr" }
-                    }
-                }
-            }
-        }
-    })
-};

+ 0 - 69
examples/web/btns.rs

@@ -1,69 +0,0 @@
-//! Example: Webview Renderer
-//! -------------------------
-//!
-//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
-//!
-//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuit application running
-//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
-//! into the native VDom instance.
-//!
-//! Currently, NodeRefs won't work properly, but all other event functionality will.
-
-use dioxus::prelude::*;
-use dioxus_core as dioxus;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-
-// #[cfg]
-fn main() {
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    dioxus_web::launch(App);
-    // env_logger::init();
-    // dioxus::web::launch(App);
-}
-
-static App: Component = |cx| {
-    dbg!("rednering parent");
-    cx.render(rsx! {
-        div {
-            But {
-                h1 {"he"}
-            }
-            // But {
-            //     h1 {"llo"}
-            // }
-            // But {
-            //     h1 {"world"}
-            // }
-        }
-    })
-};
-
-static But: Component = |cx| {
-    let mut count = use_state(&cx, || 0);
-
-    // let d = Dropper { name: "asd" };
-    // let handler = move |_| {
-    //     dbg!(d.name);
-    // };
-
-    cx.render(rsx! {
-        div {
-            h1 { "Hifive counter: {count}" }
-            {cx.children()}
-            button { onclick: move |_| count += 1, "Up high!" }
-            button { onclick: move |_| count -= 1, "Down low!" }
-            // button { onclick: {handler}, "Down low!" }
-        }
-    })
-};
-
-// struct Dropper {
-//     name: &'static str,
-// }
-// impl Drop for Dropper {
-//     fn drop(&mut self) {
-//         dbg!("dropped");
-//     }
-// }

+ 0 - 116
examples/web/crm2.rs

@@ -1,116 +0,0 @@
-/*
-Tiny CRM: A port of the Yew CRM example to Dioxus.
-*/
-
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-fn main() {
-    // Setup logging
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    console_error_panic_hook::set_once();
-
-    dioxus_web::launch(App)
-}
-
-enum Scene {
-    ClientsList,
-    NewClientForm,
-    Settings,
-}
-
-#[derive(Clone, Debug, Default)]
-pub struct Client {
-    pub first_name: String,
-    pub last_name: String,
-    pub description: String,
-}
-
-static App: Component = |cx| {
-    let scene = use_state(&cx, || Scene::ClientsList);
-    let clients = use_ref(&cx, || vec![] as Vec<Client>);
-
-    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 |evt| firstname.set(evt.value.clone())
-                    }
-                    input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
-                        oninput: move |evt| lastname.set(evt.value.clone())
-                    }
-                    textarea { class: "new-client description" placeholder: "Description" value: "{description}"
-                        oninput: move |evt| description.set(evt.value.clone())
-                    }
-                }
-                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();
-                        scene.set(Scene::ClientsList);
-                    },
-                    "Remove all clients"
-                }
-                button {
-                    class: "pure-button pure-button-primary"
-                    onclick: move |_| scene.set(Scene::ClientsList),
-                    "Go Back"
-                }
-            })
-        }
-    };
-
-    rsx!(cx, 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}
-    })
-};

+ 0 - 147
examples/web/demo.rs

@@ -1,147 +0,0 @@
-//! Basic example that renders a simple VNode to the browser.
-
-use dioxus::events::on::MouseEvent;
-use dioxus_core as dioxus;
-use dioxus_core::prelude::*;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-use std::future::Future;
-
-use std::{pin::Pin, time::Duration};
-
-use dioxus::prelude::*;
-use dioxus_html::SvgAttributes;
-
-use dioxus_web::*;
-
-fn main() {
-    // Setup logging
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    console_error_panic_hook::set_once();
-
-    // Run the app
-    dioxus_web::launch(App)
-}
-
-pub static App: Component = |cx| {
-    cx.render(rsx!(
-        div { class: "overflow-hidden"
-            link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
-            Header {}
-            // Entry {}
-            Hero {}
-            Hero {}
-            Hero {}
-            Hero {}
-            Hero {}
-        }
-    ))
-};
-
-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"
-                        StacksIcon {}
-                        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"}
-                    }
-                    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"
-                        "Button"
-                        RightArrowIcon {}
-                    }
-                }
-            }
-        }
-    })
-};
-
-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"
-                        br { class: "hidden lg:inline-block" }
-                        "Dioxus Sneak Peek"
-                    }
-                    p {
-                        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"
-                        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"
-                            "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" 
-                            "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" 
-                    referrerpolicy:"no-referrer"
-                }
-                }
-            }
-        }
-    })
-};
-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"
-                textarea {
-
-                }
-            }
-        }
-    })
-};
-
-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"
-            viewBox: "0 0 24 24"
-            path { d: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"}
-        }
-    ))
-};
-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"
-            viewBox: "0 0 24 24"
-            path { d: "M5 12h14M12 5l7 7-7 7"}
-        }
-    ))
-};

+ 0 - 127
examples/web_tick.rs

@@ -1,127 +0,0 @@
-#![allow(non_upper_case_globals, non_snake_case)]
-//! Example: Webview Renderer
-//! -------------------------
-//!
-//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
-//!
-//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuilt application running
-//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
-//! into the native VDom instance.
-//!
-//! Currently, NodeRefs won't work properly, but all other event functionality will.
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus::desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let mut rng = SmallRng::from_entropy();
-
-    cx.render(rsx! {
-        table {
-            tbody {
-                (0..1_000).map(|f| {
-                    let label = Label::new(&mut rng);
-                    rsx! (Row { row_id: f, label: label })
-                })
-            }
-        }
-    })
-}
-
-#[derive(PartialEq, Props)]
-struct RowProps {
-    row_id: usize,
-    label: Label,
-}
-fn Row(cx: Scope<RowProps>) -> Element {
-    let [adj, col, noun] = cx.props.label.0;
-    cx.render(rsx! {
-        tr {
-            td { class:"col-md-1", "{cx.props.row_id}" }
-            td { class:"col-md-1", onclick: move |_| { /* run onselect */ },
-                a { class: "lbl", "{adj}" "{col}" "{noun}" }
-            }
-            td { class: "col-md-1",
-                a { class: "remove", onclick: move |_| {/* remove */},
-                    span { class: "glyphicon glyphicon-remove remove", aria_hidden: "true" }
-                }
-            }
-            td { class: "col-md-6" }
-        }
-    })
-}
-use rand::prelude::*;
-
-#[derive(PartialEq)]
-struct Label([&'static str; 3]);
-
-impl Label {
-    fn new(rng: &mut SmallRng) -> Self {
-        Label([
-            ADJECTIVES.choose(rng).unwrap(),
-            COLOURS.choose(rng).unwrap(),
-            NOUNS.choose(rng).unwrap(),
-        ])
-    }
-}
-
-static ADJECTIVES: &[&str] = &[
-    "pretty",
-    "large",
-    "big",
-    "small",
-    "tall",
-    "short",
-    "long",
-    "handsome",
-    "plain",
-    "quaint",
-    "clean",
-    "elegant",
-    "easy",
-    "angry",
-    "crazy",
-    "helpful",
-    "mushy",
-    "odd",
-    "unsightly",
-    "adorable",
-    "important",
-    "inexpensive",
-    "cheap",
-    "expensive",
-    "fancy",
-];
-
-static COLOURS: &[&str] = &[
-    "red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
-    "orange",
-];
-
-static NOUNS: &[&str] = &[
-    "table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
-    "pizza", "mouse", "keyboard",
-];
-
-#[cfg(target_arch = "wasm32")]
-fn intern_strings() {
-    for adj in ADJECTIVES {
-        wasm_bindgen::intern(adj);
-    }
-    for col in COLOURS {
-        wasm_bindgen::intern(col);
-    }
-    for no in NOUNS {
-        wasm_bindgen::intern(no);
-    }
-    wasm_bindgen::intern("col-md-1");
-    wasm_bindgen::intern("col-md-6");
-    wasm_bindgen::intern("glyphicon glyphicon-remove remove");
-    wasm_bindgen::intern("remove");
-    wasm_bindgen::intern("dioxus");
-    wasm_bindgen::intern("lbl");
-    wasm_bindgen::intern("true");
-}

+ 0 - 28
examples/webview_web.rs

@@ -1,28 +0,0 @@
-//! Example: Webview Renderer
-//! -------------------------
-//!
-//! This example shows how to use the dioxus_desktop crate to build a basic desktop application.
-//!
-//! Under the hood, the dioxus_desktop crate bridges a native Dioxus VirtualDom with a custom prebuilt application running
-//! in the webview runtime. Custom handlers are provided for the webview instance to consume patches and emit user events
-//! into the native VDom instance.
-//!
-//! Currently, NodeRefs won't work properly, but all other event functionality will.
-
-use dioxus::prelude::*;
-
-fn main() {
-    dioxus::desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let mut count = use_state(&cx, || 0);
-
-    cx.render(rsx! {
-        div {
-            h1 { "Hifive counter: {count}" }
-            button { onclick: move |_| count += 1, "Up high!" }
-            button { onclick: move |_| count -= 1, "Down low!" }
-        }
-    })
-}

+ 4 - 1
examples/xss_safety.rs

@@ -9,10 +9,13 @@ fn main() {
 }
 
 fn app(cx: Scope) -> Element {
-    let contents = use_state(&cx, || String::from("<script>alert(123)</script>"));
+    let contents = use_state(&cx, || {
+        String::from("<script>alert(\"hello world\")</script>")
+    });
 
     cx.render(rsx! {
         div {
+            h1 {"Dioxus is XSS-Safe"}
             h3 { "{contents}" }
             input {
                 value: "{contents}",

+ 3 - 3
packages/desktop/src/index.html

@@ -5,7 +5,6 @@
 
 <head>
     <meta name="viewport" content="width=device-width, initial-scale=1.0" charset="utf-8" />
-    <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0 user-scalable=no" charset="utf-8" /> -->
 </head>
 
 
@@ -13,6 +12,7 @@
     <div id="main">
     </div>
 </body>
-<script type="text/javascript" src="index.js"> </script>
+<script type="text/javascript" src="index.js">
+</script>
 
-</html>
+</html>

+ 1 - 1
packages/desktop/src/lib.rs

@@ -191,7 +191,7 @@ impl DesktopController {
             // create `first_menu`
             let mut first_menu = MenuBar::new();
 
-            first_menu.add_native_item(MenuItem::About("Todos".to_string()));
+            first_menu.add_native_item(MenuItem::About("App".to_string()));
             first_menu.add_native_item(MenuItem::Services);
             first_menu.add_native_item(MenuItem::Separator);
             first_menu.add_native_item(MenuItem::Hide);

+ 52 - 31
packages/hooks/src/usefuture.rs

@@ -3,27 +3,16 @@ use std::{cell::Cell, future::Future, rc::Rc};
 
 pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
     cx: &'a ScopeState,
-    f: impl FnOnce() -> F,
-) -> (Option<&T>, FutureHandle<'a, T>) {
-    let state = cx.use_hook(|_| {
+    new_fut: impl FnOnce() -> F,
+) -> &'a UseFuture<T> {
+    let state = cx.use_hook(move |_| {
         //
-        let fut = f();
-        let slot = Rc::new(Cell::new(None));
-        let updater = cx.schedule_update();
-
-        let _slot = slot.clone();
-        let new_fut = async move {
-            let res = fut.await;
-            _slot.set(Some(res));
-            updater();
-        };
-        let task = cx.push_future(new_fut);
-
-        UseFutureInner {
-            needs_regen: true,
-            slot,
+        UseFuture {
+            update: cx.schedule_update(),
+            needs_regen: Cell::new(true),
+            slot: Rc::new(Cell::new(None)),
             value: None,
-            task: Some(task),
+            task: None,
         }
     });
 
@@ -32,23 +21,55 @@ pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
         state.task = None;
     }
 
-    (
-        state.value.as_ref(),
-        FutureHandle {
-            cx,
-            value: Cell::new(None),
-        },
-    )
+    if state.needs_regen.get() {
+        // We don't need regen anymore
+        state.needs_regen.set(false);
+
+        // Create the new future
+        let fut = new_fut();
+
+        // Clone in our cells
+        let slot = state.slot.clone();
+        let updater = state.update.clone();
+
+        state.task = Some(cx.push_future(async move {
+            let res = fut.await;
+            slot.set(Some(res));
+            updater();
+        }));
+    }
+
+    state
 }
 
-struct UseFutureInner<T> {
-    needs_regen: bool,
+pub struct UseFuture<T> {
+    update: Rc<dyn Fn()>,
+    needs_regen: Cell<bool>,
     value: Option<T>,
     slot: Rc<Cell<Option<T>>>,
     task: Option<TaskId>,
 }
 
-pub struct FutureHandle<'a, T> {
-    cx: &'a ScopeState,
-    value: Cell<Option<T>>,
+impl<T> UseFuture<T> {
+    pub fn restart(&self) {
+        self.needs_regen.set(true);
+        (self.update)();
+    }
+
+    // clears the value in the future slot without starting the future over
+    pub fn clear(&self) -> Option<T> {
+        (self.update)();
+        self.slot.replace(None)
+    }
+
+    // Manually set the value in the future slot without starting the future over
+    pub fn set(&self, new_value: T) {
+        self.slot.set(Some(new_value));
+        self.needs_regen.set(true);
+        (self.update)();
+    }
+
+    pub fn value(&self) -> Option<&T> {
+        self.value.as_ref()
+    }
 }

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

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

+ 1 - 1
packages/ssr/src/lib.rs

@@ -194,7 +194,7 @@ impl<'a> TextRenderer<'a, '_> {
                 // when the page is loaded, the `querySelectorAll` will be used to collect all the nodes, and then add
                 // them interpreter's stack
                 if let (true, Some(id)) = (self.cfg.pre_render, node.try_mounted_id()) {
-                    write!(f, " dio_el=\"{}\"", id)?;
+                    write!(f, " dioxus-id=\"{}\"", id)?;
 
                     for _listener in el.listeners {
                         // todo: write the listeners

+ 2 - 2
packages/web/src/dom.rs

@@ -55,13 +55,13 @@ impl WebsysDom {
         // re-hydrate the page - only supports one virtualdom per page
         if cfg.hydrate {
             // Load all the elements into the arena
-            let node_list: NodeList = document.query_selector_all("dio_el").unwrap();
+            let node_list: NodeList = document.query_selector_all("dioxus-id").unwrap();
             let len = node_list.length() as usize;
 
             for x in 0..len {
                 let node: Node = node_list.get(x as u32).unwrap();
                 let el: &Element = node.dyn_ref::<Element>().unwrap();
-                let id: String = el.get_attribute("dio_el").unwrap();
+                let id: String = el.get_attribute("dioxus-id").unwrap();
                 let id = id.parse::<usize>().unwrap();
                 nodes[id] = Some(node);
             }