{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented-control",
  "title": "Segmented Control",
  "author": "AniBT <https://anibt.net>",
  "description": "AniBT segmented track: a sunk well of 24px choices where the chosen one is a white sticker thumb with a pink glyph, with radio semantics and an optional label.",
  "dependencies": [
    "radix-ui"
  ],
  "registryDependencies": [
    "@anibt/theme"
  ],
  "files": [
    {
      "path": "registry/anibt/ui/segmented-control.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\n/**\n * SegmentedControl — a few mutually exclusive choices side by side\n * (AniBT 「贴纸手账」): 编写 / 分栏 / 预览, 列表 / 网格, 全部 / 简中 / 繁中.\n *\n * A's segmented track: the sunk `--muted` well on radius 10 with 4px of\n * padding and 24px items in 12/600 `--anibt-ink2`; the chosen one is a white\n * sticker thumb in ink with its glyph in pink. It is a Radix radio group\n * underneath — one tab stop, arrow keys move the choice, a screen reader\n * hears \"radio, 2 of 3\" — so it carries a form value (`name`) and always has\n * exactly one choice. For buttons that toggle on and off, or several at once,\n * use `ToggleGroup`.\n *\n * `size=\"default\"` keeps a thumb-sized 36px item on a phone and tightens to\n * A's 24px from `sm`; `compact` is 24px everywhere (a toolbar). `iconOnly`\n * hides the words and keeps each as the item's accessible name and title.\n * The paint is the `anibt-seg` recipe spelled out in classes, so a caller's\n * classes merge.\n */\n\ntype SegmentedControlOption<TValue extends string> = {\n  value: TValue\n  label: string\n  icon?: React.ComponentType<{ className?: string; \"aria-hidden\"?: boolean | \"true\" }>\n  disabled?: boolean\n}\n\ntype SegmentedControlProps<TValue extends string> = Omit<\n  React.ComponentProps<typeof RadioGroupPrimitive.Root>,\n  \"value\" | \"defaultValue\" | \"onValueChange\" | \"children\" | \"dir\" | \"orientation\"\n> & {\n  options: ReadonlyArray<SegmentedControlOption<TValue>>\n  value?: TValue\n  defaultValue?: TValue\n  onValueChange?: (value: TValue) => void\n  /** A visible label above the track; it names the group. */\n  label?: React.ReactNode\n  /** Hides the words (each option needs an `icon`). */\n  iconOnly?: boolean\n  size?: \"default\" | \"compact\"\n  /** Stretch the track to its container, the items sharing it equally. */\n  block?: boolean\n}\n\nfunction SegmentedControl<TValue extends string>({\n  options,\n  value,\n  defaultValue,\n  onValueChange,\n  label,\n  iconOnly = false,\n  size = \"default\",\n  block = false,\n  className,\n  \"aria-labelledby\": labelledBy,\n  ...props\n}: SegmentedControlProps<TValue>) {\n  const labelId = React.useId()\n\n  const track = (\n    <RadioGroupPrimitive.Root\n      data-slot=\"segmented-control\"\n      data-size={size}\n      orientation=\"horizontal\"\n      value={value}\n      defaultValue={defaultValue}\n      onValueChange={(next) => onValueChange?.(next as TValue)}\n      aria-labelledby={label ? labelId : labelledBy}\n      className={cn(\n        \"max-w-full items-center gap-0.5 rounded-[10px] bg-muted p-1 shadow-[inset_0_0_0_1px_var(--border)]\",\n        block ? \"grid w-full auto-cols-fr grid-flow-col\" : \"inline-flex w-fit\",\n        !label && className\n      )}\n      {...props}\n    >\n      {options.map((option) => {\n        const Icon = option.icon\n        return (\n          <RadioGroupPrimitive.Item\n            key={option.value}\n            value={option.value}\n            disabled={option.disabled}\n            title={iconOnly ? option.label : undefined}\n            aria-label={iconOnly ? option.label : undefined}\n            data-slot=\"segmented-control-item\"\n            className={cn(\n              \"inline-flex min-w-0 cursor-pointer touch-manipulation items-center justify-center gap-1 rounded-[6px] bg-transparent text-[12px] leading-none font-semibold whitespace-nowrap text-anibt-ink2 outline-hidden transition-[color,background-color,box-shadow] duration-200 ease-anibt-out\",\n              \"hover:text-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground\",\n              \"data-[state=checked]:bg-card data-[state=checked]:text-foreground data-[state=checked]:shadow-(--anibt-shadow-sticker) data-[state=checked]:[&_svg]:text-anibt-mark\",\n              \"focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring focus-visible:outline-solid\",\n              \"disabled:cursor-not-allowed disabled:text-muted-foreground\",\n              size === \"compact\" ? \"h-6\" : \"h-9 sm:h-6\",\n              iconOnly ? (size === \"compact\" ? \"w-6\" : \"w-9 sm:w-6\") : \"px-2.5 sm:px-2\"\n            )}\n          >\n            {Icon ? <Icon aria-hidden=\"true\" /> : null}\n            {iconOnly && Icon ? null : <span className=\"truncate\">{option.label}</span>}\n          </RadioGroupPrimitive.Item>\n        )\n      })}\n    </RadioGroupPrimitive.Root>\n  )\n\n  if (!label) return track\n\n  return (\n    <div data-slot=\"segmented-control-field\" className={cn(\"flex min-w-0 flex-col gap-1.5\", className)}>\n      {/* The label tier (13/600 ink2) on a span: it names the group through\n          `aria-labelledby`; a <label> would point at nothing. */}\n      <span id={labelId} data-slot=\"segmented-control-label\" className=\"text-[13px] leading-5 font-semibold text-anibt-ink2\">\n        {label}\n      </span>\n      {track}\n    </div>\n  )\n}\n\nexport { SegmentedControl }\nexport type { SegmentedControlOption, SegmentedControlProps }\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}