1use dioxus_html::{DragData, FormData, HtmlEventConverter, ImageData, PlatformEventData};
2use form::WebFormData;
3use load::WebImageEvent;
4use wasm_bindgen::JsCast;
5use web_sys::{Document, Element, Event};
6
7mod animation;
8mod cancel;
9mod clipboard;
10mod composition;
11mod drag;
12mod file;
13mod focus;
14mod form;
15mod keyboard;
16mod load;
17mod media;
18#[cfg(feature = "mounted")]
19mod mounted;
20mod mouse;
21mod pointer;
22mod resize;
23mod scroll;
24mod selection;
25mod toggle;
26mod touch;
27mod transition;
28mod visible;
29mod wheel;
30
31pub(crate) struct Synthetic<T: 'static> {
33 pub event: T,
35}
36
37impl<T: 'static> Synthetic<T> {
38 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
53macro_rules! with_web_event_converters {
54 ($macro:ident) => {
55 $macro! {
56 convert_animation_data(AnimationData) => web_sys::AnimationEvent;
57 convert_cancel_data(CancelData) => web_sys::Event;
58 convert_clipboard_data(ClipboardData) => web_sys::Event;
59 convert_composition_data(CompositionData) => web_sys::CompositionEvent;
60 convert_drag_data(DragData) => web_sys::DragEvent => |event| {
61 let event = downcast_event(event);
62 DragData::new(Synthetic::new(
63 event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
64 ))
65 };
66 convert_focus_data(FocusData) => web_sys::FocusEvent;
67 convert_form_data(FormData) => web_sys::Event => |event| {
68 let event = downcast_event(event);
69 FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
70 };
71 convert_image_data(ImageData) => web_sys::Event => |event| {
72 let event = downcast_event(event);
73 ImageData::new(WebImageEvent::new(
74 event.raw.clone(),
75 event.raw.type_() == "error",
76 ))
77 };
78 convert_keyboard_data(KeyboardData) => web_sys::KeyboardEvent;
79 convert_media_data(MediaData) => web_sys::Event;
80 convert_mounted_data(MountedData) => web_sys::Element => |event| {
81 #[cfg(feature = "mounted")]
82 {
83 Synthetic::new(
84 event
85 .downcast::<web_sys::Element>()
86 .expect("event should be a web_sys::Element")
87 .clone(),
88 )
89 .into()
90 }
91 #[cfg(not(feature = "mounted"))]
92 {
93 let _ = event;
94 panic!("mounted events require the `mounted` feature on dioxus-web")
95 }
96 };
97 convert_mouse_data(MouseData) => web_sys::MouseEvent;
98 convert_pointer_data(PointerData) => web_sys::PointerEvent;
99 convert_resize_data(ResizeData) => web_sys::CustomEvent => |event| {
100 Synthetic::<web_sys::ResizeObserverEntry>::from(downcast_event(event).raw.clone()).into()
101 };
102 convert_scroll_data(ScrollData) => web_sys::Event;
103 convert_selection_data(SelectionData) => web_sys::Event;
104 convert_toggle_data(ToggleData) => web_sys::Event;
105 convert_touch_data(TouchData) => web_sys::TouchEvent;
106 convert_transition_data(TransitionData) => web_sys::TransitionEvent;
107 convert_visible_data(VisibleData) => web_sys::CustomEvent => |event| {
108 Synthetic::<web_sys::IntersectionObserverEntry>::from(downcast_event(event).raw.clone()).into()
109 };
110 convert_wheel_data(WheelData) => web_sys::WheelEvent;
111 }
112 };
113}
114
115macro_rules! expand_web_event_converter {
116 (
117 $(
118 $converter:ident($data:ident) => $web_ty:ty $(=> |$event:ident| $body:block)?;
119 )*
120 ) => {
121 macro_rules! web_event_type_matches {
122 $(
123 ($event_name:ident, $converter) => {
124 $event_name.is_instance_of::<$web_ty>()
125 };
126 )*
127 }
128
129 impl HtmlEventConverter for WebEventConverter {
130 $(
131 expand_web_event_converter!(@method $converter, $data, $web_ty $(, $event, $body)?);
132 )*
133 }
134 };
135
136 (@method $converter:ident, $data:ident, $web_ty:ty) => {
137 #[inline(always)]
138 fn $converter(&self, event: &PlatformEventData) -> dioxus_html::$data {
139 Synthetic::new(downcast_event(event).raw.clone().unchecked_into::<$web_ty>()).into()
140 }
141 };
142
143 (@method $converter:ident, $data:ident, $web_ty:ty, $event:ident, $body:block) => {
144 #[inline(always)]
145 fn $converter(&self, $event: &PlatformEventData) -> dioxus_html::$data $body
146 };
147}
148
149with_web_event_converters!(expand_web_event_converter);
150
151macro_rules! expand_web_event_changes {
152 (
153 enum Event {
154 $(
155 #[convert = $converter:ident]
156 #[events = [
157 $(
158 $( #[$attr:meta] )*
159 $name:ident => $raw:ident,
160 )*
161 ]]
162 $(#[raw = [$($raw_only:ident),* $(,)?]])?
163 $group:ident($data:ident),
164 )*
165 }
166 ) => {
167 pub(crate) fn event_type_matches(name: &str, event: &web_sys::Event) -> bool {
168 let m = match name {
169 $(
170 $( stringify!($raw) )|* $($(| stringify!($raw_only))*)? => {
171 web_event_type_matches!(event, $converter)
172 }
173 )*
174 _ => true,
175 };
176 if !m {
177 tracing::warn!("Ignoring \"{name}\": not the expected type: {event:?}");
178 }
179 m
180 }
181 };
182}
183
184dioxus_html::with_html_event_groups!(expand_web_event_changes);
185
186pub trait WebEventExt {
188 type WebEvent;
190
191 fn try_as_web_event(&self) -> Option<Self::WebEvent>;
193
194 #[inline(always)]
196 fn as_web_event(&self) -> Self::WebEvent
197 where
198 Self::WebEvent: 'static,
199 {
200 self.try_as_web_event().unwrap_or_else(|| {
201 panic!(
202 "Error downcasting to `web-sys`, event should be a {}.",
203 std::any::type_name::<Self::WebEvent>()
204 )
205 })
206 }
207}
208
209struct GenericWebSysEvent {
210 raw: Event,
211 element: Element,
212}
213
214pub(crate) fn virtual_event_from_websys_event(
217 event: web_sys::Event,
218 target: Element,
219) -> PlatformEventData {
220 PlatformEventData::new(Box::new(GenericWebSysEvent {
221 raw: event,
222 element: target,
223 }))
224}
225
226pub(crate) fn load_document() -> Document {
227 web_sys::window()
228 .expect("should have access to the Window")
229 .document()
230 .expect("should have access to the Document")
231}