import { useEffect, useMemo, useState } from 'react'; import { createPortal } from 'react-dom'; import { OVERLAY_Z_TOAST } from '../../lib/overlayLayers'; import { __ } from '../../lib/i18n'; import { NavIcon } from '../NavIcon'; export type ToastKind = 'success' | 'error' | 'info'; export type ToastState = { open: boolean; kind: ToastKind; title: string; message?: string; /** Auto-dismiss after this many ms. Set 0 to require manual close. */ ttlMs?: number; }; export function useTopRightToast(defaultTtlMs = 3800) { const [toast, setToast] = useState(null); useEffect(() => { if (!toast?.open) return; const ttl = toast.ttlMs ?? defaultTtlMs; if (!ttl || ttl <= 0) return; const t = window.setTimeout(() => setToast(null), ttl); return () => window.clearTimeout(t); }, [toast, defaultTtlMs]); const api = useMemo(() => { return { toast, clear: () => setToast(null), show: (next: Omit & { open?: boolean }) => setToast({ ttlMs: defaultTtlMs, ...next, open: next.open ?? true }), success: (title: string, message?: string) => setToast({ open: true, kind: 'success', title, message, ttlMs: defaultTtlMs }), error: (title: string, message?: string) => setToast({ open: true, kind: 'error', title, message, ttlMs: defaultTtlMs }), info: (title: string, message?: string) => setToast({ open: true, kind: 'info', title, message, ttlMs: defaultTtlMs }), }; }, [toast, defaultTtlMs]); return api; } export function TopRightToast(props: { toast: ToastState | null; onDismiss: () => void }) { const { toast, onDismiss } = props; if (!toast?.open || typeof document === 'undefined') return null; const palette = toast.kind === 'success' ? { border: 'border-emerald-200 dark:border-emerald-900/50', bg: 'bg-emerald-50/95 text-emerald-900 dark:bg-emerald-950/60 dark:text-emerald-100', iconBg: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-200', icon: 'badge' as const, } : toast.kind === 'error' ? { border: 'border-rose-200 dark:border-rose-900/50', bg: 'bg-rose-50/95 text-rose-900 dark:bg-rose-950/60 dark:text-rose-100', iconBg: 'bg-rose-100 text-rose-700 dark:bg-rose-900/40 dark:text-rose-200', icon: 'helpCircle' as const, } : { border: 'border-slate-200 dark:border-slate-800', bg: 'bg-white/95 text-slate-900 dark:bg-slate-900/90 dark:text-slate-100', iconBg: 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-200', icon: 'helpCircle' as const, }; return createPortal(
{toast.title}
{toast.message ? (
{toast.message}
) : null}
, document.body ); }