Skip to content

Commit 58aee94

Browse files
committed
refactor: 토스트 로직 수정
1 parent 18af294 commit 58aee94

File tree

5 files changed

+63
-87
lines changed

5 files changed

+63
-87
lines changed

src/components/ui/Toast/Toast.module.scss

Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,17 @@
1919
}
2020
}
2121

22-
// .Toast {
23-
// width: 100%;
24-
// height: 3.25rem;
25-
// z-index: 2;
26-
// border-radius: 0.75rem;
27-
// background-color: white;
28-
// padding: 0.875rem 1.125rem;
29-
30-
// transition:
31-
// opacity 0.3s,
32-
// transform 0.3s;
33-
// }
34-
35-
// .show {
36-
// opacity: 1;
37-
// transform: translateY(0);
38-
// }
39-
40-
// .ToastRoot[data-state="open"] {
41-
// animation: slideUp 150ms cubic-bezier(0.16, 1, 0.3, 1);
42-
// }
43-
// .ToastRoot[data-state="closed"] {
44-
// animation: hide 100ms ease-in;
45-
// }
46-
47-
// @keyframes hide {
48-
// from {
49-
// opacity: 1;
50-
// }
51-
// to {
52-
// opacity: 0;
53-
// }
54-
// }
55-
56-
// @keyframes slideUp {
57-
// from {
58-
// transform: translateY(100%);
59-
// opacity: 0;
60-
// }
61-
// to {
62-
// transform: translateY(0);
63-
// opacity: 1;
64-
// }
65-
// }
66-
6722
.ToastViewport {
6823
position: fixed;
6924
left: 50%;
70-
top: 0.75rem;
25+
bottom: 7rem;
7126
z-index: 9999;
7227
width: fit-content;
7328
display: flex;
7429
flex-direction: column;
7530
gap: 0.5rem;
7631
transform: translateX(-50%);
32+
width: calc(100% - 2.5rem);
7733
}
7834

7935
.Toast {
@@ -87,31 +43,43 @@
8743
transform: translateX(var(--radix-toast-swipe-move-x));
8844
}
8945
&[data-state="open"] {
90-
animation: slide-in-from-top 0.3s ease-out forwards;
46+
animation: slide-in-from-bottom-full 0.4s ease-out forwards;
9147
}
48+
9249
&[data-state="closed"] {
93-
animation: slide-out-to-top 0.3s ease-in forwards;
50+
animation: slide-out-to-bottom-full 0.4s ease-in forwards;
9451
}
9552
}
9653

