import React, { forwardRef, useId, useState } from 'react'; import { AccordionItem, ChevronDownIcon, Surface, cn, focusRing, surfaceClasses, useEffectiveSurface, } from '../common'; /* ───────────────────────────────────────────────────────────────────────── PixelAccordion — list of expandable items with aria-controls + id wiring. ───────────────────────────────────────────────────────────────────────── */ /** Public prop bag for {@link PixelAccordion}. */ export interface PixelAccordionProps { /** Accordion items rendered in order. First item is expanded by default. */ items: AccordionItem[]; /** When true, multiple items can be expanded simultaneously. */ allowMultiple?: boolean; /** When set, no item is initially expanded. Defaults to expanding the first. */ collapsedByDefault?: boolean; /** Visual surface treatment override. */ surface?: Surface; } export const PixelAccordion = forwardRef(function PixelAccordion( { items, allowMultiple = false, collapsedByDefault = false, surface: surfaceProp }, ref, ) { const surface = useEffectiveSurface(surfaceProp); const s = surfaceClasses(surface); const baseId = useId(); const [openIds, setOpenIds] = useState>( () => new Set(!collapsedByDefault && items[0] ? [items[0].id] : []), ); const toggle = (id: string) => { setOpenIds((prev) => { const next = new Set(prev); if (next.has(id)) next.delete(id); else { if (!allowMultiple) next.clear(); next.add(id); } return next; }); }; return (
{items.map((item) => { const isOpen = openIds.has(item.id); const headerId = `${baseId}-h-${item.id}`; const panelId = `${baseId}-p-${item.id}`; return (
{isOpen && ( // No role="region": the component cannot guarantee unique panel // names across instances, and duplicate region landmarks trip // axe's landmark-unique rule (APG also discourages region here // to avoid landmark proliferation).
{item.content}
)}
); })}
); }); PixelAccordion.displayName = 'PixelAccordion';