Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / components / hooks / useDropdownArrowNavigation.ts
blobb2b6150c31d3b722fc37e561d925db5c3429bd7d
1 import type { MutableRefObject } from 'react';
3 import { tabbable } from 'tabbable';
5 import type { HotkeyTuple } from './useHotkeys';
7 interface Context {
8     rootRef: MutableRefObject<HTMLDivElement | null>;
11 const useDropdownArrowNavigation = ({ rootRef }: Context) => {
12     const getDropdownMenuItems = () => {
13         if (!rootRef.current) {
14             return [];
15         }
17         return tabbable(rootRef.current, { includeContainer: false }).filter(
18             (elm) => !elm.dataset.preventArrowNavigation
19         );
20     };
22     const getFocusIndex = () => getDropdownMenuItems().findIndex((elm) => elm === document.activeElement);
24     const findElementAndFocus = (startingIndex: number) => {
25         const dropdownMenuItems = getDropdownMenuItems();
27         if (!dropdownMenuItems.length) {
28             return;
29         }
31         const lastIndex = dropdownMenuItems.length - 1;
32         let index = startingIndex;
34         /* loop from last to first item */
35         if (index > lastIndex) {
36             index = 0;
37         }
38         /* reverse loop from first to last item */
39         if (index < 0) {
40             index = lastIndex;
41         }
43         const elem = dropdownMenuItems[index] as any;
45         elem.focus();
46     };
48     const focusOnFirst = () => {
49         const index = 0;
50         findElementAndFocus(index);
51     };
53     const focusOnLast = () => {
54         const dropdownMenuItems = getDropdownMenuItems();
55         const index = dropdownMenuItems.length - 1;
56         findElementAndFocus(index);
57     };
59     const focusOnPrevious = () => {
60         const focusIndex = getFocusIndex();
61         findElementAndFocus(focusIndex - 1);
62     };
64     const focusOnNext = () => {
65         const focusIndex = getFocusIndex();
66         findElementAndFocus(focusIndex + 1);
67     };
69     const shortcutHandlers: HotkeyTuple[] = [
70         [
71             'ArrowUp',
72             (e) => {
73                 e.preventDefault();
74                 focusOnPrevious();
75             },
76         ],
77         [
78             ['Meta', 'ArrowUp'],
79             (e) => {
80                 e.preventDefault();
81                 focusOnFirst();
82             },
83         ],
84         [
85             'ArrowDown',
86             (e) => {
87                 e.preventDefault();
88                 focusOnNext();
89             },
90         ],
91         [
92             ['Meta', 'ArrowDown'],
93             (e) => {
94                 e.preventDefault();
95                 focusOnLast();
96             },
97         ],
98     ];
100     return {
101         shortcutHandlers,
102     };
105 export default useDropdownArrowNavigation;