32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
"use client";
|
|
|
|
import { useUi } from "@/lib/ui-state";
|
|
import { CheckCircle2, AlertTriangle, Info, X } from "lucide-react";
|
|
import clsx from "clsx";
|
|
|
|
export function Toaster() {
|
|
const { toasts, dismissToast } = useUi();
|
|
if (!toasts.length) return null;
|
|
return (
|
|
<div className="fixed bottom-4 right-4 z-[70] flex flex-col gap-2">
|
|
{toasts.map((t) => {
|
|
const Icon = t.tone === "success" ? CheckCircle2 : t.tone === "danger" ? AlertTriangle : Info;
|
|
return (
|
|
<div
|
|
key={t.id}
|
|
role="status"
|
|
className={clsx(
|
|
"flex items-center gap-2.5 rounded-control border bg-elevated px-3.5 py-2.5 text-[13px] shadow-pop animate-slide-up",
|
|
t.tone === "success" ? "border-success/40" : t.tone === "danger" ? "border-danger/40" : "border-border",
|
|
)}
|
|
>
|
|
<Icon size={16} className={clsx(t.tone === "success" ? "text-success" : t.tone === "danger" ? "text-danger" : "text-info")} />
|
|
<span className="max-w-xs">{t.message}</span>
|
|
<button onClick={() => dismissToast(t.id)} className="ml-1 text-muted hover:text-ink"><X size={14} /></button>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|