{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented-control",
  "type": "registry:ui",
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "segmented-control.tsx",
      "target": "components/ui/segmented-control.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SegmentedControlItem {\n  value: string;\n  label: React.ReactNode;\n  icon?: React.ReactNode;\n}\n\nexport interface SegmentedControlProps\n  extends Omit<\n    React.HTMLAttributes<HTMLDivElement>,\n    \"onChange\" | \"defaultValue\"\n  > {\n  items: SegmentedControlItem[];\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  size?: \"sm\" | \"md\";\n  orientation?: \"horizontal\" | \"vertical\";\n}\n\nconst sizeClasses: Record<\n  NonNullable<SegmentedControlProps[\"size\"]>,\n  string\n> = {\n  sm: \"h-8 px-3\",\n  md: \"h-9 px-4\",\n};\n\nexport const SegmentedControl = React.forwardRef<\n  HTMLDivElement,\n  SegmentedControlProps\n>(\n  (\n    {\n      items,\n      value,\n      defaultValue,\n      onValueChange,\n      size = \"md\",\n      orientation = \"horizontal\",\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const vertical = orientation === \"vertical\";\n    const isControlled = value !== undefined;\n    const [uncontrolled, setUncontrolled] = React.useState(\n      () => defaultValue ?? items[0]?.value,\n    );\n    const current = isControlled ? value : uncontrolled;\n\n    const segmentRefs = React.useRef<Array<HTMLButtonElement | null>>([]);\n    const [indicator, setIndicator] = React.useState({\n      left: 0,\n      top: 0,\n      width: 0,\n      height: 0,\n      ready: false,\n    });\n\n    const activeIndex = Math.max(\n      0,\n      items.findIndex((item) => item.value === current),\n    );\n\n    const measure = React.useCallback(() => {\n      const el = segmentRefs.current[activeIndex];\n      if (!el) return;\n      setIndicator({\n        left: el.offsetLeft,\n        top: el.offsetTop,\n        width: el.offsetWidth,\n        height: el.offsetHeight,\n        ready: true,\n      });\n    }, [activeIndex]);\n\n    React.useLayoutEffect(() => {\n      measure();\n    }, [measure]);\n\n    React.useEffect(() => {\n      if (typeof ResizeObserver === \"undefined\") return;\n      const observer = new ResizeObserver(() => measure());\n      for (const el of segmentRefs.current) {\n        if (el) observer.observe(el);\n      }\n      return () => observer.disconnect();\n    }, [measure]);\n\n    const select = React.useCallback(\n      (next: string) => {\n        if (!isControlled) setUncontrolled(next);\n        onValueChange?.(next);\n      },\n      [isControlled, onValueChange],\n    );\n\n    const focusIndex = (index: number) => {\n      const clamped = (index + items.length) % items.length;\n      const target = items[clamped];\n      if (!target) return;\n      segmentRefs.current[clamped]?.focus();\n      select(target.value);\n    };\n\n    const onKeyDown = (event: React.KeyboardEvent, index: number) => {\n      switch (event.key) {\n        case \"ArrowRight\":\n        case \"ArrowDown\":\n          event.preventDefault();\n          focusIndex(index + 1);\n          break;\n        case \"ArrowLeft\":\n        case \"ArrowUp\":\n          event.preventDefault();\n          focusIndex(index - 1);\n          break;\n        case \"Home\":\n          event.preventDefault();\n          focusIndex(0);\n          break;\n        case \"End\":\n          event.preventDefault();\n          focusIndex(items.length - 1);\n          break;\n        default:\n          break;\n      }\n    };\n\n    return (\n      <div\n        ref={ref}\n        role=\"radiogroup\"\n        aria-orientation={orientation}\n        className={cn(\n          \"relative inline-flex border border-border bg-surface p-1\",\n          vertical\n            ? \"flex-col items-stretch rounded-3xl\"\n            : \"items-center rounded-full\",\n          className,\n        )}\n        {...props}\n      >\n        <span\n          aria-hidden=\"true\"\n          className={cn(\n            \"pointer-events-none absolute bg-surface-2 shadow-raised\",\n            vertical\n              ? \"top-0 right-1 left-1 rounded-2xl\"\n              : \"top-1 bottom-1 left-0 rounded-full\",\n            \"[filter:drop-shadow(var(--usva-glow-accent))]\",\n            \"transition-layout duration-slow ease-spring motion-reduce:transition-none\",\n            !indicator.ready && \"opacity-0\",\n          )}\n          style={\n            vertical\n              ? {\n                  height: indicator.height,\n                  transform: `translateY(${indicator.top}px)`,\n                }\n              : {\n                  width: indicator.width,\n                  transform: `translateX(${indicator.left}px)`,\n                }\n          }\n        />\n        {items.map((item, index) => {\n          const checked = item.value === current;\n          return (\n            // biome-ignore lint/a11y/useSemanticElements: segmented control needs a button with a roving tabindex and a custom indicator; a native radio input can't render this pattern\n            <button\n              key={item.value}\n              type=\"button\"\n              role=\"radio\"\n              aria-checked={checked}\n              tabIndex={checked ? 0 : -1}\n              ref={(node) => {\n                segmentRefs.current[index] = node;\n              }}\n              onClick={() => select(item.value)}\n              onKeyDown={(event) => onKeyDown(event, index)}\n              className={cn(\n                \"relative z-10 inline-flex items-center justify-center gap-1.5 text-sm whitespace-nowrap outline-none\",\n                sizeClasses[size],\n                vertical ? \"w-full rounded-2xl\" : \"rounded-full\",\n                \"text-muted transition-tint duration-fast ease-soft\",\n                \"hover:text-ink aria-checked:text-ink\",\n                \"focus-visible:ring-focus\",\n              )}\n            >\n              {item.icon ? (\n                <span className=\"inline-flex shrink-0\" aria-hidden=\"true\">\n                  {item.icon}\n                </span>\n              ) : null}\n              {item.label}\n            </button>\n          );\n        })}\n      </div>\n    );\n  },\n);\nSegmentedControl.displayName = \"SegmentedControl\";\n"
    }
  ]
}