wip: enable more diffing
This commit is contained in:
1 parent
b5e5ef171a
commit
e8f29a8f8a
26 files changed
+1326
-367
No files matched your search
+1
-1
@@ -60,7 +60,7 @@ members = [
|
|||||||
"packages/html-namespace",
|
"packages/html-namespace",
|
||||||
"packages/web",
|
"packages/web",
|
||||||
# "packages/webview"
|
# "packages/webview"
|
||||||
# "packages/cli",
|
"packages/cli",
|
||||||
# "packages/atoms",
|
# "packages/atoms",
|
||||||
# "packages/ssr",
|
# "packages/ssr",
|
||||||
# "packages/docsite",
|
# "packages/docsite",
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font: 100 14px 'Roboto';
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: block;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
box-shadow: inset 0px 0px 80px 0px rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper {
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 320px;
|
||||||
|
height: 520px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: black;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wrapper .calculator {
|
||||||
|
box-shadow: 0px 0px 20px 0px #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-display {
|
||||||
|
color: white;
|
||||||
|
background: #1c191c;
|
||||||
|
line-height: 130px;
|
||||||
|
font-size: 6em;
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto-scaling-text {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-display .auto-scaling-text {
|
||||||
|
padding: 0 30px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
transform-origin: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-keypad {
|
||||||
|
height: 400px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator .input-keys {
|
||||||
|
width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator .function-keys {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator .digit-keys {
|
||||||
|
background: #e0e0e7;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator-key {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-top: 1px solid #777;
|
||||||
|
border-right: 1px solid #666;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 80px;
|
||||||
|
}
|
||||||
|
.calculator .function-keys .calculator-key {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
.calculator .function-keys .key-multiply {
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
.calculator .digit-keys .calculator-key {
|
||||||
|
font-size: 2.25em;
|
||||||
|
}
|
||||||
|
.calculator .digit-keys .key-0 {
|
||||||
|
width: 160px;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
.calculator .digit-keys .key-dot {
|
||||||
|
padding-top: 1em;
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
.calculator .operator-keys .calculator-key {
|
||||||
|
color: white;
|
||||||
|
border-right: 0;
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculator .function-keys {
|
||||||
|
background: linear-gradient(to bottom, rgba(202,202,204,1) 0%, rgba(196,194,204,1) 100%);
|
||||||
|
}
|
||||||
|
.calculator .operator-keys {
|
||||||
|
background: linear-gradient(to bottom, rgba(252,156,23,1) 0%, rgba(247,126,27,1) 100%);
|
||||||
|
}
|
||||||
@@ -98,16 +98,16 @@ static App: FC<()> = |cx| {
|
|||||||
CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" }
|
CalculatorKey { name: "key-0", onclick: move |_| input_digit(0), "0" }
|
||||||
CalculatorKey { name: "key-dot", onclick: move |_| input_dot(), "●" }
|
CalculatorKey { name: "key-dot", onclick: move |_| input_dot(), "●" }
|
||||||
|
|
||||||
{(1..9).map(|k| rsx!{
|
{(1..9).map(move |k| rsx!{
|
||||||
CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" }
|
CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" }
|
||||||
})}
|
})}
|
||||||
}
|
}
|
||||||
div { class: "operator-keys"
|
div { class: "operator-keys"
|
||||||
CalculatorKey { name:"key-divide", onclick: move |_| operator.set(Some(Operator::Div)) "÷" }
|
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-multiply", onclick: move |_| operator.set(Some(Operator::Mul)) "×" }
|
||||||
CalculatorKey { name:"key-subtract", onclick: move |_| operator.set(Some(Operator::Sub)) "−" }
|
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-add", onclick: move |_| operator.set(Some(Operator::Add)) "+" }
|
||||||
CalculatorKey { name:"key-equals", onclick: move |_| perform_operation() "=" }
|
CalculatorKey { name: "key-equals", onclick: move |_| perform_operation() "=" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,10 @@ static App: FC<()> = |cx| {
|
|||||||
|
|
||||||
#[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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
fn main() {}
|
||||||
|
|
||||||
|
struct Title(String);
|
||||||
|
|
||||||
|
struct Position([f32; 3]);
|
||||||
|
|
||||||
|
struct Velocity([f32; 3]);
|
||||||
|
|
||||||
|
type Batch<T> = fn(&mut T) -> ();
|
||||||
|
|
||||||
|
static Atom: Batch<(Title, Position, Velocity)> = |_| {};
|
||||||
|
|
||||||
|
enum VNode<'a> {
|
||||||
|
El(El<'a>),
|
||||||
|
Text(&'a str),
|
||||||
|
Fragment(&'a [VNode<'a>]),
|
||||||
|
}
|
||||||
|
struct El<'a> {
|
||||||
|
name: &'static str,
|
||||||
|
key: Option<&'a str>,
|
||||||
|
attrs: &'a [(&'static str, AttrType<'a>)],
|
||||||
|
children: &'a [El<'a>],
|
||||||
|
}
|
||||||
|
enum AttrType<'a> {
|
||||||
|
Numeric(usize),
|
||||||
|
Text(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn example() {
|
||||||
|
use AttrType::Numeric;
|
||||||
|
let el = El {
|
||||||
|
name: "div",
|
||||||
|
attrs: &[("type", Numeric(10)), ("type", Numeric(10))],
|
||||||
|
key: None,
|
||||||
|
children: &[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use dioxus::prelude::bumpalo::Bump;
|
||||||
|
trait IntoVnode {
|
||||||
|
fn into_vnode<'a>(self, b: &'a Bump) -> VNode<'a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for VNode<'a> {
|
||||||
|
type Item = VNode<'a>;
|
||||||
|
type IntoIter = std::iter::Once<VNode<'a>>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
std::iter::once(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_iterable<F: IntoVnode>(f: impl IntoIterator<Item = F>) {
|
||||||
|
let iter = f.into_iter();
|
||||||
|
let b = Bump::new();
|
||||||
|
for f in iter {
|
||||||
|
let v = f.into_vnode(&b);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
static Example: FC<()> = |cx| {
|
||||||
|
let (g, set_g) = use_state(&cx, || 0);
|
||||||
|
let v = (0..10).map(move |f| {
|
||||||
|
rsx!(li {
|
||||||
|
onclick: move |_| set_g(10)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
{v}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
+42
-27
@@ -4,51 +4,54 @@
|
|||||||
//! Some components benefit through the use of "Models". Models are a single block of encapsulated state that allow mutative
|
//! Some components benefit through the use of "Models". Models are a single block of encapsulated state that allow mutative
|
||||||
//! methods to be performed on them. Dioxus exposes the ability to use the model pattern through the "use_model" hook.
|
//! methods to be performed on them. Dioxus exposes the ability to use the model pattern through the "use_model" hook.
|
||||||
//!
|
//!
|
||||||
|
//! Models are commonly used in the "Model-View-Component" approach for building UI state.
|
||||||
|
//!
|
||||||
//! `use_model` is basically just a fancy wrapper around set_state, but saves a "working copy" of the new state behind a
|
//! `use_model` is basically just a fancy wrapper around set_state, but saves a "working copy" of the new state behind a
|
||||||
//! RefCell. To modify the working copy, you need to call "modify" which returns the RefMut. This makes it easy to write
|
//! RefCell. To modify the working copy, you need to call "get_mut" which returns the RefMut. This makes it easy to write
|
||||||
//! fully encapsulated apps that retain a certain feel of native Rusty-ness. A calculator app is a good example of when this
|
//! fully encapsulated apps that retain a certain feel of native Rusty-ness. A calculator app is a good example of when this
|
||||||
//! is useful.
|
//! is useful.
|
||||||
|
//!
|
||||||
|
//! Do note that "get_mut" returns a `RefMut` (a lock over a RefCell). If two `RefMut`s are held at the same time (ie in a loop)
|
||||||
|
//! the RefCell will panic and crash. You can use `try_get_mut` or `.modify` to avoid this problem, or just not hold two
|
||||||
|
//! RefMuts at the same time.
|
||||||
|
|
||||||
use dioxus::events::on::*;
|
use dioxus::events::on::*;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use std::{
|
|
||||||
cell::RefCell,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
dioxus::webview::launch(App)
|
dioxus::webview::launch(App)
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: FC<()> = |cx| {
|
static App: FC<()> = |cx| {
|
||||||
let state = use_model(&cx, || CalculatorState::new());
|
let calc = use_model(&cx, || Calculator::new());
|
||||||
|
|
||||||
let clear_display = state.display_value.eq("0");
|
let clear_display = calc.display_value.eq("0");
|
||||||
let clear_text = if clear_display { "C" } else { "AC" };
|
let clear_text = if clear_display { "C" } else { "AC" };
|
||||||
let formatted = state.formatted();
|
let formatted = calc.formatted();
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { class: "calculator", onkeydown: move |evt| state.modify().handle_keydown(evt),
|
div { class: "calculator", onkeydown: move |evt| calc.get_mut().handle_keydown(evt),
|
||||||
div { class: "calculator-display", "{formatted}"}
|
div { class: "calculator-display","{formatted}"}
|
||||||
div { class: "input-keys"
|
div { class: "input-keys"
|
||||||
div { class: "function-keys"
|
div { class: "function-keys"
|
||||||
CalculatorKey { name: "key-clear", onclick: move |_| state.modify().clear_display() "{clear_text}" }
|
// All there ways that the calculator may be modified:
|
||||||
CalculatorKey { name: "key-sign", onclick: move |_| state.modify().toggle_sign(), "±"}
|
CalculatorKey { name: "key-clear", onclick: move |_| calc.get_mut().clear_display() "{clear_text}" }
|
||||||
CalculatorKey { name: "key-percent", onclick: move |_| state.modify().toggle_percent() "%"}
|
CalculatorKey { name: "key-sign", onclick: move |_| calc.modify(Calculator::toggle_sign), "±"}
|
||||||
|
CalculatorKey { name: "key-percent", onclick: move |_| calc.modify(|f| f.toggle_percent()) "%"}
|
||||||
}
|
}
|
||||||
div { class: "digit-keys"
|
div { class: "digit-keys"
|
||||||
CalculatorKey { name: "key-0", onclick: move |_| state.modify().input_digit(0), "0" }
|
CalculatorKey { name: "key-0", onclick: move |_| calc.get_mut().input_digit(0), "0" }
|
||||||
CalculatorKey { name: "key-dot", onclick: move |_| state.modify().input_dot(), "●" }
|
CalculatorKey { name: "key-dot", onclick: move |_| calc.get_mut().input_dot(), "●" }
|
||||||
{(1..9).map(|k| rsx!{
|
{(1..9).map(move |k| rsx!{
|
||||||
CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| state.modify().input_digit(k), "{k}" }
|
CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| calc.get_mut().input_digit(k), "{k}" }
|
||||||
})}
|
})}
|
||||||
}
|
}
|
||||||
div { class: "operator-keys"
|
div { class: "operator-keys"
|
||||||
CalculatorKey { name:"key-divide", onclick: move |_| state.modify().set_operator(Operator::Div) "÷" }
|
CalculatorKey { name:"key-divide", onclick: move |_| calc.get_mut().set_operator(Operator::Div) "÷" }
|
||||||
CalculatorKey { name:"key-multiply", onclick: move |_| state.modify().set_operator(Operator::Mul) "×" }
|
CalculatorKey { name:"key-multiply", onclick: move |_| calc.get_mut().set_operator(Operator::Mul) "×" }
|
||||||
CalculatorKey { name:"key-subtract", onclick: move |_| state.modify().set_operator(Operator::Sub) "−" }
|
CalculatorKey { name:"key-subtract", onclick: move |_| calc.get_mut().set_operator(Operator::Sub) "−" }
|
||||||
CalculatorKey { name:"key-add", onclick: move |_| state.modify().set_operator(Operator::Add) "+" }
|
CalculatorKey { name:"key-add", onclick: move |_| calc.get_mut().set_operator(Operator::Add) "+" }
|
||||||
CalculatorKey { name:"key-equals", onclick: move |_| state.modify().perform_operation() "=" }
|
CalculatorKey { name:"key-equals", onclick: move |_| calc.get_mut().perform_operation() "=" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +59,7 @@ static App: FC<()> = |cx| {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CalculatorState {
|
struct Calculator {
|
||||||
display_value: String,
|
display_value: String,
|
||||||
operator: Option<Operator>,
|
operator: Option<Operator>,
|
||||||
cur_val: f64,
|
cur_val: f64,
|
||||||
@@ -68,9 +71,10 @@ enum Operator {
|
|||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
}
|
}
|
||||||
impl CalculatorState {
|
|
||||||
|
impl Calculator {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
CalculatorState {
|
Calculator {
|
||||||
display_value: "0".to_string(),
|
display_value: "0".to_string(),
|
||||||
operator: None,
|
operator: None,
|
||||||
cur_val: 0.0,
|
cur_val: 0.0,
|
||||||
@@ -118,7 +122,9 @@ impl CalculatorState {
|
|||||||
self.display_value.pop();
|
self.display_value.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn set_operator(&mut self, operator: Operator) {}
|
fn set_operator(&mut self, operator: Operator) {
|
||||||
|
self.operator = Some(operator)
|
||||||
|
}
|
||||||
fn handle_keydown(&mut self, evt: KeyboardEvent) {
|
fn handle_keydown(&mut self, evt: KeyboardEvent) {
|
||||||
match evt.key_code() {
|
match evt.key_code() {
|
||||||
KeyCode::Backspace => self.backspace(),
|
KeyCode::Backspace => self.backspace(),
|
||||||
@@ -160,6 +166,10 @@ fn CalculatorKey<'a, 'r>(cx: Context<'a, CalculatorKeyProps<'r>>) -> VNode<'a> {
|
|||||||
use odl::use_model;
|
use odl::use_model;
|
||||||
mod odl {
|
mod odl {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
/// Use model makes it easy to use "models" as state for components. To modify the model, call "modify" and a clone of the
|
/// Use model makes it easy to use "models" as state for components. To modify the model, call "modify" and a clone of the
|
||||||
/// current model will be made, with a RefMut lock on it. Dioxus will never run your components multithreaded, so you can
|
/// current model will be made, with a RefMut lock on it. Dioxus will never run your components multithreaded, so you can
|
||||||
/// be relatively sure that this won't fail in practice
|
/// be relatively sure that this won't fail in practice
|
||||||
@@ -208,9 +218,14 @@ mod odl {
|
|||||||
wip: RefCell::new(t),
|
wip: RefCell::new(t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn modify(&self) -> RefMut<'_, T> {
|
pub fn get_mut(&self) -> RefMut<'_, T> {
|
||||||
self.wip.borrow_mut()
|
self.wip.borrow_mut()
|
||||||
}
|
}
|
||||||
|
pub fn modify(&self, f: impl FnOnce(&mut T)) {
|
||||||
|
let mut g = self.get_mut();
|
||||||
|
let r = g.deref_mut();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|||||||
@@ -2,9 +2,17 @@ use dioxus::prelude::*;
|
|||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
static Example: FC<()> = |cx| {
|
static Example: FC<()> = |cx| {
|
||||||
|
let (g, set_g) = use_state(&cx, || 0);
|
||||||
|
let v = (0..10).map(|f| {
|
||||||
|
rsx! {
|
||||||
|
li {
|
||||||
|
onclick: move |_| set_g(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
{v}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// mod iterator;
|
||||||
|
// mod memo;
|
||||||
|
mod iterators;
|
||||||
|
mod listener;
|
||||||
|
mod memo;
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
mod iterator;
|
|
||||||
mod memo;
|
|
||||||
@@ -70,15 +70,20 @@ pub fn build(config: &Config, _build_config: &BuildConfig) -> Result<()> {
|
|||||||
// [3] Bindgen the final binary for use easy linking
|
// [3] Bindgen the final binary for use easy linking
|
||||||
let mut bindgen_builder = Bindgen::new();
|
let mut bindgen_builder = Bindgen::new();
|
||||||
|
|
||||||
|
let release_type = match config.release {
|
||||||
|
true => "release",
|
||||||
|
false => "debug",
|
||||||
|
};
|
||||||
|
|
||||||
let input_path = match executable {
|
let input_path = match executable {
|
||||||
ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir
|
ExecutableType::Binary(name) | ExecutableType::Lib(name) => target_dir
|
||||||
// .join("wasm32-unknown-unknown/release")
|
// .join("wasm32-unknown-unknown/release")
|
||||||
.join("wasm32-unknown-unknown/debug")
|
.join(format!("wasm32-unknown-unknown/{}", release_type))
|
||||||
.join(format!("{}.wasm", name)),
|
.join(format!("{}.wasm", name)),
|
||||||
|
|
||||||
ExecutableType::Example(name) => target_dir
|
ExecutableType::Example(name) => target_dir
|
||||||
// .join("wasm32-unknown-unknown/release/examples")
|
// .join("wasm32-unknown-unknown/release/examples")
|
||||||
.join("wasm32-unknown-unknown/debug/examples")
|
.join(format!("wasm32-unknown-unknown/{}/examples", release_type))
|
||||||
.join(format!("{}.wasm", name)),
|
.join(format!("{}.wasm", name)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -68,19 +68,43 @@ impl ToTokens for Element {
|
|||||||
let name = &self.name.to_string();
|
let name = &self.name.to_string();
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
dioxus::builder::ElementBuilder::new(__cx, #name)
|
__cx.element(#name)
|
||||||
});
|
});
|
||||||
|
// dioxus::builder::ElementBuilder::new(__cx, #name)
|
||||||
|
// dioxus::builder::ElementBuilder::new(__cx, #name)
|
||||||
|
|
||||||
|
// Add attributes
|
||||||
|
// TODO: conver to the "attrs" syntax for compile-time known sizes
|
||||||
for attr in self.attrs.iter() {
|
for attr in self.attrs.iter() {
|
||||||
attr.to_tokens(tokens);
|
attr.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut children = self.children.iter();
|
// let mut children = self.children.iter();
|
||||||
while let Some(child) = children.next() {
|
// while let Some(child) = children.next() {
|
||||||
let inner_toks = child.to_token_stream();
|
// let inner_toks = child.to_token_stream();
|
||||||
|
// tokens.append_all(quote! {
|
||||||
|
// .iter_child(#inner_toks)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
let mut childs = quote! {};
|
||||||
|
for child in &self.children {
|
||||||
|
match child {
|
||||||
|
Node::Text(e) => e.to_tokens(&mut childs),
|
||||||
|
Node::Element(e) => e.to_tokens(&mut childs),
|
||||||
|
Node::RawExpr(e) => quote! {
|
||||||
|
__cx.fragment_from_iter(#e)
|
||||||
|
}
|
||||||
|
.to_tokens(&mut childs),
|
||||||
|
}
|
||||||
|
childs.append_all(quote! {,})
|
||||||
|
}
|
||||||
|
if self.children.len() > 0 {
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
.iter_child(#inner_toks)
|
.children([
|
||||||
})
|
#childs
|
||||||
|
])
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
|
|||||||
@@ -99,14 +99,14 @@ impl ToTokens for RsxRender {
|
|||||||
// The `in cx` pattern allows directly rendering
|
// The `in cx` pattern allows directly rendering
|
||||||
Some(ident) => out_tokens.append_all(quote! {
|
Some(ident) => out_tokens.append_all(quote! {
|
||||||
#ident.render(dioxus::prelude::LazyNodes::new(move |__cx|{
|
#ident.render(dioxus::prelude::LazyNodes::new(move |__cx|{
|
||||||
let bump = &__cx.bump();
|
// let bump = &__cx.bump();
|
||||||
#inner
|
#inner
|
||||||
}))
|
}))
|
||||||
}),
|
}),
|
||||||
// Otherwise we just build the LazyNode wrapper
|
// Otherwise we just build the LazyNode wrapper
|
||||||
None => out_tokens.append_all(quote! {
|
None => out_tokens.append_all(quote! {
|
||||||
dioxus::prelude::LazyNodes::new(move |__cx: &NodeFactory|{
|
dioxus::prelude::LazyNodes::new(move |__cx: &NodeFactory|{
|
||||||
let bump = &__cx.bump();
|
// let bump = &__cx.bump();
|
||||||
#inner
|
#inner
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -61,13 +61,7 @@ impl ToTokens for TextNode {
|
|||||||
// todo: use heuristics to see if we can promote to &static str
|
// todo: use heuristics to see if we can promote to &static str
|
||||||
let token_stream = &self.0.to_token_stream();
|
let token_stream = &self.0.to_token_stream();
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
{
|
__cx.text(format_args_f!(#token_stream))
|
||||||
// use bumpalo::core_alloc::fmt::Write;
|
|
||||||
// let mut s = bumpalo::collections::String::new_in(bump);
|
|
||||||
// s.write_fmt(format_args_f!(#token_stream)).unwrap();
|
|
||||||
dioxus::builder::text3(bump, format_args_f!(#token_stream))
|
|
||||||
// dioxus::builder::text2(s)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ static HTML_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
|
|||||||
"source",
|
"source",
|
||||||
"span",
|
"span",
|
||||||
"strong",
|
"strong",
|
||||||
// "style",
|
"style",
|
||||||
"sub",
|
"sub",
|
||||||
"summary",
|
"summary",
|
||||||
"sup",
|
"sup",
|
||||||
|
|||||||
+371
-202
@@ -135,197 +135,357 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
||||||
When re-entering, we reuse the EditList in DiffState
|
When re-entering, we reuse the EditList in DiffState
|
||||||
*/
|
*/
|
||||||
match old_node {
|
log::debug!("diffing...");
|
||||||
VNode::Element(old) => match new_node {
|
match (old_node, new_node) {
|
||||||
// New node is an element, old node was en element, need to investiage more deeply
|
// Handle the "sane" cases first.
|
||||||
VNode::Element(new) => {
|
// The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
|
||||||
// If the element type is completely different, the element needs to be re-rendered completely
|
// So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
|
||||||
// This is an optimization React makes due to how users structure their code
|
(VNode::Text(old), VNode::Text(new)) => {
|
||||||
if new.tag_name != old.tag_name || new.namespace != old.namespace {
|
if old.text != new.text {
|
||||||
self.create(new_node);
|
self.dom.push_root(old.dom_id.get());
|
||||||
self.dom.replace_with();
|
log::debug!("Text has changed {}, {}", old.text, new.text);
|
||||||
return;
|
self.dom.set_text(new.text);
|
||||||
}
|
|
||||||
new.dom_id.set(old.dom_id.get());
|
|
||||||
|
|
||||||
self.diff_listeners(old.listeners, new.listeners);
|
|
||||||
self.diff_attr(old.attributes, new.attributes, new.namespace);
|
|
||||||
self.diff_children(old.children, new.children);
|
|
||||||
}
|
|
||||||
// New node is a text element, need to replace the element with a simple text node
|
|
||||||
VNode::Text(_) => {
|
|
||||||
self.create(new_node);
|
|
||||||
self.dom.replace_with();
|
|
||||||
}
|
|
||||||
|
|
||||||
// New node is a component
|
|
||||||
// Make the component and replace our element on the stack with it
|
|
||||||
VNode::Component(_) => {
|
|
||||||
self.create(new_node);
|
|
||||||
self.dom.replace_with();
|
|
||||||
}
|
|
||||||
|
|
||||||
// New node is actually a sequence of nodes.
|
|
||||||
// We need to replace this one node with a sequence of nodes
|
|
||||||
// Not yet implement because it's kinda hairy
|
|
||||||
VNode::Fragment(new) => {
|
|
||||||
match new.children.len() {
|
|
||||||
0 => {
|
|
||||||
// remove
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
// replace
|
|
||||||
self.create(&new.children[0]);
|
|
||||||
self.dom.replace_with();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
todo!()
|
|
||||||
// remove and mount many
|
|
||||||
// self.remove_self_and_next_siblings(old)
|
|
||||||
//
|
|
||||||
// let iter = ChildIterator::new(new_node, self.components).rev();
|
|
||||||
// for child in iter {}
|
|
||||||
// for child in new.children.iter().skip(1).rev() {
|
|
||||||
// self.dom.remove();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New Node is actually suspended. Todo
|
|
||||||
VNode::Suspended { real } => todo!(),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Old element was text
|
|
||||||
VNode::Text(old) => match new_node {
|
|
||||||
VNode::Text(new) => {
|
|
||||||
if old.text != new.text {
|
|
||||||
log::debug!("Text has changed {}, {}", old.text, new.text);
|
|
||||||
self.dom.set_text(new.text);
|
|
||||||
}
|
|
||||||
new.dom_id.set(old.dom_id.get());
|
|
||||||
}
|
|
||||||
VNode::Element(_) | VNode::Component(_) => {
|
|
||||||
self.create(new_node);
|
|
||||||
self.dom.replace_with();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO on handling these types
|
|
||||||
VNode::Fragment(frag) => {
|
|
||||||
if frag.children.len() == 0 {
|
|
||||||
// do nothing
|
|
||||||
} else {
|
|
||||||
self.create(&frag.children[0]);
|
|
||||||
self.dom.replace_with();
|
|
||||||
for child in frag.children.iter().skip(1) {
|
|
||||||
self.create(child);
|
|
||||||
self.dom.append_child();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VNode::Suspended { real } => todo!(),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Old element was a component
|
|
||||||
VNode::Component(old) => {
|
|
||||||
match new_node {
|
|
||||||
// It's something entirely different
|
|
||||||
VNode::Element(_) | VNode::Text(_) => {
|
|
||||||
self.create(new_node);
|
|
||||||
self.dom.replace_with();
|
|
||||||
}
|
|
||||||
|
|
||||||
// It's also a component
|
|
||||||
VNode::Component(new) => {
|
|
||||||
if old.user_fc == new.user_fc {
|
|
||||||
// Make sure we're dealing with the same component (by function pointer)
|
|
||||||
|
|
||||||
// Make sure the new component vnode is referencing the right scope id
|
|
||||||
let scope_id = old.ass_scope.get();
|
|
||||||
new.ass_scope.set(scope_id);
|
|
||||||
new.mounted_root.set(old.mounted_root.get());
|
|
||||||
|
|
||||||
// make sure the component's caller function is up to date
|
|
||||||
self.components
|
|
||||||
.with_scope(scope_id.unwrap(), |scope| {
|
|
||||||
scope.caller = Rc::downgrade(&new.caller)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// React doesn't automatically memoize, but we do.
|
|
||||||
// The cost is low enough to make it worth checking
|
|
||||||
// Rust produces fairly performant comparison methods, sometimes SIMD accelerated
|
|
||||||
let should_render = match old.comparator {
|
|
||||||
Some(comparator) => comparator(new),
|
|
||||||
None => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_render {
|
|
||||||
// // self.dom.commit_traversal();
|
|
||||||
self.components
|
|
||||||
.with_scope(scope_id.unwrap(), |f| {
|
|
||||||
f.run_scope().unwrap();
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
// diff_machine.change_list.load_known_root(root_id);
|
|
||||||
// run the scope
|
|
||||||
//
|
|
||||||
} else {
|
|
||||||
// Component has memoized itself and doesn't need to be re-rendered.
|
|
||||||
// We still need to make sure the child's props are up-to-date.
|
|
||||||
// Don't commit traversal
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// It's an entirely different component
|
|
||||||
|
|
||||||
// A new component has shown up! We need to destroy the old node
|
|
||||||
|
|
||||||
// Wipe the old one and plant the new one
|
|
||||||
// self.dom.commit_traversal();
|
|
||||||
// self.dom.replace_node_with(old.dom_id, new.dom_id);
|
|
||||||
// self.create(new_node);
|
|
||||||
// self.dom.replace_with();
|
|
||||||
self.create(new_node);
|
|
||||||
// self.create_and_repalce(new_node, old.mounted_root.get());
|
|
||||||
|
|
||||||
// Now we need to remove the old scope and all of its descendents
|
|
||||||
let old_scope = old.ass_scope.get().unwrap();
|
|
||||||
self.destroy_scopes(old_scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VNode::Fragment(_) => todo!(),
|
|
||||||
VNode::Suspended { real } => todo!(),
|
|
||||||
}
|
}
|
||||||
|
new.dom_id.set(old.dom_id.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
VNode::Fragment(old) => {
|
(VNode::Element(old), VNode::Element(new)) => {
|
||||||
|
// If the element type is completely different, the element needs to be re-rendered completely
|
||||||
|
// This is an optimization React makes due to how users structure their code
|
||||||
//
|
//
|
||||||
match new_node {
|
// In Dioxus, this is less likely to occur unless through a fragment
|
||||||
VNode::Fragment(new) => todo!(),
|
if new.tag_name != old.tag_name || new.namespace != old.namespace {
|
||||||
|
log::debug!("things have changed....?");
|
||||||
|
self.dom.push_root(old.dom_id.get());
|
||||||
|
self.create(new_node);
|
||||||
|
self.dom.replace_with();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log::debug!("Important Things haven't changed!");
|
||||||
|
new.dom_id.set(old.dom_id.get());
|
||||||
|
|
||||||
// going from fragment to element means we're going from many (or potentially none) to one
|
self.diff_listeners(old.listeners, new.listeners);
|
||||||
VNode::Element(new) => {}
|
self.diff_attr(old.attributes, new.attributes, new.namespace);
|
||||||
VNode::Text(_) => todo!(),
|
self.diff_children(old.children, new.children);
|
||||||
VNode::Suspended { real } => todo!(),
|
}
|
||||||
VNode::Component(_) => todo!(),
|
|
||||||
|
(VNode::Component(old), VNode::Component(new)) => {
|
||||||
|
log::warn!("diffing components? {:#?}", new.user_fc);
|
||||||
|
if old.user_fc == new.user_fc {
|
||||||
|
// Make sure we're dealing with the same component (by function pointer)
|
||||||
|
|
||||||
|
// Make sure the new component vnode is referencing the right scope id
|
||||||
|
let scope_id = old.ass_scope.get();
|
||||||
|
new.ass_scope.set(scope_id);
|
||||||
|
|
||||||
|
// new.mounted_root.set(old.mounted_root.get());
|
||||||
|
|
||||||
|
// make sure the component's caller function is up to date
|
||||||
|
let scope = self.components.try_get_mut(scope_id.unwrap()).unwrap();
|
||||||
|
// .with_scope(scope_id.unwrap(), |scope| {
|
||||||
|
scope.caller = Rc::downgrade(&new.caller);
|
||||||
|
scope.update_children(new.children);
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// React doesn't automatically memoize, but we do.
|
||||||
|
// The cost is low enough to make it worth checking
|
||||||
|
// Rust produces fairly performant comparison methods, sometimes SIMD accelerated
|
||||||
|
// let should_render = match old.comparator {
|
||||||
|
// Some(comparator) => comparator(new),
|
||||||
|
// None => true,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if should_render {
|
||||||
|
// // self.dom.commit_traversal();
|
||||||
|
// let f = self.components.try_get_mut(scope_id.unwrap()).unwrap();
|
||||||
|
// self.components
|
||||||
|
// .with_scope(scope_id.unwrap(), |f| {
|
||||||
|
log::debug!("running scope during diff {:#?}", scope_id);
|
||||||
|
scope.run_scope().unwrap();
|
||||||
|
self.diff_node(scope.old_frame(), scope.next_frame());
|
||||||
|
log::debug!("scope completed {:#?}", scope_id);
|
||||||
|
self.seen_nodes.insert(scope_id.unwrap());
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// diff_machine.change_list.load_known_root(root_id);
|
||||||
|
// run the scope
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// log::error!("Memoized componented");
|
||||||
|
// // Component has memoized itself and doesn't need to be re-rendered.
|
||||||
|
// // We still need to make sure the child's props are up-to-date.
|
||||||
|
// // Don't commit traversal
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
// It's an entirely different component
|
||||||
|
|
||||||
|
// A new component has shown up! We need to destroy the old node
|
||||||
|
|
||||||
|
// Wipe the old one and plant the new one
|
||||||
|
// self.dom.commit_traversal();
|
||||||
|
// self.dom.replace_node_with(old.dom_id, new.dom_id);
|
||||||
|
// self.create(new_node);
|
||||||
|
log::warn!("creating and replacing...");
|
||||||
|
self.create(new_node);
|
||||||
|
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// self.create_and_repalce(new_node, old.mounted_root.get());
|
||||||
|
|
||||||
|
// Now we need to remove the old scope and all of its descendents
|
||||||
|
let old_scope = old.ass_scope.get().unwrap();
|
||||||
|
self.destroy_scopes(old_scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a suspended node will perform a mem-copy of the previous elements until it is ready
|
(VNode::Fragment(old), VNode::Fragment(new)) => {
|
||||||
// this means that event listeners will need to be disabled and removed
|
log::debug!("diffing as fragment");
|
||||||
// it also means that props will need to disabled - IE if the node "came out of hibernation" any props should be considered outdated
|
// This is the case where options or direct vnodes might be used.
|
||||||
VNode::Suspended { real: old_real } => {
|
// In this case, it's faster to just skip ahead to their diff
|
||||||
|
if old.children.len() == 1 && new.children.len() == 1 {
|
||||||
|
self.diff_node(old.children.get(0).unwrap(), new.children.get(0).unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diff using the approach where we're looking for added or removed nodes.
|
||||||
|
if old.children.len() != new.children.len() {}
|
||||||
|
|
||||||
|
// Diff where we think the elements are the same
|
||||||
|
if old.children.len() == new.children.len() {}
|
||||||
|
|
||||||
|
self.diff_children(old.children, new.children);
|
||||||
|
// todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okay - these are the "insane" cases where the structure is entirely different.
|
||||||
|
// The factory and rsx! APIs don't really produce structures like this, so we don't take any too complicated
|
||||||
|
// code paths.
|
||||||
|
|
||||||
|
// in the case where the old node was a fragment but the new nodes are text,
|
||||||
|
(VNode::Fragment(_) | VNode::Component(_), VNode::Element(_) | VNode::Text(_)) => {
|
||||||
|
// find the first real element int the old node
|
||||||
|
let mut iter = RealChildIterator::new(old_node, self.components);
|
||||||
|
if let Some(first) = iter.next() {
|
||||||
|
// replace the very first node with the creation of the element or text
|
||||||
|
} else {
|
||||||
|
// there are no real elements in the old fragment...
|
||||||
|
// We need to load up the next real
|
||||||
|
}
|
||||||
|
if let VNode::Component(old) = old_node {
|
||||||
|
// schedule this component's destructor to be run
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// In the case where real nodes are being replaced by potentially
|
||||||
|
(VNode::Element(_) | VNode::Text(_), VNode::Fragment(new)) => {
|
||||||
//
|
//
|
||||||
match new_node {
|
|
||||||
VNode::Suspended { real: new_real } => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
VNode::Element(_) => todo!(),
|
|
||||||
VNode::Text(_) => todo!(),
|
|
||||||
VNode::Fragment(_) => todo!(),
|
|
||||||
VNode::Component(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
(VNode::Text(_), VNode::Element(_)) => {
|
||||||
|
self.create(new_node);
|
||||||
|
self.dom.replace_with();
|
||||||
|
}
|
||||||
|
(VNode::Element(_), VNode::Text(_)) => {
|
||||||
|
self.create(new_node);
|
||||||
|
self.dom.replace_with();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
//
|
||||||
|
} /*
|
||||||
|
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
// VNode::Element(old) => match new_node {
|
||||||
|
// // New node is an element, old node was en element, need to investiage more deeply
|
||||||
|
// VNode::Element(new) => {
|
||||||
|
// // If the element type is completely different, the element needs to be re-rendered completely
|
||||||
|
// // This is an optimization React makes due to how users structure their code
|
||||||
|
// if new.tag_name != old.tag_name || new.namespace != old.namespace {
|
||||||
|
// self.create(new_node);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// new.dom_id.set(old.dom_id.get());
|
||||||
|
|
||||||
|
// self.diff_listeners(old.listeners, new.listeners);
|
||||||
|
// self.diff_attr(old.attributes, new.attributes, new.namespace);
|
||||||
|
// self.diff_children(old.children, new.children);
|
||||||
|
// }
|
||||||
|
// // New node is a text element, need to replace the element with a simple text node
|
||||||
|
// VNode::Text(_) => {
|
||||||
|
// self.create(new_node);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // New node is a component
|
||||||
|
// // Make the component and replace our element on the stack with it
|
||||||
|
// VNode::Component(_) => {
|
||||||
|
// self.create(new_node);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // New node is actually a sequence of nodes.
|
||||||
|
// // We need to replace this one node with a sequence of nodes
|
||||||
|
// // Not yet implement because it's kinda hairy
|
||||||
|
// VNode::Fragment(new) => {
|
||||||
|
// match new.children.len() {
|
||||||
|
// 0 => {
|
||||||
|
// // remove
|
||||||
|
// }
|
||||||
|
// 1 => {
|
||||||
|
// // replace
|
||||||
|
// self.create(&new.children[0]);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// }
|
||||||
|
// _ => {
|
||||||
|
// todo!()
|
||||||
|
// // remove and mount many
|
||||||
|
// // self.remove_self_and_next_siblings(old)
|
||||||
|
// //
|
||||||
|
// // let iter = ChildIterator::new(new_node, self.components).rev();
|
||||||
|
// // for child in iter {}
|
||||||
|
// // for child in new.children.iter().skip(1).rev() {
|
||||||
|
// // self.dom.remove();
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // New Node is actually suspended. Todo
|
||||||
|
// VNode::Suspended { real } => todo!(),
|
||||||
|
// },
|
||||||
|
|
||||||
|
// // Old element was text
|
||||||
|
// VNode::Text(old) => match new_node {
|
||||||
|
// VNode::Text(new) => {
|
||||||
|
// if old.text != new.text {
|
||||||
|
// log::debug!("Text has changed {}, {}", old.text, new.text);
|
||||||
|
// self.dom.set_text(new.text);
|
||||||
|
// }
|
||||||
|
// new.dom_id.set(old.dom_id.get());
|
||||||
|
// }
|
||||||
|
// VNode::Element(_) | VNode::Component(_) => {
|
||||||
|
// self.create(new_node);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // TODO on handling these types
|
||||||
|
// VNode::Fragment(frag) => {
|
||||||
|
// if frag.children.len() == 0 {
|
||||||
|
// // do nothing
|
||||||
|
// } else {
|
||||||
|
// self.create(&frag.children[0]);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// for child in frag.children.iter().skip(1) {
|
||||||
|
// self.create(child);
|
||||||
|
// self.dom.append_child();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// VNode::Suspended { real } => todo!(),
|
||||||
|
// },
|
||||||
|
|
||||||
|
// // Old element was a component
|
||||||
|
// VNode::Component(old) => {
|
||||||
|
// match new_node {
|
||||||
|
// // It's something entirely different
|
||||||
|
// VNode::Element(_) | VNode::Text(_) => {
|
||||||
|
// self.create(new_node);
|
||||||
|
// self.dom.replace_with();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // It's also a component
|
||||||
|
// VNode::Component(new) => {
|
||||||
|
// if old.user_fc == new.user_fc {
|
||||||
|
// // Make sure we're dealing with the same component (by function pointer)
|
||||||
|
|
||||||
|
// // Make sure the new component vnode is referencing the right scope id
|
||||||
|
// let scope_id = old.ass_scope.get();
|
||||||
|
// new.ass_scope.set(scope_id);
|
||||||
|
// new.mounted_root.set(old.mounted_root.get());
|
||||||
|
|
||||||
|
// // make sure the component's caller function is up to date
|
||||||
|
// self.components
|
||||||
|
// .with_scope(scope_id.unwrap(), |scope| {
|
||||||
|
// scope.caller = Rc::downgrade(&new.caller)
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// // React doesn't automatically memoize, but we do.
|
||||||
|
// // The cost is low enough to make it worth checking
|
||||||
|
// // Rust produces fairly performant comparison methods, sometimes SIMD accelerated
|
||||||
|
// let should_render = match old.comparator {
|
||||||
|
// Some(comparator) => comparator(new),
|
||||||
|
// None => true,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if should_render {
|
||||||
|
// // // self.dom.commit_traversal();
|
||||||
|
// self.components
|
||||||
|
// .with_scope(scope_id.unwrap(), |f| {
|
||||||
|
// f.run_scope().unwrap();
|
||||||
|
// })
|
||||||
|
// .unwrap();
|
||||||
|
// // diff_machine.change_list.load_known_root(root_id);
|
||||||
|
// // run the scope
|
||||||
|
// //
|
||||||
|
// } else {
|
||||||
|
// // Component has memoized itself and doesn't need to be re-rendered.
|
||||||
|
// // We still need to make sure the child's props are up-to-date.
|
||||||
|
// // Don't commit traversal
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// // It's an entirely different component
|
||||||
|
|
||||||
|
// // A new component has shown up! We need to destroy the old node
|
||||||
|
|
||||||
|
// // Wipe the old one and plant the new one
|
||||||
|
// // self.dom.commit_traversal();
|
||||||
|
// // self.dom.replace_node_with(old.dom_id, new.dom_id);
|
||||||
|
// // self.create(new_node);
|
||||||
|
// // self.dom.replace_with();
|
||||||
|
// self.create(new_node);
|
||||||
|
// // self.create_and_repalce(new_node, old.mounted_root.get());
|
||||||
|
|
||||||
|
// // Now we need to remove the old scope and all of its descendents
|
||||||
|
// let old_scope = old.ass_scope.get().unwrap();
|
||||||
|
// self.destroy_scopes(old_scope);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// VNode::Fragment(_) => todo!(),
|
||||||
|
// VNode::Suspended { real } => todo!(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// VNode::Fragment(old) => {
|
||||||
|
// //
|
||||||
|
// match new_node {
|
||||||
|
// VNode::Fragment(new) => todo!(),
|
||||||
|
|
||||||
|
// // going from fragment to element means we're going from many (or potentially none) to one
|
||||||
|
// VNode::Element(new) => {}
|
||||||
|
// VNode::Text(_) => todo!(),
|
||||||
|
// VNode::Suspended { real } => todo!(),
|
||||||
|
// VNode::Component(_) => todo!(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // a suspended node will perform a mem-copy of the previous elements until it is ready
|
||||||
|
// // this means that event listeners will need to be disabled and removed
|
||||||
|
// // it also means that props will need to disabled - IE if the node "came out of hibernation" any props should be considered outdated
|
||||||
|
// VNode::Suspended { real: old_real } => {
|
||||||
|
// //
|
||||||
|
// match new_node {
|
||||||
|
// VNode::Suspended { real: new_real } => {
|
||||||
|
// //
|
||||||
|
// }
|
||||||
|
// VNode::Element(_) => todo!(),
|
||||||
|
// VNode::Text(_) => todo!(),
|
||||||
|
// VNode::Fragment(_) => todo!(),
|
||||||
|
// VNode::Component(_) => todo!(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,6 +500,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
// [... node]
|
// [... node]
|
||||||
fn create(&mut self, node: &'bump VNode<'bump>) {
|
fn create(&mut self, node: &'bump VNode<'bump>) {
|
||||||
// debug_assert!(self.dom.traversal_is_committed());
|
// debug_assert!(self.dom.traversal_is_committed());
|
||||||
|
log::warn!("Creating node!");
|
||||||
match node {
|
match node {
|
||||||
VNode::Text(text) => {
|
VNode::Text(text) => {
|
||||||
let real_id = self.dom.create_text_node(text.text);
|
let real_id = self.dom.create_text_node(text.text);
|
||||||
@@ -402,7 +563,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VNode::Component(component) => {
|
VNode::Component(component) => {
|
||||||
let real_id = self.dom.create_placeholder();
|
// let real_id = self.dom.create_placeholder();
|
||||||
|
|
||||||
// let root_id = next_id();
|
// let root_id = next_id();
|
||||||
// self.dom.save_known_root(root_id);
|
// self.dom.save_known_root(root_id);
|
||||||
@@ -455,6 +616,7 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
|||||||
new_component.run_scope().unwrap();
|
new_component.run_scope().unwrap();
|
||||||
|
|
||||||
// And then run the diff algorithm
|
// And then run the diff algorithm
|
||||||
|
let _real_id = self.dom.create_placeholder();
|
||||||
self.diff_node(new_component.old_frame(), new_component.next_frame());
|
self.diff_node(new_component.old_frame(), new_component.next_frame());
|
||||||
|
|
||||||
// Finally, insert this node as a seen node.
|
// Finally, insert this node as a seen node.
|
||||||
@@ -675,11 +837,14 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if new_is_keyed && old_is_keyed {
|
if new_is_keyed && old_is_keyed {
|
||||||
todo!("Not yet implemented a migration away from temporaries");
|
log::warn!("using the wrong approach");
|
||||||
|
self.diff_non_keyed_children(old, new);
|
||||||
|
// todo!("Not yet implemented a migration away from temporaries");
|
||||||
// let t = self.dom.next_temporary();
|
// let t = self.dom.next_temporary();
|
||||||
// self.diff_keyed_children(old, new);
|
// self.diff_keyed_children(old, new);
|
||||||
// self.dom.set_next_temporary(t);
|
// self.dom.set_next_temporary(t);
|
||||||
} else {
|
} else {
|
||||||
|
log::debug!("diffing non keyed children");
|
||||||
self.diff_non_keyed_children(old, new);
|
self.diff_non_keyed_children(old, new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1120,24 +1285,24 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
|||||||
// self.dom.go_to_sibling(i);
|
// self.dom.go_to_sibling(i);
|
||||||
// [... parent this_child]
|
// [... parent this_child]
|
||||||
|
|
||||||
let did = old_child.get_mounted_id(self.components).unwrap();
|
// let did = old_child.get_mounted_id(self.components).unwrap();
|
||||||
if did.0 == 0 {
|
// if did.0 == 0 {
|
||||||
log::debug!("Root is bad: {:#?}", old_child);
|
// log::debug!("Root is bad: {:#?}", old_child);
|
||||||
}
|
// }
|
||||||
self.dom.push_root(did);
|
// self.dom.push_root(did);
|
||||||
self.diff_node(old_child, new_child);
|
self.diff_node(old_child, new_child);
|
||||||
|
|
||||||
let old_id = old_child.get_mounted_id(self.components).unwrap();
|
// let old_id = old_child.get_mounted_id(self.components).unwrap();
|
||||||
let new_id = new_child.get_mounted_id(self.components).unwrap();
|
// let new_id = new_child.get_mounted_id(self.components).unwrap();
|
||||||
|
|
||||||
log::debug!(
|
// log::debug!(
|
||||||
"pushed root. {:?}, {:?}",
|
// "pushed root. {:?}, {:?}",
|
||||||
old_child.get_mounted_id(self.components).unwrap(),
|
// old_child.get_mounted_id(self.components).unwrap(),
|
||||||
new_child.get_mounted_id(self.components).unwrap()
|
// new_child.get_mounted_id(self.components).unwrap()
|
||||||
);
|
// );
|
||||||
if old_id != new_id {
|
// if old_id != new_id {
|
||||||
log::debug!("Mismatch: {:?}", new_child);
|
// log::debug!("Mismatch: {:?}", new_child);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// match old.len().cmp(&new.len()) {
|
// match old.len().cmp(&new.len()) {
|
||||||
@@ -1256,7 +1421,11 @@ enum KeyedPrefixResult {
|
|||||||
MoreWorkToDo(usize),
|
MoreWorkToDo(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChildIterator<'a> {
|
/// This iterator iterates through a list of virtual children and only returns real children (Elements or Text).
|
||||||
|
///
|
||||||
|
/// This iterator is useful when it's important to load the next real root onto the top of the stack for operations like
|
||||||
|
/// "InsertBefore".
|
||||||
|
struct RealChildIterator<'a> {
|
||||||
scopes: &'a ScopeArena,
|
scopes: &'a ScopeArena,
|
||||||
|
|
||||||
// Heuristcally we should never bleed into 5 completely nested fragments/components
|
// Heuristcally we should never bleed into 5 completely nested fragments/components
|
||||||
@@ -1264,7 +1433,7 @@ struct ChildIterator<'a> {
|
|||||||
stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>,
|
stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ChildIterator<'a> {
|
impl<'a> RealChildIterator<'a> {
|
||||||
fn new(starter: &'a VNode<'a>, scopes: &'a ScopeArena) -> Self {
|
fn new(starter: &'a VNode<'a>, scopes: &'a ScopeArena) -> Self {
|
||||||
Self {
|
Self {
|
||||||
scopes,
|
scopes,
|
||||||
@@ -1279,7 +1448,7 @@ impl<'a> ChildIterator<'a> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
impl<'a> Iterator for ChildIterator<'a> {
|
impl<'a> Iterator for RealChildIterator<'a> {
|
||||||
type Item = &'a VNode<'a>;
|
type Item = &'a VNode<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a VNode<'a>> {
|
fn next(&mut self) -> Option<&'a VNode<'a>> {
|
||||||
@@ -1402,7 +1571,7 @@ mod tests {
|
|||||||
let mut renderer = DebugDom::new();
|
let mut renderer = DebugDom::new();
|
||||||
dom.rebuild(&mut renderer).unwrap();
|
dom.rebuild(&mut renderer).unwrap();
|
||||||
let starter = dom.base_scope().root();
|
let starter = dom.base_scope().root();
|
||||||
let ite = ChildIterator::new(starter, &dom.components);
|
let ite = RealChildIterator::new(starter, &dom.components);
|
||||||
for child in ite {
|
for child in ite {
|
||||||
match child {
|
match child {
|
||||||
VNode::Element(el) => println!("Found: Element {}", el.tag_name),
|
VNode::Element(el) => println!("Found: Element {}", el.tag_name),
|
||||||
|
|||||||
@@ -10,14 +10,62 @@ use crate::{innerlude::ScopeIdx, virtual_dom::RealDomNode};
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EventTrigger {
|
pub struct EventTrigger {
|
||||||
|
///
|
||||||
pub component_id: ScopeIdx,
|
pub component_id: ScopeIdx,
|
||||||
|
|
||||||
|
///
|
||||||
pub real_node_id: RealDomNode,
|
pub real_node_id: RealDomNode,
|
||||||
|
|
||||||
|
///
|
||||||
pub event: VirtualEvent,
|
pub event: VirtualEvent,
|
||||||
|
|
||||||
|
///
|
||||||
|
pub priority: EventPriority,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Priority of Event Triggers.
|
||||||
|
///
|
||||||
|
/// Internally, Dioxus will abort work that's taking too long if new, more important, work arrives. Unlike React, Dioxus
|
||||||
|
/// won't be afraid to pause work or flush changes to the RealDOM. This is called "cooperative scheduling". Some Renderers
|
||||||
|
/// implement this form of scheduling internally, however Dioxus will perform its own scheduling as well.
|
||||||
|
///
|
||||||
|
/// The ultimate goal of the scheduler is to manage latency of changes, prioritizing "flashier" changes over "subtler" changes.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EventPriority {
|
||||||
|
/// "Immediate" work will interrupt whatever work is currently being done and force its way through. This type of work
|
||||||
|
/// is typically reserved for small changes to single elements.
|
||||||
|
///
|
||||||
|
/// The primary user of the "Immediate" priority is the `Signal` API which performs surgical mutations to the DOM.
|
||||||
|
Immediate,
|
||||||
|
|
||||||
|
/// "High Priority" work will not interrupt other high priority work, but will interrupt long medium and low priority work.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// This is typically reserved for things like user interaction.
|
||||||
|
High,
|
||||||
|
|
||||||
|
/// "Medium priority" work is generated by page events not triggered by the user. These types of events are less important
|
||||||
|
/// than "High Priority" events and will take presedence over low priority events.
|
||||||
|
///
|
||||||
|
/// This is typically reserved for VirtualEvents that are not related to keyboard or mouse input.
|
||||||
|
Medium,
|
||||||
|
|
||||||
|
/// "Low Priority" work will always be pre-empted unless the work is significantly delayed, in which case it will be
|
||||||
|
/// advanced to the front of the work queue until completed.
|
||||||
|
///
|
||||||
|
/// The primary user of Low Priority work is the asynchronous work system (suspense).
|
||||||
|
Low,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventTrigger {
|
impl EventTrigger {
|
||||||
pub fn new(event: VirtualEvent, scope: ScopeIdx, mounted_dom_id: RealDomNode) -> Self {
|
pub fn new(
|
||||||
|
event: VirtualEvent,
|
||||||
|
scope: ScopeIdx,
|
||||||
|
mounted_dom_id: RealDomNode,
|
||||||
|
priority: EventPriority,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
priority,
|
||||||
component_id: scope,
|
component_id: scope,
|
||||||
real_node_id: mounted_dom_id,
|
real_node_id: mounted_dom_id,
|
||||||
event,
|
event,
|
||||||
@@ -78,7 +126,7 @@ pub mod on {
|
|||||||
( $( $eventdata:ident($wrapper:ident): [ $( $name:ident )* ]; )* ) => {
|
( $( $eventdata:ident($wrapper:ident): [ $( $name:ident )* ]; )* ) => {
|
||||||
$(
|
$(
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct $wrapper(Rc<dyn $eventdata>);
|
pub struct $wrapper(pub Rc<dyn $eventdata>);
|
||||||
impl Deref for $wrapper {
|
impl Deref for $wrapper {
|
||||||
type Target = Rc<dyn $eventdata>;
|
type Target = Rc<dyn $eventdata>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use std::{
|
|||||||
u128,
|
u128,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::VirtualEvent,
|
events::VirtualEvent,
|
||||||
innerlude::{Properties, VComponent, FC},
|
innerlude::{Properties, VComponent, FC},
|
||||||
@@ -654,8 +656,9 @@ pub fn text3<'a>(bump: &'a bumpalo::Bump, args: std::fmt::Arguments) -> VNode<'a
|
|||||||
// we can just short-circuit to the &'static str version instead of having to allocate in the bump arena.
|
// we can just short-circuit to the &'static str version instead of having to allocate in the bump arena.
|
||||||
//
|
//
|
||||||
// In the most general case, this prevents the need for any string allocations for simple code IE:
|
// In the most general case, this prevents the need for any string allocations for simple code IE:
|
||||||
// div {"abc"}
|
// ```
|
||||||
//
|
// div {"abc"}
|
||||||
|
// ```
|
||||||
match args.as_str() {
|
match args.as_str() {
|
||||||
Some(static_str) => VNode::text(static_str),
|
Some(static_str) => VNode::text(static_str),
|
||||||
None => {
|
None => {
|
||||||
@@ -735,13 +738,14 @@ impl<'a, 'b> ChildrenList<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeFactory is used to build VNodes in the component's memory space.
|
/// This struct provides an ergonomic API to quickly build VNodes.
|
||||||
// This struct adds metadata to the final VNode about listeners, attributes, and children
|
///
|
||||||
|
/// NodeFactory is used to build VNodes in the component's memory space.
|
||||||
|
/// This struct adds metadata to the final VNode about listeners, attributes, and children
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NodeFactory<'a> {
|
pub struct NodeFactory<'a> {
|
||||||
pub scope_ref: &'a Scope,
|
pub scope_ref: &'a Scope,
|
||||||
pub listener_id: Cell<usize>,
|
pub listener_id: Cell<usize>,
|
||||||
// pub listener_id: RefCell<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NodeFactory<'a> {
|
impl<'a> NodeFactory<'a> {
|
||||||
@@ -756,7 +760,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create an element builder
|
/// Create an element builder
|
||||||
pub fn element<'b, Listeners, Attributes, Children>(
|
pub fn element<'b>(
|
||||||
&'b self,
|
&'b self,
|
||||||
tag_name: &'static str,
|
tag_name: &'static str,
|
||||||
) -> ElementBuilder<
|
) -> ElementBuilder<
|
||||||
@@ -787,6 +791,20 @@ impl<'a> NodeFactory<'a> {
|
|||||||
key: NodeKey::new_opt(key),
|
key: NodeKey::new_opt(key),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fragment_from_iter(
|
||||||
|
&self,
|
||||||
|
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
||||||
|
) -> VNode<'a> {
|
||||||
|
let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
|
||||||
|
for node in node_iter.into_iter() {
|
||||||
|
nodes.push(node.into_vnode(&self));
|
||||||
|
}
|
||||||
|
VNode::Fragment(
|
||||||
|
self.bump()
|
||||||
|
.alloc(VFragment::new(None, nodes.into_bump_slice())),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ pub enum VNode<'src> {
|
|||||||
/// A text node (node type `TEXT_NODE`).
|
/// A text node (node type `TEXT_NODE`).
|
||||||
Text(VText<'src>),
|
Text(VText<'src>),
|
||||||
|
|
||||||
/// A fragment is a "virtual position" in the DOM
|
/// A fragment is a list of elements that might have a dynamic order.
|
||||||
|
/// Normally, children will have a fixed order. However, Fragments allow a dynamic order and must be diffed differently.
|
||||||
|
///
|
||||||
|
/// Fragments don't have a single mount into the dom, so their position is characterized by the head and tail nodes.
|
||||||
|
///
|
||||||
/// Fragments may have children and keys
|
/// Fragments may have children and keys
|
||||||
Fragment(&'src VFragment<'src>),
|
Fragment(&'src VFragment<'src>),
|
||||||
|
|
||||||
|
|||||||
@@ -487,6 +487,14 @@ impl Scope {
|
|||||||
self.caller = broken_caller;
|
self.caller = broken_caller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_children<'creator_node>(
|
||||||
|
&mut self,
|
||||||
|
child_nodes: &'creator_node [VNode<'creator_node>],
|
||||||
|
) {
|
||||||
|
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
||||||
|
self.child_nodes = child_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new context and run the component with references from the Virtual Dom
|
/// Create a new context and run the component with references from the Virtual Dom
|
||||||
/// This function downcasts the function pointer based on the stored props_type
|
/// This function downcasts the function pointer based on the stored props_type
|
||||||
///
|
///
|
||||||
@@ -597,9 +605,9 @@ impl Scope {
|
|||||||
///
|
///
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn example(cx: Context, props: &Props -> VNode {
|
/// fn example(cx: Context<Props>) -> VNode {
|
||||||
/// html! {
|
/// html! {
|
||||||
/// <div> "Hello, {cx.cx.name}" </div>
|
/// <div> "Hello, {cx.name}" </div>
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ license = "MIT/Apache-2.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus-core = { path="../core", version="0.1.2" }
|
dioxus-core = { path="../core", version="0.1.2" }
|
||||||
js-sys = "0.3"
|
js-sys = "0.3"
|
||||||
wasm-bindgen = "0.2.71"
|
wasm-bindgen = { version="0.2.71", 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"
|
||||||
wasm-logger = "0.2.0"
|
wasm-logger = "0.2.0"
|
||||||
@@ -31,6 +31,7 @@ slotmap = "1.0.3"
|
|||||||
version = "0.3.50"
|
version = "0.3.50"
|
||||||
features = [
|
features = [
|
||||||
"Comment",
|
"Comment",
|
||||||
|
"Attr",
|
||||||
"Document",
|
"Document",
|
||||||
"Element",
|
"Element",
|
||||||
"HtmlElement",
|
"HtmlElement",
|
||||||
@@ -47,6 +48,7 @@ features = [
|
|||||||
"MouseEvent",
|
"MouseEvent",
|
||||||
"InputEvent",
|
"InputEvent",
|
||||||
"ClipboardEvent",
|
"ClipboardEvent",
|
||||||
|
"NamedNodeMap",
|
||||||
"KeyboardEvent",
|
"KeyboardEvent",
|
||||||
"TouchEvent",
|
"TouchEvent",
|
||||||
"WheelEvent",
|
"WheelEvent",
|
||||||
@@ -70,4 +72,5 @@ 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"] }
|
rand = { version="0.8.4", features=["small_rng"] }
|
||||||
|
separator = "0.4.1"
|
||||||
uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
|
uuid = { version="0.8.2", features=["v4", "wasm-bindgen"] }
|
||||||
@@ -3,3 +3,9 @@
|
|||||||
Build interactive user experiences directly in the web browser!
|
Build interactive user experiences directly in the web browser!
|
||||||
|
|
||||||
Dioxus-web provides a `WebsysRenderer` for the Dioxus Virtual Dom that handles events, progresses components, and updates the actual DOM using web-sys methods.
|
Dioxus-web provides a `WebsysRenderer` for the Dioxus Virtual Dom that handles events, progresses components, and updates the actual DOM using web-sys methods.
|
||||||
|
|
||||||
|
|
||||||
|
## Web-specific Optimizations
|
||||||
|
- Uses string interning of all common node types
|
||||||
|
- Optimistically interns short strings
|
||||||
|
- Builds trees completely before mounting them
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Basic example that renders a simple VNode to the browser.
|
//! Basic example that renders a simple VNode to the browser.
|
||||||
|
|
||||||
|
use dioxus::events::on::MouseEvent;
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
use dioxus_web::*;
|
use dioxus_web::*;
|
||||||
@@ -14,56 +15,42 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static App: FC<()> = |cx| {
|
static App: FC<()> = |cx| {
|
||||||
|
let (state, set_state) = use_state(&cx, || 0);
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 {"hello"}
|
section { class: "py-12 px-4 text-center"
|
||||||
C1 {}
|
div { class: "w-full max-w-2xl mx-auto"
|
||||||
C2 {}
|
span { class: "text-sm font-semibold"
|
||||||
|
"count: {state}"
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
C1 {
|
||||||
|
onclick: move |_| set_state(state + 1)
|
||||||
|
"incr"
|
||||||
|
}
|
||||||
|
C1 {
|
||||||
|
onclick: move |_| set_state(state - 1)
|
||||||
|
"decr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static C1: FC<()> = |cx| {
|
#[derive(Props)]
|
||||||
|
struct IncrementerProps<'a> {
|
||||||
|
onclick: &'a dyn Fn(MouseEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn C1<'a, 'b>(cx: Context<'a, IncrementerProps<'b>>) -> VNode<'a> {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
button {
|
button {
|
||||||
"numba 1"
|
class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
||||||
|
onclick: {cx.onclick}
|
||||||
|
"becr"
|
||||||
|
{cx.children()}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
static C2: FC<()> = |cx| {
|
|
||||||
cx.render(rsx! {
|
|
||||||
button {
|
|
||||||
"numba 2"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
static DocExamples: FC<()> = |cx| {
|
|
||||||
//
|
|
||||||
|
|
||||||
let is_ready = false;
|
|
||||||
|
|
||||||
let items = (0..10).map(|i| rsx! { li {"{i}"} });
|
|
||||||
let _ = rsx! {
|
|
||||||
ul {
|
|
||||||
{items}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// rsx! {
|
|
||||||
// div {}
|
|
||||||
// h1 {}
|
|
||||||
// {""}
|
|
||||||
// "asbasd"
|
|
||||||
// dioxus::Fragment {
|
|
||||||
// //
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
cx.render(rsx! {
|
|
||||||
div {
|
|
||||||
{ is_ready.then(|| rsx!{ h1 {"We are ready!"} }) }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
//! Example: Calculator
|
||||||
|
//! -------------------------
|
||||||
|
|
||||||
|
// use dioxus::events::on::*;
|
||||||
|
// use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_web::WebsysRenderer;
|
||||||
|
|
||||||
|
const STYLE: &str = include_str!("../../../examples/assets/calculator.css");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Setup logging
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
use dioxus::events::on::*;
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
enum Operator {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
}
|
||||||
|
|
||||||
|
static App: FC<()> = |cx| {
|
||||||
|
let (cur_val, set_cur_val) = use_state(&cx, || 0.0_f64);
|
||||||
|
let (operator, set_operator) = use_state(&cx, || None as Option<Operator>);
|
||||||
|
let (display_value, set_display_value) = use_state(&cx, || "".to_string());
|
||||||
|
|
||||||
|
let clear_display = display_value.eq("0");
|
||||||
|
let clear_text = if clear_display { "C" } else { "AC" };
|
||||||
|
|
||||||
|
let input_digit =
|
||||||
|
move |num: u8| {
|
||||||
|
log::warn!("Inputting digit {:#?}", num);
|
||||||
|
let mut new = display_value.clone();
|
||||||
|
new.push_str(num.to_string().as_str());
|
||||||
|
set_display_value(new);
|
||||||
|
};
|
||||||
|
|
||||||
|
let input_dot = move || {
|
||||||
|
let mut new = display_value.clone();
|
||||||
|
new.push_str(".");
|
||||||
|
set_display_value(new);
|
||||||
|
};
|
||||||
|
|
||||||
|
let perform_operation = move || {
|
||||||
|
if let Some(op) = operator.as_ref() {
|
||||||
|
let rhs = display_value.parse::<f64>().unwrap();
|
||||||
|
let new_val = match op {
|
||||||
|
Operator::Add => *cur_val + rhs,
|
||||||
|
Operator::Sub => *cur_val - rhs,
|
||||||
|
Operator::Mul => *cur_val * rhs,
|
||||||
|
Operator::Div => *cur_val / rhs,
|
||||||
|
};
|
||||||
|
set_cur_val(new_val);
|
||||||
|
set_display_value(new_val.to_string());
|
||||||
|
set_operator(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let toggle_sign = move |_| {
|
||||||
|
if display_value.starts_with("-") {
|
||||||
|
set_display_value(display_value.trim_start_matches("-").to_string())
|
||||||
|
} else {
|
||||||
|
set_display_value(format!("-{}", *display_value))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let toggle_percent = move |_| todo!();
|
||||||
|
|
||||||
|
let clear_key = move |_| {
|
||||||
|
set_display_value("0".to_string());
|
||||||
|
if !clear_display {
|
||||||
|
set_operator(None);
|
||||||
|
set_cur_val(0.0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let keydownhandler = move |evt: KeyboardEvent| match evt.key_code() {
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
//
|
||||||
|
let mut new = display_value.clone();
|
||||||
|
if !new.as_str().eq("0") {
|
||||||
|
new.pop();
|
||||||
|
}
|
||||||
|
set_display_value(new);
|
||||||
|
|
||||||
|
},
|
||||||
|
KeyCode::_0 => input_digit(0),
|
||||||
|
KeyCode::_1 => input_digit(1),
|
||||||
|
KeyCode::_2 => input_digit(2),
|
||||||
|
KeyCode::_3 => input_digit(3),
|
||||||
|
KeyCode::_4 => input_digit(4),
|
||||||
|
KeyCode::_5 => input_digit(5),
|
||||||
|
KeyCode::_6 => input_digit(6),
|
||||||
|
KeyCode::_7 => input_digit(7),
|
||||||
|
KeyCode::_8 => input_digit(8),
|
||||||
|
KeyCode::_9 => input_digit(9),
|
||||||
|
KeyCode::Add => set_operator(Some(Operator::Add)),
|
||||||
|
KeyCode::Subtract => set_operator(Some(Operator::Sub)),
|
||||||
|
KeyCode::Divide => set_operator(Some(Operator::Div)),
|
||||||
|
KeyCode::Multiply => set_operator(Some(Operator::Mul)),
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div { class: "app"
|
||||||
|
onkeydown: {keydownhandler}
|
||||||
|
style {
|
||||||
|
"{STYLE}"
|
||||||
|
}
|
||||||
|
div { class: "calculator",
|
||||||
|
div { class: "input-keys"
|
||||||
|
CalculatorDisplay { val: *cur_val }
|
||||||
|
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(move |k| rsx!{
|
||||||
|
CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" }
|
||||||
|
})}
|
||||||
|
}
|
||||||
|
div { class: "operator-keys"
|
||||||
|
CalculatorKey { name: "key-divide", onclick: move |_| set_operator(Some(Operator::Div)) "÷" }
|
||||||
|
CalculatorKey { name: "key-multiply", onclick: move |_| set_operator(Some(Operator::Mul)) "×" }
|
||||||
|
CalculatorKey { name: "key-subtract", onclick: move |_| set_operator(Some(Operator::Sub)) "−" }
|
||||||
|
CalculatorKey { name: "key-add", onclick: move |_| set_operator(Some(Operator::Add)) "+" }
|
||||||
|
CalculatorKey { name: "key-equals", onclick: move |_| perform_operation() "=" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
struct CalculatorKeyProps<'a> {
|
||||||
|
/// Name!
|
||||||
|
name: &'static str,
|
||||||
|
|
||||||
|
/// Click!
|
||||||
|
onclick: &'a dyn Fn(MouseEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CalculatorKey<'a, 'r>(cx: Context<'a, CalculatorKeyProps<'r>>) -> VNode<'a> {
|
||||||
|
cx.render(rsx! {
|
||||||
|
button {
|
||||||
|
class: "calculator-key {cx.name}"
|
||||||
|
onclick: {cx.onclick}
|
||||||
|
{cx.children()}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Props, PartialEq)]
|
||||||
|
struct CalculatorDisplayProps {
|
||||||
|
val: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn CalculatorDisplay(cx: Context<CalculatorDisplayProps>) -> VNode {
|
||||||
|
use separator::Separatable;
|
||||||
|
// Todo, add float support to the num-format crate
|
||||||
|
let formatted = cx.val.separated_string();
|
||||||
|
// TODO: make it autoscaling with css
|
||||||
|
cx.render(rsx! {
|
||||||
|
div { class: "calculator-display"
|
||||||
|
"{formatted}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -3,15 +3,17 @@ use dioxus_core::prelude::*;
|
|||||||
use dioxus_web::WebsysRenderer;
|
use dioxus_web::WebsysRenderer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
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();
|
||||||
|
|
||||||
|
// log::info!("hello world");
|
||||||
|
dioxus_web::intern_cache();
|
||||||
|
|
||||||
log::info!("hello world");
|
|
||||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Example: FC<()> = |cx| {
|
static Example: FC<()> = |cx| {
|
||||||
let nodes = (0..15).map(|f| rsx! (li { key: "{f}", "{f}"}));
|
let nodes = (0..500).map(|f| rsx! (li {"div"}));
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
span {
|
span {
|
||||||
|
|||||||
+294
-44
@@ -77,49 +77,6 @@ impl WebsysRenderer {
|
|||||||
.progress_with_event(&mut websys_dom, trigger)?;
|
.progress_with_event(&mut websys_dom, trigger)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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());
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
// 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);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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
|
Ok(()) // should actually never return from this, should be an error, rustc just cant see it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +92,7 @@ fn prepare_websys_dom() -> Element {
|
|||||||
|
|
||||||
// Build a dummy div
|
// Build a dummy div
|
||||||
let container: &Element = body.as_ref();
|
let container: &Element = body.as_ref();
|
||||||
container.set_inner_html("");
|
// container.set_inner_html("");
|
||||||
container
|
container
|
||||||
.append_child(
|
.append_child(
|
||||||
document
|
document
|
||||||
@@ -164,6 +121,299 @@ fn prepare_websys_dom() -> Element {
|
|||||||
// };
|
// };
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/// Wasm-bindgen has a performance option to intern commonly used phrases
|
||||||
|
/// This saves the decoding cost, making the interaction of Rust<->JS more performant.
|
||||||
|
/// We intern all the HTML tags and attributes, making most operations much faster.
|
||||||
|
///
|
||||||
|
/// Interning takes about 1ms at the start of the app, but saves a *ton* of time later on.
|
||||||
|
pub fn intern_cache() {
|
||||||
|
let cached_words = [
|
||||||
|
// All the HTML Tags
|
||||||
|
"a",
|
||||||
|
"abbr",
|
||||||
|
"address",
|
||||||
|
"area",
|
||||||
|
"article",
|
||||||
|
"aside",
|
||||||
|
"audio",
|
||||||
|
"b",
|
||||||
|
"base",
|
||||||
|
"bdi",
|
||||||
|
"bdo",
|
||||||
|
"big",
|
||||||
|
"blockquote",
|
||||||
|
"body",
|
||||||
|
"br",
|
||||||
|
"button",
|
||||||
|
"canvas",
|
||||||
|
"caption",
|
||||||
|
"cite",
|
||||||
|
"code",
|
||||||
|
"col",
|
||||||
|
"colgroup",
|
||||||
|
"command",
|
||||||
|
"data",
|
||||||
|
"datalist",
|
||||||
|
"dd",
|
||||||
|
"del",
|
||||||
|
"details",
|
||||||
|
"dfn",
|
||||||
|
"dialog",
|
||||||
|
"div",
|
||||||
|
"dl",
|
||||||
|
"dt",
|
||||||
|
"em",
|
||||||
|
"embed",
|
||||||
|
"fieldset",
|
||||||
|
"figcaption",
|
||||||
|
"figure",
|
||||||
|
"footer",
|
||||||
|
"form",
|
||||||
|
"h1",
|
||||||
|
"h2",
|
||||||
|
"h3",
|
||||||
|
"h4",
|
||||||
|
"h5",
|
||||||
|
"h6",
|
||||||
|
"head",
|
||||||
|
"header",
|
||||||
|
"hr",
|
||||||
|
"html",
|
||||||
|
"i",
|
||||||
|
"iframe",
|
||||||
|
"img",
|
||||||
|
"input",
|
||||||
|
"ins",
|
||||||
|
"kbd",
|
||||||
|
"keygen",
|
||||||
|
"label",
|
||||||
|
"legend",
|
||||||
|
"li",
|
||||||
|
"link",
|
||||||
|
"main",
|
||||||
|
"map",
|
||||||
|
"mark",
|
||||||
|
"menu",
|
||||||
|
"menuitem",
|
||||||
|
"meta",
|
||||||
|
"meter",
|
||||||
|
"nav",
|
||||||
|
"noscript",
|
||||||
|
"object",
|
||||||
|
"ol",
|
||||||
|
"optgroup",
|
||||||
|
"option",
|
||||||
|
"output",
|
||||||
|
"p",
|
||||||
|
"param",
|
||||||
|
"picture",
|
||||||
|
"pre",
|
||||||
|
"progress",
|
||||||
|
"q",
|
||||||
|
"rp",
|
||||||
|
"rt",
|
||||||
|
"ruby",
|
||||||
|
"s",
|
||||||
|
"samp",
|
||||||
|
"script",
|
||||||
|
"section",
|
||||||
|
"select",
|
||||||
|
"small",
|
||||||
|
"source",
|
||||||
|
"span",
|
||||||
|
"strong",
|
||||||
|
"style",
|
||||||
|
"sub",
|
||||||
|
"summary",
|
||||||
|
"sup",
|
||||||
|
"table",
|
||||||
|
"tbody",
|
||||||
|
"td",
|
||||||
|
"textarea",
|
||||||
|
"tfoot",
|
||||||
|
"th",
|
||||||
|
"thead",
|
||||||
|
"time",
|
||||||
|
"title",
|
||||||
|
"tr",
|
||||||
|
"track",
|
||||||
|
"u",
|
||||||
|
"ul",
|
||||||
|
"var",
|
||||||
|
"video",
|
||||||
|
"wbr",
|
||||||
|
// All the event handlers
|
||||||
|
"Attribute",
|
||||||
|
"accept",
|
||||||
|
"accept-charset",
|
||||||
|
"accesskey",
|
||||||
|
"action",
|
||||||
|
"alt",
|
||||||
|
"async",
|
||||||
|
"autocomplete",
|
||||||
|
"autofocus",
|
||||||
|
"autoplay",
|
||||||
|
"charset",
|
||||||
|
"checked",
|
||||||
|
"cite",
|
||||||
|
"class",
|
||||||
|
"cols",
|
||||||
|
"colspan",
|
||||||
|
"content",
|
||||||
|
"contenteditable",
|
||||||
|
"controls",
|
||||||
|
"coords",
|
||||||
|
"data",
|
||||||
|
"data-*",
|
||||||
|
"datetime",
|
||||||
|
"default",
|
||||||
|
"defer",
|
||||||
|
"dir",
|
||||||
|
"dirname",
|
||||||
|
"disabled",
|
||||||
|
"download",
|
||||||
|
"draggable",
|
||||||
|
"enctype",
|
||||||
|
"for",
|
||||||
|
"form",
|
||||||
|
"formaction",
|
||||||
|
"headers",
|
||||||
|
"height",
|
||||||
|
"hidden",
|
||||||
|
"high",
|
||||||
|
"href",
|
||||||
|
"hreflang",
|
||||||
|
"http-equiv",
|
||||||
|
"id",
|
||||||
|
"ismap",
|
||||||
|
"kind",
|
||||||
|
"label",
|
||||||
|
"lang",
|
||||||
|
"list",
|
||||||
|
"loop",
|
||||||
|
"low",
|
||||||
|
"max",
|
||||||
|
"maxlength",
|
||||||
|
"media",
|
||||||
|
"method",
|
||||||
|
"min",
|
||||||
|
"multiple",
|
||||||
|
"muted",
|
||||||
|
"name",
|
||||||
|
"novalidate",
|
||||||
|
"onabort",
|
||||||
|
"onafterprint",
|
||||||
|
"onbeforeprint",
|
||||||
|
"onbeforeunload",
|
||||||
|
"onblur",
|
||||||
|
"oncanplay",
|
||||||
|
"oncanplaythrough",
|
||||||
|
"onchange",
|
||||||
|
"onclick",
|
||||||
|
"oncontextmenu",
|
||||||
|
"oncopy",
|
||||||
|
"oncuechange",
|
||||||
|
"oncut",
|
||||||
|
"ondblclick",
|
||||||
|
"ondrag",
|
||||||
|
"ondragend",
|
||||||
|
"ondragenter",
|
||||||
|
"ondragleave",
|
||||||
|
"ondragover",
|
||||||
|
"ondragstart",
|
||||||
|
"ondrop",
|
||||||
|
"ondurationchange",
|
||||||
|
"onemptied",
|
||||||
|
"onended",
|
||||||
|
"onerror",
|
||||||
|
"onfocus",
|
||||||
|
"onhashchange",
|
||||||
|
"oninput",
|
||||||
|
"oninvalid",
|
||||||
|
"onkeydown",
|
||||||
|
"onkeypress",
|
||||||
|
"onkeyup",
|
||||||
|
"onload",
|
||||||
|
"onloadeddata",
|
||||||
|
"onloadedmetadata",
|
||||||
|
"onloadstart",
|
||||||
|
"onmousedown",
|
||||||
|
"onmousemove",
|
||||||
|
"onmouseout",
|
||||||
|
"onmouseover",
|
||||||
|
"onmouseup",
|
||||||
|
"onmousewheel",
|
||||||
|
"onoffline",
|
||||||
|
"ononline",
|
||||||
|
"<body>",
|
||||||
|
"onpageshow",
|
||||||
|
"onpaste",
|
||||||
|
"onpause",
|
||||||
|
"onplay",
|
||||||
|
"onplaying",
|
||||||
|
"<body>",
|
||||||
|
"onprogress",
|
||||||
|
"onratechange",
|
||||||
|
"onreset",
|
||||||
|
"onresize",
|
||||||
|
"onscroll",
|
||||||
|
"onsearch",
|
||||||
|
"onseeked",
|
||||||
|
"onseeking",
|
||||||
|
"onselect",
|
||||||
|
"onstalled",
|
||||||
|
"<body>",
|
||||||
|
"onsubmit",
|
||||||
|
"onsuspend",
|
||||||
|
"ontimeupdate",
|
||||||
|
"ontoggle",
|
||||||
|
"onunload",
|
||||||
|
"onvolumechange",
|
||||||
|
"onwaiting",
|
||||||
|
"onwheel",
|
||||||
|
"open",
|
||||||
|
"optimum",
|
||||||
|
"pattern",
|
||||||
|
"placeholder",
|
||||||
|
"poster",
|
||||||
|
"preload",
|
||||||
|
"readonly",
|
||||||
|
"rel",
|
||||||
|
"required",
|
||||||
|
"reversed",
|
||||||
|
"rows",
|
||||||
|
"rowspan",
|
||||||
|
"sandbox",
|
||||||
|
"scope",
|
||||||
|
"selected",
|
||||||
|
"shape",
|
||||||
|
"size",
|
||||||
|
"sizes",
|
||||||
|
"span",
|
||||||
|
"spellcheck",
|
||||||
|
"src",
|
||||||
|
"srcdoc",
|
||||||
|
"srclang",
|
||||||
|
"srcset",
|
||||||
|
"start",
|
||||||
|
"step",
|
||||||
|
"style",
|
||||||
|
"tabindex",
|
||||||
|
"target",
|
||||||
|
"title",
|
||||||
|
"translate",
|
||||||
|
"type",
|
||||||
|
"usemap",
|
||||||
|
"value",
|
||||||
|
"width",
|
||||||
|
"wrap",
|
||||||
|
];
|
||||||
|
|
||||||
|
for s in cached_words {
|
||||||
|
wasm_bindgen::intern(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|||||||
+24
-7
@@ -171,6 +171,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
|||||||
tag: &str,
|
tag: &str,
|
||||||
ns: Option<&'static str>,
|
ns: Option<&'static str>,
|
||||||
) -> dioxus_core::virtual_dom::RealDomNode {
|
) -> dioxus_core::virtual_dom::RealDomNode {
|
||||||
|
let tag = wasm_bindgen::intern(tag);
|
||||||
let el = match ns {
|
let el = match ns {
|
||||||
Some(ns) => self
|
Some(ns) => self
|
||||||
.document
|
.document
|
||||||
@@ -200,6 +201,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
|||||||
el_id: usize,
|
el_id: usize,
|
||||||
real_id: RealDomNode,
|
real_id: RealDomNode,
|
||||||
) {
|
) {
|
||||||
|
let event = wasm_bindgen::intern(event);
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Called [`new_event_listener`]: {}, {:?}, {}, {:?}",
|
"Called [`new_event_listener`]: {}, {:?}, {}, {:?}",
|
||||||
event,
|
event,
|
||||||
@@ -237,7 +239,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
|||||||
// Instead, we just build and immediately execute a closure that returns result
|
// Instead, we just build and immediately execute a closure that returns result
|
||||||
match decode_trigger(event) {
|
match decode_trigger(event) {
|
||||||
Ok(synthetic_event) => trigger.as_ref()(synthetic_event),
|
Ok(synthetic_event) => trigger.as_ref()(synthetic_event),
|
||||||
Err(_) => log::error!("Error decoding Dioxus event attribute."),
|
Err(e) => log::error!("Error decoding Dioxus event attribute. {:#?}", e),
|
||||||
};
|
};
|
||||||
}) as Box<dyn FnMut(&Event)>);
|
}) as Box<dyn FnMut(&Event)>);
|
||||||
|
|
||||||
@@ -407,7 +409,7 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CustomMouseEvent(web_sys::MouseEvent);
|
pub struct CustomMouseEvent(web_sys::MouseEvent);
|
||||||
impl dioxus_core::events::on::MouseEvent for CustomMouseEvent {
|
impl dioxus_core::events::on::MouseEventInner for CustomMouseEvent {
|
||||||
fn alt_key(&self) -> bool {
|
fn alt_key(&self) -> bool {
|
||||||
self.0.alt_key()
|
self.0.alt_key()
|
||||||
}
|
}
|
||||||
@@ -451,7 +453,7 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
|
|||||||
self.0.get_modifier_state(key_code)
|
self.0.get_modifier_state(key_code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VirtualEvent::MouseEvent(Rc::new(CustomMouseEvent(evt)))
|
VirtualEvent::MouseEvent(MouseEvent(Rc::new(CustomMouseEvent(evt))))
|
||||||
// MouseEvent(Box::new(RawMouseEvent {
|
// MouseEvent(Box::new(RawMouseEvent {
|
||||||
// alt_key: evt.alt_key(),
|
// alt_key: evt.alt_key(),
|
||||||
// button: evt.button() as i32,
|
// button: evt.button() as i32,
|
||||||
@@ -547,25 +549,36 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
|
// for attr in {
|
||||||
|
let attrs = target.attributes();
|
||||||
|
for x in 0..attrs.length() {
|
||||||
|
let attr = attrs.item(x).unwrap();
|
||||||
|
log::debug!("attrs include: {:#?}", attr);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
// for attr in target.attributes() {
|
||||||
|
// log::debug!("attrs include: {:#?}", attr);
|
||||||
|
// }
|
||||||
|
|
||||||
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
|
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
|
||||||
let val: String = target
|
let val: String = target
|
||||||
.get_attribute(&format!("dioxus-event-{}", typ))
|
.get_attribute(&format!("dioxus-event-{}", typ))
|
||||||
.context("")?;
|
.context(format!("wrong format - received {:#?}", typ))?;
|
||||||
|
|
||||||
let mut fields = val.splitn(3, ".");
|
let mut fields = val.splitn(3, ".");
|
||||||
|
|
||||||
let gi_id = fields
|
let gi_id = fields
|
||||||
.next()
|
.next()
|
||||||
.and_then(|f| f.parse::<u64>().ok())
|
.and_then(|f| f.parse::<u64>().ok())
|
||||||
.context("")?;
|
.context("failed to parse gi id")?;
|
||||||
let el_id = fields
|
let el_id = fields
|
||||||
.next()
|
.next()
|
||||||
.and_then(|f| f.parse::<usize>().ok())
|
.and_then(|f| f.parse::<usize>().ok())
|
||||||
.context("")?;
|
.context("failed to parse el id")?;
|
||||||
let real_id = fields
|
let real_id = fields
|
||||||
.next()
|
.next()
|
||||||
.and_then(|f| f.parse::<u64>().ok().map(RealDomNode::new))
|
.and_then(|f| f.parse::<u64>().ok().map(RealDomNode::new))
|
||||||
.context("")?;
|
.context("failed to parse real id")?;
|
||||||
|
|
||||||
// Call the trigger
|
// Call the trigger
|
||||||
log::debug!("decoded gi_id: {}, li_idx: {}", gi_id, el_id);
|
log::debug!("decoded gi_id: {}, li_idx: {}", gi_id, el_id);
|
||||||
@@ -575,6 +588,7 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<EventTrigger> {
|
|||||||
virtual_event_from_websys_event(event),
|
virtual_event_from_websys_event(event),
|
||||||
triggered_scope,
|
triggered_scope,
|
||||||
real_id,
|
real_id,
|
||||||
|
dioxus_core::events::EventPriority::High,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,3 +598,6 @@ impl ListenerMap {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct JsStringCache {}
|
||||||
|
impl JsStringCache {}
|
||||||
Reference in new issue
Block a user