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;
18use wasm_bindgen::JsValue;
19
20mod event_handlers;
21mod event_name;
22mod virtual_events;
23#[cfg(feature = "web")]
24mod web;
25
26/// We need a custom implementation of fmt::Debug since JsValue doesn't implement debug.
27pub struct Events<Dom: RealDom> {
28    // TODO: Store multiple events for a given event name, not just one.
29    //  `Vec<(EventName, EventHandler<Dom>>`
30    events: HashMap<EventName, EventHandler<Dom>>,
31}
32
33impl<Dom: RealDom> PartialEq for Events<Dom> {
34    fn eq(&self, other: &Self) -> bool {
35        let Events { events: lhs_events } = self;
36        let Events { events: rhs_events } = other;
37
38        lhs_events == rhs_events
39    }
40}
41
42impl<Dom: RealDom> Events<Dom> {
43    /// Whether or not there is at least one event.
44    pub fn has_events(&self) -> bool {
45        !self.events.is_empty()
46    }
47
48    /// All of the events.
49    pub fn events(&self) -> &HashMap<EventName, EventHandler<Dom>> {
50        &self.events
51    }
52
53    /// Insert an event handler that does not have any arguments.
54    pub fn insert_no_args(&mut self, event_name: EventName, event: Rc<RefCell<dyn FnMut()>>) {
55        self.events
56            .insert(event_name, EventHandler::<Dom>::NoArgs(event));
57    }
58
59    // Used by the html! macro
60    #[doc(hidden)]
61    pub fn __insert_unsupported_signature(
62        &mut self,
63        event_name: EventName,
64        event: Dom::EventCallback,
65    ) {
66        self.events.insert(event_name, EventHandler::Custom(event));
67    }
68
69    /// Insert a mouse event handler.
70    pub fn insert_mouse_event(
71        &mut self,
72        event_name: EventName,
73        event: Rc<RefCell<dyn FnMut(Dom::MouseEvent)>>,
74    ) {
75        self.events
76            .insert(event_name, EventHandler::MouseEvent(event));
77    }
78
79    /// Removes the element's events and returns them.
80    pub fn take_events(&mut self) -> Drain<'_, EventName, EventHandler<Dom>> {
81        self.events.drain()
82    }
83
84    /// Wrap the events in the given closure.
85    pub fn convert_all<New: RealDom>(
86        mut self,
87        convert: impl Fn(EventHandler<Dom>) -> EventHandler<New>,
88    ) -> Events<New> {
89        let mut new_events = HashMap::with_capacity(self.events.len());
90
91        for (event_name, before) in self.events.drain() {
92            let after = convert(before);
93            new_events.insert(event_name, after);
94        }
95
96        Events { events: new_events }
97    }
98}
99
100impl<Dom: RealDom> Events<Dom> {
101    /// Create a new Events.
102    pub fn new() -> Self {
103        Events {
104            events: HashMap::new(),
105        }
106    }
107}
108
109/// In some applications, [`VirtualNode`] get converted into real DOM nodes.
110///
111/// This trait contains types and methods for manipulating a real DOM.
112///
113/// When running a client-side web application, consider using [`web_sys::Window`] as the
114/// [`RealDom`].
115/// When running on a server, consider using the null `()` type as the [`RealDom`].
116///
117/// To control how a [`VirtualNode`] gets rendered to a DOM element, implement [`RealDom`] for your
118/// own custom type.
119///
120/// [`VirtualNode`]: crate::VirtualNode
121pub trait RealDom {
122    /// The event type. In the web this is [`web_sys::Event`].
123    type Event;
124    /// The event type for mouse events. In the web this is [`web_sys::MouseEvent`].
125    type MouseEvent;
126    /// The type for callbacks such as `|some_event| { ... }`.
127    type EventCallback: Clone;
128}
129
130/// An [`RealDom`] implementation that uses [`web_sys`]'s event types.
131#[cfg(feature = "web")]
132impl RealDom for web_sys::Window {
133    type Event = web_sys::Event;
134    type MouseEvent = crate::event::MouseEventWebSys;
135    type EventCallback = Rc<dyn AsRef<wasm_bindgen::JsValue>>;
136}
137
138impl RealDom for () {
139    type Event = ();
140    type MouseEvent = ();
141    type EventCallback = ();
142}
143
144#[cfg(feature = "web")]
145impl EventAttribFn {
146    /// Currently used by `crates/percy-dom`'s test suite.
147    #[doc(hidden)]
148    pub fn new_noop() -> EventAttribFn {
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}