From a03226f202276361e409d40ba5f169c5b3468716 Mon Sep 17 00:00:00 2001 From: kandrelczyk Date: Tue, 24 Sep 2024 01:14:11 +0200 Subject: [PATCH] Toast intent (#269) * add toast intent support * toast intent * toast intent * fix CI --- demo/src/components/site_header.rs | 5 ++- demo/src/main.rs | 2 +- demo/src/pages/home.rs | 2 +- demo_markdown/src/markdown/mod.rs | 2 +- thaw/src/toast/docs/mod.md | 58 +++++++++++++++++++++++- thaw/src/toast/toast.rs | 3 +- thaw/src/toast/toast_title.rs | 71 +++++++++++++++++++++++------- thaw/src/toast/toaster.css | 17 ++++++- thaw/src/toast/toaster.rs | 18 +++++--- thaw/src/toast/toaster_provider.rs | 7 ++- 10 files changed, 154 insertions(+), 31 deletions(-) diff --git a/demo/src/components/site_header.rs b/demo/src/components/site_header.rs index 4035bc2..69b7a94 100644 --- a/demo/src/components/site_header.rs +++ b/demo/src/components/site_header.rs @@ -79,7 +79,9 @@ pub fn SiteHeader() -> impl IntoView { { use leptos::ev; let handle = window_event_listener(ev::keydown, move |e| { - if js_sys::Reflect::has(&e, &js_sys::wasm_bindgen::JsValue::from_str("key")).unwrap_or_default() { + if js_sys::Reflect::has(&e, &js_sys::wasm_bindgen::JsValue::from_str("key")) + .unwrap_or_default() + { let key = e.key(); if key == *"/" { if let Some(auto_complete_ref) = auto_complete_ref.get_untracked() { @@ -88,7 +90,6 @@ pub fn SiteHeader() -> impl IntoView { } } } - }); on_cleanup(move || handle.remove()); } diff --git a/demo/src/main.rs b/demo/src/main.rs index 1237da3..12f40f0 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -8,6 +8,6 @@ use leptos::prelude::*; fn main() { let _ = console_log::init_with_level(log::Level::Debug); console_error_panic_hook::set_once(); - + mount_to_body(App) } diff --git a/demo/src/pages/home.rs b/demo/src/pages/home.rs index b7f5f50..44f34c8 100644 --- a/demo/src/pages/home.rs +++ b/demo/src/pages/home.rs @@ -1,8 +1,8 @@ use crate::components::SiteHeader; use leptos::prelude::*; +use leptos_meta::Style; use leptos_router::hooks::{use_navigate, use_query_map}; use thaw::*; -use leptos_meta::Style; #[component] pub fn Home() -> impl IntoView { diff --git a/demo_markdown/src/markdown/mod.rs b/demo_markdown/src/markdown/mod.rs index 7555099..abfb8f0 100644 --- a/demo_markdown/src/markdown/mod.rs +++ b/demo_markdown/src/markdown/mod.rs @@ -158,7 +158,7 @@ fn iter_nodes<'a>( NodeValue::Superscript => quote!("Superscript todo!!!"), NodeValue::Link(node_link) => { let NodeLink { url, title } = node_link; - + quote!( #(#children)* diff --git a/thaw/src/toast/docs/mod.md b/thaw/src/toast/docs/mod.md index 5020c2c..93ac72b 100644 --- a/thaw/src/toast/docs/mod.md +++ b/thaw/src/toast/docs/mod.md @@ -63,11 +63,67 @@ view! { } ``` +### Toast Intent + +```rust demo +let toaster = ToasterInjection::expect_context(); + +fn dispatch_toast(toaster: ToasterInjection, intent: ToastIntent) { + toaster.dispatch_toast(move || view! { + + "Email sent" + + "This is a toast body" + + "Subtitle" + + + + "Footer" + + + }, ToastOptions::default().with_intent(intent)); +}; + +view! { + + + + + + +} +``` + +### Toast Title Media + +```rust demo +let toaster = ToasterInjection::expect_context(); + +let on_click = move |_| { + toaster.dispatch_toast(move || view! { + + + "Loading" + + + + + + }, Default::default()); +}; + +view! { + +} +``` + ### ToasterProvider Props | Name | Type | Default | Description | | -------- | --------------- | -------------------------- | ------------------------------------- | | position | `ToastPosition` | `ToastPosition::BottomEnd` | The position the toast should render. | +| intent | `ToastIntent ` | `ToastPosition::Info` | The intent of the toast. | | children | `Children` | | | ### ToastOptions Props @@ -76,7 +132,7 @@ view! { | ------------- | --------------------------------------- | ------------------------------------- | | with_position | `Fn(mut self, position: ToastPosition)` | The position the toast should render. | | with_timeout | `Fn(mut self, timeout: Duration)` | Auto dismiss timeout in milliseconds. | -| with_intent | `Fn(mut self, intent: ToastIntent)` | Intent. | +| with_intent | `Fn(mut self, intent: ToastIntent)` | The intent of the toast. | ### Toast & ToastFooter Props diff --git a/thaw/src/toast/toast.rs b/thaw/src/toast/toast.rs index 4bb73a8..d1c9b0e 100644 --- a/thaw/src/toast/toast.rs +++ b/thaw/src/toast/toast.rs @@ -34,9 +34,10 @@ impl ToastPosition { } } -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub enum ToastIntent { Success, + #[default] Info, Warning, Error, diff --git a/thaw/src/toast/toast_title.rs b/thaw/src/toast/toast_title.rs index 2131f05..355bfd3 100644 --- a/thaw/src/toast/toast_title.rs +++ b/thaw/src/toast/toast_title.rs @@ -1,5 +1,8 @@ use leptos::{either::Either, prelude::*}; use thaw_components::OptionComp; +use thaw_utils::class_list; + +use crate::ToastIntent; #[component] pub fn ToastTitle( @@ -7,27 +10,63 @@ pub fn ToastTitle( children: Children, #[prop(optional)] toast_title_action: Option, ) -> impl IntoView { + let intent: ToastIntent = expect_context(); + view! { -
+
{if let Some(media) = toast_title_media { Either::Left((media.children)()) } else { - Either::Right( - view! { - - }, - ) + aria-hidden="true" + width="1em" + height="1em" + viewBox="0 0 20 20" + > + {match intent { + ToastIntent::Info => { + view! { + + }.into_any() + }, + ToastIntent::Success => { + view! { + + }.into_any() + }, + ToastIntent::Warning => { + view! { + + }.into_any() + }, + ToastIntent::Error => { + view! { + + }.into_any() + } + }} + + }, + ) + } }}
{children()}
diff --git a/thaw/src/toast/toaster.css b/thaw/src/toast/toaster.css index cf3b453..cf38822 100644 --- a/thaw/src/toast/toaster.css +++ b/thaw/src/toast/toaster.css @@ -108,10 +108,25 @@ div.thaw-toaster-wrapper { grid-column-end: 2; padding-right: 8px; font-size: 16px; - color: var(--colorNeutralForeground1); color: var(--colorNeutralForeground2); } +.thaw-toast-title__info { + color: var(--colorNeutralForeground2); +} + +.thaw-toast-title__success { + color: var(--colorStatusSuccessForeground1); +} + +.thaw-toast-title__warning { + color: var(--colorStatusWarningForeground3); +} + +.thaw-toast-title__error { + color: var(--colorStatusDangerForeground1); +} + .thaw-toast-title__media > svg { display: inline; line-height: 0; diff --git a/thaw/src/toast/toaster.rs b/thaw/src/toast/toaster.rs index 9461254..f6838c7 100644 --- a/thaw/src/toast/toaster.rs +++ b/thaw/src/toast/toaster.rs @@ -1,6 +1,6 @@ -use super::{ToastOptions, ToastPosition, ToasterReceiver}; +use super::{ToastIntent, ToastOptions, ToastPosition, ToasterReceiver}; use crate::ConfigInjection; -use leptos::{either::Either, html, prelude::*}; +use leptos::{context::Provider, either::Either, html, prelude::*}; use send_wrapper::SendWrapper; use std::{collections::HashMap, time::Duration}; use thaw_components::{CSSTransition, Teleport}; @@ -11,6 +11,7 @@ use wasm_bindgen::UnwrapThrowExt; pub fn Toaster( receiver: ToasterReceiver, #[prop(optional)] position: ToastPosition, + #[prop(optional)] intent: ToastIntent, #[prop(default = Duration::from_secs(3))] timeout: Duration, ) -> impl IntoView { mount_style("toaster", include_str!("./toaster.css")); @@ -42,6 +43,9 @@ pub fn Toaster( if options.timeout.is_none() { options.timeout = Some(timeout); } + if options.intent.is_none() { + options.intent = Some(intent); + } let list = id_list(&options.position.unwrap_throw()); let id = options.id; @@ -171,10 +175,12 @@ fn ToasterContainer( id, timeout, position, + intent, .. } = options; let timeout = timeout.unwrap_throw(); let position = position.unwrap_throw(); + let intent = intent.unwrap_throw(); if !timeout.is_zero() { set_timeout( @@ -209,9 +215,11 @@ fn ToasterContainer( on_after_leave=on_after_leave let:_ > -
- {children()} -
+ +
+ {children()} +
+
} } diff --git a/thaw/src/toast/toaster_provider.rs b/thaw/src/toast/toaster_provider.rs index 44cc9c8..28a334c 100644 --- a/thaw/src/toast/toaster_provider.rs +++ b/thaw/src/toast/toaster_provider.rs @@ -1,4 +1,4 @@ -use super::{toaster::Toaster, ToastPosition, ToasterInjection}; +use super::{toaster::Toaster, ToastIntent, ToastPosition, ToasterInjection}; use leptos::{context::Provider, prelude::*}; #[component] @@ -6,11 +6,14 @@ pub fn ToasterProvider( /// The position the toast should render. #[prop(optional)] position: ToastPosition, + /// The intent of the toasts + #[prop(optional)] + intent: ToastIntent, children: Children, ) -> impl IntoView { let (injection, receiver) = ToasterInjection::channel(); view! { - + {children()} } }