2026-01-13 23:45:28 +01:00

68 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect } from 'react'
import type { ReactNode } from 'react'
export type ToastVariant = 'info' | 'success' | 'warning' | 'error'
interface ToastProps {
children: ReactNode
variant?: ToastVariant
duration?: number
onClose: () => void
'aria-label'?: string
}
function getVariantClasses(variant: ToastVariant): string {
switch (variant) {
case 'info':
return 'bg-neon-cyan/20 text-neon-cyan border-neon-cyan/50'
case 'success':
return 'bg-neon-green/20 text-neon-green border-neon-green/50'
case 'warning':
return 'bg-yellow-500/20 text-yellow-400 border-yellow-500/50'
case 'error':
return 'bg-red-500/20 text-red-400 border-red-500/50'
default:
return 'bg-neon-cyan/20 text-neon-cyan border-neon-cyan/50'
}
}
export function Toast({
children,
variant = 'info',
duration = 5000,
onClose,
'aria-label': ariaLabel,
}: ToastProps): React.ReactElement {
useEffect(() => {
if (duration > 0) {
const timer = setTimeout(() => {
onClose()
}, duration)
return () => clearTimeout(timer)
}
return undefined
}, [duration, onClose])
const variantClasses = getVariantClasses(variant)
const baseClasses = 'border rounded-lg p-4 shadow-lg flex items-center justify-between min-w-[300px] max-w-md'
const combinedClasses = `${baseClasses} ${variantClasses}`.trim()
return (
<div
className={combinedClasses}
role="alert"
aria-live="polite"
aria-label={ariaLabel}
>
<div className="flex-1">{children}</div>
<button
onClick={onClose}
className="ml-4 text-current hover:opacity-70 transition-opacity focus:outline-none focus:ring-2 focus:ring-current rounded"
aria-label="Close notification"
>
×
</button>
</div>
)
}