mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
feat: optimize Toast component
This commit is contained in:
parent
8b122d0986
commit
ca13969a01
4 changed files with 160 additions and 29 deletions
|
@ -126,11 +126,11 @@ fn TheProvider(children: Children) -> impl IntoView {
|
|||
|
||||
view! {
|
||||
<ConfigProvider>
|
||||
// <ToasterProvider>
|
||||
<ToasterProvider>
|
||||
<LoadingBarProvider>
|
||||
{children()}
|
||||
</LoadingBarProvider>
|
||||
// </ToasterProvider>
|
||||
</ToasterProvider>
|
||||
</ConfigProvider>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use leptos::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
#[component]
|
||||
pub fn Toast(children: Children) -> impl IntoView {
|
||||
|
@ -9,14 +10,14 @@ pub fn Toast(children: Children) -> impl IntoView {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum ToastPosition {
|
||||
Top,
|
||||
TopStart,
|
||||
#[default]
|
||||
TopEnd,
|
||||
Bottom,
|
||||
BottomStart,
|
||||
#[default]
|
||||
BottomEnd,
|
||||
}
|
||||
|
||||
|
@ -35,15 +36,17 @@ impl ToastPosition {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct ToastOptions {
|
||||
pub id: uuid::Uuid,
|
||||
pub postition: Option<ToastPosition>,
|
||||
pub(crate) id: uuid::Uuid,
|
||||
pub(crate) position: Option<ToastPosition>,
|
||||
pub(crate) timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl Default for ToastOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
id: uuid::Uuid::new_v4(),
|
||||
postition: None,
|
||||
position: None,
|
||||
timeout: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
div.thaw-toaster-container {
|
||||
div.thaw-toaster {
|
||||
z-index: 1000000;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
@ -13,15 +13,45 @@ div.thaw-toaster-container {
|
|||
color: var(--colorNeutralForeground1);
|
||||
}
|
||||
|
||||
.thaw-toaster {
|
||||
.thaw-toaster-container {
|
||||
bottom: 16px;
|
||||
right: 20px;
|
||||
|
||||
|
||||
position: fixed;
|
||||
width: 292px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.thaw-toaster-container.fade-in-height-expand-transition-leave-from,
|
||||
.thaw-toaster-container.fade-in-height-expand-transition-enter-to {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.thaw-toaster-container.fade-in-height-expand-transition-leave-to,
|
||||
.thaw-toaster-container.fade-in-height-expand-transition-enter-from {
|
||||
transform: scale(0.85);
|
||||
opacity: 0;
|
||||
margin-bottom: 0 !important;
|
||||
max-height: 0 !important;
|
||||
}
|
||||
|
||||
.thaw-toaster-container.fade-in-height-expand-transition-leave-active {
|
||||
overflow: visible;
|
||||
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0s,
|
||||
opacity 0.3s cubic-bezier(0, 0, 0.2, 1) 0s,
|
||||
margin-bottom 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0s,
|
||||
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.thaw-toaster-container.fade-in-height-expand-transition-enter-active {
|
||||
overflow: visible;
|
||||
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.3s cubic-bezier(0.4, 0, 1, 1),
|
||||
margin-bottom 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.thaw-toast {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
|
|
|
@ -1,36 +1,134 @@
|
|||
use super::{ToastPosition, ToasterReceiver};
|
||||
use leptos::prelude::*;
|
||||
use thaw_components::Teleport;
|
||||
use thaw_utils::{class_list, mount_style};
|
||||
use super::{ToastOptions, ToastPosition, ToasterReceiver};
|
||||
use leptos::{either::Either, html, prelude::*, tachys::view::any_view::AnyView};
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::{collections::HashMap, time::Duration};
|
||||
use thaw_components::{CSSTransition, Teleport};
|
||||
use thaw_utils::mount_style;
|
||||
|
||||
#[component]
|
||||
pub fn Toaster(
|
||||
receiver: ToasterReceiver,
|
||||
#[prop(optional)] position: ToastPosition,
|
||||
#[prop(default = Duration::from_secs(3))] timeout: Duration,
|
||||
) -> impl IntoView {
|
||||
mount_style("toaster", include_str!("./toaster.css"));
|
||||
// let toast_list = RwSignal::new(vec![]);
|
||||
let bottom_start_id_list = RwSignal::<Vec<uuid::Uuid>>::new(Default::default());
|
||||
let toasts = StoredValue::<HashMap<uuid::Uuid, (SendWrapper<AnyView<Dom>>, ToastOptions)>>::new(
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
Effect::new(move |_| {
|
||||
for view in receiver.try_recv() {
|
||||
// toast_list.update(move |list| {
|
||||
// list.push(view.0);
|
||||
// });
|
||||
for (view, mut options) in receiver.try_recv() {
|
||||
if options.position.is_none() {
|
||||
options.position = Some(position);
|
||||
}
|
||||
if options.timeout.is_none() {
|
||||
options.timeout = Some(timeout);
|
||||
}
|
||||
match options.position.unwrap() {
|
||||
ToastPosition::Top => todo!(),
|
||||
ToastPosition::TopStart => todo!(),
|
||||
ToastPosition::TopEnd => todo!(),
|
||||
ToastPosition::Bottom => todo!(),
|
||||
ToastPosition::BottomStart => {
|
||||
let id = options.id;
|
||||
toasts.update_value(|map| {
|
||||
map.insert(id, (SendWrapper::new(view), options));
|
||||
});
|
||||
bottom_start_id_list.update(|list| {
|
||||
list.push(id);
|
||||
});
|
||||
}
|
||||
ToastPosition::BottomEnd => todo!(),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let on_close = move |(id, position)| match position {
|
||||
ToastPosition::Top => todo!(),
|
||||
ToastPosition::TopStart => todo!(),
|
||||
ToastPosition::TopEnd => todo!(),
|
||||
ToastPosition::Bottom => todo!(),
|
||||
ToastPosition::BottomStart => {
|
||||
bottom_start_id_list.update(move |list| {
|
||||
let Some(index) = list.iter().position(|item_id| &id == item_id) else {
|
||||
return;
|
||||
};
|
||||
list.remove(index);
|
||||
});
|
||||
}
|
||||
ToastPosition::BottomEnd => todo!(),
|
||||
};
|
||||
|
||||
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 class="thaw-config-provider thaw-toaster">
|
||||
<For
|
||||
each=move || bottom_start_id_list.get()
|
||||
key=|id| id.clone()
|
||||
let:id
|
||||
>
|
||||
{
|
||||
if let Some((view, options)) = toasts.try_update_value(|map| { map.remove(&id) }).flatten() {
|
||||
Either::Left(view! { <ToasterContainer on_close view=view.take() options/> })
|
||||
} else {
|
||||
Either::Right(())
|
||||
}
|
||||
}
|
||||
</For>
|
||||
</div>
|
||||
</Teleport>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn ToasterContainer(
|
||||
view: AnyView<Dom>,
|
||||
options: ToastOptions,
|
||||
#[prop(into)] on_close: Callback<(uuid::Uuid, ToastPosition)>,
|
||||
) -> impl IntoView {
|
||||
let container_ref = NodeRef::<html::Div>::new();
|
||||
let is_show = RwSignal::new(true);
|
||||
let ToastOptions {
|
||||
id,
|
||||
timeout,
|
||||
position,
|
||||
} = options;
|
||||
let timeout = timeout.unwrap();
|
||||
let position = position.unwrap();
|
||||
|
||||
if !timeout.is_zero() {
|
||||
set_timeout(
|
||||
move || {
|
||||
is_show.set(false);
|
||||
},
|
||||
timeout,
|
||||
);
|
||||
}
|
||||
|
||||
let on_before_leave = move |_| {
|
||||
let Some(el) = container_ref.get_untracked() else {
|
||||
return;
|
||||
};
|
||||
el.style(("max-height", format!("{}px", el.offset_height())));
|
||||
};
|
||||
let on_after_leave = move |_| {
|
||||
request_animation_frame(move || on_close.call((id, position)));
|
||||
};
|
||||
|
||||
view! {
|
||||
<CSSTransition
|
||||
node_ref=container_ref
|
||||
name="fade-in-height-expand-transition"
|
||||
show=is_show
|
||||
appear=true
|
||||
on_before_leave=on_before_leave
|
||||
on_after_leave=on_after_leave
|
||||
let:_
|
||||
>
|
||||
<div class="thaw-toaster-container" node_ref=container_ref>
|
||||
{view}
|
||||
</div>
|
||||
</CSSTransition>
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue