leptos_use/core/
element_maybe_signal.rs1use leptos::prelude::*;
2use leptos::reactive::wrappers::read::Signal;
3use send_wrapper::SendWrapper;
4use std::{ops::Deref, rc::Rc, time::Duration};
5
6use crate::{
7 UseMutationObserverOptions, UseMutationObserverReturn, use_mutation_observer_with_options,
8};
9
10#[cfg_attr(not(debug_assertions), repr(transparent))]
58pub struct ElementMaybeSignal<T: 'static> {
59 #[cfg(debug_assertions)]
60 defined_at: &'static std::panic::Location<'static>,
61 inner: ElementMaybeSignalType<T>,
62}
63
64impl<T> Clone for ElementMaybeSignal<T> {
65 fn clone(&self) -> Self {
66 *self
67 }
68}
69
70impl<T> Copy for ElementMaybeSignal<T> {}
71
72pub enum ElementMaybeSignalType<T: 'static> {
73 Static(StoredValue<Option<T>, LocalStorage>),
74 Dynamic(Signal<Option<T>, LocalStorage>),
75}
76
77impl<T> Clone for ElementMaybeSignalType<T> {
78 fn clone(&self) -> Self {
79 *self
80 }
81}
82
83impl<T> Copy for ElementMaybeSignalType<T> {}
84
85impl<T: 'static> Default for ElementMaybeSignalType<T> {
86 fn default() -> Self {
87 Self::Static(StoredValue::new_local(None))
88 }
89}
90
91impl<T> Default for ElementMaybeSignal<T> {
92 fn default() -> Self {
93 Self {
94 inner: ElementMaybeSignalType::default(),
95 #[cfg(debug_assertions)]
96 defined_at: std::panic::Location::caller(),
97 }
98 }
99}
100
101impl<T> DefinedAt for ElementMaybeSignal<T> {
102 fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
103 #[cfg(debug_assertions)]
104 {
105 Some(self.defined_at)
106 }
107 #[cfg(not(debug_assertions))]
108 {
109 None
110 }
111 }
112}
113
114impl<T> With for ElementMaybeSignal<T> {
115 type Value = Option<T>;
116
117 fn try_with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
118 match &self.inner {
119 ElementMaybeSignalType::Static(v) => v.try_with_value(f),
120 ElementMaybeSignalType::Dynamic(s) => s.try_with(f),
121 }
122 }
123}
124
125impl<T> WithUntracked for ElementMaybeSignal<T> {
126 type Value = Option<T>;
127
128 fn try_with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
129 match &self.inner {
130 ElementMaybeSignalType::Static(t) => t.try_with_value(f),
131 ElementMaybeSignalType::Dynamic(s) => s.try_with_untracked(f),
132 }
133 }
134}
135
136pub trait IntoElementMaybeSignal<T, Marker> {
137 fn into_element_maybe_signal(self) -> ElementMaybeSignal<T>;
138}
139
140impl<El, T, Marker> IntoElementMaybeSignal<T, Marker> for El
141where
142 El: IntoElementMaybeSignalType<T, Marker>,
143{
144 fn into_element_maybe_signal(self) -> ElementMaybeSignal<T> {
145 ElementMaybeSignal {
146 inner: self.into_element_maybe_signal_type(),
147 #[cfg(debug_assertions)]
148 defined_at: std::panic::Location::caller(),
149 }
150 }
151}
152
153pub trait IntoElementMaybeSignalType<T, Marker> {
154 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T>;
155}
156
157impl<T, Js> IntoElementMaybeSignalType<T, web_sys::Element> for Js
161where
162 T: From<Js> + Clone,
163{
164 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
165 ElementMaybeSignalType::Static(StoredValue::new_local(Some(T::from(self).clone())))
166 }
167}
168
169impl<T, Js> IntoElementMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
171where
172 T: From<Js> + Clone,
173{
174 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
175 ElementMaybeSignalType::Static(StoredValue::new_local(self.map(|el| T::from(el).clone())))
176 }
177}
178
179impl<T, E, Js> IntoElementMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
181where
182 Js: Deref<Target = Option<E>>,
183 E: Clone,
184 T: From<E> + Clone,
185{
186 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
187 ElementMaybeSignalType::Static(StoredValue::new_local(
188 self.as_ref().map(|e| T::from(e.clone())),
189 ))
190 }
191}
192
193pub struct StrMarker;
196
197impl<T, V> IntoElementMaybeSignalType<T, StrMarker> for V
199where
200 V: AsRef<str>,
201 T: From<web_sys::Element> + Clone,
202{
203 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
204 if cfg!(feature = "ssr") {
205 ElementMaybeSignalType::Static(StoredValue::new_local(None))
206 } else {
207 ElementMaybeSignalType::Dynamic(el_signal_by_sel(self.as_ref()))
208 }
209 }
210}
211
212pub fn el_by_sel<T>(sel: &str) -> Option<T>
213where
214 T: From<web_sys::Element> + Clone,
215{
216 document()
217 .query_selector(sel)
218 .unwrap_or_default()
219 .map(|el| T::from(el).clone())
220}
221
222pub fn el_signal_by_sel<T>(sel: &str) -> Signal<Option<T>, LocalStorage>
223where
224 T: From<web_sys::Element> + Clone + 'static,
225{
226 let (el_signal, set_el_signal) = signal_local(None);
227
228 let sel = sel.to_string();
229
230 set_timeout(
231 move || {
232 if let Some(el) = el_by_sel(&sel) {
233 set_el_signal.set(Some(el));
234 } else {
235 let stop_observer = StoredValue::new_local(Rc::new(|| {}) as Rc<dyn Fn()>);
236
237 let UseMutationObserverReturn { stop, .. } = use_mutation_observer_with_options(
238 document().body().unwrap(),
239 move |_, _| {
240 if let Some(el) = el_by_sel(&sel) {
241 set_el_signal.set(Some(el));
242 stop_observer.get_value()();
243 } else {
244 set_el_signal.set(None)
245 }
246 },
247 UseMutationObserverOptions::default()
248 .child_list(true)
249 .subtree(true),
250 );
251
252 stop_observer.set_value(Rc::new(stop));
253 }
254 },
255 Duration::ZERO,
256 );
257
258 el_signal.into()
259}
260
261pub struct SignalStrMarker;
262
263impl<T, V, I> IntoElementMaybeSignalType<T, SignalStrMarker> for V
265where
266 V: Get<Value = I> + 'static,
267 I: AsRef<str>,
268 T: From<web_sys::Element> + Clone,
269{
270 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
271 if cfg!(feature = "ssr") {
272 ElementMaybeSignalType::Static(StoredValue::new_local(None))
273 } else {
274 ElementMaybeSignalType::Dynamic(Signal::derive_local(move || {
275 document()
276 .query_selector(self.get().as_ref())
277 .unwrap_or_default()
278 .map(|el| T::from(el).clone())
279 }))
280 }
281 }
282}
283
284pub struct SignalMarker;
287
288impl<T, V, E> IntoElementMaybeSignalType<T, SignalMarker> for V
290where
291 V: Get<Value = E> + 'static,
292 T: From<E> + Clone,
293{
294 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
295 ElementMaybeSignalType::Dynamic(Signal::derive_local(move || Some(T::from(self.get()))))
296 }
297}
298
299pub struct SendWrapperSignalMarker;
300
301impl<T, V, E> IntoElementMaybeSignalType<T, SendWrapperSignalMarker> for V
303where
304 E: Clone,
305 V: Get<Value = SendWrapper<E>> + 'static,
306 T: From<E> + Clone,
307{
308 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
309 ElementMaybeSignalType::Dynamic(Signal::derive_local(move || {
310 Some(T::from((*self.get()).clone()))
311 }))
312 }
313}
314
315pub struct OptionSignalMarker;
316
317impl<T, V, E> IntoElementMaybeSignalType<T, OptionSignalMarker> for V
319where
320 V: Get<Value = Option<E>> + 'static,
321 T: From<E> + Clone,
322{
323 fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
324 ElementMaybeSignalType::Dynamic(Signal::derive_local(move || self.get().map(T::from)))
325 }
326}