|
|
@@ -0,0 +1,43 @@
|
|
|
+type NotificationType = 'success' | 'error' | 'info' | 'loading';
|
|
|
+
|
|
|
+type Notification = {
|
|
|
+ id: string;
|
|
|
+ type: NotificationType;
|
|
|
+ message: string;
|
|
|
+ href?: string;
|
|
|
+ durationMs?: number;
|
|
|
+};
|
|
|
+
|
|
|
+class NotificationsStore {
|
|
|
+ items = $state<Notification[]>([]);
|
|
|
+
|
|
|
+ push(n: Omit<Notification, 'id'>): string {
|
|
|
+ const item: Notification = { ...n, id: crypto.randomUUID() };
|
|
|
+ this.items = [...this.items, item];
|
|
|
+ const duration = n.durationMs ?? 4000;
|
|
|
+ if (duration > 0) setTimeout(() => this.dismiss(item.id), duration);
|
|
|
+ return item.id;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** Replace fields on an existing notification by id. If type changes away from
|
|
|
+ * 'loading', schedules auto-dismiss using the updated durationMs (default 4000). */
|
|
|
+ update(id: string, partial: Partial<Omit<Notification, 'id'>>) {
|
|
|
+ this.items = this.items.map((n) => (n.id === id ? { ...n, ...partial } : n));
|
|
|
+ if (partial.type && partial.type !== 'loading') {
|
|
|
+ const item = this.items.find((n) => n.id === id);
|
|
|
+ if (item) {
|
|
|
+ const duration = item.durationMs ?? 4000;
|
|
|
+ if (duration > 0) setTimeout(() => this.dismiss(id), duration);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dismiss(id: string) {
|
|
|
+ this.items = this.items.filter((n) => n.id !== id);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const notificationsStore = new NotificationsStore();
|
|
|
+
|
|
|
+export type { Notification, NotificationType };
|
|
|
+export { notificationsStore };
|