use leptos::prelude::{guards::ReadGuard, *};
use leptos::reactive::wrappers::read::Signal;
use send_wrapper::SendWrapper;
use std::{ops::Deref, rc::Rc, time::Duration};
use crate::{
UseMutationObserverOptions, UseMutationObserverReturn, use_mutation_observer_with_options,
};
#[cfg_attr(not(debug_assertions), repr(transparent))]
pub struct ElementMaybeSignal<T: 'static> {
#[cfg(debug_assertions)]
defined_at: &'static std::panic::Location<'static>,
inner: ElementMaybeSignalType<T>,
}
impl<T> Clone for ElementMaybeSignal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ElementMaybeSignal<T> {}
pub struct ElementMaybeSignalType<T: 'static>(Signal<Option<SendWrapper<T>>>);
impl<T> Clone for ElementMaybeSignalType<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ElementMaybeSignalType<T> {}
impl<T: 'static> Default for ElementMaybeSignalType<T> {
fn default() -> Self {
Self(Signal::stored(None))
}
}
impl<T> Default for ElementMaybeSignal<T> {
fn default() -> Self {
Self {
inner: ElementMaybeSignalType::default(),
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
}
impl<T> DefinedAt for ElementMaybeSignal<T> {
fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T> ReadUntracked for ElementMaybeSignal<T> {
type Value =
ReadGuard<Option<SendWrapper<T>>, SignalReadGuard<Option<SendWrapper<T>>, SyncStorage>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.inner.0.try_read_untracked()
}
}
impl<T> Track for ElementMaybeSignal<T> {
fn track(&self) {
self.inner.0.track();
}
}
pub trait IntoElementMaybeSignal<T, Marker> {
fn into_element_maybe_signal(self) -> ElementMaybeSignal<T>;
}
impl<El, T, Marker> IntoElementMaybeSignal<T, Marker> for El
where
El: IntoElementMaybeSignalType<T, Marker>,
{
fn into_element_maybe_signal(self) -> ElementMaybeSignal<T> {
ElementMaybeSignal {
inner: self.into_element_maybe_signal_type(),
#[cfg(debug_assertions)]
defined_at: std::panic::Location::caller(),
}
}
}
pub trait IntoElementMaybeSignalType<T, Marker> {
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T>;
}
impl<T, Js> IntoElementMaybeSignalType<T, web_sys::Element> for Js
where
T: From<Js> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType(Signal::stored(Some(SendWrapper::new(
T::from(self).clone(),
))))
}
}
impl<T, Js> IntoElementMaybeSignalType<T, Option<web_sys::Element>> for Option<Js>
where
T: From<Js> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType(Signal::stored(
self.map(|el| SendWrapper::new(T::from(el).clone())),
))
}
}
impl<T, E, Js> IntoElementMaybeSignalType<T, Option<Option<web_sys::Element>>> for Js
where
Js: Deref<Target = Option<E>>,
E: Clone,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType(Signal::stored(
self.as_ref().map(|e| SendWrapper::new(T::from(e.clone()))),
))
}
}
pub struct StrMarker;
impl<T, V> IntoElementMaybeSignalType<T, StrMarker> for V
where
V: AsRef<str>,
T: From<web_sys::Element> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementMaybeSignalType(Signal::stored(None))
} else {
ElementMaybeSignalType(el_signal_by_sel(self.as_ref()))
}
}
}
pub fn el_by_sel<T>(sel: &str) -> Option<T>
where
T: From<web_sys::Element> + Clone,
{
document()
.query_selector(sel)
.unwrap_or_default()
.map(|el| T::from(el).clone())
}
pub fn el_signal_by_sel<T>(sel: &str) -> Signal<Option<SendWrapper<T>>>
where
T: From<web_sys::Element> + Clone + 'static,
{
let (el_signal, set_el_signal) = signal(None);
let sel = sel.to_string();
set_timeout(
move || {
if let Some(el) = el_by_sel(&sel) {
set_el_signal.set(Some(SendWrapper::new(el)));
} else {
let stop_observer = StoredValue::new_local(Rc::new(|| {}) as Rc<dyn Fn()>);
let UseMutationObserverReturn { stop, .. } = use_mutation_observer_with_options(
document().body().unwrap(),
move |_, _| {
if let Some(el) = el_by_sel(&sel) {
set_el_signal.set(Some(SendWrapper::new(el)));
stop_observer.get_value()();
} else {
set_el_signal.set(None)
}
},
UseMutationObserverOptions::default()
.child_list(true)
.subtree(true),
);
stop_observer.set_value(Rc::new(stop));
}
},
Duration::ZERO,
);
el_signal.into()
}
pub struct SignalStrMarker;
impl<T, V, I> IntoElementMaybeSignalType<T, SignalStrMarker> for V
where
V: Get<Value = I> + Send + Sync + 'static,
I: AsRef<str>,
T: From<web_sys::Element> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementMaybeSignalType(Signal::stored(None))
} else {
ElementMaybeSignalType(Signal::derive(move || {
document()
.query_selector(self.get().as_ref())
.unwrap_or_default()
.map(|el| SendWrapper::new(T::from(el).clone()))
}))
}
}
}
pub struct SignalMarker;
pub struct SendWrapperSignalMarker;
impl<T, V, E> IntoElementMaybeSignalType<T, SendWrapperSignalMarker> for V
where
E: Clone,
V: Get<Value = SendWrapper<E>> + Send + Sync + 'static,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
if cfg!(feature = "ssr") {
ElementMaybeSignalType(Signal::stored(None))
} else {
ElementMaybeSignalType(Signal::derive(move || {
Some(SendWrapper::new(T::from((self.get().take()).clone())))
}))
}
}
}
pub struct OptionSignalMarker;
impl<T, V, E> IntoElementMaybeSignalType<T, OptionSignalMarker> for V
where
V: Get<Value = Option<E>> + Send + Sync + 'static,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType(Signal::derive(move || {
self.get().map(|v| SendWrapper::new(T::from(v)))
}))
}
}
pub struct OptionSendWrapperSignalMarker;
impl<T, V, E> IntoElementMaybeSignalType<T, OptionSendWrapperSignalMarker> for V
where
V: Get<Value = Option<SendWrapper<E>>> + Send + Sync + 'static,
T: From<E> + Clone,
{
fn into_element_maybe_signal_type(self) -> ElementMaybeSignalType<T> {
ElementMaybeSignalType(Signal::derive(move || {
self.get().map(|v| SendWrapper::new(T::from(v.take())))
}))
}
}