Generalized ToDynProp

This commit is contained in:
Filipe Rodrigues 2025-08-10 10:43:55 +01:00
parent 54a39a9b0b
commit c852c5a116
Signed by: zenithsiz
SSH Key Fingerprint: SHA256:Mb5ppb3Sh7IarBO/sBTXLHbYEOz37hJAlslLQPPAPaU
4 changed files with 106 additions and 98 deletions

View File

@ -13,6 +13,7 @@ mod object_attach_context;
mod object_attach_effect;
mod object_attach_value;
mod object_dyn_prop;
mod to_dyn_prop;
mod with_dyn_pred;
mod with_dyn_text;
@ -27,7 +28,8 @@ pub use {
object_attach_context::{ObjectAttachContext, ObjectWithContext},
object_attach_effect::{ObjectAttachEffect, ObjectWithEffect},
object_attach_value::{ObjectAttachValue, ObjectWithValue},
object_dyn_prop::{ObjectDynProp, ObjectWithDynProp, ToDynProp},
object_dyn_prop::{ObjectDynProp, ObjectWithDynProp},
to_dyn_prop::ToDynProp,
with_dyn_pred::WithDynPred,
with_dyn_text::WithDynText,
},

View File

@ -2,12 +2,10 @@
// Imports
use {
crate::ObjectAttachEffect,
core::ops::Deref,
crate::{ObjectAttachEffect, ToDynProp},
dynatos_html::{ObjectRemoveProp, ObjectSetProp, WeakRef},
dynatos_reactive::{derived::DerivedRun, Derived, Effect, Memo, Signal, SignalWith, WithDefault},
dynatos_reactive::Effect,
dynatos_util::TryOrReturnExt,
wasm_bindgen::JsValue,
};
/// Extension trait to add reactive prop to an object
@ -71,95 +69,3 @@ where
self
}
}
/// Trait for values accepted by [`ObjectDynProp`].
///
/// This allows it to work with the following types:
/// - `impl Fn() -> N`
/// - `impl Fn() -> Option<N>`
/// - `N`
/// - `Option<N>`
///
/// Where `N` is a dyn prop.
pub trait ToDynProp {
/// Gets the current prop
fn to_prop(&self) -> Option<JsValue>;
}
impl<F, T> ToDynProp for F
where
F: Fn() -> T,
T: ToDynProp,
{
fn to_prop(&self) -> Option<JsValue> {
self().to_prop()
}
}
impl<T> ToDynProp for Option<T>
where
T: ToDynProp,
{
fn to_prop(&self) -> Option<JsValue> {
self.as_ref().and_then(T::to_prop)
}
}
// TODO: Generalize to `impl Into<JsValue>`
#[duplicate::duplicate_item(
Ty;
[&'_ str];
[&'_ String];
[bool];
[f32];
[f64];
[i128];
[i16];
[i32];
[i64];
[i8];
[isize];
[u128];
[u16];
[u32];
[u64];
[u8];
[usize];
)]
impl ToDynProp for Ty {
fn to_prop(&self) -> Option<JsValue> {
Some(JsValue::from(*self))
}
}
#[allow(clippy::allow_attributes, reason = "This only applies in some branches")]
#[allow(clippy::use_self, reason = "We always want to use `JsValue`, not `Ty`")]
#[duplicate::duplicate_item(
Ty;
[JsValue];
[String];
)]
impl ToDynProp for Ty {
fn to_prop(&self) -> Option<JsValue> {
Some(JsValue::from(self))
}
}
// TODO: Allow impl for `impl SignalGet<Value: WithDynText>`
#[duplicate::duplicate_item(
Generics Ty;
[T] [Signal<T> where T: ToDynProp + 'static];
[T, F] [Derived<T, F> where T: ToDynProp + 'static, F: ?Sized + DerivedRun<T> + 'static];
[T, F] [Memo<T, F> where T: ToDynProp + 'static, F: ?Sized + 'static];
[S, T] [WithDefault<S, T> where Self: for<'a> SignalWith<Value<'a>: Deref<Target: ToDynProp>>];
)]
impl<Generics> ToDynProp for Ty {
fn to_prop(&self) -> Option<JsValue> {
#[allow(
clippy::allow_attributes,
clippy::redundant_closure_for_method_calls,
reason = "In some branches it isn't redundant"
)]
self.with(|prop| prop.to_prop())
}
}

100
dynatos/src/to_dyn_prop.rs Normal file
View File

@ -0,0 +1,100 @@
//! Dynamic properties
use {
core::ops::Deref,
dynatos_reactive::{Derived, Memo, Signal, SignalWith, WithDefault, derived::DerivedRun},
wasm_bindgen::JsValue,
};
/// Values that may be used as possible dynamic properties.
///
/// This allows it to work with the following types:
/// - `i*`, `u*`, `bool`, `String`
/// - `&str`, `&String`
/// - `JsValue`
/// - `impl Fn() -> N`
/// - `Option<N>`
/// - [`Signal`], [`Derived`], [`Memo`], [`WithDefault`]
///
/// Where `N` is a dyn prop.
pub trait ToDynProp {
/// Gets the current prop
fn to_prop(&self) -> Option<JsValue>;
}
impl<F, T> ToDynProp for F
where
F: Fn() -> T,
T: ToDynProp,
{
fn to_prop(&self) -> Option<JsValue> {
self().to_prop()
}
}
impl<T> ToDynProp for Option<T>
where
T: ToDynProp,
{
fn to_prop(&self) -> Option<JsValue> {
self.as_ref().and_then(T::to_prop)
}
}
// TODO: Generalize to `impl Into<JsValue>`
#[duplicate::duplicate_item(
Ty;
[&'_ str];
[&'_ String];
[bool];
[f32];
[f64];
[i128];
[i16];
[i32];
[i64];
[i8];
[isize];
[u128];
[u16];
[u32];
[u64];
[u8];
[usize];
)]
impl ToDynProp for Ty {
fn to_prop(&self) -> Option<JsValue> {
Some(JsValue::from(*self))
}
}
#[allow(clippy::allow_attributes, reason = "This only applies in some branches")]
#[allow(clippy::use_self, reason = "We always want to use `JsValue`, not `Ty`")]
#[duplicate::duplicate_item(
Ty;
[JsValue];
[String];
)]
impl ToDynProp for Ty {
fn to_prop(&self) -> Option<JsValue> {
Some(JsValue::from(self))
}
}
#[duplicate::duplicate_item(
Generics Ty;
[T] [Signal<T> where T: ToDynProp + 'static];
[T, F] [Derived<T, F> where T: ToDynProp + 'static, F: ?Sized + DerivedRun<T> + 'static];
[T, F] [Memo<T, F> where T: ToDynProp + 'static, F: ?Sized + 'static];
[S, T] [WithDefault<S, T> where Self: for<'a> SignalWith<Value<'a>: Deref<Target: ToDynProp>>];
)]
impl<Generics> ToDynProp for Ty {
fn to_prop(&self) -> Option<JsValue> {
#[allow(
clippy::allow_attributes,
clippy::redundant_closure_for_method_calls,
reason = "In some branches it isn't redundant"
)]
self.with(|prop| prop.to_prop())
}
}

View File

@ -10,7 +10,7 @@ use {
///
/// This allows it to work with the following types:
/// - `bool`
/// - `Signal<B>`
/// - [`Signal`], [`Derived`], [`Memo`], [`WithDefault`]
/// - `impl Fn() -> B`
///
/// Where `B` is any of the types above.