UNPKG

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