feat: events work again!
This commit is contained in:
parent
73047fe956
commit
9d7ee79826
|
@ -53,7 +53,7 @@ members = [
|
||||||
"packages/core",
|
"packages/core",
|
||||||
"packages/html-namespace",
|
"packages/html-namespace",
|
||||||
"packages/web",
|
"packages/web",
|
||||||
"packages/cli",
|
# "packages/cli",
|
||||||
# "packages/atoms",
|
# "packages/atoms",
|
||||||
# "packages/ssr",
|
# "packages/ssr",
|
||||||
# "packages/docsite",
|
# "packages/docsite",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
- [] Transition away from names and towards compile-time safe tags
|
- [] Transition away from names and towards compile-time safe tags
|
||||||
- [] Fix diffing of fragments
|
- [] Fix diffing of fragments
|
||||||
- [] Properly integrate memoization to prevent safety issues with children
|
- [] Properly integrate memoization to prevent safety issues with children
|
||||||
- [x] Understand the issue with callbacks (outdated generations)
|
- [] Understand and fix the issue with callbacks (outdated generations)
|
||||||
- [] Fix examples for core, web, ssr, and general
|
- [] Fix examples for core, web, ssr, and general
|
||||||
- [] Finish up documentation
|
- [] Finish up documentation
|
||||||
- [] Polish the Recoil (Dirac?) API
|
- [] Polish the Recoil (Dirac?) API
|
||||||
|
@ -16,3 +16,5 @@
|
||||||
- [] ...some how deserialize (hydrate) the dom state?
|
- [] ...some how deserialize (hydrate) the dom state?
|
||||||
- [] Implement controlled inputs for select and textarea
|
- [] Implement controlled inputs for select and textarea
|
||||||
- [] ...somehow... noderefs....
|
- [] ...somehow... noderefs....
|
||||||
|
|
||||||
|
use_state(&cx, )
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"rust-analyzer.inlayHints.enable": false
|
"rust-analyzer.inlayHints.enable": true
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ dioxus-core-macro = { path="../core-macro", version="0.1.1" }
|
||||||
# Backs scopes and graphs between parent and children
|
# Backs scopes and graphs between parent and children
|
||||||
generational-arena = { version="0.2.8" }
|
generational-arena = { version="0.2.8" }
|
||||||
|
|
||||||
# Bumpalo backs the VNode creation
|
# Bumpalo is used as a micro heap backing each component
|
||||||
bumpalo = { version="3.6.0", features=["collections", "boxed"] }
|
bumpalo = { version="3.6.0", features=["collections", "boxed"] }
|
||||||
|
|
||||||
# custom error type
|
# custom error type
|
||||||
|
@ -25,6 +25,9 @@ thiserror = "1"
|
||||||
# faster hashmaps
|
# faster hashmaps
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
|
|
||||||
|
# even *faster* hashmaps for index-based types
|
||||||
|
nohash-hasher = "0.2.0"
|
||||||
|
|
||||||
# Used in diffing
|
# Used in diffing
|
||||||
longest-increasing-subsequence = "0.1.0"
|
longest-increasing-subsequence = "0.1.0"
|
||||||
|
|
||||||
|
@ -33,8 +36,10 @@ log = "0.4"
|
||||||
|
|
||||||
# Serialize the Edits for use in Webview/Liveview instances
|
# Serialize the Edits for use in Webview/Liveview instances
|
||||||
serde = { version="1", features=["derive"], optional=true }
|
serde = { version="1", features=["derive"], optional=true }
|
||||||
|
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
serialize = ["serde", "generational-arena/serde"]
|
serialize = ["serde", "generational-arena/serde"]
|
||||||
|
|
|
@ -16,7 +16,7 @@ impl DebugRenderer {
|
||||||
///
|
///
|
||||||
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
||||||
/// The root component can access things like routing in its context.
|
/// The root component can access things like routing in its context.
|
||||||
pub fn new(root: impl for<'a> Fn(Context<'a, ()>) -> VNode + 'static) -> Self {
|
pub fn new(root: FC<()>) -> Self {
|
||||||
Self::new_with_props(root, ())
|
Self::new_with_props(root, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,10 +24,7 @@ impl DebugRenderer {
|
||||||
/// Automatically progresses the creation of the VNode tree to completion.
|
/// Automatically progresses the creation of the VNode tree to completion.
|
||||||
///
|
///
|
||||||
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
|
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
|
||||||
pub fn new_with_props<T: Properties + 'static>(
|
pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
|
||||||
root: impl for<'a> Fn(Context<'a, T>) -> VNode + 'static,
|
|
||||||
root_props: T,
|
|
||||||
) -> Self {
|
|
||||||
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
|
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,13 @@ pub trait RealDom {
|
||||||
fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
|
fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
|
||||||
|
|
||||||
// events
|
// events
|
||||||
fn new_event_listener(&mut self, event: &str, scope: ScopeIdx, id: usize);
|
fn new_event_listener(
|
||||||
|
&mut self,
|
||||||
|
event: &str,
|
||||||
|
scope: ScopeIdx,
|
||||||
|
element_id: usize,
|
||||||
|
realnode: RealDomNode,
|
||||||
|
);
|
||||||
// fn new_event_listener(&mut self, event: &str);
|
// fn new_event_listener(&mut self, event: &str);
|
||||||
fn remove_event_listener(&mut self, event: &str);
|
fn remove_event_listener(&mut self, event: &str);
|
||||||
|
|
||||||
|
@ -138,6 +144,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
self.dom.replace_with();
|
self.dom.replace_with();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
new.dom_id.set(old.dom_id.get());
|
||||||
|
|
||||||
self.diff_listeners(old.listeners, new.listeners);
|
self.diff_listeners(old.listeners, new.listeners);
|
||||||
self.diff_attr(old.attributes, new.attributes, new.namespace.is_some());
|
self.diff_attr(old.attributes, new.attributes, new.namespace.is_some());
|
||||||
|
@ -145,6 +152,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
}
|
}
|
||||||
// New node is a text element, need to replace the element with a simple text node
|
// New node is a text element, need to replace the element with a simple text node
|
||||||
VNode::Text(_) => {
|
VNode::Text(_) => {
|
||||||
|
log::debug!("Replacing el with text");
|
||||||
self.create(new_node);
|
self.create(new_node);
|
||||||
self.dom.replace_with();
|
self.dom.replace_with();
|
||||||
}
|
}
|
||||||
|
@ -169,8 +177,10 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
VNode::Text(old) => match new_node {
|
VNode::Text(old) => match new_node {
|
||||||
VNode::Text(new) => {
|
VNode::Text(new) => {
|
||||||
if old.text != new.text {
|
if old.text != new.text {
|
||||||
|
log::debug!("Text has changed {}, {}", old.text, new.text);
|
||||||
self.dom.set_text(new.text);
|
self.dom.set_text(new.text);
|
||||||
}
|
}
|
||||||
|
new.dom_id.set(old.dom_id.get());
|
||||||
}
|
}
|
||||||
VNode::Element(_) | VNode::Component(_) => {
|
VNode::Element(_) | VNode::Component(_) => {
|
||||||
self.create(new_node);
|
self.create(new_node);
|
||||||
|
@ -316,10 +326,10 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
};
|
};
|
||||||
el.dom_id.set(real_id);
|
el.dom_id.set(real_id);
|
||||||
|
|
||||||
listeners.iter().enumerate().for_each(|(_id, listener)| {
|
listeners.iter().enumerate().for_each(|(idx, listener)| {
|
||||||
todo!()
|
self.dom
|
||||||
// dom
|
.new_event_listener(listener.event, listener.scope, idx, real_id);
|
||||||
// .new_event_listener(listener.event, listener.scope, listener.id)
|
listener.mounted_node.set(real_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
for attr in *attributes {
|
for attr in *attributes {
|
||||||
|
@ -333,12 +343,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
// to emit three instructions to (1) create a text node, (2) set its
|
// to emit three instructions to (1) create a text node, (2) set its
|
||||||
// text content, and finally (3) append the text node to this
|
// text content, and finally (3) append the text node to this
|
||||||
// parent.
|
// parent.
|
||||||
if children.len() == 1 {
|
// if children.len() == 1 {
|
||||||
if let VNode::Text(text) = &children[0] {
|
// if let VNode::Text(text) = &children[0] {
|
||||||
self.dom.set_text(text.text);
|
// self.dom.set_text(text.text);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for child in *children {
|
for child in *children {
|
||||||
self.create(child);
|
self.create(child);
|
||||||
|
@ -405,8 +415,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
new_component.run_scope().unwrap();
|
new_component.run_scope().unwrap();
|
||||||
|
|
||||||
// And then run the diff algorithm
|
// And then run the diff algorithm
|
||||||
todo!();
|
// todo!();
|
||||||
// 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.
|
||||||
self.seen_nodes.insert(idx);
|
self.seen_nodes.insert(idx);
|
||||||
|
@ -473,6 +483,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
if !old.is_empty() || !new.is_empty() {
|
if !old.is_empty() || !new.is_empty() {
|
||||||
// self.dom.commit_traversal();
|
// self.dom.commit_traversal();
|
||||||
}
|
}
|
||||||
|
// TODO
|
||||||
|
// what does "diffing listeners" even mean?
|
||||||
|
|
||||||
'outer1: for (_l_idx, new_l) in new.iter().enumerate() {
|
'outer1: for (_l_idx, new_l) in new.iter().enumerate() {
|
||||||
// go through each new listener
|
// go through each new listener
|
||||||
|
@ -480,34 +492,34 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
// if any characteristics changed, remove and then re-add
|
// if any characteristics changed, remove and then re-add
|
||||||
|
|
||||||
// if nothing changed, then just move on
|
// if nothing changed, then just move on
|
||||||
|
|
||||||
let event_type = new_l.event;
|
let event_type = new_l.event;
|
||||||
|
|
||||||
for old_l in old {
|
for old_l in old {
|
||||||
if new_l.event == old_l.event {
|
if new_l.event == old_l.event {
|
||||||
if new_l.id != old_l.id {
|
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||||
self.dom.remove_event_listener(event_type);
|
// if new_l.id != old_l.id {
|
||||||
// TODO! we need to mess with events and assign them by RealDomNode
|
// self.dom.remove_event_listener(event_type);
|
||||||
// self.dom
|
// // TODO! we need to mess with events and assign them by RealDomNode
|
||||||
// .update_event_listener(event_type, new_l.scope, new_l.id)
|
// // self.dom
|
||||||
}
|
// // .update_event_listener(event_type, new_l.scope, new_l.id)
|
||||||
|
// }
|
||||||
|
|
||||||
continue 'outer1;
|
continue 'outer1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dom
|
// self.dom
|
||||||
.new_event_listener(event_type, new_l.scope, new_l.id);
|
// .new_event_listener(event_type, new_l.scope, new_l.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
'outer2: for old_l in old {
|
// 'outer2: for old_l in old {
|
||||||
for new_l in new {
|
// for new_l in new {
|
||||||
if new_l.event == old_l.event {
|
// if new_l.event == old_l.event {
|
||||||
continue 'outer2;
|
// continue 'outer2;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
self.dom.remove_event_listener(old_l.event);
|
// self.dom.remove_event_listener(old_l.event);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diff a node's attributes.
|
// Diff a node's attributes.
|
||||||
|
@ -590,11 +602,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
// Don't take this fast path...
|
// Don't take this fast path...
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, VNode::Text(text)) => {
|
// (_, VNode::Text(text)) => {
|
||||||
// self.dom.commit_traversal();
|
// // self.dom.commit_traversal();
|
||||||
self.dom.set_text(text.text);
|
// log::debug!("using optimized text set");
|
||||||
return;
|
// self.dom.set_text(text.text);
|
||||||
}
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
// todo: any more optimizations
|
// todo: any more optimizations
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
|
@ -1047,7 +1060,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
// [... parent]
|
// [... parent]
|
||||||
//
|
//
|
||||||
// the change list stack is in the same state when this function returns.
|
// the change list stack is in the same state when this function returns.
|
||||||
fn diff_non_keyed_children(&self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
fn diff_non_keyed_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||||
// Handled these cases in `diff_children` before calling this function.
|
// Handled these cases in `diff_children` before calling this function.
|
||||||
debug_assert!(!new.is_empty());
|
debug_assert!(!new.is_empty());
|
||||||
debug_assert!(!old.is_empty());
|
debug_assert!(!old.is_empty());
|
||||||
|
@ -1057,13 +1070,26 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
// self.dom.push_root()
|
// self.dom.push_root()
|
||||||
// [... parent child]
|
// [... parent child]
|
||||||
|
|
||||||
todo!()
|
// todo!()
|
||||||
// for (i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
|
for (i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
|
||||||
// // [... parent prev_child]
|
// [... parent prev_child]
|
||||||
// self.dom.go_to_sibling(i);
|
// self.dom.go_to_sibling(i);
|
||||||
// // [... parent this_child]
|
// [... parent this_child]
|
||||||
// self.diff_node(old_child, new_child);
|
self.dom.push_root(old_child.get_mounted_id().unwrap());
|
||||||
// }
|
self.diff_node(old_child, new_child);
|
||||||
|
|
||||||
|
let old_id = old_child.get_mounted_id().unwrap();
|
||||||
|
let new_id = new_child.get_mounted_id().unwrap();
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"pushed root. {:?}, {:?}",
|
||||||
|
old_child.get_mounted_id().unwrap(),
|
||||||
|
new_child.get_mounted_id().unwrap()
|
||||||
|
);
|
||||||
|
if old_id != new_id {
|
||||||
|
log::debug!("Mismatch: {:?}", new_child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// match old.len().cmp(&new.len()) {
|
// match old.len().cmp(&new.len()) {
|
||||||
// // old.len > new.len -> removing some nodes
|
// // old.len > new.len -> removing some nodes
|
||||||
|
|
|
@ -6,20 +6,20 @@
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::innerlude::ScopeIdx;
|
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 listener_id: usize,
|
pub real_node_id: RealDomNode,
|
||||||
pub event: VirtualEvent,
|
pub event: VirtualEvent,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventTrigger {
|
impl EventTrigger {
|
||||||
pub fn new(event: VirtualEvent, scope: ScopeIdx, id: usize) -> Self {
|
pub fn new(event: VirtualEvent, scope: ScopeIdx, mounted_dom_id: RealDomNode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
component_id: scope,
|
component_id: scope,
|
||||||
listener_id: id,
|
real_node_id: mounted_dom_id,
|
||||||
event,
|
event,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,17 +55,15 @@ pub mod on {
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::ElementBuilder,
|
builder::ElementBuilder,
|
||||||
builder::NodeCtx,
|
builder::NodeCtx,
|
||||||
innerlude::{Attribute, Listener, VNode},
|
innerlude::{Attribute, Listener, RealDomNode, VNode},
|
||||||
};
|
};
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
use super::VirtualEvent;
|
use super::VirtualEvent;
|
||||||
|
|
||||||
macro_rules! event_directory {
|
macro_rules! event_directory {
|
||||||
( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
|
( $( $eventdata:ident: [ $( $name:ident )* ]; )* ) => {
|
||||||
$(
|
$(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$(
|
$(
|
||||||
pub fn $name<'a>(
|
pub fn $name<'a>(
|
||||||
c: &'_ NodeCtx<'a>,
|
c: &'_ NodeCtx<'a>,
|
||||||
|
@ -74,7 +72,7 @@ pub mod on {
|
||||||
let bump = &c.bump();
|
let bump = &c.bump();
|
||||||
Listener {
|
Listener {
|
||||||
event: stringify!($name),
|
event: stringify!($name),
|
||||||
id: *c.listener_id.borrow(),
|
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
|
||||||
scope: c.scope_ref.arena_idx,
|
scope: c.scope_ref.arena_idx,
|
||||||
callback: bump.alloc(move |evt: VirtualEvent| match evt {
|
callback: bump.alloc(move |evt: VirtualEvent| match evt {
|
||||||
VirtualEvent::$eventdata(event) => callback(event),
|
VirtualEvent::$eventdata(event) => callback(event),
|
||||||
|
@ -121,31 +119,31 @@ pub mod on {
|
||||||
/// Returns whether or not a specific event is a bubbling event
|
/// Returns whether or not a specific event is a bubbling event
|
||||||
fn bubbles(&self) -> bool;
|
fn bubbles(&self) -> bool;
|
||||||
/// Sets or returns whether the event should propagate up the hierarchy or not
|
/// Sets or returns whether the event should propagate up the hierarchy or not
|
||||||
fn cancelBubble(&self) -> ();
|
fn cancel_bubble(&self);
|
||||||
/// Returns whether or not an event can have its default action prevented
|
/// Returns whether or not an event can have its default action prevented
|
||||||
fn cancelable(&self) -> bool;
|
fn cancelable(&self) -> bool;
|
||||||
/// Returns whether the event is composed or not
|
/// Returns whether the event is composed or not
|
||||||
fn composed(&self) -> bool;
|
fn composed(&self) -> bool;
|
||||||
/// Returns the event's path
|
/// Returns the event's path
|
||||||
fn composedPath(&self) -> ();
|
fn composed_path(&self) -> String;
|
||||||
/// Returns the element whose event listeners triggered the event
|
/// Returns the element whose event listeners triggered the event
|
||||||
fn currentTarget(&self) -> ();
|
fn current_target(&self);
|
||||||
/// Returns whether or not the preventDefault method was called for the event
|
/// Returns whether or not the preventDefault method was called for the event
|
||||||
fn defaultPrevented(&self) -> ();
|
fn default_prevented(&self) -> bool;
|
||||||
/// Returns which phase of the event flow is currently being evaluated
|
/// Returns which phase of the event flow is currently being evaluated
|
||||||
fn eventPhase(&self) -> ();
|
fn event_phase(&self) -> usize;
|
||||||
/// Returns whether or not an event is trusted
|
/// Returns whether or not an event is trusted
|
||||||
fn isTrusted(&self) -> ();
|
fn is_trusted(&self) -> bool;
|
||||||
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
/// Cancels the event if it is cancelable, meaning that the default action that belongs to the event will
|
||||||
fn preventDefault(&self) -> ();
|
fn prevent_default(&self);
|
||||||
/// Prevents other listeners of the same event from being called
|
/// Prevents other listeners of the same event from being called
|
||||||
fn stopImmediatePropagation(&self) -> ();
|
fn stop_immediate_propagation(&self);
|
||||||
/// Prevents further propagation of an event during event flow
|
/// Prevents further propagation of an event during event flow
|
||||||
fn stopPropagation(&self) -> ();
|
fn stop_propagation(&self);
|
||||||
/// Returns the element that triggered the event
|
/// Returns the element that triggered the event
|
||||||
fn target(&self) -> ();
|
fn target(&self);
|
||||||
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
/// Returns the time (in milliseconds relative to the epoch) at which the event was created
|
||||||
fn timeStamp(&self) -> usize;
|
fn time_stamp(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ClipboardEvent: Debug {
|
pub trait ClipboardEvent: Debug {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
//! Helpers for building virtual DOM VNodes.
|
//! Helpers for building virtual DOM VNodes.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
any::Any, borrow::BorrowMut, cell::RefCell, fmt::Arguments, intrinsics::transmute, u128,
|
any::Any,
|
||||||
|
borrow::BorrowMut,
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
fmt::Arguments,
|
||||||
|
intrinsics::transmute,
|
||||||
|
u128,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -9,7 +14,7 @@ use crate::{
|
||||||
innerlude::{Properties, VComponent, FC},
|
innerlude::{Properties, VComponent, FC},
|
||||||
nodes::{Attribute, Listener, NodeKey, VNode},
|
nodes::{Attribute, Listener, NodeKey, VNode},
|
||||||
prelude::{VElement, VFragment},
|
prelude::{VElement, VFragment},
|
||||||
virtual_dom::Scope,
|
virtual_dom::{RealDomNode, Scope},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A virtual DOM element builder.
|
/// A virtual DOM element builder.
|
||||||
|
@ -353,12 +358,11 @@ where
|
||||||
/// ```
|
/// ```
|
||||||
pub fn on(self, event: &'static str, callback: impl Fn(VirtualEvent) + 'a) -> Self {
|
pub fn on(self, event: &'static str, callback: impl Fn(VirtualEvent) + 'a) -> Self {
|
||||||
let bump = &self.ctx.bump();
|
let bump = &self.ctx.bump();
|
||||||
|
|
||||||
let listener = Listener {
|
let listener = Listener {
|
||||||
event,
|
event,
|
||||||
callback: bump.alloc(callback),
|
callback: bump.alloc(callback),
|
||||||
id: *self.ctx.listener_id.borrow(),
|
|
||||||
scope: self.ctx.scope_ref.arena_idx,
|
scope: self.ctx.scope_ref.arena_idx,
|
||||||
|
mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
|
||||||
};
|
};
|
||||||
self.add_listener(listener)
|
self.add_listener(listener)
|
||||||
}
|
}
|
||||||
|
@ -367,7 +371,8 @@ where
|
||||||
self.listeners.push(listener);
|
self.listeners.push(listener);
|
||||||
|
|
||||||
// bump the context id forward
|
// bump the context id forward
|
||||||
*self.ctx.listener_id.borrow_mut() += 1;
|
let id = self.ctx.listener_id.get();
|
||||||
|
self.ctx.listener_id.set(id + 1);
|
||||||
|
|
||||||
// Add this listener to the context list
|
// Add this listener to the context list
|
||||||
// This casts the listener to a self-referential pointer
|
// This casts the listener to a self-referential pointer
|
||||||
|
@ -378,7 +383,7 @@ where
|
||||||
.scope_ref
|
.scope_ref
|
||||||
.listeners
|
.listeners
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.push(r.callback as *const _);
|
.push((r.mounted_node as *const _, r.callback as *const _));
|
||||||
});
|
});
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -499,15 +504,19 @@ where
|
||||||
let child = item.into_vnode(&self.ctx);
|
let child = item.into_vnode(&self.ctx);
|
||||||
self.children.push(child);
|
self.children.push(child);
|
||||||
}
|
}
|
||||||
let len_after = self.children.len();
|
if self.children.len() > len_before + 1 {
|
||||||
if len_after > len_before {
|
if self.children.last().unwrap().key().is_none() {
|
||||||
let last_child = self.children.last().unwrap();
|
if cfg!(debug_assertions) {
|
||||||
if last_child.key().is_none() {
|
log::error!(
|
||||||
// TODO: Somehow get the name of the component when NodeCtx is being made
|
r#"
|
||||||
const ERR_MSG: &str = r#"Warning: Each child in an array or iterator should have a unique "key" prop.
|
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||||
Check the render method of XXXX.
|
Not providing a key will lead to poor performance with lists.
|
||||||
See fb.me/react-warning-keys for more information. "#;
|
See docs.rs/dioxus for more information.
|
||||||
log::error!("{}", ERR_MSG);
|
---
|
||||||
|
To help you identify where this error is coming from, we've generated a backtrace.
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
|
@ -549,17 +558,6 @@ impl<'a, F> VNodeBuilder<'a, LazyNodes<'a, F>> for LazyNodes<'a, F> where
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cover the cases where nodes are pre-rendered.
|
|
||||||
// Likely used by enums.
|
|
||||||
// ----
|
|
||||||
// let nodes = ctx.render(rsx!{ ... };
|
|
||||||
// rsx! { {nodes } }
|
|
||||||
// impl<'a> IntoVNode<'a> for VNode {
|
|
||||||
// fn into_vnode(self, _ctx: &NodeCtx<'a>) -> VNode<'a> {
|
|
||||||
// self.root
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Wrap the the node-builder closure in a concrete type.
|
// Wrap the the node-builder closure in a concrete type.
|
||||||
// ---
|
// ---
|
||||||
// This is a bit of a hack to implement the IntoVNode trait for closure types.
|
// This is a bit of a hack to implement the IntoVNode trait for closure types.
|
||||||
|
@ -611,12 +609,14 @@ where
|
||||||
|
|
||||||
impl<'a> IntoVNode<'a> for () {
|
impl<'a> IntoVNode<'a> for () {
|
||||||
fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a> {
|
fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a> {
|
||||||
|
todo!();
|
||||||
VNode::Suspended
|
VNode::Suspended
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoVNode<'a> for Option<()> {
|
impl<'a> IntoVNode<'a> for Option<()> {
|
||||||
fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a> {
|
fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a> {
|
||||||
|
todo!();
|
||||||
VNode::Suspended
|
VNode::Suspended
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -735,7 +735,8 @@ impl<'a, 'b> ChildrenList<'a, 'b> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NodeCtx<'a> {
|
pub struct NodeCtx<'a> {
|
||||||
pub scope_ref: &'a Scope,
|
pub scope_ref: &'a Scope,
|
||||||
pub listener_id: RefCell<usize>,
|
pub listener_id: Cell<usize>,
|
||||||
|
// pub listener_id: RefCell<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NodeCtx<'a> {
|
impl<'a> NodeCtx<'a> {
|
||||||
|
@ -744,11 +745,13 @@ impl<'a> NodeCtx<'a> {
|
||||||
&self.scope_ref.cur_frame().bump
|
&self.scope_ref.cur_frame().bump
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text(&self, args: Arguments) -> VNode<'a> {
|
/// Create some text that's allocated along with the other vnodes
|
||||||
|
pub fn text(&self, args: Arguments) -> VNode<'a> {
|
||||||
text3(self.bump(), args)
|
text3(self.bump(), args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn element<'b, Listeners, Attributes, Children>(
|
/// Create an element builder
|
||||||
|
pub fn element<'b, Listeners, Attributes, Children>(
|
||||||
&'b self,
|
&'b self,
|
||||||
tag_name: &'static str,
|
tag_name: &'static str,
|
||||||
) -> ElementBuilder<
|
) -> ElementBuilder<
|
||||||
|
|
|
@ -13,7 +13,7 @@ use bumpalo::Bump;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Arguments, Debug},
|
fmt::{Arguments, Debug, Formatter},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
@ -127,8 +127,8 @@ impl<'a> VNode<'a> {
|
||||||
|
|
||||||
pub fn get_mounted_id(&self) -> Option<RealDomNode> {
|
pub fn get_mounted_id(&self) -> Option<RealDomNode> {
|
||||||
match self {
|
match self {
|
||||||
VNode::Element(_) => todo!(),
|
VNode::Element(el) => Some(el.dom_id.get()),
|
||||||
VNode::Text(_) => todo!(),
|
VNode::Text(te) => Some(te.dom_id.get()),
|
||||||
VNode::Fragment(_) => todo!(),
|
VNode::Fragment(_) => todo!(),
|
||||||
VNode::Suspended => todo!(),
|
VNode::Suspended => todo!(),
|
||||||
VNode::Component(_) => todo!(),
|
VNode::Component(_) => todo!(),
|
||||||
|
@ -136,6 +136,18 @@ impl<'a> VNode<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for VNode<'_> {
|
||||||
|
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
VNode::Element(el) => write!(s, "element, {}", el.tag_name),
|
||||||
|
VNode::Text(t) => write!(s, "text, {}", t.text),
|
||||||
|
VNode::Fragment(_) => write!(s, "fragment"),
|
||||||
|
VNode::Suspended => write!(s, "suspended"),
|
||||||
|
VNode::Component(_) => write!(s, "component"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VText<'src> {
|
pub struct VText<'src> {
|
||||||
pub text: &'src str,
|
pub text: &'src str,
|
||||||
|
@ -201,11 +213,14 @@ pub struct Listener<'bump> {
|
||||||
/// The type of event to listen for.
|
/// The type of event to listen for.
|
||||||
pub(crate) event: &'static str,
|
pub(crate) event: &'static str,
|
||||||
|
|
||||||
|
/// Which scope?
|
||||||
|
/// This might not actually be relevant
|
||||||
pub scope: ScopeIdx,
|
pub scope: ScopeIdx,
|
||||||
pub id: usize,
|
|
||||||
|
pub mounted_node: &'bump Cell<RealDomNode>,
|
||||||
|
|
||||||
/// The callback to invoke when the event happens.
|
/// The callback to invoke when the event happens.
|
||||||
pub(crate) callback: &'bump (dyn Fn(VirtualEvent)),
|
pub(crate) callback: &'bump dyn Fn(VirtualEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The key for keyed children.
|
/// The key for keyed children.
|
||||||
|
@ -259,6 +274,7 @@ pub struct VComponent<'src> {
|
||||||
pub key: NodeKey<'src>,
|
pub key: NodeKey<'src>,
|
||||||
|
|
||||||
pub mounted_root: Cell<RealDomNode>,
|
pub mounted_root: Cell<RealDomNode>,
|
||||||
|
|
||||||
pub ass_scope: RefCell<VCompAssociatedScope>,
|
pub ass_scope: RefCell<VCompAssociatedScope>,
|
||||||
|
|
||||||
// pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
|
// pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
|
||||||
|
|
|
@ -24,7 +24,7 @@ use bumpalo::Bump;
|
||||||
use generational_arena::Arena;
|
use generational_arena::Arena;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::RefCell,
|
cell::{Cell, RefCell},
|
||||||
collections::{HashMap, HashSet, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
future::Future,
|
future::Future,
|
||||||
|
@ -63,7 +63,7 @@ pub struct VirtualDom {
|
||||||
_root_prop_type: std::any::TypeId,
|
_root_prop_type: std::any::TypeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct RealDomNode(pub u32);
|
pub struct RealDomNode(pub u32);
|
||||||
impl RealDomNode {
|
impl RealDomNode {
|
||||||
pub fn new(id: u32) -> Self {
|
pub fn new(id: u32) -> Self {
|
||||||
|
@ -109,7 +109,7 @@ impl VirtualDom {
|
||||||
///
|
///
|
||||||
/// let dom = VirtualDom::new(Example);
|
/// let dom = VirtualDom::new(Example);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(root: impl Fn(Context<()>) -> VNode + 'static) -> Self {
|
pub fn new(root: FC<()>) -> Self {
|
||||||
Self::new_with_props(root, ())
|
Self::new_with_props(root, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,10 +141,7 @@ impl VirtualDom {
|
||||||
///
|
///
|
||||||
/// let dom = VirtualDom::new(Example);
|
/// let dom = VirtualDom::new(Example);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new_with_props<P: Properties + 'static>(
|
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||||
root: impl Fn(Context<P>) -> VNode + 'static,
|
|
||||||
root_props: P,
|
|
||||||
) -> Self {
|
|
||||||
let components = ScopeArena::new(Arena::new());
|
let components = ScopeArena::new(Arena::new());
|
||||||
|
|
||||||
// Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
|
// Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents
|
||||||
|
@ -260,11 +257,11 @@ impl VirtualDom {
|
||||||
pub fn progress_with_event<Dom: RealDom>(
|
pub fn progress_with_event<Dom: RealDom>(
|
||||||
&mut self,
|
&mut self,
|
||||||
realdom: &mut Dom,
|
realdom: &mut Dom,
|
||||||
event: EventTrigger,
|
trigger: EventTrigger,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let id = event.component_id.clone();
|
let id = trigger.component_id.clone();
|
||||||
|
|
||||||
self.components.try_get_mut(id)?.call_listener(event)?;
|
self.components.try_get_mut(id)?.call_listener(trigger)?;
|
||||||
|
|
||||||
let mut diff_machine = DiffMachine::new(
|
let mut diff_machine = DiffMachine::new(
|
||||||
realdom,
|
realdom,
|
||||||
|
@ -397,7 +394,12 @@ pub struct Scope {
|
||||||
// - is self-refenrential and therefore needs to point into the bump
|
// - is self-refenrential and therefore needs to point into the bump
|
||||||
// Stores references into the listeners attached to the vnodes
|
// Stores references into the listeners attached to the vnodes
|
||||||
// NEEDS TO BE PRIVATE
|
// NEEDS TO BE PRIVATE
|
||||||
pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
|
pub(crate) listeners: RefCell<Vec<(*const Cell<RealDomNode>, *const dyn Fn(VirtualEvent))>>,
|
||||||
|
// pub(crate) listeners: RefCell<nohash_hasher::IntMap<u32, *const dyn Fn(VirtualEvent)>>,
|
||||||
|
// pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
|
||||||
|
// pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
|
||||||
|
// NoHashMap<RealDomNode, <*const dyn Fn(VirtualEvent)>
|
||||||
|
// pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to pin the hook so it doesn't move as we initialize the list of hooks
|
// We need to pin the hook so it doesn't move as we initialize the list of hooks
|
||||||
|
@ -450,7 +452,7 @@ impl Scope {
|
||||||
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
child_nodes: child_nodes,
|
child_nodes,
|
||||||
caller,
|
caller,
|
||||||
parent,
|
parent,
|
||||||
arena_idx,
|
arena_idx,
|
||||||
|
@ -490,12 +492,12 @@ impl Scope {
|
||||||
self.frames.next().bump.reset();
|
self.frames.next().bump.reset();
|
||||||
|
|
||||||
// Remove all the outdated listeners
|
// Remove all the outdated listeners
|
||||||
//
|
self.listeners.borrow_mut().clear();
|
||||||
self.listeners
|
// self.listeners
|
||||||
.try_borrow_mut()
|
// .try_borrow_mut()
|
||||||
.ok()
|
// .ok()
|
||||||
.ok_or(Error::FatalInternal("Borrowing listener failed"))?
|
// .ok_or(Error::FatalInternal("Borrowing listener failed"))?
|
||||||
.drain(..);
|
// .drain(..);
|
||||||
|
|
||||||
*self.hookidx.borrow_mut() = 0;
|
*self.hookidx.borrow_mut() = 0;
|
||||||
|
|
||||||
|
@ -529,26 +531,35 @@ impl Scope {
|
||||||
// The listener list will be completely drained because the next frame will write over previous listeners
|
// The listener list will be completely drained because the next frame will write over previous listeners
|
||||||
pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
|
pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
|
||||||
let EventTrigger {
|
let EventTrigger {
|
||||||
listener_id, event, ..
|
real_node_id,
|
||||||
|
event,
|
||||||
|
..
|
||||||
} = trigger;
|
} = trigger;
|
||||||
|
|
||||||
// todo: implement scanning for outdated events
|
// todo: implement scanning for outdated events
|
||||||
unsafe {
|
|
||||||
// Convert the raw ptr into an actual object
|
|
||||||
// This operation is assumed to be safe
|
|
||||||
let listener_fn = self
|
|
||||||
.listeners
|
|
||||||
.try_borrow()
|
|
||||||
.ok()
|
|
||||||
.ok_or(Error::FatalInternal("Borrowing listener failed"))?
|
|
||||||
.get(listener_id as usize)
|
|
||||||
.ok_or(Error::FatalInternal("Event should exist if triggered"))?
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(Error::FatalInternal("Raw event ptr is invalid"))?;
|
|
||||||
|
|
||||||
// Run the callback with the user event
|
// Convert the raw ptr into an actual object
|
||||||
|
// This operation is assumed to be safe
|
||||||
|
|
||||||
|
log::debug!("Calling listeners! {:?}", self.listeners.borrow().len());
|
||||||
|
let listners = self.listeners.borrow();
|
||||||
|
let (_, listener) = listners
|
||||||
|
.iter()
|
||||||
|
.find(|(domptr, _)| {
|
||||||
|
let p = unsafe { &**domptr };
|
||||||
|
p.get() == real_node_id
|
||||||
|
})
|
||||||
|
.expect(&format!(
|
||||||
|
"Failed to find real node with ID {:?}",
|
||||||
|
real_node_id
|
||||||
|
));
|
||||||
|
|
||||||
|
// TODO: Don'tdo a linear scan! Do a hashmap lookup! It'll be faster!
|
||||||
|
unsafe {
|
||||||
|
let listener_fn = &**listener;
|
||||||
listener_fn(event);
|
listener_fn(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,9 @@ atoms = { path="../atoms" }
|
||||||
# futures = "0.3.12"
|
# futures = "0.3.12"
|
||||||
# html-validation = { path = "../html-validation", version = "0.1.1" }
|
# html-validation = { path = "../html-validation", version = "0.1.1" }
|
||||||
|
|
||||||
# async-channel = "1.6.1"
|
async-channel = "1.6.1"
|
||||||
nohash-hasher = "0.2.0"
|
nohash-hasher = "0.2.0"
|
||||||
|
anyhow = "1.0.41"
|
||||||
# futures-lite = "1.11.3"
|
# futures-lite = "1.11.3"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
|
|
|
@ -11,7 +11,7 @@ fn App(ctx: Context<()>) -> VNode {
|
||||||
ctx.render(rsx! {
|
ctx.render(rsx! {
|
||||||
main { class: "dark:bg-gray-800 bg-white relative h-screen"
|
main { class: "dark:bg-gray-800 bg-white relative h-screen"
|
||||||
NavBar {}
|
NavBar {}
|
||||||
{(0..10).map(|f| rsx!{ Landing {} })}
|
{(0..10).map(|f| rsx!(Landing { key: "{f}" }))}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ fn Landing(ctx: Context<()>) -> VNode {
|
||||||
div { class: "container mx-auto px-6 flex flex-col justify-between items-center relative py-8"
|
div { class: "container mx-auto px-6 flex flex-col justify-between items-center relative py-8"
|
||||||
div { class: "flex flex-col"
|
div { class: "flex flex-col"
|
||||||
h1 { class: "font-light w-full uppercase text-center text-4xl sm:text-5xl dark:text-white text-gray-800"
|
h1 { class: "font-light w-full uppercase text-center text-4xl sm:text-5xl dark:text-white text-gray-800"
|
||||||
"The React Framework for Production"
|
"The Dioxus Framework for Production"
|
||||||
}
|
}
|
||||||
h2{ class: "font-light max-w-2xl mx-auto w-full text-xl dark:text-white text-gray-500 text-center py-8"
|
h2{ class: "font-light max-w-2xl mx-auto w-full text-xl dark:text-white text-gray-500 text-center py-8"
|
||||||
"Next.js gives you the best developer experience with all the features you need for production: \n
|
"Next.js gives you the best developer experience with all the features you need for production: \n
|
||||||
|
|
|
@ -11,11 +11,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Example: FC<()> = |ctx| {
|
static Example: FC<()> = |ctx| {
|
||||||
let nodes = (0..5).map(|f| {
|
let nodes = (0..15).map(|f| rsx! (li { key: "{f}", "{f}"}));
|
||||||
rsx! {
|
|
||||||
li {"{f}"}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ctx.render(rsx! {
|
ctx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
span {
|
span {
|
||||||
|
|
|
@ -33,12 +33,11 @@ static Example: FC<()> = |ctx| {
|
||||||
<button
|
<button
|
||||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
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={move |_| set_name("jack")}>
|
onclick={move |_| set_name("jack")}>
|
||||||
"Jack!"
|
"Jack!"
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
|
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={move |_| set_name("jill")}
|
|
||||||
onclick={move |_| set_name("jill")}>
|
onclick={move |_| set_name("jill")}>
|
||||||
"Jill!"
|
"Jill!"
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
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();
|
||||||
|
|
||||||
|
log::info!("hello world");
|
||||||
|
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(JonsFavoriteCustomApp));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn JonsFavoriteCustomApp(cx: Context<()>) -> VNode {
|
||||||
|
let items = (0..20).map(|f| {
|
||||||
|
rsx! {
|
||||||
|
li {"{f}"}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {
|
||||||
|
"list"
|
||||||
|
ul {
|
||||||
|
{items}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ pub use dioxus_core as dioxus;
|
||||||
use dioxus_core::{events::EventTrigger, prelude::FC};
|
use dioxus_core::{events::EventTrigger, prelude::FC};
|
||||||
|
|
||||||
pub use dioxus_core::prelude;
|
pub use dioxus_core::prelude;
|
||||||
pub mod interpreter;
|
// pub mod interpreter;
|
||||||
pub mod new;
|
pub mod new;
|
||||||
|
|
||||||
/// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
|
/// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
|
||||||
|
@ -34,7 +34,7 @@ impl WebsysRenderer {
|
||||||
///
|
///
|
||||||
/// Run the app to completion, panicing if any error occurs while rendering.
|
/// Run the app to completion, panicing if any error occurs while rendering.
|
||||||
/// Pairs well with the wasm_bindgen async handler
|
/// Pairs well with the wasm_bindgen async handler
|
||||||
pub async fn start(root: impl for<'a> Fn(Context<'a, ()>) -> VNode + 'static) {
|
pub async fn start(root: FC<()>) {
|
||||||
Self::new(root).run().await.expect("Virtual DOM failed :(");
|
Self::new(root).run().await.expect("Virtual DOM failed :(");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ impl WebsysRenderer {
|
||||||
///
|
///
|
||||||
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
/// This means that the root component must either consumes its own context, or statics are used to generate the page.
|
||||||
/// The root component can access things like routing in its context.
|
/// The root component can access things like routing in its context.
|
||||||
pub fn new(root: impl for<'a> Fn(Context<'a, ()>) -> VNode + 'static) -> Self {
|
pub fn new(root: FC<()>) -> Self {
|
||||||
Self::new_with_props(root, ())
|
Self::new_with_props(root, ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +50,7 @@ impl WebsysRenderer {
|
||||||
/// Automatically progresses the creation of the VNode tree to completion.
|
/// Automatically progresses the creation of the VNode tree to completion.
|
||||||
///
|
///
|
||||||
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
|
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
|
||||||
pub fn new_with_props<T: Properties + 'static>(
|
pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
|
||||||
root: impl for<'a> Fn(Context<'a, T>) -> VNode + 'static,
|
|
||||||
root_props: T,
|
|
||||||
) -> Self {
|
|
||||||
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
|
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,28 +60,23 @@ impl WebsysRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
|
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
|
||||||
// let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
|
|
||||||
|
|
||||||
let body_element = prepare_websys_dom();
|
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 root_node = body_element.first_child().unwrap();
|
let root_node = body_element.first_child().unwrap();
|
||||||
|
|
||||||
let mut websys_dom = crate::new::WebsysDom::new(body_element);
|
let mut websys_dom = crate::new::WebsysDom::new(body_element.clone());
|
||||||
|
|
||||||
websys_dom.stack.push(root_node);
|
websys_dom.stack.push(root_node);
|
||||||
// patch_machine.stack.push(root_node.clone());
|
|
||||||
|
|
||||||
// todo: initialize the event registry properly on the root
|
|
||||||
|
|
||||||
self.internal_dom.rebuild(&mut websys_dom)?;
|
self.internal_dom.rebuild(&mut websys_dom)?;
|
||||||
|
|
||||||
|
while let Some(trigger) = websys_dom.wait_for_event().await {
|
||||||
|
let root_node = body_element.first_child().unwrap();
|
||||||
|
websys_dom.stack.push(root_node.clone());
|
||||||
|
self.internal_dom
|
||||||
|
.progress_with_event(&mut websys_dom, trigger)?;
|
||||||
|
}
|
||||||
|
|
||||||
// let edits = self.internal_dom.rebuild()?;
|
// let edits = self.internal_dom.rebuild()?;
|
||||||
// log::debug!("Received edits: {:#?}", edits);
|
// log::debug!("Received edits: {:#?}", edits);
|
||||||
// edits.iter().for_each(|edit| {
|
// edits.iter().for_each(|edit| {
|
||||||
|
|
|
@ -1,19 +1,35 @@
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use dioxus_core::virtual_dom::RealDomNode;
|
use dioxus_core::{
|
||||||
|
events::{EventTrigger, VirtualEvent},
|
||||||
|
prelude::ScopeIdx,
|
||||||
|
virtual_dom::RealDomNode,
|
||||||
|
};
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use nohash_hasher::IntMap;
|
use nohash_hasher::IntMap;
|
||||||
use wasm_bindgen::{closure::Closure, JsCast};
|
use wasm_bindgen::{closure::Closure, JsCast};
|
||||||
use web_sys::{
|
use web_sys::{
|
||||||
window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
|
window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::interpreter::Stack;
|
|
||||||
pub struct WebsysDom {
|
pub struct WebsysDom {
|
||||||
pub stack: Stack,
|
pub stack: Stack,
|
||||||
nodes: IntMap<u32, Node>,
|
nodes: IntMap<u32, Node>,
|
||||||
document: Document,
|
document: Document,
|
||||||
root: Element,
|
root: Element,
|
||||||
|
|
||||||
|
event_receiver: async_channel::Receiver<EventTrigger>,
|
||||||
|
trigger: Arc<dyn Fn(EventTrigger)>,
|
||||||
|
|
||||||
|
// every callback gets a monotomically increasing callback ID
|
||||||
|
callback_id: usize,
|
||||||
|
|
||||||
|
// map of listener types to number of those listeners
|
||||||
|
listeners: FxHashMap<String, (usize, Closure<dyn FnMut(&Event)>)>,
|
||||||
|
|
||||||
|
// Map of callback_id to component index and listener id
|
||||||
|
callback_map: FxHashMap<usize, (usize, usize)>,
|
||||||
|
|
||||||
// We need to make sure to add comments between text nodes
|
// 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
|
// 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.
|
// neighboring text nodes. Originally inspired by some of React's work from 2016.
|
||||||
|
@ -33,18 +49,39 @@ impl WebsysDom {
|
||||||
.document()
|
.document()
|
||||||
.expect("must have access to the Document");
|
.expect("must have access to the Document");
|
||||||
|
|
||||||
|
let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
|
||||||
|
|
||||||
|
let sender_callback = Arc::new(move |ev| {
|
||||||
|
let mut c = sender.clone();
|
||||||
|
wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
c.send(ev).await.unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut nodes =
|
||||||
|
HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
|
||||||
|
|
||||||
|
nodes.insert(0_u32, root.clone().dyn_into::<Node>().unwrap());
|
||||||
Self {
|
Self {
|
||||||
stack: Stack::with_capacity(10),
|
stack: Stack::with_capacity(10),
|
||||||
nodes: HashMap::with_capacity_and_hasher(
|
nodes,
|
||||||
1000,
|
|
||||||
nohash_hasher::BuildNoHashHasher::default(),
|
callback_id: 0,
|
||||||
),
|
listeners: FxHashMap::default(),
|
||||||
|
callback_map: FxHashMap::default(),
|
||||||
document,
|
document,
|
||||||
|
event_receiver: receiver,
|
||||||
|
trigger: sender_callback,
|
||||||
root,
|
root,
|
||||||
last_node_was_text: false,
|
last_node_was_text: false,
|
||||||
node_counter: Counter(0),
|
node_counter: Counter(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn wait_for_event(&mut self) -> Option<EventTrigger> {
|
||||||
|
let v = self.event_receiver.recv().await.unwrap();
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Counter(u32);
|
struct Counter(u32);
|
||||||
|
@ -56,11 +93,13 @@ impl Counter {
|
||||||
}
|
}
|
||||||
impl dioxus_core::diff::RealDom for WebsysDom {
|
impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
|
fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
|
||||||
|
log::debug!("Called `[`push_root] {:?}", root);
|
||||||
let domnode = self.nodes.get(&root.0).expect("Failed to pop know root");
|
let domnode = self.nodes.get(&root.0).expect("Failed to pop know root");
|
||||||
self.stack.push(domnode.clone());
|
self.stack.push(domnode.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_child(&mut self) {
|
fn append_child(&mut self) {
|
||||||
|
log::debug!("Called [`append_child`]");
|
||||||
let child = self.stack.pop();
|
let child = self.stack.pop();
|
||||||
|
|
||||||
if child.dyn_ref::<web_sys::Text>().is_some() {
|
if child.dyn_ref::<web_sys::Text>().is_some() {
|
||||||
|
@ -81,6 +120,7 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_with(&mut self) {
|
fn replace_with(&mut self) {
|
||||||
|
log::debug!("Called [`replace_with`]");
|
||||||
let new_node = self.stack.pop();
|
let new_node = self.stack.pop();
|
||||||
let old_node = self.stack.pop();
|
let old_node = self.stack.pop();
|
||||||
|
|
||||||
|
@ -117,10 +157,12 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&mut self) {
|
fn remove(&mut self) {
|
||||||
|
log::debug!("Called [`remove`]");
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_all_children(&mut self) {
|
fn remove_all_children(&mut self) {
|
||||||
|
log::debug!("Called [`remove_all_children`]");
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +176,8 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
self.stack.push(textnode.clone());
|
self.stack.push(textnode.clone());
|
||||||
self.nodes.insert(nid, textnode);
|
self.nodes.insert(nid, textnode);
|
||||||
|
|
||||||
|
log::debug!("Called [`create_text_node`]: {}, {}", text, nid);
|
||||||
|
|
||||||
RealDomNode::new(nid)
|
RealDomNode::new(nid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,6 +192,7 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
self.stack.push(el.clone());
|
self.stack.push(el.clone());
|
||||||
let nid = self.node_counter.next();
|
let nid = self.node_counter.next();
|
||||||
self.nodes.insert(nid, el);
|
self.nodes.insert(nid, el);
|
||||||
|
log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
|
||||||
RealDomNode::new(nid)
|
RealDomNode::new(nid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +211,7 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
self.stack.push(el.clone());
|
self.stack.push(el.clone());
|
||||||
let nid = self.node_counter.next();
|
let nid = self.node_counter.next();
|
||||||
self.nodes.insert(nid, el);
|
self.nodes.insert(nid, el);
|
||||||
|
log::debug!("Called [`create_element_ns`]: {:}", nid);
|
||||||
RealDomNode::new(nid)
|
RealDomNode::new(nid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,81 +219,133 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &str,
|
event: &str,
|
||||||
scope: dioxus_core::prelude::ScopeIdx,
|
scope: dioxus_core::prelude::ScopeIdx,
|
||||||
id: usize,
|
el_id: usize,
|
||||||
|
real_id: RealDomNode,
|
||||||
) {
|
) {
|
||||||
// if let Some(entry) = self.listeners.get_mut(event) {
|
log::debug!(
|
||||||
// entry.0 += 1;
|
"Called [`new_event_listener`]: {}, {:?}, {}, {:?}",
|
||||||
// } else {
|
event,
|
||||||
// let trigger = self.trigger.clone();
|
scope,
|
||||||
// let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
el_id,
|
||||||
// log::debug!("Handling event!");
|
real_id
|
||||||
|
);
|
||||||
|
// attach the correct attributes to the element
|
||||||
|
// these will be used by accessing the event's target
|
||||||
|
// This ensures we only ever have one handler attached to the root, but decide
|
||||||
|
// dynamically when we want to call a listener.
|
||||||
|
|
||||||
// let target = event
|
let el = self.stack.top();
|
||||||
// .target()
|
|
||||||
// .expect("missing target")
|
|
||||||
// .dyn_into::<Element>()
|
|
||||||
// .expect("not a valid element");
|
|
||||||
|
|
||||||
// let typ = event.type_();
|
let el = el
|
||||||
|
.dyn_ref::<Element>()
|
||||||
|
.expect(&format!("not an element: {:?}", el));
|
||||||
|
|
||||||
// let gi_id: Option<usize> = target
|
let (gi_id, gi_gen) = (&scope).into_raw_parts();
|
||||||
// .get_attribute(&format!("dioxus-giid-{}", typ))
|
el.set_attribute(
|
||||||
// .and_then(|v| v.parse().ok());
|
&format!("dioxus-event-{}", event),
|
||||||
|
&format!("{}.{}.{}.{}", gi_id, gi_gen, el_id, real_id.0),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// let gi_gen: Option<u64> = target
|
// Register the callback to decode
|
||||||
// .get_attribute(&format!("dioxus-gigen-{}", typ))
|
|
||||||
// .and_then(|v| v.parse().ok());
|
|
||||||
|
|
||||||
// let li_idx: Option<usize> = target
|
if let Some(entry) = self.listeners.get_mut(event) {
|
||||||
// .get_attribute(&format!("dioxus-lidx-{}", typ))
|
entry.0 += 1;
|
||||||
// .and_then(|v| v.parse().ok());
|
} else {
|
||||||
|
let trigger = self.trigger.clone();
|
||||||
|
let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
||||||
|
// "Result" cannot be received from JS
|
||||||
|
// Instead, we just build and immediately execute a closure that returns result
|
||||||
|
let res = || -> anyhow::Result<EventTrigger> {
|
||||||
|
log::debug!("Handling event!");
|
||||||
|
|
||||||
// if let (Some(gi_id), Some(gi_gen), Some(li_idx)) = (gi_id, gi_gen, li_idx) {
|
let target = event
|
||||||
// // Call the trigger
|
.target()
|
||||||
// log::debug!(
|
.expect("missing target")
|
||||||
// "decoded gi_id: {}, gi_gen: {}, li_idx: {}",
|
.dyn_into::<Element>()
|
||||||
// gi_id,
|
.expect("not a valid element");
|
||||||
// gi_gen,
|
|
||||||
// li_idx
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let triggered_scope = ScopeIdx::from_raw_parts(gi_id, gi_gen);
|
let typ = event.type_();
|
||||||
// trigger.0.as_ref()(EventTrigger::new(
|
use anyhow::Context;
|
||||||
// virtual_event_from_websys_event(event),
|
let val: String = target
|
||||||
// triggered_scope,
|
.get_attribute(&format!("dioxus-event-{}", typ))
|
||||||
// // scope,
|
.context("")?;
|
||||||
// li_idx,
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }) as Box<dyn FnMut(&Event)>);
|
|
||||||
|
|
||||||
// self.root
|
let mut fields = val.splitn(4, ".");
|
||||||
// .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
|
|
||||||
// .unwrap();
|
|
||||||
|
|
||||||
// // Increment the listeners
|
let gi_id = fields
|
||||||
// self.listeners.insert(event.into(), (1, handler));
|
.next()
|
||||||
// }
|
.and_then(|f| f.parse::<usize>().ok())
|
||||||
|
.context("")?;
|
||||||
|
let gi_gen = fields
|
||||||
|
.next()
|
||||||
|
.and_then(|f| f.parse::<u64>().ok())
|
||||||
|
.context("")?;
|
||||||
|
let el_id = fields
|
||||||
|
.next()
|
||||||
|
.and_then(|f| f.parse::<usize>().ok())
|
||||||
|
.context("")?;
|
||||||
|
let real_id = fields
|
||||||
|
.next()
|
||||||
|
.and_then(|f| f.parse::<u32>().ok().map(RealDomNode::new))
|
||||||
|
.context("")?;
|
||||||
|
|
||||||
|
// Call the trigger
|
||||||
|
log::debug!(
|
||||||
|
"decoded gi_id: {}, gi_gen: {}, li_idx: {}",
|
||||||
|
gi_id,
|
||||||
|
gi_gen,
|
||||||
|
el_id
|
||||||
|
);
|
||||||
|
|
||||||
|
let triggered_scope = ScopeIdx::from_raw_parts(gi_id, gi_gen);
|
||||||
|
Ok(EventTrigger::new(
|
||||||
|
virtual_event_from_websys_event(event),
|
||||||
|
triggered_scope,
|
||||||
|
real_id,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
match res() {
|
||||||
|
Ok(synthetic_event) => trigger.as_ref()(synthetic_event),
|
||||||
|
Err(_) => log::error!("Error decoding Dioxus event attribute."),
|
||||||
|
};
|
||||||
|
}) 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) {
|
fn remove_event_listener(&mut self, event: &str) {
|
||||||
|
log::debug!("Called [`remove_event_listener`]: {}", event);
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_text(&mut self, text: &str) {
|
fn set_text(&mut self, text: &str) {
|
||||||
|
log::debug!("Called [`set_text`]: {}", text);
|
||||||
self.stack.top().set_text_content(Some(text))
|
self.stack.top().set_text_content(Some(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
|
fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
|
||||||
|
log::debug!("Called [`set_attribute`]: {}, {}", name, value);
|
||||||
if name == "class" {
|
if name == "class" {
|
||||||
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
el.set_class_name(value);
|
el.set_class_name(value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if let Some(el) = self.stack.top().dyn_ref::<Element>() {
|
||||||
|
el.set_attribute(name, value).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_attribute(&mut self, name: &str) {
|
fn remove_attribute(&mut self, name: &str) {
|
||||||
|
log::debug!("Called [`remove_attribute`]: {}", name);
|
||||||
let node = self.stack.top();
|
let node = self.stack.top();
|
||||||
if let Some(node) = node.dyn_ref::<web_sys::Element>() {
|
if let Some(node) = node.dyn_ref::<web_sys::Element>() {
|
||||||
node.remove_attribute(name).unwrap();
|
node.remove_attribute(name).unwrap();
|
||||||
|
@ -270,6 +368,234 @@ impl dioxus_core::diff::RealDom for WebsysDom {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
|
fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
|
||||||
|
log::debug!("Called [`raw_node_as_any_mut`]");
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Stack {
|
||||||
|
list: Vec<Node>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack {
|
||||||
|
pub fn with_capacity(cap: usize) -> Self {
|
||||||
|
Stack {
|
||||||
|
list: Vec::with_capacity(cap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, node: Node) {
|
||||||
|
// debug!("stack-push: {:?}", node);
|
||||||
|
self.list.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Node {
|
||||||
|
let res = self.list.pop().unwrap();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn top(&self) -> &Node {
|
||||||
|
match self.list.last() {
|
||||||
|
Some(a) => a,
|
||||||
|
None => panic!("Called 'top' of an empty stack, make sure to push the root first"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
|
||||||
|
use dioxus_core::events::on::*;
|
||||||
|
match event.type_().as_str() {
|
||||||
|
"copy" | "cut" | "paste" => {
|
||||||
|
// let evt: web_sys::ClipboardEvent = event.clone().dyn_into().unwrap();
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||||
|
let evt: web_sys::CompositionEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"keydown" | "keypress" | "keyup" => {
|
||||||
|
let evt: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"focus" | "blur" => {
|
||||||
|
let evt: web_sys::FocusEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"change" => {
|
||||||
|
let evt: web_sys::Event = event.clone().dyn_into().expect("wrong error typ");
|
||||||
|
todo!()
|
||||||
|
// VirtualEvent::FormEvent(FormEvent {value:})
|
||||||
|
}
|
||||||
|
|
||||||
|
"input" | "invalid" | "reset" | "submit" => {
|
||||||
|
// is a special react events
|
||||||
|
let evt: web_sys::InputEvent = event.clone().dyn_into().expect("wrong event type");
|
||||||
|
let this: web_sys::EventTarget = evt.target().unwrap();
|
||||||
|
|
||||||
|
let value = (&this)
|
||||||
|
.dyn_ref()
|
||||||
|
.map(|input: &web_sys::HtmlInputElement| input.value())
|
||||||
|
.or_else(|| {
|
||||||
|
(&this)
|
||||||
|
.dyn_ref()
|
||||||
|
.map(|input: &web_sys::HtmlTextAreaElement| input.value())
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
(&this)
|
||||||
|
.dyn_ref::<web_sys::HtmlElement>()
|
||||||
|
.unwrap()
|
||||||
|
.text_content()
|
||||||
|
})
|
||||||
|
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
||||||
|
|
||||||
|
// let p2 = evt.data_transfer();
|
||||||
|
|
||||||
|
// let value: Option<String> = (&evt).data();
|
||||||
|
// let value = val;
|
||||||
|
// let value = value.unwrap_or_default();
|
||||||
|
// let value = (&evt).data().expect("No data to unwrap");
|
||||||
|
|
||||||
|
// todo - this needs to be a "controlled" event
|
||||||
|
// these events won't carry the right data with them
|
||||||
|
todo!()
|
||||||
|
// VirtualEvent::FormEvent(FormEvent { value })
|
||||||
|
}
|
||||||
|
|
||||||
|
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||||
|
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||||
|
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||||
|
let evt: web_sys::MouseEvent = event.clone().dyn_into().unwrap();
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CustomMouseEvent(web_sys::MouseEvent);
|
||||||
|
impl dioxus_core::events::on::MouseEvent for CustomMouseEvent {
|
||||||
|
fn alt_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn button(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn buttons(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn client_x(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn client_y(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn ctrl_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn meta_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn page_x(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn page_y(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn screen_x(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn screen_y(&self) -> i32 {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn shift_key(&self) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn get_modifier_state(&self, key_code: usize) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VirtualEvent::MouseEvent(Rc::new(CustomMouseEvent(evt)))
|
||||||
|
// MouseEvent(Box::new(RawMouseEvent {
|
||||||
|
// alt_key: evt.alt_key(),
|
||||||
|
// button: evt.button() as i32,
|
||||||
|
// buttons: evt.buttons() as i32,
|
||||||
|
// client_x: evt.client_x(),
|
||||||
|
// client_y: evt.client_y(),
|
||||||
|
// ctrl_key: evt.ctrl_key(),
|
||||||
|
// meta_key: evt.meta_key(),
|
||||||
|
// page_x: evt.page_x(),
|
||||||
|
// page_y: evt.page_y(),
|
||||||
|
// screen_x: evt.screen_x(),
|
||||||
|
// screen_y: evt.screen_y(),
|
||||||
|
// shift_key: evt.shift_key(),
|
||||||
|
// get_modifier_state: GetModifierKey(Box::new(|f| {
|
||||||
|
// // evt.get_modifier_state(f)
|
||||||
|
// todo!("This is not yet implemented properly, sorry :(");
|
||||||
|
// })),
|
||||||
|
// }))
|
||||||
|
// todo!()
|
||||||
|
// VirtualEvent::MouseEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||||
|
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||||
|
let evt: web_sys::PointerEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"select" => {
|
||||||
|
// let evt: web_sys::SelectionEvent = event.clone().dyn_into().unwrap();
|
||||||
|
// not required to construct anything special beyond standard event stuff
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"touchcancel" | "touchend" | "touchmove" | "touchstart" => {
|
||||||
|
let evt: web_sys::TouchEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"scroll" => {
|
||||||
|
// let evt: web_sys::UIEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"wheel" => {
|
||||||
|
let evt: web_sys::WheelEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||||
|
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||||
|
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||||
|
| "timeupdate" | "volumechange" | "waiting" => {
|
||||||
|
// not required to construct anything special beyond standard event stuff
|
||||||
|
|
||||||
|
// let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
|
||||||
|
// let evt: web_sys::MediaEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"animationstart" | "animationend" | "animationiteration" => {
|
||||||
|
let evt: web_sys::AnimationEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"transitionend" => {
|
||||||
|
let evt: web_sys::TransitionEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
"toggle" => {
|
||||||
|
// not required to construct anything special beyond standard event stuff (target)
|
||||||
|
|
||||||
|
// let evt: web_sys::ToggleEvent = event.clone().dyn_into().unwrap();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
_ => VirtualEvent::OtherEvent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{borrow::Borrow, convert::TryInto, default, fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
events::{EventTrigger, VirtualEvent},
|
events::{EventTrigger, VirtualEvent},
|
||||||
patch::Edit,
|
|
||||||
prelude::ScopeIdx,
|
prelude::ScopeIdx,
|
||||||
};
|
};
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
Loading…
Reference in New Issue