UNPKG

downshift

Version:

🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

1,258 lines (1,172 loc) 182 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('preact/compat'), require('prop-types')) : typeof define === 'function' && define.amd ? define(['exports', 'preact/compat', 'prop-types'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Downshift = {}, global.React, global.PropTypes)); })(this, (function (exports, React, PropTypes) { 'use strict'; function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _inheritsLoose(t, o) { t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); } var reactIs = {exports: {}}; var reactIs_development = {}; /** * @license React * react-is.development.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredReactIs_development; function requireReactIs_development () { if (hasRequiredReactIs_development) return reactIs_development; hasRequiredReactIs_development = 1; { (function() { // ATTENTION // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' // The Symbol used to tag the ReactElement-like types. var REACT_ELEMENT_TYPE = Symbol.for('react.element'); var REACT_PORTAL_TYPE = Symbol.for('react.portal'); var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); var REACT_PROFILER_TYPE = Symbol.for('react.profiler'); var REACT_PROVIDER_TYPE = Symbol.for('react.provider'); var REACT_CONTEXT_TYPE = Symbol.for('react.context'); var REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context'); var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); var REACT_MEMO_TYPE = Symbol.for('react.memo'); var REACT_LAZY_TYPE = Symbol.for('react.lazy'); var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); // ----------------------------------------------------------------------------- var enableScopeAPI = false; // Experimental Create Event Handle API. var enableCacheElement = false; var enableTransitionTracing = false; // No known bugs, but needs performance testing var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber // stuff. Intended to enable React core members to more easily debug scheduling // issues in DEV builds. var enableDebugTracing = false; // Track which Fiber(s) schedule render work. var REACT_MODULE_REFERENCE; { REACT_MODULE_REFERENCE = Symbol.for('react.module.reference'); } function isValidElementType(type) { if (typeof type === 'string' || typeof type === 'function') { return true; } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill). if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) { return true; } if (typeof type === 'object' && type !== null) { if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object // types supported by any Flight configuration anywhere since // we don't know which Flight build this will end up being used // with. type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) { return true; } } return false; } function typeOf(object) { if (typeof object === 'object' && object !== null) { var $$typeof = object.$$typeof; switch ($$typeof) { case REACT_ELEMENT_TYPE: var type = object.type; switch (type) { case REACT_FRAGMENT_TYPE: case REACT_PROFILER_TYPE: case REACT_STRICT_MODE_TYPE: case REACT_SUSPENSE_TYPE: case REACT_SUSPENSE_LIST_TYPE: return type; default: var $$typeofType = type && type.$$typeof; switch ($$typeofType) { case REACT_SERVER_CONTEXT_TYPE: case REACT_CONTEXT_TYPE: case REACT_FORWARD_REF_TYPE: case REACT_LAZY_TYPE: case REACT_MEMO_TYPE: case REACT_PROVIDER_TYPE: return $$typeofType; default: return $$typeof; } } case REACT_PORTAL_TYPE: return $$typeof; } } return undefined; } var ContextConsumer = REACT_CONTEXT_TYPE; var ContextProvider = REACT_PROVIDER_TYPE; var Element = REACT_ELEMENT_TYPE; var ForwardRef = REACT_FORWARD_REF_TYPE; var Fragment = REACT_FRAGMENT_TYPE; var Lazy = REACT_LAZY_TYPE; var Memo = REACT_MEMO_TYPE; var Portal = REACT_PORTAL_TYPE; var Profiler = REACT_PROFILER_TYPE; var StrictMode = REACT_STRICT_MODE_TYPE; var Suspense = REACT_SUSPENSE_TYPE; var SuspenseList = REACT_SUSPENSE_LIST_TYPE; var hasWarnedAboutDeprecatedIsAsyncMode = false; var hasWarnedAboutDeprecatedIsConcurrentMode = false; // AsyncMode should be deprecated function isAsyncMode(object) { { if (!hasWarnedAboutDeprecatedIsAsyncMode) { hasWarnedAboutDeprecatedIsAsyncMode = true; // Using console['warn'] to evade Babel and ESLint console['warn']('The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 18+.'); } } return false; } function isConcurrentMode(object) { { if (!hasWarnedAboutDeprecatedIsConcurrentMode) { hasWarnedAboutDeprecatedIsConcurrentMode = true; // Using console['warn'] to evade Babel and ESLint console['warn']('The ReactIs.isConcurrentMode() alias has been deprecated, ' + 'and will be removed in React 18+.'); } } return false; } function isContextConsumer(object) { return typeOf(object) === REACT_CONTEXT_TYPE; } function isContextProvider(object) { return typeOf(object) === REACT_PROVIDER_TYPE; } function isElement(object) { return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; } function isForwardRef(object) { return typeOf(object) === REACT_FORWARD_REF_TYPE; } function isFragment(object) { return typeOf(object) === REACT_FRAGMENT_TYPE; } function isLazy(object) { return typeOf(object) === REACT_LAZY_TYPE; } function isMemo(object) { return typeOf(object) === REACT_MEMO_TYPE; } function isPortal(object) { return typeOf(object) === REACT_PORTAL_TYPE; } function isProfiler(object) { return typeOf(object) === REACT_PROFILER_TYPE; } function isStrictMode(object) { return typeOf(object) === REACT_STRICT_MODE_TYPE; } function isSuspense(object) { return typeOf(object) === REACT_SUSPENSE_TYPE; } function isSuspenseList(object) { return typeOf(object) === REACT_SUSPENSE_LIST_TYPE; } reactIs_development.ContextConsumer = ContextConsumer; reactIs_development.ContextProvider = ContextProvider; reactIs_development.Element = Element; reactIs_development.ForwardRef = ForwardRef; reactIs_development.Fragment = Fragment; reactIs_development.Lazy = Lazy; reactIs_development.Memo = Memo; reactIs_development.Portal = Portal; reactIs_development.Profiler = Profiler; reactIs_development.StrictMode = StrictMode; reactIs_development.Suspense = Suspense; reactIs_development.SuspenseList = SuspenseList; reactIs_development.isAsyncMode = isAsyncMode; reactIs_development.isConcurrentMode = isConcurrentMode; reactIs_development.isContextConsumer = isContextConsumer; reactIs_development.isContextProvider = isContextProvider; reactIs_development.isElement = isElement; reactIs_development.isForwardRef = isForwardRef; reactIs_development.isFragment = isFragment; reactIs_development.isLazy = isLazy; reactIs_development.isMemo = isMemo; reactIs_development.isPortal = isPortal; reactIs_development.isProfiler = isProfiler; reactIs_development.isStrictMode = isStrictMode; reactIs_development.isSuspense = isSuspense; reactIs_development.isSuspenseList = isSuspenseList; reactIs_development.isValidElementType = isValidElementType; reactIs_development.typeOf = typeOf; })(); } return reactIs_development; } var hasRequiredReactIs; function requireReactIs () { if (hasRequiredReactIs) return reactIs.exports; hasRequiredReactIs = 1; { reactIs.exports = requireReactIs_development(); } return reactIs.exports; } var reactIsExports = requireReactIs(); var unknown = '__autocomplete_unknown__' ; var mouseUp = '__autocomplete_mouseup__' ; var itemMouseEnter = '__autocomplete_item_mouseenter__' ; var keyDownArrowUp = '__autocomplete_keydown_arrow_up__' ; var keyDownArrowDown = '__autocomplete_keydown_arrow_down__' ; var keyDownEscape = '__autocomplete_keydown_escape__' ; var keyDownEnter = '__autocomplete_keydown_enter__' ; var keyDownHome = '__autocomplete_keydown_home__' ; var keyDownEnd = '__autocomplete_keydown_end__' ; var clickItem = '__autocomplete_click_item__' ; var blurInput = '__autocomplete_blur_input__' ; var changeInput = '__autocomplete_change_input__' ; var keyDownSpaceButton = '__autocomplete_keydown_space_button__' ; var clickButton = '__autocomplete_click_button__' ; var blurButton = '__autocomplete_blur_button__' ; var controlledPropUpdatedSelectedItem = '__autocomplete_controlled_prop_updated_selected_item__' ; var touchEnd = '__autocomplete_touchend__' ; var stateChangeTypes$4 = /*#__PURE__*/Object.freeze({ __proto__: null, blurButton: blurButton, blurInput: blurInput, changeInput: changeInput, clickButton: clickButton, clickItem: clickItem, controlledPropUpdatedSelectedItem: controlledPropUpdatedSelectedItem, itemMouseEnter: itemMouseEnter, keyDownArrowDown: keyDownArrowDown, keyDownArrowUp: keyDownArrowUp, keyDownEnd: keyDownEnd, keyDownEnter: keyDownEnter, keyDownEscape: keyDownEscape, keyDownHome: keyDownHome, keyDownSpaceButton: keyDownSpaceButton, mouseUp: mouseUp, touchEnd: touchEnd, unknown: unknown }); var idCounter = 0; /** * 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() { // istanbul ignore next if ('useId' in React__namespace) { console.warn("It is not necessary to call resetIdCounter when using React 18+"); return; } idCounter = 0; } function useLatestRef(val) { var ref = React__namespace.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; } function handleRefs$1() { for (var _len = arguments.length, refs = new Array(_len), _key = 0; _key < _len; _key++) { refs[_key] = arguments[_key]; } return function (node) { refs.forEach(function (ref) { if (typeof ref === 'function') { ref(node); } else if (ref) { ref.current = node; } }); }; } /** * This is intended to be used to compose event handlers. * They are executed in order until one of them sets * `event.preventDownshiftDefault = true`. * @param fns the event handler functions * @return the event handler to add to an element */ function callAllEventHandlers$1() { for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) { fns[_key] = arguments[_key]; } return function (event) { for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } return fns.some(function (fn) { if (fn) { fn.apply(void 0, [event].concat(args)); } return event.preventDownshiftDefault || event.hasOwnProperty('nativeEvent') && event.nativeEvent.preventDownshiftDefault; }); }; } /** * Simple debounce implementation. Will call the given * function once after the time given has passed since * it was last called. */ function debounce$1(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; } var cleanupStatus = debounce$1(function (document) { getStatusDiv(document).textContent = ''; }, 500); /** * Get the status node or create it if it does not already exist. */ function getStatusDiv(document) { var statusDiv = document.getElementById('a11y-status-message'); if (statusDiv) { return statusDiv; } statusDiv = document.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' }); document.body.appendChild(statusDiv); return statusDiv; } /** * Sets aria live status to a div element that's visually hidden. */ function setStatus(status, document) { if (!status || !document) { return; } var div = getStatusDiv(document); div.textContent = status; cleanupStatus(document); } /** * Removes the status element from the DOM */ function cleanupStatusDiv(document) { var statusDiv = document == null ? void 0 : document.getElementById('a11y-status-message'); if (statusDiv) { statusDiv.remove(); } } function noop() {} // eslint-disable-next-line import/no-mutable-exports var validatePropTypes$1 = noop; /* istanbul ignore next */ { validatePropTypes$1 = function validatePropTypes(options, caller, propTypes) { PropTypes.checkPropTypes(propTypes, options, 'prop', caller.name); }; } /** * 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 state The state of the component/hook. * @param props The props that may contain controlled values. * @returns The merged controlled state. */ function getState(state, props) { if (!props) { return state; } var keys = Object.keys(state); return keys.reduce(function (newState, key) { if (props[key] !== undefined) { newState[key] = props[key]; } return newState; }, _extends({}, state)); } const t=t=>"object"==typeof t&&null!=t&&1===t.nodeType,e=(t,e)=>(!e||"hidden"!==t)&&("visible"!==t&&"clip"!==t),n=(t,n)=>{if(t.clientHeight<t.scrollHeight||t.clientWidth<t.scrollWidth){const o=getComputedStyle(t,null);return e(o.overflowY,n)||e(o.overflowX,n)||(t=>{const e=(t=>{if(!t.ownerDocument||!t.ownerDocument.defaultView)return null;try{return t.ownerDocument.defaultView.frameElement}catch(t){return null}})(t);return !!e&&(e.clientHeight<t.scrollHeight||e.clientWidth<t.scrollWidth)})(t)}return false},o=(t,e,n,o,l,r,i,s)=>r<t&&i>e||r>t&&i<e?0:r<=t&&s<=n||i>=e&&s>=n?r-t-o:i>e&&s<n||r<t&&s>n?i-e+l:0,l=t=>{const e=t.parentElement;return null==e?t.getRootNode().host||null:e},r=(e,r)=>{var i,s,d,h;if("undefined"==typeof document)return [];const{inline:u,boundary:a,skipOverflowHiddenElements:g}=r,p="function"==typeof a?a:t=>t!==a;if(!t(e))throw new TypeError("Invalid target");const m=document.scrollingElement||document.documentElement,w=[];let W=e;for(;t(W)&&p(W);){if(W=l(W),W===m){w.push(W);break}null!=W&&W===document.body&&n(W)&&!n(document.documentElement)||null!=W&&n(W,g)&&w.push(W);}const b=null!=(s=null==(i=window.visualViewport)?void 0:i.width)?s:innerWidth,H=null!=(h=null==(d=window.visualViewport)?void 0:d.height)?h:innerHeight,{scrollX:y,scrollY:M}=window,{height:v,width:E,top:x,right:C,bottom:I,left:R}=e.getBoundingClientRect(),{top:T,right:B,left:V}=(t=>{const e=window.getComputedStyle(t);return {top:parseFloat(e.scrollMarginTop)||0,right:parseFloat(e.scrollMarginRight)||0,bottom:parseFloat(e.scrollMarginBottom)||0,left:parseFloat(e.scrollMarginLeft)||0}})(e);let k=x-T,D="center"===u?R+E/2-V+B:"end"===u?C+B:R-V;const L=[];for(let t=0;t<w.length;t++){const e=w[t],{height:l,width:r,top:i,right:s,bottom:d,left:h}=e.getBoundingClientRect();if(x>=0&&R>=0&&I<=H&&C<=b&&(e===m&&!n(e)||x>=i&&I<=d&&R>=h&&C<=s))return L;const a=getComputedStyle(e),g=parseInt(a.borderLeftWidth,10),p=parseInt(a.borderTopWidth,10),W=parseInt(a.borderRightWidth,10),T=parseInt(a.borderBottomWidth,10);let B=0,F=0;const V="offsetWidth"in e?e.offsetWidth-e.clientWidth-g-W:0,S="offsetHeight"in e?e.offsetHeight-e.clientHeight-p-T:0,X="offsetWidth"in e?0===e.offsetWidth?0:r/e.offsetWidth:0,Y="offsetHeight"in e?0===e.offsetHeight?0:l/e.offsetHeight:0;if(m===e)B=o(M,M+H,H,p,T,M+k,M+k+v,v),F="start"===u?D:"center"===u?D-b/2:"end"===u?D-b:o(y,y+b,b,g,W,y+D,y+D+E,E),B=Math.max(0,B+M),F=Math.max(0,F+y);else {B=o(i,d,l,p,T+S,k,k+v,v),F="start"===u?D-h-g:"center"===u?D-(h+r/2)+V/2:"end"===u?D-s+W+V:o(h,s,r,g,W+V,D,D+E,E);const{scrollLeft:t,scrollTop:n}=e;B=0===Y?0:Math.max(0,Math.min(n+B/Y,e.scrollHeight-l/Y+S)),F=0===X?0:Math.max(0,Math.min(t+F/X,e.scrollWidth-r/X+V)),k+=n-B,D+=t-F;}L.push({el:e,top:B,left:F});}return L}; /** * 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 = r(node, { boundary: menuNode}); actions.forEach(function (_ref) { var el = _ref.el, top = _ref.top, left = _ref.left; el.scrollTop = top; el.scrollLeft = left; }); } /** * 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; } /** * @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; } }); }; } /** * 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(_ref) { var isOpen = _ref.isOpen, resultCount = _ref.resultCount, previousResultCount = _ref.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) { /* istanbul ignore if */ { // then this is preact or preact X return typeof element.type === 'string' || typeof element.nodeName === 'string'; } } /** * @param {Object} element (P)react element * @return {Object} the props */ function getElementProps(element) { // props for react, attributes for preact /* istanbul ignore if */ { return element.props || element.attributes; } } /** * 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 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 next non-disabled highlightedIndex value. * * @param {number} start The current highlightedIndex. * @param {number} offset The offset from the current highlightedIndex to start searching. * @param {unknown[]} items The items array. * @param {(item: unknown, index: number) => boolean} isItemDisabled Function that tells if an item is disabled or not. * @param {boolean?} circular If the search reaches the end, if it can search again starting from the other end. * @returns {number} The next highlightedIndex. */ function getHighlightedIndex(start, offset, items, isItemDisabled, circular) { if (circular === void 0) { circular = false; } var count = items.length; if (count === 0) { return -1; } var itemsLastIndex = count - 1; if (typeof start !== 'number' || start < 0 || start > itemsLastIndex) { start = offset > 0 ? -1 : itemsLastIndex + 1; } var current = start + offset; if (current < 0) { current = circular ? itemsLastIndex : 0; } else if (current > itemsLastIndex) { current = circular ? 0 : itemsLastIndex; } var highlightedIndex = getNonDisabledIndex(current, offset < 0, items, isItemDisabled, circular); if (highlightedIndex === -1) { return start >= count ? -1 : start; } return highlightedIndex; } /** * Returns the next non-disabled highlightedIndex value. * * @param {number} start The current highlightedIndex. * @param {boolean} backwards If true, it will search backwards from the start. * @param {unknown[]} items The items array. * @param {(item: unknown, index: number) => boolean} isItemDisabled Function that tells if an item is disabled or not. * @param {boolean} circular If the search reaches the end, if it can search again starting from the other end. * @returns {number} The next non-disabled index. */ function getNonDisabledIndex(start, backwards, items, isItemDisabled, circular) { if (circular === void 0) { circular = false; } var count = items.length; if (backwards) { for (var index = start; index >= 0; index--) { if (!isItemDisabled(items[index], index)) { return index; } } } else { for (var _index = start; _index < count; _index++) { if (!isItemDisabled(items[_index], _index)) { return _index; } } } if (circular) { return getNonDisabledIndex(backwards ? count - 1 : 0, backwards, items, isItemDisabled); } 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 environment && 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 */ { 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 _excluded$4 = ["refKey", "ref"], _excluded2$4 = ["onClick", "onPress", "onKeyDown", "onKeyUp", "onBlur"], _excluded3$3 = ["onKeyDown", "onBlur", "onChange", "onInput", "onChangeText"], _excluded4$2 = ["refKey", "ref"], _excluded5 = ["onMouseMove", "onMouseDown", "onClick", "onPress", "index", "item"]; var Downshift = /*#__PURE__*/function () { var Downshift = /*#__PURE__*/function (_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.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.isItemDisabled = function (_item, index) { var currentElementNode = _this.getItemNodeFromIndex(index); return currentElementNode && currentElementNode.hasAttribute('disabled'); }; _this.setHighlightedIndex = function (highlightedIndex, otherStateToSet) { if (highlightedIndex === void 0) { highlightedIndex = _this.props.defaultHighlightedIndex; } if (otherStateToSet === void 0) { otherStateToSet = {}; } otherStateToSet = pickState(otherStateToSet); _this.internalSetState(_extends({ 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({ 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({}, _this.getStateAndHelpers(), stateToSet)); } return _this.setState(function (state) { var _newStateToSet; 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 = 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({}, _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(_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((_extends2 = {}, _extends2[refKey] = handleRefs(ref, _this.rootRef), _extends2.role = 'combobox', _extends2['aria-expanded'] = isOpen, _extends2['aria-haspopup'] = 'listbox', _extends2['aria-owns'] = isOpen ? _this.menuId : undefined, _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 = getHighlightedIndex(highlightedIndex, 1, { length: itemCount }, _this2.isItemDisabled, true); _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 = getHighlightedIndex(highlightedIndex, -1, { length: itemCount }, _this3.isItemDisabled, true); _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({ type: keyDownEscape }, !this.state.isOpen && { selectedItem: null, inputValue: '' })); } }; //////////////////////////// BUTTON _this.buttonKeyDownHandlers = _extends({}, _this.keyDownHandlers, { ' ': function _(event) { event.preventDefault(); this.toggleMenu({ type: keyDownSpaceButton }); } }); _this.inputKeyDownHandlers = _extends({}, _this.keyDownHandlers, { Home: function Home(event) { 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 = getNonDisabledIndex(0, false, { length: itemCount }, this.isItemDisabled); this.setHighlightedIndex(newHighlightedIndex, { type: keyDownHome }); }, End: function End(event) { 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 = getNonDisabledIndex(itemCount - 1, true, { length: itemCount }, this.isItemDisabled); this.setHighlightedIndex(newHighlightedIndex, { type: keyDownEnd }); } }); _this.getToggleButtonProps = function (_temp3) { var _ref3 = _temp3 === void 0 ? {} : _temp3, onClick = _ref3.onClick; _ref3.onPress; var onKeyDown = _ref3.onKeyDown, onKeyUp = _ref3.onKeyUp, onBlur = _ref3.onBlur, rest = _objectWithoutPropertiesLoose(_ref3, _excluded2$4); var _this$getState5 = _this.getState(), isOpen = _this$getState5.isOpen; var enabledEventHandlers = { onClick: callAllEventHandlers(onClick, _this.buttonHandleClick), onKeyDown: callAllEventHandlers(onKeyDown, _this.buttonHandleKeyDown), onKeyUp: callAllEventHandlers(onKeyUp, _this.buttonHandleKeyUp), onBlur: callAllEventHandlers(onBlur, _this.buttonHandleBlur) }; var eventHandlers = rest.disabled ? {} : enabledEventHandlers; return _extends({ 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(_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) { var _this$props$environme = _this.props.environment.document, body = _this$props$environme.body, activeElement = _this$props$environme.