use leptos::html::ElementDescriptor; use leptos::*; use std::marker::PhantomData; use std::ops::Deref; /// Used as an argument type to make it easily possible to pass either /// * a `web_sys` element that implements `E` (for example `EventTarget` or `Element`), /// * 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 [`use_event_listener`]. pub enum ElementMaybeSignal where T: Into + Clone + 'static, { Static(Option), Dynamic(Signal>), _Phantom(PhantomData), } impl Default for ElementMaybeSignal where T: Into + Clone + 'static, { fn default() -> Self { Self::Static(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 SignalGet> for ElementMaybeSignal where T: Into + Clone + 'static, { fn get(&self) -> Option { match self { Self::Static(t) => t.clone(), Self::Dynamic(s) => s.get(), _ => unreachable!(), } } fn try_get(&self) -> Option> { match self { Self::Static(t) => Some(t.clone()), Self::Dynamic(s) => s.try_get(), _ => unreachable!(), } } } impl SignalWith> for ElementMaybeSignal where T: Into + Clone + 'static, { fn with(&self, f: impl FnOnce(&Option) -> O) -> O { match self { Self::Static(t) => f(t), Self::Dynamic(s) => s.with(f), _ => 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 SignalWithUntracked> for ElementMaybeSignal where T: Into + Clone + 'static, { 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!(), } } } impl SignalGetUntracked> for ElementMaybeSignal where T: Into + Clone + 'static, { fn get_untracked(&self) -> Option { match self { Self::Static(t) => t.clone(), Self::Dynamic(s) => s.get_untracked(), _ => unreachable!(), } } fn try_get_untracked(&self) -> Option> { match self { Self::Static(t) => Some(t.clone()), Self::Dynamic(s) => s.try_get_untracked(), _ => unreachable!(), } } } // From static element ////////////////////////////////////////////////////////////// impl From<(Scope, T)> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(value: (Scope, T)) -> Self { ElementMaybeSignal::Static(Some(value.1)) } } impl From<(Scope, Option)> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(target: (Scope, Option)) -> Self { ElementMaybeSignal::Static(target.1) } } // From string (selector) /////////////////////////////////////////////////////////////// impl<'a, E> From<(Scope, &'a str)> for ElementMaybeSignal where E: From + 'static, { fn from(target: (Scope, &'a str)) -> Self { Self::Static(document().query_selector(target.1).unwrap_or_default()) } } impl From<(Scope, Signal)> for ElementMaybeSignal where E: From + 'static, { fn from(target: (Scope, Signal)) -> Self { let (cx, signal) = target; Self::Dynamic( create_memo(cx, move |_| { document().query_selector(&signal.get()).unwrap_or_default() }) .into(), ) } } // From signal /////////////////////////////////////////////////////////////// macro_rules! impl_from_signal_option { ($ty:ty) => { impl From<(Scope, $ty)> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(target: (Scope, $ty)) -> Self { Self::Dynamic(target.1.into()) } } }; } impl_from_signal_option!(Signal>); impl_from_signal_option!(ReadSignal>); impl_from_signal_option!(RwSignal>); impl_from_signal_option!(Memo>); macro_rules! impl_from_signal { ($ty:ty) => { impl From<(Scope, $ty)> for ElementMaybeSignal where T: Into + Clone + 'static, { fn from(target: (Scope, $ty)) -> Self { let (cx, signal) = target; Self::Dynamic(Signal::derive(cx, 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<(Scope, NodeRef)> for ElementMaybeSignal<$ty, $ty> where R: ElementDescriptor + Clone + 'static, { fn from(target: (Scope, NodeRef)) -> Self { let (cx, node_ref) = target; Self::Dynamic(Signal::derive(cx, move || { node_ref.get().map(move |el| { let el = el.into_any(); let el: $ty = el.deref().clone().into(); el }) })) } } }; } impl_from_node_ref!(web_sys::EventTarget); impl_from_node_ref!(web_sys::Element);