UNPKG

@ntragas/pouncejstest

Version:

A collection of UI components from Panther labs

215 lines (186 loc) 8.36 kB
"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;