UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

1 lines 13.2 kB
{"version":3,"file":"useExternalNav.cjs","sources":["../../../src/hooks/useExternalNav.ts"],"sourcesContent":["import { useEffect, useRef, useCallback } from 'react';\nimport { type NavElement } from '../types';\nimport type ResourceManager from \"../managers/ResourceManager\";\n\n// Define EventCallback type to match ResourceManager's definition\ntype EventCallback = EventListenerOrEventListenerObject;\n\n// Development environment check\nconst isDevelopment = import.meta.env?.MODE === 'development';\n\ninterface UseExternalNavProps {\n externalNav: boolean;\n navElement: NavElement;\n handleNext: () => void;\n handlePrev: () => void;\n resourceManager?: ResourceManager | null;\n}\n\n/**\n * Hook to set up external navigation elements for the slider\n * Fully optimized with:\n * - Batch event listener registration\n * - Stable event handler references\n * - Comprehensive error handling\n * - Memory leak prevention\n * - Element reference caching\n * - Event propagation control\n * - Optimized dependency tracking\n * - Type safety improvements\n */\nconst useExternalNav = ({\n externalNav,\n navElement,\n handleNext,\n handlePrev,\n resourceManager\n }: UseExternalNavProps) => {\n // Track found elements to avoid unnecessary DOM queries\n const elementsRef = useRef<{\n prevNav: Element | null;\n nextNav: Element | null;\n }>({ prevNav: null, nextNav: null });\n\n // Define a stable interface for our event handlers\n interface StableHandlers {\n prevHandler: EventCallback;\n nextHandler: EventCallback;\n latestPrevFn: (() => void) | null;\n latestNextFn: (() => void) | null;\n }\n\n // Create stable event handlers that internally reference the latest callback functions\n const handlersRef = useRef<StableHandlers>({\n prevHandler: (e: Event) => {\n try {\n e.preventDefault();\n // Call the latest function reference\n const latestHandler = handlersRef.current.latestPrevFn;\n if (typeof latestHandler === 'function') {\n latestHandler();\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in previous navigation handler:', error);\n }\n }\n },\n nextHandler: (e: Event) => {\n try {\n e.preventDefault();\n // Call the latest function reference\n const latestHandler = handlersRef.current.latestNextFn;\n if (typeof latestHandler === 'function') {\n latestHandler();\n }\n } catch (error) {\n if (isDevelopment) {\n console.error('Error in next navigation handler:', error);\n }\n }\n },\n latestPrevFn: null,\n latestNextFn: null\n });\n\n // Keep the latest function references updated\n useEffect(() => {\n handlersRef.current.latestPrevFn = handlePrev;\n handlersRef.current.latestNextFn = handleNext;\n }, [handlePrev, handleNext]);\n\n // Memoize the batch registration function for better performance\n const setupBatchListeners = useCallback((\n prevElement: Element,\n nextElement: Element,\n prevHandler: EventCallback,\n nextHandler: EventCallback\n ) => {\n try {\n if (!resourceManager) return false;\n\n // Create listeners maps for each element\n const prevListenersMap = new Map<string, EventCallback[]>();\n prevListenersMap.set('click', [prevHandler]);\n\n const nextListenersMap = new Map<string, EventCallback[]>();\n nextListenersMap.set('click', [nextHandler]);\n\n // Register event listeners in batch operations\n resourceManager.addEventListenerBatch(prevElement, prevListenersMap);\n resourceManager.addEventListenerBatch(nextElement, nextListenersMap);\n\n return true;\n } catch (error) {\n if (isDevelopment) {\n console.error('Error setting up batch listeners:', error);\n }\n return false;\n }\n }, [resourceManager]);\n\n // Setup regular DOM event listeners\n const setupDirectListeners = useCallback((\n prevElement: Element,\n nextElement: Element,\n prevHandler: EventCallback,\n nextHandler: EventCallback\n ) => {\n try {\n prevElement.addEventListener('click', prevHandler);\n nextElement.addEventListener('click', nextHandler);\n return true;\n } catch (error) {\n if (isDevelopment) {\n console.error('Error setting up direct listeners:', error);\n }\n return false;\n }\n }, []);\n\n // Main effect for setting up and cleaning up navigation\n useEffect(() => {\n // Skip during server-side rendering\n if (typeof window === 'undefined') return;\n\n // Skip if external navigation is not enabled\n if (!externalNav) return;\n\n // Track initialization status for cleanup\n let isInitialized = false;\n\n try {\n // Find the navigation elements in the DOM\n const prevNav = document.querySelector(navElement.prev);\n const nextNav = document.querySelector(navElement.next);\n\n // Store references to found elements\n elementsRef.current = { prevNav, nextNav };\n\n // Check if both elements are found\n if (!prevNav || !nextNav) {\n // Create helpful error message\n const missingElements: string[] = [];\n if (!prevNav) missingElements.push(`\"${navElement.prev}\"`);\n if (!nextNav) missingElements.push(`\"${navElement.next}\"`);\n\n // Log warning in development mode\n if (isDevelopment) {\n console.warn(\n `KineticSlider: External navigation elements not found: ${missingElements.join(', ')}. ` +\n `Ensure these selectors exist in the DOM.`\n );\n }\n return;\n }\n\n // Get stable event handlers\n const { prevHandler, nextHandler } = handlersRef.current;\n\n // Try batch registration first, fall back to direct listeners if needed\n let registrationSuccessful = false;\n\n if (resourceManager) {\n registrationSuccessful = setupBatchListeners(\n prevNav, nextNav, prevHandler, nextHandler\n );\n }\n\n // Fall back to direct listeners if batch registration failed or unavailable\n if (!registrationSuccessful) {\n registrationSuccessful = setupDirectListeners(\n prevNav, nextNav, prevHandler, nextHandler\n );\n }\n\n // Mark as successfully initialized\n isInitialized = registrationSuccessful;\n\n } catch (error) {\n // Handle any unexpected errors during initialization\n if (isDevelopment) {\n console.error('Error initializing external navigation:', error);\n }\n }\n\n // Cleanup on unmount or dependencies change\n return () => {\n try {\n // Skip cleanup if not initialized\n if (!isInitialized) return;\n\n // Get current element references for cleanup\n const { prevNav, nextNav } = elementsRef.current;\n const { prevHandler, nextHandler } = handlersRef.current;\n\n // ResourceManager handles its own cleanup\n if (!resourceManager && prevNav && nextNav) {\n // Safely remove event listeners\n try {\n prevNav.removeEventListener('click', prevHandler);\n } catch (e) {\n if (isDevelopment) {\n console.warn('Error removing event listener from previous nav:', e);\n }\n }\n\n try {\n nextNav.removeEventListener('click', nextHandler);\n } catch (e) {\n if (isDevelopment) {\n console.warn('Error removing event listener from next nav:', e);\n }\n }\n }\n\n // Clear element references to help garbage collection\n elementsRef.current = { prevNav: null, nextNav: null };\n } catch (cleanupError) {\n // Handle errors during cleanup\n if (isDevelopment) {\n console.error('Error during navigation cleanup:', cleanupError);\n }\n }\n };\n }, [\n externalNav,\n navElement.prev,\n navElement.next,\n resourceManager,\n setupBatchListeners,\n setupDirectListeners\n ]);\n\n // Return current elements for potential external use\n return {\n elements: elementsRef.current\n };\n};\n\nexport default useExternalNav;"],"names":["useRef","useEffect","useCallback"],"mappings":"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"}