make toasts accessible: ARIA live region, dismiss button, pause on hover
This commit is contained in:
@@ -12,9 +12,28 @@ interface ToastState {
|
||||
toasts: Toast[];
|
||||
addToast: (message: string, type?: ToastType) => void;
|
||||
removeToast: (id: string) => void;
|
||||
pauseToast: (id: string) => void;
|
||||
resumeToast: (id: string) => void;
|
||||
}
|
||||
|
||||
let nextId = 0;
|
||||
const timers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
const remaining = new Map<string, number>();
|
||||
const startTimes = new Map<string, number>();
|
||||
|
||||
const TOAST_DURATION = 8000;
|
||||
|
||||
function startTimer(id: string, duration: number, set: (fn: (s: ToastState) => Partial<ToastState>) => void) {
|
||||
startTimes.set(id, Date.now());
|
||||
remaining.set(id, duration);
|
||||
const timer = setTimeout(() => {
|
||||
set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }));
|
||||
timers.delete(id);
|
||||
remaining.delete(id);
|
||||
startTimes.delete(id);
|
||||
}, duration);
|
||||
timers.set(id, timer);
|
||||
}
|
||||
|
||||
export const useToastStore = create<ToastState>((set) => ({
|
||||
toasts: [],
|
||||
@@ -22,12 +41,33 @@ export const useToastStore = create<ToastState>((set) => ({
|
||||
addToast: (message, type = "info") => {
|
||||
const id = String(++nextId);
|
||||
set((s) => ({ toasts: [...s.toasts, { id, message, type }] }));
|
||||
setTimeout(() => {
|
||||
set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }));
|
||||
}, 3000);
|
||||
startTimer(id, TOAST_DURATION, set);
|
||||
},
|
||||
|
||||
removeToast: (id) => {
|
||||
const timer = timers.get(id);
|
||||
if (timer) clearTimeout(timer);
|
||||
timers.delete(id);
|
||||
remaining.delete(id);
|
||||
startTimes.delete(id);
|
||||
set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) }));
|
||||
},
|
||||
|
||||
pauseToast: (id) => {
|
||||
const timer = timers.get(id);
|
||||
const start = startTimes.get(id);
|
||||
const rem = remaining.get(id);
|
||||
if (timer && start != null && rem != null) {
|
||||
clearTimeout(timer);
|
||||
timers.delete(id);
|
||||
remaining.set(id, rem - (Date.now() - start));
|
||||
}
|
||||
},
|
||||
|
||||
resumeToast: (id) => {
|
||||
const rem = remaining.get(id);
|
||||
if (rem != null && rem > 0) {
|
||||
startTimer(id, rem, set);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user