File
Blob: src/client/components/toast-store.ts
| 1 | export type ToastVariant = "success" | "error" | "info"; |
| 2 | |
| 3 | export interface Toast { |
| 4 | id: number; |
| 5 | message: string; |
| 6 | variant: ToastVariant; |
| 7 | exiting?: boolean; |
| 8 | } |
| 9 | |
| 10 | let nextId = 0; |
| 11 | let toasts: Toast[] = []; |
| 12 | const listeners = new Set<() => void>(); |
| 13 | |
| 14 | function emit() { |
| 15 | for (const listener of listeners) listener(); |
| 16 | } |
| 17 | |
| 18 | function add(message: string, variant: ToastVariant) { |
| 19 | const id = ++nextId; |
| 20 | toasts = [{ id, message, variant }, ...toasts].slice(0, 3); |
| 21 | emit(); |
| 22 | setTimeout(() => { |
| 23 | toasts = toasts.map((toast) => (toast.id === id ? { ...toast, exiting: true } : toast)); |
| 24 | emit(); |
| 25 | }, 3800); |
| 26 | setTimeout(() => { |
| 27 | toasts = toasts.filter((toast) => toast.id !== id); |
| 28 | emit(); |
| 29 | }, 4000); |
| 30 | } |
| 31 | |
| 32 | export function subscribeToast(listener: () => void) { |
| 33 | listeners.add(listener); |
| 34 | return () => listeners.delete(listener); |
| 35 | } |
| 36 | |
| 37 | export function getToastSnapshot() { |
| 38 | return toasts; |
| 39 | } |
| 40 | |
| 41 | export const toast = { |
| 42 | success: (message: string) => add(message, "success"), |
| 43 | error: (message: string) => add(message, "error"), |
| 44 | info: (message: string) => add(message, "info"), |
| 45 | }; |