import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react'; import { cn } from '../lib/utils'; import { Alert, AlertDescription } from './Alert'; export type ToastVariant = 'default' | 'success' | 'error' | 'warning' | 'info'; export interface Toast { id: string; title?: string; message: string; variant: ToastVariant; duration?: number; } interface ToastContextType { toasts: Toast[]; addToast: (toast: Omit) => void; removeToast: (id: string) => void; success: (message: string, title?: string) => void; error: (message: string, title?: string) => void; warning: (message: string, title?: string) => void; info: (message: string, title?: string) => void; } const ToastContext = createContext(undefined); export function useToast() { const context = useContext(ToastContext); if (!context) { throw new Error('useToast must be used within a ToastProvider'); } return context; } export function ToastProvider({ children }: { children: ReactNode }) { const [toasts, setToasts] = useState([]); const removeToast = useCallback((id: string) => { setToasts((prev) => prev.filter((toast) => toast.id !== id)); }, []); const addToast = useCallback( (toast: Omit) => { const id = Math.random().toString(36).substring(2, 9); const newToast: Toast = { ...toast, id, duration: toast.duration || 5000, }; setToasts((prev) => [...prev, newToast]); if (newToast.duration && newToast.duration > 0) { setTimeout(() => { removeToast(id); }, newToast.duration); } }, [removeToast] ); const success = useCallback( (message: string, title?: string) => { addToast({ message, title, variant: 'success' }); }, [addToast] ); const error = useCallback( (message: string, title?: string) => { addToast({ message, title, variant: 'error' }); }, [addToast] ); const warning = useCallback( (message: string, title?: string) => { addToast({ message, title, variant: 'warning' }); }, [addToast] ); const info = useCallback( (message: string, title?: string) => { addToast({ message, title, variant: 'info' }); }, [addToast] ); return ( {children} ); } function ToastContainer({ toasts, removeToast }: { toasts: Toast[]; removeToast: (id: string) => void }) { if (toasts.length === 0) return null; return (
{toasts.map((toast) => ( ))}
); } function ToastItem({ toast, onRemove }: { toast: Toast; onRemove: (id: string) => void }) { const variantMap: Record = { default: { alert: 'default', icon: 'ℹ️' }, success: { alert: 'success', icon: '✓' }, error: { alert: 'destructive', icon: '✗' }, warning: { alert: 'warning', icon: '⚠' }, info: { alert: 'default', icon: 'ℹ️' }, }; const variant = variantMap[toast.variant]; return (
{toast.title && ( {toast.title} )} {toast.message}
); }