mega-plant/components/Toast.tsx

69 lines
2.4 KiB
TypeScript
Raw Normal View History

"use client";
import { useEffect, useState } from "react";
import { CheckCircle2, X } from "lucide-react";
// A minimal toast for a confirmed import. Deliberately not a dependency: the app needs
// exactly one, and a full toast library would ship far more surface than that justifies.
//
// Solid teal rather than a new green: #00798C is already this app's success colour (the
// "All booked" badge, the income tiles, the import button), so it reads as success without
// introducing a hue outside the palette.
//
// Announced politely rather than assertively — a confirmation is not urgent, and it
// should not interrupt whatever a screen reader is currently saying.
export function Toast({
message,
detail,
onDismiss,
duration = 6000,
}: {
message: string;
detail?: string;
onDismiss: () => void;
duration?: number;
}) {
const [leaving, setLeaving] = useState(false);
const close = () => {
setLeaving(true);
setTimeout(onDismiss, 200); // let the transition run before unmounting
};
useEffect(() => {
const t = setTimeout(close, duration);
return () => clearTimeout(t);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [message, detail, duration]);
return (
<div
role="status"
aria-live="polite"
className={`pointer-events-none fixed z-[60] inset-x-3 sm:inset-x-auto sm:right-6 sm:w-auto sm:max-w-sm
pt-[max(0.75rem,env(safe-area-inset-top))] sm:pt-0 sm:top-6
transition-all duration-200 ease-out
${leaving ? "opacity-0 -translate-y-2" : "opacity-100 translate-y-0"}`}
>
<div
className={`pointer-events-auto flex items-start gap-3 px-4 py-3 rounded-xl
bg-[#00798C] text-white shadow-xl shadow-teal-900/20 border border-white/15
${leaving ? "" : "ring-1 ring-[#00798C]/30"}`}
>
<CheckCircle2 className="w-5 h-5 text-white shrink-0 mt-0.5" />
<div className="min-w-0 flex-1">
<div className="text-sm font-semibold text-white">{message}</div>
{detail && <div className="text-sm text-white/85 mt-0.5 break-words">{detail}</div>}
</div>
<button
type="button"
onClick={close}
aria-label="Dismiss"
className="grid place-items-center w-11 h-11 -mr-2 -my-1 rounded-lg text-white/80 hover:bg-white/15 hover:text-white transition shrink-0"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
);
}