leptos_use/core/
element_maybe_signal.rs

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