use std::borrow::Cow;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::{AddEventListenerOptions, Event, EventTarget};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum EventPhase {
Bubble,
Capture,
}
impl EventPhase {
#[inline]
pub fn is_capture(&self) -> bool {
self == &EventPhase::Capture
}
#[inline]
pub fn is_bubble(&self) -> bool {
self == &EventPhase::Bubble
}
}
#[derive(Debug)]
pub struct Builder {
passive: bool,
phase: EventPhase,
}
impl Builder {
pub fn new() -> Self {
Self {
passive: true,
phase: EventPhase::Bubble,
}
}
pub fn phase(mut self, phase: EventPhase) -> Self {
self.phase = phase;
self
}
pub fn passive(mut self, passive: bool) -> Self {
self.passive = passive;
self
}
pub fn listen<S, F>(self, target: &EventTarget, event_type: S, callback: F) -> EventListener
where
S: Into<Cow<'static, str>>,
F: FnMut(&Event) + 'static,
{
let callback = Closure::wrap(Box::new(callback) as Box<dyn FnMut(&Event)>);
let event_type = event_type.into();
let mut options = AddEventListenerOptions::new();
options.once(false);
options.passive(self.passive);
options.capture(self.phase.is_capture());
target
.add_event_listener_with_callback_and_add_event_listener_options(
&event_type,
callback.as_ref().unchecked_ref(),
&options,
)
.unwrap_throw();
EventListener {
target: target.clone(),
event_type,
callback: Some(callback),
phase: self.phase,
}
}
pub fn listen_once<S, F>(target: &EventTarget, event_type: S, callback: F) -> EventListener
where
S: Into<Cow<'static, str>>,
F: FnOnce(&Event) + 'static,
{
let callback = Closure::once(callback);
let phase = EventPhase::Bubble;
let event_type = event_type.into();
let mut options = AddEventListenerOptions::new();
options.once(false);
options.passive(true);
target
.add_event_listener_with_callback_and_add_event_listener_options(
&event_type,
callback.as_ref().unchecked_ref(),
&options,
)
.unwrap_throw();
EventListener {
target: target.clone(),
event_type,
callback: Some(callback),
phase,
}
}
}
#[derive(Debug)]
#[must_use = "Event listener unsubscribes on drop"]
pub struct EventListener {
target: EventTarget,
event_type: Cow<'static, str>,
callback: Option<Closure<dyn FnMut(&Event)>>,
phase: EventPhase,
}
impl EventListener {
#[inline]
pub fn listen<S, F>(target: &EventTarget, event_type: S, callback: F) -> Self
where
S: Into<Cow<'static, str>>,
F: FnMut(&Event) + 'static,
{
Builder::new().listen(target, event_type, callback)
}
#[inline]
pub fn listen_once<S, F>(target: &EventTarget, event_type: S, callback: F) -> Self
where
S: Into<Cow<'static, str>>,
F: FnOnce(&Event) + 'static,
{
let callback = Closure::once(callback);
let phase = EventPhase::Bubble;
let event_type = event_type.into();
let mut options = AddEventListenerOptions::new();
options.once(false);
options.passive(true);
target
.add_event_listener_with_callback_and_add_event_listener_options(
&event_type,
callback.as_ref().unchecked_ref(),
&options,
)
.unwrap_throw();
Self {
target: target.clone(),
event_type,
callback: Some(callback),
phase,
}
}
#[inline]
pub fn forget(mut self) {
self.callback.take().unwrap_throw().forget()
}
#[inline]
pub fn target(&self) -> &EventTarget {
&self.target
}
#[inline]
pub fn event_type(&self) -> &str {
&self.event_type
}
#[inline]
pub fn phase(&self) -> EventPhase {
self.phase
}
}
impl Drop for EventListener {
#[inline]
fn drop(&mut self) {
if let Some(callback) = &self.callback {
self.target
.remove_event_listener_with_callback_and_bool(
self.event_type(),
callback.as_ref().unchecked_ref(),
self.phase.is_capture(),
)
.unwrap_throw();
}
}
}