|
1 | | -import { useRef, useState } from 'react'; |
2 | | -import { useLayoutEffect } from './useLayoutEffect'; |
| 1 | +import { useCallback, useState } from 'react'; |
3 | 2 |
|
4 | 3 | // https://www.w3.org/TR/wai-aria-practices-1.1/#kbd_roving_tabindex |
5 | 4 | export function useRovingCellRef(isSelected: boolean) { |
6 | | - const ref = useRef<HTMLDivElement>(null); |
7 | 5 | // https://www.w3.org/TR/wai-aria-practices-1.1/#gridNav_focus |
8 | | - const isChildFocused = useRef(false); |
9 | | - const [, forceRender] = useState<unknown>({}); |
| 6 | + const [isChildFocused, setIsChildFocused] = useState(false); |
10 | 7 |
|
11 | | - useLayoutEffect(() => { |
12 | | - if (!isSelected) { |
13 | | - isChildFocused.current = false; |
14 | | - return; |
15 | | - } |
| 8 | + if (isChildFocused && !isSelected) { |
| 9 | + setIsChildFocused(false); |
| 10 | + } |
16 | 11 |
|
17 | | - if (isChildFocused.current) { |
18 | | - // When the child is focused, we need to rerender |
19 | | - // the cell again so tabIndex is updated to -1 |
20 | | - forceRender({}); |
21 | | - return; |
22 | | - } |
23 | | - ref.current?.focus({ preventScroll: true }); |
24 | | - }, [isSelected]); |
| 12 | + const ref = useCallback( |
| 13 | + (cell: HTMLDivElement | null) => { |
| 14 | + if (cell === null || !isSelected || cell.contains(document.activeElement)) return; |
| 15 | + |
| 16 | + cell.focus({ preventScroll: true }); |
| 17 | + }, |
| 18 | + [isSelected] |
| 19 | + ); |
25 | 20 |
|
26 | 21 | function onFocus(event: React.FocusEvent<HTMLDivElement>) { |
27 | | - if (event.target !== ref.current) { |
28 | | - isChildFocused.current = true; |
| 22 | + if (event.target !== event.currentTarget) { |
| 23 | + setIsChildFocused(true); |
29 | 24 | } |
30 | 25 | } |
31 | 26 |
|
32 | | - const isFocused = isSelected && !isChildFocused.current; |
| 27 | + const isFocused = isSelected && !isChildFocused; |
33 | 28 |
|
34 | 29 | return { |
35 | 30 | ref, |
|
0 commit comments