@ntragas/pouncejstest
Version:
A collection of UI components from Panther labs
215 lines (186 loc) • 8.36 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var _react = _interopRequireDefault(require("react"));
var _downshift = _interopRequireDefault(require("downshift"));
var _fuzzaldrin = require("fuzzaldrin");
var _Box = _interopRequireDefault(require("../Box"));
var _Flex = _interopRequireDefault(require("../Flex"));
var _IconButton = _interopRequireDefault(require("../IconButton"));
var _Input = require("../utils/Input");
var _helpers = require("../../utils/helpers");
var _Menu = _interopRequireDefault(require("../utils/Menu"));
var _ComboBoxItems = _interopRequireDefault(require("../utils/ComboBoxItems/ComboBoxItems"));
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* A simple Combobox can be thought of as a typical `<select>` component. Whenerever you would
* use a normal select, you should now pass the `<Combobox>` component.
*/
function Combobox(_ref) {
var onChange = _ref.onChange,
value = _ref.value,
items = _ref.items,
_ref$variant = _ref.variant,
variant = _ref$variant === void 0 ? 'outline' : _ref$variant,
_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$disableItem = _ref.disableItem,
disableItem = _ref$disableItem === void 0 ? function () {
return false;
} : _ref$disableItem,
_ref$itemToString = _ref.itemToString,
itemToString = _ref$itemToString === void 0 ? function (item) {
return String(item);
} : _ref$itemToString,
itemToGroup = _ref.itemToGroup,
_ref$maxHeight = _ref.maxHeight,
maxHeight = _ref$maxHeight === void 0 ? 300 : _ref$maxHeight,
maxResults = _ref.maxResults,
invalid = _ref.invalid,
required = _ref.required,
hidden = _ref.hidden,
rest = (0, _objectWithoutPropertiesLoose2.default)(_ref, ["onChange", "value", "items", "variant", "searchable", "label", "hideLabel", "disabled", "disableItem", "itemToString", "itemToGroup", "maxHeight", "maxResults", "invalid", "required", "hidden"]);
// convert item to a string with a fallback of empty string
var safeItemToString = function safeItemToString(item) {
return item != undefined ? itemToString(item) : '';
}; // Due to the way we want our Combobox to behave, we want to control the input value ourselves.
// We make sure to update it on every selection made, on every keystroke within the search input,
// plus some focus/blur events that are tied to the searchable behaviour (see below)
var _React$useState = _react.default.useState(''),
inputValue = _React$useState[0],
setInputValue = _React$useState[1];
var getVariant = _react.default.useCallback(function (isOpen) {
if (variant === 'solid') {
return 'solid';
}
return isOpen ? 'solid' : 'outline';
}, [variant]);
_react.default.useLayoutEffect(function () {
setInputValue(safeItemToString(value));
}, [value]);
return /*#__PURE__*/_react.default.createElement(_downshift.default, {
onSelect: function onSelect(selectedItem) {
return setInputValue(safeItemToString(selectedItem));
},
onChange: onChange,
selectedItem: value,
inputValue: inputValue,
itemToString: safeItemToString
}, function (_ref2) {
var getRootProps = _ref2.getRootProps,
getInputProps = _ref2.getInputProps,
getItemProps = _ref2.getItemProps,
getMenuProps = _ref2.getMenuProps,
getLabelProps = _ref2.getLabelProps,
selectedItem = _ref2.selectedItem,
isOpen = _ref2.isOpen,
toggleMenu = _ref2.toggleMenu,
openMenu = _ref2.openMenu,
closeMenu = _ref2.closeMenu;
var comboboxVariant = 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. When the combobox is not searchable, we make the input "behave" like a div. We
// still want an input though for placeholder, spacings, etc.
// 2. When the combobox is searchable, we want it to behave like an empty input when
// focused (showcasing the current value through the placeholder) so that the user can
// see all the options even if he has already selected an option (to do that we need to
// clear the input). If the user blurs, then we revert back to a normal behaviour
var additionalInputProps = (0, _extends2.default)({}, rest, !searchable && {
cursor: 'pointer',
onMouseDown: toggleMenu,
onFocus: openMenu,
readOnly: true
}, searchable && {
placeholder: value != undefined ? itemToString(value) : rest.placeholder,
onChange: function onChange(e) {
return setInputValue(e.currentTarget.value);
},
onFocus: function onFocus() {
openMenu();
setInputValue('');
},
onBlur: function onBlur() {
closeMenu();
setInputValue(safeItemToString(value));
}
});
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,
required: required,
variant: comboboxVariant,
hidden: hidden
}, /*#__PURE__*/_react.default.createElement(_Input.InputElement, (0, _extends2.default)({
as: "input",
type: "text",
truncated: true,
standalone: hideLabel,
pr: 8
/* account for absolute position of caret */
}, getInputProps(additionalInputProps))), /*#__PURE__*/_react.default.createElement(_Input.InputLabel, (0, _extends2.default)({
visuallyHidden: hideLabel,
raised: value != null
}, getLabelProps()), label)), /*#__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 closeMenu();
},
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: selectedItem ? [selectedItem] : undefined
})));
});
}
var _default = (0, _helpers.typedMemo)(Combobox);
exports.default = _default;