@pushwoosh/dumb-components 1.1.142 → 1.1.144
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/Combobox/Combobox.js +45 -18
- package/Combobox/index.d.ts +1 -1
- package/Combobox/types.d.ts +12 -5
- package/ComboboxMulti/ComboboxMulti.d.ts +1 -1
- package/ComboboxMulti/ComboboxMulti.js +15 -6
- package/ComboboxMulti/types.d.ts +4 -3
- package/index.d.ts +1 -1
- package/package.json +2 -2
- package/shared/dropdownField/FieldShell.d.ts +4 -1
- package/shared/dropdownField/FieldShell.js +7 -0
- package/shared/dropdownField/SuggestionsDropdown.d.ts +5 -1
- package/shared/dropdownField/SuggestionsDropdown.js +23 -5
- package/shared/dropdownField/index.d.ts +2 -2
- package/shared/dropdownField/index.js +1 -1
- package/shared/dropdownField/shared.d.ts +3 -1
- package/shared/dropdownField/shared.js +3 -0
- package/shared/dropdownField/styles.d.ts +1 -0
- package/shared/dropdownField/styles.js +5 -1
- package/shared/dropdownField/types.d.ts +9 -1
- package/shared/dropdownField/useAsyncItems.d.ts +7 -2
- package/shared/dropdownField/useAsyncItems.js +52 -20
- package/shared/dropdownField/useDropdownItems.d.ts +7 -2
- package/shared/dropdownField/useDropdownItems.js +9 -3
- package/shared/dropdownField/useListNavigation.d.ts +3 -1
- package/shared/dropdownField/useListNavigation.js +20 -3
package/Combobox/Combobox.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useId, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { DEFAULT_DEBOUNCE_MS, FieldInput, FieldShell, defaultGetKey, useActiveIndex, useDropdownItems, useFieldOpenEffects, useListNavigation, useShellMouseDown } from '../shared/dropdownField';
|
|
3
|
+
import { DEFAULT_DEBOUNCE_MS, FieldInput, FieldShell, defaultGetKey, resolveDropdownText, useActiveIndex, useDropdownItems, useFieldOpenEffects, useListNavigation, useShellMouseDown } from '../shared/dropdownField';
|
|
4
4
|
function FreeCombobox({
|
|
5
5
|
items,
|
|
6
6
|
loadItems,
|
|
7
7
|
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
8
|
+
minChars,
|
|
8
9
|
value,
|
|
9
10
|
onChange,
|
|
10
11
|
onClose,
|
|
@@ -12,13 +13,14 @@ function FreeCombobox({
|
|
|
12
13
|
getLabel: getLabelProp,
|
|
13
14
|
filter,
|
|
14
15
|
renderItem,
|
|
15
|
-
|
|
16
|
+
emptyText,
|
|
17
|
+
loadingText,
|
|
16
18
|
placeholder,
|
|
17
19
|
disabled,
|
|
18
20
|
loading,
|
|
19
21
|
clearable,
|
|
20
22
|
autosize,
|
|
21
|
-
|
|
23
|
+
hasError,
|
|
22
24
|
width,
|
|
23
25
|
minWidth
|
|
24
26
|
}) {
|
|
@@ -29,11 +31,14 @@ function FreeCombobox({
|
|
|
29
31
|
const [isOpen, setIsOpen] = useState(false);
|
|
30
32
|
const {
|
|
31
33
|
filteredItems,
|
|
32
|
-
loading: asyncLoading
|
|
34
|
+
loading: asyncLoading,
|
|
35
|
+
loadingMore,
|
|
36
|
+
loadMore
|
|
33
37
|
} = useDropdownItems({
|
|
34
38
|
items,
|
|
35
39
|
loadItems,
|
|
36
40
|
debounceMs,
|
|
41
|
+
minChars,
|
|
37
42
|
getLabel,
|
|
38
43
|
filter,
|
|
39
44
|
query: value,
|
|
@@ -46,7 +51,9 @@ function FreeCombobox({
|
|
|
46
51
|
setActiveIndex(-1);
|
|
47
52
|
}, [setActiveIndex]);
|
|
48
53
|
const pick = useCallback(item => {
|
|
49
|
-
onChange(getLabel(item)
|
|
54
|
+
onChange(getLabel(item), {
|
|
55
|
+
source: 'select'
|
|
56
|
+
});
|
|
50
57
|
setIsOpen(false);
|
|
51
58
|
setActiveIndex(-1);
|
|
52
59
|
}, [onChange, getLabel, setActiveIndex]);
|
|
@@ -57,7 +64,9 @@ function FreeCombobox({
|
|
|
57
64
|
activeIndex,
|
|
58
65
|
setActiveIndex,
|
|
59
66
|
onSelectIndex: index => pick(filteredItems[index]),
|
|
60
|
-
onEscape: close
|
|
67
|
+
onEscape: close,
|
|
68
|
+
onSubmit: close,
|
|
69
|
+
onReachEnd: loadMore
|
|
61
70
|
});
|
|
62
71
|
const onShellMouseDown = useShellMouseDown(inputRef, isOpen, open);
|
|
63
72
|
useFieldOpenEffects({
|
|
@@ -82,18 +91,22 @@ function FreeCombobox({
|
|
|
82
91
|
onSelect: () => pick(item)
|
|
83
92
|
};
|
|
84
93
|
}), [filteredItems, activeIndex, value, getLabel, renderItem, pick, baseId]);
|
|
85
|
-
const
|
|
94
|
+
const isLoading = loading || asyncLoading;
|
|
95
|
+
const hasSource = items !== undefined || loadItems !== undefined;
|
|
96
|
+
const dropdownVisible = isOpen && (filteredItems.length > 0 || isLoading || hasSource);
|
|
86
97
|
const activeDescendant = activeIndex >= 0 && activeIndex < filteredItems.length ? `${baseId}-opt-${filteredItems[activeIndex]}` : undefined;
|
|
87
98
|
const onInputChange = event => {
|
|
88
|
-
onChange(event.target.value
|
|
99
|
+
onChange(event.target.value, {
|
|
100
|
+
source: 'input'
|
|
101
|
+
});
|
|
89
102
|
if (!isOpen) open();
|
|
90
103
|
};
|
|
91
104
|
return _jsx(FieldShell, {
|
|
92
105
|
isOpen: isOpen,
|
|
93
106
|
dropdownOpen: dropdownVisible,
|
|
94
|
-
isErrored:
|
|
107
|
+
isErrored: hasError,
|
|
95
108
|
disabled: disabled,
|
|
96
|
-
loading:
|
|
109
|
+
loading: isLoading,
|
|
97
110
|
width: width,
|
|
98
111
|
minWidth: minWidth,
|
|
99
112
|
autosize: autosize,
|
|
@@ -102,11 +115,16 @@ function FreeCombobox({
|
|
|
102
115
|
showClear: !!(clearable && value),
|
|
103
116
|
onClear: () => {
|
|
104
117
|
var _inputRef$current;
|
|
105
|
-
onChange(''
|
|
118
|
+
onChange('', {
|
|
119
|
+
source: 'input'
|
|
120
|
+
});
|
|
106
121
|
(_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
|
|
107
122
|
},
|
|
108
123
|
dropdownItems: dropdownItems,
|
|
109
|
-
emptyText:
|
|
124
|
+
emptyText: resolveDropdownText(emptyText, value),
|
|
125
|
+
loadingText: resolveDropdownText(loadingText, value),
|
|
126
|
+
loadingMore: loadingMore,
|
|
127
|
+
onLoadMore: loadMore,
|
|
110
128
|
listboxId: listboxId,
|
|
111
129
|
children: _jsx(FieldInput, {
|
|
112
130
|
ref: inputRef,
|
|
@@ -129,6 +147,7 @@ function SelectCombobox({
|
|
|
129
147
|
items,
|
|
130
148
|
loadItems,
|
|
131
149
|
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
150
|
+
minChars,
|
|
132
151
|
value,
|
|
133
152
|
onChange,
|
|
134
153
|
onClose,
|
|
@@ -137,13 +156,14 @@ function SelectCombobox({
|
|
|
137
156
|
getKey: getKeyProp,
|
|
138
157
|
filter,
|
|
139
158
|
renderItem,
|
|
140
|
-
|
|
159
|
+
emptyText,
|
|
160
|
+
loadingText,
|
|
141
161
|
placeholder,
|
|
142
162
|
disabled,
|
|
143
163
|
loading,
|
|
144
164
|
clearable,
|
|
145
165
|
autosize,
|
|
146
|
-
|
|
166
|
+
hasError,
|
|
147
167
|
width,
|
|
148
168
|
minWidth
|
|
149
169
|
}) {
|
|
@@ -157,11 +177,14 @@ function SelectCombobox({
|
|
|
157
177
|
const {
|
|
158
178
|
sourceItems,
|
|
159
179
|
filteredItems,
|
|
160
|
-
loading: asyncLoading
|
|
180
|
+
loading: asyncLoading,
|
|
181
|
+
loadingMore,
|
|
182
|
+
loadMore
|
|
161
183
|
} = useDropdownItems({
|
|
162
184
|
items,
|
|
163
185
|
loadItems,
|
|
164
186
|
debounceMs,
|
|
187
|
+
minChars,
|
|
165
188
|
getLabel,
|
|
166
189
|
filter,
|
|
167
190
|
query,
|
|
@@ -198,7 +221,8 @@ function SelectCombobox({
|
|
|
198
221
|
activeIndex,
|
|
199
222
|
setActiveIndex,
|
|
200
223
|
onSelectIndex: index => commit(filteredItems[index]),
|
|
201
|
-
onEscape: close
|
|
224
|
+
onEscape: close,
|
|
225
|
+
onReachEnd: loadMore
|
|
202
226
|
});
|
|
203
227
|
const onShellMouseDown = useShellMouseDown(inputRef, isOpen, open);
|
|
204
228
|
useFieldOpenEffects({
|
|
@@ -228,7 +252,7 @@ function SelectCombobox({
|
|
|
228
252
|
return _jsx(FieldShell, {
|
|
229
253
|
isOpen: isOpen,
|
|
230
254
|
dropdownOpen: isOpen,
|
|
231
|
-
isErrored:
|
|
255
|
+
isErrored: hasError,
|
|
232
256
|
disabled: disabled,
|
|
233
257
|
loading: loading || asyncLoading,
|
|
234
258
|
width: width,
|
|
@@ -243,7 +267,10 @@ function SelectCombobox({
|
|
|
243
267
|
(_inputRef$current3 = inputRef.current) === null || _inputRef$current3 === void 0 || _inputRef$current3.focus();
|
|
244
268
|
},
|
|
245
269
|
dropdownItems: dropdownItems,
|
|
246
|
-
emptyText:
|
|
270
|
+
emptyText: resolveDropdownText(emptyText, query),
|
|
271
|
+
loadingText: resolveDropdownText(loadingText, query),
|
|
272
|
+
loadingMore: loadingMore,
|
|
273
|
+
onLoadMore: loadMore,
|
|
247
274
|
listboxId: listboxId,
|
|
248
275
|
children: _jsx(FieldInput, {
|
|
249
276
|
ref: inputRef,
|
package/Combobox/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Combobox } from './Combobox';
|
|
2
|
-
export type { ComboboxProps, FreeComboboxProps, SelectComboboxProps, } from './types';
|
|
2
|
+
export type { ComboboxProps, ComboboxChangeSource, ComboboxChangeMeta, FreeComboboxProps, SelectComboboxProps, } from './types';
|
package/Combobox/types.d.ts
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { GetKeyProp, ItemState } from '../shared/dropdownField';
|
|
2
|
+
import type { DropdownText, GetKeyProp, ItemState, LoadItems } from '../shared/dropdownField';
|
|
3
3
|
type ComboboxSource<T> = {
|
|
4
4
|
items?: T[];
|
|
5
5
|
loadItems?: never;
|
|
6
6
|
debounceMs?: never;
|
|
7
|
+
minChars?: never;
|
|
7
8
|
} | {
|
|
8
9
|
items?: never;
|
|
9
|
-
loadItems:
|
|
10
|
+
loadItems: LoadItems<T>;
|
|
10
11
|
debounceMs?: number;
|
|
12
|
+
minChars?: number;
|
|
13
|
+
};
|
|
14
|
+
export type ComboboxChangeSource = 'input' | 'select';
|
|
15
|
+
export type ComboboxChangeMeta = {
|
|
16
|
+
source: ComboboxChangeSource;
|
|
11
17
|
};
|
|
12
18
|
type CommonProps<T> = {
|
|
13
19
|
filter?: (item: T, query: string) => boolean;
|
|
14
20
|
renderItem?: (item: T, state: ItemState) => ReactNode;
|
|
15
|
-
|
|
21
|
+
emptyText?: DropdownText;
|
|
22
|
+
loadingText?: DropdownText;
|
|
16
23
|
onClose?: () => void;
|
|
17
24
|
autoFocus?: boolean;
|
|
18
25
|
placeholder?: string;
|
|
@@ -20,14 +27,14 @@ type CommonProps<T> = {
|
|
|
20
27
|
loading?: boolean;
|
|
21
28
|
clearable?: boolean;
|
|
22
29
|
autosize?: boolean;
|
|
23
|
-
|
|
30
|
+
hasError?: boolean;
|
|
24
31
|
width?: string;
|
|
25
32
|
minWidth?: string;
|
|
26
33
|
};
|
|
27
34
|
export type FreeComboboxProps = ComboboxSource<string> & CommonProps<string> & {
|
|
28
35
|
select?: false;
|
|
29
36
|
value: string;
|
|
30
|
-
onChange: (value: string) => void;
|
|
37
|
+
onChange: (value: string, meta: ComboboxChangeMeta) => void;
|
|
31
38
|
getLabel?: (item: string) => string;
|
|
32
39
|
};
|
|
33
40
|
export type SelectComboboxProps<T> = ComboboxSource<T> & GetKeyProp<T> & CommonProps<T> & {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ReactElement } from 'react';
|
|
2
2
|
import { type ComboboxMultiProps } from './types';
|
|
3
|
-
export declare function ComboboxMulti<T>({ items, loadItems, debounceMs, value, onChange, onClose, autoFocus, getLabel, getKey: getKeyProp, filter, renderItem,
|
|
3
|
+
export declare function ComboboxMulti<T>({ items, loadItems, debounceMs, minChars, value, onChange, onClose, autoFocus, getLabel, getKey: getKeyProp, filter, renderItem, emptyText, loadingText, creatable, onCreate, canCreate, renderCreateLabel, maxValues, placeholder, disabled, loading, clearable, autosize, hasError, width, minWidth, }: ComboboxMultiProps<T>): ReactElement;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useId, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Chip } from '../Chip';
|
|
4
|
-
import { DEFAULT_DEBOUNCE_MS, FieldInput, FieldShell, defaultGetKey, useActiveIndex, useDropdownItems, useFieldOpenEffects, useListNavigation, useShellMouseDown } from '../shared/dropdownField';
|
|
4
|
+
import { DEFAULT_DEBOUNCE_MS, FieldInput, FieldShell, defaultGetKey, resolveDropdownText, useActiveIndex, useDropdownItems, useFieldOpenEffects, useListNavigation, useShellMouseDown } from '../shared/dropdownField';
|
|
5
5
|
function defaultCanCreate(query, items, getLabel) {
|
|
6
6
|
const trimmed = query.trim();
|
|
7
7
|
if (!trimmed) return false;
|
|
@@ -12,6 +12,7 @@ export function ComboboxMulti({
|
|
|
12
12
|
items,
|
|
13
13
|
loadItems,
|
|
14
14
|
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
15
|
+
minChars,
|
|
15
16
|
value,
|
|
16
17
|
onChange,
|
|
17
18
|
onClose,
|
|
@@ -20,7 +21,8 @@ export function ComboboxMulti({
|
|
|
20
21
|
getKey: getKeyProp,
|
|
21
22
|
filter,
|
|
22
23
|
renderItem,
|
|
23
|
-
|
|
24
|
+
emptyText,
|
|
25
|
+
loadingText,
|
|
24
26
|
creatable,
|
|
25
27
|
onCreate,
|
|
26
28
|
canCreate,
|
|
@@ -31,7 +33,7 @@ export function ComboboxMulti({
|
|
|
31
33
|
loading,
|
|
32
34
|
clearable,
|
|
33
35
|
autosize,
|
|
34
|
-
|
|
36
|
+
hasError,
|
|
35
37
|
width,
|
|
36
38
|
minWidth
|
|
37
39
|
}) {
|
|
@@ -48,12 +50,15 @@ export function ComboboxMulti({
|
|
|
48
50
|
const {
|
|
49
51
|
sourceItems,
|
|
50
52
|
filteredItems,
|
|
51
|
-
loading: asyncLoading
|
|
53
|
+
loading: asyncLoading,
|
|
54
|
+
loadingMore,
|
|
55
|
+
loadMore
|
|
52
56
|
} = useDropdownItems({
|
|
53
57
|
items,
|
|
54
58
|
loadItems,
|
|
55
59
|
localItems: createdItems,
|
|
56
60
|
debounceMs,
|
|
61
|
+
minChars,
|
|
57
62
|
getLabel,
|
|
58
63
|
filter,
|
|
59
64
|
query,
|
|
@@ -103,6 +108,7 @@ export function ComboboxMulti({
|
|
|
103
108
|
setActiveIndex,
|
|
104
109
|
onSelectIndex: index => toggle(visibleItems[index]),
|
|
105
110
|
onEscape: close,
|
|
111
|
+
onReachEnd: loadMore,
|
|
106
112
|
hasExtraRow: canCreateNow,
|
|
107
113
|
onSelectExtra: create,
|
|
108
114
|
isQueryEmpty: !query && value.length > 0,
|
|
@@ -155,7 +161,7 @@ export function ComboboxMulti({
|
|
|
155
161
|
return _jsxs(FieldShell, {
|
|
156
162
|
isOpen: isOpen,
|
|
157
163
|
dropdownOpen: isOpen,
|
|
158
|
-
isErrored:
|
|
164
|
+
isErrored: hasError,
|
|
159
165
|
disabled: disabled,
|
|
160
166
|
loading: loading || asyncLoading,
|
|
161
167
|
width: width,
|
|
@@ -170,7 +176,10 @@ export function ComboboxMulti({
|
|
|
170
176
|
showChevron: true,
|
|
171
177
|
onToggle: toggleOpen,
|
|
172
178
|
dropdownItems: dropdownItems,
|
|
173
|
-
emptyText:
|
|
179
|
+
emptyText: resolveDropdownText(emptyText, query),
|
|
180
|
+
loadingText: resolveDropdownText(loadingText, query),
|
|
181
|
+
loadingMore: loadingMore,
|
|
182
|
+
onLoadMore: loadMore,
|
|
174
183
|
createLabel: createLabel,
|
|
175
184
|
createActive: createActive,
|
|
176
185
|
onCreate: create,
|
package/ComboboxMulti/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { GetKeyProp, ItemState, ItemsSource } from '../shared/dropdownField';
|
|
2
|
+
import type { DropdownText, GetKeyProp, ItemState, ItemsSource } from '../shared/dropdownField';
|
|
3
3
|
type CreatableProp<T> = [T] extends [string] ? {
|
|
4
4
|
creatable?: boolean;
|
|
5
5
|
onCreate?: (query: string) => T;
|
|
@@ -18,7 +18,8 @@ export type ComboboxMultiProps<T> = ItemsSource<T> & GetKeyProp<T> & CreatablePr
|
|
|
18
18
|
getLabel: (item: T) => string;
|
|
19
19
|
filter?: (item: T, query: string) => boolean;
|
|
20
20
|
renderItem?: (item: T, state: ItemState) => ReactNode;
|
|
21
|
-
|
|
21
|
+
emptyText?: DropdownText;
|
|
22
|
+
loadingText?: DropdownText;
|
|
22
23
|
canCreate?: (query: string, items: T[]) => boolean;
|
|
23
24
|
renderCreateLabel?: (query: string) => ReactNode;
|
|
24
25
|
maxValues?: number;
|
|
@@ -27,7 +28,7 @@ export type ComboboxMultiProps<T> = ItemsSource<T> & GetKeyProp<T> & CreatablePr
|
|
|
27
28
|
loading?: boolean;
|
|
28
29
|
clearable?: boolean;
|
|
29
30
|
autosize?: boolean;
|
|
30
|
-
|
|
31
|
+
hasError?: boolean;
|
|
31
32
|
width?: string;
|
|
32
33
|
minWidth?: string;
|
|
33
34
|
};
|
package/index.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export { ReCaptcha, useRecaptcha, asyncScriptLoad } from './ReCaptcha';
|
|
|
22
22
|
export { Spinner } from './Spinner';
|
|
23
23
|
export { SortButton, type SortButtonParams } from './SortButton';
|
|
24
24
|
export { Select, SelectAsync, CreatableSelect, SelectComponents, CLASS_NAME_PREFIX, type SelectBase, type SelectOption, type MultiSelectOptions, type SingleValue, type MultiValue, } from './Select';
|
|
25
|
-
export { Combobox, type ComboboxProps, type FreeComboboxProps, type SelectComboboxProps, } from './Combobox';
|
|
25
|
+
export { Combobox, type ComboboxProps, type ComboboxChangeSource, type ComboboxChangeMeta, type FreeComboboxProps, type SelectComboboxProps, } from './Combobox';
|
|
26
26
|
export { ComboboxMulti, type ComboboxMultiProps } from './ComboboxMulti';
|
|
27
27
|
export { Switch } from './Switch';
|
|
28
28
|
export { TextSkeleton, RectangularSkeleton, CircularSkeleton } from './Skeleton';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushwoosh/dumb-components",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.144",
|
|
4
4
|
"description": "React components to build Pushwoosh products",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.js",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@types/react": "^18.2.69",
|
|
32
32
|
"@types/react-dom": "^18.2.22",
|
|
33
33
|
"@types/react-transition-group": "^4.4.12",
|
|
34
|
-
"@types/styled-components": "^5.1.
|
|
34
|
+
"@types/styled-components": "^5.1.36",
|
|
35
35
|
"glob": "^11.0.3",
|
|
36
36
|
"jest": "^30.2.0",
|
|
37
37
|
"jest-environment-jsdom": "^30.2.0",
|
|
@@ -20,11 +20,14 @@ type FieldShellProps = {
|
|
|
20
20
|
onToggle?: () => void;
|
|
21
21
|
dropdownItems: DropdownItemDescriptor[];
|
|
22
22
|
emptyText?: ReactNode;
|
|
23
|
+
loadingText?: ReactNode;
|
|
24
|
+
loadingMore?: boolean;
|
|
25
|
+
onLoadMore?: () => void;
|
|
23
26
|
createLabel?: ReactNode;
|
|
24
27
|
createActive?: boolean;
|
|
25
28
|
onCreate?: () => void;
|
|
26
29
|
listboxId?: string;
|
|
27
30
|
createId?: string;
|
|
28
31
|
};
|
|
29
|
-
export declare function FieldShell({ children, isOpen, dropdownOpen, isErrored, disabled, loading, width, minWidth, autosize, tightLeft, onShellMouseDown, onClose, showClear, onClear, clearLabel, showChevron, onToggle, dropdownItems, emptyText, createLabel, createActive, onCreate, listboxId, createId, }: FieldShellProps): ReactElement;
|
|
32
|
+
export declare function FieldShell({ children, isOpen, dropdownOpen, isErrored, disabled, loading, width, minWidth, autosize, tightLeft, onShellMouseDown, onClose, showClear, onClear, clearLabel, showChevron, onToggle, dropdownItems, emptyText, loadingText, loadingMore, onLoadMore, createLabel, createActive, onCreate, listboxId, createId, }: FieldShellProps): ReactElement;
|
|
30
33
|
export {};
|
|
@@ -24,6 +24,9 @@ export function FieldShell({
|
|
|
24
24
|
onToggle,
|
|
25
25
|
dropdownItems,
|
|
26
26
|
emptyText,
|
|
27
|
+
loadingText,
|
|
28
|
+
loadingMore,
|
|
29
|
+
onLoadMore,
|
|
27
30
|
createLabel,
|
|
28
31
|
createActive,
|
|
29
32
|
onCreate,
|
|
@@ -99,6 +102,10 @@ export function FieldShell({
|
|
|
99
102
|
floatingStyles: floatingStyles,
|
|
100
103
|
items: dropdownItems,
|
|
101
104
|
emptyText: emptyText,
|
|
105
|
+
loading: loading,
|
|
106
|
+
loadingText: loadingText,
|
|
107
|
+
loadingMore: loadingMore,
|
|
108
|
+
onLoadMore: onLoadMore,
|
|
102
109
|
createLabel: createLabel,
|
|
103
110
|
createActive: createActive,
|
|
104
111
|
onCreate: onCreate,
|
|
@@ -5,6 +5,10 @@ type SuggestionsDropdownProps = {
|
|
|
5
5
|
floatingStyles: CSSProperties;
|
|
6
6
|
items: DropdownItemDescriptor[];
|
|
7
7
|
emptyText?: ReactNode;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
loadingText?: ReactNode;
|
|
10
|
+
loadingMore?: boolean;
|
|
11
|
+
onLoadMore?: () => void;
|
|
8
12
|
createLabel?: ReactNode;
|
|
9
13
|
createActive?: boolean;
|
|
10
14
|
onCreate?: () => void;
|
|
@@ -12,5 +16,5 @@ type SuggestionsDropdownProps = {
|
|
|
12
16
|
createId?: string;
|
|
13
17
|
zIndex?: number;
|
|
14
18
|
};
|
|
15
|
-
export declare function SuggestionsDropdown({ floatingRef, floatingStyles, items, emptyText, createLabel, createActive, onCreate, listboxId, createId, zIndex, }: SuggestionsDropdownProps): ReactElement | null;
|
|
19
|
+
export declare function SuggestionsDropdown({ floatingRef, floatingStyles, items, emptyText, loading, loadingText, loadingMore, onLoadMore, createLabel, createActive, onCreate, listboxId, createId, zIndex, }: SuggestionsDropdownProps): ReactElement | null;
|
|
16
20
|
export {};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
|
-
import { CreateItemLabel, Dropdown, DropdownEmpty, DropdownItem } from './styles';
|
|
4
|
+
import { CreateItemLabel, Dropdown, DropdownEmpty, DropdownItem, DropdownLoadingMore } from './styles';
|
|
5
|
+
import { Spinner } from '../../Spinner';
|
|
6
|
+
const LOAD_MORE_THRESHOLD = 24;
|
|
5
7
|
const CREATE_KEY = '__create__';
|
|
6
8
|
export function SuggestionsDropdown({
|
|
7
9
|
floatingRef,
|
|
8
10
|
floatingStyles,
|
|
9
11
|
items,
|
|
10
12
|
emptyText = 'No options',
|
|
13
|
+
loading,
|
|
14
|
+
loadingText = 'Loading…',
|
|
15
|
+
loadingMore,
|
|
16
|
+
onLoadMore,
|
|
11
17
|
createLabel,
|
|
12
18
|
createActive,
|
|
13
19
|
onCreate,
|
|
@@ -16,6 +22,11 @@ export function SuggestionsDropdown({
|
|
|
16
22
|
zIndex
|
|
17
23
|
}) {
|
|
18
24
|
const activeItemRef = useRef(null);
|
|
25
|
+
const onScroll = event => {
|
|
26
|
+
if (!onLoadMore) return;
|
|
27
|
+
const el = event.currentTarget;
|
|
28
|
+
if (el.scrollHeight - el.scrollTop - el.clientHeight <= LOAD_MORE_THRESHOLD) onLoadMore();
|
|
29
|
+
};
|
|
19
30
|
const activeItem = items.find(item => item.active);
|
|
20
31
|
const activeKey = createActive ? CREATE_KEY : (activeItem === null || activeItem === void 0 ? void 0 : activeItem.key) ?? null;
|
|
21
32
|
useEffect(() => {
|
|
@@ -26,7 +37,7 @@ export function SuggestionsDropdown({
|
|
|
26
37
|
});
|
|
27
38
|
}, [activeKey]);
|
|
28
39
|
if (typeof document === 'undefined') return null;
|
|
29
|
-
const isEmpty = items.length === 0 && !createLabel;
|
|
40
|
+
const isEmpty = !loading && items.length === 0 && !createLabel;
|
|
30
41
|
return createPortal(_jsxs(Dropdown, {
|
|
31
42
|
ref: floatingRef,
|
|
32
43
|
style: {
|
|
@@ -35,7 +46,10 @@ export function SuggestionsDropdown({
|
|
|
35
46
|
},
|
|
36
47
|
id: listboxId,
|
|
37
48
|
role: "listbox",
|
|
38
|
-
|
|
49
|
+
onScroll: onScroll,
|
|
50
|
+
children: [loading && _jsx(DropdownEmpty, {
|
|
51
|
+
children: loadingText
|
|
52
|
+
}), !loading && createLabel && _jsx(DropdownItem, {
|
|
39
53
|
ref: createActive ? activeItemRef : undefined,
|
|
40
54
|
id: createId,
|
|
41
55
|
role: "option",
|
|
@@ -48,7 +62,7 @@ export function SuggestionsDropdown({
|
|
|
48
62
|
children: _jsx(CreateItemLabel, {
|
|
49
63
|
children: createLabel
|
|
50
64
|
})
|
|
51
|
-
}), items.map(item => _jsx(DropdownItem, {
|
|
65
|
+
}), !loading && items.map(item => _jsx(DropdownItem, {
|
|
52
66
|
ref: item.active ? activeItemRef : undefined,
|
|
53
67
|
id: item.id,
|
|
54
68
|
role: "option",
|
|
@@ -61,7 +75,11 @@ export function SuggestionsDropdown({
|
|
|
61
75
|
(_item$onSelect = item.onSelect) === null || _item$onSelect === void 0 || _item$onSelect.call(item);
|
|
62
76
|
},
|
|
63
77
|
children: item.label
|
|
64
|
-
}, item.key)),
|
|
78
|
+
}, item.key)), !loading && loadingMore && _jsx(DropdownLoadingMore, {
|
|
79
|
+
children: _jsx(Spinner, {
|
|
80
|
+
size: "small"
|
|
81
|
+
})
|
|
82
|
+
}), isEmpty && _jsx(DropdownEmpty, {
|
|
65
83
|
children: emptyText
|
|
66
84
|
})]
|
|
67
85
|
}), document.body);
|
|
@@ -8,6 +8,6 @@ export { useDropdownItems } from './useDropdownItems';
|
|
|
8
8
|
export { useActiveIndex } from './useActiveIndex';
|
|
9
9
|
export { useFieldOpenEffects } from './useFieldOpenEffects';
|
|
10
10
|
export { useListNavigation } from './useListNavigation';
|
|
11
|
-
export { defaultGetKey, defaultLabelFilter, useShellMouseDown } from './shared';
|
|
11
|
+
export { defaultGetKey, defaultLabelFilter, resolveDropdownText, useShellMouseDown, } from './shared';
|
|
12
12
|
export { DEFAULT_DEBOUNCE_MS } from './constants';
|
|
13
|
-
export type { ItemState, DropdownItemDescriptor, GetKeyProp, ItemsSource, } from './types';
|
|
13
|
+
export type { ItemState, DropdownItemDescriptor, DropdownText, GetKeyProp, ItemsSource, LoadItems, LoadItemsResult, } from './types';
|
|
@@ -8,5 +8,5 @@ export { useDropdownItems } from './useDropdownItems';
|
|
|
8
8
|
export { useActiveIndex } from './useActiveIndex';
|
|
9
9
|
export { useFieldOpenEffects } from './useFieldOpenEffects';
|
|
10
10
|
export { useListNavigation } from './useListNavigation';
|
|
11
|
-
export { defaultGetKey, defaultLabelFilter, useShellMouseDown } from './shared';
|
|
11
|
+
export { defaultGetKey, defaultLabelFilter, resolveDropdownText, useShellMouseDown } from './shared';
|
|
12
12
|
export { DEFAULT_DEBOUNCE_MS } from './constants';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { type MouseEvent, type RefObject } from 'react';
|
|
1
|
+
import { type MouseEvent, type ReactNode, type RefObject } from 'react';
|
|
2
|
+
import { type DropdownText } from './types';
|
|
3
|
+
export declare function resolveDropdownText(text: DropdownText, query: string): ReactNode;
|
|
2
4
|
export declare function defaultGetKey<T>(item: T): string | number;
|
|
3
5
|
export declare function defaultLabelFilter<T>(item: T, query: string, getLabel: (item: T) => string): boolean;
|
|
4
6
|
export declare function useShellMouseDown(inputRef: RefObject<HTMLInputElement>, isOpen: boolean, open: () => void): (event: MouseEvent<HTMLDivElement>) => void;
|
|
@@ -21,4 +21,5 @@ export declare const DropdownItem: import("styled-components").StyledComponent<"
|
|
|
21
21
|
$selected?: boolean;
|
|
22
22
|
}, never>;
|
|
23
23
|
export declare const DropdownEmpty: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
24
|
+
export declare const DropdownLoadingMore: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
24
25
|
export declare const CreateItemLabel: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
@@ -75,7 +75,11 @@ export const DropdownEmpty = styled.div.withConfig({
|
|
|
75
75
|
displayName: "DropdownEmpty",
|
|
76
76
|
componentId: "sc-1pvwhhw-8"
|
|
77
77
|
})(["padding:6px 12px;", ";color:", ";"], textBase, Color.PHANTOM);
|
|
78
|
+
export const DropdownLoadingMore = styled.div.withConfig({
|
|
79
|
+
displayName: "DropdownLoadingMore",
|
|
80
|
+
componentId: "sc-1pvwhhw-9"
|
|
81
|
+
})(["display:flex;align-items:center;justify-content:center;padding:8px 12px;"]);
|
|
78
82
|
export const CreateItemLabel = styled.span.withConfig({
|
|
79
83
|
displayName: "CreateItemLabel",
|
|
80
|
-
componentId: "sc-1pvwhhw-
|
|
84
|
+
componentId: "sc-1pvwhhw-10"
|
|
81
85
|
})(["color:", ";"], Color.PRIMARY);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
export type DropdownText = ReactNode | ((query: string) => ReactNode);
|
|
2
3
|
export type ItemState = {
|
|
3
4
|
selected: boolean;
|
|
4
5
|
active: boolean;
|
|
@@ -8,14 +9,21 @@ export type GetKeyProp<T> = [T] extends [string | number] ? {
|
|
|
8
9
|
} : {
|
|
9
10
|
getKey: (item: T) => string | number;
|
|
10
11
|
};
|
|
12
|
+
export type LoadItemsResult<T> = {
|
|
13
|
+
items: T[];
|
|
14
|
+
hasMore?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type LoadItems<T> = (query: string, page: number) => Promise<LoadItemsResult<T>>;
|
|
11
17
|
export type ItemsSource<T> = {
|
|
12
18
|
items: T[];
|
|
13
19
|
loadItems?: never;
|
|
14
20
|
debounceMs?: never;
|
|
21
|
+
minChars?: never;
|
|
15
22
|
} | {
|
|
16
23
|
items?: never;
|
|
17
|
-
loadItems:
|
|
24
|
+
loadItems: LoadItems<T>;
|
|
18
25
|
debounceMs?: number;
|
|
26
|
+
minChars?: number;
|
|
19
27
|
};
|
|
20
28
|
export type DropdownItemDescriptor = {
|
|
21
29
|
key: string | number;
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { type LoadItems } from './types';
|
|
1
2
|
type Args<T> = {
|
|
2
|
-
loadItems?:
|
|
3
|
+
loadItems?: LoadItems<T>;
|
|
3
4
|
query: string;
|
|
4
5
|
isOpen: boolean;
|
|
5
6
|
debounceMs?: number;
|
|
7
|
+
minChars?: number;
|
|
6
8
|
};
|
|
7
|
-
|
|
9
|
+
type Result<T> = {
|
|
8
10
|
items: T[];
|
|
9
11
|
loading: boolean;
|
|
12
|
+
loadingMore: boolean;
|
|
13
|
+
loadMore: () => void;
|
|
10
14
|
};
|
|
15
|
+
export declare function useAsyncItems<T>({ loadItems, query, isOpen, debounceMs, minChars, }: Args<T>): Result<T>;
|
|
11
16
|
export {};
|
|
@@ -1,46 +1,78 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { DEFAULT_DEBOUNCE_MS } from './constants';
|
|
3
3
|
import { useDebouncedValue } from './useDebouncedValue';
|
|
4
4
|
export function useAsyncItems({
|
|
5
5
|
loadItems,
|
|
6
6
|
query,
|
|
7
7
|
isOpen,
|
|
8
|
-
debounceMs = DEFAULT_DEBOUNCE_MS
|
|
8
|
+
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
9
|
+
minChars = 0
|
|
9
10
|
}) {
|
|
10
11
|
const [items, setItems] = useState([]);
|
|
11
12
|
const [loading, setLoading] = useState(false);
|
|
13
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
12
14
|
const debouncedQuery = useDebouncedValue(query, debounceMs);
|
|
13
15
|
const loadItemsRef = useRef(loadItems);
|
|
14
16
|
loadItemsRef.current = loadItems;
|
|
15
|
-
const
|
|
17
|
+
const sessionRef = useRef(0);
|
|
18
|
+
const pageRef = useRef(0);
|
|
19
|
+
const queryRef = useRef('');
|
|
20
|
+
const loadingRef = useRef(false);
|
|
21
|
+
const hasMoreRef = useRef(false);
|
|
16
22
|
useEffect(() => {
|
|
17
23
|
const loader = loadItemsRef.current;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
const belowMin = debouncedQuery.trim().length < minChars;
|
|
25
|
+
sessionRef.current += 1;
|
|
26
|
+
pageRef.current = 0;
|
|
27
|
+
loadingRef.current = false;
|
|
28
|
+
hasMoreRef.current = false;
|
|
29
|
+
setLoadingMore(false);
|
|
30
|
+
if (!loader || !isOpen || belowMin) {
|
|
23
31
|
setLoading(false);
|
|
32
|
+
if (!loader || belowMin) setItems([]);
|
|
24
33
|
return undefined;
|
|
25
34
|
}
|
|
26
|
-
|
|
35
|
+
const session = sessionRef.current;
|
|
36
|
+
queryRef.current = debouncedQuery;
|
|
37
|
+
loadingRef.current = true;
|
|
27
38
|
setLoading(true);
|
|
28
|
-
Promise.resolve(loader(debouncedQuery)).then(result => {
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
Promise.resolve(loader(debouncedQuery, 0)).then(result => {
|
|
40
|
+
if (session !== sessionRef.current) return;
|
|
41
|
+
setItems(result.items);
|
|
42
|
+
hasMoreRef.current = result.hasMore ?? false;
|
|
43
|
+
loadingRef.current = false;
|
|
32
44
|
setLoading(false);
|
|
33
45
|
}).catch(() => {
|
|
34
|
-
if (
|
|
46
|
+
if (session !== sessionRef.current) return;
|
|
47
|
+
loadingRef.current = false;
|
|
35
48
|
setLoading(false);
|
|
36
49
|
});
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
return undefined;
|
|
51
|
+
}, [debouncedQuery, isOpen, minChars]);
|
|
52
|
+
const loadMore = useCallback(() => {
|
|
53
|
+
const loader = loadItemsRef.current;
|
|
54
|
+
if (!loader || loadingRef.current || !hasMoreRef.current) return;
|
|
55
|
+
const session = sessionRef.current;
|
|
56
|
+
const nextPage = pageRef.current + 1;
|
|
57
|
+
loadingRef.current = true;
|
|
58
|
+
setLoadingMore(true);
|
|
59
|
+
Promise.resolve(loader(queryRef.current, nextPage)).then(result => {
|
|
60
|
+
if (session !== sessionRef.current) return;
|
|
61
|
+
pageRef.current = nextPage;
|
|
62
|
+
setItems(prev => [...prev, ...result.items]);
|
|
63
|
+
hasMoreRef.current = result.hasMore ?? false;
|
|
64
|
+
loadingRef.current = false;
|
|
65
|
+
setLoadingMore(false);
|
|
66
|
+
}).catch(() => {
|
|
67
|
+
if (session !== sessionRef.current) return;
|
|
68
|
+
loadingRef.current = false;
|
|
69
|
+
setLoadingMore(false);
|
|
70
|
+
});
|
|
71
|
+
}, []);
|
|
42
72
|
return {
|
|
43
73
|
items,
|
|
44
|
-
loading
|
|
74
|
+
loading,
|
|
75
|
+
loadingMore,
|
|
76
|
+
loadMore
|
|
45
77
|
};
|
|
46
78
|
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
import { type LoadItems } from './types';
|
|
1
2
|
type Args<T> = {
|
|
2
3
|
items?: T[];
|
|
3
|
-
loadItems?:
|
|
4
|
+
loadItems?: LoadItems<T>;
|
|
4
5
|
localItems?: T[];
|
|
5
6
|
debounceMs?: number;
|
|
7
|
+
minChars?: number;
|
|
6
8
|
getLabel: (item: T) => string;
|
|
7
9
|
filter?: (item: T, query: string) => boolean;
|
|
8
10
|
query: string;
|
|
9
11
|
isOpen: boolean;
|
|
10
12
|
};
|
|
11
|
-
|
|
13
|
+
type Result<T> = {
|
|
12
14
|
sourceItems: T[];
|
|
13
15
|
filteredItems: T[];
|
|
14
16
|
loading: boolean;
|
|
17
|
+
loadingMore: boolean;
|
|
18
|
+
loadMore: () => void;
|
|
15
19
|
};
|
|
20
|
+
export declare function useDropdownItems<T>({ items, loadItems, localItems, debounceMs, minChars, getLabel, filter, query, isOpen, }: Args<T>): Result<T>;
|
|
16
21
|
export {};
|
|
@@ -7,6 +7,7 @@ export function useDropdownItems({
|
|
|
7
7
|
loadItems,
|
|
8
8
|
localItems,
|
|
9
9
|
debounceMs = DEFAULT_DEBOUNCE_MS,
|
|
10
|
+
minChars,
|
|
10
11
|
getLabel,
|
|
11
12
|
filter,
|
|
12
13
|
query,
|
|
@@ -15,12 +16,15 @@ export function useDropdownItems({
|
|
|
15
16
|
const isAsync = !!loadItems;
|
|
16
17
|
const {
|
|
17
18
|
items: loadedItems,
|
|
18
|
-
loading
|
|
19
|
+
loading,
|
|
20
|
+
loadingMore,
|
|
21
|
+
loadMore
|
|
19
22
|
} = useAsyncItems({
|
|
20
23
|
loadItems,
|
|
21
24
|
query,
|
|
22
25
|
isOpen,
|
|
23
|
-
debounceMs
|
|
26
|
+
debounceMs,
|
|
27
|
+
minChars
|
|
24
28
|
});
|
|
25
29
|
const sourceItems = useMemo(() => isAsync ? loadedItems : [...(items ?? []), ...(localItems ?? [])], [isAsync, loadedItems, items, localItems]);
|
|
26
30
|
const filteredItems = useMemo(() => {
|
|
@@ -31,6 +35,8 @@ export function useDropdownItems({
|
|
|
31
35
|
return {
|
|
32
36
|
sourceItems,
|
|
33
37
|
filteredItems,
|
|
34
|
-
loading
|
|
38
|
+
loading,
|
|
39
|
+
loadingMore,
|
|
40
|
+
loadMore
|
|
35
41
|
};
|
|
36
42
|
}
|
|
@@ -7,10 +7,12 @@ type Args = {
|
|
|
7
7
|
setActiveIndex: Dispatch<SetStateAction<number>>;
|
|
8
8
|
onSelectIndex: (index: number) => void;
|
|
9
9
|
onEscape: () => void;
|
|
10
|
+
onSubmit?: () => void;
|
|
11
|
+
onReachEnd?: () => void;
|
|
10
12
|
hasExtraRow?: boolean;
|
|
11
13
|
onSelectExtra?: () => void;
|
|
12
14
|
isQueryEmpty?: boolean;
|
|
13
15
|
onBackspaceEmpty?: () => void;
|
|
14
16
|
};
|
|
15
|
-
export declare function useListNavigation({ isOpen, open, itemCount, activeIndex, setActiveIndex, onSelectIndex, onEscape, hasExtraRow, onSelectExtra, isQueryEmpty, onBackspaceEmpty, }: Args): (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
17
|
+
export declare function useListNavigation({ isOpen, open, itemCount, activeIndex, setActiveIndex, onSelectIndex, onEscape, onSubmit, onReachEnd, hasExtraRow, onSelectExtra, isQueryEmpty, onBackspaceEmpty, }: Args): (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
16
18
|
export {};
|
|
@@ -7,6 +7,8 @@ export function useListNavigation({
|
|
|
7
7
|
setActiveIndex,
|
|
8
8
|
onSelectIndex,
|
|
9
9
|
onEscape,
|
|
10
|
+
onSubmit,
|
|
11
|
+
onReachEnd,
|
|
10
12
|
hasExtraRow,
|
|
11
13
|
onSelectExtra,
|
|
12
14
|
isQueryEmpty,
|
|
@@ -16,7 +18,15 @@ export function useListNavigation({
|
|
|
16
18
|
return useCallback(event => {
|
|
17
19
|
if (event.key === 'ArrowDown') {
|
|
18
20
|
event.preventDefault();
|
|
19
|
-
if (!isOpen)
|
|
21
|
+
if (!isOpen) {
|
|
22
|
+
open();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (total > 0) {
|
|
26
|
+
const next = activeIndex >= total - 1 ? 0 : activeIndex + 1;
|
|
27
|
+
setActiveIndex(next);
|
|
28
|
+
if (next > 0 && next >= total - 1) onReachEnd === null || onReachEnd === void 0 || onReachEnd();
|
|
29
|
+
}
|
|
20
30
|
return;
|
|
21
31
|
}
|
|
22
32
|
if (event.key === 'ArrowUp') {
|
|
@@ -25,7 +35,14 @@ export function useListNavigation({
|
|
|
25
35
|
return;
|
|
26
36
|
}
|
|
27
37
|
if (event.key === 'Enter') {
|
|
28
|
-
if (!isOpen
|
|
38
|
+
if (!isOpen) return;
|
|
39
|
+
if (activeIndex < 0 || activeIndex >= total) {
|
|
40
|
+
if (onSubmit) {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
onSubmit();
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
29
46
|
event.preventDefault();
|
|
30
47
|
if (hasExtraRow && activeIndex === 0) onSelectExtra === null || onSelectExtra === void 0 || onSelectExtra();else onSelectIndex(hasExtraRow ? activeIndex - 1 : activeIndex);
|
|
31
48
|
return;
|
|
@@ -41,5 +58,5 @@ export function useListNavigation({
|
|
|
41
58
|
if (event.key === 'Backspace' && isQueryEmpty && onBackspaceEmpty) {
|
|
42
59
|
onBackspaceEmpty();
|
|
43
60
|
}
|
|
44
|
-
}, [isOpen, open, total, activeIndex, setActiveIndex, onSelectIndex, onSelectExtra, onEscape, hasExtraRow, isQueryEmpty, onBackspaceEmpty]);
|
|
61
|
+
}, [isOpen, open, total, activeIndex, setActiveIndex, onSelectIndex, onSelectExtra, onEscape, onSubmit, onReachEnd, hasExtraRow, isQueryEmpty, onBackspaceEmpty]);
|
|
45
62
|
}
|