56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
import * as React from 'react';
|
|
import { AlertCircle, CheckCircle2, Info, TriangleAlert } from 'lucide-react';
|
|
import { clsx } from 'clsx';
|
|
|
|
export type AlertBannerTone = 'info' | 'success' | 'warning' | 'error' | 'neutral';
|
|
|
|
export interface AlertBannerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
tone?: AlertBannerTone;
|
|
title?: React.ReactNode;
|
|
icon?: React.ReactNode;
|
|
}
|
|
|
|
const toneClass: Record<AlertBannerTone, string> = {
|
|
info: 'border-[var(--bl-info-border,var(--bl-border))] bg-[var(--bl-info-muted,var(--bl-surface-muted))] text-[var(--bl-info,var(--bl-accent))]',
|
|
success:
|
|
'border-[var(--bl-success-border,var(--bl-border))] bg-[var(--bl-success-muted,var(--bl-surface-muted))] text-[var(--bl-success)]',
|
|
warning:
|
|
'border-[var(--bl-warning-border,var(--bl-border))] bg-[var(--bl-warning-muted,var(--bl-surface-muted))] text-[var(--bl-warning)]',
|
|
error:
|
|
'border-[var(--bl-danger-border,var(--bl-border))] bg-[var(--bl-danger-muted,var(--bl-surface-muted))] text-[var(--bl-danger)]',
|
|
neutral: 'border-[var(--bl-border)] bg-[var(--bl-surface-muted)] text-[var(--bl-text-secondary)]',
|
|
};
|
|
|
|
const iconByTone: Record<AlertBannerTone, React.ReactNode> = {
|
|
info: <Info className="h-4 w-4" aria-hidden="true" />,
|
|
success: <CheckCircle2 className="h-4 w-4" aria-hidden="true" />,
|
|
warning: <TriangleAlert className="h-4 w-4" aria-hidden="true" />,
|
|
error: <AlertCircle className="h-4 w-4" aria-hidden="true" />,
|
|
neutral: <Info className="h-4 w-4" aria-hidden="true" />,
|
|
};
|
|
|
|
export function AlertBanner({
|
|
tone = 'info',
|
|
title,
|
|
icon,
|
|
className,
|
|
children,
|
|
...props
|
|
}: AlertBannerProps) {
|
|
return (
|
|
<div
|
|
className={clsx('flex gap-3 rounded-xl border px-4 py-3 text-sm', toneClass[tone], className)}
|
|
role={tone === 'error' || tone === 'warning' ? 'alert' : 'status'}
|
|
{...props}
|
|
>
|
|
<div className="mt-0.5 shrink-0">{icon ?? iconByTone[tone]}</div>
|
|
<div className="min-w-0">
|
|
{title && (
|
|
<div className="font-semibold leading-5 text-[var(--bl-text-primary)]">{title}</div>
|
|
)}
|
|
<div className="leading-6 text-[var(--bl-text-secondary)]">{children}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|