dioxus_web/events/
mod.rs

1use dioxus_html::{
2    DragData, FormData, HtmlEventConverter, ImageData, MountedData, PlatformEventData,
3};
4use form::WebFormData;
5use load::WebImageEvent;
6use wasm_bindgen::JsCast;
7use web_sys::{Document, Element, Event};
8
9mod animation;
10mod cancel;
11mod clipboard;
12mod composition;
13mod drag;
14mod file;
15mod focus;
16mod form;
17mod keyboard;
18mod load;
19mod media;
20#[cfg(feature = "mounted")]
21mod mounted;
22mod mouse;
23mod pointer;
24mod resize;
25mod scroll;
26mod selection;
27mod toggle;
28mod touch;
29mod transition;
30mod visible;
31mod wheel;
32
33/// A wrapper for the websys event that allows us to give it the impls from dioxus-html
34pub(crate) struct Synthetic<T: 'static> {
35    /// The inner web sys event that the synthetic event wraps
36    pub event: T,
37}
38
39impl<T: 'static> Synthetic<T> {
40    /// Create a new synthetic event from a web sys event
41    pub fn new(event: T) -> Self {
42        Self { event }
43    }
44}
45
46pub(crate) struct WebEventConverter;
47
48#[inline(always)]
49fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent {
50    event
51        .downcast::<GenericWebSysEvent>()
52        .expect("event should be a GenericWebSysEvent")
53}
54
55impl HtmlEventConverter for WebEventConverter {
56    #[inline(always)]
57    fn convert_animation_data(
58        &self,
59        event: &dioxus_html::PlatformEventData,
60    ) -> dioxus_html::AnimationData {
61        Synthetic::<web_sys::AnimationEvent>::from(downcast_event(event).raw.clone()).into()
62    }
63
64    #[inline(always)]
65    fn convert_cancel_data(
66        &self,
67        event: &dioxus_html::PlatformEventData,
68    ) -> dioxus_html::CancelData {
69        Synthetic::new(downcast_event(event).raw.clone()).into()
70    }
71
72    #[inline(always)]
73    fn convert_clipboard_data(
74        &self,
75        event: &dioxus_html::PlatformEventData,
76    ) -> dioxus_html::ClipboardData {
77        Synthetic::new(downcast_event(event).raw.clone()).into()
78    }
79
80    #[inline(always)]
81    fn convert_composition_data(
82        &self,
83        event: &dioxus_html::PlatformEventData,
84    ) -> dioxus_html::CompositionData {
85        Synthetic::<web_sys::CompositionEvent>::from(downcast_event(event).raw.clone()).into()
86    }
87
88    #[inline(always)]
89    fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
90        let event = downcast_event(event);
91        DragData::new(Synthetic::new(
92            event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
93        ))
94    }
95
96    #[inline(always)]
97    fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
98        Synthetic::<web_sys::FocusEvent>::from(downcast_event(event).raw.clone()).into()
99    }
100
101    #[inline(always)]
102    fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData {
103        let event = downcast_event(event);
104        FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
105    }
106
107    #[inline(always)]
108    fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData {
109        let event = downcast_event(event);
110        let error = event.raw.type_() == "error";
111        ImageData::new(WebImageEvent::new(event.raw.clone(), error))
112    }
113
114    #[inline(always)]
115    fn convert_keyboard_data(
116        &self,
117        event: &dioxus_html::PlatformEventData,
118    ) -> dioxus_html::KeyboardData {
119        Synthetic::<web_sys::KeyboardEvent>::from(downcast_event(event).raw.clone()).into()
120    }
121
122    #[inline(always)]
123    fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
124        Synthetic::new(downcast_event(event).raw.clone()).into()
125    }
126
127    #[allow(unused_variables)]
128    #[inline(always)]
129    fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
130        #[cfg(feature = "mounted")]
131        {
132            Synthetic::new(
133                event
134                    .downcast::<web_sys::Element>()
135                    .expect("event should be a web_sys::Element")
136                    .clone(),
137            )
138            .into()
139        }
140        #[cfg(not(feature = "mounted"))]
141        {
142            panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled")
143        }
144    }
145
146    #[inline(always)]
147    fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData {
148        Synthetic::<web_sys::MouseEvent>::from(downcast_event(event).raw.clone()).into()
149    }
150
151    #[inline(always)]
152    fn convert_pointer_data(
153        &self,
154        event: &dioxus_html::PlatformEventData,
155    ) -> dioxus_html::PointerData {
156        Synthetic::<web_sys::PointerEvent>::from(downcast_event(event).raw.clone()).into()
157    }
158
159    #[inline(always)]
160    fn convert_resize_data(
161        &self,
162        event: &dioxus_html::PlatformEventData,
163    ) -> dioxus_html::ResizeData {
164        Synthetic::<web_sys::ResizeObserverEntry>::from(downcast_event(event).raw.clone()).into()
165    }
166
167    #[inline(always)]
168    fn convert_scroll_data(
169        &self,
170        event: &dioxus_html::PlatformEventData,
171    ) -> dioxus_html::ScrollData {
172        Synthetic::new(downcast_event(event).raw.clone()).into()
173    }
174
175    #[inline(always)]
176    fn convert_selection_data(
177        &self,
178        event: &dioxus_html::PlatformEventData,
179    ) -> dioxus_html::SelectionData {
180        Synthetic::new(downcast_event(event).raw.clone()).into()
181    }
182
183    #[inline(always)]
184    fn convert_toggle_data(
185        &self,
186        event: &dioxus_html::PlatformEventData,
187    ) -> dioxus_html::ToggleData {
188        Synthetic::new(downcast_event(event).raw.clone()).into()
189    }
190
191    #[inline(always)]
192    fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
193        Synthetic::<web_sys::TouchEvent>::from(downcast_event(event).raw.clone()).into()
194    }
195
196    #[inline(always)]
197    fn convert_transition_data(
198        &self,
199        event: &dioxus_html::PlatformEventData,
200    ) -> dioxus_html::TransitionData {
201        Synthetic::<web_sys::TransitionEvent>::from(downcast_event(event).raw.clone()).into()
202    }
203
204    #[inline(always)]
205    fn convert_visible_data(
206        &self,
207        event: &dioxus_html::PlatformEventData,
208    ) -> dioxus_html::VisibleData {
209        Synthetic::<web_sys::IntersectionObserverEntry>::from(downcast_event(event).raw.clone())
210            .into()
211    }
212
213    #[inline(always)]
214    fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
215        Synthetic::<web_sys::WheelEvent>::from(downcast_event(event).raw.clone()).into()
216    }
217}
218
219/// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
220pub trait WebEventExt {
221    /// The web specific event type
222    type WebEvent;
223
224    /// Try to downcast this event as a `web-sys` event.
225    fn try_as_web_event(&self) -> Option<Self::WebEvent>;
226
227    /// Downcast this event as a `web-sys` event.
228    #[inline(always)]
229    fn as_web_event(&self) -> Self::WebEvent
230    where
231        Self::WebEvent: 'static,
232    {
233        self.try_as_web_event().unwrap_or_else(|| {
234            panic!(
235                "Error downcasting to `web-sys`, event should be a {}.",
236                std::any::type_name::<Self::WebEvent>()
237            )
238        })
239    }
240}
241
242struct GenericWebSysEvent {
243    raw: Event,
244    element: Element,
245}
246
247// todo: some of these events are being casted to the wrong event type.
248// We need tests that simulate clicks/etc and make sure every event type works.
249pub(crate) fn virtual_event_from_websys_event(
250    event: web_sys::Event,
251    target: Element,
252) -> PlatformEventData {
253    PlatformEventData::new(Box::new(GenericWebSysEvent {
254        raw: event,
255        element: target,
256    }))
257}
258
259pub(crate) fn load_document() -> Document {
260    web_sys::window()
261        .expect("should have access to the Window")
262        .document()
263        .expect("should have access to the Document")
264}
265
266macro_rules! uncheck_convert {
267    ($t:ty) => {
268        impl From<Event> for Synthetic<$t> {
269            #[inline]
270            fn from(e: Event) -> Self {
271                let e: $t = e.unchecked_into();
272                Self::new(e)
273            }
274        }
275
276        impl From<&Event> for Synthetic<$t> {
277            #[inline]
278            fn from(e: &Event) -> Self {
279                let e: &$t = e.unchecked_ref();
280                Self::new(e.clone())
281            }
282        }
283    };
284    ($($t:ty),+ $(,)?) => {
285        $(uncheck_convert!($t);)+
286    };
287}
288
289uncheck_convert![
290    web_sys::CompositionEvent,
291    web_sys::KeyboardEvent,
292    web_sys::TouchEvent,
293    web_sys::PointerEvent,
294    web_sys::WheelEvent,
295    web_sys::AnimationEvent,
296    web_sys::TransitionEvent,
297    web_sys::MouseEvent,
298    web_sys::FocusEvent,
299];