kinetic-slider
Version:
A WebGL-powered kinetic slider component using PIXI.js
1 lines • 17.6 kB
Source Map (JSON)
{"version":3,"file":"useNavigation.cjs","sources":["../../../src/hooks/useNavigation.ts"],"sourcesContent":["import { useEffect, useRef, useCallback } from 'react';\nimport ResourceManager from '../managers/ResourceManager';\n\n// Development environment check\nconst isDevelopment = import.meta.env?.MODE === 'development';\n\n// Enhanced interfaces for better type safety\ninterface UseNavigationProps {\n onNext: () => void;\n onPrev: () => void;\n enableKeyboardNav?: boolean;\n resourceManager?: ResourceManager | null;\n}\n\ninterface NavigationResult {\n goNext: () => void;\n goPrev: () => void;\n isKeyboardEnabled: boolean;\n}\n\n// Type definition for event callbacks to match ResourceManager's expected type\ntype EventCallback = EventListenerOrEventListenerObject;\n\n// Interface for stable handler storage\ninterface StableHandlers {\n keyDownHandler: EventCallback;\n slideChangeHandler: EventCallback;\n latestNextFn: (() => void) | null;\n latestPrevFn: (() => void) | null;\n}\n\n// Interface for batch operation states\ninterface BatchOperationState {\n pendingListeners: Map<EventTarget, Map<string, EventCallback[]>>;\n processedCount: number;\n}\n\n/**\n * Hook to set up navigation controls for the slider\n * Fully optimized with:\n * - Batch event listener registration\n * - Comprehensive error handling\n * - Memory leak prevention\n * - Stable handler references\n * - Strong cancellation mechanisms\n * - Server-side rendering safety\n */\nconst useNavigation = ({\n onNext,\n onPrev,\n enableKeyboardNav = true,\n resourceManager\n }: UseNavigationProps): NavigationResult => {\n // Track component mount state\n const isMountedRef = useRef(true);\n\n // Track batch operations\n const batchOperationsRef = useRef<BatchOperationState>({\n pendingListeners: new Map(),\n processedCount: 0\n });\n\n // Define stable handler interface with ref\n const handlersRef = useRef<StableHandlers>({\n keyDownHandler: (event: Event) => {\n try {\n if (!isMountedRef.current || !enableKeyboardNav) return;\n\n const keyEvent = event as KeyboardEvent;\n const handlers = handlersRef.current;\n\n // Handle keyboard navigation\n switch (keyEvent.key) {\n case 'ArrowLeft':\n if (handlers.latestPrevFn) {\n handlers.latestPrevFn();\n }\n break;\n case 'ArrowRight':\n if (handlers.latestNextFn) {\n handlers.latestNextFn();\n }\n break;\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in keyboard navigation handler:', error);\n }\n }\n },\n slideChangeHandler: (event: Event) => {\n try {\n if (!isMountedRef.current) return;\n\n const customEvent = event as CustomEvent;\n const handlers = handlersRef.current;\n\n // Handle custom slide change events\n if (customEvent.detail && typeof customEvent.detail.nextIndex === 'number') {\n if (handlers.latestNextFn) {\n handlers.latestNextFn();\n }\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in slide change handler:', error);\n }\n }\n },\n latestNextFn: null,\n latestPrevFn: null\n });\n\n /**\n * Process any pending event listeners in batch\n */\n const processPendingListeners = useCallback(() => {\n try {\n const { pendingListeners } = batchOperationsRef.current;\n\n // Skip if no ResourceManager or no pending listeners\n if (!resourceManager || pendingListeners.size === 0) return;\n\n let totalProcessed = 0;\n\n // Process each event target\n pendingListeners.forEach((listenerMap, target) => {\n try {\n resourceManager.addEventListenerBatch(target, listenerMap);\n totalProcessed += Array.from(listenerMap.values())\n .reduce((sum, callbacks) => sum + callbacks.length, 0);\n } catch (error) {\n if (isDevelopment) {\n console.error('Error processing listener batch for target:', error);\n }\n }\n });\n\n // Clear the pending map\n pendingListeners.clear();\n\n // Update processed count\n batchOperationsRef.current.processedCount += totalProcessed;\n\n if (isDevelopment) {\n console.log(`Processed ${totalProcessed} event listeners in batch`);\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error processing batch listeners:', error);\n }\n // Clear pending listeners even on error\n batchOperationsRef.current.pendingListeners.clear();\n }\n }, [resourceManager]);\n\n /**\n * Add a listener to the pending batch\n */\n const addListenerToBatch = useCallback((\n target: EventTarget,\n eventType: string,\n callback: EventCallback\n ) => {\n try {\n const { pendingListeners } = batchOperationsRef.current;\n\n // Get or create map for this target\n if (!pendingListeners.has(target)) {\n pendingListeners.set(target, new Map());\n }\n\n const targetMap = pendingListeners.get(target)!;\n\n // Get or create array for this event type\n if (!targetMap.has(eventType)) {\n targetMap.set(eventType, []);\n }\n\n // Add callback to the list\n targetMap.get(eventType)!.push(callback);\n\n return true;\n } catch (error) {\n if (isDevelopment) {\n console.error('Error adding listener to batch:', error);\n }\n return false;\n }\n }, []);\n\n // Keep the latest function references updated\n useEffect(() => {\n handlersRef.current.latestNextFn = onNext;\n handlersRef.current.latestPrevFn = onPrev;\n }, [onNext, onPrev]);\n\n // Set up keyboard navigation\n useEffect(() => {\n // Skip during server-side rendering\n if (typeof window === 'undefined') return;\n\n // Skip if keyboard navigation is disabled\n if (!enableKeyboardNav) return;\n\n // Reset mounted state\n isMountedRef.current = true;\n\n try {\n const { keyDownHandler } = handlersRef.current;\n\n // Start performance timer if in development\n const startTime = isDevelopment ? performance.now() : 0;\n\n // Register event listener with batch processing if ResourceManager available\n if (resourceManager) {\n // Add to batch operations\n addListenerToBatch(window, 'keydown', keyDownHandler);\n\n // Process in batch\n processPendingListeners();\n } else {\n // Direct registration\n window.addEventListener('keydown', keyDownHandler);\n }\n\n // Log performance if in development\n if (isDevelopment && startTime > 0) {\n const setupTime = performance.now() - startTime;\n console.log(`Keyboard navigation setup completed in ${setupTime.toFixed(2)}ms`);\n }\n\n // Cleanup on unmount\n return () => {\n // Update mounted state immediately\n isMountedRef.current = false;\n\n try {\n // ResourceManager handles its own cleanup\n if (!resourceManager) {\n window.removeEventListener('keydown', keyDownHandler);\n }\n } catch (cleanupError) {\n if (isDevelopment) {\n console.error('Error during keyboard navigation cleanup:', cleanupError);\n }\n }\n };\n } catch (error) {\n if (isDevelopment) {\n console.error('Error setting up keyboard navigation:', error);\n }\n // Return empty cleanup function\n return () => {};\n }\n }, [enableKeyboardNav, resourceManager, addListenerToBatch, processPendingListeners]);\n\n // Listen for custom slide change events\n useEffect(() => {\n // Skip during server-side rendering\n if (typeof window === 'undefined') return;\n\n try {\n const { slideChangeHandler } = handlersRef.current;\n\n // Start performance timer if in development\n const startTime = isDevelopment ? performance.now() : 0;\n\n // Register with batch processing if ResourceManager is available\n if (resourceManager) {\n // Add to batch operations\n addListenerToBatch(window, 'slideChange', slideChangeHandler);\n\n // Process in batch\n processPendingListeners();\n } else {\n // Direct registration\n window.addEventListener('slideChange', slideChangeHandler);\n }\n\n // Log performance if in development\n if (isDevelopment && startTime > 0) {\n const setupTime = performance.now() - startTime;\n console.log(`Slide change listener setup completed in ${setupTime.toFixed(2)}ms`);\n }\n\n // Cleanup on unmount\n return () => {\n try {\n // ResourceManager handles its own cleanup\n if (!resourceManager) {\n window.removeEventListener('slideChange', slideChangeHandler);\n }\n } catch (cleanupError) {\n if (isDevelopment) {\n console.error('Error removing slide change event listener:', cleanupError);\n }\n }\n };\n } catch (error) {\n if (isDevelopment) {\n console.error('Error setting up slide change listener:', error);\n }\n // Return empty cleanup function\n return () => {};\n }\n }, [resourceManager, addListenerToBatch, processPendingListeners]);\n\n // Expose memoized navigation methods\n const goNext = useCallback(() => {\n try {\n if (isMountedRef.current && handlersRef.current.latestNextFn) {\n const startTime = isDevelopment ? performance.now() : 0;\n\n handlersRef.current.latestNextFn();\n\n if (isDevelopment && startTime > 0) {\n const executionTime = performance.now() - startTime;\n console.log(`Navigation next completed in ${executionTime.toFixed(2)}ms`);\n }\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in goNext navigation function:', error);\n }\n }\n }, []);\n\n const goPrev = useCallback(() => {\n try {\n if (isMountedRef.current && handlersRef.current.latestPrevFn) {\n const startTime = isDevelopment ? performance.now() : 0;\n\n handlersRef.current.latestPrevFn();\n\n if (isDevelopment && startTime > 0) {\n const executionTime = performance.now() - startTime;\n console.log(`Navigation prev completed in ${executionTime.toFixed(2)}ms`);\n }\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in goPrev navigation function:', error);\n }\n }\n }, []);\n\n return {\n goNext,\n goPrev,\n isKeyboardEnabled: enableKeyboardNav\n };\n};\n\nexport default useNavigation;"],"names":["useRef","useCallback","useEffect"],"mappings":"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"}