sauron_core/dom/
events.rs1use crate::dom::DomNode;
5use crate::dom::{document, window, Event};
6use crate::vdom;
7use crate::vdom::ComponentEventCallback;
8use crate::vdom::{Attribute, AttributeValue, EventCallback};
9use wasm_bindgen::JsCast;
10pub use web_sys::ClipboardEvent;
11pub use web_sys::{
12 AnimationEvent, FocusEvent, HashChangeEvent, KeyboardEvent, MouseEvent, Selection, TouchEvent,
13 TransitionEvent,
14};
15use web_sys::{
16 EventTarget, HtmlDetailsElement, HtmlElement, HtmlInputElement, HtmlSelectElement,
17 HtmlTextAreaElement,
18};
19
20#[derive(Clone, Copy)]
21#[repr(i16)]
22pub enum MouseButton {
24 Primary = 0,
26
27 Auxiliary = 1,
29
30 Secondary = 2,
32
33 Fourth = 3,
35
36 Fifth = 4,
38}
39
40impl MouseButton {
41 pub fn is_primary(me: &MouseEvent) -> bool {
43 me.button() == MouseButton::Primary as i16
44 }
45
46 pub fn is_auxiliary(me: &MouseEvent) -> bool {
48 me.button() == MouseButton::Auxiliary as i16
49 }
50}
51
52impl Event {
53 pub fn as_web(self) -> Option<web_sys::Event> {
55 match self {
56 Event::WebEvent(web_event) => Some(web_event),
57 _ => None,
58 }
59 }
60}
61
62impl From<MountEvent> for Event {
63 fn from(mount_event: MountEvent) -> Self {
64 Event::MountEvent(mount_event)
65 }
66}
67
68impl From<web_sys::Event> for Event {
69 fn from(web_event: web_sys::Event) -> Self {
70 Event::WebEvent(web_event)
71 }
72}
73
74impl From<web_sys::MouseEvent> for Event {
75 fn from(mouse_event: MouseEvent) -> Self {
76 let event: web_sys::Event = mouse_event
77 .dyn_into()
78 .expect("Unable to cast mouse event into event");
79 Event::from(event)
80 }
81}
82
83pub fn on<F, MSG>(event_name: &'static str, f: F) -> Attribute<MSG>
85where
86 F: FnMut(Event) -> MSG + 'static,
87 MSG: 'static,
88{
89 vdom::attr(
90 event_name,
91 AttributeValue::EventListener(EventCallback::from(f)),
92 )
93}
94
95pub fn on_click<F, MSG>(mut f: F) -> Attribute<MSG>
97where
98 F: FnMut(MouseEvent) -> MSG + 'static,
99 MSG: 'static,
100{
101 on("click", move |event: Event| f(to_mouse_event(event)))
102}
103
104pub fn on_scroll<F, MSG>(mut f: F) -> Attribute<MSG>
106where
107 F: FnMut((i32, i32)) -> MSG + 'static,
108 MSG: 'static,
109{
110 on("scroll", move |event: Event| {
111 let web_event = event.as_web().expect("must be a web event");
112 let target = web_event.target().expect("can't get target");
113 if let Some(element) = target.dyn_ref::<web_sys::Element>() {
114 let scroll_top = element.scroll_top();
115 let scroll_left = element.scroll_left();
116 f((scroll_top, scroll_left))
117 } else {
118 let window = window();
119 let scroll_top = window.page_y_offset().expect("must get page offset") as i32;
120 let scroll_left = window.page_x_offset().expect("must get page offset") as i32;
121 f((scroll_top, scroll_left))
122 }
123 })
124}
125
126#[derive(Debug, Clone)]
129pub struct MountEvent {
130 pub target_node: DomNode,
132}
133
134impl MountEvent {
135 pub fn create_web_event() -> web_sys::Event {
137 web_sys::Event::new("mount").expect("as event")
138 }
139}
140
141pub fn on_mount<F, MSG>(mut f: F) -> Attribute<MSG>
143where
144 F: FnMut(MountEvent) -> MSG + 'static,
145 MSG: 'static,
146{
147 on("mount", move |event: Event| {
148 let web_event = event.as_web().expect("must be a web event");
149 let event_target = web_event.target().expect("must have a target");
150 let target_node: web_sys::Node = event_target.unchecked_into();
151 let me = MountEvent {
152 target_node: DomNode::from(target_node),
153 };
154 f(me)
155 })
156}
157
158pub fn on_component_mount<F, MSG>(mut f: F) -> Attribute<MSG>
160where
161 F: FnMut(MountEvent) + 'static,
162 MSG: 'static,
163{
164 let cb = move |event: Event| {
165 let web_event = event.as_web().expect("must be a web event");
166 let event_target = web_event.target().expect("must have a target");
167 let target_node: web_sys::Node = event_target.unchecked_into();
168 let me = MountEvent {
169 target_node: DomNode::from(target_node),
170 };
171 f(me);
172 };
173 vdom::attr(
174 "mount",
175 AttributeValue::ComponentEventListener(ComponentEventCallback::from(cb)),
176 )
177}
178
179macro_rules! declare_events {
180
181 ( $(
182 $(#[$attr:meta])*
183 $name:ident => $event:ident => $mapper:ident => $ret:ty;
184 )*
185 ) => {
186 $(
187 doc_comment!{
188 concat!("attach an [",stringify!($name),"](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/",stringify!($name),") event to the html element"),
189 $(#[$attr])*
190 #[inline]
191 pub fn $name<CB, MSG>(mut cb: CB) -> crate::vdom::Attribute<MSG>
192 where CB: FnMut($ret) -> MSG + 'static,
193 MSG: 'static,
194 {
195 on(stringify!($event), move|event:Event|{
196 cb($mapper(event))
197 })
198 }
199 }
200 )*
201 }
202}
203
204macro_rules! declare_html_events{
205 ( $(
206 $(#[$attr:meta])*
207 $name:ident => $event:ident => $mapper:ident => $ret:ty;
208 )*
209 ) => {
210 declare_events!{
211 $(
212 $(#[$attr])*
213 $name => $event => $mapper => $ret;
214 )*
215 }
216
217 pub const HTML_EVENTS: &[&'static str] = &[$(stringify!($event),)*];
219 }
220}
221
222fn to_mouse_event(event: Event) -> MouseEvent {
224 let web_event = event.as_web().expect("must be a web_sys event");
225 web_event.dyn_into().expect("Unable to cast to mouse event")
226}
227
228fn to_focus_event(event: Event) -> FocusEvent {
229 let web_event = event.as_web().expect("must be a web_sys event");
230 web_event.dyn_into().expect("Unable to cast to focus event")
231}
232
233fn to_keyboard_event(event: Event) -> KeyboardEvent {
234 let web_event = event.as_web().expect("must be a web_sys event");
235 web_event
236 .dyn_into()
237 .expect("unable to cast to keyboard event")
238}
239
240fn to_animation_event(event: Event) -> AnimationEvent {
241 let web_event = event.as_web().expect("must be a web_sys event");
242 web_event
243 .dyn_into()
244 .expect("unable to cast to animation event")
245}
246
247fn to_transition_event(event: Event) -> TransitionEvent {
248 let web_event = event.as_web().expect("must be a web_sys event");
249 web_event
250 .dyn_into()
251 .expect("unable to cast to transition event")
252}
253
254fn to_touch_event(event: Event) -> TouchEvent {
255 let web_event = event.as_web().expect("must be web sys event");
256 web_event.dyn_into().expect("unable to cast to touch event")
257}
258
259fn to_webevent(event: Event) -> web_sys::Event {
260 match event {
261 Event::WebEvent(event) => event,
262 _ => panic!("not a web_event"),
263 }
264}
265
266fn to_hashchange_event(event: Event) -> HashChangeEvent {
267 let web_event = event.as_web().expect("must be a web_sys event");
268 web_event
269 .dyn_into()
270 .expect("unable to cast to hashchange event")
271}
272
273#[derive(Debug)]
277pub struct InputEvent {
278 pub event: web_sys::Event,
280}
281
282impl InputEvent {
283 fn new(event: web_sys::Event) -> Self {
284 InputEvent { event }
285 }
286
287 pub fn prevent_default(&self) {
289 self.event.prevent_default()
290 }
291
292 pub fn stop_propagation(&self) {
294 self.event.stop_propagation()
295 }
296
297 pub fn value(&self) -> String {
301 let target: EventTarget = self.event.target().expect("Unable to get event target");
302 if let Some(input) = target.dyn_ref::<HtmlInputElement>() {
303 input.value()
304 } else if let Some(textarea) = target.dyn_ref::<HtmlTextAreaElement>() {
305 textarea.value()
306 } else if let Some(select) = target.dyn_ref::<HtmlSelectElement>() {
307 select.value()
308 } else if let Some(html_elm) = target.dyn_ref::<HtmlElement>() {
309 if let Some(value) = html_elm.get_attribute("value") {
310 log::info!("got value: {}", value);
311 value
312 } else {
313 log::info!("no value..");
314 if let Some(content) = html_elm.get_attribute("content") {
315 log::info!("got content: {}", content);
316 content
317 } else {
318 log::info!("no content either..");
319 "".to_string()
320 }
321 }
322 } else {
323 panic!("fail in mapping event into input event");
324 }
325 }
326
327 pub fn create_web_event() -> web_sys::Event {
329 web_sys::Event::new("input").expect("as event")
330 }
331
332 pub fn create_web_event_composed() -> web_sys::Event {
334 let event_init = web_sys::EventInit::new();
335 event_init.set_composed(true);
336 web_sys::Event::new_with_event_init_dict("input", &event_init).expect("event init")
337 }
338}
339
340fn to_input_event(event: Event) -> InputEvent {
341 let web_event = event.as_web().expect("must be a web event");
342 InputEvent::new(web_event)
343}
344
345fn to_checked(event: Event) -> bool {
346 let web_event = event.as_web().expect("must be a web event");
347 let target: EventTarget = web_event.target().expect("Unable to get event target");
348 if let Some(input) = target.dyn_ref::<HtmlInputElement>() {
349 input.checked()
350 } else {
351 panic!("must be a html input element");
352 }
353}
354
355fn to_open(event: Event) -> bool {
356 let web_event = event.as_web().expect("must be a web event");
357 let target: EventTarget = web_event.target().expect("Unable to get event target");
358 if let Some(details) = target.dyn_ref::<HtmlDetailsElement>() {
359 details.open()
360 } else {
361 panic!("must be a html input element");
362 }
363}
364
365fn to_clipboard_event(event: Event) -> ClipboardEvent {
369 event
370 .as_web()
371 .expect("must be a web event")
372 .dyn_into()
373 .expect("unable to cast to clipboard event")
374}
375
376fn to_selection(_event: Event) -> Option<Selection> {
377 if let Ok(Some(selection)) = document().get_selection() {
378 Some(selection)
379 } else {
380 None
381 }
382}
383
384declare_html_events! {
386 on_auxclick => auxclick => to_mouse_event => MouseEvent;
387 on_animationend => animationend => to_animation_event => AnimationEvent;
388 on_transitionend => transitionend => to_transition_event => TransitionEvent;
389 on_contextmenu => contextmenu => to_mouse_event => MouseEvent;
390 on_dblclick => dblclick => to_mouse_event => MouseEvent;
391 on_mousedown => mousedown => to_mouse_event => MouseEvent;
392 on_mouseenter => mouseenter => to_mouse_event => MouseEvent;
393 on_mouseleave => mouseleave => to_mouse_event => MouseEvent;
394 on_mousemove => mousemove => to_mouse_event => MouseEvent;
395 on_mouseover => mouseover => to_mouse_event => MouseEvent;
396 on_mouseout => mouseout => to_mouse_event => MouseEvent;
397 on_mouseup => mouseup => to_mouse_event => MouseEvent;
398 on_pointerlockchange => pointerlockchange => to_mouse_event => MouseEvent;
399 on_pointerlockerror => pointerlockerror => to_mouse_event => MouseEvent;
400 on_popstate => popstate => to_webevent => web_sys::Event;
401 on_select => select => to_webevent => web_sys::Event;
402 on_wheel => wheel => to_mouse_event => MouseEvent;
403 on_doubleclick => dblclick => to_mouse_event => MouseEvent;
404 on_keydown => keydown => to_keyboard_event => KeyboardEvent;
405 on_keypress => keypress => to_keyboard_event => KeyboardEvent;
406 on_keyup => keyup => to_keyboard_event => KeyboardEvent;
407 on_toggle => toggle => to_open => bool;
408 on_touchstart => touchstart => to_touch_event => TouchEvent;
409 on_touchend => touchend => to_touch_event => TouchEvent;
410 on_touchmove => touchmove => to_touch_event => TouchEvent;
411 on_focus => focus => to_focus_event => FocusEvent;
412 on_blur => blur => to_focus_event => FocusEvent;
413 on_reset => reset => to_webevent => web_sys::Event;
414 on_submit => submit => to_webevent => web_sys::Event;
415 on_input => input => to_input_event => InputEvent;
416 on_checked => input => to_checked => bool;
417 on_paste => paste => to_clipboard_event => ClipboardEvent;
418 on_copy => copy => to_clipboard_event => ClipboardEvent;
419 on_change => change => to_input_event => InputEvent;
420 on_broadcast => broadcast => to_input_event => InputEvent;
421 on_hashchange => hashchange => to_hashchange_event => HashChangeEvent;
422 on_readystatechange => readystatechange => to_webevent => web_sys::Event;
423 on_selectionchange => selectionchange => to_selection => Option<Selection>;
424}