Toast.jsx
2.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React, { createContext, useContext, useState, useCallback } from 'react';
import { CheckCircle2, AlertCircle, Info, X } from 'lucide-react';
const ToastContext = createContext(null);
export function ToastProvider({ children }) {
const [toasts, setToasts] = useState([]);
const addToast = useCallback((msg, type = 'info', duration = 3000) => {
const id = Date.now() + Math.random().toString(36).substring(2, 6);
setToasts((prev) => [...prev, { id, msg, type }]);
if (duration > 0) {
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="fixed top-4 right-4 z-50 flex flex-col gap-2 pointer-events-none">
{toasts.map((t) => (
<div
key={t.id}
className={`pointer-events-auto flex items-center gap-3 px-4 py-3 rounded-xl shadow-2xl border backdrop-blur-md transition-all duration-300 animate-in slide-in-from-top-2 ${
t.type === 'success'
? 'bg-emerald-950/90 border-emerald-500/40 text-emerald-200 shadow-emerald-950/50'
: t.type === 'error'
? 'bg-rose-950/90 border-rose-500/40 text-rose-200 shadow-rose-950/50'
: 'bg-zinc-900/95 border-zinc-700/60 text-zinc-200 shadow-black/60'
}`}
>
{t.type === 'success' && <CheckCircle2 className="w-5 h-5 text-emerald-400 shrink-0" />}
{t.type === 'error' && <AlertCircle className="w-5 h-5 text-rose-400 shrink-0" />}
{t.type === 'info' && <Info className="w-5 h-5 text-indigo-400 shrink-0" />}
<span className="text-sm font-medium">{t.msg}</span>
<button
onClick={() => removeToast(t.id)}
className="ml-2 text-zinc-400 hover:text-white p-0.5 rounded-lg hover:bg-white/10 transition-colors"
>
<X className="w-4 h-4" />
</button>
</div>
))}
</div>
</ToastContext.Provider>
);
}
export function useToast() {
const ctx = useContext(ToastContext);
if (!ctx) throw new Error('useToast must be used within ToastProvider');
return ctx;
}