import { useSyncExternalStore } from "react"; import { CircleAlert, CircleCheck, Info, X } from "lucide-react"; import { cn } from "@/lib/cn"; type ToastType = "success" | "error" | "info"; interface ToastItem { id: number; type: ToastType; message: string; } const AUTO_DISMISS_MS = 4000; let items: ToastItem[] = []; let nextId = 1; const listeners = new Set<() => void>(); function emit() { for (const listener of listeners) listener(); } function dismiss(id: number) { items = items.filter((t) => t.id !== id); emit(); } function push(type: ToastType, message: string) { const id = nextId++; items = [...items, { id, type, message }]; emit(); setTimeout(() => dismiss(id), AUTO_DISMISS_MS); } export const toast = { success: (message: string) => push("success", message), error: (message: string) => push("error", message), info: (message: string) => push("info", message), }; const STYLES: Record = { success: "border-emerald-500/30 text-emerald-400", error: "border-red-500/30 text-red-400", info: "border-accent-500/30 text-accent-400", }; const ICONS: Record = { success: CircleCheck, error: CircleAlert, info: Info, }; export function ToastContainer() { const toasts = useSyncExternalStore( (cb) => { listeners.add(cb); return () => listeners.delete(cb); }, () => items, () => items, ); return (
{toasts.map((t) => { const Icon = ICONS[t.type]; return (
); })}
); }