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, children: Children) -> impl IntoView { +pub fn Menu( + #[prop(optional, into)] selected: MaybeRwSignal, + 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(RwSignal); + +impl Default for MaybeRwSignal { + fn default() -> Self { + Self(RwSignal::new(Default::default())) + } +} + +impl Clone for MaybeRwSignal { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for MaybeRwSignal {} + +impl Deref for MaybeRwSignal { + type Target = RwSignal; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for MaybeRwSignal { + fn from(value: RwSignal) -> 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; +} + +impl SignalWatch for RwSignal { + 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 { + 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(); + }) + } +}