"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EuiComboBox = void 0; var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); var _services = require("../../services"); var _portal = require("../portal"); var _i18n = require("../i18n"); var _combo_box_options_list = require("./combo_box_options_list"); var _matching_options = require("./matching_options"); var _combo_box_input = require("./combo_box_input/combo_box_input"); var _popover = require("../../services/popover"); var _react2 = require("@emotion/react"); var _excluded = ["data-test-subj", "async", "className", "compressed", "customOptionText", "fullWidth", "id", "inputRef", "isCaseSensitive", "isClearable", "isDisabled", "isInvalid", "isLoading", "noSuggestions", "onBlur", "onChange", "onCreateOption", "onSearchChange", "options", "placeholder", "renderOption", "rowHeight", "selectedOptions", "singleSelection", "prepend", "sortMatchesBy", "delimiter", "append", "autoFocus", "aria-label", "aria-labelledby"]; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ /** * Elements within EuiComboBox which would normally be tabbable (inputs, buttons) have been removed * from the tab order with tabindex={-1} so that we can control the keyboard navigation interface. */ /** * Because of how TypeScript's LibraryManagedAttributes is designed to handle defaultProps (https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#support-for-defaultprops-in-jsx) * we can't directly export the above Props definitions, as the defaulted values are not made optional * as it isn't processed by LibraryManagedAttributes. To get around this, we: * - remove the props which have default values applied * - additionally re-define `options` and `selectedOptions` defaults, necessary as static members can't access generics and become never[] * - export (Props - Defaults) & Partial */ var initialSearchValue = ''; var EuiComboBox = /*#__PURE__*/function (_Component) { _inherits(EuiComboBox, _Component); var _super = _createSuper(EuiComboBox); function EuiComboBox() { var _this; _classCallCheck(this, EuiComboBox); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _super.call.apply(_super, [this].concat(args)); _defineProperty(_assertThisInitialized(_this), "state", { activeOptionIndex: -1, hasFocus: false, isListOpen: false, listElement: null, listPosition: 'bottom', listZIndex: undefined, matchingOptions: (0, _matching_options.getMatchingOptions)({ options: _this.props.options, selectedOptions: _this.props.selectedOptions, searchValue: initialSearchValue, isCaseSensitive: _this.props.isCaseSensitive, isPreFiltered: _this.props.async, showPrevSelected: Boolean(_this.props.singleSelection), sortMatchesBy: _this.props.sortMatchesBy }), searchValue: initialSearchValue, width: 0 }); _defineProperty(_assertThisInitialized(_this), "_isMounted", false); _defineProperty(_assertThisInitialized(_this), "rootId", (0, _services.htmlIdGenerator)()); // Refs _defineProperty(_assertThisInitialized(_this), "comboBoxRefInstance", null); _defineProperty(_assertThisInitialized(_this), "comboBoxRefCallback", function (ref) { _this.comboBoxRefInstance = ref; if (_this.comboBoxRefInstance) { var comboBoxBounds = _this.comboBoxRefInstance.getBoundingClientRect(); _this.setState({ width: comboBoxBounds.width }); } }); _defineProperty(_assertThisInitialized(_this), "autoSizeInputRefInstance", null); _defineProperty(_assertThisInitialized(_this), "autoSizeInputRefCallback", function (ref) { _this.autoSizeInputRefInstance = ref; }); _defineProperty(_assertThisInitialized(_this), "searchInputRefInstance", null); _defineProperty(_assertThisInitialized(_this), "searchInputRefCallback", function (ref) { _this.searchInputRefInstance = ref; if (_this.props.inputRef) _this.props.inputRef(ref); }); _defineProperty(_assertThisInitialized(_this), "listRefInstance", null); _defineProperty(_assertThisInitialized(_this), "listRefCallback", function (ref) { if (_this.comboBoxRefInstance) { // find the zIndex of the combobox relative to the page body // and use that to depth-position the list box // adds an extra `100` to provide some defense around neighboring elements' positioning var listZIndex = (0, _popover.getElementZIndex)(_this.comboBoxRefInstance, document.body) + 100; _this.setState({ listZIndex: listZIndex }); } _this.listRefInstance = ref; }); _defineProperty(_assertThisInitialized(_this), "toggleButtonRefInstance", null); _defineProperty(_assertThisInitialized(_this), "toggleButtonRefCallback", function (ref) { _this.toggleButtonRefInstance = ref; }); _defineProperty(_assertThisInitialized(_this), "optionsRefInstances", []); _defineProperty(_assertThisInitialized(_this), "optionRefCallback", function (index, ref) { _this.optionsRefInstances[index] = ref; }); _defineProperty(_assertThisInitialized(_this), "openList", function () { _this.setState({ isListOpen: true }); }); _defineProperty(_assertThisInitialized(_this), "closeList", function (event) { if (event && event.target === _this.searchInputRefInstance) { // really long search values / custom entries triggers a scroll event on the input // which the EuiComboBoxOptionsList passes through here return; } _this.clearActiveOption(); _this.setState({ listZIndex: undefined, isListOpen: false }); }); _defineProperty(_assertThisInitialized(_this), "updatePosition", function () { var listElement = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.state.listElement; if (!_this._isMounted) { return; } if (!_this.state.isListOpen) { return; } if (!listElement) { return; } // it's possible that updateListPosition is called when listElement is becoming visible, but isn't yet var listElementBounds = listElement.getBoundingClientRect(); if (listElementBounds.width === 0 || listElementBounds.height === 0) { return; } if (!_this.comboBoxRefInstance) { return; } var comboBoxBounds = _this.comboBoxRefInstance.getBoundingClientRect(); var _ref = (0, _services.findPopoverPosition)({ allowCrossAxis: false, anchor: _this.comboBoxRefInstance, popover: listElement, position: 'bottom' }), position = _ref.position, top = _ref.top; if (_this.listRefInstance) { _this.listRefInstance.style.top = "".concat(top, "px"); // listElement doesn't have its width set until after updating the position // which means the popover service won't know about the correct width // however, we already know where to position the element _this.listRefInstance.style.left = "".concat(comboBoxBounds.left + window.pageXOffset, "px"); _this.listRefInstance.style.width = "".concat(comboBoxBounds.width, "px"); } // Cache for future calls. _this.setState({ listElement: listElement, listPosition: position, width: comboBoxBounds.width }); }); _defineProperty(_assertThisInitialized(_this), "incrementActiveOptionIndex", function (amount) { // If there are no options available, do nothing. if (!_this.state.matchingOptions.length) { return; } _this.setState(function (_ref2) { var activeOptionIndex = _ref2.activeOptionIndex, matchingOptions = _ref2.matchingOptions; var nextActiveOptionIndex; if (activeOptionIndex < 0) { // If this is the beginning of the user's keyboard navigation of the menu, then we'll focus // either the first or last item. nextActiveOptionIndex = amount < 0 ? matchingOptions.length - 1 : 0; } else { nextActiveOptionIndex = activeOptionIndex + amount; if (nextActiveOptionIndex < 0) { nextActiveOptionIndex = matchingOptions.length - 1; } else if (nextActiveOptionIndex === matchingOptions.length) { nextActiveOptionIndex = 0; } } // Group titles are included in option list but are not selectable // Skip group title options var direction = amount > 0 ? 1 : -1; while (matchingOptions[nextActiveOptionIndex].isGroupLabelOption) { nextActiveOptionIndex = nextActiveOptionIndex + direction; if (nextActiveOptionIndex < 0) { nextActiveOptionIndex = matchingOptions.length - 1; } else if (nextActiveOptionIndex === matchingOptions.length) { nextActiveOptionIndex = 0; } } return { activeOptionIndex: nextActiveOptionIndex }; }); }); _defineProperty(_assertThisInitialized(_this), "hasActiveOption", function () { return _this.state.activeOptionIndex > -1 && _this.state.activeOptionIndex < _this.state.matchingOptions.length; }); _defineProperty(_assertThisInitialized(_this), "clearActiveOption", function () { _this.setState({ activeOptionIndex: -1 }); }); _defineProperty(_assertThisInitialized(_this), "clearSearchValue", function () { _this.onSearchChange(''); }); _defineProperty(_assertThisInitialized(_this), "addCustomOption", function (isContainerBlur, searchValue) { var _this$props = _this.props, isCaseSensitive = _this$props.isCaseSensitive, onCreateOption = _this$props.onCreateOption, options = _this$props.options, selectedOptions = _this$props.selectedOptions, singleSelection = _this$props.singleSelection; var matchingOptions = _this.state.matchingOptions; if (_this.doesSearchMatchOnlyOption()) { _this.onAddOption(matchingOptions[0], isContainerBlur); return; } if (!onCreateOption) { return; } // Don't bother trying to create an option if the user hasn't typed anything. if (!searchValue) { return; } // Don't create the value if it's already been selected. if ((0, _matching_options.getSelectedOptionForSearchValue)({ isCaseSensitive: isCaseSensitive, searchValue: searchValue, selectedOptions: selectedOptions })) { return; } // Add new custom pill if this is custom input, even if it partially matches an option. var isOptionCreated = onCreateOption(searchValue, (0, _matching_options.flattenOptionGroups)(options)); // Expect the consumer to be explicit in rejecting a custom option. if (isOptionCreated === false) { return; } _this.clearSearchValue(); if (Boolean(singleSelection)) { // Adding a custom option to a single select that does not appear in the list of options _this.closeList(); } }); _defineProperty(_assertThisInitialized(_this), "doesSearchMatchOnlyOption", function () { var searchValue = _this.state.searchValue; if (_this.state.matchingOptions.length !== 1) { return false; } var normalizedSearchSubject = (0, _matching_options.transformForCaseSensitivity)(_this.state.matchingOptions[0].label, _this.props.isCaseSensitive); var normalizedSearchValue = (0, _matching_options.transformForCaseSensitivity)(searchValue, _this.props.isCaseSensitive); return normalizedSearchSubject === normalizedSearchValue; }); _defineProperty(_assertThisInitialized(_this), "areAllOptionsSelected", function () { var _this$props2 = _this.props, options = _this$props2.options, selectedOptions = _this$props2.selectedOptions, async = _this$props2.async, isCaseSensitive = _this$props2.isCaseSensitive; // Assume if this is async then there could be infinite options. if (async) { return false; } var flattenOptions = (0, _matching_options.flattenOptionGroups)(options).map(function (option) { return _objectSpread(_objectSpread({}, option), {}, { label: (0, _matching_options.transformForCaseSensitivity)(option.label.trim(), isCaseSensitive) }); }); var numberOfSelectedOptions = 0; selectedOptions.forEach(function (_ref3) { var label = _ref3.label; var trimmedLabel = (0, _matching_options.transformForCaseSensitivity)(label.trim(), isCaseSensitive); if (flattenOptions.findIndex(function (option) { return option.label === trimmedLabel; }) !== -1) numberOfSelectedOptions += 1; }); return flattenOptions.length === numberOfSelectedOptions; }); _defineProperty(_assertThisInitialized(_this), "onComboBoxFocus", function (event) { if (_this.props.onFocus) { _this.props.onFocus(event); } _this.openList(); _this.setState({ hasFocus: true }); }); _defineProperty(_assertThisInitialized(_this), "setCustomOptions", function (isContainerBlur) { var searchValue = _this.state.searchValue; var delimiter = _this.props.delimiter; if (delimiter) { searchValue.split(delimiter).forEach(function (option) { if (option.length > 0) _this.addCustomOption(isContainerBlur, option); }); } else { _this.addCustomOption(isContainerBlur, searchValue); } }); _defineProperty(_assertThisInitialized(_this), "onContainerBlur", function (event) { // close the options list, unless the user clicked on an option var relatedTarget = event.relatedTarget; var focusedInOptionsList = relatedTarget && _this.listRefInstance && _this.listRefInstance.contains(relatedTarget); var focusedInInput = relatedTarget && _this.comboBoxRefInstance && _this.comboBoxRefInstance.contains(relatedTarget); if (!focusedInOptionsList && !focusedInInput) { _this.closeList(); if (_this.props.onBlur) { _this.props.onBlur(event); } _this.setState({ hasFocus: false }); // If the user tabs away or changes focus to another element, take whatever input they've // typed and convert it into a pill, to prevent the combo box from looking like a text input. if (!_this.hasActiveOption()) { _this.setCustomOptions(true); } } else if (focusedInOptionsList) { // https://github.com/elastic/eui/issues/5179 // need to restore focus to the input box when clicking non-interactive elements // firefox doesn't support calling .focus() during a blur event // https://bugzilla.mozilla.org/show_bug.cgi?id=53579 requestAnimationFrame(function () { var _this$searchInputRefI; (_this$searchInputRefI = _this.searchInputRefInstance) === null || _this$searchInputRefI === void 0 ? void 0 : _this$searchInputRefI.focus(); }); } }); _defineProperty(_assertThisInitialized(_this), "onKeyDown", function (event) { if (_this.props.isDisabled) return; switch (event.key) { case _services.keys.ARROW_UP: event.preventDefault(); event.stopPropagation(); if (_this.state.isListOpen) { _this.incrementActiveOptionIndex(-1); } else { _this.openList(); } break; case _services.keys.ARROW_DOWN: event.preventDefault(); event.stopPropagation(); if (_this.state.isListOpen) { _this.incrementActiveOptionIndex(1); } else { _this.openList(); } break; case _services.keys.ESCAPE: if (_this.state.isListOpen) { event.preventDefault(); event.stopPropagation(); _this.closeList(); } break; case _services.keys.ENTER: event.preventDefault(); event.stopPropagation(); if (_this.hasActiveOption()) { _this.onAddOption(_this.state.matchingOptions[_this.state.activeOptionIndex]); } else { _this.setCustomOptions(false); } break; case _services.keys.TAB: // Disallow tabbing when the user is navigating the options. if (_this.hasActiveOption() && _this.state.isListOpen) { event.preventDefault(); event.stopPropagation(); } break; default: if (_this.props.onKeyDown) { _this.props.onKeyDown(event); } } }); _defineProperty(_assertThisInitialized(_this), "onOptionEnterKey", function (option) { _this.onAddOption(option); }); _defineProperty(_assertThisInitialized(_this), "onOptionClick", function (option) { _this.onAddOption(option); }); _defineProperty(_assertThisInitialized(_this), "onAddOption", function (addedOption, isContainerBlur) { if (addedOption.disabled) { return; } var _this$props3 = _this.props, onChange = _this$props3.onChange, selectedOptions = _this$props3.selectedOptions, singleSelectionProp = _this$props3.singleSelection; var singleSelection = Boolean(singleSelectionProp); var changeOptions = singleSelection ? [addedOption] : selectedOptions.concat(addedOption); if (onChange) { onChange(changeOptions); } _this.clearSearchValue(); _this.clearActiveOption(); if (!isContainerBlur) { if (_this.searchInputRefInstance) { _this.searchInputRefInstance.focus(); } } if (singleSelection) { requestAnimationFrame(function () { return _this.closeList(); }); } else { _this.setState({ activeOptionIndex: _this.state.matchingOptions.indexOf(addedOption) }); } }); _defineProperty(_assertThisInitialized(_this), "onRemoveOption", function (removedOption) { var _this$props4 = _this.props, onChange = _this$props4.onChange, selectedOptions = _this$props4.selectedOptions; if (onChange) { onChange(selectedOptions.filter(function (option) { return option !== removedOption; })); } _this.clearActiveOption(); }); _defineProperty(_assertThisInitialized(_this), "clearSelectedOptions", function () { var onChange = _this.props.onChange; if (onChange) { onChange([]); } // Clicking the clear button will also cause it to disappear. This would result in focus // shifting unexpectedly to the body element so we set it to the input which is more reasonable, if (_this.searchInputRefInstance) { _this.searchInputRefInstance.focus(); } if (!_this.state.isListOpen) { _this.openList(); } }); _defineProperty(_assertThisInitialized(_this), "onComboBoxClick", function () { // When the user clicks anywhere on the box, enter the interaction state. if (_this.searchInputRefInstance) { _this.searchInputRefInstance.focus(); } // If the user does this from a state in which an option has focus, then we need to reset it or clear it. if (Boolean(_this.props.singleSelection) && _this.props.selectedOptions.length === 1) { var selectedOptionIndex = _this.state.matchingOptions.findIndex(function (option) { return option.label === _this.props.selectedOptions[0].label && option.key === _this.props.selectedOptions[0].key; }); _this.setState({ activeOptionIndex: selectedOptionIndex }); } else { _this.clearActiveOption(); } }); _defineProperty(_assertThisInitialized(_this), "onOpenListClick", function () { if (_this.searchInputRefInstance) { _this.searchInputRefInstance.focus(); } if (!_this.state.isListOpen) { _this.openList(); } }); _defineProperty(_assertThisInitialized(_this), "onOptionListScroll", function () { if (_this.searchInputRefInstance) { _this.searchInputRefInstance.focus(); } }); _defineProperty(_assertThisInitialized(_this), "onCloseListClick", function () { _this.closeList(); }); _defineProperty(_assertThisInitialized(_this), "onSearchChange", function (searchValue) { var _this$props5 = _this.props, onSearchChange = _this$props5.onSearchChange, delimiter = _this$props5.delimiter; if (onSearchChange) { var _hasMatchingOptions = _this.state.matchingOptions.length > 0; onSearchChange(searchValue, _hasMatchingOptions); } _this.setState({ searchValue: searchValue }, function () { if (searchValue && _this.state.isListOpen === false) _this.openList(); }); if (delimiter && searchValue.endsWith(delimiter)) { _this.setCustomOptions(false); } }); _defineProperty(_assertThisInitialized(_this), "updateMatchingOptionsIfDifferent", function (newMatchingOptions) { var _this$state = _this.state, matchingOptions = _this$state.matchingOptions, activeOptionIndex = _this$state.activeOptionIndex; var _this$props6 = _this.props, singleSelection = _this$props6.singleSelection, selectedOptions = _this$props6.selectedOptions; var areOptionsDifferent = false; if (matchingOptions.length !== newMatchingOptions.length) { areOptionsDifferent = true; } else { for (var i = 0; i < matchingOptions.length; i++) { if (matchingOptions[i].label !== newMatchingOptions[i].label) { areOptionsDifferent = true; break; } } } if (areOptionsDifferent) { _this.optionsRefInstances = []; var nextActiveOptionIndex = activeOptionIndex; // ensure that the currently selected single option is active if it is in the matchingOptions if (Boolean(singleSelection) && selectedOptions.length === 1) { if (newMatchingOptions.includes(selectedOptions[0])) { nextActiveOptionIndex = newMatchingOptions.indexOf(selectedOptions[0]); } } _this.setState({ matchingOptions: newMatchingOptions, activeOptionIndex: nextActiveOptionIndex }); if (!newMatchingOptions.length) { // Prevent endless setState -> componentWillUpdate -> setState loop. if (_this.hasActiveOption()) { _this.clearActiveOption(); } } } }); return _this; } _createClass(EuiComboBox, [{ key: "componentDidMount", value: function componentDidMount() { var _this2 = this; this._isMounted = true; // TODO: This will need to be called once the actual stylesheet loads. setTimeout(function () { if (_this2.autoSizeInputRefInstance) { _this2.autoSizeInputRefInstance.copyInputStyles(); } }, 100); } }, { key: "componentDidUpdate", value: function componentDidUpdate() { var _this$props7 = this.props, options = _this$props7.options, selectedOptions = _this$props7.selectedOptions, singleSelection = _this$props7.singleSelection, sortMatchesBy = _this$props7.sortMatchesBy; var searchValue = this.state.searchValue; // React 16.3 has a bug (fixed in 16.4) where getDerivedStateFromProps // isn't called after a state change, and we track `searchValue` in state // instead we need to react to a change in searchValue here this.updateMatchingOptionsIfDifferent((0, _matching_options.getMatchingOptions)({ options: options, selectedOptions: selectedOptions, searchValue: searchValue, isCaseSensitive: this.props.isCaseSensitive, isPreFiltered: this.props.async, showPrevSelected: Boolean(singleSelection), sortMatchesBy: sortMatchesBy })); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { this._isMounted = false; } }, { key: "render", value: function render() { var _this3 = this; var _this$props8 = this.props, dataTestSubj = _this$props8['data-test-subj'], async = _this$props8.async, className = _this$props8.className, compressed = _this$props8.compressed, customOptionText = _this$props8.customOptionText, fullWidth = _this$props8.fullWidth, id = _this$props8.id, inputRef = _this$props8.inputRef, isCaseSensitive = _this$props8.isCaseSensitive, isClearable = _this$props8.isClearable, isDisabled = _this$props8.isDisabled, isInvalid = _this$props8.isInvalid, isLoading = _this$props8.isLoading, noSuggestions = _this$props8.noSuggestions, onBlur = _this$props8.onBlur, onChange = _this$props8.onChange, onCreateOption = _this$props8.onCreateOption, onSearchChange = _this$props8.onSearchChange, options = _this$props8.options, placeholder = _this$props8.placeholder, renderOption = _this$props8.renderOption, rowHeight = _this$props8.rowHeight, selectedOptions = _this$props8.selectedOptions, singleSelection = _this$props8.singleSelection, prepend = _this$props8.prepend, sortMatchesBy = _this$props8.sortMatchesBy, delimiter = _this$props8.delimiter, append = _this$props8.append, autoFocus = _this$props8.autoFocus, ariaLabel = _this$props8['aria-label'], ariaLabelledby = _this$props8['aria-labelledby'], rest = _objectWithoutProperties(_this$props8, _excluded); var _this$state2 = this.state, activeOptionIndex = _this$state2.activeOptionIndex, hasFocus = _this$state2.hasFocus, isListOpen = _this$state2.isListOpen, listPosition = _this$state2.listPosition, searchValue = _this$state2.searchValue, width = _this$state2.width, matchingOptions = _this$state2.matchingOptions; // Make sure we have a valid ID if users don't pass one as a prop var inputId = id !== null && id !== void 0 ? id : this.rootId('_eui-combobox-id'); // Visually indicate the combobox is in an invalid state if it has lost focus but there is text entered in the input. // When custom options are disabled and the user leaves the combo box after entering text that does not match any // options, this tells the user that they've entered invalid input. var markAsInvalid = !!(isInvalid || (hasFocus === false || isListOpen === false) && searchValue); var classes = (0, _classnames.default)('euiComboBox', className, { 'euiComboBox--compressed': compressed, 'euiComboBox--fullWidth': fullWidth, 'euiComboBox--prepended': prepend, 'euiComboBox--appended': append, 'euiComboBox-isDisabled': isDisabled, 'euiComboBox-isInvalid': markAsInvalid, 'euiComboBox-isOpen': isListOpen }); var value = selectedOptions.map(function (selectedOption) { return selectedOption.label; }).join(', '); var optionsList; if (!noSuggestions && isListOpen) { var optionsListDataTestSubj = dataTestSubj ? "".concat(dataTestSubj, "-optionsList") : undefined; optionsList = (0, _react2.jsx)(_portal.EuiPortal, null, (0, _react2.jsx)(_i18n.EuiI18n, { token: "euiComboBox.listboxAriaLabel", default: "Choose from the following options" }, function (listboxAriaLabel) { return (0, _react2.jsx)(_combo_box_options_list.EuiComboBoxOptionsList, { zIndex: _this3.state.listZIndex, activeOptionIndex: _this3.state.activeOptionIndex, areAllOptionsSelected: _this3.areAllOptionsSelected(), customOptionText: customOptionText, "data-test-subj": optionsListDataTestSubj, fullWidth: fullWidth, isCaseSensitive: isCaseSensitive, isLoading: isLoading, listRef: _this3.listRefCallback, matchingOptions: matchingOptions, onCloseList: _this3.closeList, onCreateOption: onCreateOption, onOptionClick: _this3.onOptionClick, onOptionEnterKey: _this3.onOptionEnterKey, onScroll: _this3.onOptionListScroll, optionRef: _this3.optionRefCallback, options: options, position: listPosition, singleSelection: singleSelection, renderOption: renderOption, rootId: _this3.rootId, rowHeight: rowHeight, scrollToIndex: activeOptionIndex, searchValue: searchValue, selectedOptions: selectedOptions, updatePosition: _this3.updatePosition, width: width, delimiter: delimiter, getSelectedOptionForSearchValue: _matching_options.getSelectedOptionForSearchValue, listboxAriaLabel: listboxAriaLabel }); })); } return ( /** * EuiComboBox follows the WAI-ARIA 1.2 spec for editable comboboxes * with list autocomplete. This pattern is an improvement on the user * experience for screen readers over the WAI-ARIA 1.1 pattern. * * https://www.w3.org/TR/wai-aria-practices-1.2/examples/combobox/combobox-autocomplete-list.html */ (0, _react2.jsx)("div", _extends({}, rest, { className: classes, "data-test-subj": dataTestSubj, onKeyDown: this.onKeyDown, onBlur: this.onContainerBlur, ref: this.comboBoxRefCallback }), (0, _react2.jsx)(_combo_box_input.EuiComboBoxInput, { autoSizeInputRef: this.autoSizeInputRefCallback, compressed: compressed, focusedOptionId: this.hasActiveOption() ? this.rootId("_option-".concat(this.state.activeOptionIndex)) : undefined, fullWidth: fullWidth, hasSelectedOptions: selectedOptions.length > 0, id: inputId, inputRef: this.searchInputRefCallback, isDisabled: isDisabled, isListOpen: isListOpen, noIcon: !!noSuggestions, onChange: this.onSearchChange, onClear: isClearable && !isDisabled ? this.clearSelectedOptions : undefined, onClick: this.onComboBoxClick, onCloseListClick: this.onCloseListClick, onFocus: this.onComboBoxFocus, onOpenListClick: this.onOpenListClick, onRemoveOption: this.onRemoveOption, placeholder: placeholder, rootId: this.rootId, searchValue: searchValue, selectedOptions: selectedOptions, singleSelection: singleSelection, toggleButtonRef: this.toggleButtonRefCallback, updatePosition: this.updatePosition, value: value, append: singleSelection ? append : undefined, prepend: singleSelection ? prepend : undefined, isLoading: isLoading, isInvalid: markAsInvalid, autoFocus: autoFocus, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby }), optionsList) ); } }], [{ key: "getDerivedStateFromProps", value: function getDerivedStateFromProps(nextProps, prevState) { var async = nextProps.async, isCaseSensitive = nextProps.isCaseSensitive, options = nextProps.options, selectedOptions = nextProps.selectedOptions, singleSelection = nextProps.singleSelection, sortMatchesBy = nextProps.sortMatchesBy; var activeOptionIndex = prevState.activeOptionIndex, searchValue = prevState.searchValue; // Calculate and cache the options which match the searchValue, because we use this information // in multiple places and it would be expensive to calculate repeatedly. var matchingOptions = (0, _matching_options.getMatchingOptions)({ options: options, selectedOptions: selectedOptions, searchValue: searchValue, isCaseSensitive: isCaseSensitive, isPreFiltered: async, showPrevSelected: Boolean(singleSelection), sortMatchesBy: sortMatchesBy }); var stateUpdate = { matchingOptions: matchingOptions }; if (activeOptionIndex >= matchingOptions.length) { stateUpdate.activeOptionIndex = -1; } return stateUpdate; } }]); return EuiComboBox; }(_react.Component); exports.EuiComboBox = EuiComboBox; _defineProperty(EuiComboBox, "defaultProps", { async: false, compressed: false, fullWidth: false, isClearable: true, options: [], selectedOptions: [], singleSelection: false, prepend: undefined, append: undefined, sortMatchesBy: 'none' }); EuiComboBox.propTypes = { "data-test-subj": _propTypes.default.string, /** * Updates the list of options asynchronously */ async: _propTypes.default.bool.isRequired, className: _propTypes.default.string, /** * When `true` creates a shorter height input */ compressed: _propTypes.default.bool.isRequired, /** * When `true` expands to the entire width available */ fullWidth: _propTypes.default.bool.isRequired, id: _propTypes.default.string, inputRef: _propTypes.default.any, /** * Shows a button that quickly clears any input */ isClearable: _propTypes.default.bool.isRequired, /** * Disables the input */ isDisabled: _propTypes.default.bool, isInvalid: _propTypes.default.bool, /** * Swaps the dropdown options for a loading spinner */ isLoading: _propTypes.default.bool, /** * Doesn't show the suggestions list/dropdown */ noSuggestions: _propTypes.default.bool, onBlur: _propTypes.default.any, /** * Called every time the query in the combo box is parsed */ onChange: _propTypes.default.func, onFocus: _propTypes.default.any, onKeyDown: _propTypes.default.any, /** * Called every time the text query in the search box is parsed */ onSearchChange: _propTypes.default.func, /** * Sets the placeholder of the input */ placeholder: _propTypes.default.string, /** * Every option must be the same height and must be explicitly set if using a custom render */ rowHeight: _propTypes.default.number, /** * When `true` only allows the user to select a single option. Set to `{ asPlainText: true }` to not render input selection as pills */ singleSelection: _propTypes.default.oneOfType([_propTypes.default.bool.isRequired, _propTypes.default.shape({ asPlainText: _propTypes.default.bool }).isRequired]).isRequired, /** * Display matching options by: * `startsWith`: moves items that start with search value to top of the list; * `none`: don't change the sort order of initial object */ sortMatchesBy: _propTypes.default.oneOf(["none", "startsWith"]).isRequired, /** * Whether to match options with case sensitivity. */ isCaseSensitive: _propTypes.default.bool, /** * Creates an input group with element(s) coming before input. It won't show if `singleSelection` is set to `false`. * `string` | `ReactElement` or an array of these */ prepend: _propTypes.default.any, /** * Creates an input group with element(s) coming after input. It won't show if `singleSelection` is set to `false`. * `string` | `ReactElement` or an array of these */ append: _propTypes.default.any, /** * A special character to use as a value separator. Typically a comma `,` */ delimiter: _propTypes.default.string, /** * Specifies that the input should have focus when the component loads */ autoFocus: _propTypes.default.bool, /** * Required when rendering without a visible label from [EuiFormRow](/#/forms/form-layouts). */ "aria-label": _propTypes.default.string, /** * Reference ID of a text element containing the visible label for the combo box when not * supplied by `aria-label` or from [EuiFormRow](/#/forms/form-layouts). */ "aria-labelledby": _propTypes.default.string, css: _propTypes.default.any };