{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "core-segmented-control",
  "type": "registry:ui",
  "title": "Segmented Control",
  "description": "One choice out of a few as a pill track of pressed-or-not buttons on Base UI's ToggleGroup and Toggle (roving arrow keys, aria-pressed). Selection never empties; pressing the pressed option reports onReselect. The pressed option has a fill and an edge at least 3:1 against the track. sm/md/lg heights (md 28px by default) through the control-height variables, per-option tooltips and names, and a wrapping layout.",
  "dependencies": [
    "@base-ui/react",
    "maruaudio@>=0.0.3 <0.1.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ui/segmented-control.tsx",
      "content": "/**\n * Source: https://maru.audio/ui-kit/segmented-control\n * Copyright © [Maru](https://maru.audio)\n *\n * Maru UI Kit — https://maru.audio/ui-kit\n *\n * Installed from the Maru UI registry. This file is yours now: edit it\n * freely, it will not be overwritten unless you re-run the add command.\n *\n * Keep this item’s companion files and CSS imports together. Dependencies\n * declared by the registry are installed with the source.\n */\n/**\n * SegmentedControl — one choice out of a few, as a row of pressed-or-not\n * buttons on a pill track: a mode, a grid size, a direction.\n *\n * Built on Base UI's ToggleGroup and Toggle, which own the roving focus (arrow\n * keys move between options, Tab leaves the group), `aria-pressed` on every\n * option and the group's `role=\"group\"`. What this adds is single selection\n * that never empties: pressing the pressed option does not unpress it. That\n * press is still worth hearing about, since \"press the default again\" is how a\n * user writes a default out explicitly, so it goes to `onReselect`.\n *\n * Controlled: `value` is the pressed option, or undefined for none (a value\n * that is not set yet). Options can be disabled one by one, carry a tooltip\n * and an accessible name of their own for an icon-only label.\n *\n * `wrap` lets many options flow onto several lines (a grid-size picker in a\n * narrow palette); without it the options share one track.\n *\n * `size` picks the control height (\"sm\" 24px, \"md\" 28px, the default, \"lg\"\n * 32px), read through the `--uikit-control-h-*` variables so a touch face\n * can raise it. The pressed option has a fill and an edge that stand at least\n * 3:1 against the track, so the choice does not rest on text weight alone.\n *\n * SSR-safe; styled with inline layout, theme tokens and the kit's console\n * stylesheet for hover, focus and pressed states.\n */\n\nimport type { CSSProperties, JSX, ReactNode } from \"react\"\nimport { Toggle } from \"@base-ui/react/toggle\"\nimport { ToggleGroup } from \"@base-ui/react/toggle-group\"\nimport { token } from \"maruaudio/ui/shared/theme\"\nimport {\n  CONSOLE_FONT_SIZE,\n  ConsoleStyles,\n  consoleHeight,\n  type ConsoleSize,\n} from \"maruaudio/ui/shared/console-styles\"\n\nexport interface SegmentedControlOption<Value extends string = string> {\n  readonly value: Value\n  readonly label: ReactNode\n  /** Tooltip. */\n  readonly title?: string\n  readonly disabled?: boolean\n  /** Accessible name, for a label that is an icon or an abbreviation. */\n  readonly \"aria-label\"?: string\n}\n\nexport interface SegmentedControlProps<Value extends string = string> {\n  /** The pressed option; undefined presses none. */\n  readonly value: Value | undefined\n  readonly options: readonly SegmentedControlOption<Value>[]\n  /** Pressing an option other than the pressed one. */\n  readonly onValueChange: (value: Value) => void\n  /** Pressing the already-pressed option (it never unpresses): e.g. write a default explicitly. */\n  readonly onReselect?: (value: Value) => void\n  /** Control height: \"sm\" 24px, \"md\" 28px, \"lg\" 32px, through the `--uikit-control-h-*` variables. Defaults to \"md\". */\n  readonly size?: ConsoleSize\n  /** Let many options wrap onto several lines. Defaults to false: one pill track. */\n  readonly wrap?: boolean\n  readonly disabled?: boolean\n  readonly \"aria-label\"?: string\n  readonly \"aria-labelledby\"?: string\n  readonly className?: string\n  /** Panel uses flat accent-filled choices and the inherited corner radius. */\n  readonly variant?: \"default\" | \"panel\"\n  /** Retain selection/focus behavior while a visual family supplies CSS. */\n  readonly unstyled?: boolean\n  readonly orientation?: \"horizontal\" | \"vertical\"\n  readonly decoration?: ReactNode\n  readonly style?: CSSProperties\n}\n\n/** The track's inner padding, in pixels. */\nconst TRACK_PAD = 2\n\nexport function SegmentedControl<Value extends string = string>({\n  value,\n  options,\n  onValueChange,\n  onReselect,\n  size = \"md\",\n  wrap = false,\n  disabled = false,\n  \"aria-label\": ariaLabel,\n  \"aria-labelledby\": ariaLabelledBy,\n  className,\n  style,\n  unstyled = false,\n  variant = \"default\",\n  orientation = \"horizontal\",\n  decoration,\n}: SegmentedControlProps<Value>): JSX.Element {\n  const height = consoleHeight(size)\n  // Inside the track's padding and its 1px border, top and bottom.\n  const optionHeight = consoleHeight(size, TRACK_PAD * 2 + 2)\n  const pressed: Value[] = value === undefined ? [] : [value]\n\n  return (\n    <ToggleGroup<Value>\n      value={pressed}\n      onValueChange={(next) => {\n        const chosen = next.find((candidate) => candidate !== value)\n        if (chosen !== undefined) onValueChange(chosen)\n      }}\n      disabled={disabled}\n      orientation={orientation}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledBy}\n      data-segmented-control={unstyled ? undefined : \"\"}\n      data-size={size}\n      className={className}\n      style={\n        unstyled\n          ? style\n          : {\n              boxSizing: \"border-box\",\n              display: wrap ? \"flex\" : \"inline-flex\",\n              flexWrap: wrap ? \"wrap\" : \"nowrap\",\n              alignItems: \"center\",\n              gap: 2,\n              maxWidth: \"100%\",\n              minHeight: height,\n              padding: TRACK_PAD,\n              border: `1px solid ${token.border}`,\n              borderRadius: variant === \"panel\" ? token.radius : wrap ? 8 : 999,\n              background: variant === \"panel\" ? token.track : undefined,\n              fontFamily: token.font,\n              fontSize: CONSOLE_FONT_SIZE,\n              lineHeight: 1,\n              opacity: disabled ? 0.55 : 1,\n              ...style,\n            }\n      }\n    >\n      {!unstyled && <ConsoleStyles />}\n      {decoration}\n      {options.map((option) => (\n        <Toggle<Value>\n          key={option.value}\n          value={option.value}\n          aria-description={option.title}\n          disabled={option.disabled}\n          aria-label={option[\"aria-label\"]}\n          data-segmented-option={unstyled ? undefined : option.value}\n          onPressedChange={(nextPressed, details) => {\n            if (nextPressed) return\n            // Single selection never empties: the pressed option stays\n            // pressed, and the press is reported as a reselect instead.\n            details.cancel()\n            onReselect?.(option.value)\n          }}\n          style={\n            unstyled\n              ? undefined\n              : {\n                  boxSizing: \"border-box\",\n                  display: \"inline-flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  gap: 4,\n                  flex: wrap ? \"0 0 auto\" : \"1 1 auto\",\n                  minWidth: optionHeight,\n                  height: optionHeight,\n                  padding: \"0 8px\",\n                  margin: 0,\n                  // The colour comes from the stylesheet: transparent, or the\n                  // pressed edge.\n                  borderWidth: 1,\n                  borderStyle: \"solid\",\n                  borderRadius:\n                    variant === \"panel\" ? token.radius : wrap ? 6 : 999,\n                  ...(variant === \"panel\"\n                    ? {\n                        background:\n                          option.value === value ? token.accent : \"transparent\",\n                        color:\n                          option.value === value\n                            ? token.surface\n                            : token.textMuted,\n                        borderColor: \"transparent\",\n                        boxShadow: \"none\",\n                      }\n                    : {}),\n                  fontFamily: \"inherit\",\n                  fontSize: \"inherit\",\n                  fontWeight: option.value === value ? 600 : 500,\n                  whiteSpace: \"nowrap\",\n                  cursor:\n                    option.disabled || disabled ? \"not-allowed\" : \"pointer\",\n                }\n          }\n        >\n          {option.label}\n        </Toggle>\n      ))}\n    </ToggleGroup>\n  )\n}\n",
      "type": "registry:component",
      "target": "@ui/segmented-control.tsx"
    }
  ]
}
