@wix/design-system
Version:
@wix/design-system
92 lines • 3.81 kB
JavaScript
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