leptos_use/core/
element_maybe_signal.rs

1use leptos::prelude::*;
2use leptos::reactive::wrappers::read::Signal;
3use std::ops::Deref;
4
5/// Used as an argument type to make it easily possible to pass either
6///
7/// * a `web_sys` element that implements `E` (for example `EventTarget`, `Element` or `HtmlElement`),
8/// * an `Option<T>` where `T` is the web_sys element,
9/// * a `Signal<T>` where `T` is the web_sys element,
10/// * a `Signal<Option<T>>` where `T` is the web_sys element,
11/// * a `NodeRef`
12///
13/// into a function. Used for example in [`fn@crate::use_event_listener`].
14#[cfg_attr(not(debug_assertions), repr(transparent))]
15pub struct ElementMaybeSignal<T: 'static> {
16    #[cfg(debug_assertions)]
17    defined_at: &'static std::panic::Location<'static>,
18    inner: ElementMaybeSignalType<T>,
19}
20
21impl<T> Clone for ElementMaybeSignal<T> {
22    fn clone(&self) -> Self {
23        *self
24    }
25}
26
27impl<T> Copy for ElementMaybeSignal<T> {}
28
29pub enum ElementMaybeSignalType<T: 'static> {
30    Static(StoredValue<Option<T>, LocalStorage>),
31    Dynamic(Signal<Option<T>, LocalStorage>),
32}
33
34impl<T> Clone for ElementMaybeSignalType<T> {
35    fn clone(&self) -> Self {
36        *self
37    }
38}
39
40impl<T> Copy for ElementMaybeSignalType<T> {}
41
42impl<T: 'static> Default for ElementMaybeSignalType<T> {
43    fn default() -> Self {
44        Self::Static(StoredValue::new_local(None))
45    }
46}
47
48impl<T> Default for ElementMaybeSignal<T> {
49    fn default() -> Self {
50        Self {
51            inner: ElementMaybeSignalType::default(),
52            #[cfg(debug_assertions)]
53            defined_at: std::panic::Location::caller(),
54        }
55    }
56}
57
58impl<T> DefinedAt for ElementMaybeSignal<T> {
59    fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
60        #[cfg(debug_assertions)]
61        {
62            Some(self.defined_at)
63        }
64        #[cfg(not(debug_assertions))]
65        {
66            None
67        }
68    }
69}
70
71impl<T> With for ElementMaybeSignal<T> {
72    type Value = Option<T>;
73
74    fn try_with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
75        match &self.inner {
76            ElementMaybeSignalType::Static(v) => v.try_with_value(f),
77            ElementMaybeSignalType::Dynamic(s) => s.try_with(f),
78        }
79    }
80}
81
82impl<T> WithUntracked for ElementMaybeSignal<T> {
83    type Value = Option<T>;
84
85    fn try_with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
86        match &self.inner {
87            ElementMaybeSignalType::Static(t) => t.try_with_value(f),
88            ElementMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
89        }
90    }
91}
92
93pub trait IntoElementMaybeSignal<T, Marker: ?Sized> {
94    fn into_element_maybe_signal(self) -> ElementMaybeSignal<T>;
95}
96
97impl<El, T, Marker: ?Sized> IntoElementMaybeSignal<T, Marker> for El
98where
99    El: IntoElementMaybeSignalType<T, Marker>,
100{
101    fn into_element_maybe_signal(self) -> ElementMaybeSignal<T> {
102        ElementMaybeSignal {
103            inner: self.into_element_maybe_signal_type(),
104            #[cfg(debug_assertions)]
105            defined_at: std::panic::Location::caller(),
106        }
107    }
108}
109
110pub trait IntoElementMaybeSignalType<T, Marker: ?Sized> {
111    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T>;
112}
113
114// From static element //////////////////////////////////////////////////////////////
115
116/// Handles `window()` or `document()`
117impl<T, Js> IntoElementMaybeSignalType<T, web_sys::Element> for Js
118where
119    T: From<Js> + Clone,
120{
121    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
122        ElementMaybeSignalType::Static(StoredValue::new_local(Some(T::from(self).clone())))
123    }
124}
125
126/// Handles `window().body()`
127impl<T, Js> IntoElementMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
128where
129    T: From<Js> + Clone,
130{
131    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
132        ElementMaybeSignalType::Static(StoredValue::new_local(self.map(|el| T::from(el).clone())))
133    }
134}
135
136/// Handles `use_window()` or `use_document()`
137impl<T, E, Js> IntoElementMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
138where
139    Js: Deref<Target = Option<E>>,
140    E: Clone,
141    T: From<E> + Clone,
142{
143    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
144        ElementMaybeSignalType::Static(StoredValue::new_local(
145            self.as_ref().map(|e| T::from(e.clone())),
146        ))
147    }
148}
149
150// From string (selector) ///////////////////////////////////////////////////////////////
151
152/// Handles `"body"` or `"#app"`
153impl<T, V> IntoElementMaybeSignalType<T, str> for V
154where
155    V: AsRef<str>,
156    T: From<web_sys::Element> + Clone,
157{
158    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
159        if cfg!(feature = "ssr") {
160            ElementMaybeSignalType::Static(StoredValue::new_local(None))
161        } else {
162            ElementMaybeSignalType::Static(StoredValue::new_local(
163                document()
164                    .query_selector(self.as_ref())
165                    .unwrap_or_default()
166                    .map(|el| T::from(el).clone()),
167            ))
168        }
169    }
170}
171
172pub struct SignalStrMarker;
173
174/// Handles `Signal<&str>`
175impl<T, V, I> IntoElementMaybeSignalType<T, SignalStrMarker> for V
176where
177    V: Get<Value = I> + 'static,
178    I: AsRef<str>,
179    T: From<web_sys::Element> + Clone,
180{
181    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
182        if cfg!(feature = "ssr") {
183            ElementMaybeSignalType::Static(StoredValue::new_local(None))
184        } else {
185            ElementMaybeSignalType::Dynamic(Signal::derive_local(move || {
186                document()
187                    .query_selector(self.get().as_ref())
188                    .unwrap_or_default()
189                    .map(|el| T::from(el).clone())
190            }))
191        }
192    }
193}
194
195// From signal ///////////////////////////////////////////////////////////////
196
197pub struct SignalMarker;
198
199/// Handles `Signal<web_sys::*>`
200impl<T, V, E> IntoElementMaybeSignalType<T, SignalMarker> for V
201where
202    V: Get<Value = E> + 'static,
203    T: From<E> + Clone,
204{
205    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
206        ElementMaybeSignalType::Dynamic(Signal::derive_local(move || Some(T::from(self.get()))))
207    }
208}
209
210pub struct OptionSignalMarker;
211
212/// Handles `Signal<Option<web_sys::*>>` and `NodeRef` and `ElementMaybeSignal`
213impl<T, V, E> IntoElementMaybeSignalType<T, OptionSignalMarker> for V
214where
215    V: Get<Value = Option<E>> + 'static,
216    T: From<E> + Clone,
217{
218    fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
219        ElementMaybeSignalType::Dynamic(Signal::derive_local(move || self.get().map(T::from)))
220    }
221}