import { useState, useCallback, useEffect, useRef, createRef } from 'react';
interface TabProps {
tabIndex: number;
ref: React.RefObject<HTMLElement>;
}
interface TabListProps {
onKeyDown: (e: React.KeyboardEvent) => void;
ref: React.RefObject<HTMLElement>;
}
export function useTabs() {
const [focusedIndex, setFocusedIndex] = useState<number>(0);
const tabListRef = useRef<HTMLElement>(null);
const tabRefs = useRef<React.RefObject<HTMLElement>[]>([]);
const isInitialMount = useRef(true);
const getTabCount = useCallback(() => {
return tabListRef.current?.querySelectorAll('[role="tab"]').length || 0;
}, []);
useEffect(() => {
if (tabListRef.current) {
const count = getTabCount();
tabRefs.current = Array(count)
.fill(null)
.map((_, i) => tabRefs.current[i] || createRef<HTMLElement>());
}
}, [getTabCount]);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
return;
}
if (focusedIndex >= 0 && tabRefs.current[focusedIndex]?.current) {
tabRefs.current[focusedIndex].current?.focus();
}
}, [focusedIndex]);
const onKeyDown = useCallback(
(e: React.KeyboardEvent) => {
const count = getTabCount();
if (count === 0) return;
let newIndex = focusedIndex;
if (e.key === 'ArrowRight') {
e.preventDefault();
newIndex = (focusedIndex + 1) % count;
} else if (e.key === 'ArrowLeft') {
e.preventDefault();
newIndex = (focusedIndex - 1 + count) % count;
} else if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
const element = tabRefs.current[focusedIndex]?.current;
if (element) {
element.click();
}
return;
}
if (newIndex !== focusedIndex) {
setFocusedIndex(newIndex);
}
},
[focusedIndex]
);
const getTabProps = useCallback(
(index: number): TabProps => {
if (!tabRefs.current[index]) {
tabRefs.current[index] = createRef<HTMLElement>();
}
return {
tabIndex: index === focusedIndex ? 0 : -1,
ref: tabRefs.current[index],
};
},
[focusedIndex]
);
const getTabListProps = useCallback((): TabListProps => {
return {
onKeyDown,
ref: tabListRef,
};
}, [onKeyDown]);
return {
focusedIndex,
getTabListProps,
getTabProps,
} as const;
}
답글 남기기
댓글을 달기 위해서는 로그인해야합니다.