{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "segmented-control",
  "title": "Segmented Control",
  "description": "A short set of mutually exclusive views, always one on, as a thumb in a track, chips, an underline or square icons.",
  "dependencies": [
    "radix-ui",
    "motion"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/segmented-control.tsx",
      "content": "import type * as React from \"react\"\r\nimport { createContext, useContext, useId, useMemo, useState } from \"react\"\r\nimport { RadioGroup as RadioGroupPrimitive } from \"radix-ui\"\r\nimport { LayoutGroup, motion, useReducedMotion } from \"motion/react\"\r\nimport { SPRING_DEFAULT } from \"@/registry/miseui/lib/motion\"\r\nimport { cn } from \"@/registry/miseui/lib/utils\"\r\n\r\ntype SegmentedControlVariant = \"plain\" | \"pill\" | \"underline\" | \"icon\"\r\ntype SegmentedControlEffect = \"spring\"\r\n\r\nconst INSTANT = { duration: 0 } as const\r\n\r\n/** Equal tracks, so the thumb keeps its size in flight and its corners stay undistorted. */\r\nconst ROOT_CLASS: Record<SegmentedControlVariant, string> = {\r\n  plain: \"isolate inline-grid max-w-full auto-cols-fr grid-flow-col gap-1 rounded-lg bg-muted p-1\",\r\n  pill: \"isolate inline-grid max-w-full auto-cols-fr grid-flow-col gap-1\",\r\n  underline: \"isolate inline-grid max-w-full auto-cols-fr grid-flow-col border-b\",\r\n  icon: \"isolate inline-grid max-w-full auto-cols-fr grid-flow-col gap-1 rounded-lg border p-1\",\r\n}\r\n\r\nconst ITEM_CLASS: Record<SegmentedControlVariant, string> = {\r\n  plain: \"h-8 rounded-md px-3 pointer-coarse:h-10\",\r\n  pill: \"h-8 rounded-full px-3 pointer-coarse:h-10\",\r\n  underline: \"h-10 px-3\",\r\n  icon: \"size-8 rounded-md pointer-coarse:size-10 [&_svg]:size-4\",\r\n}\r\n\r\nconst ITEM_ON_CLASS: Record<SegmentedControlVariant, string> = {\r\n  plain: \"data-[state=checked]:text-foreground\",\r\n  pill: \"data-[state=checked]:text-primary-foreground\",\r\n  underline: \"data-[state=checked]:text-foreground\",\r\n  icon: \"data-[state=checked]:text-mise-foreground\",\r\n}\r\n\r\n/** Lighter than the track in dark mode: `bg-background` there would be a dark hole, not a raised thumb. */\r\nconst THUMB_CLASS: Record<SegmentedControlVariant, string> = {\r\n  plain: \"inset-0 rounded-[inherit] bg-background shadow-sm dark:bg-accent dark:shadow-black/30\",\r\n  pill: \"inset-0 rounded-[inherit] bg-primary\",\r\n  underline: \"inset-x-0 -bottom-px h-0.5 bg-foreground\",\r\n  icon: \"inset-0 rounded-[inherit] bg-mise\",\r\n}\r\n\r\ninterface SegmentedContextValue {\r\n  variant: SegmentedControlVariant\r\n  value: string | null | undefined\r\n  animated: boolean\r\n}\r\n\r\nconst SegmentedContext = createContext<SegmentedContextValue>({ variant: \"plain\", value: undefined, animated: false })\r\n\r\ntype SegmentedControlProps = React.ComponentProps<typeof RadioGroupPrimitive.Root> & {\r\n  /** `plain` a thumb in a track, `pill` separate chips, `underline` a line under the text, `icon` square glyphs. Give `icon` items an aria-label. */\r\n  variant?: SegmentedControlVariant\r\n  /** `spring` slides one thumb between the options and follows the option when the control resizes. */\r\n  effect?: SegmentedControlEffect\r\n}\r\n\r\nfunction SegmentedControl({ variant = \"plain\", effect, className, value, defaultValue, onValueChange, ...props }: SegmentedControlProps) {\r\n  // layoutId is global to the page; without a per-instance group two controls would fight over one thumb.\r\n  const groupId = useId()\r\n  const reduceMotion = useReducedMotion() ?? false\r\n  const [uncontrolled, setUncontrolled] = useState<string | null | undefined>(defaultValue)\r\n  const current = value !== undefined ? value : uncontrolled\r\n  const animated = effect === \"spring\" && !reduceMotion\r\n  const context = useMemo(() => ({ variant, value: current, animated }), [variant, current, animated])\r\n  return (\r\n    <SegmentedContext.Provider value={context}>\r\n      <LayoutGroup id={groupId}>\r\n        <RadioGroupPrimitive.Root\r\n          data-slot=\"segmented-control\"\r\n          data-variant={variant}\r\n          data-effect={effect}\r\n          orientation=\"horizontal\"\r\n          className={cn(ROOT_CLASS[variant], className)}\r\n          value={current}\r\n          onValueChange={(next) => {\r\n            setUncontrolled(next)\r\n            onValueChange?.(next)\r\n          }}\r\n          {...props}\r\n        />\r\n      </LayoutGroup>\r\n    </SegmentedContext.Provider>\r\n  )\r\n}\r\n\r\nfunction Thumb({ variant, animated }: { variant: SegmentedControlVariant; animated: boolean }) {\r\n  return (\r\n    <motion.span\r\n      aria-hidden=\"true\"\r\n      data-slot=\"segmented-control-thumb\"\r\n      // Mounted inside the chosen option and sized by it, not measured from outside, so no resize can leave it behind.\r\n      layoutId={animated ? \"segmented-control-thumb\" : undefined}\r\n      transition={animated ? SPRING_DEFAULT : INSTANT}\r\n      className={cn(\"absolute -z-10\", THUMB_CLASS[variant])}\r\n    />\r\n  )\r\n}\r\n\r\nfunction SegmentedControlItem({ value, className, children, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {\r\n  const { variant, value: current, animated } = useContext(SegmentedContext)\r\n  return (\r\n    <RadioGroupPrimitive.Item\r\n      data-slot=\"segmented-control-item\"\r\n      value={value}\r\n      className={cn(\r\n        \"relative inline-flex min-w-0 items-center justify-center gap-2 text-sm font-medium whitespace-nowrap text-muted-foreground outline-none select-none\",\r\n        \"transition-[color,scale,box-shadow] duration-(--duration-fast) ease-(--ease-mise) enabled:hover:text-foreground\",\r\n        \"focus-visible:ring-[3px] focus-visible:ring-ring/50 active:scale-[0.97] active:duration-100 motion-reduce:active:scale-100\",\r\n        \"disabled:cursor-not-allowed disabled:opacity-50\",\r\n        ITEM_CLASS[variant],\r\n        ITEM_ON_CLASS[variant],\r\n        className,\r\n      )}\r\n      {...props}\r\n    >\r\n      {current === value ? <Thumb variant={variant} animated={animated} /> : null}\r\n      {children}\r\n    </RadioGroupPrimitive.Item>\r\n  )\r\n}\r\n\r\nexport { SegmentedControl, SegmentedControlItem, type SegmentedControlProps, type SegmentedControlVariant, type SegmentedControlEffect }\r\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}