{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-togglegroup",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "class-variance-authority", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-togglegroup.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 { cva, type VariantProps } from \"class-variance-authority\";\nimport { animate, motion, useMotionValue, useTransform } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst toggleCornerClassName =\n  \"rounded-lg supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[11px]\";\n\nconst toggleCornerSmClassName =\n  \"rounded-[min(var(--radius-md),12px)] supports-[corner-shape:squircle]:corner-squircle supports-[corner-shape:squircle]:rounded-[14px]\";\n\nconst togglePressedTextBase =\n  \"text-muted-foreground aria-pressed:text-foreground data-pressed:text-foreground\";\n\nconst toggleFillClassName =\n  \"pointer-events-none absolute inset-0 z-0 rounded-[inherit] bg-muted supports-[corner-shape:squircle]:[corner-shape:inherit]\";\n\nconst ICON_REST = 0.93;\n\nconst FLUID_FILL = {\n  type: \"spring\" as const,\n  stiffness: 210,\n  damping: 28,\n  mass: 1.08,\n};\nconst FLUID_ICON = {\n  type: \"spring\" as const,\n  stiffness: 340,\n  damping: 32,\n  mass: 0.92,\n};\nconst FLUID_TAP = {\n  type: \"spring\" as const,\n  stiffness: 520,\n  damping: 34,\n  mass: 0.72,\n};\nconst FLUID_SNAP = {\n  type: \"spring\" as const,\n  duration: 0.45,\n  bounce: 0.32,\n};\nconst FLUID_SHEEN = {\n  duration: 0.58,\n  ease: [0.22, 1, 0.36, 1] as const,\n};\n\nfunction runSheenSweep(\n  sheenX: ReturnType<typeof useMotionValue<number>>,\n  sheenOpacity: ReturnType<typeof useMotionValue<number>>\n) {\n  sheenX.set(-0.4);\n  sheenOpacity.set(0.85);\n\n  animate(sheenX, 1.4, FLUID_SHEEN);\n  animate(sheenOpacity, 0, {\n    duration: 0.58,\n    ease: [0.4, 0, 0.2, 1],\n  });\n}\n\nfunction resolveIsPressed({\n  isPressed,\n  \"data-state\": dataState,\n  \"data-pressed\": dataPressed,\n  \"aria-pressed\": ariaPressed,\n}: {\n  isPressed?: boolean;\n  \"data-state\"?: string;\n  \"data-pressed\"?: string | boolean;\n  \"aria-pressed\"?: boolean | \"true\" | \"false\" | \"mixed\";\n}) {\n  if (isPressed !== undefined) {\n    return isPressed;\n  }\n\n  if (dataState === \"on\") {\n    return true;\n  }\n\n  if (dataState === \"off\") {\n    return false;\n  }\n\n  if (dataPressed === true || dataPressed === \"true\" || dataPressed === \"\") {\n    return true;\n  }\n\n  if (dataPressed === false || dataPressed === \"false\") {\n    return false;\n  }\n\n  if (ariaPressed === true || ariaPressed === \"true\") {\n    return true;\n  }\n\n  if (ariaPressed === false || ariaPressed === \"false\") {\n    return false;\n  }\n\n  return false;\n}\n\nfunction setToggleRef<T>(ref: React.Ref<T> | undefined, value: T | null) {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    (ref as React.MutableRefObject<T | null>).current = value;\n  }\n}\n\nfunction useToggleFluidMotion(isPressed: boolean) {\n  const fillProgress = useMotionValue(isPressed ? 1 : 0);\n  const iconScale = useMotionValue(isPressed ? 1 : ICON_REST);\n  const iconScaleX = useMotionValue(1);\n  const iconScaleY = useMotionValue(1);\n  const sheenX = useMotionValue(-0.4);\n  const sheenOpacity = useMotionValue(0);\n\n  const fillOpacity = useTransform(fillProgress, [0, 1], [0, 1]);\n  const sheenLeft = useTransform(sheenX, (value) => `${value * 100}%`);\n\n  const prevPressedRef = React.useRef(isPressed);\n  const isPointerDownRef = React.useRef(false);\n\n  React.useEffect(() => {\n    if (prevPressedRef.current === isPressed) {\n      return;\n    }\n\n    prevPressedRef.current = isPressed;\n\n    animate(fillProgress, isPressed ? 1 : 0, FLUID_FILL);\n    animate(iconScale, isPressed ? 1 : ICON_REST, FLUID_ICON);\n    runSheenSweep(sheenX, sheenOpacity);\n\n    animate(iconScaleX, 1.1, FLUID_TAP).then(() => {\n      animate(iconScaleX, 1, FLUID_SNAP);\n    });\n    animate(iconScaleY, 0.91, FLUID_TAP).then(() => {\n      animate(iconScaleY, 1, FLUID_SNAP);\n    });\n  }, [\n    fillProgress,\n    iconScale,\n    iconScaleX,\n    iconScaleY,\n    isPressed,\n    sheenOpacity,\n    sheenX,\n  ]);\n\n  const resetTapScale = React.useCallback(() => {\n    animate(iconScaleX, 1, FLUID_SNAP);\n    animate(iconScaleY, 1, FLUID_SNAP);\n  }, [iconScaleX, iconScaleY]);\n\n  const handlePointerDown = React.useCallback(\n    (event: React.PointerEvent<HTMLButtonElement>, disabled?: boolean) => {\n      if (event.defaultPrevented || disabled || event.button !== 0) {\n        return;\n      }\n\n      isPointerDownRef.current = true;\n\n      animate(iconScaleX, 0.86, FLUID_TAP);\n      animate(iconScaleY, 1.08, FLUID_TAP);\n    },\n    [iconScaleX, iconScaleY]\n  );\n\n  const handlePointerUp = React.useCallback(() => {\n    if (!isPointerDownRef.current) {\n      return;\n    }\n\n    isPointerDownRef.current = false;\n    resetTapScale();\n  }, [resetTapScale]);\n\n  const handlePointerLeave = React.useCallback(() => {\n    if (!isPointerDownRef.current) {\n      return;\n    }\n\n    isPointerDownRef.current = false;\n    resetTapScale();\n  }, [resetTapScale]);\n\n  return {\n    fillOpacity,\n    sheenLeft,\n    sheenOpacity,\n    iconScale,\n    iconScaleX,\n    iconScaleY,\n    handlePointerDown,\n    handlePointerUp,\n    handlePointerLeave,\n  };\n}\n\nfunction ToggleFluidMotionLayers({\n  children,\n  fillOpacity,\n  sheenLeft,\n  sheenOpacity,\n  iconScale,\n  iconScaleX,\n  iconScaleY,\n}: ReturnType<typeof useToggleFluidMotion> & {\n  children: React.ReactNode;\n}) {\n  return (\n    <>\n      <motion.span\n        aria-hidden\n        className={toggleFillClassName}\n        style={{\n          opacity: fillOpacity,\n        }}\n      />\n      <span\n        aria-hidden\n        className=\"pointer-events-none absolute inset-0 z-[1] overflow-hidden rounded-[inherit]\"\n      >\n        <motion.span\n          className=\"absolute inset-y-[-20%] w-[42%] -skew-x-[18deg] bg-gradient-to-r from-transparent via-foreground/18 to-transparent dark:via-foreground/26\"\n          style={{\n            left: sheenLeft,\n            opacity: sheenOpacity,\n          }}\n        />\n      </span>\n      <span className=\"relative z-10 inline-flex items-center justify-center gap-[inherit] [&_svg]:shrink-0\">\n        <motion.span\n          className=\"inline-flex origin-center items-center justify-center gap-[inherit]\"\n          style={{ scale: iconScale }}\n        >\n          <motion.span\n            className=\"inline-flex origin-center items-center justify-center gap-[inherit] [&_svg]:block\"\n            style={{\n              scaleX: iconScaleX,\n              scaleY: iconScaleY,\n            }}\n          >\n            {children}\n          </motion.span>\n        </motion.span>\n      </span>\n    </>\n  );\n}\n\ntype FluidToggleButtonProps = React.ComponentProps<\"button\"> & {\n  children: React.ReactNode;\n  isPressed?: boolean;\n  \"data-pressed\"?: string | boolean;\n  \"data-state\"?: \"on\" | \"off\" | string;\n};\n\nconst FluidToggleButton = React.forwardRef<\n  HTMLButtonElement,\n  FluidToggleButtonProps\n>(function FluidToggleButton(\n  {\n    children,\n    className,\n    disabled,\n    isPressed: isPressedProp,\n    onPointerDown,\n    onPointerLeave,\n    onPointerUp,\n    type = \"button\",\n    \"aria-pressed\": ariaPressed,\n    \"data-pressed\": dataPressed,\n    \"data-state\": dataState,\n    ...props\n  },\n  ref\n) {\n  const isPressed = resolveIsPressed({\n    isPressed: isPressedProp,\n    \"aria-pressed\": ariaPressed,\n    \"data-pressed\": dataPressed,\n    \"data-state\": dataState,\n  });\n  const fluidMotion = useToggleFluidMotion(isPressed);\n\n  return (\n    <button\n      aria-pressed={ariaPressed}\n      className={className}\n      data-pressed={dataPressed}\n      data-state={dataState}\n      disabled={disabled}\n      onPointerDown={(event) => {\n        onPointerDown?.(event);\n        fluidMotion.handlePointerDown(event, disabled);\n      }}\n      onPointerLeave={(event) => {\n        onPointerLeave?.(event);\n        fluidMotion.handlePointerLeave();\n      }}\n      onPointerUp={(event) => {\n        onPointerUp?.(event);\n        fluidMotion.handlePointerUp();\n      }}\n      ref={ref}\n      type={type}\n      {...props}\n    >\n      <ToggleFluidMotionLayers {...fluidMotion}>\n        {children}\n      </ToggleFluidMotionLayers>\n    </button>\n  );\n});\n\nFluidToggleButton.displayName = \"FluidToggleButton\";\nconst toggleGroupRootClassName = cn(\n  \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-vertical:flex-col data-vertical:items-stretch\",\n  \"data-[spacing=0]:overflow-hidden\",\n  \"data-[spacing=0]:rounded-lg\",\n  \"data-[spacing=0]:supports-[corner-shape:squircle]:corner-squircle\",\n  \"data-[spacing=0]:supports-[corner-shape:squircle]:rounded-[11px]\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:border\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:border-input\",\n  \"data-[spacing=0]:[&>[data-slot=toggle-group-item]]:rounded-none\",\n  \"data-[spacing=0]:[&>[data-slot=toggle-group-item]]:supports-[corner-shape:squircle]:rounded-none\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:[&>[data-slot=toggle-group-item][data-variant=outline]]:border-0\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:data-horizontal:[&>[data-slot=toggle-group-item][data-variant=outline]~[data-slot=toggle-group-item][data-variant=outline]]:border-l\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:data-horizontal:[&>[data-slot=toggle-group-item][data-variant=outline]~[data-slot=toggle-group-item][data-variant=outline]]:border-input\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:data-vertical:[&>[data-slot=toggle-group-item][data-variant=outline]~[data-slot=toggle-group-item][data-variant=outline]]:border-t\",\n  \"data-[spacing=0]:has-[>[data-slot=toggle-group-item][data-variant=outline]]:data-vertical:[&>[data-slot=toggle-group-item][data-variant=outline]~[data-slot=toggle-group-item][data-variant=outline]]:border-input\"\n);\n\nconst toggleGroupItemVariants = cva(\n  cn(\n    toggleCornerClassName,\n    \"relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-1 overflow-hidden whitespace-nowrap font-medium text-sm outline-none transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\"\n  ),\n  {\n    variants: {\n      variant: {\n        default: cn(\"bg-transparent\", togglePressedTextBase),\n        outline: cn(\n          \"border border-input bg-transparent\",\n          togglePressedTextBase\n        ),\n      },\n      size: {\n        default:\n          \"h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n        sm: cn(\n          \"h-7 min-w-7 px-2.5 text-[0.8rem] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\n          toggleCornerSmClassName\n        ),\n        lg: \"h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nconst ToggleGroupContext = React.createContext<\n  VariantProps<typeof toggleGroupItemVariants> & {\n    spacing?: number;\n    orientation?: \"horizontal\" | \"vertical\";\n  }\n>({\n  size: \"default\",\n  variant: \"default\",\n  spacing: 1,\n  orientation: \"horizontal\",\n});\n\ntype ToggleGroupProps = ToggleGroupPrimitive.Props &\n  VariantProps<typeof toggleGroupItemVariants> & {\n    spacing?: number;\n    orientation?: \"horizontal\" | \"vertical\";\n  };\n\ntype ToggleGroupItemProps = TogglePrimitive.Props &\n  VariantProps<typeof toggleGroupItemVariants>;\n\nconst ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(\n  function ToggleGroup(\n    {\n      className,\n      variant,\n      size,\n      spacing = 1,\n      orientation = \"horizontal\",\n      multiple = true,\n      children,\n      ...props\n    },\n    ref\n  ) {\n    return (\n      <ToggleGroupPrimitive\n        aria-orientation={orientation}\n        className={cn(toggleGroupRootClassName, className)}\n        data-horizontal={orientation === \"horizontal\" ? \"\" : undefined}\n        data-orientation={orientation}\n        data-size={size}\n        data-slot=\"toggle-group\"\n        data-spacing={spacing}\n        data-variant={variant}\n        data-vertical={orientation === \"vertical\" ? \"\" : undefined}\n        multiple={multiple}\n        ref={ref}\n        style={{ \"--gap\": spacing } as React.CSSProperties}\n        {...props}\n      >\n        <ToggleGroupContext.Provider\n          value={{\n            variant,\n            size,\n            spacing,\n            orientation,\n          }}\n        >\n          {children}\n        </ToggleGroupContext.Provider>\n      </ToggleGroupPrimitive>\n    );\n  }\n);\n\nToggleGroup.displayName = \"ToggleGroup\";\n\nconst ToggleGroupItem = React.forwardRef<\n  HTMLButtonElement,\n  ToggleGroupItemProps\n>(function ToggleGroupItem(\n  { className, children, variant = \"default\", size = \"default\", ...props },\n  forwardedRef\n) {\n  const context = React.useContext(ToggleGroupContext);\n  const itemClassName = cn(\n    \"focus:z-10 focus-visible:z-10\",\n    toggleGroupItemVariants({\n      variant: context.variant || variant,\n      size: context.size || size,\n    }),\n    className\n  );\n\n  return (\n    <TogglePrimitive\n      data-size={context.size || size}\n      data-slot=\"toggle-group-item\"\n      data-spacing={context.spacing}\n      data-variant={context.variant || variant}\n      render={(renderProps, state) => {\n        const {\n          className: renderClassName,\n          onPointerDown: _onPointerDown,\n          onPointerLeave: _onPointerLeave,\n          onPointerUp: _onPointerUp,\n          ref: renderRef,\n          ...resolvedRenderProps\n        } = renderProps;\n\n        return (\n          <FluidToggleButton\n            {...resolvedRenderProps}\n            className={cn(itemClassName, renderClassName)}\n            isPressed={state.pressed}\n            ref={(node) => {\n              setToggleRef(forwardedRef, node);\n              setToggleRef(renderRef, node);\n            }}\n          >\n            {children}\n          </FluidToggleButton>\n        );\n      }}\n      {...props}\n    />\n  );\n});\n\nToggleGroupItem.displayName = \"ToggleGroupItem\";\n\nexport { ToggleGroup, ToggleGroupItem, toggleGroupItemVariants };\nexport type { ToggleGroupItemProps, ToggleGroupProps };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Toggle Group (Base UI)",
  "description": "Segmented toggle group with the same fluid wipe fill, sheen sweep, and icon press feedback as the standalone toggle, over Base UI toggle-group primitives."
}
