duid_core/core/events/
mod.rs

1pub mod listener;
2
3use crate::core::html::attributes::{AttributeValue, Attribute, attr};
4use wasm_bindgen::JsCast;
5
6#[cfg(web_sys_unstable_apis)]
7pub use web_sys::ClipboardEvent;
8pub use web_sys::{
9    AnimationEvent, HashChangeEvent, KeyboardEvent, MouseEvent, TransitionEvent, Event as WebEvent
10};
11use web_sys::{
12    EventTarget, HtmlInputElement, HtmlSelectElement, HtmlTextAreaElement
13};
14
15
16pub type Listener<MSG> = listener::Listener<Event, MSG>;
17
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum Event {
21    WebEvent(web_sys::Event),
22    MountEvent(MountEvent),
23}
24
25impl Event {
26    pub fn as_web(self) -> Option<web_sys::Event> {
27        match self {
28            Event::WebEvent(web_event) => Some(web_event),
29            _ => None,
30        }
31    }
32}
33
34impl From<MountEvent> for Event {
35    fn from(mount_event: MountEvent) -> Self {
36        Event::MountEvent(mount_event)
37    }
38}
39
40impl From<web_sys::Event> for Event {
41    fn from(web_event: web_sys::Event) -> Self {
42        Event::WebEvent(web_event)
43    }
44}
45
46impl From<web_sys::MouseEvent> for Event {
47    fn from(mouse_event: MouseEvent) -> Self {
48        let event: web_sys::Event = mouse_event
49            .dyn_into()
50            .expect("Unable to cast mouse event into event");
51        Event::from(event)
52    }
53}
54
55pub fn on<F, MSG>(event_name: &'static str, f: F) -> Attribute<MSG>
56where
57    F: Fn(Event) -> MSG + 'static,
58    MSG: 'static
59{
60    attr(event_name, AttributeValue::EventListener(Listener::from(f)))
61}
62
63pub fn on_click<F, MSG>(f: F) -> Attribute<MSG>
64where
65    F: Fn(MouseEvent) -> MSG + 'static,
66    MSG: 'static
67{
68    on("click", move |event: Event| f(to_mouse_event(event)))
69}
70
71pub fn on_enter<F, MSG>(f: F) -> Attribute<MSG>
72where
73    F: Fn(KeyboardEvent) -> MSG + 'static,
74    MSG: 'static
75{
76    on("enter", move |event: Event| f(to_keyboard_event(event)))
77}
78
79pub fn on_scroll<F, MSG>(f: F) -> Attribute<MSG>
80where
81    F: Fn((i32, i32)) -> MSG + 'static,
82    MSG: 'static
83{
84    on("scroll", move |event: Event| {
85        let web_event = event.as_web().expect("must be a web event");
86        let target = web_event.target().expect("can't get target");
87        if let Some(element) = target.dyn_ref::<web_sys::Element>() {
88            let scroll_top = element.scroll_top();
89            let scroll_left = element.scroll_left();
90            f((scroll_top, scroll_left))
91        } else {
92            let window = crate::core::util::window();
93            let scroll_top =
94                window.page_y_offset().expect("must get page offset") as i32;
95            let scroll_left =
96                window.page_x_offset().expect("must get page offset") as i32;
97            f((scroll_top, scroll_left))
98        }
99    })
100}
101
102#[derive(Debug, Clone, PartialEq)]
103pub struct MountEvent {
104    pub target_node: web_sys::Node,
105}
106
107pub fn on_mount<F, MSG>(f: F) -> Attribute<MSG>
108where
109    F: Fn(MountEvent) -> MSG + 'static,
110    MSG: 'static
111{
112    on("mount", move |event: Event| match event {
113        Event::MountEvent(me) => f(me),
114        _ => {
115            unreachable!()
116        }
117    })
118}
119
120macro_rules! declare_events {
121
122    ( $(
123         $(#[$attr:meta])*
124         $name:ident => $event:ident => $mapper:ident => $ret:ty;
125       )*
126     ) => {
127        $(
128            doc_comment::doc_comment!{
129                concat!("attach an [",stringify!($name),"](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/",stringify!($name),") event to the html element"),
130                $(#[$attr])*
131                #[inline]
132                pub fn $name<CB, MSG>(cb: CB) -> crate::core::html::attributes::Attribute<MSG>
133                    where CB: Fn($ret) -> MSG + 'static,
134                        MSG: 'static
135                    {
136                        on(stringify!($event), move|event:Event|{
137                            cb($mapper(event))
138                        })
139                }
140            }
141         )*
142    }
143}
144
145macro_rules! declare_html_events{
146    ( $(
147         $(#[$attr:meta])*
148         $name:ident => $event:ident => $mapper:ident => $ret:ty;
149       )*
150     ) => {
151        declare_events!{
152            $(
153                $(#[$attr])*
154                $name => $event => $mapper => $ret;
155            )*
156        }
157
158        pub const HTML_EVENTS: [&'static str; 34] = [$(stringify!($event),)*];
159    }
160}
161
162fn to_mouse_event(event: Event) -> MouseEvent {
163    let web_event = event.as_web().expect("must be a web_sys event");
164    web_event.dyn_into().expect("Unable to cast to mouse event")
165}
166
167fn to_keyboard_event(event: Event) -> KeyboardEvent {
168    let web_event = event.as_web().expect("must be a web_sys event");
169    web_event
170        .dyn_into()
171        .expect("unable to cast to keyboard event")
172}
173
174fn to_animation_event(event: Event) -> AnimationEvent {
175    let web_event = event.as_web().expect("must be a web_sys event");
176    web_event
177        .dyn_into()
178        .expect("unable to cast to animation event")
179}
180
181fn to_transition_event(event: Event) -> TransitionEvent {
182    let web_event = event.as_web().expect("must be a web_sys event");
183    web_event
184        .dyn_into()
185        .expect("unable to cast to transition event")
186}
187
188fn to_webevent(event: Event) -> web_sys::Event {
189    match event {
190        Event::WebEvent(event) => event,
191        _ => unreachable!(),
192    }
193}
194
195fn to_hashchange_event(event: Event) -> HashChangeEvent {
196    let web_event = event.as_web().expect("must be a web_sys event");
197    web_event
198        .dyn_into()
199        .expect("unable to cast to hashchange event")
200}
201
202#[derive(Debug)]
203pub struct InputEvent {
204    pub value: String,
205    pub event: web_sys::Event,
206}
207
208impl InputEvent {
209    fn new(value: String, event: web_sys::Event) -> Self {
210        InputEvent { value, event }
211    }
212}
213
214fn to_input_event(event: Event) -> InputEvent {
215    let web_event = event.as_web().expect("must be a web event");
216    let target: EventTarget =
217        web_event.target().expect("Unable to get event target");
218    if let Some(input) = target.dyn_ref::<HtmlInputElement>() {
219        InputEvent::new(input.value(), web_event)
220    } else if let Some(textarea) = target.dyn_ref::<HtmlTextAreaElement>() {
221        InputEvent::new(textarea.value(), web_event)
222    } else if let Some(select) = target.dyn_ref::<HtmlSelectElement>() {
223        InputEvent::new(select.value(), web_event)
224    } else {
225        panic!("fail in mapping event into input event");
226    }
227}
228
229fn to_checked(event: Event) -> bool {
230    let web_event = event.as_web().expect("must be a web event");
231    let target: EventTarget =
232        web_event.target().expect("Unable to get event target");
233    if let Some(input) = target.dyn_ref::<HtmlInputElement>() {
234        input.checked()
235    } else {
236        panic!("must be a html input element");
237    }
238}
239
240#[cfg(web_sys_unstable_apis)]
241fn to_clipboard_event(event: Event) -> ClipboardEvent {
242    event
243        .as_web()
244        .expect("must be a web event")
245        .dyn_into()
246        .expect("unable to cast to clipboard event")
247}
248
249declare_html_events! {
250    on_auxclick => auxclick => to_mouse_event => MouseEvent;
251    on_animationend => animationend => to_animation_event => AnimationEvent;
252    on_transitionend => transitionend => to_transition_event => TransitionEvent;
253    on_contextmenu => contextmenu => to_mouse_event => MouseEvent;
254    on_dblclick  => dblclick => to_mouse_event => MouseEvent;
255    on_mousedown => mousedown => to_mouse_event => MouseEvent;
256    on_mouseenter => mouseenter => to_mouse_event => MouseEvent;
257    on_mouseleave => mouseleave => to_mouse_event => MouseEvent;
258    on_mousemove => mousemove => to_mouse_event => MouseEvent;
259    on_mouseover => mouseover => to_mouse_event => MouseEvent;
260    on_mouseout => mouseout => to_mouse_event => MouseEvent;
261    on_mouseup => mouseup => to_mouse_event => MouseEvent;
262    on_pointerlockchange => pointerlockchange => to_mouse_event => MouseEvent;
263    on_pointerlockerror => pointerlockerror => to_mouse_event => MouseEvent;
264    on_popstate => popstate => to_webevent => web_sys::Event;
265    on_select => select => to_webevent => web_sys::Event;
266    on_wheel => wheel => to_mouse_event => MouseEvent;
267    on_doubleclick => dblclick => to_mouse_event => MouseEvent;
268    on_keydown => keydown => to_keyboard_event => KeyboardEvent;
269    on_keypress => keypress => to_keyboard_event => KeyboardEvent;
270    on_keyup => keyup => to_keyboard_event => KeyboardEvent;
271    on_toggle => toggle => to_webevent => web_sys::Event;
272    on_focus => focus => to_webevent => web_sys::Event;
273    on_blur => blur => to_webevent => web_sys::Event;
274    on_reset => reset => to_webevent => web_sys::Event;
275    on_submit => submit => to_webevent => web_sys::Event;
276    on_input => input => to_input_event => InputEvent;
277    on_checked => input => to_checked => bool;
278    #[cfg(web_sys_unstable_apis)]
279    on_paste => paste => to_clipboard_event => ClipboardEvent;
280    #[cfg(web_sys_unstable_apis)]
281    on_copy => copy => to_clipboard_event => ClipboardEvent;
282    on_change => change => to_input_event => InputEvent;
283    on_broadcast => broadcast => to_input_event => InputEvent;
284    on_hashchange => hashchange => to_hashchange_event => HashChangeEvent;
285    on_readystatechange => readystatechange => to_webevent => web_sys::Event;
286}