{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cursor",
  "type": "registry:ui",
  "title": "Cursor",
  "description": "An animated cursor component that allows you to customize both the cursor and cursor follow elements with smooth animations.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/components/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 { cn } from '@/lib/utils';\n\ntype CursorContextType = {\n  cursorPos: { x: number; y: number };\n  isActive: boolean;\n  containerRef: React.RefObject<HTMLDivElement | null>;\n  cursorRef: React.RefObject<HTMLDivElement | null>;\n};\n\nconst CursorContext = React.createContext<CursorContextType | undefined>(\n  undefined,\n);\n\nconst useCursor = (): CursorContextType => {\n  const context = React.useContext(CursorContext);\n  if (!context) {\n    throw new Error('useCursor must be used within a CursorProvider');\n  }\n  return context;\n};\n\ntype CursorProviderProps = React.ComponentProps<'div'> & {\n  children: React.ReactNode;\n};\n\nfunction CursorProvider({ ref, children, ...props }: CursorProviderProps) {\n  const [cursorPos, setCursorPos] = React.useState({ x: 0, y: 0 });\n  const [isActive, setIsActive] = React.useState(false);\n  const containerRef = React.useRef<HTMLDivElement>(null);\n  const cursorRef = React.useRef<HTMLDivElement>(null);\n  React.useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);\n\n  React.useEffect(() => {\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 handleMouseMove = (e: MouseEvent) => {\n      const rect = parent.getBoundingClientRect();\n      setCursorPos({ x: e.clientX - rect.left, y: e.clientY - rect.top });\n      setIsActive(true);\n    };\n    const handleMouseLeave = () => setIsActive(false);\n\n    parent.addEventListener('mousemove', handleMouseMove);\n    parent.addEventListener('mouseleave', handleMouseLeave);\n\n    return () => {\n      parent.removeEventListener('mousemove', handleMouseMove);\n      parent.removeEventListener('mouseleave', handleMouseLeave);\n    };\n  }, []);\n\n  return (\n    <CursorContext.Provider\n      value={{ cursorPos, isActive, containerRef, cursorRef }}\n    >\n      <div ref={containerRef} data-slot=\"cursor-provider\" {...props}>\n        {children}\n      </div>\n    </CursorContext.Provider>\n  );\n}\n\ntype CursorProps = HTMLMotionProps<'div'> & {\n  children: React.ReactNode;\n};\n\nfunction Cursor({ ref, children, className, style, ...props }: CursorProps) {\n  const { cursorPos, isActive, containerRef, cursorRef } = 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 parentElement = containerRef.current?.parentElement;\n\n    if (parentElement && isActive) parentElement.style.cursor = 'none';\n\n    return () => {\n      if (parentElement) parentElement.style.cursor = 'default';\n    };\n  }, [containerRef, cursorPos, isActive]);\n\n  React.useEffect(() => {\n    x.set(cursorPos.x);\n    y.set(cursorPos.y);\n  }, [cursorPos, x, y]);\n\n  return (\n    <AnimatePresence>\n      {isActive && (\n        <motion.div\n          ref={cursorRef}\n          data-slot=\"cursor\"\n          className={cn(\n            'transform-[translate(-50%,-50%)] pointer-events-none z-[9999] absolute',\n            className,\n          )}\n          style={{ top: y, left: x, ...style }}\n          initial={{ scale: 0, opacity: 0 }}\n          animate={{ scale: 1, opacity: 1 }}\n          exit={{ scale: 0, opacity: 0 }}\n          {...props}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  );\n}\n\ntype Align =\n  | 'top'\n  | 'top-left'\n  | 'top-right'\n  | 'bottom'\n  | 'bottom-left'\n  | 'bottom-right'\n  | 'left'\n  | 'right'\n  | 'center';\n\ntype CursorFollowProps = HTMLMotionProps<'div'> & {\n  sideOffset?: number;\n  align?: Align;\n  transition?: SpringOptions;\n  children: React.ReactNode;\n};\n\nfunction CursorFollow({\n  ref,\n  sideOffset = 15,\n  align = 'bottom-right',\n  children,\n  className,\n  style,\n  transition = { stiffness: 500, damping: 50, bounce: 0 },\n  ...props\n}: CursorFollowProps) {\n  const { cursorPos, isActive, cursorRef } = 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 newOffset;\n\n    switch (align) {\n      case 'center':\n        newOffset = { x: width / 2, y: height / 2 };\n        break;\n      case 'top':\n        newOffset = { x: width / 2, y: height + sideOffset };\n        break;\n      case 'top-left':\n        newOffset = { x: width + sideOffset, y: height + sideOffset };\n        break;\n      case 'top-right':\n        newOffset = { x: -sideOffset, y: height + sideOffset };\n        break;\n      case 'bottom':\n        newOffset = { x: width / 2, y: -sideOffset };\n        break;\n      case 'bottom-left':\n        newOffset = { x: width + sideOffset, y: -sideOffset };\n        break;\n      case 'bottom-right':\n        newOffset = { x: -sideOffset, y: -sideOffset };\n        break;\n      case 'left':\n        newOffset = { x: width + sideOffset, y: height / 2 };\n        break;\n      case 'right':\n        newOffset = { x: -sideOffset, y: height / 2 };\n        break;\n      default:\n        newOffset = { x: 0, y: 0 };\n    }\n\n    return newOffset;\n  }, [align, sideOffset]);\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  return (\n    <AnimatePresence>\n      {isActive && (\n        <motion.div\n          ref={cursorFollowRef}\n          data-slot=\"cursor-follow\"\n          className={cn(\n            'transform-[translate(-50%,-50%)] pointer-events-none z-[9998] absolute',\n            className,\n          )}\n          style={{ top: springY, left: springX, ...style }}\n          initial={{ scale: 0, opacity: 0 }}\n          animate={{ scale: 1, opacity: 1 }}\n          exit={{ scale: 0, opacity: 0 }}\n          {...props}\n        >\n          {children}\n        </motion.div>\n      )}\n    </AnimatePresence>\n  );\n}\n\nexport {\n  CursorProvider,\n  Cursor,\n  CursorFollow,\n  useCursor,\n  type CursorContextType,\n  type CursorProviderProps,\n  type CursorProps,\n  type CursorFollowProps,\n};\n",
      "type": "registry:ui",
      "target": "components/animate-ui/components/cursor.tsx"
    }
  ]
}