sycamore_web/
events.rs

1//! Definition for all the events that can be listened to.
2
3#[cfg(feature = "suspense")]
4use sycamore_futures::spawn_local_scoped;
5use wasm_bindgen::JsCast;
6pub use web_sys::{
7    AnimationEvent, BeforeUnloadEvent, CompositionEvent, DeviceMotionEvent, DeviceOrientationEvent,
8    DragEvent, ErrorEvent, Event, FocusEvent, GamepadEvent, HashChangeEvent, InputEvent,
9    KeyboardEvent, MessageEvent, MouseEvent, PageTransitionEvent, PointerEvent, PopStateEvent,
10    ProgressEvent, PromiseRejectionEvent, SecurityPolicyViolationEvent, StorageEvent, SubmitEvent,
11    TouchEvent, TransitionEvent, UiEvent, WheelEvent,
12};
13
14/// Description of a type of event.
15pub trait EventDescriptor {
16    /// The JS type of the event data that is passed to the event handler.
17    type EventTy: Into<Event> + JsCast;
18    /// The name of the event.
19    const NAME: &'static str;
20}
21
22macro_rules! impl_event {
23    ($name:ident: $ty:ty) => {
24        #[allow(non_camel_case_types)]
25        pub struct $name;
26        impl EventDescriptor for $name {
27            type EventTy = $ty;
28            const NAME: &'static str = stringify!($name);
29        }
30    };
31}
32
33macro_rules! impl_events {
34    ($($name:ident: $ty:ty,)*) => {
35        $(impl_event!($name: $ty);)*
36    };
37}
38
39impl_events! {
40    afterprint: Event,
41    beforeprint: Event,
42    beforeunload: BeforeUnloadEvent,
43    gamepadconnected: GamepadEvent,
44    gamepaddisconnected: GamepadEvent,
45    hashchange: HashChangeEvent,
46    languagechange: Event,
47    message: MessageEvent,
48    messageerror: MessageEvent,
49    offline: Event,
50    online: Event,
51    pagehide: PageTransitionEvent,
52    pageshow: PageTransitionEvent,
53    popstate: PopStateEvent,
54    rejectionhandled: PromiseRejectionEvent,
55    storage: StorageEvent,
56    unhandledrejection: PromiseRejectionEvent,
57    unload: Event,
58
59    /*
60    GlobalEventHandlersEventMap
61    */
62    abort: UiEvent,
63    animationcancel: AnimationEvent,
64    animationend: AnimationEvent,
65    animationiteration: AnimationEvent,
66    animationstart: AnimationEvent,
67    auxclick: MouseEvent,
68    beforeinput: InputEvent,
69    blur: FocusEvent,
70    canplay: Event,
71    canplaythrough: Event,
72    change: Event,
73    click: MouseEvent,
74    close: Event,
75    compositionend: CompositionEvent,
76    compositionstart: CompositionEvent,
77    compositionupdate: CompositionEvent,
78    contextmenu: MouseEvent,
79    cuechange: Event,
80    dblclick: MouseEvent,
81    drag: DragEvent,
82    dragend: DragEvent,
83    dragenter: DragEvent,
84    dragleave: DragEvent,
85    dragover: DragEvent,
86    dragstart: DragEvent,
87    drop: DragEvent,
88    durationchange: Event,
89    emptied: Event,
90    ended: Event,
91    error: ErrorEvent,
92    focus: FocusEvent,
93    focusin: FocusEvent,
94    focusout: FocusEvent,
95    formdata: Event, // web_sys does not include `FormDataEvent`
96    gotpointercapture: PointerEvent,
97    input: Event,
98    invalid: Event,
99    keydown: KeyboardEvent,
100    keypress: KeyboardEvent,
101    keyup: KeyboardEvent,
102    load: Event,
103    loadeddata: Event,
104    loadedmetadata: Event,
105    loadstart: Event,
106    lostpointercapture: PointerEvent,
107    mousedown: MouseEvent,
108    mouseenter: MouseEvent,
109    mouseleave: MouseEvent,
110    mousemove: MouseEvent,
111    mouseout: MouseEvent,
112    mouseover: MouseEvent,
113    mouseup: MouseEvent,
114    pause: Event,
115    play: Event,
116    playing: Event,
117    pointercancel: PointerEvent,
118    pointerdown: PointerEvent,
119    pointerenter: PointerEvent,
120    pointerleave: PointerEvent,
121    pointermove: PointerEvent,
122    pointerout: PointerEvent,
123    pointerover: PointerEvent,
124    pointerup: PointerEvent,
125    progress: ProgressEvent,
126    ratechange: Event,
127    reset: Event,
128    resize: UiEvent,
129    scroll: Event,
130    securitypolicyviolation: SecurityPolicyViolationEvent,
131    seeked: Event,
132    seeking: Event,
133    select: Event,
134    selectionchange: Event,
135    selectstart: Event,
136    slotchange: Event,
137    stalled: Event,
138    submit: SubmitEvent,
139    suspend: Event,
140    timeupdate: Event,
141    toggle: Event,
142    touchcancel: TouchEvent,
143    touchend: TouchEvent,
144    touchmove: TouchEvent,
145    touchstart: TouchEvent,
146    transitioncancel: TransitionEvent,
147    transitionend: TransitionEvent,
148    transitionrun: TransitionEvent,
149    transitionstart: TransitionEvent,
150    volumechange: Event,
151    waiting: Event,
152    webkitanimationend: Event,
153    webkitanimationiteration: Event,
154    webkitanimationstart: Event,
155    webkittransitionend: Event,
156    wheel: WheelEvent,
157
158    /*
159    WindowEventMap
160    */
161    DOMContentLoaded: Event,
162    devicemotion: DeviceMotionEvent,
163    deviceorientation: DeviceOrientationEvent,
164    orientationchange: Event,
165
166    /*
167    DocumentAndElementEventHandlersEventMap
168    */
169    copy: Event, // ClipboardEvent is unstable
170    cut: Event, // ClipboardEvent is unstable
171    paste: Event, // ClipboardEvent is unstable
172
173    /*
174    DocumentEventMap
175    */
176    fullscreenchange: Event,
177    fullscreenerror: Event,
178    pointerlockchange: Event,
179    pointerlockerror: Event,
180    readystatechange: Event,
181    visibilitychange: Event,
182}
183
184pub trait EventHandler<E: EventDescriptor, R = ()>: 'static {
185    fn call(&mut self, event: E::EventTy);
186}
187
188impl<E, F> EventHandler<E, ()> for F
189where
190    E: EventDescriptor,
191    F: FnMut(E::EventTy) + 'static,
192{
193    fn call(&mut self, event: E::EventTy) {
194        self(event);
195    }
196}
197
198/// Marker trait to workaround specialisation.
199#[doc(hidden)]
200#[cfg(feature = "suspense")]
201pub struct AsyncHandler;
202
203/// Support calling async functions as well.
204#[cfg(feature = "suspense")]
205impl<E, F, R> EventHandler<E, AsyncHandler> for F
206where
207    E: EventDescriptor,
208    F: FnMut(E::EventTy) -> R + 'static,
209    R: std::future::Future<Output = ()> + 'static,
210{
211    fn call(&mut self, event: E::EventTy) {
212        spawn_local_scoped(self(event));
213    }
214}