-
-
Notifications
You must be signed in to change notification settings - Fork 135
/
Copy pathuseKeyboardNavigation.ts
76 lines (66 loc) · 1.88 KB
/
useKeyboardNavigation.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { useState } from "react";
interface UseKeyboardNavigationProps {
items: { languageName: string; subLanguageName?: string }[];
isOpen: boolean;
toggleDropdown: (languageName: string) => void;
onSelect: (languageName: string, subLanguageName?: string) => void;
onClose: () => void;
}
const keyboardEventKeys = {
arrowDown: "ArrowDown",
arrowUp: "ArrowUp",
arrowRight: "ArrowRight",
enter: "Enter",
escape: "Escape",
} as const;
type KeyboardEventKeys =
(typeof keyboardEventKeys)[keyof typeof keyboardEventKeys];
export const useKeyboardNavigation = ({
items,
isOpen,
toggleDropdown,
onSelect,
onClose,
}: UseKeyboardNavigationProps) => {
const [focusedIndex, setFocusedIndex] = useState<number>(-1);
const handleKeyDown = (event: React.KeyboardEvent) => {
if (!isOpen) {
return;
}
const key = event.key as KeyboardEventKeys;
if (Object.values(keyboardEventKeys).includes(key)) {
event.preventDefault();
switch (key) {
case "ArrowDown":
setFocusedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));
break;
case "ArrowUp":
setFocusedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));
break;
case "ArrowRight":
if (focusedIndex >= 0) {
const selectedItem = items[focusedIndex];
toggleDropdown(selectedItem.languageName);
}
break;
case "Enter":
if (focusedIndex >= 0) {
const selectedItem = items[focusedIndex];
onSelect(selectedItem.languageName, selectedItem.subLanguageName);
}
break;
case "Escape":
onClose();
break;
}
}
};
const resetFocus = () => setFocusedIndex(-1);
const focusFirst = () => setFocusedIndex(0);
return {
focusedIndex,
handleKeyDown,
resetFocus,
focusFirst,
};
};