@heroui/autocomplete 2.3.12 → 2.3.14
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/dist/autocomplete.d.mts +21 -0
- package/dist/autocomplete.d.ts +21 -0
- package/dist/autocomplete.js +446 -0
- package/dist/autocomplete.mjs +8 -0
- package/dist/chunk-DNIGSRME.mjs +55 -0
- package/dist/chunk-OHYOYGT2.mjs +378 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +459 -0
- package/dist/index.mjs +16 -0
- package/dist/use-autocomplete.d.mts +229 -0
- package/dist/use-autocomplete.d.ts +229 -0
- package/dist/use-autocomplete.js +401 -0
- package/dist/use-autocomplete.mjs +7 -0
- package/package.json +14 -14
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/use-autocomplete.ts
|
|
4
|
+
import { mapPropsVariants, useProviderContext } from "@heroui/system";
|
|
5
|
+
import { useSafeLayoutEffect } from "@heroui/use-safe-layout-effect";
|
|
6
|
+
import { autocomplete } from "@heroui/theme";
|
|
7
|
+
import { useFilter } from "@react-aria/i18n";
|
|
8
|
+
import { useComboBoxState } from "@react-stately/combobox";
|
|
9
|
+
import { useDOMRef } from "@heroui/react-utils";
|
|
10
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
11
|
+
import { clsx, dataAttr, objectToDeps } from "@heroui/shared-utils";
|
|
12
|
+
import { chain, mergeProps } from "@react-aria/utils";
|
|
13
|
+
import { useComboBox } from "@react-aria/combobox";
|
|
14
|
+
import { FormContext, useSlottedContext } from "@heroui/form";
|
|
15
|
+
import { ariaShouldCloseOnInteractOutside } from "@heroui/aria-utils";
|
|
16
|
+
function useAutocomplete(originalProps) {
|
|
17
|
+
var _a, _b, _c, _d, _e;
|
|
18
|
+
const globalContext = useProviderContext();
|
|
19
|
+
const { validationBehavior: formValidationBehavior } = useSlottedContext(FormContext) || {};
|
|
20
|
+
const [props, variantProps] = mapPropsVariants(originalProps, autocomplete.variantKeys);
|
|
21
|
+
const disableAnimation = (_b = (_a = originalProps.disableAnimation) != null ? _a : globalContext == null ? void 0 : globalContext.disableAnimation) != null ? _b : false;
|
|
22
|
+
const isClearable = originalProps.disableClearable !== void 0 ? !originalProps.disableClearable : originalProps.isReadOnly ? false : originalProps.isClearable;
|
|
23
|
+
const {
|
|
24
|
+
ref,
|
|
25
|
+
as,
|
|
26
|
+
label,
|
|
27
|
+
isLoading,
|
|
28
|
+
menuTrigger = "focus",
|
|
29
|
+
filterOptions = {
|
|
30
|
+
sensitivity: "base"
|
|
31
|
+
},
|
|
32
|
+
children,
|
|
33
|
+
selectorIcon,
|
|
34
|
+
clearIcon,
|
|
35
|
+
scrollRef: scrollRefProp,
|
|
36
|
+
defaultFilter,
|
|
37
|
+
endContent,
|
|
38
|
+
allowsEmptyCollection = true,
|
|
39
|
+
shouldCloseOnBlur = true,
|
|
40
|
+
popoverProps = {},
|
|
41
|
+
inputProps: userInputProps = {},
|
|
42
|
+
scrollShadowProps = {},
|
|
43
|
+
listboxProps = {},
|
|
44
|
+
selectorButtonProps = {},
|
|
45
|
+
clearButtonProps = {},
|
|
46
|
+
showScrollIndicators = true,
|
|
47
|
+
allowsCustomValue = false,
|
|
48
|
+
isVirtualized,
|
|
49
|
+
maxListboxHeight = 256,
|
|
50
|
+
itemHeight = 32,
|
|
51
|
+
validationBehavior = (_c = formValidationBehavior != null ? formValidationBehavior : globalContext == null ? void 0 : globalContext.validationBehavior) != null ? _c : "native",
|
|
52
|
+
className,
|
|
53
|
+
classNames,
|
|
54
|
+
errorMessage,
|
|
55
|
+
onOpenChange,
|
|
56
|
+
onClose,
|
|
57
|
+
isReadOnly = false,
|
|
58
|
+
...otherProps
|
|
59
|
+
} = props;
|
|
60
|
+
const { contains } = useFilter(filterOptions);
|
|
61
|
+
let state = useComboBoxState({
|
|
62
|
+
...originalProps,
|
|
63
|
+
children,
|
|
64
|
+
menuTrigger,
|
|
65
|
+
validationBehavior,
|
|
66
|
+
shouldCloseOnBlur,
|
|
67
|
+
allowsEmptyCollection,
|
|
68
|
+
defaultFilter: defaultFilter && typeof defaultFilter === "function" ? defaultFilter : contains,
|
|
69
|
+
onOpenChange: (open, menuTrigger2) => {
|
|
70
|
+
onOpenChange == null ? void 0 : onOpenChange(open, menuTrigger2);
|
|
71
|
+
if (!open) {
|
|
72
|
+
onClose == null ? void 0 : onClose();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
state = {
|
|
77
|
+
...state,
|
|
78
|
+
...isReadOnly && {
|
|
79
|
+
disabledKeys: /* @__PURE__ */ new Set([...state.collection.getKeys()])
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const buttonRef = useRef(null);
|
|
83
|
+
const inputWrapperRef = useRef(null);
|
|
84
|
+
const listBoxRef = useRef(null);
|
|
85
|
+
const popoverRef = useRef(null);
|
|
86
|
+
const inputRef = useDOMRef(ref);
|
|
87
|
+
const scrollShadowRef = useDOMRef(scrollRefProp);
|
|
88
|
+
const {
|
|
89
|
+
buttonProps,
|
|
90
|
+
inputProps,
|
|
91
|
+
listBoxProps,
|
|
92
|
+
isInvalid: isAriaInvalid,
|
|
93
|
+
validationDetails,
|
|
94
|
+
validationErrors
|
|
95
|
+
} = useComboBox(
|
|
96
|
+
{
|
|
97
|
+
validationBehavior,
|
|
98
|
+
...originalProps,
|
|
99
|
+
inputRef,
|
|
100
|
+
buttonRef,
|
|
101
|
+
listBoxRef,
|
|
102
|
+
popoverRef
|
|
103
|
+
},
|
|
104
|
+
state
|
|
105
|
+
);
|
|
106
|
+
const isInvalid = originalProps.isInvalid || isAriaInvalid;
|
|
107
|
+
const slotsProps = {
|
|
108
|
+
inputProps: mergeProps(
|
|
109
|
+
{
|
|
110
|
+
label,
|
|
111
|
+
ref: inputRef,
|
|
112
|
+
wrapperRef: inputWrapperRef,
|
|
113
|
+
onClick: () => {
|
|
114
|
+
if (!state.isOpen && !!state.selectedItem) {
|
|
115
|
+
state.open();
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
isClearable: false,
|
|
119
|
+
disableAnimation
|
|
120
|
+
},
|
|
121
|
+
userInputProps
|
|
122
|
+
),
|
|
123
|
+
popoverProps: mergeProps(
|
|
124
|
+
{
|
|
125
|
+
offset: 5,
|
|
126
|
+
placement: "bottom",
|
|
127
|
+
triggerScaleOnOpen: false,
|
|
128
|
+
disableAnimation
|
|
129
|
+
},
|
|
130
|
+
popoverProps
|
|
131
|
+
),
|
|
132
|
+
scrollShadowProps: mergeProps(
|
|
133
|
+
{
|
|
134
|
+
ref: scrollShadowRef,
|
|
135
|
+
isEnabled: (_d = showScrollIndicators && state.collection.size > 5) != null ? _d : true,
|
|
136
|
+
hideScrollBar: true,
|
|
137
|
+
offset: 15
|
|
138
|
+
},
|
|
139
|
+
scrollShadowProps
|
|
140
|
+
),
|
|
141
|
+
listboxProps: mergeProps(
|
|
142
|
+
{
|
|
143
|
+
hideEmptyContent: allowsCustomValue,
|
|
144
|
+
emptyContent: "No results found.",
|
|
145
|
+
disableAnimation
|
|
146
|
+
},
|
|
147
|
+
listboxProps
|
|
148
|
+
),
|
|
149
|
+
selectorButtonProps: mergeProps(
|
|
150
|
+
{
|
|
151
|
+
isLoading,
|
|
152
|
+
size: "sm",
|
|
153
|
+
variant: "light",
|
|
154
|
+
radius: "full",
|
|
155
|
+
color: isInvalid ? "danger" : originalProps == null ? void 0 : originalProps.color,
|
|
156
|
+
isIconOnly: true,
|
|
157
|
+
disableAnimation
|
|
158
|
+
},
|
|
159
|
+
selectorButtonProps
|
|
160
|
+
),
|
|
161
|
+
clearButtonProps: mergeProps(
|
|
162
|
+
{
|
|
163
|
+
size: "sm",
|
|
164
|
+
variant: "light",
|
|
165
|
+
radius: "full",
|
|
166
|
+
color: isInvalid ? "danger" : originalProps == null ? void 0 : originalProps.color,
|
|
167
|
+
isIconOnly: true,
|
|
168
|
+
disableAnimation
|
|
169
|
+
},
|
|
170
|
+
clearButtonProps
|
|
171
|
+
)
|
|
172
|
+
};
|
|
173
|
+
const baseStyles = clsx(classNames == null ? void 0 : classNames.base, className);
|
|
174
|
+
const isOpen = ((_e = slotsProps.listboxProps) == null ? void 0 : _e.hideEmptyContent) ? state.isOpen && !!state.collection.size : state.isOpen;
|
|
175
|
+
useSafeLayoutEffect(() => {
|
|
176
|
+
if (!inputRef.current) return;
|
|
177
|
+
const key = inputRef.current.value;
|
|
178
|
+
const item = state.collection.getItem(key);
|
|
179
|
+
if (item && state.inputValue !== item.textValue) {
|
|
180
|
+
state.setSelectedKey(key);
|
|
181
|
+
state.setInputValue(item.textValue);
|
|
182
|
+
}
|
|
183
|
+
}, [inputRef.current]);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
let key = state.collection.getFirstKey();
|
|
186
|
+
while (key && state.disabledKeys.has(key)) {
|
|
187
|
+
key = state.collection.getKeyAfter(key);
|
|
188
|
+
}
|
|
189
|
+
state.selectionManager.setFocusedKey(key);
|
|
190
|
+
}, [state.collection, state.disabledKeys]);
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (isOpen) {
|
|
193
|
+
if (popoverRef.current && inputWrapperRef.current) {
|
|
194
|
+
let rect = inputWrapperRef.current.getBoundingClientRect();
|
|
195
|
+
let popover = popoverRef.current;
|
|
196
|
+
popover.style.width = rect.width + "px";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}, [isOpen]);
|
|
200
|
+
if (inputProps.onKeyDown) {
|
|
201
|
+
const originalOnKeyDown = inputProps.onKeyDown;
|
|
202
|
+
inputProps.onKeyDown = (e) => {
|
|
203
|
+
if ("continuePropagation" in e) {
|
|
204
|
+
e.stopPropagation = () => {
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return originalOnKeyDown(e);
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const Component = as || "div";
|
|
211
|
+
const slots = useMemo(
|
|
212
|
+
() => autocomplete({
|
|
213
|
+
...variantProps,
|
|
214
|
+
isClearable,
|
|
215
|
+
disableAnimation
|
|
216
|
+
}),
|
|
217
|
+
[objectToDeps(variantProps), isClearable, disableAnimation]
|
|
218
|
+
);
|
|
219
|
+
const getBaseProps = () => ({
|
|
220
|
+
"data-invalid": dataAttr(isInvalid),
|
|
221
|
+
"data-open": dataAttr(state.isOpen),
|
|
222
|
+
className: slots.base({ class: baseStyles })
|
|
223
|
+
});
|
|
224
|
+
const getSelectorButtonProps = () => {
|
|
225
|
+
var _a2;
|
|
226
|
+
return {
|
|
227
|
+
ref: buttonRef,
|
|
228
|
+
...mergeProps(buttonProps, slotsProps.selectorButtonProps),
|
|
229
|
+
"data-open": dataAttr(state.isOpen),
|
|
230
|
+
className: slots.selectorButton({
|
|
231
|
+
class: clsx(classNames == null ? void 0 : classNames.selectorButton, (_a2 = slotsProps.selectorButtonProps) == null ? void 0 : _a2.className)
|
|
232
|
+
})
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
const getClearButtonProps = () => {
|
|
236
|
+
var _a2, _b2;
|
|
237
|
+
return {
|
|
238
|
+
...mergeProps(buttonProps, slotsProps.clearButtonProps),
|
|
239
|
+
// disable original focus and state toggle from react aria
|
|
240
|
+
onPressStart: () => {
|
|
241
|
+
var _a3;
|
|
242
|
+
(_a3 = inputRef.current) == null ? void 0 : _a3.focus();
|
|
243
|
+
},
|
|
244
|
+
onPress: (e) => {
|
|
245
|
+
var _a3, _b3;
|
|
246
|
+
(_b3 = (_a3 = slotsProps.clearButtonProps) == null ? void 0 : _a3.onPress) == null ? void 0 : _b3.call(_a3, e);
|
|
247
|
+
if (state.selectedItem) {
|
|
248
|
+
state.setSelectedKey(null);
|
|
249
|
+
}
|
|
250
|
+
state.setInputValue("");
|
|
251
|
+
state.open();
|
|
252
|
+
},
|
|
253
|
+
"data-visible": !!state.selectedItem || ((_a2 = state.inputValue) == null ? void 0 : _a2.length) > 0,
|
|
254
|
+
className: slots.clearButton({
|
|
255
|
+
class: clsx(classNames == null ? void 0 : classNames.clearButton, (_b2 = slotsProps.clearButtonProps) == null ? void 0 : _b2.className)
|
|
256
|
+
})
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
const hasUncommittedValidation = validationBehavior === "native" && state.displayValidation.isInvalid === false && state.realtimeValidation.isInvalid === true;
|
|
260
|
+
const getInputProps = () => ({
|
|
261
|
+
...otherProps,
|
|
262
|
+
...inputProps,
|
|
263
|
+
...slotsProps.inputProps,
|
|
264
|
+
isInvalid: hasUncommittedValidation ? void 0 : isInvalid,
|
|
265
|
+
validationBehavior,
|
|
266
|
+
errorMessage: typeof errorMessage === "function" ? errorMessage({ isInvalid, validationErrors, validationDetails }) : errorMessage || (validationErrors == null ? void 0 : validationErrors.join(" ")),
|
|
267
|
+
onClick: chain(slotsProps.inputProps.onClick, otherProps.onClick)
|
|
268
|
+
});
|
|
269
|
+
const getListBoxProps = () => {
|
|
270
|
+
const shouldVirtualize = isVirtualized != null ? isVirtualized : state.collection.size > 50;
|
|
271
|
+
return {
|
|
272
|
+
state,
|
|
273
|
+
ref: listBoxRef,
|
|
274
|
+
isVirtualized: shouldVirtualize,
|
|
275
|
+
virtualization: shouldVirtualize ? {
|
|
276
|
+
maxListboxHeight,
|
|
277
|
+
itemHeight
|
|
278
|
+
} : void 0,
|
|
279
|
+
scrollShadowProps: slotsProps.scrollShadowProps,
|
|
280
|
+
...mergeProps(slotsProps.listboxProps, listBoxProps, {
|
|
281
|
+
shouldHighlightOnFocus: true
|
|
282
|
+
})
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
const getPopoverProps = (props2 = {}) => {
|
|
286
|
+
var _a2, _b2, _c2;
|
|
287
|
+
const popoverProps2 = mergeProps(slotsProps.popoverProps, props2);
|
|
288
|
+
return {
|
|
289
|
+
state,
|
|
290
|
+
ref: popoverRef,
|
|
291
|
+
triggerRef: inputWrapperRef,
|
|
292
|
+
scrollRef: listBoxRef,
|
|
293
|
+
triggerType: "listbox",
|
|
294
|
+
...popoverProps2,
|
|
295
|
+
classNames: {
|
|
296
|
+
...(_a2 = slotsProps.popoverProps) == null ? void 0 : _a2.classNames,
|
|
297
|
+
content: slots.popoverContent({
|
|
298
|
+
class: clsx(
|
|
299
|
+
classNames == null ? void 0 : classNames.popoverContent,
|
|
300
|
+
(_c2 = (_b2 = slotsProps.popoverProps) == null ? void 0 : _b2.classNames) == null ? void 0 : _c2["content"],
|
|
301
|
+
props2.className
|
|
302
|
+
)
|
|
303
|
+
})
|
|
304
|
+
},
|
|
305
|
+
shouldCloseOnInteractOutside: (popoverProps2 == null ? void 0 : popoverProps2.shouldCloseOnInteractOutside) ? popoverProps2.shouldCloseOnInteractOutside : (element) => ariaShouldCloseOnInteractOutside(element, inputWrapperRef, state),
|
|
306
|
+
// when the popover is open, the focus should be on input instead of dialog
|
|
307
|
+
// therefore, we skip dialog focus here
|
|
308
|
+
disableDialogFocus: true
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
const getEmptyPopoverProps = () => {
|
|
312
|
+
return {
|
|
313
|
+
ref: popoverRef,
|
|
314
|
+
className: "hidden"
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
const getListBoxWrapperProps = (props2 = {}) => {
|
|
318
|
+
var _a2, _b2;
|
|
319
|
+
return {
|
|
320
|
+
...mergeProps(slotsProps.scrollShadowProps, props2),
|
|
321
|
+
className: slots.listboxWrapper({
|
|
322
|
+
class: clsx(
|
|
323
|
+
classNames == null ? void 0 : classNames.listboxWrapper,
|
|
324
|
+
(_a2 = slotsProps.scrollShadowProps) == null ? void 0 : _a2.className,
|
|
325
|
+
props2 == null ? void 0 : props2.className
|
|
326
|
+
)
|
|
327
|
+
}),
|
|
328
|
+
style: {
|
|
329
|
+
maxHeight: (_b2 = originalProps.maxListboxHeight) != null ? _b2 : 256
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
const getEndContentWrapperProps = (props2 = {}) => ({
|
|
334
|
+
className: slots.endContentWrapper({
|
|
335
|
+
class: clsx(classNames == null ? void 0 : classNames.endContentWrapper, props2 == null ? void 0 : props2.className)
|
|
336
|
+
}),
|
|
337
|
+
onPointerDown: chain(props2.onPointerDown, (e) => {
|
|
338
|
+
var _a2;
|
|
339
|
+
if (e.button === 0 && e.currentTarget === e.target) {
|
|
340
|
+
(_a2 = inputRef.current) == null ? void 0 : _a2.focus();
|
|
341
|
+
}
|
|
342
|
+
}),
|
|
343
|
+
onMouseDown: chain(props2.onMouseDown, (e) => {
|
|
344
|
+
if (e.button === 0 && e.currentTarget === e.target) {
|
|
345
|
+
e.preventDefault();
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
Component,
|
|
351
|
+
inputRef,
|
|
352
|
+
label,
|
|
353
|
+
state,
|
|
354
|
+
slots,
|
|
355
|
+
classNames,
|
|
356
|
+
isLoading,
|
|
357
|
+
clearIcon,
|
|
358
|
+
isOpen,
|
|
359
|
+
endContent,
|
|
360
|
+
isClearable,
|
|
361
|
+
disableAnimation,
|
|
362
|
+
allowsCustomValue,
|
|
363
|
+
selectorIcon,
|
|
364
|
+
getBaseProps,
|
|
365
|
+
getInputProps,
|
|
366
|
+
getListBoxProps,
|
|
367
|
+
getPopoverProps,
|
|
368
|
+
getEmptyPopoverProps,
|
|
369
|
+
getClearButtonProps,
|
|
370
|
+
getSelectorButtonProps,
|
|
371
|
+
getListBoxWrapperProps,
|
|
372
|
+
getEndContentWrapperProps
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export {
|
|
377
|
+
useAutocomplete
|
|
378
|
+
};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { ListboxItem as AutocompleteItem, ListboxItemProps as AutocompleteItemProps, ListboxSection as AutocompleteSection, ListboxSectionProps as AutocompleteSectionProps } from '@heroui/listbox';
|
|
2
|
+
import { MenuTriggerAction as MenuTriggerAction$1 } from '@react-types/combobox';
|
|
3
|
+
export { default as Autocomplete, AutocompleteProps } from './autocomplete.mjs';
|
|
4
|
+
export { useAutocomplete } from './use-autocomplete.mjs';
|
|
5
|
+
import 'react';
|
|
6
|
+
import 'tailwind-variants';
|
|
7
|
+
import '@react-stately/combobox';
|
|
8
|
+
import '@heroui/system';
|
|
9
|
+
import '@heroui/theme';
|
|
10
|
+
import '@heroui/react-utils';
|
|
11
|
+
import '@heroui/popover';
|
|
12
|
+
import '@heroui/input';
|
|
13
|
+
import '@heroui/scroll-shadow';
|
|
14
|
+
import '@heroui/button';
|
|
15
|
+
import '@react-types/shared';
|
|
16
|
+
|
|
17
|
+
type MenuTriggerAction = MenuTriggerAction$1 | undefined;
|
|
18
|
+
|
|
19
|
+
export type { MenuTriggerAction };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { ListboxItem as AutocompleteItem, ListboxItemProps as AutocompleteItemProps, ListboxSection as AutocompleteSection, ListboxSectionProps as AutocompleteSectionProps } from '@heroui/listbox';
|
|
2
|
+
import { MenuTriggerAction as MenuTriggerAction$1 } from '@react-types/combobox';
|
|
3
|
+
export { default as Autocomplete, AutocompleteProps } from './autocomplete.js';
|
|
4
|
+
export { useAutocomplete } from './use-autocomplete.js';
|
|
5
|
+
import 'react';
|
|
6
|
+
import 'tailwind-variants';
|
|
7
|
+
import '@react-stately/combobox';
|
|
8
|
+
import '@heroui/system';
|
|
9
|
+
import '@heroui/theme';
|
|
10
|
+
import '@heroui/react-utils';
|
|
11
|
+
import '@heroui/popover';
|
|
12
|
+
import '@heroui/input';
|
|
13
|
+
import '@heroui/scroll-shadow';
|
|
14
|
+
import '@heroui/button';
|
|
15
|
+
import '@react-types/shared';
|
|
16
|
+
|
|
17
|
+
type MenuTriggerAction = MenuTriggerAction$1 | undefined;
|
|
18
|
+
|
|
19
|
+
export type { MenuTriggerAction };
|