Skip to main content

virtual_node/
event.rs

1pub use self::event_handlers::*;
2pub use self::event_name::EventName;
3#[cfg(feature = "web")]
4pub(crate) use self::virtual_events::set_events_id;
5#[cfg(feature = "web")]
6pub use self::virtual_events::VirtualEventsWebSys;
7pub use self::virtual_events::{
8    ElementEventsId, VirtualEventElement, VirtualEventNode, VirtualEvents, ELEMENT_EVENTS_ID_PROP,
9};
10#[cfg(feature = "web")]
11pub use self::web::{insert_non_delegated_event, EventAttribFn};
12use std::cell::RefCell;
13use std::collections::hash_map::Drain;
14use std::collections::HashMap;
15use std::fmt;
16use std::ops::{Deref, DerefMut};
17use std::rc::Rc;
18
19mod event_handlers;
20mod event_name;
21mod virtual_events;
22#[cfg(feature = "web")]
23mod web;
24
25/// We need a custom implementation of fmt::Debug since JsValue doesn't implement debug.
26pub struct Events<Dom: RealDom> {
27    // TODO: Store multiple events for a given event name, not just one.
28    //  `Vec<(EventName, EventHandler<Dom>>`
29    events: HashMap<EventName, EventHandler<Dom>>,
30}
31
32impl<Dom: RealDom> PartialEq for Events<Dom> {
33    fn eq(&self, other: &Self) -> bool {
34        let Events { events: lhs_events } = self;
35        let Events { events: rhs_events } = other;
36
37        lhs_events == rhs_events
38    }
39}
40
41impl<Dom: RealDom> Events<Dom> {
42    /// Whether or not there is at least one event.
43    pub fn has_events(&self) -> bool {
44        !self.events.is_empty()
45    }
46
47    /// All of the events.
48    pub fn events(&self) -> &HashMap<EventName, EventHandler<Dom>> {
49        &self.events
50    }
51
52    /// Insert an event handler that does not have any arguments.
53    pub fn insert_no_args(&mut self, event_name: EventName, event: Rc<RefCell<dyn FnMut()>>) {
54        self.events
55            .insert(event_name, EventHandler::<Dom>::NoArgs(event));
56    }
57
58    // Used by the html! macro
59    #[doc(hidden)]
60    pub fn __insert_unsupported_signature(
61        &mut self,
62        event_name: EventName,
63        event: Dom::EventCallback,
64    ) {
65        self.events.insert(event_name, EventHandler::Custom(event));
66    }
67
68    /// Insert a mouse event handler.
69    pub fn insert_mouse_event(
70        &mut self,
71        event_name: EventName,
72        event: Rc<RefCell<dyn FnMut(Dom::MouseEvent)>>,
73    ) {
74        self.events
75            .insert(event_name, EventHandler::MouseEvent(event));
76    }
77
78    /// Removes the element's events and returns them.
79    pub fn take_events(&mut self) -> Drain<'_, EventName, EventHandler<Dom>> {
80        self.events.drain()
81    }
82
83    /// Wrap the events in the given closure.
84    pub fn convert_all<New: RealDom>(
85        mut self,
86        convert: impl Fn(EventHandler<Dom>) -> EventHandler<New>,
87    ) -> Events<New> {
88        let mut new_events = HashMap::with_capacity(self.events.len());
89
90        for (event_name, before) in self.events.drain() {
91            let after = convert(before);
92            new_events.insert(event_name, after);
93        }
94
95        Events { events: new_events }
96    }
97}
98
99impl<Dom: RealDom> Events<Dom> {
100    /// Create a new Events.
101    pub fn new() -> Self {
102        Events {
103            events: HashMap::new(),
104        }
105    }
106}
107
108/// In some applications, [`VirtualNode`] get converted into real DOM nodes.
109///
110/// This trait contains types and methods for manipulating a real DOM.
111///
112/// When running a client-side web application, consider using [`web_sys::Window`] as the
113/// [`RealDom`].
114/// When running on a server, consider using the null `()` type as the [`RealDom`].
115///
116/// To control how a [`VirtualNode`] gets rendered to a DOM element, implement [`RealDom`] for your
117/// own custom type.
118///
119/// [`VirtualNode`]: crate::VirtualNode
120pub trait RealDom {
121    /// The event type. In the web this is [`web_sys::Event`].
122    type Event;
123    /// The event type for mouse events. In the web this is [`web_sys::MouseEvent`].
124    type MouseEvent;
125    /// The type for callbacks such as `|some_event| { ... }`.
126    type EventCallback: Clone;
127}
128
129/// An [`RealDom`] implementation that uses [`web_sys`]'s event types.
130#[cfg(feature = "web")]
131impl RealDom for web_sys::Window {
132    type Event = web_sys::Event;
133    type MouseEvent = crate::event::MouseEventWebSys;
134    type EventCallback = Rc<dyn AsRef<wasm_bindgen::JsValue>>;
135}
136
137impl RealDom for () {
138    type Event = ();
139    type MouseEvent = ();
140    type EventCallback = ();
141}
142
143#[cfg(feature = "web")]
144impl EventAttribFn {
145    /// Currently used by `crates/percy-dom`'s test suite.
146    #[doc(hidden)]
147    pub fn new_noop() -> EventAttribFn {
148        use wasm_bindgen::JsValue;
149        let noop = Rc::new(JsValue::NULL);
150        EventAttribFn::new(noop)
151    }
152}
153
154impl<Dom: RealDom> fmt::Debug for Events<Dom> {
155    // Print out all of the event names for this VirtualNode
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157        let events: String = self
158            .events
159            .keys()
160            .map(|key| " ".to_string() + key.with_on_prefix())
161            .collect();
162        write!(f, "{}", events)
163    }
164}
165
166impl<Dom: RealDom> Deref for Events<Dom> {
167    type Target = HashMap<EventName, EventHandler<Dom>>;
168
169    fn deref(&self) -> &Self::Target {
170        &self.events
171    }
172}
173
174impl<Dom: RealDom> DerefMut for Events<Dom> {
175    fn deref_mut(&mut self) -> &mut Self::Target {
176        &mut self.events
177    }
178}