@coreui/react-pro
Version:
UI Components Library for React.js
246 lines (243 loc) • 17.7 kB
JavaScript
import { __rest, __spreadArray, __assign } from '../../node_modules/tslib/tslib.es6.js';
import React, { forwardRef, useRef, useState, useMemo, useEffect } from 'react';
import classNames from '../../_virtual/index.js';
import PropTypes from 'prop-types';
import { CFormControlWrapper } from '../form/CFormControlWrapper.js';
import { CConditionalPortal } from '../conditional-portal/CConditionalPortal.js';
import { CMultiSelectNativeSelect } from './CMultiSelectNativeSelect.js';
import { CMultiSelectOptions } from './CMultiSelectOptions.js';
import { CMultiSelectSelection } from './CMultiSelectSelection.js';
import { useDropdownWithPopper } from '../../hooks/useDropdownWithPopper.js';
import '@popperjs/core';
import getNextActiveElement from '../../utils/getNextActiveElement.js';
import { flattenOptionsArray, isExternalSearch, filterOptionsList, createOption, selectOptions, isGlobalSearch, getOptionsList } from './utils.js';
var CMultiSelect = forwardRef(function (_a, ref) {
var _b;
var allowCreateOptions = _a.allowCreateOptions, _c = _a.ariaCleanerLabel, ariaCleanerLabel = _c === void 0 ? 'Clear all selections' : _c, className = _a.className, _d = _a.cleaner, cleaner = _d === void 0 ? true : _d, clearSearchOnSelect = _a.clearSearchOnSelect, container = _a.container, disabled = _a.disabled, feedback = _a.feedback, feedbackInvalid = _a.feedbackInvalid, feedbackValid = _a.feedbackValid, id = _a.id, invalid = _a.invalid, label = _a.label, loading = _a.loading, _e = _a.multiple, multiple = _e === void 0 ? true : _e, name = _a.name, onChange = _a.onChange, onFilterChange = _a.onFilterChange, onHide = _a.onHide, onShow = _a.onShow, options = _a.options, _f = _a.optionsMaxHeight, optionsMaxHeight = _f === void 0 ? 'auto' : _f, _g = _a.optionsStyle, optionsStyle = _g === void 0 ? 'checkbox' : _g, optionsTemplate = _a.optionsTemplate, optionsGroupsTemplate = _a.optionsGroupsTemplate, _h = _a.placeholder, placeholder = _h === void 0 ? 'Select...' : _h, _j = _a.portal, portal = _j === void 0 ? false : _j, required = _a.required, _k = _a.resetSelectionOnOptionsChange, resetSelectionOnOptionsChange = _k === void 0 ? false : _k, _l = _a.search, search = _l === void 0 ? true : _l, _m = _a.searchNoResultsLabel, searchNoResultsLabel = _m === void 0 ? 'No results found' : _m, _o = _a.selectAll, selectAll = _o === void 0 ? true : _o, _p = _a.selectAllLabel, selectAllLabel = _p === void 0 ? 'Select all options' : _p, _q = _a.selectionType, selectionType = _q === void 0 ? 'tags' : _q, _r = _a.selectionTypeCounterText, selectionTypeCounterText = _r === void 0 ? 'item(s) selected' : _r, size = _a.size, text = _a.text, tooltipFeedback = _a.tooltipFeedback, valid = _a.valid, value = _a.value, virtualScroller = _a.virtualScroller, _s = _a.visible, visible = _s === void 0 ? false : _s, _t = _a.visibleItems, visibleItems = _t === void 0 ? 10 : _t, rest = __rest(_a, ["allowCreateOptions", "ariaCleanerLabel", "className", "cleaner", "clearSearchOnSelect", "container", "disabled", "feedback", "feedbackInvalid", "feedbackValid", "id", "invalid", "label", "loading", "multiple", "name", "onChange", "onFilterChange", "onHide", "onShow", "options", "optionsMaxHeight", "optionsStyle", "optionsTemplate", "optionsGroupsTemplate", "placeholder", "portal", "required", "resetSelectionOnOptionsChange", "search", "searchNoResultsLabel", "selectAll", "selectAllLabel", "selectionType", "selectionTypeCounterText", "size", "text", "tooltipFeedback", "valid", "value", "virtualScroller", "visible", "visibleItems"]);
var _u = useDropdownWithPopper(), dropdownMenuElement = _u.dropdownMenuElement, dropdownRefElement = _u.dropdownRefElement, isOpen = _u.isOpen, closeDropdown = _u.closeDropdown, openDropdown = _u.openDropdown, toggleDropdown = _u.toggleDropdown, updatePopper = _u.updatePopper;
var nativeSelectRef = useRef(null);
var searchRef = useRef(null);
var isInitialMount = useRef(true);
var _v = useState(''), searchValue = _v[0], setSearchValue = _v[1];
var _w = useState([]), selected = _w[0], setSelected = _w[1];
var _x = useState([]), userOptions = _x[0], setUserOptions = _x[1];
var filteredOptions = useMemo(function () {
return flattenOptionsArray(isExternalSearch(search)
? __spreadArray(__spreadArray([], options, true), filterOptionsList(searchValue, userOptions), true) : filterOptionsList(searchValue, __spreadArray(__spreadArray([], options, true), userOptions, true)), true);
}, [options, searchValue, userOptions]);
var flattenedOptions = useMemo(function () {
return flattenOptionsArray(options).map(function (option) {
if (value && Array.isArray(value)) {
return __assign(__assign({}, option), { selected: value.includes(option.value) });
}
if (value === option.value) {
return __assign(__assign({}, option), { selected: true });
}
return option;
});
}, [options, value]);
var userOption = useMemo(function () {
if (allowCreateOptions &&
filteredOptions.some(function (option) { return option.label && option.label.toLowerCase() === searchValue.toLowerCase(); })) {
return false;
}
return searchRef.current && createOption(String(searchValue), flattenedOptions);
}, [filteredOptions, searchValue]);
useEffect(function () {
if (resetSelectionOnOptionsChange) {
return setSelected([]);
}
var _selected = flattenedOptions.filter(function (option) { return option.selected === true; });
var deselected = flattenedOptions.filter(function (option) { return option.selected === false; });
if (_selected.length > 0) {
setSelected(selectOptions(multiple, _selected, selected, deselected));
}
}, [flattenedOptions]);
useEffect(function () {
if (!isInitialMount.current && onFilterChange) {
onFilterChange(searchValue);
}
}, [searchValue]);
useEffect(function () {
if (!isInitialMount.current && nativeSelectRef.current) {
nativeSelectRef.current.dispatchEvent(new Event('change', { bubbles: true }));
}
updatePopper();
}, [JSON.stringify(selected)]);
useEffect(function () {
visible ? openDropdown() : closeDropdown();
}, [visible]);
useEffect(function () {
var _a;
if (isOpen) {
if (onShow)
onShow();
if (portal && dropdownMenuElement.current && dropdownRefElement.current) {
dropdownMenuElement.current.style.minWidth = "".concat(dropdownRefElement.current.offsetWidth, "px");
}
(_a = searchRef.current) === null || _a === void 0 ? void 0 : _a.focus();
}
return function () {
if (onHide)
onHide();
setSearchValue('');
if (searchRef.current) {
searchRef.current.value = '';
}
};
}, [isOpen]);
useEffect(function () {
isInitialMount.current = false;
}, []);
var handleSearchChange = function (event) {
setSearchValue(event.target.value);
};
var handleSearchKeyDown = function (event) {
if (!isOpen) {
openDropdown();
}
if (event.key === 'ArrowDown' &&
dropdownMenuElement.current &&
searchRef.current &&
searchRef.current.value.length === searchRef.current.selectionStart) {
event.preventDefault();
var items = getOptionsList(dropdownMenuElement.current);
var target = event.target;
getNextActiveElement(items, target, event.key === 'ArrowDown', !items.includes(target)).focus();
return;
}
if (event.key === 'Enter' && searchValue && allowCreateOptions) {
event.preventDefault();
if (userOption) {
setSelected(__spreadArray(__spreadArray([], selected, true), userOption, true));
setUserOptions(__spreadArray(__spreadArray([], userOptions, true), userOption, true));
}
if (!userOption) {
setSelected(__spreadArray(__spreadArray([], selected, true), [
filteredOptions.find(function (option) { return String(option.label).toLowerCase() === searchValue.toLowerCase(); }),
], false));
}
setSearchValue('');
if (searchRef.current) {
searchRef.current.value = '';
}
return;
}
if (searchValue.length > 0) {
return;
}
if (event.key === 'Backspace' || event.key === 'Delete') {
var last_1 = selected.filter(function (option) { return !option.disabled; }).pop();
if (last_1) {
setSelected(selected.filter(function (option) { return option.value !== last_1.value; }));
}
}
};
var handleTogglerKeyDown = function (event) {
if (!isOpen && (event.key === 'Enter' || event.key === 'ArrowDown')) {
event.preventDefault();
openDropdown();
return;
}
if (isOpen && dropdownMenuElement.current && event.key === 'ArrowDown') {
event.preventDefault();
var items = getOptionsList(dropdownMenuElement.current);
var target = event.target;
getNextActiveElement(items, target, event.key === 'ArrowDown', !items.includes(target)).focus();
}
};
var handleGlobalSearch = function (event) {
if (isGlobalSearch(search) &&
searchRef.current &&
(event.key.length === 1 || event.key === 'Backspace' || event.key === 'Delete')) {
searchRef.current.focus();
}
};
var handleOnOptionClick = function (option) {
if (!multiple) {
setSelected([option]);
closeDropdown();
setSearchValue('');
if (searchRef.current) {
searchRef.current.value = '';
}
return;
}
if (option.custom && !userOptions.some(function (_option) { return _option.value === option.value; })) {
setUserOptions(__spreadArray(__spreadArray([], userOptions, true), [option], false));
}
if (clearSearchOnSelect || option.custom) {
setSearchValue('');
if (searchRef.current) {
searchRef.current.value = '';
searchRef.current.focus();
}
}
if (selected.some(function (_option) { return _option.value === option.value; })) {
setSelected(selected.filter(function (_option) { return _option.value !== option.value; }));
}
else {
setSelected(__spreadArray(__spreadArray([], selected, true), [option], false));
}
};
var handleSelectAll = function () {
setSelected(selectOptions(multiple, __spreadArray(__spreadArray([], flattenedOptions.filter(function (option) { return !option.disabled; }), true), userOptions, true), selected));
};
var handleDeselectAll = function () {
setSelected(selected.filter(function (option) { return option.disabled; }));
};
return (React.createElement(CFormControlWrapper, { describedby: rest['aria-describedby'], feedback: feedback, feedbackInvalid: feedbackInvalid, feedbackValid: feedbackValid, id: id, invalid: invalid, label: label, text: text, tooltipFeedback: tooltipFeedback, valid: valid },
React.createElement(CMultiSelectNativeSelect, { id: id, multiple: multiple, name: name, options: selected, required: required, value: multiple
? selected.map(function (option) { return option.value.toString(); })
: selected.map(function (option) { return option.value; })[0], onChange: function () { return onChange && onChange(selected); }, ref: nativeSelectRef }),
React.createElement("div", { className: classNames('form-multi-select', (_b = {},
_b["form-multi-select-".concat(size)] = size,
_b.disabled = disabled,
_b['is-invalid'] = invalid,
_b['is-valid'] = valid,
_b.show = isOpen,
_b), className), onKeyDown: handleGlobalSearch, "aria-expanded": isOpen, ref: ref },
React.createElement("div", __assign({ className: "form-multi-select-input-group" }, (!search && !disabled && { tabIndex: 0 }), { onClick: function () { return !disabled && openDropdown(); }, onKeyDown: handleTogglerKeyDown, ref: dropdownRefElement }),
React.createElement(CMultiSelectSelection, { disabled: disabled, multiple: multiple, onRemove: function (option) { return !disabled && handleOnOptionClick(option); }, placeholder: placeholder, search: search, selected: selected, selectionType: selectionType, selectionTypeCounterText: selectionTypeCounterText },
search && (React.createElement("input", __assign({ type: "text", className: "form-multi-select-search", disabled: disabled, autoComplete: "off", onChange: handleSearchChange, onKeyDown: handleSearchKeyDown }, (selected.length === 0 && { placeholder: placeholder }), (selected.length > 0 &&
selectionType === 'counter' && {
placeholder: "".concat(selected.length, " ").concat(selectionTypeCounterText),
}), (selected.length > 0 &&
!multiple && { placeholder: selected.map(function (option) { return option.label; })[0] }), (multiple &&
selected.length > 0 &&
selectionType !== 'counter' && { size: searchValue.length + 2 }), { ref: searchRef }))),
!search && selected.length === 0 && (React.createElement("span", { className: "form-multi-select-placeholder" }, placeholder))),
React.createElement("div", { className: "form-multi-select-buttons" },
!disabled && cleaner && selected.length > 0 && (React.createElement("button", { type: "button", className: "form-multi-select-cleaner", onClick: function () { return handleDeselectAll(); }, "aria-label": ariaCleanerLabel })),
React.createElement("button", __assign({ type: "button", className: "form-multi-select-indicator", onClick: function (event) {
event.preventDefault();
event.stopPropagation();
if (!disabled) {
toggleDropdown();
}
} }, (disabled && { tabIndex: -1 }))))),
React.createElement(CConditionalPortal, { container: container, portal: portal },
React.createElement("div", { className: classNames('form-multi-select-dropdown', {
show: portal && isOpen,
}), onKeyDown: handleGlobalSearch, role: "menu", ref: dropdownMenuElement },
multiple && selectAll && (React.createElement("button", { type: "button", className: "form-multi-select-all", onClick: function () { return handleSelectAll(); } }, selectAllLabel)),
React.createElement(CMultiSelectOptions, { loading: loading, onOptionOnClick: function (option) { return !disabled && handleOnOptionClick(option); }, options: filteredOptions.length === 0 && allowCreateOptions
? userOption || []
: filteredOptions, optionsMaxHeight: optionsMaxHeight, optionsStyle: optionsStyle, optionsTemplate: optionsTemplate, optionsGroupsTemplate: optionsGroupsTemplate, searchNoResultsLabel: searchNoResultsLabel, selected: selected, virtualScroller: virtualScroller, visibleItems: visibleItems }))))));
});
CMultiSelect.propTypes = __assign({ allowCreateOptions: PropTypes.bool, ariaCleanerLabel: PropTypes.string, className: PropTypes.string, cleaner: PropTypes.bool, clearSearchOnSelect: PropTypes.bool, container: PropTypes.any, disabled: PropTypes.bool, loading: PropTypes.bool, multiple: PropTypes.bool, name: PropTypes.string, onChange: PropTypes.func, onFilterChange: PropTypes.func, onHide: PropTypes.func, onShow: PropTypes.func, options: PropTypes.array.isRequired, optionsMaxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), optionsStyle: PropTypes.oneOf(['checkbox', 'text']), optionsTemplate: PropTypes.func, optionsGroupsTemplate: PropTypes.func, placeholder: PropTypes.string, portal: PropTypes.bool, required: PropTypes.bool, resetSelectionOnOptionsChange: PropTypes.bool, search: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.oneOf(['external', 'global']),
PropTypes.shape({
external: PropTypes.bool.isRequired,
global: PropTypes.bool.isRequired,
}),
]), searchNoResultsLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), selectAll: PropTypes.bool, selectAllLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), selectionType: PropTypes.oneOf(['counter', 'tags', 'text']), selectionTypeCounterText: PropTypes.string, size: PropTypes.oneOf(['sm', 'lg']), value: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
PropTypes.arrayOf(PropTypes.number),
PropTypes.arrayOf(PropTypes.string),
]), virtualScroller: PropTypes.bool, visible: PropTypes.bool, visibleItems: PropTypes.number }, CFormControlWrapper.propTypes);
CMultiSelect.displayName = 'CMultiSelect';
export { CMultiSelect };
//# sourceMappingURL=CMultiSelect.js.map