96 lines
2.2 KiB
React
96 lines
2.2 KiB
React
|
|
import { useEffect } from 'react'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Toast Notification Component
|
|||
|
|
*
|
|||
|
|
* Auto-closing notification that appears at the top of the screen
|
|||
|
|
*
|
|||
|
|
* Props:
|
|||
|
|
* - message: string
|
|||
|
|
* - type: 'success' | 'error' | 'warning' | 'info'
|
|||
|
|
* - duration: number (ms, default 3000)
|
|||
|
|
* - onClose: callback when toast closes
|
|||
|
|
*/
|
|||
|
|
export function Toast({ message, type = 'info', duration = 3000, onClose }) {
|
|||
|
|
useEffect(() => {
|
|||
|
|
const timer = setTimeout(() => {
|
|||
|
|
if (onClose) onClose()
|
|||
|
|
}, duration)
|
|||
|
|
|
|||
|
|
return () => clearTimeout(timer)
|
|||
|
|
}, [duration, onClose])
|
|||
|
|
|
|||
|
|
const styles = {
|
|||
|
|
success: {
|
|||
|
|
background: '#4CAF50',
|
|||
|
|
color: 'white',
|
|||
|
|
icon: '✅'
|
|||
|
|
},
|
|||
|
|
error: {
|
|||
|
|
background: 'var(--danger)',
|
|||
|
|
color: 'white',
|
|||
|
|
icon: '❌'
|
|||
|
|
},
|
|||
|
|
warning: {
|
|||
|
|
background: '#FFC107',
|
|||
|
|
color: '#856404',
|
|||
|
|
icon: '⚠️'
|
|||
|
|
},
|
|||
|
|
info: {
|
|||
|
|
background: 'var(--accent)',
|
|||
|
|
color: 'white',
|
|||
|
|
icon: 'ℹ️'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const style = styles[type] || styles.info
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
style={{
|
|||
|
|
position: 'fixed',
|
|||
|
|
top: '80px',
|
|||
|
|
left: '50%',
|
|||
|
|
transform: 'translateX(-50%)',
|
|||
|
|
background: style.background,
|
|||
|
|
color: style.color,
|
|||
|
|
padding: '12px 24px',
|
|||
|
|
borderRadius: '8px',
|
|||
|
|
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
|||
|
|
zIndex: 10000,
|
|||
|
|
display: 'flex',
|
|||
|
|
alignItems: 'center',
|
|||
|
|
gap: '8px',
|
|||
|
|
fontSize: '14px',
|
|||
|
|
fontWeight: 500,
|
|||
|
|
minWidth: '300px',
|
|||
|
|
maxWidth: '600px',
|
|||
|
|
animation: 'slideDown 0.3s ease-out'
|
|||
|
|
}}
|
|||
|
|
onClick={onClose}
|
|||
|
|
>
|
|||
|
|
<span style={{ fontSize: '18px' }}>{style.icon}</span>
|
|||
|
|
<span>{message}</span>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Add animation CSS if not already in global styles
|
|||
|
|
const styleElement = document.createElement('style')
|
|||
|
|
styleElement.textContent = `
|
|||
|
|
@keyframes slideDown {
|
|||
|
|
from {
|
|||
|
|
opacity: 0;
|
|||
|
|
transform: translateX(-50%) translateY(-20px);
|
|||
|
|
}
|
|||
|
|
to {
|
|||
|
|
opacity: 1;
|
|||
|
|
transform: translateX(-50%) translateY(0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
`
|
|||
|
|
if (!document.querySelector('style[data-toast-styles]')) {
|
|||
|
|
styleElement.setAttribute('data-toast-styles', 'true')
|
|||
|
|
document.head.appendChild(styleElement)
|
|||
|
|
}
|