@vellira-ui/core 2.24.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;CACtB;AAED,eAAO,MAAM,qBAAqB,GAAI,KAAK,SAAS,aAAa,EAAE,4EAQhE,2BAA2B,CAAC,KAAK,CAAC;uBAqBzB,uBAAuB;CAqElC,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(items.findIndex((item) => !item.disabled));
80
+ setActiveIndex(getFirstEnabledIndex());
47
81
  break;
48
82
  case 'End':
49
83
  event.preventDefault();
50
- for (let i = items.length - 1; i >= 0; i--) {
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellira-ui/core",
3
- "version": "2.24.0",
3
+ "version": "2.25.0",
4
4
  "description": "Shared logic and hooks for Vellira Design System",
5
5
  "author": "Roman Bakurov",
6
6
  "license": "MIT",