import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from 'react'; import { AlertTriangle, CheckCircle2, Info, X, XCircle } from 'lucide-react'; import { cn } from '../lib/utils'; type ToastType = 'success' | 'error' | 'info' | 'warning'; type ToastInput = { type?: ToastType; title?: string; message: string; duration?: number; }; type ToastItem = Required> & { id: string; }; type ToastContextValue = { toast: (input: ToastInput) => string; dismissToast: (id: string) => void; }; const ToastContext = createContext(null); const toastStyles: Record = { success: { icon: , className: 'border-emerald-500/25 bg-emerald-950/85', iconClassName: 'text-emerald-300' }, error: { icon: , className: 'border-rose-500/25 bg-rose-950/85', iconClassName: 'text-rose-300' }, info: { icon: , className: 'border-sky-500/25 bg-sky-950/85', iconClassName: 'text-sky-300' }, warning: { icon: , className: 'border-amber-500/25 bg-amber-950/85', iconClassName: 'text-amber-300' } }; export function ToastProvider({ children }: { children: ReactNode }) { const [toasts, setToasts] = useState([]); const timers = useRef>(new Map()); const dismissToast = useCallback((id: string) => { const timer = timers.current.get(id); if (timer) window.clearTimeout(timer); timers.current.delete(id); setToasts((prev) => prev.filter((toastItem) => toastItem.id !== id)); }, []); const toast = useCallback( ({ type = 'info', title = '', message, duration = 3500 }: ToastInput) => { const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; const toastItem: ToastItem = { id, type, title, message }; setToasts((prev) => [toastItem, ...prev].slice(0, 4)); if (duration > 0) { const timer = window.setTimeout(() => dismissToast(id), duration); timers.current.set(id, timer); } return id; }, [dismissToast] ); useEffect(() => { return () => { timers.current.forEach((timer) => window.clearTimeout(timer)); timers.current.clear(); }; }, []); const contextValue = useMemo(() => ({ toast, dismissToast }), [toast, dismissToast]); return ( {children}
{toasts.map((toastItem) => { const style = toastStyles[toastItem.type]; return (
{style.icon}
{toastItem.title ?

{toastItem.title}

: null}

{toastItem.message}

); })}
); } export function useToast() { const context = useContext(ToastContext); if (!context) { throw new Error('useToast는 ToastProvider 내부에서 사용해야 합니다.'); } return context; }