update the web renderer to the new dioxus core

This commit is contained in:
Evan Almloff committed 2024-01-06 16:44:07 -06:00
1 parent f4af297777
commit 88e2da6c11
8 files changed
+272 -202

No files matched your search

+1 -1
View File
@@ -128,7 +128,7 @@ pub trait WriteMutations {
/// Push the given root node onto our stack. /// Push the given root node onto our stack.
/// ///
/// Id: The ID of the root node to push. /// Id: The ID of the root node to push.
fn push_root(&mut self, _id: ElementId); fn push_root(&mut self, id: ElementId);
/// Swap to a new subtree /// Swap to a new subtree
fn swap_subtree(&mut self, _subtree_index: usize) {} fn swap_subtree(&mut self, _subtree_index: usize) {}
+18 -3
View File
@@ -12,7 +12,12 @@ fn events_propagate() {
_ = dom.rebuild(); _ = dom.rebuild();
// Top-level click is registered // Top-level click is registered
dom.handle_event("click", Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())), ElementId(1), true); dom.handle_event(
"click",
Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())),
ElementId(1),
true,
);
assert_eq!(*CLICKS.lock().unwrap(), 1); assert_eq!(*CLICKS.lock().unwrap(), 1);
// break reference.... // break reference....
@@ -22,7 +27,12 @@ fn events_propagate() {
} }
// Lower click is registered // Lower click is registered
dom.handle_event("click", Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())), ElementId(2), true); dom.handle_event(
"click",
Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())),
ElementId(2),
true,
);
assert_eq!(*CLICKS.lock().unwrap(), 3); assert_eq!(*CLICKS.lock().unwrap(), 3);
// break reference.... // break reference....
@@ -32,7 +42,12 @@ fn events_propagate() {
} }
// Stop propagation occurs // Stop propagation occurs
dom.handle_event("click", Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())), ElementId(2), true); dom.handle_event(
"click",
Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())),
ElementId(2),
true,
);
assert_eq!(*CLICKS.lock().unwrap(), 3); assert_eq!(*CLICKS.lock().unwrap(), 3);
} }
+1 -1
View File
@@ -16,7 +16,7 @@ macro_rules! impl_event {
::dioxus_core::Attribute::new( ::dioxus_core::Attribute::new(
stringify!($name), stringify!($name),
::dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<crate::PlatformEventData>| { ::dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<crate::PlatformEventData>| {
_f(e.map(|e|e.into())).spawn(_cx); _f(e.map(|e|e.into())).spawn();
}), }),
None, None,
false, false,
+5 -166
View File
@@ -6,9 +6,7 @@
//! - tests to ensure dyn_into works for various event types. //! - tests to ensure dyn_into works for various event types.
//! - Partial delegation? //! - Partial delegation?
use dioxus_core::{ use dioxus_core::{ElementId, Mutation, Template, TemplateAttribute, TemplateNode};
BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
};
use dioxus_html::{event_bubbles, MountedData, PlatformEventData}; use dioxus_html::{event_bubbles, MountedData, PlatformEventData};
use dioxus_interpreter_js::get_node; use dioxus_interpreter_js::get_node;
use dioxus_interpreter_js::{minimal_bindings, save_template, Channel}; use dioxus_interpreter_js::{minimal_bindings, save_template, Channel};
@@ -20,14 +18,14 @@ use web_sys::{Document, Element, Event};
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter}; use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
pub struct WebsysDom { pub struct WebsysDom {
document: Document, pub(crate) document: Document,
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) root: Element, pub(crate) root: Element,
templates: FxHashMap<String, u16>, pub(crate) templates: FxHashMap<String, u16>,
max_template_id: u16, pub(crate) max_template_id: u16,
pub(crate) interpreter: Channel, pub(crate) interpreter: Channel,
#[cfg(feature = "mounted")] #[cfg(feature = "mounted")]
event_channel: mpsc::UnboundedSender<UiEvent>, pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
} }
pub struct UiEvent { pub struct UiEvent {
@@ -118,165 +116,6 @@ impl WebsysDom {
pub fn mount(&mut self) { pub fn mount(&mut self) {
self.interpreter.mount_to_root(); self.interpreter.mount_to_root();
} }
pub fn load_templates(&mut self, templates: &[Template]) {
for template in templates {
let mut roots = vec![];
for root in template.roots {
roots.push(self.create_template_node(root))
}
self.templates
.insert(template.name.to_owned(), self.max_template_id);
save_template(roots, self.max_template_id);
self.max_template_id += 1
}
}
fn create_template_node(&self, v: &TemplateNode) -> web_sys::Node {
use TemplateNode::*;
match v {
Element {
tag,
namespace,
attrs,
children,
..
} => {
let el = match namespace {
Some(ns) => self.document.create_element_ns(Some(ns), tag).unwrap(),
None => self.document.create_element(tag).unwrap(),
};
for attr in *attrs {
if let TemplateAttribute::Static {
name,
value,
namespace,
} = attr
{
minimal_bindings::setAttributeInner(
el.clone().into(),
name,
JsValue::from_str(value),
*namespace,
);
}
}
for child in *children {
let _ = el.append_child(&self.create_template_node(child));
}
el.dyn_into().unwrap()
}
Text { text } => self.document.create_text_node(text).dyn_into().unwrap(),
DynamicText { .. } => self.document.create_text_node("p").dyn_into().unwrap(),
Dynamic { .. } => {
let el = self.document.create_element("pre").unwrap();
let _ = el.toggle_attribute("hidden");
el.dyn_into().unwrap()
}
}
}
pub fn apply_edits(&mut self, mut edits: Vec<Mutation>) {
use Mutation::*;
let i = &mut self.interpreter;
#[cfg(feature = "mounted")]
// we need to apply the mount events last, so we collect them here
let mut to_mount = Vec::new();
for edit in &edits {
match edit {
AppendChildren { id, m } => i.append_children(id.0 as u32, *m as u16),
AssignId { path, id } => {
i.assign_id(path.as_ptr() as u32, path.len() as u8, id.0 as u32)
}
CreatePlaceholder { id } => i.create_placeholder(id.0 as u32),
CreateTextNode { value, id } => i.create_text_node(value, id.0 as u32),
HydrateText { path, value, id } => {
i.hydrate_text(path.as_ptr() as u32, path.len() as u8, value, id.0 as u32)
}
LoadTemplate { name, index, id } => {
if let Some(tmpl_id) = self.templates.get(*name) {
i.load_template(*tmpl_id, *index as u16, id.0 as u32)
}
}
ReplaceWith { id, m } => i.replace_with(id.0 as u32, *m as u16),
ReplacePlaceholder { path, m } => {
i.replace_placeholder(path.as_ptr() as u32, path.len() as u8, *m as u16)
}
InsertAfter { id, m } => i.insert_after(id.0 as u32, *m as u16),
InsertBefore { id, m } => i.insert_before(id.0 as u32, *m as u16),
SetAttribute {
name,
value,
id,
ns,
} => match value {
BorrowedAttributeValue::Text(txt) => {
i.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
}
BorrowedAttributeValue::Float(f) => {
i.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default())
}
BorrowedAttributeValue::Int(n) => {
i.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default())
}
BorrowedAttributeValue::Bool(b) => i.set_attribute(
id.0 as u32,
name,
if *b { "true" } else { "false" },
ns.unwrap_or_default(),
),
BorrowedAttributeValue::None => {
i.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
}
_ => unreachable!(),
},
SetText { value, id } => i.set_text(id.0 as u32, value),
NewEventListener { name, id, .. } => {
match *name {
// mounted events are fired immediately after the element is mounted.
"mounted" => {
#[cfg(feature = "mounted")]
to_mount.push(*id);
}
_ => {
i.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
}
}
}
RemoveEventListener { name, id } => match *name {
"mounted" => {}
_ => {
i.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
}
},
Remove { id } => i.remove(id.0 as u32),
PushRoot { id } => i.push_root(id.0 as u32),
}
}
edits.clear();
i.flush();
#[cfg(feature = "mounted")]
for id in to_mount {
self.send_mount_event(id);
}
}
pub(crate) fn send_mount_event(&self, id: ElementId) {
let node = get_node(id.0 as u32);
if let Some(element) = node.dyn_ref::<Element>() {
let data: MountedData = element.into();
let data = Box::new(data);
let _ = self.event_channel.unbounded_send(UiEvent {
name: "mounted".to_string(),
bubbles: false,
element: id,
data: PlatformEventData::new(data),
});
}
}
} }
fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> { fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> {
+6 -5
View File
@@ -1,5 +1,6 @@
use async_trait::async_trait; use async_trait::async_trait;
use dioxus_core::ScopeState; use dioxus_core::prelude::provide_context;
use dioxus_core::ScopeId;
use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator}; use dioxus_html::prelude::{EvalError, EvalProvider, Evaluator};
use js_sys::Function; use js_sys::Function;
use serde_json::Value; use serde_json::Value;
@@ -7,12 +8,12 @@ use std::{cell::RefCell, rc::Rc, str::FromStr};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
/// Provides the WebEvalProvider through [`cx.provide_context`]. /// Provides the WebEvalProvider through [`cx.provide_context`].
pub fn init_eval(cx: &ScopeState) { pub fn init_eval() {
let provider: Rc<dyn EvalProvider> = Rc::new(WebEvalProvider {}); let provider: Rc<dyn EvalProvider> = Rc::new(WebEvalProvider {});
cx.provide_context(provider); ScopeId::ROOT.provide_context(provider);
} }
/// Reprents the web-target's provider of evaluators. /// Represents the web-target's provider of evaluators.
pub struct WebEvalProvider; pub struct WebEvalProvider;
impl EvalProvider for WebEvalProvider { impl EvalProvider for WebEvalProvider {
fn new_evaluator(&self, js: String) -> Result<Rc<dyn Evaluator>, EvalError> { fn new_evaluator(&self, js: String) -> Result<Rc<dyn Evaluator>, EvalError> {
@@ -28,7 +29,7 @@ const PROMISE_WRAPPER: &str = r#"
}); });
"#; "#;
/// Reprents a web-target's JavaScript evaluator. /// Represents a web-target's JavaScript evaluator.
pub struct WebEvaluator { pub struct WebEvaluator {
dioxus: Dioxus, dioxus: Dioxus,
channel_receiver: async_channel::Receiver<serde_json::Value>, channel_receiver: async_channel::Receiver<serde_json::Value>,
+2 -2
View File
@@ -4,7 +4,7 @@ use futures_channel::mpsc::UnboundedReceiver;
use dioxus_core::Template; use dioxus_core::Template;
pub(crate) fn init() -> UnboundedReceiver<Template<'static>> { pub(crate) fn init() -> UnboundedReceiver<Template> {
use wasm_bindgen::closure::Closure; use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::{MessageEvent, WebSocket}; use web_sys::{MessageEvent, WebSocket};
@@ -34,7 +34,7 @@ pub(crate) fn init() -> UnboundedReceiver<Template<'static>> {
let val = serde_json::from_str::<serde_json::Value>(&string).unwrap(); let val = serde_json::from_str::<serde_json::Value>(&string).unwrap();
// leak the value // leak the value
let val: &'static serde_json::Value = Box::leak(Box::new(val)); let val: &'static serde_json::Value = Box::leak(Box::new(val));
let template: Template<'_> = Template::deserialize(val).unwrap(); let template: Template = Template::deserialize(val).unwrap();
tx.unbounded_send(template).unwrap(); tx.unbounded_send(template).unwrap();
} }
}) as Box<dyn FnMut(MessageEvent)>); }) as Box<dyn FnMut(MessageEvent)>);
+24 -24
View File
@@ -60,7 +60,7 @@ use std::rc::Rc;
pub use crate::cfg::Config; pub use crate::cfg::Config;
#[cfg(feature = "file_engine")] #[cfg(feature = "file_engine")]
pub use crate::file_engine::WebFileEngineExt; pub use crate::file_engine::WebFileEngineExt;
use dioxus_core::{Element, Scope, VirtualDom}; use dioxus_core::{Element, VirtualDom};
use futures_util::{ use futures_util::{
future::{select, Either}, future::{select, Either},
pin_mut, FutureExt, StreamExt, pin_mut, FutureExt, StreamExt,
@@ -72,6 +72,7 @@ mod dom;
#[cfg(feature = "eval")] #[cfg(feature = "eval")]
mod eval; mod eval;
mod event; mod event;
mod mutations;
pub use event::*; pub use event::*;
#[cfg(feature = "file_engine")] #[cfg(feature = "file_engine")]
mod file_engine; mod file_engine;
@@ -105,8 +106,8 @@ mod rehydrate;
/// render!(div {"hello world"}) /// render!(div {"hello world"})
/// } /// }
/// ``` /// ```
pub fn launch(root_component: fn(Scope) -> Element) { pub fn launch(root_component: fn(()) -> Element) {
launch_with_props(root_component, (), Config::default()); launch_with_props( root_component, (), Config::default());
} }
/// Launch your app and run the event loop, with configuration. /// Launch your app and run the event loop, with configuration.
@@ -122,13 +123,13 @@ pub fn launch(root_component: fn(Scope) -> Element) {
/// dioxus_web::launch_with_props(App, Config::new().pre_render(true)); /// dioxus_web::launch_with_props(App, Config::new().pre_render(true));
/// } /// }
/// ///
/// fn app(cx: Scope) -> Element { /// fn app() -> Element {
/// cx.render(rsx!{ /// cx.render(rsx!{
/// h1 {"hello world!"} /// h1 {"hello world!"}
/// }) /// })
/// } /// }
/// ``` /// ```
pub fn launch_cfg(root: fn(Scope) -> Element, config: Config) { pub fn launch_cfg(root: fn(()) -> Element, config: Config) {
launch_with_props(root, (), config) launch_with_props(root, (), config)
} }
@@ -156,8 +157,8 @@ pub fn launch_cfg(root: fn(Scope) -> Element, config: Config) {
/// render!(div {"hello {cx.props.name}"}) /// render!(div {"hello {cx.props.name}"})
/// } /// }
/// ``` /// ```
pub fn launch_with_props<T: 'static>( pub fn launch_with_props<T: Clone + 'static>(
root_component: fn(Scope<T>) -> Element, root_component: fn(T) -> Element,
root_properties: T, root_properties: T,
config: Config, config: Config,
) { ) {
@@ -176,7 +177,11 @@ pub fn launch_with_props<T: 'static>(
/// wasm_bindgen_futures::spawn_local(app_fut); /// wasm_bindgen_futures::spawn_local(app_fut);
/// } /// }
/// ``` /// ```
pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_props: T, cfg: Config) { pub async fn run_with_props<T: Clone + 'static>(
root: fn(T) -> Element,
root_props: T,
cfg: Config,
) {
tracing::info!("Starting up"); tracing::info!("Starting up");
let mut dom = VirtualDom::new_with_props(root, root_props); let mut dom = VirtualDom::new_with_props(root, root_props);
@@ -184,8 +189,9 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
#[cfg(feature = "eval")] #[cfg(feature = "eval")]
{ {
// Eval // Eval
let cx = dom.base_scope(); dom.in_runtime(|| {
eval::init_eval(cx); eval::init_eval();
});
} }
#[cfg(feature = "panic_hook")] #[cfg(feature = "panic_hook")]
@@ -221,28 +227,23 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
// it's a waste to produce edits just to get the vdom loaded // it's a waste to produce edits just to get the vdom loaded
{ {
let mutations = dom.rebuild(); dom.rebuild(&mut websys_dom);
web_sys::console::log_1(&format!("mutations: {:#?}", mutations).into()); websys_dom.flush_edits();
let templates = mutations.templates;
websys_dom.load_templates(&templates);
websys_dom.interpreter.flush();
} }
if let Err(err) = websys_dom.rehydrate(&dom) { if let Err(err) = websys_dom.rehydrate(&dom) {
tracing::error!("Rehydration failed. {:?}", err); tracing::error!("Rehydration failed. {:?}", err);
tracing::error!("Rebuild DOM into element from scratch"); tracing::error!("Rebuild DOM into element from scratch");
websys_dom.root.set_text_content(None); websys_dom.root.set_text_content(None);
let edits = dom.rebuild(); dom.rebuild(&mut websys_dom);
websys_dom.load_templates(&edits.templates); websys_dom.flush_edits();
websys_dom.apply_edits(edits.edits);
} }
} }
} else { } else {
let edits = dom.rebuild(); dom.rebuild(&mut websys_dom);
websys_dom.load_templates(&edits.templates); websys_dom.flush_edits();
websys_dom.apply_edits(edits.edits);
} }
// the mutations come back with nothing - we need to actually mount them // the mutations come back with nothing - we need to actually mount them
@@ -298,12 +299,11 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
// let deadline = work_loop.wait_for_idle_time().await; // let deadline = work_loop.wait_for_idle_time().await;
// run the virtualdom work phase until the frame deadline is reached // run the virtualdom work phase until the frame deadline is reached
let edits = dom.render_immediate(); dom.render_immediate(&mut websys_dom);
// wait for the animation frame to fire so we can apply our changes // wait for the animation frame to fire so we can apply our changes
// work_loop.wait_for_raf().await; // work_loop.wait_for_raf().await;
websys_dom.load_templates(&edits.templates); websys_dom.flush_edits();
websys_dom.apply_edits(edits.edits);
} }
} }
+215
View File
@@ -0,0 +1,215 @@
use wasm_bindgen::JsCast;use crate::dom::UiEvent;
use crate::dom::WebsysDom;
use dioxus_core::prelude::*;
use dioxus_core::WriteMutations;
use dioxus_core::{AttributeValue, ElementId, ScopeId};
use dioxus_html::event_bubbles;
use dioxus_html::MountedData;
use dioxus_html::PlatformEventData;
use dioxus_interpreter_js::get_node;
use dioxus_interpreter_js::minimal_bindings;
use dioxus_interpreter_js::save_template;
use wasm_bindgen::JsValue;
impl WebsysDom {
fn create_template_node(&self, v: &TemplateNode) -> web_sys::Node {
use TemplateNode::*;
match v {
Element {
tag,
namespace,
attrs,
children,
..
} => {
let el = match namespace {
Some(ns) => self.document.create_element_ns(Some(ns), tag).unwrap(),
None => self.document.create_element(tag).unwrap(),
};
for attr in *attrs {
if let TemplateAttribute::Static {
name,
value,
namespace,
} = attr
{
minimal_bindings::setAttributeInner(
el.clone().into(),
name,
JsValue::from_str(value),
*namespace,
);
}
}
for child in *children {
let _ = el.append_child(&self.create_template_node(child));
}
el.dyn_into().unwrap()
}
Text { text } => self.document.create_text_node(text).dyn_into().unwrap(),
DynamicText { .. } => self.document.create_text_node("p").dyn_into().unwrap(),
Dynamic { .. } => {
let el = self.document.create_element("pre").unwrap();
let _ = el.toggle_attribute("hidden");
el.dyn_into().unwrap()
}
}
}
pub fn flush_edits(&mut self) {
self.interpreter.flush()
}
#[cfg(feature = "mounted")]
pub(crate) fn send_mount_event(&self, id: ElementId) {
let node = get_node(id.0 as u32);
if let Some(element) = node.dyn_ref::<web_sys::Element>() {
let data: MountedData = element.into();
let data = Box::new(data);
let _ = self.event_channel.unbounded_send(UiEvent {
name: "mounted".to_string(),
bubbles: false,
element: id,
data: PlatformEventData::new(data),
});
}
}
}
impl WriteMutations for WebsysDom {
fn register_template(&mut self, template: Template) {
let mut roots = vec![];
for root in template.roots {
roots.push(self.create_template_node(root))
}
self.templates
.insert(template.name.to_owned(), self.max_template_id);
save_template(roots, self.max_template_id);
self.max_template_id += 1
}
fn append_children(&mut self, id: ElementId, m: usize) {
self.interpreter.append_children(id.0 as u32, m as u16)
}
fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
self.interpreter
.assign_id(path.as_ptr() as u32, path.len() as u8, id.0 as u32)
}
fn create_placeholder(&mut self, id: ElementId) {
self.interpreter.create_placeholder(id.0 as u32)
}
fn create_text_node(&mut self, value: &str, id: ElementId) {
self.interpreter.create_text_node(value, id.0 as u32)
}
fn hydrate_text_node(&mut self, path: &'static [u8], value: &str, id: ElementId) {
self.interpreter
.hydrate_text(path.as_ptr() as u32, path.len() as u8, value, id.0 as u32)
}
fn load_template(&mut self, name: &'static str, index: usize, id: ElementId) {
if let Some(tmpl_id) = self.templates.get(name) {
self.interpreter
.load_template(*tmpl_id, index as u16, id.0 as u32)
}
}
fn replace_node_with(&mut self, id: ElementId, m: usize) {
self.interpreter.replace_with(id.0 as u32, m as u16)
}
fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
self.interpreter
.replace_placeholder(path.as_ptr() as u32, path.len() as u8, m as u16)
}
fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
self.interpreter.insert_after(id.0 as u32, m as u16)
}
fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
self.interpreter.insert_before(id.0 as u32, m as u16)
}
fn set_attribute(
&mut self,
name: &'static str,
ns: Option<&'static str>,
value: &AttributeValue,
id: ElementId,
) {
match value {
AttributeValue::Text(txt) => {
self.interpreter
.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default())
}
AttributeValue::Float(f) => self.interpreter.set_attribute(
id.0 as u32,
name,
&f.to_string(),
ns.unwrap_or_default(),
),
AttributeValue::Int(n) => self.interpreter.set_attribute(
id.0 as u32,
name,
&n.to_string(),
ns.unwrap_or_default(),
),
AttributeValue::Bool(b) => self.interpreter.set_attribute(
id.0 as u32,
name,
if *b { "true" } else { "false" },
ns.unwrap_or_default(),
),
AttributeValue::None => {
self.interpreter
.remove_attribute(id.0 as u32, name, ns.unwrap_or_default())
}
_ => unreachable!(),
}
}
fn set_node_text(&mut self, value: &str, id: ElementId) {
self.interpreter.set_text(id.0 as u32, value)
}
fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
match name {
// mounted events are fired immediately after the element is mounted.
"mounted" => {
#[cfg(feature = "mounted")]
self.send_mount_event(id);
}
_ => {
self.interpreter
.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
}
}
}
fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
match name {
"mounted" => {}
_ => {
self.interpreter.remove_event_listener(
name,
id.0 as u32,
event_bubbles(name) as u8,
);
}
}
}
fn remove_node(&mut self, id: ElementId) {
self.interpreter.remove(id.0 as u32)
}
fn push_root(&mut self, id: ElementId) {
self.interpreter.push_root(id.0 as u32)
}
}