mirror of
https://github.com/adoyle0/thaw.git
synced 2025-03-13 05:59:49 -04:00
feature: closable messages and message position (#90)
* feature: closable messages and message position * all positions * move close button * review changes --------- Co-authored-by: Cristobal Andrada <kandrelczyk@gmail.com>
This commit is contained in:
parent
cef2310866
commit
93b75b2082
7 changed files with 131 additions and 20 deletions
|
@ -10,7 +10,7 @@ let success = move |_| {
|
||||||
message.create(
|
message.create(
|
||||||
"Success".into(),
|
"Success".into(),
|
||||||
MessageVariant::Success,
|
MessageVariant::Success,
|
||||||
Default::default(),
|
MessageOptions {closable: true, duration: std::time::Duration::from_secs(0)},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let warning = move |_| {
|
let warning = move |_| {
|
||||||
|
@ -33,8 +33,21 @@ view! {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### MessageProvider Props
|
||||||
|
|
||||||
|
| Name | Type | Default | Desciption |
|
||||||
|
| --------- | ----------------------------- | ----------------------- | ------------------------------- |
|
||||||
|
| placement | `MessagePlacement` | `MessagePlacement::Top` | Position to place the messages. |
|
||||||
|
|
||||||
### MessageProvider Injection Methods
|
### MessageProvider Injection Methods
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
| ------ | ------------------------------------------------------------------------------ | ------------------------ |
|
| ------ | ------------------------------------------------------------------------------ | ------------------------ |
|
||||||
| create | `fn(&self, content: String, variant: MessageVariant, options: MessageOptions)` | Use create type message. |
|
| create | `fn(&self, content: String, variant: MessageVariant, options: MessageOptions)` | Use create type message. |
|
||||||
|
|
||||||
|
### MessageOptions fields
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| -------- | ----------------- | ------------------------- | --------------------------------------------------------------- |
|
||||||
|
| duration | `Duration` | `Duration::from_secs(3)` | How long the message will be displayed. 0 for permanent message |
|
||||||
|
| closable | `bool` | `false` | Can the message be manually closed. |
|
||||||
|
|
|
@ -107,6 +107,7 @@ pub fn AutoComplete(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if key == *"Enter" {
|
} else if key == *"Enter" {
|
||||||
|
event.prevent_default();
|
||||||
let option_value = options.with_untracked(|options| {
|
let option_value = options.with_untracked(|options| {
|
||||||
let index = select_option_index.get_untracked();
|
let index = select_option_index.get_untracked();
|
||||||
if options.len() > index {
|
if options.len() > index {
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub fn Teleport(
|
||||||
let mount = mount.unwrap_or_else(|| {
|
let mount = mount.unwrap_or_else(|| {
|
||||||
document()
|
document()
|
||||||
.body()
|
.body()
|
||||||
.expect("body element not to exist")
|
.expect("body element to exist")
|
||||||
.unchecked_into()
|
.unchecked_into()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,57 @@
|
||||||
.thaw-message-container {
|
.thaw-message-container {
|
||||||
z-index: 6000;
|
z-index: 6000;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 12px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 0;
|
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-message-container--top {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
top: 12px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-message-container--bottom {
|
||||||
|
align-items: center;
|
||||||
|
bottom: 12px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-message-container--bottom-left {
|
||||||
|
align-items: start;
|
||||||
|
bottom: 12px;
|
||||||
|
left: 12px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-message-container--bottom-right {
|
||||||
|
align-items: end;
|
||||||
|
bottom: 12px;
|
||||||
|
left: 0px;
|
||||||
|
right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-message-container--top-left{
|
||||||
|
align-items: start;
|
||||||
|
top: 12px;
|
||||||
|
left: 12px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thaw-message-container--top-right {
|
||||||
|
align-items: end;
|
||||||
|
top: 12px;
|
||||||
|
left: 0px;
|
||||||
|
right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-message-wrapper {
|
.thaw-message-wrapper {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-message {
|
.thaw-message {
|
||||||
|
@ -21,8 +60,8 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
|
||||||
max-width: 75vh;
|
max-width: 75vh;
|
||||||
background: var(--thaw-background-color);
|
background: var(--thaw-background-color);
|
||||||
|
|
||||||
|
@ -30,6 +69,8 @@
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||||
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-message__icon {
|
.thaw-message__icon {
|
||||||
|
@ -42,3 +83,9 @@
|
||||||
.thaw-message__content {
|
.thaw-message__content {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.thaw-message__close {
|
||||||
|
margin-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
|
@ -5,15 +5,18 @@ use uuid::Uuid;
|
||||||
#[component]
|
#[component]
|
||||||
pub fn MessageEnvironment(
|
pub fn MessageEnvironment(
|
||||||
message: MessageType,
|
message: MessageType,
|
||||||
#[prop(into)] on_internal_after_leave: Callback<Uuid>,
|
#[prop(into)] on_internal_after_leave: Callback<Uuid, ()>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let (id, content, variant, options) = message;
|
let (id, content, variant, options) = message;
|
||||||
set_timeout(
|
|
||||||
move || {
|
|
||||||
on_internal_after_leave.call(id);
|
|
||||||
},
|
|
||||||
options.duration,
|
|
||||||
);
|
|
||||||
|
|
||||||
view! { <Message content variant/> }
|
if !options.duration.is_zero() {
|
||||||
|
set_timeout(
|
||||||
|
move || {
|
||||||
|
on_internal_after_leave.call(id);
|
||||||
|
},
|
||||||
|
options.duration,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
view! { <Message id closable=options.closable content variant on_close=on_internal_after_leave/> }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,39 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use super::{message_environment::MessageEnvironment, MessageVariant};
|
use super::{message_environment::MessageEnvironment, MessageVariant};
|
||||||
use crate::{components::Teleport, utils::mount_style};
|
use crate::{components::Teleport, utils::{class_list::class_list, mount_style}};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub enum MessagePlacement {
|
||||||
|
#[default]
|
||||||
|
Top,
|
||||||
|
TopLeft,
|
||||||
|
TopRight,
|
||||||
|
Bottom,
|
||||||
|
BottomLeft,
|
||||||
|
BottomRight
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessagePlacement {
|
||||||
|
fn container_style(&self) -> String {
|
||||||
|
match self {
|
||||||
|
MessagePlacement::Top => "thaw-message-container--top".to_owned(),
|
||||||
|
MessagePlacement::TopLeft => "thaw-message-container--top-left".to_owned(),
|
||||||
|
MessagePlacement::TopRight => "thaw-message-container--top-right".to_owned(),
|
||||||
|
MessagePlacement::Bottom => "thaw-message-container--bottom".to_owned(),
|
||||||
|
MessagePlacement::BottomLeft => "thaw-message-container--bottom-left".to_owned(),
|
||||||
|
MessagePlacement::BottomRight=> "thaw-message-container--bottom-right".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn MessageProvider(children: Children) -> impl IntoView {
|
pub fn MessageProvider(
|
||||||
|
#[prop(optional)] placement: MessagePlacement,
|
||||||
|
children: Children,
|
||||||
|
) -> impl IntoView {
|
||||||
mount_style("message", include_str!("./message.css"));
|
mount_style("message", include_str!("./message.css"));
|
||||||
|
|
||||||
let message_list = create_rw_signal::<Vec<MessageType>>(vec![]);
|
let message_list = create_rw_signal::<Vec<MessageType>>(vec![]);
|
||||||
|
@ -25,7 +52,7 @@ pub fn MessageProvider(children: Children) -> impl IntoView {
|
||||||
message_list,
|
message_list,
|
||||||
)>
|
)>
|
||||||
{children()} <Teleport>
|
{children()} <Teleport>
|
||||||
<div class="thaw-message-container">
|
<div class=class_list!["thaw-message-container", placement.container_style()]>
|
||||||
<For
|
<For
|
||||||
each=move || message_list.get()
|
each=move || message_list.get()
|
||||||
key=|message| message.0
|
key=|message| message.0
|
||||||
|
@ -50,12 +77,14 @@ pub(crate) type MessageType = (Uuid, String, MessageVariant, MessageOptions);
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MessageOptions {
|
pub struct MessageOptions {
|
||||||
pub duration: Duration,
|
pub duration: Duration,
|
||||||
|
pub closable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MessageOptions {
|
impl Default for MessageOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
duration: Duration::from_secs(3),
|
duration: Duration::from_secs(3),
|
||||||
|
closable: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,3 +111,5 @@ impl MessageInjection {
|
||||||
pub fn use_message() -> MessageInjection {
|
pub fn use_message() -> MessageInjection {
|
||||||
expect_context::<MessageInjection>()
|
expect_context::<MessageInjection>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@ mod message_environment;
|
||||||
mod message_provider;
|
mod message_provider;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use crate::{theme::use_theme, Icon, Theme};
|
use crate::{theme::use_theme, Icon, Theme, components::{If, Then}};
|
||||||
use icondata::*;
|
use icondata::*;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use uuid::Uuid;
|
||||||
pub use message_provider::*;
|
pub use message_provider::*;
|
||||||
pub use theme::MessageTheme;
|
pub use theme::MessageTheme;
|
||||||
|
|
||||||
|
@ -34,7 +35,13 @@ impl MessageVariant {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub(crate) fn Message(variant: MessageVariant, content: String) -> impl IntoView {
|
pub(crate) fn Message(
|
||||||
|
variant: MessageVariant,
|
||||||
|
content: String,
|
||||||
|
closable: bool,
|
||||||
|
id: Uuid,
|
||||||
|
#[prop(into)] on_close: Callback<Uuid, ()>,
|
||||||
|
) -> impl IntoView {
|
||||||
let theme = use_theme(Theme::light);
|
let theme = use_theme(Theme::light);
|
||||||
let css_vars = create_memo(move |_| {
|
let css_vars = create_memo(move |_| {
|
||||||
let mut css_vars = String::new();
|
let mut css_vars = String::new();
|
||||||
|
@ -54,7 +61,16 @@ pub(crate) fn Message(variant: MessageVariant, content: String) -> impl IntoView
|
||||||
<Icon icon=variant.icon() style/>
|
<Icon icon=variant.icon() style/>
|
||||||
</div>
|
</div>
|
||||||
<div class="thaw-message__content">{content}</div>
|
<div class="thaw-message__content">{content}</div>
|
||||||
|
<If cond=closable>
|
||||||
|
<Then slot>
|
||||||
|
<div class="thaw-message__close" on:click=move |_| on_close.call(id)>
|
||||||
|
<Icon icon=icondata::Icon::Ai(AiCloseOutlined)/>
|
||||||
|
</div>
|
||||||
|
</Then>
|
||||||
|
</If>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue