UNPKG

@talend/react-components

Version:
335 lines (330 loc) 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = require("react"); var _reactI18next = require("react-i18next"); var _classnames = _interopRequireDefault(require("classnames")); var _memoizeOne = _interopRequireDefault(require("memoize-one")); var _propTypes = _interopRequireDefault(require("prop-types")); var _Actions = require("../Actions"); var _constants = _interopRequireDefault(require("../constants")); var _Icon = _interopRequireDefault(require("../Icon")); var _VirtualizedList = _interopRequireDefault(require("../VirtualizedList")); var _constants2 = require("./constants"); var _Dropdown = _interopRequireDefault(require("./Dropdown.container")); var _ItemOption = require("./ItemOption.component"); var _ItemView = require("./ItemView.component"); var _MultiSelectModule = _interopRequireDefault(require("./MultiSelect.module.scss")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable react/sort-comp */ function initSelectedMap(selected) { return selected.reduce((acc, current) => { acc.set(current, true); return acc; }, new Map()); } function isIn(element, container) { if (element.parentElement === null) { return false; } if (element.parentElement !== container) { return isIn(element.parentElement, container); } return true; } class MultiSelect extends _react.Component { constructor(props) { super(props); this.state = { added: [] }; this.onSearchChange = this.onSearchChange.bind(this); this.onRowClick = this.onRowClick.bind(this); this.onInputFocus = this.onInputFocus.bind(this); this.onInputKeyDown = this.onInputKeyDown.bind(this); this.onClearAll = this.onClearAll.bind(this); this.closeOnOutsideClick = this.closeOnOutsideClick.bind(this); this.initSelectedMap = (0, _memoizeOne.default)(initSelectedMap); } componentDidMount() { document.addEventListener('click', this.closeOnOutsideClick); } componentWillUnmount() { document.removeEventListener('click', this.closeOnOutsideClick); } closeOnOutsideClick(event) { if (this.containerRef !== null && !isIn(event.target, this.containerRef)) { this.setState({ showDropdown: false, searchTerm: '' }); } } onInputFocus(event) { this.setState({ showDropdown: true }); if (this.props.onFocus) { this.props.onFocus(event); } } onInputKeyDown(event) { if (event.key === 'Esc' || event.key === 'Escape') { event.preventDefault(); this.setState({ showDropdown: false }); } } onClearAll(event) { this.updateSelection(event, new Map()); } onSearchChange(event) { this.setState({ searchTerm: event.target.value }); } onRowClick(event, action) { switch (action) { case _constants2.SELECT_ALL_VALUE: this.selectAll(event); break; case _constants2.CREATE_NEW_VALUE: this.createNew(event); break; default: this.selectOne(event, action); } } getSelectedMap() { // uncontrolled if (this.state.selected) { return this.state.selected; } // controlled return this.initSelectedMap(this.props.selected); } getSelectedItems() { const selected = this.getSelectedMap(); return this.props.options.concat(this.state.added || []).filter(item => selected.get(item.value)); } getFilteredOptions() { const { added = [], searchTerm = '' } = this.state; const { options, restricted, t } = this.props; const baseOptions = options.concat(added); if (!searchTerm) { return baseOptions; } // filter base options let hasExactMatch = false; const lowerSearchTerm = searchTerm.toLowerCase(); const filteredOptions = baseOptions.filter(({ name }) => { const lowerOption = name.toLowerCase(); hasExactMatch = hasExactMatch || lowerOption === lowerSearchTerm; return name.toLowerCase().indexOf(lowerSearchTerm) !== -1; }); // insert new value creation option if (!restricted && !hasExactMatch) { filteredOptions.push({ value: _constants2.CREATE_NEW_VALUE, name: t('MULTI_SELECT_LABEL_CREATE_NEW', { defaultValue: '{{name}} (Create entry)', name: searchTerm }) }); } return filteredOptions; } getListItems() { const { searchTerm } = this.state; const { t } = this.props; // apply search term on passed + added options const options = this.getFilteredOptions().map(item => ({ name: item.name, value: item.value, selected: this.getSelectedMap().get(item.value), searchTerm })); // insert select all option if (options.length) { const allSelected = options.every(item => item.selected); options.unshift({ value: _constants2.SELECT_ALL_VALUE, name: allSelected ? t('MULTI_SELECT_LABEL_DESELECT_ALL', { defaultValue: 'Deselect all' }) : t('MULTI_SELECT_LABEL_SELECT_ALL', { defaultValue: 'Select all' }), selected: allSelected }); } return options; } createNew(event) { const newItem = { name: this.state.searchTerm, value: this.state.searchTerm }; const selected = new Map(this.getSelectedMap()); selected.set(newItem.value, true); this.updateSelection(event, selected); this.setState(({ added }) => ({ added: added.concat([newItem]) })); } selectAll(event) { // toggle the select only if all visible items are already selected const selected = this.getSelectedMap(); const options = this.getFilteredOptions(); const alreadySelected = options.every(({ value }) => selected.get(value)); const newSelected = new Map(selected); options.reduce((acc, current) => { if (!alreadySelected) { acc.set(current.value, true); } else { acc.delete(current.value); } return acc; }, newSelected); this.updateSelection(event, newSelected); } selectOne(event, id) { const selected = new Map(this.getSelectedMap()); if (selected.get(id)) { selected.delete(id); } else { selected.set(id, true); } this.updateSelection(event, selected); } updateSelection(event, selected) { if (this.props.onChange) { // controlled this.props.onChange(event, Array.from(selected.keys())); } else { // uncontrolled this.setState({ selected }); } } render() { const items = this.getListItems(); // If we don't have item to display, we show the message that tell there is no items const itemsDisplayed = Math.min(items.length, 6) || 1; const height = this.props.isLoading ? 120 : this.props.itemOptionRender.rowHeight * itemsDisplayed; const nbSelected = this.getSelectedMap().size; const viewHeight = this.props.itemViewRender.rowHeight * Math.min(6, nbSelected + 1); return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { id: this.props.id, className: (0, _classnames.default)('tc-multiselect', _MultiSelectModule.default.container), ref: ref => { this.containerRef = ref; }, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Icon.default, { name: "talend-caret-down", className: _MultiSelectModule.default.caret, transform: this.state.showDropdown ? 'flip-vertical' : null }), !!nbSelected && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Actions.ActionButton, { icon: "talend-cross", bsStyle: "link", className: (0, _classnames.default)('btn-icon-only', 'btn-sm', _MultiSelectModule.default.clearall), label: this.props.t('MULTI_SELECT_LABEL_CLEAR_ALL', { defaultValue: 'Clear all' }), onClick: this.onClearAll, hideLabel: true }), /*#__PURE__*/(0, _jsxRuntime.jsx)("input", { type: "text" // eslint-disable-next-line , role: "search", className: "form-control", name: this.props.name, onBlur: this.props.onBlur, onChange: this.onSearchChange, onFocus: this.onInputFocus, onKeyDown: this.onInputKeyDown, disabled: this.props.disabled // eslint-disable-next-line , autoFocus: this.props.autoFocus, placeholder: this.props.placeholder, readOnly: this.props.readOnly, value: this.state.searchTerm, ref: ref => { this.inputRef = ref; } }), this.state.showDropdown && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Dropdown.default, { height: height, isLoading: this.props.isLoading, items: items, onRowClick: this.onRowClick, renderItem: this.props.itemOptionRender }), !this.state.showDropdown && nbSelected > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { style: { height: viewHeight }, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_VirtualizedList.default, { type: "tc-multiselect", rowHeight: this.props.itemViewRender.rowHeight, rowRenderers: { 'tc-multiselect': this.props.itemViewRender }, collection: this.getSelectedItems(), onRowClick: this.onRowClick }) })] }); } } _defineProperty(MultiSelect, "displayName", 'MultiSelect'); _defineProperty(MultiSelect, "theme", _MultiSelectModule.default); _defineProperty(MultiSelect, "defaultProps", { itemOptionRender: _ItemOption.ItemOption, itemViewRender: _ItemView.ItemView, selected: [], options: [] }); _defineProperty(MultiSelect, "propTypes", { id: _propTypes.default.string.isRequired, name: _propTypes.default.string, placeholder: _propTypes.default.string, t: _propTypes.default.func, selected: _propTypes.default.arrayOf(_propTypes.default.string), itemOptionRender: _propTypes.default.func, itemViewRender: _propTypes.default.func, restricted: _propTypes.default.bool, readOnly: _propTypes.default.bool, disabled: _propTypes.default.bool, autoFocus: _propTypes.default.bool, isLoading: _propTypes.default.bool, onChange: _propTypes.default.func, onBlur: _propTypes.default.func, onFocus: _propTypes.default.func, options: _propTypes.default.array }); var _default = exports.default = (0, _reactI18next.withTranslation)(_constants.default)(MultiSelect); //# sourceMappingURL=MultiSelect.container.js.map