UNPKG

@yhattav/react-component-cursor

Version:

A lightweight, TypeScript-first React library for creating beautiful custom cursors with SSR support, smooth animations, and zero dependencies. Perfect for interactive websites, games, and creative applications.

1 lines 33.5 kB
{"version":3,"sources":["../src/hooks/useMousePosition.ts","../src/hooks/useSmoothAnimation.ts","../src/CustomCursor.tsx"],"names":["useMousePosition","id","containerRef","offsetX","offsetY","throttleMs","position","setPosition","useState","targetPosition","setTargetPosition","isInitialized","useRef","isVisible","updateTargetWithBoundsCheck","useCallback","globalPosition","adjustedPosition","rect","isInside","handleUpdate","useEffect","container","handleMouseLeave","isCleanedUp","subscriptionRef","CursorCoordinator","cursorCoordinator","error","_a","SMOOTHING_THRESHOLD","useSmoothAnimation","smoothFactor","calculateNewPosition","currentPosition","dx","dy","animate","animationFrameId","smoothing","prev","newPosition","isSSR","mediaQuery","prefersReducedMotion","ANIMATION_DURATION","ANIMATION_NAME","DEFAULT_Z_INDEX","DevIndicator","show","arePropsEqual","prevProps","nextProps","_b","_c","_d","_e","_f","prevStyle","nextStyle","prevStyleKeys","nextStyleKeys","key","generateCursorId","CustomCursor","enabled","children","className","style","zIndex","offset","smoothness","centered","showDevIndicator","onMove","onVisibilityChange","dataTestId","role","ariaLabel","cursorId","offsetValues","isMobile","isMobileDevice","mousePositionHook","portalContainer","setPortalContainer","getPortalContainerMemo","doc","safeDocument","existingContainer","e","styleSheetContent","centerTransform","styleId","existingStyle","styleSheet","handleMove","cursorPosition","handleVisibilityChange","cursorStyle","baseTransform","__spreadValues","globalStyleContent","createPortal","CustomCursor_default"],"mappings":"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","file":"index.mjs","sourcesContent":["import React, { useEffect, useState, useCallback, useRef } from 'react';\nimport { NullablePosition } from '../types.js';\n\nexport function useMousePosition(\n id: string,\n containerRef: React.RefObject<HTMLElement> | undefined,\n offsetX: number,\n offsetY: number,\n throttleMs = 0\n): {\n position: NullablePosition;\n setPosition: React.Dispatch<React.SetStateAction<NullablePosition>>;\n targetPosition: NullablePosition;\n isVisible: boolean;\n} {\n const [position, setPosition] = useState<NullablePosition>({ x: null, y: null });\n const [targetPosition, setTargetPosition] = useState<NullablePosition>({ x: null, y: null });\n const isInitialized = useRef(false);\n\n // Simple rule: visible if we have a valid position\n const isVisible = targetPosition.x !== null && targetPosition.y !== null;\n \n\n\n // Core function to check position against container bounds and update target\n const updateTargetWithBoundsCheck = useCallback((globalPosition: { x: number; y: number }) => {\n // Apply offsets\n const adjustedPosition = {\n x: globalPosition.x + offsetX,\n y: globalPosition.y + offsetY,\n };\n\n // Check container bounds if specified\n if (containerRef?.current) {\n const rect = containerRef.current.getBoundingClientRect();\n \n const isInside = \n globalPosition.x >= rect.left &&\n globalPosition.x <= rect.right &&\n globalPosition.y >= rect.top &&\n globalPosition.y <= rect.bottom;\n \n if (isInside) {\n setTargetPosition(adjustedPosition);\n } else {\n setTargetPosition({ x: null, y: null });\n }\n } else {\n setTargetPosition(adjustedPosition);\n }\n }, [containerRef, offsetX, offsetY]);\n\n // Handle updates from coordinator (mouse movement, scroll, resize) - unified callback\n const handleUpdate = useCallback((globalPosition: { x: number; y: number }) => {\n updateTargetWithBoundsCheck(globalPosition);\n }, [updateTargetWithBoundsCheck]);\n\n // Handle mouse leave - hide cursor\n useEffect(() => {\n if (!containerRef?.current) return;\n\n const container = containerRef.current;\n \n const handleMouseLeave = () => {\n setTargetPosition({ x: null, y: null });\n };\n\n container.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n container.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [containerRef]);\n\n // Subscribe to CursorCoordinator (dynamically loaded)\n useEffect(() => {\n let isCleanedUp = false;\n // Use an object to store unsubscribe so cleanup can access latest value\n const subscriptionRef = { unsubscribe: null as (() => void) | null };\n\n // Dynamic import of the entire coordinator chunk\n import('../utils/CursorCoordinator')\n .then(({ CursorCoordinator }) => {\n // Don't subscribe if component already unmounted\n if (isCleanedUp) return;\n \n const cursorCoordinator = CursorCoordinator.getInstance();\n \n subscriptionRef.unsubscribe = cursorCoordinator.subscribe({\n id,\n onPositionChange: handleUpdate,\n throttleMs,\n });\n })\n .catch((error) => {\n console.warn('Failed to load cursor coordinator:', error);\n });\n\n return () => {\n isCleanedUp = true;\n // Access the latest unsubscribe function via reference\n subscriptionRef.unsubscribe?.();\n };\n }, [id, throttleMs, handleUpdate]);\n\n // Initialize position when targetPosition first becomes available\n // After initialization, useSmoothAnimation handles all updates\n useEffect(() => {\n if (targetPosition.x !== null && targetPosition.y !== null && !isInitialized.current) {\n setPosition(targetPosition);\n isInitialized.current = true;\n }\n }, [targetPosition]);\n\n return { position, setPosition, targetPosition, isVisible };\n}\n","import { useEffect, useCallback } from 'react';\nimport { NullablePosition } from '../types.js';\nimport { isSSR } from '../utils/ssr';\n\nconst SMOOTHING_THRESHOLD = 0.1;\n\nexport function useSmoothAnimation(\n targetPosition: NullablePosition,\n smoothFactor: number,\n setPosition: React.Dispatch<React.SetStateAction<NullablePosition>>\n): void {\n // Memoize the smoothing calculation\n const calculateNewPosition = useCallback(\n (currentPosition: NullablePosition) => {\n if (\n currentPosition.x === null ||\n currentPosition.y === null ||\n targetPosition.x === null ||\n targetPosition.y === null\n ) {\n return currentPosition;\n }\n\n const dx = targetPosition.x - currentPosition.x;\n const dy = targetPosition.y - currentPosition.y;\n\n if (\n Math.abs(dx) < SMOOTHING_THRESHOLD &&\n Math.abs(dy) < SMOOTHING_THRESHOLD\n ) {\n return currentPosition;\n }\n\n return {\n x: currentPosition.x + dx / smoothFactor,\n y: currentPosition.y + dy / smoothFactor,\n };\n },\n [targetPosition.x, targetPosition.y, smoothFactor]\n );\n\n // Memoize the animation frame callback\n const animate = useCallback(() => {\n let animationFrameId: number;\n\n const smoothing = () => {\n setPosition((prev) => {\n const newPosition = calculateNewPosition(prev);\n\n // Only trigger update if position actually changed\n if (newPosition.x === prev.x && newPosition.y === prev.y) {\n return prev;\n }\n\n return newPosition;\n });\n\n animationFrameId = requestAnimationFrame(smoothing);\n };\n\n animationFrameId = requestAnimationFrame(smoothing);\n\n return () => {\n if (animationFrameId) {\n cancelAnimationFrame(animationFrameId);\n }\n };\n }, [calculateNewPosition, setPosition, targetPosition]);\n\n useEffect(() => {\n // Skip animation during SSR\n if (isSSR()) return;\n \n // Check for reduced motion preference (only in browser)\n const mediaQuery = typeof window !== 'undefined' && window.matchMedia ? \n window.matchMedia('(prefers-reduced-motion: reduce)') : null;\n const prefersReducedMotion = mediaQuery?.matches ?? false;\n \n // If smoothFactor is 1 or user prefers reduced motion, set position directly\n if (smoothFactor <= 1 || prefersReducedMotion) {\n setPosition(targetPosition);\n return;\n }\n\n return animate();\n }, [smoothFactor, targetPosition.x, targetPosition.y, animate, setPosition]);\n}\n","import * as React from 'react';\nimport { createPortal } from 'react-dom';\nimport { useMousePosition, useSmoothAnimation } from './hooks';\nimport {\n CursorPosition,\n CursorOffset,\n CursorMoveHandler,\n CursorVisibilityHandler,\n CursorVisibilityReason,\n} from './types.js';\nimport { validateProps } from './utils/validation';\nimport { isSSR, safeDocument, isMobileDevice } from './utils/ssr';\n\n// Clean props interface\nexport interface CustomCursorProps {\n // Core Configuration\n id?: string; // Auto-generated UUID if not provided\n enabled?: boolean;\n \n // Content & Styling\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n zIndex?: number;\n \n // Positioning & Movement\n offset?: CursorOffset | { x: number; y: number };\n smoothness?: number; // 0 = instant, higher = smoother\n containerRef?: React.RefObject<HTMLElement>;\n centered?: boolean; // Auto-center cursor on mouse position (default: true)\n \n // Behavior\n throttleMs?: number; // Performance throttling\n \n // Development\n showDevIndicator?: boolean; // Show red debug circle in development (default: true)\n \n // Event Handlers\n onMove?: CursorMoveHandler;\n onVisibilityChange?: CursorVisibilityHandler;\n \n // Testing & Accessibility\n 'data-testid'?: string;\n role?: string;\n 'aria-label'?: string;\n}\n\nconst ANIMATION_DURATION = '0.3s';\nconst ANIMATION_NAME = 'cursorFadeIn';\nconst DEFAULT_Z_INDEX = 9999;\n\nconst DevIndicator: React.FC<{\n position: { x: number | null; y: number | null };\n show: boolean;\n}> = ({ position, show }) => {\n if (process.env.NODE_ENV !== 'development' || !show) return null;\n\n return (\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n transform: `translate(${position.x ?? 0}px, ${position.y ?? 0}px)`,\n width: '50px',\n height: '50px',\n border: '2px solid red',\n borderRadius: '50%',\n pointerEvents: 'none',\n zIndex: 10000,\n opacity: 0.5,\n // Center the circle around the cursor\n marginLeft: '-25px',\n marginTop: '-25px',\n }}\n />\n );\n};\n\n// Custom comparison function for React.memo\nconst arePropsEqual = (\n prevProps: CustomCursorProps,\n nextProps: CustomCursorProps\n): boolean => {\n // Check primitive props\n if (\n prevProps.id !== nextProps.id ||\n prevProps.enabled !== nextProps.enabled ||\n prevProps.className !== nextProps.className ||\n prevProps.zIndex !== nextProps.zIndex ||\n prevProps.smoothness !== nextProps.smoothness ||\n prevProps.centered !== nextProps.centered ||\n prevProps.throttleMs !== nextProps.throttleMs ||\n prevProps.showDevIndicator !== nextProps.showDevIndicator\n ) {\n return false;\n }\n\n // Check offset object\n if (\n prevProps.offset?.x !== nextProps.offset?.x ||\n prevProps.offset?.y !== nextProps.offset?.y\n ) {\n return false;\n }\n\n // Check containerRef\n if (prevProps.containerRef?.current !== nextProps.containerRef?.current) {\n return false;\n }\n\n // Check style object (shallow comparison)\n const prevStyle = prevProps.style || {};\n const nextStyle = nextProps.style || {};\n const prevStyleKeys = Object.keys(prevStyle);\n const nextStyleKeys = Object.keys(nextStyle);\n \n if (prevStyleKeys.length !== nextStyleKeys.length) {\n return false;\n }\n \n for (const key of prevStyleKeys) {\n if (prevStyle[key as keyof React.CSSProperties] !== nextStyle[key as keyof React.CSSProperties]) {\n return false;\n }\n }\n\n // Function props are assumed to be stable (should be wrapped in useCallback by consumers)\n // We'll do reference equality check\n if (\n prevProps.onMove !== nextProps.onMove ||\n prevProps.onVisibilityChange !== nextProps.onVisibilityChange\n ) {\n return false;\n }\n\n // Children comparison (basic reference check)\n if (prevProps.children !== nextProps.children) {\n return false;\n }\n\n return true;\n};\n\n// Generate a unique ID for each cursor instance\nconst generateCursorId = (): string => {\n return `cursor-${Math.random().toString(36).substr(2, 9)}-${Date.now().toString(36)}`;\n};\n\nexport const CustomCursor: React.FC<CustomCursorProps> = React.memo(\n ({\n id,\n enabled = true,\n children,\n className = '',\n style = {},\n zIndex = DEFAULT_Z_INDEX,\n offset = { x: 0, y: 0 },\n smoothness = 1,\n containerRef,\n centered = true,\n throttleMs = 0,\n showDevIndicator = true,\n onMove,\n onVisibilityChange,\n 'data-testid': dataTestId,\n role,\n 'aria-label': ariaLabel,\n }) => {\n // Generate unique ID if not provided\n const cursorId = React.useMemo(() => id || generateCursorId(), [id]);\n\n // Validate props in development mode (always called first)\n validateProps({\n id: cursorId,\n enabled,\n children,\n className,\n style,\n zIndex,\n offset,\n smoothness,\n containerRef,\n centered,\n throttleMs,\n showDevIndicator,\n onMove,\n onVisibilityChange,\n });\n\n // Memoize offset values to avoid recreating object (always called)\n const offsetValues = React.useMemo(() => ({\n x: typeof offset === 'object' ? offset.x : 0,\n y: typeof offset === 'object' ? offset.y : 0,\n }), [offset]);\n\n // Check for mobile device early (after hooks are called)\n const isMobile = React.useMemo(() => isMobileDevice(), []);\n\n // Always call hooks, even if we'll return null (Rules of Hooks)\n const mousePositionHook = useMousePosition(cursorId, containerRef, offsetValues.x, offsetValues.y, throttleMs);\n const { position, setPosition, targetPosition, isVisible } = mousePositionHook;\n useSmoothAnimation(targetPosition, smoothness, setPosition);\n\n const [portalContainer, setPortalContainer] =\n React.useState<HTMLElement | null>(null);\n\n // Memoize portal container creation\n const getPortalContainerMemo = React.useCallback(() => {\n const doc = safeDocument();\n if (!doc) return null;\n\n const existingContainer = doc.getElementById('cursor-container');\n if (existingContainer) {\n // Update existing container's z-index to match current props\n existingContainer.style.zIndex = zIndex.toString();\n return existingContainer;\n }\n\n const container = doc.createElement('div');\n container.id = 'cursor-container';\n container.style.position = 'fixed';\n container.style.top = '0';\n container.style.left = '0';\n container.style.pointerEvents = 'none';\n container.style.zIndex = zIndex.toString(); // Use user's zIndex instead of DEFAULT_Z_INDEX\n doc.body.appendChild(container);\n return container;\n }, [zIndex]); // Add zIndex to dependencies\n\n React.useEffect(() => {\n setPortalContainer(getPortalContainerMemo());\n return () => {\n const doc = safeDocument();\n if (!doc) return;\n \n const container = doc.getElementById('cursor-container');\n if (container && container.children.length === 0) {\n try {\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n } catch (e) {\n // Ignore cleanup errors in tests\n if (process.env.NODE_ENV !== 'test') {\n console.warn('Portal container cleanup failed:', e);\n }\n }\n }\n };\n }, [getPortalContainerMemo]);\n\n // Memoize style sheet content with reduced motion support\n const styleSheetContent = React.useMemo(() => {\n const centerTransform = centered ? ' translate(-50%, -50%)' : '';\n return `\n @keyframes ${ANIMATION_NAME} {\n from {\n opacity: 0;\n transform: translate(var(--cursor-x), var(--cursor-y))${centerTransform} scale(0.8);\n }\n to {\n opacity: 1;\n transform: translate(var(--cursor-x), var(--cursor-y))${centerTransform} scale(1);\n }\n }\n \n @media (prefers-reduced-motion: reduce) {\n @keyframes ${ANIMATION_NAME} {\n from {\n opacity: 0;\n transform: translate(var(--cursor-x), var(--cursor-y))${centerTransform} scale(1);\n }\n to {\n opacity: 1;\n transform: translate(var(--cursor-x), var(--cursor-y))${centerTransform} scale(1);\n }\n }\n }\n `;\n }, [centered]);\n\n React.useEffect(() => {\n const doc = safeDocument();\n if (!doc) return;\n \n const styleId = `cursor-style-${cursorId}`;\n const existingStyle = doc.getElementById(styleId);\n if (existingStyle) {\n existingStyle.remove();\n }\n\n const styleSheet = doc.createElement('style');\n styleSheet.id = styleId;\n styleSheet.textContent = styleSheetContent;\n doc.head.appendChild(styleSheet);\n\n return () => {\n const style = doc.getElementById(styleId);\n if (style) {\n try {\n style.remove();\n } catch (e) {\n // Ignore cleanup errors in tests\n if (process.env.NODE_ENV !== 'test') {\n console.warn('Style cleanup failed:', e);\n }\n }\n }\n };\n }, [cursorId, styleSheetContent]);\n\n // Memoize move callback to avoid recreation\n const handleMove = React.useCallback(() => {\n if (position.x !== null && position.y !== null && typeof onMove === 'function') {\n const cursorPosition: CursorPosition = { x: position.x, y: position.y };\n onMove(cursorPosition);\n }\n }, [position.x, position.y, onMove]);\n\n // Handle move callback\n React.useEffect(() => {\n handleMove();\n }, [handleMove]);\n\n // Memoize visibility callback to avoid recreation\n const handleVisibilityChange = React.useCallback(() => {\n if (typeof onVisibilityChange === 'function') {\n const actuallyVisible = enabled && isVisible;\n const reason: CursorVisibilityReason = !enabled ? 'disabled' : 'container';\n onVisibilityChange(actuallyVisible, reason);\n }\n }, [enabled, isVisible, onVisibilityChange]);\n\n // Handle visibility callback\n React.useEffect(() => {\n handleVisibilityChange();\n }, [handleVisibilityChange]);\n\n // Handle mobile-specific visibility callback\n React.useEffect(() => {\n if (isMobile && typeof onVisibilityChange === 'function') {\n onVisibilityChange(false, 'touch');\n }\n }, [isMobile, onVisibilityChange]);\n\n // Early return for mobile devices - no cursor rendering\n if (isMobile) {\n return null;\n }\n\n const cursorStyle = React.useMemo(\n () => {\n const baseTransform = `translate(${position.x ?? 0}px, ${position.y ?? 0}px)`;\n const centerTransform = centered ? ' translate(-50%, -50%)' : '';\n \n return {\n position: 'fixed',\n top: 0,\n left: 0,\n transform: baseTransform + centerTransform,\n pointerEvents: 'none',\n zIndex,\n opacity: 1,\n visibility: 'visible',\n animation: `${ANIMATION_NAME} ${ANIMATION_DURATION} ease-out`,\n '--cursor-x': `${position.x ?? 0}px`,\n '--cursor-y': `${position.y ?? 0}px`,\n ...style,\n } as React.CSSProperties;\n },\n [position.x, position.y, zIndex, centered, style]\n );\n\n // Memoize global style content\n const globalStyleContent = React.useMemo(() => `\n #cursor-container {\n pointer-events: none !important;\n }\n `, []);\n\n // Determine if we should render anything (SSR safety + enabled check)\n const shouldRender = !isSSR() && \n enabled && \n isVisible && \n position.x !== null && \n position.y !== null && \n portalContainer;\n\n if (!shouldRender) return null;\n\n return (\n <>\n <style id={`cursor-style-global-${cursorId}`}>{globalStyleContent}</style>\n {createPortal(\n <React.Fragment key={`cursor-${cursorId}`}>\n <div\n id={`custom-cursor-${cursorId}`}\n style={cursorStyle}\n className={className}\n aria-hidden=\"true\"\n data-testid={dataTestId}\n role={role}\n aria-label={ariaLabel}\n >\n {children}\n </div>\n <DevIndicator position={position} show={showDevIndicator} />\n </React.Fragment>,\n portalContainer\n )}\n </>\n );\n },\n arePropsEqual\n);\n\nCustomCursor.displayName = 'CustomCursor';\n\nexport default CustomCursor;\n"]}