/** * @file Toaster.tsx * @description Renders the app-wide toast queue from `lib/toast.ts`, mounted * once in `App.tsx`. Two live regions — `status` for success/info, `alert` for * errors — so a screen reader announces each message at the right urgency. * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { AlertCircle, Check, Info, X } from "lucide-react"; import { dismissToast, subscribeToasts, type ToastItem } from "../lib/toast"; const TONE: Record = { success: "border-status-success/40 bg-status-success/10 text-status-success", info: "border-border bg-surface-2 text-fg-primary", error: "border-status-danger/50 bg-status-danger/10 text-status-danger", }; function Item({ item }: { item: ToastItem }) { const { t } = useTranslation("common"); const Icon = item.kind === "error" ? AlertCircle : item.kind === "info" ? Info : Check; return (
{item.message}
); } /** The single toast renderer. */ export function Toaster() { const [items, setItems] = useState([]); useEffect(() => subscribeToasts(setItems), []); const errors = items.filter((i) => i.kind === "error"); const rest = items.filter((i) => i.kind !== "error"); return (
{rest.map((i) => ( ))}
{errors.map((i) => ( ))}
); }