All checks were successful
Deploy / deploy (push) Successful in 11s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
57 lines
1.7 KiB
JavaScript
57 lines
1.7 KiB
JavaScript
import { createContext, useContext, useState, useCallback } from 'react'
|
|
import Toast from './Toast'
|
|
|
|
const ToastContext = createContext()
|
|
|
|
export function useToast() {
|
|
const context = useContext(ToastContext)
|
|
if (!context) {
|
|
throw new Error('useToast must be used within ToastProvider')
|
|
}
|
|
return context
|
|
}
|
|
|
|
export function ToastProvider({ children }) {
|
|
const [toasts, setToasts] = useState([])
|
|
|
|
const addToast = useCallback((message, type = 'info', duration = 4000) => {
|
|
const id = Date.now() + Math.random()
|
|
setToasts(prev => [...prev, { id, message, type, duration }])
|
|
}, [])
|
|
|
|
const removeToast = useCallback((id) => {
|
|
setToasts(prev => prev.map(t => t.id === id ? { ...t, exiting: true } : t))
|
|
setTimeout(() => {
|
|
setToasts(prev => prev.filter(t => t.id !== id))
|
|
}, 300)
|
|
}, [])
|
|
|
|
const toast = {
|
|
success: (message, duration) => addToast(message, 'success', duration),
|
|
error: (message, duration) => addToast(message, 'error', duration),
|
|
info: (message, duration) => addToast(message, 'info', duration),
|
|
warning: (message, duration) => addToast(message, 'warning', duration),
|
|
}
|
|
|
|
return (
|
|
<ToastContext.Provider value={toast}>
|
|
{children}
|
|
{/* Toast container - fixed position */}
|
|
<div className="fixed top-4 right-4 z-[10000] flex flex-col gap-2 pointer-events-none">
|
|
<div className="flex flex-col gap-2 pointer-events-auto">
|
|
{toasts.map(t => (
|
|
<Toast
|
|
key={t.id}
|
|
message={t.message}
|
|
type={t.type}
|
|
duration={t.duration}
|
|
exiting={t.exiting}
|
|
onClose={() => removeToast(t.id)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</ToastContext.Provider>
|
|
)
|
|
}
|