Skip to main content

virtual_node/
event.rs

1pub use self::event_handlers::*;
2pub use self::event_name::EventName;
3pub use self::non_delegated_event_wrapper::insert_non_delegated_event;
4pub(crate) use self::virtual_events::set_events_id;
5pub use self::virtual_events::{
6    ElementEventsId, VirtualEventElement, VirtualEventNode, VirtualEvents, ELEMENT_EVENTS_ID_PROP,
7};
8use std::cell::RefCell;
9use std::collections::HashMap;
10use std::fmt;
11use std::fmt::Formatter;
12use std::ops::{Deref, DerefMut};
13use std::rc::Rc;
14use wasm_bindgen::JsValue;
15use web_sys::Event;
16
17mod event_handlers;
18mod event_name;
19mod non_delegated_event_wrapper;
20mod virtual_events;
21
22/// A type-erased [`js_sys::Closure`]. Stored as a trait object to enable any number of arguments.
23pub(crate) type EventWrapper = std::rc::Rc<dyn EventCallback>;
24
25/// A handler for an event, such the closure in `onmyevent = || {}`.
26#[derive(Clone)]
27pub struct EventAttribFn(EventWrapper);
28
29/// We need a custom implementation of fmt::Debug since JsValue doesn't implement debug.
30#[derive(PartialEq)]
31pub struct Events {
32    events: HashMap<EventName, EventHandler>,
33}
34
35impl Events {
36    /// Whether or not there is at least one event.
37    pub fn has_events(&self) -> bool {
38        !self.events.is_empty()
39    }
40
41    /// All of the events.
42    pub fn events(&self) -> &HashMap<EventName, EventHandler> {
43        &self.events
44    }
45
46    /// Insert an event handler that does not have any arguments.
47    pub fn insert_no_args(&mut self, event_name: EventName, event: Rc<RefCell<dyn FnMut()>>) {
48        self.events.insert(event_name, EventHandler::NoArgs(event));
49    }
50
51    // Used by the html! macro
52    #[doc(hidden)]
53    pub fn __insert_unsupported_signature(&mut self, event_name: EventName, event: EventWrapper) {
54        self.events
55            .insert(event_name, EventHandler::UnsupportedSignature(event.into()));
56    }
57
58    /// Insert a mouse event handler.
59    pub fn insert_mouse_event(
60        &mut self,
61        event_name: EventName,
62        event: Rc<RefCell<dyn FnMut(MouseEvent)>>,
63    ) {
64        self.events
65            .insert(event_name, EventHandler::MouseEvent(event));
66    }
67}
68
69impl Events {
70    /// Create a new Events.
71    pub fn new() -> Self {
72        Events {
73            events: HashMap::new(),
74        }
75    }
76}
77
78/// A callback that runs when an event is triggered.
79///
80/// For instance, this might represent the closure in `onmyevent = |_| {}`.
81pub trait EventCallback {
82    /// Invoke the callback.
83    fn call(&self, event: &web_sys::Event);
84
85    /// Get the underlying [`wasm_bindgen::JsValue`].
86    fn as_js_value(&self) -> Option<&wasm_bindgen::JsValue>;
87}
88
89impl<T: ?Sized> EventCallback for wasm_bindgen::closure::Closure<T> {
90    fn call(&self, event: &web_sys::Event) {
91        use wasm_bindgen::JsCast;
92
93        let context = wasm_bindgen::JsValue::NULL;
94        let callback: &js_sys::Function = self.as_ref().as_ref().unchecked_ref();
95        callback.call1(&context, &event).unwrap();
96    }
97
98    fn as_js_value(&self) -> Option<&wasm_bindgen::JsValue> {
99        Some(self.as_ref())
100    }
101}
102
103impl EventAttribFn {
104    /// Currently used by `crates/percy-dom`'s test suite.
105    #[doc(hidden)]
106    pub fn new_noop() -> Self {
107        struct Noop;
108        impl EventCallback for Noop {
109            fn call(&self, _event: &Event) {}
110
111            fn as_js_value(&self) -> Option<&JsValue> {
112                None
113            }
114        }
115        Self(Rc::new(Noop))
116    }
117
118    pub(crate) fn call(&self, event: &web_sys::Event) {
119        self.0.call(event)
120    }
121}
122
123// Allows us to easily derive PartialEq for some of the types that contain events.
124// Those PartialEq implementations are used for testing.
125// Maybe we can put some of the event related PartialEq implementations
126// behind a #[cfg(any(test, feature = "__test-utils"))].
127impl PartialEq for EventAttribFn {
128    fn eq(&self, _other: &Self) -> bool {
129        true
130    }
131}
132
133impl fmt::Debug for Events {
134    // Print out all of the event names for this VirtualNode
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        let events: String = self
137            .events
138            .keys()
139            .map(|key| " ".to_string() + key.with_on_prefix())
140            .collect();
141        write!(f, "{}", events)
142    }
143}
144
145impl fmt::Debug for EventAttribFn {
146    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
147        write!(f, "event_handler()")
148    }
149}
150
151impl From<EventWrapper> for EventAttribFn {
152    fn from(inner: EventWrapper) -> Self {
153        EventAttribFn(inner)
154    }
155}
156
157impl Deref for Events {
158    type Target = HashMap<EventName, EventHandler>;
159
160    fn deref(&self) -> &Self::Target {
161        &self.events
162    }
163}
164
165impl DerefMut for Events {
166    fn deref_mut(&mut self) -> &mut Self::Target {
167        &mut self.events
168    }
169}