use dioxus::prelude::*; #[derive(Clone, PartialEq)] pub enum ToastType { Success, Error, Info, } #[derive(Clone, PartialEq)] pub struct ToastMessage { pub id: usize, pub message: String, pub toast_type: ToastType, } #[derive(Clone, Copy)] pub struct Toasts { items: Signal>, next_id: Signal, } impl Default for Toasts { fn default() -> Self { Self::new() } } impl Toasts { pub fn new() -> Self { Self { items: Signal::new(vec![]), next_id: Signal::new(0), } } pub fn push(&mut self, toast_type: ToastType, message: impl Into) { let id = *self.next_id.read(); *self.next_id.write() = id + 1; self.items.write().push(ToastMessage { id, message: message.into(), toast_type, }); #[cfg(feature = "web")] { let mut items = self.items; spawn(async move { gloo_timers::future::TimeoutFuture::new(4_000).await; items.write().retain(|t| t.id != id); }); } } pub fn remove(&mut self, id: usize) { self.items.write().retain(|t| t.id != id); } } #[component] pub fn ToastContainer() -> Element { let mut toasts = use_context::(); let items = toasts.items.read(); rsx! { div { class: "toast-container", for toast in items.iter() { { let toast_id = toast.id; let type_class = match toast.toast_type { ToastType::Success => "toast-success", ToastType::Error => "toast-error", ToastType::Info => "toast-info", }; rsx! { div { key: "{toast_id}", class: "toast {type_class}", span { "{toast.message}" } button { class: "toast-dismiss", onclick: move |_| toasts.remove(toast_id), "\u{00d7}" } } } } } } } }