{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented-control",
  "title": "Segmented Control",
  "description": "One pick among a few, marked by a pill that slides to it.",
  "dependencies": [
    "@base-ui/react",
    "@stylexjs/stylex"
  ],
  "registryDependencies": [
    "https://constella.aayush.cv/r/base.json"
  ],
  "files": [
    {
      "path": "src/components/primitives/segmented-control.tsx",
      "content": "\"use client\";\n\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\";\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { useLayoutEffect, useRef, useState } from \"react\";\n\nimport type { Sx } from \"@/lib/constella/styles\";\nimport { typeStyles } from \"@/lib/constella/styles\";\nimport {\n  colors,\n  durations,\n  easings,\n  fontWeights,\n  media,\n  opacities,\n  radii,\n  sizes,\n  space,\n} from \"@/lib/constella/tokens.stylex\";\n\ntype Styled<T> = Omit<T, \"className\" | \"style\"> & {\n  sx?: Sx;\n};\n\ntype SegmentedControlProps<Value extends string> = Styled<\n  Omit<\n    ToggleGroupPrimitive.Props<Value>,\n    \"multiple\" | \"onValueChange\" | \"value\"\n  >\n> & {\n  value: Value;\n  onValueChange: (value: Value) => void;\n};\n\ninterface Place {\n  x: number;\n  width: number;\n}\n\nconst styles = stylex.create({\n  root: {\n    display: \"flex\",\n    position: \"relative\",\n    width: \"fit-content\",\n  },\n  // Reads like a tab: the pill behind it, not its own fill, marks the pick.\n  item: {\n    alignItems: \"center\",\n    backgroundColor: {\n      default: \"transparent\",\n      \":is([data-pressed]):not([data-placed] *)\": colors.fill,\n    },\n    borderStartStartRadius: radii.full,\n    borderStartEndRadius: radii.full,\n    borderEndStartRadius: radii.full,\n    borderEndEndRadius: radii.full,\n    borderBlockEndWidth: 0,\n    borderBlockStartWidth: 0,\n    borderInlineEndWidth: 0,\n    borderInlineStartWidth: 0,\n    color: {\n      default: colors.textSecondary,\n      \":is([data-pressed])\": colors.textPrimary,\n      [media.hover]: {\n        default: colors.textSecondary,\n        \":hover:not(:disabled)\": colors.textPrimary,\n        \":is([data-pressed])\": colors.textPrimary,\n      },\n    },\n    cursor: { default: \"pointer\", \":disabled\": \"not-allowed\" },\n    display: \"inline-flex\",\n    fontWeight: fontWeights.semimedium,\n    height: sizes.controlSm,\n    opacity: { default: 1, \":disabled\": opacities.disabled },\n    paddingInline: space.sm,\n    position: \"relative\",\n    transitionDuration: durations.hover,\n    transitionProperty: \"color\",\n    transitionTimingFunction: \"ease\",\n    userSelect: \"none\",\n    whiteSpace: \"nowrap\",\n    zIndex: 1,\n  },\n  indicator: {\n    backgroundColor: colors.fill,\n    borderStartStartRadius: radii.full,\n    borderStartEndRadius: radii.full,\n    borderEndStartRadius: radii.full,\n    borderEndEndRadius: radii.full,\n    height: sizes.controlSm,\n    left: 0,\n    position: \"absolute\",\n    top: 0,\n    // Arrow keys move focus visibly and a click doesn't, so only the pointer slides it.\n    transitionDuration: {\n      default: durations.move,\n      \":is([data-slot='segmented-control']:has(:focus-visible) > *)\": \"0s\",\n      [media.reducedMotion]: \"0s\",\n    },\n    transitionProperty: \"transform, width\",\n    transitionTimingFunction: easings.inOut,\n  },\n  place: (x: number, width: number) => ({\n    transform: `translateX(${x}px)`,\n    width,\n  }),\n});\n\n/** One pick among a few, marked by a pill that slides to it, as a tab's does. */\nfunction SegmentedControl<Value extends string>({\n  children,\n  onValueChange,\n  sx,\n  value,\n  ...props\n}: SegmentedControlProps<Value>) {\n  const root = useRef<HTMLDivElement>(null);\n  const [place, setPlace] = useState<Place | null>(null);\n\n  useLayoutEffect(() => {\n    const element = root.current;\n    const measure = () => {\n      const pressed = element?.querySelector<HTMLElement>(\"[data-pressed]\");\n      setPlace(\n        pressed === null || pressed === undefined\n          ? null\n          : { x: pressed.offsetLeft, width: pressed.offsetWidth }\n      );\n    };\n    measure();\n    // A new pick moves the pill; webfonts and container changes move the items.\n    const picks = new MutationObserver(measure);\n    const resizes = new ResizeObserver(measure);\n    if (element !== null) {\n      picks.observe(element, {\n        attributeFilter: [\"data-pressed\"],\n        subtree: true,\n      });\n      resizes.observe(element);\n    }\n    return () => {\n      picks.disconnect();\n      resizes.disconnect();\n    };\n  }, []);\n\n  return (\n    <ToggleGroupPrimitive\n      data-placed={place === null ? undefined : \"\"}\n      data-slot=\"segmented-control\"\n      onValueChange={(next) => {\n        const [picked] = next;\n        if (picked !== undefined) {\n          onValueChange(picked);\n        }\n      }}\n      ref={root}\n      value={[value]}\n      {...props}\n      {...stylex.props([typeStyles.textSm, styles.root], sx)}\n    >\n      {place === null ? null : (\n        <span\n          aria-hidden\n          {...stylex.props(\n            styles.indicator,\n            styles.place(place.x, place.width)\n          )}\n        />\n      )}\n      {children}\n    </ToggleGroupPrimitive>\n  );\n}\n\nfunction SegmentedControlItem({ sx, ...props }: Styled<TogglePrimitive.Props>) {\n  return (\n    <TogglePrimitive\n      data-slot=\"segmented-control-item\"\n      {...props}\n      {...stylex.props(styles.item, sx)}\n    />\n  );\n}\n\nexport { SegmentedControl, SegmentedControlItem };\nexport type { SegmentedControlProps };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Usage: https://constella.aayush.cv/docs/components/segmented-control/index.md",
  "type": "registry:ui"
}