polish: examples
This commit is contained in:
1 parent
4b6ca05f2c
commit
1a2f91ed91
19 files changed
+327
-183
No files matched your search
+3
-1
@@ -51,8 +51,10 @@ argh = "0.1.5"
|
|||||||
env_logger = "*"
|
env_logger = "*"
|
||||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||||
surf = { version = "2.2.0", git = "https://github.com/jkelleyrtp/surf/", branch = "jk/fix-the-wasm" }
|
|
||||||
gloo-timers = "0.2.1"
|
gloo-timers = "0.2.1"
|
||||||
|
# surf = { version = "2.3.1", default-features = false, features = [
|
||||||
|
# "wasm-client",
|
||||||
|
# ] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||||
gloo-timers = "0.2.1"
|
gloo-timers = "0.2.1"
|
||||||
|
|||||||
+26
-24
@@ -1,41 +1,43 @@
|
|||||||
//! Example: README.md showcase
|
/*
|
||||||
//!
|
This example shows how to use async and loops to implement a coroutine in a component. Coroutines can be controlled via
|
||||||
//! The example from the README.md
|
the `TaskHandle` object.
|
||||||
|
*/
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use gloo_timers::future::TimeoutFuture;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::desktop::launch(App, |c| c).expect("faield to launch");
|
dioxus::desktop::launch(App, |c| c).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static App: FC<()> = |cx, props| {
|
pub static App: FC<()> = |cx, _| {
|
||||||
let count = use_state(cx, || 0);
|
let count = use_state(cx, || 0);
|
||||||
let mut direction = use_state(cx, || 1);
|
let mut direction = use_state(cx, || 1);
|
||||||
|
|
||||||
let (async_count, dir) = (count.for_async(), *direction);
|
let (async_count, dir) = (count.for_async(), *direction);
|
||||||
let (task, _result) = use_task(cx, move || async move {
|
|
||||||
|
let (task, _) = use_task(cx, move || async move {
|
||||||
loop {
|
loop {
|
||||||
gloo_timers::future::TimeoutFuture::new(250).await;
|
TimeoutFuture::new(250).await;
|
||||||
*async_count.get_mut() += dir;
|
*async_count.get_mut() += dir;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx!(cx, div {
|
||||||
div {
|
h1 {"count is {count}"}
|
||||||
h1 {"count is {count}"}
|
button {
|
||||||
button {
|
"Stop counting"
|
||||||
"Stop counting"
|
onclick: move |_| task.stop()
|
||||||
onclick: move |_| task.stop()
|
}
|
||||||
}
|
button {
|
||||||
button {
|
"Start counting"
|
||||||
"Start counting"
|
onclick: move |_| task.resume()
|
||||||
onclick: move |_| task.resume()
|
}
|
||||||
}
|
button {
|
||||||
button {
|
"Switch counting direcion"
|
||||||
"Switch counting direcion"
|
onclick: move |_| {
|
||||||
onclick: move |_| {
|
direction *= -1;
|
||||||
direction *= -1;
|
task.restart();
|
||||||
task.restart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
+15
-17
@@ -1,20 +1,18 @@
|
|||||||
#![allow(non_snake_case)]
|
/*
|
||||||
//! Example: Extremely nested borrowing
|
Dioxus manages borrow lifetimes for you. This means any child may borrow from its parent. However, it is not possible
|
||||||
//! -----------------------------------
|
to hand out an &mut T to children - all props are consumed by &P, so you'd only get an &&mut T.
|
||||||
//!
|
|
||||||
//! Dioxus manages borrow lifetimes for you. This means any child may borrow from its parent. However, it is not possible
|
How does it work?
|
||||||
//! to hand out an &mut T to children - all props are consumed by &P, so you'd only get an &&mut T.
|
|
||||||
//!
|
Dioxus will manually drop closures and props - things that borrow data before the component is ran again. This is done
|
||||||
//! How does it work?
|
"bottom up" from the lowest child all the way to the initiating parent. As it traverses each listener and prop, the
|
||||||
//!
|
drop implementation is manually called, freeing any memory and ensuring that memory is not leaked.
|
||||||
//! Dioxus will manually drop closures and props - things that borrow data before the component is ran again. This is done
|
|
||||||
//! "bottom up" from the lowest child all the way to the initiating parent. As it traverses each listener and prop, the
|
We cannot drop from the parent to the children - if the drop implementation modifies the data, downstream references
|
||||||
//! drop implementation is manually called, freeing any memory and ensuring that memory is not leaked.
|
might be broken since we take an &mut T and and &T to the data. Instead, we work bottom up, making sure to remove any
|
||||||
//!
|
potential references to the data before finally giving out an &mut T. This prevents us from mutably aliasing the data,
|
||||||
//! We cannot drop from the parent to the children - if the drop implementation modifies the data, downstream references
|
and is proven to be safe with MIRI.
|
||||||
//! might be broken since we take an &mut T and and &T to the data. Instead, we work bottom up, making sure to remove any
|
*/
|
||||||
//! potential references to the data before finally giving out an &mut T. This prevents us from mutably aliasing the data,
|
|
||||||
//! and is proven to be safe with MIRI.
|
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
|||||||
+54
-72
@@ -1,25 +1,19 @@
|
|||||||
//! Example: Calculator
|
/*
|
||||||
//! -------------------------
|
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.
|
||||||
fn main() {
|
*/
|
||||||
env_logger::init();
|
|
||||||
dioxus::desktop::launch(App, |cfg| cfg).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
use dioxus::events::on::*;
|
use dioxus::events::on::*;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
enum Operator {
|
fn main() {
|
||||||
Add,
|
dioxus::desktop::launch(APP, |cfg| cfg).unwrap();
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const App: FC<()> = |cx, props| {
|
const APP: FC<()> = |cx, _| {
|
||||||
let cur_val = use_state(cx, || 0.0_f64);
|
let cur_val = use_state(cx, || 0.0_f64);
|
||||||
let operator = use_state(cx, || None as Option<Operator>);
|
let operator = use_state(cx, || None);
|
||||||
let display_value = use_state(cx, || "".to_string());
|
let display_value = use_state(cx, || String::from(""));
|
||||||
|
|
||||||
let clear_display = display_value == "0";
|
let clear_display = display_value == "0";
|
||||||
let clear_text = if clear_display { "C" } else { "AC" };
|
let clear_text = if clear_display { "C" } else { "AC" };
|
||||||
@@ -62,11 +56,10 @@ const App: FC<()> = |cx, props| {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let keydownhandler = move |evt: KeyboardEvent| match evt.key_code() {
|
let keydownhandler = move |evt: KeyboardEvent| match evt.key_code() {
|
||||||
KeyCode::Backspace => {
|
KeyCode::Add => operator.set(Some(Operator::Add)),
|
||||||
if !display_value.as_str().eq("0") {
|
KeyCode::Subtract => operator.set(Some(Operator::Sub)),
|
||||||
display_value.modify().pop();
|
KeyCode::Divide => operator.set(Some(Operator::Div)),
|
||||||
}
|
KeyCode::Multiply => operator.set(Some(Operator::Mul)),
|
||||||
}
|
|
||||||
KeyCode::Num0 => input_digit(0),
|
KeyCode::Num0 => input_digit(0),
|
||||||
KeyCode::Num1 => input_digit(1),
|
KeyCode::Num1 => input_digit(1),
|
||||||
KeyCode::Num2 => input_digit(2),
|
KeyCode::Num2 => input_digit(2),
|
||||||
@@ -77,73 +70,62 @@ const App: FC<()> = |cx, props| {
|
|||||||
KeyCode::Num7 => input_digit(7),
|
KeyCode::Num7 => input_digit(7),
|
||||||
KeyCode::Num8 => input_digit(8),
|
KeyCode::Num8 => input_digit(8),
|
||||||
KeyCode::Num9 => input_digit(9),
|
KeyCode::Num9 => input_digit(9),
|
||||||
KeyCode::Add => operator.set(Some(Operator::Add)),
|
KeyCode::Backspace => {
|
||||||
KeyCode::Subtract => operator.set(Some(Operator::Sub)),
|
if !display_value.as_str().eq("0") {
|
||||||
KeyCode::Divide => operator.set(Some(Operator::Div)),
|
display_value.modify().pop();
|
||||||
KeyCode::Multiply => operator.set(Some(Operator::Mul)),
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
cx.render(rsx! {
|
use separator::Separatable;
|
||||||
div { class: "calculator", onkeydown: {keydownhandler}
|
let formatted_display = cur_val.separated_string();
|
||||||
div { class: "input-keys"
|
|
||||||
div { class: "function-keys"
|
|
||||||
CalculatorKey { name: "key-clear", onclick: {clear_key} "{clear_text}" }
|
|
||||||
CalculatorKey { name: "key-sign", onclick: {toggle_sign}, "±"}
|
|
||||||
CalculatorKey { name: "key-percent", onclick: {toggle_percent} "%"}
|
|
||||||
}
|
|
||||||
div { class: "digit-keys"
|
|
||||||
CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" }
|
|
||||||
CalculatorKey { name: "key-dot", onclick: move |_| input_dot(), "●" }
|
|
||||||
|
|
||||||
{(1..9).map(|k| rsx!{
|
rsx!(cx, div {
|
||||||
CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" }
|
class: "calculator", onkeydown: {keydownhandler}
|
||||||
})}
|
div { class: "calculator-display", "{formatted_display}" }
|
||||||
}
|
div { class: "input-keys"
|
||||||
div { class: "operator-keys"
|
div { class: "function-keys"
|
||||||
CalculatorKey { name: "key-divide", onclick: move |_| operator.set(Some(Operator::Div)) "÷" }
|
CalculatorKey { name: "key-clear", onclick: {clear_key} "{clear_text}" }
|
||||||
CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some(Operator::Mul)) "×" }
|
CalculatorKey { name: "key-sign", onclick: {toggle_sign}, "±"}
|
||||||
CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some(Operator::Sub)) "−" }
|
CalculatorKey { name: "key-percent", onclick: {toggle_percent} "%"}
|
||||||
CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some(Operator::Add)) "+" }
|
}
|
||||||
CalculatorKey { name: "key-equals", onclick: move |_| perform_operation() "=" }
|
div { class: "digit-keys"
|
||||||
}
|
CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" }
|
||||||
|
CalculatorKey { name: "key-dot", onclick: move |_| input_dot(), "●" }
|
||||||
|
|
||||||
|
{(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(Operator::Div)) "÷" }
|
||||||
|
CalculatorKey { name: "key-multiply", onclick: move |_| operator.set(Some(Operator::Mul)) "×" }
|
||||||
|
CalculatorKey { name: "key-subtract", onclick: move |_| operator.set(Some(Operator::Sub)) "−" }
|
||||||
|
CalculatorKey { name: "key-add", onclick: move |_| operator.set(Some(Operator::Add)) "+" }
|
||||||
|
CalculatorKey { name: "key-equals", onclick: move |_| perform_operation() "=" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Operator {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
struct CalculatorKeyProps<'a> {
|
struct CalculatorKeyProps<'a> {
|
||||||
/// Name!
|
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
|
||||||
/// Click!
|
|
||||||
onclick: &'a dyn Fn(MouseEvent),
|
onclick: &'a dyn Fn(MouseEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn CalculatorKey<'a>(cx: Context<'a>, props: &'a CalculatorKeyProps) -> DomTree<'a> {
|
fn CalculatorKey<'a>(cx: Context<'a>, props: &'a CalculatorKeyProps) -> DomTree<'a> {
|
||||||
cx.render(rsx! {
|
rsx!(cx, button {
|
||||||
button {
|
class: "calculator-key {props.name}"
|
||||||
class: "calculator-key {props.name}"
|
onclick: {props.onclick}
|
||||||
onclick: {props.onclick}
|
{cx.children()}
|
||||||
{cx.children()}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Props, PartialEq)]
|
|
||||||
struct CalculatorDisplayProps {
|
|
||||||
val: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn CalculatorDisplay<'a>(cx: Context<'a>, props: &CalculatorDisplayProps) -> DomTree<'a> {
|
|
||||||
use separator::Separatable;
|
|
||||||
// Todo, add float support to the num-format crate
|
|
||||||
let formatted = props.val.separated_string();
|
|
||||||
// TODO: make it autoscaling with css
|
|
||||||
cx.render(rsx! {
|
|
||||||
div { class: "calculator-display"
|
|
||||||
"{formatted}"
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -461,9 +461,10 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
let mut please_commit = |edits: &mut Vec<DomEdit>| {
|
let mut please_commit = |edits: &mut Vec<DomEdit>| {
|
||||||
if !has_comitted {
|
if !has_comitted {
|
||||||
has_comitted = true;
|
has_comitted = true;
|
||||||
if let Some(root) = root {
|
log::info!("planning on committing... {:#?}, {:#?}", old, new);
|
||||||
edits.push(PushRoot { id: root.as_u64() });
|
edits.push(PushRoot {
|
||||||
}
|
id: root.unwrap().as_u64(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -475,7 +476,12 @@ impl<'bump> DiffMachine<'bump> {
|
|||||||
// TODO: take a more efficient path than this
|
// TODO: take a more efficient path than this
|
||||||
if old.attributes.len() == new.attributes.len() {
|
if old.attributes.len() == new.attributes.len() {
|
||||||
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
||||||
if old_attr.value != new_attr.value || old_attr.is_volatile {
|
log::info!("checking attribute");
|
||||||
|
if old_attr.value != new_attr.value {
|
||||||
|
please_commit(&mut self.mutations.edits);
|
||||||
|
self.mutations.set_attribute(new_attr);
|
||||||
|
} else if new_attr.is_volatile {
|
||||||
|
log::debug!("setting due to volatile atttributes");
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
self.mutations.set_attribute(new_attr);
|
self.mutations.set_attribute(new_attr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,8 +264,10 @@ pub mod on {
|
|||||||
ClipboardEventInner(ClipboardEvent): [
|
ClipboardEventInner(ClipboardEvent): [
|
||||||
/// Called when "copy"
|
/// Called when "copy"
|
||||||
oncopy
|
oncopy
|
||||||
|
|
||||||
/// oncut
|
/// oncut
|
||||||
oncut
|
oncut
|
||||||
|
|
||||||
/// onpaste
|
/// onpaste
|
||||||
onpaste
|
onpaste
|
||||||
];
|
];
|
||||||
@@ -305,7 +307,7 @@ pub mod on {
|
|||||||
/// onchange
|
/// onchange
|
||||||
onchange
|
onchange
|
||||||
|
|
||||||
/// oninput
|
/// oninput handler
|
||||||
oninput
|
oninput
|
||||||
|
|
||||||
/// oninvalid
|
/// oninvalid
|
||||||
@@ -446,22 +448,31 @@ pub mod on {
|
|||||||
PointerEventInner(PointerEvent): [
|
PointerEventInner(PointerEvent): [
|
||||||
/// pointerdown
|
/// pointerdown
|
||||||
onpointerdown
|
onpointerdown
|
||||||
|
|
||||||
/// pointermove
|
/// pointermove
|
||||||
onpointermove
|
onpointermove
|
||||||
|
|
||||||
/// pointerup
|
/// pointerup
|
||||||
onpointerup
|
onpointerup
|
||||||
|
|
||||||
/// pointercancel
|
/// pointercancel
|
||||||
onpointercancel
|
onpointercancel
|
||||||
|
|
||||||
/// gotpointercapture
|
/// gotpointercapture
|
||||||
ongotpointercapture
|
ongotpointercapture
|
||||||
|
|
||||||
/// lostpointercapture
|
/// lostpointercapture
|
||||||
onlostpointercapture
|
onlostpointercapture
|
||||||
|
|
||||||
/// pointerenter
|
/// pointerenter
|
||||||
onpointerenter
|
onpointerenter
|
||||||
|
|
||||||
/// pointerleave
|
/// pointerleave
|
||||||
onpointerleave
|
onpointerleave
|
||||||
|
|
||||||
/// pointerover
|
/// pointerover
|
||||||
onpointerover
|
onpointerover
|
||||||
|
|
||||||
/// pointerout
|
/// pointerout
|
||||||
onpointerout
|
onpointerout
|
||||||
];
|
];
|
||||||
@@ -474,10 +485,13 @@ pub mod on {
|
|||||||
TouchEventInner(TouchEvent): [
|
TouchEventInner(TouchEvent): [
|
||||||
/// ontouchcancel
|
/// ontouchcancel
|
||||||
ontouchcancel
|
ontouchcancel
|
||||||
|
|
||||||
/// ontouchend
|
/// ontouchend
|
||||||
ontouchend
|
ontouchend
|
||||||
|
|
||||||
/// ontouchmove
|
/// ontouchmove
|
||||||
ontouchmove
|
ontouchmove
|
||||||
|
|
||||||
/// ontouchstart
|
/// ontouchstart
|
||||||
ontouchstart
|
ontouchstart
|
||||||
];
|
];
|
||||||
@@ -490,48 +504,70 @@ pub mod on {
|
|||||||
MediaEventInner(MediaEvent): [
|
MediaEventInner(MediaEvent): [
|
||||||
///abort
|
///abort
|
||||||
onabort
|
onabort
|
||||||
|
|
||||||
///canplay
|
///canplay
|
||||||
oncanplay
|
oncanplay
|
||||||
|
|
||||||
///canplaythrough
|
///canplaythrough
|
||||||
oncanplaythrough
|
oncanplaythrough
|
||||||
|
|
||||||
///durationchange
|
///durationchange
|
||||||
ondurationchange
|
ondurationchange
|
||||||
|
|
||||||
///emptied
|
///emptied
|
||||||
onemptied
|
onemptied
|
||||||
|
|
||||||
///encrypted
|
///encrypted
|
||||||
onencrypted
|
onencrypted
|
||||||
|
|
||||||
///ended
|
///ended
|
||||||
onended
|
onended
|
||||||
|
|
||||||
///error
|
///error
|
||||||
onerror
|
onerror
|
||||||
|
|
||||||
///loadeddata
|
///loadeddata
|
||||||
onloadeddata
|
onloadeddata
|
||||||
|
|
||||||
///loadedmetadata
|
///loadedmetadata
|
||||||
onloadedmetadata
|
onloadedmetadata
|
||||||
|
|
||||||
///loadstart
|
///loadstart
|
||||||
onloadstart
|
onloadstart
|
||||||
|
|
||||||
///pause
|
///pause
|
||||||
onpause
|
onpause
|
||||||
|
|
||||||
///play
|
///play
|
||||||
onplay
|
onplay
|
||||||
|
|
||||||
///playing
|
///playing
|
||||||
onplaying
|
onplaying
|
||||||
|
|
||||||
///progress
|
///progress
|
||||||
onprogress
|
onprogress
|
||||||
|
|
||||||
///ratechange
|
///ratechange
|
||||||
onratechange
|
onratechange
|
||||||
|
|
||||||
///seeked
|
///seeked
|
||||||
onseeked
|
onseeked
|
||||||
|
|
||||||
///seeking
|
///seeking
|
||||||
onseeking
|
onseeking
|
||||||
|
|
||||||
///stalled
|
///stalled
|
||||||
onstalled
|
onstalled
|
||||||
|
|
||||||
///suspend
|
///suspend
|
||||||
onsuspend
|
onsuspend
|
||||||
|
|
||||||
///timeupdate
|
///timeupdate
|
||||||
ontimeupdate
|
ontimeupdate
|
||||||
|
|
||||||
///volumechange
|
///volumechange
|
||||||
onvolumechange
|
onvolumechange
|
||||||
|
|
||||||
///waiting
|
///waiting
|
||||||
onwaiting
|
onwaiting
|
||||||
];
|
];
|
||||||
@@ -539,8 +575,10 @@ pub mod on {
|
|||||||
AnimationEventInner(AnimationEvent): [
|
AnimationEventInner(AnimationEvent): [
|
||||||
/// onanimationstart
|
/// onanimationstart
|
||||||
onanimationstart
|
onanimationstart
|
||||||
|
|
||||||
/// onanimationend
|
/// onanimationend
|
||||||
onanimationend
|
onanimationend
|
||||||
|
|
||||||
/// onanimationiteration
|
/// onanimationiteration
|
||||||
onanimationiteration
|
onanimationiteration
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -197,6 +197,21 @@ pub struct VElement<'a> {
|
|||||||
pub children: &'a [VNode<'a>],
|
pub children: &'a [VNode<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for VElement<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("VElement")
|
||||||
|
.field("tag_name", &self.tag_name)
|
||||||
|
.field("namespace", &self.namespace)
|
||||||
|
.field("key", &self.key)
|
||||||
|
.field("dom_id", &self.dom_id)
|
||||||
|
.field("parent_id", &self.parent_id)
|
||||||
|
.field("listeners", &self.listeners.len())
|
||||||
|
.field("attributes", &self.attributes)
|
||||||
|
.field("children", &self.children)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A trait for any generic Dioxus Element.
|
/// A trait for any generic Dioxus Element.
|
||||||
///
|
///
|
||||||
/// This trait provides the ability to use custom elements in the `rsx!` macro.
|
/// This trait provides the ability to use custom elements in the `rsx!` macro.
|
||||||
@@ -429,11 +444,6 @@ impl<'a> NodeFactory<'a> {
|
|||||||
is_volatile: bool,
|
is_volatile: bool,
|
||||||
) -> Attribute<'a> {
|
) -> Attribute<'a> {
|
||||||
let (value, is_static) = self.raw_text(val);
|
let (value, is_static) = self.raw_text(val);
|
||||||
let is_volatile = match name {
|
|
||||||
"value" | "checked" | "selected" => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
Attribute {
|
Attribute {
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ use crate::heuristics::*;
|
|||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||||
use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
|
use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
@@ -357,6 +358,8 @@ impl Scheduler {
|
|||||||
let shared = self.pool.clone();
|
let shared = self.pool.clone();
|
||||||
let mut machine = unsafe { saved_state.promote(&shared) };
|
let mut machine = unsafe { saved_state.promote(&shared) };
|
||||||
|
|
||||||
|
let mut ran_scopes = FxHashSet::default();
|
||||||
|
|
||||||
if machine.stack.is_empty() {
|
if machine.stack.is_empty() {
|
||||||
let shared = self.pool.clone();
|
let shared = self.pool.clone();
|
||||||
|
|
||||||
@@ -367,11 +370,18 @@ impl Scheduler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(scopeid) = self.dirty_scopes.pop() {
|
if let Some(scopeid) = self.dirty_scopes.pop() {
|
||||||
let component = self.pool.get_scope(scopeid).unwrap();
|
log::info!("handlng dirty scope {:#?}", scopeid);
|
||||||
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
if !ran_scopes.contains(&scopeid) {
|
||||||
// let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
ran_scopes.insert(scopeid);
|
||||||
machine.stack.scope_stack.push(scopeid);
|
|
||||||
machine.stack.push(DiffInstruction::Diff { new, old });
|
let mut component = self.pool.get_scope_mut(scopeid).unwrap();
|
||||||
|
if component.run_scope(&self.pool) {
|
||||||
|
let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
||||||
|
// let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
|
||||||
|
machine.stack.scope_stack.push(scopeid);
|
||||||
|
machine.stack.push(DiffInstruction::Diff { new, old });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,15 +482,7 @@ impl Scheduler {
|
|||||||
while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
|
while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
|
||||||
match dirty_scope {
|
match dirty_scope {
|
||||||
SchedulerMsg::Immediate(im) => {
|
SchedulerMsg::Immediate(im) => {
|
||||||
log::debug!("Handling immediate {:?}", im);
|
self.dirty_scopes.insert(im);
|
||||||
|
|
||||||
if let Some(scope) = self.pool.get_scope_mut(im) {
|
|
||||||
if scope.run_scope(&self.pool) {
|
|
||||||
self.dirty_scopes.insert(im);
|
|
||||||
} else {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
|
SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
|
||||||
SchedulerMsg::Task(_) => todo!(),
|
SchedulerMsg::Task(_) => todo!(),
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use dioxus_core as dioxus;
|
|||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
static Parent: FC<()> = |cx, props| {
|
static Parent: FC<()> = |cx, props| {
|
||||||
//
|
|
||||||
let value = cx.use_hook(|_| String::new(), |f| &*f, |_| {});
|
let value = cx.use_hook(|_| String::new(), |f| &*f, |_| {});
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ use std::{
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>(
|
pub fn use_state<'a, 'c, T: 'static>(
|
||||||
cx: Context<'a>,
|
cx: Context<'a>,
|
||||||
initial_state_fn: F,
|
initial_state_fn: impl FnOnce() -> T,
|
||||||
) -> UseState<'a, T> {
|
) -> UseState<'a, T> {
|
||||||
cx.use_hook(
|
cx.use_hook(
|
||||||
move |_| UseStateInner {
|
move |_| UseStateInner {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-core = { path="../core" }
|
dioxus-core = { path = "../core" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
scraper = "0.12.0"
|
scraper = "0.12.0"
|
||||||
@@ -681,7 +681,10 @@ macro_rules! builder_constructors {
|
|||||||
$(
|
$(
|
||||||
$(#[$attr:meta])*
|
$(#[$attr:meta])*
|
||||||
$name:ident {
|
$name:ident {
|
||||||
$($fil:ident: $vil:ident,)*
|
$(
|
||||||
|
$(#[$attr_method:meta])*
|
||||||
|
$fil:ident: $vil:ident,
|
||||||
|
)*
|
||||||
};
|
};
|
||||||
)*
|
)*
|
||||||
) => {
|
) => {
|
||||||
@@ -699,6 +702,7 @@ macro_rules! builder_constructors {
|
|||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
$(
|
$(
|
||||||
|
$(#[$attr_method])*
|
||||||
pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
|
pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
|
||||||
cx.attr(stringify!($fil), val, None, false)
|
cx.attr(stringify!($fil), val, None, false)
|
||||||
}
|
}
|
||||||
@@ -1525,7 +1529,10 @@ builder_constructors! {
|
|||||||
src: Uri,
|
src: Uri,
|
||||||
step: String,
|
step: String,
|
||||||
tabindex: usize,
|
tabindex: usize,
|
||||||
r#type: InputType,
|
|
||||||
|
// This has a manual implementation below
|
||||||
|
// r#type: InputType,
|
||||||
|
|
||||||
value: String,
|
value: String,
|
||||||
width: isize,
|
width: isize,
|
||||||
};
|
};
|
||||||
@@ -1570,7 +1577,10 @@ builder_constructors! {
|
|||||||
option {
|
option {
|
||||||
disabled: Bool,
|
disabled: Bool,
|
||||||
label: String,
|
label: String,
|
||||||
selected: Bool,
|
|
||||||
|
// defined below
|
||||||
|
// selected: Bool,
|
||||||
|
|
||||||
value: String,
|
value: String,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1595,7 +1605,8 @@ builder_constructors! {
|
|||||||
/// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
|
/// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
|
||||||
/// element.
|
/// element.
|
||||||
select {
|
select {
|
||||||
value: String,
|
// defined below
|
||||||
|
// value: String,
|
||||||
autocomplete: String,
|
autocomplete: String,
|
||||||
autofocus: Bool,
|
autofocus: Bool,
|
||||||
disabled: Bool,
|
disabled: Bool,
|
||||||
@@ -1656,6 +1667,54 @@ builder_constructors! {
|
|||||||
template {};
|
template {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl input {
|
||||||
|
/// The type of input
|
||||||
|
///
|
||||||
|
/// Here are the different input types you can use in HTML:
|
||||||
|
///
|
||||||
|
/// - `button`
|
||||||
|
/// - `checkbox`
|
||||||
|
/// - `color`
|
||||||
|
/// - `date`
|
||||||
|
/// - `datetime-local`
|
||||||
|
/// - `email`
|
||||||
|
/// - `file`
|
||||||
|
/// - `hidden`
|
||||||
|
/// - `image`
|
||||||
|
/// - `month`
|
||||||
|
/// - `number`
|
||||||
|
/// - `password`
|
||||||
|
/// - `radio`
|
||||||
|
/// - `range`
|
||||||
|
/// - `reset`
|
||||||
|
/// - `search`
|
||||||
|
/// - `submit`
|
||||||
|
/// - `tel`
|
||||||
|
/// - `text`
|
||||||
|
/// - `time`
|
||||||
|
/// - `url`
|
||||||
|
/// - `week`
|
||||||
|
pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
|
||||||
|
cx.attr("type", val, None, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
volatile attributes
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl select {
|
||||||
|
pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
|
||||||
|
cx.attr("value", val, None, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl option {
|
||||||
|
fn selected<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
|
||||||
|
cx.attr("selected", val, None, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait SvgAttributes {
|
pub trait SvgAttributes {
|
||||||
aria_trait_methods! {
|
aria_trait_methods! {
|
||||||
accent_height: "accent-height",
|
accent_height: "accent-height",
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Known quirks for browsers and their workarounds
|
||||||
|
|
||||||
|
- text merging (solved through comment nodes)
|
||||||
|
- cursor jumping to end on inputs (not yet solved, solved in React already)
|
||||||
|
- SVG attributes cannot be set (solved using the correct method)
|
||||||
|
- volatile components
|
||||||
@@ -11,7 +11,7 @@ license = "MIT/Apache-2.0"
|
|||||||
dioxus-core = { path = "../core", version = "0.1.2" }
|
dioxus-core = { path = "../core", version = "0.1.2" }
|
||||||
dioxus-html = { path = "../html" }
|
dioxus-html = { path = "../html" }
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
wasm-bindgen = { version = "0.2.71", features = ["enable-interning"] }
|
wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
wasm-bindgen-futures = "0.4.20"
|
wasm-bindgen-futures = "0.4.20"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
@@ -70,12 +70,12 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
im-rc = "15.0.0"
|
im-rc = "15.0.0"
|
||||||
# rand = { version="0.8.4", features=["small_rng"] }
|
|
||||||
separator = "0.4.1"
|
separator = "0.4.1"
|
||||||
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
|
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
|
||||||
dioxus-hooks = { path = "../hooks" }
|
dioxus-hooks = { path = "../hooks" }
|
||||||
serde = { version = "1.0.126", features = ["derive"] }
|
serde = { version = "1.0.126", features = ["derive"] }
|
||||||
surf = { git = "https://github.com/http-rs/surf", rev = "1ffaba8873", default-features = false, features = [
|
|
||||||
"wasm-client",
|
# rand = { version="0.8.4", features=["small_rng"] }
|
||||||
] }
|
# surf = { version = "2.3.1", default-features = false, features = [
|
||||||
# wasm-timer = "0.2.5"
|
# "wasm-client",
|
||||||
|
# ] }
|
||||||
@@ -15,7 +15,7 @@ use std::{pin::Pin, time::Duration};
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Setup logging
|
// Setup logging
|
||||||
// wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
// Run the app
|
// Run the app
|
||||||
@@ -24,32 +24,44 @@ fn main() {
|
|||||||
|
|
||||||
static APP: FC<()> = |cx, props| {
|
static APP: FC<()> = |cx, props| {
|
||||||
let mut count = use_state(cx, || 3);
|
let mut count = use_state(cx, || 3);
|
||||||
|
let mut content = use_state(cx, || String::from("h1"));
|
||||||
|
let mut text_content = use_state(cx, || String::from("Hello, world!"));
|
||||||
|
|
||||||
|
log::debug!("running scope...");
|
||||||
|
|
||||||
let mut content = use_state(cx, || String::new());
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
h1 { "content val is {content}" }
|
||||||
|
|
||||||
input {
|
input {
|
||||||
value: "{content}"
|
r#type: "text",
|
||||||
oninput: move |e| {
|
value: "{text_content}"
|
||||||
content.set(e.value());
|
oninput: move |e| text_content.set(e.value())
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
onclick: move |_| count += 1,
|
|
||||||
"Click to add."
|
|
||||||
"Current count: {count}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
br {}
|
||||||
|
{(0..10).map(|f| {
|
||||||
|
rsx!(
|
||||||
|
button {
|
||||||
|
onclick: move |_| count += 1,
|
||||||
|
"Click to add."
|
||||||
|
"Current count: {count}"
|
||||||
|
}
|
||||||
|
br {}
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
name: "cars"
|
name: "cars"
|
||||||
id: "cars"
|
id: "cars"
|
||||||
value: "h1"
|
value: "{content}"
|
||||||
oninput: move |ev| {
|
oninput: move |ev| {
|
||||||
|
content.set(ev.value());
|
||||||
match ev.value().as_str() {
|
match ev.value().as_str() {
|
||||||
"h1" => count.set(0),
|
"h1" => count.set(0),
|
||||||
"h2" => count.set(5),
|
"h2" => count.set(5),
|
||||||
"h3" => count.set(10),
|
"h3" => count.set(10),
|
||||||
s => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -58,14 +70,7 @@ static APP: FC<()> = |cx, props| {
|
|||||||
option { value: "h3", "h3" }
|
option { value: "h3", "h3" }
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
{render_list(cx, *count)}
|
||||||
{(0..*count).map(|f| rsx!{
|
|
||||||
li { "a - {f}" }
|
|
||||||
li { "b - {f}" }
|
|
||||||
li { "c - {f}" }
|
|
||||||
})}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
{render_bullets(cx)}
|
{render_bullets(cx)}
|
||||||
|
|
||||||
@@ -80,4 +85,19 @@ fn render_bullets(cx: Context) -> DomTree {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static Child: FC<()> = |cx, props| rsx!(cx, div {"hello child"});
|
fn render_list(cx: Context, count: usize) -> DomTree {
|
||||||
|
let items = (0..count).map(|f| {
|
||||||
|
rsx! {
|
||||||
|
li { "a - {f}" }
|
||||||
|
li { "b - {f}" }
|
||||||
|
li { "c - {f}" }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
rsx!(cx, ul { {items} })
|
||||||
|
}
|
||||||
|
|
||||||
|
static Child: FC<()> = |cx, props| {
|
||||||
|
// render
|
||||||
|
rsx!(cx, div {"hello child"})
|
||||||
|
};
|
||||||
@@ -24,7 +24,7 @@ fn main() {
|
|||||||
dioxus_web::launch(App, |c| c)
|
dioxus_web::launch(App, |c| c)
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: FC<()> = |cx, props|{
|
static App: FC<()> = |cx, props| {
|
||||||
let mut state = use_state(cx, || 0);
|
let mut state = use_state(cx, || 0);
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
|||||||
+11
-3
@@ -314,12 +314,20 @@ impl WebsysDom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
|
if let Some(input) = node.dyn_ref::<HtmlInputElement>() {
|
||||||
if name == "value" {
|
if name == "value" {
|
||||||
node.set_value(value);
|
/*
|
||||||
|
if the attribute being set is the same as the value of the input, then don't bother setting it.
|
||||||
|
This is used in controlled components to keep the cursor in the right spot.
|
||||||
|
|
||||||
|
this logic should be moved into the virtualdom since we have the notion of "volatile"
|
||||||
|
*/
|
||||||
|
if input.value() != value {
|
||||||
|
input.set_value(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if name == "checked" {
|
if name == "checked" {
|
||||||
node.set_checked(true);
|
input.set_checked(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T,
|
|||||||
work_loop.wait_for_raf().await;
|
work_loop.wait_for_raf().await;
|
||||||
|
|
||||||
for mut edit in mutations {
|
for mut edit in mutations {
|
||||||
|
log::debug!("edits are {:#?}", edit);
|
||||||
// actually apply our changes during the animation frame
|
// actually apply our changes during the animation frame
|
||||||
websys_dom.process_edits(&mut edit.edits);
|
websys_dom.process_edits(&mut edit.edits);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use gloo_timers::future::TimeoutFuture;
|
|||||||
use js_sys::Function;
|
use js_sys::Function;
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen::{prelude::Closure, JsValue};
|
use wasm_bindgen::{prelude::Closure, JsValue};
|
||||||
use web_sys::Window;
|
use web_sys::{window, Window};
|
||||||
|
|
||||||
pub struct RafLoop {
|
pub struct RafLoop {
|
||||||
window: Window,
|
window: Window,
|
||||||
@@ -24,10 +24,21 @@ impl RafLoop {
|
|||||||
|
|
||||||
let (ric_sender, ric_receiver) = async_channel::unbounded();
|
let (ric_sender, ric_receiver) = async_channel::unbounded();
|
||||||
|
|
||||||
let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
|
let has_idle_callback = {
|
||||||
//
|
let bo = window().unwrap().dyn_into::<js_sys::Object>().unwrap();
|
||||||
let deadline = _v.dyn_into::<web_sys::IdleDeadline>().unwrap();
|
bo.has_own_property(&JsValue::from_str("requestIdleCallback"))
|
||||||
let time_remaining = deadline.time_remaining() as u32;
|
};
|
||||||
|
let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |v: JsValue| {
|
||||||
|
let time_remaining = if has_idle_callback {
|
||||||
|
if let Ok(deadline) = v.dyn_into::<web_sys::IdleDeadline>() {
|
||||||
|
deadline.time_remaining() as u32
|
||||||
|
} else {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
10
|
||||||
|
};
|
||||||
|
|
||||||
ric_sender.try_send(time_remaining).unwrap()
|
ric_sender.try_send(time_remaining).unwrap()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user