import type { CSSProperties, ReactNode } from "react"; import { ActionIcon, Menu } from "@mantine/core"; import CloseIcon from "@mui/icons-material/Close"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import type { IconBadgeAccent } from "@shared/components/IconBadge"; import "@shared/components/PanelHeader.css"; export interface PanelHeaderMenuItem { /** Stable key; falls back to the item index. */ key?: string; /** Optional leading glyph. */ icon?: ReactNode; label: ReactNode; onClick: () => void; disabled?: boolean; } export interface PanelHeaderProps { /** Glyph rendered in the tinted circular badge at the header's leading edge. */ icon: ReactNode; /** Header title. */ title: ReactNode; /** Close (X) handler. The trailing close button renders only when supplied. */ onClose?: () => void; /** aria-label for the close button. */ closeLabel?: string; /** * When provided, the header becomes a dropdown trigger: a disclosure chevron is * shown and clicking it opens a menu of these items (e.g. "Clear chat"). */ menuItems?: PanelHeaderMenuItem[]; /** aria-label for the header when it acts as a menu trigger. */ menuLabel?: string; /** * Tints the icon badge with a category colour (blue/purple/green/amber/red). * Defaults to the standard blue when omitted (tool + AI chat headers). */ accent?: IconBadgeAccent; /** Shows a pulsing status dot on the icon + a tinted border (e.g. AI running). */ loading?: boolean; /** Right-aligned content rendered inside the header bar, after the title * (e.g. a status badge). */ actions?: ReactNode; /** Applied to the inner header element — e.g. to set a view-transition-name. */ barClassName?: string; /** Applied to the outer header container. */ className?: string; } /** * The header shared by the rail surfaces — the active tool panel, the AI chat * panel, and the Policies detail/wizard. A tinted icon badge + title sit in a * rounded bar, with an optional dropdown menu and a trailing close button. The * styling stays legible in dark mode (thin border, no heavy fill) across every * surface. */ export function PanelHeader({ icon, title, onClose, closeLabel, menuItems, menuLabel, accent, loading = false, actions, barClassName, className, }: PanelHeaderProps) { const hasMenu = menuItems != null && menuItems.length > 0; // Tint the icon badge with the category colour when an accent is given. Inline // so it wins over the default blue treatment in both light and dark mode; the // --color-* tokens are theme-aware and match the badge tint used elsewhere. const iconStyle: CSSProperties | undefined = accent ? { color: `var(--color-${accent})`, background: `color-mix(in srgb, var(--color-${accent}) 14%, transparent)`, } : undefined; const barClasses = [ "sui-panelhdr__bar", loading ? "sui-panelhdr__bar--loading" : "", barClassName ?? "", ] .filter(Boolean) .join(" "); const barBody = ( <> {icon} {loading && } {title} {actions != null && ( {actions} )} {hasMenu && ( )} ); return (
{hasMenu ? ( {(menuItems ?? []).map((item, i) => ( {item.label} ))} ) : (
{barBody}
)} {onClose && ( )}
); }