1use before_input::WebBeforeInputData;
2use clipboard::WebClipboardData;
3use dioxus_html::{
4 BeforeInputData, ClipboardData, DragData, FormData, HtmlEventConverter, ImageData,
5 PlatformEventData,
6};
7use form::WebFormData;
8use load::WebImageEvent;
9use wasm_bindgen::JsCast;
10use web_sys::{Document, Element, Event};
11
12mod animation;
13mod before_input;
14mod cancel;
15mod clipboard;
16mod composition;
17mod drag;
18mod file;
19mod focus;
20mod form;
21mod keyboard;
22mod load;
23mod media;
24#[cfg(feature = "mounted")]
25mod mounted;
26mod mouse;
27mod pointer;
28mod resize;
29mod scroll;
30mod selection;
31mod toggle;
32mod touch;
33mod transition;
34mod visible;
35mod wheel;
36
37pub(crate) struct Synthetic<T: 'static> {
39 pub event: T,
41}
42
43impl<T: 'static> Synthetic<T> {
44 pub fn new(event: T) -> Self {
46 Self { event }
47 }
48}
49
50pub(crate) struct WebEventConverter;
51
52#[inline(always)]
53fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent {
54 event
55 .downcast::<GenericWebSysEvent>()
56 .expect("event should be a GenericWebSysEvent")
57}
58
59pub(crate) fn editable_element_value(element: &Element) -> Option<String> {
67 element
68 .dyn_ref()
69 .map(
70 |input: &web_sys::HtmlInputElement| match input.type_().as_str() {
71 "checkbox" => match input.checked() {
73 true => "true".to_string(),
74 false => "false".to_string(),
75 },
76 _ => input.value(),
77 },
78 )
79 .or_else(|| {
80 element
81 .dyn_ref()
82 .map(|input: &web_sys::HtmlTextAreaElement| input.value())
83 })
84 .or_else(|| {
86 element
87 .dyn_ref()
88 .map(|input: &web_sys::HtmlSelectElement| input.value())
89 })
90 .or_else(|| {
91 element
92 .dyn_ref::<web_sys::HtmlElement>()
93 .and_then(|el| el.text_content())
94 })
95}
96
97macro_rules! with_web_event_converters {
98 ($macro:ident) => {
99 $macro! {
100 convert_animation_data(AnimationData) => web_sys::AnimationEvent;
101 convert_before_input_data(BeforeInputData) => web_sys::InputEvent => |event| {
102 let event = downcast_event(event);
103 BeforeInputData::new(WebBeforeInputData::new(
104 event.element.clone(),
105 event.raw.clone().unchecked_into::<web_sys::InputEvent>(),
106 ))
107 };
108 convert_cancel_data(CancelData) => web_sys::Event;
109 convert_clipboard_data(ClipboardData) => web_sys::ClipboardEvent => |event| {
110 let event = downcast_event(event);
111 ClipboardData::new(WebClipboardData::new(
112 event.raw.clone().unchecked_into::<web_sys::ClipboardEvent>(),
113 ))
114 };
115 convert_composition_data(CompositionData) => web_sys::CompositionEvent;
116 convert_drag_data(DragData) => web_sys::DragEvent => |event| {
117 let event = downcast_event(event);
118 DragData::new(Synthetic::new(
119 event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
120 ))
121 };
122 convert_focus_data(FocusData) => web_sys::FocusEvent;
123 convert_form_data(FormData) => web_sys::Event => |event| {
124 let event = downcast_event(event);
125 FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
126 };
127 convert_image_data(ImageData) => web_sys::Event => |event| {
128 let event = downcast_event(event);
129 ImageData::new(WebImageEvent::new(
130 event.raw.clone(),
131 event.raw.type_() == "error",
132 ))
133 };
134 convert_keyboard_data(KeyboardData) => web_sys::KeyboardEvent;
135 convert_media_data(MediaData) => web_sys::Event;
136 convert_mounted_data(MountedData) => web_sys::Element => |event| {
137 #[cfg(feature = "mounted")]
138 {
139 Synthetic::new(
140 event
141 .downcast::<web_sys::Element>()
142 .expect("event should be a web_sys::Element")
143 .clone(),
144 )
145 .into()
146 }
147 #[cfg(not(feature = "mounted"))]
148 {
149 let _ = event;
150 panic!("mounted events require the `mounted` feature on dioxus-web")
151 }
152 };
153 convert_mouse_data(MouseData) => web_sys::MouseEvent;
154 convert_pointer_data(PointerData) => web_sys::PointerEvent;
155 convert_resize_data(ResizeData) => web_sys::CustomEvent => |event| {
156 Synthetic::<web_sys::ResizeObserverEntry>::from(downcast_event(event).raw.clone()).into()
157 };
158 convert_scroll_data(ScrollData) => web_sys::Event;
159 convert_selection_data(SelectionData) => web_sys::Event;
160 convert_toggle_data(ToggleData) => web_sys::Event;
161 convert_touch_data(TouchData) => web_sys::TouchEvent;
162 convert_transition_data(TransitionData) => web_sys::TransitionEvent;
163 convert_visible_data(VisibleData) => web_sys::CustomEvent => |event| {
164 Synthetic::<web_sys::IntersectionObserverEntry>::from(downcast_event(event).raw.clone()).into()
165 };
166 convert_wheel_data(WheelData) => web_sys::WheelEvent;
167 }
168 };
169}
170
171macro_rules! expand_web_event_converter {
172 (
173 $(
174 $converter:ident($data:ident) => $web_ty:ty $(=> |$event:ident| $body:block)?;
175 )*
176 ) => {
177 macro_rules! web_event_type_matches {
178 $(
179 ($event_name:ident, $converter) => {
180 $event_name.is_instance_of::<$web_ty>()
181 };
182 )*
183 }
184
185 impl HtmlEventConverter for WebEventConverter {
186 $(
187 expand_web_event_converter!(@method $converter, $data, $web_ty $(, $event, $body)?);
188 )*
189 }
190 };
191
192 (@method $converter:ident, $data:ident, $web_ty:ty) => {
193 #[inline(always)]
194 fn $converter(&self, event: &PlatformEventData) -> dioxus_html::$data {
195 Synthetic::new(downcast_event(event).raw.clone().unchecked_into::<$web_ty>()).into()
196 }
197 };
198
199 (@method $converter:ident, $data:ident, $web_ty:ty, $event:ident, $body:block) => {
200 #[inline(always)]
201 fn $converter(&self, $event: &PlatformEventData) -> dioxus_html::$data $body
202 };
203}
204
205with_web_event_converters!(expand_web_event_converter);
206
207macro_rules! expand_web_event_changes {
208 (
209 enum Event {
210 $(
211 #[convert = $converter:ident]
212 #[events = [
213 $(
214 $( #[$attr:meta] )*
215 $name:ident => $raw:ident,
216 )*
217 ]]
218 $(#[raw = [$($raw_only:ident),* $(,)?]])?
219 $group:ident($data:ident),
220 )*
221 }
222 ) => {
223 pub(crate) fn event_type_matches(name: &str, event: &web_sys::Event) -> bool {
224 let m = match name {
225 $(
226 $( stringify!($raw) )|* $($(| stringify!($raw_only))*)? => {
227 web_event_type_matches!(event, $converter)
228 }
229 )*
230 _ => true,
231 };
232 if !m {
233 tracing::warn!("Ignoring \"{name}\": not the expected type: {event:?}");
234 }
235 m
236 }
237 };
238}
239
240dioxus_html::with_html_event_groups!(expand_web_event_changes);
241
242pub trait WebEventExt {
244 type WebEvent;
246
247 fn try_as_web_event(&self) -> Option<Self::WebEvent>;
249
250 #[inline(always)]
252 fn as_web_event(&self) -> Self::WebEvent
253 where
254 Self::WebEvent: 'static,
255 {
256 self.try_as_web_event().unwrap_or_else(|| {
257 panic!(
258 "Error downcasting to `web-sys`, event should be a {}.",
259 std::any::type_name::<Self::WebEvent>()
260 )
261 })
262 }
263}
264
265struct GenericWebSysEvent {
266 raw: Event,
267 element: Element,
268}
269
270pub(crate) fn virtual_event_from_websys_event(
273 event: web_sys::Event,
274 target: Element,
275) -> PlatformEventData {
276 PlatformEventData::new(Box::new(GenericWebSysEvent {
277 raw: event,
278 element: target,
279 }))
280}
281
282pub(crate) fn load_document() -> Document {
283 web_sys::window()
284 .expect("should have access to the Window")
285 .document()
286 .expect("should have access to the Document")
287}