1use std::{borrow::Cow, fmt::Debug, hash::Hash, ops::Deref};
2
3use gloo::events::EventListener;
4use wasm_bindgen::JsCast;
5use web_sys::Element;
6
7use crate::{component::callback::Callback, virtual_dom::dom};
8
9#[macro_use]
10mod macros;
11
12define_events!(
13 Event,
14 AnimationEvent,
15 DragEvent,
16 FocusEvent,
17 InputEvent,
18 KeyboardEvent,
19 MouseEvent,
20 PointerEvent,
21 ProgressEvent,
22 SubmitEvent,
23 TouchEvent,
24 TransitionEvent,
25 WheelEvent
26);
27
28pub trait EventCreator {
29 fn get_event_type(&self) -> Cow<'static, str>;
30 fn create_callback(&self) -> Box<dyn FnMut(&web_sys::Event)>;
31}
32
33impl Debug for dyn EventCreator {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 format!("EventCreator of type {}", self.get_event_type()).fmt(f)
36 }
37}
38
39impl PartialEq for dyn EventCreator {
40 fn eq(&self, other: &Self) -> bool {
41 self.get_event_type() == other.get_event_type()
42 }
43}
44
45trait EventCreatorType {
46 type Creator: EventCreator;
47}
48
49pub struct UnspecializedEventCreator {
50 pub event_type: Cow<'static, str>,
51 pub callback: Callback<Event>,
52}
53
54impl EventCreator for UnspecializedEventCreator {
55 fn get_event_type(&self) -> Cow<'static, str> {
56 self.event_type.clone()
57 }
58
59 fn create_callback(&self) -> Box<dyn FnMut(&web_sys::Event)> {
60 let callback = self.callback.clone();
61 Box::new(move |event: &web_sys::Event| {
62 let event = Event(event.clone());
63 callback.emit(event);
64 })
65 }
66}
67
68event_creators! {
69 AnimationEventCreator(AnimationEvent),
70 DragEventCreator(DragEvent),
71 FocusEventCreator(FocusEvent),
72 InputEventCreator(InputEvent),
73 KeyboardEventCreator(KeyboardEvent),
74 MouseEventCreator(MouseEvent),
75 PointerEventCreator(PointerEvent),
76 ProgressEventCreator(ProgressEvent),
77 SubmitEventCreator(SubmitEvent),
78 TouchEventCreator(TouchEvent),
79 TransitionEventCreator(TransitionEvent),
80 WheelEventCreator(WheelEvent)
81}
82
83unspecialized_event_creators_constructor! {
84 onabort,
85 oncancel,
86 oncanplay,
87 oncanplaythrough,
88 onchange,
89 onclose,
90 oncopy,
91 oncuechange,
92 oncut,
93 ondurationchange,
94 onemptied,
95 onended,
96 onerror,
97 oninvalid,
98 onload,
99 onloadeddata,
100 onloadedmetadata,
101 onpaste,
102 onpause,
103 onplay,
104 onplaying,
105 onpointerlockchange,
106 onpointerlockerror,
107 onratechange,
108 onreset,
109 onresize,
110 onscroll,
111 onsecuritypolicyviolation,
112 onseeked,
113 onseeking,
114 onselect,
115 onselectionchange,
116 onselectstart,
117 onshow,
118 onslotchange,
119 onstalled,
120 onsuspend,
121 ontimeupdate,
122 ontoggle,
123 onvolumechange,
124 onwaiting,
125
126 onformdata }
129
130event_creators_constructor! {
131 onanimationcancel(AnimationEvent),
133 onanimationend(AnimationEvent),
134 onanimationiteration(AnimationEvent),
135 onanimationstart(AnimationEvent),
136
137 ondrag(DragEvent),
139 ondragend(DragEvent),
140 ondragenter(DragEvent),
141 ondragexit(DragEvent),
142 ondragleave(DragEvent),
143 ondragover(DragEvent),
144 ondragstart(DragEvent),
145 ondrop(DragEvent),
146
147 onblur(FocusEvent),
149 onfocus(FocusEvent),
150 onfocusin(FocusEvent),
151 onfocusout(FocusEvent),
152
153 oninput(InputEvent),
155
156 onkeydown(KeyboardEvent),
158 onkeypress(KeyboardEvent),
159 onkeyup(KeyboardEvent),
160
161 onauxclick(MouseEvent),
163 onclick(MouseEvent),
164 oncontextmenu(MouseEvent),
165 ondblclick(MouseEvent),
166 onmousedown(MouseEvent),
167 onmouseenter(MouseEvent),
168 onmouseleave(MouseEvent),
169 onmousemove(MouseEvent),
170 onmouseout(MouseEvent),
171 onmouseover(MouseEvent),
172 onmouseup(MouseEvent),
173
174 ongotpointercapture(PointerEvent),
176 onlostpointercapture(PointerEvent),
177 onpointercancel(PointerEvent),
178 onpointerdown(PointerEvent),
179 onpointerenter(PointerEvent),
180 onpointerleave(PointerEvent),
181 onpointermove(PointerEvent),
182 onpointerout(PointerEvent),
183 onpointerover(PointerEvent),
184 onpointerup(PointerEvent),
185
186 onloadend(ProgressEvent),
188 onloadstart(ProgressEvent),
189 onprogress(ProgressEvent),
190
191 onsubmit(SubmitEvent),
193
194 ontouchcancel(TouchEvent),
196 ontouchend(TouchEvent),
197 ontouchmove(TouchEvent),
198 ontouchstart(TouchEvent),
199
200 ontransitioncancel(TransitionEvent),
202 ontransitionend(TransitionEvent),
203 ontransitionrun(TransitionEvent),
204 ontransitionstart(TransitionEvent),
205
206 onwheel(WheelEvent)
208}
209
210#[derive(Debug)]
211pub struct EventHandler {
212 event_creator: Box<dyn EventCreator>,
213 event_listener: Option<EventListener>,
214}
215
216impl EventHandler {
217 pub fn new(event_creator: Box<dyn EventCreator>) -> Self {
218 Self {
219 event_creator,
220 event_listener: None,
221 }
222 }
223
224 pub(crate) fn attach(&mut self, element: &Element) {
225 let event_type = self.get_event_type();
226 let callback = self.event_creator.create_callback();
227 self.event_listener = Some(dom::create_event_listener(element, event_type, callback));
228 }
229
230 pub(crate) fn get_event_type(&self) -> Cow<'static, str> {
231 self.event_creator.get_event_type()
232 }
233}
234
235impl PartialEq for EventHandler {
236 fn eq(&self, other: &Self) -> bool {
237 let event_listener_eq = match (&self.event_listener, &other.event_listener) {
238 (Some(self_event_listener), Some(other_event_listener)) => {
239 self_event_listener.event_type() == other_event_listener.event_type()
240 }
241 (None, None) => true,
242 _ => false,
243 };
244 event_listener_eq && *self.event_creator == *other.event_creator
245 }
246}
247
248impl Eq for EventHandler {}
249
250impl Hash for EventHandler {
251 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
252 self.get_event_type().hash(state);
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use std::{cell::RefCell, rc::Rc};
259
260 use super::*;
261 use wasm_bindgen_test::*;
262
263 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
264
265 #[wasm_bindgen_test]
266 fn onclick_sepcialized_event_handler_constructor_should_create_event_handler_of_correct_type() {
267 let callback = Callback::new(|_| {});
269
270 let event_creator = onclick(callback);
272
273 let event_type = event_creator.get_event_type();
275 assert_eq!(event_type, "click");
276 }
277
278 #[wasm_bindgen_test]
279 fn onclick_specialized_event_handler_constructor_should_create_event_handler_with_correct_callback(
280 ) {
281 let was_callback_executed = Rc::new(RefCell::new(false));
283 let was_callback_executed_clone = was_callback_executed.clone();
284 let click = "click";
285 let callback = Callback::new(move |mouse_event: MouseEvent| {
286 assert_eq!(mouse_event.type_(), click);
287 *was_callback_executed_clone.borrow_mut() = true;
288 });
289 let web_sys_mouse_click_event = web_sys::MouseEvent::new(click).unwrap();
290
291 let event_creator = onclick(callback);
293
294 let mut event_callback = event_creator.create_callback();
296 (*event_callback)(web_sys_mouse_click_event.as_ref());
297 assert!(*was_callback_executed.borrow());
298 }
299
300 #[wasm_bindgen_test]
301 fn onabort_unspecialized_event_handler_constructor_should_create_event_handler_of_correct_type()
302 {
303 let callback = Callback::new(|_| {});
305
306 let event_creator = onabort(callback);
308
309 let event_type = event_creator.get_event_type();
311 assert_eq!(event_type, "abort");
312 }
313
314 #[wasm_bindgen_test]
315 fn onabort_unspecialized_event_handler_constructor_should_create_event_handler_with_correct_callback(
316 ) {
317 let was_callback_executed = Rc::new(RefCell::new(false));
319 let was_callback_executed_clone = was_callback_executed.clone();
320 let abort = "abort";
321 let callback = Callback::new(move |event: Event| {
322 assert_eq!(event.type_(), abort);
323 *was_callback_executed_clone.borrow_mut() = true;
324 });
325
326 let event_creator = onabort(callback);
328
329 let mut event_callback = event_creator.create_callback();
331 (*event_callback)(web_sys::Event::new(abort).unwrap().as_ref());
332 assert!(*was_callback_executed.borrow());
333 }
334
335 #[wasm_bindgen_test]
336 fn define_mouse_event_should_define_correct_event() {
337 let click = "click";
339 let web_sys_mouse_click_event = web_sys::MouseEvent::new(click).unwrap();
340
341 let event = MouseEvent::new(web_sys_mouse_click_event);
343
344 assert_eq!(event.type_(), click);
346 }
347
348 struct TestEventCreator {
349 event_type: Cow<'static, str>,
350 flag: Rc<RefCell<bool>>,
351 }
352
353 impl EventCreator for TestEventCreator {
354 fn get_event_type(&self) -> Cow<'static, str> {
355 self.event_type.clone()
356 }
357
358 fn create_callback(&self) -> Box<dyn FnMut(&web_sys::Event)> {
359 let flag = self.flag.clone();
360 Box::new(move |_| {
361 *flag.borrow_mut() = true;
362 })
363 }
364 }
365
366 #[wasm_bindgen_test]
367 fn event_handler_attach_should_attach_event_to_an_element() {
368 let window = web_sys::window().unwrap();
370 let document = window.document().unwrap();
371 let body = document.body().unwrap();
372
373 let flag = Rc::new(RefCell::new(false));
374 let event_creator = Box::new(TestEventCreator {
375 event_type: Cow::from("click"),
376 flag: flag.clone(),
377 });
378 let mut handler = EventHandler::new(event_creator);
379 handler.attach(&body);
380 let event = web_sys::Event::new("click").unwrap();
381
382 body.dispatch_event(&event).unwrap();
384
385 assert!(*flag.borrow());
387 }
388
389 #[wasm_bindgen_test]
390 fn get_event_type_should_get_correct_event_type() {
391 let callback = Callback::new(|_| {});
393 let event_creator = onclick(callback);
394 let handler = EventHandler::new(event_creator);
395
396 let event_type = handler.get_event_type();
398
399 assert_eq!(event_type, "click");
401 }
402}