use crate::{UseDocument, UseWindow}; use cfg_if::cfg_if; use leptos::html::{CreateElement, ElementType}; use leptos::prelude::*; use leptos::reactive_graph::wrappers::read::Signal; use send_wrapper::SendWrapper; use std::marker::PhantomData; use wasm_bindgen::JsCast; /// Used as an argument type to make it easily possible to pass either /// /// * a `web_sys` element that implements `E` (for example `EventTarget`, `Element` or `HtmlElement`), /// * an `Option` where `T` is the web_sys element, /// * a `Signal` where `T` is the web_sys element, /// * a `Signal>` where `T` is the web_sys element, /// * a `NodeRef` /// /// into a function. Used for example in [`fn@crate::use_event_listener`]. pub enum ElementMaybeSignal where T: Into + Clone + 'static, { Static(SendWrapper>), Dynamic(Signal, LocalStorage>), _Phantom(PhantomData E>), } impl Default for ElementMaybeSignal where T: Into + Clone + 'static, { fn default() -> Self { Self::Static(SendWrapper::new(None)) } } impl Clone for ElementMaybeSignal where T: Into + Clone + 'static, { fn clone(&self) -> Self { match self { Self::Static(t) => Self::Static(t.clone()), Self::Dynamic(s) => Self::Dynamic(*s), _ => unreachable!(), } } } impl DefinedAt for ElementMaybeSignal where T: Into + Clone + 'static, { fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> { None } } impl With for ElementMaybeSignal where T: Into + Clone + 'static, { type Value = Option; fn with(&self, f: impl FnOnce(&Option) -> O) -> O { match self { Self::Static(t) => f(t), Self::Dynamic(s) => { let value = s.get(); f(&value) } _ => unreachable!(), } } fn try_with(&self, f: impl FnOnce(&Option) -> O) -> Option { match self { Self::Static(t) => Some(f(t)), Self::Dynamic(s) => s.try_with(f), _ => unreachable!(), } } } impl WithUntracked for ElementMaybeSignal where T: Into + Clone + 'static, { type Value = Option; fn with_untracked(&self, f: impl FnOnce(&Option) -> O) -> O { match self { Self::Static(t) => f(t), Self::Dynamic(s) => s.with_untracked(f), _ => unreachable!(), } } fn try_with_untracked(&self, f: impl FnOnce(&Option) -> O) -> Option { match self { Self::Static(t) => Some(f(t)), Self::Dynamic(s) => s.try_with_untracked(f), _ => unreachable!(), } } } // From static element ////////////////////////////////////////////////////////////// impl From for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(value: T) -> Self { ElementMaybeSignal::Static(SendWrapper::new(Some(value))) } } impl From> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(target: Option) -> Self { ElementMaybeSignal::Static(SendWrapper::new(target)) } } macro_rules! impl_from_deref_option { ($ty:ty, $ty2:ty) => { impl From<$ty> for ElementMaybeSignal<$ty2, E> where E: From<$ty2> + 'static, { fn from(value: $ty) -> Self { Self::Static(SendWrapper::new((*value).clone())) } } }; } impl_from_deref_option!(UseWindow, web_sys::Window); impl_from_deref_option!(UseDocument, web_sys::Document); // From string (selector) /////////////////////////////////////////////////////////////// impl<'a, E> From<&'a str> for ElementMaybeSignal where E: From + 'static, { fn from(target: &'a str) -> Self { cfg_if! { if #[cfg(feature = "ssr")] { let _ = target; Self::Static(SendWrapper::new(None)) } else { Self::Static(SendWrapper::new(document().query_selector(target).unwrap_or_default())) }} } } impl From for ElementMaybeSignal where E: From + 'static, { fn from(target: String) -> Self { Self::from(target.as_str()) } } macro_rules! impl_from_signal_string { ($ty:ty) => { impl From<$ty> for ElementMaybeSignal where E: From + 'static, { fn from(signal: $ty) -> Self { cfg_if! { if #[cfg(feature = "ssr")] { let _ = signal; Self::Dynamic(Signal::derive_local(|| None)) } else { Self::Dynamic( Signal::derive_local(move || document().query_selector(&signal.get()).unwrap_or_default()), ) }} } } }; } impl_from_signal_string!(Signal); impl_from_signal_string!(ReadSignal); impl_from_signal_string!(RwSignal); impl_from_signal_string!(Memo); impl_from_signal_string!(Signal<&'static str>); impl_from_signal_string!(ReadSignal<&'static str>); impl_from_signal_string!(RwSignal<&'static str>); impl_from_signal_string!(Memo<&'static str>); // From signal /////////////////////////////////////////////////////////////// macro_rules! impl_from_signal_option { ($ty:ty) => { impl From<$ty> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(target: $ty) -> Self { Self::Dynamic(target.into()) } } }; } impl_from_signal_option!(Signal, LocalStorage>); impl_from_signal_option!(ReadSignal, LocalStorage>); impl_from_signal_option!(RwSignal, LocalStorage>); impl_from_signal_option!(Memo, LocalStorage>); macro_rules! impl_from_signal { ($ty:ty) => { impl From<$ty> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(signal: $ty) -> Self { Self::Dynamic(Signal::derive_local(move || Some(signal.get()))) } } }; } impl_from_signal!(Signal); impl_from_signal!(ReadSignal); impl_from_signal!(RwSignal); impl_from_signal!(Memo); // From NodeRef ////////////////////////////////////////////////////////////// macro_rules! impl_from_node_ref { ($ty:ty) => { impl From> for ElementMaybeSignal<$ty, $ty> where R: ElementType + CreateElement + Clone + Send + Sync + 'static, R::Output: JsCast + Into<$ty> + Clone + 'static, { fn from(node_ref: NodeRef) -> Self { Self::Dynamic(Signal::derive_local(move || { node_ref.get().map(move |el| { let el: $ty = el.clone().into(); el }) })) } } }; } impl_from_node_ref!(web_sys::EventTarget); impl_from_node_ref!(web_sys::Element); impl_from_node_ref!(web_sys::HtmlElement); // // From leptos::html::HTMLElement /////////////////////////////////////////////// // // macro_rules! impl_from_html_element { // ($ty:ty) => { // impl From> for ElementMaybeSignal<$ty, $ty> // where // HtmlEl: ElementDescriptor + std::ops::Deref, // { // fn from(value: HtmlElement) -> Self { // let el: &$ty = value.deref(); // Self::Static(Some(el.clone())) // } // } // }; // } // // impl_from_html_element!(web_sys::EventTarget); // impl_from_html_element!(web_sys::Element); // impl_from_html_element!(web_sys::HtmlElement); // // // From Signal ///////////////////////////////////////// // // macro_rules! impl_from_signal_html_element { // ($signal:ty, $ty:ty) => { // impl From<$signal> for ElementMaybeSignal<$ty, $ty> // where // HtmlEl: ElementDescriptor + std::ops::Deref + Clone, // { // fn from(value: $signal) -> Self { // Self::Dynamic(Signal::derive(move || { // let value = value.get(); // let el: &$ty = value.deref(); // Some(el.clone()) // })) // } // } // }; // } // // impl_from_signal_html_element!(Signal>, web_sys::EventTarget); // impl_from_signal_html_element!(ReadSignal>, web_sys::EventTarget); // impl_from_signal_html_element!(RwSignal>, web_sys::EventTarget); // impl_from_signal_html_element!(Memo>, web_sys::EventTarget); // // impl_from_signal_html_element!(Signal>, web_sys::Element); // impl_from_signal_html_element!(ReadSignal>, web_sys::Element); // impl_from_signal_html_element!(RwSignal>, web_sys::Element); // impl_from_signal_html_element!(Memo>, web_sys::Element); // // // From Signal> ///////////////////////////////////////// // // macro_rules! impl_from_signal_html_element { // ($signal:ty, $ty:ty) => { // impl From<$signal> for ElementMaybeSignal<$ty, $ty> // where // HtmlEl: ElementDescriptor + std::ops::Deref + Clone, // { // fn from(value: $signal) -> Self { // Self::Dynamic(Signal::derive(move || { // let el: Option<$ty> = value.get().map(|el| el.deref().clone()); // el // })) // } // } // }; // } // // impl_from_signal_html_element!(Signal>>, web_sys::EventTarget); // impl_from_signal_html_element!( // ReadSignal>>, // web_sys::EventTarget // ); // impl_from_signal_html_element!(RwSignal>>, web_sys::EventTarget); // impl_from_signal_html_element!(Memo>>, web_sys::EventTarget); // // impl_from_signal_html_element!(Signal>>, web_sys::Element); // impl_from_signal_html_element!(ReadSignal>>, web_sys::Element); // impl_from_signal_html_element!(RwSignal>>, web_sys::Element); // impl_from_signal_html_element!(Memo>>, web_sys::Element); // // impl_from_signal_html_element!(Signal>>, web_sys::HtmlElement); // impl_from_signal_html_element!( // ReadSignal>>, // web_sys::HtmlElement // ); // impl_from_signal_html_element!(RwSignal>>, web_sys::HtmlElement); // impl_from_signal_html_element!(Memo>>, web_sys::HtmlElement);