polish: examples
This commit is contained in:
1 parent
4b6ca05f2c
commit
1a2f91ed91
19 files changed
+327
-183
No files matched your search
@@ -461,9 +461,10 @@ impl<'bump> DiffMachine<'bump> {
|
||||
let mut please_commit = |edits: &mut Vec<DomEdit>| {
|
||||
if !has_comitted {
|
||||
has_comitted = true;
|
||||
if let Some(root) = root {
|
||||
edits.push(PushRoot { id: root.as_u64() });
|
||||
}
|
||||
log::info!("planning on committing... {:#?}, {:#?}", old, new);
|
||||
edits.push(PushRoot {
|
||||
id: root.unwrap().as_u64(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -475,7 +476,12 @@ impl<'bump> DiffMachine<'bump> {
|
||||
// TODO: take a more efficient path than this
|
||||
if old.attributes.len() == new.attributes.len() {
|
||||
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);
|
||||
self.mutations.set_attribute(new_attr);
|
||||
}
|
||||
|
||||
@@ -264,8 +264,10 @@ pub mod on {
|
||||
ClipboardEventInner(ClipboardEvent): [
|
||||
/// Called when "copy"
|
||||
oncopy
|
||||
|
||||
/// oncut
|
||||
oncut
|
||||
|
||||
/// onpaste
|
||||
onpaste
|
||||
];
|
||||
@@ -305,7 +307,7 @@ pub mod on {
|
||||
/// onchange
|
||||
onchange
|
||||
|
||||
/// oninput
|
||||
/// oninput handler
|
||||
oninput
|
||||
|
||||
/// oninvalid
|
||||
@@ -446,22 +448,31 @@ pub mod on {
|
||||
PointerEventInner(PointerEvent): [
|
||||
/// pointerdown
|
||||
onpointerdown
|
||||
|
||||
/// pointermove
|
||||
onpointermove
|
||||
|
||||
/// pointerup
|
||||
onpointerup
|
||||
|
||||
/// pointercancel
|
||||
onpointercancel
|
||||
|
||||
/// gotpointercapture
|
||||
ongotpointercapture
|
||||
|
||||
/// lostpointercapture
|
||||
onlostpointercapture
|
||||
|
||||
/// pointerenter
|
||||
onpointerenter
|
||||
|
||||
/// pointerleave
|
||||
onpointerleave
|
||||
|
||||
/// pointerover
|
||||
onpointerover
|
||||
|
||||
/// pointerout
|
||||
onpointerout
|
||||
];
|
||||
@@ -474,10 +485,13 @@ pub mod on {
|
||||
TouchEventInner(TouchEvent): [
|
||||
/// ontouchcancel
|
||||
ontouchcancel
|
||||
|
||||
/// ontouchend
|
||||
ontouchend
|
||||
|
||||
/// ontouchmove
|
||||
ontouchmove
|
||||
|
||||
/// ontouchstart
|
||||
ontouchstart
|
||||
];
|
||||
@@ -490,48 +504,70 @@ pub mod on {
|
||||
MediaEventInner(MediaEvent): [
|
||||
///abort
|
||||
onabort
|
||||
|
||||
///canplay
|
||||
oncanplay
|
||||
|
||||
///canplaythrough
|
||||
oncanplaythrough
|
||||
|
||||
///durationchange
|
||||
ondurationchange
|
||||
|
||||
///emptied
|
||||
onemptied
|
||||
|
||||
///encrypted
|
||||
onencrypted
|
||||
|
||||
///ended
|
||||
onended
|
||||
|
||||
///error
|
||||
onerror
|
||||
|
||||
///loadeddata
|
||||
onloadeddata
|
||||
|
||||
///loadedmetadata
|
||||
onloadedmetadata
|
||||
|
||||
///loadstart
|
||||
onloadstart
|
||||
|
||||
///pause
|
||||
onpause
|
||||
|
||||
///play
|
||||
onplay
|
||||
|
||||
///playing
|
||||
onplaying
|
||||
|
||||
///progress
|
||||
onprogress
|
||||
|
||||
///ratechange
|
||||
onratechange
|
||||
|
||||
///seeked
|
||||
onseeked
|
||||
|
||||
///seeking
|
||||
onseeking
|
||||
|
||||
///stalled
|
||||
onstalled
|
||||
|
||||
///suspend
|
||||
onsuspend
|
||||
|
||||
///timeupdate
|
||||
ontimeupdate
|
||||
|
||||
///volumechange
|
||||
onvolumechange
|
||||
|
||||
///waiting
|
||||
onwaiting
|
||||
];
|
||||
@@ -539,8 +575,10 @@ pub mod on {
|
||||
AnimationEventInner(AnimationEvent): [
|
||||
/// onanimationstart
|
||||
onanimationstart
|
||||
|
||||
/// onanimationend
|
||||
onanimationend
|
||||
|
||||
/// onanimationiteration
|
||||
onanimationiteration
|
||||
];
|
||||
|
||||
@@ -197,6 +197,21 @@ pub struct VElement<'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.
|
||||
///
|
||||
/// This trait provides the ability to use custom elements in the `rsx!` macro.
|
||||
@@ -429,11 +444,6 @@ impl<'a> NodeFactory<'a> {
|
||||
is_volatile: bool,
|
||||
) -> Attribute<'a> {
|
||||
let (value, is_static) = self.raw_text(val);
|
||||
let is_volatile = match name {
|
||||
"value" | "checked" | "selected" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
Attribute {
|
||||
name,
|
||||
value,
|
||||
|
||||
@@ -72,6 +72,7 @@ use crate::heuristics::*;
|
||||
use crate::innerlude::*;
|
||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
|
||||
use fxhash::FxHashMap;
|
||||
use fxhash::FxHashSet;
|
||||
use indexmap::IndexSet;
|
||||
use slab::Slab;
|
||||
@@ -357,6 +358,8 @@ impl Scheduler {
|
||||
let shared = self.pool.clone();
|
||||
let mut machine = unsafe { saved_state.promote(&shared) };
|
||||
|
||||
let mut ran_scopes = FxHashSet::default();
|
||||
|
||||
if machine.stack.is_empty() {
|
||||
let shared = self.pool.clone();
|
||||
|
||||
@@ -367,11 +370,18 @@ impl Scheduler {
|
||||
});
|
||||
|
||||
if let Some(scopeid) = self.dirty_scopes.pop() {
|
||||
let component = self.pool.get_scope(scopeid).unwrap();
|
||||
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 });
|
||||
log::info!("handlng dirty scope {:#?}", scopeid);
|
||||
if !ran_scopes.contains(&scopeid) {
|
||||
ran_scopes.insert(scopeid);
|
||||
|
||||
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() {
|
||||
match dirty_scope {
|
||||
SchedulerMsg::Immediate(im) => {
|
||||
log::debug!("Handling immediate {:?}", im);
|
||||
|
||||
if let Some(scope) = self.pool.get_scope_mut(im) {
|
||||
if scope.run_scope(&self.pool) {
|
||||
self.dirty_scopes.insert(im);
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
self.dirty_scopes.insert(im);
|
||||
}
|
||||
SchedulerMsg::UiEvent(e) => self.ui_events.push_back(e),
|
||||
SchedulerMsg::Task(_) => todo!(),
|
||||
|
||||
@@ -3,7 +3,6 @@ use dioxus_core as dioxus;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
static Parent: FC<()> = |cx, props| {
|
||||
//
|
||||
let value = cx.use_hook(|_| String::new(), |f| &*f, |_| {});
|
||||
|
||||
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>,
|
||||
initial_state_fn: F,
|
||||
initial_state_fn: impl FnOnce() -> T,
|
||||
) -> UseState<'a, T> {
|
||||
cx.use_hook(
|
||||
move |_| UseStateInner {
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = { path="../core" }
|
||||
dioxus-core = { path = "../core" }
|
||||
|
||||
[dev-dependencies]
|
||||
scraper = "0.12.0"
|
||||
@@ -681,7 +681,10 @@ macro_rules! builder_constructors {
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
$name:ident {
|
||||
$($fil:ident: $vil:ident,)*
|
||||
$(
|
||||
$(#[$attr_method:meta])*
|
||||
$fil:ident: $vil:ident,
|
||||
)*
|
||||
};
|
||||
)*
|
||||
) => {
|
||||
@@ -699,6 +702,7 @@ macro_rules! builder_constructors {
|
||||
|
||||
impl $name {
|
||||
$(
|
||||
$(#[$attr_method])*
|
||||
pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
|
||||
cx.attr(stringify!($fil), val, None, false)
|
||||
}
|
||||
@@ -1525,7 +1529,10 @@ builder_constructors! {
|
||||
src: Uri,
|
||||
step: String,
|
||||
tabindex: usize,
|
||||
r#type: InputType,
|
||||
|
||||
// This has a manual implementation below
|
||||
// r#type: InputType,
|
||||
|
||||
value: String,
|
||||
width: isize,
|
||||
};
|
||||
@@ -1570,7 +1577,10 @@ builder_constructors! {
|
||||
option {
|
||||
disabled: Bool,
|
||||
label: String,
|
||||
selected: Bool,
|
||||
|
||||
// defined below
|
||||
// selected: Bool,
|
||||
|
||||
value: String,
|
||||
};
|
||||
|
||||
@@ -1595,7 +1605,8 @@ builder_constructors! {
|
||||
/// [`<select>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
|
||||
/// element.
|
||||
select {
|
||||
value: String,
|
||||
// defined below
|
||||
// value: String,
|
||||
autocomplete: String,
|
||||
autofocus: Bool,
|
||||
disabled: Bool,
|
||||
@@ -1656,6 +1667,54 @@ builder_constructors! {
|
||||
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 {
|
||||
aria_trait_methods! {
|
||||
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-html = { path = "../html" }
|
||||
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"
|
||||
wasm-bindgen-futures = "0.4.20"
|
||||
log = "0.4.14"
|
||||
@@ -70,12 +70,12 @@ crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
im-rc = "15.0.0"
|
||||
# rand = { version="0.8.4", features=["small_rng"] }
|
||||
separator = "0.4.1"
|
||||
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
|
||||
dioxus-hooks = { path = "../hooks" }
|
||||
serde = { version = "1.0.126", features = ["derive"] }
|
||||
surf = { git = "https://github.com/http-rs/surf", rev = "1ffaba8873", default-features = false, features = [
|
||||
"wasm-client",
|
||||
] }
|
||||
# wasm-timer = "0.2.5"
|
||||
|
||||
# rand = { version="0.8.4", features=["small_rng"] }
|
||||
# surf = { version = "2.3.1", default-features = false, features = [
|
||||
# "wasm-client",
|
||||
# ] }
|
||||
@@ -15,7 +15,7 @@ use std::{pin::Pin, time::Duration};
|
||||
|
||||
fn main() {
|
||||
// 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();
|
||||
|
||||
// Run the app
|
||||
@@ -24,32 +24,44 @@ fn main() {
|
||||
|
||||
static APP: FC<()> = |cx, props| {
|
||||
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! {
|
||||
div {
|
||||
h1 { "content val is {content}" }
|
||||
|
||||
input {
|
||||
value: "{content}"
|
||||
oninput: move |e| {
|
||||
content.set(e.value());
|
||||
}
|
||||
}
|
||||
button {
|
||||
onclick: move |_| count += 1,
|
||||
"Click to add."
|
||||
"Current count: {count}"
|
||||
r#type: "text",
|
||||
value: "{text_content}"
|
||||
oninput: move |e| text_content.set(e.value())
|
||||
}
|
||||
|
||||
br {}
|
||||
{(0..10).map(|f| {
|
||||
rsx!(
|
||||
button {
|
||||
onclick: move |_| count += 1,
|
||||
"Click to add."
|
||||
"Current count: {count}"
|
||||
}
|
||||
br {}
|
||||
)
|
||||
})}
|
||||
|
||||
select {
|
||||
name: "cars"
|
||||
id: "cars"
|
||||
value: "h1"
|
||||
value: "{content}"
|
||||
oninput: move |ev| {
|
||||
content.set(ev.value());
|
||||
match ev.value().as_str() {
|
||||
"h1" => count.set(0),
|
||||
"h2" => count.set(5),
|
||||
"h3" => count.set(10),
|
||||
s => {}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -58,14 +70,7 @@ static APP: FC<()> = |cx, props| {
|
||||
option { value: "h3", "h3" }
|
||||
}
|
||||
|
||||
ul {
|
||||
{(0..*count).map(|f| rsx!{
|
||||
li { "a - {f}" }
|
||||
li { "b - {f}" }
|
||||
li { "c - {f}" }
|
||||
})}
|
||||
|
||||
}
|
||||
{render_list(cx, *count)}
|
||||
|
||||
{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)
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx, props|{
|
||||
static App: FC<()> = |cx, props| {
|
||||
let mut state = use_state(cx, || 0);
|
||||
cx.render(rsx! {
|
||||
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" {
|
||||
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" {
|
||||
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;
|
||||
|
||||
for mut edit in mutations {
|
||||
log::debug!("edits are {:#?}", edit);
|
||||
// actually apply our changes during the animation frame
|
||||
websys_dom.process_edits(&mut edit.edits);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use gloo_timers::future::TimeoutFuture;
|
||||
use js_sys::Function;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::{prelude::Closure, JsValue};
|
||||
use web_sys::Window;
|
||||
use web_sys::{window, Window};
|
||||
|
||||
pub struct RafLoop {
|
||||
window: Window,
|
||||
@@ -24,10 +24,21 @@ impl RafLoop {
|
||||
|
||||
let (ric_sender, ric_receiver) = async_channel::unbounded();
|
||||
|
||||
let ric_closure: Closure<dyn Fn(JsValue)> = Closure::wrap(Box::new(move |_v: JsValue| {
|
||||
//
|
||||
let deadline = _v.dyn_into::<web_sys::IdleDeadline>().unwrap();
|
||||
let time_remaining = deadline.time_remaining() as u32;
|
||||
let has_idle_callback = {
|
||||
let bo = window().unwrap().dyn_into::<js_sys::Object>().unwrap();
|
||||
bo.has_own_property(&JsValue::from_str("requestIdleCallback"))
|
||||
};
|
||||
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()
|
||||
}));
|
||||
|
||||
|
||||
Reference in new issue
Block a user