import { type KeyboardEvent, type MouseEvent, type ReactNode } from 'react' import clsx from 'clsx' import { IconChevronDownOutline14 } from './icons/index.tsx' import css from './DisclosureRow.module.css' /** Shared 24px disclosure chrome for compact flow rows. */ export interface DisclosureRowProps { icon: ReactNode title: string open: boolean expandable: boolean onToggle: () => void /** Makes the complete title row the disclosure target. */ expandOnRowClick?: boolean | undefined /** Replaces the collapsed icon with a chevron while the row is hovered. */ previewChevron?: boolean | undefined /** Keeps `collapsedContent` inline while open. */ keepContentWhenOpen?: boolean | undefined collapsedContent?: ReactNode children?: ReactNode className?: string | undefined rowClassName?: string | undefined leadingClassName?: string | undefined chevronClassName?: string | undefined titleClassName?: string | undefined } /** * Render one disclosure header and its controlled expanded content. * @param props - Visual content, controlled state, and interaction policy. * @returns the disclosure row. */ export function DisclosureRow({ icon, title, open, expandable, onToggle, expandOnRowClick = false, previewChevron = expandable, keepContentWhenOpen = false, collapsedContent, children, className, rowClassName, leadingClassName, chevronClassName, titleClassName, }: DisclosureRowProps) { const rowExpands = expandable && expandOnRowClick const toggleFromLeading = (event: MouseEvent) => { event.stopPropagation() onToggle() } const toggleFromKeyboard = (event: KeyboardEvent) => { if (!rowExpands || (event.key !== 'Enter' && event.key !== ' ')) return event.preventDefault() onToggle() } const collapsedLeading = previewChevron ? ( <> {icon} ) : icon const leading = open ? : collapsedLeading return (
{expandable && !rowExpands ? ( ) : ( {leading} )} {title} {(keepContentWhenOpen || !open) && collapsedContent}
{open && children}
) }