ComboboxMultiSelect
ComboboxMultiSelect allows users to choose multiple options from a predefined list. When the input is focused, a popover appears below the input, which displays a list of all available options. Users can filter the options based on string match, by typing into the input element. Each selected option is presented as a Tag, which includes a dismiss button to remove that selection. selected.
The options must be known as the components are rendered, which means this component is not suitable for server-side searches.
Available from eds-core/1.20.0
Quick Start
- Installation
npm install @adaptavant/eds-core- Import
import { ComboboxMultiSelect } from '@adaptavant/eds-core';
Open Menu
By default, the menu opens when the user types in the trigger input. Also, using menuTrigger="focus" prop allows you to open the menu when the input is focused.
const initialOptions = [
{
id: "opiasri",
name: "Oscar Piastri",
},
{
id: "dricciardo",
name: "Daniel Ricciardo",
},
{
id: "kchandhok",
name: "Karun Chandhok",
},
{
id: "jdaruvala",
name: "Jehan Daruvala",
},
{
id: "rkubia",
name: "Robert Kubica",
},
];
const selectionReducer = (state, action) => {
switch (action.type) {
case "ADD":
return [...state, action.payload];
case "REMOVE":
return state.filter((item) => item.id !== action.payload.id);
case 'DESELECT_ALL':
return [];
case 'DROP_LAST':
if(state.length === 0) return state;
return state.slice(0, -1);
default:
return state;
}
};
const [selectedOptions, dispatch] = React.useReducer(selectionReducer, []);
const [searchTerm, setSearchTerm] = React.useState("");
const filteredOptions = React.useMemo(() => {
if (searchTerm === "") return initialOptions;
return initialOptions.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [initialOptions, searchTerm]);
const handleSelectionChange = (item) => {
if(!item) return;
const isSelected = selectedOptions.some(
(selected) => selected.id === item.id
);
dispatch({
type: isSelected ? "REMOVE" : "ADD",
payload: item,
});
setSearchTerm("");
};
return (
<Field label="Select Items">
<ComboboxMultiSelect
inputValue={searchTerm}
menuTrigger="focus"
onClose={() => setSearchTerm("")}
onClearAll={() => dispatch({ type: 'DESELECT_ALL' })}
onClearInput={() => setSearchTerm("")}
onRemoveLast={() => dispatch({ type: 'DROP_LAST' })}
onInputChange={setSearchTerm}
onSelectionChange={handleSelectionChange}
options={filteredOptions}
selectedKey="id"
selectedOptions={selectedOptions}
>
<ComboboxMultiSelectTextInput
placeholder="Search..."
renderItem={({ name }) => name}
/>
<ComboboxMultiSelectPopover inputPlaceholder="Search...">
<ComboboxMultiSelectListbox
noResultsFallback={
<Text className="text-secondary text-center text-body-12 py-4">
No matching results
</Text>
}
options={filteredOptions}
>
{(item) => (
<ComboboxMultiSelectItem option={item}>
{item.name}
</ComboboxMultiSelectItem>
)}
</ComboboxMultiSelectListbox>
</ComboboxMultiSelectPopover>
</ComboboxMultiSelect>
</Field>
);
Size
Customise the size of the Combobox MultiSelect by using the size prop for the Field.
The size can be either large or standard with standard being the default.
const initialOptions = [
{
id: "opiasri",
name: "Oscar Piastri",
},
{
id: "dricciardo",
name: "Daniel Ricciardo",
},
{
id: "kchandhok",
name: "Karun Chandhok",
},
{
id: "jdaruvala",
name: "Jehan Daruvala",
},
{
id: "rkubia",
name: "Robert Kubica",
},
];
const selectionReducer = (state, action) => {
switch (action.type) {
case "ADD":
return [...state, action.payload];
case "REMOVE":
return state.filter((item) => item.id !== action.payload.id);
case 'DESELECT_ALL':
return [];
case 'DROP_LAST':
if(state.length === 0) return state;
return state.slice(0, -1);
default:
return state;
}
};
const [selectedOptions, dispatch] = React.useReducer(selectionReducer, []);
const [searchTerm, setSearchTerm] = React.useState("");
const filteredOptions = React.useMemo(() => {
if (searchTerm === "") return initialOptions;
return initialOptions.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [initialOptions, searchTerm]);
const handleSelectionChange = (item) => {
if(!item) return;
const isSelected = selectedOptions.some(
(selected) => selected.id === item.id
);
dispatch({
type: isSelected ? "REMOVE" : "ADD",
payload: item,
});
setSearchTerm("");
};
return (
<Field label="Select Items" size="large">
<ComboboxMultiSelect
inputValue={searchTerm}
menuTrigger="focus"
onClose={() => setSearchTerm("")}
onClearAll={() => dispatch({ type: 'DESELECT_ALL' })}
onClearInput={() => setSearchTerm("")}
onRemoveLast={() => dispatch({ type: 'DROP_LAST' })}
onInputChange={setSearchTerm}
onSelectionChange={handleSelectionChange}
options={filteredOptions}
selectedKey="id"
selectedOptions={selectedOptions}
>
<ComboboxMultiSelectTextInput
placeholder="Search..."
renderItem={({ name }) => name}
/>
<ComboboxMultiSelectPopover inputPlaceholder="Search...">
<ComboboxMultiSelectListbox
noResultsFallback={
<Text className="text-secondary text-center text-body-12 py-4">
No matching results
</Text>
}
options={filteredOptions}
>
{(item) => (
<ComboboxMultiSelectItem option={item}>
{item.name}
</ComboboxMultiSelectItem>
)}
</ComboboxMultiSelectListbox>
</ComboboxMultiSelectPopover>
</ComboboxMultiSelect>
</Field>
);
Select All Option
Use the ComboboxMultiSelectAll component to render a “Select all” option inside the listbox. This lets users select or deselect all options at once and also supports the indeterminate state when only some items are selected
// Data
const allOptions = [
{
id: 'opiasri',
name: 'Oscar Piastri',
},
{
id: 'dricciardo',
name: 'Daniel Ricciardo',
},
{
id: 'kchandhok',
name: 'Karun Chandhok',
},
{
id: 'jdaruvala',
name: 'Jehan Daruvala',
},
{
id: 'rkubia',
name: 'Robert Kubica',
},
];
const selectionKey = 'id';
// Actions
const createSelectionReducer = (selectionKey) => {
return (state, action) => {
switch (action.type) {
case 'ADD':
return [...state, action.payload];
case 'REMOVE':
return state.filter(
(item) => item[selectionKey] !== action.payload[selectionKey]
);
case 'DESELECT_ALL':
return [];
case 'DROP_LAST':
if (state.length === 0) return state;
return state.slice(0, -1);
case 'REPLACE':
return action.payload;
default:
return state;
}
};
};
// States
const [searchTerm, setSearchTerm] = React.useState('');
const deferredSearchTerm = React.useDeferredValue(searchTerm);
const selectionReducer = createSelectionReducer(selectionKey);
const [selectedOptions, dispatch] = React.useReducer(selectionReducer, []);
const isAllSelected = selectedOptions.length === allOptions.length;
const isPartiallySelected = selectedOptions.length > 0 && !isAllSelected;
// Handlers
function handleSelectAll() {
if (isAllSelected) {
dispatch({ type: 'DESELECT_ALL' });
} else {
dispatch({
type: 'REPLACE',
payload: allOptions,
});
}
}
const handleSelectionChange = (tappedOption) => {
if (!tappedOption) return;
const isSelected = selectedOptions.some(
(selected) => selected[selectionKey] === tappedOption[selectionKey]
);
dispatch({
type: isSelected ? 'REMOVE' : 'ADD',
payload: tappedOption,
});
setSearchTerm('');
};
// Filter Options with selected options
const filteredOptions = React.useMemo(() => {
const filteredOptions =
deferredSearchTerm === ''
? allOptions
: allOptions.filter((user) =>
user.name
.toLowerCase()
.includes(deferredSearchTerm.toLowerCase())
);
const selectedFilteredOptions = filteredOptions.map((option) => {
const isSelected = selectedOptions.some(
(selected) => selected[selectionKey] === option[selectionKey]
);
return {
...option,
isSelected,
};
});
if (filteredOptions.length === allOptions.length) {
return [
{
[selectionKey]: 'select-all',
value: 'Select All',
isSelected: isAllSelected,
},
...selectedFilteredOptions,
];
}
return selectedFilteredOptions;
}, [
deferredSearchTerm,
selectedOptions,
selectionKey,
isAllSelected,
allOptions,
]);
return (
<Field label="Select Fruit">
<ComboboxMultiSelect
inputValue={searchTerm}
menuTrigger="focus"
onClearAll={() => dispatch({ type: 'DESELECT_ALL' })}
onClearInput={() => setSearchTerm('')}
onClose={() => setSearchTerm('')}
onInputChange={setSearchTerm}
onRemoveLast={() => dispatch({ type: 'DROP_LAST' })}
onSelectionChange={handleSelectionChange}
selectedKey="name"
selectedOptions={selectedOptions}
options={allOptions}
>
<ComboboxMultiSelectTextInput
placeholder="Search..."
renderItem={({ name }) => name}
/>
<ComboboxMultiSelectPopover>
<ComboboxMultiSelectListbox
noResultsFallback={<Text className="text-secondary text-center text-body-12 py-4">No matching results</Text>}
options={filteredOptions}
>
{(user) => {
const isSelectAll = user.id === 'select-all';
if (isSelectAll) {
return (
<ComboboxMultiSelectAll
onClick={handleSelectAll}
>
Select all
</ComboboxMultiSelectAll>
);
}
return (
<ComboboxMultiSelectItem id={user.name} option={user}>
{user.name}
</ComboboxMultiSelectItem>
);
}}
</ComboboxMultiSelectListbox>
</ComboboxMultiSelectPopover>
</ComboboxMultiSelect>
</Field>
);
Max Visible Items
Use maxVisibleItems prop to control how many selected tags can be shown in the input field. If the selection exceeds this, the rest will be collapsed into a "+X more" indicator to save space.
const initialOptions = [
{
id: "opiasri",
name: "Oscar Piastri",
},
{
id: "dricciardo",
name: "Daniel Ricciardo",
},
{
id: "kchandhok",
name: "Karun Chandhok",
},
{
id: "jdaruvala",
name: "Jehan Daruvala",
},
{
id: "rkubia",
name: "Robert Kubica",
},
];
const selectionReducer = (state, action) => {
switch (action.type) {
case "ADD":
return [...state, action.payload];
case "REMOVE":
return state.filter((item) => item.id !== action.payload.id);
case 'DESELECT_ALL':
return [];
case 'DROP_LAST':
if(state.length === 0) return state;
return state.slice(0, -1);
default:
return state;
}
};
const [selectedOptions, dispatch] = React.useReducer(selectionReducer, []);
const [searchTerm, setSearchTerm] = React.useState("");
const filteredOptions = React.useMemo(() => {
if (searchTerm === "") return initialOptions;
return initialOptions.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [initialOptions, searchTerm]);
const handleSelectionChange = (item) => {
if(!item) return;
const isSelected = selectedOptions.some(
(selected) => selected.id === item.id
);
dispatch({
type: isSelected ? "REMOVE" : "ADD",
payload: item,
});
setSearchTerm("");
};
return (
<Field label="Select Items">
<ComboboxMultiSelect
inputValue={searchTerm}
menuTrigger="focus"
onClose={() => setSearchTerm("")}
onClearAll={() => dispatch({ type: 'DESELECT_ALL' })}
onClearInput={() => setSearchTerm("")}
onRemoveLast={() => dispatch({ type: 'DROP_LAST' })}
onInputChange={setSearchTerm}
onSelectionChange={handleSelectionChange}
options={filteredOptions}
selectedKey="id"
selectedOptions={selectedOptions}
>
<ComboboxMultiSelectTextInput
maxVisibleItems={2} // Display maximum of 3 items, rest truncates
placeholder="Search..."
renderItem={({ name }) => name}
/>
<ComboboxMultiSelectPopover>
<ComboboxMultiSelectListbox
noResultsFallback={
<Text className="text-secondary text-center text-body-12 py-4">
No matching results
</Text>
}
options={filteredOptions}
>
{(item) => (
<ComboboxMultiSelectItem option={item}>
{item.name}
</ComboboxMultiSelectItem>
)}
</ComboboxMultiSelectListbox>
</ComboboxMultiSelectPopover>
</ComboboxMultiSelect>
</Field>
);
With Apply Button
For large lists and to improve the mobile experience, you can enable draft-to-save mode. Ideally, this introduces a two-step selection process where users selects their choices, reviews them and then confirm by tapping "Apply" button.
- Use
shouldUseDraftMode={true}on ComboboxMultiSelect to enable this draft-to-save mode on mobile. - Use
draftSaveButtonon ComboboxMultiSelectPopover to add an apply button in mobile view. - Use
onDraftSavecallback on ComboboxMultiSelect to save the draft options into the selected options. This is called when the user taps the apply button.
Note: This is a mobile-only feature. The apply button will not appear on desktop views - it's specifically designed for mobile interfaces where this component opens as a sheet.
const initialOptions = [
{
id: "apple",
name: "Apple",
},
{
id: "banana",
name: "Banana",
},
{
id: "cherry",
name: "Cherry",
},
{
id: "grape",
name: "Grape",
},
{
id: "orange",
name: "Orange",
},
];
const selectionReducer = (state, action) => {
switch (action.type) {
case "ADD":
return [...state, action.payload];
case "REMOVE":
return state.filter((item) => item.id !== action.payload.id);
case 'DESELECT_ALL':
return [];
case 'DROP_LAST':
if(state.length === 0) return state;
return state.slice(0, -1);
case 'REPLACE':
return action.payload;
default:
return state;
}
};
const [selectedOptions, dispatch] = React.useReducer(selectionReducer, []);
const [searchTerm, setSearchTerm] = React.useState("");
const filteredOptions = React.useMemo(() => {
if (searchTerm === "") return initialOptions;
return initialOptions.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [initialOptions, searchTerm]);
const handleSelectionChange = (item) => {
if(!item) return;
const isSelected = selectedOptions.some(
(selected) => selected.id === item.id
);
dispatch({
type: isSelected ? "REMOVE" : "ADD",
payload: item,
});
setSearchTerm("");
};
const handleDraftSave = (draftSelectedOptions) => {
dispatch({
type: 'REPLACE',
payload: draftSelectedOptions,
});
};
return (
<Field label="Select Fruits">
<ComboboxMultiSelect
inputValue={searchTerm}
menuTrigger="focus"
onClose={() => setSearchTerm("")}
onClearAll={() => dispatch({ type: 'DESELECT_ALL' })}
onClearInput={() => setSearchTerm("")}
onRemoveLast={() => dispatch({ type: 'DROP_LAST' })}
onInputChange={setSearchTerm}
onSelectionChange={handleSelectionChange}
options={initialOptions}
selectedKey="id"
selectedOptions={selectedOptions}
shouldUseDraftMode={true} // Enable draft-to-save mode on mobile
onDraftSave={handleDraftSave}
>
<ComboboxMultiSelectTextInput
placeholder="Search fruits..."
renderItem={({ name }) => name}
/>
<ComboboxMultiSelectPopover
draftSaveButton={({ triggerProps }) => (
<Button
className="w-full"
size="large"
variant="accentSecondary"
{...triggerProps}
>
Apply
</Button>
)}
>
<ComboboxMultiSelectListbox
noResultsFallback={
<Text className="text-secondary text-center text-body-12 py-4">
No matching results
</Text>
}
options={filteredOptions}
>
{(item) => (
<ComboboxMultiSelectItem option={item}>
{item.name}
</ComboboxMultiSelectItem>
)}
</ComboboxMultiSelectListbox>
</ComboboxMultiSelectPopover>
</ComboboxMultiSelect>
</Field>
);
API Reference
ComboboxMultiSelect
| Prop | Default | Description |
|---|---|---|
children | _ | ((menuState: { isMenuOpen: boolean }) => React.ReactNode) | React.ReactNodeAccepts either a React node or a render function. The render function provides the menu's state (isMenuOpen). |
closeButtonPropsForMobile? | _ | { label: string, onClick: function, size: 'large' | 'standard' | 'small' }If closeButtonPropsForMobile is provided then the mobile version of the component will have a close button |
inputValue? | _ | stringValue of the combobox input. |
menuTrigger? | input | 'focus' | 'input'Interaction type to display the Combobox menu. |
mobileFriendly? | true | booleanIndicates if the opened element should be rendered as a Sheet in mobile viewports |
onClearAll? | _ | () => voidHandler that is called when the Combobox input value is cleared. |
onClearInput? | _ | () => voidFunction to be invoked for clearing the Combobox input value. |
onClose? | _ | () => voidHandler that is called when the Combobox is closed |
onInputChange | _ | (value: string) => voidFunction to be invoked when the input value changes. |
onRemoveLast? | _ | () => voidFunction to remove the last item in the selection options list. Invoked when user presses backspace. |
onSelectionChange | _ | (option: object) => voidFunction to be invoked when the selection changes. |
popoverMatchReferenceWidth? | false | booleanMatch the width of the popover with the reference element. |
popoverMaxHeight? | 356 | numberThe max height of the popover. |
popoverMaxWidth? | 400 | numberThe max width of the popover. |
popoverOffset? | 4 | numberThe offset of the combobox popover. |
popoverPlacement? | 'bottom-start' | 'bottom' | 'bottom-start' | 'bottom-end'The placement of the popover relative to the combobox Input. |
selectedKey | _ | keyOf objectKey for identifying the selected option. |
selectedOptions | _ | object[] | undefinedCurrently selected options from the list. |
strategy? | 'absolute' | 'absolute' | 'fixed'The strategy used to position the floating element. |
titleForMobile? | _ | stringOn mobile ComboboxMultiSelect renders a Sheet component. If titleForMobile is provided then the Sheet will have a Header with title rendered |
options? | _ | object[] | undefinedComplete list of all available options. Required for "Select All" functionality to determine selection count. |
shouldUseDraftMode? | false | booleanIf true, the draft-to-save mode will be enabled in mobile view. Use it along with draftSaveButton prop in ComboboxMultiSelectPopover. This is a mobile-only feature. |
onDraftSave? | _ | (draftSelectedOptions: object[]) => voidFunction to be invoked when the draft selection is to be saved. This is called when the user taps the apply button. |
fullScreenForMobile | false | booleanEnables fullscreen mode for the mobile select menu, making it cover the entire viewport. |
ComboboxMultiSelectTextInput
This API applies to both ComboboxSearchInput and ComboboxTextInput.
| Prop | Default | Description |
|---|---|---|
aria-activedescendant? | _ | stringidentifies the currently active element when using ARIA widgets. |
aria-autocomplete? | _ | 'inline' | 'list' | 'both' | 'none'Indicates whether input completion suggestions are provided, and their type. |
aria-controls? | _ | stringIdentifies the element(s) that the input controls. |
aria-expanded? | _ | booleanIndicates whether the popover is currently expanded or collapsed. |
aria-haspopup? | _ | 'dialog' | 'grid' | 'listbox' | 'menu' | 'tree' | 'boolean'Indicates the availability of a popup related to the input. |
autoComplete? | _ | stringSpecifies whether autocomplete is enabled for the input. |
autoCorrect? | _ | 'on' | 'off'Specifies whether auto-correction is enabled for the input. |
autoFocus? | _ | booleanAutomatically focuses the input element when it is rendered. |
focusContainerRef? | _ | React.Ref<HTMLDivElement>Reference to the wrapper FocusContainer element. |
inputMode? | _ | 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'Hints to browsers about what kind of virtual keyboard to display. |
name? | _ | stringName of the input, used for form submissions. |
onBlur? | _ | functionFunction to be invoked when the input loses focus. |
onChange? | _ | functionFunction to be invoked when a new item is selected. |
onFocus? | _ | functionFunction to be invoked when the input is focused. |
onKeyDown? | _ | functionFunction to be invoked when a key is pressed while the input is focused. |
onPaste? | _ | functionFunction to be invoked when content is pasted into the input. |
pattern? | _ | stringRegular expression pattern the input's value must match for validation. |
placeholder? | _ | stringPlaceholder text displayed when the input is empty. |
renderItem | _ | (option: Option) => string;Function to control the formatting of the string in the selected item Tag. |
role? | _ | stringARIA role for the input. |
spellCheck? | _ | booleanSpecifies whether the input value should be checked for spelling errors. |
type? | _ | 'email' | 'password' | 'search' | 'tel' | 'text' | 'url'Type of the input element. |
maxVisibleItems? | _ | numberThe number of items to show in the input |
ComboboxMultiSelectPopover
| Prop | Default | Description |
|---|---|---|
shouldUsePortal? | true | booleanDetermines whether the Popover should be rendered in a React Portal. If true, the Popover will be rendered outside the DOM hierarchy of the parent component. |
children? | _ | React.ReactNodeContent of the ComboboxList with the options. |
inputAppearance? | "SearchInput" | "SearchInput" | "TextInput" | "none"Controls whether to include or exclude an input inside the mobile popover / sheet. |
inputPlaceholder? | "Search..." | stringThe placeholder text for the input inside the mobile popover / sheet. |
draftSaveButton? | _ | (props: { triggerProps: { onClick: () => void } }) => React.ReactNodeUsed to render the mobile-only confirmation button, while in draft-to-save mode. Passes triggerProps that must be spread on the trigger element to handle the action. This is a mobile-only prop. |
ComboboxMultiSelectListbox
| Prop | Default | Description |
|---|---|---|
noResultsFallback? | _ | React.ReactNodeComponent to render when there are no options left in the filtered result. |
options | _ | Array of ObjectsOptions to be rendered in the dropdown. |
ComboboxMultiSelectItem
| Prop | Default | Description |
|---|---|---|
children | _ | React.ReactNodeThe content of the combobox menu item. |
id? | _ | stringAn optional ID for the menu item; auto-generated if not provided. |
isDisabled? | false | booleanIf true, the item is disabled. |
onClick? | _ | () => voidFunction to be invoked when the item is clicked. |
option | _ | objectOption associated with the item |
size? | standard | 'standard' | 'large'Size of the combobox item. |
showSelectionIndicator? | true | booleanControls whether to show the selection indicator (check icon) for selected items. |
ComboboxMultiSelectAll
| Prop | Default | Description |
|---|---|---|
children | _ | React.ReactNodeThe content of the combobox menu item. |
id? | _ | stringAn optional ID for the menu item; auto-generated if not provided. |
isSelected? | false | booleanControls whether the checkbox is selected.
|
isDisabled? | false | booleanIf true, the item is disabled. |
isHighlighted? | false | booleanIndicates if the menu item is currently highlighted. Used for styling.
|
onClick? | _ | () => voidFunction to be invoked when the item is clicked. |
size? | standard | 'standard' | 'large'Size of the combobox item. |
indeterminate? | false | booleanControls whether to show the checkbox's indeterminate state.
|
Style API
Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.
Please refer to the Style API documentation for more insights.
ComboboxMultiSelect parts
const initialOptions = [
{
id: "opiasri",
name: "Oscar Piastri",
},
{
id: "dricciardo",
name: "Daniel Ricciardo",
},
{
id: "kchandhok",
name: "Karun Chandhok",
},
{
id: "jdaruvala",
name: "Jehan Daruvala",
},
{
id: "rkubia",
name: "Robert Kubica",
},
];
const selectionReducer = (state, action) => {
switch (action.type) {
case "ADD":
return [...state, action.payload];
case "REMOVE":
return state.filter((item) => item.id !== action.payload.id);
case 'DROP_LAST':
if(state.length === 0) return state;
return state.slice(0, -1);
default:
return state;
}
};
const [selectedOptions, dispatch] = React.useReducer(selectionReducer, []);
const [searchTerm, setSearchTerm] = React.useState("");
const filteredOptions = React.useMemo(() => {
if (searchTerm === "") return initialOptions;
return initialOptions.filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [initialOptions, searchTerm]);
const handleSelectionChange = (item) => {
if(!item) return;
const isSelected = selectedOptions.some(
(selected) => selected.id === item.id
);
dispatch({
type: isSelected ? "REMOVE" : "ADD",
payload: item,
});
setSearchTerm("");
};
return (
<Field label="Select Items">
<ComboboxMultiSelect
inputValue={searchTerm}
menuTrigger="focus"
onClearInput={() => setSearchTerm("")}
onInputChange={setSearchTerm}
onRemoveLast={() => dispatch({ type: 'DROP_LAST' })}
onSelectionChange={handleSelectionChange}
options={filteredOptions}
selectedKey="id"
selectedOptions={selectedOptions}
>
<ComboboxMultiSelectTextInput
className="bg-palette-violet-background"
classNames={{
focusIndicator: 'border-palette-violet-border',
tag: 'bg-palette-blue-background',
tagLabel: 'text-palette-blue-text',
tagCloseButtonIcon: 'fill-palette-blue-border',
}}
placeholder="Search..."
renderItem={({ name }) => name}
/>
<ComboboxMultiSelectPopover className="bg-palette-violet-background"
classNames={{
sheet: "bg-palette-violet-background",
sheetHeader: "text-secondary",
sheetHeading: "text-heading-24",
sheetWrapper: "text-positive",
sheetContent: "bg-accent",
}}>
<ComboboxMultiSelectListbox
noResultsFallback={
<Text className="text-secondary text-center text-body-12 py-4">
No matching results
</Text>
}
options={filteredOptions}
>
{(item) => (
<ComboboxMultiSelectItem
option={item}
className={`
bg-palette-violet-background
active:bg-palette-violet-background-active
active:data-[highlighted]:bg-palette-violet-background-active
data-[highlighted]:bg-palette-violet-background-active
`}
classNames={{
checkboxControl:
'peer-checked:bg-palette-violet-background-active',
}}
>
{item.name}
</ComboboxMultiSelectItem>
)}
</ComboboxMultiSelectListbox>
</ComboboxMultiSelectPopover>
</ComboboxMultiSelect>
</Field>
);
No parts available. Only root.
ComboboxMultiSelectItem parts
| Part | Description |
|---|---|
| center | The main area of the 'track' |
| checkbox | The checkbox root |
| checkboxControl | The interactive area of the checkbox |
| checkboxIcon | The icon within the checkbox |
| rail | Common style applied to both railStart and railEnd via styles.rail. Used for shared customization |
| railEnd | The right fixed element. For icons, buttons, or trailing content. |
| railStart | The left fixed element. For icons, buttons, or any content to appear before the main area . |
ComboboxMultiSelectTextInput parts
| Part | Description |
|---|---|
| adornmentEnd | Container for the clear (remove all) icon that appears at the end. |
| focusIndicator | A hidden span or visual indicator to show when the input is focused. |
| inner | The flex element which contains the input and the tags |
| input | Styles applied to the input field. |
| root | Wrapper around the whole input, tags, and adornments. |
| tag | The element which represents a selected item |
| tagCloseButton | The button within a Tag which dismisses the selection |
| tagCloseButtonIcon | The icon element within the TagCloseButton |
| tagLabel | The text element within the tag |
ComboboxMultiSelectPopover parts
| Part | Description |
|---|---|
| root | Parent Container on desktop view. |
| sheet | Parent Container of the popover in mobile view. |
| sheetWrapper | The inner wrapper of the sheet, typically used for controlling the layout or the sheet content's alignment and size. |
| sheetHeader | Header Container holding the heading elements. |
| sheetHeading | Styles to the heading text. |
| sheetContent | Box holding the options in the sheet |
| closeButton | The button element used to close the sheet, typically placed at the top-right corner of the dialog. |
Note: Styles applied to the popover root will not be automatically applied to sheet , recommend to add sheet styles as shown in the example.