| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
- import Toast, { ToastType } from '../components/Toast';
- import { registerToastHandler } from '../src/utils/toast';
- interface ToastItem {
- id: string;
- type: ToastType;
- title?: string;
- message: string;
- duration?: number;
- }
- interface ToastContextType {
- showToast: (type: ToastType, message: string, title?: string, duration?: number) => void;
- showSuccess: (message: string, title?: string) => void;
- showError: (message: string, title?: string) => void;
- showWarning: (message: string, title?: string) => void;
- showInfo: (message: string, title?: string) => void;
- }
- const ToastContext = createContext<ToastContextType | undefined>(undefined);
- export const useToast = () => {
- const context = useContext(ToastContext);
- if (!context) {
- throw new Error('useToast must be used within a ToastProvider');
- }
- return context;
- };
- interface ToastProviderProps {
- children: ReactNode;
- }
- export const ToastProvider: React.FC<ToastProviderProps> = ({ children }) => {
- const [toasts, setToasts] = useState<ToastItem[]>([]);
- const showToast = (type: ToastType, message: string, title?: string, duration?: number) => {
- const id = Date.now().toString();
- const newToast: ToastItem = { id, type, message, title, duration };
- setToasts(prev => {
- // Deduplicate identical messages: discard old one if current type and content are the same
- const filtered = prev.filter(t => t.message !== message || t.type !== type);
- return [...filtered, newToast];
- });
- };
- useEffect(() => {
- registerToastHandler({ showToast });
- }, []);
- const removeToast = (id: string) => {
- setToasts(prev => prev.filter(toast => toast.id !== id));
- };
- const showSuccess = (message: string, title?: string) => showToast('success', message, title);
- const showError = (message: string, title?: string) => showToast('error', message, title);
- const showWarning = (message: string, title?: string) => showToast('warning', message, title);
- const showInfo = (message: string, title?: string) => showToast('info', message, title);
- return (
- <ToastContext.Provider value={{ showToast, showSuccess, showError, showWarning, showInfo }}>
- {children}
- <div className="fixed bottom-0 right-0 z-[9999] p-4 space-y-3 pointer-events-none w-full max-w-sm flex flex-col items-end">
- {toasts.map((toast) => (
- <div
- key={toast.id}
- className="pointer-events-auto w-full"
- >
- <Toast
- type={toast.type}
- title={toast.title}
- message={toast.message}
- duration={toast.duration}
- onClose={() => removeToast(toast.id)}
- />
- </div>
- ))}
- </div>
- </ToastContext.Provider>
- );
- };
|