diff --git a/demo/src/app.rs b/demo/src/app.rs index 38818b1..d120200 100644 --- a/demo/src/app.rs +++ b/demo/src/app.rs @@ -110,9 +110,9 @@ fn TheProvider(children: Children) -> impl IntoView { view! { - // - {children()} - // + + {children()} + } } diff --git a/demo_markdown/docs/toast/mod.md b/demo_markdown/docs/toast/mod.md index d77d29b..8bda118 100644 --- a/demo_markdown/docs/toast/mod.md +++ b/demo_markdown/docs/toast/mod.md @@ -1,9 +1,14 @@ # Toast -```rust +```rust demo +let toaster = ToasterInjection::use_(); + +let on_click = move |_| { + toaster.dispatch_toast(view! { "Hello" }.into_any(), Default::default()); +}; + view! { - - + } ``` \ No newline at end of file diff --git a/thaw/src/toast/mod.rs b/thaw/src/toast/mod.rs index 4baa1df..2a74025 100644 --- a/thaw/src/toast/mod.rs +++ b/thaw/src/toast/mod.rs @@ -1,7 +1,59 @@ mod toast; -mod toast_title; -mod toaster; mod toast_body; mod toast_footer; +mod toast_title; +mod toaster; +mod toaster_provider; +pub use toast::*; pub use toast_title::*; +pub use toaster_provider::*; + +use leptos::{html::AnyElement, *}; +use std::sync::mpsc::{channel, Receiver, Sender, TryIter}; + +#[derive(Clone)] +pub struct ToasterInjection { + sender: Sender<(HtmlElement, ToastOptions)>, + trigger: Trigger, +} + +impl ToasterInjection { + pub fn use_() -> Self { + expect_context() + } + + pub fn channel() -> (Self, ToasterReceiver) { + let (sender, receiver) = channel::<(HtmlElement, ToastOptions)>(); + let trigger = Trigger::new(); + + ( + Self { sender, trigger }, + ToasterReceiver::new(receiver, trigger), + ) + } + + pub fn dispatch_toast(&self, any_element: HtmlElement, options: ToastOptions) { + self.sender.send((any_element, options)).unwrap(); + self.trigger.notify(); + } +} + +pub struct ToasterReceiver { + receiver: Receiver<(HtmlElement, ToastOptions)>, + trigger: Trigger, +} + +impl ToasterReceiver { + pub fn new( + receiver: Receiver<(HtmlElement, ToastOptions)>, + trigger: Trigger, + ) -> Self { + Self { receiver, trigger } + } + + pub fn try_recv(&self) -> TryIter<'_, (HtmlElement, ToastOptions)> { + self.trigger.track(); + self.receiver.try_iter() + } +} diff --git a/thaw/src/toast/toast.rs b/thaw/src/toast/toast.rs index 3bf9735..399fd5c 100644 --- a/thaw/src/toast/toast.rs +++ b/thaw/src/toast/toast.rs @@ -1,8 +1,49 @@ use leptos::*; #[component] -pub fn Toast(id: String) -> impl IntoView { +pub fn Toast(children: Children) -> impl IntoView { view! { - +
+ {children()} +
+ } +} + +#[derive(Default, Clone)] +pub enum ToastPosition { + Top, + TopStart, + #[default] + TopEnd, + Bottom, + BottomStart, + BottomEnd, +} + +impl ToastPosition { + pub fn as_str(&self) -> &'static str { + match self { + Self::Top => "top", + Self::TopStart => "top-left", + Self::TopEnd => "top-right", + Self::Bottom => "bottom", + Self::BottomStart => "bottom-left", + Self::BottomEnd => "bottom-right", + } + } +} + +#[derive(Clone)] +pub struct ToastOptions { + pub id: uuid::Uuid, + pub postition: Option, +} + +impl Default for ToastOptions { + fn default() -> Self { + Self { + id: uuid::Uuid::new_v4(), + postition: None, + } } } diff --git a/thaw/src/toast/toaster.css b/thaw/src/toast/toaster.css index 972920f..d323a47 100644 --- a/thaw/src/toast/toaster.css +++ b/thaw/src/toast/toaster.css @@ -1,3 +1,27 @@ +div.thaw-toaster-container { + z-index: 1000000; + position: absolute; + top: 0px; + left: 0px; + right: 0px; + line-height: var(--lineHeightBase300); + font-weight: var(--fontWeightRegular); + font-size: var(--fontSizeBase300); + font-family: var(--fontFamilyBase); + text-align: left; + background-color: var(--colorNeutralBackground1); + color: var(--colorNeutralForeground1); +} + +.thaw-toaster { + bottom: 16px; + right: 20px; + + position: fixed; + width: 292px; + pointer-events: none; +} + .thaw-toast { display: grid; grid-template-columns: auto 1fr auto; diff --git a/thaw/src/toast/toaster.rs b/thaw/src/toast/toaster.rs index b668d83..3f66118 100644 --- a/thaw/src/toast/toaster.rs +++ b/thaw/src/toast/toaster.rs @@ -1,10 +1,36 @@ +use super::{ToastPosition, ToasterReceiver}; use leptos::*; -use thaw_utils::mount_style; +use thaw_components::Teleport; +use thaw_utils::{class_list, mount_style}; #[component] -pub fn Toaster(toaster_id: String) -> impl IntoView { +pub fn Toaster( + receiver: ToasterReceiver, + #[prop(optional)] position: ToastPosition, +) -> impl IntoView { mount_style("toaster", include_str!("./toaster.css")); - view! { + let toast_list = RwSignal::new(vec![]); + Effect::new(move |_| { + for view in receiver.try_recv() { + toast_list.update(move |list| { + list.push(view); + }); + } + }); + view! { + +
+ +
+ {toast.0} +
+
+
+
} } diff --git a/thaw/src/toast/toaster_provider.rs b/thaw/src/toast/toaster_provider.rs new file mode 100644 index 0000000..504bc86 --- /dev/null +++ b/thaw/src/toast/toaster_provider.rs @@ -0,0 +1,16 @@ +use super::{toaster::Toaster, ToastPosition, ToasterInjection}; +use leptos::*; + +#[component] +pub fn ToasterProvider( + #[prop(optional)] position: ToastPosition, + children: Children, +) -> impl IntoView { + let (injection, receiver) = ToasterInjection::channel(); + view! { + + + {children()} + + } +}