@vellira-ui/core 2.23.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -3,6 +3,9 @@ export interface NavigableItem {
|
|
|
3
3
|
}
|
|
4
4
|
export interface KeyboardNavigationEvent {
|
|
5
5
|
key: string;
|
|
6
|
+
altKey?: boolean;
|
|
7
|
+
ctrlKey?: boolean;
|
|
8
|
+
metaKey?: boolean;
|
|
6
9
|
preventDefault: () => void;
|
|
7
10
|
}
|
|
8
11
|
export interface UseKeyboardNavigationParams<TItem extends NavigableItem> {
|
|
@@ -13,8 +16,9 @@ export interface UseKeyboardNavigationParams<TItem extends NavigableItem> {
|
|
|
13
16
|
onOpen: () => void;
|
|
14
17
|
onSelect?: () => void;
|
|
15
18
|
onClose?: () => void;
|
|
19
|
+
getItemText?: (item: TItem) => string;
|
|
16
20
|
}
|
|
17
|
-
export declare const useKeyboardNavigation: <TItem extends NavigableItem>({ activeIndex, setActiveIndex, items, isOpen, onOpen, onSelect, onClose, }: UseKeyboardNavigationParams<TItem>) => {
|
|
21
|
+
export declare const useKeyboardNavigation: <TItem extends NavigableItem>({ activeIndex, setActiveIndex, items, isOpen, onOpen, onSelect, onClose, getItemText, }: UseKeyboardNavigationParams<TItem>) => {
|
|
18
22
|
onKeyDown: (event: KeyboardNavigationEvent) => void;
|
|
19
23
|
};
|
|
20
24
|
//# sourceMappingURL=useKeyboardNavigation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useKeyboardNavigation.d.ts","sourceRoot":"","sources":["../src/useKeyboardNavigation.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B,CAAC,KAAK,SAAS,aAAa;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useKeyboardNavigation.d.ts","sourceRoot":"","sources":["../src/useKeyboardNavigation.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B,CAAC,KAAK,SAAS,aAAa;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;CACvC;AAED,eAAO,MAAM,qBAAqB,GAAI,KAAK,SAAS,aAAa,EAAE,yFAShE,2BAA2B,CAAC,KAAK,CAAC;uBA2EzB,uBAAuB;CA8FlC,CAAC"}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
2
|
-
export const useKeyboardNavigation = ({ activeIndex, setActiveIndex, items, isOpen, onOpen, onSelect, onClose, }) => {
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
export const useKeyboardNavigation = ({ activeIndex, setActiveIndex, items, isOpen, onOpen, onSelect, onClose, getItemText, }) => {
|
|
3
|
+
const searchRef = useRef({
|
|
4
|
+
value: '',
|
|
5
|
+
timeoutId: undefined,
|
|
6
|
+
});
|
|
7
|
+
useEffect(() => () => {
|
|
8
|
+
if (searchRef.current.timeoutId) {
|
|
9
|
+
clearTimeout(searchRef.current.timeoutId);
|
|
10
|
+
}
|
|
11
|
+
}, []);
|
|
3
12
|
const getNextEnabledIndex = useCallback((current, direction) => {
|
|
4
13
|
if (!items.length)
|
|
5
14
|
return current;
|
|
@@ -12,6 +21,31 @@ export const useKeyboardNavigation = ({ activeIndex, setActiveIndex, items, isOp
|
|
|
12
21
|
}
|
|
13
22
|
return current;
|
|
14
23
|
}, [items]);
|
|
24
|
+
const getFirstEnabledIndex = useCallback(() => items.findIndex((item) => !item.disabled), [items]);
|
|
25
|
+
const getLastEnabledIndex = useCallback(() => {
|
|
26
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
27
|
+
if (!items[i].disabled) {
|
|
28
|
+
return i;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return -1;
|
|
32
|
+
}, [items]);
|
|
33
|
+
const getTypeaheadIndex = useCallback((query) => {
|
|
34
|
+
if (!getItemText || !query)
|
|
35
|
+
return -1;
|
|
36
|
+
const normalizedQuery = query.toLocaleLowerCase();
|
|
37
|
+
const startIndex = activeIndex >= 0 ? activeIndex + 1 : 0;
|
|
38
|
+
for (let offset = 0; offset < items.length; offset++) {
|
|
39
|
+
const index = (startIndex + offset) % items.length;
|
|
40
|
+
const item = items[index];
|
|
41
|
+
if (item &&
|
|
42
|
+
!item.disabled &&
|
|
43
|
+
getItemText(item).toLocaleLowerCase().startsWith(normalizedQuery)) {
|
|
44
|
+
return index;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return -1;
|
|
48
|
+
}, [activeIndex, getItemText, items]);
|
|
15
49
|
const onKeyDown = useCallback((event) => {
|
|
16
50
|
if (!isOpen) {
|
|
17
51
|
if (event.key === ' ' ||
|
|
@@ -43,20 +77,35 @@ export const useKeyboardNavigation = ({ activeIndex, setActiveIndex, items, isOp
|
|
|
43
77
|
break;
|
|
44
78
|
case 'Home':
|
|
45
79
|
event.preventDefault();
|
|
46
|
-
setActiveIndex(
|
|
80
|
+
setActiveIndex(getFirstEnabledIndex());
|
|
47
81
|
break;
|
|
48
82
|
case 'End':
|
|
49
83
|
event.preventDefault();
|
|
50
|
-
|
|
51
|
-
if (!items[i].disabled) {
|
|
52
|
-
setActiveIndex(i);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
84
|
+
setActiveIndex(getLastEnabledIndex());
|
|
56
85
|
break;
|
|
57
86
|
case 'Tab':
|
|
58
87
|
onClose?.();
|
|
59
88
|
break;
|
|
89
|
+
default:
|
|
90
|
+
if (event.key.length === 1 &&
|
|
91
|
+
!event.altKey &&
|
|
92
|
+
!event.ctrlKey &&
|
|
93
|
+
!event.metaKey) {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
if (searchRef.current.timeoutId) {
|
|
96
|
+
clearTimeout(searchRef.current.timeoutId);
|
|
97
|
+
}
|
|
98
|
+
searchRef.current.value += event.key;
|
|
99
|
+
searchRef.current.timeoutId = setTimeout(() => {
|
|
100
|
+
searchRef.current.value = '';
|
|
101
|
+
searchRef.current.timeoutId = undefined;
|
|
102
|
+
}, 700);
|
|
103
|
+
const nextIndex = getTypeaheadIndex(searchRef.current.value);
|
|
104
|
+
if (nextIndex >= 0) {
|
|
105
|
+
setActiveIndex(nextIndex);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
60
109
|
}
|
|
61
110
|
}, [
|
|
62
111
|
activeIndex,
|
|
@@ -67,6 +116,9 @@ export const useKeyboardNavigation = ({ activeIndex, setActiveIndex, items, isOp
|
|
|
67
116
|
onClose,
|
|
68
117
|
setActiveIndex,
|
|
69
118
|
getNextEnabledIndex,
|
|
119
|
+
getFirstEnabledIndex,
|
|
120
|
+
getLastEnabledIndex,
|
|
121
|
+
getTypeaheadIndex,
|
|
70
122
|
]);
|
|
71
123
|
return { onKeyDown };
|
|
72
124
|
};
|