{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented",
  "title": "Segmented",
  "description": "A single-choice segmented control whose thumb glides to the selection on a spring. Base UI RadioGroup behaviour, concentric thumb, labels that never change weight.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "https://eased.dev/r/eased-tokens.json",
    "https://eased.dev/r/eased-indicator.json"
  ],
  "files": [
    {
      "path": "src/registry/eased/segmented/segmented.tsx",
      "content": "\"use client\";\n\nimport { Radio } from \"@base-ui/react/radio\";\nimport { RadioGroup } from \"@base-ui/react/radio-group\";\nimport type { ReactNode } from \"react\";\nimport { Indicator, IndicatorGroup } from \"../_primitives/indicator/indicator\";\n\nexport type SegmentedOption<V extends string> = {\n  value: V;\n  /** The visible label. For an icon-only segment pass `icon` and an `aria-label` instead. */\n  label?: ReactNode;\n  /** A 16px icon before the label. */\n  icon?: ReactNode;\n  /** Required when there is no text label. */\n  \"aria-label\"?: string;\n  disabled?: boolean;\n};\n\nexport type SegmentedProps<V extends string> = {\n  options: readonly SegmentedOption<V>[];\n  /** The selected value (controlled). */\n  value?: V;\n  /** The initially selected value (uncontrolled). Defaults to the first option. */\n  defaultValue?: V;\n  onValueChange?: (value: V) => void;\n  /** `sm` is 32px (dense UI, toolbars, demo headers); `md` is 36px (forms, settings). Default `sm`. */\n  size?: \"sm\" | \"md\";\n  /** Stretch to the container and share the width equally between segments. */\n  fullWidth?: boolean;\n  /** Submits the value with a form under this name. */\n  name?: string;\n  disabled?: boolean;\n  /** Accessible name, e.g. \"Date range\". Or point `aria-labelledby` at a visible label. */\n  \"aria-label\"?: string;\n  \"aria-labelledby\"?: string;\n  className?: string;\n};\n\nconst SIZES = {\n  sm: {\n    track: \"h-control-sm rounded-control-sm [--radius-outer:var(--eased-radius-control-sm)]\",\n    item: \"gap-1.5 px-3 text-ui-sm\",\n    icon: \"px-2\",\n  },\n  md: {\n    track: \"h-control-md rounded-control [--radius-outer:var(--eased-radius-control)]\",\n    item: \"gap-2 px-3.5 text-ui\",\n    icon: \"px-2.5\",\n  },\n} as const;\n\n/**\n * A single-choice control whose thumb glides to the selected segment on the snappy spring.\n * Base UI's RadioGroup supplies the behaviour: one tab stop, arrow keys move selection,\n * Home/End, form submission. Concentric by construction: the thumb's radius is the track's\n * minus its padding and border (`radius-nested`). Labels never change weight; the selected\n * one only darkens, and its cursor goes back to default because clicking it does nothing.\n * Segments carry no extended hit area: they sit edge to edge, and the track is the target.\n */\nexport function Segmented<V extends string>({\n  options,\n  value,\n  defaultValue,\n  onValueChange,\n  size = \"sm\",\n  fullWidth = false,\n  name,\n  disabled,\n  className = \"\",\n  ...aria\n}: SegmentedProps<V>) {\n  const s = SIZES[size];\n  return (\n    <RadioGroup<V>\n      value={value}\n      defaultValue={value === undefined ? (defaultValue ?? options[0]?.value) : undefined}\n      onValueChange={(next) => onValueChange?.(next)}\n      name={name}\n      disabled={disabled}\n      {...aria}\n      className={`${fullWidth ? \"flex w-full\" : \"inline-flex\"} shrink-0 items-stretch border border-line-subtle bg-surface-sunken p-0.5 [--border-w:1px] [--pad:2px] ${s.track} ${className}`}\n    >\n      <IndicatorGroup>\n        {options.map((option) => (\n          <Radio.Root\n            key={option.value}\n            value={option.value}\n            disabled={option.disabled}\n            aria-label={option[\"aria-label\"]}\n            nativeButton\n            render={(props, state) => (\n              <button type=\"button\" {...props}>\n                {state.checked && (\n                  <Indicator className=\"inset-0 radius-nested bg-surface-raised shadow-thumb\" />\n                )}\n                {option.icon && <span className=\"relative flex size-4 shrink-0 items-center justify-center [&_svg]:size-4\">{option.icon}</span>}\n                {option.label !== undefined && <span className=\"relative truncate\">{option.label}</span>}\n              </button>\n            )}\n            className={(state) =>\n              `relative flex min-w-0 items-center justify-center font-medium whitespace-nowrap radius-nested select-none transition-colors duration-150 ease-standard focus-ring ${\n                option.label === undefined ? s.icon : s.item\n              } ${fullWidth ? \"flex-1\" : \"\"} ${\n                state.disabled\n                  ? \"cursor-not-allowed text-text-disabled\"\n                  : state.checked\n                    ? \"cursor-default text-text\"\n                    : \"cursor-pointer text-text-muted hover-fine:text-text-secondary\"\n              }`\n            }\n          />\n        ))}\n      </IndicatorGroup>\n    </RadioGroup>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/eased/segmented/segmented.tsx"
    }
  ],
  "docs": "Import Segmented from components/eased/segmented/segmented. The Eased tokens were merged into your global CSS; nothing to import. Dark mode follows .dark or data-theme=\"dark\".",
  "type": "registry:ui"
}