From ecffde423f3f363d17ab7de230289109b9a0d52d Mon Sep 17 00:00:00 2001 From: luoxiao <luoxiaozero@163.com> Date: Fri, 6 Oct 2023 16:53:57 +0800 Subject: [PATCH] feat: add MaybeRwSignal and watch --- src/lib.rs | 2 +- src/menu/mod.rs | 6 ++++- src/utils/maybe_rw_signal.rs | 32 +++++++++++++++++++++++++ src/utils/mod.rs | 2 ++ src/utils/signal.rs | 46 ++++++++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 src/utils/maybe_rw_signal.rs create mode 100644 src/utils/signal.rs diff --git a/src/lib.rs b/src/lib.rs index 13a7480..570e86e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,5 +38,5 @@ pub use space::*; pub use table::*; pub use tabs::*; pub use theme::Theme; -pub use utils::mount_style::mount_style; +pub use utils::{mount_style::mount_style, signal::SignalWatch}; pub use wave::*; diff --git a/src/menu/mod.rs b/src/menu/mod.rs index 76a5c48..df23d8f 100644 --- a/src/menu/mod.rs +++ b/src/menu/mod.rs @@ -2,13 +2,17 @@ mod menu_group; mod menu_item; mod theme; +use crate::utils::maybe_rw_signal::MaybeRwSignal; use leptos::*; pub use menu_group::MenuGroup; pub use menu_item::*; pub use theme::MenuTheme; #[component] -pub fn Menu(#[prop(into)] selected: RwSignal<String>, children: Children) -> impl IntoView { +pub fn Menu( + #[prop(optional, into)] selected: MaybeRwSignal<String>, + children: Children, +) -> impl IntoView { let menu_injection_key = create_rw_signal(MenuInjectionKey::new(selected.get_untracked())); create_effect(move |_| { let selected_key = selected.get(); diff --git a/src/utils/maybe_rw_signal.rs b/src/utils/maybe_rw_signal.rs new file mode 100644 index 0000000..47fb42e --- /dev/null +++ b/src/utils/maybe_rw_signal.rs @@ -0,0 +1,32 @@ +use leptos::RwSignal; +use std::ops::Deref; + +pub struct MaybeRwSignal<T: Default + 'static>(RwSignal<T>); + +impl<T: Default> Default for MaybeRwSignal<T> { + fn default() -> Self { + Self(RwSignal::new(Default::default())) + } +} + +impl<T: Default> Clone for MaybeRwSignal<T> { + fn clone(&self) -> Self { + *self + } +} + +impl<T: Default> Copy for MaybeRwSignal<T> {} + +impl<T: Default> Deref for MaybeRwSignal<T> { + type Target = RwSignal<T>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<T: Default> From<RwSignal<T>> for MaybeRwSignal<T> { + fn from(value: RwSignal<T>) -> Self { + Self(value) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f21060a..e265b39 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1 +1,3 @@ +pub mod maybe_rw_signal; pub mod mount_style; +pub mod signal; diff --git a/src/utils/signal.rs b/src/utils/signal.rs new file mode 100644 index 0000000..80cbeec --- /dev/null +++ b/src/utils/signal.rs @@ -0,0 +1,46 @@ +use leptos::{create_effect, untrack, RwSignal, SignalDispose, SignalWith}; + +pub trait SignalWatch { + type Value; + + fn watch(&self, f: impl Fn(&Self::Value) + 'static) -> Box<dyn FnOnce()>; +} + +impl<T> SignalWatch for RwSignal<T> { + type Value = T; + + /// Listens for RwSignal changes and is not executed immediately + /// + /// ## Usage + /// + /// ```rust + /// use leptos::*; + /// use melt_ui::*; + /// + /// let count = create_rw_signal(0); + /// let stop = count.watch(|count| { + /// assert_eq!(count, &1); + /// }); + /// + /// count.set(1); // assert_eq!(count, &1); + /// + /// stop(); // stop watching + /// + /// count.set(2); // nothing happens + /// ``` + fn watch(&self, f: impl Fn(&Self::Value) + 'static) -> Box<dyn FnOnce()> { + let signal = self.clone(); + + let effect = create_effect(move |prev| { + signal.with(|value| { + if prev.is_some() { + untrack(|| f(value)); + } + }); + }); + + Box::new(move || { + effect.dispose(); + }) + } +}