@octanejs/floating-ui 0.1.9 → 0.1.10
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.
- package/package.json +3 -3
- package/src/Composite.ts +118 -20
- package/src/FloatingArrow.ts +61 -4
- package/src/FloatingFocusManager.ts +127 -26
- package/src/FloatingList.ts +58 -13
- package/src/FloatingOverlay.ts +26 -3
- package/src/FloatingPortal.ts +100 -23
- package/src/context.ts +43 -15
- package/src/delayGroup.ts +66 -8
- package/src/index.ts +108 -0
- package/src/pubsub.ts +3 -1
- package/src/safePolygon.ts +13 -4
- package/src/transitions.ts +76 -10
- package/src/tree.ts +53 -11
- package/src/types.ts +337 -0
- package/src/useClick.ts +63 -13
- package/src/useClientPoint.ts +50 -10
- package/src/useDismiss.ts +86 -12
- package/src/useFloating.ts +46 -14
- package/src/useFocus.ts +33 -7
- package/src/useHover.ts +80 -19
- package/src/useId.ts +1 -1
- package/src/useInteractions.ts +12 -3
- package/src/useListNavigation.ts +162 -8
- package/src/useMergeRefs.ts +6 -1
- package/src/useRole.ts +41 -12
- package/src/useTypeahead.ts +75 -10
- package/src/utils/index.ts +159 -62
package/src/useListNavigation.ts
CHANGED
|
@@ -30,6 +30,150 @@ import {
|
|
|
30
30
|
useLatestRef,
|
|
31
31
|
useModernLayoutEffect,
|
|
32
32
|
} from './utils';
|
|
33
|
+
import type { Dimensions } from '@floating-ui/dom';
|
|
34
|
+
import type { ElementProps, FloatingRootContext, MutableRefObject } from './types';
|
|
35
|
+
|
|
36
|
+
export interface UseListNavigationProps {
|
|
37
|
+
/**
|
|
38
|
+
* A ref that holds an array of list items.
|
|
39
|
+
* @default empty list
|
|
40
|
+
*/
|
|
41
|
+
listRef: MutableRefObject<Array<HTMLElement | null>>;
|
|
42
|
+
/**
|
|
43
|
+
* The index of the currently active (focused or highlighted) item, which may
|
|
44
|
+
* or may not be selected.
|
|
45
|
+
* @default null
|
|
46
|
+
*/
|
|
47
|
+
activeIndex: number | null;
|
|
48
|
+
/**
|
|
49
|
+
* A callback that is called when the user navigates to a new active item,
|
|
50
|
+
* passed in a new `activeIndex`.
|
|
51
|
+
*/
|
|
52
|
+
onNavigate?: (activeIndex: number | null) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the Hook is enabled, including all internal Effects and event
|
|
55
|
+
* handlers.
|
|
56
|
+
* @default true
|
|
57
|
+
*/
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The currently selected item index, which may or may not be active.
|
|
61
|
+
* @default null
|
|
62
|
+
*/
|
|
63
|
+
selectedIndex?: number | null;
|
|
64
|
+
/**
|
|
65
|
+
* Whether to focus the item upon opening the floating element. 'auto' infers
|
|
66
|
+
* what to do based on the input type (keyboard vs. pointer), while a boolean
|
|
67
|
+
* value will force the value.
|
|
68
|
+
* @default 'auto'
|
|
69
|
+
*/
|
|
70
|
+
focusItemOnOpen?: boolean | 'auto';
|
|
71
|
+
/**
|
|
72
|
+
* Whether hovering an item synchronizes the focus.
|
|
73
|
+
* @default true
|
|
74
|
+
*/
|
|
75
|
+
focusItemOnHover?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Whether pressing an arrow key on the navigation’s main axis opens the
|
|
78
|
+
* floating element.
|
|
79
|
+
* @default true
|
|
80
|
+
*/
|
|
81
|
+
openOnArrowKeyDown?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* By default elements with either a `disabled` or `aria-disabled` attribute
|
|
84
|
+
* are skipped in the list navigation — however, this requires the items to
|
|
85
|
+
* be rendered.
|
|
86
|
+
* This prop allows you to manually specify indices which should be disabled,
|
|
87
|
+
* overriding the default logic.
|
|
88
|
+
* For Windows-style select menus, where the menu does not open when
|
|
89
|
+
* navigating via arrow keys, specify an empty array.
|
|
90
|
+
* @default undefined
|
|
91
|
+
*/
|
|
92
|
+
disabledIndices?: Array<number> | ((index: number) => boolean);
|
|
93
|
+
/**
|
|
94
|
+
* Determines whether focus can escape the list, such that nothing is selected
|
|
95
|
+
* after navigating beyond the boundary of the list. In some
|
|
96
|
+
* autocomplete/combobox components, this may be desired, as screen
|
|
97
|
+
* readers will return to the input.
|
|
98
|
+
* `loop` must be `true`.
|
|
99
|
+
* @default false
|
|
100
|
+
*/
|
|
101
|
+
allowEscape?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Determines whether focus should loop around when navigating past the first
|
|
104
|
+
* or last item.
|
|
105
|
+
* @default false
|
|
106
|
+
*/
|
|
107
|
+
loop?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* If the list is nested within another one (e.g. a nested submenu), the
|
|
110
|
+
* navigation semantics change.
|
|
111
|
+
* @default false
|
|
112
|
+
*/
|
|
113
|
+
nested?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Allows to specify the orientation of the parent list, which is used to
|
|
116
|
+
* determine the direction of the navigation.
|
|
117
|
+
* This is useful when list navigation is used within a Composite,
|
|
118
|
+
* as the hook can't determine the orientation of the parent list automatically.
|
|
119
|
+
*/
|
|
120
|
+
parentOrientation?: UseListNavigationProps['orientation'];
|
|
121
|
+
/**
|
|
122
|
+
* Whether the direction of the floating element’s navigation is in RTL
|
|
123
|
+
* layout.
|
|
124
|
+
* @default false
|
|
125
|
+
*/
|
|
126
|
+
rtl?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Whether the focus is virtual (using `aria-activedescendant`).
|
|
129
|
+
* Use this if you need focus to remain on the reference element
|
|
130
|
+
* (such as an input), but allow arrow keys to navigate list items.
|
|
131
|
+
* This is common in autocomplete listbox components.
|
|
132
|
+
* Your virtually-focused list items must have a unique `id` set on them.
|
|
133
|
+
* If you’re using a component role with the `useRole()` Hook, then an `id` is
|
|
134
|
+
* generated automatically.
|
|
135
|
+
* @default false
|
|
136
|
+
*/
|
|
137
|
+
virtual?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* The orientation in which navigation occurs.
|
|
140
|
+
* @default 'vertical'
|
|
141
|
+
*/
|
|
142
|
+
orientation?: 'vertical' | 'horizontal' | 'both';
|
|
143
|
+
/**
|
|
144
|
+
* Specifies how many columns the list has (i.e., it’s a grid). Use an
|
|
145
|
+
* orientation of 'horizontal' (e.g. for an emoji picker/date picker, where
|
|
146
|
+
* pressing ArrowRight or ArrowLeft can change rows), or 'both' (where the
|
|
147
|
+
* current row cannot be escaped with ArrowRight or ArrowLeft, only ArrowUp
|
|
148
|
+
* and ArrowDown).
|
|
149
|
+
* @default 1
|
|
150
|
+
*/
|
|
151
|
+
cols?: number;
|
|
152
|
+
/**
|
|
153
|
+
* Whether to scroll the active item into view when navigating. The default
|
|
154
|
+
* value uses nearest options.
|
|
155
|
+
*/
|
|
156
|
+
scrollItemIntoView?: boolean | ScrollIntoViewOptions;
|
|
157
|
+
/**
|
|
158
|
+
* When using virtual focus management, this holds a ref to the
|
|
159
|
+
* virtually-focused item. This allows nested virtual navigation to be
|
|
160
|
+
* enabled, and lets you know when a nested element is virtually focused from
|
|
161
|
+
* the root reference handling the events. Requires `FloatingTree` to be
|
|
162
|
+
* setup.
|
|
163
|
+
*/
|
|
164
|
+
virtualItemRef?: MutableRefObject<HTMLElement | null>;
|
|
165
|
+
/**
|
|
166
|
+
* Only for `cols > 1`, specify sizes for grid items.
|
|
167
|
+
* `{ width: 2, height: 2 }` means an item is 2 columns wide and 2 rows tall.
|
|
168
|
+
*/
|
|
169
|
+
itemSizes?: Dimensions[];
|
|
170
|
+
/**
|
|
171
|
+
* Only relevant for `cols > 1` and items with different sizes, specify if
|
|
172
|
+
* the grid is dense (as defined in the CSS spec for `grid-auto-flow`).
|
|
173
|
+
* @default false
|
|
174
|
+
*/
|
|
175
|
+
dense?: boolean;
|
|
176
|
+
}
|
|
33
177
|
|
|
34
178
|
const ARROW_UP = 'ArrowUp';
|
|
35
179
|
const ARROW_DOWN = 'ArrowDown';
|
|
@@ -73,10 +217,20 @@ function isCrossOrientationCloseKey(key: string, orientation: any, rtl: boolean,
|
|
|
73
217
|
return doSwitch(orientation, vertical, horizontal);
|
|
74
218
|
}
|
|
75
219
|
|
|
76
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Adds arrow key-based navigation of a list of items, either using real DOM
|
|
222
|
+
* focus or virtual focus.
|
|
223
|
+
* @see https://floating-ui.com/docs/useListNavigation
|
|
224
|
+
*/
|
|
225
|
+
export function useListNavigation(
|
|
226
|
+
context: FloatingRootContext,
|
|
227
|
+
props: UseListNavigationProps,
|
|
228
|
+
slot?: symbol,
|
|
229
|
+
): ElementProps;
|
|
230
|
+
export function useListNavigation(...args: any[]): ElementProps {
|
|
77
231
|
const [user, slot] = splitSlot(args);
|
|
78
|
-
const context = user[0];
|
|
79
|
-
const props = (user[1] as
|
|
232
|
+
const context = user[0] as FloatingRootContext;
|
|
233
|
+
const props = (user[1] as UseListNavigationProps) ?? {};
|
|
80
234
|
|
|
81
235
|
const open = context.open;
|
|
82
236
|
const onOpenChange = context.onOpenChange;
|
|
@@ -128,7 +282,7 @@ export function useListNavigation(...args: any[]): any {
|
|
|
128
282
|
const typeableComboboxReference = isTypeableCombobox(elements.domReference);
|
|
129
283
|
const focusItemOnOpenRef = useRef(focusItemOnOpen, subSlot(slot, 'fioo'));
|
|
130
284
|
const indexRef = useRef(selectedIndex != null ? selectedIndex : -1, subSlot(slot, 'index'));
|
|
131
|
-
const keyRef = useRef<
|
|
285
|
+
const keyRef = useRef<string | null>(null, subSlot(slot, 'key'));
|
|
132
286
|
const isPointerModalityRef = useRef(true, subSlot(slot, 'pm'));
|
|
133
287
|
const previousOnNavigateRef = useRef(onNavigate, subSlot(slot, 'ponav'));
|
|
134
288
|
const previousMountedRef = useRef(!!elements.floating, subSlot(slot, 'pmount'));
|
|
@@ -139,8 +293,8 @@ export function useListNavigation(...args: any[]): any {
|
|
|
139
293
|
const latestOpenRef = useLatestRef(open, subSlot(slot, 'lor'));
|
|
140
294
|
const scrollItemIntoViewRef = useLatestRef(scrollItemIntoView, subSlot(slot, 'siir'));
|
|
141
295
|
const selectedIndexRef = useLatestRef(selectedIndex, subSlot(slot, 'sir'));
|
|
142
|
-
const [activeId, setActiveId] = useState<
|
|
143
|
-
const [virtualId, setVirtualId] = useState<
|
|
296
|
+
const [activeId, setActiveId] = useState<string | undefined>(undefined, subSlot(slot, 'aid'));
|
|
297
|
+
const [virtualId, setVirtualId] = useState<string | undefined>(undefined, subSlot(slot, 'vid'));
|
|
144
298
|
|
|
145
299
|
const focusItem = useEffectEvent(
|
|
146
300
|
() => {
|
|
@@ -436,7 +590,7 @@ export function useListNavigation(...args: any[]): any {
|
|
|
436
590
|
const minGridIndex = cellMap.findIndex(
|
|
437
591
|
(index) => index != null && !isListIndexDisabled(listRef, index, disabledIndices),
|
|
438
592
|
);
|
|
439
|
-
const maxGridIndex = cellMap.reduce(
|
|
593
|
+
const maxGridIndex = cellMap.reduce<number>(
|
|
440
594
|
(foundIndex, index, cellIndex) =>
|
|
441
595
|
index != null && !isListIndexDisabled(listRef, index, disabledIndices)
|
|
442
596
|
? cellIndex
|
|
@@ -713,7 +867,7 @@ export function useListNavigation(...args: any[]): any {
|
|
|
713
867
|
subSlot(slot, 'm:ref'),
|
|
714
868
|
);
|
|
715
869
|
|
|
716
|
-
return useMemo(
|
|
870
|
+
return useMemo<ElementProps>(
|
|
717
871
|
() => (enabled ? { reference, floating, item } : {}),
|
|
718
872
|
[enabled, reference, floating, item],
|
|
719
873
|
subSlot(slot, 'm:ret'),
|
package/src/useMergeRefs.ts
CHANGED
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
import { useCallback, useMemo, useRef } from 'octane';
|
|
4
4
|
|
|
5
5
|
import { splitSlot, subSlot } from './internal';
|
|
6
|
+
import type { MutableRefObject, RefCallback } from './types';
|
|
6
7
|
|
|
8
|
+
export function useMergeRefs<Instance>(
|
|
9
|
+
refs: Array<MutableRefObject<Instance | null> | RefCallback<Instance> | null | undefined>,
|
|
10
|
+
slot?: symbol,
|
|
11
|
+
): ((node: Instance | null) => void) | null;
|
|
7
12
|
export function useMergeRefs(...args: any[]): any {
|
|
8
13
|
const [user, slot] = splitSlot(args);
|
|
9
14
|
const refs = (user[0] as any[]) ?? [];
|
|
10
15
|
|
|
11
|
-
const cleanupRef = useRef<
|
|
16
|
+
const cleanupRef = useRef<(() => void) | undefined>(undefined, subSlot(slot, 'cleanup'));
|
|
12
17
|
|
|
13
18
|
const refEffect = useCallback(
|
|
14
19
|
(instance: any) => {
|
package/src/useRole.ts
CHANGED
|
@@ -7,17 +7,45 @@ import { splitSlot, subSlot } from './internal';
|
|
|
7
7
|
import { useFloatingParentNodeId } from './tree';
|
|
8
8
|
import { useId } from './useId';
|
|
9
9
|
import { getFloatingFocusElement } from './utils';
|
|
10
|
+
import type { ElementProps, ExtendedUserProps, FloatingRootContext, HTMLProps } from './types';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
type AriaRole = 'tooltip' | 'dialog' | 'alertdialog' | 'menu' | 'listbox' | 'grid' | 'tree';
|
|
13
|
+
type ComponentRole = 'select' | 'label' | 'combobox';
|
|
14
|
+
|
|
15
|
+
export interface UseRoleProps {
|
|
16
|
+
/**
|
|
17
|
+
* Whether the Hook is enabled, including all internal Effects and event
|
|
18
|
+
* handlers.
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* The role of the floating element.
|
|
24
|
+
* @default 'dialog'
|
|
25
|
+
*/
|
|
26
|
+
role?: AriaRole | ComponentRole;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const componentRoleToAriaRoleMap = new Map<AriaRole | ComponentRole, AriaRole | false>([
|
|
12
30
|
['select', 'listbox'],
|
|
13
31
|
['combobox', 'listbox'],
|
|
14
32
|
['label', false],
|
|
15
33
|
]);
|
|
16
34
|
|
|
17
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Adds base screen reader props to the reference and floating elements for a
|
|
37
|
+
* given floating element `role`.
|
|
38
|
+
* @see https://floating-ui.com/docs/useRole
|
|
39
|
+
*/
|
|
40
|
+
export function useRole(
|
|
41
|
+
context: FloatingRootContext,
|
|
42
|
+
props?: UseRoleProps,
|
|
43
|
+
slot?: symbol,
|
|
44
|
+
): ElementProps;
|
|
45
|
+
export function useRole(...args: any[]): ElementProps {
|
|
18
46
|
const [user, slot] = splitSlot(args);
|
|
19
|
-
const context = user[0];
|
|
20
|
-
const props = (user[1] as
|
|
47
|
+
const context = user[0] as FloatingRootContext;
|
|
48
|
+
const props = (user[1] as UseRoleProps) ?? {};
|
|
21
49
|
|
|
22
50
|
const open = context.open;
|
|
23
51
|
const elements = context.elements;
|
|
@@ -34,18 +62,19 @@ export function useRole(...args: any[]): any {
|
|
|
34
62
|
subSlot(slot, 'm:fid'),
|
|
35
63
|
);
|
|
36
64
|
const mapped = componentRoleToAriaRoleMap.get(role);
|
|
37
|
-
|
|
65
|
+
// The map covers every ComponentRole, so the fallback `role` is an AriaRole.
|
|
66
|
+
const ariaRole = (mapped != null ? mapped : role) as AriaRole | false;
|
|
38
67
|
const parentId = useFloatingParentNodeId();
|
|
39
68
|
const isNested = parentId != null;
|
|
40
69
|
|
|
41
|
-
const reference = useMemo(
|
|
70
|
+
const reference = useMemo<HTMLProps<Element>>(
|
|
42
71
|
() => {
|
|
43
72
|
if (ariaRole === 'tooltip' || role === 'label') {
|
|
44
73
|
return {
|
|
45
74
|
['aria-' + (role === 'label' ? 'labelledby' : 'describedby')]: open
|
|
46
75
|
? floatingId
|
|
47
76
|
: undefined,
|
|
48
|
-
}
|
|
77
|
+
} as HTMLProps<Element>;
|
|
49
78
|
}
|
|
50
79
|
return {
|
|
51
80
|
'aria-expanded': open ? 'true' : 'false',
|
|
@@ -62,9 +91,9 @@ export function useRole(...args: any[]): any {
|
|
|
62
91
|
subSlot(slot, 'm:ref'),
|
|
63
92
|
);
|
|
64
93
|
|
|
65
|
-
const floating = useMemo(
|
|
94
|
+
const floating = useMemo<HTMLProps<HTMLElement>>(
|
|
66
95
|
() => {
|
|
67
|
-
const floatingProps:
|
|
96
|
+
const floatingProps: HTMLProps<HTMLElement> = {
|
|
68
97
|
id: floatingId,
|
|
69
98
|
...(ariaRole && { role: ariaRole }),
|
|
70
99
|
};
|
|
@@ -81,9 +110,9 @@ export function useRole(...args: any[]): any {
|
|
|
81
110
|
);
|
|
82
111
|
|
|
83
112
|
const item = useCallback(
|
|
84
|
-
(_ref:
|
|
113
|
+
(_ref: ExtendedUserProps): HTMLProps<HTMLElement> => {
|
|
85
114
|
const { active, selected } = _ref;
|
|
86
|
-
const commonProps:
|
|
115
|
+
const commonProps: HTMLProps<HTMLElement> = {
|
|
87
116
|
role: 'option',
|
|
88
117
|
...(active && { id: floatingId + '-fui-option' }),
|
|
89
118
|
};
|
|
@@ -98,7 +127,7 @@ export function useRole(...args: any[]): any {
|
|
|
98
127
|
subSlot(slot, 'cb:item'),
|
|
99
128
|
);
|
|
100
129
|
|
|
101
|
-
return useMemo(
|
|
130
|
+
return useMemo<ElementProps>(
|
|
102
131
|
() => (enabled ? { reference, floating, item } : {}),
|
|
103
132
|
[enabled, reference, floating, item],
|
|
104
133
|
subSlot(slot, 'm:ret'),
|
package/src/useTypeahead.ts
CHANGED
|
@@ -10,11 +10,72 @@ import {
|
|
|
10
10
|
useLatestRef,
|
|
11
11
|
useModernLayoutEffect,
|
|
12
12
|
} from './utils';
|
|
13
|
+
import type { ElementProps, FloatingRootContext, MutableRefObject } from './types';
|
|
13
14
|
|
|
14
|
-
export
|
|
15
|
+
export interface UseTypeaheadProps {
|
|
16
|
+
/**
|
|
17
|
+
* A ref which contains an array of strings whose indices match the HTML
|
|
18
|
+
* elements of the list.
|
|
19
|
+
* @default empty list
|
|
20
|
+
*/
|
|
21
|
+
listRef: MutableRefObject<Array<string | null>>;
|
|
22
|
+
/**
|
|
23
|
+
* The index of the active (focused or highlighted) item in the list.
|
|
24
|
+
* @default null
|
|
25
|
+
*/
|
|
26
|
+
activeIndex: number | null;
|
|
27
|
+
/**
|
|
28
|
+
* Callback invoked with the matching index if found as the user types.
|
|
29
|
+
*/
|
|
30
|
+
onMatch?: (index: number) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Callback invoked with the typing state as the user types.
|
|
33
|
+
*/
|
|
34
|
+
onTypingChange?: (isTyping: boolean) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the Hook is enabled, including all internal Effects and event
|
|
37
|
+
* handlers.
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* A function that returns the matching string from the list.
|
|
43
|
+
* @default lowercase-finder
|
|
44
|
+
*/
|
|
45
|
+
findMatch?:
|
|
46
|
+
| null
|
|
47
|
+
| ((list: Array<string | null>, typedString: string) => string | null | undefined);
|
|
48
|
+
/**
|
|
49
|
+
* The number of milliseconds to wait before resetting the typed string.
|
|
50
|
+
* @default 750
|
|
51
|
+
*/
|
|
52
|
+
resetMs?: number;
|
|
53
|
+
/**
|
|
54
|
+
* An array of keys to ignore when typing.
|
|
55
|
+
* @default []
|
|
56
|
+
*/
|
|
57
|
+
ignoreKeys?: Array<string>;
|
|
58
|
+
/**
|
|
59
|
+
* The index of the selected item in the list, if available.
|
|
60
|
+
* @default null
|
|
61
|
+
*/
|
|
62
|
+
selectedIndex?: number | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Provides a matching callback that can be used to focus an item as the user
|
|
67
|
+
* types, often used in tandem with `useListNavigation()`.
|
|
68
|
+
* @see https://floating-ui.com/docs/useTypeahead
|
|
69
|
+
*/
|
|
70
|
+
export function useTypeahead(
|
|
71
|
+
context: FloatingRootContext,
|
|
72
|
+
props: UseTypeaheadProps,
|
|
73
|
+
slot?: symbol,
|
|
74
|
+
): ElementProps;
|
|
75
|
+
export function useTypeahead(...args: any[]): ElementProps {
|
|
15
76
|
const [user, slot] = splitSlot(args);
|
|
16
|
-
const context = user[0];
|
|
17
|
-
const props = (user[1] as
|
|
77
|
+
const context = user[0] as FloatingRootContext;
|
|
78
|
+
const props = (user[1] as UseTypeaheadProps) ?? {};
|
|
18
79
|
|
|
19
80
|
const open = context.open;
|
|
20
81
|
const dataRef = context.dataRef;
|
|
@@ -31,11 +92,11 @@ export function useTypeahead(...args: any[]): any {
|
|
|
31
92
|
|
|
32
93
|
const timeoutIdRef = useRef(-1, subSlot(slot, 'timeout'));
|
|
33
94
|
const stringRef = useRef('', subSlot(slot, 'string'));
|
|
34
|
-
const prevIndexRef = useRef(
|
|
95
|
+
const prevIndexRef = useRef<number | null>(
|
|
35
96
|
(selectedIndex != null ? selectedIndex : activeIndex) ?? -1,
|
|
36
97
|
subSlot(slot, 'prev'),
|
|
37
98
|
);
|
|
38
|
-
const matchIndexRef = useRef<
|
|
99
|
+
const matchIndexRef = useRef<number | null>(null, subSlot(slot, 'match'));
|
|
39
100
|
|
|
40
101
|
const onMatch = useEffectEvent(unstableOnMatch, subSlot(slot, 'onmatch'));
|
|
41
102
|
const onTypingChange = useEffectEvent(unstableOnTypingChange, subSlot(slot, 'ontyping'));
|
|
@@ -65,7 +126,7 @@ export function useTypeahead(...args: any[]): any {
|
|
|
65
126
|
);
|
|
66
127
|
|
|
67
128
|
const setTypingChange = useEffectEvent(
|
|
68
|
-
(value:
|
|
129
|
+
(value: boolean) => {
|
|
69
130
|
if (value) {
|
|
70
131
|
if (!dataRef.current.typing) {
|
|
71
132
|
dataRef.current.typing = value;
|
|
@@ -82,8 +143,12 @@ export function useTypeahead(...args: any[]): any {
|
|
|
82
143
|
);
|
|
83
144
|
|
|
84
145
|
const onKeyDown = useEffectEvent(
|
|
85
|
-
(event:
|
|
86
|
-
function getMatchingIndex(
|
|
146
|
+
(event: KeyboardEvent) => {
|
|
147
|
+
function getMatchingIndex(
|
|
148
|
+
list: Array<string | null>,
|
|
149
|
+
orderedList: Array<string | null>,
|
|
150
|
+
string: string,
|
|
151
|
+
) {
|
|
87
152
|
const str = findMatchRef.current
|
|
88
153
|
? findMatchRef.current(orderedList, string)
|
|
89
154
|
: orderedList.find(
|
|
@@ -150,7 +215,7 @@ export function useTypeahead(...args: any[]): any {
|
|
|
150
215
|
const floating = useMemo(
|
|
151
216
|
() => ({
|
|
152
217
|
onKeyDown,
|
|
153
|
-
onKeyUp(event:
|
|
218
|
+
onKeyUp(event: KeyboardEvent) {
|
|
154
219
|
if (event.key === ' ') {
|
|
155
220
|
setTypingChange(false);
|
|
156
221
|
}
|
|
@@ -160,7 +225,7 @@ export function useTypeahead(...args: any[]): any {
|
|
|
160
225
|
subSlot(slot, 'm:flo'),
|
|
161
226
|
);
|
|
162
227
|
|
|
163
|
-
return useMemo(
|
|
228
|
+
return useMemo<ElementProps>(
|
|
164
229
|
() => (enabled ? { reference, floating } : {}),
|
|
165
230
|
[enabled, reference, floating],
|
|
166
231
|
subSlot(slot, 'm:ret'),
|