Files
metabuilder/components/fakemui/feedback/NotificationContainer.tsx
2026-03-09 22:30:41 +00:00

103 lines
2.6 KiB
TypeScript
Raw Permalink 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.
'use client'
import React, { useEffect, useCallback } from 'react'
import { classNames } from '../utils/classNames'
export type NotificationType = 'success' | 'error' | 'warning' | 'info'
export type NotificationPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center'
export interface NotificationData {
id: string
type: NotificationType
message: string
duration?: number
}
export interface NotificationContainerProps {
notifications: NotificationData[]
onClose: (id: string) => void
position?: NotificationPosition
maxVisible?: number
className?: string
testId?: string
}
const ICONS: Record<NotificationType, string> = {
success: '✓',
error: '✕',
warning: '⚠',
info: '',
}
interface NotificationItemProps {
notification: NotificationData
onClose: (id: string) => void
}
const NotificationItem: React.FC<NotificationItemProps> = ({ notification, onClose }) => {
const { id, type, message, duration = 5000 } = notification
useEffect(() => {
if (duration > 0) {
const timer = setTimeout(() => onClose(id), duration)
return () => clearTimeout(timer)
}
}, [id, duration, onClose])
const rootClass = classNames(
'fakemui-notification',
`fakemui-notification--${type}`
)
return (
<div className={rootClass} role="alert" aria-live="polite">
<span className="fakemui-notification__icon">{ICONS[type]}</span>
<span className="fakemui-notification__message">{message}</span>
<button
className="fakemui-notification__close"
onClick={() => onClose(id)}
aria-label="Close notification"
>
×
</button>
</div>
)
}
export const NotificationContainer: React.FC<NotificationContainerProps> = ({
notifications,
onClose,
position = 'top-right',
maxVisible = 5,
className = '',
testId,
}) => {
const handleClose = useCallback((id: string) => {
onClose(id)
}, [onClose])
const visibleNotifications = notifications.slice(0, maxVisible)
if (visibleNotifications.length === 0) return null
const rootClass = classNames(
'fakemui-notification-container',
`fakemui-notification-container--${position}`,
className
)
return (
<div className={rootClass} data-testid={testId} role="region" aria-label="Notifications" aria-live="polite">
{visibleNotifications.map((notification) => (
<NotificationItem
key={notification.id}
notification={notification}
onClose={handleClose}
/>
))}
</div>
)
}
export default NotificationContainer