UNPKG

@material-ui/lab

Version:

Material-UI Lab - Incubator for Material-UI React components.

1,091 lines (905 loc) 34.5 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.createFilterOptions = createFilterOptions; exports.default = useAutocomplete; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var React = _interopRequireWildcard(require("react")); var _utils = require("@material-ui/core/utils"); /* eslint-disable no-constant-condition */ // https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript // Give up on IE 11 support for this feature function stripDiacritics(string) { return typeof string.normalize !== 'undefined' ? string.normalize('NFD').replace(/[\u0300-\u036f]/g, '') : string; } function createFilterOptions() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _config$ignoreAccents = config.ignoreAccents, ignoreAccents = _config$ignoreAccents === void 0 ? true : _config$ignoreAccents, _config$ignoreCase = config.ignoreCase, ignoreCase = _config$ignoreCase === void 0 ? true : _config$ignoreCase, limit = config.limit, _config$matchFrom = config.matchFrom, matchFrom = _config$matchFrom === void 0 ? 'any' : _config$matchFrom, stringify = config.stringify, _config$trim = config.trim, trim = _config$trim === void 0 ? false : _config$trim; return function (options, _ref) { var inputValue = _ref.inputValue, getOptionLabel = _ref.getOptionLabel; var input = trim ? inputValue.trim() : inputValue; if (ignoreCase) { input = input.toLowerCase(); } if (ignoreAccents) { input = stripDiacritics(input); } var filteredOptions = options.filter(function (option) { var candidate = (stringify || getOptionLabel)(option); if (ignoreCase) { candidate = candidate.toLowerCase(); } if (ignoreAccents) { candidate = stripDiacritics(candidate); } return matchFrom === 'start' ? candidate.indexOf(input) === 0 : candidate.indexOf(input) > -1; }); return typeof limit === 'number' ? filteredOptions.slice(0, limit) : filteredOptions; }; } // To replace with .findIndex() once we stop IE 11 support. function findIndex(array, comp) { for (var i = 0; i < array.length; i += 1) { if (comp(array[i])) { return i; } } return -1; } var defaultFilterOptions = createFilterOptions(); // Number of options to jump in list box when pageup and pagedown keys are used. var pageSize = 5; function useAutocomplete(props) { var _props$autoComplete = props.autoComplete, autoComplete = _props$autoComplete === void 0 ? false : _props$autoComplete, _props$autoHighlight = props.autoHighlight, autoHighlight = _props$autoHighlight === void 0 ? false : _props$autoHighlight, _props$autoSelect = props.autoSelect, autoSelect = _props$autoSelect === void 0 ? false : _props$autoSelect, _props$blurOnSelect = props.blurOnSelect, blurOnSelect = _props$blurOnSelect === void 0 ? false : _props$blurOnSelect, _props$clearOnBlur = props.clearOnBlur, clearOnBlur = _props$clearOnBlur === void 0 ? !props.freeSolo : _props$clearOnBlur, _props$clearOnEscape = props.clearOnEscape, clearOnEscape = _props$clearOnEscape === void 0 ? false : _props$clearOnEscape, _props$componentName = props.componentName, componentName = _props$componentName === void 0 ? 'useAutocomplete' : _props$componentName, _props$debug = props.debug, debug = _props$debug === void 0 ? false : _props$debug, _props$defaultValue = props.defaultValue, defaultValue = _props$defaultValue === void 0 ? props.multiple ? [] : null : _props$defaultValue, _props$disableClearab = props.disableClearable, disableClearable = _props$disableClearab === void 0 ? false : _props$disableClearab, _props$disableCloseOn = props.disableCloseOnSelect, disableCloseOnSelect = _props$disableCloseOn === void 0 ? false : _props$disableCloseOn, _props$disabledItemsF = props.disabledItemsFocusable, disabledItemsFocusable = _props$disabledItemsF === void 0 ? false : _props$disabledItemsF, _props$disableListWra = props.disableListWrap, disableListWrap = _props$disableListWra === void 0 ? false : _props$disableListWra, _props$filterOptions = props.filterOptions, filterOptions = _props$filterOptions === void 0 ? defaultFilterOptions : _props$filterOptions, _props$filterSelected = props.filterSelectedOptions, filterSelectedOptions = _props$filterSelected === void 0 ? false : _props$filterSelected, _props$freeSolo = props.freeSolo, freeSolo = _props$freeSolo === void 0 ? false : _props$freeSolo, getOptionDisabled = props.getOptionDisabled, _props$getOptionLabel = props.getOptionLabel, getOptionLabelProp = _props$getOptionLabel === void 0 ? function (option) { return option; } : _props$getOptionLabel, _props$getOptionSelec = props.getOptionSelected, getOptionSelected = _props$getOptionSelec === void 0 ? function (option, value) { return option === value; } : _props$getOptionSelec, groupBy = props.groupBy, _props$handleHomeEndK = props.handleHomeEndKeys, handleHomeEndKeys = _props$handleHomeEndK === void 0 ? !props.freeSolo : _props$handleHomeEndK, idProp = props.id, _props$includeInputIn = props.includeInputInList, includeInputInList = _props$includeInputIn === void 0 ? false : _props$includeInputIn, inputValueProp = props.inputValue, _props$multiple = props.multiple, multiple = _props$multiple === void 0 ? false : _props$multiple, onChange = props.onChange, onClose = props.onClose, onHighlightChange = props.onHighlightChange, onInputChange = props.onInputChange, onOpen = props.onOpen, openProp = props.open, _props$openOnFocus = props.openOnFocus, openOnFocus = _props$openOnFocus === void 0 ? false : _props$openOnFocus, options = props.options, _props$selectOnFocus = props.selectOnFocus, selectOnFocus = _props$selectOnFocus === void 0 ? !props.freeSolo : _props$selectOnFocus, valueProp = props.value; var id = (0, _utils.unstable_useId)(idProp); var getOptionLabel = getOptionLabelProp; if (process.env.NODE_ENV !== 'production') { getOptionLabel = function getOptionLabel(option) { var optionLabel = getOptionLabelProp(option); if (typeof optionLabel !== 'string') { var erroneousReturn = optionLabel === undefined ? 'undefined' : "".concat((0, _typeof2.default)(optionLabel), " (").concat(optionLabel, ")"); console.error("Material-UI: The `getOptionLabel` method of ".concat(componentName, " returned ").concat(erroneousReturn, " instead of a string for ").concat(JSON.stringify(option), ".")); } return optionLabel; }; } var ignoreFocus = React.useRef(false); var firstFocus = React.useRef(true); var inputRef = React.useRef(null); var listboxRef = React.useRef(null); var _React$useState = React.useState(null), anchorEl = _React$useState[0], setAnchorEl = _React$useState[1]; var _React$useState2 = React.useState(-1), focusedTag = _React$useState2[0], setFocusedTag = _React$useState2[1]; var defaultHighlighted = autoHighlight ? 0 : -1; var highlightedIndexRef = React.useRef(defaultHighlighted); var _useControlled = (0, _utils.useControlled)({ controlled: valueProp, default: defaultValue, name: componentName }), _useControlled2 = (0, _slicedToArray2.default)(_useControlled, 2), value = _useControlled2[0], setValue = _useControlled2[1]; var _useControlled3 = (0, _utils.useControlled)({ controlled: inputValueProp, default: '', name: componentName, state: 'inputValue' }), _useControlled4 = (0, _slicedToArray2.default)(_useControlled3, 2), inputValue = _useControlled4[0], setInputValue = _useControlled4[1]; var _React$useState3 = React.useState(false), focused = _React$useState3[0], setFocused = _React$useState3[1]; var resetInputValue = (0, _utils.useEventCallback)(function (event, newValue) { var newInputValue; if (multiple) { newInputValue = ''; } else if (newValue == null) { newInputValue = ''; } else { var optionLabel = getOptionLabel(newValue); newInputValue = typeof optionLabel === 'string' ? optionLabel : ''; } if (inputValue === newInputValue) { return; } setInputValue(newInputValue); if (onInputChange) { onInputChange(event, newInputValue, 'reset'); } }); React.useEffect(function () { resetInputValue(null, value); }, [value, resetInputValue]); var _useControlled5 = (0, _utils.useControlled)({ controlled: openProp, default: false, name: componentName, state: 'open' }), _useControlled6 = (0, _slicedToArray2.default)(_useControlled5, 2), open = _useControlled6[0], setOpenState = _useControlled6[1]; var inputValueIsSelectedValue = !multiple && value != null && inputValue === getOptionLabel(value); var popupOpen = open; var filteredOptions = popupOpen ? filterOptions(options.filter(function (option) { if (filterSelectedOptions && (multiple ? value : [value]).some(function (value2) { return value2 !== null && getOptionSelected(option, value2); })) { return false; } return true; }), // we use the empty string to manipulate `filterOptions` to not filter any options // i.e. the filter predicate always returns true { inputValue: inputValueIsSelectedValue ? '' : inputValue, getOptionLabel: getOptionLabel }) : []; if (process.env.NODE_ENV !== 'production') { if (value !== null && !freeSolo && options.length > 0) { var missingValue = (multiple ? value : [value]).filter(function (value2) { return !options.some(function (option) { return getOptionSelected(option, value2); }); }); if (missingValue.length > 0) { console.warn(["Material-UI: The value provided to ".concat(componentName, " is invalid."), "None of the options match with `".concat(missingValue.length > 1 ? JSON.stringify(missingValue) : JSON.stringify(missingValue[0]), "`."), 'You can use the `getOptionSelected` prop to customize the equality test.'].join('\n')); } } } var focusTag = (0, _utils.useEventCallback)(function (tagToFocus) { if (tagToFocus === -1) { inputRef.current.focus(); } else { anchorEl.querySelector("[data-tag-index=\"".concat(tagToFocus, "\"]")).focus(); } }); // Ensure the focusedTag is never inconsistent React.useEffect(function () { if (multiple && focusedTag > value.length - 1) { setFocusedTag(-1); focusTag(-1); } }, [value, multiple, focusedTag, focusTag]); function validOptionIndex(index, direction) { if (!listboxRef.current || index === -1) { return -1; } var nextFocus = index; while (true) { // Out of range if (direction === 'next' && nextFocus === filteredOptions.length || direction === 'previous' && nextFocus === -1) { return -1; } var option = listboxRef.current.querySelector("[data-option-index=\"".concat(nextFocus, "\"]")); // Same logic as MenuList.js var nextFocusDisabled = disabledItemsFocusable ? false : option && (option.disabled || option.getAttribute('aria-disabled') === 'true'); if (option && !option.hasAttribute('tabindex') || nextFocusDisabled) { // Move to the next element. nextFocus += direction === 'next' ? 1 : -1; } else { return nextFocus; } } } var setHighlightedIndex = (0, _utils.useEventCallback)(function (_ref2) { var event = _ref2.event, index = _ref2.index, _ref2$reason = _ref2.reason, reason = _ref2$reason === void 0 ? 'auto' : _ref2$reason; highlightedIndexRef.current = index; // does the index exist? if (index === -1) { inputRef.current.removeAttribute('aria-activedescendant'); } else { inputRef.current.setAttribute('aria-activedescendant', "".concat(id, "-option-").concat(index)); } if (onHighlightChange) { onHighlightChange(event, index === -1 ? null : filteredOptions[index], reason); } if (!listboxRef.current) { return; } var prev = listboxRef.current.querySelector('[data-focus]'); if (prev) { prev.removeAttribute('data-focus'); } var listboxNode = listboxRef.current.parentElement.querySelector('[role="listbox"]'); // "No results" if (!listboxNode) { return; } if (index === -1) { listboxNode.scrollTop = 0; return; } var option = listboxRef.current.querySelector("[data-option-index=\"".concat(index, "\"]")); if (!option) { return; } option.setAttribute('data-focus', 'true'); // Scroll active descendant into view. // Logic copied from https://www.w3.org/TR/wai-aria-practices/examples/listbox/js/listbox.js // // Consider this API instead once it has a better browser support: // .scrollIntoView({ scrollMode: 'if-needed', block: 'nearest' }); if (listboxNode.scrollHeight > listboxNode.clientHeight && reason !== 'mouse') { var element = option; var scrollBottom = listboxNode.clientHeight + listboxNode.scrollTop; var elementBottom = element.offsetTop + element.offsetHeight; if (elementBottom > scrollBottom) { listboxNode.scrollTop = elementBottom - listboxNode.clientHeight; } else if (element.offsetTop - element.offsetHeight * (groupBy ? 1.3 : 0) < listboxNode.scrollTop) { listboxNode.scrollTop = element.offsetTop - element.offsetHeight * (groupBy ? 1.3 : 0); } } }); var changeHighlightedIndex = (0, _utils.useEventCallback)(function (_ref3) { var event = _ref3.event, diff = _ref3.diff, _ref3$direction = _ref3.direction, direction = _ref3$direction === void 0 ? 'next' : _ref3$direction, _ref3$reason = _ref3.reason, reason = _ref3$reason === void 0 ? 'auto' : _ref3$reason; if (!popupOpen) { return; } var getNextIndex = function getNextIndex() { var maxIndex = filteredOptions.length - 1; if (diff === 'reset') { return defaultHighlighted; } if (diff === 'start') { return 0; } if (diff === 'end') { return maxIndex; } var newIndex = highlightedIndexRef.current + diff; if (newIndex < 0) { if (newIndex === -1 && includeInputInList) { return -1; } if (disableListWrap && highlightedIndexRef.current !== -1 || Math.abs(diff) > 1) { return 0; } return maxIndex; } if (newIndex > maxIndex) { if (newIndex === maxIndex + 1 && includeInputInList) { return -1; } if (disableListWrap || Math.abs(diff) > 1) { return maxIndex; } return 0; } return newIndex; }; var nextIndex = validOptionIndex(getNextIndex(), direction); setHighlightedIndex({ index: nextIndex, reason: reason, event: event }); // Sync the content of the input with the highlighted option. if (autoComplete && diff !== 'reset') { if (nextIndex === -1) { inputRef.current.value = inputValue; } else { var option = getOptionLabel(filteredOptions[nextIndex]); inputRef.current.value = option; // The portion of the selected suggestion that has not been typed by the user, // a completion string, appears inline after the input cursor in the textbox. var index = option.toLowerCase().indexOf(inputValue.toLowerCase()); if (index === 0 && inputValue.length > 0) { inputRef.current.setSelectionRange(inputValue.length, option.length); } } } }); var syncHighlightedIndex = React.useCallback(function () { if (!popupOpen) { return; } var valueItem = multiple ? value[0] : value; // The popup is empty, reset if (filteredOptions.length === 0 || valueItem == null) { changeHighlightedIndex({ diff: 'reset' }); return; } if (!listboxRef.current) { return; } // Synchronize the value with the highlighted index if (!filterSelectedOptions && valueItem != null) { var currentOption = filteredOptions[highlightedIndexRef.current]; // Keep the current highlighted index if possible if (multiple && currentOption && findIndex(value, function (val) { return getOptionSelected(currentOption, val); }) !== -1) { return; } var itemIndex = findIndex(filteredOptions, function (optionItem) { return getOptionSelected(optionItem, valueItem); }); if (itemIndex === -1) { changeHighlightedIndex({ diff: 'reset' }); } else { setHighlightedIndex({ index: itemIndex }); } return; } // Prevent the highlighted index to leak outside the boundaries. if (highlightedIndexRef.current >= filteredOptions.length - 1) { setHighlightedIndex({ index: filteredOptions.length - 1 }); return; } // Restore the focus to the previous index. setHighlightedIndex({ index: highlightedIndexRef.current }); // Ignore filteredOptions (and options, getOptionSelected, getOptionLabel) not to break the scroll position // eslint-disable-next-line react-hooks/exhaustive-deps }, [// Only sync the highlighted index when the option switch between empty and not // eslint-disable-next-line react-hooks/exhaustive-deps filteredOptions.length === 0, // Don't sync the highlighted index with the value when multiple // eslint-disable-next-line react-hooks/exhaustive-deps multiple ? false : value, filterSelectedOptions, changeHighlightedIndex, setHighlightedIndex, popupOpen, inputValue, multiple]); var handleListboxRef = (0, _utils.useEventCallback)(function (node) { (0, _utils.setRef)(listboxRef, node); if (!node) { return; } syncHighlightedIndex(); }); React.useEffect(function () { syncHighlightedIndex(); }, [syncHighlightedIndex]); var handleOpen = function handleOpen(event) { if (open) { return; } setOpenState(true); if (onOpen) { onOpen(event); } }; var handleClose = function handleClose(event, reason) { if (!open) { return; } setOpenState(false); if (onClose) { onClose(event, reason); } }; var handleValue = function handleValue(event, newValue, reason, details) { if (value === newValue) { return; } if (onChange) { onChange(event, newValue, reason, details); } setValue(newValue); }; var isTouch = React.useRef(false); var selectNewValue = function selectNewValue(event, option) { var reasonProp = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'select-option'; var origin = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'options'; var reason = reasonProp; var newValue = option; if (multiple) { newValue = Array.isArray(value) ? value.slice() : []; if (process.env.NODE_ENV !== 'production') { var matches = newValue.filter(function (val) { return getOptionSelected(option, val); }); if (matches.length > 1) { console.error(["Material-UI: The `getOptionSelected` method of ".concat(componentName, " do not handle the arguments correctly."), "The component expects a single value to match a given option but found ".concat(matches.length, " matches.")].join('\n')); } } var itemIndex = findIndex(newValue, function (valueItem) { return getOptionSelected(option, valueItem); }); if (itemIndex === -1) { newValue.push(option); } else if (origin !== 'freeSolo') { newValue.splice(itemIndex, 1); reason = 'remove-option'; } } resetInputValue(event, newValue); handleValue(event, newValue, reason, { option: option }); if (!disableCloseOnSelect) { handleClose(event, reason); } if (blurOnSelect === true || blurOnSelect === 'touch' && isTouch.current || blurOnSelect === 'mouse' && !isTouch.current) { inputRef.current.blur(); } }; function validTagIndex(index, direction) { if (index === -1) { return -1; } var nextFocus = index; while (true) { // Out of range if (direction === 'next' && nextFocus === value.length || direction === 'previous' && nextFocus === -1) { return -1; } var option = anchorEl.querySelector("[data-tag-index=\"".concat(nextFocus, "\"]")); // Same logic as MenuList.js if (option && (!option.hasAttribute('tabindex') || option.disabled || option.getAttribute('aria-disabled') === 'true')) { nextFocus += direction === 'next' ? 1 : -1; } else { return nextFocus; } } } var handleFocusTag = function handleFocusTag(event, direction) { if (!multiple) { return; } handleClose(event, 'toggleInput'); var nextTag = focusedTag; if (focusedTag === -1) { if (inputValue === '' && direction === 'previous') { nextTag = value.length - 1; } } else { nextTag += direction === 'next' ? 1 : -1; if (nextTag < 0) { nextTag = 0; } if (nextTag === value.length) { nextTag = -1; } } nextTag = validTagIndex(nextTag, direction); setFocusedTag(nextTag); focusTag(nextTag); }; var handleClear = function handleClear(event) { ignoreFocus.current = true; setInputValue(''); if (onInputChange) { onInputChange(event, '', 'clear'); } handleValue(event, multiple ? [] : null, 'clear'); }; var handleKeyDown = function handleKeyDown(other) { return function (event) { if (focusedTag !== -1 && ['ArrowLeft', 'ArrowRight'].indexOf(event.key) === -1) { setFocusedTag(-1); focusTag(-1); } switch (event.key) { case 'Home': if (popupOpen && handleHomeEndKeys) { // Prevent scroll of the page event.preventDefault(); changeHighlightedIndex({ diff: 'start', direction: 'next', reason: 'keyboard', event: event }); } break; case 'End': if (popupOpen && handleHomeEndKeys) { // Prevent scroll of the page event.preventDefault(); changeHighlightedIndex({ diff: 'end', direction: 'previous', reason: 'keyboard', event: event }); } break; case 'PageUp': // Prevent scroll of the page event.preventDefault(); changeHighlightedIndex({ diff: -pageSize, direction: 'previous', reason: 'keyboard', event: event }); handleOpen(event); break; case 'PageDown': // Prevent scroll of the page event.preventDefault(); changeHighlightedIndex({ diff: pageSize, direction: 'next', reason: 'keyboard', event: event }); handleOpen(event); break; case 'ArrowDown': // Prevent cursor move event.preventDefault(); changeHighlightedIndex({ diff: 1, direction: 'next', reason: 'keyboard', event: event }); handleOpen(event); break; case 'ArrowUp': // Prevent cursor move event.preventDefault(); changeHighlightedIndex({ diff: -1, direction: 'previous', reason: 'keyboard', event: event }); handleOpen(event); break; case 'ArrowLeft': handleFocusTag(event, 'previous'); break; case 'ArrowRight': handleFocusTag(event, 'next'); break; case 'Enter': // Wait until IME is settled. if (event.which === 229) { break; } if (highlightedIndexRef.current !== -1 && popupOpen) { var option = filteredOptions[highlightedIndexRef.current]; var disabled = getOptionDisabled ? getOptionDisabled(option) : false; // We don't want to validate the form. event.preventDefault(); if (disabled) { return; } selectNewValue(event, option, 'select-option'); // Move the selection to the end. if (autoComplete) { inputRef.current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length); } } else if (freeSolo && inputValue !== '' && inputValueIsSelectedValue === false) { if (multiple) { // Allow people to add new values before they submit the form. event.preventDefault(); } selectNewValue(event, inputValue, 'create-option', 'freeSolo'); } break; case 'Escape': if (popupOpen) { // Avoid Opera to exit fullscreen mode. event.preventDefault(); // Avoid the Modal to handle the event. event.stopPropagation(); handleClose(event, 'escape'); } else if (clearOnEscape && (inputValue !== '' || multiple && value.length > 0)) { // Avoid Opera to exit fullscreen mode. event.preventDefault(); // Avoid the Modal to handle the event. event.stopPropagation(); handleClear(event); } break; case 'Backspace': if (multiple && inputValue === '' && value.length > 0) { var index = focusedTag === -1 ? value.length - 1 : focusedTag; var newValue = value.slice(); newValue.splice(index, 1); handleValue(event, newValue, 'remove-option', { option: value[index] }); } break; default: } if (other.onKeyDown) { other.onKeyDown(event); } }; }; var handleFocus = function handleFocus(event) { setFocused(true); if (openOnFocus && !ignoreFocus.current) { handleOpen(event); } }; var handleBlur = function handleBlur(event) { // Ignore the event when using the scrollbar with IE 11 if (listboxRef.current !== null && document.activeElement === listboxRef.current.parentElement) { inputRef.current.focus(); return; } setFocused(false); firstFocus.current = true; ignoreFocus.current = false; if (debug && inputValue !== '') { return; } if (autoSelect && highlightedIndexRef.current !== -1 && popupOpen) { selectNewValue(event, filteredOptions[highlightedIndexRef.current], 'blur'); } else if (autoSelect && freeSolo && inputValue !== '') { selectNewValue(event, inputValue, 'blur', 'freeSolo'); } else if (clearOnBlur) { resetInputValue(event, value); } handleClose(event, 'blur'); }; var handleInputChange = function handleInputChange(event) { var newValue = event.target.value; if (inputValue !== newValue) { setInputValue(newValue); if (onInputChange) { onInputChange(event, newValue, 'input'); } } if (newValue === '') { if (!disableClearable && !multiple) { handleValue(event, null, 'clear'); } } else { handleOpen(event); } }; var handleOptionMouseOver = function handleOptionMouseOver(event) { setHighlightedIndex({ event: event, index: Number(event.currentTarget.getAttribute('data-option-index')), reason: 'mouse' }); }; var handleOptionTouchStart = function handleOptionTouchStart() { isTouch.current = true; }; var handleOptionClick = function handleOptionClick(event) { var index = Number(event.currentTarget.getAttribute('data-option-index')); selectNewValue(event, filteredOptions[index], 'select-option'); isTouch.current = false; }; var handleTagDelete = function handleTagDelete(index) { return function (event) { var newValue = value.slice(); newValue.splice(index, 1); handleValue(event, newValue, 'remove-option', { option: value[index] }); }; }; var handlePopupIndicator = function handlePopupIndicator(event) { if (open) { handleClose(event, 'toggleInput'); } else { handleOpen(event); } }; // Prevent input blur when interacting with the combobox var handleMouseDown = function handleMouseDown(event) { if (event.target.getAttribute('id') !== id) { event.preventDefault(); } }; // Focus the input when interacting with the combobox var handleClick = function handleClick() { inputRef.current.focus(); if (selectOnFocus && firstFocus.current && inputRef.current.selectionEnd - inputRef.current.selectionStart === 0) { inputRef.current.select(); } firstFocus.current = false; }; var handleInputMouseDown = function handleInputMouseDown(event) { if (inputValue === '' || !open) { handlePopupIndicator(event); } }; var dirty = freeSolo && inputValue.length > 0; dirty = dirty || (multiple ? value.length > 0 : value !== null); var groupedOptions = filteredOptions; if (groupBy) { // used to keep track of key and indexes in the result array var indexBy = new Map(); var warn = false; groupedOptions = filteredOptions.reduce(function (acc, option, index) { var group = groupBy(option); if (acc.length > 0 && acc[acc.length - 1].group === group) { acc[acc.length - 1].options.push(option); } else { if (process.env.NODE_ENV !== 'production') { if (indexBy.get(group) && !warn) { console.warn("Material-UI: The options provided combined with the `groupBy` method of ".concat(componentName, " returns duplicated headers."), 'You can solve the issue by sorting the options with the output of `groupBy`.'); warn = true; } indexBy.set(group, true); } acc.push({ key: index, index: index, group: group, options: [option] }); } return acc; }, []); } return { getRootProps: function getRootProps() { var other = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return (0, _extends2.default)({ 'aria-owns': popupOpen ? "".concat(id, "-popup") : null, role: 'combobox', 'aria-expanded': popupOpen }, other, { onKeyDown: handleKeyDown(other), onMouseDown: handleMouseDown, onClick: handleClick }); }, getInputLabelProps: function getInputLabelProps() { return { id: "".concat(id, "-label"), htmlFor: id }; }, getInputProps: function getInputProps() { return { id: id, value: inputValue, onBlur: handleBlur, onFocus: handleFocus, onChange: handleInputChange, onMouseDown: handleInputMouseDown, // if open then this is handled imperativeley so don't let react override // only have an opinion about this when closed 'aria-activedescendant': popupOpen ? '' : null, 'aria-autocomplete': autoComplete ? 'both' : 'list', 'aria-controls': popupOpen ? "".concat(id, "-popup") : null, // Disable browser's suggestion that might overlap with the popup. // Handle autocomplete but not autofill. autoComplete: 'off', ref: inputRef, autoCapitalize: 'none', spellCheck: 'false' }; }, getClearProps: function getClearProps() { return { tabIndex: -1, onClick: handleClear }; }, getPopupIndicatorProps: function getPopupIndicatorProps() { return { tabIndex: -1, onClick: handlePopupIndicator }; }, getTagProps: function getTagProps(_ref4) { var index = _ref4.index; return { key: index, 'data-tag-index': index, tabIndex: -1, onDelete: handleTagDelete(index) }; }, getListboxProps: function getListboxProps() { return { role: 'listbox', id: "".concat(id, "-popup"), 'aria-labelledby': "".concat(id, "-label"), ref: handleListboxRef, onMouseDown: function onMouseDown(event) { // Prevent blur event.preventDefault(); } }; }, getOptionProps: function getOptionProps(_ref5) { var index = _ref5.index, option = _ref5.option; var selected = (multiple ? value : [value]).some(function (value2) { return value2 != null && getOptionSelected(option, value2); }); var disabled = getOptionDisabled ? getOptionDisabled(option) : false; return { key: index, tabIndex: -1, role: 'option', id: "".concat(id, "-option-").concat(index), onMouseOver: handleOptionMouseOver, onClick: handleOptionClick, onTouchStart: handleOptionTouchStart, 'data-option-index': index, 'aria-disabled': disabled, 'aria-selected': selected }; }, id: id, inputValue: inputValue, value: value, dirty: dirty, popupOpen: popupOpen, focused: focused || focusedTag !== -1, anchorEl: anchorEl, setAnchorEl: setAnchorEl, focusedTag: focusedTag, groupedOptions: groupedOptions }; }