39 lines
1.4 KiB
TypeScript
39 lines
1.4 KiB
TypeScript
import React from 'react';
|
|
|
|
type Toast = { id: number; text: string; type?: 'success'|'error' };
|
|
const Ctx = React.createContext<{ push: (t: Omit<Toast,'id'>) => void } | null>(null);
|
|
|
|
export function ToastProvider({ children }: { children: React.ReactNode }) {
|
|
const [list, setList] = React.useState<Toast[]>([]);
|
|
const push = React.useCallback((t: Omit<Toast,'id'>) => {
|
|
const id = Date.now() + Math.random();
|
|
setList((arr) => [...arr, { id, ...t }]);
|
|
setTimeout(() => setList((arr) => arr.filter((x) => x.id !== id)), 3000);
|
|
}, []);
|
|
React.useEffect(() => {
|
|
const onEvt = (e: any) => push(e.detail);
|
|
window.addEventListener('guest-toast', onEvt);
|
|
return () => window.removeEventListener('guest-toast', onEvt);
|
|
}, [push]);
|
|
return (
|
|
<Ctx.Provider value={{ push }}>
|
|
{children}
|
|
<div className="pointer-events-none fixed inset-x-0 bottom-4 z-50 flex justify-center px-4">
|
|
<div className="flex w-full max-w-sm flex-col gap-2">
|
|
{list.map((t) => (
|
|
<div key={t.id} className={`pointer-events-auto rounded-md border p-3 shadow-sm ${t.type==='error'?'border-red-300 bg-red-50 text-red-700':'border-green-300 bg-green-50 text-green-700'}`}>
|
|
{t.text}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Ctx.Provider>
|
|
);
|
|
}
|
|
|
|
export function useToast() {
|
|
const ctx = React.useContext(Ctx);
|
|
if (!ctx) throw new Error('ToastProvider missing');
|
|
return ctx;
|
|
}
|