'use client'; import * as React from 'react'; import { X, CheckCircle, AlertCircle, Info, AlertTriangle } from 'lucide-react'; import { cn } from '@/lib/utils/cn'; export interface Toast { id: string; title?: string; description?: string; variant?: 'default' | 'success' | 'error' | 'warning' | 'info'; duration?: number; } interface ToastContextType { toasts: Toast[]; addToast: (toast: Omit) => void; removeToast: (id: string) => void; } const ToastContext = React.createContext( undefined ); export function useToast() { const context = React.useContext(ToastContext); if (!context) { throw new Error('useToast must be used within ToastProvider'); } return context; } export function ToastProvider({ children }: { children: React.ReactNode }) { const [toasts, setToasts] = React.useState([]); const addToast = React.useCallback((toast: Omit) => { const id = Math.random().toString(36).substring(2, 9); const newToast: Toast = { id, ...toast }; setToasts((prev) => [...prev, newToast]); // Auto remove after duration const duration = toast.duration ?? 5000; if (duration > 0) { setTimeout(() => { removeToast(id); }, duration); } }, []); const removeToast = React.useCallback((id: string) => { setToasts((prev) => prev.filter((toast) => toast.id !== id)); }, []); return ( {children} ); } function ToastContainer({ toasts, onRemove, }: { toasts: Toast[]; onRemove: (id: string) => void; }) { if (toasts.length === 0) return null; return (
{toasts.map((toast) => ( ))}
); } function ToastItem({ toast, onRemove, }: { toast: Toast; onRemove: (id: string) => void; }) { const variant = toast.variant ?? 'default'; const icons = { default: Info, success: CheckCircle, error: AlertCircle, warning: AlertTriangle, info: Info, }; const Icon = icons[variant]; return (
{toast.title && (
{toast.title}
)} {toast.description && (
{toast.description}
)}
); }