feat(frontend): add shared banner system (error/warning/notice/success)
A plain module-level singleton store (banner.ts) plus a single renderer (BannerStack, mounted once above LoginGate in main.tsx) -- no React Context, since this codebase has none today and a plain exported function is callable from anywhere, including api/client.ts (a plain module, not a component) in a later task. Nothing calls it yet; that's each of this plan's remaining tasks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
54
frontend/src/banner.ts
Normal file
54
frontend/src/banner.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
export type BannerSeverity = "error" | "warning" | "notice" | "success";
|
||||
|
||||
export interface BannerEntry {
|
||||
id: number;
|
||||
severity: BannerSeverity;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const DISMISS_AFTER_MS = 8000;
|
||||
|
||||
let banners: BannerEntry[] = [];
|
||||
let nextId = 1;
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
function notify() {
|
||||
for (const listener of listeners) listener();
|
||||
}
|
||||
|
||||
function show(severity: BannerSeverity, message: string): void {
|
||||
const id = nextId++;
|
||||
banners = [{ id, severity, message }, ...banners];
|
||||
notify();
|
||||
setTimeout(() => dismiss(id), DISMISS_AFTER_MS);
|
||||
}
|
||||
|
||||
export function showError(message: string): void {
|
||||
show("error", message);
|
||||
}
|
||||
|
||||
export function showWarning(message: string): void {
|
||||
show("warning", message);
|
||||
}
|
||||
|
||||
export function showNotice(message: string): void {
|
||||
show("notice", message);
|
||||
}
|
||||
|
||||
export function showSuccess(message: string): void {
|
||||
show("success", message);
|
||||
}
|
||||
|
||||
export function dismiss(id: number): void {
|
||||
banners = banners.filter((b) => b.id !== id);
|
||||
notify();
|
||||
}
|
||||
|
||||
export function subscribe(listener: () => void): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
}
|
||||
|
||||
export function getSnapshot(): BannerEntry[] {
|
||||
return banners;
|
||||
}
|
||||
Reference in New Issue
Block a user