75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
'use client'
|
|
|
|
import { createContext, useContext, useState, ReactNode } from 'react'
|
|
import { X, CheckCircle, AlertCircle, Info } from 'lucide-react'
|
|
|
|
type ToastType = 'success' | 'error' | 'info'
|
|
|
|
interface Toast {
|
|
id: number
|
|
message: string
|
|
type: ToastType
|
|
}
|
|
|
|
interface ToastContextType {
|
|
showToast: (message: string, type?: ToastType) => void
|
|
}
|
|
|
|
const ToastContext = createContext<ToastContextType | undefined>(undefined)
|
|
|
|
export function ToastProvider({ children }: { children: ReactNode }) {
|
|
const [toasts, setToasts] = useState<Toast[]>([])
|
|
|
|
const showToast = (message: string, type: ToastType = 'info') => {
|
|
const id = Date.now()
|
|
setToasts((prev) => [...prev, { id, message, type }])
|
|
|
|
setTimeout(() => {
|
|
setToasts((prev) => prev.filter((toast) => toast.id !== id))
|
|
}, 3000)
|
|
}
|
|
|
|
const removeToast = (id: number) => {
|
|
setToasts((prev) => prev.filter((toast) => toast.id !== id))
|
|
}
|
|
|
|
return (
|
|
<ToastContext.Provider value={{ showToast }}>
|
|
{children}
|
|
<div className="fixed bottom-24 right-6 z-50 flex flex-col gap-2">
|
|
{toasts.map((toast) => (
|
|
<div
|
|
key={toast.id}
|
|
className={`flex items-center gap-3 px-4 py-3 rounded-lg shadow-lg backdrop-blur-sm animate-slide-in-right ${
|
|
toast.type === 'success'
|
|
? 'bg-green-500/90 text-white'
|
|
: toast.type === 'error'
|
|
? 'bg-red-500/90 text-white'
|
|
: 'bg-blue-500/90 text-white'
|
|
}`}
|
|
>
|
|
{toast.type === 'success' && <CheckCircle size={20} />}
|
|
{toast.type === 'error' && <AlertCircle size={20} />}
|
|
{toast.type === 'info' && <Info size={20} />}
|
|
<p className="flex-1">{toast.message}</p>
|
|
<button
|
|
onClick={() => removeToast(toast.id)}
|
|
className="hover:opacity-70 transition-opacity"
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</ToastContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useToast() {
|
|
const context = useContext(ToastContext)
|
|
if (!context) {
|
|
throw new Error('useToast must be used within ToastProvider')
|
|
}
|
|
return context
|
|
}
|