import { selectTabIndex, useRovingFocus, useSinglePicker, } from "@charcuterie/logic" import type { ControlSize } from "@charcuterie/tokens" import type { ReactNode } from "react" import { useCallback, useEffect, useRef } from "react" import { toClassName } from "../toClassName.ts" import { SegmentedOption } from "./SegmentedOption.tsx" export type SegmentedItem = { /** * A disabled option is simply **not registered** with the roving * group, so the arrow keys skip it without any command in * `RovingFocus` having to know what "disabled" means. It stays * registered with the picker: it is still one of the options. */ isDisabled?: boolean label: ReactNode value: string } export type SegmentedControlProps = { className?: string /** * Fill the available inline space, with every option claiming an * equal share where label minimum widths allow. Options wrap * onto another row before squeezing rich labels below their * minimum content width. A board's Narrow View selector needs * this: its options name the only lane on screen, so a content-width strip * leaves the rest of the board looking disconnected. * * The default stays content-width. Most segmented controls choose * a compact view or density, where stretching three short words * across a whole toolbar would weaken their grouping. */ isFullWidth?: boolean items: readonly SegmentedItem[] /** The group's accessible name. Required. */ label: string onChange?: (selectedValue: string | null) => void /** **Initial** only. Charcuterie owns it from then on. */ selectedValue?: string size?: ControlSize } const GAP_CLASS: Record = { sm: "gap-0.5", md: "gap-1", lg: "gap-1", } /** * One choice out of a handful, all of them on screen at once. * * This is `Tabs`' composition — `SinglePicker` + `RovingFocus` — * with the panels taken away, and it exists because taking the * panels away used to be impossible. Under M4's model a tab bar's * selection *was* a group of visibilities, so a control that chooses * something without revealing anything had nothing to reuse and * hand-rolled its own state. rip-deck's `ColumnPicker` is that * hand-roll * ([decision](../../../docs/decisions/2026-07-30-tab-selection-is-a-single-picker.md)). * * ### `radiogroup`, not a row of pressed buttons * * rip-deck's version is five `