diff --git a/tachys/src/html/event.rs b/tachys/src/html/event.rs index 81e091be7..d5b644aa7 100644 --- a/tachys/src/html/event.rs +++ b/tachys/src/html/event.rs @@ -344,8 +344,85 @@ pub trait EventDescriptor: Clone { /// Return the options for this type. This is only used when you create a [`Custom`] event /// handler. #[inline(always)] - fn options(&self) -> &Option { - &None + fn options(&self) -> Option<&web_sys::AddEventListenerOptions> { + None + } +} + +/// A custom event. +#[derive(Debug)] +pub struct Custom { + name: Cow<'static, str>, + options: Option>, + _event_type: PhantomData E>, +} + +impl Clone for Custom { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + options: self.options.clone(), + _event_type: PhantomData, + } + } +} + +impl EventDescriptor for Custom { + type EventType = E; + + fn name(&self) -> Cow<'static, str> { + self.name.clone() + } + + fn event_delegation_key(&self) -> Cow<'static, str> { + format!("$$${}", self.name).into() + } + + const BUBBLES: bool = false; + + #[inline(always)] + fn options(&self) -> Option<&web_sys::AddEventListenerOptions> { + self.options.as_deref() + } +} + +impl Custom { + /// Creates a custom event type that can be used within + /// [`OnAttribute::on`](crate::prelude::OnAttribute::on), for events + /// which are not covered in the [`ev`](crate::html::event) module. + pub fn new(name: impl Into>) -> Self { + Self { + name: name.into(), + options: None, + _event_type: PhantomData, + } + } + + /// Modify the [`AddEventListenerOptions`] used for this event listener. + /// + /// ```rust + /// # use tachys::prelude::*; + /// # use tachys::html; + /// # use tachys::html::event as ev; + /// # fn custom_event() -> impl Render { + /// let mut non_passive_wheel = ev::Custom::new("wheel"); + /// non_passive_wheel.options_mut().passive(false); + /// + /// let canvas = + /// html::element::canvas().on(non_passive_wheel, |e: ev::WheelEvent| { + /// // handle event + /// }); + /// # canvas + /// # } + /// ``` + /// + /// [`AddEventListenerOptions`]: web_sys::AddEventListenerOptions + pub fn options_mut(&mut self) -> &mut web_sys::AddEventListenerOptions { + // It is valid to construct a `SendWrapper` here because + // its inner data will only be accessed in the browser's main thread. + self.options.get_or_insert_with(|| { + SendWrapper::new(web_sys::AddEventListenerOptions::new()) + }) } }