@carbon/react
Version:
React components for the Carbon Design System
734 lines (723 loc) • 27 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
;
var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
var iconsReact = require('@carbon/icons-react');
var cx = require('classnames');
var Downshift = require('downshift');
var isEqual = require('react-fast-compare');
var PropTypes = require('prop-types');
var React = require('react');
var index$2 = require('../ListBox/index.js');
var MultiSelectPropTypes = require('./MultiSelectPropTypes.js');
var sorting = require('./tools/sorting.js');
var Selection = require('../../internal/Selection.js');
var useId = require('../../internal/useId.js');
var mergeRefs = require('../../tools/mergeRefs.js');
var deprecate = require('../../prop-types/deprecate.js');
var keys = require('../../internal/keyboard/keys.js');
var match = require('../../internal/keyboard/match.js');
var usePrefix = require('../../internal/usePrefix.js');
require('../FluidForm/FluidForm.js');
var FormContext = require('../FluidForm/FormContext.js');
var Checkbox = require('../Checkbox/Checkbox.js');
require('../Checkbox/Checkbox.Skeleton.js');
var noopFn = require('../../internal/noopFn.js');
var react = require('@floating-ui/react');
var index = require('../FeatureFlags/index.js');
var index$1 = require('../AILabel/index.js');
var defaultItemToString = require('../../internal/defaultItemToString.js');
var utils = require('../../internal/utils.js');
var useNormalizedInputProps = require('../../internal/useNormalizedInputProps.js');
var useIsomorphicEffect = require('../../internal/useIsomorphicEffect.js');
var ListBoxPropTypes = require('../ListBox/ListBoxPropTypes.js');
const {
ItemClick,
ToggleButtonBlur,
ToggleButtonKeyDownArrowDown,
ToggleButtonKeyDownArrowUp,
ToggleButtonKeyDownEnter,
ToggleButtonKeyDownEscape,
ToggleButtonKeyDownSpaceButton,
ItemMouseMove,
MenuMouseLeave,
ToggleButtonClick,
ToggleButtonKeyDownPageDown,
ToggleButtonKeyDownPageUp,
FunctionSetHighlightedIndex
} = Downshift.useSelect.stateChangeTypes;
const MultiSelect = /*#__PURE__*/React.forwardRef(({
autoAlign = false,
className: containerClassName,
decorator,
id,
items,
itemToElement,
itemToString = defaultItemToString.defaultItemToString,
titleText = false,
hideLabel,
helperText,
label,
type = 'default',
size,
disabled = false,
initialSelectedItems = [],
sortItems = sorting.defaultSortItems,
compareItems = sorting.defaultCompareItems,
clearSelectionText = 'To clear selection, press Delete or Backspace',
clearAnnouncement = 'all items have been cleared',
clearSelectionDescription = 'Total items selected: ',
light,
invalid = false,
invalidText,
warn = false,
warnText,
useTitleInItem,
translateWithId,
downshiftProps,
open = false,
selectionFeedback = 'top-after-reopen',
onChange,
onMenuChange,
direction = 'bottom',
selectedItems: selected,
readOnly,
locale = 'en',
slug
}, ref) => {
const filteredItems = React.useMemo(() => {
return items.filter(item => {
if (typeof item === 'object' && item !== null) {
for (const key in item) {
if (Object.hasOwn(item, key) && item[key] === undefined) {
return false; // Return false if any property has an undefined value
}
}
}
return true; // Return true if item is not an object with undefined values
});
}, [items]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
const selectAll = filteredItems.some(item => item.isSelectAll);
const prefix = usePrefix.usePrefix();
const {
isFluid
} = React.useContext(FormContext.FormContext);
const multiSelectInstanceId = useId.useId();
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/carbon-design-system/carbon/issues/20452
const [isFocused, setIsFocused] = React.useState(false);
const [inputFocused, setInputFocused] = React.useState(false);
const [isOpen, setIsOpen] = React.useState(open || false);
const [prevOpenProp, setPrevOpenProp] = React.useState(open);
const [topItems, setTopItems] = React.useState([]);
const [itemsCleared, setItemsCleared] = React.useState(false);
const enableFloatingStyles = index.useFeatureFlag('enable-v12-dynamic-floating-styles') || autoAlign;
const {
refs,
floatingStyles,
middlewareData
} = react.useFloating(enableFloatingStyles ? {
placement: direction,
// The floating element is positioned relative to its nearest
// containing block (usually the viewport). It will in many cases also
// “break” the floating element out of a clipping ancestor.
// https://floating-ui.com/docs/misc#clipping
strategy: 'fixed',
// Middleware order matters, arrow should be last
middleware: [autoAlign && react.flip({
crossAxis: false
}), react.size({
apply({
rects,
elements
}) {
Object.assign(elements.floating.style, {
width: `${rects.reference.width}px`
});
}
}), autoAlign && react.hide()],
whileElementsMounted: react.autoUpdate
} : {});
useIsomorphicEffect.default(() => {
if (enableFloatingStyles) {
const updatedFloatingStyles = {
...floatingStyles,
visibility: middlewareData.hide?.referenceHidden ? 'hidden' : 'visible'
};
Object.keys(updatedFloatingStyles).forEach(style => {
if (refs.floating.current) {
refs.floating.current.style[style] = updatedFloatingStyles[style];
}
});
}
}, [enableFloatingStyles, floatingStyles, refs.floating, middlewareData, open]);
const {
selectedItems: controlledSelectedItems,
onItemChange,
clearSelection
} = Selection.useSelection({
disabled,
initialSelectedItems,
onChange,
selectedItems: selected,
selectAll,
filteredItems
});
const sortOptions = {
selectedItems: controlledSelectedItems,
itemToString,
compareItems,
locale
};
const selectProps = {
stateReducer,
isOpen,
itemToString: filteredItems => {
return Array.isArray(filteredItems) && filteredItems.map(item => {
return itemToString(item);
}).join(', ') || '';
},
selectedItem: controlledSelectedItems,
items: filteredItems,
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/carbon-design-system/carbon/issues/20452
isItemDisabled(item, _index) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
return item?.disabled;
},
...downshiftProps
};
const {
getToggleButtonProps,
getLabelProps,
getMenuProps,
getItemProps,
selectedItem,
highlightedIndex,
setHighlightedIndex
} = Downshift.useSelect(selectProps);
const toggleButtonProps = getToggleButtonProps({
onFocus: () => {
setInputFocused(true);
},
onBlur: () => {
setInputFocused(false);
},
onKeyDown: e => {
if (!disabled) {
if ((match.match(e, keys.Delete) || match.match(e, keys.Escape)) && !isOpen) {
clearSelection();
e.stopPropagation();
}
if (!isOpen && match.match(e, keys.Delete) && selectedItems.length > 0) {
setItemsCleared(true);
}
if ((match.match(e, keys.Space) || match.match(e, keys.ArrowDown) || match.match(e, keys.Enter)) && !isOpen) {
setHighlightedIndex(0);
setItemsCleared(false);
setIsOpenWrapper(true);
}
if (match.match(e, keys.ArrowDown) && selectedItems.length === 0) {
setInputFocused(false);
setIsFocused(false);
}
if (match.match(e, keys.Escape) && isOpen) {
setInputFocused(true);
}
if (match.match(e, keys.Enter) && isOpen) {
setInputFocused(true);
}
}
}
});
const toggleButtonRef = React.useRef(null);
const mergedRef = mergeRefs.mergeRefs(toggleButtonProps.ref, ref, toggleButtonRef);
const selectedItems = selectedItem;
/**
* wrapper function to forward changes to consumer
*/
const setIsOpenWrapper = open => {
setIsOpen(open);
if (onMenuChange) {
onMenuChange(open);
}
};
/**
* programmatically control this `open` prop
*/
if (prevOpenProp !== open) {
setIsOpenWrapper(open);
setPrevOpenProp(open);
}
const normalizedProps = useNormalizedInputProps.useNormalizedInputProps({
id,
disabled,
readOnly,
invalid,
warn
});
const inline = type === 'inline';
const showWarning = normalizedProps.warn;
const showHelperText = !normalizedProps.warn && !normalizedProps.invalid && helperText;
const wrapperClasses = cx(`${prefix}--multi-select__wrapper`, `${prefix}--list-box__wrapper`, containerClassName, {
[`${prefix}--multi-select__wrapper--inline`]: inline,
[`${prefix}--list-box__wrapper--inline`]: inline,
[`${prefix}--multi-select__wrapper--inline--invalid`]: inline && normalizedProps.invalid,
[`${prefix}--list-box__wrapper--inline--invalid`]: inline && normalizedProps.invalid,
[`${prefix}--list-box__wrapper--fluid--invalid`]: isFluid && normalizedProps.invalid,
[`${prefix}--list-box__wrapper--slug`]: slug,
[`${prefix}--list-box__wrapper--decorator`]: decorator
});
const titleClasses = cx(`${prefix}--label`, {
[`${prefix}--label--disabled`]: disabled,
[`${prefix}--visually-hidden`]: hideLabel
});
const helperId = !helperText ? undefined : `multiselect-helper-text-${multiSelectInstanceId}`;
const fieldLabelId = `multiselect-field-label-${multiSelectInstanceId}`;
const helperClasses = cx(`${prefix}--form__helper-text`, {
[`${prefix}--form__helper-text--disabled`]: disabled
});
const className = cx(`${prefix}--multi-select`, {
[`${prefix}--multi-select--invalid`]: normalizedProps.invalid,
[`${prefix}--multi-select--invalid--focused`]: inputFocused && normalizedProps.invalid,
[`${prefix}--multi-select--warning`]: showWarning,
[`${prefix}--multi-select--inline`]: inline,
[`${prefix}--multi-select--selected`]: selectedItems && selectedItems.length > 0,
[`${prefix}--list-box--up`]: direction === 'top',
[`${prefix}--multi-select--readonly`]: readOnly,
[`${prefix}--autoalign`]: enableFloatingStyles,
[`${prefix}--multi-select--selectall`]: selectAll
});
if (selectionFeedback === 'fixed') {
sortOptions.selectedItems = [];
} else if (selectionFeedback === 'top-after-reopen') {
sortOptions.selectedItems = topItems;
}
function stateReducer(state, actionAndChanges) {
const {
changes,
props,
type
} = actionAndChanges;
const {
highlightedIndex
} = changes;
if (changes.isOpen && !isOpen) {
setTopItems(controlledSelectedItems);
}
switch (type) {
case ToggleButtonKeyDownSpaceButton:
case ToggleButtonKeyDownEnter:
if (changes.selectedItem === undefined) {
break;
}
if (Array.isArray(changes.selectedItem)) {
break;
}
onItemChange(changes.selectedItem);
return {
...changes,
highlightedIndex: state.highlightedIndex
};
case ToggleButtonBlur:
case ToggleButtonKeyDownEscape:
setIsOpenWrapper(false);
break;
case ToggleButtonClick:
setIsOpenWrapper(changes.isOpen || false);
return {
...changes,
highlightedIndex: controlledSelectedItems.length > 0 ? 0 : undefined
};
case ItemClick:
setHighlightedIndex(changes.selectedItem);
onItemChange(changes.selectedItem);
return {
...changes,
highlightedIndex: state.highlightedIndex
};
case MenuMouseLeave:
return {
...changes,
highlightedIndex: state.highlightedIndex
};
case FunctionSetHighlightedIndex:
if (!isOpen) {
return {
...changes,
highlightedIndex: 0
};
} else {
return {
...changes,
highlightedIndex: filteredItems.indexOf(highlightedIndex)
};
}
case ToggleButtonKeyDownArrowDown:
case ToggleButtonKeyDownArrowUp:
case ToggleButtonKeyDownPageDown:
case ToggleButtonKeyDownPageUp:
if (highlightedIndex > -1) {
const itemArray = document.querySelectorAll(`li.${prefix}--list-box__menu-item[role="option"]`);
props.scrollIntoView(itemArray[highlightedIndex]);
}
if (highlightedIndex === -1) {
return {
...changes,
highlightedIndex: 0
};
}
return changes;
case ItemMouseMove:
return {
...changes,
highlightedIndex: state.highlightedIndex
};
}
return changes;
}
const multiSelectFieldWrapperClasses = cx(`${prefix}--list-box__field--wrapper`, {
[`${prefix}--list-box__field--wrapper--input-focused`]: inputFocused
});
const handleFocus = evt => {
if (evt.target.classList.contains(`${prefix}--tag__close-icon`)) {
setIsFocused(false);
} else {
setIsFocused(evt.type === 'focus');
}
};
const readOnlyEventHandlers = readOnly ? {
onClick: evt => {
// NOTE: does not prevent click
evt.preventDefault();
// focus on the element as per readonly input behavior
if (toggleButtonRef.current) {
toggleButtonRef.current.focus();
}
},
onKeyDown: evt => {
const selectAccessKeys = ['ArrowDown', 'ArrowUp', ' ', 'Enter'];
// This prevents the select from opening for the above keys
if (selectAccessKeys.includes(evt.key)) {
evt.preventDefault();
}
}
} : {};
// AILabel always size `mini`
const candidate = slug ?? decorator;
const candidateIsAILabel = utils.isComponentElement(candidate, index$1.AILabel);
const normalizedDecorator = candidateIsAILabel ? /*#__PURE__*/React.cloneElement(candidate, {
size: 'mini'
}) : candidate;
const itemsSelectedText = selectedItems.length > 0 && selectedItems.map(item => item?.text);
const selectedItemsLength = selectAll ?
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
selectedItems.filter(item => !item.isSelectAll).length : selectedItems.length;
// Memoize the value of getMenuProps to avoid an infinite loop
const menuProps = React.useMemo(() => getMenuProps({
ref: enableFloatingStyles ? refs.setFloating : null
}), [enableFloatingStyles, getMenuProps, refs.setFloating]);
const allLabelProps = getLabelProps();
const labelProps = /*#__PURE__*/React.isValidElement(titleText) ? {
id: allLabelProps.id
} : allLabelProps;
const getSelectionStats = React.useCallback((selectedItems, filteredItems) => {
const hasIndividualSelections = selectedItems.some(selected => !selected.isSelectAll);
const nonSelectAllSelectedCount = selectedItems.filter(selected => !selected.isSelectAll).length;
const totalSelectableCount = filteredItems.filter(item => !item.isSelectAll && !item.disabled).length;
return {
hasIndividualSelections,
nonSelectAllSelectedCount,
totalSelectableCount
};
},
// eslint-disable-next-line react-hooks/exhaustive-deps -- https://github.com/carbon-design-system/carbon/issues/20452
[selectedItems, filteredItems]);
return /*#__PURE__*/React.createElement("div", {
className: wrapperClasses
}, /*#__PURE__*/React.createElement("label", _rollupPluginBabelHelpers.extends({
className: titleClasses
}, labelProps), titleText && titleText, selectedItems.length > 0 && /*#__PURE__*/React.createElement("span", {
className: `${prefix}--visually-hidden`
}, clearSelectionDescription, " ", selectedItems.length, ' ', itemsSelectedText, ",", clearSelectionText)), /*#__PURE__*/React.createElement(index$2.default, {
onFocus: isFluid ? handleFocus : undefined,
onBlur: isFluid ? handleFocus : undefined,
type: type,
size: size,
className: className,
disabled: disabled,
light: light,
invalid: normalizedProps.invalid,
invalidText: invalidText,
warn: normalizedProps.warn,
warnText: warnText,
isOpen: isOpen,
id: id
}, normalizedProps.invalid && /*#__PURE__*/React.createElement(iconsReact.WarningFilled, {
className: `${prefix}--list-box__invalid-icon`
}), showWarning && /*#__PURE__*/React.createElement(iconsReact.WarningAltFilled, {
className: `${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`
}), /*#__PURE__*/React.createElement("div", {
className: multiSelectFieldWrapperClasses,
ref: enableFloatingStyles ? refs.setReference : null
}, selectedItems.length > 0 && /*#__PURE__*/React.createElement(index$2.default.Selection, {
readOnly: readOnly,
clearSelection: !disabled && !readOnly ? clearSelection : noopFn.noopFn,
selectionCount: selectedItemsLength
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
,
translateWithId: translateWithId,
disabled: disabled
}), /*#__PURE__*/React.createElement("button", _rollupPluginBabelHelpers.extends({
type: "button",
className: `${prefix}--list-box__field`,
disabled: disabled,
"aria-disabled": disabled || readOnly,
"aria-describedby": !inline && showHelperText ? helperId : undefined
}, toggleButtonProps, {
ref: mergedRef
}, readOnlyEventHandlers), /*#__PURE__*/React.createElement("span", {
id: fieldLabelId,
className: `${prefix}--list-box__label`
}, label), /*#__PURE__*/React.createElement(index$2.default.MenuIcon, {
isOpen: isOpen,
translateWithId: translateWithId
})), slug ? normalizedDecorator : decorator ? /*#__PURE__*/React.createElement("div", {
className: `${prefix}--list-box__inner-wrapper--decorator`
}, normalizedDecorator) : ''), /*#__PURE__*/React.createElement(index$2.default.Menu, menuProps, isOpen &&
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- https://github.com/carbon-design-system/carbon/issues/20452
sortItems(filteredItems, sortOptions).map((item, index) => {
const {
hasIndividualSelections,
nonSelectAllSelectedCount,
totalSelectableCount
} = getSelectionStats(selectedItems, filteredItems);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
const isChecked = item.isSelectAll ? nonSelectAllSelectedCount === totalSelectableCount && totalSelectableCount > 0 : selectedItems.some(selected => isEqual(selected, item));
const isIndeterminate =
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
item.isSelectAll && hasIndividualSelections && nonSelectAllSelectedCount < totalSelectableCount;
const itemProps = getItemProps({
item,
// we don't want Downshift to set aria-selected for us
// we also don't want to set 'false' for reader verbosity's sake
['aria-selected']: isChecked
});
const itemText = itemToString(item);
return /*#__PURE__*/React.createElement(index$2.default.MenuItem, _rollupPluginBabelHelpers.extends({
key: itemProps.id,
isActive: isChecked && !item['isSelectAll'],
"aria-label": itemText,
"aria-checked": isIndeterminate ? 'mixed' : isChecked,
isHighlighted: highlightedIndex === index,
title: itemText,
disabled: itemProps['aria-disabled']
}, itemProps), /*#__PURE__*/React.createElement("div", {
className: `${prefix}--checkbox-wrapper`
}, /*#__PURE__*/React.createElement(Checkbox.default, {
id: `${itemProps.id}__checkbox`,
labelText: itemToElement ? itemToElement(item) : itemText,
checked: isChecked,
title: useTitleInItem ? itemText : undefined,
indeterminate: isIndeterminate,
disabled: disabled
})));
})), itemsCleared && /*#__PURE__*/React.createElement("span", {
"aria-live": "assertive",
"aria-label": clearAnnouncement
})), !inline && showHelperText && /*#__PURE__*/React.createElement("div", {
id: helperId,
className: helperClasses
}, helperText));
});
MultiSelect.displayName = 'MultiSelect';
MultiSelect.propTypes = {
...MultiSelectPropTypes.sortingPropTypes,
/**
* **Experimental**: Will attempt to automatically align the floating
* element to avoid collisions with the viewport and being clipped by
* ancestor elements. Requires React v17+
* @see https://github.com/carbon-design-system/carbon/issues/18714
*/
autoAlign: PropTypes.bool,
/**
* Provide a custom class name to be added to the outermost node in the
* component
*/
className: PropTypes.string,
/**
* Specify the text that should be read for screen readers that describes total items selected
*/
clearSelectionDescription: PropTypes.string,
/**
* Specify the text that should be read for screen readers to clear selection.
*/
clearSelectionText: PropTypes.string,
/**
* Provide a compare function that is used to determine the ordering of
* options. See 'sortItems' for more control. Consider
* declaring function with `useCallback` to prevent unnecessary re-renders.
*/
compareItems: PropTypes.func,
/**
* **Experimental**: Provide a decorator component to be rendered inside the `MultiSelect` component
*/
decorator: PropTypes.node,
/**
* Specify the direction of the multiselect dropdown. Can be either top or bottom.
*/
direction: PropTypes.oneOf(['top', 'bottom']),
/**
* Disable the control
*/
disabled: PropTypes.bool,
/**
* Additional props passed to Downshift.
*
* **Use with caution:** anything you define here overrides the components'
* internal handling of that prop. Downshift APIs and internals are subject to
* change, and in some cases they can not be shimmed by Carbon to shield you
* from potentially breaking changes.
*/
downshiftProps: PropTypes.object,
/**
* Provide helper text that is used alongside the control label for
* additional help
*/
helperText: PropTypes.node,
/**
* Specify whether the title text should be hidden or not
*/
hideLabel: PropTypes.bool,
/**
* Specify a custom `id`
*/
id: PropTypes.string.isRequired,
/**
* Allow users to pass in arbitrary items from their collection that are
* pre-selected
*/
initialSelectedItems: PropTypes.array,
/**
* Is the current selection invalid?
*/
invalid: PropTypes.bool,
/**
* If invalid, what is the error?
*/
invalidText: PropTypes.node,
/**
* Renders an item as a custom React node instead of a string.
*/
itemToElement: PropTypes.func,
/**
* Helper function passed to downshift that allows the library to render a
* given item to a string label. By default, it extracts the `label` field
* from a given item to serve as the item label in the list. Consider
* declaring function with `useCallback` to prevent unnecessary re-renders.
*/
itemToString: PropTypes.func,
/**
* We try to stay as generic as possible here to allow individuals to pass
* in a collection of whatever kind of data structure they prefer
*/
items: PropTypes.array.isRequired,
/**
* Generic `label` that will be used as the textual representation of what
* this field is for
*/
label: PropTypes.node.isRequired,
/**
* `true` to use the light version.
*/
light: deprecate.deprecate(PropTypes.bool, 'The `light` prop for `MultiSelect` has ' + 'been deprecated in favor of the new `Layer` component. It will be removed in the next major release.'),
/**
* Specify the locale of the control. Used for the default `compareItems`
* used for sorting the list of items in the control.
*/
locale: PropTypes.string,
/**
* `onChange` is a utility for this controlled component to communicate to a
* consuming component what kind of internal state changes are occurring.
*/
onChange: PropTypes.func,
/**
* `onMenuChange` is a utility for this controlled component to communicate to a
* consuming component that the menu was open(`true`)/closed(`false`).
*/
onMenuChange: PropTypes.func,
/**
* Initialize the component with an open(`true`)/closed(`false`) menu.
*/
open: PropTypes.bool,
/**
* Whether or not the Dropdown is readonly
*/
readOnly: PropTypes.bool,
/**
* For full control of the selected items
*/
selectedItems: PropTypes.array,
/**
* Specify feedback (mode) of the selection.
* `top`: selected item jumps to top
* `fixed`: selected item stays at it's position
* `top-after-reopen`: selected item jump to top after reopen dropdown
*/
selectionFeedback: PropTypes.oneOf(['top', 'fixed', 'top-after-reopen']),
/**
* Specify the size of the ListBox. Currently supports either `sm`, `md` or `lg` as an option.
*/
size: ListBoxPropTypes.ListBoxSizePropType,
slug: deprecate.deprecate(PropTypes.node, 'The `slug` prop has been deprecated and will be removed in the next major version. Use the decorator prop instead.'),
/**
* Provide a method that sorts all options in the control. Overriding this
* prop means that you also have to handle the sort logic for selected versus
* un-selected items. If you just want to control ordering, consider the
* `compareItems` prop instead.
*
* The return value should be a number whose sign indicates the relative order
* of the two elements: negative if a is less than b, positive if a is greater
* than b, and zero if they are equal.
*
* sortItems :
* (items: Array<Item>, {
* selectedItems: Array<Item>,
* itemToString: Item => string,
* compareItems: (itemA: string, itemB: string, {
* locale: string
* }) => number,
* locale: string,
* }) => Array<Item>
*/
sortItems: PropTypes.func,
/**
* Provide text to be used in a `<label>` element that is tied to the
* multiselect via ARIA attributes.
*/
titleText: PropTypes.node,
/**
* Translates component strings using your i18n tool.
*/
translateWithId: PropTypes.func,
/**
* Specify 'inline' to create an inline multi-select.
*/
type: ListBoxPropTypes.ListBoxTypePropType,
/**
* Specify title to show title on hover
*/
useTitleInItem: PropTypes.bool,
/**
* Specify whether the control is currently in warning state
*/
warn: PropTypes.bool,
/**
* Provide the text that is displayed when the control is in warning state
*/
warnText: PropTypes.node
};
exports.MultiSelect = MultiSelect;