UNPKG

@wix/design-system

Version:

@wix/design-system

92 lines 3.81 kB
import { useCallback, useEffect, useRef } from 'react'; export function useTabNavigation(options = {}) { const tabRefs = useRef([]); const containerRef = useRef(null); const getSelectedTabIndex = () => tabRefs.current?.findIndex(el => el.getAttribute('aria-selected') === 'true'); const setFocusableTab = (index) => { if (tabRefs.current?.[index]) { tabRefs.current.forEach((el, _index) => { el.tabIndex = _index === index ? 0 : -1; }); } }; const handleKeyDown = useCallback((e) => { const target = e.target; const currentIndex = tabRefs.current.indexOf(target); if (currentIndex === -1) return; const orientation = options.orientation || 'horizontal'; let nextIndex = currentIndex; const totalTabs = tabRefs.current.length; switch (e.key) { case 'ArrowRight': if (orientation === 'horizontal') { nextIndex = (currentIndex + 1) % totalTabs; } break; case 'ArrowLeft': if (orientation === 'horizontal') { nextIndex = (currentIndex - 1 + totalTabs) % totalTabs; } break; case 'ArrowDown': if (orientation === 'vertical') { nextIndex = (currentIndex + 1) % totalTabs; } break; case 'ArrowUp': if (orientation === 'vertical') { nextIndex = (currentIndex - 1 + totalTabs) % totalTabs; } break; case 'Home': nextIndex = 0; break; case 'End': nextIndex = totalTabs - 1; break; default: return; } if (nextIndex !== currentIndex && tabRefs.current[nextIndex]) { setFocusableTab(nextIndex); tabRefs.current[nextIndex].focus(); e.preventDefault(); } }, [options.orientation]); const handleFocusOut = useCallback((e) => { const relatedTarget = e.relatedTarget; if (relatedTarget && containerRef.current?.contains(relatedTarget)) { return; // Focus is still within the container, don't adjust } const selectedTabIndex = getSelectedTabIndex(); setFocusableTab(selectedTabIndex !== -1 ? selectedTabIndex : 0); }, []); const setupTabs = useCallback(() => { if (!containerRef?.current) return; containerRef.current.removeEventListener('keydown', handleKeyDown); containerRef.current.removeEventListener('focusout', handleFocusOut); const elements = containerRef.current.querySelectorAll('[role="tab"]'); tabRefs.current = Array.from(elements); const selectedTabIndex = getSelectedTabIndex(); const focusedTabIndex = selectedTabIndex !== -1 ? selectedTabIndex : 0; tabRefs.current.forEach((el, index) => { el.tabIndex = index === focusedTabIndex ? 0 : -1; }); containerRef.current.addEventListener('keydown', handleKeyDown); containerRef.current.addEventListener('focusout', handleFocusOut); }, [handleKeyDown, handleFocusOut]); useEffect(() => { setupTabs(); const currentContainer = containerRef.current; return () => { if (currentContainer) { currentContainer.removeEventListener('keydown', handleKeyDown); currentContainer.removeEventListener('focusout', handleFocusOut); } }; }, [handleKeyDown, handleFocusOut, setupTabs]); return containerRef; } //# sourceMappingURL=useTabNavigation.js.map