{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cursor-follow",
  "type": "registry:block",
  "title": "Cursor follow",
  "description": "Cursor follow",
  "files": [
    {
      "path": "components/usages/cursorfollowusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport CursorFollow from \"@/registry/open-source/cursor-follow\";\n\nconst images = [\n\t{\n\t\tsrc: \"/itjustworks.jpg\",\n\t\tlabel: \"A beautiful forest probando el largo limite del texto\",\n\t},\n\t{\n\t\tsrc: \"/itjustworks.jpg\",\n\t\tlabel: \"Mountain at sunset\",\n\t},\n];\n\nconst CursorFollowDemo = () => {\n\treturn (\n\t\t<CursorFollow>\n\t\t\t<div className=\"flex flex-row items-center justify-center gap-8 py-8\">\n\t\t\t\t{images.map((img, i) => (\n\t\t\t\t\t<div key={i} className=\"flex flex-col items-center\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={img.src}\n\t\t\t\t\t\t\talt={img.label}\n\t\t\t\t\t\t\tdata-cursor-text={img.label}\n\t\t\t\t\t\t\tclassName=\"border-background h-48 w-48 rounded-xl object-cover transition-transform duration-200 hover:scale-105\"\n\t\t\t\t\t\t\tstyle={{ cursor: \"none\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</CursorFollow>\n\t);\n};\n\nexport default CursorFollowDemo;\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/cursorfollowusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport CursorFollow from \"@/registry/open-source/cursor-follow\";\n\nconst images = [\n\t{\n\t\tsrc: \"/itjustworks.jpg\",\n\t\tlabel: \"A beautiful forest probando el largo limite del texto\",\n\t},\n\t{\n\t\tsrc: \"/itjustworks.jpg\",\n\t\tlabel: \"Mountain at sunset\",\n\t},\n];\n\nconst CursorFollowDemo = () => {\n\treturn (\n\t\t<CursorFollow>\n\t\t\t<div className=\"flex flex-row items-center justify-center gap-8 py-8\">\n\t\t\t\t{images.map((img, i) => (\n\t\t\t\t\t<div key={i} className=\"flex flex-col items-center\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={img.src}\n\t\t\t\t\t\t\talt={img.label}\n\t\t\t\t\t\t\tdata-cursor-text={img.label}\n\t\t\t\t\t\t\tclassName=\"border-background h-48 w-48 rounded-xl object-cover transition-transform duration-200 hover:scale-105\"\n\t\t\t\t\t\t\tstyle={{ cursor: \"none\" }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</CursorFollow>\n\t);\n};\n\nexport default CursorFollowDemo;\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/cursor-follow.tsx",
      "content": "\"use client\";\n\nimport React, { useEffect, useRef, useState } from \"react\";\n\nimport { motion, useMotionValue, useSpring } from \"motion/react\";\n\nexport function useCursorPosition() {\n\tconst [position, setPosition] = useState({ x: 0, y: 0 });\n\n\tuseEffect(() => {\n\t\tconst handleMouseMove = (event: MouseEvent) => {\n\t\t\tsetPosition({ x: event.clientX, y: event.clientY });\n\t\t};\n\t\twindow.addEventListener(\"mousemove\", handleMouseMove);\n\t\treturn () => window.removeEventListener(\"mousemove\", handleMouseMove);\n\t}, []);\n\n\treturn position;\n}\n\ninterface CursorFollowProps {\n\tchildren: React.ReactNode;\n\tclassName?: string;\n}\n\nconst CIRCLE_SIZE = 16;\n\nconst CursorFollow: React.FC<CursorFollowProps> = ({\n\tchildren,\n\tclassName = \"\",\n}) => {\n\tconst { x: mouseX, y: mouseY } = useCursorPosition();\n\tconst [cursorText, setCursorText] = useState<string | null>(null);\n\tconst [pendingText, setPendingText] = useState<string | null>(null);\n\tconst [textWidth, setTextWidth] = useState<number>(0);\n\tconst measureRef = useRef<HTMLSpanElement>(null);\n\n\t// Motion values for smooth follow\n\tconst x = useMotionValue(0);\n\tconst y = useMotionValue(0);\n\tconst springX = useSpring(x, { stiffness: 350, damping: 40 });\n\tconst springY = useSpring(y, { stiffness: 350, damping: 40 });\n\n\t// Calculate bubble width and height\n\tconst bubbleWidth = cursorText ? Math.max(textWidth + 32, 40) : CIRCLE_SIZE;\n\tconst bubbleHeight = cursorText ? 40 : CIRCLE_SIZE;\n\n\t// Update target position on mouse move\n\tuseEffect(() => {\n\t\tx.set(mouseX - bubbleWidth / 2);\n\t\ty.set(mouseY - bubbleHeight / 2);\n\t}, [mouseX, mouseY, bubbleWidth, bubbleHeight, x, y]);\n\n\t// Pre-measure text width before showing bubble\n\tuseEffect(() => {\n\t\tif (pendingText && measureRef.current) {\n\t\t\tconst width = measureRef.current.offsetWidth;\n\t\t\tsetTextWidth(width);\n\t\t\tsetCursorText(pendingText);\n\t\t\tsetPendingText(null);\n\t\t}\n\t\tif (!pendingText && !cursorText) {\n\t\t\tsetTextWidth(0);\n\t\t}\n\t}, [pendingText, cursorText]);\n\n\t// Handlers for child hover\n\tconst handleMouseOver = (e: React.MouseEvent) => {\n\t\tconst target = e.target as HTMLElement;\n\t\tconst text = target.getAttribute(\"data-cursor-text\");\n\t\tif (text) {\n\t\t\tsetPendingText(text);\n\t\t}\n\t};\n\tconst handleMouseOut = () => {\n\t\tsetCursorText(null);\n\t\tsetPendingText(null);\n\t};\n\n\treturn (\n\t\t<div\n\t\t\tclassName={`relative h-full w-full ${className}`}\n\t\t\tonMouseOver={handleMouseOver}\n\t\t\tonMouseOut={handleMouseOut}\n\t\t>\n\t\t\t{children}\n\t\t\t<motion.div\n\t\t\t\tinitial={{ opacity: 0, scale: 0.7 }}\n\t\t\t\tanimate={{\n\t\t\t\t\topacity: 1,\n\t\t\t\t\tscale: 1,\n\t\t\t\t\ttop: !!cursorText ? \"-30px\" : 0,\n\t\t\t\t\ttransition: { duration: 0.32, ease: \"easeInOut\" },\n\t\t\t\t}}\n\t\t\t\texit={{ opacity: 0, scale: 0.7 }}\n\t\t\t\tclassName=\"pointer-events-none fixed z-50\"\n\t\t\t\tstyle={{\n\t\t\t\t\tleft: 0,\n\t\t\t\t\ttop: 0,\n\t\t\t\t\tx: springX,\n\t\t\t\t\ty: springY,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<motion.div\n\t\t\t\t\tlayout\n\t\t\t\t\ttransition={{ duration: 0.32, ease: \"easeInOut\" }}\n\t\t\t\t\tanimate={\n\t\t\t\t\t\tcursorText\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\twidth: bubbleWidth,\n\t\t\t\t\t\t\t\t\theight: 40,\n\t\t\t\t\t\t\t\t\tborderRadius: 20,\n\t\t\t\t\t\t\t\t\tbackground: \"var(--color-brand, #6366f1)\",\n\t\t\t\t\t\t\t\t\tcolor: \"#fff\",\n\t\t\t\t\t\t\t\t\tpaddingLeft: 16,\n\t\t\t\t\t\t\t\t\tpaddingRight: 16,\n\t\t\t\t\t\t\t\t\tminWidth: 40,\n\t\t\t\t\t\t\t\t\tminHeight: 32,\n\t\t\t\t\t\t\t\t\tscale: 1.1,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\twidth: CIRCLE_SIZE,\n\t\t\t\t\t\t\t\t\theight: CIRCLE_SIZE,\n\t\t\t\t\t\t\t\t\tborderRadius: 999,\n\t\t\t\t\t\t\t\t\tbackground: \"var(--color-brand, #6366f1)\",\n\t\t\t\t\t\t\t\t\tcolor: \"#fff\",\n\t\t\t\t\t\t\t\t\tpaddingLeft: 0,\n\t\t\t\t\t\t\t\t\tpaddingRight: 0,\n\t\t\t\t\t\t\t\t\tminWidth: CIRCLE_SIZE,\n\t\t\t\t\t\t\t\t\tminHeight: CIRCLE_SIZE,\n\t\t\t\t\t\t\t\t\tscale: 1,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tclassName=\"flex items-center justify-center text-xs font-medium shadow-lg\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tdisplay: \"flex\",\n\t\t\t\t\t\talignItems: \"center\",\n\t\t\t\t\t\tjustifyContent: \"center\",\n\t\t\t\t\t\tposition: \"relative\",\n\t\t\t\t\t\tzIndex: 1,\n\t\t\t\t\t\tboxShadow: \"0 2px 8px 0 rgba(0,0,0,0.10)\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{cursorText && (\n\t\t\t\t\t\t<motion.span\n\t\t\t\t\t\t\tinitial={{ opacity: 0, filter: \"blur(8px)\" }}\n\t\t\t\t\t\t\tanimate={{ opacity: 1, filter: \"blur(0px)\" }}\n\t\t\t\t\t\t\texit={{ opacity: 0, filter: \"blur(8px)\" }}\n\t\t\t\t\t\t\ttransition={{\n\t\t\t\t\t\t\t\tduration: 0.28,\n\t\t\t\t\t\t\t\tdelay: 0.1,\n\t\t\t\t\t\t\t\tease: \"easeInOut\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\t\t\t\ttextAlign: \"center\",\n\t\t\t\t\t\t\t\tcolor: \"#fff\",\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{cursorText}\n\t\t\t\t\t\t</motion.span>\n\t\t\t\t\t)}\n\t\t\t\t</motion.div>\n\t\t\t\t{/* Hidden span for pre-measuring text width */}\n\t\t\t\t{(pendingText || cursorText) && (\n\t\t\t\t\t<span\n\t\t\t\t\t\tref={measureRef}\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\t\t\tvisibility: \"hidden\",\n\t\t\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t\t\t\twhiteSpace: \"nowrap\",\n\t\t\t\t\t\t\tfontSize: \"0.75rem\",\n\t\t\t\t\t\t\tfontWeight: 500,\n\t\t\t\t\t\t\tpaddingLeft: 16,\n\t\t\t\t\t\t\tpaddingRight: 16,\n\t\t\t\t\t\t\tfontFamily: \"inherit\",\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t{pendingText || cursorText}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</motion.div>\n\t\t</div>\n\t);\n};\n\nexport default CursorFollow;\n",
      "type": "registry:ui"
    }
  ]
}