mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
Toast dismiss (#270)
* broken toaster * toast dismiss * toaster test * fixed dismiss * cargo fmt * dipose signal
This commit is contained in:
parent
a03226f202
commit
b831008e09
6 changed files with 159 additions and 41 deletions
|
@ -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"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::components::{Demo, DemoCode};
|
||||
use leptos::{ev, prelude::*};
|
||||
use thaw::*;
|
||||
use uuid;
|
||||
|
||||
demo_markdown::include_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! {
|
||||
<Toast>
|
||||
<ToastTitle>"Email sent"</ToastTitle>
|
||||
<ToastBody>
|
||||
"This is a toast body"
|
||||
<ToastBodySubtitle slot>
|
||||
"Subtitle"
|
||||
</ToastBodySubtitle>
|
||||
</ToastBody>
|
||||
</Toast>
|
||||
},ToastOptions::default().with_id(id));
|
||||
};
|
||||
|
||||
let dismiss = move |_| {
|
||||
toaster.dismiss_toast(id);
|
||||
};
|
||||
|
||||
view! {
|
||||
<Button on_click=dispatch>"Show toast"</Button>
|
||||
<Button on_click=dismiss>"Hide toast"</Button>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Toast Title Media
|
||||
|
||||
```rust demo
|
||||
|
|
|
@ -17,17 +17,22 @@ use wasm_bindgen::UnwrapThrowExt;
|
|||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ToasterInjection {
|
||||
sender: StoredValue<Sender<(Children, ToastOptions)>>,
|
||||
sender: StoredValue<Sender<ToasterMessage>>,
|
||||
trigger: StoredValue<ArcTrigger>,
|
||||
}
|
||||
|
||||
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::<ToasterMessage>();
|
||||
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<C, IV>(&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<ToasterMessage>,
|
||||
trigger: ArcTrigger,
|
||||
}
|
||||
|
||||
impl ToasterReceiver {
|
||||
pub fn new(receiver: Receiver<(Children, ToastOptions)>, trigger: ArcTrigger) -> Self {
|
||||
fn new(receiver: Receiver<ToasterMessage>, 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()
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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::<Vec<uuid::Uuid>>::new(Default::default());
|
||||
let bottom_start_id_list = RwSignal::<Vec<uuid::Uuid>>::new(Default::default());
|
||||
let bottom_end_id_list = RwSignal::<Vec<uuid::Uuid>>::new(Default::default());
|
||||
let toasts = StoredValue::<HashMap<uuid::Uuid, (SendWrapper<Children>, ToastOptions)>>::new(
|
||||
Default::default(),
|
||||
);
|
||||
let toasts = StoredValue::<
|
||||
HashMap<uuid::Uuid, (SendWrapper<Children>, ToastOptions, RwSignal<bool>)>,
|
||||
>::new(Default::default());
|
||||
let toast_show_list =
|
||||
StoredValue::<HashMap<uuid::Uuid, RwSignal<bool>>>::new(Default::default());
|
||||
|
||||
let id_list = move |position: &ToastPosition| match position {
|
||||
ToastPosition::Top => top_id_list,
|
||||
|
@ -35,8 +37,11 @@ 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() {
|
||||
for message in receiver.try_recv() {
|
||||
match message {
|
||||
ToasterMessage::Dispatch(view, mut options) => {
|
||||
if options.position.is_none() {
|
||||
options.position = Some(position);
|
||||
}
|
||||
|
@ -49,13 +54,22 @@ pub fn Toaster(
|
|||
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let on_close = StoredValue::new(ArcTwoCallback::new(move |id, position| {
|
||||
|
@ -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(
|
|||
>
|
||||
<div class="thaw-toaster thaw-toaster--top">
|
||||
<For each=move || top_id_list.get() key=|id| id.clone() let:id>
|
||||
{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! { <ToasterContainer on_close children=view.take() options /> },
|
||||
view! {
|
||||
<ToasterContainer
|
||||
on_close
|
||||
children=view.take()
|
||||
options
|
||||
is_show
|
||||
/>
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Either::Right(())
|
||||
|
@ -90,12 +115,19 @@ pub fn Toaster(
|
|||
</div>
|
||||
<div class="thaw-toaster thaw-toaster--top-start">
|
||||
<For each=move || top_start_id_list.get() key=|id| id.clone() let:id>
|
||||
{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! { <ToasterContainer on_close children=view.take() options /> },
|
||||
view! {
|
||||
<ToasterContainer
|
||||
on_close
|
||||
children=view.take()
|
||||
options
|
||||
is_show
|
||||
/>
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Either::Right(())
|
||||
|
@ -104,12 +136,19 @@ pub fn Toaster(
|
|||
</div>
|
||||
<div class="thaw-toaster thaw-toaster--top-end">
|
||||
<For each=move || top_end_id_list.get() key=|id| id.clone() let:id>
|
||||
{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! { <ToasterContainer on_close children=view.take() options /> },
|
||||
view! {
|
||||
<ToasterContainer
|
||||
on_close
|
||||
children=view.take()
|
||||
options
|
||||
is_show
|
||||
/>
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Either::Right(())
|
||||
|
@ -118,12 +157,19 @@ pub fn Toaster(
|
|||
</div>
|
||||
<div class="thaw-toaster thaw-toaster--bottom">
|
||||
<For each=move || bottom_id_list.get() key=|id| id.clone() let:id>
|
||||
{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! { <ToasterContainer on_close children=view.take() options /> },
|
||||
view! {
|
||||
<ToasterContainer
|
||||
on_close
|
||||
children=view.take()
|
||||
options
|
||||
is_show
|
||||
/>
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Either::Right(())
|
||||
|
@ -132,12 +178,19 @@ pub fn Toaster(
|
|||
</div>
|
||||
<div class="thaw-toaster thaw-toaster--bottom-start">
|
||||
<For each=move || bottom_start_id_list.get() key=|id| id.clone() let:id>
|
||||
{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! { <ToasterContainer on_close children=view.take() options /> },
|
||||
view! {
|
||||
<ToasterContainer
|
||||
on_close
|
||||
children=view.take()
|
||||
options
|
||||
is_show
|
||||
/>
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Either::Right(())
|
||||
|
@ -146,12 +199,19 @@ pub fn Toaster(
|
|||
</div>
|
||||
<div class="thaw-toaster thaw-toaster--bottom-end">
|
||||
<For each=move || bottom_end_id_list.get() key=|id| id.clone() let:id>
|
||||
{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! { <ToasterContainer on_close children=view.take() options /> },
|
||||
view! {
|
||||
<ToasterContainer
|
||||
on_close
|
||||
children=view.take()
|
||||
options
|
||||
is_show
|
||||
/>
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Either::Right(())
|
||||
|
@ -168,9 +228,9 @@ fn ToasterContainer(
|
|||
options: ToastOptions,
|
||||
#[prop(into)] on_close: StoredValue<ArcTwoCallback<uuid::Uuid, ToastPosition>>,
|
||||
children: Children,
|
||||
is_show: RwSignal<bool>,
|
||||
) -> impl IntoView {
|
||||
let container_ref = NodeRef::<html::Div>::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();
|
||||
|
|
Loading…
Add table
Reference in a new issue