leptos_use/core/
elements_maybe_signal.rs

1use crate::core::{SignalStrMarker, StrMarker};
2use leptos::prelude::{guards::ReadGuard, *};
3use send_wrapper::SendWrapper;
4use std::{ops::Deref, rc::Rc, time::Duration};
5use wasm_bindgen::JsCast;
6
7use crate::{
8    UseMutationObserverOptions, UseMutationObserverReturn, use_mutation_observer_with_options,
9};
10
11/// Used as an argument type to make it easily possible to pass either
12///
13/// * a `web_sys` element that implements `E` (for example `EventTarget` or `Element`),
14/// * an `Option<T>` where `T` is the web_sys element,
15/// * a `Signal<T>` where `T` is the web_sys element,
16/// * a `Signal<Option<T>>` where `T` is the web_sys element,
17/// * a `NodeRef`
18///
19/// into a function. Used for example in [`fn@crate::use_event_listener`].
20#[cfg_attr(not(debug_assertions), repr(transparent))]
21pub struct ElementsMaybeSignal<T: 'static> {
22    #[cfg(debug_assertions)]
23    defined_at: &'static std::panic::Location<'static>,
24    inner: ElementsMaybeSignalType<T>,
25}
26
27impl<T> Clone for ElementsMaybeSignal<T> {
28    fn clone(&self) -> Self {
29        *self
30    }
31}
32
33impl<T> Copy for ElementsMaybeSignal<T> {}
34
35pub struct ElementsMaybeSignalType<T: 'static>(Signal<Vec<Option<SendWrapper<T>>>>);
36
37impl<T> Clone for ElementsMaybeSignalType<T> {
38    fn clone(&self) -> Self {
39        *self
40    }
41}
42
43impl<T> Copy for ElementsMaybeSignalType<T> {}
44
45impl<T: 'static> Default for ElementsMaybeSignalType<T> {
46    fn default() -> Self {
47        Self(Signal::stored(vec![]))
48    }
49}
50
51impl<T> Default for ElementsMaybeSignal<T> {
52    fn default() -> Self {
53        Self {
54            inner: ElementsMaybeSignalType::default(),
55            #[cfg(debug_assertions)]
56            defined_at: std::panic::Location::caller(),
57        }
58    }
59}
60
61impl<T> DefinedAt for ElementsMaybeSignal<T> {
62    fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
63        #[cfg(debug_assertions)]
64        {
65            Some(self.defined_at)
66        }
67        #[cfg(not(debug_assertions))]
68        {
69            None
70        }
71    }
72}
73
74impl<T> ReadUntracked for ElementsMaybeSignal<T> {
75    type Value = ReadGuard<
76        Vec<Option<SendWrapper<T>>>,
77        SignalReadGuard<Vec<Option<SendWrapper<T>>>, SyncStorage>,
78    >;
79
80    fn try_read_untracked(&self) -> Option<Self::Value> {
81        self.inner.0.try_read_untracked()
82    }
83}
84
85impl<T> Track for ElementsMaybeSignal<T> {
86    fn track(&self) {
87        self.inner.0.track();
88    }
89}
90
91pub trait IntoElementsMaybeSignal<T, Marker> {
92    fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T>;
93}
94
95impl<El, T, Marker> IntoElementsMaybeSignal<T, Marker> for El
96where
97    El: IntoElementsMaybeSignalType<T, Marker>,
98{
99    fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T> {
100        ElementsMaybeSignal {
101            inner: self.into_elements_maybe_signal_type(),
102            #[cfg(debug_assertions)]
103            defined_at: std::panic::Location::caller(),
104        }
105    }
106}
107
108pub trait IntoElementsMaybeSignalType<T, Marker> {
109    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T>;
110}
111
112// From single static element //////////////////////////////////////////////////////////////
113
114/// Handles `window()` or `document()`
115impl<T, Js> IntoElementsMaybeSignalType<T, web_sys::Element> for Js
116where
117    T: From<Js> + Clone,
118{
119    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
120        ElementsMaybeSignalType(Signal::stored(vec![Some(SendWrapper::new(
121            T::from(self).clone(),
122        ))]))
123    }
124}
125
126/// Handles `window().body()`
127impl<T, Js> IntoElementsMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
128where
129    T: From<Js> + Clone,
130{
131    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
132        ElementsMaybeSignalType(Signal::stored(vec![
133            self.map(|el| SendWrapper::new(T::from(el).clone())),
134        ]))
135    }
136}
137
138/// Handles `use_window()` or `use_document()`
139impl<T, E, Js> IntoElementsMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
140where
141    Js: Deref<Target = Option<E>>,
142    E: Clone,
143    T: From<E> + Clone,
144{
145    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
146        ElementsMaybeSignalType(Signal::stored(vec![
147            self.as_ref().map(|e| SendWrapper::new(T::from(e.clone()))),
148        ]))
149    }
150}
151
152// From string (selector) ///////////////////////////////////////////////////////////////
153
154/// Handles `"body"` or `"#app"`
155impl<T, V> IntoElementsMaybeSignalType<T, StrMarker> for V
156where
157    V: AsRef<str>,
158    T: From<web_sys::Element> + Clone,
159{
160    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
161        if cfg!(feature = "ssr") {
162            ElementsMaybeSignalType(Signal::stored(vec![]))
163        } else {
164            ElementsMaybeSignalType(els_signal_by_sel::<T>(self.as_ref()))
165        }
166    }
167}
168
169/// Handles `Signal<&str>`
170impl<T, V, I> IntoElementsMaybeSignalType<T, SignalStrMarker> for V
171where
172    V: Get<Value = I> + Send + Sync + 'static,
173    I: AsRef<str>,
174    T: From<web_sys::Element> + Clone,
175{
176    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
177        if cfg!(feature = "ssr") {
178            ElementsMaybeSignalType(Signal::stored(vec![]))
179        } else {
180            ElementsMaybeSignalType(Signal::derive(move || {
181                vec![
182                    document()
183                        .query_selector(self.get().as_ref())
184                        .unwrap_or_default()
185                        .map(|el| SendWrapper::new(T::from(el).clone())),
186                ]
187            }))
188        }
189    }
190}
191
192// From multiple static elements //////////////////////////////////////////////////////
193
194pub struct ElementMarker;
195
196/// Handles `&[web_sys::*]`
197impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, ElementMarker> for C
198where
199    Js: Clone + 'a,
200    T: From<Js>,
201    C: IntoIterator<Item = &'a Js>,
202{
203    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
204        ElementsMaybeSignalType(Signal::stored(
205            self.into_iter()
206                .map(|t| Some(SendWrapper::new(T::from(t.clone()))))
207                .collect(),
208        ))
209    }
210}
211
212/// Handles `&[Option<web_sys::*>]`
213impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [Option<web_sys::Element>]> for C
214where
215    Js: Clone + 'a,
216    T: From<Js>,
217    C: IntoIterator<Item = &'a Option<Js>>,
218{
219    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
220        ElementsMaybeSignalType(Signal::stored(
221            self.into_iter()
222                .map(|t| t.clone().map(|js| SendWrapper::new(T::from(js))))
223                .collect(),
224        ))
225    }
226}
227
228/// Handles `Vec<web_sys::*>`
229impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<web_sys::Element>> for C
230where
231    T: From<Js> + Clone,
232    C: IntoIterator<Item = Js>,
233{
234    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
235        ElementsMaybeSignalType(Signal::stored(
236            self.into_iter()
237                .map(|t| Some(SendWrapper::new(T::from(t))))
238                .collect(),
239        ))
240    }
241}
242
243/// Handles `Vec<Option<web_sys::*>>`
244impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<Option<web_sys::Element>>> for C
245where
246    T: From<Js> + Clone,
247    C: IntoIterator<Item = Option<Js>>,
248{
249    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
250        ElementsMaybeSignalType(Signal::stored(
251            self.into_iter()
252                .map(|t| t.map(|js| SendWrapper::new(T::from(js))))
253                .collect(),
254        ))
255    }
256}
257
258// From multiple strings //////////////////////////////////////////////////////
259
260pub struct StrIterMarker;
261
262pub fn els_by_sel<T>(sel: &str) -> Vec<Option<SendWrapper<T>>>
263where
264    T: From<web_sys::Element> + Clone,
265{
266    let mut els: Vec<web_sys::Element> = Vec::new();
267
268    if let Ok(queried_els) = document().query_selector_all(sel.as_ref()) {
269        for i in 0..queried_els.length() {
270            if let Ok(el) = queried_els.get(i).expect("checked length").dyn_into() {
271                els.push(el);
272            }
273        }
274    }
275    els.into_iter()
276        .map(|v| Some(SendWrapper::new(T::from(v))))
277        .collect()
278}
279
280pub fn els_signal_by_sel<T>(sel: &str) -> Signal<Vec<Option<SendWrapper<T>>>>
281where
282    T: From<web_sys::Element> + Clone + 'static,
283{
284    let (el_signal, set_el_signal) = signal(Vec::new());
285
286    let sel = sel.to_string();
287
288    set_timeout(
289        move || {
290            let els = els_by_sel::<T>(&sel);
291            if !els.is_empty() {
292                set_el_signal.set(els);
293            } else {
294                let stop_observer = StoredValue::new_local(Rc::new(|| {}) as Rc<dyn Fn()>);
295
296                let UseMutationObserverReturn { stop, .. } = use_mutation_observer_with_options(
297                    document().body().unwrap(),
298                    move |_, _| {
299                        let els = els_by_sel(&sel);
300                        if !els.is_empty() {
301                            set_el_signal.set(els);
302                            stop_observer.get_value()();
303                        } else {
304                            set_el_signal.set(Vec::new());
305                        }
306                    },
307                    UseMutationObserverOptions::default()
308                        .child_list(true)
309                        .subtree(true),
310                );
311
312                stop_observer.set_value(Rc::new(stop));
313            }
314        },
315        Duration::ZERO,
316    );
317
318    el_signal.into()
319}
320
321/// Handles `["body", "#app"]`
322impl<T, V, C> IntoElementsMaybeSignalType<T, StrIterMarker> for C
323where
324    V: AsRef<str>,
325    T: From<web_sys::Element> + Clone,
326    C: IntoIterator<Item = V>,
327{
328    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
329        if cfg!(feature = "ssr") {
330            ElementsMaybeSignalType(Signal::stored(vec![]))
331        } else {
332            ElementsMaybeSignalType(els_signal_by_sel::<T>(
333                &self
334                    .into_iter()
335                    .map(|sel| sel.as_ref().to_string())
336                    .collect::<Vec<_>>()
337                    .join(","),
338            ))
339        }
340    }
341}
342
343// From signal of multiple ////////////////////////////////////////////////////////////////
344
345pub struct SignalVecMarker;
346
347/// Handles `Signal<Vec<web_sys::*>>` and `Signal<Option<web_sys::*>>` and `NodeRef`
348impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecMarker> for G
349where
350    T: From<Js> + Clone,
351    G: Get<Value = C> + Send + Sync + 'static,
352    C: IntoIterator<Item = Js>,
353{
354    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
355        ElementsMaybeSignalType(Signal::derive(move || {
356            self.get()
357                .into_iter()
358                .map(|t| Some(SendWrapper::new(T::from(t))))
359                .collect()
360        }))
361    }
362}
363
364pub struct SignalVecSendWrapperMarker;
365
366/// Handles `Signal<Vec<SendWrapper<web_sys::*>>>` and `Signal<Option<SendWrapper<web_sys::*>>>` and `ElementMaybeSignal`
367impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecSendWrapperMarker> for G
368where
369    T: From<Js> + Clone,
370    G: Get<Value = C> + Send + Sync + 'static,
371    C: IntoIterator<Item = SendWrapper<Js>>,
372{
373    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
374        ElementsMaybeSignalType(Signal::derive(move || {
375            self.get()
376                .into_iter()
377                .map(|t| Some(SendWrapper::new(T::from(t.take()))))
378                .collect()
379        }))
380    }
381}
382
383pub struct SignalVecOptionMarker;
384
385/// Handles `Signal<Vec<Option<web_sys::*>>>`
386impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecOptionMarker> for G
387where
388    T: From<Js> + Clone,
389    G: Get<Value = C> + Send + Sync + 'static,
390    C: IntoIterator<Item = Option<Js>>,
391{
392    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
393        ElementsMaybeSignalType(Signal::derive(move || {
394            self.get()
395                .into_iter()
396                .map(|t| t.map(|js| SendWrapper::new(T::from(js))))
397                .collect()
398        }))
399    }
400}
401
402// From multiple signals //////////////////////////////////////////////////////////////
403
404pub struct VecSignalMarker;
405
406/// Handles `Vec<Signal<web_sys::*>>`
407impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalMarker> for C
408where
409    T: From<Js> + Clone,
410    C: IntoIterator<Item = G> + Clone + Send + Sync + 'static,
411    G: Get<Value = Js>,
412{
413    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
414        let signals = self.clone();
415
416        ElementsMaybeSignalType(Signal::derive(move || {
417            signals
418                .clone()
419                .into_iter()
420                .map(|t| Some(SendWrapper::new(T::from(t.get()))))
421                .collect()
422        }))
423    }
424}
425
426pub struct VecSignalOptionMarker;
427
428/// Handles `Vec<Signal<Option<web_sys::*>>>`, `Vec<NodeRef>`
429impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalOptionMarker> for C
430where
431    T: From<Js> + Clone,
432    C: IntoIterator<Item = G> + Clone + Send + Sync + 'static,
433    G: Get<Value = Option<Js>>,
434{
435    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
436        let signals = self.clone();
437
438        ElementsMaybeSignalType(Signal::derive(move || {
439            signals
440                .clone()
441                .into_iter()
442                .map(|t| t.get().map(|js| SendWrapper::new(T::from(js))))
443                .collect()
444        }))
445    }
446}
447
448// handles Vec<Signal<Vec<Option<web_sys::*>>>
449pub struct VecSignalVecOptionMarker;
450
451impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalVecOptionMarker> for C
452where
453    T: From<Js> + Clone,
454    C: IntoIterator<Item = G> + Clone + Send + Sync + 'static,
455    G: Get<Value = Vec<Option<Js>>>,
456{
457    fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
458        let signals = self.clone();
459
460        ElementsMaybeSignalType(Signal::derive(move || {
461            signals
462                .clone()
463                .into_iter()
464                .flat_map(|t| t.get())
465                .map(|j| j.map(|j| SendWrapper::new(T::from(j))))
466                .collect()
467        }))
468    }
469}