import { useState, type CSSProperties } from 'react'; import type { BannerItem, BannerKind } from './types.js'; export interface BannerStackProps { banners: BannerItem[]; /** Called when a user dismisses a banner. */ onDismiss?: (id: string) => void; /** Max banners visible. Overflow stays in DOM but collapses behind a "+N more". */ maxVisible?: number; className?: string; style?: CSSProperties; } const TONE: Record = { info: { bg: 'var(--bl-info-muted, rgba(56,189,248,0.12))', fg: 'var(--bl-text-primary, inherit)', accent: 'var(--bl-info, #38bdf8)', }, success: { bg: 'var(--bl-success-muted, rgba(34,197,94,0.12))', fg: 'var(--bl-text-primary, inherit)', accent: 'var(--bl-success, #22c55e)', }, warning: { bg: 'var(--bl-warning-muted, rgba(245,158,11,0.14))', fg: 'var(--bl-text-primary, inherit)', accent: 'var(--bl-warning, #f59e0b)', }, danger: { bg: 'var(--bl-danger-muted, rgba(239,68,68,0.12))', fg: 'var(--bl-text-primary, inherit)', accent: 'var(--bl-danger, #ef4444)', }, announcement: { bg: 'linear-gradient(90deg, var(--bl-accent-muted, rgba(99,102,241,0.18)), transparent)', fg: 'var(--bl-text-primary, inherit)', accent: 'var(--bl-accent, #6366f1)', }, }; /** * `` — top-of-page announcement strip. Renders an * accent-bordered tile for each banner; dismissible by default. */ export function BannerStack({ banners, onDismiss, maxVisible = 3, className, style, }: BannerStackProps) { const [collapsed, setCollapsed] = useState(true); const visible = collapsed ? banners.slice(0, maxVisible) : banners; const hidden = banners.length - visible.length; if (banners.length === 0) return null; return (
{visible.map(b => { const t = TONE[b.kind ?? 'info']; return (
{b.title}
{b.body && (
{b.body}
)}
{b.cta && ( { if (b.cta?.onSelect) { e.preventDefault(); b.cta.onSelect(); } }} style={{ fontSize: '0.78rem', fontWeight: 700, color: t.accent, textDecoration: 'none', padding: '2px 10px', borderRadius: 'var(--bl-radius-pill, 999px)', border: `1px solid ${t.accent}`, flexShrink: 0, whiteSpace: 'nowrap', }} > {b.cta.label} )} {b.dismissible !== false && ( )}
); })} {hidden > 0 && ( )}
); }