Files
bakery-ia/frontend/src/hooks/ui/useToast.ts

182 lines
4.6 KiB
TypeScript
Raw Normal View History

2025-08-28 10:41:04 +02:00
/**
* Toast hook for managing toast notifications
*/
import { useState, useCallback, useEffect } from 'react';
export type ToastType = 'success' | 'error' | 'warning' | 'info';
export type ToastPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
export interface Toast {
id: string;
type: ToastType;
title?: string;
message: string;
duration?: number;
dismissible?: boolean;
action?: {
label: string;
onClick: () => void;
};
timestamp: number;
}
interface ToastState {
toasts: Toast[];
position: ToastPosition;
maxToasts: number;
}
interface ToastOptions {
type?: ToastType;
title?: string;
duration?: number;
dismissible?: boolean;
action?: {
label: string;
onClick: () => void;
};
}
interface ToastActions {
addToast: (message: string, options?: ToastOptions) => string;
removeToast: (id: string) => void;
clearToasts: () => void;
success: (message: string, options?: Omit<ToastOptions, 'type'>) => string;
error: (message: string, options?: Omit<ToastOptions, 'type'>) => string;
warning: (message: string, options?: Omit<ToastOptions, 'type'>) => string;
info: (message: string, options?: Omit<ToastOptions, 'type'>) => string;
setPosition: (position: ToastPosition) => void;
setMaxToasts: (max: number) => void;
}
const DEFAULT_DURATION = 5000; // 5 seconds
const DEFAULT_POSITION: ToastPosition = 'top-right';
const DEFAULT_MAX_TOASTS = 6;
// Generate unique ID
const generateId = (): string => {
return `toast_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
};
export const useToast = (
initialPosition: ToastPosition = DEFAULT_POSITION,
initialMaxToasts: number = DEFAULT_MAX_TOASTS
): ToastState & ToastActions => {
const [state, setState] = useState<ToastState>({
toasts: [],
position: initialPosition,
maxToasts: initialMaxToasts,
});
// Remove toast by ID
const removeToast = useCallback((id: string) => {
setState(prev => ({
...prev,
toasts: prev.toasts.filter(toast => toast.id !== id),
}));
}, []);
// Add toast
const addToast = useCallback((message: string, options: ToastOptions = {}): string => {
const id = generateId();
const toast: Toast = {
id,
type: options.type || 'info',
title: options.title,
message,
duration: options.duration ?? DEFAULT_DURATION,
dismissible: options.dismissible ?? true,
action: options.action,
timestamp: Date.now(),
};
setState(prev => {
const newToasts = [...prev.toasts, toast];
// Limit number of toasts
if (newToasts.length > prev.maxToasts) {
return {
...prev,
toasts: newToasts.slice(-prev.maxToasts),
};
}
return {
...prev,
toasts: newToasts,
};
});
// Auto-dismiss toast if duration is set
if (toast.duration && toast.duration > 0) {
setTimeout(() => {
removeToast(id);
}, toast.duration);
}
return id;
}, [removeToast]);
// Clear all toasts
const clearToasts = useCallback(() => {
setState(prev => ({
...prev,
toasts: [],
}));
}, []);
// Convenience methods for different toast types
const success = useCallback((message: string, options: Omit<ToastOptions, 'type'> = {}) => {
return addToast(message, { ...options, type: 'success' });
}, [addToast]);
const error = useCallback((message: string, options: Omit<ToastOptions, 'type'> = {}) => {
return addToast(message, { ...options, type: 'error', duration: options.duration ?? 8000 });
}, [addToast]);
const warning = useCallback((message: string, options: Omit<ToastOptions, 'type'> = {}) => {
return addToast(message, { ...options, type: 'warning' });
}, [addToast]);
const info = useCallback((message: string, options: Omit<ToastOptions, 'type'> = {}) => {
return addToast(message, { ...options, type: 'info' });
}, [addToast]);
// Set toast position
const setPosition = useCallback((position: ToastPosition) => {
setState(prev => ({
...prev,
position,
}));
}, []);
// Set maximum number of toasts
const setMaxToasts = useCallback((maxToasts: number) => {
setState(prev => {
const newToasts = prev.toasts.length > maxToasts
? prev.toasts.slice(-maxToasts)
: prev.toasts;
return {
...prev,
maxToasts,
toasts: newToasts,
};
});
}, []);
return {
...state,
addToast,
removeToast,
clearToasts,
success,
error,
warning,
info,
setPosition,
setMaxToasts,
};
};