mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 14:09:21 -05:00
Toast intent (#269)
* add toast intent support * toast intent * toast intent * fix CI
This commit is contained in:
parent
3545744335
commit
a03226f202
10 changed files with 154 additions and 31 deletions
|
@ -79,7 +79,9 @@ pub fn SiteHeader() -> impl IntoView {
|
||||||
{
|
{
|
||||||
use leptos::ev;
|
use leptos::ev;
|
||||||
let handle = window_event_listener(ev::keydown, move |e| {
|
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();
|
let key = e.key();
|
||||||
if key == *"/" {
|
if key == *"/" {
|
||||||
if let Some(auto_complete_ref) = auto_complete_ref.get_untracked() {
|
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());
|
on_cleanup(move || handle.remove());
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,6 @@ use leptos::prelude::*;
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = console_log::init_with_level(log::Level::Debug);
|
let _ = console_log::init_with_level(log::Level::Debug);
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
mount_to_body(App)
|
mount_to_body(App)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::components::SiteHeader;
|
use crate::components::SiteHeader;
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
|
use leptos_meta::Style;
|
||||||
use leptos_router::hooks::{use_navigate, use_query_map};
|
use leptos_router::hooks::{use_navigate, use_query_map};
|
||||||
use thaw::*;
|
use thaw::*;
|
||||||
use leptos_meta::Style;
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Home() -> impl IntoView {
|
pub fn Home() -> impl IntoView {
|
||||||
|
|
|
@ -158,7 +158,7 @@ fn iter_nodes<'a>(
|
||||||
NodeValue::Superscript => quote!("Superscript todo!!!"),
|
NodeValue::Superscript => quote!("Superscript todo!!!"),
|
||||||
NodeValue::Link(node_link) => {
|
NodeValue::Link(node_link) => {
|
||||||
let NodeLink { url, title } = node_link;
|
let NodeLink { url, title } = node_link;
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
<Link href=#url attr:title=#title>
|
<Link href=#url attr:title=#title>
|
||||||
#(#children)*
|
#(#children)*
|
||||||
|
|
|
@ -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! {
|
||||||
|
<Toast>
|
||||||
|
<ToastTitle>"Email sent"</ToastTitle>
|
||||||
|
<ToastBody>
|
||||||
|
"This is a toast body"
|
||||||
|
<ToastBodySubtitle slot>
|
||||||
|
"Subtitle"
|
||||||
|
</ToastBodySubtitle>
|
||||||
|
</ToastBody>
|
||||||
|
<ToastFooter>
|
||||||
|
"Footer"
|
||||||
|
</ToastFooter>
|
||||||
|
</Toast>
|
||||||
|
}, ToastOptions::default().with_intent(intent));
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Space>
|
||||||
|
<Button on_click=move |_| dispatch_toast(toaster, ToastIntent::Info)>"Info"</Button>
|
||||||
|
<Button on_click=move |_| dispatch_toast(toaster, ToastIntent::Success)>"Success"</Button>
|
||||||
|
<Button on_click=move |_| dispatch_toast(toaster, ToastIntent::Warning)>"Warning"</Button>
|
||||||
|
<Button on_click=move |_| dispatch_toast(toaster, ToastIntent::Error)>"Error"</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Toast Title Media
|
||||||
|
|
||||||
|
```rust demo
|
||||||
|
let toaster = ToasterInjection::expect_context();
|
||||||
|
|
||||||
|
let on_click = move |_| {
|
||||||
|
toaster.dispatch_toast(move || view! {
|
||||||
|
<Toast>
|
||||||
|
<ToastTitle>
|
||||||
|
"Loading"
|
||||||
|
<ToastTitleMedia slot>
|
||||||
|
<Spinner size=SpinnerSize::Tiny/>
|
||||||
|
</ToastTitleMedia>
|
||||||
|
</ToastTitle>
|
||||||
|
</Toast>
|
||||||
|
}, Default::default());
|
||||||
|
};
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Button on_click=on_click>"Make toast"</Button>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### ToasterProvider Props
|
### ToasterProvider Props
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| -------- | --------------- | -------------------------- | ------------------------------------- |
|
| -------- | --------------- | -------------------------- | ------------------------------------- |
|
||||||
| position | `ToastPosition` | `ToastPosition::BottomEnd` | The position the toast should render. |
|
| position | `ToastPosition` | `ToastPosition::BottomEnd` | The position the toast should render. |
|
||||||
|
| intent | `ToastIntent ` | `ToastPosition::Info` | The intent of the toast. |
|
||||||
| children | `Children` | | |
|
| children | `Children` | | |
|
||||||
|
|
||||||
### ToastOptions Props
|
### ToastOptions Props
|
||||||
|
@ -76,7 +132,7 @@ view! {
|
||||||
| ------------- | --------------------------------------- | ------------------------------------- |
|
| ------------- | --------------------------------------- | ------------------------------------- |
|
||||||
| with_position | `Fn(mut self, position: ToastPosition)` | The position the toast should render. |
|
| 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_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
|
### Toast & ToastFooter Props
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,10 @@ impl ToastPosition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub enum ToastIntent {
|
pub enum ToastIntent {
|
||||||
Success,
|
Success,
|
||||||
|
#[default]
|
||||||
Info,
|
Info,
|
||||||
Warning,
|
Warning,
|
||||||
Error,
|
Error,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use leptos::{either::Either, prelude::*};
|
use leptos::{either::Either, prelude::*};
|
||||||
use thaw_components::OptionComp;
|
use thaw_components::OptionComp;
|
||||||
|
use thaw_utils::class_list;
|
||||||
|
|
||||||
|
use crate::ToastIntent;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ToastTitle(
|
pub fn ToastTitle(
|
||||||
|
@ -7,27 +10,63 @@ pub fn ToastTitle(
|
||||||
children: Children,
|
children: Children,
|
||||||
#[prop(optional)] toast_title_action: Option<ToastTitleAction>,
|
#[prop(optional)] toast_title_action: Option<ToastTitleAction>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
|
let intent: ToastIntent = expect_context();
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="thaw-toast-title__media">
|
<div class=class_list![
|
||||||
|
"thaw-toast-title__media", format!("thaw-toast-title__{:?}", intent).to_lowercase()
|
||||||
|
]>
|
||||||
{if let Some(media) = toast_title_media {
|
{if let Some(media) = toast_title_media {
|
||||||
Either::Left((media.children)())
|
Either::Left((media.children)())
|
||||||
} else {
|
} else {
|
||||||
Either::Right(
|
{
|
||||||
view! {
|
Either::Right(
|
||||||
<svg
|
view! {
|
||||||
fill="currentColor"
|
<svg
|
||||||
aria-hidden="true"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M18 10a8 8 0 1 0-16 0 8 8 0 0 0 16 0ZM9.5 8.91a.5.5 0 0 1 1 0V13.6a.5.5 0 0 1-1 0V8.9Zm-.25-2.16a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Z"
|
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
></path>
|
aria-hidden="true"
|
||||||
</svg>
|
width="1em"
|
||||||
},
|
height="1em"
|
||||||
)
|
viewBox="0 0 20 20"
|
||||||
|
>
|
||||||
|
{match intent {
|
||||||
|
ToastIntent::Info => {
|
||||||
|
view! {
|
||||||
|
<path
|
||||||
|
d="M18 10a8 8 0 1 0-16 0 8 8 0 0 0 16 0ZM9.5 8.91a.5.5 0 0 1 1 0V13.6a.5.5 0 0 1-1 0V8.9Zm-.25-2.16a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
}.into_any()
|
||||||
|
},
|
||||||
|
ToastIntent::Success => {
|
||||||
|
view! {
|
||||||
|
<path
|
||||||
|
d="M10 2a8 8 0 1 1 0 16 8 8 0 0 1 0-16Zm3.36 5.65a.5.5 0 0 0-.64-.06l-.07.06L9 11.3 7.35 9.65l-.07-.06a.5.5 0 0 0-.7.7l.07.07 2 2 .07.06c.17.11.4.11.56 0l.07-.06 4-4 .07-.08a.5.5 0 0 0-.06-.63Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
}.into_any()
|
||||||
|
},
|
||||||
|
ToastIntent::Warning => {
|
||||||
|
view! {
|
||||||
|
<path
|
||||||
|
d="M8.68 2.79a1.5 1.5 0 0 1 2.64 0l6.5 12A1.5 1.5 0 0 1 16.5 17h-13a1.5 1.5 0 0 1-1.32-2.21l6.5-12ZM10.5 7.5a.5.5 0 0 0-1 0v4a.5.5 0 0 0 1 0v-4Zm.25 6.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
}.into_any()
|
||||||
|
},
|
||||||
|
ToastIntent::Error => {
|
||||||
|
view! {
|
||||||
|
<path
|
||||||
|
d="M10 2a8 8 0 1 1 0 16 8 8 0 0 1 0-16ZM7.8 7.11a.5.5 0 0 0-.63.06l-.06.07a.5.5 0 0 0 .06.64L9.3 10l-2.12 2.12-.06.07a.5.5 0 0 0 .06.64l.07.06c.2.13.47.11.64-.06L10 10.7l2.12 2.12.07.06c.2.13.46.11.64-.06l.06-.07a.5.5 0 0 0-.06-.64L10.7 10l2.12-2.12.06-.07a.5.5 0 0 0-.06-.64l-.07-.06a.5.5 0 0 0-.64.06L10 9.3 7.88 7.17l-.07-.06Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
}.into_any()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</svg>
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div class="thaw-toast-title">{children()}</div>
|
<div class="thaw-toast-title">{children()}</div>
|
||||||
|
|
|
@ -108,10 +108,25 @@ div.thaw-toaster-wrapper {
|
||||||
grid-column-end: 2;
|
grid-column-end: 2;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: var(--colorNeutralForeground1);
|
|
||||||
color: var(--colorNeutralForeground2);
|
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 {
|
.thaw-toast-title__media > svg {
|
||||||
display: inline;
|
display: inline;
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{ToastOptions, ToastPosition, ToasterReceiver};
|
use super::{ToastIntent, ToastOptions, ToastPosition, ToasterReceiver};
|
||||||
use crate::ConfigInjection;
|
use crate::ConfigInjection;
|
||||||
use leptos::{either::Either, html, prelude::*};
|
use leptos::{context::Provider, either::Either, html, prelude::*};
|
||||||
use send_wrapper::SendWrapper;
|
use send_wrapper::SendWrapper;
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
use thaw_components::{CSSTransition, Teleport};
|
use thaw_components::{CSSTransition, Teleport};
|
||||||
|
@ -11,6 +11,7 @@ use wasm_bindgen::UnwrapThrowExt;
|
||||||
pub fn Toaster(
|
pub fn Toaster(
|
||||||
receiver: ToasterReceiver,
|
receiver: ToasterReceiver,
|
||||||
#[prop(optional)] position: ToastPosition,
|
#[prop(optional)] position: ToastPosition,
|
||||||
|
#[prop(optional)] intent: ToastIntent,
|
||||||
#[prop(default = Duration::from_secs(3))] timeout: Duration,
|
#[prop(default = Duration::from_secs(3))] timeout: Duration,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("toaster", include_str!("./toaster.css"));
|
mount_style("toaster", include_str!("./toaster.css"));
|
||||||
|
@ -42,6 +43,9 @@ pub fn Toaster(
|
||||||
if options.timeout.is_none() {
|
if options.timeout.is_none() {
|
||||||
options.timeout = Some(timeout);
|
options.timeout = Some(timeout);
|
||||||
}
|
}
|
||||||
|
if options.intent.is_none() {
|
||||||
|
options.intent = Some(intent);
|
||||||
|
}
|
||||||
|
|
||||||
let list = id_list(&options.position.unwrap_throw());
|
let list = id_list(&options.position.unwrap_throw());
|
||||||
let id = options.id;
|
let id = options.id;
|
||||||
|
@ -171,10 +175,12 @@ fn ToasterContainer(
|
||||||
id,
|
id,
|
||||||
timeout,
|
timeout,
|
||||||
position,
|
position,
|
||||||
|
intent,
|
||||||
..
|
..
|
||||||
} = options;
|
} = options;
|
||||||
let timeout = timeout.unwrap_throw();
|
let timeout = timeout.unwrap_throw();
|
||||||
let position = position.unwrap_throw();
|
let position = position.unwrap_throw();
|
||||||
|
let intent = intent.unwrap_throw();
|
||||||
|
|
||||||
if !timeout.is_zero() {
|
if !timeout.is_zero() {
|
||||||
set_timeout(
|
set_timeout(
|
||||||
|
@ -209,9 +215,11 @@ fn ToasterContainer(
|
||||||
on_after_leave=on_after_leave
|
on_after_leave=on_after_leave
|
||||||
let:_
|
let:_
|
||||||
>
|
>
|
||||||
<div class="thaw-toaster-container" node_ref=container_ref>
|
<Provider value=intent>
|
||||||
{children()}
|
<div class="thaw-toaster-container" node_ref=container_ref>
|
||||||
</div>
|
{children()}
|
||||||
|
</div>
|
||||||
|
</Provider>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{toaster::Toaster, ToastPosition, ToasterInjection};
|
use super::{toaster::Toaster, ToastIntent, ToastPosition, ToasterInjection};
|
||||||
use leptos::{context::Provider, prelude::*};
|
use leptos::{context::Provider, prelude::*};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
@ -6,11 +6,14 @@ pub fn ToasterProvider(
|
||||||
/// The position the toast should render.
|
/// The position the toast should render.
|
||||||
#[prop(optional)]
|
#[prop(optional)]
|
||||||
position: ToastPosition,
|
position: ToastPosition,
|
||||||
|
/// The intent of the toasts
|
||||||
|
#[prop(optional)]
|
||||||
|
intent: ToastIntent,
|
||||||
children: Children,
|
children: Children,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let (injection, receiver) = ToasterInjection::channel();
|
let (injection, receiver) = ToasterInjection::channel();
|
||||||
view! {
|
view! {
|
||||||
<Toaster receiver position />
|
<Toaster receiver position intent />
|
||||||
<Provider value=injection>{children()}</Provider>
|
<Provider value=injection>{children()}</Provider>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue