65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
export type ToastType = 'success' | 'error' | 'info';
|
|
|
|
export interface ToastProps {
|
|
message: string;
|
|
type: ToastType;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export const Toast: React.FC<ToastProps> = ({ message, type, onClose }) => {
|
|
const [visible, setVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Start entry animation
|
|
requestAnimationFrame(() => setVisible(true));
|
|
|
|
// Auto dismiss
|
|
const timer = setTimeout(() => {
|
|
setVisible(false);
|
|
// Allow exit animation to complete before unmounting
|
|
setTimeout(onClose, 300);
|
|
}, 3000);
|
|
|
|
return () => clearTimeout(timer);
|
|
}, [onClose]);
|
|
|
|
const baseClasses = "fixed top-6 left-1/2 transform -translate-x-1/2 z-[100] flex items-center gap-3 px-5 py-3 rounded-lg shadow-xl text-white font-medium text-sm transition-all duration-300 ease-out";
|
|
|
|
const typeClasses = {
|
|
success: "bg-emerald-600 ring-1 ring-emerald-500",
|
|
error: "bg-rose-600 ring-1 ring-rose-500",
|
|
info: "bg-indigo-600 ring-1 ring-indigo-500"
|
|
};
|
|
|
|
// Animation states
|
|
const opacityClass = visible ? "opacity-100 translate-y-0 scale-100" : "opacity-0 -translate-y-4 scale-95";
|
|
|
|
const icons = {
|
|
success: (
|
|
<svg className="w-5 h-5 text-emerald-100" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
),
|
|
error: (
|
|
<svg className="w-5 h-5 text-rose-100" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
),
|
|
info: (
|
|
<svg className="w-5 h-5 text-indigo-100" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
)
|
|
};
|
|
|
|
return (
|
|
<div className={`${baseClasses} ${typeClasses[type]} ${opacityClass}`}>
|
|
{icons[type]}
|
|
<span className="drop-shadow-sm">{message}</span>
|
|
</div>
|
|
);
|
|
};
|