UNPKG

@elastic/eui

Version:

Elastic UI Component Library

303 lines (301 loc) 14.6 kB
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import _typeof from "@babel/runtime/helpers/typeof"; import _extends from "@babel/runtime/helpers/extends"; import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/inherits"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; var _excluded = ["key", "label", "color", "onClick", "append", "prepend", "truncationProps", "toolTipContent", "toolTipProps"]; function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* * 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. */ import React, { Component } from 'react'; import classNames from 'classnames'; import { keys, CanvasTextUtils, RenderWithEuiStylesMemoizer } from '../../../services'; import { EuiScreenReaderOnly } from '../../accessibility'; import { EuiFormControlLayout } from '../../form/form_control_layout'; import { EuiComboBoxOptionAppendPrepend } from '../utils'; import { EuiComboBoxPill } from './combo_box_pill'; import { euiComboBoxInputStyles } from './combo_box_input.styles'; import { jsx as ___EmotionJSX } from "@emotion/react"; export var EuiComboBoxInput = /*#__PURE__*/function (_Component) { function EuiComboBoxInput() { var _this; _classCallCheck(this, EuiComboBoxInput); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _callSuper(this, EuiComboBoxInput, [].concat(args)); _defineProperty(_this, "state", { inputWidth: 2, hasFocus: false }); _defineProperty(_this, "widthUtils", void 0); _defineProperty(_this, "inputRefCallback", function (el) { var _this$props$inputRef, _this$props; _this.widthUtils = new CanvasTextUtils({ container: el }); (_this$props$inputRef = (_this$props = _this.props).inputRef) === null || _this$props$inputRef === void 0 || _this$props$inputRef.call(_this$props, el); }); _defineProperty(_this, "updateInputSize", function (inputValue) { if (!_this.widthUtils) return; if (_this.asPlainText) return; _this.widthUtils.setTextToCheck(inputValue); // Canvas has minute subpixel differences in rendering compared to DOM // We'll buffer the input by ~2px just to ensure sufficient width var inputWidth = Math.ceil(_this.widthUtils.textWidth) + 2; _this.setState({ inputWidth: inputWidth }); }); _defineProperty(_this, "onFocus", function (event) { _this.props.onFocus(event); _this.setState({ hasFocus: true }); }); _defineProperty(_this, "onBlur", function (event) { if (_this.props.onBlur) { _this.props.onBlur(event); } _this.setState({ hasFocus: false }); }); _defineProperty(_this, "onKeyDown", function (event) { var _this$props2 = _this.props, searchValue = _this$props2.searchValue, hasSelectedOptions = _this$props2.hasSelectedOptions, selectedOptions = _this$props2.selectedOptions, onRemoveOption = _this$props2.onRemoveOption, singleSelection = _this$props2.singleSelection, isListOpen = _this$props2.isListOpen, onOpenListClick = _this$props2.onOpenListClick, onChange = _this$props2.onChange; var searchIsEmpty = !searchValue.length; if (event.key === keys.BACKSPACE) { // When backspacing in a plain text combobox, change normally and remove the selection if (_this.asPlainText) { onChange(event.currentTarget.value); if (hasSelectedOptions) { onRemoveOption(selectedOptions[selectedOptions.length - 1]); } } // When backspacing from an empty input, delete the last pill option in the list else if (searchIsEmpty && hasSelectedOptions) { onRemoveOption(selectedOptions[selectedOptions.length - 1]); if (!!singleSelection && !isListOpen) { onOpenListClick(); } } } }); _defineProperty(_this, "renderPills", function () { // Don't render a pill for plain text comboboxes - use the input instead if (_this.asPlainText) return null; // Don't render the single pill selection while searching if (_this.props.singleSelection && _this.props.searchValue) return null; var _this$props3 = _this.props, selectedOptions = _this$props3.selectedOptions, isDisabled = _this$props3.isDisabled, onRemoveOption = _this$props3.onRemoveOption; if (!selectedOptions || !selectedOptions.length) return null; return selectedOptions.map(function (option) { var key = option.key, label = option.label, color = option.color, onClick = option.onClick, append = option.append, prepend = option.prepend, truncationProps = option.truncationProps, toolTipContent = option.toolTipContent, toolTipProps = option.toolTipProps, rest = _objectWithoutProperties(option, _excluded); var pillOnClose = isDisabled || _this.props.singleSelection || onClick ? undefined : onRemoveOption; return ___EmotionJSX(EuiComboBoxPill, _extends({ option: option, onClose: pillOnClose, key: key !== null && key !== void 0 ? key : label.toLowerCase(), color: color, onClick: onClick, onClickAriaLabel: onClick ? 'Change' : undefined }, rest), label); }); }); return _this; } _inherits(EuiComboBoxInput, _Component); return _createClass(EuiComboBoxInput, [{ key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { if (prevProps.searchValue !== this.props.searchValue) { this.updateInputSize(this.props.searchValue); } } }, { key: "asPlainText", get: function get() { var singleSelection = this.props.singleSelection; var isSingleSelectionConfig = singleSelection && _typeof(singleSelection) === 'object'; return !!(isSingleSelectionConfig && singleSelection.asPlainText); } }, { key: "searchValue", get: function get() { var _this$props4 = this.props, searchValue = _this$props4.searchValue, selectedOptions = _this$props4.selectedOptions; if (this.asPlainText) { var _selectedOptions$; return searchValue || (selectedOptions === null || selectedOptions === void 0 || (_selectedOptions$ = selectedOptions[0]) === null || _selectedOptions$ === void 0 ? void 0 : _selectedOptions$.label) || ''; } else { return searchValue; } } }, { key: "render", value: function render() { var _this2 = this; var _this$props5 = this.props, compressed = _this$props5.compressed, focusedOptionId = _this$props5.focusedOptionId, fullWidth = _this$props5.fullWidth, hasSelectedOptions = _this$props5.hasSelectedOptions, id = _this$props5.id, isDisabled = _this$props5.isDisabled, isListOpen = _this$props5.isListOpen, noIcon = _this$props5.noIcon, _onChange = _this$props5.onChange, onClear = _this$props5.onClear, onClick = _this$props5.onClick, onFocus = _this$props5.onFocus, onCloseListClick = _this$props5.onCloseListClick, onOpenListClick = _this$props5.onOpenListClick, placeholder = _this$props5.placeholder, rootId = _this$props5.rootId, searchValue = _this$props5.searchValue, selectedOptions = _this$props5.selectedOptions, singleSelection = _this$props5.singleSelection, value = _this$props5.value, prepend = _this$props5.prepend, append = _this$props5.append, isLoading = _this$props5.isLoading, isInvalid = _this$props5.isInvalid, autoFocus = _this$props5.autoFocus, ariaLabel = _this$props5['aria-label'], ariaLabelledby = _this$props5['aria-labelledby'], ariaDescribedby = _this$props5['aria-describedby']; var removeOptionMessage; var removeOptionMessageId; if (this.state.hasFocus) { var readPlaceholder = placeholder ? "".concat(placeholder, ".") : ''; var removeOptionMessageContent = "Combo box. Selected. ".concat(searchValue ? "".concat(searchValue, ". Selected. ") : '').concat(selectedOptions && selectedOptions.length > 0 ? "".concat(value, ". Press Backspace to delete ").concat(selectedOptions[selectedOptions.length - 1].label, ". ") : '', "Combo box input. ").concat(readPlaceholder, " Type some text or, to display a list of choices, press Down Arrow. ") + 'To exit the list of choices, press Escape.'; removeOptionMessageId = rootId('removeOptionMessage'); // aria-live="assertive" will read this message aloud immediately once it enters the DOM. // We'll render to the DOM when the input gains focus and remove it when the input loses focus. // We'll use aria-hidden to prevent default aria information from being read by the screen // reader. removeOptionMessage = ___EmotionJSX(EuiScreenReaderOnly, null, ___EmotionJSX("span", { "aria-live": "polite", id: removeOptionMessageId }, removeOptionMessageContent)); } var isInGroup = singleSelection && (prepend || append); var showPlaceholder = placeholder && !(selectedOptions !== null && selectedOptions !== void 0 && selectedOptions.length) && !searchValue; var clickProps = {}; if (!isDisabled && onClear && hasSelectedOptions) { clickProps.clear = { 'data-test-subj': 'comboBoxClearButton', onClick: onClear }; } var icon; if (!noIcon && !isDisabled) { icon = { 'aria-label': isListOpen ? 'Close list of options' : 'Open list of options', 'data-test-subj': 'comboBoxToggleListButton', disabled: isDisabled, onClick: isListOpen && !isDisabled ? onCloseListClick : onOpenListClick, side: 'right', tabIndex: -1, type: 'chevronSingleDown' }; } var wrapClasses = classNames('euiComboBox__inputWrap', { 'euiComboBox__inputWrap--plainText': this.asPlainText }); return ___EmotionJSX(RenderWithEuiStylesMemoizer, null, function (stylesMemoizer) { var styles = stylesMemoizer(euiComboBoxInputStyles); var stateCss = isListOpen ? styles.open : isInvalid ? styles.invalid : undefined; var cssStyles = [styles.euiComboBoxInputWrapper, !singleSelection && styles.multiSelect, compressed ? styles.compressed : styles.uncompressed].concat(_toConsumableArray(_this2.asPlainText || showPlaceholder ? [styles.plainText.plainText, compressed ? styles.plainText.compressed : styles.plainText.uncompressed] : []), [isDisabled ? styles.disabled : stateCss, isInGroup && styles.inGroup]); var formLayoutStyles = [styles.formLayout.euiComboBox__formControlLayout, !singleSelection && styles.formLayout.multiSelect]; return ___EmotionJSX(EuiFormControlLayout, _extends({ icon: icon }, clickProps, { inputId: id, isLoading: isLoading, isInvalid: isInvalid, isDisabled: isDisabled, compressed: compressed, fullWidth: fullWidth, prepend: prepend, append: append, css: formLayoutStyles }), ___EmotionJSX("div", { css: cssStyles, className: wrapClasses, "data-test-subj": "comboBoxInput", onClick: onClick, tabIndex: -1 // becomes onBlur event's relatedTarget, otherwise relatedTarget is null when clicking on this div }, _this2.renderPills(), ___EmotionJSX(EuiComboBoxOptionAppendPrepend, { option: _this2.asPlainText ? selectedOptions === null || selectedOptions === void 0 ? void 0 : selectedOptions[0] : undefined, classNamePrefix: "euiComboBoxPlainTextSelection", marginSize: "xxs" }, ___EmotionJSX("input", { "aria-activedescendant": focusedOptionId, "aria-autocomplete": "list", "aria-controls": isListOpen ? rootId('listbox') : '', "aria-expanded": isListOpen, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, "aria-invalid": isInvalid, "aria-haspopup": "listbox", css: styles.euiComboBoxInput, className: "euiComboBox__input", "data-test-subj": "comboBoxSearchInput", disabled: isDisabled, id: id, onBlur: _this2.onBlur, onChange: function onChange(event) { return _onChange(event.target.value); }, onFocus: _this2.onFocus, onKeyDown: _this2.onKeyDown, ref: _this2.inputRefCallback, role: "combobox", style: { inlineSize: _this2.asPlainText || showPlaceholder ? '100%' : _this2.state.inputWidth }, placeholder: showPlaceholder ? placeholder : undefined, value: _this2.searchValue, autoFocus: autoFocus, autoComplete: "off" // Force the menu to re-open on every input click - only necessary when plain text , onClick: _this2.asPlainText ? onFocus : undefined // Type shenanigans - event should be mostly the same })), removeOptionMessage)); }); } }]); }(Component);