{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-effects-magnetic",
  "title": "Magnetic",
  "description": "A magnetic effect that clings to the cursor, creating a magnetic attraction effect.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@unlumen-ui/primitives-animate-slot"
  ],
  "files": [
    {
      "path": "registry/primitives/effects/magnetic/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  motion,\n  useMotionValue,\n  useSpring,\n  type SpringOptions,\n  type HTMLMotionProps,\n} from \"motion/react\";\n\nimport { Slot, type WithAsChild } from \"@/components/unlumen-ui/primitives/animate/slot\";\n\ntype MagneticProps = WithAsChild<\n  {\n    children?: React.ReactNode;\n    strength?: number;\n    range?: number;\n    springOptions?: SpringOptions;\n    onlyOnHover?: boolean;\n    disableOnTouch?: boolean;\n    ref?: React.Ref<HTMLElement>;\n  } & HTMLMotionProps<\"div\">\n>;\n\nfunction Magnetic({\n  ref,\n  strength = 0.5,\n  range = 120,\n  springOptions = { stiffness: 100, damping: 10, mass: 0.5 },\n  onlyOnHover = false,\n  disableOnTouch = true,\n  style,\n  onMouseEnter,\n  onMouseLeave,\n  onMouseMove,\n  asChild = false,\n  ...props\n}: MagneticProps) {\n  const localRef = React.useRef<HTMLDivElement>(null);\n  React.useImperativeHandle(ref, () => localRef.current as HTMLDivElement);\n\n  const isTouchDevice = React.useMemo(() => {\n    if (typeof window === \"undefined\") return false;\n    return window.matchMedia(\"(pointer:coarse)\").matches;\n  }, []);\n\n  const [active, setActive] = React.useState(!onlyOnHover);\n\n  const rawX = useMotionValue(0);\n  const rawY = useMotionValue(0);\n  const x = useSpring(rawX, springOptions);\n  const y = useSpring(rawY, springOptions);\n\n  const compute = React.useCallback(\n    (e: MouseEvent | React.MouseEvent) => {\n      if (!localRef.current) return;\n      const { left, top, width, height } =\n        localRef.current.getBoundingClientRect();\n      const cx = left + width / 2;\n      const cy = top + height / 2;\n      const dx = e.clientX - cx;\n      const dy = e.clientY - cy;\n      const dist = Math.hypot(dx, dy);\n\n      if ((active || !onlyOnHover) && dist <= range) {\n        const factor = (1 - dist / range) * strength;\n        rawX.set(dx * factor);\n        rawY.set(dy * factor);\n      } else {\n        rawX.set(0);\n        rawY.set(0);\n      }\n    },\n    [active, onlyOnHover, range, strength, rawX, rawY],\n  );\n\n  React.useEffect(() => {\n    if (disableOnTouch && isTouchDevice) return;\n    const handle = (e: MouseEvent) => compute(e);\n    window.addEventListener(\"mousemove\", handle);\n    return () => window.removeEventListener(\"mousemove\", handle);\n  }, [compute, disableOnTouch, isTouchDevice]);\n\n  const Component = asChild ? Slot : motion.div;\n\n  return (\n    <Component\n      ref={localRef}\n      style={{ display: \"inline-block\", ...style, x, y }}\n      onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {\n        if (onlyOnHover) setActive(true);\n        onMouseEnter?.(e);\n      }}\n      onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {\n        if (onlyOnHover) setActive(false);\n        rawX.set(0);\n        rawY.set(0);\n        onMouseLeave?.(e);\n      }}\n      onMouseMove={(e: React.MouseEvent<HTMLDivElement>) => {\n        if (onlyOnHover) compute(e);\n        onMouseMove?.(e);\n      }}\n      {...props}\n    />\n  );\n}\n\nexport { Magnetic, type MagneticProps };\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/primitives/effects/magnetic.tsx"
    }
  ],
  "type": "registry:ui"
}