@@ -9,6 +9,7 @@ interface Toast {
99 message : string ;
1010 type : 'success' | 'error' | 'warning' | 'info' ;
1111 duration ?: number ;
12+ isExisting : boolean ;
1213}
1314
1415interface ToastContextType {
@@ -21,9 +22,9 @@ const ToastContext = createContext<ToastContextType | undefined>(undefined);
2122export function ToastProvider ( { children } : { children : ReactNode } ) {
2223 const [ toasts , setToasts ] = useState < Toast [ ] > ( [ ] ) ;
2324
24- const showToast = ( message : string , type : Toast [ 'type' ] = 'info ' , duration = 3000 ) => {
25+ const showToast = ( message : string , type : Toast [ 'type' ] = 'warning ' , duration = 2500 ) => {
2526 const id = Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) ;
26- const newToast : Toast = { id, message, type, duration } ;
27+ const newToast : Toast = { id, message, type, duration, isExisting : false } ;
2728
2829 setToasts ( prev => [ ...prev , newToast ] ) ;
2930
@@ -35,9 +36,14 @@ export function ToastProvider({ children }: { children: ReactNode }) {
3536 } ;
3637
3738 const hideToast = ( id : string ) => {
38- setToasts ( prev => prev . filter ( toast => toast . id !== id ) ) ;
39- } ;
39+ // 먼저 isExiting라는 플래그 변수를 true로 설정하여 exit 애니메이션 시작
40+ setToasts ( prev => prev . map ( toast => ( toast . id === id ? { ... toast , isExisting : true } : toast ) ) ) ;
4041
42+ // 애니메이션 완료 후 실제로 토스트 제거 (300ms는 애니메이션 duration)
43+ setTimeout ( ( ) => {
44+ setToasts ( prev => prev . filter ( toast => toast . id !== id ) ) ;
45+ } , 300 ) ;
46+ } ;
4147 return (
4248 < ToastContext . Provider value = { { showToast, hideToast } } >
4349 { children }
@@ -51,42 +57,38 @@ export function ToastProvider({ children }: { children: ReactNode }) {
5157}
5258
5359function ToastItem ( { toast, onClose } : { toast : Toast ; onClose : ( ) => void } ) {
54- const getToastStyles = ( type : Toast [ 'type' ] ) => {
55- switch ( type ) {
56- case 'success' :
57- return 'bg-green-500 text-white border-green-600' ;
58- case 'error' :
59- return 'bg-red-500 text-white border-red-600' ;
60- case 'warning' :
61- return 'bg-yellow-500 text-white border-yellow-600' ;
62- case 'info' :
63- default :
64- return 'bg-blue-500 text-white border-blue-600' ;
65- }
66- } ;
67-
6860 const getIcon = ( type : Toast [ 'type' ] ) => {
6961 switch ( type ) {
7062 case 'success' :
7163 return (
72- < svg className = "w-5 h-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
73- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M5 13l4 4L19 7" />
64+ < svg width = "22" height = "22" viewBox = "0 0 22 22" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
65+ < path
66+ fillRule = "evenodd"
67+ clipRule = "evenodd"
68+ d = "M1.92499 10.9999C1.92499 5.98794 5.98799 1.92493 11 1.92493C16.0119 1.92493 20.0749 5.98794 20.0749 10.9999C20.0749 16.0119 16.0119 20.0749 11 20.0749C5.98799 20.0749 1.92499 16.0119 1.92499 10.9999ZM15.2594 9.05278C15.5763 8.72539 15.5678 8.2031 15.2404 7.88621C14.913 7.56932 14.3907 7.57784 14.0738 7.90523L9.78756 12.3336L7.9269 10.406C7.61046 10.0782 7.08818 10.069 6.76036 10.3854C6.43254 10.7019 6.42331 11.2242 6.73975 11.552L9.19318 14.0936C9.34851 14.2546 9.56253 14.3455 9.78619 14.3457C10.0099 14.3458 10.224 14.2552 10.3795 14.0944L15.2594 9.05278Z"
69+ fill = "#1ED45A"
70+ />
7471 </ svg >
7572 ) ;
7673 case 'error' :
7774 return (
78- < svg className = "w-5 h-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
79- < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M6 18L18 6M6 6l12 12" />
75+ < svg width = "22" height = "22" viewBox = "0 0 20 20" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
76+ < path
77+ fillRule = "evenodd"
78+ clipRule = "evenodd"
79+ d = "M0.924881 10C0.924881 4.98807 4.98788 0.925049 9.99985 0.925049C15.0118 0.925049 19.0748 4.98807 19.0748 10C19.0748 15.012 15.0118 19.075 9.99985 19.075C4.98788 19.075 0.924881 15.012 0.924881 10ZM9.99993 5.50829C10.4556 5.50829 10.8249 5.87766 10.8249 6.33329V10.4583C10.8249 10.9139 10.4556 11.2833 9.99993 11.2833C9.5443 11.2833 9.17493 10.9139 9.17493 10.4583V6.33329C9.17493 5.87766 9.5443 5.50829 9.99993 5.50829ZM10.9165 13.6666C10.9165 14.1729 10.5061 14.5833 9.99982 14.5833C9.49356 14.5833 9.08316 14.1729 9.08316 13.6666C9.08316 13.1604 9.49356 12.75 9.99982 12.75C10.5061 12.75 10.9165 13.1604 10.9165 13.6666Z"
80+ fill = "#FF6363"
81+ />
8082 </ svg >
8183 ) ;
8284 case 'warning' :
8385 return (
84- < svg className = "w-5 h-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24 " >
86+ < svg width = "22" height = "22" viewBox = "0 0 22 22" fill = "none" xmlns = "http://www.w3.org/2000/svg ">
8587 < path
86- strokeLinecap = "round "
87- strokeLinejoin = "round "
88- strokeWidth = { 2 }
89- d = "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z "
88+ fillRule = "evenodd "
89+ clipRule = "evenodd "
90+ d = "M9.91854 2.84201C10.6068 2.53557 11.3928 2.53557 12.081 2.84201C12.5607 3.05559 12.9071 3.4471 13.208 3.87311C13.5064 4.29544 13.8347 4.86408 14.2341 5.55592L18.9191 13.6705C19.3185 14.3624 19.6468 14.931 19.8634 15.4006C20.0818 15.8742 20.2477 16.3699 20.1928 16.8921C20.1141 17.6414 19.7211 18.3221 19.1116 18.7649C18.6868 19.0736 18.1745 19.1777 17.6551 19.2254C17.1402 19.2726 16.4836 19.2726 15.6847 19.2726H6.31484C5.51597 19.2726 4.85937 19.2726 4.34442 19.2254C3.82501 19.1777 3.31279 19.0736 2.88796 18.7649C2.27846 18.3221 1.88548 17.6414 1.80673 16.8921C1.75184 16.3699 1.91773 15.8742 2.13617 15.4006C2.35273 14.931 2.68104 14.3624 3.08049 13.6705L7.76543 5.55593C8.16485 4.86408 8.49314 4.29544 8.79152 3.8731C9.0925 3.4471 9.43882 3.05559 9.91854 2.84201ZM10.9999 7.42468C11.4556 7.42468 11.8249 7.79405 11.8249 8.24968V11.9163C11.8249 12.372 11.4556 12.7413 10.9999 12.7413C10.5443 12.7413 10.1749 12.372 10.1749 11.9163V8.24968C10.1749 7.79405 10.5443 7.42468 10.9999 7.42468ZM11.9165 15.1247C11.9165 15.6309 11.5061 16.0413 10.9998 16.0413C10.4936 16.0413 10.0831 15.6309 10.0831 15.1247C10.0831 14.6184 10.4936 14.208 10.9998 14.208C11.5061 14.208 11.9165 14.6184 11.9165 15.1247Z"
91+ fill = "#FFA938 "
9092 />
9193 </ svg >
9294 ) ;
@@ -108,12 +110,15 @@ function ToastItem({ toast, onClose }: { toast: Toast; onClose: () => void }) {
108110 return (
109111 < div
110112 className = { cn (
111- 'flex items-center justify-between p-4 rounded-lg shadow-lg border min-w-[300px] max-w-[400px] animate-in slide-in-from-right-full duration-300' ,
112- getToastStyles ( toast . type )
113+ 'flex items-center justify-between p-4 rounded-lg shadow-lg w-[335px] md:w-[420px] bg-gray-500' ,
114+ toast . isExisting
115+ ? 'animate-out slide-out-to-right-full duration-300'
116+ : 'animate-in slide-in-from-right-full duration-300'
113117 ) }
114118 >
115- < div className = "flex items-center space-x-3" >
116- < span className = "text-sm font-medium" > { toast . message } </ span >
119+ < div className = "flex items-center space-x-3 text-label-normal" >
120+ { getIcon ( toast . type ) }
121+ < span className = "text-body-2-normal" > { toast . message } </ span >
117122 </ div >
118123 < button onClick = { onClose } className = "ml-4 text-white/80 hover:text-white transition-colors" >
119124 < XIcon className = "w-4 h-4" />
0 commit comments