@pantheon-systems/design-toolkit-react
Version:
Pantheon's React Design Toolkit
248 lines (244 loc) • 10.6 kB
JavaScript
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