UNPKG

@ntragas/pouncejstest

Version:

A collection of UI components from Panther labs

351 lines (303 loc) 13 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _downshift = _interopRequireDefault(require("downshift")); var _fuzzaldrin = require("fuzzaldrin"); var _Box = _interopRequireDefault(require("../Box")); var _IconButton = _interopRequireDefault(require("../IconButton")); var _Flex = _interopRequireDefault(require("../Flex")); var _Input = require("../utils/Input"); var _Tag = _interopRequireDefault(require("./Tag")); var _helpers = require("../../utils/helpers"); var _Menu = _interopRequireDefault(require("../utils/Menu")); var _AbstractButton = _interopRequireDefault(require("../AbstractButton")); var _ComboBoxItems = _interopRequireDefault(require("../utils/ComboBoxItems/ComboBoxItems")); /* eslint-disable @typescript-eslint/no-explicit-any */ var stateReducer = function stateReducer(state, changes) { switch (changes.type) { case _downshift.default.stateChangeTypes.keyDownEnter: case _downshift.default.stateChangeTypes.clickItem: // Whenever the user makes a selection, make sure to reset the input so that he can keep // searching for more. We also make sure to always highlight the first item return (0, _extends2.default)({}, changes, { inputValue: '', highlightedIndex: state.highlightedIndex, isOpen: true }); case _downshift.default.stateChangeTypes.blurInput: return (0, _extends2.default)({}, changes, { inputValue: '' }); default: return changes; } }; /** * A simple MultiCombobox can be thought of as a typical `<select>` component. Whenever you would * use a normal select, you should now pass the `<MultiCombobox>` component. */ function MultiCombobox(_ref) { var onChange = _ref.onChange, value = _ref.value, _ref$variant = _ref.variant, variant = _ref$variant === void 0 ? 'outline' : _ref$variant, items = _ref.items, _ref$disableItem = _ref.disableItem, disableItem = _ref$disableItem === void 0 ? function () { return false; } : _ref$disableItem, _ref$searchable = _ref.searchable, searchable = _ref$searchable === void 0 ? false : _ref$searchable, _ref$label = _ref.label, label = _ref$label === void 0 ? '' : _ref$label, hideLabel = _ref.hideLabel, _ref$disabled = _ref.disabled, disabled = _ref$disabled === void 0 ? false : _ref$disabled, _ref$placeholder = _ref.placeholder, placeholder = _ref$placeholder === void 0 ? '' : _ref$placeholder, _ref$itemToString = _ref.itemToString, _itemToString = _ref$itemToString === void 0 ? function (item) { return String(item); } : _ref$itemToString, itemToGroup = _ref.itemToGroup, _ref$allowAdditions = _ref.allowAdditions, allowAdditions = _ref$allowAdditions === void 0 ? false : _ref$allowAdditions, _ref$validateAddition = _ref.validateAddition, validateAddition = _ref$validateAddition === void 0 ? function () { return true; } : _ref$validateAddition, _ref$maxHeight = _ref.maxHeight, maxHeight = _ref$maxHeight === void 0 ? 300 : _ref$maxHeight, maxResults = _ref.maxResults, canClearAllAfter = _ref.canClearAllAfter, invalid = _ref.invalid, hidden = _ref.hidden, rest = (0, _objectWithoutPropertiesLoose2.default)(_ref, ["onChange", "value", "variant", "items", "disableItem", "searchable", "label", "hideLabel", "disabled", "placeholder", "itemToString", "itemToGroup", "allowAdditions", "validateAddition", "maxHeight", "maxResults", "canClearAllAfter", "invalid", "hidden"]); var getVariant = _react.default.useCallback(function (isOpen) { if (variant === 'solid') { return 'solid'; } return isOpen ? 'solid' : 'outline'; }, [variant]); var removeItem = function removeItem(item) { onChange(value.filter(function (i) { return i !== item; })); }; var handleChange = function handleChange(item) { if (item === null) { return; } var changedItems = Array.isArray(item) ? item : [item]; // If items are already added, remove them from the selected items list if (changedItems.every(function (item) { return value.map(_itemToString).includes(_itemToString(item)); })) { return onChange(value.filter(function (i) { return !changedItems.map(_itemToString).includes(_itemToString(i)); })); } // Append non existing items to the list of selected items var newItems = changedItems.filter(function (i) { return !value.map(_itemToString).includes(_itemToString(i)); }); return onChange([].concat(value, newItems)); }; var clearSelectedItems = function clearSelectedItems() { onChange([]); }; var itemsPt = hideLabel ? 3 : '19px'; return /*#__PURE__*/_react.default.createElement(_downshift.default, { stateReducer: stateReducer, onChange: handleChange, selectedItem: null, itemToString: function itemToString(item) { return item && !Array.isArray(item) ? _itemToString(item) : ''; }, initialInputValue: "" }, function (_ref2) { var getRootProps = _ref2.getRootProps, getInputProps = _ref2.getInputProps, getItemProps = _ref2.getItemProps, getMenuProps = _ref2.getMenuProps, getLabelProps = _ref2.getLabelProps, inputValue = _ref2.inputValue, isOpen = _ref2.isOpen, toggleMenu = _ref2.toggleMenu, openMenu = _ref2.openMenu, selectItem = _ref2.selectItem; var processUserValue = function processUserValue(inputVal) { return (inputVal || '').trim(); }; var validateUserValue = function validateUserValue(inputVal) { return inputVal !== '' && validateAddition(inputVal, value); }; var multiComboboxVariant = getVariant(isOpen); var results = items.slice(0, maxResults); // If it's searchable, only filter results by search term when the searching // functionality is available. if (searchable) { // We map the items to a new type in order to feed it to the fuzzySearch generic function. var itemsToSearch = items.map(function (i) { return { // Contains the string representation of the item that will be tested. searchString: itemToGroup ? "" + itemToGroup(i) + _itemToString(i) : _itemToString(i), // Include the actual item in the object so we can map it back after we are done with the search. item: i }; }); results = (0, _fuzzaldrin.filter)(itemsToSearch, inputValue || '', { key: 'searchString', maxResults }).map(function (i) { return i.item; }); } // We add 2 types of additional data to the input that is going to be renders: // 1. A handler for the `Delete` button, so that you can delete tokens with a single key // 3. When the combobox is not searchable, we make the input "behave" like a div. We // still want an input though for placeholder, spacings, etc. var additionalInputProps = (0, _extends2.default)({}, rest, !searchable && { cursor: 'pointer', onMouseDown: toggleMenu, readOnly: true, placeholder: !value.length ? placeholder : '' }, searchable && { placeholder: hideLabel && value.length && !isOpen || value.length ? '' : placeholder, p: isOpen ? 1 : null }, { position: 'absolute', width: '100%', height: '100%', top: 0, left: 0, onFocus: openMenu, onKeyDown: function onKeyDown(event) { // Allow deletions of selections by pressing backspace if (event.key === 'Backspace' && !inputValue) { removeItem(value[value.length - 1]); } // Allow the user to add custom selections if both `searchable` and `allowAdditions` // have a truthy value if ((event.key === 'Enter' || event.key === ',') && allowAdditions) { event.preventDefault(); // By default validateAddition always returns true. Can be overriden by the user // for fine-grained addition var processedUserValue = processUserValue(inputValue); if (validateUserValue(processedUserValue)) { selectItem(processedUserValue, { inputValue: '', isOpen: true }); } } }, onBlur: function onBlur() { var processedUserValue = processUserValue(inputValue); if (allowAdditions && validateUserValue(processedUserValue)) { selectItem(processedUserValue, { inputValue: '' }); } }, onPaste: function onPaste(e) { // prevent it when we can only select values from the dropdown if (!allowAdditions) { return; } // Get clipboard data and split them based on newline and/or commas var clipboardData = e.clipboardData.getData('Text'); var items = clipboardData.replace(/\r?\n/g, ',').split(',').map(processUserValue).filter(validateUserValue); if (items.length > 1) { // Prevent the text from actually being pasted to the underlying input e.preventDefault(); e.stopPropagation(); // extend existing values with new ones onChange([].concat(value, items)); } } }); return /*#__PURE__*/_react.default.createElement(_Box.default, (0, _extends2.default)({ position: "relative" }, getRootProps()), /*#__PURE__*/_react.default.createElement(_Box.default, { position: "relative" }, /*#__PURE__*/_react.default.createElement(_Input.InputControl, { invalid: invalid, disabled: disabled, variant: multiComboboxVariant, hidden: hidden }, /*#__PURE__*/_react.default.createElement(_Flex.default, { as: "ul", wrap: "wrap", align: "baseline", pl: 3, pr: 10, pt: itemsPt, pb: "2px" }, /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, value.map(function (selectedItem) { return /*#__PURE__*/_react.default.createElement(_Tag.default, { as: "li", key: _itemToString(selectedItem), m: 1, onRemove: function onRemove() { return removeItem(selectedItem); } }, _itemToString(selectedItem)); }), /*#__PURE__*/_react.default.createElement(_Box.default, { as: "li", maxWidth: "100%", flexGrow: 1, position: isOpen && searchable ? 'relative' : 'initial' }, isOpen && searchable && /*#__PURE__*/_react.default.createElement(_Input.InputElement, { as: "span", px: 1, py: 0, standalone: hideLabel, visibility: "hidden", color: "transparent", whiteSpace: "pre" }, inputValue), /*#__PURE__*/_react.default.createElement(_Input.InputElement, (0, _extends2.default)({ type: "text", standalone: hideLabel }, getInputProps(additionalInputProps)))))), isOpen && canClearAllAfter && value.length >= canClearAllAfter && /*#__PURE__*/_react.default.createElement(_AbstractButton.default, { width: "100%", py: 1, backgroundColor: "blue-400", fontSize: "2x-small", onClick: clearSelectedItems }, "Clear All"), /*#__PURE__*/_react.default.createElement(_Input.InputLabel, (0, _extends2.default)({ visuallyHidden: hideLabel, raised: !!value.length || isOpen }, getLabelProps()), label)), items.length > 0 && /*#__PURE__*/_react.default.createElement(_Flex.default, { opacity: disabled ? 0.3 : 1, position: "absolute", top: 0, bottom: 0, right: 2, align: "center", justify: "center", pointerEvents: isOpen ? 'auto' : 'none' }, /*#__PURE__*/_react.default.createElement(_IconButton.default, { tabIndex: -1, icon: isOpen ? 'caret-up' : 'caret-down', size: "medium", "aria-label": "Toggle Menu", onClick: function onClick() { return toggleMenu({ inputValue: '' }); }, variant: "unstyled" }))), /*#__PURE__*/_react.default.createElement(_Menu.default, (0, _extends2.default)({ as: "ul", maxHeight: maxHeight, isOpen: isOpen && results.length > 0 }, getMenuProps()), /*#__PURE__*/_react.default.createElement(_ComboBoxItems.default, { items: results, disableItem: disableItem, getItemProps: getItemProps, itemToString: _itemToString, itemToGroup: itemToGroup, selectedItems: value, allowMultipleSelection: true }))); }); } var _default = (0, _helpers.typedMemo)(MultiCombobox); exports.default = _default;