UNPKG

81.3 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7var _objectWithoutPropertiesLoose = _interopDefault(require('@babel/runtime/helpers/objectWithoutPropertiesLoose'));
8var _extends = _interopDefault(require('@babel/runtime/helpers/extends'));
9var _assertThisInitialized = _interopDefault(require('@babel/runtime/helpers/assertThisInitialized'));
10var _inheritsLoose = _interopDefault(require('@babel/runtime/helpers/inheritsLoose'));
11var PropTypes = _interopDefault(require('prop-types'));
12var React = require('react');
13var React__default = _interopDefault(React);
14var reactIs = require('react-is');
15var computeScrollIntoView = _interopDefault(require('compute-scroll-into-view'));
16var keyboardKey = _interopDefault(require('keyboard-key'));
17var autoId = require('@reach/auto-id');
18
19var idCounter = 0;
20/**
21 * Accepts a parameter and returns it if it's a function
22 * or a noop function if it's not. This allows us to
23 * accept a callback, but not worry about it if it's not
24 * passed.
25 * @param {Function} cb the callback
26 * @return {Function} a function
27 */
28
29function cbToCb(cb) {
30 return typeof cb === 'function' ? cb : noop;
31}
32
33function noop() {}
34/**
35 * Scroll node into view if necessary
36 * @param {HTMLElement} node the element that should scroll into view
37 * @param {HTMLElement} menuNode the menu element of the component
38 */
39
40
41function scrollIntoView(node, menuNode) {
42 if (node === null) {
43 return;
44 }
45
46 var actions = computeScrollIntoView(node, {
47 boundary: menuNode,
48 block: 'nearest',
49 scrollMode: 'if-needed'
50 });
51 actions.forEach(function (_ref) {
52 var el = _ref.el,
53 top = _ref.top,
54 left = _ref.left;
55 el.scrollTop = top;
56 el.scrollLeft = left;
57 });
58}
59/**
60 * @param {HTMLElement} parent the parent node
61 * @param {HTMLElement} child the child node
62 * @return {Boolean} whether the parent is the child or the child is in the parent
63 */
64
65
66function isOrContainsNode(parent, child) {
67 return parent === child || parent.contains && parent.contains(child);
68}
69/**
70 * Simple debounce implementation. Will call the given
71 * function once after the time given has passed since
72 * it was last called.
73 * @param {Function} fn the function to call after the time
74 * @param {Number} time the time to wait
75 * @return {Function} the debounced function
76 */
77
78
79function debounce(fn, time) {
80 var timeoutId;
81
82 function cancel() {
83 if (timeoutId) {
84 clearTimeout(timeoutId);
85 }
86 }
87
88 function wrapper() {
89 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
90 args[_key] = arguments[_key];
91 }
92
93 cancel();
94 timeoutId = setTimeout(function () {
95 timeoutId = null;
96 fn.apply(void 0, args);
97 }, time);
98 }
99
100 wrapper.cancel = cancel;
101 return wrapper;
102}
103/**
104 * This is intended to be used to compose event handlers.
105 * They are executed in order until one of them sets
106 * `event.preventDownshiftDefault = true`.
107 * @param {...Function} fns the event handler functions
108 * @return {Function} the event handler to add to an element
109 */
110
111
112function callAllEventHandlers() {
113 for (var _len2 = arguments.length, fns = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
114 fns[_key2] = arguments[_key2];
115 }
116
117 return function (event) {
118 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
119 args[_key3 - 1] = arguments[_key3];
120 }
121
122 return fns.some(function (fn) {
123 if (fn) {
124 fn.apply(void 0, [event].concat(args));
125 }
126
127 return event.preventDownshiftDefault || event.hasOwnProperty('nativeEvent') && event.nativeEvent.preventDownshiftDefault;
128 });
129 };
130}
131/**
132 * This return a function that will call all the given functions with
133 * the arguments with which it's called. It does a null-check before
134 * attempting to call the functions and can take any number of functions.
135 * @param {...Function} fns the functions to call
136 * @return {Function} the function that calls all the functions
137 */
138
139
140function callAll() {
141 for (var _len4 = arguments.length, fns = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
142 fns[_key4] = arguments[_key4];
143 }
144
145 return function () {
146 for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
147 args[_key5] = arguments[_key5];
148 }
149
150 fns.forEach(function (fn) {
151 if (fn) {
152 fn.apply(void 0, args);
153 }
154 });
155 };
156}
157/**
158 * This generates a unique ID for an instance of Downshift
159 * @return {String} the unique ID
160 */
161
162
163function generateId() {
164 return String(idCounter++);
165}
166/**
167 * Resets idCounter to 0. Used for SSR.
168 */
169
170
171function resetIdCounter() {
172 idCounter = 0;
173}
174/**
175 * @param {Object} param the downshift state and other relevant properties
176 * @return {String} the a11y status message
177 */
178
179
180function getA11yStatusMessage(_ref2) {
181 var isOpen = _ref2.isOpen,
182 selectedItem = _ref2.selectedItem,
183 resultCount = _ref2.resultCount,
184 previousResultCount = _ref2.previousResultCount,
185 itemToString = _ref2.itemToString;
186
187 if (!isOpen) {
188 return selectedItem ? itemToString(selectedItem) : '';
189 }
190
191 if (!resultCount) {
192 return 'No results are available.';
193 }
194
195 if (resultCount !== previousResultCount) {
196 return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
197 }
198
199 return '';
200}
201/**
202 * Takes an argument and if it's an array, returns the first item in the array
203 * otherwise returns the argument
204 * @param {*} arg the maybe-array
205 * @param {*} defaultValue the value if arg is falsey not defined
206 * @return {*} the arg or it's first item
207 */
208
209
210function unwrapArray(arg, defaultValue) {
211 arg = Array.isArray(arg) ?
212 /* istanbul ignore next (preact) */
213 arg[0] : arg;
214
215 if (!arg && defaultValue) {
216 return defaultValue;
217 } else {
218 return arg;
219 }
220}
221/**
222 * @param {Object} element (P)react element
223 * @return {Boolean} whether it's a DOM element
224 */
225
226
227function isDOMElement(element) {
228 // then we assume this is react
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 return element.props;
239}
240/**
241 * Throws a helpful error message for required properties. Useful
242 * to be used as a default in destructuring or object params.
243 * @param {String} fnName the function name
244 * @param {String} propName the prop name
245 */
246
247
248function requiredProp(fnName, propName) {
249 // eslint-disable-next-line no-console
250 console.error("The property \"" + propName + "\" is required in \"" + fnName + "\"");
251}
252
253var stateKeys = ['highlightedIndex', 'inputValue', 'isOpen', 'selectedItem', 'type'];
254/**
255 * @param {Object} state the state object
256 * @return {Object} state that is relevant to downshift
257 */
258
259function pickState(state) {
260 if (state === void 0) {
261 state = {};
262 }
263
264 var result = {};
265 stateKeys.forEach(function (k) {
266 if (state.hasOwnProperty(k)) {
267 result[k] = state[k];
268 }
269 });
270 return result;
271}
272/**
273 * Normalizes the 'key' property of a KeyboardEvent in IE/Edge
274 * @param {Object} event a keyboardEvent object
275 * @return {String} keyboard key
276 */
277
278
279function normalizeArrowKey(event) {
280 var key = event.key,
281 keyCode = event.keyCode;
282 /* istanbul ignore next (ie) */
283
284 if (keyCode >= 37 && keyCode <= 40 && key.indexOf('Arrow') !== 0) {
285 return "Arrow" + key;
286 }
287
288 return key;
289}
290/**
291 * Simple check if the value passed is object literal
292 * @param {*} obj any things
293 * @return {Boolean} whether it's object literal
294 */
295
296
297function isPlainObject(obj) {
298 return Object.prototype.toString.call(obj) === '[object Object]';
299}
300/**
301 * Returns the new index in the list, in a circular way. If next value is out of bonds from the total,
302 * it will wrap to either 0 or itemCount - 1.
303 *
304 * @param {number} moveAmount Number of positions to move. Negative to move backwards, positive forwards.
305 * @param {number} baseIndex The initial position to move from.
306 * @param {number} itemCount The total number of items.
307 * @returns {number} The new index after the move.
308 */
309
310
311function getNextWrappingIndex(moveAmount, baseIndex, itemCount) {
312 var itemsLastIndex = itemCount - 1;
313
314 if (typeof baseIndex !== 'number' || baseIndex < 0 || baseIndex >= itemCount) {
315 baseIndex = moveAmount > 0 ? -1 : itemsLastIndex + 1;
316 }
317
318 var newIndex = baseIndex + moveAmount;
319
320 if (newIndex < 0) {
321 newIndex = itemsLastIndex;
322 } else if (newIndex > itemsLastIndex) {
323 newIndex = 0;
324 }
325
326 return newIndex;
327}
328
329var statusDiv;
330var cleanupStatus = debounce(function () {
331 getStatusDiv().textContent = '';
332}, 500);
333/**
334 * @param {String} status the status message
335 * @param {Object} documentProp document passed by the user.
336 */
337
338function setStatus(status, documentProp) {
339 var div = getStatusDiv(documentProp);
340
341 if (!status) {
342 return;
343 }
344
345 div.textContent = status;
346 cleanupStatus();
347}
348/**
349 * Get the status node or create it if it does not already exist.
350 * @param {Object} documentProp document passed by the user.
351 * @return {HTMLElement} the status node.
352 */
353
354
355function getStatusDiv(documentProp) {
356 if (documentProp === void 0) {
357 documentProp = document;
358 }
359
360 if (statusDiv) {
361 return statusDiv;
362 }
363
364 statusDiv = documentProp.createElement('div');
365 statusDiv.setAttribute('id', 'a11y-status-message');
366 statusDiv.setAttribute('role', 'status');
367 statusDiv.setAttribute('aria-live', 'polite');
368 statusDiv.setAttribute('aria-relevant', 'additions text');
369 Object.assign(statusDiv.style, {
370 border: '0',
371 clip: 'rect(0 0 0 0)',
372 height: '1px',
373 margin: '-1px',
374 overflow: 'hidden',
375 padding: '0',
376 position: 'absolute',
377 width: '1px'
378 });
379 documentProp.body.appendChild(statusDiv);
380 return statusDiv;
381}
382
383var unknown = process.env.NODE_ENV !== "production" ? '__autocomplete_unknown__' : 0;
384var mouseUp = process.env.NODE_ENV !== "production" ? '__autocomplete_mouseup__' : 1;
385var itemMouseEnter = process.env.NODE_ENV !== "production" ? '__autocomplete_item_mouseenter__' : 2;
386var keyDownArrowUp = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_arrow_up__' : 3;
387var keyDownArrowDown = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_arrow_down__' : 4;
388var keyDownEscape = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_escape__' : 5;
389var keyDownEnter = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_enter__' : 6;
390var keyDownHome = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_home__' : 7;
391var keyDownEnd = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_end__' : 8;
392var clickItem = process.env.NODE_ENV !== "production" ? '__autocomplete_click_item__' : 9;
393var blurInput = process.env.NODE_ENV !== "production" ? '__autocomplete_blur_input__' : 10;
394var changeInput = process.env.NODE_ENV !== "production" ? '__autocomplete_change_input__' : 11;
395var keyDownSpaceButton = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_space_button__' : 12;
396var clickButton = process.env.NODE_ENV !== "production" ? '__autocomplete_click_button__' : 13;
397var blurButton = process.env.NODE_ENV !== "production" ? '__autocomplete_blur_button__' : 14;
398var controlledPropUpdatedSelectedItem = process.env.NODE_ENV !== "production" ? '__autocomplete_controlled_prop_updated_selected_item__' : 15;
399var touchEnd = process.env.NODE_ENV !== "production" ? '__autocomplete_touchend__' : 16;
400
401var stateChangeTypes = /*#__PURE__*/Object.freeze({
402 unknown: unknown,
403 mouseUp: mouseUp,
404 itemMouseEnter: itemMouseEnter,
405 keyDownArrowUp: keyDownArrowUp,
406 keyDownArrowDown: keyDownArrowDown,
407 keyDownEscape: keyDownEscape,
408 keyDownEnter: keyDownEnter,
409 keyDownHome: keyDownHome,
410 keyDownEnd: keyDownEnd,
411 clickItem: clickItem,
412 blurInput: blurInput,
413 changeInput: changeInput,
414 keyDownSpaceButton: keyDownSpaceButton,
415 clickButton: clickButton,
416 blurButton: blurButton,
417 controlledPropUpdatedSelectedItem: controlledPropUpdatedSelectedItem,
418 touchEnd: touchEnd
419});
420
421var Downshift =
422/*#__PURE__*/
423function (_Component) {
424 _inheritsLoose(Downshift, _Component);
425
426 function Downshift(_props) {
427 var _this = _Component.call(this, _props) || this;
428
429 _this.id = _this.props.id || "downshift-" + generateId();
430 _this.menuId = _this.props.menuId || _this.id + "-menu";
431 _this.labelId = _this.props.labelId || _this.id + "-label";
432 _this.inputId = _this.props.inputId || _this.id + "-input";
433
434 _this.getItemId = _this.props.getItemId || function (index) {
435 return _this.id + "-item-" + index;
436 };
437
438 _this.input = null;
439 _this.items = [];
440 _this.itemCount = null;
441 _this.previousResultCount = 0;
442 _this.timeoutIds = [];
443
444 _this.internalSetTimeout = function (fn, time) {
445 var id = setTimeout(function () {
446 _this.timeoutIds = _this.timeoutIds.filter(function (i) {
447 return i !== id;
448 });
449 fn();
450 }, time);
451
452 _this.timeoutIds.push(id);
453 };
454
455 _this.setItemCount = function (count) {
456 _this.itemCount = count;
457 };
458
459 _this.unsetItemCount = function () {
460 _this.itemCount = null;
461 };
462
463 _this.setHighlightedIndex = function (highlightedIndex, otherStateToSet) {
464 if (highlightedIndex === void 0) {
465 highlightedIndex = _this.props.defaultHighlightedIndex;
466 }
467
468 if (otherStateToSet === void 0) {
469 otherStateToSet = {};
470 }
471
472 otherStateToSet = pickState(otherStateToSet);
473
474 _this.internalSetState(_extends({
475 highlightedIndex: highlightedIndex
476 }, otherStateToSet));
477 };
478
479 _this.clearSelection = function (cb) {
480 _this.internalSetState({
481 selectedItem: null,
482 inputValue: '',
483 highlightedIndex: _this.props.defaultHighlightedIndex,
484 isOpen: _this.props.defaultIsOpen
485 }, cb);
486 };
487
488 _this.selectItem = function (item, otherStateToSet, cb) {
489 otherStateToSet = pickState(otherStateToSet);
490
491 _this.internalSetState(_extends({
492 isOpen: _this.props.defaultIsOpen,
493 highlightedIndex: _this.props.defaultHighlightedIndex,
494 selectedItem: item,
495 inputValue: _this.props.itemToString(item)
496 }, otherStateToSet), cb);
497 };
498
499 _this.selectItemAtIndex = function (itemIndex, otherStateToSet, cb) {
500 var item = _this.items[itemIndex];
501
502 if (item == null) {
503 return;
504 }
505
506 _this.selectItem(item, otherStateToSet, cb);
507 };
508
509 _this.selectHighlightedItem = function (otherStateToSet, cb) {
510 return _this.selectItemAtIndex(_this.getState().highlightedIndex, otherStateToSet, cb);
511 };
512
513 _this.internalSetState = function (stateToSet, cb) {
514 var isItemSelected, onChangeArg;
515 var onStateChangeArg = {};
516 var isStateToSetFunction = typeof stateToSet === 'function'; // we want to call `onInputValueChange` before the `setState` call
517 // so someone controlling the `inputValue` state gets notified of
518 // the input change as soon as possible. This avoids issues with
519 // preserving the cursor position.
520 // See https://github.com/downshift-js/downshift/issues/217 for more info.
521
522 if (!isStateToSetFunction && stateToSet.hasOwnProperty('inputValue')) {
523 _this.props.onInputValueChange(stateToSet.inputValue, _extends({}, _this.getStateAndHelpers(), {}, stateToSet));
524 }
525
526 return _this.setState(function (state) {
527 state = _this.getState(state);
528 var newStateToSet = isStateToSetFunction ? stateToSet(state) : stateToSet; // Your own function that could modify the state that will be set.
529
530 newStateToSet = _this.props.stateReducer(state, newStateToSet); // checks if an item is selected, regardless of if it's different from
531 // what was selected before
532 // used to determine if onSelect and onChange callbacks should be called
533
534 isItemSelected = newStateToSet.hasOwnProperty('selectedItem'); // this keeps track of the object we want to call with setState
535
536 var nextState = {}; // this is just used to tell whether the state changed
537
538 var nextFullState = {}; // we need to call on change if the outside world is controlling any of our state
539 // and we're trying to update that state. OR if the selection has changed and we're
540 // trying to update the selection
541
542 if (isItemSelected && newStateToSet.selectedItem !== state.selectedItem) {
543 onChangeArg = newStateToSet.selectedItem;
544 }
545
546 newStateToSet.type = newStateToSet.type || unknown;
547 Object.keys(newStateToSet).forEach(function (key) {
548 // onStateChangeArg should only have the state that is
549 // actually changing
550 if (state[key] !== newStateToSet[key]) {
551 onStateChangeArg[key] = newStateToSet[key];
552 } // the type is useful for the onStateChangeArg
553 // but we don't actually want to set it in internal state.
554 // this is an undocumented feature for now... Not all internalSetState
555 // calls support it and I'm not certain we want them to yet.
556 // But it enables users controlling the isOpen state to know when
557 // the isOpen state changes due to mouseup events which is quite handy.
558
559
560 if (key === 'type') {
561 return;
562 }
563
564 nextFullState[key] = newStateToSet[key]; // if it's coming from props, then we don't care to set it internally
565
566 if (!_this.isControlledProp(key)) {
567 nextState[key] = newStateToSet[key];
568 }
569 }); // if stateToSet is a function, then we weren't able to call onInputValueChange
570 // earlier, so we'll call it now that we know what the inputValue state will be.
571
572 if (isStateToSetFunction && newStateToSet.hasOwnProperty('inputValue')) {
573 _this.props.onInputValueChange(newStateToSet.inputValue, _extends({}, _this.getStateAndHelpers(), {}, newStateToSet));
574 }
575
576 return nextState;
577 }, function () {
578 // call the provided callback if it's a function
579 cbToCb(cb)(); // only call the onStateChange and onChange callbacks if
580 // we have relevant information to pass them.
581
582 var hasMoreStateThanType = Object.keys(onStateChangeArg).length > 1;
583
584 if (hasMoreStateThanType) {
585 _this.props.onStateChange(onStateChangeArg, _this.getStateAndHelpers());
586 }
587
588 if (isItemSelected) {
589 _this.props.onSelect(stateToSet.selectedItem, _this.getStateAndHelpers());
590 }
591
592 if (onChangeArg !== undefined) {
593 _this.props.onChange(onChangeArg, _this.getStateAndHelpers());
594 } // this is currently undocumented and therefore subject to change
595 // We'll try to not break it, but just be warned.
596
597
598 _this.props.onUserAction(onStateChangeArg, _this.getStateAndHelpers());
599 });
600 };
601
602 _this.rootRef = function (node) {
603 return _this._rootNode = node;
604 };
605
606 _this.getRootProps = function (_temp, _temp2) {
607 var _extends2;
608
609 var _ref = _temp === void 0 ? {} : _temp,
610 _ref$refKey = _ref.refKey,
611 refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
612 ref = _ref.ref,
613 rest = _objectWithoutPropertiesLoose(_ref, ["refKey", "ref"]);
614
615 var _ref2 = _temp2 === void 0 ? {} : _temp2,
616 _ref2$suppressRefErro = _ref2.suppressRefError,
617 suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
618
619 // this is used in the render to know whether the user has called getRootProps.
620 // It uses that to know whether to apply the props automatically
621 _this.getRootProps.called = true;
622 _this.getRootProps.refKey = refKey;
623 _this.getRootProps.suppressRefError = suppressRefError;
624
625 var _this$getState = _this.getState(),
626 isOpen = _this$getState.isOpen;
627
628 return _extends((_extends2 = {}, _extends2[refKey] = callAll(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);
629 };
630
631 _this.keyDownHandlers = {
632 ArrowDown: function ArrowDown(event) {
633 var _this2 = this;
634
635 event.preventDefault();
636
637 if (this.getState().isOpen) {
638 var amount = event.shiftKey ? 5 : 1;
639 this.moveHighlightedIndex(amount, {
640 type: keyDownArrowDown
641 });
642 } else {
643 this.internalSetState({
644 isOpen: true,
645 type: keyDownArrowDown
646 }, function () {
647 var itemCount = _this2.getItemCount();
648
649 if (itemCount > 0) {
650 _this2.setHighlightedIndex(getNextWrappingIndex(1, _this2.getState().highlightedIndex, itemCount), {
651 type: keyDownArrowDown
652 });
653 }
654 });
655 }
656 },
657 ArrowUp: function ArrowUp(event) {
658 var _this3 = this;
659
660 event.preventDefault();
661
662 if (this.getState().isOpen) {
663 var amount = event.shiftKey ? -5 : -1;
664 this.moveHighlightedIndex(amount, {
665 type: keyDownArrowUp
666 });
667 } else {
668 this.internalSetState({
669 isOpen: true,
670 type: keyDownArrowUp
671 }, function () {
672 var itemCount = _this3.getItemCount();
673
674 if (itemCount > 0) {
675 _this3.setHighlightedIndex(getNextWrappingIndex(-1, _this3.getState().highlightedIndex, itemCount), {
676 type: keyDownArrowDown
677 });
678 }
679 });
680 }
681 },
682 Enter: function Enter(event) {
683 var _this$getState2 = this.getState(),
684 isOpen = _this$getState2.isOpen,
685 highlightedIndex = _this$getState2.highlightedIndex;
686
687 if (isOpen && highlightedIndex != null) {
688 event.preventDefault();
689 var item = this.items[highlightedIndex];
690 var itemNode = this.getItemNodeFromIndex(highlightedIndex);
691
692 if (item == null || itemNode && itemNode.hasAttribute('disabled')) {
693 return;
694 }
695
696 this.selectHighlightedItem({
697 type: keyDownEnter
698 });
699 }
700 },
701 Escape: function Escape(event) {
702 event.preventDefault();
703 this.reset({
704 type: keyDownEscape,
705 selectedItem: null,
706 inputValue: ''
707 });
708 }
709 };
710 _this.buttonKeyDownHandlers = _extends({}, _this.keyDownHandlers, {
711 ' ': function _(event) {
712 event.preventDefault();
713 this.toggleMenu({
714 type: keyDownSpaceButton
715 });
716 }
717 });
718 _this.inputKeyDownHandlers = _extends({}, _this.keyDownHandlers, {
719 Home: function Home(event) {
720 this.highlightFirstOrLastIndex(event, true, {
721 type: keyDownHome
722 });
723 },
724 End: function End(event) {
725 this.highlightFirstOrLastIndex(event, false, {
726 type: keyDownEnd
727 });
728 }
729 });
730
731 _this.getToggleButtonProps = function (_temp3) {
732 var _ref3 = _temp3 === void 0 ? {} : _temp3,
733 onClick = _ref3.onClick,
734 onPress = _ref3.onPress,
735 onKeyDown = _ref3.onKeyDown,
736 onKeyUp = _ref3.onKeyUp,
737 onBlur = _ref3.onBlur,
738 rest = _objectWithoutPropertiesLoose(_ref3, ["onClick", "onPress", "onKeyDown", "onKeyUp", "onBlur"]);
739
740 var _this$getState3 = _this.getState(),
741 isOpen = _this$getState3.isOpen;
742
743 var enabledEventHandlers = {
744 onClick: callAllEventHandlers(onClick, _this.buttonHandleClick),
745 onKeyDown: callAllEventHandlers(onKeyDown, _this.buttonHandleKeyDown),
746 onKeyUp: callAllEventHandlers(onKeyUp, _this.buttonHandleKeyUp),
747 onBlur: callAllEventHandlers(onBlur, _this.buttonHandleBlur)
748 };
749 var eventHandlers = rest.disabled ? {} : enabledEventHandlers;
750 return _extends({
751 type: 'button',
752 role: 'button',
753 'aria-label': isOpen ? 'close menu' : 'open menu',
754 'aria-haspopup': true,
755 'data-toggle': true
756 }, eventHandlers, {}, rest);
757 };
758
759 _this.buttonHandleKeyUp = function (event) {
760 // Prevent click event from emitting in Firefox
761 event.preventDefault();
762 };
763
764 _this.buttonHandleKeyDown = function (event) {
765 var key = normalizeArrowKey(event);
766
767 if (_this.buttonKeyDownHandlers[key]) {
768 _this.buttonKeyDownHandlers[key].call(_assertThisInitialized(_this), event);
769 }
770 };
771
772 _this.buttonHandleClick = function (event) {
773 event.preventDefault(); // handle odd case for Safari and Firefox which
774 // don't give the button the focus properly.
775
776 /* istanbul ignore if (can't reasonably test this) */
777
778 if ( _this.props.environment.document.activeElement === _this.props.environment.document.body) {
779 event.target.focus();
780 } // to simplify testing components that use downshift, we'll not wrap this in a setTimeout
781 // if the NODE_ENV is test. With the proper build system, this should be dead code eliminated
782 // when building for production and should therefore have no impact on production code.
783
784
785 if (process.env.NODE_ENV === 'test') {
786 _this.toggleMenu({
787 type: clickButton
788 });
789 } else {
790 // Ensure that toggle of menu occurs after the potential blur event in iOS
791 _this.internalSetTimeout(function () {
792 return _this.toggleMenu({
793 type: clickButton
794 });
795 });
796 }
797 };
798
799 _this.buttonHandleBlur = function (event) {
800 var blurTarget = event.target; // Save blur target for comparison with activeElement later
801 // Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not body element
802
803 _this.internalSetTimeout(function () {
804 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)
805 ) {
806 _this.reset({
807 type: blurButton
808 });
809 }
810 });
811 };
812
813 _this.getLabelProps = function (props) {
814 return _extends({
815 htmlFor: _this.inputId,
816 id: _this.labelId
817 }, props);
818 };
819
820 _this.getInputProps = function (_temp4) {
821 var _ref4 = _temp4 === void 0 ? {} : _temp4,
822 onKeyDown = _ref4.onKeyDown,
823 onBlur = _ref4.onBlur,
824 onChange = _ref4.onChange,
825 onInput = _ref4.onInput,
826 onChangeText = _ref4.onChangeText,
827 rest = _objectWithoutPropertiesLoose(_ref4, ["onKeyDown", "onBlur", "onChange", "onInput", "onChangeText"]);
828
829 var onChangeKey;
830 var eventHandlers = {};
831 /* istanbul ignore next (preact) */
832
833 onChangeKey = 'onChange';
834
835 var _this$getState4 = _this.getState(),
836 inputValue = _this$getState4.inputValue,
837 isOpen = _this$getState4.isOpen,
838 highlightedIndex = _this$getState4.highlightedIndex;
839
840 if (!rest.disabled) {
841 var _eventHandlers;
842
843 eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, _this.inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, _this.inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, _this.inputHandleBlur), _eventHandlers);
844 }
845 /* istanbul ignore if (react-native) */
846
847
848 return _extends({
849 'aria-autocomplete': 'list',
850 'aria-activedescendant': isOpen && typeof highlightedIndex === 'number' && highlightedIndex >= 0 ? _this.getItemId(highlightedIndex) : null,
851 'aria-controls': isOpen ? _this.menuId : null,
852 'aria-labelledby': _this.labelId,
853 // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
854 // revert back since autocomplete="nope" is ignored on latest Chrome and Opera
855 autoComplete: 'off',
856 value: inputValue,
857 id: _this.inputId
858 }, eventHandlers, {}, rest);
859 };
860
861 _this.inputHandleKeyDown = function (event) {
862 var key = normalizeArrowKey(event);
863
864 if (key && _this.inputKeyDownHandlers[key]) {
865 _this.inputKeyDownHandlers[key].call(_assertThisInitialized(_this), event);
866 }
867 };
868
869 _this.inputHandleChange = function (event) {
870 _this.internalSetState({
871 type: changeInput,
872 isOpen: true,
873 inputValue: event.target.value,
874 highlightedIndex: _this.props.defaultHighlightedIndex
875 });
876 };
877
878 _this.inputHandleBlur = function () {
879 // Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not the body element
880 _this.internalSetTimeout(function () {
881 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);
882
883 if (!_this.isMouseDown && !downshiftButtonIsActive) {
884 _this.reset({
885 type: blurInput
886 });
887 }
888 });
889 };
890
891 _this.menuRef = function (node) {
892 _this._menuNode = node;
893 };
894
895 _this.getMenuProps = function (_temp5, _temp6) {
896 var _extends3;
897
898 var _ref5 = _temp5 === void 0 ? {} : _temp5,
899 _ref5$refKey = _ref5.refKey,
900 refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
901 ref = _ref5.ref,
902 props = _objectWithoutPropertiesLoose(_ref5, ["refKey", "ref"]);
903
904 var _ref6 = _temp6 === void 0 ? {} : _temp6,
905 _ref6$suppressRefErro = _ref6.suppressRefError,
906 suppressRefError = _ref6$suppressRefErro === void 0 ? false : _ref6$suppressRefErro;
907
908 _this.getMenuProps.called = true;
909 _this.getMenuProps.refKey = refKey;
910 _this.getMenuProps.suppressRefError = suppressRefError;
911 return _extends((_extends3 = {}, _extends3[refKey] = callAll(ref, _this.menuRef), _extends3.role = 'listbox', _extends3['aria-labelledby'] = props && props['aria-label'] ? null : _this.labelId, _extends3.id = _this.menuId, _extends3), props);
912 };
913
914 _this.getItemProps = function (_temp7) {
915 var _enabledEventHandlers;
916
917 var _ref7 = _temp7 === void 0 ? {} : _temp7,
918 onMouseMove = _ref7.onMouseMove,
919 onMouseDown = _ref7.onMouseDown,
920 onClick = _ref7.onClick,
921 onPress = _ref7.onPress,
922 index = _ref7.index,
923 _ref7$item = _ref7.item,
924 item = _ref7$item === void 0 ? process.env.NODE_ENV === 'production' ?
925 /* istanbul ignore next */
926 undefined : requiredProp('getItemProps', 'item') : _ref7$item,
927 rest = _objectWithoutPropertiesLoose(_ref7, ["onMouseMove", "onMouseDown", "onClick", "onPress", "index", "item"]);
928
929 if (index === undefined) {
930 _this.items.push(item);
931
932 index = _this.items.indexOf(item);
933 } else {
934 _this.items[index] = item;
935 }
936
937 var onSelectKey = 'onClick';
938 var customClickHandler = onClick;
939 var enabledEventHandlers = (_enabledEventHandlers = {
940 // onMouseMove is used over onMouseEnter here. onMouseMove
941 // is only triggered on actual mouse movement while onMouseEnter
942 // can fire on DOM changes, interrupting keyboard navigation
943 onMouseMove: callAllEventHandlers(onMouseMove, function () {
944 if (index === _this.getState().highlightedIndex) {
945 return;
946 }
947
948 _this.setHighlightedIndex(index, {
949 type: itemMouseEnter
950 }); // We never want to manually scroll when changing state based
951 // on `onMouseMove` because we will be moving the element out
952 // from under the user which is currently scrolling/moving the
953 // cursor
954
955
956 _this.avoidScrolling = true;
957
958 _this.internalSetTimeout(function () {
959 return _this.avoidScrolling = false;
960 }, 250);
961 }),
962 onMouseDown: callAllEventHandlers(onMouseDown, function (event) {
963 // This prevents the activeElement from being changed
964 // to the item so it can remain with the current activeElement
965 // which is a more common use case.
966 event.preventDefault();
967 })
968 }, _enabledEventHandlers[onSelectKey] = callAllEventHandlers(customClickHandler, function () {
969 _this.selectItemAtIndex(index, {
970 type: clickItem
971 });
972 }), _enabledEventHandlers); // Passing down the onMouseDown handler to prevent redirect
973 // of the activeElement if clicking on disabled items
974
975 var eventHandlers = rest.disabled ? {
976 onMouseDown: enabledEventHandlers.onMouseDown
977 } : enabledEventHandlers;
978 return _extends({
979 id: _this.getItemId(index),
980 role: 'option',
981 'aria-selected': _this.getState().highlightedIndex === index
982 }, eventHandlers, {}, rest);
983 };
984
985 _this.clearItems = function () {
986 _this.items = [];
987 };
988
989 _this.reset = function (otherStateToSet, cb) {
990 if (otherStateToSet === void 0) {
991 otherStateToSet = {};
992 }
993
994 otherStateToSet = pickState(otherStateToSet);
995
996 _this.internalSetState(function (_ref8) {
997 var selectedItem = _ref8.selectedItem;
998 return _extends({
999 isOpen: _this.props.defaultIsOpen,
1000 highlightedIndex: _this.props.defaultHighlightedIndex,
1001 inputValue: _this.props.itemToString(selectedItem)
1002 }, otherStateToSet);
1003 }, cb);
1004 };
1005
1006 _this.toggleMenu = function (otherStateToSet, cb) {
1007 if (otherStateToSet === void 0) {
1008 otherStateToSet = {};
1009 }
1010
1011 otherStateToSet = pickState(otherStateToSet);
1012
1013 _this.internalSetState(function (_ref9) {
1014 var isOpen = _ref9.isOpen;
1015 return _extends({
1016 isOpen: !isOpen
1017 }, isOpen && {
1018 highlightedIndex: _this.props.defaultHighlightedIndex
1019 }, {}, otherStateToSet);
1020 }, function () {
1021 var _this$getState5 = _this.getState(),
1022 isOpen = _this$getState5.isOpen,
1023 highlightedIndex = _this$getState5.highlightedIndex;
1024
1025 if (isOpen) {
1026 if (_this.getItemCount() > 0 && typeof highlightedIndex === 'number') {
1027 _this.setHighlightedIndex(highlightedIndex, otherStateToSet);
1028 }
1029 }
1030
1031 cbToCb(cb)();
1032 });
1033 };
1034
1035 _this.openMenu = function (cb) {
1036 _this.internalSetState({
1037 isOpen: true
1038 }, cb);
1039 };
1040
1041 _this.closeMenu = function (cb) {
1042 _this.internalSetState({
1043 isOpen: false
1044 }, cb);
1045 };
1046
1047 _this.updateStatus = debounce(function () {
1048 var state = _this.getState();
1049
1050 var item = _this.items[state.highlightedIndex];
1051
1052 var resultCount = _this.getItemCount();
1053
1054 var status = _this.props.getA11yStatusMessage(_extends({
1055 itemToString: _this.props.itemToString,
1056 previousResultCount: _this.previousResultCount,
1057 resultCount: resultCount,
1058 highlightedItem: item
1059 }, state));
1060
1061 _this.previousResultCount = resultCount;
1062 setStatus(status, _this.props.environment.document);
1063 }, 200);
1064
1065 // fancy destructuring + defaults + aliases
1066 // this basically says each value of state should either be set to
1067 // the initial value or the default value if the initial value is not provided
1068 var _this$props = _this.props,
1069 defaultHighlightedIndex = _this$props.defaultHighlightedIndex,
1070 _this$props$initialHi = _this$props.initialHighlightedIndex,
1071 _highlightedIndex = _this$props$initialHi === void 0 ? defaultHighlightedIndex : _this$props$initialHi,
1072 defaultIsOpen = _this$props.defaultIsOpen,
1073 _this$props$initialIs = _this$props.initialIsOpen,
1074 _isOpen = _this$props$initialIs === void 0 ? defaultIsOpen : _this$props$initialIs,
1075 _this$props$initialIn = _this$props.initialInputValue,
1076 _inputValue = _this$props$initialIn === void 0 ? '' : _this$props$initialIn,
1077 _this$props$initialSe = _this$props.initialSelectedItem,
1078 _selectedItem = _this$props$initialSe === void 0 ? null : _this$props$initialSe;
1079
1080 var _state = _this.getState({
1081 highlightedIndex: _highlightedIndex,
1082 isOpen: _isOpen,
1083 inputValue: _inputValue,
1084 selectedItem: _selectedItem
1085 });
1086
1087 if (_state.selectedItem != null && _this.props.initialInputValue === undefined) {
1088 _state.inputValue = _this.props.itemToString(_state.selectedItem);
1089 }
1090
1091 _this.state = _state;
1092 return _this;
1093 }
1094
1095 var _proto = Downshift.prototype;
1096
1097 /**
1098 * Clear all running timeouts
1099 */
1100 _proto.internalClearTimeouts = function internalClearTimeouts() {
1101 this.timeoutIds.forEach(function (id) {
1102 clearTimeout(id);
1103 });
1104 this.timeoutIds = [];
1105 }
1106 /**
1107 * Gets the state based on internal state or props
1108 * If a state value is passed via props, then that
1109 * is the value given, otherwise it's retrieved from
1110 * stateToMerge
1111 *
1112 * This will perform a shallow merge of the given state object
1113 * with the state coming from props
1114 * (for the controlled component scenario)
1115 * This is used in state updater functions so they're referencing
1116 * the right state regardless of where it comes from.
1117 *
1118 * @param {Object} stateToMerge defaults to this.state
1119 * @return {Object} the state
1120 */
1121 ;
1122
1123 _proto.getState = function getState(stateToMerge) {
1124 var _this4 = this;
1125
1126 if (stateToMerge === void 0) {
1127 stateToMerge = this.state;
1128 }
1129
1130 return Object.keys(stateToMerge).reduce(function (state, key) {
1131 state[key] = _this4.isControlledProp(key) ? _this4.props[key] : stateToMerge[key];
1132 return state;
1133 }, {});
1134 }
1135 /**
1136 * This determines whether a prop is a "controlled prop" meaning it is
1137 * state which is controlled by the outside of this component rather
1138 * than within this component.
1139 * @param {String} key the key to check
1140 * @return {Boolean} whether it is a controlled controlled prop
1141 */
1142 ;
1143
1144 _proto.isControlledProp = function isControlledProp(key) {
1145 return this.props[key] !== undefined;
1146 };
1147
1148 _proto.getItemCount = function getItemCount() {
1149 // things read better this way. They're in priority order:
1150 // 1. `this.itemCount`
1151 // 2. `this.props.itemCount`
1152 // 3. `this.items.length`
1153 var itemCount = this.items.length;
1154
1155 if (this.itemCount != null) {
1156 itemCount = this.itemCount;
1157 } else if (this.props.itemCount !== undefined) {
1158 itemCount = this.props.itemCount;
1159 }
1160
1161 return itemCount;
1162 };
1163
1164 _proto.getItemNodeFromIndex = function getItemNodeFromIndex(index) {
1165 return this.props.environment.document.getElementById(this.getItemId(index));
1166 };
1167
1168 _proto.scrollHighlightedItemIntoView = function scrollHighlightedItemIntoView() {
1169 /* istanbul ignore else (react-native) */
1170 {
1171 var node = this.getItemNodeFromIndex(this.getState().highlightedIndex);
1172 this.props.scrollIntoView(node, this._menuNode);
1173 }
1174 };
1175
1176 _proto.moveHighlightedIndex = function moveHighlightedIndex(amount, otherStateToSet) {
1177 var itemCount = this.getItemCount();
1178
1179 if (itemCount > 0) {
1180 var nextHighlightedIndex = getNextWrappingIndex(amount, this.getState().highlightedIndex, itemCount);
1181 this.setHighlightedIndex(nextHighlightedIndex, otherStateToSet);
1182 }
1183 };
1184
1185 _proto.highlightFirstOrLastIndex = function highlightFirstOrLastIndex(event, first, otherStateToSet) {
1186 var itemsLastIndex = this.getItemCount() - 1;
1187
1188 if (itemsLastIndex < 0 || !this.getState().isOpen) {
1189 return;
1190 }
1191
1192 event.preventDefault();
1193 this.setHighlightedIndex(first ? 0 : itemsLastIndex, otherStateToSet);
1194 };
1195
1196 _proto.getStateAndHelpers = function getStateAndHelpers() {
1197 var _this$getState6 = this.getState(),
1198 highlightedIndex = _this$getState6.highlightedIndex,
1199 inputValue = _this$getState6.inputValue,
1200 selectedItem = _this$getState6.selectedItem,
1201 isOpen = _this$getState6.isOpen;
1202
1203 var itemToString = this.props.itemToString;
1204 var id = this.id;
1205 var getRootProps = this.getRootProps,
1206 getToggleButtonProps = this.getToggleButtonProps,
1207 getLabelProps = this.getLabelProps,
1208 getMenuProps = this.getMenuProps,
1209 getInputProps = this.getInputProps,
1210 getItemProps = this.getItemProps,
1211 openMenu = this.openMenu,
1212 closeMenu = this.closeMenu,
1213 toggleMenu = this.toggleMenu,
1214 selectItem = this.selectItem,
1215 selectItemAtIndex = this.selectItemAtIndex,
1216 selectHighlightedItem = this.selectHighlightedItem,
1217 setHighlightedIndex = this.setHighlightedIndex,
1218 clearSelection = this.clearSelection,
1219 clearItems = this.clearItems,
1220 reset = this.reset,
1221 setItemCount = this.setItemCount,
1222 unsetItemCount = this.unsetItemCount,
1223 setState = this.internalSetState;
1224 return {
1225 // prop getters
1226 getRootProps: getRootProps,
1227 getToggleButtonProps: getToggleButtonProps,
1228 getLabelProps: getLabelProps,
1229 getMenuProps: getMenuProps,
1230 getInputProps: getInputProps,
1231 getItemProps: getItemProps,
1232 // actions
1233 reset: reset,
1234 openMenu: openMenu,
1235 closeMenu: closeMenu,
1236 toggleMenu: toggleMenu,
1237 selectItem: selectItem,
1238 selectItemAtIndex: selectItemAtIndex,
1239 selectHighlightedItem: selectHighlightedItem,
1240 setHighlightedIndex: setHighlightedIndex,
1241 clearSelection: clearSelection,
1242 clearItems: clearItems,
1243 setItemCount: setItemCount,
1244 unsetItemCount: unsetItemCount,
1245 setState: setState,
1246 // props
1247 itemToString: itemToString,
1248 // derived
1249 id: id,
1250 // state
1251 highlightedIndex: highlightedIndex,
1252 inputValue: inputValue,
1253 isOpen: isOpen,
1254 selectedItem: selectedItem
1255 };
1256 } //////////////////////////// ROOT
1257 ;
1258
1259 _proto.componentDidMount = function componentDidMount() {
1260 var _this5 = this;
1261
1262 /* istanbul ignore if (react-native) */
1263 if (process.env.NODE_ENV !== 'production' && !false && this.getMenuProps.called && !this.getMenuProps.suppressRefError) {
1264 validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps);
1265 }
1266 /* istanbul ignore if (react-native) */
1267
1268
1269 {
1270 var targetWithinDownshift = function (target, checkActiveElement) {
1271 if (checkActiveElement === void 0) {
1272 checkActiveElement = true;
1273 }
1274
1275 var document = _this5.props.environment.document;
1276 return [_this5._rootNode, _this5._menuNode].some(function (contextNode) {
1277 return contextNode && (isOrContainsNode(contextNode, target) || checkActiveElement && isOrContainsNode(contextNode, document.activeElement));
1278 });
1279 }; // this.isMouseDown helps us track whether the mouse is currently held down.
1280 // This is useful when the user clicks on an item in the list, but holds the mouse
1281 // down long enough for the list to disappear (because the blur event fires on the input)
1282 // this.isMouseDown is used in the blur handler on the input to determine whether the blur event should
1283 // trigger hiding the menu.
1284
1285
1286 var onMouseDown = function () {
1287 _this5.isMouseDown = true;
1288 };
1289
1290 var onMouseUp = function (event) {
1291 _this5.isMouseDown = false; // if the target element or the activeElement is within a downshift node
1292 // then we don't want to reset downshift
1293
1294 var contextWithinDownshift = targetWithinDownshift(event.target);
1295
1296 if (!contextWithinDownshift && _this5.getState().isOpen) {
1297 _this5.reset({
1298 type: mouseUp
1299 }, function () {
1300 return _this5.props.onOuterClick(_this5.getStateAndHelpers());
1301 });
1302 }
1303 }; // Touching an element in iOS gives focus and hover states, but touching out of
1304 // the element will remove hover, and persist the focus state, resulting in the
1305 // blur event not being triggered.
1306 // this.isTouchMove helps us track whether the user is tapping or swiping on a touch screen.
1307 // If the user taps outside of Downshift, the component should be reset,
1308 // but not if the user is swiping
1309
1310
1311 var onTouchStart = function () {
1312 _this5.isTouchMove = false;
1313 };
1314
1315 var onTouchMove = function () {
1316 _this5.isTouchMove = true;
1317 };
1318
1319 var onTouchEnd = function (event) {
1320 var contextWithinDownshift = targetWithinDownshift(event.target, false);
1321
1322 if (!_this5.isTouchMove && !contextWithinDownshift && _this5.getState().isOpen) {
1323 _this5.reset({
1324 type: touchEnd
1325 }, function () {
1326 return _this5.props.onOuterClick(_this5.getStateAndHelpers());
1327 });
1328 }
1329 };
1330
1331 var environment = this.props.environment;
1332 environment.addEventListener('mousedown', onMouseDown);
1333 environment.addEventListener('mouseup', onMouseUp);
1334 environment.addEventListener('touchstart', onTouchStart);
1335 environment.addEventListener('touchmove', onTouchMove);
1336 environment.addEventListener('touchend', onTouchEnd);
1337
1338 this.cleanup = function () {
1339 _this5.internalClearTimeouts();
1340
1341 _this5.updateStatus.cancel();
1342
1343 environment.removeEventListener('mousedown', onMouseDown);
1344 environment.removeEventListener('mouseup', onMouseUp);
1345 environment.removeEventListener('touchstart', onTouchStart);
1346 environment.removeEventListener('touchmove', onTouchMove);
1347 environment.removeEventListener('touchend', onTouchEnd);
1348 };
1349 }
1350 };
1351
1352 _proto.shouldScroll = function shouldScroll(prevState, prevProps) {
1353 var _ref10 = this.props.highlightedIndex === undefined ? this.getState() : this.props,
1354 currentHighlightedIndex = _ref10.highlightedIndex;
1355
1356 var _ref11 = prevProps.highlightedIndex === undefined ? prevState : prevProps,
1357 prevHighlightedIndex = _ref11.highlightedIndex;
1358
1359 var scrollWhenOpen = currentHighlightedIndex && this.getState().isOpen && !prevState.isOpen;
1360 return scrollWhenOpen || currentHighlightedIndex !== prevHighlightedIndex;
1361 };
1362
1363 _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
1364 if (process.env.NODE_ENV !== 'production') {
1365 validateControlledUnchanged(prevProps, this.props);
1366 /* istanbul ignore if (react-native) */
1367
1368 if ( this.getMenuProps.called && !this.getMenuProps.suppressRefError) {
1369 validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps);
1370 }
1371 }
1372
1373 if (this.isControlledProp('selectedItem') && this.props.selectedItemChanged(prevProps.selectedItem, this.props.selectedItem)) {
1374 this.internalSetState({
1375 type: controlledPropUpdatedSelectedItem,
1376 inputValue: this.props.itemToString(this.props.selectedItem)
1377 });
1378 }
1379
1380 if (!this.avoidScrolling && this.shouldScroll(prevState, prevProps)) {
1381 this.scrollHighlightedItemIntoView();
1382 }
1383 /* istanbul ignore else (react-native) */
1384
1385
1386 this.updateStatus();
1387 };
1388
1389 _proto.componentWillUnmount = function componentWillUnmount() {
1390 this.cleanup(); // avoids memory leak
1391 };
1392
1393 _proto.render = function render() {
1394 var children = unwrapArray(this.props.children, noop); // because the items are rerendered every time we call the children
1395 // we clear this out each render and it will be populated again as
1396 // getItemProps is called.
1397
1398 this.clearItems(); // we reset this so we know whether the user calls getRootProps during
1399 // this render. If they do then we don't need to do anything,
1400 // if they don't then we need to clone the element they return and
1401 // apply the props for them.
1402
1403 this.getRootProps.called = false;
1404 this.getRootProps.refKey = undefined;
1405 this.getRootProps.suppressRefError = undefined; // we do something similar for getMenuProps
1406
1407 this.getMenuProps.called = false;
1408 this.getMenuProps.refKey = undefined;
1409 this.getMenuProps.suppressRefError = undefined; // we do something similar for getLabelProps
1410
1411 this.getLabelProps.called = false; // and something similar for getInputProps
1412
1413 this.getInputProps.called = false;
1414 var element = unwrapArray(children(this.getStateAndHelpers()));
1415
1416 if (!element) {
1417 return null;
1418 }
1419
1420 if (this.getRootProps.called || this.props.suppressRefError) {
1421 if (process.env.NODE_ENV !== 'production' && !this.getRootProps.suppressRefError && !this.props.suppressRefError) {
1422 validateGetRootPropsCalledCorrectly(element, this.getRootProps);
1423 }
1424
1425 return element;
1426 } else if (isDOMElement(element)) {
1427 // they didn't apply the root props, but we can clone
1428 // this and apply the props ourselves
1429 return React__default.cloneElement(element, this.getRootProps(getElementProps(element)));
1430 }
1431 /* istanbul ignore else */
1432
1433
1434 if (process.env.NODE_ENV !== 'production') {
1435 // they didn't apply the root props, but they need to
1436 // otherwise we can't query around the autocomplete
1437 throw new Error('downshift: If you return a non-DOM element, you must use apply the getRootProps function');
1438 }
1439 /* istanbul ignore next */
1440
1441
1442 return undefined;
1443 };
1444
1445 return Downshift;
1446}(React.Component);
1447
1448Downshift.defaultProps = {
1449 defaultHighlightedIndex: null,
1450 defaultIsOpen: false,
1451 getA11yStatusMessage: getA11yStatusMessage,
1452 itemToString: function itemToString(i) {
1453 if (i == null) {
1454 return '';
1455 }
1456
1457 if (process.env.NODE_ENV !== 'production' && isPlainObject(i) && !i.hasOwnProperty('toString')) {
1458 // eslint-disable-next-line no-console
1459 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);
1460 }
1461
1462 return String(i);
1463 },
1464 onStateChange: noop,
1465 onInputValueChange: noop,
1466 onUserAction: noop,
1467 onChange: noop,
1468 onSelect: noop,
1469 onOuterClick: noop,
1470 selectedItemChanged: function selectedItemChanged(prevItem, item) {
1471 return prevItem !== item;
1472 },
1473 environment: typeof window === 'undefined'
1474 /* istanbul ignore next (ssr) */
1475 ? {} : window,
1476 stateReducer: function stateReducer(state, stateToSet) {
1477 return stateToSet;
1478 },
1479 suppressRefError: false,
1480 scrollIntoView: scrollIntoView
1481};
1482Downshift.stateChangeTypes = stateChangeTypes;
1483process.env.NODE_ENV !== "production" ? Downshift.propTypes = {
1484 children: PropTypes.func,
1485 defaultHighlightedIndex: PropTypes.number,
1486 defaultIsOpen: PropTypes.bool,
1487 initialHighlightedIndex: PropTypes.number,
1488 initialSelectedItem: PropTypes.any,
1489 initialInputValue: PropTypes.string,
1490 initialIsOpen: PropTypes.bool,
1491 getA11yStatusMessage: PropTypes.func,
1492 itemToString: PropTypes.func,
1493 onChange: PropTypes.func,
1494 onSelect: PropTypes.func,
1495 onStateChange: PropTypes.func,
1496 onInputValueChange: PropTypes.func,
1497 onUserAction: PropTypes.func,
1498 onOuterClick: PropTypes.func,
1499 selectedItemChanged: PropTypes.func,
1500 stateReducer: PropTypes.func,
1501 itemCount: PropTypes.number,
1502 id: PropTypes.string,
1503 environment: PropTypes.shape({
1504 addEventListener: PropTypes.func,
1505 removeEventListener: PropTypes.func,
1506 document: PropTypes.shape({
1507 getElementById: PropTypes.func,
1508 activeElement: PropTypes.any,
1509 body: PropTypes.any
1510 })
1511 }),
1512 suppressRefError: PropTypes.bool,
1513 scrollIntoView: PropTypes.func,
1514 // things we keep in state for uncontrolled components
1515 // but can accept as props for controlled components
1516
1517 /* eslint-disable react/no-unused-prop-types */
1518 selectedItem: PropTypes.any,
1519 isOpen: PropTypes.bool,
1520 inputValue: PropTypes.string,
1521 highlightedIndex: PropTypes.number,
1522 labelId: PropTypes.string,
1523 inputId: PropTypes.string,
1524 menuId: PropTypes.string,
1525 getItemId: PropTypes.func
1526 /* eslint-enable react/no-unused-prop-types */
1527
1528} : void 0;
1529
1530function validateGetMenuPropsCalledCorrectly(node, _ref12) {
1531 var refKey = _ref12.refKey;
1532
1533 if (!node) {
1534 // eslint-disable-next-line no-console
1535 console.error("downshift: The ref prop \"" + refKey + "\" from getMenuProps was not applied correctly on your menu element.");
1536 }
1537}
1538
1539function validateGetRootPropsCalledCorrectly(element, _ref13) {
1540 var refKey = _ref13.refKey;
1541 var refKeySpecified = refKey !== 'ref';
1542 var isComposite = !isDOMElement(element);
1543
1544 if (isComposite && !refKeySpecified && !reactIs.isForwardRef(element)) {
1545 // eslint-disable-next-line no-console
1546 console.error('downshift: You returned a non-DOM element. You must specify a refKey in getRootProps');
1547 } else if (!isComposite && refKeySpecified) {
1548 // eslint-disable-next-line no-console
1549 console.error("downshift: You returned a DOM element. You should not specify a refKey in getRootProps. You specified \"" + refKey + "\"");
1550 }
1551
1552 if (!reactIs.isForwardRef(element) && !getElementProps(element)[refKey]) {
1553 // eslint-disable-next-line no-console
1554 console.error("downshift: You must apply the ref prop \"" + refKey + "\" from getRootProps onto your root element.");
1555 }
1556}
1557
1558function validateControlledUnchanged(prevProps, nextProps) {
1559 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";
1560 ['selectedItem', 'isOpen', 'inputValue', 'highlightedIndex'].forEach(function (propKey) {
1561 if (prevProps[propKey] !== undefined && nextProps[propKey] === undefined) {
1562 // eslint-disable-next-line no-console
1563 console.error("downshift: A component has changed the controlled prop \"" + propKey + "\" to be uncontrolled. " + warningDescription);
1564 } else if (prevProps[propKey] === undefined && nextProps[propKey] !== undefined) {
1565 // eslint-disable-next-line no-console
1566 console.error("downshift: A component has changed the uncontrolled prop \"" + propKey + "\" to be controlled. " + warningDescription);
1567 }
1568 });
1569}
1570
1571function getElementIds(generateDefaultId, _temp) {
1572 var _ref = _temp === void 0 ? {} : _temp,
1573 id = _ref.id,
1574 labelId = _ref.labelId,
1575 menuId = _ref.menuId,
1576 getItemId = _ref.getItemId,
1577 toggleButtonId = _ref.toggleButtonId;
1578
1579 var uniqueId = id === undefined ? "downshift-" + generateDefaultId() : id;
1580 return {
1581 labelId: labelId || uniqueId + "-label",
1582 menuId: menuId || uniqueId + "-menu",
1583 getItemId: getItemId || function (index) {
1584 return uniqueId + "-item-" + index;
1585 },
1586 toggleButtonId: toggleButtonId || uniqueId + "-toggle-button"
1587 };
1588}
1589
1590function getNextWrappingIndex$1(moveAmount, baseIndex, itemsLength, circular) {
1591 if (baseIndex === -1) {
1592 return moveAmount > 0 ? 0 : itemsLength - 1;
1593 }
1594
1595 var nextIndex = baseIndex + moveAmount;
1596
1597 if (nextIndex < 0) {
1598 return circular ? itemsLength - 1 : 0;
1599 }
1600
1601 if (nextIndex >= itemsLength) {
1602 return circular ? 0 : itemsLength - 1;
1603 }
1604
1605 return nextIndex;
1606}
1607
1608function getItemIndexByCharacterKey(keysSoFar, highlightedIndex, items, itemToStringParam) {
1609 var newHighlightedIndex = -1;
1610 var itemStrings = items.map(function (item) {
1611 return itemToStringParam(item).toLowerCase();
1612 });
1613 var startPosition = highlightedIndex + 1;
1614 newHighlightedIndex = itemStrings.slice(startPosition).findIndex(function (itemString) {
1615 return itemString.startsWith(keysSoFar);
1616 });
1617
1618 if (newHighlightedIndex > -1) {
1619 return newHighlightedIndex + startPosition;
1620 } else {
1621 return itemStrings.slice(0, startPosition).findIndex(function (itemString) {
1622 return itemString.startsWith(keysSoFar);
1623 });
1624 }
1625}
1626
1627function getState(state, props) {
1628 return Object.keys(state).reduce(function (prevState, key) {
1629 // eslint-disable-next-line no-param-reassign
1630 prevState[key] = props[key] === undefined ? state[key] : props[key];
1631 return prevState;
1632 }, {});
1633}
1634
1635function getItemIndex(index, item, items) {
1636 if (index !== undefined) {
1637 return index;
1638 }
1639
1640 if (items.length === 0) {
1641 return -1;
1642 }
1643
1644 return items.indexOf(item);
1645}
1646
1647function itemToString(item) {
1648 return item ? String(item) : '';
1649}
1650
1651function getPropTypesValidator(caller, propTypes) {
1652 // istanbul ignore next
1653 return function (options) {
1654 if (options === void 0) {
1655 options = {};
1656 }
1657
1658 Object.entries(propTypes).forEach(function (_ref2) {
1659 var key = _ref2[0];
1660 PropTypes.checkPropTypes(propTypes, options, key, caller.name);
1661 });
1662 };
1663}
1664
1665function isAcceptedCharacterKey(key) {
1666 return /^\S{1}$/.test(key);
1667}
1668
1669var defaultStateValues = {
1670 highlightedIndex: -1,
1671 isOpen: false,
1672 selectedItem: null
1673};
1674
1675function getA11yStatusMessage$1(_ref) {
1676 var isOpen = _ref.isOpen,
1677 items = _ref.items;
1678
1679 if (!items) {
1680 return '';
1681 }
1682
1683 var resultCount = items.length;
1684
1685 if (isOpen) {
1686 if (resultCount === 0) {
1687 return 'No results are available';
1688 }
1689
1690 return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
1691 }
1692
1693 return '';
1694}
1695
1696function getA11ySelectionMessage(_ref2) {
1697 var selectedItem = _ref2.selectedItem,
1698 itemToString = _ref2.itemToString;
1699 return itemToString(selectedItem) + " has been selected.";
1700}
1701
1702function getHighlightedIndexOnOpen(props, state, offset) {
1703 var items = props.items,
1704 initialHighlightedIndex = props.initialHighlightedIndex,
1705 defaultHighlightedIndex = props.defaultHighlightedIndex;
1706 var selectedItem = state.selectedItem,
1707 highlightedIndex = state.highlightedIndex; // initialHighlightedIndex will give value to highlightedIndex on initial state only.
1708
1709 if (initialHighlightedIndex !== undefined && highlightedIndex > -1) {
1710 return initialHighlightedIndex;
1711 }
1712
1713 if (defaultHighlightedIndex !== undefined) {
1714 return defaultHighlightedIndex;
1715 }
1716
1717 if (selectedItem) {
1718 if (offset === 0) {
1719 return items.indexOf(selectedItem);
1720 }
1721
1722 return getNextWrappingIndex$1(offset, items.indexOf(selectedItem), items.length, false);
1723 }
1724
1725 if (offset === 0) {
1726 return -1;
1727 }
1728
1729 return offset < 0 ? items.length - 1 : 0;
1730}
1731
1732function capitalizeString(string) {
1733 return "" + string.slice(0, 1).toUpperCase() + string.slice(1);
1734}
1735
1736function getDefaultValue(props, propKey) {
1737 var defaultPropKey = "default" + capitalizeString(propKey);
1738
1739 if (props[defaultPropKey] !== undefined) {
1740 return props[defaultPropKey];
1741 }
1742
1743 return defaultStateValues[propKey];
1744}
1745
1746function getInitialValue(props, propKey) {
1747 if (props[propKey] !== undefined) {
1748 return props[propKey];
1749 }
1750
1751 var initialPropKey = "initial" + capitalizeString(propKey);
1752
1753 if (props[initialPropKey] !== undefined) {
1754 return props[initialPropKey];
1755 }
1756
1757 return getDefaultValue(props, propKey);
1758}
1759
1760function getInitialState(props) {
1761 return {
1762 highlightedIndex: getInitialValue(props, 'highlightedIndex'),
1763 isOpen: getInitialValue(props, 'isOpen'),
1764 selectedItem: getInitialValue(props, 'selectedItem'),
1765 keysSoFar: ''
1766 };
1767}
1768
1769function invokeOnChangeHandler(propKey, props, state, changes) {
1770 var handler = "on" + capitalizeString(propKey) + "Change";
1771
1772 if (props[handler] && changes[propKey] !== undefined && changes[propKey] !== state[propKey]) {
1773 props[handler](changes);
1774 }
1775}
1776
1777function callOnChangeProps(props, state, changes) {
1778 ['isOpen', 'highlightedIndex', 'selectedItem'].forEach(function (propKey) {
1779 invokeOnChangeHandler(propKey, props, state, changes);
1780 });
1781
1782 if (props.onStateChange && changes !== undefined) {
1783 props.onStateChange(changes);
1784 }
1785}
1786
1787var propTypes = {
1788 items: PropTypes.array.isRequired,
1789 itemToString: PropTypes.func,
1790 getA11yStatusMessage: PropTypes.func,
1791 getA11ySelectionMessage: PropTypes.func,
1792 circularNavigation: PropTypes.bool,
1793 highlightedIndex: PropTypes.number,
1794 defaultHighlightedIndex: PropTypes.number,
1795 initialHighlightedIndex: PropTypes.number,
1796 isOpen: PropTypes.bool,
1797 defaultIsOpen: PropTypes.bool,
1798 initialIsOpen: PropTypes.bool,
1799 selectedItem: PropTypes.any,
1800 initialSelectedItem: PropTypes.any,
1801 defaultSelectedItem: PropTypes.any,
1802 id: PropTypes.string,
1803 labelId: PropTypes.string,
1804 menuId: PropTypes.string,
1805 getItemId: PropTypes.func,
1806 toggleButtonId: PropTypes.string,
1807 stateReducer: PropTypes.func,
1808 onSelectedItemChange: PropTypes.func,
1809 onHighlightedIndexChange: PropTypes.func,
1810 onStateChange: PropTypes.func,
1811 onIsOpenChange: PropTypes.func,
1812 environment: PropTypes.shape({
1813 addEventListener: PropTypes.func,
1814 removeEventListener: PropTypes.func,
1815 document: PropTypes.shape({
1816 getElementById: PropTypes.func,
1817 activeElement: PropTypes.any,
1818 body: PropTypes.any
1819 })
1820 })
1821};
1822
1823var MenuKeyDownArrowDown = process.env.NODE_ENV !== "production" ? '__menu_keydown_arrow_down__' : 0;
1824var MenuKeyDownArrowUp = process.env.NODE_ENV !== "production" ? '__menu_keydown_arrow_up__' : 1;
1825var MenuKeyDownEscape = process.env.NODE_ENV !== "production" ? '__menu_keydown_escape__' : 2;
1826var MenuKeyDownHome = process.env.NODE_ENV !== "production" ? '__menu_keydown_home__' : 3;
1827var MenuKeyDownEnd = process.env.NODE_ENV !== "production" ? '__menu_keydown_end__' : 4;
1828var MenuKeyDownEnter = process.env.NODE_ENV !== "production" ? '__menu_keydown_enter__' : 5;
1829var MenuKeyDownCharacter = process.env.NODE_ENV !== "production" ? '__menu_keydown_character__' : 6;
1830var MenuBlur = process.env.NODE_ENV !== "production" ? '__menu_blur__' : 7;
1831var ItemMouseMove = process.env.NODE_ENV !== "production" ? '__item_mouse_move__' : 8;
1832var ItemClick = process.env.NODE_ENV !== "production" ? '__item_click__' : 9;
1833var ToggleButtonKeyDownCharacter = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_character__' : 10;
1834var ToggleButtonKeyDownArrowDown = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_arrow_down__' : 11;
1835var ToggleButtonKeyDownArrowUp = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_arrow_up__' : 12;
1836var ToggleButtonClick = process.env.NODE_ENV !== "production" ? '__togglebutton_click__' : 13;
1837var FunctionToggleMenu = process.env.NODE_ENV !== "production" ? '__function_toggle_menu__' : 14;
1838var FunctionOpenMenu = process.env.NODE_ENV !== "production" ? '__function_open_menu__' : 15;
1839var FunctionCloseMenu = process.env.NODE_ENV !== "production" ? '__function_close_menu__' : 16;
1840var FunctionSetHighlightedIndex = process.env.NODE_ENV !== "production" ? '__function_set_highlighted_index__' : 17;
1841var FunctionSelectItem = process.env.NODE_ENV !== "production" ? '__function_select_item__' : 18;
1842var FunctionClearKeysSoFar = process.env.NODE_ENV !== "production" ? '__function_clear_keys_so_far__' : 19;
1843var FunctionReset = process.env.NODE_ENV !== "production" ? '__function_reset__' : 20;
1844
1845var stateChangeTypes$1 = /*#__PURE__*/Object.freeze({
1846 MenuKeyDownArrowDown: MenuKeyDownArrowDown,
1847 MenuKeyDownArrowUp: MenuKeyDownArrowUp,
1848 MenuKeyDownEscape: MenuKeyDownEscape,
1849 MenuKeyDownHome: MenuKeyDownHome,
1850 MenuKeyDownEnd: MenuKeyDownEnd,
1851 MenuKeyDownEnter: MenuKeyDownEnter,
1852 MenuKeyDownCharacter: MenuKeyDownCharacter,
1853 MenuBlur: MenuBlur,
1854 ItemMouseMove: ItemMouseMove,
1855 ItemClick: ItemClick,
1856 ToggleButtonKeyDownCharacter: ToggleButtonKeyDownCharacter,
1857 ToggleButtonKeyDownArrowDown: ToggleButtonKeyDownArrowDown,
1858 ToggleButtonKeyDownArrowUp: ToggleButtonKeyDownArrowUp,
1859 ToggleButtonClick: ToggleButtonClick,
1860 FunctionToggleMenu: FunctionToggleMenu,
1861 FunctionOpenMenu: FunctionOpenMenu,
1862 FunctionCloseMenu: FunctionCloseMenu,
1863 FunctionSetHighlightedIndex: FunctionSetHighlightedIndex,
1864 FunctionSelectItem: FunctionSelectItem,
1865 FunctionClearKeysSoFar: FunctionClearKeysSoFar,
1866 FunctionReset: FunctionReset
1867});
1868
1869/* eslint-disable complexity */
1870
1871function downshiftSelectReducer(state, action) {
1872 var type = action.type,
1873 props = action.props,
1874 shiftKey = action.shiftKey;
1875 var changes;
1876
1877 switch (type) {
1878 case ItemMouseMove:
1879 changes = {
1880 highlightedIndex: action.index
1881 };
1882 break;
1883
1884 case ItemClick:
1885 changes = {
1886 isOpen: getDefaultValue(props, 'isOpen'),
1887 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
1888 selectedItem: props.items[action.index]
1889 };
1890 break;
1891
1892 case MenuBlur:
1893 changes = _extends({
1894 isOpen: false,
1895 highlightedIndex: -1
1896 }, state.highlightedIndex >= 0 && {
1897 selectedItem: props.items[state.highlightedIndex]
1898 });
1899 break;
1900
1901 case MenuKeyDownArrowDown:
1902 changes = {
1903 highlightedIndex: getNextWrappingIndex$1(shiftKey ? 5 : 1, state.highlightedIndex, props.items.length, props.circularNavigation)
1904 };
1905 break;
1906
1907 case MenuKeyDownArrowUp:
1908 changes = {
1909 highlightedIndex: getNextWrappingIndex$1(shiftKey ? -5 : -1, state.highlightedIndex, props.items.length, props.circularNavigation)
1910 };
1911 break;
1912
1913 case MenuKeyDownHome:
1914 changes = {
1915 highlightedIndex: 0
1916 };
1917 break;
1918
1919 case MenuKeyDownEnd:
1920 changes = {
1921 highlightedIndex: props.items.length - 1
1922 };
1923 break;
1924
1925 case MenuKeyDownEscape:
1926 changes = {
1927 isOpen: false,
1928 highlightedIndex: -1
1929 };
1930 break;
1931
1932 case MenuKeyDownEnter:
1933 changes = _extends({
1934 isOpen: getDefaultValue(props, 'isOpen'),
1935 highlightedIndex: getDefaultValue(props, 'highlightedIndex')
1936 }, state.highlightedIndex >= 0 && {
1937 selectedItem: props.items[state.highlightedIndex]
1938 });
1939 break;
1940
1941 case MenuKeyDownCharacter:
1942 {
1943 var lowercasedKey = action.key;
1944 var keysSoFar = "" + state.keysSoFar + lowercasedKey;
1945 var highlightedIndex = getItemIndexByCharacterKey(keysSoFar, state.highlightedIndex, props.items, props.itemToString);
1946 changes = _extends({
1947 keysSoFar: keysSoFar
1948 }, highlightedIndex >= 0 && {
1949 highlightedIndex: highlightedIndex
1950 });
1951 }
1952 break;
1953
1954 case ToggleButtonKeyDownCharacter:
1955 {
1956 var _lowercasedKey = action.key;
1957
1958 var _keysSoFar = "" + state.keysSoFar + _lowercasedKey;
1959
1960 var itemIndex = getItemIndexByCharacterKey(_keysSoFar, state.selectedItem ? props.items.indexOf(state.selectedItem) : -1, props.items, props.itemToString);
1961 changes = _extends({
1962 keysSoFar: _keysSoFar
1963 }, itemIndex >= 0 && {
1964 selectedItem: props.items[itemIndex]
1965 });
1966 }
1967 break;
1968
1969 case ToggleButtonKeyDownArrowDown:
1970 {
1971 changes = {
1972 isOpen: true,
1973 highlightedIndex: getHighlightedIndexOnOpen(props, state, 1)
1974 };
1975 break;
1976 }
1977
1978 case ToggleButtonKeyDownArrowUp:
1979 changes = {
1980 isOpen: true,
1981 highlightedIndex: getHighlightedIndexOnOpen(props, state, -1)
1982 };
1983 break;
1984
1985 case ToggleButtonClick:
1986 case FunctionToggleMenu:
1987 changes = {
1988 isOpen: !state.isOpen,
1989 highlightedIndex: state.isOpen ? -1 : getHighlightedIndexOnOpen(props, state, 0)
1990 };
1991 break;
1992
1993 case FunctionOpenMenu:
1994 changes = {
1995 isOpen: true,
1996 highlightedIndex: getHighlightedIndexOnOpen(props, state, 0)
1997 };
1998 break;
1999
2000 case FunctionCloseMenu:
2001 changes = {
2002 isOpen: false
2003 };
2004 break;
2005
2006 case FunctionSetHighlightedIndex:
2007 changes = {
2008 highlightedIndex: action.highlightedIndex
2009 };
2010 break;
2011
2012 case FunctionSelectItem:
2013 changes = {
2014 selectedItem: action.selectedItem
2015 };
2016 break;
2017
2018 case FunctionClearKeysSoFar:
2019 changes = {
2020 keysSoFar: ''
2021 };
2022 break;
2023
2024 case FunctionReset:
2025 changes = {
2026 highlightedIndex: getDefaultValue(props, 'highlightedIndex'),
2027 isOpen: getDefaultValue(props, 'isOpen'),
2028 selectedItem: getDefaultValue(props, 'selectedItem')
2029 };
2030 break;
2031
2032 default:
2033 throw new Error('Reducer called without proper action type.');
2034 }
2035
2036 return _extends({}, state, {}, changes);
2037}
2038/* eslint-enable complexity */
2039
2040var validatePropTypes = getPropTypesValidator(useSelect, propTypes);
2041var defaultProps = {
2042 itemToString: itemToString,
2043 stateReducer: function stateReducer(s, a) {
2044 return a.changes;
2045 },
2046 getA11yStatusMessage: getA11yStatusMessage$1,
2047 getA11ySelectionMessage: getA11ySelectionMessage,
2048 scrollIntoView: scrollIntoView,
2049 environment: typeof window === 'undefined'
2050 /* istanbul ignore next (ssr) */
2051 ? {} : window
2052};
2053var clearTimeout$1;
2054useSelect.stateChangeTypes = stateChangeTypes$1;
2055
2056function useSelect(userProps) {
2057 if (userProps === void 0) {
2058 userProps = {};
2059 }
2060
2061 validatePropTypes(userProps); // Props defaults and destructuring.
2062
2063 var props = _extends({}, defaultProps, {}, userProps);
2064
2065 var items = props.items,
2066 itemToString = props.itemToString,
2067 getA11yStatusMessage = props.getA11yStatusMessage,
2068 getA11ySelectionMessage = props.getA11ySelectionMessage,
2069 initialIsOpen = props.initialIsOpen,
2070 defaultIsOpen = props.defaultIsOpen,
2071 stateReducer = props.stateReducer,
2072 scrollIntoView = props.scrollIntoView,
2073 environment = props.environment; // Initial state depending on controlled props.
2074
2075 var initialState = getInitialState(props); // Reducer init.
2076
2077 var _useReducer = React.useReducer(function (state, action) {
2078 var changes = downshiftSelectReducer(state, action); // state after original reducer.
2079
2080 var reducedState = stateReducer(state, _extends({}, action, {
2081 changes: changes
2082 })); // state after user reducer.
2083
2084 callOnChangeProps(props, state, reducedState); // call onChange with state resulted.
2085
2086 return getState(reducedState, props); // state is merged with controlled props.
2087 }, initialState),
2088 _useReducer$ = _useReducer[0],
2089 isOpen = _useReducer$.isOpen,
2090 highlightedIndex = _useReducer$.highlightedIndex,
2091 selectedItem = _useReducer$.selectedItem,
2092 keysSoFar = _useReducer$.keysSoFar,
2093 dispatch = _useReducer[1]; // IDs generation.
2094
2095
2096 var _getElementIds = getElementIds(autoId.useId, props),
2097 labelId = _getElementIds.labelId,
2098 getItemId = _getElementIds.getItemId,
2099 menuId = _getElementIds.menuId,
2100 toggleButtonId = _getElementIds.toggleButtonId;
2101 /* Refs */
2102
2103
2104 var toggleButtonRef = React.useRef(null);
2105 var menuRef = React.useRef(null);
2106 var itemRefs = React.useRef();
2107 itemRefs.current = [];
2108 var isInitialMount = React.useRef(true);
2109 var shouldScroll = React.useRef(true);
2110 /* Effects */
2111
2112 /* Sets a11y status message on changes in isOpen. */
2113
2114 React.useEffect(function () {
2115 if (isInitialMount.current) {
2116 return;
2117 }
2118
2119 setStatus(getA11yStatusMessage({
2120 isOpen: isOpen,
2121 items: items,
2122 selectedItem: selectedItem,
2123 itemToString: itemToString
2124 })); // eslint-disable-next-line react-hooks/exhaustive-deps
2125 }, [isOpen]);
2126 /* Sets a11y status message on changes in selectedItem. */
2127
2128 React.useEffect(function () {
2129 if (isInitialMount.current) {
2130 return;
2131 }
2132
2133 setStatus(getA11ySelectionMessage({
2134 isOpen: isOpen,
2135 items: items,
2136 selectedItem: selectedItem,
2137 itemToString: itemToString
2138 })); // eslint-disable-next-line react-hooks/exhaustive-deps
2139 }, [selectedItem]);
2140 /* Sets cleanup for the keysSoFar after 500ms. */
2141
2142 React.useEffect(function () {
2143 // init the clean function here as we need access to dispatch.
2144 if (isInitialMount.current) {
2145 clearTimeout$1 = debounce(function () {
2146 dispatch({
2147 type: FunctionClearKeysSoFar,
2148 props: props
2149 });
2150 }, 500);
2151 }
2152
2153 if (!keysSoFar) {
2154 return;
2155 }
2156
2157 clearTimeout$1(); // eslint-disable-next-line react-hooks/exhaustive-deps
2158 }, [keysSoFar]);
2159 /* Controls the focus on the menu or the toggle button. */
2160
2161 React.useEffect(function () {
2162 // Don't focus menu on first render.
2163 if (isInitialMount.current) {
2164 // Unless it was initialised as open.
2165 if (initialIsOpen || defaultIsOpen || isOpen) {
2166 menuRef.current.focus();
2167 }
2168
2169 return;
2170 } // Focus menu on open.
2171 // istanbul ignore next
2172
2173
2174 if (isOpen) {
2175 menuRef.current.focus(); // Focus toggleButton on close.
2176 } else if (environment.document.activeElement === menuRef.current) {
2177 toggleButtonRef.current.focus();
2178 } // eslint-disable-next-line react-hooks/exhaustive-deps
2179
2180 }, [isOpen]);
2181 /* Scroll on highlighted item if change comes from keyboard. */
2182
2183 React.useEffect(function () {
2184 if (highlightedIndex < 0 || !isOpen || !itemRefs.current.length) {
2185 return;
2186 }
2187
2188 if (shouldScroll.current === false) {
2189 shouldScroll.current = true;
2190 } else {
2191 scrollIntoView(itemRefs.current[highlightedIndex], menuRef.current);
2192 } // eslint-disable-next-line react-hooks/exhaustive-deps
2193
2194 }, [highlightedIndex]);
2195 /* Make initial ref false. */
2196
2197 React.useEffect(function () {
2198 isInitialMount.current = false;
2199 }, []);
2200 /* Event handler functions */
2201
2202 var menuKeyDownHandlers = {
2203 ArrowDown: function ArrowDown(event) {
2204 event.preventDefault();
2205 dispatch({
2206 type: MenuKeyDownArrowDown,
2207 props: props,
2208 shiftKey: event.shiftKey
2209 });
2210 },
2211 ArrowUp: function ArrowUp(event) {
2212 event.preventDefault();
2213 dispatch({
2214 type: MenuKeyDownArrowUp,
2215 props: props,
2216 shiftKey: event.shiftKey
2217 });
2218 },
2219 Home: function Home(event) {
2220 event.preventDefault();
2221 dispatch({
2222 type: MenuKeyDownHome,
2223 props: props
2224 });
2225 },
2226 End: function End(event) {
2227 event.preventDefault();
2228 dispatch({
2229 type: MenuKeyDownEnd,
2230 props: props
2231 });
2232 },
2233 Escape: function Escape() {
2234 dispatch({
2235 type: MenuKeyDownEscape,
2236 props: props
2237 });
2238 },
2239 Enter: function Enter() {
2240 dispatch({
2241 type: MenuKeyDownEnter,
2242 props: props
2243 });
2244 },
2245 Tab: function Tab(event) {
2246 // The exception that calls MenuBlur.
2247 // istanbul ignore next
2248 if (event.shiftKey) {
2249 dispatch({
2250 type: MenuBlur,
2251 props: props
2252 });
2253 }
2254 }
2255 };
2256 var toggleButtonKeyDownHandlers = {
2257 ArrowDown: function ArrowDown(event) {
2258 event.preventDefault();
2259 dispatch({
2260 type: ToggleButtonKeyDownArrowDown,
2261 props: props
2262 });
2263 },
2264 ArrowUp: function ArrowUp(event) {
2265 event.preventDefault();
2266 dispatch({
2267 type: ToggleButtonKeyDownArrowUp,
2268 props: props
2269 });
2270 }
2271 }; // Event handlers.
2272
2273 var menuHandleKeyDown = function (event) {
2274 var key = keyboardKey.getKey(event);
2275
2276 if (key && menuKeyDownHandlers[key]) {
2277 menuKeyDownHandlers[key](event);
2278 } else if (isAcceptedCharacterKey(key)) {
2279 dispatch({
2280 type: MenuKeyDownCharacter,
2281 key: key,
2282 props: props
2283 });
2284 }
2285 }; // Focus going back to the toggleButton is something we control (Escape, Enter, Click).
2286 // We are toggleing special actions for these cases in reducer, not MenuBlur.
2287 // Since Shift-Tab also lands focus on toggleButton, we will handle it as exception and call MenuBlur.
2288
2289
2290 var menuHandleBlur = function (event) {
2291 if (event.relatedTarget !== toggleButtonRef.current) {
2292 dispatch({
2293 type: MenuBlur,
2294 props: props
2295 });
2296 }
2297 };
2298
2299 var toggleButtonHandleClick = function () {
2300 dispatch({
2301 type: ToggleButtonClick,
2302 props: props
2303 });
2304 };
2305
2306 var toggleButtonHandleKeyDown = function (event) {
2307 var key = keyboardKey.getKey(event);
2308
2309 if (key && toggleButtonKeyDownHandlers[key]) {
2310 toggleButtonKeyDownHandlers[key](event);
2311 } else if (isAcceptedCharacterKey(key)) {
2312 dispatch({
2313 type: ToggleButtonKeyDownCharacter,
2314 key: key,
2315 props: props
2316 });
2317 }
2318 };
2319
2320 var itemHandleMouseMove = function (index) {
2321 if (index === highlightedIndex) {
2322 return;
2323 }
2324
2325 shouldScroll.current = false;
2326 dispatch({
2327 type: ItemMouseMove,
2328 props: props,
2329 index: index
2330 });
2331 };
2332
2333 var itemHandleClick = function (index) {
2334 dispatch({
2335 type: ItemClick,
2336 props: props,
2337 index: index
2338 });
2339 }; // returns
2340
2341
2342 return {
2343 // prop getters.
2344 getToggleButtonProps: function getToggleButtonProps(_temp2) {
2345 var _extends3;
2346
2347 var _ref2 = _temp2 === void 0 ? {} : _temp2,
2348 onClick = _ref2.onClick,
2349 onKeyDown = _ref2.onKeyDown,
2350 _ref2$refKey = _ref2.refKey,
2351 refKey = _ref2$refKey === void 0 ? 'ref' : _ref2$refKey,
2352 ref = _ref2.ref,
2353 rest = _objectWithoutPropertiesLoose(_ref2, ["onClick", "onKeyDown", "refKey", "ref"]);
2354
2355 return _extends((_extends3 = {}, _extends3[refKey] = callAll(ref, function (toggleButtonNode) {
2356 toggleButtonRef.current = toggleButtonNode;
2357 }), _extends3.id = toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = labelId + " " + toggleButtonId, _extends3.onClick = callAllEventHandlers(onClick, toggleButtonHandleClick), _extends3.onKeyDown = callAllEventHandlers(onKeyDown, toggleButtonHandleKeyDown), _extends3), rest);
2358 },
2359 getLabelProps: function getLabelProps(labelProps) {
2360 return _extends({
2361 id: labelId
2362 }, labelProps);
2363 },
2364 getMenuProps: function getMenuProps(_temp) {
2365 var _extends2;
2366
2367 var _ref = _temp === void 0 ? {} : _temp,
2368 onKeyDown = _ref.onKeyDown,
2369 onBlur = _ref.onBlur,
2370 _ref$refKey = _ref.refKey,
2371 refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
2372 ref = _ref.ref,
2373 rest = _objectWithoutPropertiesLoose(_ref, ["onKeyDown", "onBlur", "refKey", "ref"]);
2374
2375 return _extends((_extends2 = {}, _extends2[refKey] = callAll(ref, function (menuNode) {
2376 menuRef.current = menuNode;
2377 }), _extends2.id = menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = labelId, _extends2.tabIndex = -1, _extends2), highlightedIndex > -1 && {
2378 'aria-activedescendant': getItemId(highlightedIndex)
2379 }, {
2380 onKeyDown: callAllEventHandlers(onKeyDown, menuHandleKeyDown),
2381 onBlur: callAllEventHandlers(onBlur, menuHandleBlur)
2382 }, rest);
2383 },
2384 getItemProps: function getItemProps(_temp3) {
2385 var _extends4;
2386
2387 var _ref3 = _temp3 === void 0 ? {} : _temp3,
2388 item = _ref3.item,
2389 index = _ref3.index,
2390 _ref3$refKey = _ref3.refKey,
2391 refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
2392 ref = _ref3.ref,
2393 onMouseMove = _ref3.onMouseMove,
2394 onClick = _ref3.onClick,
2395 rest = _objectWithoutPropertiesLoose(_ref3, ["item", "index", "refKey", "ref", "onMouseMove", "onClick"]);
2396
2397 var itemIndex = getItemIndex(index, item, items);
2398
2399 if (itemIndex < 0) {
2400 throw new Error('Pass either item or item index in getItemProps!');
2401 }
2402
2403 return _extends((_extends4 = {}, _extends4[refKey] = callAll(ref, function (itemNode) {
2404 if (itemNode) {
2405 itemRefs.current.push(itemNode);
2406 }
2407 }), _extends4.role = 'option', _extends4), itemIndex === highlightedIndex && {
2408 'aria-selected': true
2409 }, {
2410 id: getItemId(itemIndex),
2411 onMouseMove: callAllEventHandlers(onMouseMove, function () {
2412 return itemHandleMouseMove(itemIndex);
2413 }),
2414 onClick: callAllEventHandlers(onClick, function () {
2415 return itemHandleClick(itemIndex);
2416 })
2417 }, rest);
2418 },
2419 // actions.
2420 toggleMenu: function toggleMenu() {
2421 dispatch({
2422 type: FunctionToggleMenu,
2423 props: props
2424 });
2425 },
2426 openMenu: function openMenu() {
2427 dispatch({
2428 type: FunctionOpenMenu,
2429 props: props
2430 });
2431 },
2432 closeMenu: function closeMenu() {
2433 dispatch({
2434 type: FunctionCloseMenu
2435 });
2436 },
2437 setHighlightedIndex: function setHighlightedIndex(newHighlightedIndex) {
2438 dispatch({
2439 type: FunctionSetHighlightedIndex,
2440 highlightedIndex: newHighlightedIndex
2441 });
2442 },
2443 selectItem: function selectItem(newSelectedItem) {
2444 dispatch({
2445 type: FunctionSelectItem,
2446 selectedItem: newSelectedItem
2447 });
2448 },
2449 reset: function reset() {
2450 dispatch({
2451 type: FunctionReset,
2452 props: props
2453 });
2454 },
2455 // state.
2456 highlightedIndex: highlightedIndex,
2457 isOpen: isOpen,
2458 selectedItem: selectedItem
2459 };
2460}
2461
2462exports.default = Downshift;
2463exports.resetIdCounter = resetIdCounter;
2464exports.useSelect = useSelect;