Files
SRCmail/stores/toast-store.ts
T
Matthieu MALVACHEandMatthieu MALVACHE a43096485b feat: add UI/UX polish with navigation rail, confirm dialogs, welcome banner, and form validation
- Add NavigationRail component (desktop vertical icon sidebar + mobile bottom tab bar)
- Add ConfirmDialog with promise-based useConfirmDialog hook for async confirmation flow
- Add WelcomeBanner onboarding component (one-time display, localStorage persistence)
- Polish login form UX (shake on error, TOTP slide animation, password visibility toggle, session expired banner)
- Add inline form validation with shake animation in email composer and contacts
- Add empty state patterns for contacts (no data vs no search results with contextual actions)
- Improve toast notification system with undo action support and typed durations
- Add WCAG AA prefers-reduced-motion media query, safe area insets, sr-only live regions
- Add template settings tab and keyboard shortcut integration
- Update all 8 locale translations
2026-02-17 02:31:50 +01:00

61 lines
1.6 KiB
TypeScript

import { create } from "zustand";
import { Toast, ToastAction } from "@/components/ui/toast";
interface ToastStore {
toasts: Toast[];
addToast: (toast: Omit<Toast, "id">) => void;
removeToast: (id: string) => void;
clearToasts: () => void;
}
export const useToastStore = create<ToastStore>((set) => ({
toasts: [],
addToast: (toast) => {
const id = Math.random().toString(36).substring(2, 11);
const newToast: Toast = {
...toast,
id,
duration: toast.duration ?? 5000,
};
set((state) => ({
toasts: [...state.toasts, newToast],
}));
},
removeToast: (id) => {
set((state) => ({
toasts: state.toasts.filter((toast) => toast.id !== id),
}));
},
clearToasts: () => {
set({ toasts: [] });
},
}));
interface ToastOptions {
message?: string;
action?: ToastAction;
duration?: number;
}
function showToast(type: Toast["type"], title: string, options?: string | ToastOptions, defaultDuration?: number): void {
const opts = typeof options === "string" ? { message: options } : options;
useToastStore.getState().addToast({
type,
title,
message: opts?.message,
action: opts?.action,
duration: opts?.duration ?? defaultDuration,
});
}
export const toast = {
success: (title: string, options?: string | ToastOptions) => showToast("success", title, options),
error: (title: string, options?: string | ToastOptions) => showToast("error", title, options, 10000),
info: (title: string, options?: string | ToastOptions) => showToast("info", title, options),
warning: (title: string, options?: string | ToastOptions) => showToast("warning", title, options),
};