File size: 1,446 Bytes
1f21206 | 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 | import { useUIStore, type Toast as ToastType } from '../../stores/uiStore'
const typeStyles: Record<ToastType['type'], string> = {
success: 'border-l-4 border-l-[var(--color-success)]',
error: 'border-l-4 border-l-[var(--color-error)]',
warning: 'border-l-4 border-l-[var(--color-warning)]',
info: 'border-l-4 border-l-[var(--color-text-accent)]',
}
function ToastItem({ toast }: { toast: ToastType }) {
const removeToast = useUIStore((s) => s.removeToast)
return (
<div
className={`
bg-[var(--color-surface)] rounded-[var(--radius-md)] shadow-[var(--shadow-dropdown)]
px-4 py-3 text-sm text-[var(--color-text-primary)]
${typeStyles[toast.type]}
animate-in slide-in-from-right fade-in duration-200
`}
>
<div className="flex items-center justify-between gap-2">
<span>{toast.message}</span>
<button
onClick={() => removeToast(toast.id)}
className="text-[var(--color-text-tertiary)] hover:text-[var(--color-text-primary)] text-lg leading-none"
>
×
</button>
</div>
</div>
)
}
export function ToastContainer() {
const toasts = useUIStore((s) => s.toasts)
if (toasts.length === 0) return null
return (
<div className="fixed bottom-4 right-4 z-[100] flex flex-col gap-2 max-w-sm">
{toasts.map((toast) => (
<ToastItem key={toast.id} toast={toast} />
))}
</div>
)
}
|