Skip to content

Commit a14a6e4

Browse files
committed
feat: enhance Toast component with customizable duration and improved styling
1 parent 9a994b6 commit a14a6e4

File tree

3 files changed

+66
-13
lines changed

3 files changed

+66
-13
lines changed

src/components/toast/Toast.stories.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,17 @@ export const ExampleToast = () => {
1717

1818
const renderToast = () => {
1919
toast({
20-
title: "Error",
21-
color: "error",
20+
title: "Cannot delete the last administrative role",
21+
color: "warning",
2222
dismissible: true,
23-
children: "INVALID_LOGIN_DATA",
23+
duration: 10000,
24+
children: <Text>Some content</Text>
2425
})
2526
}
2627

2728
return (
2829
<div className={"h-screen w-screen flex items-center justify-center"}>
29-
<Toaster duration={Infinity} position="top-right"/>
30+
<Toaster className={"sdsasa"} duration={Infinity} position="top-right"/>
3031
<Button onClick={() => renderToast()}>
3132
Test
3233
</Button>

src/components/toast/Toast.style.scss

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
@use "../../styles/helpers";
22
@use "../../styles/variables";
33
@use "../../styles/box";
4+
@use "sass:math";
45

56
.toast {
67

78
padding: variables.$md;
9+
width: 100%;
10+
overflow: hidden;
811

912
& {
1013
@include helpers.borderRadius();
@@ -14,7 +17,7 @@
1417
&__header {
1518
align-items: flex-start;
1619
justify-content: space-between;
17-
gap: variables.$xxs;
20+
gap: variables.$xs;
1821
}
1922

2023
&__icon {
@@ -24,13 +27,13 @@
2427
}
2528

2629
&__content {
27-
margin-top: variables.$xxs;
2830
box-sizing: border-box;
31+
margin-left: 27px;
2932
}
3033

3134
&__header-wrapper {
32-
align-items: center;
33-
gap: variables.$xxs;
35+
align-items: flex-start;
36+
gap: variables.$xs;
3437

3538
> svg {
3639
min-width: variables.$md;
@@ -51,8 +54,49 @@
5154
}
5255

5356
svg {
54-
min-width: variables.$md;
55-
min-height: variables.$md;
57+
min-width: variables.$md;
58+
min-height: variables.$md;
59+
}
60+
}
61+
62+
@keyframes toast-duration {
63+
from {
64+
width: 0%;
65+
}
66+
to {
67+
width: 100%;
68+
}
69+
}
70+
71+
&:hover {
72+
.toast__duration {
73+
&:after {
74+
animation-play-state: paused;
75+
}
76+
}
77+
}
78+
79+
&__duration {
80+
position: relative;
81+
margin: variables.$md (-1 * variables.$md) (-1 * variables.$md);
82+
padding: variables.$xxs variables.$md (variables.$xxs + 0.25rem);
83+
84+
& {
85+
border-top: 1px solid helpers.borderColor();
86+
}
87+
88+
&::after {
89+
content: "";
90+
position: absolute;
91+
left: 0;
92+
bottom: 0;
93+
width: 0%; // Start: leer
94+
height: 4px;
95+
background: helpers.borderColor();
96+
animation-name: toast-duration;
97+
animation-duration: var(--toast-duration, 4000ms);
98+
animation-timing-function: linear;
99+
animation-fill-mode: forwards;
56100
}
57101
}
58102
}

src/components/toast/Toast.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,28 @@ export interface ToastProps extends Omit<Code0Component<HTMLDivElement>, "title"
2424
//defaults to false
2525
dismissible?: boolean
2626
onClose?: (event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => void
27+
duration?: number //defaults to 4000
2728
}
2829

2930
export function toast(toast: Omit<ToastProps, 'id'>) {
3031
return sonnerToast.custom((id) => (
3132
<Toast id={id} {...toast}>
3233
{toast.children}
3334
</Toast>
34-
))
35+
), {
36+
duration: toast.duration ?? 4000
37+
})
3538
}
3639

3740
export function Toast(props: ToastProps) {
38-
const {dismissible = false, color = "secondary", title, onClose = () => {}, children, ...rest} = props
41+
const {dismissible = false, color = "secondary", title, onClose = () => {}, children, duration = 4000, ...rest} = props
3942

4043
return (
4144
<div {...mergeCode0Props(`toast toast--${color}`, rest)}>
4245
<Flex className={"toast__header"}>
4346
<Flex className={"toast__header-wrapper"}>
4447
{color && <ToastIcon color={color}/>}
45-
<Text size={"md"} hierarchy={"primary"}>{title}</Text>
48+
<Text size={"md"}>{title}</Text>
4649
</Flex>
4750
{dismissible &&
4851
<span className={"toast__dismissible"} onClick={() => sonnerToast.dismiss(props.id)}>
@@ -55,6 +58,11 @@ export function Toast(props: ToastProps) {
5558
{children}
5659
</div>
5760
}
61+
<div className={"toast__duration"} style={{
62+
["--toast-duration" as any]: `${duration}ms`,
63+
}}>
64+
<Text hierarchy={"tertiary"}>This message will close in <Text hierarchy={"primary"}>{duration / 1000}</Text> seconds</Text>
65+
</div>
5866
</div>
5967
)
6068
}

0 commit comments

Comments
 (0)