From b831008e09f008fa4a4317145136b1c259466970 Mon Sep 17 00:00:00 2001 From: kandrelczyk Date: Sun, 29 Sep 2024 05:22:14 +0200 Subject: [PATCH] Toast dismiss (#270) * broken toaster * toast dismiss * toaster test * fixed dismiss * cargo fmt * dipose signal --- demo/Cargo.toml | 1 + demo/src/pages/markdown.rs | 1 + thaw/src/toast/docs/mod.md | 32 +++++++++ thaw/src/toast/mod.rs | 29 ++++++-- thaw/src/toast/toast.rs | 6 ++ thaw/src/toast/toaster.rs | 131 +++++++++++++++++++++++++++---------- 6 files changed, 159 insertions(+), 41 deletions(-) diff --git a/demo/Cargo.toml b/demo/Cargo.toml index f9ee643..74db6a5 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -18,6 +18,7 @@ chrono = "0.4.38" cfg-if = "1.0.0" # leptos-use = "0.10.10" send_wrapper = "0.6" +uuid = { version = "1.10.0", features = ["v4", "js"] } console_error_panic_hook = "0.1.7" console_log = "1" log = "0.4" diff --git a/demo/src/pages/markdown.rs b/demo/src/pages/markdown.rs index 6d4235c..14119d8 100644 --- a/demo/src/pages/markdown.rs +++ b/demo/src/pages/markdown.rs @@ -1,5 +1,6 @@ use crate::components::{Demo, DemoCode}; use leptos::{ev, prelude::*}; use thaw::*; +use uuid; demo_markdown::include_md! {} diff --git a/thaw/src/toast/docs/mod.md b/thaw/src/toast/docs/mod.md index 93ac72b..151717a 100644 --- a/thaw/src/toast/docs/mod.md +++ b/thaw/src/toast/docs/mod.md @@ -95,6 +95,38 @@ view! { } ``` +### Dismiss Toast + +```rust demo +let toaster = ToasterInjection::expect_context(); +let id = uuid::Uuid::new_v4(); + + +let dispatch = move |_| { + toaster.dispatch_toast(move || view! { + + "Email sent" + + "This is a toast body" + + "Subtitle" + + + + },ToastOptions::default().with_id(id)); +}; + +let dismiss = move |_| { + toaster.dismiss_toast(id); +}; + +view! { + + +} +``` + + ### Toast Title Media ```rust demo diff --git a/thaw/src/toast/mod.rs b/thaw/src/toast/mod.rs index 9ed0c8a..3014c96 100644 --- a/thaw/src/toast/mod.rs +++ b/thaw/src/toast/mod.rs @@ -17,17 +17,22 @@ use wasm_bindgen::UnwrapThrowExt; #[derive(Clone, Copy)] pub struct ToasterInjection { - sender: StoredValue>, + sender: StoredValue>, trigger: StoredValue, } +enum ToasterMessage { + Dispatch(Children, ToastOptions), + Dismiss(uuid::Uuid), +} + impl ToasterInjection { pub fn expect_context() -> Self { expect_context() } pub fn channel() -> (Self, ToasterReceiver) { - let (sender, receiver) = channel::<(Children, ToastOptions)>(); + let (sender, receiver) = channel::(); let trigger = ArcTrigger::new(); ( @@ -39,6 +44,15 @@ impl ToasterInjection { ) } + pub fn dismiss_toast(&self, toast_id: uuid::Uuid) { + self.sender.with_value(|sender| { + sender + .send(ToasterMessage::Dismiss(toast_id)) + .unwrap_throw() + }); + self.trigger.with_value(|trigger| trigger.notify()); + } + pub fn dispatch_toast(&self, children: C, options: ToastOptions) where C: FnOnce() -> IV + Send + 'static, @@ -46,7 +60,10 @@ impl ToasterInjection { { self.sender.with_value(|sender| { sender - .send((Box::new(move || children().into_any()), options)) + .send(ToasterMessage::Dispatch( + Box::new(move || children().into_any()), + options, + )) .unwrap_throw() }); self.trigger.with_value(|trigger| trigger.notify()); @@ -54,16 +71,16 @@ impl ToasterInjection { } pub struct ToasterReceiver { - receiver: Receiver<(Children, ToastOptions)>, + receiver: Receiver, trigger: ArcTrigger, } impl ToasterReceiver { - pub fn new(receiver: Receiver<(Children, ToastOptions)>, trigger: ArcTrigger) -> Self { + fn new(receiver: Receiver, trigger: ArcTrigger) -> Self { Self { receiver, trigger } } - pub fn try_recv(&self) -> TryIter<'_, (Children, ToastOptions)> { + fn try_recv(&self) -> TryIter<'_, ToasterMessage> { self.trigger.track(); self.receiver.try_iter() } diff --git a/thaw/src/toast/toast.rs b/thaw/src/toast/toast.rs index d1c9b0e..2f7dbfb 100644 --- a/thaw/src/toast/toast.rs +++ b/thaw/src/toast/toast.rs @@ -63,6 +63,12 @@ impl Default for ToastOptions { } impl ToastOptions { + /// The id that will be assigned to this toast. + pub fn with_id(mut self, id: uuid::Uuid) -> Self { + self.id = id; + self + } + /// The position the toast should render. pub fn with_position(mut self, position: ToastPosition) -> Self { self.position = Some(position); diff --git a/thaw/src/toast/toaster.rs b/thaw/src/toast/toaster.rs index f6838c7..048d2af 100644 --- a/thaw/src/toast/toaster.rs +++ b/thaw/src/toast/toaster.rs @@ -1,5 +1,5 @@ use super::{ToastIntent, ToastOptions, ToastPosition, ToasterReceiver}; -use crate::ConfigInjection; +use crate::{toast::ToasterMessage, ConfigInjection}; use leptos::{context::Provider, either::Either, html, prelude::*}; use send_wrapper::SendWrapper; use std::{collections::HashMap, time::Duration}; @@ -22,9 +22,11 @@ pub fn Toaster( let bottom_id_list = RwSignal::>::new(Default::default()); let bottom_start_id_list = RwSignal::>::new(Default::default()); let bottom_end_id_list = RwSignal::>::new(Default::default()); - let toasts = StoredValue::, ToastOptions)>>::new( - Default::default(), - ); + let toasts = StoredValue::< + HashMap, ToastOptions, RwSignal)>, + >::new(Default::default()); + let toast_show_list = + StoredValue::>>::new(Default::default()); let id_list = move |position: &ToastPosition| match position { ToastPosition::Top => top_id_list, @@ -35,26 +37,38 @@ pub fn Toaster( ToastPosition::BottomEnd => bottom_end_id_list, }; + let owner = Owner::current().unwrap(); Effect::new(move |_| { - for (view, mut options) in receiver.try_recv() { - if options.position.is_none() { - options.position = Some(position); - } - if options.timeout.is_none() { - options.timeout = Some(timeout); - } - if options.intent.is_none() { - options.intent = Some(intent); - } + for message in receiver.try_recv() { + match message { + ToasterMessage::Dispatch(view, mut options) => { + if options.position.is_none() { + options.position = Some(position); + } + if options.timeout.is_none() { + options.timeout = Some(timeout); + } + if options.intent.is_none() { + options.intent = Some(intent); + } - let list = id_list(&options.position.unwrap_throw()); - let id = options.id; - toasts.update_value(|map| { - map.insert(id, (SendWrapper::new(view), options)); - }); - list.update(|list| { - list.push(id); - }); + let list = id_list(&options.position.unwrap_throw()); + let id = options.id; + let is_show = owner.with(|| RwSignal::new(true)); + toasts.update_value(|map| { + map.insert(id, (SendWrapper::new(view), options, is_show)); + }); + toast_show_list.update_value(|map| { + map.insert(id, is_show); + }); + list.update(|list| { + list.push(id); + }); + } + ToasterMessage::Dismiss(toast_id) => { + toast_show_list.with_value(|map| map.get(&toast_id).unwrap_throw().set(false)); + } + } } }); @@ -66,6 +80,10 @@ pub fn Toaster( }; list.remove(index); }); + let is_show = toast_show_list.try_update_value(|map| { map.remove(&id) } ).flatten(); + if let Some(is_show) = is_show { + is_show.dispose(); + } })); view! { @@ -76,12 +94,19 @@ pub fn Toaster( >
- {if let Some((view, options)) = toasts + {if let Some((view, options, is_show)) = toasts .try_update_value(|map| { map.remove(&id) }) .flatten() { Either::Left( - view! { }, + view! { + + }, ) } else { Either::Right(()) @@ -90,12 +115,19 @@ pub fn Toaster(
- {if let Some((view, options)) = toasts + {if let Some((view, options, is_show)) = toasts .try_update_value(|map| { map.remove(&id) }) .flatten() { Either::Left( - view! { }, + view! { + + }, ) } else { Either::Right(()) @@ -104,12 +136,19 @@ pub fn Toaster(
- {if let Some((view, options)) = toasts + {if let Some((view, options, is_show)) = toasts .try_update_value(|map| { map.remove(&id) }) .flatten() { Either::Left( - view! { }, + view! { + + }, ) } else { Either::Right(()) @@ -118,12 +157,19 @@ pub fn Toaster(
- {if let Some((view, options)) = toasts + {if let Some((view, options, is_show)) = toasts .try_update_value(|map| { map.remove(&id) }) .flatten() { Either::Left( - view! { }, + view! { + + }, ) } else { Either::Right(()) @@ -132,12 +178,19 @@ pub fn Toaster(
- {if let Some((view, options)) = toasts + {if let Some((view, options, is_show)) = toasts .try_update_value(|map| { map.remove(&id) }) .flatten() { Either::Left( - view! { }, + view! { + + }, ) } else { Either::Right(()) @@ -146,12 +199,19 @@ pub fn Toaster(
- {if let Some((view, options)) = toasts + {if let Some((view, options, is_show)) = toasts .try_update_value(|map| { map.remove(&id) }) .flatten() { Either::Left( - view! { }, + view! { + + }, ) } else { Either::Right(()) @@ -168,9 +228,9 @@ fn ToasterContainer( options: ToastOptions, #[prop(into)] on_close: StoredValue>, children: Children, + is_show: RwSignal, ) -> impl IntoView { let container_ref = NodeRef::::new(); - let is_show = RwSignal::new(true); let ToastOptions { id, timeout, @@ -178,6 +238,7 @@ fn ToasterContainer( intent, .. } = options; + let timeout = timeout.unwrap_throw(); let position = position.unwrap_throw(); let intent = intent.unwrap_throw();