{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "r-toggle",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["radix-ui", "class-variance-authority", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "r-toggle.tsx",
      "content": "\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { animate, motion, useMotionValue, useTransform } from \"motion/react\";\nimport { Toggle as TogglePrimitive } from \"radix-ui\";\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 togglePressedTextRadix =\n  \"text-muted-foreground data-[state=on]: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 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 toggleVariants = cva(\n  cn(\n    toggleCornerClassName,\n    \"group/toggle relative inline-flex 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\", togglePressedTextRadix),\n        outline: cn(\n          \"border border-input bg-transparent\",\n          togglePressedTextRadix\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\ntype ToggleProps = React.ComponentProps<typeof TogglePrimitive.Root> &\n  VariantProps<typeof toggleVariants>;\n\nconst Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(function Toggle(\n  { className, variant = \"default\", size = \"default\", children, ...props },\n  forwardedRef\n) {\n  return (\n    <TogglePrimitive.Root asChild {...props}>\n      <FluidToggleButton\n        className={cn(toggleVariants({ variant, size, className }))}\n        data-slot=\"toggle\"\n        ref={forwardedRef}\n      >\n        {children}\n      </FluidToggleButton>\n    </TogglePrimitive.Root>\n  );\n});\n\nToggle.displayName = \"Toggle\";\n\nexport { Toggle, toggleVariants };\nexport type { ToggleProps };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Toggle (Radix UI)",
  "description": "Two-state button with spring press feedback, a muted fill that bounces in when pressed, and a subtle content lift layered over Radix UI toggle primitives."
}
