toast.rs
⎇
Raw
1use std::time::Duration;
2
3use leptos::prelude::*;
4use wasm_bindgen_futures::spawn_local;
5
6/// Context type: read/write access to the current toast.
7///
8/// The state is `(message, is_error)`: errors get their own styling so they
9/// do not read like routine confirmations.
10#[derive(Copy, Clone)]
11pub struct ToastMsg(pub RwSignal<Option<(String, bool)>>);
12
13pub fn provide_toast() -> ToastMsg {
14 let toast = RwSignal::new(Option::<(String, bool)>::None);
15 let t = ToastMsg(toast);
16 provide_context(t);
17 t
18}
19
20/// Show a toast that disappears after ~3 seconds.
21pub fn show(toast: ToastMsg, msg: impl Into<String>) {
22 set(toast, msg.into(), false);
23}
24
25/// Show a failure toast (danger styling).
26pub fn show_error(toast: ToastMsg, msg: impl Into<String>) {
27 set(toast, msg.into(), true);
28}
29
30fn set(toast: ToastMsg, msg: String, is_error: bool) {
31 toast.0.set(Some((msg.clone(), is_error)));
32 spawn_local(async move {
33 gloo_timers::future::sleep(Duration::from_secs(3)).await;
34 if toast.0.get().as_ref().map(|(m, _)| m.as_str()) == Some(msg.as_str()) {
35 toast.0.set(None);
36 }
37 });
38}
39
40#[component]
41pub fn ToastView(toast: ToastMsg) -> impl IntoView {
42 view! {
43 <Show when=move || toast.0.get().is_some()>
44 <div class="toast" class:toast-error=move || toast.0.get().is_some_and(|(_, e)| e)>
45 {move || toast.0.get().map(|(m, _)| m).unwrap_or_default()}
46 </div>
47 </Show>
48 }
49}
50