mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-21 03:39:54 +00:00
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { createPortal } from "react-dom";
|
|
|
|
export function Toast({ toast }: { toast: { message: string; type: "success" | "error" } | null }) {
|
|
const [visible, setVisible] = useState(false);
|
|
const [current, setCurrent] = useState(toast);
|
|
|
|
useEffect(() => {
|
|
if (toast) {
|
|
setCurrent(toast);
|
|
setVisible(true);
|
|
} else {
|
|
setVisible(false);
|
|
const timer = setTimeout(() => setCurrent(null), 200);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [toast]);
|
|
|
|
if (!current) return null;
|
|
|
|
// Portal to document.body so the toast escapes any ancestor stacking context
|
|
// (e.g. <main> has `relative z-2`, which would trap z-50 below the header's z-40).
|
|
return createPortal(
|
|
<div
|
|
role="status"
|
|
aria-live="polite"
|
|
className={`fixed top-16 right-4 z-50 border px-4 py-2.5 font-courier text-xs tracking-wider uppercase backdrop-blur-sm ${
|
|
current.type === "success"
|
|
? "bg-success/15 text-success border-success/30"
|
|
: "bg-destructive/15 text-destructive border-destructive/30"
|
|
}`}
|
|
style={{
|
|
animation: visible ? "toast-in 200ms ease-out forwards" : "toast-out 200ms ease-in forwards",
|
|
}}
|
|
>
|
|
{current.message}
|
|
</div>,
|
|
document.body,
|
|
);
|
|
}
|