51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import { AnimatePresence, motion } from 'framer-motion'
|
|
import { X, Info, AlertTriangle, CheckCircle, AlertCircle } from 'lucide-react'
|
|
import { useNotificationStore, type NotificationType } from '../../store/notificationStore'
|
|
|
|
const icons: Record<NotificationType, React.ReactNode> = {
|
|
info: <Info className="w-4 h-4 flex-shrink-0" />,
|
|
warning: <AlertTriangle className="w-4 h-4 flex-shrink-0" />,
|
|
success: <CheckCircle className="w-4 h-4 flex-shrink-0" />,
|
|
error: <AlertCircle className="w-4 h-4 flex-shrink-0" />,
|
|
}
|
|
|
|
const styles: Record<NotificationType, string> = {
|
|
info: 'bg-[#1e293b] text-white border-l-4 border-l-blue-400',
|
|
warning: 'bg-[#1e293b] text-white border-l-4 border-l-amber-400',
|
|
success: 'bg-[#1e293b] text-white border-l-4 border-l-emerald-400',
|
|
error: 'bg-[#1e293b] text-white border-l-4 border-l-red-400',
|
|
}
|
|
|
|
export default function NotificationToast() {
|
|
const { notifications, remove } = useNotificationStore()
|
|
|
|
return (
|
|
<div className="fixed top-4 right-4 z-[9999] flex flex-col gap-2 pointer-events-none">
|
|
<AnimatePresence>
|
|
{notifications.map((n) => (
|
|
<motion.div
|
|
key={n.id}
|
|
initial={{ opacity: 0, x: 60, scale: 0.95 }}
|
|
animate={{ opacity: 1, x: 0, scale: 1 }}
|
|
exit={{ opacity: 0, x: 60, scale: 0.95 }}
|
|
transition={{ duration: 0.2, ease: 'easeOut' }}
|
|
className={`pointer-events-auto flex items-start gap-3 px-4 py-3 rounded-lg shadow-xl text-sm max-w-sm ${styles[n.type]}`}
|
|
>
|
|
<span className="mt-0.5">{icons[n.type]}</span>
|
|
<span className="flex-1 leading-snug">{n.message}</span>
|
|
<button
|
|
onClick={() => remove(n.id)}
|
|
className="ml-1 opacity-60 hover:opacity-100 transition-opacity"
|
|
>
|
|
<X className="w-3.5 h-3.5" />
|
|
</button>
|
|
</motion.div>
|
|
))}
|
|
</AnimatePresence>
|
|
</div>
|
|
)
|
|
}
|