Files
echo-nexus/node_modules/downshift/dist/downshift.nativeweb.cjs.js
T

3861 lines
152 KiB
JavaScript

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _objectWithoutPropertiesLoose = require('@babel/runtime/helpers/objectWithoutPropertiesLoose');
var _extends = require('@babel/runtime/helpers/extends');
var _assertThisInitialized = require('@babel/runtime/helpers/assertThisInitialized');
var _inheritsLoose = require('@babel/runtime/helpers/inheritsLoose');
var PropTypes = require('prop-types');
var react = require('react');
var reactIs = require('react-is');
var compute = require('compute-scroll-into-view');
var tslib = require('tslib');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var _objectWithoutPropertiesLoose__default = /*#__PURE__*/_interopDefaultLegacy(_objectWithoutPropertiesLoose);
var _extends__default = /*#__PURE__*/_interopDefaultLegacy(_extends);
var _assertThisInitialized__default = /*#__PURE__*/_interopDefaultLegacy(_assertThisInitialized);
var _inheritsLoose__default = /*#__PURE__*/_interopDefaultLegacy(_inheritsLoose);
var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
var compute__default = /*#__PURE__*/_interopDefaultLegacy(compute);
var idCounter = 0;
/**
* Accepts a parameter and returns it if it's a function
* or a noop function if it's not. This allows us to
* accept a callback, but not worry about it if it's not
* passed.
* @param {Function} cb the callback
* @return {Function} a function
*/
function cbToCb(cb) {
return typeof cb === 'function' ? cb : noop;
}
function noop() {}
/**
* Scroll node into view if necessary
* @param {HTMLElement} node the element that should scroll into view
* @param {HTMLElement} menuNode the menu element of the component
*/
function scrollIntoView(node, menuNode) {
if (!node) {
return;
}
var actions = compute__default["default"](node, {
boundary: menuNode,
block: 'nearest',
scrollMode: 'if-needed'
});
actions.forEach(function (_ref) {
var el = _ref.el,
top = _ref.top,
left = _ref.left;
el.scrollTop = top;
el.scrollLeft = left;
});
}
/**
* @param {HTMLElement} parent the parent node
* @param {HTMLElement} child the child node
* @param {Window} environment The window context where downshift renders.
* @return {Boolean} whether the parent is the child or the child is in the parent
*/
function isOrContainsNode(parent, child, environment) {
var result = parent === child || child instanceof environment.Node && parent.contains && parent.contains(child);
return result;
}
/**
* Simple debounce implementation. Will call the given
* function once after the time given has passed since
* it was last called.
* @param {Function} fn the function to call after the time
* @param {Number} time the time to wait
* @return {Function} the debounced function
*/
function debounce(fn, time) {
var timeoutId;
function cancel() {
if (timeoutId) {
clearTimeout(timeoutId);
}
}
function wrapper() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
cancel();
timeoutId = setTimeout(function () {
timeoutId = null;
fn.apply(void 0, args);
}, time);
}
wrapper.cancel = cancel;
return wrapper;
}
/**
* This is intended to be used to compose event handlers.
* They are executed in order until one of them sets
* `event.preventDownshiftDefault = true`.
* @param {...Function} fns the event handler functions
* @return {Function} the event handler to add to an element
*/
function callAllEventHandlers() {
for (var _len2 = arguments.length, fns = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
fns[_key2] = arguments[_key2];
}
return function (event) {
for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
return fns.some(function (fn) {
if (fn) {
fn.apply(void 0, [event].concat(args));
}
return event.preventDownshiftDefault || event.hasOwnProperty('nativeEvent') && event.nativeEvent.preventDownshiftDefault;
});
};
}
function handleRefs() {
for (var _len4 = arguments.length, refs = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
refs[_key4] = arguments[_key4];
}
return function (node) {
refs.forEach(function (ref) {
if (typeof ref === 'function') {
ref(node);
} else if (ref) {
ref.current = node;
}
});
};
}
/**
* This generates a unique ID for an instance of Downshift
* @return {String} the unique ID
*/
function generateId() {
return String(idCounter++);
}
/**
* Resets idCounter to 0. Used for SSR.
*/
function resetIdCounter() {
idCounter = 0;
}
/**
* Default implementation for status message. Only added when menu is open.
* Will specify if there are results in the list, and if so, how many,
* and what keys are relevant.
*
* @param {Object} param the downshift state and other relevant properties
* @return {String} the a11y status message
*/
function getA11yStatusMessage$1(_ref2) {
var isOpen = _ref2.isOpen,
resultCount = _ref2.resultCount,
previousResultCount = _ref2.previousResultCount;
if (!isOpen) {
return '';
}
if (!resultCount) {
return 'No results are available.';
}
if (resultCount !== previousResultCount) {
return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
}
return '';
}
/**
* Takes an argument and if it's an array, returns the first item in the array
* otherwise returns the argument
* @param {*} arg the maybe-array
* @param {*} defaultValue the value if arg is falsey not defined
* @return {*} the arg or it's first item
*/
function unwrapArray(arg, defaultValue) {
arg = Array.isArray(arg) ? /* istanbul ignore next (preact) */arg[0] : arg;
if (!arg && defaultValue) {
return defaultValue;
} else {
return arg;
}
}
/**
* @param {Object} element (P)react element
* @return {Boolean} whether it's a DOM element
*/
function isDOMElement(element) {
// then we assume this is react
return typeof element.type === 'string';
}
/**
* @param {Object} element (P)react element
* @return {Object} the props
*/
function getElementProps(element) {
return element.props;
}
/**
* Throws a helpful error message for required properties. Useful
* to be used as a default in destructuring or object params.
* @param {String} fnName the function name
* @param {String} propName the prop name
*/
function requiredProp(fnName, propName) {
// eslint-disable-next-line no-console
console.error("The property \"" + propName + "\" is required in \"" + fnName + "\"");
}
var stateKeys = ['highlightedIndex', 'inputValue', 'isOpen', 'selectedItem', 'type'];
/**
* @param {Object} state the state object
* @return {Object} state that is relevant to downshift
*/
function pickState(state) {
if (state === void 0) {
state = {};
}
var result = {};
stateKeys.forEach(function (k) {
if (state.hasOwnProperty(k)) {
result[k] = state[k];
}
});
return result;
}
/**
* This will perform a shallow merge of the given state object
* with the state coming from props
* (for the controlled component scenario)
* This is used in state updater functions so they're referencing
* the right state regardless of where it comes from.
*
* @param {Object} state The state of the component/hook.
* @param {Object} props The props that may contain controlled values.
* @returns {Object} The merged controlled state.
*/
function getState(state, props) {
return Object.keys(state).reduce(function (prevState, key) {
prevState[key] = isControlledProp(props, key) ? props[key] : state[key];
return prevState;
}, {});
}
/**
* This determines whether a prop is a "controlled prop" meaning it is
* state which is controlled by the outside of this component rather
* than within this component.
*
* @param {Object} props The props that may contain controlled values.
* @param {String} key the key to check
* @return {Boolean} whether it is a controlled controlled prop
*/
function isControlledProp(props, key) {
return props[key] !== undefined;
}
/**
* Normalizes the 'key' property of a KeyboardEvent in IE/Edge
* @param {Object} event a keyboardEvent object
* @return {String} keyboard key
*/
function normalizeArrowKey(event) {
var key = event.key,
keyCode = event.keyCode;
/* istanbul ignore next (ie) */
if (keyCode >= 37 && keyCode <= 40 && key.indexOf('Arrow') !== 0) {
return "Arrow" + key;
}
return key;
}
/**
* Simple check if the value passed is object literal
* @param {*} obj any things
* @return {Boolean} whether it's object literal
*/
function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]';
}
/**
* Returns the new index in the list, in a circular way. If next value is out of bonds from the total,
* it will wrap to either 0 or itemCount - 1.
*
* @param {number} moveAmount Number of positions to move. Negative to move backwards, positive forwards.
* @param {number} baseIndex The initial position to move from.
* @param {number} itemCount The total number of items.
* @param {Function} getItemNodeFromIndex Used to check if item is disabled.
* @param {boolean} circular Specify if navigation is circular. Default is true.
* @returns {number} The new index after the move.
*/
function getNextWrappingIndex(moveAmount, baseIndex, itemCount, getItemNodeFromIndex, circular) {
if (circular === void 0) {
circular = true;
}
if (itemCount === 0) {
return -1;
}
var itemsLastIndex = itemCount - 1;
if (typeof baseIndex !== 'number' || baseIndex < 0 || baseIndex >= itemCount) {
baseIndex = moveAmount > 0 ? -1 : itemsLastIndex + 1;
}
var newIndex = baseIndex + moveAmount;
if (newIndex < 0) {
newIndex = circular ? itemsLastIndex : 0;
} else if (newIndex > itemsLastIndex) {
newIndex = circular ? 0 : itemsLastIndex;
}
var nonDisabledNewIndex = getNextNonDisabledIndex(moveAmount, newIndex, itemCount, getItemNodeFromIndex, circular);
if (nonDisabledNewIndex === -1) {
return baseIndex >= itemCount ? -1 : baseIndex;
}
return nonDisabledNewIndex;
}
/**
* Returns the next index in the list of an item that is not disabled.
*
* @param {number} moveAmount Number of positions to move. Negative to move backwards, positive forwards.
* @param {number} baseIndex The initial position to move from.
* @param {number} itemCount The total number of items.
* @param {Function} getItemNodeFromIndex Used to check if item is disabled.
* @param {boolean} circular Specify if navigation is circular. Default is true.
* @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.
*/
function getNextNonDisabledIndex(moveAmount, baseIndex, itemCount, getItemNodeFromIndex, circular) {
var currentElementNode = getItemNodeFromIndex(baseIndex);
if (!currentElementNode || !currentElementNode.hasAttribute('disabled')) {
return baseIndex;
}
if (moveAmount > 0) {
for (var index = baseIndex + 1; index < itemCount; index++) {
if (!getItemNodeFromIndex(index).hasAttribute('disabled')) {
return index;
}
}
} else {
for (var _index = baseIndex - 1; _index >= 0; _index--) {
if (!getItemNodeFromIndex(_index).hasAttribute('disabled')) {
return _index;
}
}
}
if (circular) {
return moveAmount > 0 ? getNextNonDisabledIndex(1, 0, itemCount, getItemNodeFromIndex, false) : getNextNonDisabledIndex(-1, itemCount - 1, itemCount, getItemNodeFromIndex, false);
}
return -1;
}
/**
* Checks if event target is within the downshift elements.
*
* @param {EventTarget} target Target to check.
* @param {HTMLElement[]} downshiftElements The elements that form downshift (list, toggle button etc).
* @param {Window} environment The window context where downshift renders.
* @param {boolean} checkActiveElement Whether to also check activeElement.
*
* @returns {boolean} Whether or not the target is within downshift elements.
*/
function targetWithinDownshift(target, downshiftElements, environment, checkActiveElement) {
if (checkActiveElement === void 0) {
checkActiveElement = true;
}
return downshiftElements.some(function (contextNode) {
return contextNode && (isOrContainsNode(contextNode, target, environment) || checkActiveElement && isOrContainsNode(contextNode, environment.document.activeElement, environment));
});
}
// eslint-disable-next-line import/no-mutable-exports
var validateControlledUnchanged = noop;
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
validateControlledUnchanged = function validateControlledUnchanged(state, prevProps, nextProps) {
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";
Object.keys(state).forEach(function (propKey) {
if (prevProps[propKey] !== undefined && nextProps[propKey] === undefined) {
// eslint-disable-next-line no-console
console.error("downshift: A component has changed the controlled prop \"" + propKey + "\" to be uncontrolled. " + warningDescription);
} else if (prevProps[propKey] === undefined && nextProps[propKey] !== undefined) {
// eslint-disable-next-line no-console
console.error("downshift: A component has changed the uncontrolled prop \"" + propKey + "\" to be controlled. " + warningDescription);
}
});
};
}
var cleanupStatus = debounce(function (documentProp) {
getStatusDiv(documentProp).textContent = '';
}, 500);
/**
* @param {String} status the status message
* @param {Object} documentProp document passed by the user.
*/
function setStatus(status, documentProp) {
var div = getStatusDiv(documentProp);
if (!status) {
return;
}
div.textContent = status;
cleanupStatus(documentProp);
}
/**
* Get the status node or create it if it does not already exist.
* @param {Object} documentProp document passed by the user.
* @return {HTMLElement} the status node.
*/
function getStatusDiv(documentProp) {
if (documentProp === void 0) {
documentProp = document;
}
var statusDiv = documentProp.getElementById('a11y-status-message');
if (statusDiv) {
return statusDiv;
}
statusDiv = documentProp.createElement('div');
statusDiv.setAttribute('id', 'a11y-status-message');
statusDiv.setAttribute('role', 'status');
statusDiv.setAttribute('aria-live', 'polite');
statusDiv.setAttribute('aria-relevant', 'additions text');
Object.assign(statusDiv.style, {
border: '0',
clip: 'rect(0 0 0 0)',
height: '1px',
margin: '-1px',
overflow: 'hidden',
padding: '0',
position: 'absolute',
width: '1px'
});
documentProp.body.appendChild(statusDiv);
return statusDiv;
}
var unknown = process.env.NODE_ENV !== "production" ? '__autocomplete_unknown__' : 0;
var mouseUp = process.env.NODE_ENV !== "production" ? '__autocomplete_mouseup__' : 1;
var itemMouseEnter = process.env.NODE_ENV !== "production" ? '__autocomplete_item_mouseenter__' : 2;
var keyDownArrowUp = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_arrow_up__' : 3;
var keyDownArrowDown = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_arrow_down__' : 4;
var keyDownEscape = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_escape__' : 5;
var keyDownEnter = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_enter__' : 6;
var keyDownHome = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_home__' : 7;
var keyDownEnd = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_end__' : 8;
var clickItem = process.env.NODE_ENV !== "production" ? '__autocomplete_click_item__' : 9;
var blurInput = process.env.NODE_ENV !== "production" ? '__autocomplete_blur_input__' : 10;
var changeInput = process.env.NODE_ENV !== "production" ? '__autocomplete_change_input__' : 11;
var keyDownSpaceButton = process.env.NODE_ENV !== "production" ? '__autocomplete_keydown_space_button__' : 12;
var clickButton = process.env.NODE_ENV !== "production" ? '__autocomplete_click_button__' : 13;
var blurButton = process.env.NODE_ENV !== "production" ? '__autocomplete_blur_button__' : 14;
var controlledPropUpdatedSelectedItem = process.env.NODE_ENV !== "production" ? '__autocomplete_controlled_prop_updated_selected_item__' : 15;
var touchEnd = process.env.NODE_ENV !== "production" ? '__autocomplete_touchend__' : 16;
var stateChangeTypes$3 = /*#__PURE__*/Object.freeze({
__proto__: null,
unknown: unknown,
mouseUp: mouseUp,
itemMouseEnter: itemMouseEnter,
keyDownArrowUp: keyDownArrowUp,
keyDownArrowDown: keyDownArrowDown,
keyDownEscape: keyDownEscape,
keyDownEnter: keyDownEnter,
keyDownHome: keyDownHome,
keyDownEnd: keyDownEnd,
clickItem: clickItem,
blurInput: blurInput,
changeInput: changeInput,
keyDownSpaceButton: keyDownSpaceButton,
clickButton: clickButton,
blurButton: blurButton,
controlledPropUpdatedSelectedItem: controlledPropUpdatedSelectedItem,
touchEnd: touchEnd
});
var _excluded$4 = ["refKey", "ref"],
_excluded2$3 = ["onClick", "onPress", "onKeyDown", "onKeyUp", "onBlur"],
_excluded3$2 = ["onKeyDown", "onBlur", "onChange", "onInput", "onChangeText"],
_excluded4$1 = ["refKey", "ref"],
_excluded5 = ["onMouseMove", "onMouseDown", "onClick", "onPress", "index", "item"];
var Downshift = /*#__PURE__*/function () {
var Downshift = /*#__PURE__*/function (_Component) {
_inheritsLoose__default["default"](Downshift, _Component);
function Downshift(_props) {
var _this;
_this = _Component.call(this, _props) || this;
// fancy destructuring + defaults + aliases
// this basically says each value of state should either be set to
// the initial value or the default value if the initial value is not provided
_this.id = _this.props.id || "downshift-" + generateId();
_this.menuId = _this.props.menuId || _this.id + "-menu";
_this.labelId = _this.props.labelId || _this.id + "-label";
_this.inputId = _this.props.inputId || _this.id + "-input";
_this.getItemId = _this.props.getItemId || function (index) {
return _this.id + "-item-" + index;
};
_this.input = null;
_this.items = [];
// itemCount can be changed asynchronously
// from within downshift (so it can't come from a prop)
// this is why we store it as an instance and use
// getItemCount rather than just use items.length
// (to support windowing + async)
_this.itemCount = null;
_this.previousResultCount = 0;
_this.timeoutIds = [];
/**
* @param {Function} fn the function to call after the time
* @param {Number} time the time to wait
*/
_this.internalSetTimeout = function (fn, time) {
var id = setTimeout(function () {
_this.timeoutIds = _this.timeoutIds.filter(function (i) {
return i !== id;
});
fn();
}, time);
_this.timeoutIds.push(id);
};
_this.setItemCount = function (count) {
_this.itemCount = count;
};
_this.unsetItemCount = function () {
_this.itemCount = null;
};
_this.setHighlightedIndex = function (highlightedIndex, otherStateToSet) {
if (highlightedIndex === void 0) {
highlightedIndex = _this.props.defaultHighlightedIndex;
}
if (otherStateToSet === void 0) {
otherStateToSet = {};
}
otherStateToSet = pickState(otherStateToSet);
_this.internalSetState(_extends__default["default"]({
highlightedIndex: highlightedIndex
}, otherStateToSet));
};
_this.clearSelection = function (cb) {
_this.internalSetState({
selectedItem: null,
inputValue: '',
highlightedIndex: _this.props.defaultHighlightedIndex,
isOpen: _this.props.defaultIsOpen
}, cb);
};
_this.selectItem = function (item, otherStateToSet, cb) {
otherStateToSet = pickState(otherStateToSet);
_this.internalSetState(_extends__default["default"]({
isOpen: _this.props.defaultIsOpen,
highlightedIndex: _this.props.defaultHighlightedIndex,
selectedItem: item,
inputValue: _this.props.itemToString(item)
}, otherStateToSet), cb);
};
_this.selectItemAtIndex = function (itemIndex, otherStateToSet, cb) {
var item = _this.items[itemIndex];
if (item == null) {
return;
}
_this.selectItem(item, otherStateToSet, cb);
};
_this.selectHighlightedItem = function (otherStateToSet, cb) {
return _this.selectItemAtIndex(_this.getState().highlightedIndex, otherStateToSet, cb);
};
// any piece of our state can live in two places:
// 1. Uncontrolled: it's internal (this.state)
// We will call this.setState to update that state
// 2. Controlled: it's external (this.props)
// We will call this.props.onStateChange to update that state
//
// In addition, we'll call this.props.onChange if the
// selectedItem is changed.
_this.internalSetState = function (stateToSet, cb) {
var isItemSelected, onChangeArg;
var onStateChangeArg = {};
var isStateToSetFunction = typeof stateToSet === 'function';
// we want to call `onInputValueChange` before the `setState` call
// so someone controlling the `inputValue` state gets notified of
// the input change as soon as possible. This avoids issues with
// preserving the cursor position.
// See https://github.com/downshift-js/downshift/issues/217 for more info.
if (!isStateToSetFunction && stateToSet.hasOwnProperty('inputValue')) {
_this.props.onInputValueChange(stateToSet.inputValue, _extends__default["default"]({}, _this.getStateAndHelpers(), stateToSet));
}
return _this.setState(function (state) {
state = _this.getState(state);
var newStateToSet = isStateToSetFunction ? stateToSet(state) : stateToSet;
// Your own function that could modify the state that will be set.
newStateToSet = _this.props.stateReducer(state, newStateToSet);
// checks if an item is selected, regardless of if it's different from
// what was selected before
// used to determine if onSelect and onChange callbacks should be called
isItemSelected = newStateToSet.hasOwnProperty('selectedItem');
// this keeps track of the object we want to call with setState
var nextState = {};
// we need to call on change if the outside world is controlling any of our state
// and we're trying to update that state. OR if the selection has changed and we're
// trying to update the selection
if (isItemSelected && newStateToSet.selectedItem !== state.selectedItem) {
onChangeArg = newStateToSet.selectedItem;
}
newStateToSet.type = newStateToSet.type || unknown;
Object.keys(newStateToSet).forEach(function (key) {
// onStateChangeArg should only have the state that is
// actually changing
if (state[key] !== newStateToSet[key]) {
onStateChangeArg[key] = newStateToSet[key];
}
// the type is useful for the onStateChangeArg
// but we don't actually want to set it in internal state.
// this is an undocumented feature for now... Not all internalSetState
// calls support it and I'm not certain we want them to yet.
// But it enables users controlling the isOpen state to know when
// the isOpen state changes due to mouseup events which is quite handy.
if (key === 'type') {
return;
}
newStateToSet[key];
// if it's coming from props, then we don't care to set it internally
if (!isControlledProp(_this.props, key)) {
nextState[key] = newStateToSet[key];
}
});
// if stateToSet is a function, then we weren't able to call onInputValueChange
// earlier, so we'll call it now that we know what the inputValue state will be.
if (isStateToSetFunction && newStateToSet.hasOwnProperty('inputValue')) {
_this.props.onInputValueChange(newStateToSet.inputValue, _extends__default["default"]({}, _this.getStateAndHelpers(), newStateToSet));
}
return nextState;
}, function () {
// call the provided callback if it's a function
cbToCb(cb)();
// only call the onStateChange and onChange callbacks if
// we have relevant information to pass them.
var hasMoreStateThanType = Object.keys(onStateChangeArg).length > 1;
if (hasMoreStateThanType) {
_this.props.onStateChange(onStateChangeArg, _this.getStateAndHelpers());
}
if (isItemSelected) {
_this.props.onSelect(stateToSet.selectedItem, _this.getStateAndHelpers());
}
if (onChangeArg !== undefined) {
_this.props.onChange(onChangeArg, _this.getStateAndHelpers());
}
// this is currently undocumented and therefore subject to change
// We'll try to not break it, but just be warned.
_this.props.onUserAction(onStateChangeArg, _this.getStateAndHelpers());
});
};
//////////////////////////// ROOT
_this.rootRef = function (node) {
return _this._rootNode = node;
};
_this.getRootProps = function (_temp, _temp2) {
var _extends2;
var _ref = _temp === void 0 ? {} : _temp,
_ref$refKey = _ref.refKey,
refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
ref = _ref.ref,
rest = _objectWithoutPropertiesLoose__default["default"](_ref, _excluded$4);
var _ref2 = _temp2 === void 0 ? {} : _temp2,
_ref2$suppressRefErro = _ref2.suppressRefError,
suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
// this is used in the render to know whether the user has called getRootProps.
// It uses that to know whether to apply the props automatically
_this.getRootProps.called = true;
_this.getRootProps.refKey = refKey;
_this.getRootProps.suppressRefError = suppressRefError;
var _this$getState = _this.getState(),
isOpen = _this$getState.isOpen;
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);
};
//\\\\\\\\\\\\\\\\\\\\\\\\\\ ROOT
_this.keyDownHandlers = {
ArrowDown: function ArrowDown(event) {
var _this2 = this;
event.preventDefault();
if (this.getState().isOpen) {
var amount = event.shiftKey ? 5 : 1;
this.moveHighlightedIndex(amount, {
type: keyDownArrowDown
});
} else {
this.internalSetState({
isOpen: true,
type: keyDownArrowDown
}, function () {
var itemCount = _this2.getItemCount();
if (itemCount > 0) {
var _this2$getState = _this2.getState(),
highlightedIndex = _this2$getState.highlightedIndex;
var nextHighlightedIndex = getNextWrappingIndex(1, highlightedIndex, itemCount, function (index) {
return _this2.getItemNodeFromIndex(index);
});
_this2.setHighlightedIndex(nextHighlightedIndex, {
type: keyDownArrowDown
});
}
});
}
},
ArrowUp: function ArrowUp(event) {
var _this3 = this;
event.preventDefault();
if (this.getState().isOpen) {
var amount = event.shiftKey ? -5 : -1;
this.moveHighlightedIndex(amount, {
type: keyDownArrowUp
});
} else {
this.internalSetState({
isOpen: true,
type: keyDownArrowUp
}, function () {
var itemCount = _this3.getItemCount();
if (itemCount > 0) {
var _this3$getState = _this3.getState(),
highlightedIndex = _this3$getState.highlightedIndex;
var nextHighlightedIndex = getNextWrappingIndex(-1, highlightedIndex, itemCount, function (index) {
return _this3.getItemNodeFromIndex(index);
});
_this3.setHighlightedIndex(nextHighlightedIndex, {
type: keyDownArrowUp
});
}
});
}
},
Enter: function Enter(event) {
if (event.which === 229) {
return;
}
var _this$getState2 = this.getState(),
isOpen = _this$getState2.isOpen,
highlightedIndex = _this$getState2.highlightedIndex;
if (isOpen && highlightedIndex != null) {
event.preventDefault();
var item = this.items[highlightedIndex];
var itemNode = this.getItemNodeFromIndex(highlightedIndex);
if (item == null || itemNode && itemNode.hasAttribute('disabled')) {
return;
}
this.selectHighlightedItem({
type: keyDownEnter
});
}
},
Escape: function Escape(event) {
event.preventDefault();
this.reset(_extends__default["default"]({
type: keyDownEscape
}, !this.state.isOpen && {
selectedItem: null,
inputValue: ''
}));
}
};
//////////////////////////// BUTTON
_this.buttonKeyDownHandlers = _extends__default["default"]({}, _this.keyDownHandlers, {
' ': function _(event) {
event.preventDefault();
this.toggleMenu({
type: keyDownSpaceButton
});
}
});
_this.inputKeyDownHandlers = _extends__default["default"]({}, _this.keyDownHandlers, {
Home: function Home(event) {
var _this4 = this;
var _this$getState3 = this.getState(),
isOpen = _this$getState3.isOpen;
if (!isOpen) {
return;
}
event.preventDefault();
var itemCount = this.getItemCount();
if (itemCount <= 0 || !isOpen) {
return;
}
// get next non-disabled starting downwards from 0 if that's disabled.
var newHighlightedIndex = getNextNonDisabledIndex(1, 0, itemCount, function (index) {
return _this4.getItemNodeFromIndex(index);
}, false);
this.setHighlightedIndex(newHighlightedIndex, {
type: keyDownHome
});
},
End: function End(event) {
var _this5 = this;
var _this$getState4 = this.getState(),
isOpen = _this$getState4.isOpen;
if (!isOpen) {
return;
}
event.preventDefault();
var itemCount = this.getItemCount();
if (itemCount <= 0 || !isOpen) {
return;
}
// get next non-disabled starting upwards from last index if that's disabled.
var newHighlightedIndex = getNextNonDisabledIndex(-1, itemCount - 1, itemCount, function (index) {
return _this5.getItemNodeFromIndex(index);
}, false);
this.setHighlightedIndex(newHighlightedIndex, {
type: keyDownEnd
});
}
});
_this.getToggleButtonProps = function (_temp3) {
var _ref3 = _temp3 === void 0 ? {} : _temp3;
_ref3.onClick;
var onPress = _ref3.onPress;
_ref3.onKeyDown;
_ref3.onKeyUp;
_ref3.onBlur;
var rest = _objectWithoutPropertiesLoose__default["default"](_ref3, _excluded2$3);
var _this$getState5 = _this.getState(),
isOpen = _this$getState5.isOpen;
var enabledEventHandlers = /* istanbul ignore next (react-native) */
{
onPress: callAllEventHandlers(onPress, _this.buttonHandleClick)
} ;
var eventHandlers = rest.disabled ? {} : enabledEventHandlers;
return _extends__default["default"]({
type: 'button',
role: 'button',
'aria-label': isOpen ? 'close menu' : 'open menu',
'aria-haspopup': true,
'data-toggle': true
}, eventHandlers, rest);
};
_this.buttonHandleKeyUp = function (event) {
// Prevent click event from emitting in Firefox
event.preventDefault();
};
_this.buttonHandleKeyDown = function (event) {
var key = normalizeArrowKey(event);
if (_this.buttonKeyDownHandlers[key]) {
_this.buttonKeyDownHandlers[key].call(_assertThisInitialized__default["default"](_this), event);
}
};
_this.buttonHandleClick = function (event) {
event.preventDefault();
// handle odd case for Safari and Firefox which
// don't give the button the focus properly.
/* istanbul ignore if (can't reasonably test this) */
if (_this.props.environment.document.activeElement === _this.props.environment.document.body) {
event.target.focus();
}
// to simplify testing components that use downshift, we'll not wrap this in a setTimeout
// if the NODE_ENV is test. With the proper build system, this should be dead code eliminated
// when building for production and should therefore have no impact on production code.
if (process.env.NODE_ENV === 'test') {
_this.toggleMenu({
type: clickButton
});
} else {
// Ensure that toggle of menu occurs after the potential blur event in iOS
_this.internalSetTimeout(function () {
return _this.toggleMenu({
type: clickButton
});
});
}
};
_this.buttonHandleBlur = function (event) {
var blurTarget = event.target; // Save blur target for comparison with activeElement later
// Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not body element
_this.internalSetTimeout(function () {
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)
) {
_this.reset({
type: blurButton
});
}
});
};
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ BUTTON
/////////////////////////////// LABEL
_this.getLabelProps = function (props) {
return _extends__default["default"]({
htmlFor: _this.inputId,
id: _this.labelId
}, props);
};
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ LABEL
/////////////////////////////// INPUT
_this.getInputProps = function (_temp4) {
var _ref4 = _temp4 === void 0 ? {} : _temp4,
onKeyDown = _ref4.onKeyDown,
onBlur = _ref4.onBlur,
onChange = _ref4.onChange,
onInput = _ref4.onInput;
_ref4.onChangeText;
var rest = _objectWithoutPropertiesLoose__default["default"](_ref4, _excluded3$2);
var onChangeKey;
var eventHandlers = {};
/* istanbul ignore next (preact) */
{
onChangeKey = 'onChange';
}
var _this$getState6 = _this.getState(),
inputValue = _this$getState6.inputValue,
isOpen = _this$getState6.isOpen,
highlightedIndex = _this$getState6.highlightedIndex;
if (!rest.disabled) {
var _eventHandlers;
eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, _this.inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, _this.inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, _this.inputHandleBlur), _eventHandlers);
}
return _extends__default["default"]({
'aria-autocomplete': 'list',
'aria-activedescendant': isOpen && typeof highlightedIndex === 'number' && highlightedIndex >= 0 ? _this.getItemId(highlightedIndex) : null,
'aria-controls': isOpen ? _this.menuId : null,
'aria-labelledby': rest && rest['aria-label'] ? undefined : _this.labelId,
// https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
// revert back since autocomplete="nope" is ignored on latest Chrome and Opera
autoComplete: 'off',
value: inputValue,
id: _this.inputId
}, eventHandlers, rest);
};
_this.inputHandleKeyDown = function (event) {
var key = normalizeArrowKey(event);
if (key && _this.inputKeyDownHandlers[key]) {
_this.inputKeyDownHandlers[key].call(_assertThisInitialized__default["default"](_this), event);
}
};
_this.inputHandleChange = function (event) {
_this.internalSetState({
type: changeInput,
isOpen: true,
inputValue: /* istanbul ignore next (react-native) */event.nativeEvent.text ,
highlightedIndex: _this.props.defaultHighlightedIndex
});
};
_this.inputHandleBlur = function () {
// Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not the body element
_this.internalSetTimeout(function () {
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);
if (!_this.isMouseDown && !downshiftButtonIsActive) {
_this.reset({
type: blurInput
});
}
});
};
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ INPUT
/////////////////////////////// MENU
_this.menuRef = function (node) {
_this._menuNode = node;
};
_this.getMenuProps = function (_temp5, _temp6) {
var _extends3;
var _ref5 = _temp5 === void 0 ? {} : _temp5,
_ref5$refKey = _ref5.refKey,
refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
ref = _ref5.ref,
props = _objectWithoutPropertiesLoose__default["default"](_ref5, _excluded4$1);
var _ref6 = _temp6 === void 0 ? {} : _temp6,
_ref6$suppressRefErro = _ref6.suppressRefError,
suppressRefError = _ref6$suppressRefErro === void 0 ? false : _ref6$suppressRefErro;
_this.getMenuProps.called = true;
_this.getMenuProps.refKey = refKey;
_this.getMenuProps.suppressRefError = suppressRefError;
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);
};
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MENU
/////////////////////////////// ITEM
_this.getItemProps = function (_temp7) {
var _enabledEventHandlers;
var _ref7 = _temp7 === void 0 ? {} : _temp7,
onMouseMove = _ref7.onMouseMove,
onMouseDown = _ref7.onMouseDown,
onClick = _ref7.onClick;
_ref7.onPress;
var index = _ref7.index,
_ref7$item = _ref7.item,
item = _ref7$item === void 0 ? process.env.NODE_ENV === 'production' ? /* istanbul ignore next */undefined : requiredProp('getItemProps', 'item') : _ref7$item,
rest = _objectWithoutPropertiesLoose__default["default"](_ref7, _excluded5);
if (index === undefined) {
_this.items.push(item);
index = _this.items.indexOf(item);
} else {
_this.items[index] = item;
}
var onSelectKey = /* istanbul ignore next (react-native) */'onPress' ;
var customClickHandler = onClick;
var enabledEventHandlers = (_enabledEventHandlers = {
// onMouseMove is used over onMouseEnter here. onMouseMove
// is only triggered on actual mouse movement while onMouseEnter
// can fire on DOM changes, interrupting keyboard navigation
onMouseMove: callAllEventHandlers(onMouseMove, function () {
if (index === _this.getState().highlightedIndex) {
return;
}
_this.setHighlightedIndex(index, {
type: itemMouseEnter
});
// We never want to manually scroll when changing state based
// on `onMouseMove` because we will be moving the element out
// from under the user which is currently scrolling/moving the
// cursor
_this.avoidScrolling = true;
_this.internalSetTimeout(function () {
return _this.avoidScrolling = false;
}, 250);
}),
onMouseDown: callAllEventHandlers(onMouseDown, function (event) {
// This prevents the activeElement from being changed
// to the item so it can remain with the current activeElement
// which is a more common use case.
event.preventDefault();
})
}, _enabledEventHandlers[onSelectKey] = callAllEventHandlers(customClickHandler, function () {
_this.selectItemAtIndex(index, {
type: clickItem
});
}), _enabledEventHandlers);
// Passing down the onMouseDown handler to prevent redirect
// of the activeElement if clicking on disabled items
var eventHandlers = rest.disabled ? {
onMouseDown: enabledEventHandlers.onMouseDown
} : enabledEventHandlers;
return _extends__default["default"]({
id: _this.getItemId(index),
role: 'option',
'aria-selected': _this.getState().highlightedIndex === index
}, eventHandlers, rest);
};
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ITEM
_this.clearItems = function () {
_this.items = [];
};
_this.reset = function (otherStateToSet, cb) {
if (otherStateToSet === void 0) {
otherStateToSet = {};
}
otherStateToSet = pickState(otherStateToSet);
_this.internalSetState(function (_ref8) {
var selectedItem = _ref8.selectedItem;
return _extends__default["default"]({
isOpen: _this.props.defaultIsOpen,
highlightedIndex: _this.props.defaultHighlightedIndex,
inputValue: _this.props.itemToString(selectedItem)
}, otherStateToSet);
}, cb);
};
_this.toggleMenu = function (otherStateToSet, cb) {
if (otherStateToSet === void 0) {
otherStateToSet = {};
}
otherStateToSet = pickState(otherStateToSet);
_this.internalSetState(function (_ref9) {
var isOpen = _ref9.isOpen;
return _extends__default["default"]({
isOpen: !isOpen
}, isOpen && {
highlightedIndex: _this.props.defaultHighlightedIndex
}, otherStateToSet);
}, function () {
var _this$getState7 = _this.getState(),
isOpen = _this$getState7.isOpen,
highlightedIndex = _this$getState7.highlightedIndex;
if (isOpen) {
if (_this.getItemCount() > 0 && typeof highlightedIndex === 'number') {
_this.setHighlightedIndex(highlightedIndex, otherStateToSet);
}
}
cbToCb(cb)();
});
};
_this.openMenu = function (cb) {
_this.internalSetState({
isOpen: true
}, cb);
};
_this.closeMenu = function (cb) {
_this.internalSetState({
isOpen: false
}, cb);
};
_this.updateStatus = debounce(function () {
var state = _this.getState();
var item = _this.items[state.highlightedIndex];
var resultCount = _this.getItemCount();
var status = _this.props.getA11yStatusMessage(_extends__default["default"]({
itemToString: _this.props.itemToString,
previousResultCount: _this.previousResultCount,
resultCount: resultCount,
highlightedItem: item
}, state));
_this.previousResultCount = resultCount;
setStatus(status, _this.props.environment.document);
}, 200);
var _this$props = _this.props,
defaultHighlightedIndex = _this$props.defaultHighlightedIndex,
_this$props$initialHi = _this$props.initialHighlightedIndex,
_highlightedIndex = _this$props$initialHi === void 0 ? defaultHighlightedIndex : _this$props$initialHi,
defaultIsOpen = _this$props.defaultIsOpen,
_this$props$initialIs = _this$props.initialIsOpen,
_isOpen = _this$props$initialIs === void 0 ? defaultIsOpen : _this$props$initialIs,
_this$props$initialIn = _this$props.initialInputValue,
_inputValue = _this$props$initialIn === void 0 ? '' : _this$props$initialIn,
_this$props$initialSe = _this$props.initialSelectedItem,
_selectedItem = _this$props$initialSe === void 0 ? null : _this$props$initialSe;
var _state = _this.getState({
highlightedIndex: _highlightedIndex,
isOpen: _isOpen,
inputValue: _inputValue,
selectedItem: _selectedItem
});
if (_state.selectedItem != null && _this.props.initialInputValue === undefined) {
_state.inputValue = _this.props.itemToString(_state.selectedItem);
}
_this.state = _state;
return _this;
}
var _proto = Downshift.prototype;
/**
* Clear all running timeouts
*/
_proto.internalClearTimeouts = function internalClearTimeouts() {
this.timeoutIds.forEach(function (id) {
clearTimeout(id);
});
this.timeoutIds = [];
}
/**
* Gets the state based on internal state or props
* If a state value is passed via props, then that
* is the value given, otherwise it's retrieved from
* stateToMerge
*
* @param {Object} stateToMerge defaults to this.state
* @return {Object} the state
*/;
_proto.getState = function getState$1(stateToMerge) {
if (stateToMerge === void 0) {
stateToMerge = this.state;
}
return getState(stateToMerge, this.props);
};
_proto.getItemCount = function getItemCount() {
// things read better this way. They're in priority order:
// 1. `this.itemCount`
// 2. `this.props.itemCount`
// 3. `this.items.length`
var itemCount = this.items.length;
if (this.itemCount != null) {
itemCount = this.itemCount;
} else if (this.props.itemCount !== undefined) {
itemCount = this.props.itemCount;
}
return itemCount;
};
_proto.getItemNodeFromIndex = function getItemNodeFromIndex(index) {
return this.props.environment.document.getElementById(this.getItemId(index));
};
_proto.scrollHighlightedItemIntoView = function scrollHighlightedItemIntoView() {
/* istanbul ignore else (react-native) */
{
var node = this.getItemNodeFromIndex(this.getState().highlightedIndex);
this.props.scrollIntoView(node, this._menuNode);
}
};
_proto.moveHighlightedIndex = function moveHighlightedIndex(amount, otherStateToSet) {
var _this6 = this;
var itemCount = this.getItemCount();
var _this$getState8 = this.getState(),
highlightedIndex = _this$getState8.highlightedIndex;
if (itemCount > 0) {
var nextHighlightedIndex = getNextWrappingIndex(amount, highlightedIndex, itemCount, function (index) {
return _this6.getItemNodeFromIndex(index);
});
this.setHighlightedIndex(nextHighlightedIndex, otherStateToSet);
}
};
_proto.getStateAndHelpers = function getStateAndHelpers() {
var _this$getState9 = this.getState(),
highlightedIndex = _this$getState9.highlightedIndex,
inputValue = _this$getState9.inputValue,
selectedItem = _this$getState9.selectedItem,
isOpen = _this$getState9.isOpen;
var itemToString = this.props.itemToString;
var id = this.id;
var getRootProps = this.getRootProps,
getToggleButtonProps = this.getToggleButtonProps,
getLabelProps = this.getLabelProps,
getMenuProps = this.getMenuProps,
getInputProps = this.getInputProps,
getItemProps = this.getItemProps,
openMenu = this.openMenu,
closeMenu = this.closeMenu,
toggleMenu = this.toggleMenu,
selectItem = this.selectItem,
selectItemAtIndex = this.selectItemAtIndex,
selectHighlightedItem = this.selectHighlightedItem,
setHighlightedIndex = this.setHighlightedIndex,
clearSelection = this.clearSelection,
clearItems = this.clearItems,
reset = this.reset,
setItemCount = this.setItemCount,
unsetItemCount = this.unsetItemCount,
setState = this.internalSetState;
return {
// prop getters
getRootProps: getRootProps,
getToggleButtonProps: getToggleButtonProps,
getLabelProps: getLabelProps,
getMenuProps: getMenuProps,
getInputProps: getInputProps,
getItemProps: getItemProps,
// actions
reset: reset,
openMenu: openMenu,
closeMenu: closeMenu,
toggleMenu: toggleMenu,
selectItem: selectItem,
selectItemAtIndex: selectItemAtIndex,
selectHighlightedItem: selectHighlightedItem,
setHighlightedIndex: setHighlightedIndex,
clearSelection: clearSelection,
clearItems: clearItems,
setItemCount: setItemCount,
unsetItemCount: unsetItemCount,
setState: setState,
// props
itemToString: itemToString,
// derived
id: id,
// state
highlightedIndex: highlightedIndex,
inputValue: inputValue,
isOpen: isOpen,
selectedItem: selectedItem
};
};
_proto.componentDidMount = function componentDidMount() {
var _this7 = this;
/* istanbul ignore if (react-native) */
if (process.env.NODE_ENV !== 'production' && !false && this.getMenuProps.called && !this.getMenuProps.suppressRefError) {
validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps);
}
/* istanbul ignore if (react-native) */
{
// this.isMouseDown helps us track whether the mouse is currently held down.
// This is useful when the user clicks on an item in the list, but holds the mouse
// down long enough for the list to disappear (because the blur event fires on the input)
// this.isMouseDown is used in the blur handler on the input to determine whether the blur event should
// trigger hiding the menu.
var onMouseDown = function onMouseDown() {
_this7.isMouseDown = true;
};
var onMouseUp = function onMouseUp(event) {
_this7.isMouseDown = false;
// if the target element or the activeElement is within a downshift node
// then we don't want to reset downshift
var contextWithinDownshift = targetWithinDownshift(event.target, [_this7._rootNode, _this7._menuNode], _this7.props.environment);
if (!contextWithinDownshift && _this7.getState().isOpen) {
_this7.reset({
type: mouseUp
}, function () {
return _this7.props.onOuterClick(_this7.getStateAndHelpers());
});
}
};
// Touching an element in iOS gives focus and hover states, but touching out of
// the element will remove hover, and persist the focus state, resulting in the
// blur event not being triggered.
// this.isTouchMove helps us track whether the user is tapping or swiping on a touch screen.
// If the user taps outside of Downshift, the component should be reset,
// but not if the user is swiping
var onTouchStart = function onTouchStart() {
_this7.isTouchMove = false;
};
var onTouchMove = function onTouchMove() {
_this7.isTouchMove = true;
};
var onTouchEnd = function onTouchEnd(event) {
var contextWithinDownshift = targetWithinDownshift(event.target, [_this7._rootNode, _this7._menuNode], _this7.props.environment, false);
if (!_this7.isTouchMove && !contextWithinDownshift && _this7.getState().isOpen) {
_this7.reset({
type: touchEnd
}, function () {
return _this7.props.onOuterClick(_this7.getStateAndHelpers());
});
}
};
var environment = this.props.environment;
environment.addEventListener('mousedown', onMouseDown);
environment.addEventListener('mouseup', onMouseUp);
environment.addEventListener('touchstart', onTouchStart);
environment.addEventListener('touchmove', onTouchMove);
environment.addEventListener('touchend', onTouchEnd);
this.cleanup = function () {
_this7.internalClearTimeouts();
_this7.updateStatus.cancel();
environment.removeEventListener('mousedown', onMouseDown);
environment.removeEventListener('mouseup', onMouseUp);
environment.removeEventListener('touchstart', onTouchStart);
environment.removeEventListener('touchmove', onTouchMove);
environment.removeEventListener('touchend', onTouchEnd);
};
}
};
_proto.shouldScroll = function shouldScroll(prevState, prevProps) {
var _ref10 = this.props.highlightedIndex === undefined ? this.getState() : this.props,
currentHighlightedIndex = _ref10.highlightedIndex;
var _ref11 = prevProps.highlightedIndex === undefined ? prevState : prevProps,
prevHighlightedIndex = _ref11.highlightedIndex;
var scrollWhenOpen = currentHighlightedIndex && this.getState().isOpen && !prevState.isOpen;
var scrollWhenNavigating = currentHighlightedIndex !== prevHighlightedIndex;
return scrollWhenOpen || scrollWhenNavigating;
};
_proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
if (process.env.NODE_ENV !== 'production') {
validateControlledUnchanged(this.state, prevProps, this.props);
/* istanbul ignore if (react-native) */
if (this.getMenuProps.called && !this.getMenuProps.suppressRefError) {
validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps);
}
}
if (isControlledProp(this.props, 'selectedItem') && this.props.selectedItemChanged(prevProps.selectedItem, this.props.selectedItem)) {
this.internalSetState({
type: controlledPropUpdatedSelectedItem,
inputValue: this.props.itemToString(this.props.selectedItem)
});
}
if (!this.avoidScrolling && this.shouldScroll(prevState, prevProps)) {
this.scrollHighlightedItemIntoView();
}
/* istanbul ignore else (react-native) */
{
this.updateStatus();
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
this.cleanup(); // avoids memory leak
};
_proto.render = function render() {
var children = unwrapArray(this.props.children, noop);
// because the items are rerendered every time we call the children
// we clear this out each render and it will be populated again as
// getItemProps is called.
this.clearItems();
// we reset this so we know whether the user calls getRootProps during
// this render. If they do then we don't need to do anything,
// if they don't then we need to clone the element they return and
// apply the props for them.
this.getRootProps.called = false;
this.getRootProps.refKey = undefined;
this.getRootProps.suppressRefError = undefined;
// we do something similar for getMenuProps
this.getMenuProps.called = false;
this.getMenuProps.refKey = undefined;
this.getMenuProps.suppressRefError = undefined;
// we do something similar for getLabelProps
this.getLabelProps.called = false;
// and something similar for getInputProps
this.getInputProps.called = false;
var element = unwrapArray(children(this.getStateAndHelpers()));
if (!element) {
return null;
}
if (this.getRootProps.called || this.props.suppressRefError) {
if (process.env.NODE_ENV !== 'production' && !this.getRootProps.suppressRefError && !this.props.suppressRefError) {
validateGetRootPropsCalledCorrectly(element, this.getRootProps);
}
return element;
} else if (isDOMElement(element)) {
// they didn't apply the root props, but we can clone
// this and apply the props ourselves
return /*#__PURE__*/react.cloneElement(element, this.getRootProps(getElementProps(element)));
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
// they didn't apply the root props, but they need to
// otherwise we can't query around the autocomplete
throw new Error('downshift: If you return a non-DOM element, you must apply the getRootProps function');
}
/* istanbul ignore next */
return undefined;
};
return Downshift;
}(react.Component);
Downshift.defaultProps = {
defaultHighlightedIndex: null,
defaultIsOpen: false,
getA11yStatusMessage: getA11yStatusMessage$1,
itemToString: function itemToString(i) {
if (i == null) {
return '';
}
if (process.env.NODE_ENV !== 'production' && isPlainObject(i) && !i.hasOwnProperty('toString')) {
// eslint-disable-next-line no-console
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);
}
return String(i);
},
onStateChange: noop,
onInputValueChange: noop,
onUserAction: noop,
onChange: noop,
onSelect: noop,
onOuterClick: noop,
selectedItemChanged: function selectedItemChanged(prevItem, item) {
return prevItem !== item;
},
environment: /* istanbul ignore next (ssr) */
typeof window === 'undefined' ? {} : window,
stateReducer: function stateReducer(state, stateToSet) {
return stateToSet;
},
suppressRefError: false,
scrollIntoView: scrollIntoView
};
Downshift.stateChangeTypes = stateChangeTypes$3;
return Downshift;
}();
process.env.NODE_ENV !== "production" ? Downshift.propTypes = {
children: PropTypes__default["default"].func,
defaultHighlightedIndex: PropTypes__default["default"].number,
defaultIsOpen: PropTypes__default["default"].bool,
initialHighlightedIndex: PropTypes__default["default"].number,
initialSelectedItem: PropTypes__default["default"].any,
initialInputValue: PropTypes__default["default"].string,
initialIsOpen: PropTypes__default["default"].bool,
getA11yStatusMessage: PropTypes__default["default"].func,
itemToString: PropTypes__default["default"].func,
onChange: PropTypes__default["default"].func,
onSelect: PropTypes__default["default"].func,
onStateChange: PropTypes__default["default"].func,
onInputValueChange: PropTypes__default["default"].func,
onUserAction: PropTypes__default["default"].func,
onOuterClick: PropTypes__default["default"].func,
selectedItemChanged: PropTypes__default["default"].func,
stateReducer: PropTypes__default["default"].func,
itemCount: PropTypes__default["default"].number,
id: PropTypes__default["default"].string,
environment: PropTypes__default["default"].shape({
addEventListener: PropTypes__default["default"].func,
removeEventListener: PropTypes__default["default"].func,
document: PropTypes__default["default"].shape({
getElementById: PropTypes__default["default"].func,
activeElement: PropTypes__default["default"].any,
body: PropTypes__default["default"].any
})
}),
suppressRefError: PropTypes__default["default"].bool,
scrollIntoView: PropTypes__default["default"].func,
// things we keep in state for uncontrolled components
// but can accept as props for controlled components
/* eslint-disable react/no-unused-prop-types */
selectedItem: PropTypes__default["default"].any,
isOpen: PropTypes__default["default"].bool,
inputValue: PropTypes__default["default"].string,
highlightedIndex: PropTypes__default["default"].number,
labelId: PropTypes__default["default"].string,
inputId: PropTypes__default["default"].string,
menuId: PropTypes__default["default"].string,
getItemId: PropTypes__default["default"].func
/* eslint-enable react/no-unused-prop-types */
} : void 0;
var Downshift$1 = Downshift;
function validateGetMenuPropsCalledCorrectly(node, _ref12) {
var refKey = _ref12.refKey;
if (!node) {
// eslint-disable-next-line no-console
console.error("downshift: The ref prop \"" + refKey + "\" from getMenuProps was not applied correctly on your menu element.");
}
}
function validateGetRootPropsCalledCorrectly(element, _ref13) {
var refKey = _ref13.refKey;
var refKeySpecified = refKey !== 'ref';
var isComposite = !isDOMElement(element);
if (isComposite && !refKeySpecified && !reactIs.isForwardRef(element)) {
// eslint-disable-next-line no-console
console.error('downshift: You returned a non-DOM element. You must specify a refKey in getRootProps');
} else if (!isComposite && refKeySpecified) {
// eslint-disable-next-line no-console
console.error("downshift: You returned a DOM element. You should not specify a refKey in getRootProps. You specified \"" + refKey + "\"");
}
if (!reactIs.isForwardRef(element) && !getElementProps(element)[refKey]) {
// eslint-disable-next-line no-console
console.error("downshift: You must apply the ref prop \"" + refKey + "\" from getRootProps onto your root element.");
}
}
var _excluded$3 = ["isInitialMount", "highlightedIndex", "items", "environment"];
var dropdownDefaultStateValues = {
highlightedIndex: -1,
isOpen: false,
selectedItem: null,
inputValue: ''
};
function callOnChangeProps(action, state, newState) {
var props = action.props,
type = action.type;
var changes = {};
Object.keys(state).forEach(function (key) {
invokeOnChangeHandler(key, action, state, newState);
if (newState[key] !== state[key]) {
changes[key] = newState[key];
}
});
if (props.onStateChange && Object.keys(changes).length) {
props.onStateChange(_extends__default["default"]({
type: type
}, changes));
}
}
function invokeOnChangeHandler(key, action, state, newState) {
var props = action.props,
type = action.type;
var handler = "on" + capitalizeString(key) + "Change";
if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
props[handler](_extends__default["default"]({
type: type
}, newState));
}
}
/**
* Default state reducer that returns the changes.
*
* @param {Object} s state.
* @param {Object} a action with changes.
* @returns {Object} changes.
*/
function stateReducer(s, a) {
return a.changes;
}
/**
* Returns a message to be added to aria-live region when item is selected.
*
* @param {Object} selectionParameters Parameters required to build the message.
* @returns {string} The a11y message.
*/
function getA11ySelectionMessage(selectionParameters) {
var selectedItem = selectionParameters.selectedItem,
itemToStringLocal = selectionParameters.itemToString;
return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
}
/**
* Debounced call for updating the a11y message.
*/
var updateA11yStatus = debounce(function (getA11yMessage, document) {
setStatus(getA11yMessage(), document);
}, 200);
// istanbul ignore next
var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? react.useLayoutEffect : react.useEffect;
function useElementIds(_ref) {
var _ref$id = _ref.id,
id = _ref$id === void 0 ? "downshift-" + generateId() : _ref$id,
labelId = _ref.labelId,
menuId = _ref.menuId,
getItemId = _ref.getItemId,
toggleButtonId = _ref.toggleButtonId,
inputId = _ref.inputId;
var elementIdsRef = react.useRef({
labelId: labelId || id + "-label",
menuId: menuId || id + "-menu",
getItemId: getItemId || function (index) {
return id + "-item-" + index;
},
toggleButtonId: toggleButtonId || id + "-toggle-button",
inputId: inputId || id + "-input"
});
return elementIdsRef.current;
}
function getItemAndIndex(itemProp, indexProp, items, errorMessage) {
var item, index;
if (itemProp === undefined) {
if (indexProp === undefined) {
throw new Error(errorMessage);
}
item = items[indexProp];
index = indexProp;
} else {
index = indexProp === undefined ? items.indexOf(itemProp) : indexProp;
item = itemProp;
}
return [item, index];
}
function itemToString(item) {
return item ? String(item) : '';
}
function capitalizeString(string) {
return "" + string.slice(0, 1).toUpperCase() + string.slice(1);
}
function useLatestRef(val) {
var ref = react.useRef(val);
// technically this is not "concurrent mode safe" because we're manipulating
// the value during render (so it's not idempotent). However, the places this
// hook is used is to support memoizing callbacks which will be called
// *during* render, so we need the latest values *during* render.
// If not for this, then we'd probably want to use useLayoutEffect instead.
ref.current = val;
return ref;
}
/**
* Computes the controlled state using a the previous state, props,
* two reducers, one from downshift and an optional one from the user.
* Also calls the onChange handlers for state values that have changed.
*
* @param {Function} reducer Reducer function from downshift.
* @param {Object} initialState Initial state of the hook.
* @param {Object} props The hook props.
* @returns {Array} An array with the state and an action dispatcher.
*/
function useEnhancedReducer(reducer, initialState, props) {
var prevStateRef = react.useRef();
var actionRef = react.useRef();
var enhancedReducer = react.useCallback(function (state, action) {
actionRef.current = action;
state = getState(state, action.props);
var changes = reducer(state, action);
var newState = action.props.stateReducer(state, _extends__default["default"]({}, action, {
changes: changes
}));
return newState;
}, [reducer]);
var _useReducer = react.useReducer(enhancedReducer, initialState),
state = _useReducer[0],
dispatch = _useReducer[1];
var propsRef = useLatestRef(props);
var dispatchWithProps = react.useCallback(function (action) {
return dispatch(_extends__default["default"]({
props: propsRef.current
}, action));
}, [propsRef]);
var action = actionRef.current;
react.useEffect(function () {
if (action && prevStateRef.current && prevStateRef.current !== state) {
callOnChangeProps(action, getState(prevStateRef.current, action.props), state);
}
prevStateRef.current = state;
}, [state, props, action]);
return [state, dispatchWithProps];
}
/**
* Wraps the useEnhancedReducer and applies the controlled prop values before
* returning the new state.
*
* @param {Function} reducer Reducer function from downshift.
* @param {Object} initialState Initial state of the hook.
* @param {Object} props The hook props.
* @returns {Array} An array with the state and an action dispatcher.
*/
function useControlledReducer$1(reducer, initialState, props) {
var _useEnhancedReducer = useEnhancedReducer(reducer, initialState, props),
state = _useEnhancedReducer[0],
dispatch = _useEnhancedReducer[1];
return [getState(state, props), dispatch];
}
var defaultProps$3 = {
itemToString: itemToString,
stateReducer: stateReducer,
getA11ySelectionMessage: getA11ySelectionMessage,
scrollIntoView: scrollIntoView,
environment: /* istanbul ignore next (ssr) */
typeof window === 'undefined' ? {} : window
};
function getDefaultValue$1(props, propKey, defaultStateValues) {
if (defaultStateValues === void 0) {
defaultStateValues = dropdownDefaultStateValues;
}
var defaultValue = props["default" + capitalizeString(propKey)];
if (defaultValue !== undefined) {
return defaultValue;
}
return defaultStateValues[propKey];
}
function getInitialValue$1(props, propKey, defaultStateValues) {
if (defaultStateValues === void 0) {
defaultStateValues = dropdownDefaultStateValues;
}
var value = props[propKey];
if (value !== undefined) {
return value;
}
var initialValue = props["initial" + capitalizeString(propKey)];
if (initialValue !== undefined) {
return initialValue;
}
return getDefaultValue$1(props, propKey, defaultStateValues);
}
function getInitialState$2(props) {
var selectedItem = getInitialValue$1(props, 'selectedItem');
var isOpen = getInitialValue$1(props, 'isOpen');
var highlightedIndex = getInitialValue$1(props, 'highlightedIndex');
var inputValue = getInitialValue$1(props, 'inputValue');
return {
highlightedIndex: highlightedIndex < 0 && selectedItem && isOpen ? props.items.indexOf(selectedItem) : highlightedIndex,
isOpen: isOpen,
selectedItem: selectedItem,
inputValue: inputValue
};
}
function getHighlightedIndexOnOpen(props, state, offset) {
var items = props.items,
initialHighlightedIndex = props.initialHighlightedIndex,
defaultHighlightedIndex = props.defaultHighlightedIndex;
var selectedItem = state.selectedItem,
highlightedIndex = state.highlightedIndex;
if (items.length === 0) {
return -1;
}
// initialHighlightedIndex will give value to highlightedIndex on initial state only.
if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex) {
return initialHighlightedIndex;
}
if (defaultHighlightedIndex !== undefined) {
return defaultHighlightedIndex;
}
if (selectedItem) {
return items.indexOf(selectedItem);
}
if (offset === 0) {
return -1;
}
return offset < 0 ? items.length - 1 : 0;
}
/**
* Reuse the movement tracking of mouse and touch events.
*
* @param {boolean} isOpen Whether the dropdown is open or not.
* @param {Array<Object>} downshiftElementRefs Downshift element refs to track movement (toggleButton, menu etc.)
* @param {Object} environment Environment where component/hook exists.
* @param {Function} handleBlur Handler on blur from mouse or touch.
* @returns {Object} Ref containing whether mouseDown or touchMove event is happening
*/
function useMouseAndTouchTracker(isOpen, downshiftElementRefs, environment, handleBlur) {
var mouseAndTouchTrackersRef = react.useRef({
isMouseDown: false,
isTouchMove: false
});
react.useEffect(function () {
if ((environment == null ? void 0 : environment.addEventListener) == null) {
return;
}
// The same strategy for checking if a click occurred inside or outside downshift
// as in downshift.js.
var onMouseDown = function onMouseDown() {
mouseAndTouchTrackersRef.current.isMouseDown = true;
};
var onMouseUp = function onMouseUp(event) {
mouseAndTouchTrackersRef.current.isMouseDown = false;
if (isOpen && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
return ref.current;
}), environment)) {
handleBlur();
}
};
var onTouchStart = function onTouchStart() {
mouseAndTouchTrackersRef.current.isTouchMove = false;
};
var onTouchMove = function onTouchMove() {
mouseAndTouchTrackersRef.current.isTouchMove = true;
};
var onTouchEnd = function onTouchEnd(event) {
if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, downshiftElementRefs.map(function (ref) {
return ref.current;
}), environment, false)) {
handleBlur();
}
};
environment.addEventListener('mousedown', onMouseDown);
environment.addEventListener('mouseup', onMouseUp);
environment.addEventListener('touchstart', onTouchStart);
environment.addEventListener('touchmove', onTouchMove);
environment.addEventListener('touchend', onTouchEnd);
// eslint-disable-next-line consistent-return
return function cleanup() {
environment.removeEventListener('mousedown', onMouseDown);
environment.removeEventListener('mouseup', onMouseUp);
environment.removeEventListener('touchstart', onTouchStart);
environment.removeEventListener('touchmove', onTouchMove);
environment.removeEventListener('touchend', onTouchEnd);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen, environment]);
return mouseAndTouchTrackersRef;
}
/* istanbul ignore next */
// eslint-disable-next-line import/no-mutable-exports
var useGetterPropsCalledChecker = function useGetterPropsCalledChecker() {
return noop;
};
/**
* Custom hook that checks if getter props are called correctly.
*
* @param {...any} propKeys Getter prop names to be handled.
* @returns {Function} Setter function called inside getter props to set call information.
*/
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
useGetterPropsCalledChecker = function useGetterPropsCalledChecker() {
var isInitialMountRef = react.useRef(true);
for (var _len = arguments.length, propKeys = new Array(_len), _key = 0; _key < _len; _key++) {
propKeys[_key] = arguments[_key];
}
var getterPropsCalledRef = react.useRef(propKeys.reduce(function (acc, propKey) {
acc[propKey] = {};
return acc;
}, {}));
react.useEffect(function () {
Object.keys(getterPropsCalledRef.current).forEach(function (propKey) {
var propCallInfo = getterPropsCalledRef.current[propKey];
if (isInitialMountRef.current) {
if (!Object.keys(propCallInfo).length) {
// eslint-disable-next-line no-console
console.error("downshift: You forgot to call the " + propKey + " getter function on your component / element.");
return;
}
}
var suppressRefError = propCallInfo.suppressRefError,
refKey = propCallInfo.refKey,
elementRef = propCallInfo.elementRef;
if ((!elementRef || !elementRef.current) && !suppressRefError) {
// eslint-disable-next-line no-console
console.error("downshift: The ref prop \"" + refKey + "\" from " + propKey + " was not applied correctly on your element.");
}
});
isInitialMountRef.current = false;
});
var setGetterPropCallInfo = react.useCallback(function (propKey, suppressRefError, refKey, elementRef) {
getterPropsCalledRef.current[propKey] = {
suppressRefError: suppressRefError,
refKey: refKey,
elementRef: elementRef
};
}, []);
return setGetterPropCallInfo;
};
}
function useA11yMessageSetter(getA11yMessage, dependencyArray, _ref2) {
var isInitialMount = _ref2.isInitialMount,
highlightedIndex = _ref2.highlightedIndex,
items = _ref2.items,
environment = _ref2.environment,
rest = _objectWithoutPropertiesLoose__default["default"](_ref2, _excluded$3);
// Sets a11y status message on changes in state.
react.useEffect(function () {
if (isInitialMount || false) {
return;
}
updateA11yStatus(function () {
return getA11yMessage(_extends__default["default"]({
highlightedIndex: highlightedIndex,
highlightedItem: items[highlightedIndex],
resultCount: items.length
}, rest));
}, environment.document);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencyArray);
}
function useScrollIntoView(_ref3) {
var highlightedIndex = _ref3.highlightedIndex,
isOpen = _ref3.isOpen,
itemRefs = _ref3.itemRefs,
getItemNodeFromIndex = _ref3.getItemNodeFromIndex,
menuElement = _ref3.menuElement,
scrollIntoViewProp = _ref3.scrollIntoView;
// used not to scroll on highlight by mouse.
var shouldScrollRef = react.useRef(true);
// Scroll on highlighted item if change comes from keyboard.
useIsomorphicLayoutEffect(function () {
if (highlightedIndex < 0 || !isOpen || !Object.keys(itemRefs.current).length) {
return;
}
if (shouldScrollRef.current === false) {
shouldScrollRef.current = true;
} else {
scrollIntoViewProp(getItemNodeFromIndex(highlightedIndex), menuElement);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [highlightedIndex]);
return shouldScrollRef;
}
// eslint-disable-next-line import/no-mutable-exports
var useControlPropsValidator = noop;
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
useControlPropsValidator = function useControlPropsValidator(_ref4) {
var isInitialMount = _ref4.isInitialMount,
props = _ref4.props,
state = _ref4.state;
// used for checking when props are moving from controlled to uncontrolled.
var prevPropsRef = react.useRef(props);
react.useEffect(function () {
if (isInitialMount) {
return;
}
validateControlledUnchanged(state, prevPropsRef.current, props);
prevPropsRef.current = props;
}, [state, props, isInitialMount]);
};
}
/**
* Handles selection on Enter / Alt + ArrowUp. Closes the menu and resets the highlighted index, unless there is a highlighted.
* In that case, selects the item and resets to defaults for open state and highlighted idex.
* @param {Object} props The useCombobox props.
* @param {number} highlightedIndex The index from the state.
* @param {boolean} inputValue Also return the input value for state.
* @returns The changes for the state.
*/
function getChangesOnSelection(props, highlightedIndex, inputValue) {
var _props$items;
if (inputValue === void 0) {
inputValue = true;
}
var shouldSelect = ((_props$items = props.items) == null ? void 0 : _props$items.length) && highlightedIndex >= 0;
return _extends__default["default"]({
isOpen: false,
highlightedIndex: -1
}, shouldSelect && _extends__default["default"]({
selectedItem: props.items[highlightedIndex],
isOpen: getDefaultValue$1(props, 'isOpen'),
highlightedIndex: getDefaultValue$1(props, 'highlightedIndex')
}, inputValue && {
inputValue: props.itemToString(props.items[highlightedIndex])
}));
}
function downshiftCommonReducer(state, action, stateChangeTypes) {
var type = action.type,
props = action.props;
var changes;
switch (type) {
case stateChangeTypes.ItemMouseMove:
changes = {
highlightedIndex: action.disabled ? -1 : action.index
};
break;
case stateChangeTypes.MenuMouseLeave:
changes = {
highlightedIndex: -1
};
break;
case stateChangeTypes.ToggleButtonClick:
case stateChangeTypes.FunctionToggleMenu:
changes = {
isOpen: !state.isOpen,
highlightedIndex: state.isOpen ? -1 : getHighlightedIndexOnOpen(props, state, 0)
};
break;
case stateChangeTypes.FunctionOpenMenu:
changes = {
isOpen: true,
highlightedIndex: getHighlightedIndexOnOpen(props, state, 0)
};
break;
case stateChangeTypes.FunctionCloseMenu:
changes = {
isOpen: false
};
break;
case stateChangeTypes.FunctionSetHighlightedIndex:
changes = {
highlightedIndex: action.highlightedIndex
};
break;
case stateChangeTypes.FunctionSetInputValue:
changes = {
inputValue: action.inputValue
};
break;
case stateChangeTypes.FunctionReset:
changes = {
highlightedIndex: getDefaultValue$1(props, 'highlightedIndex'),
isOpen: getDefaultValue$1(props, 'isOpen'),
selectedItem: getDefaultValue$1(props, 'selectedItem'),
inputValue: getDefaultValue$1(props, 'inputValue')
};
break;
default:
throw new Error('Reducer called without proper action type.');
}
return _extends__default["default"]({}, state, changes);
}
/* eslint-enable complexity */
function getItemIndexByCharacterKey(_a) {
var keysSoFar = _a.keysSoFar, highlightedIndex = _a.highlightedIndex, items = _a.items, itemToString = _a.itemToString, getItemNodeFromIndex = _a.getItemNodeFromIndex;
var lowerCasedKeysSoFar = keysSoFar.toLowerCase();
for (var index = 0; index < items.length; index++) {
// if we already have a search query in progress, we also consider the current highlighted item.
var offsetIndex = (index + highlightedIndex + (keysSoFar.length < 2 ? 1 : 0)) % items.length;
var item = items[offsetIndex];
if (item !== undefined &&
itemToString(item).toLowerCase().startsWith(lowerCasedKeysSoFar)) {
var element = getItemNodeFromIndex(offsetIndex);
if (!(element === null || element === void 0 ? void 0 : element.hasAttribute('disabled'))) {
return offsetIndex;
}
}
}
return highlightedIndex;
}
var propTypes$2 = {
items: PropTypes__default["default"].array.isRequired,
itemToString: PropTypes__default["default"].func,
getA11yStatusMessage: PropTypes__default["default"].func,
getA11ySelectionMessage: PropTypes__default["default"].func,
highlightedIndex: PropTypes__default["default"].number,
defaultHighlightedIndex: PropTypes__default["default"].number,
initialHighlightedIndex: PropTypes__default["default"].number,
isOpen: PropTypes__default["default"].bool,
defaultIsOpen: PropTypes__default["default"].bool,
initialIsOpen: PropTypes__default["default"].bool,
selectedItem: PropTypes__default["default"].any,
initialSelectedItem: PropTypes__default["default"].any,
defaultSelectedItem: PropTypes__default["default"].any,
id: PropTypes__default["default"].string,
labelId: PropTypes__default["default"].string,
menuId: PropTypes__default["default"].string,
getItemId: PropTypes__default["default"].func,
toggleButtonId: PropTypes__default["default"].string,
stateReducer: PropTypes__default["default"].func,
onSelectedItemChange: PropTypes__default["default"].func,
onHighlightedIndexChange: PropTypes__default["default"].func,
onStateChange: PropTypes__default["default"].func,
onIsOpenChange: PropTypes__default["default"].func,
environment: PropTypes__default["default"].shape({
addEventListener: PropTypes__default["default"].func,
removeEventListener: PropTypes__default["default"].func,
document: PropTypes__default["default"].shape({
getElementById: PropTypes__default["default"].func,
activeElement: PropTypes__default["default"].any,
body: PropTypes__default["default"].any
})
})
};
/**
* Default implementation for status message. Only added when menu is open.
* Will specift if there are results in the list, and if so, how many,
* and what keys are relevant.
*
* @param {Object} param the downshift state and other relevant properties
* @return {String} the a11y status message
*/
function getA11yStatusMessage(_a) {
var isOpen = _a.isOpen, resultCount = _a.resultCount, previousResultCount = _a.previousResultCount;
if (!isOpen) {
return '';
}
if (!resultCount) {
return 'No results are available.';
}
if (resultCount !== previousResultCount) {
return "".concat(resultCount, " result").concat(resultCount === 1 ? ' is' : 's are', " available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.");
}
return '';
}
var defaultProps$2 = tslib.__assign(tslib.__assign({}, defaultProps$3), { getA11yStatusMessage: getA11yStatusMessage });
// eslint-disable-next-line import/no-mutable-exports
var validatePropTypes$2 = noop;
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
validatePropTypes$2 = function (options, caller) {
PropTypes__default["default"].checkPropTypes(propTypes$2, options, 'prop', caller.name);
};
}
var ToggleButtonClick$1 = process.env.NODE_ENV !== "production" ? '__togglebutton_click__' : 0;
var ToggleButtonKeyDownArrowDown = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_arrow_down__' : 1;
var ToggleButtonKeyDownArrowUp = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_arrow_up__' : 2;
var ToggleButtonKeyDownCharacter = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_character__' : 3;
var ToggleButtonKeyDownEscape = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_escape__' : 4;
var ToggleButtonKeyDownHome = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_home__' : 5;
var ToggleButtonKeyDownEnd = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_end__' : 6;
var ToggleButtonKeyDownEnter = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_enter__' : 7;
var ToggleButtonKeyDownSpaceButton = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_space_button__' : 8;
var ToggleButtonKeyDownPageUp = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_page_up__' : 9;
var ToggleButtonKeyDownPageDown = process.env.NODE_ENV !== "production" ? '__togglebutton_keydown_page_down__' : 10;
var ToggleButtonBlur = process.env.NODE_ENV !== "production" ? '__togglebutton_blur__' : 11;
var MenuMouseLeave$1 = process.env.NODE_ENV !== "production" ? '__menu_mouse_leave__' : 12;
var ItemMouseMove$1 = process.env.NODE_ENV !== "production" ? '__item_mouse_move__' : 13;
var ItemClick$1 = process.env.NODE_ENV !== "production" ? '__item_click__' : 14;
var FunctionToggleMenu$1 = process.env.NODE_ENV !== "production" ? '__function_toggle_menu__' : 15;
var FunctionOpenMenu$1 = process.env.NODE_ENV !== "production" ? '__function_open_menu__' : 16;
var FunctionCloseMenu$1 = process.env.NODE_ENV !== "production" ? '__function_close_menu__' : 17;
var FunctionSetHighlightedIndex$1 = process.env.NODE_ENV !== "production" ? '__function_set_highlighted_index__' : 18;
var FunctionSelectItem$1 = process.env.NODE_ENV !== "production" ? '__function_select_item__' : 19;
var FunctionSetInputValue$1 = process.env.NODE_ENV !== "production" ? '__function_set_input_value__' : 20;
var FunctionReset$2 = process.env.NODE_ENV !== "production" ? '__function_reset__' : 21;
var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
__proto__: null,
ToggleButtonClick: ToggleButtonClick$1,
ToggleButtonKeyDownArrowDown: ToggleButtonKeyDownArrowDown,
ToggleButtonKeyDownArrowUp: ToggleButtonKeyDownArrowUp,
ToggleButtonKeyDownCharacter: ToggleButtonKeyDownCharacter,
ToggleButtonKeyDownEscape: ToggleButtonKeyDownEscape,
ToggleButtonKeyDownHome: ToggleButtonKeyDownHome,
ToggleButtonKeyDownEnd: ToggleButtonKeyDownEnd,
ToggleButtonKeyDownEnter: ToggleButtonKeyDownEnter,
ToggleButtonKeyDownSpaceButton: ToggleButtonKeyDownSpaceButton,
ToggleButtonKeyDownPageUp: ToggleButtonKeyDownPageUp,
ToggleButtonKeyDownPageDown: ToggleButtonKeyDownPageDown,
ToggleButtonBlur: ToggleButtonBlur,
MenuMouseLeave: MenuMouseLeave$1,
ItemMouseMove: ItemMouseMove$1,
ItemClick: ItemClick$1,
FunctionToggleMenu: FunctionToggleMenu$1,
FunctionOpenMenu: FunctionOpenMenu$1,
FunctionCloseMenu: FunctionCloseMenu$1,
FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
FunctionSelectItem: FunctionSelectItem$1,
FunctionSetInputValue: FunctionSetInputValue$1,
FunctionReset: FunctionReset$2
});
/* eslint-disable complexity */
function downshiftSelectReducer(state, action) {
var _props$items;
var type = action.type,
props = action.props,
altKey = action.altKey;
var changes;
switch (type) {
case ItemClick$1:
changes = {
isOpen: getDefaultValue$1(props, 'isOpen'),
highlightedIndex: getDefaultValue$1(props, 'highlightedIndex'),
selectedItem: props.items[action.index]
};
break;
case ToggleButtonKeyDownCharacter:
{
var lowercasedKey = action.key;
var inputValue = "" + state.inputValue + lowercasedKey;
var prevHighlightedIndex = !state.isOpen && state.selectedItem ? props.items.indexOf(state.selectedItem) : state.highlightedIndex;
var highlightedIndex = getItemIndexByCharacterKey({
keysSoFar: inputValue,
highlightedIndex: prevHighlightedIndex,
items: props.items,
itemToString: props.itemToString,
getItemNodeFromIndex: action.getItemNodeFromIndex
});
changes = {
inputValue: inputValue,
highlightedIndex: highlightedIndex,
isOpen: true
};
}
break;
case ToggleButtonKeyDownArrowDown:
{
var _highlightedIndex = state.isOpen ? getNextWrappingIndex(1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, false) : altKey && state.selectedItem == null ? -1 : getHighlightedIndexOnOpen(props, state, 1);
changes = {
highlightedIndex: _highlightedIndex,
isOpen: true
};
}
break;
case ToggleButtonKeyDownArrowUp:
if (state.isOpen && altKey) {
changes = getChangesOnSelection(props, state.highlightedIndex, false);
} else {
var _highlightedIndex2 = state.isOpen ? getNextWrappingIndex(-1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, false) : getHighlightedIndexOnOpen(props, state, -1);
changes = {
highlightedIndex: _highlightedIndex2,
isOpen: true
};
}
break;
// only triggered when menu is open.
case ToggleButtonKeyDownEnter:
case ToggleButtonKeyDownSpaceButton:
changes = getChangesOnSelection(props, state.highlightedIndex, false);
break;
case ToggleButtonKeyDownHome:
changes = {
highlightedIndex: getNextNonDisabledIndex(1, 0, props.items.length, action.getItemNodeFromIndex, false),
isOpen: true
};
break;
case ToggleButtonKeyDownEnd:
changes = {
highlightedIndex: getNextNonDisabledIndex(-1, props.items.length - 1, props.items.length, action.getItemNodeFromIndex, false),
isOpen: true
};
break;
case ToggleButtonKeyDownPageUp:
changes = {
highlightedIndex: getNextWrappingIndex(-10, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, false)
};
break;
case ToggleButtonKeyDownPageDown:
changes = {
highlightedIndex: getNextWrappingIndex(10, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, false)
};
break;
case ToggleButtonKeyDownEscape:
changes = {
isOpen: false,
highlightedIndex: -1
};
break;
case ToggleButtonBlur:
changes = _extends__default["default"]({
isOpen: false,
highlightedIndex: -1
}, state.highlightedIndex >= 0 && ((_props$items = props.items) == null ? void 0 : _props$items.length) && {
selectedItem: props.items[state.highlightedIndex]
});
break;
case FunctionSelectItem$1:
changes = {
selectedItem: action.selectedItem
};
break;
default:
return downshiftCommonReducer(state, action, stateChangeTypes$2);
}
return _extends__default["default"]({}, state, changes);
}
/* eslint-enable complexity */
var _excluded$2 = ["onMouseLeave", "refKey", "onKeyDown", "onBlur", "ref"],
_excluded2$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref"],
_excluded3$1 = ["item", "index", "onMouseMove", "onClick", "onPress", "refKey", "ref", "disabled"];
useSelect.stateChangeTypes = stateChangeTypes$2;
function useSelect(userProps) {
if (userProps === void 0) {
userProps = {};
}
validatePropTypes$2(userProps, useSelect);
// Props defaults and destructuring.
var props = _extends__default["default"]({}, defaultProps$2, userProps);
var items = props.items,
scrollIntoView = props.scrollIntoView,
environment = props.environment,
itemToString = props.itemToString,
getA11ySelectionMessage = props.getA11ySelectionMessage,
getA11yStatusMessage = props.getA11yStatusMessage;
// Initial state depending on controlled props.
var initialState = getInitialState$2(props);
var _useControlledReducer = useControlledReducer$1(downshiftSelectReducer, initialState, props),
state = _useControlledReducer[0],
dispatch = _useControlledReducer[1];
var isOpen = state.isOpen,
highlightedIndex = state.highlightedIndex,
selectedItem = state.selectedItem,
inputValue = state.inputValue;
// Element efs.
var toggleButtonRef = react.useRef(null);
var menuRef = react.useRef(null);
var itemRefs = react.useRef({});
// used to keep the inputValue clearTimeout object between renders.
var clearTimeoutRef = react.useRef(null);
// prevent id re-generation between renders.
var elementIds = useElementIds(props);
// used to keep track of how many items we had on previous cycle.
var previousResultCountRef = react.useRef();
var isInitialMountRef = react.useRef(true);
// utility callback to get item element.
var latest = useLatestRef({
state: state,
props: props
});
// Some utils.
var getItemNodeFromIndex = react.useCallback(function (index) {
return itemRefs.current[elementIds.getItemId(index)];
}, [elementIds]);
// Effects.
// Sets a11y status message on changes in state.
useA11yMessageSetter(getA11yStatusMessage, [isOpen, highlightedIndex, inputValue, items], _extends__default["default"]({
isInitialMount: isInitialMountRef.current,
previousResultCount: previousResultCountRef.current,
items: items,
environment: environment,
itemToString: itemToString
}, state));
// Sets a11y status message on changes in selectedItem.
useA11yMessageSetter(getA11ySelectionMessage, [selectedItem], _extends__default["default"]({
isInitialMount: isInitialMountRef.current,
previousResultCount: previousResultCountRef.current,
items: items,
environment: environment,
itemToString: itemToString
}, state));
// Scroll on highlighted item if change comes from keyboard.
var shouldScrollRef = useScrollIntoView({
menuElement: menuRef.current,
highlightedIndex: highlightedIndex,
isOpen: isOpen,
itemRefs: itemRefs,
scrollIntoView: scrollIntoView,
getItemNodeFromIndex: getItemNodeFromIndex
});
// Sets cleanup for the keysSoFar callback, debounded after 500ms.
react.useEffect(function () {
// init the clean function here as we need access to dispatch.
clearTimeoutRef.current = debounce(function (outerDispatch) {
outerDispatch({
type: FunctionSetInputValue$1,
inputValue: ''
});
}, 500);
// Cancel any pending debounced calls on mount
return function () {
clearTimeoutRef.current.cancel();
};
}, []);
// Invokes the keysSoFar callback set up above.
react.useEffect(function () {
if (!inputValue) {
return;
}
clearTimeoutRef.current(dispatch);
}, [dispatch, inputValue]);
useControlPropsValidator({
isInitialMount: isInitialMountRef.current,
props: props,
state: state
});
react.useEffect(function () {
if (isInitialMountRef.current) {
return;
}
previousResultCountRef.current = items.length;
});
// Add mouse/touch events to document.
var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [menuRef, toggleButtonRef], environment, function () {
dispatch({
type: ToggleButtonBlur
});
});
var setGetterPropCallInfo = useGetterPropsCalledChecker('getMenuProps', 'getToggleButtonProps');
// Make initial ref false.
react.useEffect(function () {
isInitialMountRef.current = false;
return function () {
isInitialMountRef.current = true;
};
}, []);
// Reset itemRefs on close.
react.useEffect(function () {
if (!isOpen) {
itemRefs.current = {};
}
}, [isOpen]);
// Event handler functions.
var toggleButtonKeyDownHandlers = react.useMemo(function () {
return {
ArrowDown: function ArrowDown(event) {
event.preventDefault();
dispatch({
type: ToggleButtonKeyDownArrowDown,
getItemNodeFromIndex: getItemNodeFromIndex,
altKey: event.altKey
});
},
ArrowUp: function ArrowUp(event) {
event.preventDefault();
dispatch({
type: ToggleButtonKeyDownArrowUp,
getItemNodeFromIndex: getItemNodeFromIndex,
altKey: event.altKey
});
},
Home: function Home(event) {
event.preventDefault();
dispatch({
type: ToggleButtonKeyDownHome,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
End: function End(event) {
event.preventDefault();
dispatch({
type: ToggleButtonKeyDownEnd,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
Escape: function Escape() {
if (latest.current.state.isOpen) {
dispatch({
type: ToggleButtonKeyDownEscape
});
}
},
Enter: function Enter(event) {
event.preventDefault();
dispatch({
type: latest.current.state.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
});
},
PageUp: function PageUp(event) {
if (latest.current.state.isOpen) {
event.preventDefault();
dispatch({
type: ToggleButtonKeyDownPageUp,
getItemNodeFromIndex: getItemNodeFromIndex
});
}
},
PageDown: function PageDown(event) {
if (latest.current.state.isOpen) {
event.preventDefault();
dispatch({
type: ToggleButtonKeyDownPageDown,
getItemNodeFromIndex: getItemNodeFromIndex
});
}
},
' ': function _(event) {
event.preventDefault();
var currentState = latest.current.state;
if (!currentState.isOpen) {
dispatch({
type: ToggleButtonClick$1
});
return;
}
if (currentState.inputValue) {
dispatch({
type: ToggleButtonKeyDownCharacter,
key: ' ',
getItemNodeFromIndex: getItemNodeFromIndex
});
} else {
dispatch({
type: ToggleButtonKeyDownSpaceButton
});
}
}
};
}, [dispatch, getItemNodeFromIndex, latest]);
// Action functions.
var toggleMenu = react.useCallback(function () {
dispatch({
type: FunctionToggleMenu$1
});
}, [dispatch]);
var closeMenu = react.useCallback(function () {
dispatch({
type: FunctionCloseMenu$1
});
}, [dispatch]);
var openMenu = react.useCallback(function () {
dispatch({
type: FunctionOpenMenu$1
});
}, [dispatch]);
var setHighlightedIndex = react.useCallback(function (newHighlightedIndex) {
dispatch({
type: FunctionSetHighlightedIndex$1,
highlightedIndex: newHighlightedIndex
});
}, [dispatch]);
var selectItem = react.useCallback(function (newSelectedItem) {
dispatch({
type: FunctionSelectItem$1,
selectedItem: newSelectedItem
});
}, [dispatch]);
var reset = react.useCallback(function () {
dispatch({
type: FunctionReset$2
});
}, [dispatch]);
var setInputValue = react.useCallback(function (newInputValue) {
dispatch({
type: FunctionSetInputValue$1,
inputValue: newInputValue
});
}, [dispatch]);
// Getter functions.
var getLabelProps = react.useCallback(function (labelProps) {
return _extends__default["default"]({
id: elementIds.labelId,
htmlFor: elementIds.toggleButtonId
}, labelProps);
}, [elementIds]);
var getMenuProps = react.useCallback(function (_temp, _temp2) {
var _extends2;
var _ref = _temp === void 0 ? {} : _temp,
onMouseLeave = _ref.onMouseLeave,
_ref$refKey = _ref.refKey,
refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey;
_ref.onKeyDown;
_ref.onBlur;
var ref = _ref.ref,
rest = _objectWithoutPropertiesLoose__default["default"](_ref, _excluded$2);
var _ref2 = _temp2 === void 0 ? {} : _temp2,
_ref2$suppressRefErro = _ref2.suppressRefError,
suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
var menuHandleMouseLeave = function menuHandleMouseLeave() {
dispatch({
type: MenuMouseLeave$1
});
};
setGetterPropCallInfo('getMenuProps', suppressRefError, refKey, menuRef);
return _extends__default["default"]((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
menuRef.current = menuNode;
}), _extends2.id = elementIds.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = rest && rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends2.onMouseLeave = callAllEventHandlers(onMouseLeave, menuHandleMouseLeave), _extends2), rest);
}, [dispatch, setGetterPropCallInfo, elementIds]);
var getToggleButtonProps = react.useCallback(function (_temp3, _temp4) {
var _extends3;
var _ref3 = _temp3 === void 0 ? {} : _temp3,
onBlur = _ref3.onBlur;
_ref3.onClick;
var onPress = _ref3.onPress;
_ref3.onKeyDown;
var _ref3$refKey = _ref3.refKey,
refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
ref = _ref3.ref,
rest = _objectWithoutPropertiesLoose__default["default"](_ref3, _excluded2$2);
var _ref4 = _temp4 === void 0 ? {} : _temp4,
_ref4$suppressRefErro = _ref4.suppressRefError,
suppressRefError = _ref4$suppressRefErro === void 0 ? false : _ref4$suppressRefErro;
var latestState = latest.current.state;
var toggleButtonHandleClick = function toggleButtonHandleClick() {
dispatch({
type: ToggleButtonClick$1
});
};
var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
if (latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
dispatch({
type: ToggleButtonBlur
});
}
};
var toggleProps = _extends__default["default"]((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
toggleButtonRef.current = toggleButtonNode;
}), _extends3['aria-activedescendant'] = latestState.isOpen && latestState.highlightedIndex > -1 ? elementIds.getItemId(latestState.highlightedIndex) : '', _extends3['aria-controls'] = elementIds.menuId, _extends3['aria-expanded'] = latest.current.state.isOpen, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-labelledby'] = rest && rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends3.id = elementIds.toggleButtonId, _extends3.role = 'combobox', _extends3.tabIndex = 0, _extends3.onBlur = callAllEventHandlers(onBlur, toggleButtonHandleBlur), _extends3), rest);
if (!rest.disabled) {
/* istanbul ignore if (react-native) */
{
toggleProps.onPress = callAllEventHandlers(onPress, toggleButtonHandleClick);
}
}
setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
return toggleProps;
}, [latest, elementIds, setGetterPropCallInfo, dispatch, mouseAndTouchTrackersRef, toggleButtonKeyDownHandlers, getItemNodeFromIndex]);
var getItemProps = react.useCallback(function (_temp5) {
var _extends4;
var _ref5 = _temp5 === void 0 ? {} : _temp5,
itemProp = _ref5.item,
indexProp = _ref5.index,
onMouseMove = _ref5.onMouseMove;
_ref5.onClick;
var onPress = _ref5.onPress,
_ref5$refKey = _ref5.refKey,
refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
ref = _ref5.ref,
disabled = _ref5.disabled,
rest = _objectWithoutPropertiesLoose__default["default"](_ref5, _excluded3$1);
var _latest$current = latest.current,
latestState = _latest$current.state,
latestProps = _latest$current.props;
var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
item = _getItemAndIndex[0],
index = _getItemAndIndex[1];
var itemHandleMouseMove = function itemHandleMouseMove() {
if (index === latestState.highlightedIndex) {
return;
}
shouldScrollRef.current = false;
dispatch({
type: ItemMouseMove$1,
index: index,
disabled: disabled
});
};
var itemHandleClick = function itemHandleClick() {
dispatch({
type: ItemClick$1,
index: index
});
};
var itemProps = _extends__default["default"]((_extends4 = {
disabled: disabled,
role: 'option',
'aria-selected': "" + (item === selectedItem),
id: elementIds.getItemId(index)
}, _extends4[refKey] = handleRefs(ref, function (itemNode) {
if (itemNode) {
itemRefs.current[elementIds.getItemId(index)] = itemNode;
}
}), _extends4), rest);
if (!disabled) {
/* istanbul ignore next (react-native) */
{
itemProps.onPress = callAllEventHandlers(onPress, itemHandleClick);
}
}
itemProps.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove);
return itemProps;
}, [latest, selectedItem, elementIds, shouldScrollRef, dispatch]);
return {
// prop getters.
getToggleButtonProps: getToggleButtonProps,
getLabelProps: getLabelProps,
getMenuProps: getMenuProps,
getItemProps: getItemProps,
// actions.
toggleMenu: toggleMenu,
openMenu: openMenu,
closeMenu: closeMenu,
setHighlightedIndex: setHighlightedIndex,
selectItem: selectItem,
reset: reset,
setInputValue: setInputValue,
// state.
highlightedIndex: highlightedIndex,
isOpen: isOpen,
selectedItem: selectedItem,
inputValue: inputValue
};
}
var InputKeyDownArrowDown = process.env.NODE_ENV !== "production" ? '__input_keydown_arrow_down__' : 0;
var InputKeyDownArrowUp = process.env.NODE_ENV !== "production" ? '__input_keydown_arrow_up__' : 1;
var InputKeyDownEscape = process.env.NODE_ENV !== "production" ? '__input_keydown_escape__' : 2;
var InputKeyDownHome = process.env.NODE_ENV !== "production" ? '__input_keydown_home__' : 3;
var InputKeyDownEnd = process.env.NODE_ENV !== "production" ? '__input_keydown_end__' : 4;
var InputKeyDownPageUp = process.env.NODE_ENV !== "production" ? '__input_keydown_page_up__' : 5;
var InputKeyDownPageDown = process.env.NODE_ENV !== "production" ? '__input_keydown_page_down__' : 6;
var InputKeyDownEnter = process.env.NODE_ENV !== "production" ? '__input_keydown_enter__' : 7;
var InputChange = process.env.NODE_ENV !== "production" ? '__input_change__' : 8;
var InputBlur = process.env.NODE_ENV !== "production" ? '__input_blur__' : 9;
var InputFocus = process.env.NODE_ENV !== "production" ? '__input_focus__' : 10;
var MenuMouseLeave = process.env.NODE_ENV !== "production" ? '__menu_mouse_leave__' : 11;
var ItemMouseMove = process.env.NODE_ENV !== "production" ? '__item_mouse_move__' : 12;
var ItemClick = process.env.NODE_ENV !== "production" ? '__item_click__' : 13;
var ToggleButtonClick = process.env.NODE_ENV !== "production" ? '__togglebutton_click__' : 14;
var FunctionToggleMenu = process.env.NODE_ENV !== "production" ? '__function_toggle_menu__' : 15;
var FunctionOpenMenu = process.env.NODE_ENV !== "production" ? '__function_open_menu__' : 16;
var FunctionCloseMenu = process.env.NODE_ENV !== "production" ? '__function_close_menu__' : 17;
var FunctionSetHighlightedIndex = process.env.NODE_ENV !== "production" ? '__function_set_highlighted_index__' : 18;
var FunctionSelectItem = process.env.NODE_ENV !== "production" ? '__function_select_item__' : 19;
var FunctionSetInputValue = process.env.NODE_ENV !== "production" ? '__function_set_input_value__' : 20;
var FunctionReset$1 = process.env.NODE_ENV !== "production" ? '__function_reset__' : 21;
var ControlledPropUpdatedSelectedItem = process.env.NODE_ENV !== "production" ? '__controlled_prop_updated_selected_item__' : 22;
var stateChangeTypes$1 = /*#__PURE__*/Object.freeze({
__proto__: null,
InputKeyDownArrowDown: InputKeyDownArrowDown,
InputKeyDownArrowUp: InputKeyDownArrowUp,
InputKeyDownEscape: InputKeyDownEscape,
InputKeyDownHome: InputKeyDownHome,
InputKeyDownEnd: InputKeyDownEnd,
InputKeyDownPageUp: InputKeyDownPageUp,
InputKeyDownPageDown: InputKeyDownPageDown,
InputKeyDownEnter: InputKeyDownEnter,
InputChange: InputChange,
InputBlur: InputBlur,
InputFocus: InputFocus,
MenuMouseLeave: MenuMouseLeave,
ItemMouseMove: ItemMouseMove,
ItemClick: ItemClick,
ToggleButtonClick: ToggleButtonClick,
FunctionToggleMenu: FunctionToggleMenu,
FunctionOpenMenu: FunctionOpenMenu,
FunctionCloseMenu: FunctionCloseMenu,
FunctionSetHighlightedIndex: FunctionSetHighlightedIndex,
FunctionSelectItem: FunctionSelectItem,
FunctionSetInputValue: FunctionSetInputValue,
FunctionReset: FunctionReset$1,
ControlledPropUpdatedSelectedItem: ControlledPropUpdatedSelectedItem
});
function getInitialState$1(props) {
var initialState = getInitialState$2(props);
var selectedItem = initialState.selectedItem;
var inputValue = initialState.inputValue;
if (inputValue === '' && selectedItem && props.defaultInputValue === undefined && props.initialInputValue === undefined && props.inputValue === undefined) {
inputValue = props.itemToString(selectedItem);
}
return _extends__default["default"]({}, initialState, {
inputValue: inputValue
});
}
var propTypes$1 = {
items: PropTypes__default["default"].array.isRequired,
itemToString: PropTypes__default["default"].func,
selectedItemChanged: PropTypes__default["default"].func,
getA11yStatusMessage: PropTypes__default["default"].func,
getA11ySelectionMessage: PropTypes__default["default"].func,
highlightedIndex: PropTypes__default["default"].number,
defaultHighlightedIndex: PropTypes__default["default"].number,
initialHighlightedIndex: PropTypes__default["default"].number,
isOpen: PropTypes__default["default"].bool,
defaultIsOpen: PropTypes__default["default"].bool,
initialIsOpen: PropTypes__default["default"].bool,
selectedItem: PropTypes__default["default"].any,
initialSelectedItem: PropTypes__default["default"].any,
defaultSelectedItem: PropTypes__default["default"].any,
inputValue: PropTypes__default["default"].string,
defaultInputValue: PropTypes__default["default"].string,
initialInputValue: PropTypes__default["default"].string,
id: PropTypes__default["default"].string,
labelId: PropTypes__default["default"].string,
menuId: PropTypes__default["default"].string,
getItemId: PropTypes__default["default"].func,
inputId: PropTypes__default["default"].string,
toggleButtonId: PropTypes__default["default"].string,
stateReducer: PropTypes__default["default"].func,
onSelectedItemChange: PropTypes__default["default"].func,
onHighlightedIndexChange: PropTypes__default["default"].func,
onStateChange: PropTypes__default["default"].func,
onIsOpenChange: PropTypes__default["default"].func,
onInputValueChange: PropTypes__default["default"].func,
environment: PropTypes__default["default"].shape({
addEventListener: PropTypes__default["default"].func,
removeEventListener: PropTypes__default["default"].func,
document: PropTypes__default["default"].shape({
getElementById: PropTypes__default["default"].func,
activeElement: PropTypes__default["default"].any,
body: PropTypes__default["default"].any
})
})
};
/**
* The useCombobox version of useControlledReducer, which also
* checks if the controlled prop selectedItem changed between
* renders. If so, it will also update inputValue with its
* string equivalent. It uses the common useEnhancedReducer to
* compute the rest of the state.
*
* @param {Function} reducer Reducer function from downshift.
* @param {Object} initialState Initial state of the hook.
* @param {Object} props The hook props.
* @returns {Array} An array with the state and an action dispatcher.
*/
function useControlledReducer(reducer, initialState, props) {
var previousSelectedItemRef = react.useRef();
var _useEnhancedReducer = useEnhancedReducer(reducer, initialState, props),
state = _useEnhancedReducer[0],
dispatch = _useEnhancedReducer[1];
// ToDo: if needed, make same approach as selectedItemChanged from Downshift.
react.useEffect(function () {
if (!isControlledProp(props, 'selectedItem')) {
return;
}
if (props.selectedItemChanged(previousSelectedItemRef.current, props.selectedItem)) {
dispatch({
type: ControlledPropUpdatedSelectedItem,
inputValue: props.itemToString(props.selectedItem)
});
}
previousSelectedItemRef.current = state.selectedItem === previousSelectedItemRef.current ? props.selectedItem : state.selectedItem;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state.selectedItem, props.selectedItem]);
return [getState(state, props), dispatch];
}
// eslint-disable-next-line import/no-mutable-exports
var validatePropTypes$1 = noop;
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
validatePropTypes$1 = function validatePropTypes(options, caller) {
PropTypes__default["default"].checkPropTypes(propTypes$1, options, 'prop', caller.name);
};
}
var defaultProps$1 = _extends__default["default"]({}, defaultProps$3, {
selectedItemChanged: function selectedItemChanged(prevItem, item) {
return prevItem !== item;
},
getA11yStatusMessage: getA11yStatusMessage$1
});
/* eslint-disable complexity */
function downshiftUseComboboxReducer(state, action) {
var _props$items;
var type = action.type,
props = action.props,
altKey = action.altKey;
var changes;
switch (type) {
case ItemClick:
changes = {
isOpen: getDefaultValue$1(props, 'isOpen'),
highlightedIndex: getDefaultValue$1(props, 'highlightedIndex'),
selectedItem: props.items[action.index],
inputValue: props.itemToString(props.items[action.index])
};
break;
case InputKeyDownArrowDown:
if (state.isOpen) {
changes = {
highlightedIndex: getNextWrappingIndex(1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, true)
};
} else {
changes = {
highlightedIndex: altKey && state.selectedItem == null ? -1 : getHighlightedIndexOnOpen(props, state, 1, action.getItemNodeFromIndex),
isOpen: props.items.length >= 0
};
}
break;
case InputKeyDownArrowUp:
if (state.isOpen) {
if (altKey) {
changes = getChangesOnSelection(props, state.highlightedIndex);
} else {
changes = {
highlightedIndex: getNextWrappingIndex(-1, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, true)
};
}
} else {
changes = {
highlightedIndex: getHighlightedIndexOnOpen(props, state, -1, action.getItemNodeFromIndex),
isOpen: props.items.length >= 0
};
}
break;
case InputKeyDownEnter:
changes = getChangesOnSelection(props, state.highlightedIndex);
break;
case InputKeyDownEscape:
changes = _extends__default["default"]({
isOpen: false,
highlightedIndex: -1
}, !state.isOpen && {
selectedItem: null,
inputValue: ''
});
break;
case InputKeyDownPageUp:
changes = {
highlightedIndex: getNextWrappingIndex(-10, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, false)
};
break;
case InputKeyDownPageDown:
changes = {
highlightedIndex: getNextWrappingIndex(10, state.highlightedIndex, props.items.length, action.getItemNodeFromIndex, false)
};
break;
case InputKeyDownHome:
changes = {
highlightedIndex: getNextNonDisabledIndex(1, 0, props.items.length, action.getItemNodeFromIndex, false)
};
break;
case InputKeyDownEnd:
changes = {
highlightedIndex: getNextNonDisabledIndex(-1, props.items.length - 1, props.items.length, action.getItemNodeFromIndex, false)
};
break;
case InputBlur:
changes = _extends__default["default"]({
isOpen: false,
highlightedIndex: -1
}, state.highlightedIndex >= 0 && ((_props$items = props.items) == null ? void 0 : _props$items.length) && action.selectItem && {
selectedItem: props.items[state.highlightedIndex],
inputValue: props.itemToString(props.items[state.highlightedIndex])
});
break;
case InputChange:
changes = {
isOpen: true,
highlightedIndex: getDefaultValue$1(props, 'highlightedIndex'),
inputValue: action.inputValue
};
break;
case InputFocus:
changes = {
isOpen: true,
highlightedIndex: getHighlightedIndexOnOpen(props, state, 0)
};
break;
case FunctionSelectItem:
changes = {
selectedItem: action.selectedItem,
inputValue: props.itemToString(action.selectedItem)
};
break;
case ControlledPropUpdatedSelectedItem:
changes = {
inputValue: action.inputValue
};
break;
default:
return downshiftCommonReducer(state, action, stateChangeTypes$1);
}
return _extends__default["default"]({}, state, changes);
}
/* eslint-enable complexity */
var _excluded$1 = ["onMouseLeave", "refKey", "ref"],
_excluded2$1 = ["item", "index", "refKey", "ref", "onMouseMove", "onMouseDown", "onClick", "onPress", "disabled"],
_excluded3 = ["onClick", "onPress", "refKey", "ref"],
_excluded4 = ["onKeyDown", "onChange", "onInput", "onFocus", "onBlur", "onChangeText", "refKey", "ref"];
useCombobox.stateChangeTypes = stateChangeTypes$1;
function useCombobox(userProps) {
if (userProps === void 0) {
userProps = {};
}
validatePropTypes$1(userProps, useCombobox);
// Props defaults and destructuring.
var props = _extends__default["default"]({}, defaultProps$1, userProps);
var initialIsOpen = props.initialIsOpen,
defaultIsOpen = props.defaultIsOpen,
items = props.items,
scrollIntoView = props.scrollIntoView,
environment = props.environment,
getA11yStatusMessage = props.getA11yStatusMessage,
getA11ySelectionMessage = props.getA11ySelectionMessage,
itemToString = props.itemToString;
// Initial state depending on controlled props.
var initialState = getInitialState$1(props);
var _useControlledReducer = useControlledReducer(downshiftUseComboboxReducer, initialState, props),
state = _useControlledReducer[0],
dispatch = _useControlledReducer[1];
var isOpen = state.isOpen,
highlightedIndex = state.highlightedIndex,
selectedItem = state.selectedItem,
inputValue = state.inputValue;
// Element refs.
var menuRef = react.useRef(null);
var itemRefs = react.useRef({});
var inputRef = react.useRef(null);
var toggleButtonRef = react.useRef(null);
var isInitialMountRef = react.useRef(true);
// prevent id re-generation between renders.
var elementIds = useElementIds(props);
// used to keep track of how many items we had on previous cycle.
var previousResultCountRef = react.useRef();
// utility callback to get item element.
var latest = useLatestRef({
state: state,
props: props
});
var getItemNodeFromIndex = react.useCallback(function (index) {
return itemRefs.current[elementIds.getItemId(index)];
}, [elementIds]);
// Effects.
// Sets a11y status message on changes in state.
useA11yMessageSetter(getA11yStatusMessage, [isOpen, highlightedIndex, inputValue, items], _extends__default["default"]({
isInitialMount: isInitialMountRef.current,
previousResultCount: previousResultCountRef.current,
items: items,
environment: environment,
itemToString: itemToString
}, state));
// Sets a11y status message on changes in selectedItem.
useA11yMessageSetter(getA11ySelectionMessage, [selectedItem], _extends__default["default"]({
isInitialMount: isInitialMountRef.current,
previousResultCount: previousResultCountRef.current,
items: items,
environment: environment,
itemToString: itemToString
}, state));
// Scroll on highlighted item if change comes from keyboard.
var shouldScrollRef = useScrollIntoView({
menuElement: menuRef.current,
highlightedIndex: highlightedIndex,
isOpen: isOpen,
itemRefs: itemRefs,
scrollIntoView: scrollIntoView,
getItemNodeFromIndex: getItemNodeFromIndex
});
useControlPropsValidator({
isInitialMount: isInitialMountRef.current,
props: props,
state: state
});
// Focus the input on first render if required.
react.useEffect(function () {
var focusOnOpen = initialIsOpen || defaultIsOpen || isOpen;
if (focusOnOpen && inputRef.current) {
inputRef.current.focus();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
react.useEffect(function () {
if (isInitialMountRef.current) {
return;
}
previousResultCountRef.current = items.length;
});
// Add mouse/touch events to document.
var mouseAndTouchTrackersRef = useMouseAndTouchTracker(isOpen, [inputRef, menuRef, toggleButtonRef], environment, function () {
dispatch({
type: InputBlur,
selectItem: false
});
});
var setGetterPropCallInfo = useGetterPropsCalledChecker('getInputProps', 'getMenuProps');
// Make initial ref false.
react.useEffect(function () {
isInitialMountRef.current = false;
return function () {
isInitialMountRef.current = true;
};
}, []);
// Reset itemRefs on close.
react.useEffect(function () {
var _environment$document;
if (!isOpen) {
itemRefs.current = {};
} else if (((_environment$document = environment.document) == null ? void 0 : _environment$document.activeElement) !== inputRef.current) {
var _inputRef$current;
inputRef == null || (_inputRef$current = inputRef.current) == null ? void 0 : _inputRef$current.focus();
}
}, [isOpen, environment]);
/* Event handler functions */
var inputKeyDownHandlers = react.useMemo(function () {
return {
ArrowDown: function ArrowDown(event) {
event.preventDefault();
dispatch({
type: InputKeyDownArrowDown,
altKey: event.altKey,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
ArrowUp: function ArrowUp(event) {
event.preventDefault();
dispatch({
type: InputKeyDownArrowUp,
altKey: event.altKey,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
Home: function Home(event) {
if (!latest.current.state.isOpen) {
return;
}
event.preventDefault();
dispatch({
type: InputKeyDownHome,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
End: function End(event) {
if (!latest.current.state.isOpen) {
return;
}
event.preventDefault();
dispatch({
type: InputKeyDownEnd,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
Escape: function Escape(event) {
var latestState = latest.current.state;
if (latestState.isOpen || latestState.inputValue || latestState.selectedItem || latestState.highlightedIndex > -1) {
event.preventDefault();
dispatch({
type: InputKeyDownEscape
});
}
},
Enter: function Enter(event) {
var latestState = latest.current.state;
// if closed or no highlighted index, do nothing.
if (!latestState.isOpen || event.which === 229 // if IME composing, wait for next Enter keydown event.
) {
return;
}
event.preventDefault();
dispatch({
type: InputKeyDownEnter,
getItemNodeFromIndex: getItemNodeFromIndex
});
},
PageUp: function PageUp(event) {
if (latest.current.state.isOpen) {
event.preventDefault();
dispatch({
type: InputKeyDownPageUp,
getItemNodeFromIndex: getItemNodeFromIndex
});
}
},
PageDown: function PageDown(event) {
if (latest.current.state.isOpen) {
event.preventDefault();
dispatch({
type: InputKeyDownPageDown,
getItemNodeFromIndex: getItemNodeFromIndex
});
}
}
};
}, [dispatch, latest, getItemNodeFromIndex]);
// Getter props.
var getLabelProps = react.useCallback(function (labelProps) {
return _extends__default["default"]({
id: elementIds.labelId,
htmlFor: elementIds.inputId
}, labelProps);
}, [elementIds]);
var getMenuProps = react.useCallback(function (_temp, _temp2) {
var _extends2;
var _ref = _temp === void 0 ? {} : _temp,
onMouseLeave = _ref.onMouseLeave,
_ref$refKey = _ref.refKey,
refKey = _ref$refKey === void 0 ? 'ref' : _ref$refKey,
ref = _ref.ref,
rest = _objectWithoutPropertiesLoose__default["default"](_ref, _excluded$1);
var _ref2 = _temp2 === void 0 ? {} : _temp2,
_ref2$suppressRefErro = _ref2.suppressRefError,
suppressRefError = _ref2$suppressRefErro === void 0 ? false : _ref2$suppressRefErro;
setGetterPropCallInfo('getMenuProps', suppressRefError, refKey, menuRef);
return _extends__default["default"]((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
menuRef.current = menuNode;
}), _extends2.id = elementIds.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = rest && rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends2.onMouseLeave = callAllEventHandlers(onMouseLeave, function () {
dispatch({
type: MenuMouseLeave
});
}), _extends2), rest);
}, [dispatch, setGetterPropCallInfo, elementIds]);
var getItemProps = react.useCallback(function (_temp3) {
var _extends3, _ref4;
var _ref3 = _temp3 === void 0 ? {} : _temp3,
itemProp = _ref3.item,
indexProp = _ref3.index,
_ref3$refKey = _ref3.refKey,
refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
ref = _ref3.ref,
onMouseMove = _ref3.onMouseMove,
onMouseDown = _ref3.onMouseDown,
onClick = _ref3.onClick;
_ref3.onPress;
var disabled = _ref3.disabled,
rest = _objectWithoutPropertiesLoose__default["default"](_ref3, _excluded2$1);
var _latest$current = latest.current,
latestProps = _latest$current.props,
latestState = _latest$current.state;
var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
index = _getItemAndIndex[1];
var onSelectKey = /* istanbul ignore next (react-native) */'onPress' ;
var customClickHandler = onClick;
var itemHandleMouseMove = function itemHandleMouseMove() {
if (index === latestState.highlightedIndex) {
return;
}
shouldScrollRef.current = false;
dispatch({
type: ItemMouseMove,
index: index,
disabled: disabled
});
};
var itemHandleClick = function itemHandleClick() {
dispatch({
type: ItemClick,
index: index
});
};
var itemHandleMouseDown = function itemHandleMouseDown(e) {
return e.preventDefault();
};
return _extends__default["default"]((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (itemNode) {
if (itemNode) {
itemRefs.current[elementIds.getItemId(index)] = itemNode;
}
}), _extends3.disabled = disabled, _extends3.role = 'option', _extends3['aria-selected'] = "" + (index === latestState.highlightedIndex), _extends3.id = elementIds.getItemId(index), _extends3), !disabled && (_ref4 = {}, _ref4[onSelectKey] = callAllEventHandlers(customClickHandler, itemHandleClick), _ref4), {
onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
}, rest);
}, [dispatch, latest, shouldScrollRef, elementIds]);
var getToggleButtonProps = react.useCallback(function (_temp4) {
var _extends4;
var _ref5 = _temp4 === void 0 ? {} : _temp4;
_ref5.onClick;
var onPress = _ref5.onPress,
_ref5$refKey = _ref5.refKey,
refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
ref = _ref5.ref,
rest = _objectWithoutPropertiesLoose__default["default"](_ref5, _excluded3);
var latestState = latest.current.state;
var toggleButtonHandleClick = function toggleButtonHandleClick() {
dispatch({
type: ToggleButtonClick
});
};
return _extends__default["default"]((_extends4 = {}, _extends4[refKey] = handleRefs(ref, function (toggleButtonNode) {
toggleButtonRef.current = toggleButtonNode;
}), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = latestState.isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !rest.disabled && _extends__default["default"]({}, /* istanbul ignore next (react-native) */{
onPress: callAllEventHandlers(onPress, toggleButtonHandleClick)
} ), rest);
}, [dispatch, latest, elementIds]);
var getInputProps = react.useCallback(function (_temp5, _temp6) {
var _extends5;
var _ref6 = _temp5 === void 0 ? {} : _temp5,
onKeyDown = _ref6.onKeyDown,
onChange = _ref6.onChange,
onInput = _ref6.onInput,
onFocus = _ref6.onFocus,
onBlur = _ref6.onBlur;
_ref6.onChangeText;
var _ref6$refKey = _ref6.refKey,
refKey = _ref6$refKey === void 0 ? 'ref' : _ref6$refKey,
ref = _ref6.ref,
rest = _objectWithoutPropertiesLoose__default["default"](_ref6, _excluded4);
var _ref7 = _temp6 === void 0 ? {} : _temp6,
_ref7$suppressRefErro = _ref7.suppressRefError,
suppressRefError = _ref7$suppressRefErro === void 0 ? false : _ref7$suppressRefErro;
setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef);
var latestState = latest.current.state;
var inputHandleKeyDown = function inputHandleKeyDown(event) {
var key = normalizeArrowKey(event);
if (key && inputKeyDownHandlers[key]) {
inputKeyDownHandlers[key](event);
}
};
var inputHandleChange = function inputHandleChange(event) {
dispatch({
type: InputChange,
inputValue: /* istanbul ignore next (react-native) */event.nativeEvent.text
});
};
var inputHandleBlur = function inputHandleBlur(event) {
/* istanbul ignore else */
if (latestState.isOpen && !mouseAndTouchTrackersRef.current.isMouseDown) {
var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
dispatch({
type: InputBlur,
selectItem: !isBlurByTabChange
});
}
};
var inputHandleFocus = function inputHandleFocus() {
if (!latestState.isOpen) {
dispatch({
type: InputFocus
});
}
};
/* istanbul ignore next (preact) */
var onChangeKey = 'onChange';
var eventHandlers = {};
if (!rest.disabled) {
var _eventHandlers;
eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, inputHandleBlur), _eventHandlers.onFocus = callAllEventHandlers(onFocus, inputHandleFocus), _eventHandlers);
}
return _extends__default["default"]((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
inputRef.current = inputNode;
}), _extends5['aria-activedescendant'] = latestState.isOpen && latestState.highlightedIndex > -1 ? elementIds.getItemId(latestState.highlightedIndex) : '', _extends5['aria-autocomplete'] = 'list', _extends5['aria-controls'] = elementIds.menuId, _extends5['aria-expanded'] = latestState.isOpen, _extends5['aria-labelledby'] = rest && rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends5.autoComplete = 'off', _extends5.id = elementIds.inputId, _extends5.role = 'combobox', _extends5.value = latestState.inputValue, _extends5), eventHandlers, rest);
}, [setGetterPropCallInfo, latest, elementIds, inputKeyDownHandlers, dispatch, mouseAndTouchTrackersRef, environment]);
// returns
var toggleMenu = react.useCallback(function () {
dispatch({
type: FunctionToggleMenu
});
}, [dispatch]);
var closeMenu = react.useCallback(function () {
dispatch({
type: FunctionCloseMenu
});
}, [dispatch]);
var openMenu = react.useCallback(function () {
dispatch({
type: FunctionOpenMenu
});
}, [dispatch]);
var setHighlightedIndex = react.useCallback(function (newHighlightedIndex) {
dispatch({
type: FunctionSetHighlightedIndex,
highlightedIndex: newHighlightedIndex
});
}, [dispatch]);
var selectItem = react.useCallback(function (newSelectedItem) {
dispatch({
type: FunctionSelectItem,
selectedItem: newSelectedItem
});
}, [dispatch]);
var setInputValue = react.useCallback(function (newInputValue) {
dispatch({
type: FunctionSetInputValue,
inputValue: newInputValue
});
}, [dispatch]);
var reset = react.useCallback(function () {
dispatch({
type: FunctionReset$1
});
}, [dispatch]);
return {
// prop getters.
getItemProps: getItemProps,
getLabelProps: getLabelProps,
getMenuProps: getMenuProps,
getInputProps: getInputProps,
getToggleButtonProps: getToggleButtonProps,
// actions.
toggleMenu: toggleMenu,
openMenu: openMenu,
closeMenu: closeMenu,
setHighlightedIndex: setHighlightedIndex,
setInputValue: setInputValue,
selectItem: selectItem,
reset: reset,
// state.
highlightedIndex: highlightedIndex,
isOpen: isOpen,
selectedItem: selectedItem,
inputValue: inputValue
};
}
var defaultStateValues = {
activeIndex: -1,
selectedItems: []
};
/**
* Returns the initial value for a state key in the following order:
* 1. controlled prop, 2. initial prop, 3. default prop, 4. default
* value from Downshift.
*
* @param {Object} props Props passed to the hook.
* @param {string} propKey Props key to generate the value for.
* @returns {any} The initial value for that prop.
*/
function getInitialValue(props, propKey) {
return getInitialValue$1(props, propKey, defaultStateValues);
}
/**
* Returns the default value for a state key in the following order:
* 1. controlled prop, 2. default prop, 3. default value from Downshift.
*
* @param {Object} props Props passed to the hook.
* @param {string} propKey Props key to generate the value for.
* @returns {any} The initial value for that prop.
*/
function getDefaultValue(props, propKey) {
return getDefaultValue$1(props, propKey, defaultStateValues);
}
/**
* Gets the initial state based on the provided props. It uses initial, default
* and controlled props related to state in order to compute the initial value.
*
* @param {Object} props Props passed to the hook.
* @returns {Object} The initial state.
*/
function getInitialState(props) {
var activeIndex = getInitialValue(props, 'activeIndex');
var selectedItems = getInitialValue(props, 'selectedItems');
return {
activeIndex: activeIndex,
selectedItems: selectedItems
};
}
/**
* Returns true if dropdown keydown operation is permitted. Should not be
* allowed on keydown with modifier keys (ctrl, alt, shift, meta), on
* input element with text content that is either highlighted or selection
* cursor is not at the starting position.
*
* @param {KeyboardEvent} event The event from keydown.
* @returns {boolean} Whether the operation is allowed.
*/
function isKeyDownOperationPermitted(event) {
if (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey) {
return false;
}
var element = event.target;
if (element instanceof HTMLInputElement &&
// if element is a text input
element.value !== '' && (
// and we have text in it
// and cursor is either not at the start or is currently highlighting text.
element.selectionStart !== 0 || element.selectionEnd !== 0)) {
return false;
}
return true;
}
/**
* Returns a message to be added to aria-live region when item is removed.
*
* @param {Object} selectionParameters Parameters required to build the message.
* @returns {string} The a11y message.
*/
function getA11yRemovalMessage(selectionParameters) {
var removedSelectedItem = selectionParameters.removedSelectedItem,
itemToStringLocal = selectionParameters.itemToString;
return itemToStringLocal(removedSelectedItem) + " has been removed.";
}
var propTypes = {
selectedItems: PropTypes__default["default"].array,
initialSelectedItems: PropTypes__default["default"].array,
defaultSelectedItems: PropTypes__default["default"].array,
itemToString: PropTypes__default["default"].func,
getA11yRemovalMessage: PropTypes__default["default"].func,
stateReducer: PropTypes__default["default"].func,
activeIndex: PropTypes__default["default"].number,
initialActiveIndex: PropTypes__default["default"].number,
defaultActiveIndex: PropTypes__default["default"].number,
onActiveIndexChange: PropTypes__default["default"].func,
onSelectedItemsChange: PropTypes__default["default"].func,
keyNavigationNext: PropTypes__default["default"].string,
keyNavigationPrevious: PropTypes__default["default"].string,
environment: PropTypes__default["default"].shape({
addEventListener: PropTypes__default["default"].func,
removeEventListener: PropTypes__default["default"].func,
document: PropTypes__default["default"].shape({
getElementById: PropTypes__default["default"].func,
activeElement: PropTypes__default["default"].any,
body: PropTypes__default["default"].any
})
})
};
var defaultProps = {
itemToString: defaultProps$3.itemToString,
stateReducer: defaultProps$3.stateReducer,
environment: defaultProps$3.environment,
getA11yRemovalMessage: getA11yRemovalMessage,
keyNavigationNext: 'ArrowRight',
keyNavigationPrevious: 'ArrowLeft'
};
// eslint-disable-next-line import/no-mutable-exports
var validatePropTypes = noop;
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
validatePropTypes = function validatePropTypes(options, caller) {
PropTypes__default["default"].checkPropTypes(propTypes, options, 'prop', caller.name);
};
}
var SelectedItemClick = process.env.NODE_ENV !== "production" ? '__selected_item_click__' : 0;
var SelectedItemKeyDownDelete = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_delete__' : 1;
var SelectedItemKeyDownBackspace = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_backspace__' : 2;
var SelectedItemKeyDownNavigationNext = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_navigation_next__' : 3;
var SelectedItemKeyDownNavigationPrevious = process.env.NODE_ENV !== "production" ? '__selected_item_keydown_navigation_previous__' : 4;
var DropdownKeyDownNavigationPrevious = process.env.NODE_ENV !== "production" ? '__dropdown_keydown_navigation_previous__' : 5;
var DropdownKeyDownBackspace = process.env.NODE_ENV !== "production" ? '__dropdown_keydown_backspace__' : 6;
var DropdownClick = process.env.NODE_ENV !== "production" ? '__dropdown_click__' : 7;
var FunctionAddSelectedItem = process.env.NODE_ENV !== "production" ? '__function_add_selected_item__' : 8;
var FunctionRemoveSelectedItem = process.env.NODE_ENV !== "production" ? '__function_remove_selected_item__' : 9;
var FunctionSetSelectedItems = process.env.NODE_ENV !== "production" ? '__function_set_selected_items__' : 10;
var FunctionSetActiveIndex = process.env.NODE_ENV !== "production" ? '__function_set_active_index__' : 11;
var FunctionReset = process.env.NODE_ENV !== "production" ? '__function_reset__' : 12;
var stateChangeTypes = /*#__PURE__*/Object.freeze({
__proto__: null,
SelectedItemClick: SelectedItemClick,
SelectedItemKeyDownDelete: SelectedItemKeyDownDelete,
SelectedItemKeyDownBackspace: SelectedItemKeyDownBackspace,
SelectedItemKeyDownNavigationNext: SelectedItemKeyDownNavigationNext,
SelectedItemKeyDownNavigationPrevious: SelectedItemKeyDownNavigationPrevious,
DropdownKeyDownNavigationPrevious: DropdownKeyDownNavigationPrevious,
DropdownKeyDownBackspace: DropdownKeyDownBackspace,
DropdownClick: DropdownClick,
FunctionAddSelectedItem: FunctionAddSelectedItem,
FunctionRemoveSelectedItem: FunctionRemoveSelectedItem,
FunctionSetSelectedItems: FunctionSetSelectedItems,
FunctionSetActiveIndex: FunctionSetActiveIndex,
FunctionReset: FunctionReset
});
/* eslint-disable complexity */
function downshiftMultipleSelectionReducer(state, action) {
var type = action.type,
index = action.index,
props = action.props,
selectedItem = action.selectedItem;
var activeIndex = state.activeIndex,
selectedItems = state.selectedItems;
var changes;
switch (type) {
case SelectedItemClick:
changes = {
activeIndex: index
};
break;
case SelectedItemKeyDownNavigationPrevious:
changes = {
activeIndex: activeIndex - 1 < 0 ? 0 : activeIndex - 1
};
break;
case SelectedItemKeyDownNavigationNext:
changes = {
activeIndex: activeIndex + 1 >= selectedItems.length ? -1 : activeIndex + 1
};
break;
case SelectedItemKeyDownBackspace:
case SelectedItemKeyDownDelete:
{
if (activeIndex < 0) {
break;
}
var newActiveIndex = activeIndex;
if (selectedItems.length === 1) {
newActiveIndex = -1;
} else if (activeIndex === selectedItems.length - 1) {
newActiveIndex = selectedItems.length - 2;
}
changes = _extends__default["default"]({
selectedItems: [].concat(selectedItems.slice(0, activeIndex), selectedItems.slice(activeIndex + 1))
}, {
activeIndex: newActiveIndex
});
break;
}
case DropdownKeyDownNavigationPrevious:
changes = {
activeIndex: selectedItems.length - 1
};
break;
case DropdownKeyDownBackspace:
changes = {
selectedItems: selectedItems.slice(0, selectedItems.length - 1)
};
break;
case FunctionAddSelectedItem:
changes = {
selectedItems: [].concat(selectedItems, [selectedItem])
};
break;
case DropdownClick:
changes = {
activeIndex: -1
};
break;
case FunctionRemoveSelectedItem:
{
var _newActiveIndex = activeIndex;
var selectedItemIndex = selectedItems.indexOf(selectedItem);
if (selectedItemIndex < 0) {
break;
}
if (selectedItems.length === 1) {
_newActiveIndex = -1;
} else if (selectedItemIndex === selectedItems.length - 1) {
_newActiveIndex = selectedItems.length - 2;
}
changes = {
selectedItems: [].concat(selectedItems.slice(0, selectedItemIndex), selectedItems.slice(selectedItemIndex + 1)),
activeIndex: _newActiveIndex
};
break;
}
case FunctionSetSelectedItems:
{
var newSelectedItems = action.selectedItems;
changes = {
selectedItems: newSelectedItems
};
break;
}
case FunctionSetActiveIndex:
{
var _newActiveIndex2 = action.activeIndex;
changes = {
activeIndex: _newActiveIndex2
};
break;
}
case FunctionReset:
changes = {
activeIndex: getDefaultValue(props, 'activeIndex'),
selectedItems: getDefaultValue(props, 'selectedItems')
};
break;
default:
throw new Error('Reducer called without proper action type.');
}
return _extends__default["default"]({}, state, changes);
}
var _excluded = ["refKey", "ref", "onClick", "onKeyDown", "selectedItem", "index"],
_excluded2 = ["refKey", "ref", "onKeyDown", "onClick", "preventKeyAction"];
useMultipleSelection.stateChangeTypes = stateChangeTypes;
function useMultipleSelection(userProps) {
if (userProps === void 0) {
userProps = {};
}
validatePropTypes(userProps, useMultipleSelection);
// Props defaults and destructuring.
var props = _extends__default["default"]({}, defaultProps, userProps);
var getA11yRemovalMessage = props.getA11yRemovalMessage,
itemToString = props.itemToString,
environment = props.environment,
keyNavigationNext = props.keyNavigationNext,
keyNavigationPrevious = props.keyNavigationPrevious;
// Reducer init.
var _useControlledReducer = useControlledReducer$1(downshiftMultipleSelectionReducer, getInitialState(props), props),
state = _useControlledReducer[0],
dispatch = _useControlledReducer[1];
var activeIndex = state.activeIndex,
selectedItems = state.selectedItems;
// Refs.
var isInitialMountRef = react.useRef(true);
var dropdownRef = react.useRef(null);
var previousSelectedItemsRef = react.useRef(selectedItems);
var selectedItemRefs = react.useRef();
selectedItemRefs.current = [];
var latest = useLatestRef({
state: state,
props: props
});
// Effects.
/* Sets a11y status message on changes in selectedItem. */
react.useEffect(function () {
if (isInitialMountRef.current || false) {
return;
}
if (selectedItems.length < previousSelectedItemsRef.current.length) {
var removedSelectedItem = previousSelectedItemsRef.current.find(function (item) {
return selectedItems.indexOf(item) < 0;
});
setStatus(getA11yRemovalMessage({
itemToString: itemToString,
resultCount: selectedItems.length,
removedSelectedItem: removedSelectedItem,
activeIndex: activeIndex,
activeSelectedItem: selectedItems[activeIndex]
}), environment.document);
}
previousSelectedItemsRef.current = selectedItems;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedItems.length]);
// Sets focus on active item.
react.useEffect(function () {
if (isInitialMountRef.current) {
return;
}
if (activeIndex === -1 && dropdownRef.current) {
dropdownRef.current.focus();
} else if (selectedItemRefs.current[activeIndex]) {
selectedItemRefs.current[activeIndex].focus();
}
}, [activeIndex]);
useControlPropsValidator({
isInitialMount: isInitialMountRef.current,
props: props,
state: state
});
var setGetterPropCallInfo = useGetterPropsCalledChecker('getDropdownProps');
// Make initial ref false.
react.useEffect(function () {
isInitialMountRef.current = false;
return function () {
isInitialMountRef.current = true;
};
}, []);
// Event handler functions.
var selectedItemKeyDownHandlers = react.useMemo(function () {
var _ref;
return _ref = {}, _ref[keyNavigationPrevious] = function () {
dispatch({
type: SelectedItemKeyDownNavigationPrevious
});
}, _ref[keyNavigationNext] = function () {
dispatch({
type: SelectedItemKeyDownNavigationNext
});
}, _ref.Delete = function Delete() {
dispatch({
type: SelectedItemKeyDownDelete
});
}, _ref.Backspace = function Backspace() {
dispatch({
type: SelectedItemKeyDownBackspace
});
}, _ref;
}, [dispatch, keyNavigationNext, keyNavigationPrevious]);
var dropdownKeyDownHandlers = react.useMemo(function () {
var _ref2;
return _ref2 = {}, _ref2[keyNavigationPrevious] = function (event) {
if (isKeyDownOperationPermitted(event)) {
dispatch({
type: DropdownKeyDownNavigationPrevious
});
}
}, _ref2.Backspace = function Backspace(event) {
if (isKeyDownOperationPermitted(event)) {
dispatch({
type: DropdownKeyDownBackspace
});
}
}, _ref2;
}, [dispatch, keyNavigationPrevious]);
// Getter props.
var getSelectedItemProps = react.useCallback(function (_temp) {
var _extends2;
var _ref3 = _temp === void 0 ? {} : _temp,
_ref3$refKey = _ref3.refKey,
refKey = _ref3$refKey === void 0 ? 'ref' : _ref3$refKey,
ref = _ref3.ref,
onClick = _ref3.onClick,
onKeyDown = _ref3.onKeyDown,
selectedItemProp = _ref3.selectedItem,
indexProp = _ref3.index,
rest = _objectWithoutPropertiesLoose__default["default"](_ref3, _excluded);
var latestState = latest.current.state;
var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, latestState.selectedItems, 'Pass either item or index to getSelectedItemProps!'),
index = _getItemAndIndex[1];
var isFocusable = index > -1 && index === latestState.activeIndex;
var selectedItemHandleClick = function selectedItemHandleClick() {
dispatch({
type: SelectedItemClick,
index: index
});
};
var selectedItemHandleKeyDown = function selectedItemHandleKeyDown(event) {
var key = normalizeArrowKey(event);
if (key && selectedItemKeyDownHandlers[key]) {
selectedItemKeyDownHandlers[key](event);
}
};
return _extends__default["default"]((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (selectedItemNode) {
if (selectedItemNode) {
selectedItemRefs.current.push(selectedItemNode);
}
}), _extends2.tabIndex = isFocusable ? 0 : -1, _extends2.onClick = callAllEventHandlers(onClick, selectedItemHandleClick), _extends2.onKeyDown = callAllEventHandlers(onKeyDown, selectedItemHandleKeyDown), _extends2), rest);
}, [dispatch, latest, selectedItemKeyDownHandlers]);
var getDropdownProps = react.useCallback(function (_temp2, _temp3) {
var _extends3;
var _ref4 = _temp2 === void 0 ? {} : _temp2,
_ref4$refKey = _ref4.refKey,
refKey = _ref4$refKey === void 0 ? 'ref' : _ref4$refKey,
ref = _ref4.ref,
onKeyDown = _ref4.onKeyDown,
onClick = _ref4.onClick,
_ref4$preventKeyActio = _ref4.preventKeyAction,
preventKeyAction = _ref4$preventKeyActio === void 0 ? false : _ref4$preventKeyActio,
rest = _objectWithoutPropertiesLoose__default["default"](_ref4, _excluded2);
var _ref5 = _temp3 === void 0 ? {} : _temp3,
_ref5$suppressRefErro = _ref5.suppressRefError,
suppressRefError = _ref5$suppressRefErro === void 0 ? false : _ref5$suppressRefErro;
setGetterPropCallInfo('getDropdownProps', suppressRefError, refKey, dropdownRef);
var dropdownHandleKeyDown = function dropdownHandleKeyDown(event) {
var key = normalizeArrowKey(event);
if (key && dropdownKeyDownHandlers[key]) {
dropdownKeyDownHandlers[key](event);
}
};
var dropdownHandleClick = function dropdownHandleClick() {
dispatch({
type: DropdownClick
});
};
return _extends__default["default"]((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (dropdownNode) {
if (dropdownNode) {
dropdownRef.current = dropdownNode;
}
}), _extends3), !preventKeyAction && {
onKeyDown: callAllEventHandlers(onKeyDown, dropdownHandleKeyDown),
onClick: callAllEventHandlers(onClick, dropdownHandleClick)
}, rest);
}, [dispatch, dropdownKeyDownHandlers, setGetterPropCallInfo]);
// returns
var addSelectedItem = react.useCallback(function (selectedItem) {
dispatch({
type: FunctionAddSelectedItem,
selectedItem: selectedItem
});
}, [dispatch]);
var removeSelectedItem = react.useCallback(function (selectedItem) {
dispatch({
type: FunctionRemoveSelectedItem,
selectedItem: selectedItem
});
}, [dispatch]);
var setSelectedItems = react.useCallback(function (newSelectedItems) {
dispatch({
type: FunctionSetSelectedItems,
selectedItems: newSelectedItems
});
}, [dispatch]);
var setActiveIndex = react.useCallback(function (newActiveIndex) {
dispatch({
type: FunctionSetActiveIndex,
activeIndex: newActiveIndex
});
}, [dispatch]);
var reset = react.useCallback(function () {
dispatch({
type: FunctionReset
});
}, [dispatch]);
return {
getSelectedItemProps: getSelectedItemProps,
getDropdownProps: getDropdownProps,
addSelectedItem: addSelectedItem,
removeSelectedItem: removeSelectedItem,
setSelectedItems: setSelectedItems,
setActiveIndex: setActiveIndex,
reset: reset,
selectedItems: selectedItems,
activeIndex: activeIndex
};
}
exports["default"] = Downshift$1;
exports.resetIdCounter = resetIdCounter;
exports.useCombobox = useCombobox;
exports.useMultipleSelection = useMultipleSelection;
exports.useSelect = useSelect;