File
Blob: src/client/lib/toast.ts
| 1 | export type ToastVariant = "success" | "error" | "info"; |
| 2 | export type ToastItem = { id: string; variant: ToastVariant; message: string; createdAt: number }; |
| 3 | |
| 4 | const listeners = new Set<() => void>(); |
| 5 | let items: ToastItem[] = []; |
| 6 | |
| 7 | function emit() { |
| 8 | for (const fn of listeners) fn(); |
| 9 | } |
| 10 | |
| 11 | export function subscribe(fn: () => void) { |
| 12 | listeners.add(fn); |
| 13 | return () => listeners.delete(fn); |
| 14 | } |
| 15 | |
| 16 | export function snapshot() { |
| 17 | return items; |
| 18 | } |
| 19 | |
| 20 | function push(variant: ToastVariant, message: string) { |
| 21 | const id = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`; |
| 22 | items = [...items, { id, variant, message, createdAt: Date.now() }]; |
| 23 | emit(); |
| 24 | return id; |
| 25 | } |
| 26 | |
| 27 | export function dismissToast(id: string) { |
| 28 | items = items.filter((t) => t.id !== id); |
| 29 | emit(); |
| 30 | } |
| 31 | |
| 32 | export const toast = { |
| 33 | success: (message: string) => push("success", message), |
| 34 | error: (message: string) => push("error", message), |
| 35 | info: (message: string) => push("info", message), |
| 36 | dismiss: dismissToast, |
| 37 | }; |