Files
Charon/frontend/src/components/ui/Alert.tsx
GitHub Actions 8f2f18edf7 feat: implement modern UI/UX design system (#409)
- Add comprehensive design token system (colors, typography, spacing)
- Create 12 new UI components with Radix UI primitives
- Add layout components (PageShell, StatsCard, EmptyState, DataTable)
- Polish all pages with new component library
- Improve accessibility with WCAG 2.1 compliance
- Add dark mode support with semantic color tokens
- Update 947 tests to match new UI patterns

Closes #409
2025-12-16 21:21:39 +00:00

126 lines
3.1 KiB
TypeScript

import * as React from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '../../utils/cn'
import {
Info,
CheckCircle,
AlertTriangle,
XCircle,
X,
type LucideIcon,
} from 'lucide-react'
const alertVariants = cva(
'relative flex gap-3 p-4 rounded-lg border transition-all duration-normal',
{
variants: {
variant: {
default: 'bg-surface-subtle border-border text-content-primary',
info: 'bg-info-muted border-info/30 text-content-primary',
success: 'bg-success-muted border-success/30 text-content-primary',
warning: 'bg-warning-muted border-warning/30 text-content-primary',
error: 'bg-error-muted border-error/30 text-content-primary',
},
},
defaultVariants: {
variant: 'default',
},
}
)
const iconMap: Record<string, LucideIcon> = {
default: Info,
info: Info,
success: CheckCircle,
warning: AlertTriangle,
error: XCircle,
}
const iconColorMap: Record<string, string> = {
default: 'text-content-muted',
info: 'text-info',
success: 'text-success',
warning: 'text-warning',
error: 'text-error',
}
export interface AlertProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof alertVariants> {
title?: string
icon?: LucideIcon
dismissible?: boolean
onDismiss?: () => void
}
export function Alert({
className,
variant = 'default',
title,
icon,
dismissible = false,
onDismiss,
children,
...props
}: AlertProps) {
const [isVisible, setIsVisible] = React.useState(true)
if (!isVisible) return null
const IconComponent = icon || iconMap[variant || 'default']
const iconColor = iconColorMap[variant || 'default']
const handleDismiss = () => {
setIsVisible(false)
onDismiss?.()
}
return (
<div
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
>
<IconComponent className={cn('h-5 w-5 flex-shrink-0 mt-0.5', iconColor)} />
<div className="flex-1 min-w-0">
{title && (
<h5 className="font-semibold text-sm mb-1">{title}</h5>
)}
<div className="text-sm text-content-secondary">{children}</div>
</div>
{dismissible && (
<button
type="button"
onClick={handleDismiss}
className="flex-shrink-0 p-1 rounded-md text-content-muted hover:text-content-primary hover:bg-surface-muted transition-colors duration-fast"
aria-label="Dismiss alert"
>
<X className="h-4 w-4" />
</button>
)}
</div>
)
}
export type AlertTitleProps = React.HTMLAttributes<HTMLHeadingElement>
export function AlertTitle({ className, ...props }: AlertTitleProps) {
return (
<h5
className={cn('font-semibold text-sm mb-1', className)}
{...props}
/>
)
}
export type AlertDescriptionProps = React.HTMLAttributes<HTMLParagraphElement>
export function AlertDescription({ className, ...props }: AlertDescriptionProps) {
return (
<p
className={cn('text-sm text-content-secondary', className)}
{...props}
/>
)
}