import * as React from 'react'; import { Button } from '@/components/button'; import { cn } from '@/lib/utils'; /** * A segmented filter: one value out of a handful, as a group of toggle buttons. * * **It is deliberately not the kit's `Tabs`, and that is a correction.** Phase 3 * of the showcase rebuild put both of these controls — the icons page's * family/size filters and the toolbar's language switch — on `TabsList` / * `TabsTrigger`, for the roving focus and arrow keys a hand-rolled * `aria-pressed` group did not have. The `?a11y=on` panel then reported what * that traded away: Radix gives every `TabsTrigger` an `aria-controls` pointing * at the panel for its value, and neither control has panels, so every trigger * claimed to control an element that does not exist — `aria-valid-attr-value`, * serious. Announcing a panel that is not there is worse than one tab stop per * option. * * A tablist is the right primitive when the strip *is* the page's navigation * between panels — `/buttons` and `/parity` genuinely are that, and they stay on * `Tabs`. A filter that narrows a grid is a toggle-button group, which is what * this is: `role="group"` with a name, and `aria-pressed` on each button. * * Still a kit component per the rule in `playground.tsx` — the buttons are the * kit's `Button`; only the track is a plain element, because a filled rail * behind a group of controls is layout, not a component. */ export function Segmented({ values, value, onChange, label, renderValue, itemProps, className, }: { values: readonly T[]; value: string; onChange: (next: T) => void; /** Accessible name of the group. Required: a bare row of toggles names nothing. */ label: string; renderValue?: (value: T) => React.ReactNode; /** Extra attributes per button — `lang` on the language switch, for one. */ itemProps?: (value: T) => Omit, 'onClick' | 'aria-pressed'>; className?: string; }) { return ( /* The measurements are `TabsList variant="default"`'s, kept so the control looks exactly as it did before the markup underneath it changed. */
{values.map((option) => { const selected = option === value; return ( ); })}
); }