feat: optimize Toast component

This commit is contained in:
luoxiao 2024-07-03 17:27:29 +08:00
parent 82e6f68018
commit c57748f9ea
7 changed files with 177 additions and 13 deletions

View file

@ -110,9 +110,9 @@ fn TheProvider(children: Children) -> impl IntoView {
view! { view! {
<ConfigProvider theme> <ConfigProvider theme>
// <MessageProvider> <ToasterProvider>
<LoadingBarProvider>{children()}</LoadingBarProvider> <LoadingBarProvider>{children()}</LoadingBarProvider>
// </MessageProvider> </ToasterProvider>
</ConfigProvider> </ConfigProvider>
} }
} }

View file

@ -1,9 +1,14 @@
# Toast # Toast
```rust ```rust demo
let toaster = ToasterInjection::use_();
let on_click = move |_| {
toaster.dispatch_toast(view! { <span>"Hello"</span> }.into_any(), Default::default());
};
view! { view! {
<Toaster toaster_id=toaster_id /> <Button on_click=on_click>"Make toast"</Button>
<Button>"Make toast"</Button>
} }
``` ```

View file

@ -1,7 +1,59 @@
mod toast; mod toast;
mod toast_title;
mod toaster;
mod toast_body; mod toast_body;
mod toast_footer; mod toast_footer;
mod toast_title;
mod toaster;
mod toaster_provider;
pub use toast::*;
pub use toast_title::*; 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<AnyElement>, ToastOptions)>,
trigger: Trigger,
}
impl ToasterInjection {
pub fn use_() -> Self {
expect_context()
}
pub fn channel() -> (Self, ToasterReceiver) {
let (sender, receiver) = channel::<(HtmlElement<AnyElement>, ToastOptions)>();
let trigger = Trigger::new();
(
Self { sender, trigger },
ToasterReceiver::new(receiver, trigger),
)
}
pub fn dispatch_toast(&self, any_element: HtmlElement<AnyElement>, options: ToastOptions) {
self.sender.send((any_element, options)).unwrap();
self.trigger.notify();
}
}
pub struct ToasterReceiver {
receiver: Receiver<(HtmlElement<AnyElement>, ToastOptions)>,
trigger: Trigger,
}
impl ToasterReceiver {
pub fn new(
receiver: Receiver<(HtmlElement<AnyElement>, ToastOptions)>,
trigger: Trigger,
) -> Self {
Self { receiver, trigger }
}
pub fn try_recv(&self) -> TryIter<'_, (HtmlElement<AnyElement>, ToastOptions)> {
self.trigger.track();
self.receiver.try_iter()
}
}

View file

@ -1,8 +1,49 @@
use leptos::*; use leptos::*;
#[component] #[component]
pub fn Toast(id: String) -> impl IntoView { pub fn Toast(children: Children) -> impl IntoView {
view! { view! {
<div class="thaw-toast">
{children()}
</div>
}
}
#[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<ToastPosition>,
}
impl Default for ToastOptions {
fn default() -> Self {
Self {
id: uuid::Uuid::new_v4(),
postition: None,
}
} }
} }

View file

@ -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 { .thaw-toast {
display: grid; display: grid;
grid-template-columns: auto 1fr auto; grid-template-columns: auto 1fr auto;

View file

@ -1,10 +1,36 @@
use super::{ToastPosition, ToasterReceiver};
use leptos::*; use leptos::*;
use thaw_utils::mount_style; use thaw_components::Teleport;
use thaw_utils::{class_list, mount_style};
#[component] #[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")); 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! {
<Teleport>
<div class="thaw-config-provider thaw-toaster-container">
<For
each=move || toast_list.get()
key=|toast| toast.1.id
let:toast
>
<div class=class_list!["thaw-toaster", "thaw-toaster"]>
{toast.0}
</div>
</For>
</div>
</Teleport>
} }
} }

View file

@ -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! {
<Toaster receiver position/>
<Provider value=injection>
{children()}
</Provider>
}
}