leptos_use/core/
elements_maybe_signal.rs

1use crate::core::{SignalMarker, SignalStrMarker};
2use leptos::prelude::*;
3use leptos::reactive::wrappers::read::Signal;
4use std::ops::Deref;
5
6/// Used as an argument type to make it easily possible to pass either
7///
8/// * a `web_sys` element that implements `E` (for example `EventTarget` or `Element`),
9/// * an `Option<T>` where `T` is the web_sys element,
10/// * a `Signal<T>` where `T` is the web_sys element,
11/// * a `Signal<Option<T>>` where `T` is the web_sys element,
12/// * a `NodeRef`
13///
14/// into a function. Used for example in [`fn@crate::use_event_listener`].
15#[cfg_attr(not(debug_assertions), repr(transparent))]
16pub struct ElementsMaybeSignal<T: 'static> {
17    #[cfg(debug_assertions)]
18    defined_at: &'static std::panic::Location<'static>,
19    inner: ElementsMaybeSignalType<T>,
20}
21
22impl<T> Clone for ElementsMaybeSignal<T> {
23    fn clone(&self) -> Self {
24        *self
25    }
26}
27
28impl<T> Copy for ElementsMaybeSignal<T> {}
29
30pub enum ElementsMaybeSignalType<T: 'static> {
31    Static(StoredValue<Vec<Option<T>>, LocalStorage>),
32    Dynamic(Signal<Vec<Option<T>>, LocalStorage>),
33}
34
35impl<T> Clone for ElementsMaybeSignalType<T> {
36    fn clone(&self) -> Self {
37        *self
38    }
39}
40
41impl<T> Copy for ElementsMaybeSignalType<T> {}
42
43impl<T: 'static> Default for ElementsMaybeSignalType<T> {
44    fn default() -> Self {
45        Self::Static(StoredValue::new_local(vec![]))
46    }
47}
48
49impl<T> Default for ElementsMaybeSignal<T> {
50    fn default() -> Self {
51        Self {
52            inner: ElementsMaybeSignalType::default(),
53            #[cfg(debug_assertions)]
54            defined_at: std::panic::Location::caller(),
55        }
56    }
57}
58
59impl<T> DefinedAt for ElementsMaybeSignal<T> {
60    fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
61        #[cfg(debug_assertions)]
62        {
63            Some(self.defined_at)
64        }
65        #[cfg(not(debug_assertions))]
66        {
67            None
68        }
69    }
70}
71
72impl<T> With for ElementsMaybeSignal<T> {
73    type Value = Vec<Option<T>>;
74
75    fn try_with<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
76        match &self.inner {
77            ElementsMaybeSignalType::Static(v) => v.try_with_value(f),
78            ElementsMaybeSignalType::Dynamic(s) => s.try_with(f),
79        }
80    }
81}
82
83impl<T> WithUntracked for ElementsMaybeSignal<T> {
84    type Value = Vec<Option<T>>;
85
86    fn try_with_untracked<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
87        match self.inner {
88            ElementsMaybeSignalType::Static(t) => t.try_with_value(f),
89            ElementsMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
90        }
91    }
92}
93
94pub trait IntoElementsMaybeSignal<T, Marker: ?Sized> {
95    fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T>;
96}
97
98impl<El, T, Marker: ?Sized> IntoElementsMaybeSignal<T, Marker> for El
99where
100    El: IntoElementsMaybeSignalType<T, Marker>,
101{
102    fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T> {
103        ElementsMaybeSignal {
104            inner: self.into_elements_maybe_signal_type(),
105            #[cfg(debug_assertions)]
106            defined_at: std::panic::Location::caller(),
107        }
108    }
109}
110
111pub trait IntoElementsMaybeSignalType<T, Marker: ?Sized> {
112    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T>;
113}
114
115// From single static element //////////////////////////////////////////////////////////////
116
117/// Handles `window()` or `document()`
118impl<T, Js> IntoElementsMaybeSignalType<T, web_sys::Element> for Js
119where
120    T: From<Js> + Clone,
121{
122    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
123        ElementsMaybeSignalType::Static(StoredValue::new_local(vec![Some(T::from(self).clone())]))
124    }
125}
126
127/// Handles `window().body()`
128impl<T, Js> IntoElementsMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
129where
130    T: From<Js> + Clone,
131{
132    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
133        ElementsMaybeSignalType::Static(StoredValue::new_local(vec![
134            self.map(|el| T::from(el).clone())
135        ]))
136    }
137}
138
139/// Handles `use_window()` or `use_document()`
140impl<T, E, Js> IntoElementsMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
141where
142    Js: Deref<Target = Option<E>>,
143    E: Clone,
144    T: From<E> + Clone,
145{
146    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
147        ElementsMaybeSignalType::Static(StoredValue::new_local(vec![self
148            .as_ref()
149            .map(|e| T::from(e.clone()))]))
150    }
151}
152
153// From string (selector) ///////////////////////////////////////////////////////////////
154
155/// Handles `"body"` or `"#app"`
156impl<T, V> IntoElementsMaybeSignalType<T, str> for V
157where
158    V: AsRef<str>,
159    T: From<web_sys::Element> + Clone,
160{
161    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
162        if cfg!(feature = "ssr") {
163            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
164        } else {
165            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![document()
166                .query_selector(self.as_ref())
167                .unwrap_or_default()
168                .map(|el| T::from(el).clone())]))
169        }
170    }
171}
172
173/// Handles `Signal<&str>`
174impl<T, V, I> IntoElementsMaybeSignalType<T, SignalStrMarker> for V
175where
176    V: Get<Value = I> + 'static,
177    I: AsRef<str>,
178    T: From<web_sys::Element> + Clone,
179{
180    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
181        if cfg!(feature = "ssr") {
182            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
183        } else {
184            ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
185                vec![document()
186                    .query_selector(self.get().as_ref())
187                    .unwrap_or_default()
188                    .map(|el| T::from(el).clone())]
189            }))
190        }
191    }
192}
193
194// From single signal ///////////////////////////////////////////////////////////////
195
196/// Handles `Signal<web_sys::*>`
197impl<T, V, E> IntoElementsMaybeSignalType<T, SignalMarker> for V
198where
199    V: Get<Value = E> + 'static,
200    T: From<E> + Clone,
201{
202    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
203        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
204            vec![Some(T::from(self.get()))]
205        }))
206    }
207}
208
209// From multiple static elements //////////////////////////////////////////////////////
210
211/// Handles `&[web_sys::*]`
212impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [web_sys::Element]> for C
213where
214    Js: Clone + 'a,
215    T: From<Js>,
216    C: IntoIterator<Item = &'a Js>,
217{
218    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
219        ElementsMaybeSignalType::Static(StoredValue::new_local(
220            self.into_iter().map(|t| Some(T::from(t.clone()))).collect(),
221        ))
222    }
223}
224
225/// Handles `&[Option<web_sys::*>]`
226impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [Option<web_sys::Element>]> for C
227where
228    Js: Clone + 'a,
229    T: From<Js>,
230    C: IntoIterator<Item = &'a Option<Js>>,
231{
232    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
233        ElementsMaybeSignalType::Static(StoredValue::new_local(
234            self.into_iter().map(|t| t.clone().map(T::from)).collect(),
235        ))
236    }
237}
238
239/// Handles `Vec<web_sys::*>`
240impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<web_sys::Element>> for C
241where
242    T: From<Js> + Clone,
243    C: IntoIterator<Item = Js>,
244{
245    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
246        ElementsMaybeSignalType::Static(StoredValue::new_local(
247            self.into_iter().map(|t| Some(T::from(t))).collect(),
248        ))
249    }
250}
251
252/// Handles `Vec<Option<web_sys::*>>`
253impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<Option<web_sys::Element>>> for C
254where
255    T: From<Js> + Clone,
256    C: IntoIterator<Item = Option<Js>>,
257{
258    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
259        ElementsMaybeSignalType::Static(StoredValue::new_local(
260            self.into_iter().map(|t| t.map(T::from)).collect(),
261        ))
262    }
263}
264
265// From multiple strings //////////////////////////////////////////////////////
266
267/// Handles `["body", "#app"]`
268impl<T, V, C> IntoElementsMaybeSignalType<T, &[&str]> for C
269where
270    V: AsRef<str>,
271    T: From<web_sys::Element> + Clone,
272    C: IntoIterator<Item = V>,
273{
274    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
275        if cfg!(feature = "ssr") {
276            ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
277        } else {
278            ElementsMaybeSignalType::Static(StoredValue::new_local(
279                self.into_iter()
280                    .map(|sel| {
281                        document()
282                            .query_selector(sel.as_ref())
283                            .unwrap_or_default()
284                            .map(|el| T::from(el).clone())
285                    })
286                    .collect(),
287            ))
288        }
289    }
290}
291
292// From signal of multiple ////////////////////////////////////////////////////////////////
293
294pub struct SignalVecMarker;
295
296/// Handles `Signal<Vec<web_sys::*>>` and `Signal<Option<web_sys::*>>` and `NodeRef` and `ElementMaybeSignal`
297impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecMarker> for G
298where
299    T: From<Js> + Clone,
300    G: Get<Value = C> + 'static,
301    C: IntoIterator<Item = Js>,
302{
303    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
304        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
305            self.get().into_iter().map(|t| Some(T::from(t))).collect()
306        }))
307    }
308}
309
310pub struct SignalVecOptionMarker;
311
312/// Handles `Signal<Vec<Option<web_sys::*>>>`
313impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecOptionMarker> for G
314where
315    T: From<Js> + Clone,
316    G: Get<Value = C> + 'static,
317    C: IntoIterator<Item = Option<Js>>,
318{
319    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
320        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
321            self.get().into_iter().map(|t| t.map(T::from)).collect()
322        }))
323    }
324}
325
326// From multiple signals //////////////////////////////////////////////////////////////
327
328pub struct VecSignalMarker;
329
330/// Handles `Vec<Signal<web_sys::*>>`
331impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalMarker> for C
332where
333    T: From<Js> + Clone,
334    C: IntoIterator<Item = G> + Clone + 'static,
335    G: Get<Value = Js>,
336{
337    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
338        let signals = self.clone();
339
340        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
341            signals
342                .clone()
343                .into_iter()
344                .map(|t| Some(T::from(t.get())))
345                .collect()
346        }))
347    }
348}
349
350pub struct VecSignalOptionMarker;
351
352/// Handles `Vec<Signal<Option<web_sys::*>>>`, `Vec<NodeRef>`
353impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalOptionMarker> for C
354where
355    T: From<Js> + Clone,
356    C: IntoIterator<Item = G> + Clone + 'static,
357    G: Get<Value = Option<Js>>,
358{
359    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
360        let signals = self.clone();
361
362        ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
363            signals
364                .clone()
365                .into_iter()
366                .map(|t| t.get().map(T::from))
367                .collect()
368        }))
369    }
370}