UNPKG

@pantheon-systems/design-toolkit-react

Version:
248 lines (244 loc) 10.6 kB
import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import { useCombobox } from 'downshift'; import { useFormApi } from 'informed'; import PropTypes from 'prop-types'; import { useState, useEffect, Fragment } from 'react'; import Input from './Input.js'; import { jsx, jsxs } from 'react/jsx-runtime'; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var alwaysOpenTextStyle = 'py-1.5 px-3'; var textStyle = 'absolute py-1.5 px-3 left-0 w-full bg-white rounded top-full shadow-dropdown z-dropdown'; var Combobox = function Combobox(_ref) { var disabled = _ref.disabled, dropdownHeaderText = _ref.dropdownHeaderText, field = _ref.field, hideDownButton = _ref.hideDownButton, hideLabel = _ref.hideLabel, initialValue = _ref.initialValue, isLoading = _ref.isLoading, items = _ref.items, itemsAlwaysOpen = _ref.itemsAlwaysOpen, labelText = _ref.labelText, loadingText = _ref.loadingText, maxVisible = _ref.maxVisible, noBorder = _ref.noBorder, noResultsText = _ref.noResultsText, onBlur = _ref.onBlur, onItemSelect = _ref.onItemSelect, placeholder = _ref.placeholder, toggleButtonLabelText = _ref.toggleButtonLabelText, validate = _ref.validate, validateOnBlur = _ref.validateOnBlur, validateOnChange = _ref.validateOnChange, validateOnMount = _ref.validateOnMount; var _useState = useState(items), _useState2 = _slicedToArray(_useState, 2), filteredItems = _useState2[0], setFilteredItems = _useState2[1]; var initialItem = initialValue ? items.find(function (item) { return item.value === initialValue; }) : null; var initialDisplayValue = initialItem ? initialItem.displayName : initialValue; var _useState3 = useState(initialDisplayValue), _useState4 = _slicedToArray(_useState3, 2), userInput = _useState4[0], setUserInput = _useState4[1]; var formApi = useFormApi(); /** * needed to handle items that arrive asynchronously, after initial mount/render and state is * set. list filtering is being handled in this useEffect now instead of using the downshift hook, * in order to correctly handle user input, and apply the filter, prior to items arriving */ useEffect(function () { var input = userInput ? userInput.toLowerCase() : userInput; if (input) { setFilteredItems(items.filter(function (_ref2) { var searchIndex = _ref2.searchIndex; return searchIndex.some(function (item) { return item.toLowerCase().includes(input); }); })); } else { // If there is no input, show all the items. setFilteredItems(items); } }, [items, userInput]); formApi.emitter.on('reset', function () { setUserInput(initialDisplayValue); }); formApi.emitter.on('value', function (fieldName, value) { if (field === fieldName) { var itemChange = items.find(function (item) { return item.value === value; }); setUserInput(itemChange ? itemChange.displayName : value); } }); var _useCombobox = useCombobox({ items: filteredItems, itemToString: function itemToString(item) { return item ? item.displayName : ''; }, onInputValueChange: function onInputValueChange(_ref3) { var _ref3$inputValue = _ref3.inputValue, inputValue = _ref3$inputValue === void 0 ? '' : _ref3$inputValue; return setUserInput(inputValue); }, onSelectedItemChange: function onSelectedItemChange(item) { var selectedItem = item.selectedItem; // This can't be bound on initial mount, as the child component doesn't exist yet. var _formApi$getField = formApi.getField(field), fieldApi = _formApi$getField.fieldApi; fieldApi.setValue(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.value); onItemSelect(selectedItem); } }), isOpen = _useCombobox.isOpen, getComboboxProps = _useCombobox.getComboboxProps, getInputProps = _useCombobox.getInputProps, getItemProps = _useCombobox.getItemProps, getLabelProps = _useCombobox.getLabelProps, getMenuProps = _useCombobox.getMenuProps, getToggleButtonProps = _useCombobox.getToggleButtonProps, highlightedIndex = _useCombobox.highlightedIndex; var showNoResultsText = noResultsText.length > 0 && filteredItems.length === 0; var hasDropdownHeader = dropdownHeaderText.length > 0 && items.length > 0; var remCalculation = hasDropdownHeader ? maxVisible * 2.25 + 2.25 // an extra 2.25rem to account for the dropdown header : maxVisible * 2.25; var itemsHeader = hasDropdownHeader ? /*#__PURE__*/jsx("li", { className: "m-0 pt-3 pb-1 px-3 font-semibold text-sm uppercase text-gray-4", children: dropdownHeaderText }) : null; var itemsList = filteredItems.map(function (item, index) { return /*#__PURE__*/jsx("li", _objectSpread(_objectSpread({ className: "m-0 ".concat(item.isUnprivileged ? 'unprivileged-item-combobox' : "py-1.5 px-3 ".concat(index === highlightedIndex && 'bg-cyan-1 text-blue-4')) // eslint-disable-next-line react/no-array-index-key }, getItemProps({ index: index })), {}, { children: item.content }), "".concat(item.id, "-").concat(index)); }); var itemsDisplay = itemsAlwaysOpen ? /*#__PURE__*/jsxs(Fragment, { children: [/*#__PURE__*/jsxs("ul", _objectSpread(_objectSpread({}, getMenuProps({ className: 'pl-0 m-0 overflow-y-auto list-none' })), {}, { style: { maxHeight: "".concat(remCalculation, "rem") }, children: [itemsHeader, !isLoading && itemsList] })), isLoading && /*#__PURE__*/jsx("div", { className: alwaysOpenTextStyle, children: loadingText }), !isLoading && showNoResultsText && /*#__PURE__*/jsx("div", { className: alwaysOpenTextStyle, children: noResultsText })] }) : /*#__PURE__*/jsxs(Fragment, { children: [/*#__PURE__*/jsxs("ul", _objectSpread(_objectSpread({}, getMenuProps({ className: 'absolute left-0 w-full pl-0 m-0 overflow-y-auto list-none bg-white rounded top-full shadow-dropdown z-dropdown' })), {}, { style: { maxHeight: "".concat(remCalculation, "rem") }, children: [isOpen && itemsHeader, !isLoading && isOpen && itemsList] })), isLoading && isOpen && /*#__PURE__*/jsx("div", { className: textStyle, children: loadingText }), !isLoading && isOpen && showNoResultsText && /*#__PURE__*/jsx("div", { className: textStyle, children: noResultsText })] }); return /*#__PURE__*/jsxs("div", { className: "relative", children: [/*#__PURE__*/jsx("label", _objectSpread(_objectSpread({}, getLabelProps({ hidden: hideLabel, 'aria-hidden': hideLabel })), {}, { children: labelText })), /*#__PURE__*/jsx("div", _objectSpread(_objectSpread({}, getComboboxProps()), {}, { className: "relative combobox", children: /*#__PURE__*/jsx(Input, _objectSpread(_objectSpread({}, getInputProps({ disabled: disabled, field: field, initialValue: initialValue, placeholder: placeholder, refKey: 'inputRef', userInput: userInput, validate: validate, validateOnBlur: validateOnBlur, validateOnChange: validateOnChange, validateOnMount: validateOnMount })), {}, { getToggleButtonProps: getToggleButtonProps, hideDownButton: itemsAlwaysOpen || hideDownButton, noBorder: noBorder, onBlur: onBlur, toggleButtonLabelText: toggleButtonLabelText })) })), itemsDisplay] }); }; var ComboboxItemsProptypes = PropTypes.arrayOf(PropTypes.shape({ // content - this is what gets displayed in the dropdown content: PropTypes.node.isRequired, // displayName - this is what gets displayed in the input when an item is selected displayName: PropTypes.string.isRequired, // isUnprivileged - this is what determines if the item is unprivileged workspace isUnprivileged: PropTypes.bool, // searchIndex - this is what is being searched as the user types in the input searchIndex: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, // value - this is what we care about, and what we set in the Informed form, for use with a service etc value: PropTypes.string.isRequired })); Combobox.defaultProps = { disabled: false, dropdownHeaderText: '', hideDownButton: false, hideLabel: false, initialValue: '', isLoading: false, itemsAlwaysOpen: false, loadingText: '', maxVisible: 4, noBorder: false, noResultsText: '', onBlur: function onBlur() {}, onItemSelect: function onItemSelect() {}, placeholder: '', toggleButtonLabelText: undefined, validate: function validate() {}, validateOnBlur: false, validateOnChange: false, validateOnMount: false }; Combobox.propTypes = { disabled: PropTypes.bool, dropdownHeaderText: PropTypes.string, field: PropTypes.string.isRequired, hideDownButton: PropTypes.bool, hideLabel: PropTypes.bool, initialValue: PropTypes.string, isLoading: PropTypes.bool, items: ComboboxItemsProptypes.isRequired, itemsAlwaysOpen: PropTypes.bool, labelText: PropTypes.string.isRequired, loadingText: PropTypes.string, maxVisible: PropTypes.number, noBorder: PropTypes.bool, noResultsText: PropTypes.string, onBlur: PropTypes.func, onItemSelect: PropTypes.func, placeholder: PropTypes.string, toggleButtonLabelText: PropTypes.string, validate: PropTypes.func, validateOnBlur: PropTypes.bool, validateOnChange: PropTypes.bool, validateOnMount: PropTypes.bool }; var Combobox$1 = Combobox; export { ComboboxItemsProptypes, Combobox$1 as default }; //# sourceMappingURL=Combobox.js.map