{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented-control",
  "title": "Segmented Control",
  "description": "Tabs with a springy pill that slides behind the active option, with arrow-key navigation.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/new-york/segmented-control/segmented-control.tsx",
      "content": "\"use client\"\n\nimport { useId, useRef, useState } from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface SegmentedControlOption {\n  value: string\n  label: string\n  icon?: React.ReactNode\n}\n\nexport interface SegmentedControlProps {\n  /** Options to choose from, in order. */\n  options: SegmentedControlOption[]\n  /** Controlled selected value. */\n  value?: string\n  /** Initially selected value when uncontrolled. Default: first option */\n  defaultValue?: string\n  /** Called with the new value when the selection changes. */\n  onValueChange?: (value: string) => void\n  /** Control size. Default: \"md\" */\n  size?: \"sm\" | \"md\" | \"lg\"\n  /** Pill color (any CSS color). Default: the theme surface color */\n  pillColor?: string\n  /** Spring stiffness of the sliding pill. Default: 400 */\n  stiffness?: number\n  /** Spring damping of the sliding pill. Default: 32 */\n  damping?: number\n  /** Accessible label for the group. */\n  \"aria-label\"?: string\n  className?: string\n}\n\nconst sizes = {\n  sm: \"h-8 px-3 text-xs gap-1.5\",\n  md: \"h-10 px-4 text-sm gap-2\",\n  lg: \"h-12 px-5 text-base gap-2\",\n}\n\nexport function SegmentedControl({\n  options,\n  value,\n  defaultValue,\n  onValueChange,\n  size = \"md\",\n  pillColor,\n  stiffness = 400,\n  damping = 32,\n  \"aria-label\": ariaLabel,\n  className,\n}: SegmentedControlProps) {\n  const reduced = useReducedMotion()\n  const id = useId()\n  const [internal, setInternal] = useState(defaultValue ?? options[0]?.value)\n  const selected = value ?? internal\n  const refs = useRef<(HTMLButtonElement | null)[]>([])\n\n  const select = (next: string) => {\n    if (value === undefined) setInternal(next)\n    if (next !== selected) onValueChange?.(next)\n  }\n\n  const onKeyDown = (e: React.KeyboardEvent, index: number) => {\n    const delta = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 }[e.key]\n    let next: number | undefined\n    if (delta) next = (index + delta + options.length) % options.length\n    else if (e.key === \"Home\") next = 0\n    else if (e.key === \"End\") next = options.length - 1\n    if (next === undefined) return\n    e.preventDefault()\n    select(options[next].value)\n    refs.current[next]?.focus()\n  }\n\n  const activeIndex = options.findIndex((o) => o.value === selected)\n\n  return (\n    <div\n      role=\"radiogroup\"\n      aria-label={ariaLabel}\n      className={cn(\"relative inline-flex items-center rounded-full border bg-muted p-1\", className)}\n    >\n      {options.map((option, i) => {\n        const active = option.value === selected\n        return (\n          <button\n            key={option.value}\n            ref={(el) => {\n              refs.current[i] = el\n            }}\n            type=\"button\"\n            role=\"radio\"\n            aria-checked={active}\n            tabIndex={active || (activeIndex === -1 && i === 0) ? 0 : -1}\n            onClick={() => select(option.value)}\n            onKeyDown={(e) => onKeyDown(e, i)}\n            className={cn(\n              \"relative inline-flex items-center justify-center rounded-full font-medium outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n              sizes[size],\n              active ? \"text-foreground\" : \"text-muted-foreground hover:text-foreground\"\n            )}\n          >\n            {active && (\n              <motion.span\n                layoutId={`${id}-pill`}\n                aria-hidden=\"true\"\n                className=\"absolute inset-0 rounded-full bg-background shadow-sm ring-1 ring-border dark:bg-accent\"\n                style={pillColor ? { background: pillColor } : undefined}\n                transition={reduced ? { duration: 0 } : { type: \"spring\", stiffness, damping }}\n              />\n            )}\n            {option.icon && <span className=\"relative flex [&_svg]:size-[1.1em]\">{option.icon}</span>}\n            <span className=\"relative\">{option.label}</span>\n          </button>\n        )\n      })}\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Docs, live playground and every prop: https://trytweenly.vercel.app/docs/components/segmented-control",
  "type": "registry:component"
}