Action items
You can specify items in the list as actions and define what happens after they are triggered. This is useful for extending functionality, such as building a 'creatable' autocomplete feature.
- ActionItems.tsx
- data/fruits.ts
import { useState } from 'react';
import { useCombobox, autocomplete } from '@szhsin/react-autocomplete';
import FRUITS from '../../data/fruits';
type Item = { value: string; creatable?: boolean };
const ActionItems = () => {
const [value, setValue] = useState<string>();
const [selected, setSelected] = useState<Item>();
const [fruits, setFruit] = useState(FRUITS);
const items: Item[] = fruits
.filter((fruit) => fruit.toLowerCase().includes((value || '').toLowerCase()))
.map((fruit) => ({ value: fruit }));
// If the value does not exist in the list of items, add a creatable action to the end.
if (value && !items.find((item) => !item.creatable && item.value === value)) {
items.push({ value, creatable: true });
}
const {
getFocusCaptureProps,
getLabelProps,
getInputProps,
getClearProps,
getToggleProps,
getListProps,
getItemProps,
isItemSelected,
open,
focusIndex,
isInputEmpty
} = useCombobox({
items,
getItemValue: (item) => item.value,
isEqual: (a, b) => a?.value === b?.value,
// Specify how to determine if an item is an action,
isItemAction: (item) => !!item.creatable,
// and what happens after the action is triggered.
onAction: (item) => {
if (item.creatable) {
setSelected({ value: item.value });
setFruit([item.value, ...fruits]);
}
},
value,
onChange: setValue,
selected,
onSelectChange: setSelected,
feature: autocomplete({ select: true })
});
return (
<div>
<label {...getLabelProps()} {...getFocusCaptureProps()}>
Fruit
</label>
<div>
<input placeholder="Select or create..." {...getInputProps()} />
{!isInputEmpty && <button {...getClearProps()}>X</button>}
<button {...getToggleProps()}>{open ? '↑' : '↓'}</button>
</div>
<ul
{...getListProps()}
style={{
display: open ? 'block' : 'none',
position: 'absolute',
listStyle: 'none',
color: '#000',
background: '#fff',
overflow: 'auto',
maxHeight: 300,
padding: 0
}}
>
{items.length ? (
items.map((item, index) => (
<li
{...getItemProps({ item, index })}
key={item.value}
style={{
background: focusIndex === index ? '#ddd' : 'none',
textDecoration: isItemSelected(item) ? 'underline' : 'none'
}}
>
{item.creatable ? `Create "${item.value}"` : item.value}
</li>
))
) : (
<li>No options</li>
)}
</ul>
</div>
);
};
export default ActionItems;
export default ['Apple', 'Banana', 'Blueberry', 'Cherry', 'Grape', 'Pineapple', 'Strawberry'];