Improve the frontend 3
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user