"use client"; import { cva } from "class-variance-authority"; import { AlertCircle, AlertTriangle, CheckCircle2, Info, X, } from "lucide-react"; import * as React from "react"; import { cn } from "../lib/utils"; const toastVariants = cva( "pointer-events-auto relative flex w-full items-center justify-between gap-3 overflow-hidden rounded-lg border p-4 shadow-lg transition-all", { variants: { variant: { default: "border-border bg-background text-foreground", success: "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]", error: "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]", warning: "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]", info: "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]", }, }, defaultVariants: { variant: "default", }, }, ); const icons = { success: CheckCircle2, error: AlertCircle, warning: AlertTriangle, info: Info, default: Info, }; export interface Toast { id: string; title: string; description?: string; variant?: "default" | "success" | "error" | "warning" | "info"; duration?: number; } interface ToastProps extends Toast { onDismiss: (id: string) => void; } function ToastComponent({ id, title, description, variant = "default", onDismiss, }: ToastProps) { const Icon = icons[variant]; return (
); } interface ToastContainerProps { toasts: Toast[]; onDismiss: (id: string) => void; } export function ToastContainer({ toasts, onDismiss }: ToastContainerProps) { return (
{toasts.map((toast) => ( ))}
); } // Toast Context and Hook type ToastInput = Omit; interface ToastContextValue { toasts: Toast[]; toast: (input: ToastInput) => void; success: (title: string, description?: string) => void; error: (title: string, description?: string) => void; warning: (title: string, description?: string) => void; info: (title: string, description?: string) => void; dismiss: (id: string) => void; } const ToastContext = React.createContext(null); export function ToastProvider({ children }: { children: React.ReactNode }) { const [toasts, setToasts] = React.useState([]); const dismiss = React.useCallback((id: string) => { setToasts((prev) => prev.filter((t) => t.id !== id)); }, []); const toast = React.useCallback( (input: ToastInput) => { const id = Math.random().toString(36).slice(2); const newToast: Toast = { id, ...input }; setToasts((prev) => [...prev, newToast]); // Auto-dismiss after duration const duration = input.duration ?? 5000; if (duration > 0) { setTimeout(() => dismiss(id), duration); } }, [dismiss], ); const success = React.useCallback( (title: string, description?: string) => { toast({ title, description, variant: "success" }); }, [toast], ); const error = React.useCallback( (title: string, description?: string) => { toast({ title, description, variant: "error" }); }, [toast], ); const warning = React.useCallback( (title: string, description?: string) => { toast({ title, description, variant: "warning" }); }, [toast], ); const info = React.useCallback( (title: string, description?: string) => { toast({ title, description, variant: "info" }); }, [toast], ); const value = React.useMemo( () => ({ toasts, toast, success, error, warning, info, dismiss }), [toasts, toast, success, error, warning, info, dismiss], ); return ( {children} ); } export function useToast() { const context = React.useContext(ToastContext); if (!context) { throw new Error("useToast must be used within a ToastProvider"); } return context; }