{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toggle-group",
  "title": "Toggle Group",
  "description": "A row of buttons that stay pressed, one at a time or several at once: quiet fill, joined outline, wrapping chips or square icons.",
  "dependencies": [
    "radix-ui",
    "motion"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/toggle-group.tsx",
      "content": "import type * as React from \"react\"\r\nimport { createContext, useContext, useId, useMemo, useState } from \"react\"\r\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\r\nimport { AnimatePresence, 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 ToggleGroupVariant = \"plain\" | \"outline\" | \"pill\" | \"icon\"\r\ntype ToggleGroupEffect = \"spring\"\r\ntype ToggleGroupValue = string | string[]\r\n\r\nconst ROOT_CLASS: Record<ToggleGroupVariant, string> = {\r\n  plain: \"isolate inline-flex max-w-full flex-wrap items-center gap-1\",\r\n  outline: \"isolate inline-flex max-w-full items-stretch divide-x rounded-md border shadow-xs\",\r\n  pill: \"isolate inline-flex max-w-full flex-wrap items-center gap-2\",\r\n  icon: \"isolate inline-flex max-w-full items-center gap-1\",\r\n}\r\n\r\nconst ITEM_CLASS: Record<ToggleGroupVariant, string> = {\r\n  plain: \"h-9 rounded-md px-3 text-sm hover:text-foreground pointer-coarse:h-10\",\r\n  outline: \"h-9 min-w-0 px-3 text-sm first:rounded-l-[inherit] last:rounded-r-[inherit] hover:text-foreground pointer-coarse:h-10\",\r\n  pill: \"h-8 rounded-full border border-input px-3 text-[13px] hover:text-foreground data-[state=on]:border-transparent pointer-coarse:h-10\",\r\n  icon: \"size-9 rounded-md hover:text-foreground pointer-coarse:size-10 [&_svg]:size-4\",\r\n}\r\n\r\nconst ITEM_ON_CLASS: Record<ToggleGroupVariant, string> = {\r\n  plain: \"data-[state=on]:text-foreground\",\r\n  outline: \"data-[state=on]:text-foreground\",\r\n  pill: \"data-[state=on]:text-primary-foreground data-[state=on]:hover:text-primary-foreground\",\r\n  icon: \"data-[state=on]:text-mise-foreground data-[state=on]:hover:text-mise-foreground\",\r\n}\r\n\r\nconst HIGHLIGHT_CLASS: Record<ToggleGroupVariant, string> = {\r\n  plain: \"bg-accent\",\r\n  outline: \"bg-accent\",\r\n  pill: \"bg-primary\",\r\n  icon: \"bg-mise\",\r\n}\r\n\r\ninterface ToggleGroupContextValue {\r\n  variant: ToggleGroupVariant\r\n  onList: readonly string[]\r\n  multiple: boolean\r\n  spring: boolean\r\n}\r\n\r\nconst ToggleGroupContext = createContext<ToggleGroupContextValue>({\r\n  variant: \"plain\",\r\n  onList: [],\r\n  multiple: false,\r\n  spring: false,\r\n})\r\n\r\nfunction toOnList(value: ToggleGroupValue | undefined) {\r\n  if (value === undefined || value === \"\") return []\r\n  return typeof value === \"string\" ? [value] : value\r\n}\r\n\r\ntype ToggleGroupOwnProps = {\r\n  /** `plain` a quiet fill, `outline` joined buttons, `pill` wrapping chips, `icon` square glyphs in the accent. Give `icon` items an aria-label. */\r\n  variant?: ToggleGroupVariant\r\n  /** `spring` slides the highlight between options in `single`; in `multiple` each highlight grows in place. */\r\n  effect?: ToggleGroupEffect\r\n  disabled?: boolean\r\n}\r\n\r\ntype ToggleGroupRootProps = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"dir\">\r\n\r\ntype ToggleGroupProps = ToggleGroupRootProps &\r\n  ToggleGroupOwnProps &\r\n  (\r\n    | { type?: \"single\"; value?: string; defaultValue?: string; onValueChange?: (value: string) => void }\r\n    | { type: \"multiple\"; value?: string[]; defaultValue?: string[]; onValueChange?: (value: string[]) => void }\r\n  )\r\n\r\nfunction ToggleGroup({ variant = \"plain\", effect, className, type = \"single\", value, defaultValue, onValueChange, ...props }: ToggleGroupProps) {\r\n  // layoutId is global to the page; without a per-instance group two groups would fight over one highlight.\r\n  const groupId = useId()\r\n  const reduceMotion = useReducedMotion() ?? false\r\n  const [uncontrolled, setUncontrolled] = useState<ToggleGroupValue | undefined>(defaultValue)\r\n  const onList = useMemo(() => toOnList(value ?? uncontrolled), [value, uncontrolled])\r\n  const multiple = type === \"multiple\"\r\n  const spring = effect === \"spring\" && !reduceMotion\r\n  const context = useMemo(() => ({ variant, onList, multiple, spring }), [variant, onList, multiple, spring])\r\n  const change = (next: ToggleGroupValue) => {\r\n    setUncontrolled(next)\r\n    ;(onValueChange as ((value: ToggleGroupValue) => void) | undefined)?.(next)\r\n  }\r\n  const shared = {\r\n    ...props,\r\n    \"data-slot\": \"toggle-group\",\r\n    \"data-variant\": variant,\r\n    \"data-effect\": effect,\r\n    className: cn(ROOT_CLASS[variant], className),\r\n    onValueChange: change,\r\n  }\r\n  return (\r\n    <ToggleGroupContext.Provider value={context}>\r\n      <LayoutGroup id={groupId}>\r\n        {multiple ? (\r\n          <ToggleGroupPrimitive.Root {...shared} type=\"multiple\" value={onList as string[]} />\r\n        ) : (\r\n          <ToggleGroupPrimitive.Root {...shared} type=\"single\" value={onList[0] ?? \"\"} />\r\n        )}\r\n      </LayoutGroup>\r\n    </ToggleGroupContext.Provider>\r\n  )\r\n}\r\n\r\nfunction Highlight({ variant, multiple, spring }: Pick<ToggleGroupContextValue, \"variant\" | \"multiple\" | \"spring\">) {\r\n  const className = cn(\"absolute inset-0 -z-10 rounded-[inherit]\", HIGHLIGHT_CLASS[variant])\r\n  if (spring && !multiple) {\r\n    return (\r\n      <motion.span\r\n        aria-hidden=\"true\"\r\n        data-slot=\"toggle-group-highlight\"\r\n        layoutId=\"toggle-group-highlight\"\r\n        transition={SPRING_DEFAULT}\r\n        className={className}\r\n      />\r\n    )\r\n  }\r\n  if (!spring) return <span aria-hidden=\"true\" data-slot=\"toggle-group-highlight\" className={className} />\r\n  const away = { opacity: 0, scale: 0.9 }\r\n  return (\r\n    <motion.span\r\n      aria-hidden=\"true\"\r\n      data-slot=\"toggle-group-highlight\"\r\n      initial={away}\r\n      animate={{ opacity: 1, scale: 1 }}\r\n      exit={away}\r\n      transition={SPRING_DEFAULT}\r\n      className={className}\r\n    />\r\n  )\r\n}\r\n\r\nfunction ToggleGroupItem({ value, className, children, ...props }: React.ComponentProps<typeof ToggleGroupPrimitive.Item>) {\r\n  const { variant, onList, multiple, spring } = useContext(ToggleGroupContext)\r\n  const on = onList.includes(value)\r\n  return (\r\n    <ToggleGroupPrimitive.Item\r\n      data-slot=\"toggle-group-item\"\r\n      value={value}\r\n      className={cn(\r\n        \"relative inline-flex shrink-0 items-center justify-center gap-2 font-medium whitespace-nowrap text-muted-foreground outline-none select-none\",\r\n        \"transition-[color,scale,box-shadow,border-color] duration-(--duration-fast) ease-(--ease-mise)\",\r\n        \"focus-visible:z-10 focus-visible:ring-[3px] focus-visible:ring-ring/50 active:scale-[0.97] active:duration-100 motion-reduce:active:scale-100\",\r\n        \"disabled:pointer-events-none 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      <AnimatePresence initial={false}>\r\n        {on ? <Highlight key=\"highlight\" variant={variant} multiple={multiple} spring={spring} /> : null}\r\n      </AnimatePresence>\r\n      {children}\r\n    </ToggleGroupPrimitive.Item>\r\n  )\r\n}\r\n\r\nexport { ToggleGroup, ToggleGroupItem, type ToggleGroupProps, type ToggleGroupVariant, type ToggleGroupEffect }\r\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}