{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "native-follow-cursor-carbon",
"type": "registry:component",
"title": "Native Follow Cursor",
"description": "Label that smoothly follows the cursor with spring physics and customizable variants.",
"dependencies": [
"framer-motion",
"react"
],
"files": [
{
"path": "@uitripled/react-carbon/src/components/native/native-follow-cursor-carbon.tsx",
"content": "\"use client\";\n\nimport type React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n AnimatePresence,\n motion,\n useMotionValue,\n useSpring,\n} from \"framer-motion\";\nimport { useEffect, useRef, useState } from \"react\";\n\nexport interface NativeFollowCursorProps {\n /**\n * Name or text to display that follows the cursor\n * @deprecated Use children prop instead for custom content\n */\n name?: string;\n /**\n * Custom content to display (can be React nodes)\n */\n children?: React.ReactNode;\n /**\n * Size variant of the label\n * Default: \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * Visual variant\n * Default: \"default\"\n */\n variant?: \"default\" | \"glass\" | \"solid\" | \"minimal\";\n /**\n * Offset from cursor in pixels\n * Default: { x: 12, y: 12 }\n */\n offset?: { x: number; y: number };\n /**\n * Spring stiffness for smooth following\n * Default: 150\n */\n stiffness?: number;\n /**\n * Spring damping for smooth following\n * Default: 20\n */\n damping?: number;\n /**\n * Additional class names for the label\n */\n className?: string;\n /**\n * Show decorative dot indicator\n * Default: true (false when using custom children)\n */\n showDot?: boolean;\n}\n\nexport interface NativeFollowCursorAreaProps {\n /**\n * Content area to track cursor within\n */\n children: React.ReactNode;\n /**\n * Content that follows the cursor\n */\n cursorContent: React.ReactNode;\n /**\n * Size variant of the cursor label\n * Default: \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /**\n * Visual variant\n * Default: \"default\"\n */\n variant?: \"default\" | \"glass\" | \"solid\" | \"minimal\";\n /**\n * Offset from cursor in pixels\n * Default: { x: 12, y: 12 }\n */\n offset?: { x: number; y: number };\n /**\n * Spring stiffness for smooth following\n * Default: 150\n */\n stiffness?: number;\n /**\n * Spring damping for smooth following\n * Default: 20\n */\n damping?: number;\n /**\n * Additional class names for the area wrapper\n */\n className?: string;\n /**\n * Additional class names for the cursor label\n */\n cursorClassName?: string;\n /**\n * Show decorative dot indicator\n * Default: false\n */\n showDot?: boolean;\n}\n\nconst sizeVariants = {\n sm: \"text-xs px-2 py-1\",\n md: \"text-sm px-3 py-1.5\",\n lg: \"text-base px-4 py-2\",\n};\n\nconst dotSizeVariants = {\n sm: \"h-1.5 w-1.5\",\n md: \"h-2 w-2\",\n lg: \"h-2.5 w-2.5\",\n};\n\nexport function NativeFollowCursor({\n name,\n children,\n size = \"md\",\n variant = \"default\",\n offset = { x: 12, y: 12 },\n stiffness = 150,\n damping = 20,\n className,\n showDot,\n}: NativeFollowCursorProps) {\n const shouldShowDot = showDot ?? (children ? false : true);\n const content = children || name;\n\n const cursorX = useMotionValue(0);\n const cursorY = useMotionValue(0);\n\n const springConfig = { stiffness, damping };\n const x = useSpring(cursorX, springConfig);\n const y = useSpring(cursorY, springConfig);\n\n useEffect(() => {\n const handleMouseMove = (e: MouseEvent) => {\n cursorX.set(e.clientX + offset.x);\n cursorY.set(e.clientY + offset.y);\n };\n\n document.addEventListener(\"mousemove\", handleMouseMove);\n\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n };\n }, [cursorX, cursorY, offset]);\n\n const getVariantStyles = () => {\n switch (variant) {\n case \"glass\":\n return \"bg-background/80 backdrop-blur-md border border-border/50 shadow-lg\";\n case \"solid\":\n return \"bg-primary text-primary-foreground shadow-md\";\n case \"minimal\":\n return \"bg-background/95 border border-border shadow-sm\";\n case \"default\":\n default:\n return \"bg-popover text-popover-foreground border border-border shadow-lg\";\n }\n };\n\n return (\n \n {shouldShowDot && (\n \n )}\n {content}\n \n );\n}\n\nexport function NativeFollowCursorArea({\n children,\n cursorContent,\n size = \"md\",\n variant = \"default\",\n offset = { x: 12, y: 12 },\n stiffness = 150,\n damping = 20,\n className,\n cursorClassName,\n showDot = false,\n}: NativeFollowCursorAreaProps) {\n const [isVisible, setIsVisible] = useState(false);\n const containerRef = useRef(null);\n const cursorX = useMotionValue(0);\n const cursorY = useMotionValue(0);\n\n const springConfig = { stiffness, damping };\n const x = useSpring(cursorX, springConfig);\n const y = useSpring(cursorY, springConfig);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handleMouseMove = (e: MouseEvent) => {\n const rect = container.getBoundingClientRect();\n const isInside =\n e.clientX >= rect.left &&\n e.clientX <= rect.right &&\n e.clientY >= rect.top &&\n e.clientY <= rect.bottom;\n\n setIsVisible(isInside);\n\n if (isInside) {\n cursorX.set(e.clientX + offset.x);\n cursorY.set(e.clientY + offset.y);\n }\n };\n\n const handleMouseLeave = () => {\n setIsVisible(false);\n };\n\n document.addEventListener(\"mousemove\", handleMouseMove);\n container.addEventListener(\"mouseleave\", handleMouseLeave);\n\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n container.removeEventListener(\"mouseleave\", handleMouseLeave);\n };\n }, [cursorX, cursorY, offset]);\n\n const getVariantStyles = () => {\n switch (variant) {\n case \"glass\":\n return \"bg-background/80 backdrop-blur-md border border-border/50 shadow-lg\";\n case \"solid\":\n return \"bg-primary text-primary-foreground shadow-md\";\n case \"minimal\":\n return \"bg-background/95 border border-border shadow-sm\";\n case \"default\":\n default:\n return \"bg-popover text-popover-foreground border border-border shadow-lg\";\n }\n };\n\n return (\n \n {children}\n
\n {isVisible && (\n \n {showDot && (\n \n )}\n {cursorContent}
\n \n )}\n \n
\n );\n}\n",
"type": "registry:component",
"target": "components/uitripled/native-follow-cursor-carbon.tsx"
}
]
}