@ntragas/pouncejstest
Version:
A collection of UI components from Panther labs
351 lines (303 loc) • 13 kB
JavaScript
"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;