{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-animate-cursor",
  "title": "Cursor",
  "description": "An animated cursor component that allows you to customize both the cursor and cursor follow elements with smooth animations.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@animate-ui/v1/primitives-animate-slot",
    "@animate-ui/v1/lib-get-strict-context"
  ],
  "files": [
    {
      "path": "registry/primitives/animate/cursor/index.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport {\n  motion,\n  useMotionValue,\n  useSpring,\n  AnimatePresence,\n  type HTMLMotionProps,\n  type SpringOptions,\n} from 'motion/react';\n\nimport { getStrictContext } from '@/lib/get-strict-context';\nimport { Slot, type WithAsChild } from '@/components/animate-ui/primitives/animate/slot';\n\ntype CursorContextType = {\n  cursorPos: { x: number; y: number };\n  active: boolean;\n  global: boolean;\n  containerRef: React.RefObject<HTMLDivElement | null>;\n  cursorRef: React.RefObject<HTMLDivElement | null>;\n};\n\nconst [LocalCursorProvider, useCursor] =\n  getStrictContext<CursorContextType>('CursorContext');\n\ntype CursorProviderProps = {\n  children: React.ReactNode;\n  global?: boolean;\n};\n\nfunction CursorProvider({ children, global = false }: CursorProviderProps) {\n  const [cursorPos, setCursorPos] = React.useState({ x: 0, y: 0 });\n  const [active, setActive] = React.useState(false);\n\n  const containerRef = React.useRef<HTMLDivElement>(null);\n  const cursorRef = React.useRef<HTMLDivElement>(null);\n\n  React.useEffect(() => {\n    const id = '__cursor_none_style__';\n    if (document.getElementById(id)) return;\n\n    const style = document.createElement('style');\n    style.id = id;\n    style.textContent = `\n      .animate-ui-cursor-none, .animate-ui-cursor-none * { cursor: none !important; }\n    `;\n    document.head.appendChild(style);\n  }, []);\n\n  React.useEffect(() => {\n    let removeListeners: () => void;\n\n    if (global) {\n      const handlePointerMove = (e: PointerEvent) => {\n        setCursorPos({ x: e.clientX, y: e.clientY });\n        setActive(true);\n      };\n\n      const handlePointerOut = (e: PointerEvent | MouseEvent) => {\n        if (e instanceof PointerEvent && e.relatedTarget === null) {\n          setActive(false);\n        }\n      };\n\n      const handleVisibilityChange = () => {\n        if (document.visibilityState === 'hidden') setActive(false);\n      };\n\n      window.addEventListener('pointermove', handlePointerMove, {\n        passive: true,\n      });\n      window.addEventListener('pointerout', handlePointerOut, {\n        passive: true,\n      });\n      window.addEventListener('mouseout', handlePointerOut, { passive: true });\n      document.addEventListener('visibilitychange', handleVisibilityChange);\n\n      removeListeners = () => {\n        window.removeEventListener('pointermove', handlePointerMove);\n        window.removeEventListener('pointerout', handlePointerOut);\n        window.removeEventListener('mouseout', handlePointerOut);\n        document.removeEventListener(\n          'visibilitychange',\n          handleVisibilityChange,\n        );\n      };\n    } else {\n      if (!containerRef.current) return;\n\n      const parent = containerRef.current.parentElement;\n      if (!parent) return;\n\n      if (getComputedStyle(parent).position === 'static') {\n        parent.style.position = 'relative';\n      }\n\n      const handlePointerMove = (e: PointerEvent) => {\n        const rect = parent.getBoundingClientRect();\n        setCursorPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });\n        setActive(true);\n      };\n\n      const handlePointerOut = (e: PointerEvent | MouseEvent) => {\n        if (\n          e.relatedTarget === null ||\n          !(parent as Node).contains(e.relatedTarget as Node)\n        ) {\n          setActive(false);\n        }\n      };\n\n      parent.addEventListener('pointermove', handlePointerMove, {\n        passive: true,\n      });\n      parent.addEventListener('pointerout', handlePointerOut, {\n        passive: true,\n      });\n      parent.addEventListener('mouseout', handlePointerOut, { passive: true });\n\n      removeListeners = () => {\n        parent.removeEventListener('pointermove', handlePointerMove);\n        parent.removeEventListener('pointerout', handlePointerOut);\n        parent.removeEventListener('mouseout', handlePointerOut);\n      };\n    }\n\n    return removeListeners;\n  }, [global]);\n\n  return (\n    <LocalCursorProvider\n      value={{ cursorPos, active, global, containerRef, cursorRef }}\n    >\n      {children}\n    </LocalCursorProvider>\n  );\n}\n\ntype CursorContainerProps = WithAsChild<HTMLMotionProps<'div'>>;\n\nfunction CursorContainer({\n  ref,\n  asChild = false,\n  ...props\n}: CursorContainerProps) {\n  const { containerRef, global, active } = useCursor();\n  React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);\n\n  const Component = asChild ? Slot : motion.div;\n\n  return (\n    <Component\n      ref={containerRef}\n      data-slot=\"cursor-container\"\n      data-global={global}\n      data-active={active}\n      {...props}\n    />\n  );\n}\n\ntype CursorProps = WithAsChild<\n  HTMLMotionProps<'div'> & {\n    children: React.ReactNode;\n  }\n>;\n\nfunction Cursor({ ref, asChild = false, style, ...props }: CursorProps) {\n  const { cursorPos, active, containerRef, cursorRef, global } = useCursor();\n  React.useImperativeHandle(ref, () => cursorRef.current as HTMLDivElement);\n\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n\n  React.useEffect(() => {\n    const target = global\n      ? document.documentElement\n      : containerRef.current?.parentElement;\n\n    if (!target) return;\n\n    if (active) {\n      target.classList.add('animate-ui-cursor-none');\n    } else {\n      target.classList.remove('animate-ui-cursor-none');\n    }\n\n    return () => {\n      target.classList.remove('animate-ui-cursor-none');\n    };\n  }, [active, global, containerRef]);\n\n  React.useEffect(() => {\n    x.set(cursorPos.x);\n    y.set(cursorPos.y);\n  }, [cursorPos, x, y]);\n\n  const Component = asChild ? Slot : motion.div;\n\n  return (\n    <AnimatePresence>\n      {active && (\n        <Component\n          ref={cursorRef}\n          data-slot=\"cursor\"\n          data-global={global}\n          data-active={active}\n          style={{\n            transform: 'translate(-50%,-50%)',\n            pointerEvents: 'none',\n            zIndex: 9999,\n            position: global ? 'fixed' : 'absolute',\n            top: y,\n            left: x,\n            ...style,\n          }}\n          initial={{ scale: 0, opacity: 0 }}\n          animate={{ scale: 1, opacity: 1 }}\n          exit={{ scale: 0, opacity: 0 }}\n          {...props}\n        />\n      )}\n    </AnimatePresence>\n  );\n}\n\ntype CursorFollowSide = 'top' | 'right' | 'bottom' | 'left';\ntype CursorFollowAlign = 'start' | 'center' | 'end';\n\ntype CursorFollowProps = WithAsChild<\n  Omit<HTMLMotionProps<'div'>, 'transition'> & {\n    side?: CursorFollowSide;\n    sideOffset?: number;\n    align?: CursorFollowAlign;\n    alignOffset?: number;\n    transition?: SpringOptions;\n    children: React.ReactNode;\n  }\n>;\n\nfunction CursorFollow({\n  ref,\n  asChild = false,\n  side = 'bottom',\n  sideOffset = 0,\n  align = 'end',\n  alignOffset = 0,\n  style,\n  transition = { stiffness: 500, damping: 50, bounce: 0 },\n  ...props\n}: CursorFollowProps) {\n  const { cursorPos, active, cursorRef, global } = useCursor();\n  const cursorFollowRef = React.useRef<HTMLDivElement>(null);\n  React.useImperativeHandle(\n    ref,\n    () => cursorFollowRef.current as HTMLDivElement,\n  );\n\n  const x = useMotionValue(0);\n  const y = useMotionValue(0);\n\n  const springX = useSpring(x, transition);\n  const springY = useSpring(y, transition);\n\n  const calculateOffset = React.useCallback(() => {\n    const rect = cursorFollowRef.current?.getBoundingClientRect();\n    const width = rect?.width ?? 0;\n    const height = rect?.height ?? 0;\n\n    let offsetX = 0;\n    let offsetY = 0;\n\n    switch (side) {\n      case 'top':\n        offsetY = height + sideOffset;\n        switch (align) {\n          case 'start':\n            offsetX = width + alignOffset;\n            break;\n          case 'center':\n            offsetX = width / 2;\n            break;\n          case 'end':\n            offsetX = -alignOffset;\n            break;\n        }\n        break;\n\n      case 'bottom':\n        offsetY = -sideOffset;\n        switch (align) {\n          case 'start':\n            offsetX = width + alignOffset;\n            break;\n          case 'center':\n            offsetX = width / 2;\n            break;\n          case 'end':\n            offsetX = -alignOffset;\n            break;\n        }\n        break;\n\n      case 'left':\n        offsetX = width + sideOffset;\n        switch (align) {\n          case 'start':\n            offsetY = height + alignOffset;\n            break;\n          case 'center':\n            offsetY = height / 2;\n            break;\n          case 'end':\n            offsetY = -alignOffset;\n            break;\n        }\n        break;\n\n      case 'right':\n        offsetX = -sideOffset;\n        switch (align) {\n          case 'start':\n            offsetY = height + alignOffset;\n            break;\n          case 'center':\n            offsetY = height / 2;\n            break;\n          case 'end':\n            offsetY = -alignOffset;\n            break;\n        }\n        break;\n    }\n\n    return { x: offsetX, y: offsetY };\n  }, [side, align, sideOffset, alignOffset]);\n\n  React.useEffect(() => {\n    const offset = calculateOffset();\n    const cursorRect = cursorRef.current?.getBoundingClientRect();\n    const cursorWidth = cursorRect?.width ?? 20;\n    const cursorHeight = cursorRect?.height ?? 20;\n\n    x.set(cursorPos.x - offset.x + cursorWidth / 2);\n    y.set(cursorPos.y - offset.y + cursorHeight / 2);\n  }, [calculateOffset, cursorPos, cursorRef, x, y]);\n\n  const Component = asChild ? Slot : motion.div;\n\n  return (\n    <AnimatePresence>\n      {active && (\n        <Component\n          ref={cursorFollowRef}\n          data-slot=\"cursor-follow\"\n          data-global={global}\n          data-active={active}\n          style={{\n            transform: 'translate(-50%,-50%)',\n            pointerEvents: 'none',\n            zIndex: 9998,\n            position: global ? 'fixed' : 'absolute',\n            top: springY,\n            left: springX,\n            ...style,\n          }}\n          initial={{ scale: 0, opacity: 0 }}\n          animate={{ scale: 1, opacity: 1 }}\n          exit={{ scale: 0, opacity: 0 }}\n          {...props}\n        />\n      )}\n    </AnimatePresence>\n  );\n}\n\nexport {\n  CursorProvider,\n  Cursor,\n  CursorContainer,\n  CursorFollow,\n  useCursor,\n  type CursorProviderProps,\n  type CursorProps,\n  type CursorContainerProps,\n  type CursorFollowProps,\n  type CursorFollowAlign,\n  type CursorFollowSide,\n  type CursorContextType,\n};\n",
      "type": "registry:ui",
      "target": "components/animate-ui/primitives/animate/cursor.tsx"
    }
  ],
  "type": "registry:ui"
}