feat: props memoization is more powerful
This commit solves the memoization , properly memoizing properties that don't have any generic parameters. This is a rough heuristic to prevent non-static lifetimes from creeping into props and breaking our minual lifetime management. Props that have a generic parameter are opted-out of the `partialeq` requirement and props *without* lifetimes must implement partialeq. We're going to leave manual disabling of memoization for future work.
This commit is contained in:
1 parent
7102fe5f98
commit
73047fe956
37 files changed
+877
-977
No files matched your search
@@ -27,8 +27,8 @@ atoms = { path="../atoms" }
|
||||
# futures = "0.3.12"
|
||||
# html-validation = { path = "../html-validation", version = "0.1.1" }
|
||||
|
||||
async-channel = "1.6.1"
|
||||
wee_alloc = "0.4.5"
|
||||
# async-channel = "1.6.1"
|
||||
nohash-hasher = "0.2.0"
|
||||
# futures-lite = "1.11.3"
|
||||
|
||||
[dependencies.web-sys]
|
||||
@@ -79,6 +79,8 @@ opt-level = 's'
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
im-rc = "15.0.0"
|
||||
rand = { version="0.8.4", features=["small_rng"] }
|
||||
uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
|
||||
|
||||
[[example]]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Basic example that renders a simple VNode to the browser.
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::*;
|
||||
|
||||
@@ -50,15 +51,15 @@ static DocExamples: FC<()> = |ctx| {
|
||||
}
|
||||
};
|
||||
|
||||
rsx! {
|
||||
div {}
|
||||
h1 {}
|
||||
{""}
|
||||
"asbasd"
|
||||
dioxus::Fragment {
|
||||
//
|
||||
}
|
||||
}
|
||||
// rsx! {
|
||||
// div {}
|
||||
// h1 {}
|
||||
// {""}
|
||||
// "asbasd"
|
||||
// dioxus::Fragment {
|
||||
// //
|
||||
// }
|
||||
// }
|
||||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
//! --------------------
|
||||
//! This example demonstrates how to use the raw context api for sharing state throughout the VirtualDOM Tree.
|
||||
//! A custom context must be its own unique type - otherwise use_context will fail. A context may be c
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
@@ -20,7 +20,6 @@ fn main() {
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CustomContext([&'static str; 3]);
|
||||
|
||||
@@ -46,7 +45,6 @@ static Example: FC<()> = |ctx| {
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct ButtonProps {
|
||||
id: u8,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_web::{dioxus::prelude::*, WebsysRenderer};
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -8,10 +8,6 @@ fn main() {
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||
}
|
||||
|
||||
// Use `wee_alloc` as the global allocator.
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
fn App(ctx: Context<()>) -> VNode {
|
||||
let cansee = use_state_new(&ctx, || false);
|
||||
rsx! { in ctx,
|
||||
|
||||
@@ -5,17 +5,17 @@ use dioxus_core::prelude::*;
|
||||
fn main() {}
|
||||
|
||||
fn autocomplete() {
|
||||
let handler = move |evt| {
|
||||
let r = evt.alt_key();
|
||||
if evt.alt_key() {}
|
||||
};
|
||||
// let handler = move |evt| {
|
||||
// let r = evt.alt_key();
|
||||
// if evt.alt_key() {}
|
||||
// };
|
||||
|
||||
let g = rsx! {
|
||||
button {
|
||||
button {
|
||||
onclick: {handler}
|
||||
}
|
||||
}
|
||||
// let g = rsx! {
|
||||
// button {
|
||||
// button {
|
||||
// onclick: {handler}
|
||||
// }
|
||||
// }
|
||||
|
||||
};
|
||||
// };
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
//! JS Framework Benchmark
|
||||
//! ----------------------
|
||||
//!
|
||||
//! This example is used in the JS framework benchmarking tool to compare Dioxus' performance with other frontend frameworks.
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus::events::on::MouseEvent;
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||
}
|
||||
|
||||
// We use a special immutable hashmap to make hashmap operations efficient
|
||||
type RowList = im_rc::HashMap<usize, Rc<str>, nohash_hasher::BuildNoHashHasher<usize>>;
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
let (items, set_items) = use_state(&cx, || RowList::default());
|
||||
let (selection, set_selection) = use_state(&cx, || None as Option<usize>);
|
||||
|
||||
let create_rendered_rows = move |from, num| move |_| set_items(create_row_list(from, num));
|
||||
|
||||
let append_1_000_rows =
|
||||
move |_| set_items(create_row_list(items.len(), 1000).union(items.clone()));
|
||||
|
||||
let update_every_10th_row = move |_| {
|
||||
let mut new_items = items.clone();
|
||||
let mut small_rng = SmallRng::from_entropy();
|
||||
new_items
|
||||
.iter_mut()
|
||||
.step_by(10)
|
||||
.for_each(|(_, val)| *val = create_new_row_label(&mut small_rng));
|
||||
set_items(new_items);
|
||||
};
|
||||
let clear_rows = move |_| set_items(RowList::default());
|
||||
|
||||
let swap_rows = move |_| {
|
||||
// this looks a bit ugly because we're using a hashmap instead of a vec
|
||||
if items.len() > 998 {
|
||||
let mut new_items = items.clone();
|
||||
let a = new_items.get(&0).unwrap().clone();
|
||||
*new_items.get_mut(&0).unwrap() = new_items.get(&998).unwrap().clone();
|
||||
*new_items.get_mut(&998).unwrap() = a;
|
||||
set_items(new_items);
|
||||
}
|
||||
};
|
||||
|
||||
let rows = items.iter().map(|(key, value)| {
|
||||
rsx!(Row {
|
||||
key: "{key}",
|
||||
row_id: *key as usize,
|
||||
label: value.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div { class: "container"
|
||||
div { class: "jumbotron"
|
||||
div { class: "row"
|
||||
div { class: "col-md-6", h1 { "Dioxus" } }
|
||||
div { class: "col-md-6"
|
||||
div { class: "row"
|
||||
ActionButton { name: "Create 1,000 rows", id: "run", action: create_rendered_rows(0, 1_000) }
|
||||
ActionButton { name: "Create 10,000 rows", id: "runlots", action: create_rendered_rows(0, 10_000) }
|
||||
ActionButton { name: "Append 1,000 rows", id: "add", action: append_1_000_rows }
|
||||
ActionButton { name: "Update every 10th row", id: "update", action: update_every_10th_row, }
|
||||
ActionButton { name: "Clear", id: "clear", action: clear_rows }
|
||||
ActionButton { name: "Swap rows", id: "swaprows", action: swap_rows }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
table {
|
||||
tbody {
|
||||
{rows}
|
||||
}
|
||||
}
|
||||
span {}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
#[derive(Props)]
|
||||
struct ActionButtonProps<F: Fn(Rc<dyn MouseEvent>)> {
|
||||
name: &'static str,
|
||||
id: &'static str,
|
||||
action: F,
|
||||
}
|
||||
fn ActionButton<F: Fn(Rc<dyn MouseEvent>)>(cx: Context<ActionButtonProps<F>>) -> VNode {
|
||||
cx.render(rsx! {
|
||||
div { class: "col-sm-6 smallpad"
|
||||
button {class:"btn btn-primary btn-block", type: "button", id: "{cx.id}", onclick: {&cx.action},
|
||||
"{cx.name}"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct RowProps {
|
||||
row_id: usize,
|
||||
label: Rc<str>,
|
||||
}
|
||||
fn Row<'a>(cx: Context<'a, RowProps>) -> VNode {
|
||||
cx.render(rsx! {
|
||||
tr {
|
||||
td { class:"col-md-1", "{cx.row_id}" }
|
||||
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
||||
a { class: "lbl", "{cx.label}" }
|
||||
}
|
||||
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::*;
|
||||
fn create_new_row_label(rng: &mut SmallRng) -> Rc<str> {
|
||||
let mut label = String::new();
|
||||
label.push_str(ADJECTIVES.choose(rng).unwrap());
|
||||
label.push(' ');
|
||||
label.push_str(COLOURS.choose(rng).unwrap());
|
||||
label.push(' ');
|
||||
label.push_str(NOUNS.choose(rng).unwrap());
|
||||
Rc::from(label)
|
||||
}
|
||||
|
||||
fn create_row_list(from: usize, num: usize) -> RowList {
|
||||
let mut small_rng = SmallRng::from_entropy();
|
||||
(from..num + from)
|
||||
.map(|f| (f, create_new_row_label(&mut small_rng)))
|
||||
.collect::<RowList>()
|
||||
}
|
||||
|
||||
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",
|
||||
];
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
@@ -10,12 +11,24 @@ fn main() {
|
||||
}
|
||||
|
||||
static Example: FC<()> = |ctx| {
|
||||
let nodes = (0..5).map(|f| {
|
||||
rsx! {
|
||||
li {"{f}"}
|
||||
}
|
||||
});
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
span {
|
||||
class: "px-2 py-1 flex w-36 mt-4 items-center text-xs rounded-md font-semibold text-yellow-500 bg-yellow-100"
|
||||
"DUE DATE : 18 JUN"
|
||||
"DUE DATE : 189 JUN"
|
||||
}
|
||||
p {
|
||||
"these"
|
||||
"are"
|
||||
"text"
|
||||
"nodes"
|
||||
}
|
||||
{nodes}
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_web::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::{events::on::MouseEvent, prelude::*};
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
@@ -14,7 +15,7 @@ fn main() {
|
||||
static Example: FC<()> = |ctx| {
|
||||
let (event, set_event) = use_state(&ctx, || None);
|
||||
|
||||
let handler = move |evt: MouseEvent| {
|
||||
let handler = move |evt| {
|
||||
set_event(Some(evt));
|
||||
};
|
||||
|
||||
@@ -42,17 +43,15 @@ static Example: FC<()> = |ctx| {
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Props)]
|
||||
struct ExampleProps {
|
||||
name: String
|
||||
name: String,
|
||||
}
|
||||
|
||||
static Example2: FC<ExampleProps> = |ctx| {
|
||||
ctx.render(rsx!{
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
h1 {"hello {ctx.name}"}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
@@ -26,25 +26,22 @@ static App: FC<()> = |ctx| {
|
||||
id: "username"
|
||||
type: "text"
|
||||
value: "{val}"
|
||||
oninput: move |evet| {
|
||||
log::debug!("Value is {:#?}", evet);
|
||||
set_val(evet.value);
|
||||
}
|
||||
oninput: move |evet| set_val(evet.value())
|
||||
}
|
||||
p { "Val is: {val}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
static Example: FC<()> = |ctx| {
|
||||
ctx.render(rsx! {
|
||||
div { class: "max-w-lg max-w-xs bg-blue-800 shadow-2xl rounded-lg mx-auto text-center py-12 mt-4 rounded-xl"
|
||||
div { class: "container py-5 max-w-md mx-auto"
|
||||
h1 { class: "text-gray-200 text-center font-extrabold -mt-3 text-3xl",
|
||||
h1 { class: "text-gray-200 text-center font-extrabold -mt-3 text-3xl",
|
||||
"Text Input Example"
|
||||
}
|
||||
}
|
||||
UserInput {}
|
||||
}
|
||||
}
|
||||
@@ -54,19 +51,15 @@ static Example: FC<()> = |ctx| {
|
||||
static UserInput: FC<()> = |ctx| {
|
||||
let (val, set_val) = use_state(&ctx, || "asd".to_string());
|
||||
|
||||
rsx!{ in ctx,
|
||||
rsx! { in ctx,
|
||||
div { class: "mb-4"
|
||||
input { class: "shadow appearance-none rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
placeholder: "Username"
|
||||
id: "username"
|
||||
id: "username"
|
||||
type: "text"
|
||||
oninput: move |evet| {
|
||||
log::debug!("Value is {:#?}", evet);
|
||||
set_val(evet.value);
|
||||
}
|
||||
oninput: move |evet| set_val(evet.value())
|
||||
}
|
||||
p { "Val is: {val}" }
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
@@ -6,7 +6,7 @@ use dioxus_core::prelude::*;
|
||||
use dioxus_web::*;
|
||||
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
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
|
||||
|
||||
@@ -53,7 +53,7 @@ static App: FC<()> = |ctx| {
|
||||
input {
|
||||
class: "new-todo"
|
||||
placeholder: "What needs to be done?"
|
||||
oninput: move |evt| set_draft(evt.value)
|
||||
oninput: move |evt| set_draft(evt.value())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
#[derive(Props)]
|
||||
struct MyProps<'a> {
|
||||
blah: u128,
|
||||
b: &'a (),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// let p = unsafe { MyProps {}.memoize(&MyProps {}) };
|
||||
// dbg!(p);
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus_core as dioxus;
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus::{events::on::MouseEvent, prelude::*};
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async {
|
||||
let props = ExampleProps { initial_name: "..?"};
|
||||
let props = ExampleProps {
|
||||
initial_name: "..?",
|
||||
};
|
||||
WebsysRenderer::new_with_props(Example, props)
|
||||
.run()
|
||||
.await
|
||||
@@ -25,17 +29,17 @@ static Example: FC<ExampleProps> = |ctx| {
|
||||
let name = use_state_new(&ctx, move || ctx.initial_name);
|
||||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
div {
|
||||
class: "py-12 px-4 text-center w-full max-w-2xl mx-auto"
|
||||
span {
|
||||
span {
|
||||
class: "text-sm font-semibold"
|
||||
"Dioxus Example: Jack and Jill"
|
||||
}
|
||||
h2 {
|
||||
class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
|
||||
h2 {
|
||||
class: "text-5xl mt-2 mb-6 leading-tight font-semibold font-heading"
|
||||
"Hello, {name}"
|
||||
}
|
||||
|
||||
|
||||
CustomButton { name: "Jack!", handler: move |_| name.set("Jack") }
|
||||
CustomButton { name: "Jill!", handler: move |_| name.set("Jill") }
|
||||
CustomButton { name: "Bob!", handler: move |_| name.set("Bob")}
|
||||
@@ -45,12 +49,10 @@ static Example: FC<ExampleProps> = |ctx| {
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
|
||||
#[derive(Props)]
|
||||
struct ButtonProps<'src, F: Fn(MouseEvent)> {
|
||||
struct ButtonProps<'src, F: Fn(Rc<dyn MouseEvent>)> {
|
||||
name: &'src str,
|
||||
handler: F
|
||||
handler: F,
|
||||
}
|
||||
|
||||
fn CustomButton<'a, F: Fn(MouseEvent)>(ctx: Context<'a, ButtonProps<'a, F>>) -> VNode {
|
||||
@@ -69,13 +71,12 @@ impl<F: Fn(MouseEvent)> PartialEq for ButtonProps<'_, F> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct PlaceholderProps {
|
||||
val: &'static str
|
||||
val: &'static str,
|
||||
}
|
||||
fn Placeholder(ctx: Context<PlaceholderProps>) -> VNode {
|
||||
ctx.render(rsx!{
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
"child: {ctx.val}"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_web::{prelude::*, WebsysRenderer};
|
||||
|
||||
mod filtertoggles;
|
||||
mod recoil;
|
||||
mod state;
|
||||
mod todoitem;
|
||||
mod todolist;
|
||||
// mod filtertoggles;
|
||||
// mod recoil;
|
||||
// mod state;
|
||||
// mod todoitem;
|
||||
// mod todolist;
|
||||
|
||||
static APP_STYLE: &'static str = include_str!("./style.css");
|
||||
|
||||
@@ -13,10 +14,10 @@ fn main() {
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
id: "app"
|
||||
style { "{APP_STYLE}" }
|
||||
// style { "{APP_STYLE}" }
|
||||
|
||||
// list
|
||||
todolist::TodoList {}
|
||||
// todolist::TodoList {}
|
||||
|
||||
// footer
|
||||
footer {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
@@ -65,7 +66,7 @@ pub fn TodoList(ctx: Context<()>) -> VNode {
|
||||
class: "new-todo"
|
||||
placeholder: "What needs to be done?"
|
||||
value: "{draft}"
|
||||
oninput: move |evt| set_draft(evt.value)
|
||||
oninput: move |evt| set_draft(evt.value())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +79,7 @@ pub fn TodoList(ctx: Context<()>) -> VNode {
|
||||
FilterState::Completed => item.checked,
|
||||
})
|
||||
.map(|(id, item)| {
|
||||
TodoEntry!();
|
||||
// TodoEntry!();
|
||||
todo!()
|
||||
// rsx!(TodoEntry {
|
||||
// key: "{order}",
|
||||
@@ -100,17 +101,14 @@ pub struct TodoEntryProps {
|
||||
item: Rc<TodoItem>,
|
||||
}
|
||||
|
||||
pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> VNode {
|
||||
// #[inline_props]
|
||||
pub fn TodoEntry(
|
||||
ctx: Context,
|
||||
baller: &impl Fn() -> (),
|
||||
caller: &impl Fn() -> (),
|
||||
todo: &Rc<TodoItem>,
|
||||
) -> VNode {
|
||||
// pub fn TodoEntry(ctx: Context, todo: &Rc<TodoItem>) -> VNode {
|
||||
pub fn TodoEntry(ctx: Context<TodoEntryProps>) -> VNode {
|
||||
let (is_editing, set_is_editing) = use_state(&ctx, || false);
|
||||
// let todo = &ctx.item;
|
||||
let contents = "";
|
||||
let todo = TodoItem {
|
||||
checked: false,
|
||||
contents: "asd".to_string(),
|
||||
id: uuid::Uuid::new_v4(),
|
||||
};
|
||||
|
||||
ctx.render(rsx! (
|
||||
li {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//! Here, we show to use Dioxus' Recoil state management solution to simplify app logic
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus_web::dioxus::prelude::*;
|
||||
use recoil::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
+48
-40
@@ -63,62 +63,70 @@ impl WebsysRenderer {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
|
||||
let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
|
||||
// let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
|
||||
|
||||
let body_element = prepare_websys_dom();
|
||||
|
||||
let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), move |ev| {
|
||||
log::debug!("Event trigger! {:#?}", ev);
|
||||
let mut c = sender.clone();
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
c.send(ev).await.unwrap();
|
||||
});
|
||||
});
|
||||
// let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), move |ev| {
|
||||
// log::debug!("Event trigger! {:#?}", ev);
|
||||
// let mut c = sender.clone();
|
||||
// wasm_bindgen_futures::spawn_local(async move {
|
||||
// c.send(ev).await.unwrap();
|
||||
// });
|
||||
// });
|
||||
|
||||
let root_node = body_element.first_child().unwrap();
|
||||
patch_machine.stack.push(root_node.clone());
|
||||
|
||||
let mut websys_dom = crate::new::WebsysDom::new(body_element);
|
||||
|
||||
websys_dom.stack.push(root_node);
|
||||
// patch_machine.stack.push(root_node.clone());
|
||||
|
||||
// todo: initialize the event registry properly on the root
|
||||
|
||||
let edits = self.internal_dom.rebuild()?;
|
||||
log::debug!("Received edits: {:#?}", edits);
|
||||
edits.iter().for_each(|edit| {
|
||||
log::debug!("patching with {:?}", edit);
|
||||
patch_machine.handle_edit(edit);
|
||||
});
|
||||
self.internal_dom.rebuild(&mut websys_dom)?;
|
||||
// let edits = self.internal_dom.rebuild()?;
|
||||
// log::debug!("Received edits: {:#?}", edits);
|
||||
// edits.iter().for_each(|edit| {
|
||||
// log::debug!("patching with {:?}", edit);
|
||||
// patch_machine.handle_edit(edit);
|
||||
// });
|
||||
|
||||
patch_machine.reset();
|
||||
let root_node = body_element.first_child().unwrap();
|
||||
patch_machine.stack.push(root_node.clone());
|
||||
// patch_machine.reset();
|
||||
// let root_node = body_element.first_child().unwrap();
|
||||
// patch_machine.stack.push(root_node.clone());
|
||||
|
||||
// log::debug!("patch stack size {:?}", patch_machine.stack);
|
||||
|
||||
// Event loop waits for the receiver to finish up
|
||||
// TODO! Connect the sender to the virtual dom's suspense system
|
||||
// Suspense is basically an external event that can force renders to specific nodes
|
||||
while let Ok(event) = receiver.recv().await {
|
||||
log::debug!("Stack before entrance {:#?}", patch_machine.stack.top());
|
||||
// log::debug!("patch stack size before {:#?}", patch_machine.stack);
|
||||
// patch_machine.reset();
|
||||
// patch_machine.stack.push(root_node.clone());
|
||||
let edits = self.internal_dom.progress_with_event(event)?;
|
||||
log::debug!("Received edits: {:#?}", edits);
|
||||
// while let Ok(event) = receiver.recv().await {
|
||||
// log::debug!("Stack before entrance {:#?}", patch_machine.stack.top());
|
||||
// log::debug!("patch stack size before {:#?}", patch_machine.stack);
|
||||
// patch_machine.reset();
|
||||
// patch_machine.stack.push(root_node.clone());
|
||||
// self.internal_dom
|
||||
// .progress_with_event(&mut websys_dom, event)?;
|
||||
// let edits = self.internal_dom.progress_with_event(event)?;
|
||||
// log::debug!("Received edits: {:#?}", edits);
|
||||
|
||||
for edit in &edits {
|
||||
// log::debug!("edit stream {:?}", edit);
|
||||
// log::debug!("Stream stack {:#?}", patch_machine.stack.top());
|
||||
patch_machine.handle_edit(edit);
|
||||
}
|
||||
// for edit in &edits {
|
||||
// // log::debug!("edit stream {:?}", edit);
|
||||
// // log::debug!("Stream stack {:#?}", patch_machine.stack.top());
|
||||
// patch_machine.handle_edit(edit);
|
||||
// }
|
||||
|
||||
// log::debug!("patch stack size after {:#?}", patch_machine.stack);
|
||||
patch_machine.reset();
|
||||
// our root node reference gets invalidated
|
||||
// not sure why
|
||||
// for now, just select the first child again.
|
||||
// eventually, we'll just make our own root element instead of using body
|
||||
// or just use body directly IDEK
|
||||
let root_node = body_element.first_child().unwrap();
|
||||
patch_machine.stack.push(root_node.clone());
|
||||
}
|
||||
// log::debug!("patch stack size after {:#?}", patch_machine.stack);
|
||||
// patch_machine.reset();
|
||||
// our root node reference gets invalidated
|
||||
// not sure why
|
||||
// for now, just select the first child again.
|
||||
// eventually, we'll just make our own root element instead of using body
|
||||
// or just use body directly IDEK
|
||||
// let root_node = body_element.first_child().unwrap();
|
||||
// patch_machine.stack.push(root_node.clone());
|
||||
// }
|
||||
|
||||
Ok(()) // should actually never return from this, should be an error, rustc just cant see it
|
||||
}
|
||||
|
||||
+275
-2
@@ -1,2 +1,275 @@
|
||||
pub struct WebsysDom {}
|
||||
impl WebsysDom {}
|
||||
use std::collections::HashMap;
|
||||
|
||||
use dioxus_core::virtual_dom::RealDomNode;
|
||||
use nohash_hasher::IntMap;
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{
|
||||
window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
|
||||
};
|
||||
|
||||
use crate::interpreter::Stack;
|
||||
pub struct WebsysDom {
|
||||
pub stack: Stack,
|
||||
nodes: IntMap<u32, Node>,
|
||||
document: Document,
|
||||
root: Element,
|
||||
|
||||
// We need to make sure to add comments between text nodes
|
||||
// We ensure that the text siblings are patched by preventing the browser from merging
|
||||
// neighboring text nodes. Originally inspired by some of React's work from 2016.
|
||||
// -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
|
||||
// -> https://github.com/facebook/react/pull/5753
|
||||
//
|
||||
// `ptns` = Percy text node separator
|
||||
// TODO
|
||||
last_node_was_text: bool,
|
||||
|
||||
node_counter: Counter,
|
||||
}
|
||||
impl WebsysDom {
|
||||
pub fn new(root: Element) -> Self {
|
||||
let document = window()
|
||||
.expect("must have access to the window")
|
||||
.document()
|
||||
.expect("must have access to the Document");
|
||||
|
||||
Self {
|
||||
stack: Stack::with_capacity(10),
|
||||
nodes: HashMap::with_capacity_and_hasher(
|
||||
1000,
|
||||
nohash_hasher::BuildNoHashHasher::default(),
|
||||
),
|
||||
document,
|
||||
root,
|
||||
last_node_was_text: false,
|
||||
node_counter: Counter(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Counter(u32);
|
||||
impl Counter {
|
||||
fn next(&mut self) -> u32 {
|
||||
self.0 += 1;
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl dioxus_core::diff::RealDom for WebsysDom {
|
||||
fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
|
||||
let domnode = self.nodes.get(&root.0).expect("Failed to pop know root");
|
||||
self.stack.push(domnode.clone());
|
||||
}
|
||||
|
||||
fn append_child(&mut self) {
|
||||
let child = self.stack.pop();
|
||||
|
||||
if child.dyn_ref::<web_sys::Text>().is_some() {
|
||||
if self.last_node_was_text {
|
||||
let comment_node = self
|
||||
.document
|
||||
.create_comment("dioxus")
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
self.stack.top().append_child(&comment_node).unwrap();
|
||||
}
|
||||
self.last_node_was_text = true;
|
||||
} else {
|
||||
self.last_node_was_text = false;
|
||||
}
|
||||
|
||||
self.stack.top().append_child(&child).unwrap();
|
||||
}
|
||||
|
||||
fn replace_with(&mut self) {
|
||||
let new_node = self.stack.pop();
|
||||
let old_node = self.stack.pop();
|
||||
|
||||
if old_node.has_type::<Element>() {
|
||||
old_node
|
||||
.dyn_ref::<Element>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else if old_node.has_type::<web_sys::CharacterData>() {
|
||||
old_node
|
||||
.dyn_ref::<web_sys::CharacterData>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else if old_node.has_type::<web_sys::DocumentType>() {
|
||||
old_node
|
||||
.dyn_ref::<web_sys::DocumentType>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("Cannot replace node: {:?}", old_node);
|
||||
}
|
||||
|
||||
// // poc to see if this is a valid solution
|
||||
// if let Some(id) = self.current_known {
|
||||
// // update mapping
|
||||
// self.known_roots.insert(id, new_node.clone());
|
||||
// self.current_known = None;
|
||||
// }
|
||||
|
||||
self.stack.push(new_node);
|
||||
}
|
||||
|
||||
fn remove(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove_all_children(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
let nid = self.node_counter.next();
|
||||
let textnode = self
|
||||
.document
|
||||
.create_text_node(text)
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
self.stack.push(textnode.clone());
|
||||
self.nodes.insert(nid, textnode);
|
||||
|
||||
RealDomNode::new(nid)
|
||||
}
|
||||
|
||||
fn create_element(&mut self, tag: &str) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
let el = self
|
||||
.document
|
||||
.create_element(tag)
|
||||
.unwrap()
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
|
||||
self.stack.push(el.clone());
|
||||
let nid = self.node_counter.next();
|
||||
self.nodes.insert(nid, el);
|
||||
RealDomNode::new(nid)
|
||||
}
|
||||
|
||||
fn create_element_ns(
|
||||
&mut self,
|
||||
tag: &str,
|
||||
namespace: &str,
|
||||
) -> dioxus_core::virtual_dom::RealDomNode {
|
||||
let el = self
|
||||
.document
|
||||
.create_element_ns(Some(namespace), tag)
|
||||
.unwrap()
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
|
||||
self.stack.push(el.clone());
|
||||
let nid = self.node_counter.next();
|
||||
self.nodes.insert(nid, el);
|
||||
RealDomNode::new(nid)
|
||||
}
|
||||
|
||||
fn new_event_listener(
|
||||
&mut self,
|
||||
event: &str,
|
||||
scope: dioxus_core::prelude::ScopeIdx,
|
||||
id: usize,
|
||||
) {
|
||||
// if let Some(entry) = self.listeners.get_mut(event) {
|
||||
// entry.0 += 1;
|
||||
// } else {
|
||||
// let trigger = self.trigger.clone();
|
||||
// let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
||||
// log::debug!("Handling event!");
|
||||
|
||||
// let target = event
|
||||
// .target()
|
||||
// .expect("missing target")
|
||||
// .dyn_into::<Element>()
|
||||
// .expect("not a valid element");
|
||||
|
||||
// let typ = event.type_();
|
||||
|
||||
// let gi_id: Option<usize> = target
|
||||
// .get_attribute(&format!("dioxus-giid-{}", typ))
|
||||
// .and_then(|v| v.parse().ok());
|
||||
|
||||
// let gi_gen: Option<u64> = target
|
||||
// .get_attribute(&format!("dioxus-gigen-{}", typ))
|
||||
// .and_then(|v| v.parse().ok());
|
||||
|
||||
// let li_idx: Option<usize> = target
|
||||
// .get_attribute(&format!("dioxus-lidx-{}", typ))
|
||||
// .and_then(|v| v.parse().ok());
|
||||
|
||||
// if let (Some(gi_id), Some(gi_gen), Some(li_idx)) = (gi_id, gi_gen, li_idx) {
|
||||
// // Call the trigger
|
||||
// log::debug!(
|
||||
// "decoded gi_id: {}, gi_gen: {}, li_idx: {}",
|
||||
// gi_id,
|
||||
// gi_gen,
|
||||
// li_idx
|
||||
// );
|
||||
|
||||
// let triggered_scope = ScopeIdx::from_raw_parts(gi_id, gi_gen);
|
||||
// trigger.0.as_ref()(EventTrigger::new(
|
||||
// virtual_event_from_websys_event(event),
|
||||
// triggered_scope,
|
||||
// // scope,
|
||||
// li_idx,
|
||||
// ));
|
||||
// }
|
||||
// }) as Box<dyn FnMut(&Event)>);
|
||||
|
||||
// self.root
|
||||
// .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
|
||||
// .unwrap();
|
||||
|
||||
// // Increment the listeners
|
||||
// self.listeners.insert(event.into(), (1, handler));
|
||||
// }
|
||||
}
|
||||
|
||||
fn remove_event_listener(&mut self, event: &str) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_text(&mut self, text: &str) {
|
||||
self.stack.top().set_text_content(Some(text))
|
||||
}
|
||||
|
||||
fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
|
||||
if name == "class" {
|
||||
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||
el.set_class_name(value);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_attribute(&mut self, name: &str) {
|
||||
let node = self.stack.top();
|
||||
if let Some(node) = node.dyn_ref::<web_sys::Element>() {
|
||||
node.remove_attribute(name).unwrap();
|
||||
}
|
||||
if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if name == "value" {
|
||||
node.set_value("");
|
||||
}
|
||||
if name == "checked" {
|
||||
node.set_checked(false);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
|
||||
if name == "selected" {
|
||||
node.set_selected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user