Spaces:
Running
Running
| 'use client'; | |
| import { createContext, useContext, useState, useCallback } from 'react'; | |
| import './ToastContext.css'; | |
| const ToastContext = createContext(null); | |
| export function ToastProvider({ children }) { | |
| const [toasts, setToasts] = useState([]); | |
| const addToast = useCallback((message, type = 'info', duration = 3000) => { | |
| const id = Date.now(); | |
| setToasts(prev => [...prev, { id, message, type }]); | |
| setTimeout(() => { | |
| setToasts(prev => prev.filter(t => t.id !== id)); | |
| }, duration); | |
| }, []); | |
| const removeToast = useCallback(id => { | |
| setToasts(prev => prev.filter(t => t.id !== id)); | |
| }, []); | |
| return ( | |
| <ToastContext.Provider value={{ addToast }}> | |
| {children} | |
| <div className="toast-container" aria-live="polite"> | |
| {toasts.map(toast => ( | |
| <div key={toast.id} className={`toast toast-${toast.type} fade-in-up`}> | |
| {toast.type === 'success' && '✅ '} | |
| {toast.type === 'error' && '❌ '} | |
| {toast.type === 'info' && '💡 '} | |
| {toast.message} | |
| <button | |
| className="toast-close" | |
| onClick={() => removeToast(toast.id)} | |
| aria-label="Close" | |
| > | |
| × | |
| </button> | |
| </div> | |
| ))} | |
| </div> | |
| </ToastContext.Provider> | |
| ); | |
| } | |
| export function useToast() { | |
| const context = useContext(ToastContext); | |
| if (!context) { | |
| throw new Error('useToast must be used within a ToastProvider'); | |
| } | |
| return context; | |
| } | |