1use crate::core::{SignalMarker, SignalStrMarker, StrMarker};
2use leptos::prelude::*;
3use leptos::reactive::wrappers::read::Signal;
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#[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 enum ElementsMaybeSignalType<T: 'static> {
36 Static(StoredValue<Vec<Option<T>>, LocalStorage>),
37 Dynamic(Signal<Vec<Option<T>>, LocalStorage>),
38}
39
40impl<T> Clone for ElementsMaybeSignalType<T> {
41 fn clone(&self) -> Self {
42 *self
43 }
44}
45
46impl<T> Copy for ElementsMaybeSignalType<T> {}
47
48impl<T: 'static> Default for ElementsMaybeSignalType<T> {
49 fn default() -> Self {
50 Self::Static(StoredValue::new_local(vec![]))
51 }
52}
53
54impl<T> Default for ElementsMaybeSignal<T> {
55 fn default() -> Self {
56 Self {
57 inner: ElementsMaybeSignalType::default(),
58 #[cfg(debug_assertions)]
59 defined_at: std::panic::Location::caller(),
60 }
61 }
62}
63
64impl<T> DefinedAt for ElementsMaybeSignal<T> {
65 fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
66 #[cfg(debug_assertions)]
67 {
68 Some(self.defined_at)
69 }
70 #[cfg(not(debug_assertions))]
71 {
72 None
73 }
74 }
75}
76
77impl<T> With for ElementsMaybeSignal<T> {
78 type Value = Vec<Option<T>>;
79
80 fn try_with<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
81 match &self.inner {
82 ElementsMaybeSignalType::Static(v) => v.try_with_value(f),
83 ElementsMaybeSignalType::Dynamic(s) => s.try_with(f),
84 }
85 }
86}
87
88impl<T> WithUntracked for ElementsMaybeSignal<T> {
89 type Value = Vec<Option<T>>;
90
91 fn try_with_untracked<O>(&self, f: impl FnOnce(&Vec<Option<T>>) -> O) -> Option<O> {
92 match self.inner {
93 ElementsMaybeSignalType::Static(t) => t.try_with_value(f),
94 ElementsMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
95 }
96 }
97}
98
99pub trait IntoElementsMaybeSignal<T, Marker> {
100 fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T>;
101}
102
103impl<El, T, Marker> IntoElementsMaybeSignal<T, Marker> for El
104where
105 El: IntoElementsMaybeSignalType<T, Marker>,
106{
107 fn into_elements_maybe_signal(self) -> ElementsMaybeSignal<T> {
108 ElementsMaybeSignal {
109 inner: self.into_elements_maybe_signal_type(),
110 #[cfg(debug_assertions)]
111 defined_at: std::panic::Location::caller(),
112 }
113 }
114}
115
116pub trait IntoElementsMaybeSignalType<T, Marker> {
117 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T>;
118}
119
120impl<T, Js> IntoElementsMaybeSignalType<T, web_sys::Element> for Js
124where
125 T: From<Js> + Clone,
126{
127 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
128 ElementsMaybeSignalType::Static(StoredValue::new_local(vec![Some(T::from(self).clone())]))
129 }
130}
131
132impl<T, Js> IntoElementsMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
134where
135 T: From<Js> + Clone,
136{
137 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
138 ElementsMaybeSignalType::Static(StoredValue::new_local(vec![
139 self.map(|el| T::from(el).clone()),
140 ]))
141 }
142}
143
144impl<T, E, Js> IntoElementsMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
146where
147 Js: Deref<Target = Option<E>>,
148 E: Clone,
149 T: From<E> + Clone,
150{
151 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
152 ElementsMaybeSignalType::Static(StoredValue::new_local(vec![
153 self.as_ref().map(|e| T::from(e.clone())),
154 ]))
155 }
156}
157
158impl<T, V> IntoElementsMaybeSignalType<T, StrMarker> for V
162where
163 V: AsRef<str>,
164 T: From<web_sys::Element> + Clone,
165{
166 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
167 if cfg!(feature = "ssr") {
168 ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
169 } else {
170 ElementsMaybeSignalType::Dynamic(els_signal_by_sel::<T>(self.as_ref()))
171 }
172 }
173}
174
175impl<T, V, I> IntoElementsMaybeSignalType<T, SignalStrMarker> for V
177where
178 V: Get<Value = I> + 'static,
179 I: AsRef<str>,
180 T: From<web_sys::Element> + Clone,
181{
182 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
183 if cfg!(feature = "ssr") {
184 ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
185 } else {
186 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
187 vec![
188 document()
189 .query_selector(self.get().as_ref())
190 .unwrap_or_default()
191 .map(|el| T::from(el).clone()),
192 ]
193 }))
194 }
195 }
196}
197
198impl<T, V, E> IntoElementsMaybeSignalType<T, SignalMarker> for V
202where
203 V: Get<Value = E> + 'static,
204 T: From<E> + Clone,
205{
206 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
207 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
208 vec![Some(T::from(self.get()))]
209 }))
210 }
211}
212
213impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [web_sys::Element]> for C
217where
218 Js: Clone + 'a,
219 T: From<Js>,
220 C: IntoIterator<Item = &'a Js>,
221{
222 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
223 ElementsMaybeSignalType::Static(StoredValue::new_local(
224 self.into_iter().map(|t| Some(T::from(t.clone()))).collect(),
225 ))
226 }
227}
228
229impl<'a, T, Js, C> IntoElementsMaybeSignalType<T, &'a [Option<web_sys::Element>]> for C
231where
232 Js: Clone + 'a,
233 T: From<Js>,
234 C: IntoIterator<Item = &'a Option<Js>>,
235{
236 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
237 ElementsMaybeSignalType::Static(StoredValue::new_local(
238 self.into_iter().map(|t| t.clone().map(T::from)).collect(),
239 ))
240 }
241}
242
243impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<web_sys::Element>> for C
245where
246 T: From<Js> + Clone,
247 C: IntoIterator<Item = Js>,
248{
249 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
250 ElementsMaybeSignalType::Static(StoredValue::new_local(
251 self.into_iter().map(|t| Some(T::from(t))).collect(),
252 ))
253 }
254}
255
256impl<T, Js, C> IntoElementsMaybeSignalType<T, Vec<Option<web_sys::Element>>> for C
258where
259 T: From<Js> + Clone,
260 C: IntoIterator<Item = Option<Js>>,
261{
262 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
263 ElementsMaybeSignalType::Static(StoredValue::new_local(
264 self.into_iter().map(|t| t.map(T::from)).collect(),
265 ))
266 }
267}
268
269pub struct StrIterMarker;
272
273pub fn els_by_sel<T>(sel: &str) -> Vec<Option<T>>
274where
275 T: From<web_sys::Element> + Clone,
276{
277 let mut els: Vec<web_sys::Element> = Vec::new();
278
279 if let Ok(queried_els) = document().query_selector_all(sel.as_ref()) {
280 for i in 0..queried_els.length() {
281 if let Ok(el) = queried_els.get(i).expect("checked length").dyn_into() {
282 els.push(el);
283 }
284 }
285 }
286 els.into_iter().map(|v| Some(T::from(v))).collect()
287}
288
289pub fn els_signal_by_sel<T>(sel: &str) -> Signal<Vec<Option<T>>, LocalStorage>
290where
291 T: From<web_sys::Element> + Clone + 'static,
292{
293 let (el_signal, set_el_signal) = signal_local(Vec::new());
294
295 let sel = sel.to_string();
296
297 set_timeout(
298 move || {
299 let els = els_by_sel::<T>(&sel);
300 if !els.is_empty() {
301 set_el_signal.set(els);
302 } else {
303 let stop_observer = StoredValue::new_local(Rc::new(|| {}) as Rc<dyn Fn()>);
304
305 let UseMutationObserverReturn { stop, .. } = use_mutation_observer_with_options(
306 document().body().unwrap(),
307 move |_, _| {
308 let els = els_by_sel(&sel);
309 if !els.is_empty() {
310 set_el_signal.set(els);
311 stop_observer.get_value()();
312 } else {
313 set_el_signal.set(Vec::new());
314 }
315 },
316 UseMutationObserverOptions::default()
317 .child_list(true)
318 .subtree(true),
319 );
320
321 stop_observer.set_value(Rc::new(stop));
322 }
323 },
324 Duration::ZERO,
325 );
326
327 el_signal.into()
328}
329
330impl<T, V, C> IntoElementsMaybeSignalType<T, StrIterMarker> for C
332where
333 V: AsRef<str>,
334 T: From<web_sys::Element> + Clone,
335 C: IntoIterator<Item = V>,
336{
337 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
338 if cfg!(feature = "ssr") {
339 ElementsMaybeSignalType::Static(StoredValue::new_local(vec![]))
340 } else {
341 self.into_iter()
342 .map(|sel| els_signal_by_sel::<T>(sel.as_ref()))
343 .collect::<Vec<_>>()
344 .into_elements_maybe_signal_type()
345 }
346 }
347}
348
349pub struct SignalVecMarker;
352
353impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecMarker> for G
355where
356 T: From<Js> + Clone,
357 G: Get<Value = C> + 'static,
358 C: IntoIterator<Item = Js>,
359{
360 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
361 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
362 self.get().into_iter().map(|t| Some(T::from(t))).collect()
363 }))
364 }
365}
366
367pub struct SignalVecOptionMarker;
368
369impl<T, Js, C, G> IntoElementsMaybeSignalType<T, SignalVecOptionMarker> for G
371where
372 T: From<Js> + Clone,
373 G: Get<Value = C> + 'static,
374 C: IntoIterator<Item = Option<Js>>,
375{
376 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
377 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
378 self.get().into_iter().map(|t| t.map(T::from)).collect()
379 }))
380 }
381}
382
383pub struct VecSignalMarker;
386
387impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalMarker> for C
389where
390 T: From<Js> + Clone,
391 C: IntoIterator<Item = G> + Clone + 'static,
392 G: Get<Value = Js>,
393{
394 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
395 let signals = self.clone();
396
397 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
398 signals
399 .clone()
400 .into_iter()
401 .map(|t| Some(T::from(t.get())))
402 .collect()
403 }))
404 }
405}
406
407pub struct VecSignalOptionMarker;
408
409impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalOptionMarker> for C
411where
412 T: From<Js> + Clone,
413 C: IntoIterator<Item = G> + Clone + 'static,
414 G: Get<Value = Option<Js>>,
415{
416 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
417 let signals = self.clone();
418
419 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
420 signals
421 .clone()
422 .into_iter()
423 .map(|t| t.get().map(T::from))
424 .collect()
425 }))
426 }
427}
428
429pub struct VecSignalVecOptionMarker;
431
432impl<T, Js, C, G> IntoElementsMaybeSignalType<T, VecSignalVecOptionMarker> for C
433where
434 T: TryFrom<Js> + Clone,
435 C: IntoIterator<Item = G> + Clone + 'static,
436 G: Get<Value = Vec<Option<Js>>>,
437{
438 fn into_elements_maybe_signal_type(self) -> ElementsMaybeSignalType<T> {
439 let signals = self.clone();
440
441 ElementsMaybeSignalType::Dynamic(Signal::derive_local(move || {
442 signals
443 .clone()
444 .into_iter()
445 .flat_map(|t| t.get())
446 .map(|j| T::try_from(j.unwrap()).ok())
447 .collect()
448 }))
449 }
450}