Description
Add some way to do text search inside the SelectNext
options, as sketched here hypothesis/client#5775 (comment)
Based on how the SelectNext
component was architected, one way to support filtering items would be introducing a new <Select.Filterable />
wrapper for <Select.Option />
items, which allows children to be filtered via context.
function App() {
const [selected, setSelected] = useState<(typeof items)[number]>();
return (
<Select {...}>
<Select.Option value={...}>Main</Select.Option>
<Select.Filterable onFilter={(query: string, value: T) => boolean} placeholder={...}>
{items.map(item => (
<Select.Option value={item} key={item.id}>
{({ disabled, isSelected }) => <>{item.name} ({item.id})</>}
</Select.Option>
))}
</Select.Filterable>
</Select>
)
}
Then we can expose the onFilter
callback via context, down to the Option
entries, which can decide if they should render or not, based on that.
We could even allow multiple Filterable blocks, or options outside any Filterable, that cannot be filtered. Ideally the search box should be position sticky, making sure it's visible while scrolling between the options it can filter.
The Filterable
component could be something in these lines:
const FilterableContext = createContext<{ shouldRender: (value: T) => boolean } | null>(null);
function Filterable<T>({ onFilter, placeholder, children }) {
const [query, setQuery] = useState('');
const shouldRender = useCallback(
(value: T) => onFilter(query, value),
[onFilter, query],
);
return (
<FilterableContext.Provider value={{ shouldRender }}>
<input type="search" placeholder={placeholder} value={query} onInput={e => setQuery(e.target.value)} />
{children}
</FilterableContext.Provider>
);
}