54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
// App-wide notices — Svelte 5 rune store (.svelte.ts). Rendered globally by
|
|
// <NoticeDialog /> in the root layout.
|
|
export type NoticeType = 'info' | 'success' | 'warning' | 'error';
|
|
|
|
export interface Notice {
|
|
id: string;
|
|
type: NoticeType;
|
|
title: string;
|
|
message?: string;
|
|
action?: { label: string; href: string };
|
|
dismissible: boolean;
|
|
}
|
|
|
|
let idCounter = 0;
|
|
|
|
class NoticeStore {
|
|
list = $state<Notice[]>([]);
|
|
|
|
add(notice: Omit<Notice, 'id'>): string {
|
|
const id = `notice-${Date.now()}-${idCounter++}`;
|
|
this.list.push({ ...notice, id });
|
|
return id;
|
|
}
|
|
|
|
remove(id: string) {
|
|
this.list = this.list.filter((n) => n.id !== id);
|
|
}
|
|
|
|
clear() {
|
|
this.list = [];
|
|
}
|
|
|
|
success(title: string, message?: string, action?: Notice['action']) {
|
|
return this.add({ type: 'success', title, message, action, dismissible: true });
|
|
}
|
|
info(title: string, message?: string, action?: Notice['action']) {
|
|
return this.add({ type: 'info', title, message, action, dismissible: true });
|
|
}
|
|
warning(title: string, message?: string, action?: Notice['action']) {
|
|
return this.add({ type: 'warning', title, message, action, dismissible: true });
|
|
}
|
|
error(title: string, message?: string, action?: Notice['action']) {
|
|
return this.add({ type: 'error', title, message, action, dismissible: true });
|
|
}
|
|
}
|
|
|
|
export const notices = new NoticeStore();
|
|
|
|
// Fire-and-forget helper — auto-dismisses after `duration` ms.
|
|
export function addAutoDismissNotice(notice: Omit<Notice, 'id'>, duration = 5000): string {
|
|
const id = notices.add({ ...notice, dismissible: true });
|
|
setTimeout(() => notices.remove(id), duration);
|
|
return id;
|
|
} |