import "./choice_strip.css"; import type * as React from "react"; import { type RadioGroupProps } from "@base-ui/react/radio-group"; import { type IconName } from "./icon"; import { type ColorName } from "./colors"; import { type StyleProps } from "./style_props"; import { type InkColor } from "./text_ink"; export interface ChoiceStripOption { label: string; value: T; /** * A mark BEFORE the label, for a set whose members differ in KIND rather than * in degree — payment methods, channels, document types. * * It earns its place when the distinction it draws is one the reader acts on * faster than they read: a glyph plus `iconColor` separates "the money is * here" from "it is not" before the words are parsed. It is decoration on a * set whose labels already differ plainly (Low / Medium / High), and there it * costs width every cell pays for nothing. * * Never icon-ONLY: the label stays, because a cell that is a bare glyph is a * control whose value cannot be read aloud or guessed. */ icon?: IconName; /** The icon's ink ROLE. Defaults to the label's own colour, which is what a * mark that only says "which one" should take; give it a role when the icon * carries a MEANING the label does not (settled vs pending, ok vs blocked). */ iconColor?: InkColor; /** * How many rows this option leads to, as a PROP — never formatted into * `label`. A number inside the label is a second copy that goes stale the * moment the set behind it changes, and it cannot be styled apart from the * word it follows. A count is also a reason to PRESS: an option that states * how many rows sit behind it and does not filter to them states a fact it * refuses to act on. */ count?: number; /** * A status dot BEFORE the label, in the same vocabulary as `Status * variant="dot"` — so a filter and the rows it filters to cannot disagree * about what colour a state is. STATUS only: a category, a type or a place is * not a status and takes no dot. Mutually exclusive with `icon`, which marks a * difference in KIND rather than in state. */ status?: ColorName; testID?: string; } interface ChoiceStripBase extends StyleProps { /** Accessible name of the group (it renders no visible label of its own). */ accessibilityLabel: string; /** The mutually-exclusive peers. Above ~4, `segmented` cells stop fitting. */ options: ChoiceStripOption[]; /** The chosen option. As a FILTER, model the unfiltered state as an explicit * option (e.g. "All"); as a FORM selector, an empty value is valid. */ value: T; onValueChange: (value: T) => void; /** Disables the whole control (e.g. while a mode-specific action runs). */ disabled?: boolean; testID?: string; ref?: React.Ref; render?: RadioGroupProps["render"]; } /** 2–4 peer cells in one recessed track — a PARAMETER of the view on screen. */ interface ChoiceStripSegmentedProps extends ChoiceStripBase { variant?: "segmented"; overflow?: never; } /** Free-standing pills, so the set may be longer and the labels may carry counts. */ interface ChoiceStripChipsProps extends ChoiceStripBase { variant: "chips"; /** * What the row does when it runs out of width. * * `"wrap"` (the default) keeps EVERY option on screen, which is the whole * reason a form field or a composer reaches for chips instead of a `Select` — * a required single-select that hides half its values behind a swipe is a * worse `Select`, not a better one. * * `"scroll"` is for the other job: a HOT FILTER above a register, where the * set is long enough and the labels carry counts. Six status options wrap to * THREE ROWS at 390px — 136px, more than the search band and the view switcher * combined, and the largest single block above the first record. One row costs * 40px; the leading options stay a tap away and the tail is a swipe. * * Not derived from screen width, because width is not the question — the JOB * is, and both jobs occur at every width. */ overflow?: "wrap" | "scroll"; } export type ChoiceStripProps = ChoiceStripSegmentedProps | ChoiceStripChipsProps; /** * ONE of N with every option on screen. Semantically a RADIOGROUP — one choice * among peers, all visible, and never none — which is Base UI's Radio Group, so * the roving tabindex, `aria-checked`, the group's disabled state and the * WAI-ARIA radio keyboard model (arrows move focus AND select, wrapping) are all * its. `ToggleStrip` is the same strip when the cells are INDEPENDENT booleans. * * `segmented` (the default) is the recessed track with the chosen cell raised as * a white card: a binary/few-way *mode* switch where both options should be * one-tap and on screen (a generator mode, a list/grid view) — it changes a * PARAMETER of one view. `chips` is the same choice as free-standing pills, for * a hot filter above a register or a short required select in a form, where the * set is longer than a track holds and an option may carry a count, a status dot * or a kind glyph. Something that swaps the VIEW itself is `Tabs`; past what * fits on a phone row, `Select`. */ export declare function ChoiceStrip({ accessibilityLabel, options, value, onValueChange, variant, overflow, disabled, testID, render, ref, ...props }: ChoiceStripProps): React.JSX.Element; export {};