Switch to bun, clean up web implementation

This commit is contained in:
Jonathan Kelley committed 2024-03-04 15:23:48 -08:00
1 parent 199173a409
commit 22266cc560
23 files changed
+240 -173

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
requestIdleCallback and requestAnimationFrame implemenation
These currently actually slow down our DOM patching and thus are temporarily removed. Technically we can schedule around rIC and rAF but choose not to.
File renamed without changes.
File renamed without changes.
+38 -33
View File
@@ -8,7 +8,7 @@
use dioxus_core::ElementId;
use dioxus_html::PlatformEventData;
use dioxus_interpreter_js::unified_bindings::{Interpreter, InterpreterInterface};
use dioxus_interpreter_js::{unified_bindings::Interpreter, WebInterpreter};
use futures_channel::mpsc;
use rustc_hash::FxHashMap;
use wasm_bindgen::{closure::Closure, JsCast};
@@ -17,14 +17,16 @@ use web_sys::{Document, Element, Event};
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
pub struct WebsysDom {
pub(crate) document: Document,
#[allow(dead_code)]
pub(crate) root: Element,
pub(crate) document: Document,
pub(crate) templates: FxHashMap<String, u16>,
pub(crate) max_template_id: u16,
pub(crate) interpreter: InterpreterInterface,
pub(crate) interpreter: Interpreter,
#[cfg(feature = "mounted")]
pub(crate) event_channel: mpsc::UnboundedSender<UiEvent>,
#[cfg(feature = "mounted")]
pub(crate) queued_mounted_events: Vec<ElementId>,
}
@@ -64,7 +66,7 @@ impl WebsysDom {
}
};
let interpreter = InterpreterInterface::default();
let interpreter = Interpreter::default();
let handler: Closure<dyn FnMut(&Event)> = Closure::wrap(Box::new({
let event_channel = event_channel.clone();
@@ -72,42 +74,45 @@ impl WebsysDom {
let name = event.type_();
let element = walk_event_for_id(event);
let bubbles = dioxus_html::event_bubbles(name.as_str());
if let Some((element, target)) = element {
let prevent_event;
if let Some(prevent_requests) = target
.get_attribute("dioxus-prevent-default")
.as_deref()
.map(|f| f.split_whitespace())
{
prevent_event = prevent_requests
.map(|f| f.trim_start_matches("on"))
.any(|f| f == name);
} else {
prevent_event = false;
}
// Prevent forms from submitting and redirecting
if name == "submit" {
// On forms the default behavior is not to submit, if prevent default is set then we submit the form
if !prevent_event {
event.prevent_default();
}
} else if prevent_event {
let Some((element, target)) = element else {
return;
};
let prevent_event;
if let Some(prevent_requests) = target
.get_attribute("dioxus-prevent-default")
.as_deref()
.map(|f| f.split_whitespace())
{
prevent_event = prevent_requests
.map(|f| f.trim_start_matches("on"))
.any(|f| f == name);
} else {
prevent_event = false;
}
// Prevent forms from submitting and redirecting
if name == "submit" {
// On forms the default behavior is not to submit, if prevent default is set then we submit the form
if !prevent_event {
event.prevent_default();
}
let data = virtual_event_from_websys_event(event.clone(), target);
let _ = event_channel.unbounded_send(UiEvent {
name,
bubbles,
element,
data,
});
} else if prevent_event {
event.prevent_default();
}
let data = virtual_event_from_websys_event(event.clone(), target);
let _ = event_channel.unbounded_send(UiEvent {
name,
bubbles,
element,
data,
});
}
}));
let _interpreter: &Interpreter = interpreter.as_ref();
let _interpreter: &WebInterpreter = interpreter.as_web();
_interpreter.initialize(
root.clone().unchecked_into(),
handler.as_ref().unchecked_ref(),
+8 -19
View File
@@ -4,7 +4,6 @@ use dioxus_html::{
point_interaction::{
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
},
prelude::FormValue,
DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
};
@@ -385,24 +384,14 @@ impl HasFormData for WebFormData {
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
}
fn values(&self) -> HashMap<String, FormValue> {
fn values(&self) -> HashMap<String, String> {
let mut values = HashMap::new();
fn insert_value(map: &mut HashMap<String, FormValue>, key: String, new_value: String) {
match map.entry(key) {
std::collections::hash_map::Entry::Occupied(mut o) => {
let first_value = match o.get_mut() {
FormValue::Text(data) => std::mem::take(data),
FormValue::VecText(vec) => {
vec.push(new_value);
return;
}
};
let _ = o.insert(FormValue::VecText(vec![first_value, new_value]));
}
std::collections::hash_map::Entry::Vacant(v) => {
let _ = v.insert(FormValue::Text(new_value));
}
fn insert_value(map: &mut HashMap<String, String>, key: String, new_value: String) {
if let Some(value) = map.get(&key) {
map.insert(key, format!("{},{}", value, new_value));
} else {
map.insert(key, new_value);
}
}
@@ -425,8 +414,8 @@ impl HasFormData for WebFormData {
}
} else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
// try to fill in select element values
let options = get_select_data(select);
values.insert("options".to_string(), FormValue::VecText(options));
let options = get_select_data(select).join(",");
values.insert("options".to_string(), options);
}
values
+11 -11
View File
@@ -30,16 +30,21 @@ use futures_util::{pin_mut, select, FutureExt, StreamExt};
mod cfg;
mod dom;
#[cfg(feature = "eval")]
mod eval;
mod event;
pub mod launch;
mod mutations;
pub use event::*;
#[cfg(feature = "eval")]
mod eval;
#[cfg(feature = "file_engine")]
mod file_engine;
#[cfg(all(feature = "hot_reload", debug_assertions))]
mod hot_reload;
#[cfg(feature = "hydrate")]
mod rehydrate;
@@ -49,7 +54,7 @@ mod rehydrate;
///
/// # Example
///
/// ```ignore
/// ```rust, ignore
/// fn main() {
/// let app_fut = dioxus_web::run_with_props(App, RootProps { name: String::from("joe") });
/// wasm_bindgen_futures::spawn_local(app_fut);
@@ -61,12 +66,7 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
let mut dom = virtual_dom;
#[cfg(feature = "eval")]
{
// Eval
dom.in_runtime(|| {
eval::init_eval();
});
}
dom.in_runtime(|| eval::init_eval());
#[cfg(feature = "panic_hook")]
if web_config.default_panic_hook {
@@ -109,13 +109,12 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
websys_dom.mount();
loop {
tracing::trace!("waiting for work");
// if virtual dom has nothing, wait for it to have something before requesting idle time
// if there is work then this future resolves immediately.
let (mut res, template) = {
let work = dom.wait_for_work().fuse();
pin_mut!(work);
let mut rx_next = rx.select_next_some();
#[cfg(all(feature = "hot_reload", debug_assertions))]
@@ -127,6 +126,7 @@ pub async fn run(virtual_dom: VirtualDom, web_config: Config) {
evt = rx_next => (Some(evt), None),
}
}
#[cfg(not(all(feature = "hot_reload", debug_assertions)))]
select! {
_ = work => (None, None),
+32 -36
View File
@@ -5,9 +5,7 @@ use dioxus_core::WriteMutations;
use dioxus_core::{AttributeValue, ElementId};
use dioxus_html::event_bubbles;
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::JsCast;
use wasm_bindgen::JsValue;
@@ -58,19 +56,23 @@ impl WebsysDom {
pub fn flush_edits(&mut self) {
self.interpreter.flush();
#[cfg(feature = "mounted")]
// Now that we've flushed the edits and the dom nodes exist, we can send the mounted events.
{
for id in self.queued_mounted_events.drain(..) {
let node = get_node(self.interpreter.js_channel(), id.0 as u32);
if let Some(element) = node.dyn_ref::<web_sys::Element>() {
let _ = self.event_channel.unbounded_send(UiEvent {
name: "mounted".to_string(),
bubbles: false,
element: id,
data: PlatformEventData::new(Box::new(element.clone())),
});
}
#[cfg(feature = "mounted")]
self.flush_queued_mounted_events();
}
#[cfg(feature = "mounted")]
fn flush_queued_mounted_events(&mut self) {
for id in self.queued_mounted_events.drain(..) {
let node = self.interpreter.as_web().get_node(id.0 as u32);
if let Some(element) = node.dyn_ref::<web_sys::Element>() {
let _ = self.event_channel.unbounded_send(UiEvent {
name: "mounted".to_string(),
bubbles: false,
element: id,
data: PlatformEventData::new(Box::new(element.clone())),
});
}
}
}
@@ -84,14 +86,14 @@ impl WebsysDom {
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(self.interpreter.js_channel(), roots, self.max_template_id);
self.interpreter
.as_web()
.save_template(roots, self.max_template_id);
self.max_template_id += 1
}
@@ -184,30 +186,24 @@ impl WriteMutations for WebsysDom {
}
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);
}
// mounted events are fired immediately after the element is mounted.
if name == "mounted" {
#[cfg(feature = "mounted")]
self.send_mount_event(id);
return;
}
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,
);
}
if name == "mounted" {
return;
}
self.interpreter
.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8);
}
fn remove_node(&mut self, id: ElementId) {
+6 -9
View File
@@ -3,7 +3,6 @@ use dioxus_core::prelude::*;
use dioxus_core::AttributeValue;
use dioxus_core::WriteMutations;
use dioxus_core::{DynamicNode, ElementId, ScopeState, TemplateNode, VNode, VirtualDom};
use dioxus_interpreter_js::save_template;
#[derive(Debug)]
pub enum RehydrationError {
@@ -23,7 +22,7 @@ impl WebsysDom {
// Recursively rehydrate the dom from the VirtualDom
self.rehydrate_scope(root_scope, dom, &mut ids, &mut to_mount)?;
dioxus_interpreter_js::hydrate(self.interpreter.js_channel(), ids);
self.interpreter.as_web().hydrate(ids);
#[cfg(feature = "mounted")]
for id in to_mount {
@@ -40,8 +39,7 @@ impl WebsysDom {
ids: &mut Vec<u32>,
to_mount: &mut Vec<ElementId>,
) -> Result<(), RehydrationError> {
let vnode = scope.root_node();
self.rehydrate_vnode(dom, vnode, ids, to_mount)
self.rehydrate_vnode(dom, scope.root_node(), ids, to_mount)
}
fn rehydrate_vnode(
@@ -168,11 +166,10 @@ impl WriteMutations for OnlyWriteTemplates<'_> {
self.0
.templates
.insert(template.name.to_owned(), self.0.max_template_id);
save_template(
self.0.interpreter.js_channel(),
roots,
self.0.max_template_id,
);
self.0
.interpreter
.as_web()
.save_template(roots, self.0.max_template_id);
self.0.max_template_id += 1
}