import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; const ToastContext = createContext(null); const VARIANT_STYLES = { success: { container: "border-green-500/80 bg-green-950/90 text-green-100", accent: "bg-green-400" }, warning: { container: "border-yellow-500/80 bg-yellow-950/90 text-yellow-100", accent: "bg-yellow-400" }, error: { container: "border-red-500/80 bg-red-950/90 text-red-100", accent: "bg-red-400" }, info: { container: "border-sky-500/60 bg-sky-950/90 text-sky-100", accent: "bg-sky-400" } }; const DEFAULT_DURATION = 6000; export default function ToastProvider({ children }) { const [toasts, setToasts] = useState([]); const timeoutsRef = useRef(new Map()); const dismissToast = useCallback((id) => { setToasts((prev) => prev.filter((toast) => toast.id !== id)); const handle = timeoutsRef.current.get(id); if (handle && typeof window !== "undefined") { window.clearTimeout(handle); } timeoutsRef.current.delete(id); }, []); const showToast = useCallback(({ id, title, description, variant = "info", duration = DEFAULT_DURATION } = {}) => { const toastId = id ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`; const normalizedVariant = VARIANT_STYLES[variant] ? variant : "info"; setToasts((prev) => { const withoutExisting = prev.filter((toast) => toast.id !== toastId); return [ ...withoutExisting, { id: toastId, title, description, variant: normalizedVariant } ]; }); if (duration !== null && duration !== Infinity && typeof window !== "undefined") { const timeoutHandle = window.setTimeout(() => { dismissToast(toastId); }, duration); const existingHandle = timeoutsRef.current.get(toastId); if (existingHandle) { window.clearTimeout(existingHandle); } timeoutsRef.current.set(toastId, timeoutHandle); } return toastId; }, [dismissToast]); useEffect(() => { return () => { if (typeof window === "undefined") return; timeoutsRef.current.forEach((handle) => window.clearTimeout(handle)); timeoutsRef.current.clear(); }; }, []); const contextValue = useMemo(() => ({ showToast, dismissToast }), [showToast, dismissToast]); return ( {children}
{toasts.map((toast) => { const styles = VARIANT_STYLES[toast.variant] ?? VARIANT_STYLES.info; return (
{toast.title &&

{toast.title}

} {toast.description &&

{toast.description}

}
); })}
); } export function useToast() { const context = useContext(ToastContext); if (!context) { throw new Error('useToast must be used within a ToastProvider'); } return context; }