UNPKG

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