update the web renderer to the new dioxus core
This commit is contained in:
1 parent
f4af297777
commit
88e2da6c11
10 files changed
+274
-204
No files matched your search
+1
-1
@@ -61,7 +61,7 @@ fn compose(cx: Scope<ComposeProps>) -> Element {
|
|||||||
},
|
},
|
||||||
"Click to send"
|
"Click to send"
|
||||||
}
|
}
|
||||||
|
|
||||||
input { oninput: move |e| user_input.set(e.value()), value: "{user_input}" }
|
input { oninput: move |e| user_input.set(e.value()), value: "{user_input}" }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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) {}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ pub fn launch_with_props<P: Clone + 'static>(root: Component<P>, props: P, cfg:
|
|||||||
|
|
||||||
// Copy over any assets we find
|
// Copy over any assets we find
|
||||||
crate::collect_assets::copy_assets();
|
crate::collect_assets::copy_assets();
|
||||||
|
|
||||||
// Set the event converter
|
// Set the event converter
|
||||||
dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter));
|
dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter));
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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)> {
|
||||||
|
|||||||
@@ -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>,
|
||||||
|
|||||||
@@ -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
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user