97-
@keyframes slide-in-from-top {
54+
@keyframes slide-in-from-bottom-full {
9855
from {
9956
opacity: 0;
100-
transform: translateY(-10px);
57+
transform: translateY(300%);
10158
}
10259
to {
10360
opacity: 1;
10461
transform: translateY(0);
10562
}
10663
}
10764

108-
@keyframes slide-out-to-top {
65+
@keyframes slide-out-to-bottom-full {
10966
from {
11067
opacity: 1;
11168
transform: translateY(0);
11269
}
11370
to {
11471
opacity: 0;
115-
transform: translateY(-10px);
72+
transform: translateY(300%);
73+
}
74+
}
75+
76+
.Toaster {
77+
border-radius: 0.75rem;
78+
background-color: var(--color-white);
79+
width: 100%;
80+
padding: 0.875rem 1.125rem;
81+
82+
& > span {
83+
line-height: 1.5rem;
11684
}
11785
}

src/components/ui/Toast/Toast.stories.tsx

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/components/ui/Toast/Toast.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import * as React from "react";
1+
import { forwardRef } from "react";
22

33
import * as ToastPrimitives from "@radix-ui/react-toast";
44
import classNames from "classnames";
55

66
import styles from "@/components/ui/Toast/Toast.module.scss";
77

8+
import { useToast } from "@/hooks/common/useToast";
9+
810
const ToastProvider = ToastPrimitives.Provider;
911

10-
const ToastViewport = React.forwardRef<
12+
const ToastViewport = forwardRef<
1113
React.ElementRef<typeof ToastPrimitives.Viewport>,
1214
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
1315
>(({ className, ...props }, ref) => (
@@ -19,17 +21,29 @@ const ToastViewport = React.forwardRef<
1921
));
2022
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
2123

22-
const Toast = React.forwardRef<
24+
const Toast = forwardRef<
2325
React.ElementRef<typeof ToastPrimitives.Root>,
2426
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root>
25-
>(({ className, ...props }, ref) => {
27+
>(({ className, open, id, ...props }, ref) => {
28+
const { removeToast } = useToast();
29+
2630
return (
27-
<ToastPrimitives.Root ref={ref} className={classNames(styles.Toast, className)} {...props} />
31+
<ToastPrimitives.Root
32+
ref={ref}
33+
open={open}
34+
onOpenChange={(isOpen) => {
35+
if (!isOpen) {
36+
setTimeout(() => removeToast(id), 400); // 0.4초 뒤 제거
37+
}
38+
}}
39+
className={classNames(styles.Toast, className)}
40+
{...props}
41+
/>
2842
);
2943
});
3044
Toast.displayName = ToastPrimitives.Root.displayName;
3145

32-
const ToastTitle = React.forwardRef<
46+
const ToastTitle = forwardRef<
3347
React.ElementRef<typeof ToastPrimitives.Title>,
3448
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
3549
>(({ className, ...props }, ref) => (

src/components/ui/Toast/Toaster.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { Toast, ToastProvider, ToastTitle, ToastViewport } from "@/components/ui/Toast/Toast";
1+
import Text from "@/components/ui/Text/Text";
2+
import { Toast, ToastProvider, ToastViewport } from "@/components/ui/Toast/Toast";
3+
import styles from "@/components/ui/Toast/Toast.module.scss";
24

35
import { useToast } from "@/hooks/common/useToast";
46

@@ -7,15 +9,21 @@ export function Toaster() {
79

810
return (
911
<ToastProvider swipeDirection="right">
10-
{toasts.map(({ id, title, duration }) => (
12+
{toasts.map(({ id, duration, open }) => (
1113
<Toast
1214
key={id}
13-
open
15+
open={open}
1416
duration={duration}
15-
onOpenChange={(open) => !open && removeToast(id)}
16-
className="flex items-center gap-3 rounded-3 bg-white px-3 py-2 shadow-1"
17+
onOpenChange={(isOpen) => {
18+
if (!isOpen) {
19+
setTimeout(() => removeToast(id), 400);
20+
}
21+
}}
22+
className={styles.Toaster}
1723
>
18-
{title && <ToastTitle className="text-caption-1-bold text-black">{title}</ToastTitle>}
24+
<Text color="primary" variant="bodyM">
25+
링크가 복사되었어요.
26+
</Text>
1927
</Toast>
2028
))}
2129
<ToastViewport />

src/hooks/common/useToast.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type ToastType = {
1111
type ToastContextType = {
1212
toasts: ToastType[];
1313
addToast: (title: string, duration?: number) => string;
14-
removeToast: (id: string) => void;
14+
removeToast: (id?: string) => void;
1515
};
1616

1717
const ToastContext = createContext<ToastContextType | undefined>(undefined);
@@ -22,6 +22,12 @@ export function ToastProvider({ children }: { children: ReactNode }) {
2222
const addToast = (title: string, duration = 3000): string => {
2323
const id = crypto.randomUUID();
2424

25+
const isToastOpen = toasts.some((toast) => toast.open);
26+
27+
if (isToastOpen) {
28+
return "";
29+
}
30+
2531
const newToast: ToastType = {
2632
id,
2733
title,
@@ -33,8 +39,8 @@ export function ToastProvider({ children }: { children: ReactNode }) {
3339
return id;
3440
};
3541

36-
const removeToast = (id: string) => {
37-
setToasts((prev) => prev.filter((toast) => toast.id !== id));
42+
const removeToast = (id?: string) => {
43+
setToasts((prev) => prev.map((toast) => (toast.id === id ? { ...toast, open: false } : toast)));
3844
};
3945

4046
return (

0 commit comments

Comments
 (0)