@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 • 35.1 kB
Source Map (JSON)
{"version":3,"sources":["../src/CustomCursor.tsx","../src/hooks/useMousePosition.ts","../src/hooks/useSmoothAnimation.ts","../src/utils/validation.ts"],"sourcesContent":["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","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 type { CustomCursorProps } from '../CustomCursor';\n\n/**\n * Development-only validation that gets tree-shaken in production\n * Validates CustomCursor props and provides helpful error messages\n */\nexport function validateProps(props: CustomCursorProps): void {\n // The entire function body gets removed in production builds\n if (process.env.NODE_ENV !== 'development') return;\n\n const { \n id, \n smoothness, \n throttleMs, \n zIndex, \n offset, \n containerRef,\n centered,\n showDevIndicator,\n onMove,\n onVisibilityChange,\n } = props;\n\n // Validate id (empty strings are allowed since we auto-generate UUIDs)\n if (id !== undefined && typeof id !== 'string') {\n console.error(\n `CustomCursor: 'id' must be a string. Received: ${id} (${typeof id}). Note: empty strings are allowed and will auto-generate a UUID.`\n );\n }\n\n // Validate smoothness\n if (smoothness !== undefined) {\n if (typeof smoothness !== 'number' || isNaN(smoothness)) {\n console.error(\n `CustomCursor: 'smoothness' must be a number. Received: ${smoothness} (${typeof smoothness})`\n );\n } else if (smoothness < 0) {\n console.error(\n `CustomCursor: 'smoothness' must be non-negative. Received: ${smoothness}. Use 1 for no smoothing, higher values for more smoothing.`\n );\n } else if (smoothness > 20) {\n console.warn(\n `CustomCursor: 'smoothness' value ${smoothness} is very high. Values above 20 may cause poor performance. Consider using a lower value.`\n );\n }\n }\n\n // Validate throttleMs\n if (throttleMs !== undefined) {\n if (typeof throttleMs !== 'number' || isNaN(throttleMs)) {\n console.error(\n `CustomCursor: 'throttleMs' must be a number. Received: ${throttleMs} (${typeof throttleMs})`\n );\n } else if (throttleMs < 0) {\n console.error(\n `CustomCursor: 'throttleMs' must be non-negative. Received: ${throttleMs}. Use 0 for no throttling.`\n );\n } else if (throttleMs > 100) {\n console.warn(\n `CustomCursor: 'throttleMs' value ${throttleMs} is quite high. This may make the cursor feel sluggish. Consider using a lower value (0-50ms).`\n );\n }\n }\n\n // Validate zIndex\n if (zIndex !== undefined) {\n if (typeof zIndex !== 'number' || isNaN(zIndex)) {\n console.error(\n `CustomCursor: 'zIndex' must be a number. Received: ${zIndex} (${typeof zIndex})`\n );\n } else if (!Number.isInteger(zIndex)) {\n console.warn(\n `CustomCursor: 'zIndex' should be an integer. Received: ${zIndex}`\n );\n }\n }\n\n // Validate offset\n if (offset !== undefined) {\n if (typeof offset !== 'object' || offset === null) {\n console.error(\n `CustomCursor: 'offset' must be an object with x and y properties. Received: ${offset}`\n );\n } else {\n const { x, y } = offset as { x?: unknown; y?: unknown };\n if (typeof x !== 'number' || isNaN(x)) {\n console.error(\n `CustomCursor: 'offset.x' must be a number. Received: ${x} (${typeof x})`\n );\n }\n if (typeof y !== 'number' || isNaN(y)) {\n console.error(\n `CustomCursor: 'offset.y' must be a number. Received: ${y} (${typeof y})`\n );\n }\n }\n }\n\n // Validate containerRef\n if (containerRef !== undefined) {\n if (typeof containerRef !== 'object' || containerRef === null || !('current' in containerRef)) {\n console.error(\n `CustomCursor: 'containerRef' must be a React ref object (created with useRef). Received: ${containerRef}`\n );\n }\n }\n\n // Validate centered\n if (centered !== undefined && typeof centered !== 'boolean') {\n console.error(\n `CustomCursor: 'centered' must be a boolean. Received: ${centered} (${typeof centered})`\n );\n }\n\n // Validate showDevIndicator\n if (showDevIndicator !== undefined && typeof showDevIndicator !== 'boolean') {\n console.error(\n `CustomCursor: 'showDevIndicator' must be a boolean. Received: ${showDevIndicator} (${typeof showDevIndicator})`\n );\n }\n\n // Validate callbacks\n if (onMove !== undefined && typeof onMove !== 'function') {\n console.error(\n `CustomCursor: 'onMove' must be a function. Received: ${typeof onMove}`\n );\n }\n\n if (onVisibilityChange !== undefined && typeof onVisibilityChange !== 'function') {\n console.error(\n `CustomCursor: 'onVisibilityChange' must be a function. Received: ${typeof onVisibilityChange}`\n );\n }\n} "],"mappings":";;;;;;;;;;;AAAA,YAAYA,YAAW;AACvB,SAAS,oBAAoB;;;ACD7B,SAAgB,WAAW,UAAU,aAAa,cAAc;AAGzD,SAAS,iBACd,IACA,cACA,SACA,SACA,aAAa,GAMb;AACA,QAAM,CAAC,UAAU,WAAW,IAAI,SAA2B,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;AAC/E,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAA2B,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;AAC3F,QAAM,gBAAgB,OAAO,KAAK;AAGlC,QAAM,YAAY,eAAe,MAAM,QAAQ,eAAe,MAAM;AAKpE,QAAM,8BAA8B,YAAY,CAAC,mBAA6C;AAE5F,UAAM,mBAAmB;AAAA,MACvB,GAAG,eAAe,IAAI;AAAA,MACtB,GAAG,eAAe,IAAI;AAAA,IACxB;AAGA,QAAI,6CAAc,SAAS;AACzB,YAAM,OAAO,aAAa,QAAQ,sBAAsB;AAExD,YAAM,WACJ,eAAe,KAAK,KAAK,QACzB,eAAe,KAAK,KAAK,SACzB,eAAe,KAAK,KAAK,OACzB,eAAe,KAAK,KAAK;AAE3B,UAAI,UAAU;AACZ,0BAAkB,gBAAgB;AAAA,MACpC,OAAO;AACL,0BAAkB,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;AAAA,MACxC;AAAA,IACF,OAAO;AACL,wBAAkB,gBAAgB;AAAA,IACpC;AAAA,EACF,GAAG,CAAC,cAAc,SAAS,OAAO,CAAC;AAGnC,QAAM,eAAe,YAAY,CAAC,mBAA6C;AAC7E,gCAA4B,cAAc;AAAA,EAC5C,GAAG,CAAC,2BAA2B,CAAC;AAGhC,YAAU,MAAM;AACd,QAAI,EAAC,6CAAc,SAAS;AAE5B,UAAM,YAAY,aAAa;AAE/B,UAAM,mBAAmB,MAAM;AAC7B,wBAAkB,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;AAAA,IACxC;AAEA,cAAU,iBAAiB,cAAc,gBAAgB;AAEzD,WAAO,MAAM;AACX,gBAAU,oBAAoB,cAAc,gBAAgB;AAAA,IAC9D;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AAGjB,YAAU,MAAM;AACd,QAAI,cAAc;AAElB,UAAM,kBAAkB,EAAE,aAAa,KAA4B;AAGnE,WAAO,sCAA4B,EAChC,KAAK,CAAC,EAAE,kBAAkB,MAAM;AAE/B,UAAI,YAAa;AAEjB,YAAM,oBAAoB,kBAAkB,YAAY;AAExD,sBAAgB,cAAc,kBAAkB,UAAU;AAAA,QACxD;AAAA,QACA,kBAAkB;AAAA,QAClB;AAAA,MACF,CAAC;AAAA,IACH,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,cAAQ,KAAK,sCAAsC,KAAK;AAAA,IAC1D,CAAC;AAEH,WAAO,MAAM;AAlGjB;AAmGM,oBAAc;AAEd,4BAAgB,gBAAhB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,IAAI,YAAY,YAAY,CAAC;AAIjC,YAAU,MAAM;AACd,QAAI,eAAe,MAAM,QAAQ,eAAe,MAAM,QAAQ,CAAC,cAAc,SAAS;AACpF,kBAAY,cAAc;AAC1B,oBAAc,UAAU;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,SAAO,EAAE,UAAU,aAAa,gBAAgB,UAAU;AAC5D;;;ACnHA,SAAS,aAAAC,YAAW,eAAAC,oBAAmB;AAIvC,IAAM,sBAAsB;AAErB,SAAS,mBACd,gBACA,cACA,aACM;AAEN,QAAM,uBAAuBC;AAAA,IAC3B,CAAC,oBAAsC;AACrC,UACE,gBAAgB,MAAM,QACtB,gBAAgB,MAAM,QACtB,eAAe,MAAM,QACrB,eAAe,MAAM,MACrB;AACA,eAAO;AAAA,MACT;AAEA,YAAM,KAAK,eAAe,IAAI,gBAAgB;AAC9C,YAAM,KAAK,eAAe,IAAI,gBAAgB;AAE9C,UACE,KAAK,IAAI,EAAE,IAAI,uBACf,KAAK,IAAI,EAAE,IAAI,qBACf;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,QACL,GAAG,gBAAgB,IAAI,KAAK;AAAA,QAC5B,GAAG,gBAAgB,IAAI,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,CAAC,eAAe,GAAG,eAAe,GAAG,YAAY;AAAA,EACnD;AAGA,QAAM,UAAUA,aAAY,MAAM;AAChC,QAAI;AAEJ,UAAM,YAAY,MAAM;AACtB,kBAAY,CAAC,SAAS;AACpB,cAAM,cAAc,qBAAqB,IAAI;AAG7C,YAAI,YAAY,MAAM,KAAK,KAAK,YAAY,MAAM,KAAK,GAAG;AACxD,iBAAO;AAAA,QACT;AAEA,eAAO;AAAA,MACT,CAAC;AAED,yBAAmB,sBAAsB,SAAS;AAAA,IACpD;AAEA,uBAAmB,sBAAsB,SAAS;AAElD,WAAO,MAAM;AACX,UAAI,kBAAkB;AACpB,6BAAqB,gBAAgB;AAAA,MACvC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,sBAAsB,aAAa,cAAc,CAAC;AAEtD,EAAAC,WAAU,MAAM;AArElB;AAuEI,QAAI,MAAM,EAAG;AAGb,UAAM,aAAa,OAAO,WAAW,eAAe,OAAO,aACzC,OAAO,WAAW,kCAAkC,IAAI;AAC1E,UAAM,wBAAuB,8CAAY,YAAZ,YAAuB;AAGpD,QAAI,gBAAgB,KAAK,sBAAsB;AAC7C,kBAAY,cAAc;AAC1B;AAAA,IACF;AAEA,WAAO,QAAQ;AAAA,EACjB,GAAG,CAAC,cAAc,eAAe,GAAG,eAAe,GAAG,SAAS,WAAW,CAAC;AAC7E;;;AChFO,SAAS,cAAc,OAAgC;AAE5D,MAAI,MAAwC;AAE5C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,MAAI,OAAO,UAAa,OAAO,OAAO,UAAU;AAC9C,YAAQ;AAAA,MACN,kDAAkD,EAAE,KAAK,OAAO,EAAE;AAAA,IACpE;AAAA,EACF;AAGA,MAAI,eAAe,QAAW;AAC5B,QAAI,OAAO,eAAe,YAAY,MAAM,UAAU,GAAG;AACvD,cAAQ;AAAA,QACN,0DAA0D,UAAU,KAAK,OAAO,UAAU;AAAA,MAC5F;AAAA,IACF,WAAW,aAAa,GAAG;AACzB,cAAQ;AAAA,QACN,8DAA8D,UAAU;AAAA,MAC1E;AAAA,IACF,WAAW,aAAa,IAAI;AAC1B,cAAQ;AAAA,QACN,oCAAoC,UAAU;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAGA,MAAI,eAAe,QAAW;AAC5B,QAAI,OAAO,eAAe,YAAY,MAAM,UAAU,GAAG;AACvD,cAAQ;AAAA,QACN,0DAA0D,UAAU,KAAK,OAAO,UAAU;AAAA,MAC5F;AAAA,IACF,WAAW,aAAa,GAAG;AACzB,cAAQ;AAAA,QACN,8DAA8D,UAAU;AAAA,MAC1E;AAAA,IACF,WAAW,aAAa,KAAK;AAC3B,cAAQ;AAAA,QACN,oCAAoC,UAAU;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAGA,MAAI,WAAW,QAAW;AACxB,QAAI,OAAO,WAAW,YAAY,MAAM,MAAM,GAAG;AAC/C,cAAQ;AAAA,QACN,sDAAsD,MAAM,KAAK,OAAO,MAAM;AAAA,MAChF;AAAA,IACF,WAAW,CAAC,OAAO,UAAU,MAAM,GAAG;AACpC,cAAQ;AAAA,QACN,0DAA0D,MAAM;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AAGA,MAAI,WAAW,QAAW;AACxB,QAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,cAAQ;AAAA,QACN,+EAA+E,MAAM;AAAA,MACvF;AAAA,IACF,OAAO;AACL,YAAM,EAAE,GAAG,EAAE,IAAI;AACjB,UAAI,OAAO,MAAM,YAAY,MAAM,CAAC,GAAG;AACrC,gBAAQ;AAAA,UACN,wDAAwD,CAAC,KAAK,OAAO,CAAC;AAAA,QACxE;AAAA,MACF;AACA,UAAI,OAAO,MAAM,YAAY,MAAM,CAAC,GAAG;AACrC,gBAAQ;AAAA,UACN,wDAAwD,CAAC,KAAK,OAAO,CAAC;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,iBAAiB,QAAW;AAC9B,QAAI,OAAO,iBAAiB,YAAY,iBAAiB,QAAQ,EAAE,aAAa,eAAe;AAC7F,cAAQ;AAAA,QACN,4FAA4F,YAAY;AAAA,MAC1G;AAAA,IACF;AAAA,EACF;AAGA,MAAI,aAAa,UAAa,OAAO,aAAa,WAAW;AAC3D,YAAQ;AAAA,MACN,yDAAyD,QAAQ,KAAK,OAAO,QAAQ;AAAA,IACvF;AAAA,EACF;AAGA,MAAI,qBAAqB,UAAa,OAAO,qBAAqB,WAAW;AAC3E,YAAQ;AAAA,MACN,iEAAiE,gBAAgB,KAAK,OAAO,gBAAgB;AAAA,IAC/G;AAAA,EACF;AAGA,MAAI,WAAW,UAAa,OAAO,WAAW,YAAY;AACxD,YAAQ;AAAA,MACN,wDAAwD,OAAO,MAAM;AAAA,IACvE;AAAA,EACF;AAEA,MAAI,uBAAuB,UAAa,OAAO,uBAAuB,YAAY;AAChF,YAAQ;AAAA,MACN,oEAAoE,OAAO,kBAAkB;AAAA,IAC/F;AAAA,EACF;AACF;;;AHtFA,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAExB,IAAM,eAGD,CAAC,EAAE,UAAU,KAAK,MAAM;AAtD7B;AAuDE,MAA8C,CAAC,KAAM,QAAO;AAE5D,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,UAAU;AAAA,QACV,KAAK;AAAA,QACL,MAAM;AAAA,QACN,WAAW,cAAa,cAAS,MAAT,YAAc,CAAC,QAAO,cAAS,MAAT,YAAc,CAAC;AAAA,QAC7D,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,SAAS;AAAA;AAAA,QAET,YAAY;AAAA,QACZ,WAAW;AAAA,MACb;AAAA;AAAA,EACF;AAEJ;AAGA,IAAM,gBAAgB,CACpB,WACA,cACY;AAnFd;AAqFE,MACE,UAAU,OAAO,UAAU,MAC3B,UAAU,YAAY,UAAU,WAChC,UAAU,cAAc,UAAU,aAClC,UAAU,WAAW,UAAU,UAC/B,UAAU,eAAe,UAAU,cACnC,UAAU,aAAa,UAAU,YACjC,UAAU,eAAe,UAAU,cACnC,UAAU,qBAAqB,UAAU,kBACzC;AACA,WAAO;AAAA,EACT;AAGA,QACE,eAAU,WAAV,mBAAkB,SAAM,eAAU,WAAV,mBAAkB,QAC1C,eAAU,WAAV,mBAAkB,SAAM,eAAU,WAAV,mBAAkB,IAC1C;AACA,WAAO;AAAA,EACT;AAGA,QAAI,eAAU,iBAAV,mBAAwB,eAAY,eAAU,iBAAV,mBAAwB,UAAS;AACvE,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,UAAU,SAAS,CAAC;AACtC,QAAM,YAAY,UAAU,SAAS,CAAC;AACtC,QAAM,gBAAgB,OAAO,KAAK,SAAS;AAC3C,QAAM,gBAAgB,OAAO,KAAK,SAAS;AAE3C,MAAI,cAAc,WAAW,cAAc,QAAQ;AACjD,WAAO;AAAA,EACT;AAEA,aAAW,OAAO,eAAe;AAC/B,QAAI,UAAU,GAAgC,MAAM,UAAU,GAAgC,GAAG;AAC/F,aAAO;AAAA,IACT;AAAA,EACF;AAIA,MACE,UAAU,WAAW,UAAU,UAC/B,UAAU,uBAAuB,UAAU,oBAC3C;AACA,WAAO;AAAA,EACT;AAGA,MAAI,UAAU,aAAa,UAAU,UAAU;AAC7C,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAGA,IAAM,mBAAmB,MAAc;AACrC,SAAO,UAAU,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;AACrF;AAEO,IAAM,eAAkD;AAAA,EAC7D,CAAC;AAAA,IACC;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,YAAY;AAAA,IACZ,QAAQ,CAAC;AAAA,IACT,SAAS;AAAA,IACT,SAAS,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,IACtB,aAAa;AAAA,IACb;AAAA,IACA,WAAW;AAAA,IACX,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA,cAAc;AAAA,EAChB,MAAM;AAEJ,UAAM,WAAiB,eAAQ,MAAM,MAAM,iBAAiB,GAAG,CAAC,EAAE,CAAC;AAGnE,kBAAc;AAAA,MACZ,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,eAAqB,eAAQ,OAAO;AAAA,MACxC,GAAG,OAAO,WAAW,WAAW,OAAO,IAAI;AAAA,MAC3C,GAAG,OAAO,WAAW,WAAW,OAAO,IAAI;AAAA,IAC7C,IAAI,CAAC,MAAM,CAAC;AAGZ,UAAM,WAAiB,eAAQ,MAAM,eAAe,GAAG,CAAC,CAAC;AAGzD,UAAM,oBAAoB,iBAAiB,UAAU,cAAc,aAAa,GAAG,aAAa,GAAG,UAAU;AAC7G,UAAM,EAAE,UAAU,aAAa,gBAAgB,UAAU,IAAI;AAC7D,uBAAmB,gBAAgB,YAAY,WAAW;AAE1D,UAAM,CAAC,iBAAiB,kBAAkB,IAClC,gBAA6B,IAAI;AAGzC,UAAM,yBAA+B,mBAAY,MAAM;AACrD,YAAM,MAAM,aAAa;AACzB,UAAI,CAAC,IAAK,QAAO;AAEjB,YAAM,oBAAoB,IAAI,eAAe,kBAAkB;AAC/D,UAAI,mBAAmB;AAErB,0BAAkB,MAAM,SAAS,OAAO,SAAS;AACjD,eAAO;AAAA,MACT;AAEA,YAAM,YAAY,IAAI,cAAc,KAAK;AACzC,gBAAU,KAAK;AACf,gBAAU,MAAM,WAAW;AAC3B,gBAAU,MAAM,MAAM;AACtB,gBAAU,MAAM,OAAO;AACvB,gBAAU,MAAM,gBAAgB;AAChC,gBAAU,MAAM,SAAS,OAAO,SAAS;AACzC,UAAI,KAAK,YAAY,SAAS;AAC9B,aAAO;AAAA,IACT,GAAG,CAAC,MAAM,CAAC;AAEX,IAAM,iBAAU,MAAM;AACpB,yBAAmB,uBAAuB,CAAC;AAC3C,aAAO,MAAM;AACX,cAAM,MAAM,aAAa;AACzB,YAAI,CAAC,IAAK;AAEV,cAAM,YAAY,IAAI,eAAe,kBAAkB;AACvD,YAAI,aAAa,UAAU,SAAS,WAAW,GAAG;AAChD,cAAI;AACF,gBAAI,UAAU,YAAY;AACxB,wBAAU,WAAW,YAAY,SAAS;AAAA,YAC5C;AAAA,UACF,SAAS,GAAG;AAEV,gBAAI,MAAiC;AACnC,sBAAQ,KAAK,oCAAoC,CAAC;AAAA,YACpD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,CAAC,sBAAsB,CAAC;AAG3B,UAAM,oBAA0B,eAAQ,MAAM;AAC5C,YAAM,kBAAkB,WAAW,2BAA2B;AAC9D,aAAO;AAAA,qBACQ,cAAc;AAAA;AAAA;AAAA,oEAGiC,eAAe;AAAA;AAAA;AAAA;AAAA,oEAIf,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,uBAK5D,cAAc;AAAA;AAAA;AAAA,sEAGiC,eAAe;AAAA;AAAA;AAAA;AAAA,sEAIf,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,IAKjF,GAAG,CAAC,QAAQ,CAAC;AAEb,IAAM,iBAAU,MAAM;AACpB,YAAM,MAAM,aAAa;AACzB,UAAI,CAAC,IAAK;AAEV,YAAM,UAAU,gBAAgB,QAAQ;AACxC,YAAM,gBAAgB,IAAI,eAAe,OAAO;AAChD,UAAI,eAAe;AACjB,sBAAc,OAAO;AAAA,MACvB;AAEA,YAAM,aAAa,IAAI,cAAc,OAAO;AAC5C,iBAAW,KAAK;AAChB,iBAAW,cAAc;AACzB,UAAI,KAAK,YAAY,UAAU;AAE/B,aAAO,MAAM;AACX,cAAMC,SAAQ,IAAI,eAAe,OAAO;AACxC,YAAIA,QAAO;AACT,cAAI;AACF,YAAAA,OAAM,OAAO;AAAA,UACf,SAAS,GAAG;AAEV,gBAAI,MAAiC;AACnC,sBAAQ,KAAK,yBAAyB,CAAC;AAAA,YACzC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,CAAC,UAAU,iBAAiB,CAAC;AAGhC,UAAM,aAAmB,mBAAY,MAAM;AACzC,UAAI,SAAS,MAAM,QAAQ,SAAS,MAAM,QAAQ,OAAO,WAAW,YAAY;AAC9E,cAAM,iBAAiC,EAAE,GAAG,SAAS,GAAG,GAAG,SAAS,EAAE;AACtE,eAAO,cAAc;AAAA,MACvB;AAAA,IACF,GAAG,CAAC,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAGnC,IAAM,iBAAU,MAAM;AACpB,iBAAW;AAAA,IACb,GAAG,CAAC,UAAU,CAAC;AAGf,UAAM,yBAA+B,mBAAY,MAAM;AACrD,UAAI,OAAO,uBAAuB,YAAY;AAC5C,cAAM,kBAAkB,WAAW;AACnC,cAAM,SAAiC,CAAC,UAAU,aAAa;AAC/D,2BAAmB,iBAAiB,MAAM;AAAA,MAC5C;AAAA,IACF,GAAG,CAAC,SAAS,WAAW,kBAAkB,CAAC;AAG3C,IAAM,iBAAU,MAAM;AACpB,6BAAuB;AAAA,IACzB,GAAG,CAAC,sBAAsB,CAAC;AAG3B,IAAM,iBAAU,MAAM;AACpB,UAAI,YAAY,OAAO,uBAAuB,YAAY;AACxD,2BAAmB,OAAO,OAAO;AAAA,MACnC;AAAA,IACF,GAAG,CAAC,UAAU,kBAAkB,CAAC;AAGjC,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AAEA,UAAM,cAAoB;AAAA,MACxB,MAAM;AAhWZ;AAiWQ,cAAM,gBAAgB,cAAa,cAAS,MAAT,YAAc,CAAC,QAAO,cAAS,MAAT,YAAc,CAAC;AACxE,cAAM,kBAAkB,WAAW,2BAA2B;AAE9D,eAAO;AAAA,UACL,UAAU;AAAA,UACV,KAAK;AAAA,UACL,MAAM;AAAA,UACN,WAAW,gBAAgB;AAAA,UAC3B,eAAe;AAAA,UACf;AAAA,UACA,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,WAAW,GAAG,cAAc,IAAI,kBAAkB;AAAA,UAClD,cAAc,IAAG,cAAS,MAAT,YAAc,CAAC;AAAA,UAChC,cAAc,IAAG,cAAS,MAAT,YAAc,CAAC;AAAA,WAC7B;AAAA,MAEP;AAAA,MACA,CAAC,SAAS,GAAG,SAAS,GAAG,QAAQ,UAAU,KAAK;AAAA,IAClD;AAGA,UAAM,qBAA2B,eAAQ,MAAM;AAAA;AAAA;AAAA;AAAA,OAI5C,CAAC,CAAC;AAGL,UAAM,eAAe,CAAC,MAAM,KACR,WACA,aACA,SAAS,MAAM,QACf,SAAS,MAAM,QACf;AAEpB,QAAI,CAAC,aAAc,QAAO;AAE1B,WACE,4DACE,qCAAC,WAAM,IAAI,uBAAuB,QAAQ,MAAK,kBAAmB,GACjE;AAAA,MACC,qCAAO,iBAAN,EAAe,KAAK,UAAU,QAAQ,MACrC;AAAA,QAAC;AAAA;AAAA,UACC,IAAI,iBAAiB,QAAQ;AAAA,UAC7B,OAAO;AAAA,UACP;AAAA,UACA,eAAY;AAAA,UACZ,eAAa;AAAA,UACb;AAAA,UACA,cAAY;AAAA;AAAA,QAEX;AAAA,MACH,GACA,qCAAC,gBAAa,UAAoB,MAAM,kBAAkB,CAC5D;AAAA,MACA;AAAA,IACF,CACF;AAAA,EAEJ;AAAA,EACA;AACF;AAEA,aAAa,cAAc;AAE3B,IAAO,uBAAQ;","names":["React","useEffect","useCallback","useCallback","useEffect","style"]}