import type { ReactNode } from "react"; import { useTranslation } from "react-i18next"; import type { LucideIcon } from "lucide-react"; import { X } from "lucide-react"; import { Button } from "./Button"; type BannerTone = "accent" | "warning"; const TONES: Record = { accent: { bar: "bg-accent/15 border-accent/30", icon: "text-accent", action: "btn-primary", }, warning: { bar: "bg-warn/15 border-warn/30", icon: "text-warn", action: "btn-warn", }, }; // Shared top-of-app notification bar. VersionBanner and DemoBanner are thin wrappers over this; // a future news-ticker variant can build on the same shell (rotating content as children). export default function Banner({ tone, icon: Icon, children, action, onDismiss, dismissTitle, role, }: { tone: BannerTone; icon: LucideIcon; children: ReactNode; action?: { label: string; onClick: () => void }; onDismiss?: () => void; /** Accessible name (and tooltip) for the dismiss button. Optional at the call site; the component * falls back to the generic "Close" rather than shipping an unnamed icon button — a nameless * control is not an acceptable default, and `Button`'s type now says so. */ dismissTitle?: string; /** `"status"` for a bar that APPEARS in response to something the user just did, so a screen * reader announces it. Left unset for the permanent bars (demo, version): those are part of the * page when it loads, and announcing them on every visit is noise, not information. */ role?: "status"; }) { const { t } = useTranslation(); const c = TONES[tone]; return (
{children} {action && ( )} {onDismiss && (
); }