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