UNPKG

143 kBJavaScriptView Raw
1import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose';
2import _extends from '@babel/runtime/helpers/esm/extends';
3import _assertThisInitialized from '@babel/runtime/helpers/esm/assertThisInitialized';
4import _inheritsLoose from '@babel/runtime/helpers/esm/inheritsLoose';
5import PropTypes from 'prop-types';
6import { cloneElement, Component, useRef, useEffect, useLayoutEffect, useCallback, useReducer, useMemo } from 'react';
7import { isForwardRef } from 'react-is';
8import computeScrollIntoView from 'compute-scroll-into-view';
9
10var idCounter = 0;
11/**
12 * Accepts a parameter and returns it if it's a function
13 * or a noop function if it's not. This allows us to
14 * accept a callback, but not worry about it if it's not
15 * passed.
16 * @param {Function} cb the callback
17 * @return {Function} a function
18 */
19
20function cbToCb(cb) {
21 return typeof cb === 'function' ? cb : noop;
22}
23
24function noop() {}
25/**
26 * Scroll node into view if necessary
27 * @param {HTMLElement} node the element that should scroll into view
28 * @param {HTMLElement} menuNode the menu element of the component
29 */
30
31
32function scrollIntoView(node, menuNode) {
33 if (!node) {
34 return;
35 }
36
37 var actions = computeScrollIntoView(node, {
38 boundary: menuNode,
39 block: 'nearest',
40 scrollMode: 'if-needed'
41 });
42 actions.forEach(function (_ref) {
43 var el = _ref.el,
44 top = _ref.top,
45 left = _ref.left;
46 el.scrollTop = top;
47 el.scrollLeft = left;
48 });
49}
50/**
51 * @param {HTMLElement} parent the parent node
52 * @param {HTMLElement} child the child node
53 * @return {Boolean} whether the parent is the child or the child is in the parent
54 */
55
56
57function isOrContainsNode(parent, child) {
58 return parent === child || child instanceof Node && parent.contains && parent.contains(child);
59}
60/**
61 * Simple debounce implementation. Will call the given
62 * function once after the time given has passed since
63 * it was last called.
64 * @param {Function} fn the function to call after the time
65 * @param {Number} time the time to wait
66 * @return {Function} the debounced function
67 */
68
69
70function debounce(fn, time) {
71 var timeoutId;
72
73 function cancel() {
74 if (timeoutId) {
75 clearTimeout(timeoutId);
76 }
77 }
78
79 function wrapper() {
80 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
81 args[_key] = arguments[_key];
82 }
83
84 cancel();
85 timeoutId = setTimeout(function () {
86 timeoutId = null;
87 fn.apply(void 0, args);
88 }, time);
89 }
90
91 wrapper.cancel = cancel;
92 return wrapper;
93}
94/**
95 * This is intended to be used to compose event handlers.
96 * They are executed in order until one of them sets
97 * `event.preventDownshiftDefault = true`.
98 * @param {...Function} fns the event handler functions
99 * @return {Function} the event handler to add to an element
100 */
101
102
103function callAllEventHandlers() {
104 for (var _len2 = arguments.length, fns = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
105 fns[_key2] = arguments[_key2];
106 }
107
108 return function (event) {
109 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
110 args[_key3 - 1] = arguments[_key3];
111 }
112
113 return fns.some(function (fn) {
114 if (fn) {
115 fn.apply(void 0, [event].concat(args));
116 }
117
118 return event.preventDownshiftDefault || event.hasOwnProperty('nativeEvent') && event.nativeEvent.preventDownshiftDefault;
119 });
120 };
121}
122
123function handleRefs() {
124 for (var _len4 = arguments.length, refs = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
125 refs[_key4] = arguments[_key4];
126 }
127
128 return function (node) {
129 refs.forEach(function (ref) {
130 if (typeof ref === 'function') {
131 ref(node);
132 } else if (ref) {
133 ref.current = node;
134 }
135 });
136 };
137}
138/**
139 * This generates a unique ID for an instance of Downshift
140 * @return {String} the unique ID
141 */
142
143
144function generateId() {
145 return String(idCounter++);
146}
147/**
148 * Resets idCounter to 0. Used for SSR.
149 */
150
151
152function resetIdCounter() {
153 idCounter = 0;
154}
155/**
156 * Default implementation for status message. Only added when menu is open.
157 * Will specift if there are results in the list, and if so, how many,
158 * and what keys are relevant.
159 *
160 * @param {Object} param the downshift state and other relevant properties
161 * @return {String} the a11y status message
162 */
163
164
165function getA11yStatusMessage(_ref2) {
166 var isOpen = _ref2.isOpen,
167 resultCount = _ref2.resultCount,
168 previousResultCount = _ref2.previousResultCount;
169
170 if (!isOpen) {
171 return '';
172 }
173
174 if (!resultCount) {
175 return 'No results are available.';
176 }
177
178 if (resultCount !== previousResultCount) {
179 return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
180 }
181
182 return '';
183}
184/**
185 * Takes an argument and if it's an array, returns the first item in the array
186 * otherwise returns the argument
187 * @param {*} arg the maybe-array
188 * @param {*} defaultValue the value if arg is falsey not defined
189 * @return {*} the arg or it's first item
190 */
191
192
193function unwrapArray(arg, defaultValue) {
194 arg = Array.isArray(arg) ?
195 /* istanbul ignore next (preact) */
196 arg[0] : arg;
197
198 if (!arg && defaultValue) {
199 return defaultValue;
200 } else {
201 return arg;
202 }
203}
204/**
205 * @param {Object} element (P)react element
206 * @return {Boolean} whether it's a DOM element
207 */
208
209
210function isDOMElement(element) {
211 // then we assume this is react
212 return typeof element.type === 'string';
213}
214/**
215 * @param {Object} element (P)react element
216 * @return {Object} the props
217 */
218
219
220function getElementProps(element) {
221 return element.props;
222}
223/**
224 * Throws a helpful error message for required properties. Useful
225 * to be used as a default in destructuring or object params.
226 * @param {String} fnName the function name
227 * @param {String} propName the prop name
228 */
229
230
231function requiredProp(fnName, propName) {
232 // eslint-disable-next-line no-console
233 console.error("The property \"" + propName + "\" is required in \"" + fnName + "\"");
234}
235
236var stateKeys = ['highlightedIndex', 'inputValue', 'isOpen', 'selectedItem', 'type'];
237/**
238 * @param {Object} state the state object
239 * @return {Object} state that is relevant to downshift
240 */
241
242function pickState(state) {
243 if (state === void 0) {
244 state = {};
245 }
246
247 var result = {};
248 stateKeys.forEach(function (k) {
249 if (state.hasOwnProperty(k)) {
250 result[k] = state[k];
251 }
252 });
253 return result;
254}
255/**
256 * This will perform a shallow merge of the given state object
257 * with the state coming from props
258 * (for the controlled component scenario)
259 * This is used in state updater functions so they're referencing
260 * the right state regardless of where it comes from.
261 *
262 * @param {Object} state The state of the component/hook.
263 * @param {Object} props The props that may contain controlled values.
264 * @returns {Object} The merged controlled state.
265 */
266
267
268function getState(state, props) {
269 return Object.keys(state).reduce(function (prevState, key) {
270 prevState[key] = isControlledProp(props, key) ? props[key] : state[key];
271 return prevState;
272 }, {});
273}
274/**
275 * This determines whether a prop is a "controlled prop" meaning it is
276 * state which is controlled by the outside of this component rather
277 * than within this component.
278 *
279 * @param {Object} props The props that may contain controlled values.
280 * @param {String} key the key to check
281 * @return {Boolean} whether it is a controlled controlled prop
282 */
283
284
285function isControlledProp(props, key) {
286 return props[key] !== undefined;
287}
288/**
289 * Normalizes the 'key' property of a KeyboardEvent in IE/Edge
290 * @param {Object} event a keyboardEvent object
291 * @return {String} keyboard key
292 */
293
294
295function normalizeArrowKey(event) {
296 var key = event.key,
297 keyCode = event.keyCode;
298 /* istanbul ignore next (ie) */
299
300 if (keyCode >= 37 && keyCode <= 40 && key.indexOf('Arrow') !== 0) {
301 return "Arrow" + key;
302 }
303
304 return key;
305}
306/**
307 * Simple check if the value passed is object literal
308 * @param {*} obj any things
309 * @return {Boolean} whether it's object literal
310 */
311
312
313function isPlainObject(obj) {
314 return Object.prototype.toString.call(obj) === '[object Object]';
315}
316/**
317 * Returns the new index in the list, in a circular way. If next value is out of bonds from the total,
318 * it will wrap to either 0 or itemCount - 1.
319 *
320 * @param {number} moveAmount Number of positions to move. Negative to move backwards, positive forwards.
321 * @param {number} baseIndex The initial position to move from.
322 * @param {number} itemCount The total number of items.
323 * @param {Function} getItemNodeFromIndex Used to check if item is disabled.
324 * @param {boolean} circular Specify if navigation is circular. Default is true.
325 * @returns {number} The new index after the move.
326 */
327
328
329function getNextWrappingIndex(moveAmount, baseIndex, itemCount, getItemNodeFromIndex, circular) {
330 if (circular === void 0) {
331 circular = true;
332 }
333
334 if (itemCount === 0) {
335 return -1;
336 }
337
338 var itemsLastIndex = itemCount - 1;
339
340 if (typeof baseIndex !== 'number' || baseIndex < 0 || baseIndex >= itemCount) {
341 baseIndex = moveAmount > 0 ? -1 : itemsLastIndex + 1;
342 }
343
344 var newIndex = baseIndex + moveAmount;
345
346 if (newIndex < 0) {
347 newIndex = circular ? itemsLastIndex : 0;
348 } else if (newIndex > itemsLastIndex) {
349 newIndex = circular ? 0 : itemsLastIndex;
350 }
351
352 var nonDisabledNewIndex = getNextNonDisabledIndex(moveAmount, newIndex, itemCount, getItemNodeFromIndex, circular);
353
354 if (nonDisabledNewIndex === -1) {
355 return baseIndex >= itemCount ? -1 : baseIndex;
356 }
357
358 return nonDisabledNewIndex;
359}
360/**
361 * Returns the next index in the list of an item that is not disabled.
362 *
363 * @param {number} moveAmount Number of positions to move. Negative to move backwards, positive forwards.
364 * @param {number} baseIndex The initial position to move from.
365 * @param {number} itemCount The total number of items.
366 * @param {Function} getItemNodeFromIndex Used to check if item is disabled.
367 * @param {boolean} circular Specify if navigation is circular. Default is true.
368 * @returns {number} The new index. Returns baseIndex if item is not disabled. Returns next non-disabled item otherwise. If no non-disabled found it will return -1.
369 */
370
371
372function getNextNonDisabledIndex(moveAmount, baseIndex, itemCount, getItemNodeFromIndex, circular) {
373 var currentElementNode = getItemNodeFromIndex(baseIndex);
374
375 if (!currentElementNode || !currentElementNode.hasAttribute('disabled')) {
376 return baseIndex;
377 }
378
379 if (moveAmount > 0) {
380 for (var index = baseIndex + 1; index < itemCount; index++) {
381 if (!getItemNodeFromIndex(index).hasAttribute('disabled')) {
382 return index;
383 }
384 }
385 } else {
386 for (var _index = baseIndex - 1; _index >= 0; _index--) {
387 if (!getItemNodeFromIndex(_index).hasAttribute('disabled')) {
388 return _index;
389 }
390 }
391 }
392
393 if (circular) {
394 return moveAmount > 0 ? getNextNonDisabledIndex(1, 0, itemCount, getItemNodeFromIndex, false) : getNextNonDisabledIndex(-1, itemCount - 1, itemCount, getItemNodeFromIndex, false);
395 }
396
397 return -1;
398}
399/**
400 * Checks if event target is within the downshift elements.
401 *
402 * @param {EventTarget} target Target to check.
403 * @param {HTMLElement[]} downshiftElements The elements that form downshift (list, toggle button etc).
404 * @param {Document} document The document.
405 * @param {boolean} checkActiveElement Whether to also check activeElement.
406 *
407 * @returns {boolean} Whether or not the target is within downshift elements.
408 */
409
410
411function targetWithinDownshift(target, downshiftElements, document, checkActiveElement) {
412 if (checkActiveElement === void 0) {
413 checkActiveElement = true;
414 }
415
416 return downshiftElements.some(function (contextNode) {
417 return contextNode && (isOrContainsNode(contextNode, target) || checkActiveElement && isOrContainsNode(contextNode, document.activeElement));
418 });
419} // eslint-disable-next-line import/no-mutable-exports
420
421
422var validateControlledUnchanged = noop;
423/* istanbul ignore next */
424
425if (process.env.NODE_ENV !== 'production') {
426 validateControlledUnchanged = function (state, prevProps, nextProps) {
427 var warningDescription = "This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props";
428 Object.keys(state).forEach(function (propKey) {
429 if (prevProps[propKey] !== undefined && nextProps[propKey] === undefined) {
430 // eslint-disable-next-line no-console
431 console.error("downshift: A component has changed the controlled prop \"" + propKey + "\" to be uncontrolled. " + warningDescription);
432 } else if (prevProps[propKey] === undefined && nextProps[propKey] !== undefined) {
433 // eslint-disable-next-line no-console
434 console.error("downshift: A component has changed the uncontrolled prop \"" + propKey + "\" to be controlled. " + warningDescription);
435 }
436 });
437 };
438}
439
440var cleanupStatus = debounce(function (documentProp) {
441 getStatusDiv(documentProp).textContent = '';
442}, 500);
443/**
444 * @param {String} status the status message
445 * @param {Object} documentProp document passed by the user.
446 */
447
448function setStatus(status, documentProp) {
449 var div = getStatusDiv(documentProp);
450
451 if (!status) {
452 return;
453 }
454
455 div.textContent = status;
456 cleanupStatus(documentProp);
457}
458/**
459 * Get the status node or create it if it does not already exist.
460 * @param {Object} documentProp document passed by the user.
461 * @return {HTMLElement} the status node.
462 */
463
464
465function getStatusDiv(documentProp) {
466 if (documentProp === void 0) {
467 documentProp = document;
468 }
469
470 var statusDiv = documentProp.getElementById('a11y-status-message');
471
472 if (statusDiv) {
473 return statusDiv;
474 }
475
476 statusDiv = documentProp.createElement('div');
477 statusDiv.setAttribute('id', 'a11y-status-message');
478 statusDiv.setAttribute('role', 'status');
479 statusDiv.setAttribute('aria-live', 'polite');
480 statusDiv.setAttribute('aria-relevant', 'additions text');
481 Object.assign(statusDiv.style, {
482 border: '0',
483 clip: 'rect(0 0 0 0)',
484 height: '1px',
485 margin: '-1px',
486 overflow: 'hidden',
487 padding: '0',
488 position: 'absolute',
489 width: '1px'
490 });
491 documentProp.body.appendChild(statusDiv);
492 return statusDiv;
493}
494
495var unknown = process.env.NODE_ENV !== "production" ? '__autocomplete_unknown__' : 0;
496var mouseUp = process.env.NODE_ENV !== "production" ? '__autocomplete_mouseup__' : 1;
497var itemMouseEnter = process.env.NODE_ENV !== "production" ? '__autocomplete_item_mouseenter__' : 2;
498var keyDownArrowUp = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_arrow_up__' : 3;
499var keyDownArrowDown = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_arrow_down__' : 4;
500var keyDownEscape = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_escape__' : 5;
501var keyDownEnter = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_enter__' : 6;
502var keyDownHome = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_home__' : 7;
503var keyDownEnd = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_end__' : 8;
504var clickItem = process.env.NODE_ENV !== "production" ? '__autocomplete_click_item__' : 9;
505var blurInput = process.env.NODE_ENV !== "production" ? '__autocomplete_blur_input__' : 10;
506var changeInput = process.env.NODE_ENV !== "production" ? '__autocomplete_change_input__' : 11;
507var keyDownSpaceButton = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_space_button__' : 12;
508var clickButton = process.env.NODE_ENV !== "production" ? '__autocomplete_click_button__' : 13;
509var blurButton = process.env.NODE_ENV !== "production" ? '__autocomplete_blur_button__' : 14;
510var controlledPropUpdatedSelectedItem = process.env.NODE_ENV !== "production" ? '__autocomplete_controlled_prop_updated_selected_item__' : 15;
511var touchEnd = process.env.NODE_ENV !== "production" ? '__autocomplete_touchend__' : 16;
512
513var stateChangeTypes = /*#__PURE__*/Object.freeze({
514 __proto__: null,
515 unknown: unknown,
516 mouseUp: mouseUp,
517 itemMouseEnter: itemMouseEnter,
518 keyDownArrowUp: keyDownArrowUp,
519 keyDownArrowDown: keyDownArrowDown,
520 keyDownEscape: keyDownEscape,
521 keyDownEnter: keyDownEnter,
522 keyDownHome: keyDownHome,
523 keyDownEnd: keyDownEnd,
524 clickItem: clickItem,
525 blurInput: blurInput,
526 changeInput: changeInput,
527 keyDownSpaceButton: keyDownSpaceButton,
528 clickButton: clickButton,
529 blurButton: blurButton,
530 controlledPropUpdatedSelectedItem: controlledPropUpdatedSelectedItem,
531 touchEnd: touchEnd
532});
533
534var Downshift = /*#__PURE__*/function () {
535 var Downshift = /*#__PURE__*/function (_Component) {
536 _inheritsLoose(Downshift, _Component);
537
538 function Downshift(_props) {
539 var _this = _Component.call(this, _props) || this;
540
541 _this.id = _this.props.id || "downshift-" + generateId();
542 _this.menuId = _this.props.menuId || _this.id + "-menu";
543 _this.labelId = _this.props.labelId || _this.id + "-label";
544 _this.inputId = _this.props.inputId || _this.id + "-input";
545
546 _this.getItemId = _this.props.getItemId || function (index) {
547 return _this.id + "-item-" + index;
548 };
549
550 _this.input = null;
551 _this.items = [];
552 _this.itemCount = null;
553 _this.previousResultCount = 0;
554 _this.timeoutIds = [];
555
556 _this.internalSetTimeout = function (fn, time) {
557 var id = setTimeout(function () {
558 _this.timeoutIds = _this.timeoutIds.filter(function (i) {
559 return i !== id;
560 });
561 fn();
562 }, time);
563
564 _this.timeoutIds.push(id);
565 };
566
567 _this.setItemCount = function (count) {
568 _this.itemCount = count;
569 };
570
571 _this.unsetItemCount = function () {
572 _this.itemCount = null;
573 };
574
575 _this.setHighlightedIndex = function (highlightedIndex, otherStateToSet) {
576 if (highlightedIndex === void 0) {
577 highlightedIndex = _this.props.defaultHighlightedIndex;
578 }
579
580 if (otherStateToSet === void 0) {
581 otherStateToSet = {};
582 }
583
584 otherStateToSet = pickState(otherStateToSet);
585
586 _this.internalSetState(_extends({
587 highlightedIndex: highlightedIndex
588 }, otherStateToSet));
589 };
590
591 _this.clearSelection = function (cb) {
592 _this.internalSetState({
593 selectedItem: null,
594 inputValue: '',
595 highlightedIndex: _this.props.defaultHighlightedIndex,
596 isOpen: _this.props.defaultIsOpen
597 }, cb);
598 };
599
600 _this.selectItem = function (item, otherStateToSet, cb) {
601 otherStateToSet = pickState(otherStateToSet);
602
603 _this.internalSetState(_extends({
604 isOpen: _this.props.defaultIsOpen,
605 highlightedIndex: _this.props.defaultHighlightedIndex,
606 selectedItem: item,
607 inputValue: _this.props.itemToString(item)
608 }, otherStateToSet), cb);
609 };
610
611 _this.selectItemAtIndex = function (itemIndex, otherStateToSet, cb) {
612 var item = _this.items[itemIndex];
613
614 if (item == null) {
615 return;
616 }
617
618 _this.selectItem(item, otherStateToSet, cb);
619 };
620
621 _this.selectHighlightedItem = function (otherStateToSet, cb) {
622 return _this.selectItemAtIndex(_this.getState().highlightedIndex, otherStateToSet, cb);
623 };
624
625 _this.internalSetState = function (stateToSet, cb) {
626 var isItemSelected, onChangeArg;
627 var onStateChangeArg = {};
628 var isStateToSetFunction = typeof stateToSet === 'function'; // we want to call `onInputValueChange` before the `setState` call
629 // so someone controlling the `inputValue` state gets notified of
630 // the input change as soon as possible. This avoids issues with
631 // preserving the cursor position.
632 // See https://github.com/downshift-js/downshift/issues/217 for more info.
633
634 if (!isStateToSetFunction && stateToSet.hasOwnProperty('inputValue')) {
635 _this.props.onInputValueChange(stateToSet.inputValue, _extends({}, _this.getStateAndHelpers(), stateToSet));
636 }
637
638 return _this.setState(function (state) {
639 state = _this.getState(state);
640 var newStateToSet = isStateToSetFunction ? stateToSet(state) : stateToSet; // Your own function that could modify the state that will be set.
641
642 newStateToSet = _this.props.stateReducer(state, newStateToSet); // checks if an item is selected, regardless of if it's different from
643 // what was selected before
644 // used to determine if onSelect and onChange callbacks should be called
645
646 isItemSelected = newStateToSet.hasOwnProperty('selectedItem'); // this keeps track of the object we want to call with setState
647
648 var nextState = {}; // this is just used to tell whether the state changed
649 // and we're trying to update that state. OR if the selection has changed and we're
650 // trying to update the selection
651
652 if (isItemSelected && newStateToSet.selectedItem !== state.selectedItem) {
653 onChangeArg = newStateToSet.selectedItem;
654 }
655
656 newStateToSet.type = newStateToSet.type || unknown;
657 Object.keys(newStateToSet).forEach(function (key) {
658 // onStateChangeArg should only have the state that is
659 // actually changing
660 if (state[key] !== newStateToSet[key]) {
661 onStateChangeArg[key] = newStateToSet[key];
662 } // the type is useful for the onStateChangeArg
663 // but we don't actually want to set it in internal state.
664 // this is an undocumented feature for now... Not all internalSetState
665 // calls support it and I'm not certain we want them to yet.
666 // But it enables users controlling the isOpen state to know when
667 // the isOpen state changes due to mouseup events which is quite handy.
668
669
670 if (key === 'type') {
671 return;
672 }
673
674 newStateToSet[key]; // if it's coming from props, then we don't care to set it internally
675
676 if (!isControlledProp(_this.props, key)) {
677 nextState[key] = newStateToSet[key];
678 }
679 }); // if stateToSet is a function, then we weren't able to call onInputValueChange
680 // earlier, so we'll call it now that we know what the inputValue state will be.
681
682 if (isStateToSetFunction && newStateToSet.hasOwnProperty('inputValue')) {
683 _this.props.onInputValueChange(newStateToSet.inputValue, _extends({}, _this.getStateAndHelpers(), newStateToSet));
684 }
685
686 return nextState;
687 }, function () {
688 // call the provided callback if it's a function
689 cbToCb(cb)(); // only call the onStateChange and onChange callbacks if
690 // we have relevant information to pass them.
691
692 var hasMoreStateThanType = Object.keys(onStateChangeArg).length > 1;
693
694 if (hasMoreStateThanType) {
695 _this.props.onStateChange(onStateChangeArg, _this.getStateAndHelpers());
696 }
697
698 if (isItemSelected) {
699 _this.props.onSelect(stateToSet.selectedItem, _this.getStateAndHelpers());
700 }
701
702 if (onChangeArg !== undefined) {
703 _this.props.onChange(onChangeArg, _this.getStateAndHelpers());
704 } // this is currently undocumented and therefore subject to change
705 // We'll try to not break it, but just be warned.
706
707
708 _this.props.onUserAction(onStateChangeArg, _this.getStateAndHelpers());
709 });
710 };
711
712 _this.rootRef = function (node) {
713 return _this._rootNode = node;
714 };
715
716 _this.getRootProps = function (_temp, _temp2) {
717 var _extends2;
718
719 var _ref = _temp === void 0 ? {} : _temp,
720 _ref$refKey = _ref.refKey,
721 refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
722 ref = _ref.ref,
723 rest = _objectWithoutPropertiesLoose(_ref, ["refKey", "ref"]);
724
725 var _ref2 = _temp2 === void 0 ? {} : _temp2,
726 _ref2$suppressRefErro = _ref2.suppressRefError,
727 suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
728
729 // this is used in the render to know whether the user has called getRootProps.
730 // It uses that to know whether to apply the props automatically
731 _this.getRootProps.called = true;
732 _this.getRootProps.refKey = refKey;
733 _this.getRootProps.suppressRefError = suppressRefError;
734
735 var _this$getState = _this.getState(),
736 isOpen = _this$getState.isOpen;
737
738 return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, _this.rootRef), _extends2.role = 'combobox', _extends2['aria-expanded'] = isOpen, _extends2['aria-haspopup'] = 'listbox', _extends2['aria-owns'] = isOpen ? _this.menuId : null, _extends2['aria-labelledby'] = _this.labelId, _extends2), rest);
739 };
740
741 _this.keyDownHandlers = {
742 ArrowDown: function ArrowDown(event) {
743 var _this2 = this;
744
745 event.preventDefault();
746
747 if (this.getState().isOpen) {
748 var amount = event.shiftKey ? 5 : 1;
749 this.moveHighlightedIndex(amount, {
750 type: keyDownArrowDown
751 });
752 } else {
753 this.internalSetState({
754 isOpen: true,
755 type: keyDownArrowDown
756 }, function () {
757 var itemCount = _this2.getItemCount();
758
759 if (itemCount > 0) {
760 var _this2$getState = _this2.getState(),
761 highlightedIndex = _this2$getState.highlightedIndex;
762
763 var nextHighlightedIndex = getNextWrappingIndex(1, highlightedIndex, itemCount, function (index) {
764 return _this2.getItemNodeFromIndex(index);
765 });
766
767 _this2.setHighlightedIndex(nextHighlightedIndex, {
768 type: keyDownArrowDown
769 });
770 }
771 });
772 }
773 },
774 ArrowUp: function ArrowUp(event) {
775 var _this3 = this;
776
777 event.preventDefault();
778
779 if (this.getState().isOpen) {
780 var amount = event.shiftKey ? -5 : -1;
781 this.moveHighlightedIndex(amount, {
782 type: keyDownArrowUp
783 });
784 } else {
785 this.internalSetState({
786 isOpen: true,
787 type: keyDownArrowUp
788 }, function () {
789 var itemCount = _this3.getItemCount();
790
791 if (itemCount > 0) {
792 var _this3$getState = _this3.getState(),
793 highlightedIndex = _this3$getState.highlightedIndex;
794
795 var nextHighlightedIndex = getNextWrappingIndex(-1, highlightedIndex, itemCount, function (index) {
796 return _this3.getItemNodeFromIndex(index);
797 });
798
799 _this3.setHighlightedIndex(nextHighlightedIndex, {
800 type: keyDownArrowUp
801 });
802 }
803 });
804 }
805 },
806 Enter: function Enter(event) {
807 if (event.which === 229) {
808 return;
809 }
810
811 var _this$getState2 = this.getState(),
812 isOpen = _this$getState2.isOpen,
813 highlightedIndex = _this$getState2.highlightedIndex;
814
815 if (isOpen && highlightedIndex != null) {
816 event.preventDefault();
817 var item = this.items[highlightedIndex];
818 var itemNode = this.getItemNodeFromIndex(highlightedIndex);
819
820 if (item == null || itemNode && itemNode.hasAttribute('disabled')) {
821 return;
822 }
823
824 this.selectHighlightedItem({
825 type: keyDownEnter
826 });
827 }
828 },
829 Escape: function Escape(event) {
830 event.preventDefault();
831 this.reset(_extends({
832 type: keyDownEscape
833 }, !this.state.isOpen && {
834 selectedItem: null,
835 inputValue: ''
836 }));
837 }
838 };
839 _this.buttonKeyDownHandlers = _extends({}, _this.keyDownHandlers, {
840 ' ': function _(event) {
841 event.preventDefault();
842 this.toggleMenu({
843 type: keyDownSpaceButton
844 });
845 }
846 });
847 _this.inputKeyDownHandlers = _extends({}, _this.keyDownHandlers, {
848 Home: function Home(event) {
849 var _this4 = this;
850
851 event.preventDefault();
852 var itemCount = this.getItemCount();
853
854 var _this$getState3 = this.getState(),
855 isOpen = _this$getState3.isOpen;
856
857 if (itemCount <= 0 || !isOpen) {
858 return;
859 } // get next non-disabled starting downwards from 0 if that's disabled.
860
861
862 var newHighlightedIndex = getNextNonDisabledIndex(1, 0, itemCount, function (index) {
863 return _this4.getItemNodeFromIndex(index);
864 }, false);
865 this.setHighlightedIndex(newHighlightedIndex, {
866 type: keyDownHome
867 });
868 },
869 End: function End(event) {
870 var _this5 = this;
871
872 event.preventDefault();
873 var itemCount = this.getItemCount();
874
875 var _this$getState4 = this.getState(),
876 isOpen = _this$getState4.isOpen;
877
878 if (itemCount <= 0 || !isOpen) {
879 return;
880 } // get next non-disabled starting upwards from last index if that's disabled.
881
882
883 var newHighlightedIndex = getNextNonDisabledIndex(-1, itemCount - 1, itemCount, function (index) {
884 return _this5.getItemNodeFromIndex(index);
885 }, false);
886 this.setHighlightedIndex(newHighlightedIndex, {
887 type: keyDownEnd
888 });
889 }
890 });
891
892 _this.getToggleButtonProps = function (_temp3) {
893 var _ref3 = _temp3 === void 0 ? {} : _temp3,
894 onClick = _ref3.onClick,
895 onPress = _ref3.onPress,
896 onKeyDown = _ref3.onKeyDown,
897 onKeyUp = _ref3.onKeyUp,
898 onBlur = _ref3.onBlur,
899 rest = _objectWithoutPropertiesLoose(_ref3, ["onClick", "onPress", "onKeyDown", "onKeyUp", "onBlur"]);
900
901 var _this$getState5 = _this.getState(),
902 isOpen = _this$getState5.isOpen;
903
904 var enabledEventHandlers = {
905 onClick: callAllEventHandlers(onClick, _this.buttonHandleClick),
906 onKeyDown: callAllEventHandlers(onKeyDown, _this.buttonHandleKeyDown),
907 onKeyUp: callAllEventHandlers(onKeyUp, _this.buttonHandleKeyUp),
908 onBlur: callAllEventHandlers(onBlur, _this.buttonHandleBlur)
909 };
910 var eventHandlers = rest.disabled ? {} : enabledEventHandlers;
911 return _extends({
912 type: 'button',
913 role: 'button',
914 'aria-label': isOpen ? 'close menu' : 'open menu',
915 'aria-haspopup': true,
916 'data-toggle': true
917 }, eventHandlers, rest);
918 };
919
920 _this.buttonHandleKeyUp = function (event) {
921 // Prevent click event from emitting in Firefox
922 event.preventDefault();
923 };
924
925 _this.buttonHandleKeyDown = function (event) {
926 var key = normalizeArrowKey(event);
927
928 if (_this.buttonKeyDownHandlers[key]) {
929 _this.buttonKeyDownHandlers[key].call(_assertThisInitialized(_this), event);
930 }
931 };
932
933 _this.buttonHandleClick = function (event) {
934 event.preventDefault(); // handle odd case for Safari and Firefox which
935 // don't give the button the focus properly.
936
937 /* istanbul ignore if (can't reasonably test this) */
938
939 if ( _this.props.environment.document.activeElement === _this.props.environment.document.body) {
940 event.target.focus();
941 } // to simplify testing components that use downshift, we'll not wrap this in a setTimeout
942 // if the NODE_ENV is test. With the proper build system, this should be dead code eliminated
943 // when building for production and should therefore have no impact on production code.
944
945
946 if (process.env.NODE_ENV === 'test') {
947 _this.toggleMenu({
948 type: clickButton
949 });
950 } else {
951 // Ensure that toggle of menu occurs after the potential blur event in iOS
952 _this.internalSetTimeout(function () {
953 return _this.toggleMenu({
954 type: clickButton
955 });
956 });
957 }
958 };
959
960 _this.buttonHandleBlur = function (event) {
961 var blurTarget = event.target; // Save blur target for comparison with activeElement later
962 // Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not body element
963
964 _this.internalSetTimeout(function () {
965 if (!_this.isMouseDown && (_this.props.environment.document.activeElement == null || _this.props.environment.document.activeElement.id !== _this.inputId) && _this.props.environment.document.activeElement !== blurTarget // Do nothing if we refocus the same element again (to solve issue in Safari on iOS)
966 ) {
967 _this.reset({
968 type: blurButton
969 });
970 }
971 });
972 };
973
974 _this.getLabelProps = function (props) {
975 return _extends({
976 htmlFor: _this.inputId,
977 id: _this.labelId
978 }, props);
979 };
980
981 _this.getInputProps = function (_temp4) {
982 var _ref4 = _temp4 === void 0 ? {} : _temp4,
983 onKeyDown = _ref4.onKeyDown,
984 onBlur = _ref4.onBlur,
985 onChange = _ref4.onChange,
986 onInput = _ref4.onInput,
987 onChangeText = _ref4.onChangeText,
988 rest = _objectWithoutPropertiesLoose(_ref4, ["onKeyDown", "onBlur", "onChange", "onInput", "onChangeText"]);
989
990 var onChangeKey;
991 var eventHandlers = {};
992 /* istanbul ignore next (preact) */
993
994 onChangeKey = 'onChange';
995
996 var _this$getState6 = _this.getState(),
997 inputValue = _this$getState6.inputValue,
998 isOpen = _this$getState6.isOpen,
999 highlightedIndex = _this$getState6.highlightedIndex;
1000
1001 if (!rest.disabled) {
1002 var _eventHandlers;
1003
1004 eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, _this.inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, _this.inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, _this.inputHandleBlur), _eventHandlers);
1005 }
1006 /* istanbul ignore if (react-native) */
1007
1008
1009 return _extends({
1010 'aria-autocomplete': 'list',
1011 'aria-activedescendant': isOpen && typeof highlightedIndex === 'number' && highlightedIndex >= 0 ? _this.getItemId(highlightedIndex) : null,
1012 'aria-controls': isOpen ? _this.menuId : null,
1013 'aria-labelledby': _this.labelId,
1014 // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
1015 // revert back since autocomplete="nope" is ignored on latest Chrome and Opera
1016 autoComplete: 'off',
1017 value: inputValue,
1018 id: _this.inputId
1019 }, eventHandlers, rest);
1020 };
1021
1022 _this.inputHandleKeyDown = function (event) {
1023 var key = normalizeArrowKey(event);
1024
1025 if (key && _this.inputKeyDownHandlers[key]) {
1026 _this.inputKeyDownHandlers[key].call(_assertThisInitialized(_this), event);
1027 }
1028 };
1029
1030 _this.inputHandleChange = function (event) {
1031 _this.internalSetState({
1032 type: changeInput,
1033 isOpen: true,
1034 inputValue: event.target.value,
1035 highlightedIndex: _this.props.defaultHighlightedIndex
1036 });
1037 };
1038
1039 _this.inputHandleBlur = function () {
1040 // Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not the body element
1041 _this.internalSetTimeout(function () {
1042 var downshiftButtonIsActive = _this.props.environment.document && !!_this.props.environment.document.activeElement && !!_this.props.environment.document.activeElement.dataset && _this.props.environment.document.activeElement.dataset.toggle && _this._rootNode && _this._rootNode.contains(_this.props.environment.document.activeElement);
1043
1044 if (!_this.isMouseDown && !downshiftButtonIsActive) {
1045 _this.reset({
1046 type: blurInput
1047 });
1048 }
1049 });
1050 };
1051
1052 _this.menuRef = function (node) {
1053 _this._menuNode = node;
1054 };
1055
1056 _this.getMenuProps = function (_temp5, _temp6) {
1057 var _extends3;
1058
1059 var _ref5 = _temp5 === void 0 ? {} : _temp5,
1060 _ref5$refKey = _ref5.refKey,
1061 refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
1062 ref = _ref5.ref,
1063 props = _objectWithoutPropertiesLoose(_ref5, ["refKey", "ref"]);
1064
1065 var _ref6 = _temp6 === void 0 ? {} : _temp6,
1066 _ref6$suppressRefErro = _ref6.suppressRefError,
1067 suppressRefError = _ref6$suppressRefErro === void 0 ? false : _ref6$suppressRefErro;
1068
1069 _this.getMenuProps.called = true;
1070 _this.getMenuProps.refKey = refKey;
1071 _this.getMenuProps.suppressRefError = suppressRefError;
1072 return _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, _this.menuRef), _extends3.role = 'listbox', _extends3['aria-labelledby'] = props && props['aria-label'] ? null : _this.labelId, _extends3.id = _this.menuId, _extends3), props);
1073 };
1074
1075 _this.getItemProps = function (_temp7) {
1076 var _enabledEventHandlers;
1077
1078 var _ref7 = _temp7 === void 0 ? {} : _temp7,
1079 onMouseMove = _ref7.onMouseMove,
1080 onMouseDown = _ref7.onMouseDown,
1081 onClick = _ref7.onClick,
1082 onPress = _ref7.onPress,
1083 index = _ref7.index,
1084 _ref7$item = _ref7.item,
1085 item = _ref7$item === void 0 ? process.env.NODE_ENV === 'production' ?
1086 /* istanbul ignore next */
1087 undefined : requiredProp('getItemProps', 'item') : _ref7$item,
1088 rest = _objectWithoutPropertiesLoose(_ref7, ["onMouseMove", "onMouseDown", "onClick", "onPress", "index", "item"]);
1089
1090 if (index === undefined) {
1091 _this.items.push(item);
1092
1093 index = _this.items.indexOf(item);
1094 } else {
1095 _this.items[index] = item;
1096 }
1097
1098 var onSelectKey = 'onClick';
1099 var customClickHandler = onClick;
1100 var enabledEventHandlers = (_enabledEventHandlers = {
1101 // onMouseMove is used over onMouseEnter here. onMouseMove
1102 // is only triggered on actual mouse movement while onMouseEnter
1103 // can fire on DOM changes, interrupting keyboard navigation
1104 onMouseMove: callAllEventHandlers(onMouseMove, function () {
1105 if (index === _this.getState().highlightedIndex) {
1106 return;
1107 }
1108
1109 _this.setHighlightedIndex(index, {
1110 type: itemMouseEnter
1111 }); // We never want to manually scroll when changing state based
1112 // on `onMouseMove` because we will be moving the element out
1113 // from under the user which is currently scrolling/moving the
1114 // cursor
1115
1116
1117 _this.avoidScrolling = true;
1118
1119 _this.internalSetTimeout(function () {
1120 return _this.avoidScrolling = false;
1121 }, 250);
1122 }),
1123 onMouseDown: callAllEventHandlers(onMouseDown, function (event) {
1124 // This prevents the activeElement from being changed
1125 // to the item so it can remain with the current activeElement
1126 // which is a more common use case.
1127 event.preventDefault();
1128 })
1129 }, _enabledEventHandlers[onSelectKey] = callAllEventHandlers(customClickHandler, function () {
1130 _this.selectItemAtIndex(index, {
1131 type: clickItem
1132 });
1133 }), _enabledEventHandlers); // Passing down the onMouseDown handler to prevent redirect
1134 // of the activeElement if clicking on disabled items
1135
1136 var eventHandlers = rest.disabled ? {
1137 onMouseDown: enabledEventHandlers.onMouseDown
1138 } : enabledEventHandlers;
1139 return _extends({
1140 id: _this.getItemId(index),
1141 role: 'option',
1142 'aria-selected': _this.getState().highlightedIndex === index
1143 }, eventHandlers, rest);
1144 };
1145
1146 _this.clearItems = function () {
1147 _this.items = [];
1148 };
1149
1150 _this.reset = function (otherStateToSet, cb) {
1151 if (otherStateToSet === void 0) {
1152 otherStateToSet = {};
1153 }
1154
1155 otherStateToSet = pickState(otherStateToSet);
1156
1157 _this.internalSetState(function (_ref8) {
1158 var selectedItem = _ref8.selectedItem;
1159 return _extends({
1160 isOpen: _this.props.defaultIsOpen,
1161 highlightedIndex: _this.props.defaultHighlightedIndex,
1162 inputValue: _this.props.itemToString(selectedItem)
1163 }, otherStateToSet);
1164 }, cb);
1165 };
1166
1167 _this.toggleMenu = function (otherStateToSet, cb) {
1168 if (otherStateToSet === void 0) {
1169 otherStateToSet = {};
1170 }
1171
1172 otherStateToSet = pickState(otherStateToSet);
1173
1174 _this.internalSetState(function (_ref9) {
1175 var isOpen = _ref9.isOpen;
1176 return _extends({
1177 isOpen: !isOpen
1178 }, isOpen && {
1179 highlightedIndex: _this.props.defaultHighlightedIndex
1180 }, otherStateToSet);
1181 }, function () {
1182 var _this$getState7 = _this.getState(),
1183 isOpen = _this$getState7.isOpen,
1184 highlightedIndex = _this$getState7.highlightedIndex;
1185
1186 if (isOpen) {
1187 if (_this.getItemCount() > 0 && typeof highlightedIndex === 'number') {
1188 _this.setHighlightedIndex(highlightedIndex, otherStateToSet);
1189 }
1190 }
1191
1192 cbToCb(cb)();
1193 });
1194 };
1195
1196 _this.openMenu = function (cb) {
1197 _this.internalSetState({
1198 isOpen: true
1199 }, cb);
1200 };
1201
1202 _this.closeMenu = function (cb) {
1203 _this.internalSetState({
1204 isOpen: false
1205 }, cb);
1206 };
1207
1208 _this.updateStatus = debounce(function () {
1209 var state = _this.getState();
1210
1211 var item = _this.items[state.highlightedIndex];
1212
1213 var resultCount = _this.getItemCount();
1214
1215 var status = _this.props.getA11yStatusMessage(_extends({
1216 itemToString: _this.props.itemToString,
1217 previousResultCount: _this.previousResultCount,
1218 resultCount: resultCount,
1219 highlightedItem: item
1220 }, state));
1221
1222 _this.previousResultCount = resultCount;
1223 setStatus(status, _this.props.environment.document);
1224 }, 200);
1225
1226 // fancy destructuring + defaults + aliases
1227 // this basically says each value of state should either be set to
1228 // the initial value or the default value if the initial value is not provided
1229 var _this$props = _this.props,
1230 defaultHighlightedIndex = _this$props.defaultHighlightedIndex,
1231 _this$props$initialHi = _this$props.initialHighlightedIndex,
1232 _highlightedIndex = _this$props$initialHi === void 0 ? defaultHighlightedIndex : _this$props$initialHi,
1233 defaultIsOpen = _this$props.defaultIsOpen,
1234 _this$props$initialIs = _this$props.initialIsOpen,
1235 _isOpen = _this$props$initialIs === void 0 ? defaultIsOpen : _this$props$initialIs,
1236 _this$props$initialIn = _this$props.initialInputValue,
1237 _inputValue = _this$props$initialIn === void 0 ? '' : _this$props$initialIn,
1238 _this$props$initialSe = _this$props.initialSelectedItem,
1239 _selectedItem = _this$props$initialSe === void 0 ? null : _this$props$initialSe;
1240
1241 var _state = _this.getState({
1242 highlightedIndex: _highlightedIndex,
1243 isOpen: _isOpen,
1244 inputValue: _inputValue,
1245 selectedItem: _selectedItem
1246 });
1247
1248 if (_state.selectedItem != null && _this.props.initialInputValue === undefined) {
1249 _state.inputValue = _this.props.itemToString(_state.selectedItem);
1250 }
1251
1252 _this.state = _state;
1253 return _this;
1254 }
1255
1256 var _proto = Downshift.prototype;
1257
1258 /**
1259 * Clear all running timeouts
1260 */
1261 _proto.internalClearTimeouts = function internalClearTimeouts() {
1262 this.timeoutIds.forEach(function (id) {
1263 clearTimeout(id);
1264 });
1265 this.timeoutIds = [];
1266 }
1267 /**
1268 * Gets the state based on internal state or props
1269 * If a state value is passed via props, then that
1270 * is the value given, otherwise it's retrieved from
1271 * stateToMerge
1272 *
1273 * @param {Object} stateToMerge defaults to this.state
1274 * @return {Object} the state
1275 */
1276 ;
1277
1278 _proto.getState = function getState$1(stateToMerge) {
1279 if (stateToMerge === void 0) {
1280 stateToMerge = this.state;
1281 }
1282
1283 return getState(stateToMerge, this.props);
1284 };
1285
1286 _proto.getItemCount = function getItemCount() {
1287 // things read better this way. They're in priority order:
1288 // 1. `this.itemCount`
1289 // 2. `this.props.itemCount`
1290 // 3. `this.items.length`
1291 var itemCount = this.items.length;
1292
1293 if (this.itemCount != null) {
1294 itemCount = this.itemCount;
1295 } else if (this.props.itemCount !== undefined) {
1296 itemCount = this.props.itemCount;
1297 }
1298
1299 return itemCount;
1300 };
1301
1302 _proto.getItemNodeFromIndex = function getItemNodeFromIndex(index) {
1303 return this.props.environment.document.getElementById(this.getItemId(index));
1304 };
1305
1306 _proto.scrollHighlightedItemIntoView = function scrollHighlightedItemIntoView() {
1307 /* istanbul ignore else (react-native) */
1308 {
1309 var node = this.getItemNodeFromIndex(this.getState().highlightedIndex);
1310 this.props.scrollIntoView(node, this._menuNode);
1311 }
1312 };
1313
1314 _proto.moveHighlightedIndex = function moveHighlightedIndex(amount, otherStateToSet) {
1315 var _this6 = this;
1316
1317 var itemCount = this.getItemCount();
1318
1319 var _this$getState8 = this.getState(),
1320 highlightedIndex = _this$getState8.highlightedIndex;
1321
1322 if (itemCount > 0) {
1323 var nextHighlightedIndex = getNextWrappingIndex(amount, highlightedIndex, itemCount, function (index) {
1324 return _this6.getItemNodeFromIndex(index);
1325 });
1326 this.setHighlightedIndex(nextHighlightedIndex, otherStateToSet);
1327 }
1328 };
1329
1330 _proto.getStateAndHelpers = function getStateAndHelpers() {
1331 var _this$getState9 = this.getState(),
1332 highlightedIndex = _this$getState9.highlightedIndex,
1333 inputValue = _this$getState9.inputValue,
1334 selectedItem = _this$getState9.selectedItem,
1335 isOpen = _this$getState9.isOpen;
1336
1337 var itemToString = this.props.itemToString;
1338 var id = this.id;
1339 var getRootProps = this.getRootProps,
1340 getToggleButtonProps = this.getToggleButtonProps,
1341 getLabelProps = this.getLabelProps,
1342 getMenuProps = this.getMenuProps,
1343 getInputProps = this.getInputProps,
1344 getItemProps = this.getItemProps,
1345 openMenu = this.openMenu,
1346 closeMenu = this.closeMenu,
1347 toggleMenu = this.toggleMenu,
1348 selectItem = this.selectItem,
1349 selectItemAtIndex = this.selectItemAtIndex,
1350 selectHighlightedItem = this.selectHighlightedItem,
1351 setHighlightedIndex = this.setHighlightedIndex,
1352 clearSelection = this.clearSelection,
1353 clearItems = this.clearItems,
1354 reset = this.reset,
1355 setItemCount = this.setItemCount,
1356 unsetItemCount = this.unsetItemCount,
1357 setState = this.internalSetState;
1358 return {
1359 // prop getters
1360 getRootProps: getRootProps,
1361 getToggleButtonProps: getToggleButtonProps,
1362 getLabelProps: getLabelProps,
1363 getMenuProps: getMenuProps,
1364 getInputProps: getInputProps,
1365 getItemProps: getItemProps,
1366 // actions
1367 reset: reset,
1368 openMenu: openMenu,
1369 closeMenu: closeMenu,
1370 toggleMenu: toggleMenu,
1371 selectItem: selectItem,
1372 selectItemAtIndex: selectItemAtIndex,
1373 selectHighlightedItem: selectHighlightedItem,
1374 setHighlightedIndex: setHighlightedIndex,
1375 clearSelection: clearSelection,
1376 clearItems: clearItems,
1377 setItemCount: setItemCount,
1378 unsetItemCount: unsetItemCount,
1379 setState: setState,
1380 // props
1381 itemToString: itemToString,
1382 // derived
1383 id: id,
1384 // state
1385 highlightedIndex: highlightedIndex,
1386 inputValue: inputValue,
1387 isOpen: isOpen,
1388 selectedItem: selectedItem
1389 };
1390 } //////////////////////////// ROOT
1391 ;
1392
1393 _proto.componentDidMount = function componentDidMount() {
1394 var _this7 = this;
1395
1396 /* istanbul ignore if (react-native) */
1397 if (process.env.NODE_ENV !== 'production' && !false && this.getMenuProps.called && !this.getMenuProps.suppressRefError) {
1398 validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps);
1399 }
1400 /* istanbul ignore if (react-native) */
1401
1402
1403 {
1404 // this.isMouseDown helps us track whether the mouse is currently held down.
1405 // This is useful when the user clicks on an item in the list, but holds the mouse
1406 // down long enough for the list to disappear (because the blur event fires on the input)
1407 // this.isMouseDown is used in the blur handler on the input to determine whether the blur event should
1408 // trigger hiding the menu.
1409 var onMouseDown = function () {
1410 _this7.isMouseDown = true;
1411 };
1412
1413 var onMouseUp = function (event) {
1414 _this7.isMouseDown = false; // if the target element or the activeElement is within a downshift node
1415 // then we don't want to reset downshift
1416
1417 var contextWithinDownshift = targetWithinDownshift(event.target, [_this7._rootNode, _this7._menuNode], _this7.props.environment.document);
1418
1419 if (!contextWithinDownshift && _this7.getState().isOpen) {
1420 _this7.reset({
1421 type: mouseUp
1422 }, function () {
1423 return _this7.props.onOuterClick(_this7.getStateAndHelpers());
1424 });
1425 }
1426 }; // Touching an element in iOS gives focus and hover states, but touching out of
1427 // the element will remove hover, and persist the focus state, resulting in the
1428 // blur event not being triggered.
1429 // this.isTouchMove helps us track whether the user is tapping or swiping on a touch screen.
1430 // If the user taps outside of Downshift, the component should be reset,
1431 // but not if the user is swiping
1432
1433
1434 var onTouchStart = function () {
1435 _this7.isTouchMove = false;
1436 };
1437
1438 var onTouchMove = function () {
1439 _this7.isTouchMove = true;
1440 };
1441
1442 var onTouchEnd = function (event) {
1443 var contextWithinDownshift = targetWithinDownshift(event.target, [_this7._rootNode, _this7._menuNode], _this7.props.environment.document, false);
1444
1445 if (!_this7.isTouchMove && !contextWithinDownshift && _this7.getState().isOpen) {
1446 _this7.reset({
1447 type: touchEnd
1448 }, function () {
1449 return _this7.props.onOuterClick(_this7.getStateAndHelpers());
1450 });
1451 }
1452 };
1453
1454 var environment = this.props.environment;
1455 environment.addEventListener('mousedown', onMouseDown);
1456 environment.addEventListener('mouseup', onMouseUp);
1457 environment.addEventListener('touchstart', onTouchStart);
1458 environment.addEventListener('touchmove', onTouchMove);
1459 environment.addEventListener('touchend', onTouchEnd);
1460
1461 this.cleanup = function () {
1462 _this7.internalClearTimeouts();
1463
1464 _this7.updateStatus.cancel();
1465
1466 environment.removeEventListener('mousedown', onMouseDown);
1467 environment.removeEventListener('mouseup', onMouseUp);
1468 environment.removeEventListener('touchstart', onTouchStart);
1469 environment.removeEventListener('touchmove', onTouchMove);
1470 environment.removeEventListener('touchend', onTouchEnd);
1471 };
1472 }
1473 };
1474
1475 _proto.shouldScroll = function shouldScroll(prevState, prevProps) {
1476 var _ref10 = this.props.highlightedIndex === undefined ? this.getState() : this.props,
1477 currentHighlightedIndex = _ref10.highlightedIndex;
1478
1479 var _ref11 = prevProps.highlightedIndex === undefined ? prevState : prevProps,
1480 prevHighlightedIndex = _ref11.highlightedIndex;
1481
1482 var scrollWhenOpen = currentHighlightedIndex && this.getState().isOpen && !prevState.isOpen;
1483 return scrollWhenOpen || currentHighlightedIndex !== prevHighlightedIndex;
1484 };
1485
1486 _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
1487 if (process.env.NODE_ENV !== 'production') {
1488 validateControlledUnchanged(this.state, prevProps, this.props);
1489 /* istanbul ignore if (react-native) */
1490
1491 if ( this.getMenuProps.called && !this.getMenuProps.suppressRefError) {
1492 validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps);
1493 }
1494 }
1495
1496 if (isControlledProp(this.props, 'selectedItem') && this.props.selectedItemChanged(prevProps.selectedItem, this.props.selectedItem)) {
1497 this.internalSetState({
1498 type: controlledPropUpdatedSelectedItem,
1499 inputValue: this.props.itemToString(this.props.selectedItem)
1500 });
1501 }
1502
1503 if (!this.avoidScrolling && this.shouldScroll(prevState, prevProps)) {
1504 this.scrollHighlightedItemIntoView();
1505 }
1506 /* istanbul ignore else (react-native) */
1507
1508
1509 this.updateStatus();
1510 };
1511
1512 _proto.componentWillUnmount = function componentWillUnmount() {
1513 this.cleanup(); // avoids memory leak
1514 };
1515
1516 _proto.render = function render() {
1517 var children = unwrapArray(this.props.children, noop); // because the items are rerendered every time we call the children
1518 // we clear this out each render and it will be populated again as
1519 // getItemProps is called.
1520
1521 this.clearItems(); // we reset this so we know whether the user calls getRootProps during
1522 // this render. If they do then we don't need to do anything,
1523 // if they don't then we need to clone the element they return and
1524 // apply the props for them.
1525
1526 this.getRootProps.called = false;
1527 this.getRootProps.refKey = undefined;
1528 this.getRootProps.suppressRefError = undefined; // we do something similar for getMenuProps
1529
1530 this.getMenuProps.called = false;
1531 this.getMenuProps.refKey = undefined;
1532 this.getMenuProps.suppressRefError = undefined; // we do something similar for getLabelProps
1533
1534 this.getLabelProps.called = false; // and something similar for getInputProps
1535
1536 this.getInputProps.called = false;
1537 var element = unwrapArray(children(this.getStateAndHelpers()));
1538
1539 if (!element) {
1540 return null;
1541 }
1542
1543 if (this.getRootProps.called || this.props.suppressRefError) {
1544 if (process.env.NODE_ENV !== 'production' && !this.getRootProps.suppressRefError && !this.props.suppressRefError) {
1545 validateGetRootPropsCalledCorrectly(element, this.getRootProps);
1546 }
1547
1548 return element;
1549 } else if (isDOMElement(element)) {
1550 // they didn't apply the root props, but we can clone
1551 // this and apply the props ourselves
1552 return /*#__PURE__*/cloneElement(element, this.getRootProps(getElementProps(element)));
1553 }
1554 /* istanbul ignore else */
1555
1556
1557 if (process.env.NODE_ENV !== 'production') {
1558 // they didn't apply the root props, but they need to
1559 // otherwise we can't query around the autocomplete
1560 throw new Error('downshift: If you return a non-DOM element, you must apply the getRootProps function');
1561 }
1562 /* istanbul ignore next */
1563
1564
1565 return undefined;
1566 };
1567
1568 return Downshift;
1569 }(Component);
1570
1571 Downshift.defaultProps = {
1572 defaultHighlightedIndex: null,
1573 defaultIsOpen: false,
1574 getA11yStatusMessage: getA11yStatusMessage,
1575 itemToString: function itemToString(i) {
1576 if (i == null) {
1577 return '';
1578 }
1579
1580 if (process.env.NODE_ENV !== 'production' && isPlainObject(i) && !i.hasOwnProperty('toString')) {
1581 // eslint-disable-next-line no-console
1582 console.warn('downshift: An object was passed to the default implementation of `itemToString`. You should probably provide your own `itemToString` implementation. Please refer to the `itemToString` API documentation.', 'The object that was passed:', i);
1583 }
1584
1585 return String(i);
1586 },
1587 onStateChange: noop,
1588 onInputValueChange: noop,
1589 onUserAction: noop,
1590 onChange: noop,
1591 onSelect: noop,
1592 onOuterClick: noop,
1593 selectedItemChanged: function selectedItemChanged(prevItem, item) {
1594 return prevItem !== item;
1595 },
1596 environment: typeof window === 'undefined'
1597 /* istanbul ignore next (ssr) */
1598 ? {} : window,
1599 stateReducer: function stateReducer(state, stateToSet) {
1600 return stateToSet;
1601 },
1602 suppressRefError: false,
1603 scrollIntoView: scrollIntoView
1604 };
1605 Downshift.stateChangeTypes = stateChangeTypes;
1606 return Downshift;
1607}();
1608
1609process.env.NODE_ENV !== "production" ? Downshift.propTypes = {
1610 children: PropTypes.func,
1611 defaultHighlightedIndex: PropTypes.number,
1612 defaultIsOpen: PropTypes.bool,
1613 initialHighlightedIndex: PropTypes.number,
1614 initialSelectedItem: PropTypes.any,
1615 initialInputValue: PropTypes.string,
1616 initialIsOpen: PropTypes.bool,
1617 getA11yStatusMessage: PropTypes.func,
1618 itemToString: PropTypes.func,
1619 onChange: PropTypes.func,
1620 onSelect: PropTypes.func,
1621 onStateChange: PropTypes.func,
1622 onInputValueChange: PropTypes.func,
1623 onUserAction: PropTypes.func,
1624 onOuterClick: PropTypes.func,
1625 selectedItemChanged: PropTypes.func,
1626 stateReducer: PropTypes.func,
1627 itemCount: PropTypes.number,
1628 id: PropTypes.string,
1629 environment: PropTypes.shape({
1630 addEventListener: PropTypes.func,
1631 removeEventListener: PropTypes.func,
1632 document: PropTypes.shape({
1633 getElementById: PropTypes.func,
1634 activeElement: PropTypes.any,
1635 body: PropTypes.any
1636 })
1637 }),
1638 suppressRefError: PropTypes.bool,
1639 scrollIntoView: PropTypes.func,
1640 // things we keep in state for uncontrolled components
1641 // but can accept as props for controlled components
1642
1643 /* eslint-disable react/no-unused-prop-types */
1644 selectedItem: PropTypes.any,
1645 isOpen: PropTypes.bool,
1646 inputValue: PropTypes.string,
1647 highlightedIndex: PropTypes.number,
1648 labelId: PropTypes.string,
1649 inputId: PropTypes.string,
1650 menuId: PropTypes.string,
1651 getItemId: PropTypes.func
1652 /* eslint-enable react/no-unused-prop-types */
1653
1654} : void 0;
1655
1656function validateGetMenuPropsCalledCorrectly(node, _ref12) {
1657 var refKey = _ref12.refKey;
1658
1659 if (!node) {
1660 // eslint-disable-next-line no-console
1661 console.error("downshift: The ref prop \"" + refKey + "\" from getMenuProps was not applied correctly on your menu element.");
1662 }
1663}
1664
1665function validateGetRootPropsCalledCorrectly(element, _ref13) {
1666 var refKey = _ref13.refKey;
1667 var refKeySpecified = refKey !== 'ref';
1668 var isComposite = !isDOMElement(element);
1669
1670 if (isComposite && !refKeySpecified && !isForwardRef(element)) {
1671 // eslint-disable-next-line no-console
1672 console.error('downshift: You returned a non-DOM element. You must specify a refKey in getRootProps');
1673 } else if (!isComposite && refKeySpecified) {
1674 // eslint-disable-next-line no-console
1675 console.error("downshift: You returned a DOM element. You should not specify a refKey in getRootProps. You specified \"" + refKey + "\"");
1676 }
1677
1678 if (!isForwardRef(element) && !getElementProps(element)[refKey]) {
1679 // eslint-disable-next-line no-console
1680 console.error("downshift: You must apply the ref prop \"" + refKey + "\" from getRootProps onto your root element.");
1681 }
1682}
1683
1684var dropdownDefaultStateValues = {
1685 highlightedIndex: -1,
1686 isOpen: false,
1687 selectedItem: null,
1688 inputValue: ''
1689};
1690
1691function callOnChangeProps(action, state, newState) {
1692 var props = action.props,
1693 type = action.type;
1694 var changes = {};
1695 Object.keys(state).forEach(function (key) {
1696 invokeOnChangeHandler(key, action, state, newState);
1697
1698 if (newState[key] !== state[key]) {
1699 changes[key] = newState[key];
1700 }
1701 });
1702
1703 if (props.onStateChange && Object.keys(changes).length) {
1704 props.onStateChange(_extends({
1705 type: type
1706 }, changes));
1707 }
1708}
1709
1710function invokeOnChangeHandler(key, action, state, newState) {
1711 var props = action.props,
1712 type = action.type;
1713 var handler = "on" + capitalizeString(key) + "Change";
1714
1715 if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
1716 props[handler](_extends({
1717 type: type
1718 }, newState));
1719 }
1720}
1721/**
1722 * Default state reducer that returns the changes.
1723 *
1724 * @param {Object} s state.
1725 * @param {Object} a action with changes.
1726 * @returns {Object} changes.
1727 */
1728
1729
1730function stateReducer(s, a) {
1731 return a.changes;
1732}
1733/**
1734 * Returns a message to be added to aria-live region when item is selected.
1735 *
1736 * @param {Object} selectionParameters Parameters required to build the message.
1737 * @returns {string} The a11y message.
1738 */
1739
1740
1741function getA11ySelectionMessage(selectionParameters) {
1742 var selectedItem = selectionParameters.selectedItem,
1743 itemToStringLocal = selectionParameters.itemToString;
1744 return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
1745}
1746/**
1747 * Debounced call for updating the a11y message.
1748 */
1749
1750
1751var updateA11yStatus = debounce(function (getA11yMessage, document) {
1752 setStatus(getA11yMessage(), document);
1753}, 200); // istanbul ignore next
1754
1755var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? useLayoutEffect : useEffect;
1756
1757function useElementIds(_ref) {
1758 var _ref$id = _ref.id,
1759 id = _ref$id === void 0 ? "downshift-" + generateId() : _ref$id,
1760 labelId = _ref.labelId,
1761 menuId = _ref.menuId,
1762 getItemId = _ref.getItemId,
1763 toggleButtonId = _ref.toggleButtonId,
1764 inputId = _ref.inputId;
1765 var elementIdsRef = useRef({
1766 labelId: labelId || id + "-label",
1767 menuId: menuId || id + "-menu",
1768 getItemId: getItemId || function (index) {
1769 return id + "-item-" + index;
1770 },
1771 toggleButtonId: toggleButtonId || id + "-toggle-button",
1772 inputId: inputId || id + "-input"
1773 });
1774 return elementIdsRef.current;
1775}
1776
1777function getItemIndex(index, item, items) {
1778 if (index !== undefined) {
1779 return index;
1780 }
1781
1782 if (items.length === 0) {
1783 return -1;
1784 }
1785
1786 return items.indexOf(item);
1787}
1788
1789function itemToString(item) {
1790 return item ? String(item) : '';
1791}
1792
1793function isAcceptedCharacterKey(key) {
1794 return /^\S{1}$/.test(key);
1795}
1796
1797function capitalizeString(string) {
1798 return "" + string.slice(0, 1).toUpperCase() + string.slice(1);
1799}
1800
1801function useLatestRef(val) {
1802 var ref = useRef(val); // technically this is not "concurrent mode safe" because we're manipulating
1803 // the value during render (so it's not idempotent). However, the places this
1804 // hook is used is to support memoizing callbacks which will be called
1805 // *during* render, so we need the latest values *during* render.
1806 // If not for this, then we'd probably want to use useLayoutEffect instead.
1807
1808 ref.current = val;
1809 return ref;
1810}
1811/**
1812 * Computes the controlled state using a the previous state, props,
1813 * two reducers, one from downshift and an optional one from the user.
1814 * Also calls the onChange handlers for state values that have changed.
1815 *
1816 * @param {Function} reducer Reducer function from downshift.
1817 * @param {Object} initialState Initial state of the hook.
1818 * @param {Object} props The hook props.
1819 * @returns {Array} An array with the state and an action dispatcher.
1820 */
1821
1822
1823function useEnhancedReducer(reducer, initialState, props) {
1824 var prevStateRef = useRef();
1825 var actionRef = useRef();
1826 var enhancedReducer = useCallback(function (state, action) {
1827 actionRef.current = action;
1828 state = getState(state, action.props);
1829 var changes = reducer(state, action);
1830 var newState = action.props.stateReducer(state, _extends({}, action, {
1831 changes: changes
1832 }));
1833 return newState;
1834 }, [reducer]);
1835
1836 var _useReducer = useReducer(enhancedReducer, initialState),
1837 state = _useReducer[0],
1838 dispatch = _useReducer[1];
1839
1840 var propsRef = useLatestRef(props);
1841 var dispatchWithProps = useCallback(function (action) {
1842 return dispatch(_extends({
1843 props: propsRef.current
1844 }, action));
1845 }, [propsRef]);
1846 var action = actionRef.current;
1847 useEffect(function () {
1848 if (action && prevStateRef.current && prevStateRef.current !== state) {
1849 callOnChangeProps(action, getState(prevStateRef.current, action.props), state);
1850 }
1851
1852 prevStateRef.current = state;
1853 }, [state, props, action]);
1854 return [state, dispatchWithProps];
1855}
1856/**
1857 * Wraps the useEnhancedReducer and applies the controlled prop values before
1858 * returning the new state.
1859 *
1860 * @param {Function} reducer Reducer function from downshift.
1861 * @param {Object} initialState Initial state of the hook.
1862 * @param {Object} props The hook props.
1863 * @returns {Array} An array with the state and an action dispatcher.
1864 */
1865
1866
1867function useControlledReducer(reducer, initialState, props) {
1868 var _useEnhancedReducer = useEnhancedReducer(reducer, initialState, props),
1869 state = _useEnhancedReducer[0],
1870 dispatch = _useEnhancedReducer[1];
1871
1872 return [getState(state, props), dispatch];
1873}
1874
1875var defaultProps = {
1876 itemToString: itemToString,
1877 stateReducer: stateReducer,
1878 getA11ySelectionMessage: getA11ySelectionMessage,
1879 scrollIntoView: scrollIntoView,
1880 circularNavigation: false,
1881 environment: typeof window === 'undefined'
1882 /* istanbul ignore next (ssr) */
1883 ? {} : window
1884};
1885
1886function getDefaultValue(props, propKey, defaultStateValues) {
1887 if (defaultStateValues === void 0) {
1888 defaultStateValues = dropdownDefaultStateValues;
1889 }
1890
1891 var defaultPropKey = "default" + capitalizeString(propKey);
1892
1893 if (defaultPropKey in props) {
1894 return props[defaultPropKey];
1895 }
1896
1897 return defaultStateValues[propKey];
1898}
1899
1900function getInitialValue(props, propKey, defaultStateValues) {
1901 if (defaultStateValues === void 0) {
1902 defaultStateValues = dropdownDefaultStateValues;
1903 }
1904
1905 if (propKey in props) {
1906 return props[propKey];
1907 }
1908
1909 var initialPropKey = "initial" + capitalizeString(propKey);
1910
1911 if (initialPropKey in props) {
1912 return props[initialPropKey];
1913 }
1914
1915 return getDefaultValue(props, propKey, defaultStateValues);
1916}
1917
1918function getInitialState(props) {
1919 var selectedItem = getInitialValue(props, 'selectedItem');
1920 var isOpen = getInitialValue(props, 'isOpen');
1921 var highlightedIndex = getInitialValue(props, 'highlightedIndex');
1922 var inputValue = getInitialValue(props, 'inputValue');
1923 return {
1924 highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.indexOf(selectedItem) : highlightedIndex,
1925 isOpen: isOpen,
1926 selectedItem: selectedItem,
1927 inputValue: inputValue
1928 };
1929}
1930
1931function getHighlightedIndexOnOpen(props, state, offset, getItemNodeFromIndex) {
1932 var items = props.items,
1933 initialHighlightedIndex = props.initialHighlightedIndex,
1934 defaultHighlightedIndex = props.defaultHighlightedIndex;
1935 var selectedItem = state.selectedItem,
1936 highlightedIndex = state.highlightedIndex;
1937
1938 if (items.length === 0) {
1939 return -1;
1940 } // initialHighlightedIndex will give value to highlightedIndex on initial state only.
1941
1942
1943 if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex) {
1944 return initialHighlightedIndex;
1945 }
1946
1947 if (defaultHighlightedIndex !== undefined) {
1948 return defaultHighlightedIndex;
1949 }
1950
1951 if (selectedItem) {
1952 if (offset === 0) {
1953 return items.indexOf(selectedItem);
1954 }
1955
1956 return getNextWrappingIndex(offset, items.indexOf(selectedItem), items.length, getItemNodeFromIndex, false);
1957 }
1958
1959 if (offset === 0) {
1960 return -1;
1961 }
1962
1963 return offset < 0 ? items.length - 1 : 0;
1964}
1965/**
1966 * Reuse the movement tracking of mouse and touch events.
1967 *
1968 * @param {boolean} isOpen Whether the dropdown is open or not.
1969 * @param {Array<Object>} downshiftElementRefs Downshift element refs to track movement (toggleButton, menu etc.)
1970 * @param {Object} environment Environment where component/hook exists.
1971 * @param {Function} handleBlur Handler on blur from mouse or touch.
1972 * @returns {Object} Ref containing whether mouseDown or touchMove event is happening
1973 */
1974
1975
1976function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, handleBlur) {
1977 var mouseAndTouchTrackersRef = useRef({
1978 isMouseDown: false,
1979 isTouchMove: false
1980 });
1981 useEffect(function () {
1982 // The same strategy for checking if a click occurred inside or outside downsift
1983 // as in downshift.js.
1984 var onMouseDown = function () {
1985 mouseAndTouchTrackersRef.current.isMouseDown = true;
1986 };
1987
1988 var onMouseUp = function (event) {
1989 mouseAndTouchTrackersRef.current.isMouseDown = false;
1990
1991 if (isOpen && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
1992 return ref.current;
1993 }), environment.document)) {
1994 handleBlur();
1995 }
1996 };
1997
1998 var onTouchStart = function () {
1999 mouseAndTouchTrackersRef.current.isTouchMove = false;
2000 };
2001
2002 var onTouchMove = function () {
2003 mouseAndTouchTrackersRef.current.isTouchMove = true;
2004 };
2005
2006 var onTouchEnd = function (event) {
2007 if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
2008 return ref.current;
2009 }), environment.document, false)) {
2010 handleBlur();
2011 }
2012 };
2013
2014 environment.addEventListener('mousedown', onMouseDown);
2015 environment.addEventListener('mouseup', onMouseUp);
2016 environment.addEventListener('touchstart', onTouchStart);
2017 environment.addEventListener('touchmove', onTouchMove);
2018 environment.addEventListener('touchend', onTouchEnd);
2019 return function () {
2020 environment.removeEventListener('mousedown', onMouseDown);
2021 environment.removeEventListener('mouseup', onMouseUp);
2022 environment.removeEventListener('touchstart', onTouchStart);
2023 environment.removeEventListener('touchmove', onTouchMove);
2024 environment.removeEventListener('touchend', onTouchEnd);
2025 }; // eslint-disable-next-line react-hooks/exhaustive-deps
2026 }, [isOpen, environment]);
2027 return mouseAndTouchTrackersRef;
2028}
2029/* istanbul ignore next */
2030// eslint-disable-next-line import/no-mutable-exports
2031
2032
2033var useGetterPropsCalledChecker = function () {
2034 return noop;
2035};
2036/**
2037 * Custom hook that checks if getter props are called correctly.
2038 *
2039 * @param {...any} propKeys Getter prop names to be handled.
2040 * @returns {Function} Setter function called inside getter props to set call information.
2041 */
2042
2043/* istanbul ignore next */
2044
2045
2046if (process.env.NODE_ENV !== 'production') {
2047 useGetterPropsCalledChecker = function () {
2048 var isInitialMountRef = useRef(true);
2049
2050 for (var _len = arguments.length, propKeys = new Array(_len), _key = 0; _key < _len; _key++) {
2051 propKeys[_key] = arguments[_key];
2052 }
2053
2054 var getterPropsCalledRef = useRef(propKeys.reduce(function (acc, propKey) {
2055 acc[propKey] = {};
2056 return acc;
2057 }, {}));
2058 useEffect(function () {
2059 Object.keys(getterPropsCalledRef.current).forEach(function (propKey) {
2060 var propCallInfo = getterPropsCalledRef.current[propKey];
2061
2062 if (isInitialMountRef.current) {
2063 if (!Object.keys(propCallInfo).length) {
2064 // eslint-disable-next-line no-console
2065 console.error("downshift: You forgot to call the " + propKey + " getter function on your component / element.");
2066 return;
2067 }
2068 }
2069
2070 var suppressRefError = propCallInfo.suppressRefError,
2071 refKey = propCallInfo.refKey,
2072 elementRef = propCallInfo.elementRef;
2073
2074 if ((!elementRef || !elementRef.current) && !suppressRefError) {
2075 // eslint-disable-next-line no-console
2076 console.error("downshift: The ref prop \"" + refKey + "\" from " + propKey + " was not applied correctly on your element.");
2077 }
2078 });
2079 isInitialMountRef.current = false;
2080 });
2081 var setGetterPropCallInfo = useCallback(function (propKey, suppressRefError, refKey, elementRef) {
2082 getterPropsCalledRef.current[propKey] = {
2083 suppressRefError: suppressRefError,
2084 refKey: refKey,
2085 elementRef: elementRef
2086 };
2087 }, []);
2088 return setGetterPropCallInfo;
2089 };
2090}
2091
2092function useA11yMessageSetter(getA11yMessage, dependencyArray, _ref2) {
2093 var isInitialMount = _ref2.isInitialMount,
2094 highlightedIndex = _ref2.highlightedIndex,
2095 items = _ref2.items,
2096 environment = _ref2.environment,
2097 rest = _objectWithoutPropertiesLoose(_ref2, ["isInitialMount", "highlightedIndex", "items", "environment"]);
2098
2099 // Sets a11y status message on changes in state.
2100 useEffect(function () {
2101 if (isInitialMount) {
2102 return;
2103 }
2104
2105 updateA11yStatus(function () {
2106 return getA11yMessage(_extends({
2107 highlightedIndex: highlightedIndex,
2108 highlightedItem: items[highlightedIndex],
2109 resultCount: items.length
2110 }, rest));
2111 }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2112 }, dependencyArray);
2113}
2114
2115function useScrollIntoView(_ref3) {
2116 var highlightedIndex = _ref3.highlightedIndex,
2117 isOpen = _ref3.isOpen,
2118 itemRefs = _ref3.itemRefs,
2119 getItemNodeFromIndex = _ref3.getItemNodeFromIndex,
2120 menuElement = _ref3.menuElement,
2121 scrollIntoViewProp = _ref3.scrollIntoView;
2122 // used not to scroll on highlight by mouse.
2123 var shouldScrollRef = useRef(true); // Scroll on highlighted item if change comes from keyboard.
2124
2125 useIsomorphicLayoutEffect(function () {
2126 if (highlightedIndex < 0 || !isOpen || !Object.keys(itemRefs.current).length) {
2127 return;
2128 }
2129
2130 if (shouldScrollRef.current === false) {
2131 shouldScrollRef.current = true;
2132 } else {
2133 scrollIntoViewProp(getItemNodeFromIndex(highlightedIndex), menuElement);
2134 } // eslint-disable-next-line react-hooks/exhaustive-deps
2135
2136 }, [highlightedIndex]);
2137 return shouldScrollRef;
2138} // eslint-disable-next-line import/no-mutable-exports
2139
2140
2141var useControlPropsValidator = noop;
2142/* istanbul ignore next */
2143
2144if (process.env.NODE_ENV !== 'production') {
2145 useControlPropsValidator = function (_ref4) {
2146 var isInitialMount = _ref4.isInitialMount,
2147 props = _ref4.props,
2148 state = _ref4.state;
2149 // used for checking when props are moving from controlled to uncontrolled.
2150 var prevPropsRef = useRef(props);
2151 useEffect(function () {
2152 if (isInitialMount) {
2153 return;
2154 }
2155
2156 validateControlledUnchanged(state, prevPropsRef.current, props);
2157 prevPropsRef.current = props;
2158 }, [state, props, isInitialMount]);
2159 };
2160}
2161
2162var MenuMouseLeave = process.env.NODE_ENV !== "production" ? '__menu_mouse_leave__' : 0;
2163var ItemMouseMove = process.env.NODE_ENV !== "production" ? '__item_mouse_move__' : 1;
2164var ItemClick = process.env.NODE_ENV !== "production" ? '__item_click__' : 2;
2165var ToggleButtonClick = process.env.NODE_ENV !== "production" ? '__togglebutton_click__' : 3;
2166var FunctionToggleMenu = process.env.NODE_ENV !== "production" ? '__function_toggle_menu__' : 4;
2167var FunctionOpenMenu = process.env.NODE_ENV !== "production" ? '__function_open_menu__' : 5;
2168var FunctionCloseMenu = process.env.NODE_ENV !== "production" ? '__function_close_menu__' : 6;
2169var FunctionSetHighlightedIndex = process.env.NODE_ENV !== "production" ? '__function_set_highlighted_index__' : 7;
2170var FunctionSelectItem = process.env.NODE_ENV !== "production" ? '__function_select_item__' : 8;
2171var FunctionSetInputValue = process.env.NODE_ENV !== "production" ? '__function_set_input_value__' : 9;
2172var FunctionReset = process.env.NODE_ENV !== "production" ? '__function_reset__' : 10; // to be used by useSelect and useCombobox
2173
2174function productionEnumFn(state) {
2175 return process.env.NODE_ENV !== "production" ? state : 11;
2176}
2177
2178/* eslint-disable complexity */
2179
2180function downshiftCommonReducer(state, action) {
2181 var type = action.type,
2182 props = action.props;
2183 var changes;
2184
2185 switch (type) {
2186 case ItemMouseMove:
2187 changes = {
2188 highlightedIndex: action.index
2189 };
2190 break;
2191
2192 case MenuMouseLeave:
2193 changes = {
2194 highlightedIndex: -1
2195 };
2196 break;
2197
2198 case ToggleButtonClick:
2199 case FunctionToggleMenu:
2200 changes = {
2201 isOpen: !state.isOpen,
2202 highlightedIndex: state.isOpen ? -1 : getHighlightedIndexOnOpen(props, state, 0)
2203 };
2204 break;
2205
2206 case FunctionOpenMenu:
2207 changes = {
2208 isOpen: true,
2209 highlightedIndex: getHighlightedIndexOnOpen(props, state, 0)
2210 };
2211 break;
2212
2213 case FunctionCloseMenu:
2214 changes = {
2215 isOpen: false
2216 };
2217 break;
2218
2219 case FunctionSetHighlightedIndex:
2220 changes = {
2221 highlightedIndex: action.highlightedIndex
2222 };
2223 break;
2224
2225 case FunctionSetInputValue:
2226 changes = {
2227 inputValue: action.inputValue
2228 };
2229 break;
2230
2231 case FunctionReset:
2232 changes = {
2233 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
2234 isOpen: getDefaultValue(props, 'isOpen'),
2235 selectedItem: getDefaultValue(props, 'selectedItem'),
2236 inputValue: getDefaultValue(props, 'inputValue')
2237 };
2238 break;
2239
2240 default:
2241 throw new Error('Reducer called without proper action type.');
2242 }
2243
2244 return _extends({}, state, changes);
2245}
2246/* eslint-enable complexity */
2247
2248function getItemIndexByCharacterKey(keysSoFar, highlightedIndex, items, itemToString, getItemNodeFromIndex) {
2249 var lowerCasedKeysSoFar = keysSoFar.toLowerCase();
2250
2251 for (var index = 0; index < items.length; index++) {
2252 var offsetIndex = (index + highlightedIndex + 1) % items.length;
2253
2254 if (itemToString(items[offsetIndex]).toLowerCase().startsWith(lowerCasedKeysSoFar)) {
2255 var element = getItemNodeFromIndex(offsetIndex);
2256
2257 if (!(element && element.hasAttribute('disabled'))) {
2258 return offsetIndex;
2259 }
2260 }
2261 }
2262
2263 return highlightedIndex;
2264}
2265
2266var propTypes = {
2267 items: PropTypes.array.isRequired,
2268 itemToString: PropTypes.func,
2269 getA11yStatusMessage: PropTypes.func,
2270 getA11ySelectionMessage: PropTypes.func,
2271 circularNavigation: PropTypes.bool,
2272 highlightedIndex: PropTypes.number,
2273 defaultHighlightedIndex: PropTypes.number,
2274 initialHighlightedIndex: PropTypes.number,
2275 isOpen: PropTypes.bool,
2276 defaultIsOpen: PropTypes.bool,
2277 initialIsOpen: PropTypes.bool,
2278 selectedItem: PropTypes.any,
2279 initialSelectedItem: PropTypes.any,
2280 defaultSelectedItem: PropTypes.any,
2281 id: PropTypes.string,
2282 labelId: PropTypes.string,
2283 menuId: PropTypes.string,
2284 getItemId: PropTypes.func,
2285 toggleButtonId: PropTypes.string,
2286 stateReducer: PropTypes.func,
2287 onSelectedItemChange: PropTypes.func,
2288 onHighlightedIndexChange: PropTypes.func,
2289 onStateChange: PropTypes.func,
2290 onIsOpenChange: PropTypes.func,
2291 environment: PropTypes.shape({
2292 addEventListener: PropTypes.func,
2293 removeEventListener: PropTypes.func,
2294 document: PropTypes.shape({
2295 getElementById: PropTypes.func,
2296 activeElement: PropTypes.any,
2297 body: PropTypes.any
2298 })
2299 })
2300};
2301/**
2302 * Default implementation for status message. Only added when menu is open.
2303 * Will specift if there are results in the list, and if so, how many,
2304 * and what keys are relevant.
2305 *
2306 * @param {Object} param the downshift state and other relevant properties
2307 * @return {String} the a11y status message
2308 */
2309
2310function getA11yStatusMessage$1(_ref) {
2311 var isOpen = _ref.isOpen,
2312 resultCount = _ref.resultCount,
2313 previousResultCount = _ref.previousResultCount;
2314
2315 if (!isOpen) {
2316 return '';
2317 }
2318
2319 if (!resultCount) {
2320 return 'No results are available.';
2321 }
2322
2323 if (resultCount !== previousResultCount) {
2324 return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.";
2325 }
2326
2327 return '';
2328}
2329
2330var defaultProps$1 = _extends({}, defaultProps, {
2331 getA11yStatusMessage: getA11yStatusMessage$1
2332}); // eslint-disable-next-line import/no-mutable-exports
2333
2334
2335var validatePropTypes = noop;
2336/* istanbul ignore next */
2337
2338if (process.env.NODE_ENV !== 'production') {
2339 validatePropTypes = function (options, caller) {
2340 PropTypes.checkPropTypes(propTypes, options, 'prop', caller.name);
2341 };
2342}
2343
2344var MenuKeyDownArrowDown = productionEnumFn('__menu_keydown_arrow_down__');
2345var MenuKeyDownArrowUp = productionEnumFn('__menu_keydown_arrow_up__');
2346var MenuKeyDownEscape = productionEnumFn('__menu_keydown_escape__');
2347var MenuKeyDownHome = productionEnumFn('__menu_keydown_home__');
2348var MenuKeyDownEnd = productionEnumFn('__menu_keydown_end__');
2349var MenuKeyDownEnter = productionEnumFn('__menu_keydown_enter__');
2350var MenuKeyDownSpaceButton = productionEnumFn('__menu_keydown_space_button__');
2351var MenuKeyDownCharacter = productionEnumFn('__menu_keydown_character__');
2352var MenuBlur = productionEnumFn('__menu_blur__');
2353var ToggleButtonKeyDownArrowDown = productionEnumFn('__togglebutton_keydown_arrow_down__');
2354var ToggleButtonKeyDownArrowUp = productionEnumFn('__togglebutton_keydown_arrow_up__');
2355var ToggleButtonKeyDownCharacter = productionEnumFn('__togglebutton_keydown_character__');
2356
2357var stateChangeTypes$1 = /*#__PURE__*/Object.freeze({
2358 __proto__: null,
2359 MenuKeyDownArrowDown: MenuKeyDownArrowDown,
2360 MenuKeyDownArrowUp: MenuKeyDownArrowUp,
2361 MenuKeyDownEscape: MenuKeyDownEscape,
2362 MenuKeyDownHome: MenuKeyDownHome,
2363 MenuKeyDownEnd: MenuKeyDownEnd,
2364 MenuKeyDownEnter: MenuKeyDownEnter,
2365 MenuKeyDownSpaceButton: MenuKeyDownSpaceButton,
2366 MenuKeyDownCharacter: MenuKeyDownCharacter,
2367 MenuBlur: MenuBlur,
2368 ToggleButtonKeyDownArrowDown: ToggleButtonKeyDownArrowDown,
2369 ToggleButtonKeyDownArrowUp: ToggleButtonKeyDownArrowUp,
2370 ToggleButtonKeyDownCharacter: ToggleButtonKeyDownCharacter,
2371 MenuMouseLeave: MenuMouseLeave,
2372 ItemMouseMove: ItemMouseMove,
2373 ItemClick: ItemClick,
2374 ToggleButtonClick: ToggleButtonClick,
2375 FunctionToggleMenu: FunctionToggleMenu,
2376 FunctionOpenMenu: FunctionOpenMenu,
2377 FunctionCloseMenu: FunctionCloseMenu,
2378 FunctionSetHighlightedIndex: FunctionSetHighlightedIndex,
2379 FunctionSelectItem: FunctionSelectItem,
2380 FunctionSetInputValue: FunctionSetInputValue,
2381 FunctionReset: FunctionReset,
2382 productionEnumFn: productionEnumFn
2383});
2384
2385/* eslint-disable complexity */
2386
2387function downshiftSelectReducer(state, action) {
2388 var type = action.type,
2389 props = action.props,
2390 shiftKey = action.shiftKey;
2391 var changes;
2392
2393 switch (type) {
2394 case ItemClick:
2395 changes = {
2396 isOpen: getDefaultValue(props, 'isOpen'),
2397 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
2398 selectedItem: props.items[action.index]
2399 };
2400 break;
2401
2402 case ToggleButtonKeyDownCharacter:
2403 {
2404 var lowercasedKey = action.key;
2405 var inputValue = "" + state.inputValue + lowercasedKey;
2406 var itemIndex = getItemIndexByCharacterKey(inputValue, state.selectedItem ? props.items.indexOf(state.selectedItem) : -1, props.items, props.itemToString, action.getItemNodeFromIndex);
2407 changes = _extends({
2408 inputValue: inputValue
2409 }, itemIndex >= 0 && {
2410 selectedItem: props.items[itemIndex]
2411 });
2412 }
2413 break;
2414
2415 case ToggleButtonKeyDownArrowDown:
2416 changes = {
2417 highlightedIndex: getHighlightedIndexOnOpen(props, state, 1, action.getItemNodeFromIndex),
2418 isOpen: true
2419 };
2420 break;
2421
2422 case ToggleButtonKeyDownArrowUp:
2423 changes = {
2424 highlightedIndex: getHighlightedIndexOnOpen(props, state, -1, action.getItemNodeFromIndex),
2425 isOpen: true
2426 };
2427 break;
2428
2429 case MenuKeyDownEnter:
2430 case MenuKeyDownSpaceButton:
2431 changes = _extends({
2432 isOpen: getDefaultValue(props, 'isOpen'),
2433 highlightedIndex: getDefaultValue(props, 'highlightedIndex')
2434 }, state.highlightedIndex >= 0 && {
2435 selectedItem: props.items[state.highlightedIndex]
2436 });
2437 break;
2438
2439 case MenuKeyDownHome:
2440 changes = {
2441 highlightedIndex: getNextNonDisabledIndex(1, 0, props.items.length, action.getItemNodeFromIndex, false)
2442 };
2443 break;
2444
2445 case MenuKeyDownEnd:
2446 changes = {
2447 highlightedIndex: getNextNonDisabledIndex(-1, props.items.length - 1, props.items.length, action.getItemNodeFromIndex, false)
2448 };
2449 break;
2450
2451 case MenuKeyDownEscape:
2452 changes = {
2453 isOpen: false,
2454 highlightedIndex: -1
2455 };
2456 break;
2457
2458 case MenuBlur:
2459 changes = {
2460 isOpen: false,
2461 highlightedIndex: -1
2462 };
2463 break;
2464
2465 case MenuKeyDownCharacter:
2466 {
2467 var _lowercasedKey = action.key;
2468
2469 var _inputValue = "" + state.inputValue + _lowercasedKey;
2470
2471 var highlightedIndex = getItemIndexByCharacterKey(_inputValue, state.highlightedIndex, props.items, props.itemToString, action.getItemNodeFromIndex);
2472 changes = _extends({
2473 inputValue: _inputValue
2474 }, highlightedIndex >= 0 && {
2475 highlightedIndex: highlightedIndex
2476 });
2477 }
2478 break;
2479
2480 case MenuKeyDownArrowDown:
2481 changes = {
2482 highlightedIndex: getNextWrappingIndex(shiftKey ? 5 : 1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, props.circularNavigation)
2483 };
2484 break;
2485
2486 case MenuKeyDownArrowUp:
2487 changes = {
2488 highlightedIndex: getNextWrappingIndex(shiftKey ? -5 : -1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, props.circularNavigation)
2489 };
2490 break;
2491
2492 case FunctionSelectItem:
2493 changes = {
2494 selectedItem: action.selectedItem
2495 };
2496 break;
2497
2498 default:
2499 return downshiftCommonReducer(state, action);
2500 }
2501
2502 return _extends({}, state, changes);
2503}
2504/* eslint-enable complexity */
2505
2506useSelect.stateChangeTypes = stateChangeTypes$1;
2507
2508function useSelect(userProps) {
2509 if (userProps === void 0) {
2510 userProps = {};
2511 }
2512
2513 validatePropTypes(userProps, useSelect); // Props defaults and destructuring.
2514
2515 var props = _extends({}, defaultProps$1, userProps);
2516
2517 var items = props.items,
2518 scrollIntoView = props.scrollIntoView,
2519 environment = props.environment,
2520 initialIsOpen = props.initialIsOpen,
2521 defaultIsOpen = props.defaultIsOpen,
2522 itemToString = props.itemToString,
2523 getA11ySelectionMessage = props.getA11ySelectionMessage,
2524 getA11yStatusMessage = props.getA11yStatusMessage; // Initial state depending on controlled props.
2525
2526 var initialState = getInitialState(props);
2527
2528 var _useControlledReducer = useControlledReducer(downshiftSelectReducer, initialState, props),
2529 state = _useControlledReducer[0],
2530 dispatch = _useControlledReducer[1];
2531
2532 var isOpen = state.isOpen,
2533 highlightedIndex = state.highlightedIndex,
2534 selectedItem = state.selectedItem,
2535 inputValue = state.inputValue; // Element efs.
2536
2537 var toggleButtonRef = useRef(null);
2538 var menuRef = useRef(null);
2539 var itemRefs = useRef();
2540 itemRefs.current = {}; // used not to trigger menu blur action in some scenarios.
2541
2542 var shouldBlurRef = useRef(true); // used to keep the inputValue clearTimeout object between renders.
2543
2544 var clearTimeoutRef = useRef(null); // prevent id re-generation between renders.
2545
2546 var elementIds = useElementIds(props); // used to keep track of how many items we had on previous cycle.
2547
2548 var previousResultCountRef = useRef();
2549 var isInitialMountRef = useRef(true); // utility callback to get item element.
2550
2551 var latest = useLatestRef({
2552 state: state,
2553 props: props
2554 }); // Some utils.
2555
2556 var getItemNodeFromIndex = useCallback(function (index) {
2557 return itemRefs.current[elementIds.getItemId(index)];
2558 }, [elementIds]); // Effects.
2559 // Sets a11y status message on changes in state.
2560
2561 useA11yMessageSetter(getA11yStatusMessage, [isOpen, highlightedIndex, inputValue, items], _extends({
2562 isInitialMount: isInitialMountRef.current,
2563 previousResultCount: previousResultCountRef.current,
2564 items: items,
2565 environment: environment,
2566 itemToString: itemToString
2567 }, state)); // Sets a11y status message on changes in selectedItem.
2568
2569 useA11yMessageSetter(getA11ySelectionMessage, [selectedItem], _extends({
2570 isInitialMount: isInitialMountRef.current,
2571 previousResultCount: previousResultCountRef.current,
2572 items: items,
2573 environment: environment,
2574 itemToString: itemToString
2575 }, state)); // Scroll on highlighted item if change comes from keyboard.
2576
2577 var shouldScrollRef = useScrollIntoView({
2578 menuElement: menuRef.current,
2579 highlightedIndex: highlightedIndex,
2580 isOpen: isOpen,
2581 itemRefs: itemRefs,
2582 scrollIntoView: scrollIntoView,
2583 getItemNodeFromIndex: getItemNodeFromIndex
2584 }); // Sets cleanup for the keysSoFar after 500ms.
2585
2586 useEffect(function () {
2587 // init the clean function here as we need access to dispatch.
2588 if (isInitialMountRef.current) {
2589 clearTimeoutRef.current = debounce(function (outerDispatch) {
2590 outerDispatch({
2591 type: FunctionSetInputValue,
2592 inputValue: ''
2593 });
2594 }, 500);
2595 }
2596
2597 if (!inputValue) {
2598 return;
2599 }
2600
2601 clearTimeoutRef.current(dispatch);
2602 }, [dispatch, inputValue]);
2603 useControlPropsValidator({
2604 isInitialMount: isInitialMountRef.current,
2605 props: props,
2606 state: state
2607 });
2608 /* Controls the focus on the menu or the toggle button. */
2609
2610 useEffect(function () {
2611 // Don't focus menu on first render.
2612 if (isInitialMountRef.current) {
2613 // Unless it was initialised as open.
2614 if ((initialIsOpen || defaultIsOpen || isOpen) && menuRef.current) {
2615 menuRef.current.focus();
2616 }
2617
2618 return;
2619 } // Focus menu on open.
2620
2621
2622 if (isOpen) {
2623 // istanbul ignore else
2624 if (menuRef.current) {
2625 menuRef.current.focus();
2626 }
2627
2628 return;
2629 } // Focus toggleButton on close, but not if it was closed with (Shift+)Tab.
2630
2631
2632 if (environment.document.activeElement === menuRef.current) {
2633 // istanbul ignore else
2634 if (toggleButtonRef.current) {
2635 shouldBlurRef.current = false;
2636 toggleButtonRef.current.focus();
2637 }
2638 } // eslint-disable-next-line react-hooks/exhaustive-deps
2639
2640 }, [isOpen]);
2641 useEffect(function () {
2642 if (isInitialMountRef.current) {
2643 return;
2644 }
2645
2646 previousResultCountRef.current = items.length;
2647 }); // Add mouse/touch events to document.
2648
2649 var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [menuRef, toggleButtonRef], environment, function () {
2650 dispatch({
2651 type: MenuBlur
2652 });
2653 });
2654 var setGetterPropCallInfo = useGetterPropsCalledChecker('getMenuProps', 'getToggleButtonProps'); // Make initial ref false.
2655
2656 useEffect(function () {
2657 isInitialMountRef.current = false;
2658 }, []); // Event handler functions.
2659
2660 var toggleButtonKeyDownHandlers = useMemo(function () {
2661 return {
2662 ArrowDown: function ArrowDown(event) {
2663 event.preventDefault();
2664 dispatch({
2665 type: ToggleButtonKeyDownArrowDown,
2666 getItemNodeFromIndex: getItemNodeFromIndex,
2667 shiftKey: event.shiftKey
2668 });
2669 },
2670 ArrowUp: function ArrowUp(event) {
2671 event.preventDefault();
2672 dispatch({
2673 type: ToggleButtonKeyDownArrowUp,
2674 getItemNodeFromIndex: getItemNodeFromIndex,
2675 shiftKey: event.shiftKey
2676 });
2677 }
2678 };
2679 }, [dispatch, getItemNodeFromIndex]);
2680 var menuKeyDownHandlers = useMemo(function () {
2681 return {
2682 ArrowDown: function ArrowDown(event) {
2683 event.preventDefault();
2684 dispatch({
2685 type: MenuKeyDownArrowDown,
2686 getItemNodeFromIndex: getItemNodeFromIndex,
2687 shiftKey: event.shiftKey
2688 });
2689 },
2690 ArrowUp: function ArrowUp(event) {
2691 event.preventDefault();
2692 dispatch({
2693 type: MenuKeyDownArrowUp,
2694 getItemNodeFromIndex: getItemNodeFromIndex,
2695 shiftKey: event.shiftKey
2696 });
2697 },
2698 Home: function Home(event) {
2699 event.preventDefault();
2700 dispatch({
2701 type: MenuKeyDownHome,
2702 getItemNodeFromIndex: getItemNodeFromIndex
2703 });
2704 },
2705 End: function End(event) {
2706 event.preventDefault();
2707 dispatch({
2708 type: MenuKeyDownEnd,
2709 getItemNodeFromIndex: getItemNodeFromIndex
2710 });
2711 },
2712 Escape: function Escape() {
2713 dispatch({
2714 type: MenuKeyDownEscape
2715 });
2716 },
2717 Enter: function Enter(event) {
2718 event.preventDefault();
2719 dispatch({
2720 type: MenuKeyDownEnter
2721 });
2722 },
2723 ' ': function _(event) {
2724 event.preventDefault();
2725 dispatch({
2726 type: MenuKeyDownSpaceButton
2727 });
2728 }
2729 };
2730 }, [dispatch, getItemNodeFromIndex]); // Action functions.
2731
2732 var toggleMenu = useCallback(function () {
2733 dispatch({
2734 type: FunctionToggleMenu
2735 });
2736 }, [dispatch]);
2737 var closeMenu = useCallback(function () {
2738 dispatch({
2739 type: FunctionCloseMenu
2740 });
2741 }, [dispatch]);
2742 var openMenu = useCallback(function () {
2743 dispatch({
2744 type: FunctionOpenMenu
2745 });
2746 }, [dispatch]);
2747 var setHighlightedIndex = useCallback(function (newHighlightedIndex) {
2748 dispatch({
2749 type: FunctionSetHighlightedIndex,
2750 highlightedIndex: newHighlightedIndex
2751 });
2752 }, [dispatch]);
2753 var selectItem = useCallback(function (newSelectedItem) {
2754 dispatch({
2755 type: FunctionSelectItem,
2756 selectedItem: newSelectedItem
2757 });
2758 }, [dispatch]);
2759 var reset = useCallback(function () {
2760 dispatch({
2761 type: FunctionReset
2762 });
2763 }, [dispatch]);
2764 var setInputValue = useCallback(function (newInputValue) {
2765 dispatch({
2766 type: FunctionSetInputValue,
2767 inputValue: newInputValue
2768 });
2769 }, [dispatch]); // Getter functions.
2770
2771 var getLabelProps = useCallback(function (labelProps) {
2772 return _extends({
2773 id: elementIds.labelId,
2774 htmlFor: elementIds.toggleButtonId
2775 }, labelProps);
2776 }, [elementIds]);
2777 var getMenuProps = useCallback(function (_temp, _temp2) {
2778 var _extends2;
2779
2780 var _ref = _temp === void 0 ? {} : _temp,
2781 onMouseLeave = _ref.onMouseLeave,
2782 _ref$refKey = _ref.refKey,
2783 refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
2784 onKeyDown = _ref.onKeyDown,
2785 onBlur = _ref.onBlur,
2786 ref = _ref.ref,
2787 rest = _objectWithoutPropertiesLoose(_ref, ["onMouseLeave", "refKey", "onKeyDown", "onBlur", "ref"]);
2788
2789 var _ref2 = _temp2 === void 0 ? {} : _temp2,
2790 _ref2$suppressRefErro = _ref2.suppressRefError,
2791 suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
2792
2793 var latestState = latest.current.state;
2794 setGetterPropCallInfo('getMenuProps', suppressRefError, refKey, menuRef);
2795 return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
2796 menuRef.current = menuNode;
2797 }), _extends2.id = elementIds.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIds.labelId, _extends2.tabIndex = -1, _extends2), latestState.isOpen && latestState.highlightedIndex > -1 && {
2798 'aria-activedescendant': elementIds.getItemId(latestState.highlightedIndex)
2799 }, {
2800 onMouseLeave: callAllEventHandlers(onMouseLeave, function menuHandleMouseLeave() {
2801 dispatch({
2802 type: MenuMouseLeave
2803 });
2804 }),
2805 onKeyDown: callAllEventHandlers(onKeyDown, function menuHandleKeyDown(event) {
2806 var key = normalizeArrowKey(event);
2807
2808 if (key && menuKeyDownHandlers[key]) {
2809 menuKeyDownHandlers[key](event);
2810 } else if (isAcceptedCharacterKey(key)) {
2811 dispatch({
2812 type: MenuKeyDownCharacter,
2813 key: key,
2814 getItemNodeFromIndex: getItemNodeFromIndex
2815 });
2816 }
2817 }),
2818 onBlur: callAllEventHandlers(onBlur, function menuHandleBlur() {
2819 // if the blur was a result of selection, we don't trigger this action.
2820 if (shouldBlurRef.current === false) {
2821 shouldBlurRef.current = true;
2822 return;
2823 }
2824
2825 var shouldBlur = !mouseAndTouchTrackersRef.current.isMouseDown;
2826 /* istanbul ignore else */
2827
2828 if (shouldBlur) {
2829 dispatch({
2830 type: MenuBlur
2831 });
2832 }
2833 })
2834 }, rest);
2835 }, [dispatch, latest, menuKeyDownHandlers, mouseAndTouchTrackersRef, setGetterPropCallInfo, elementIds, getItemNodeFromIndex]);
2836 var getToggleButtonProps = useCallback(function (_temp3, _temp4) {
2837 var _extends3;
2838
2839 var _ref3 = _temp3 === void 0 ? {} : _temp3,
2840 onClick = _ref3.onClick,
2841 onKeyDown = _ref3.onKeyDown,
2842 _ref3$refKey = _ref3.refKey,
2843 refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
2844 ref = _ref3.ref,
2845 rest = _objectWithoutPropertiesLoose(_ref3, ["onClick", "onKeyDown", "refKey", "ref"]);
2846
2847 var _ref4 = _temp4 === void 0 ? {} : _temp4,
2848 _ref4$suppressRefErro = _ref4.suppressRefError,
2849 suppressRefError = _ref4$suppressRefErro === void 0 ? false : _ref4$suppressRefErro;
2850
2851 var toggleButtonHandleClick = function () {
2852 dispatch({
2853 type: ToggleButtonClick
2854 });
2855 };
2856
2857 var toggleButtonHandleKeyDown = function (event) {
2858 var key = normalizeArrowKey(event);
2859
2860 if (key && toggleButtonKeyDownHandlers[key]) {
2861 toggleButtonKeyDownHandlers[key](event);
2862 } else if (isAcceptedCharacterKey(key)) {
2863 dispatch({
2864 type: ToggleButtonKeyDownCharacter,
2865 key: key,
2866 getItemNodeFromIndex: getItemNodeFromIndex
2867 });
2868 }
2869 };
2870
2871 var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2872 toggleButtonRef.current = toggleButtonNode;
2873 }), _extends3.id = elementIds.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = latest.current.state.isOpen, _extends3['aria-labelledby'] = elementIds.labelId + " " + elementIds.toggleButtonId, _extends3), rest);
2874
2875 if (!rest.disabled) {
2876 toggleProps.onClick = callAllEventHandlers(onClick, toggleButtonHandleClick);
2877 toggleProps.onKeyDown = callAllEventHandlers(onKeyDown, toggleButtonHandleKeyDown);
2878 }
2879
2880 setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2881 return toggleProps;
2882 }, [dispatch, latest, toggleButtonKeyDownHandlers, setGetterPropCallInfo, elementIds, getItemNodeFromIndex]);
2883 var getItemProps = useCallback(function (_temp5) {
2884 var _extends4;
2885
2886 var _ref5 = _temp5 === void 0 ? {} : _temp5,
2887 item = _ref5.item,
2888 index = _ref5.index,
2889 onMouseMove = _ref5.onMouseMove,
2890 onClick = _ref5.onClick,
2891 _ref5$refKey = _ref5.refKey,
2892 refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
2893 ref = _ref5.ref,
2894 rest = _objectWithoutPropertiesLoose(_ref5, ["item", "index", "onMouseMove", "onClick", "refKey", "ref"]);
2895
2896 var _latest$current = latest.current,
2897 latestState = _latest$current.state,
2898 latestProps = _latest$current.props;
2899
2900 var itemHandleMouseMove = function () {
2901 if (index === latestState.highlightedIndex) {
2902 return;
2903 }
2904
2905 shouldScrollRef.current = false;
2906 dispatch({
2907 type: ItemMouseMove,
2908 index: index
2909 });
2910 };
2911
2912 var itemHandleClick = function () {
2913 dispatch({
2914 type: ItemClick,
2915 index: index
2916 });
2917 };
2918
2919 var itemIndex = getItemIndex(index, item, latestProps.items);
2920
2921 if (itemIndex < 0) {
2922 throw new Error('Pass either item or item index in getItemProps!');
2923 }
2924
2925 var itemProps = _extends((_extends4 = {
2926 role: 'option',
2927 'aria-selected': "" + (itemIndex === latestState.highlightedIndex),
2928 id: elementIds.getItemId(itemIndex)
2929 }, _extends4[refKey] = handleRefs(ref, function (itemNode) {
2930 if (itemNode) {
2931 itemRefs.current[elementIds.getItemId(itemIndex)] = itemNode;
2932 }
2933 }), _extends4), rest);
2934
2935 if (!rest.disabled) {
2936 itemProps.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove);
2937 itemProps.onClick = callAllEventHandlers(onClick, itemHandleClick);
2938 }
2939
2940 return itemProps;
2941 }, [dispatch, latest, shouldScrollRef, elementIds]);
2942 return {
2943 // prop getters.
2944 getToggleButtonProps: getToggleButtonProps,
2945 getLabelProps: getLabelProps,
2946 getMenuProps: getMenuProps,
2947 getItemProps: getItemProps,
2948 // actions.
2949 toggleMenu: toggleMenu,
2950 openMenu: openMenu,
2951 closeMenu: closeMenu,
2952 setHighlightedIndex: setHighlightedIndex,
2953 selectItem: selectItem,
2954 reset: reset,
2955 setInputValue: setInputValue,
2956 // state.
2957 highlightedIndex: highlightedIndex,
2958 isOpen: isOpen,
2959 selectedItem: selectedItem,
2960 inputValue: inputValue
2961 };
2962}
2963
2964var InputKeyDownArrowDown = productionEnumFn('__input_keydown_arrow_down__');
2965var InputKeyDownArrowUp = productionEnumFn('__input_keydown_arrow_up__');
2966var InputKeyDownEscape = productionEnumFn('__input_keydown_escape__');
2967var InputKeyDownHome = productionEnumFn('__input_keydown_home__');
2968var InputKeyDownEnd = productionEnumFn('__input_keydown_end__');
2969var InputKeyDownEnter = productionEnumFn('__input_keydown_enter__');
2970var InputChange = productionEnumFn('__input_change__');
2971var InputBlur = productionEnumFn('__input_blur__');
2972var ControlledPropUpdatedSelectedItem = productionEnumFn('__controlled_prop_updated_selected_item__');
2973
2974var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
2975 __proto__: null,
2976 InputKeyDownArrowDown: InputKeyDownArrowDown,
2977 InputKeyDownArrowUp: InputKeyDownArrowUp,
2978 InputKeyDownEscape: InputKeyDownEscape,
2979 InputKeyDownHome: InputKeyDownHome,
2980 InputKeyDownEnd: InputKeyDownEnd,
2981 InputKeyDownEnter: InputKeyDownEnter,
2982 InputChange: InputChange,
2983 InputBlur: InputBlur,
2984 ControlledPropUpdatedSelectedItem: ControlledPropUpdatedSelectedItem,
2985 MenuMouseLeave: MenuMouseLeave,
2986 ItemMouseMove: ItemMouseMove,
2987 ItemClick: ItemClick,
2988 ToggleButtonClick: ToggleButtonClick,
2989 FunctionToggleMenu: FunctionToggleMenu,
2990 FunctionOpenMenu: FunctionOpenMenu,
2991 FunctionCloseMenu: FunctionCloseMenu,
2992 FunctionSetHighlightedIndex: FunctionSetHighlightedIndex,
2993 FunctionSelectItem: FunctionSelectItem,
2994 FunctionSetInputValue: FunctionSetInputValue,
2995 FunctionReset: FunctionReset,
2996 productionEnumFn: productionEnumFn
2997});
2998
2999function getInitialState$1(props) {
3000 var initialState = getInitialState(props);
3001 var selectedItem = initialState.selectedItem;
3002 var inputValue = initialState.inputValue;
3003
3004 if (inputValue === '' && selectedItem && props.defaultInputValue === undefined && props.initialInputValue === undefined && props.inputValue === undefined) {
3005 inputValue = props.itemToString(selectedItem);
3006 }
3007
3008 return _extends({}, initialState, {
3009 inputValue: inputValue
3010 });
3011}
3012
3013var propTypes$1 = {
3014 items: PropTypes.array.isRequired,
3015 itemToString: PropTypes.func,
3016 getA11yStatusMessage: PropTypes.func,
3017 getA11ySelectionMessage: PropTypes.func,
3018 circularNavigation: PropTypes.bool,
3019 highlightedIndex: PropTypes.number,
3020 defaultHighlightedIndex: PropTypes.number,
3021 initialHighlightedIndex: PropTypes.number,
3022 isOpen: PropTypes.bool,
3023 defaultIsOpen: PropTypes.bool,
3024 initialIsOpen: PropTypes.bool,
3025 selectedItem: PropTypes.any,
3026 initialSelectedItem: PropTypes.any,
3027 defaultSelectedItem: PropTypes.any,
3028 inputValue: PropTypes.string,
3029 defaultInputValue: PropTypes.string,
3030 initialInputValue: PropTypes.string,
3031 id: PropTypes.string,
3032 labelId: PropTypes.string,
3033 menuId: PropTypes.string,
3034 getItemId: PropTypes.func,
3035 inputId: PropTypes.string,
3036 toggleButtonId: PropTypes.string,
3037 stateReducer: PropTypes.func,
3038 onSelectedItemChange: PropTypes.func,
3039 onHighlightedIndexChange: PropTypes.func,
3040 onStateChange: PropTypes.func,
3041 onIsOpenChange: PropTypes.func,
3042 onInputValueChange: PropTypes.func,
3043 environment: PropTypes.shape({
3044 addEventListener: PropTypes.func,
3045 removeEventListener: PropTypes.func,
3046 document: PropTypes.shape({
3047 getElementById: PropTypes.func,
3048 activeElement: PropTypes.any,
3049 body: PropTypes.any
3050 })
3051 })
3052};
3053/**
3054 * The useCombobox version of useControlledReducer, which also
3055 * checks if the controlled prop selectedItem changed between
3056 * renders. If so, it will also update inputValue with its
3057 * string equivalent. It uses the common useEnhancedReducer to
3058 * compute the rest of the state.
3059 *
3060 * @param {Function} reducer Reducer function from downshift.
3061 * @param {Object} initialState Initial state of the hook.
3062 * @param {Object} props The hook props.
3063 * @returns {Array} An array with the state and an action dispatcher.
3064 */
3065
3066function useControlledReducer$1(reducer, initialState, props) {
3067 var previousSelectedItemRef = useRef();
3068
3069 var _useEnhancedReducer = useEnhancedReducer(reducer, initialState, props),
3070 state = _useEnhancedReducer[0],
3071 dispatch = _useEnhancedReducer[1]; // ToDo: if needed, make same approach as selectedItemChanged from Downshift.
3072
3073
3074 useEffect(function () {
3075 if (isControlledProp(props, 'selectedItem')) {
3076 if (previousSelectedItemRef.current !== props.selectedItem) {
3077 dispatch({
3078 type: ControlledPropUpdatedSelectedItem,
3079 inputValue: props.itemToString(props.selectedItem)
3080 });
3081 }
3082
3083 previousSelectedItemRef.current = state.selectedItem === previousSelectedItemRef.current ? props.selectedItem : state.selectedItem;
3084 }
3085 });
3086 return [getState(state, props), dispatch];
3087} // eslint-disable-next-line import/no-mutable-exports
3088
3089
3090var validatePropTypes$1 = noop;
3091/* istanbul ignore next */
3092
3093if (process.env.NODE_ENV !== 'production') {
3094 validatePropTypes$1 = function (options, caller) {
3095 PropTypes.checkPropTypes(propTypes$1, options, 'prop', caller.name);
3096 };
3097}
3098
3099var defaultProps$2 = _extends({}, defaultProps, {
3100 getA11yStatusMessage: getA11yStatusMessage,
3101 circularNavigation: true
3102});
3103
3104/* eslint-disable complexity */
3105
3106function downshiftUseComboboxReducer(state, action) {
3107 var type = action.type,
3108 props = action.props,
3109 shiftKey = action.shiftKey;
3110 var changes;
3111
3112 switch (type) {
3113 case ItemClick:
3114 changes = {
3115 isOpen: getDefaultValue(props, 'isOpen'),
3116 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
3117 selectedItem: props.items[action.index],
3118 inputValue: props.itemToString(props.items[action.index])
3119 };
3120 break;
3121
3122 case InputKeyDownArrowDown:
3123 if (state.isOpen) {
3124 changes = {
3125 highlightedIndex: getNextWrappingIndex(shiftKey ? 5 : 1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, props.circularNavigation)
3126 };
3127 } else {
3128 changes = {
3129 highlightedIndex: getHighlightedIndexOnOpen(props, state, 1, action.getItemNodeFromIndex),
3130 isOpen: true
3131 };
3132 }
3133
3134 break;
3135
3136 case InputKeyDownArrowUp:
3137 if (state.isOpen) {
3138 changes = {
3139 highlightedIndex: getNextWrappingIndex(shiftKey ? -5 : -1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, props.circularNavigation)
3140 };
3141 } else {
3142 changes = {
3143 highlightedIndex: getHighlightedIndexOnOpen(props, state, -1, action.getItemNodeFromIndex),
3144 isOpen: true
3145 };
3146 }
3147
3148 break;
3149
3150 case InputKeyDownEnter:
3151 changes = _extends({}, state.isOpen && state.highlightedIndex >= 0 && {
3152 selectedItem: props.items[state.highlightedIndex],
3153 isOpen: getDefaultValue(props, 'isOpen'),
3154 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
3155 inputValue: props.itemToString(props.items[state.highlightedIndex])
3156 });
3157 break;
3158
3159 case InputKeyDownEscape:
3160 changes = _extends({
3161 isOpen: false,
3162 highlightedIndex: -1
3163 }, !state.isOpen && {
3164 selectedItem: null,
3165 inputValue: ''
3166 });
3167 break;
3168
3169 case InputKeyDownHome:
3170 changes = _extends({}, state.isOpen && {
3171 highlightedIndex: getNextNonDisabledIndex(1, 0, props.items.length, action.getItemNodeFromIndex, false)
3172 });
3173 break;
3174
3175 case InputKeyDownEnd:
3176 changes = _extends({}, state.isOpen && {
3177 highlightedIndex: getNextNonDisabledIndex(-1, props.items.length - 1, props.items.length, action.getItemNodeFromIndex, false)
3178 });
3179 break;
3180
3181 case InputBlur:
3182 if (state.isOpen) {
3183 changes = _extends({
3184 isOpen: false,
3185 highlightedIndex: -1
3186 }, state.highlightedIndex >= 0 && action.selectItem && {
3187 selectedItem: props.items[state.highlightedIndex],
3188 inputValue: props.itemToString(props.items[state.highlightedIndex])
3189 });
3190 }
3191
3192 break;
3193
3194 case InputChange:
3195 changes = {
3196 isOpen: true,
3197 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
3198 inputValue: action.inputValue
3199 };
3200 break;
3201
3202 case FunctionSelectItem:
3203 changes = {
3204 selectedItem: action.selectedItem,
3205 inputValue: props.itemToString(action.selectedItem)
3206 };
3207 break;
3208
3209 case ControlledPropUpdatedSelectedItem:
3210 changes = {
3211 inputValue: action.inputValue
3212 };
3213 break;
3214
3215 default:
3216 return downshiftCommonReducer(state, action);
3217 }
3218
3219 return _extends({}, state, changes);
3220}
3221/* eslint-enable complexity */
3222
3223useCombobox.stateChangeTypes = stateChangeTypes$2;
3224
3225function useCombobox(userProps) {
3226 if (userProps === void 0) {
3227 userProps = {};
3228 }
3229
3230 validatePropTypes$1(userProps, useCombobox); // Props defaults and destructuring.
3231
3232 var props = _extends({}, defaultProps$2, userProps);
3233
3234 var initialIsOpen = props.initialIsOpen,
3235 defaultIsOpen = props.defaultIsOpen,
3236 items = props.items,
3237 scrollIntoView = props.scrollIntoView,
3238 environment = props.environment,
3239 getA11yStatusMessage = props.getA11yStatusMessage,
3240 getA11ySelectionMessage = props.getA11ySelectionMessage,
3241 itemToString = props.itemToString; // Initial state depending on controlled props.
3242
3243 var initialState = getInitialState$1(props);
3244
3245 var _useControlledReducer = useControlledReducer$1(downshiftUseComboboxReducer, initialState, props),
3246 state = _useControlledReducer[0],
3247 dispatch = _useControlledReducer[1];
3248
3249 var isOpen = state.isOpen,
3250 highlightedIndex = state.highlightedIndex,
3251 selectedItem = state.selectedItem,
3252 inputValue = state.inputValue; // Element refs.
3253
3254 var menuRef = useRef(null);
3255 var itemRefs = useRef();
3256 var inputRef = useRef(null);
3257 var toggleButtonRef = useRef(null);
3258 var comboboxRef = useRef(null);
3259 itemRefs.current = {};
3260 var isInitialMountRef = useRef(true); // prevent id re-generation between renders.
3261
3262 var elementIds = useElementIds(props); // used to keep track of how many items we had on previous cycle.
3263
3264 var previousResultCountRef = useRef(); // utility callback to get item element.
3265
3266 var latest = useLatestRef({
3267 state: state,
3268 props: props
3269 });
3270 var getItemNodeFromIndex = useCallback(function (index) {
3271 return itemRefs.current[elementIds.getItemId(index)];
3272 }, [elementIds]); // Effects.
3273 // Sets a11y status message on changes in state.
3274
3275 useA11yMessageSetter(getA11yStatusMessage, [isOpen, highlightedIndex, inputValue, items], _extends({
3276 isInitialMount: isInitialMountRef.current,
3277 previousResultCount: previousResultCountRef.current,
3278 items: items,
3279 environment: environment,
3280 itemToString: itemToString
3281 }, state)); // Sets a11y status message on changes in selectedItem.
3282
3283 useA11yMessageSetter(getA11ySelectionMessage, [selectedItem], _extends({
3284 isInitialMount: isInitialMountRef.current,
3285 previousResultCount: previousResultCountRef.current,
3286 items: items,
3287 environment: environment,
3288 itemToString: itemToString
3289 }, state)); // Scroll on highlighted item if change comes from keyboard.
3290
3291 var shouldScrollRef = useScrollIntoView({
3292 menuElement: menuRef.current,
3293 highlightedIndex: highlightedIndex,
3294 isOpen: isOpen,
3295 itemRefs: itemRefs,
3296 scrollIntoView: scrollIntoView,
3297 getItemNodeFromIndex: getItemNodeFromIndex
3298 });
3299 useControlPropsValidator({
3300 isInitialMount: isInitialMountRef.current,
3301 props: props,
3302 state: state
3303 }); // Focus the input on first render if required.
3304
3305 useEffect(function () {
3306 if ((initialIsOpen || defaultIsOpen || isOpen) && inputRef.current) {
3307 inputRef.current.focus();
3308 } // eslint-disable-next-line react-hooks/exhaustive-deps
3309
3310 }, []);
3311 useEffect(function () {
3312 if (isInitialMountRef.current) {
3313 return;
3314 }
3315
3316 previousResultCountRef.current = items.length;
3317 }); // Add mouse/touch events to document.
3318
3319 var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [comboboxRef, menuRef, toggleButtonRef], environment, function () {
3320 dispatch({
3321 type: InputBlur,
3322 selectItem: false
3323 });
3324 });
3325 var setGetterPropCallInfo = useGetterPropsCalledChecker('getInputProps', 'getComboboxProps', 'getMenuProps'); // Make initial ref false.
3326
3327 useEffect(function () {
3328 isInitialMountRef.current = false;
3329 }, []);
3330 /* Event handler functions */
3331
3332 var inputKeyDownHandlers = useMemo(function () {
3333 return {
3334 ArrowDown: function ArrowDown(event) {
3335 event.preventDefault();
3336 dispatch({
3337 type: InputKeyDownArrowDown,
3338 shiftKey: event.shiftKey,
3339 getItemNodeFromIndex: getItemNodeFromIndex
3340 });
3341 },
3342 ArrowUp: function ArrowUp(event) {
3343 event.preventDefault();
3344 dispatch({
3345 type: InputKeyDownArrowUp,
3346 shiftKey: event.shiftKey,
3347 getItemNodeFromIndex: getItemNodeFromIndex
3348 });
3349 },
3350 Home: function Home(event) {
3351 event.preventDefault();
3352 dispatch({
3353 type: InputKeyDownHome,
3354 getItemNodeFromIndex: getItemNodeFromIndex
3355 });
3356 },
3357 End: function End(event) {
3358 event.preventDefault();
3359 dispatch({
3360 type: InputKeyDownEnd,
3361 getItemNodeFromIndex: getItemNodeFromIndex
3362 });
3363 },
3364 Escape: function Escape() {
3365 dispatch({
3366 type: InputKeyDownEscape
3367 });
3368 },
3369 Enter: function Enter(event) {
3370 // if IME composing, wait for next Enter keydown event.
3371 if (event.which === 229) {
3372 return;
3373 }
3374
3375 var latestState = latest.current.state;
3376
3377 if (latestState.isOpen) {
3378 event.preventDefault();
3379 }
3380
3381 dispatch({
3382 type: InputKeyDownEnter,
3383 getItemNodeFromIndex: getItemNodeFromIndex
3384 });
3385 }
3386 };
3387 }, [dispatch, latest, getItemNodeFromIndex]); // Getter props.
3388
3389 var getLabelProps = useCallback(function (labelProps) {
3390 return _extends({
3391 id: elementIds.labelId,
3392 htmlFor: elementIds.inputId
3393 }, labelProps);
3394 }, [elementIds]);
3395 var getMenuProps = useCallback(function (_temp, _temp2) {
3396 var _extends2;
3397
3398 var _ref = _temp === void 0 ? {} : _temp,
3399 onMouseLeave = _ref.onMouseLeave,
3400 _ref$refKey = _ref.refKey,
3401 refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
3402 ref = _ref.ref,
3403 rest = _objectWithoutPropertiesLoose(_ref, ["onMouseLeave", "refKey", "ref"]);
3404
3405 var _ref2 = _temp2 === void 0 ? {} : _temp2,
3406 _ref2$suppressRefErro = _ref2.suppressRefError,
3407 suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
3408
3409 setGetterPropCallInfo('getMenuProps', suppressRefError, refKey, menuRef);
3410 return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
3411 menuRef.current = menuNode;
3412 }), _extends2.id = elementIds.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIds.labelId, _extends2.onMouseLeave = callAllEventHandlers(onMouseLeave, function () {
3413 dispatch({
3414 type: MenuMouseLeave
3415 });
3416 }), _extends2), rest);
3417 }, [dispatch, setGetterPropCallInfo, elementIds]);
3418 var getItemProps = useCallback(function (_temp3) {
3419 var _extends3, _ref4;
3420
3421 var _ref3 = _temp3 === void 0 ? {} : _temp3,
3422 item = _ref3.item,
3423 index = _ref3.index,
3424 _ref3$refKey = _ref3.refKey,
3425 refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
3426 ref = _ref3.ref,
3427 onMouseMove = _ref3.onMouseMove,
3428 onClick = _ref3.onClick,
3429 onPress = _ref3.onPress,
3430 rest = _objectWithoutPropertiesLoose(_ref3, ["item", "index", "refKey", "ref", "onMouseMove", "onClick", "onPress"]);
3431
3432 var _latest$current = latest.current,
3433 latestProps = _latest$current.props,
3434 latestState = _latest$current.state;
3435 var itemIndex = getItemIndex(index, item, latestProps.items);
3436
3437 if (itemIndex < 0) {
3438 throw new Error('Pass either item or item index in getItemProps!');
3439 }
3440
3441 var onSelectKey = 'onClick';
3442 var customClickHandler = onClick;
3443 return _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (itemNode) {
3444 if (itemNode) {
3445 itemRefs.current[elementIds.getItemId(itemIndex)] = itemNode;
3446 }
3447 }), _extends3.role = 'option', _extends3['aria-selected'] = "" + (itemIndex === latestState.highlightedIndex), _extends3.id = elementIds.getItemId(itemIndex), _extends3), !rest.disabled && (_ref4 = {
3448 onMouseMove: callAllEventHandlers(onMouseMove, function itemHandleMouseMove() {
3449 if (index === latestState.highlightedIndex) {
3450 return;
3451 }
3452
3453 shouldScrollRef.current = false;
3454 dispatch({
3455 type: ItemMouseMove,
3456 index: index
3457 });
3458 })
3459 }, _ref4[onSelectKey] = callAllEventHandlers(customClickHandler, function itemHandleClick() {
3460 dispatch({
3461 type: ItemClick,
3462 index: index
3463 });
3464
3465 if (inputRef.current) {
3466 inputRef.current.focus();
3467 }
3468 }), _ref4), rest);
3469 }, [dispatch, latest, shouldScrollRef, elementIds]);
3470 var getToggleButtonProps = useCallback(function (_temp4) {
3471 var _extends4;
3472
3473 var _ref5 = _temp4 === void 0 ? {} : _temp4,
3474 onClick = _ref5.onClick,
3475 onPress = _ref5.onPress,
3476 _ref5$refKey = _ref5.refKey,
3477 refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
3478 ref = _ref5.ref,
3479 rest = _objectWithoutPropertiesLoose(_ref5, ["onClick", "onPress", "refKey", "ref"]);
3480
3481 var toggleButtonHandleClick = function () {
3482 dispatch({
3483 type: ToggleButtonClick
3484 });
3485
3486 if (!latest.current.state.isOpen && inputRef.current) {
3487 inputRef.current.focus();
3488 }
3489 };
3490
3491 return _extends((_extends4 = {}, _extends4[refKey] = handleRefs(ref, function (toggleButtonNode) {
3492 toggleButtonRef.current = toggleButtonNode;
3493 }), _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !rest.disabled && _extends({}, {
3494 onClick: callAllEventHandlers(onClick, toggleButtonHandleClick)
3495 }), rest);
3496 }, [dispatch, latest, elementIds]);
3497 var getInputProps = useCallback(function (_temp5, _temp6) {
3498 var _extends5;
3499
3500 var _ref6 = _temp5 === void 0 ? {} : _temp5,
3501 onKeyDown = _ref6.onKeyDown,
3502 onChange = _ref6.onChange,
3503 onInput = _ref6.onInput,
3504 onBlur = _ref6.onBlur,
3505 onChangeText = _ref6.onChangeText,
3506 _ref6$refKey = _ref6.refKey,
3507 refKey = _ref6$refKey === void 0 ? 'ref' : _ref6$refKey,
3508 ref = _ref6.ref,
3509 rest = _objectWithoutPropertiesLoose(_ref6, ["onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "refKey", "ref"]);
3510
3511 var _ref7 = _temp6 === void 0 ? {} : _temp6,
3512 _ref7$suppressRefErro = _ref7.suppressRefError,
3513 suppressRefError = _ref7$suppressRefErro === void 0 ? false : _ref7$suppressRefErro;
3514
3515 setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef);
3516 var latestState = latest.current.state;
3517
3518 var inputHandleKeyDown = function (event) {
3519 var key = normalizeArrowKey(event);
3520
3521 if (key && inputKeyDownHandlers[key]) {
3522 inputKeyDownHandlers[key](event);
3523 }
3524 };
3525
3526 var inputHandleChange = function (event) {
3527 dispatch({
3528 type: InputChange,
3529 inputValue: event.target.value
3530 });
3531 };
3532
3533 var inputHandleBlur = function () {
3534 /* istanbul ignore else */
3535 if (!mouseAndTouchTrackersRef.current.isMouseDown) {
3536 dispatch({
3537 type: InputBlur,
3538 selectItem: true
3539 });
3540 }
3541 };
3542 /* istanbul ignore next (preact) */
3543
3544
3545 var onChangeKey = 'onChange';
3546 var eventHandlers = {};
3547
3548 if (!rest.disabled) {
3549 var _eventHandlers;
3550
3551 eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, inputHandleBlur), _eventHandlers);
3552 }
3553 /* istanbul ignore if (react-native) */
3554
3555
3556 return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3557 inputRef.current = inputNode;
3558 }), _extends5.id = elementIds.inputId, _extends5['aria-autocomplete'] = 'list', _extends5['aria-controls'] = elementIds.menuId, _extends5), latestState.isOpen && latestState.highlightedIndex > -1 && {
3559 'aria-activedescendant': elementIds.getItemId(latestState.highlightedIndex)
3560 }, {
3561 'aria-labelledby': elementIds.labelId,
3562 // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
3563 // revert back since autocomplete="nope" is ignored on latest Chrome and Opera
3564 autoComplete: 'off',
3565 value: latestState.inputValue
3566 }, eventHandlers, rest);
3567 }, [dispatch, inputKeyDownHandlers, latest, mouseAndTouchTrackersRef, setGetterPropCallInfo, elementIds]);
3568 var getComboboxProps = useCallback(function (_temp7, _temp8) {
3569 var _extends6;
3570
3571 var _ref8 = _temp7 === void 0 ? {} : _temp7,
3572 _ref8$refKey = _ref8.refKey,
3573 refKey = _ref8$refKey === void 0 ? 'ref' : _ref8$refKey,
3574 ref = _ref8.ref,
3575 rest = _objectWithoutPropertiesLoose(_ref8, ["refKey", "ref"]);
3576
3577 var _ref9 = _temp8 === void 0 ? {} : _temp8,
3578 _ref9$suppressRefErro = _ref9.suppressRefError,
3579 suppressRefError = _ref9$suppressRefErro === void 0 ? false : _ref9$suppressRefErro;
3580
3581 setGetterPropCallInfo('getComboboxProps', suppressRefError, refKey, comboboxRef);
3582 return _extends((_extends6 = {}, _extends6[refKey] = handleRefs(ref, function (comboboxNode) {
3583 comboboxRef.current = comboboxNode;
3584 }), _extends6.role = 'combobox', _extends6['aria-haspopup'] = 'listbox', _extends6['aria-owns'] = elementIds.menuId, _extends6['aria-expanded'] = latest.current.state.isOpen, _extends6), rest);
3585 }, [latest, setGetterPropCallInfo, elementIds]); // returns
3586
3587 var toggleMenu = useCallback(function () {
3588 dispatch({
3589 type: FunctionToggleMenu
3590 });
3591 }, [dispatch]);
3592 var closeMenu = useCallback(function () {
3593 dispatch({
3594 type: FunctionCloseMenu
3595 });
3596 }, [dispatch]);
3597 var openMenu = useCallback(function () {
3598 dispatch({
3599 type: FunctionOpenMenu
3600 });
3601 }, [dispatch]);
3602 var setHighlightedIndex = useCallback(function (newHighlightedIndex) {
3603 dispatch({
3604 type: FunctionSetHighlightedIndex,
3605 highlightedIndex: newHighlightedIndex
3606 });
3607 }, [dispatch]);
3608 var selectItem = useCallback(function (newSelectedItem) {
3609 dispatch({
3610 type: FunctionSelectItem,
3611 selectedItem: newSelectedItem
3612 });
3613 }, [dispatch]);
3614 var setInputValue = useCallback(function (newInputValue) {
3615 dispatch({
3616 type: FunctionSetInputValue,
3617 inputValue: newInputValue
3618 });
3619 }, [dispatch]);
3620 var reset = useCallback(function () {
3621 dispatch({
3622 type: FunctionReset
3623 });
3624 }, [dispatch]);
3625 return {
3626 // prop getters.
3627 getItemProps: getItemProps,
3628 getLabelProps: getLabelProps,
3629 getMenuProps: getMenuProps,
3630 getInputProps: getInputProps,
3631 getComboboxProps: getComboboxProps,
3632 getToggleButtonProps: getToggleButtonProps,
3633 // actions.
3634 toggleMenu: toggleMenu,
3635 openMenu: openMenu,
3636 closeMenu: closeMenu,
3637 setHighlightedIndex: setHighlightedIndex,
3638 setInputValue: setInputValue,
3639 selectItem: selectItem,
3640 reset: reset,
3641 // state.
3642 highlightedIndex: highlightedIndex,
3643 isOpen: isOpen,
3644 selectedItem: selectedItem,
3645 inputValue: inputValue
3646 };
3647}
3648
3649var defaultStateValues = {
3650 activeIndex: -1,
3651 selectedItems: []
3652};
3653/**
3654 * Returns the initial value for a state key in the following order:
3655 * 1. controlled prop, 2. initial prop, 3. default prop, 4. default
3656 * value from Downshift.
3657 *
3658 * @param {Object} props Props passed to the hook.
3659 * @param {string} propKey Props key to generate the value for.
3660 * @returns {any} The initial value for that prop.
3661 */
3662
3663function getInitialValue$1(props, propKey) {
3664 return getInitialValue(props, propKey, defaultStateValues);
3665}
3666/**
3667 * Returns the default value for a state key in the following order:
3668 * 1. controlled prop, 2. default prop, 3. default value from Downshift.
3669 *
3670 * @param {Object} props Props passed to the hook.
3671 * @param {string} propKey Props key to generate the value for.
3672 * @returns {any} The initial value for that prop.
3673 */
3674
3675
3676function getDefaultValue$1(props, propKey) {
3677 return getDefaultValue(props, propKey, defaultStateValues);
3678}
3679/**
3680 * Gets the initial state based on the provided props. It uses initial, default
3681 * and controlled props related to state in order to compute the initial value.
3682 *
3683 * @param {Object} props Props passed to the hook.
3684 * @returns {Object} The initial state.
3685 */
3686
3687
3688function getInitialState$2(props) {
3689 var activeIndex = getInitialValue$1(props, 'activeIndex');
3690 var selectedItems = getInitialValue$1(props, 'selectedItems');
3691 return {
3692 activeIndex: activeIndex,
3693 selectedItems: selectedItems
3694 };
3695}
3696/**
3697 * Returns true if dropdown keydown operation is permitted. Should not be
3698 * allowed on keydown with modifier keys (ctrl, alt, shift, meta), on
3699 * input element with text content that is either highlighted or selection
3700 * cursor is not at the starting position.
3701 *
3702 * @param {KeyboardEvent} event The event from keydown.
3703 * @returns {boolean} Whether the operation is allowed.
3704 */
3705
3706
3707function isKeyDownOperationPermitted(event) {
3708 if (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey) {
3709 return false;
3710 }
3711
3712 var element = event.target;
3713
3714 if (element instanceof HTMLInputElement && // if element is a text input
3715 element.value !== '' && ( // and we have text in it
3716 // and cursor is either not at the start or is currently highlighting text.
3717 element.selectionStart !== 0 || element.selectionEnd !== 0)) {
3718 return false;
3719 }
3720
3721 return true;
3722}
3723/**
3724 * Returns a message to be added to aria-live region when item is removed.
3725 *
3726 * @param {Object} selectionParameters Parameters required to build the message.
3727 * @returns {string} The a11y message.
3728 */
3729
3730
3731function getA11yRemovalMessage(selectionParameters) {
3732 var removedSelectedItem = selectionParameters.removedSelectedItem,
3733 itemToStringLocal = selectionParameters.itemToString;
3734 return itemToStringLocal(removedSelectedItem) + " has been removed.";
3735}
3736
3737var propTypes$2 = {
3738 selectedItems: PropTypes.array,
3739 initialSelectedItems: PropTypes.array,
3740 defaultSelectedItems: PropTypes.array,
3741 itemToString: PropTypes.func,
3742 getA11yRemovalMessage: PropTypes.func,
3743 stateReducer: PropTypes.func,
3744 activeIndex: PropTypes.number,
3745 initialActiveIndex: PropTypes.number,
3746 defaultActiveIndex: PropTypes.number,
3747 onActiveIndexChange: PropTypes.func,
3748 onSelectedItemsChange: PropTypes.func,
3749 keyNavigationNext: PropTypes.string,
3750 keyNavigationPrevious: PropTypes.string,
3751 environment: PropTypes.shape({
3752 addEventListener: PropTypes.func,
3753 removeEventListener: PropTypes.func,
3754 document: PropTypes.shape({
3755 getElementById: PropTypes.func,
3756 activeElement: PropTypes.any,
3757 body: PropTypes.any
3758 })
3759 })
3760};
3761var defaultProps$3 = {
3762 itemToString: defaultProps.itemToString,
3763 stateReducer: defaultProps.stateReducer,
3764 environment: defaultProps.environment,
3765 getA11yRemovalMessage: getA11yRemovalMessage,
3766 keyNavigationNext: 'ArrowRight',
3767 keyNavigationPrevious: 'ArrowLeft'
3768}; // eslint-disable-next-line import/no-mutable-exports
3769
3770var validatePropTypes$2 = noop;
3771/* istanbul ignore next */
3772
3773if (process.env.NODE_ENV !== 'production') {
3774 validatePropTypes$2 = function (options, caller) {
3775 PropTypes.checkPropTypes(propTypes$2, options, 'prop', caller.name);
3776 };
3777}
3778
3779var SelectedItemClick = process.env.NODE_ENV !== "production" ? '__selected_item_click__' : 0;
3780var SelectedItemKeyDownDelete = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_delete__' : 1;
3781var SelectedItemKeyDownBackspace = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_backspace__' : 2;
3782var SelectedItemKeyDownNavigationNext = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_navigation_next__' : 3;
3783var SelectedItemKeyDownNavigationPrevious = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_navigation_previous__' : 4;
3784var DropdownKeyDownNavigationPrevious = process.env.NODE_ENV !== "production" ? '__dropdown_keydown_navigation_previous__' : 5;
3785var DropdownKeyDownBackspace = process.env.NODE_ENV !== "production" ? '__dropdown_keydown_backspace__' : 6;
3786var DropdownClick = process.env.NODE_ENV !== "production" ? '__dropdown_click__' : 7;
3787var FunctionAddSelectedItem = process.env.NODE_ENV !== "production" ? '__function_add_selected_item__' : 8;
3788var FunctionRemoveSelectedItem = process.env.NODE_ENV !== "production" ? '__function_remove_selected_item__' : 9;
3789var FunctionSetSelectedItems = process.env.NODE_ENV !== "production" ? '__function_set_selected_items__' : 10;
3790var FunctionSetActiveIndex = process.env.NODE_ENV !== "production" ? '__function_set_active_index__' : 11;
3791var FunctionReset$1 = process.env.NODE_ENV !== "production" ? '__function_reset__' : 12;
3792
3793var stateChangeTypes$3 = /*#__PURE__*/Object.freeze({
3794 __proto__: null,
3795 SelectedItemClick: SelectedItemClick,
3796 SelectedItemKeyDownDelete: SelectedItemKeyDownDelete,
3797 SelectedItemKeyDownBackspace: SelectedItemKeyDownBackspace,
3798 SelectedItemKeyDownNavigationNext: SelectedItemKeyDownNavigationNext,
3799 SelectedItemKeyDownNavigationPrevious: SelectedItemKeyDownNavigationPrevious,
3800 DropdownKeyDownNavigationPrevious: DropdownKeyDownNavigationPrevious,
3801 DropdownKeyDownBackspace: DropdownKeyDownBackspace,
3802 DropdownClick: DropdownClick,
3803 FunctionAddSelectedItem: FunctionAddSelectedItem,
3804 FunctionRemoveSelectedItem: FunctionRemoveSelectedItem,
3805 FunctionSetSelectedItems: FunctionSetSelectedItems,
3806 FunctionSetActiveIndex: FunctionSetActiveIndex,
3807 FunctionReset: FunctionReset$1
3808});
3809
3810/* eslint-disable complexity */
3811
3812function downshiftMultipleSelectionReducer(state, action) {
3813 var type = action.type,
3814 index = action.index,
3815 props = action.props,
3816 selectedItem = action.selectedItem;
3817 var activeIndex = state.activeIndex,
3818 selectedItems = state.selectedItems;
3819 var changes;
3820
3821 switch (type) {
3822 case SelectedItemClick:
3823 changes = {
3824 activeIndex: index
3825 };
3826 break;
3827
3828 case SelectedItemKeyDownNavigationPrevious:
3829 changes = {
3830 activeIndex: activeIndex - 1 < 0 ? 0 : activeIndex - 1
3831 };
3832 break;
3833
3834 case SelectedItemKeyDownNavigationNext:
3835 changes = {
3836 activeIndex: activeIndex + 1 >= selectedItems.length ? -1 : activeIndex + 1
3837 };
3838 break;
3839
3840 case SelectedItemKeyDownBackspace:
3841 case SelectedItemKeyDownDelete:
3842 {
3843 var newActiveIndex = activeIndex;
3844
3845 if (selectedItems.length === 1) {
3846 newActiveIndex = -1;
3847 } else if (activeIndex === selectedItems.length - 1) {
3848 newActiveIndex = selectedItems.length - 2;
3849 }
3850
3851 changes = _extends({
3852 selectedItems: [].concat(selectedItems.slice(0, activeIndex), selectedItems.slice(activeIndex + 1))
3853 }, {
3854 activeIndex: newActiveIndex
3855 });
3856 break;
3857 }
3858
3859 case DropdownKeyDownNavigationPrevious:
3860 changes = {
3861 activeIndex: selectedItems.length - 1
3862 };
3863 break;
3864
3865 case DropdownKeyDownBackspace:
3866 changes = {
3867 selectedItems: selectedItems.slice(0, selectedItems.length - 1)
3868 };
3869 break;
3870
3871 case FunctionAddSelectedItem:
3872 changes = {
3873 selectedItems: [].concat(selectedItems, [selectedItem])
3874 };
3875 break;
3876
3877 case DropdownClick:
3878 changes = {
3879 activeIndex: -1
3880 };
3881 break;
3882
3883 case FunctionRemoveSelectedItem:
3884 {
3885 var _newActiveIndex = activeIndex;
3886 var selectedItemIndex = selectedItems.indexOf(selectedItem);
3887
3888 if (selectedItems.length === 1) {
3889 _newActiveIndex = -1;
3890 } else if (selectedItemIndex === selectedItems.length - 1) {
3891 _newActiveIndex = selectedItems.length - 2;
3892 }
3893
3894 changes = _extends({
3895 selectedItems: [].concat(selectedItems.slice(0, selectedItemIndex), selectedItems.slice(selectedItemIndex + 1))
3896 }, {
3897 activeIndex: _newActiveIndex
3898 });
3899 break;
3900 }
3901
3902 case FunctionSetSelectedItems:
3903 {
3904 var newSelectedItems = action.selectedItems;
3905 changes = {
3906 selectedItems: newSelectedItems
3907 };
3908 break;
3909 }
3910
3911 case FunctionSetActiveIndex:
3912 {
3913 var _newActiveIndex2 = action.activeIndex;
3914 changes = {
3915 activeIndex: _newActiveIndex2
3916 };
3917 break;
3918 }
3919
3920 case FunctionReset$1:
3921 changes = {
3922 activeIndex: getDefaultValue$1(props, 'activeIndex'),
3923 selectedItems: getDefaultValue$1(props, 'selectedItems')
3924 };
3925 break;
3926
3927 default:
3928 throw new Error('Reducer called without proper action type.');
3929 }
3930
3931 return _extends({}, state, changes);
3932}
3933
3934useMultipleSelection.stateChangeTypes = stateChangeTypes$3;
3935
3936function useMultipleSelection(userProps) {
3937 if (userProps === void 0) {
3938 userProps = {};
3939 }
3940
3941 validatePropTypes$2(userProps, useMultipleSelection); // Props defaults and destructuring.
3942
3943 var props = _extends({}, defaultProps$3, userProps);
3944
3945 var getA11yRemovalMessage = props.getA11yRemovalMessage,
3946 itemToString = props.itemToString,
3947 environment = props.environment,
3948 keyNavigationNext = props.keyNavigationNext,
3949 keyNavigationPrevious = props.keyNavigationPrevious; // Reducer init.
3950
3951 var _useControlledReducer = useControlledReducer(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
3952 state = _useControlledReducer[0],
3953 dispatch = _useControlledReducer[1];
3954
3955 var activeIndex = state.activeIndex,
3956 selectedItems = state.selectedItems; // Refs.
3957
3958 var isInitialMountRef = useRef(true);
3959 var dropdownRef = useRef(null);
3960 var previousSelectedItemsRef = useRef(selectedItems);
3961 var selectedItemRefs = useRef();
3962 selectedItemRefs.current = [];
3963 var latest = useLatestRef({
3964 state: state,
3965 props: props
3966 }); // Effects.
3967
3968 /* Sets a11y status message on changes in selectedItem. */
3969
3970 useEffect(function () {
3971 if (isInitialMountRef.current) {
3972 return;
3973 }
3974
3975 if (selectedItems.length < previousSelectedItemsRef.current.length) {
3976 var removedSelectedItem = previousSelectedItemsRef.current.find(function (item) {
3977 return selectedItems.indexOf(item) < 0;
3978 });
3979 setStatus(getA11yRemovalMessage({
3980 itemToString: itemToString,
3981 resultCount: selectedItems.length,
3982 removedSelectedItem: removedSelectedItem,
3983 activeIndex: activeIndex,
3984 activeSelectedItem: selectedItems[activeIndex]
3985 }), environment.document);
3986 }
3987
3988 previousSelectedItemsRef.current = selectedItems; // eslint-disable-next-line react-hooks/exhaustive-deps
3989 }, [selectedItems.length]); // Sets focus on active item.
3990
3991 useEffect(function () {
3992 if (isInitialMountRef.current) {
3993 return;
3994 }
3995
3996 if (activeIndex === -1 && dropdownRef.current) {
3997 dropdownRef.current.focus();
3998 } else if (selectedItemRefs.current[activeIndex]) {
3999 selectedItemRefs.current[activeIndex].focus();
4000 }
4001 }, [activeIndex]);
4002 useControlPropsValidator({
4003 isInitialMount: isInitialMountRef.current,
4004 props: props,
4005 state: state
4006 });
4007 var setGetterPropCallInfo = useGetterPropsCalledChecker('getDropdownProps'); // Make initial ref false.
4008
4009 useEffect(function () {
4010 isInitialMountRef.current = false;
4011 }, []); // Event handler functions.
4012
4013 var selectedItemKeyDownHandlers = useMemo(function () {
4014 var _ref;
4015
4016 return _ref = {}, _ref[keyNavigationPrevious] = function () {
4017 dispatch({
4018 type: SelectedItemKeyDownNavigationPrevious
4019 });
4020 }, _ref[keyNavigationNext] = function () {
4021 dispatch({
4022 type: SelectedItemKeyDownNavigationNext
4023 });
4024 }, _ref.Delete = function Delete() {
4025 dispatch({
4026 type: SelectedItemKeyDownDelete
4027 });
4028 }, _ref.Backspace = function Backspace() {
4029 dispatch({
4030 type: SelectedItemKeyDownBackspace
4031 });
4032 }, _ref;
4033 }, [dispatch, keyNavigationNext, keyNavigationPrevious]);
4034 var dropdownKeyDownHandlers = useMemo(function () {
4035 var _ref2;
4036
4037 return _ref2 = {}, _ref2[keyNavigationPrevious] = function (event) {
4038 if (isKeyDownOperationPermitted(event)) {
4039 dispatch({
4040 type: DropdownKeyDownNavigationPrevious
4041 });
4042 }
4043 }, _ref2.Backspace = function Backspace(event) {
4044 if (isKeyDownOperationPermitted(event)) {
4045 dispatch({
4046 type: DropdownKeyDownBackspace
4047 });
4048 }
4049 }, _ref2;
4050 }, [dispatch, keyNavigationPrevious]); // Getter props.
4051
4052 var getSelectedItemProps = useCallback(function (_temp) {
4053 var _extends2;
4054
4055 var _ref3 = _temp === void 0 ? {} : _temp,
4056 _ref3$refKey = _ref3.refKey,
4057 refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
4058 ref = _ref3.ref,
4059 onClick = _ref3.onClick,
4060 onKeyDown = _ref3.onKeyDown,
4061 selectedItem = _ref3.selectedItem,
4062 index = _ref3.index,
4063 rest = _objectWithoutPropertiesLoose(_ref3, ["refKey", "ref", "onClick", "onKeyDown", "selectedItem", "index"]);
4064
4065 var latestState = latest.current.state;
4066 var itemIndex = getItemIndex(index, selectedItem, latestState.selectedItems);
4067
4068 if (itemIndex < 0) {
4069 throw new Error('Pass either selectedItem or index in getSelectedItemProps!');
4070 }
4071
4072 return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (selectedItemNode) {
4073 if (selectedItemNode) {
4074 selectedItemRefs.current.push(selectedItemNode);
4075 }
4076 }), _extends2.tabIndex = index === latestState.activeIndex ? 0 : -1, _extends2.onClick = callAllEventHandlers(onClick, function selectedItemHandleClick() {
4077 dispatch({
4078 type: SelectedItemClick,
4079 index: index
4080 });
4081 }), _extends2.onKeyDown = callAllEventHandlers(onKeyDown, function selectedItemHandleKeyDown(event) {
4082 var key = normalizeArrowKey(event);
4083
4084 if (key && selectedItemKeyDownHandlers[key]) {
4085 selectedItemKeyDownHandlers[key](event);
4086 }
4087 }), _extends2), rest);
4088 }, [dispatch, latest, selectedItemKeyDownHandlers]);
4089 var getDropdownProps = useCallback(function (_temp2, _temp3) {
4090 var _extends3;
4091
4092 var _ref4 = _temp2 === void 0 ? {} : _temp2,
4093 _ref4$refKey = _ref4.refKey,
4094 refKey = _ref4$refKey === void 0 ? 'ref' : _ref4$refKey,
4095 ref = _ref4.ref,
4096 onKeyDown = _ref4.onKeyDown,
4097 onClick = _ref4.onClick,
4098 _ref4$preventKeyActio = _ref4.preventKeyAction,
4099 preventKeyAction = _ref4$preventKeyActio === void 0 ? false : _ref4$preventKeyActio,
4100 rest = _objectWithoutPropertiesLoose(_ref4, ["refKey", "ref", "onKeyDown", "onClick", "preventKeyAction"]);
4101
4102 var _ref5 = _temp3 === void 0 ? {} : _temp3,
4103 _ref5$suppressRefErro = _ref5.suppressRefError,
4104 suppressRefError = _ref5$suppressRefErro === void 0 ? false : _ref5$suppressRefErro;
4105
4106 setGetterPropCallInfo('getDropdownProps', suppressRefError, refKey, dropdownRef);
4107 return _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (dropdownNode) {
4108 if (dropdownNode) {
4109 dropdownRef.current = dropdownNode;
4110 }
4111 }), _extends3), !preventKeyAction && {
4112 onKeyDown: callAllEventHandlers(onKeyDown, function dropdownHandleKeyDown(event) {
4113 var key = normalizeArrowKey(event);
4114
4115 if (key && dropdownKeyDownHandlers[key]) {
4116 dropdownKeyDownHandlers[key](event);
4117 }
4118 }),
4119 onClick: callAllEventHandlers(onClick, function dropdownHandleClick() {
4120 dispatch({
4121 type: DropdownClick
4122 });
4123 })
4124 }, rest);
4125 }, [dispatch, dropdownKeyDownHandlers, setGetterPropCallInfo]); // returns
4126
4127 var addSelectedItem = useCallback(function (selectedItem) {
4128 dispatch({
4129 type: FunctionAddSelectedItem,
4130 selectedItem: selectedItem
4131 });
4132 }, [dispatch]);
4133 var removeSelectedItem = useCallback(function (selectedItem) {
4134 dispatch({
4135 type: FunctionRemoveSelectedItem,
4136 selectedItem: selectedItem
4137 });
4138 }, [dispatch]);
4139 var setSelectedItems = useCallback(function (newSelectedItems) {
4140 dispatch({
4141 type: FunctionSetSelectedItems,
4142 selectedItems: newSelectedItems
4143 });
4144 }, [dispatch]);
4145 var setActiveIndex = useCallback(function (newActiveIndex) {
4146 dispatch({
4147 type: FunctionSetActiveIndex,
4148 activeIndex: newActiveIndex
4149 });
4150 }, [dispatch]);
4151 var reset = useCallback(function () {
4152 dispatch({
4153 type: FunctionReset$1
4154 });
4155 }, [dispatch]);
4156 return {
4157 getSelectedItemProps: getSelectedItemProps,
4158 getDropdownProps: getDropdownProps,
4159 addSelectedItem: addSelectedItem,
4160 removeSelectedItem: removeSelectedItem,
4161 setSelectedItems: setSelectedItems,
4162 setActiveIndex: setActiveIndex,
4163 reset: reset,
4164 selectedItems: selectedItems,
4165 activeIndex: activeIndex
4166 };
4167}
4168
4169export default Downshift;
4170export { resetIdCounter, useCombobox, useMultipleSelection, useSelect };