feat: overhaul examples and clean things up
This commit is contained in:
1 parent
601632fc7e
commit
420a30e5d4
33 files changed
+1089
-1417
No files matched your search
+2
-18
@@ -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"
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
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"]
|
||||
reqwest = { version = "0.11.8", features = ["json"] }
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
+121
-132
@@ -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))
|
||||
}
|
||||
},
|
||||
"±"
|
||||
}
|
||||
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: "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}"
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
+51
-30
@@ -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" }
|
||||
}
|
||||
files.read().err.as_ref().map(|err| rsx!(
|
||||
div {
|
||||
code { "{err}" }
|
||||
button { onclick: move |_| files.write().clear_err(), "x" }
|
||||
}
|
||||
))
|
||||
))
|
||||
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" }
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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!"
|
||||
}
|
||||
))
|
||||
}
|
||||
@@ -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"}
|
||||
}
|
||||
))
|
||||
}
|
||||
@@ -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..." } },
|
||||
})
|
||||
}
|
||||
+49
-68
@@ -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",
|
||||
|
||||
"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"
|
||||
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"
|
||||
}
|
||||
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"
|
||||
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",
|
||||
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
@@ -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"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
+76
-52
@@ -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());
|
||||
let filter = use_state(&cx, || FilterState::All);
|
||||
|
||||
let todolist = todos
|
||||
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);
|
||||
|
||||
// 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(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
@@ -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}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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"});
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -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");
|
||||
// }
|
||||
// }
|
||||
@@ -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}
|
||||
})
|
||||
};
|
||||
@@ -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"}
|
||||
}
|
||||
))
|
||||
};
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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!" }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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}",
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user