2023-05-14 22:45:38 +01:00
|
|
|
use leptos::html::ElementDescriptor;
|
|
|
|
use leptos::*;
|
2023-05-26 18:09:01 +01:00
|
|
|
use std::marker::PhantomData;
|
2023-05-14 22:45:38 +01:00
|
|
|
use std::ops::Deref;
|
|
|
|
|
2023-05-14 23:50:11 +01:00
|
|
|
/// Used as an argument type to make it easily possible to pass either
|
2023-05-26 18:09:01 +01:00
|
|
|
/// * a `web_sys` element that implements `E` (for example `EventTarget` or `Element`),
|
2023-05-14 23:50:11 +01:00
|
|
|
/// * an `Option<T>` where `T` is the web_sys element,
|
|
|
|
/// * a `Signal<T>` where `T` is the web_sys element,
|
|
|
|
/// * a `Signal<Option<T>>` where `T` is the web_sys element,
|
|
|
|
/// * a `NodeRef`
|
|
|
|
/// into a function. Used for example in [`use_event_listener`].
|
2023-05-26 18:09:01 +01:00
|
|
|
pub enum ElementMaybeSignal<T, E>
|
2023-05-14 22:45:38 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 22:45:38 +01:00
|
|
|
{
|
|
|
|
Static(Option<T>),
|
|
|
|
Dynamic(Signal<Option<T>>),
|
2023-05-26 18:09:01 +01:00
|
|
|
_Phantom(PhantomData<E>),
|
2023-05-14 22:45:38 +01:00
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> Default for ElementMaybeSignal<T, E>
|
2023-05-14 23:50:11 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 23:50:11 +01:00
|
|
|
{
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Static(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> Clone for ElementMaybeSignal<T, E>
|
2023-05-14 23:50:11 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 23:50:11 +01:00
|
|
|
{
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => Self::Static(t.clone()),
|
|
|
|
Self::Dynamic(s) => Self::Dynamic(*s),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> SignalGet<Option<T>> for ElementMaybeSignal<T, E>
|
2023-05-14 23:50:11 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 23:50:11 +01:00
|
|
|
{
|
|
|
|
fn get(&self) -> Option<T> {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => t.clone(),
|
|
|
|
Self::Dynamic(s) => s.get(),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_get(&self) -> Option<Option<T>> {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => Some(t.clone()),
|
|
|
|
Self::Dynamic(s) => s.try_get(),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> SignalWith<Option<T>> for ElementMaybeSignal<T, E>
|
2023-05-14 23:50:11 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 23:50:11 +01:00
|
|
|
{
|
|
|
|
fn with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> O {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => f(t),
|
|
|
|
Self::Dynamic(s) => s.with(f),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_with<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => Some(f(t)),
|
|
|
|
Self::Dynamic(s) => s.try_with(f),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> SignalWithUntracked<Option<T>> for ElementMaybeSignal<T, E>
|
2023-05-14 23:50:11 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 23:50:11 +01:00
|
|
|
{
|
|
|
|
fn with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> O {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => f(t),
|
|
|
|
Self::Dynamic(s) => s.with_untracked(f),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_with_untracked<O>(&self, f: impl FnOnce(&Option<T>) -> O) -> Option<O> {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => Some(f(t)),
|
|
|
|
Self::Dynamic(s) => s.try_with_untracked(f),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> SignalGetUntracked<Option<T>> for ElementMaybeSignal<T, E>
|
2023-05-14 23:50:11 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 23:50:11 +01:00
|
|
|
{
|
|
|
|
fn get_untracked(&self) -> Option<T> {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => t.clone(),
|
|
|
|
Self::Dynamic(s) => s.get_untracked(),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_get_untracked(&self) -> Option<Option<T>> {
|
|
|
|
match self {
|
|
|
|
Self::Static(t) => Some(t.clone()),
|
|
|
|
Self::Dynamic(s) => s.try_get_untracked(),
|
2023-05-26 18:09:01 +01:00
|
|
|
_ => unreachable!(),
|
2023-05-14 23:50:11 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-13 00:31:38 +01:00
|
|
|
// From static element //////////////////////////////////////////////////////////////
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> From<(Scope, T)> for ElementMaybeSignal<T, E>
|
2023-05-14 22:45:38 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 22:45:38 +01:00
|
|
|
{
|
|
|
|
fn from(value: (Scope, T)) -> Self {
|
2023-05-26 18:09:01 +01:00
|
|
|
ElementMaybeSignal::Static(Some(value.1))
|
2023-05-14 22:45:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> From<(Scope, Option<T>)> for ElementMaybeSignal<T, E>
|
2023-05-14 22:45:38 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 22:45:38 +01:00
|
|
|
{
|
|
|
|
fn from(target: (Scope, Option<T>)) -> Self {
|
2023-05-26 18:09:01 +01:00
|
|
|
ElementMaybeSignal::Static(target.1)
|
2023-05-14 22:45:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-13 00:31:38 +01:00
|
|
|
// From string (selector) ///////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
impl<'a, E> From<(Scope, &'a str)> for ElementMaybeSignal<web_sys::Element, E>
|
|
|
|
where
|
|
|
|
E: From<web_sys::Element> + 'static,
|
|
|
|
{
|
|
|
|
fn from(target: (Scope, &'a str)) -> Self {
|
|
|
|
Self::Static(document().query_selector(target.1).unwrap_or_default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-23 22:04:16 +01:00
|
|
|
impl<'a, E> From<&'a str> for ElementMaybeSignal<web_sys::Element, E>
|
|
|
|
where
|
|
|
|
E: From<web_sys::Element> + 'static,
|
|
|
|
{
|
|
|
|
fn from(target: &'a str) -> Self {
|
|
|
|
Self::Static(document().query_selector(target).unwrap_or_default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<E> From<String> for ElementMaybeSignal<web_sys::Element, E>
|
|
|
|
where
|
|
|
|
E: From<web_sys::Element> + 'static,
|
|
|
|
{
|
|
|
|
fn from(target: String) -> Self {
|
|
|
|
Self::Static(document().query_selector(&target).unwrap_or_default())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-13 00:31:38 +01:00
|
|
|
impl<E> From<(Scope, Signal<String>)> for ElementMaybeSignal<web_sys::Element, E>
|
|
|
|
where
|
|
|
|
E: From<web_sys::Element> + 'static,
|
|
|
|
{
|
|
|
|
fn from(target: (Scope, Signal<String>)) -> Self {
|
|
|
|
let (cx, signal) = target;
|
|
|
|
|
|
|
|
Self::Dynamic(
|
|
|
|
create_memo(cx, move |_| {
|
|
|
|
document().query_selector(&signal.get()).unwrap_or_default()
|
|
|
|
})
|
|
|
|
.into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// From signal ///////////////////////////////////////////////////////////////
|
|
|
|
|
2023-05-14 22:45:38 +01:00
|
|
|
macro_rules! impl_from_signal_option {
|
|
|
|
($ty:ty) => {
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> From<(Scope, $ty)> for ElementMaybeSignal<T, E>
|
2023-05-14 22:45:38 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 22:45:38 +01:00
|
|
|
{
|
|
|
|
fn from(target: (Scope, $ty)) -> Self {
|
2023-06-13 00:31:38 +01:00
|
|
|
Self::Dynamic(target.1.into())
|
2023-05-14 22:45:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_from_signal_option!(Signal<Option<T>>);
|
|
|
|
impl_from_signal_option!(ReadSignal<Option<T>>);
|
|
|
|
impl_from_signal_option!(RwSignal<Option<T>>);
|
|
|
|
impl_from_signal_option!(Memo<Option<T>>);
|
|
|
|
|
|
|
|
macro_rules! impl_from_signal {
|
|
|
|
($ty:ty) => {
|
2023-05-26 18:09:01 +01:00
|
|
|
impl<T, E> From<(Scope, $ty)> for ElementMaybeSignal<T, E>
|
2023-05-14 22:45:38 +01:00
|
|
|
where
|
2023-05-26 18:09:01 +01:00
|
|
|
T: Into<E> + Clone + 'static,
|
2023-05-14 22:45:38 +01:00
|
|
|
{
|
|
|
|
fn from(target: (Scope, $ty)) -> Self {
|
|
|
|
let (cx, signal) = target;
|
|
|
|
|
2023-06-13 00:31:38 +01:00
|
|
|
Self::Dynamic(Signal::derive(cx, move || Some(signal.get())))
|
2023-05-14 22:45:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_from_signal!(Signal<T>);
|
|
|
|
impl_from_signal!(ReadSignal<T>);
|
|
|
|
impl_from_signal!(RwSignal<T>);
|
|
|
|
impl_from_signal!(Memo<T>);
|
|
|
|
|
2023-06-13 00:31:38 +01:00
|
|
|
// From NodeRef //////////////////////////////////////////////////////////////
|
|
|
|
|
2023-05-26 18:09:01 +01:00
|
|
|
macro_rules! impl_from_node_ref {
|
|
|
|
($ty:ty) => {
|
|
|
|
impl<R> From<(Scope, NodeRef<R>)> for ElementMaybeSignal<$ty, $ty>
|
|
|
|
where
|
|
|
|
R: ElementDescriptor + Clone + 'static,
|
|
|
|
{
|
|
|
|
fn from(target: (Scope, NodeRef<R>)) -> Self {
|
|
|
|
let (cx, node_ref) = target;
|
|
|
|
|
2023-06-13 00:31:38 +01:00
|
|
|
Self::Dynamic(Signal::derive(cx, move || {
|
2023-05-26 18:09:01 +01:00
|
|
|
node_ref.get().map(move |el| {
|
|
|
|
let el = el.into_any();
|
|
|
|
let el: $ty = el.deref().clone().into();
|
|
|
|
el
|
|
|
|
})
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2023-05-14 22:45:38 +01:00
|
|
|
}
|
2023-05-26 18:09:01 +01:00
|
|
|
|
|
|
|
impl_from_node_ref!(web_sys::EventTarget);
|
|
|
|
impl_from_node_ref!(web_sys::Element);
|