'use client' import { useState, useEffect } from 'react' import { createPortal } from 'react-dom' export interface ToastProps { message: string type: 'success' | 'error' | 'warning' | 'info' duration?: number onClose: () => void } const TOAST_ICONS = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' } as const const TOAST_STYLES = { success: 'bg-green-50 border-green-200 text-green-800', error: 'bg-red-50 border-red-200 text-red-800', warning: 'bg-yellow-50 border-yellow-200 text-yellow-800', info: 'bg-blue-50 border-blue-200 text-blue-800' } as const export function Toast({ message, type, duration = 3000, onClose, position, isLatest }: ToastProps & { position: number, isLatest: boolean }) { const [isVisible, setIsVisible] = useState(!isLatest) // 舊通知直接顯示,新通知需要動畫 const [mounted, setMounted] = useState(false) const [hasShownEntrance, setHasShownEntrance] = useState(!isLatest) // 追蹤是否已經顯示過入場動畫 useEffect(() => { setMounted(true) // 只有最新的通知且尚未顯示過入場動畫才需要滑入動畫 if (isLatest && !hasShownEntrance) { const showTimer = setTimeout(() => { setIsVisible(true) setHasShownEntrance(true) }, 50) return () => clearTimeout(showTimer) } }, [isLatest, hasShownEntrance]) useEffect(() => { // 自動消失計時器 const hideTimer = setTimeout(() => { setIsVisible(false) // 等待動畫完成後關閉 setTimeout(onClose, 300) }, duration) return () => clearTimeout(hideTimer) }, [duration, onClose]) if (!mounted) return null // 計算垂直位置:第一個在 top-4,後續每個往下偏移 80px const topPosition = 16 + (position * 80) // 16px (top-4) + 80px * position return createPortal(
{message}