Browse Source

use an event converter for bundle splitting

Evan Almloff 1 năm trước cách đây
mục cha
commit
ca1a502714

+ 21 - 0
packages/core/src/events.rs

@@ -28,6 +28,27 @@ pub struct Event<T: 'static + ?Sized> {
 }
 
 impl<T> Event<T> {
+    /// Map the event data to a new type
+    ///
+    /// # Example
+    ///
+    /// ```rust, ignore
+    /// rsx! {
+    ///    button {
+    ///       onclick: move |evt: Event<FormData>| {
+    ///          let data = evt.map(|data| data.value());
+    ///          assert_eq!(data.inner(), "hello world");
+    ///       }
+    ///    }
+    /// }
+    /// ```
+    pub fn map<U: 'static, F: FnOnce(&T) -> U>(&self, f: F) -> Event<U> {
+        Event {
+            data: Rc::new(f(&self.data)),
+            propagates: self.propagates.clone(),
+        }
+    }
+
     /// Prevent this event from continuing to bubble up the tree to parent elements.
     ///
     /// # Example

+ 171 - 2
packages/html/src/events.rs

@@ -1,3 +1,6 @@
+use std::any::Any;
+use std::sync::RwLock;
+
 macro_rules! impl_event {
     (
         $data:ty;
@@ -12,8 +15,8 @@ macro_rules! impl_event {
             pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> {
                 ::dioxus_core::Attribute::new(
                     stringify!($name),
-                    _cx.listener(move |e: ::dioxus_core::Event<$data>| {
-                        _f(e).spawn(_cx);
+                    _cx.listener(move |e: ::dioxus_core::Event<crate::PlatformEventData>| {
+                        _f(e.map(|e|e.into())).spawn(_cx);
                     }),
                     None,
                     false,
@@ -23,6 +26,172 @@ macro_rules! impl_event {
     };
 }
 
+static EVENT_CONVERTER: RwLock<Option<Box<dyn HtmlEventConverter>>> = RwLock::new(None);
+
+pub fn set_event_converter(converter: Box<dyn HtmlEventConverter>) {
+    *EVENT_CONVERTER.write().unwrap() = Some(converter);
+}
+
+pub(crate) fn with_event_converter<F, R>(f: F) -> R
+where
+    F: FnOnce(&dyn HtmlEventConverter) -> R,
+{
+    let converter = EVENT_CONVERTER.read().unwrap();
+    f(converter.as_ref().unwrap().as_ref())
+}
+
+/// A platform specific event.
+pub struct PlatformEventData {
+    event: Box<dyn Any>,
+}
+
+impl PlatformEventData {
+    pub fn new(event: Box<dyn Any>) -> Self {
+        Self { event }
+    }
+
+    pub fn downcast<T: 'static>(&self) -> Option<&T> {
+        self.event.downcast_ref::<T>()
+    }
+
+    pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
+        self.event.downcast_mut::<T>()
+    }
+
+    pub fn into_inner<T: 'static>(self) -> Option<T> {
+        self.event.downcast::<T>().ok().map(|e| *e)
+    }
+}
+
+pub trait HtmlEventConverter: Send + Sync {
+    fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData;
+    fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData;
+    fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData;
+    fn convert_drag_data(&self, event: &PlatformEventData) -> DragData;
+    fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData;
+    fn convert_form_data(&self, event: &PlatformEventData) -> FormData;
+    fn convert_image_data(&self, event: &PlatformEventData) -> ImageData;
+    fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData;
+    fn convert_media_data(&self, event: &PlatformEventData) -> MediaData;
+    fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData;
+    fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData;
+    fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData;
+    fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData;
+    fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData;
+    fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData;
+    fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData;
+    fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData;
+    fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData;
+}
+
+impl Into<AnimationData> for &PlatformEventData {
+    fn into(self) -> AnimationData {
+        with_event_converter(|c| c.convert_animation_data(self))
+    }
+}
+
+impl Into<ClipboardData> for &PlatformEventData {
+    fn into(self) -> ClipboardData {
+        with_event_converter(|c| c.convert_clipboard_data(self))
+    }
+}
+
+impl Into<CompositionData> for &PlatformEventData {
+    fn into(self) -> CompositionData {
+        with_event_converter(|c| c.convert_composition_data(self))
+    }
+}
+
+impl Into<DragData> for &PlatformEventData {
+    fn into(self) -> DragData {
+        with_event_converter(|c| c.convert_drag_data(self))
+    }
+}
+
+impl Into<FocusData> for &PlatformEventData {
+    fn into(self) -> FocusData {
+        with_event_converter(|c| c.convert_focus_data(self))
+    }
+}
+
+impl Into<FormData> for &PlatformEventData {
+    fn into(self) -> FormData {
+        with_event_converter(|c| c.convert_form_data(self))
+    }
+}
+
+impl Into<ImageData> for &PlatformEventData {
+    fn into(self) -> ImageData {
+        with_event_converter(|c| c.convert_image_data(self))
+    }
+}
+
+impl Into<KeyboardData> for &PlatformEventData {
+    fn into(self) -> KeyboardData {
+        with_event_converter(|c| c.convert_keyboard_data(self))
+    }
+}
+
+impl Into<MediaData> for &PlatformEventData {
+    fn into(self) -> MediaData {
+        with_event_converter(|c| c.convert_media_data(self))
+    }
+}
+
+impl Into<MountedData> for &PlatformEventData {
+    fn into(self) -> MountedData {
+        with_event_converter(|c| c.convert_mounted_data(self))
+    }
+}
+
+impl Into<MouseData> for &PlatformEventData {
+    fn into(self) -> MouseData {
+        with_event_converter(|c| c.convert_mouse_data(self))
+    }
+}
+
+impl Into<PointerData> for &PlatformEventData {
+    fn into(self) -> PointerData {
+        with_event_converter(|c| c.convert_pointer_data(self))
+    }
+}
+
+impl Into<ScrollData> for &PlatformEventData {
+    fn into(self) -> ScrollData {
+        with_event_converter(|c| c.convert_scroll_data(self))
+    }
+}
+
+impl Into<SelectionData> for &PlatformEventData {
+    fn into(self) -> SelectionData {
+        with_event_converter(|c| c.convert_selection_data(self))
+    }
+}
+
+impl Into<ToggleData> for &PlatformEventData {
+    fn into(self) -> ToggleData {
+        with_event_converter(|c| c.convert_toggle_data(self))
+    }
+}
+
+impl Into<TouchData> for &PlatformEventData {
+    fn into(self) -> TouchData {
+        with_event_converter(|c| c.convert_touch_data(self))
+    }
+}
+
+impl Into<TransitionData> for &PlatformEventData {
+    fn into(self) -> TransitionData {
+        with_event_converter(|c| c.convert_transition_data(self))
+    }
+}
+
+impl Into<WheelData> for &PlatformEventData {
+    fn into(self) -> WheelData {
+        with_event_converter(|c| c.convert_wheel_data(self))
+    }
+}
+
 mod animation;
 mod clipboard;
 mod composition;

+ 5 - 0
packages/html/src/events/image.rs

@@ -26,6 +26,11 @@ impl PartialEq for ImageData {
 }
 
 impl ImageData {
+    /// Create a new ImageData
+    pub fn new(e: impl HasImageData) -> Self {
+        Self { inner: Box::new(e) }
+    }
+
     /// If the renderer encountered an error while loading the image
     pub fn load_error(&self) -> bool {
         self.inner.load_error()

+ 1 - 1
packages/html/src/lib.rs

@@ -38,7 +38,7 @@ pub use events::*;
 pub use global_attributes::*;
 pub use render_template::*;
 
-mod eval;
+pub mod eval;
 
 pub mod prelude {
     pub use crate::eval::*;

+ 6 - 0
packages/html/src/web_sys_bind/events.rs

@@ -386,6 +386,12 @@ impl std::fmt::Display for FocusError {
 
 impl std::error::Error for FocusError {}
 
+impl HasScrollData for Event {
+    fn as_any(&self) -> &dyn std::any::Any {
+        self
+    }
+}
+
 impl HasClipboardData for Event {
     fn as_any(&self) -> &dyn std::any::Any {
         self

+ 124 - 45
packages/web/src/dom.rs

@@ -1,7 +1,6 @@
 //! Implementation of a renderer for Dioxus on the web.
 //!
-//! Oustanding todos:
-//! - Removing event listeners (delegation)
+//! Outstanding todos:
 //! - Passive event listeners
 //! - no-op event listener patch for safari
 //! - tests to ensure dyn_into works for various event types.
@@ -10,7 +9,10 @@
 use dioxus_core::{
     BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
 };
-use dioxus_html::{event_bubbles, FileEngine, HasFormData, HasImageData, MountedData};
+use dioxus_html::{
+    event_bubbles, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData,
+    MountedData, ScrollData,
+};
 use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel};
 use futures_channel::mpsc;
 use js_sys::Array;
@@ -263,53 +265,130 @@ impl WebsysDom {
     }
 }
 
-// todo: some of these events are being casted to the wrong event type.
-// We need tests that simulate clicks/etc and make sure every event type works.
-pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc<dyn Any> {
-    use dioxus_html::events::*;
+struct WebEventConverter;
 
-    match event.type_().as_str() {
-        "copy" | "cut" | "paste" => Rc::new(ClipboardData::from(event)),
-        "compositionend" | "compositionstart" | "compositionupdate" => {
-            Rc::new(CompositionData::from(event))
-        }
-        "keydown" | "keypress" | "keyup" => Rc::new(KeyboardData::from(event)),
-        "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData::from(event)),
+fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent {
+    event
+        .downcast::<GenericWebSysEvent>()
+        .expect("event should be a GenericWebSysEvent")
+}
 
-        "change" | "input" | "invalid" | "reset" | "submit" => {
-            Rc::new(WebFormData::new(target, event))
-        }
+impl HtmlEventConverter for WebEventConverter {
+    fn convert_animation_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::AnimationData {
+        downcast_event(event).raw.clone().into()
+    }
 
-        "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
-        | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
-            Rc::new(MouseData::from(event))
-        }
-        "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
-        | "drop" => Rc::new(DragData::from(event)),
+    fn convert_clipboard_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::ClipboardData {
+        downcast_event(event).raw.clone().into()
+    }
 
-        "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
-        | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
-            Rc::new(PointerData::from(event))
-        }
-        "select" => Rc::new(SelectionData::from(event)),
-        "touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)),
+    fn convert_composition_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::CompositionData {
+        downcast_event(event).raw.clone().into()
+    }
 
-        "scroll" => Rc::new(()),
-        "wheel" => Rc::new(WheelData::from(event)),
-        "animationstart" | "animationend" | "animationiteration" => {
-            Rc::new(AnimationData::from(event))
-        }
-        "transitionend" => Rc::new(TransitionData::from(event)),
-        "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
-        | "ended" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
-        | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
-        | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData::from(event)),
-        "error" => Rc::new(WebImageEvent::new(event, true)),
-        "load" => Rc::new(WebImageEvent::new(event, false)),
-        "toggle" => Rc::new(ToggleData::from(event)),
+    fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData {
+        let event = downcast_event(event);
+        FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
+    }
+
+    fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData {
+        let event = downcast_event(event);
+        let error = event.raw.type_() == "error";
+        ImageData::new(WebImageEvent::new(event.raw.clone(), error))
+    }
+
+    fn convert_keyboard_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::KeyboardData {
+        downcast_event(event).raw.clone().into()
+    }
 
-        _ => Rc::new(()),
+    fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
+        downcast_event(event).raw.clone().into()
     }
+
+    fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
+        MountedData::from(downcast_event(event).element.clone())
+    }
+
+    fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_pointer_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::PointerData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_scroll_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::ScrollData {
+        ScrollData::from(downcast_event(event).raw.clone())
+    }
+
+    fn convert_selection_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::SelectionData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_toggle_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::ToggleData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_transition_data(
+        &self,
+        event: &dioxus_html::PlatformEventData,
+    ) -> dioxus_html::TransitionData {
+        downcast_event(event).raw.clone().into()
+    }
+
+    fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
+        downcast_event(event).raw.clone().into()
+    }
+}
+
+struct GenericWebSysEvent {
+    raw: Event,
+    element: Element,
+}
+
+// todo: some of these events are being casted to the wrong event type.
+// We need tests that simulate clicks/etc and make sure every event type works.
+pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc<dyn Any> {
+    Rc::new(GenericWebSysEvent {
+        raw: event,
+        element: target,
+    })
 }
 
 pub(crate) fn load_document() -> Document {
@@ -336,7 +415,7 @@ impl HasImageData for WebImageEvent {
     }
 
     fn as_any(&self) -> &dyn Any {
-        &self.raw
+        &self.raw as &dyn Any
     }
 }
 
@@ -432,7 +511,7 @@ impl HasFormData for WebFormData {
     }
 
     fn as_any(&self) -> &dyn Any {
-        &self.raw
+        &self.raw as &dyn Any
     }
 }