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