@boomerang-io/carbon-addons-boomerang-react
Version:
Carbon Addons for Boomerang apps
123 lines (116 loc) • 6.08 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
var cx = require('classnames');
var react = require('@carbon/react');
var icons = require('@carbon/react/icons');
var TooltipHover = require('../TooltipHover/TooltipHover.js');
var settings = require('../../internal/settings.js');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React__default = /*#__PURE__*/_interopDefault(React);
var cx__default = /*#__PURE__*/_interopDefault(cx);
/*
IBM Confidential
694970X, 69497O0
© Copyright IBM Corp. 2022, 2024
*/
function ComboBoxComponent({ disableClear = false, id, label, labelText, titleText, tooltipClassName = `${settings.prefix}--bmrg-select__tooltip`, tooltipContent, tooltipProps = { direction: "top" }, onChange, onInputChange, shouldFilterItem, ...restComboBoxProps }) {
// Set the initial selected item to the label or single value passed
const selectedItemRef = React__default.default.useRef(restComboBoxProps.initialSelectedItem?.label ?? restComboBoxProps.initialSelectedItem);
const queryRef = React__default.default.useRef(selectedItemRef.current);
const [hasQuery, setHasQuery] = React__default.default.useState(false);
// Support several props for the label text
const labelValue = titleText || label || labelText;
/**
* The following three functions are to support a better ComboBox filtering experience
* than the default or passing a `shouldFilterItem` function. With the latter, if you have a selected item
* only that will be displayed if you do a naive filtering of the input.
* We want to:
* 1. Filter options based on the input text and plain value or label of the item
* 2. After selecting a value, show all options when opening the combobox without having to clear the selection
* 3. Filter the values when you enter a query with an item selected
*/
/**
* Keep track of the selected value with a ref so it doesn' re-render and to ensure that
* onInputChange has a fresh value. `onChange` is called, then `onInputChange` when selecting an item
*/
const defaultOnChange = React__default.default.useCallback(({ selectedItem }) => {
if (!selectedItem) {
selectedItemRef.current = selectedItem;
}
if (typeof selectedItem === "string" || typeof selectedItem === "number") {
selectedItemRef.current = selectedItem;
}
else {
selectedItemRef.current = selectedItem?.label;
}
// Additional check if the onInputChange function is not called
// Isn't triggered if the query value matches the selected one
if (queryRef.current === selectedItemRef.current) {
setHasQuery(false);
}
// Call consumer
if (onChange) {
onChange({ selectedItem });
}
}, [onChange]);
/**
* When an item is selected, the `onInputChange` handler is called with the value selected
* so it is difficult to disambiguate between a keydown event and a select event
* Take a simple approach here. If the selectedItem and input values match, there isn't a query
* If they don't, there is a query. We use this to determine if we should filter the values.
*/
const defaultInputChange = (input) => {
queryRef.current = input;
if (input !== selectedItemRef.current) {
setHasQuery(true);
}
else {
setHasQuery(false);
}
// Call consumer
if (onInputChange) {
onInputChange(input);
}
};
/**
* Determine if I should filter the items or not
* Selected value and no query means show everything, otherwise filter based on the input
* No point in optimizing this because re-renders will only occur on query changes
* and we need fresh values on those events to determine how to filter
*/
const defaultShouldFilterItem = ({ item, inputValue }) => {
if (selectedItemRef.current && !hasQuery) {
return true;
}
if (typeof item === "string" || typeof item === "number") {
return String(item).toLowerCase().includes(inputValue?.toLowerCase());
}
if (item && item.label) {
return String(item.label).toLowerCase().includes(inputValue?.toLowerCase());
}
return item;
};
/**
* If a function is passed, use that
* If a false or null value is explicitely passed, then use default filtering behavior in component
* Otherwise use our filtering logic as the new default
*/
let finalShouldFilterItem;
if (typeof shouldFilterItem === "function") {
finalShouldFilterItem = shouldFilterItem;
}
else if (shouldFilterItem === false || shouldFilterItem === null) {
finalShouldFilterItem = undefined;
}
else {
finalShouldFilterItem = defaultShouldFilterItem;
}
return (React__default.default.createElement("div", { key: id, className: cx__default.default(`${settings.prefix}--bmrg-select`, { "--disableClear": disableClear }) },
React__default.default.createElement(react.ComboBox, { id: id, titleText: labelValue && (React__default.default.createElement("div", { style: { display: "flex" } },
React__default.default.createElement("div", null, titleText || labelText || label),
tooltipContent && (React__default.default.createElement("div", { className: tooltipClassName },
React__default.default.createElement(TooltipHover.default, { ...tooltipProps, tooltipText: tooltipContent },
React__default.default.createElement(icons.Information, { size: 16, fill: "currentColor" })))))), onChange: defaultOnChange, onInputChange: defaultInputChange, shouldFilterItem: finalShouldFilterItem, ...restComboBoxProps })));
}
exports.default = ComboBoxComponent;