1 | import _extends from "@babel/runtime/helpers/esm/extends";
|
2 | import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
3 | import _typeof from "@babel/runtime/helpers/esm/typeof";
|
4 |
|
5 | import * as React from 'react';
|
6 | import { unstable_setRef as setRef, unstable_useEventCallback as useEventCallback, unstable_useControlled as useControlled, unstable_useId as useId, usePreviousProps } from '@mui/utils';
|
7 |
|
8 |
|
9 |
|
10 | function stripDiacritics(string) {
|
11 | return typeof string.normalize !== 'undefined' ? string.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : string;
|
12 | }
|
13 | export function createFilterOptions() {
|
14 | var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
15 | var _config$ignoreAccents = config.ignoreAccents,
|
16 | ignoreAccents = _config$ignoreAccents === void 0 ? true : _config$ignoreAccents,
|
17 | _config$ignoreCase = config.ignoreCase,
|
18 | ignoreCase = _config$ignoreCase === void 0 ? true : _config$ignoreCase,
|
19 | limit = config.limit,
|
20 | _config$matchFrom = config.matchFrom,
|
21 | matchFrom = _config$matchFrom === void 0 ? 'any' : _config$matchFrom,
|
22 | stringify = config.stringify,
|
23 | _config$trim = config.trim,
|
24 | trim = _config$trim === void 0 ? false : _config$trim;
|
25 | return function (options, _ref) {
|
26 | var inputValue = _ref.inputValue,
|
27 | getOptionLabel = _ref.getOptionLabel;
|
28 | var input = trim ? inputValue.trim() : inputValue;
|
29 | if (ignoreCase) {
|
30 | input = input.toLowerCase();
|
31 | }
|
32 | if (ignoreAccents) {
|
33 | input = stripDiacritics(input);
|
34 | }
|
35 | var filteredOptions = !input ? options : options.filter(function (option) {
|
36 | var candidate = (stringify || getOptionLabel)(option);
|
37 | if (ignoreCase) {
|
38 | candidate = candidate.toLowerCase();
|
39 | }
|
40 | if (ignoreAccents) {
|
41 | candidate = stripDiacritics(candidate);
|
42 | }
|
43 | return matchFrom === 'start' ? candidate.indexOf(input) === 0 : candidate.indexOf(input) > -1;
|
44 | });
|
45 | return typeof limit === 'number' ? filteredOptions.slice(0, limit) : filteredOptions;
|
46 | };
|
47 | }
|
48 |
|
49 |
|
50 | function findIndex(array, comp) {
|
51 | for (var i = 0; i < array.length; i += 1) {
|
52 | if (comp(array[i])) {
|
53 | return i;
|
54 | }
|
55 | }
|
56 | return -1;
|
57 | }
|
58 | var defaultFilterOptions = createFilterOptions();
|
59 |
|
60 |
|
61 | var pageSize = 5;
|
62 | var defaultIsActiveElementInListbox = function defaultIsActiveElementInListbox(listboxRef) {
|
63 | var _listboxRef$current$p;
|
64 | return listboxRef.current !== null && ((_listboxRef$current$p = listboxRef.current.parentElement) == null ? void 0 : _listboxRef$current$p.contains(document.activeElement));
|
65 | };
|
66 | export default function useAutocomplete(props) {
|
67 | var _props$unstable_isAct = props.unstable_isActiveElementInListbox,
|
68 | unstable_isActiveElementInListbox = _props$unstable_isAct === void 0 ? defaultIsActiveElementInListbox : _props$unstable_isAct,
|
69 | _props$unstable_class = props.unstable_classNamePrefix,
|
70 | unstable_classNamePrefix = _props$unstable_class === void 0 ? 'Mui' : _props$unstable_class,
|
71 | _props$autoComplete = props.autoComplete,
|
72 | autoComplete = _props$autoComplete === void 0 ? false : _props$autoComplete,
|
73 | _props$autoHighlight = props.autoHighlight,
|
74 | autoHighlight = _props$autoHighlight === void 0 ? false : _props$autoHighlight,
|
75 | _props$autoSelect = props.autoSelect,
|
76 | autoSelect = _props$autoSelect === void 0 ? false : _props$autoSelect,
|
77 | _props$blurOnSelect = props.blurOnSelect,
|
78 | blurOnSelect = _props$blurOnSelect === void 0 ? false : _props$blurOnSelect,
|
79 | _props$clearOnBlur = props.clearOnBlur,
|
80 | clearOnBlur = _props$clearOnBlur === void 0 ? !props.freeSolo : _props$clearOnBlur,
|
81 | _props$clearOnEscape = props.clearOnEscape,
|
82 | clearOnEscape = _props$clearOnEscape === void 0 ? false : _props$clearOnEscape,
|
83 | _props$componentName = props.componentName,
|
84 | componentName = _props$componentName === void 0 ? 'useAutocomplete' : _props$componentName,
|
85 | _props$defaultValue = props.defaultValue,
|
86 | defaultValue = _props$defaultValue === void 0 ? props.multiple ? [] : null : _props$defaultValue,
|
87 | _props$disableClearab = props.disableClearable,
|
88 | disableClearable = _props$disableClearab === void 0 ? false : _props$disableClearab,
|
89 | _props$disableCloseOn = props.disableCloseOnSelect,
|
90 | disableCloseOnSelect = _props$disableCloseOn === void 0 ? false : _props$disableCloseOn,
|
91 | disabledProp = props.disabled,
|
92 | _props$disabledItemsF = props.disabledItemsFocusable,
|
93 | disabledItemsFocusable = _props$disabledItemsF === void 0 ? false : _props$disabledItemsF,
|
94 | _props$disableListWra = props.disableListWrap,
|
95 | disableListWrap = _props$disableListWra === void 0 ? false : _props$disableListWra,
|
96 | _props$filterOptions = props.filterOptions,
|
97 | filterOptions = _props$filterOptions === void 0 ? defaultFilterOptions : _props$filterOptions,
|
98 | _props$filterSelected = props.filterSelectedOptions,
|
99 | filterSelectedOptions = _props$filterSelected === void 0 ? false : _props$filterSelected,
|
100 | _props$freeSolo = props.freeSolo,
|
101 | freeSolo = _props$freeSolo === void 0 ? false : _props$freeSolo,
|
102 | getOptionDisabled = props.getOptionDisabled,
|
103 | _props$getOptionLabel = props.getOptionLabel,
|
104 | getOptionLabelProp = _props$getOptionLabel === void 0 ? function (option) {
|
105 | var _option$label;
|
106 | return (_option$label = option.label) != null ? _option$label : option;
|
107 | } : _props$getOptionLabel,
|
108 | groupBy = props.groupBy,
|
109 | _props$handleHomeEndK = props.handleHomeEndKeys,
|
110 | handleHomeEndKeys = _props$handleHomeEndK === void 0 ? !props.freeSolo : _props$handleHomeEndK,
|
111 | idProp = props.id,
|
112 | _props$includeInputIn = props.includeInputInList,
|
113 | includeInputInList = _props$includeInputIn === void 0 ? false : _props$includeInputIn,
|
114 | inputValueProp = props.inputValue,
|
115 | _props$isOptionEqualT = props.isOptionEqualToValue,
|
116 | isOptionEqualToValue = _props$isOptionEqualT === void 0 ? function (option, value) {
|
117 | return option === value;
|
118 | } : _props$isOptionEqualT,
|
119 | _props$multiple = props.multiple,
|
120 | multiple = _props$multiple === void 0 ? false : _props$multiple,
|
121 | onChange = props.onChange,
|
122 | onClose = props.onClose,
|
123 | onHighlightChange = props.onHighlightChange,
|
124 | onInputChange = props.onInputChange,
|
125 | onOpen = props.onOpen,
|
126 | openProp = props.open,
|
127 | _props$openOnFocus = props.openOnFocus,
|
128 | openOnFocus = _props$openOnFocus === void 0 ? false : _props$openOnFocus,
|
129 | options = props.options,
|
130 | _props$readOnly = props.readOnly,
|
131 | readOnly = _props$readOnly === void 0 ? false : _props$readOnly,
|
132 | _props$selectOnFocus = props.selectOnFocus,
|
133 | selectOnFocus = _props$selectOnFocus === void 0 ? !props.freeSolo : _props$selectOnFocus,
|
134 | valueProp = props.value;
|
135 | var id = useId(idProp);
|
136 | var getOptionLabel = getOptionLabelProp;
|
137 | getOptionLabel = function getOptionLabel(option) {
|
138 | var optionLabel = getOptionLabelProp(option);
|
139 | if (typeof optionLabel !== 'string') {
|
140 | if (process.env.NODE_ENV !== 'production') {
|
141 | var erroneousReturn = optionLabel === undefined ? 'undefined' : "".concat(_typeof(optionLabel), " (").concat(optionLabel, ")");
|
142 | console.error("MUI: The `getOptionLabel` method of ".concat(componentName, " returned ").concat(erroneousReturn, " instead of a string for ").concat(JSON.stringify(option), "."));
|
143 | }
|
144 | return String(optionLabel);
|
145 | }
|
146 | return optionLabel;
|
147 | };
|
148 | var ignoreFocus = React.useRef(false);
|
149 | var firstFocus = React.useRef(true);
|
150 | var inputRef = React.useRef(null);
|
151 | var listboxRef = React.useRef(null);
|
152 | var _React$useState = React.useState(null),
|
153 | anchorEl = _React$useState[0],
|
154 | setAnchorEl = _React$useState[1];
|
155 | var _React$useState2 = React.useState(-1),
|
156 | focusedTag = _React$useState2[0],
|
157 | setFocusedTag = _React$useState2[1];
|
158 | var defaultHighlighted = autoHighlight ? 0 : -1;
|
159 | var highlightedIndexRef = React.useRef(defaultHighlighted);
|
160 | var _useControlled = useControlled({
|
161 | controlled: valueProp,
|
162 | default: defaultValue,
|
163 | name: componentName
|
164 | }),
|
165 | _useControlled2 = _slicedToArray(_useControlled, 2),
|
166 | value = _useControlled2[0],
|
167 | setValueState = _useControlled2[1];
|
168 | var _useControlled3 = useControlled({
|
169 | controlled: inputValueProp,
|
170 | default: '',
|
171 | name: componentName,
|
172 | state: 'inputValue'
|
173 | }),
|
174 | _useControlled4 = _slicedToArray(_useControlled3, 2),
|
175 | inputValue = _useControlled4[0],
|
176 | setInputValueState = _useControlled4[1];
|
177 | var _React$useState3 = React.useState(false),
|
178 | focused = _React$useState3[0],
|
179 | setFocused = _React$useState3[1];
|
180 | var resetInputValue = React.useCallback(function (event, newValue) {
|
181 |
|
182 |
|
183 | var isOptionSelected = multiple ? value.length < newValue.length : newValue !== null;
|
184 | if (!isOptionSelected && !clearOnBlur) {
|
185 | return;
|
186 | }
|
187 | var newInputValue;
|
188 | if (multiple) {
|
189 | newInputValue = '';
|
190 | } else if (newValue == null) {
|
191 | newInputValue = '';
|
192 | } else {
|
193 | var optionLabel = getOptionLabel(newValue);
|
194 | newInputValue = typeof optionLabel === 'string' ? optionLabel : '';
|
195 | }
|
196 | if (inputValue === newInputValue) {
|
197 | return;
|
198 | }
|
199 | setInputValueState(newInputValue);
|
200 | if (onInputChange) {
|
201 | onInputChange(event, newInputValue, 'reset');
|
202 | }
|
203 | }, [getOptionLabel, inputValue, multiple, onInputChange, setInputValueState, clearOnBlur, value]);
|
204 | var _useControlled5 = useControlled({
|
205 | controlled: openProp,
|
206 | default: false,
|
207 | name: componentName,
|
208 | state: 'open'
|
209 | }),
|
210 | _useControlled6 = _slicedToArray(_useControlled5, 2),
|
211 | open = _useControlled6[0],
|
212 | setOpenState = _useControlled6[1];
|
213 | var _React$useState4 = React.useState(true),
|
214 | inputPristine = _React$useState4[0],
|
215 | setInputPristine = _React$useState4[1];
|
216 | var inputValueIsSelectedValue = !multiple && value != null && inputValue === getOptionLabel(value);
|
217 | var popupOpen = open && !readOnly;
|
218 | var filteredOptions = popupOpen ? filterOptions(options.filter(function (option) {
|
219 | if (filterSelectedOptions && (multiple ? value : [value]).some(function (value2) {
|
220 | return value2 !== null && isOptionEqualToValue(option, value2);
|
221 | })) {
|
222 | return false;
|
223 | }
|
224 | return true;
|
225 | }),
|
226 |
|
227 |
|
228 | {
|
229 | inputValue: inputValueIsSelectedValue && inputPristine ? '' : inputValue,
|
230 | getOptionLabel: getOptionLabel
|
231 | }) : [];
|
232 | var previousProps = usePreviousProps({
|
233 | filteredOptions: filteredOptions,
|
234 | value: value
|
235 | });
|
236 | React.useEffect(function () {
|
237 | var valueChange = value !== previousProps.value;
|
238 | if (focused && !valueChange) {
|
239 | return;
|
240 | }
|
241 |
|
242 |
|
243 | if (freeSolo && !valueChange) {
|
244 | return;
|
245 | }
|
246 | resetInputValue(null, value);
|
247 | }, [value, resetInputValue, focused, previousProps.value, freeSolo]);
|
248 | var listboxAvailable = open && filteredOptions.length > 0 && !readOnly;
|
249 | if (process.env.NODE_ENV !== 'production') {
|
250 | if (value !== null && !freeSolo && options.length > 0) {
|
251 | var missingValue = (multiple ? value : [value]).filter(function (value2) {
|
252 | return !options.some(function (option) {
|
253 | return isOptionEqualToValue(option, value2);
|
254 | });
|
255 | });
|
256 | if (missingValue.length > 0) {
|
257 | console.warn(["MUI: The value provided to ".concat(componentName, " is invalid."), "None of the options match with `".concat(missingValue.length > 1 ? JSON.stringify(missingValue) : JSON.stringify(missingValue[0]), "`."), 'You can use the `isOptionEqualToValue` prop to customize the equality test.'].join('\n'));
|
258 | }
|
259 | }
|
260 | }
|
261 | var focusTag = useEventCallback(function (tagToFocus) {
|
262 | if (tagToFocus === -1) {
|
263 | inputRef.current.focus();
|
264 | } else {
|
265 | anchorEl.querySelector("[data-tag-index=\"".concat(tagToFocus, "\"]")).focus();
|
266 | }
|
267 | });
|
268 |
|
269 |
|
270 | React.useEffect(function () {
|
271 | if (multiple && focusedTag > value.length - 1) {
|
272 | setFocusedTag(-1);
|
273 | focusTag(-1);
|
274 | }
|
275 | }, [value, multiple, focusedTag, focusTag]);
|
276 | function validOptionIndex(index, direction) {
|
277 | if (!listboxRef.current || index === -1) {
|
278 | return -1;
|
279 | }
|
280 | var nextFocus = index;
|
281 | while (true) {
|
282 |
|
283 | if (direction === 'next' && nextFocus === filteredOptions.length || direction === 'previous' && nextFocus === -1) {
|
284 | return -1;
|
285 | }
|
286 | var option = listboxRef.current.querySelector("[data-option-index=\"".concat(nextFocus, "\"]"));
|
287 |
|
288 |
|
289 | var nextFocusDisabled = disabledItemsFocusable ? false : !option || option.disabled || option.getAttribute('aria-disabled') === 'true';
|
290 | if (option && !option.hasAttribute('tabindex') || nextFocusDisabled) {
|
291 |
|
292 | nextFocus += direction === 'next' ? 1 : -1;
|
293 | } else {
|
294 | return nextFocus;
|
295 | }
|
296 | }
|
297 | }
|
298 | var setHighlightedIndex = useEventCallback(function (_ref2) {
|
299 | var event = _ref2.event,
|
300 | index = _ref2.index,
|
301 | _ref2$reason = _ref2.reason,
|
302 | reason = _ref2$reason === void 0 ? 'auto' : _ref2$reason;
|
303 | highlightedIndexRef.current = index;
|
304 |
|
305 |
|
306 | if (index === -1) {
|
307 | inputRef.current.removeAttribute('aria-activedescendant');
|
308 | } else {
|
309 | inputRef.current.setAttribute('aria-activedescendant', "".concat(id, "-option-").concat(index));
|
310 | }
|
311 | if (onHighlightChange) {
|
312 | onHighlightChange(event, index === -1 ? null : filteredOptions[index], reason);
|
313 | }
|
314 | if (!listboxRef.current) {
|
315 | return;
|
316 | }
|
317 | var prev = listboxRef.current.querySelector("[role=\"option\"].".concat(unstable_classNamePrefix, "-focused"));
|
318 | if (prev) {
|
319 | prev.classList.remove("".concat(unstable_classNamePrefix, "-focused"));
|
320 | prev.classList.remove("".concat(unstable_classNamePrefix, "-focusVisible"));
|
321 | }
|
322 | var listboxNode = listboxRef.current;
|
323 | if (listboxRef.current.getAttribute('role') !== 'listbox') {
|
324 | listboxNode = listboxRef.current.parentElement.querySelector('[role="listbox"]');
|
325 | }
|
326 |
|
327 |
|
328 | if (!listboxNode) {
|
329 | return;
|
330 | }
|
331 | if (index === -1) {
|
332 | listboxNode.scrollTop = 0;
|
333 | return;
|
334 | }
|
335 | var option = listboxRef.current.querySelector("[data-option-index=\"".concat(index, "\"]"));
|
336 | if (!option) {
|
337 | return;
|
338 | }
|
339 | option.classList.add("".concat(unstable_classNamePrefix, "-focused"));
|
340 | if (reason === 'keyboard') {
|
341 | option.classList.add("".concat(unstable_classNamePrefix, "-focusVisible"));
|
342 | }
|
343 |
|
344 |
|
345 |
|
346 |
|
347 |
|
348 |
|
349 | if (listboxNode.scrollHeight > listboxNode.clientHeight && reason !== 'mouse') {
|
350 | var element = option;
|
351 | var scrollBottom = listboxNode.clientHeight + listboxNode.scrollTop;
|
352 | var elementBottom = element.offsetTop + element.offsetHeight;
|
353 | if (elementBottom > scrollBottom) {
|
354 | listboxNode.scrollTop = elementBottom - listboxNode.clientHeight;
|
355 | } else if (element.offsetTop - element.offsetHeight * (groupBy ? 1.3 : 0) < listboxNode.scrollTop) {
|
356 | listboxNode.scrollTop = element.offsetTop - element.offsetHeight * (groupBy ? 1.3 : 0);
|
357 | }
|
358 | }
|
359 | });
|
360 | var changeHighlightedIndex = useEventCallback(function (_ref3) {
|
361 | var event = _ref3.event,
|
362 | diff = _ref3.diff,
|
363 | _ref3$direction = _ref3.direction,
|
364 | direction = _ref3$direction === void 0 ? 'next' : _ref3$direction,
|
365 | _ref3$reason = _ref3.reason,
|
366 | reason = _ref3$reason === void 0 ? 'auto' : _ref3$reason;
|
367 | if (!popupOpen) {
|
368 | return;
|
369 | }
|
370 | var getNextIndex = function getNextIndex() {
|
371 | var maxIndex = filteredOptions.length - 1;
|
372 | if (diff === 'reset') {
|
373 | return defaultHighlighted;
|
374 | }
|
375 | if (diff === 'start') {
|
376 | return 0;
|
377 | }
|
378 | if (diff === 'end') {
|
379 | return maxIndex;
|
380 | }
|
381 | var newIndex = highlightedIndexRef.current + diff;
|
382 | if (newIndex < 0) {
|
383 | if (newIndex === -1 && includeInputInList) {
|
384 | return -1;
|
385 | }
|
386 | if (disableListWrap && highlightedIndexRef.current !== -1 || Math.abs(diff) > 1) {
|
387 | return 0;
|
388 | }
|
389 | return maxIndex;
|
390 | }
|
391 | if (newIndex > maxIndex) {
|
392 | if (newIndex === maxIndex + 1 && includeInputInList) {
|
393 | return -1;
|
394 | }
|
395 | if (disableListWrap || Math.abs(diff) > 1) {
|
396 | return maxIndex;
|
397 | }
|
398 | return 0;
|
399 | }
|
400 | return newIndex;
|
401 | };
|
402 | var nextIndex = validOptionIndex(getNextIndex(), direction);
|
403 | setHighlightedIndex({
|
404 | index: nextIndex,
|
405 | reason: reason,
|
406 | event: event
|
407 | });
|
408 |
|
409 |
|
410 | if (autoComplete && diff !== 'reset') {
|
411 | if (nextIndex === -1) {
|
412 | inputRef.current.value = inputValue;
|
413 | } else {
|
414 | var option = getOptionLabel(filteredOptions[nextIndex]);
|
415 | inputRef.current.value = option;
|
416 |
|
417 |
|
418 |
|
419 | var index = option.toLowerCase().indexOf(inputValue.toLowerCase());
|
420 | if (index === 0 && inputValue.length > 0) {
|
421 | inputRef.current.setSelectionRange(inputValue.length, option.length);
|
422 | }
|
423 | }
|
424 | }
|
425 | });
|
426 | var checkHighlightedOptionExists = function checkHighlightedOptionExists() {
|
427 | var isSameValue = function isSameValue(value1, value2) {
|
428 | var label1 = value1 ? getOptionLabel(value1) : '';
|
429 | var label2 = value2 ? getOptionLabel(value2) : '';
|
430 | return label1 === label2;
|
431 | };
|
432 | if (highlightedIndexRef.current !== -1 && previousProps.filteredOptions && previousProps.filteredOptions.length !== filteredOptions.length && (multiple ? value.length === previousProps.value.length && previousProps.value.every(function (val, i) {
|
433 | return getOptionLabel(value[i]) === getOptionLabel(val);
|
434 | }) : isSameValue(previousProps.value, value))) {
|
435 | var previousHighlightedOption = previousProps.filteredOptions[highlightedIndexRef.current];
|
436 | if (previousHighlightedOption) {
|
437 | var previousHighlightedOptionExists = filteredOptions.some(function (option) {
|
438 | return getOptionLabel(option) === getOptionLabel(previousHighlightedOption);
|
439 | });
|
440 | if (previousHighlightedOptionExists) {
|
441 | return true;
|
442 | }
|
443 | }
|
444 | }
|
445 | return false;
|
446 | };
|
447 | var syncHighlightedIndex = React.useCallback(function () {
|
448 | if (!popupOpen) {
|
449 | return;
|
450 | }
|
451 |
|
452 |
|
453 |
|
454 | if (checkHighlightedOptionExists()) {
|
455 | return;
|
456 | }
|
457 | var valueItem = multiple ? value[0] : value;
|
458 |
|
459 |
|
460 | if (filteredOptions.length === 0 || valueItem == null) {
|
461 | changeHighlightedIndex({
|
462 | diff: 'reset'
|
463 | });
|
464 | return;
|
465 | }
|
466 | if (!listboxRef.current) {
|
467 | return;
|
468 | }
|
469 |
|
470 |
|
471 | if (valueItem != null) {
|
472 | var currentOption = filteredOptions[highlightedIndexRef.current];
|
473 |
|
474 |
|
475 | if (multiple && currentOption && findIndex(value, function (val) {
|
476 | return isOptionEqualToValue(currentOption, val);
|
477 | }) !== -1) {
|
478 | return;
|
479 | }
|
480 | var itemIndex = findIndex(filteredOptions, function (optionItem) {
|
481 | return isOptionEqualToValue(optionItem, valueItem);
|
482 | });
|
483 | if (itemIndex === -1) {
|
484 | changeHighlightedIndex({
|
485 | diff: 'reset'
|
486 | });
|
487 | } else {
|
488 | setHighlightedIndex({
|
489 | index: itemIndex
|
490 | });
|
491 | }
|
492 | return;
|
493 | }
|
494 |
|
495 |
|
496 | if (highlightedIndexRef.current >= filteredOptions.length - 1) {
|
497 | setHighlightedIndex({
|
498 | index: filteredOptions.length - 1
|
499 | });
|
500 | return;
|
501 | }
|
502 |
|
503 |
|
504 | setHighlightedIndex({
|
505 | index: highlightedIndexRef.current
|
506 | });
|
507 |
|
508 |
|
509 | }, [
|
510 |
|
511 | filteredOptions.length,
|
512 |
|
513 |
|
514 | multiple ? false : value, filterSelectedOptions, changeHighlightedIndex, setHighlightedIndex, popupOpen, inputValue, multiple]);
|
515 | var handleListboxRef = useEventCallback(function (node) {
|
516 | setRef(listboxRef, node);
|
517 | if (!node) {
|
518 | return;
|
519 | }
|
520 | syncHighlightedIndex();
|
521 | });
|
522 | if (process.env.NODE_ENV !== 'production') {
|
523 |
|
524 | React.useEffect(function () {
|
525 | if (!inputRef.current || inputRef.current.nodeName !== 'INPUT') {
|
526 | if (inputRef.current && inputRef.current.nodeName === 'TEXTAREA') {
|
527 | console.warn(["A textarea element was provided to ".concat(componentName, " where input was expected."), "This is not a supported scenario but it may work under certain conditions.", "A textarea keyboard navigation may conflict with Autocomplete controls (e.g. enter and arrow keys).", "Make sure to test keyboard navigation and add custom event handlers if necessary."].join('\n'));
|
528 | } else {
|
529 | console.error(["MUI: Unable to find the input element. It was resolved to ".concat(inputRef.current, " while an HTMLInputElement was expected."), "Instead, ".concat(componentName, " expects an input element."), '', componentName === 'useAutocomplete' ? 'Make sure you have bound getInputProps correctly and that the normal ref/effect resolutions order is guaranteed.' : 'Make sure you have customized the input component correctly.'].join('\n'));
|
530 | }
|
531 | }
|
532 | }, [componentName]);
|
533 | }
|
534 | React.useEffect(function () {
|
535 | syncHighlightedIndex();
|
536 | }, [syncHighlightedIndex]);
|
537 | var handleOpen = function handleOpen(event) {
|
538 | if (open) {
|
539 | return;
|
540 | }
|
541 | setOpenState(true);
|
542 | setInputPristine(true);
|
543 | if (onOpen) {
|
544 | onOpen(event);
|
545 | }
|
546 | };
|
547 | var handleClose = function handleClose(event, reason) {
|
548 | if (!open) {
|
549 | return;
|
550 | }
|
551 | setOpenState(false);
|
552 | if (onClose) {
|
553 | onClose(event, reason);
|
554 | }
|
555 | };
|
556 | var handleValue = function handleValue(event, newValue, reason, details) {
|
557 | if (multiple) {
|
558 | if (value.length === newValue.length && value.every(function (val, i) {
|
559 | return val === newValue[i];
|
560 | })) {
|
561 | return;
|
562 | }
|
563 | } else if (value === newValue) {
|
564 | return;
|
565 | }
|
566 | if (onChange) {
|
567 | onChange(event, newValue, reason, details);
|
568 | }
|
569 | setValueState(newValue);
|
570 | };
|
571 | var isTouch = React.useRef(false);
|
572 | var selectNewValue = function selectNewValue(event, option) {
|
573 | var reasonProp = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'selectOption';
|
574 | var origin = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'options';
|
575 | var reason = reasonProp;
|
576 | var newValue = option;
|
577 | if (multiple) {
|
578 | newValue = Array.isArray(value) ? value.slice() : [];
|
579 | if (process.env.NODE_ENV !== 'production') {
|
580 | var matches = newValue.filter(function (val) {
|
581 | return isOptionEqualToValue(option, val);
|
582 | });
|
583 | if (matches.length > 1) {
|
584 | console.error(["MUI: The `isOptionEqualToValue` method of ".concat(componentName, " does not handle the arguments correctly."), "The component expects a single value to match a given option but found ".concat(matches.length, " matches.")].join('\n'));
|
585 | }
|
586 | }
|
587 | var itemIndex = findIndex(newValue, function (valueItem) {
|
588 | return isOptionEqualToValue(option, valueItem);
|
589 | });
|
590 | if (itemIndex === -1) {
|
591 | newValue.push(option);
|
592 | } else if (origin !== 'freeSolo') {
|
593 | newValue.splice(itemIndex, 1);
|
594 | reason = 'removeOption';
|
595 | }
|
596 | }
|
597 | resetInputValue(event, newValue);
|
598 | handleValue(event, newValue, reason, {
|
599 | option: option
|
600 | });
|
601 | if (!disableCloseOnSelect && (!event || !event.ctrlKey && !event.metaKey)) {
|
602 | handleClose(event, reason);
|
603 | }
|
604 | if (blurOnSelect === true || blurOnSelect === 'touch' && isTouch.current || blurOnSelect === 'mouse' && !isTouch.current) {
|
605 | inputRef.current.blur();
|
606 | }
|
607 | };
|
608 | function validTagIndex(index, direction) {
|
609 | if (index === -1) {
|
610 | return -1;
|
611 | }
|
612 | var nextFocus = index;
|
613 | while (true) {
|
614 |
|
615 | if (direction === 'next' && nextFocus === value.length || direction === 'previous' && nextFocus === -1) {
|
616 | return -1;
|
617 | }
|
618 | var option = anchorEl.querySelector("[data-tag-index=\"".concat(nextFocus, "\"]"));
|
619 |
|
620 |
|
621 | if (!option || !option.hasAttribute('tabindex') || option.disabled || option.getAttribute('aria-disabled') === 'true') {
|
622 | nextFocus += direction === 'next' ? 1 : -1;
|
623 | } else {
|
624 | return nextFocus;
|
625 | }
|
626 | }
|
627 | }
|
628 | var handleFocusTag = function handleFocusTag(event, direction) {
|
629 | if (!multiple) {
|
630 | return;
|
631 | }
|
632 | if (inputValue === '') {
|
633 | handleClose(event, 'toggleInput');
|
634 | }
|
635 | var nextTag = focusedTag;
|
636 | if (focusedTag === -1) {
|
637 | if (inputValue === '' && direction === 'previous') {
|
638 | nextTag = value.length - 1;
|
639 | }
|
640 | } else {
|
641 | nextTag += direction === 'next' ? 1 : -1;
|
642 | if (nextTag < 0) {
|
643 | nextTag = 0;
|
644 | }
|
645 | if (nextTag === value.length) {
|
646 | nextTag = -1;
|
647 | }
|
648 | }
|
649 | nextTag = validTagIndex(nextTag, direction);
|
650 | setFocusedTag(nextTag);
|
651 | focusTag(nextTag);
|
652 | };
|
653 | var handleClear = function handleClear(event) {
|
654 | ignoreFocus.current = true;
|
655 | setInputValueState('');
|
656 | if (onInputChange) {
|
657 | onInputChange(event, '', 'clear');
|
658 | }
|
659 | handleValue(event, multiple ? [] : null, 'clear');
|
660 | };
|
661 | var handleKeyDown = function handleKeyDown(other) {
|
662 | return function (event) {
|
663 | if (other.onKeyDown) {
|
664 | other.onKeyDown(event);
|
665 | }
|
666 | if (event.defaultMuiPrevented) {
|
667 | return;
|
668 | }
|
669 | if (focusedTag !== -1 && ['ArrowLeft', 'ArrowRight'].indexOf(event.key) === -1) {
|
670 | setFocusedTag(-1);
|
671 | focusTag(-1);
|
672 | }
|
673 |
|
674 |
|
675 | if (event.which !== 229) {
|
676 | switch (event.key) {
|
677 | case 'Home':
|
678 | if (popupOpen && handleHomeEndKeys) {
|
679 |
|
680 | event.preventDefault();
|
681 | changeHighlightedIndex({
|
682 | diff: 'start',
|
683 | direction: 'next',
|
684 | reason: 'keyboard',
|
685 | event: event
|
686 | });
|
687 | }
|
688 | break;
|
689 | case 'End':
|
690 | if (popupOpen && handleHomeEndKeys) {
|
691 |
|
692 | event.preventDefault();
|
693 | changeHighlightedIndex({
|
694 | diff: 'end',
|
695 | direction: 'previous',
|
696 | reason: 'keyboard',
|
697 | event: event
|
698 | });
|
699 | }
|
700 | break;
|
701 | case 'PageUp':
|
702 |
|
703 | event.preventDefault();
|
704 | changeHighlightedIndex({
|
705 | diff: -pageSize,
|
706 | direction: 'previous',
|
707 | reason: 'keyboard',
|
708 | event: event
|
709 | });
|
710 | handleOpen(event);
|
711 | break;
|
712 | case 'PageDown':
|
713 |
|
714 | event.preventDefault();
|
715 | changeHighlightedIndex({
|
716 | diff: pageSize,
|
717 | direction: 'next',
|
718 | reason: 'keyboard',
|
719 | event: event
|
720 | });
|
721 | handleOpen(event);
|
722 | break;
|
723 | case 'ArrowDown':
|
724 |
|
725 | event.preventDefault();
|
726 | changeHighlightedIndex({
|
727 | diff: 1,
|
728 | direction: 'next',
|
729 | reason: 'keyboard',
|
730 | event: event
|
731 | });
|
732 | handleOpen(event);
|
733 | break;
|
734 | case 'ArrowUp':
|
735 |
|
736 | event.preventDefault();
|
737 | changeHighlightedIndex({
|
738 | diff: -1,
|
739 | direction: 'previous',
|
740 | reason: 'keyboard',
|
741 | event: event
|
742 | });
|
743 | handleOpen(event);
|
744 | break;
|
745 | case 'ArrowLeft':
|
746 | handleFocusTag(event, 'previous');
|
747 | break;
|
748 | case 'ArrowRight':
|
749 | handleFocusTag(event, 'next');
|
750 | break;
|
751 | case 'Enter':
|
752 | if (highlightedIndexRef.current !== -1 && popupOpen) {
|
753 | var option = filteredOptions[highlightedIndexRef.current];
|
754 | var disabled = getOptionDisabled ? getOptionDisabled(option) : false;
|
755 |
|
756 |
|
757 | event.preventDefault();
|
758 | if (disabled) {
|
759 | return;
|
760 | }
|
761 | selectNewValue(event, option, 'selectOption');
|
762 |
|
763 |
|
764 | if (autoComplete) {
|
765 | inputRef.current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length);
|
766 | }
|
767 | } else if (freeSolo && inputValue !== '' && inputValueIsSelectedValue === false) {
|
768 | if (multiple) {
|
769 |
|
770 | event.preventDefault();
|
771 | }
|
772 | selectNewValue(event, inputValue, 'createOption', 'freeSolo');
|
773 | }
|
774 | break;
|
775 | case 'Escape':
|
776 | if (popupOpen) {
|
777 |
|
778 | event.preventDefault();
|
779 |
|
780 | event.stopPropagation();
|
781 | handleClose(event, 'escape');
|
782 | } else if (clearOnEscape && (inputValue !== '' || multiple && value.length > 0)) {
|
783 |
|
784 | event.preventDefault();
|
785 |
|
786 | event.stopPropagation();
|
787 | handleClear(event);
|
788 | }
|
789 | break;
|
790 | case 'Backspace':
|
791 | if (multiple && !readOnly && inputValue === '' && value.length > 0) {
|
792 | var index = focusedTag === -1 ? value.length - 1 : focusedTag;
|
793 | var newValue = value.slice();
|
794 | newValue.splice(index, 1);
|
795 | handleValue(event, newValue, 'removeOption', {
|
796 | option: value[index]
|
797 | });
|
798 | }
|
799 | break;
|
800 | case 'Delete':
|
801 | if (multiple && !readOnly && inputValue === '' && value.length > 0 && focusedTag !== -1) {
|
802 | var _index = focusedTag;
|
803 | var _newValue = value.slice();
|
804 | _newValue.splice(_index, 1);
|
805 | handleValue(event, _newValue, 'removeOption', {
|
806 | option: value[_index]
|
807 | });
|
808 | }
|
809 | break;
|
810 | default:
|
811 | }
|
812 | }
|
813 | };
|
814 | };
|
815 | var handleFocus = function handleFocus(event) {
|
816 | setFocused(true);
|
817 | if (openOnFocus && !ignoreFocus.current) {
|
818 | handleOpen(event);
|
819 | }
|
820 | };
|
821 | var handleBlur = function handleBlur(event) {
|
822 |
|
823 | if (unstable_isActiveElementInListbox(listboxRef)) {
|
824 | inputRef.current.focus();
|
825 | return;
|
826 | }
|
827 | setFocused(false);
|
828 | firstFocus.current = true;
|
829 | ignoreFocus.current = false;
|
830 | if (autoSelect && highlightedIndexRef.current !== -1 && popupOpen) {
|
831 | selectNewValue(event, filteredOptions[highlightedIndexRef.current], 'blur');
|
832 | } else if (autoSelect && freeSolo && inputValue !== '') {
|
833 | selectNewValue(event, inputValue, 'blur', 'freeSolo');
|
834 | } else if (clearOnBlur) {
|
835 | resetInputValue(event, value);
|
836 | }
|
837 | handleClose(event, 'blur');
|
838 | };
|
839 | var handleInputChange = function handleInputChange(event) {
|
840 | var newValue = event.target.value;
|
841 | if (inputValue !== newValue) {
|
842 | setInputValueState(newValue);
|
843 | setInputPristine(false);
|
844 | if (onInputChange) {
|
845 | onInputChange(event, newValue, 'input');
|
846 | }
|
847 | }
|
848 | if (newValue === '') {
|
849 | if (!disableClearable && !multiple) {
|
850 | handleValue(event, null, 'clear');
|
851 | }
|
852 | } else {
|
853 | handleOpen(event);
|
854 | }
|
855 | };
|
856 | var handleOptionMouseMove = function handleOptionMouseMove(event) {
|
857 | var index = Number(event.currentTarget.getAttribute('data-option-index'));
|
858 | if (highlightedIndexRef.current !== index) {
|
859 | setHighlightedIndex({
|
860 | event: event,
|
861 | index: index,
|
862 | reason: 'mouse'
|
863 | });
|
864 | }
|
865 | };
|
866 | var handleOptionTouchStart = function handleOptionTouchStart(event) {
|
867 | setHighlightedIndex({
|
868 | event: event,
|
869 | index: Number(event.currentTarget.getAttribute('data-option-index')),
|
870 | reason: 'touch'
|
871 | });
|
872 | isTouch.current = true;
|
873 | };
|
874 | var handleOptionClick = function handleOptionClick(event) {
|
875 | var index = Number(event.currentTarget.getAttribute('data-option-index'));
|
876 | selectNewValue(event, filteredOptions[index], 'selectOption');
|
877 | isTouch.current = false;
|
878 | };
|
879 | var handleTagDelete = function handleTagDelete(index) {
|
880 | return function (event) {
|
881 | var newValue = value.slice();
|
882 | newValue.splice(index, 1);
|
883 | handleValue(event, newValue, 'removeOption', {
|
884 | option: value[index]
|
885 | });
|
886 | };
|
887 | };
|
888 | var handlePopupIndicator = function handlePopupIndicator(event) {
|
889 | if (open) {
|
890 | handleClose(event, 'toggleInput');
|
891 | } else {
|
892 | handleOpen(event);
|
893 | }
|
894 | };
|
895 |
|
896 |
|
897 | var handleMouseDown = function handleMouseDown(event) {
|
898 |
|
899 | if (!event.currentTarget.contains(event.target)) {
|
900 | return;
|
901 | }
|
902 | if (event.target.getAttribute('id') !== id) {
|
903 | event.preventDefault();
|
904 | }
|
905 | };
|
906 |
|
907 |
|
908 | var handleClick = function handleClick(event) {
|
909 |
|
910 | if (!event.currentTarget.contains(event.target)) {
|
911 | return;
|
912 | }
|
913 | inputRef.current.focus();
|
914 | if (selectOnFocus && firstFocus.current && inputRef.current.selectionEnd - inputRef.current.selectionStart === 0) {
|
915 | inputRef.current.select();
|
916 | }
|
917 | firstFocus.current = false;
|
918 | };
|
919 | var handleInputMouseDown = function handleInputMouseDown(event) {
|
920 | if (inputValue === '' || !open) {
|
921 | handlePopupIndicator(event);
|
922 | }
|
923 | };
|
924 | var dirty = freeSolo && inputValue.length > 0;
|
925 | dirty = dirty || (multiple ? value.length > 0 : value !== null);
|
926 | var groupedOptions = filteredOptions;
|
927 | if (groupBy) {
|
928 |
|
929 | var indexBy = new Map();
|
930 | var warn = false;
|
931 | groupedOptions = filteredOptions.reduce(function (acc, option, index) {
|
932 | var group = groupBy(option);
|
933 | if (acc.length > 0 && acc[acc.length - 1].group === group) {
|
934 | acc[acc.length - 1].options.push(option);
|
935 | } else {
|
936 | if (process.env.NODE_ENV !== 'production') {
|
937 | if (indexBy.get(group) && !warn) {
|
938 | console.warn("MUI: The options provided combined with the `groupBy` method of ".concat(componentName, " returns duplicated headers."), 'You can solve the issue by sorting the options with the output of `groupBy`.');
|
939 | warn = true;
|
940 | }
|
941 | indexBy.set(group, true);
|
942 | }
|
943 | acc.push({
|
944 | key: index,
|
945 | index: index,
|
946 | group: group,
|
947 | options: [option]
|
948 | });
|
949 | }
|
950 | return acc;
|
951 | }, []);
|
952 | }
|
953 | if (disabledProp && focused) {
|
954 | handleBlur();
|
955 | }
|
956 | return {
|
957 | getRootProps: function getRootProps() {
|
958 | var other = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
959 | return _extends({
|
960 | 'aria-owns': listboxAvailable ? "".concat(id, "-listbox") : null
|
961 | }, other, {
|
962 | onKeyDown: handleKeyDown(other),
|
963 | onMouseDown: handleMouseDown,
|
964 | onClick: handleClick
|
965 | });
|
966 | },
|
967 | getInputLabelProps: function getInputLabelProps() {
|
968 | return {
|
969 | id: "".concat(id, "-label"),
|
970 | htmlFor: id
|
971 | };
|
972 | },
|
973 | getInputProps: function getInputProps() {
|
974 | return {
|
975 | id: id,
|
976 | value: inputValue,
|
977 | onBlur: handleBlur,
|
978 | onFocus: handleFocus,
|
979 | onChange: handleInputChange,
|
980 | onMouseDown: handleInputMouseDown,
|
981 |
|
982 |
|
983 | 'aria-activedescendant': popupOpen ? '' : null,
|
984 | 'aria-autocomplete': autoComplete ? 'both' : 'list',
|
985 | 'aria-controls': listboxAvailable ? "".concat(id, "-listbox") : undefined,
|
986 | 'aria-expanded': listboxAvailable,
|
987 |
|
988 |
|
989 | autoComplete: 'off',
|
990 | ref: inputRef,
|
991 | autoCapitalize: 'none',
|
992 | spellCheck: 'false',
|
993 | role: 'combobox',
|
994 | disabled: disabledProp
|
995 | };
|
996 | },
|
997 | getClearProps: function getClearProps() {
|
998 | return {
|
999 | tabIndex: -1,
|
1000 | onClick: handleClear
|
1001 | };
|
1002 | },
|
1003 | getPopupIndicatorProps: function getPopupIndicatorProps() {
|
1004 | return {
|
1005 | tabIndex: -1,
|
1006 | onClick: handlePopupIndicator
|
1007 | };
|
1008 | },
|
1009 | getTagProps: function getTagProps(_ref4) {
|
1010 | var index = _ref4.index;
|
1011 | return _extends({
|
1012 | key: index,
|
1013 | 'data-tag-index': index,
|
1014 | tabIndex: -1
|
1015 | }, !readOnly && {
|
1016 | onDelete: handleTagDelete(index)
|
1017 | });
|
1018 | },
|
1019 | getListboxProps: function getListboxProps() {
|
1020 | return {
|
1021 | role: 'listbox',
|
1022 | id: "".concat(id, "-listbox"),
|
1023 | 'aria-labelledby': "".concat(id, "-label"),
|
1024 | ref: handleListboxRef,
|
1025 | onMouseDown: function onMouseDown(event) {
|
1026 |
|
1027 | event.preventDefault();
|
1028 | }
|
1029 | };
|
1030 | },
|
1031 | getOptionProps: function getOptionProps(_ref5) {
|
1032 | var index = _ref5.index,
|
1033 | option = _ref5.option;
|
1034 | var selected = (multiple ? value : [value]).some(function (value2) {
|
1035 | return value2 != null && isOptionEqualToValue(option, value2);
|
1036 | });
|
1037 | var disabled = getOptionDisabled ? getOptionDisabled(option) : false;
|
1038 | return {
|
1039 | key: getOptionLabel(option),
|
1040 | tabIndex: -1,
|
1041 | role: 'option',
|
1042 | id: "".concat(id, "-option-").concat(index),
|
1043 | onMouseMove: handleOptionMouseMove,
|
1044 | onClick: handleOptionClick,
|
1045 | onTouchStart: handleOptionTouchStart,
|
1046 | 'data-option-index': index,
|
1047 | 'aria-disabled': disabled,
|
1048 | 'aria-selected': selected
|
1049 | };
|
1050 | },
|
1051 | id: id,
|
1052 | inputValue: inputValue,
|
1053 | value: value,
|
1054 | dirty: dirty,
|
1055 | expanded: popupOpen && anchorEl,
|
1056 | popupOpen: popupOpen,
|
1057 | focused: focused || focusedTag !== -1,
|
1058 | anchorEl: anchorEl,
|
1059 | setAnchorEl: setAnchorEl,
|
1060 | focusedTag: focusedTag,
|
1061 | groupedOptions: groupedOptions
|
1062 | };
|
1063 | } |
\ | No newline at end of file |