react-multi-search v1.3.0
React Multi Search
A react hook to filter data based on multiple search queries.
Features
- 🗃️ Support for various data types
- 📈 Range queries for numbers and dates
- 📜 Search suggestions
- 📊 Support for categorized data
- ✨ Headless, bring your own UI
- 🌐 TypeScript support
Supported data types
- String
- Can be case-sensitive or case-insensitive (default).
- Exact matches can be done by enclosing the query in double quotes.
- Can come with search suggestions, which is the aggregated possible values for a specific field.
- Boolean
- Support for various truthy and falsy queries (
true,yes,1, etc.) or provide your own.
- Support for various truthy and falsy queries (
- Number
- Support for range queries with operators:
>,>=,<,<=, and!=.
- Support for range queries with operators:
- Date
- Support for range queries with operators like numbers.
- Time and timezone are ignored.
Other data types will be treated as strings.
Demo
To see the hook in action, you can check the Storybook demo.
Installation
npm:
npm i react-multi-searchyarn:
yarn add react-multi-searchUsage
import { useState } from 'react';
import { useMultiSearch } from 'react-multi-search';
const MyComponent = () => {
const initialData = [
{ name: 'John Doe', age: 20, gender: 'Male' },
{ name: 'Jane Doe', age: 25, gender: 'Female' },
// ...
];
const [filteredData, setFilteredData] = useState([]);
const {
states: {
searchString,
searchField,
searchSuggestions,
searchQueries,
isMenuOpen,
shownMenu,
},
actions: {
clearInput,
addSearchQuery,
deleteSearchQuery,
deleteAllSearchQueries,
onMenuKeyDown,
onSearchFieldSelect,
onAllSearchFieldSelect,
onSearchSuggestionSelect,
openMenu,
},
inputProps,
anchorRef,
listRef,
} = useMultiSearch({
initialData,
setFilteredData,
fields: [
{ value: 'name', label: 'Name' },
{ value: 'age', label: 'Age' },
{ value: 'gender', label: 'Gender', showSearchSuggestions: true },
],
});
return (
<>
{/* search bar ------------------------------------------------------- */}
<div ref={anchorRef}>
<button onClick={openMenu}>{searchField.label || 'All'}</button>
<input {...inputProps} />
<button onClick={clearInput}>Clear</button>
<button onClick={addSearchQuery}>Add</button>
</div>
{/* search queries --------------------------------------------------- */}
<ul
style={{
listStyle: 'none',
padding: 0,
}}
>
{searchQueries.map((query, index) => (
<li key={index}>
<span>
{query.field === '_default'
? query.value
: `${query.fieldLabel}: ${query.value}`}
</span>
<button onClick={() => deleteSearchQuery(index)}>Remove</button>
</li>
))}
</ul>
<button onClick={deleteAllSearchQueries}>Clear all queries</button>
{/* dropdown menu ---------------------------------------------------- */}
{isMenuOpen && (
<div>
<ul ref={listRef} onKeyDown={onMenuKeyDown}>
{shownMenu === 'fields' && (
<>
<li>Search by:</li>
<li>
<button
onClick={onAllSearchFieldSelect}
disabled={searchField.value === '_default'}
>
All
</button>
</li>
{...props.fields.map((field) => (
<li key={field.value as string}>
<button
onClick={() => onSearchFieldSelect(field)}
disabled={searchField.value === field.value}
>
{field.label}
</button>
</li>
))}
</>
)}
{shownMenu === 'searchSuggestions' && (
<>
<li>Search suggestions:</li>
{searchSuggestions?.map(
(value) => (
<li key={value}>
<button onClick={() => onSearchSuggestionSelect(value)}>
{value}
</button>
</li>
)
) ?? <li>No suggestions</li>}
</>
)}
</ul>
</div>
)}
{/* ... */}
</>
);
};See the demo code for a more comprehensive example.
API
Options
initialData(required)T[]- The initial data to filter. This should be an array of objects without categorization. If you want to categorize your data, use the separate
categorizerfunction.
setFilteredData(required)Dispatch<SetStateAction<T[]>> | Dispatch<SetStateAction<Record<string, T[]>>>- setState function to update the filtered data.
- Since this is a headless hook, the filtered data should be managed by the consuming component for reusability.
fields(required)(FieldWithSuggestions<T> | FieldWithoutSuggestions<T>)[]- The fields to search in. Each item in the array can be of type
FieldWithSuggestions<T>where all keys should have the value of typestringorboolean, orFieldWithoutSuggestions<T>for other data types.
categorizer(data: T[]) => Record<string, (T | Record<string, unknown>)[]>- Function to categorize and group the filtered data.
- This is separated from
initialDatato optimize performance when filtering.
showEmptyCategoriesboolean- Defaults to
false. - Show categories that have no items.
- This is only used when
categorizeris provided.
shouldInitializeboolean- Defaults to
true. - Control whether the hook should initialize or not.
- This is useful when you want to delay the initialization of the hook until the data is ready.
caseSensitiveboolean- Defaults to
false. - Whether the comparison should be case-sensitive or not.
trueLabelstring- Defaults to
Yes. - The label for
truevalue on search suggestions.
falseLabelstring- Defaults to
No. - The label for
falsevalue on search suggestions.
truthyValuesstring[]- Defaults to
['true', '1', 'on', 'yes', 'y', 't', '✓'] - A list of truthy values to match against boolean values.
falsyValuesstring[]- Defaults to
['false', '0', 'off', 'no', 'n', 'f', 'x'] - A list of falsy values to match against boolean values.
Field Options
fields[number].value(required){ [K in keyof T]: T[K] extends string | boolean ? K : never; }[keyof T]forFieldWithSuggestions<T>{ [K in keyof T]: T[K] extends string | boolean ? never : K; }[keyof T]forFieldWithoutSuggestions<T>- The field to search.
fields[number].label(required)string- The label for the field.
fields[number].showSearchSuggestionsboolean(default:false)- Show search suggestions for this field.
- This only applies to
FieldWithoutSuggestions
fields[number].strictSuggestionsboolean(default:false)- Enclose suggestions in double quotes to treat as exact match.
- This only applies to
FieldWithoutSuggestions
Return Value
states- Usable states for the search filter.searchStringstring- The current search string.
searchFieldstring[]- Selected field to search.
searchSuggestionsFieldWithSuggestions<T> | FieldWithoutSuggestions<T> | { value: '_default'; label: ''; }- Search suggestions for the selected field.
SearchQuery<T>[]- Current search queries.
isMenuOpenboolean- Dropdown menu open state.
shownMenu'fields' | 'searchSuggestions'- The type of dropdown menu currently shown.
isFilteredboolean- Whether the data is filtered or not.
isInitializedboolean- Whether the hook is initialized or not.
actions- Actions to interact with the search filter.clearInput() => void- Clear the search input.
addSearchQuery() => void- Trigger to add the current search string and field as a search query.
deleteSearchQuery(idx: number) => void- Delete a search query by index.
deleteAllSearchQueries() => void- Delete all search queries.
onMenuKeyDown(e: KeyboardEvent<HTMLUListElement>) => void- Event handler to handle key down events on the menu.
onSearchFieldSelect(field: FieldWithSuggestions<T> | FieldWithoutSuggestions<T> | { value: '_default'; label: ''; }) => void- Event handler to handle field selection.
onAllSearchFieldSelect() => void- Event handler to handle "All" field selection.
onSearchSuggestionSelect(value: string) => void- Event handler to handle search suggestion selection.
openMenu() => void- Open the dropdown menu.
closeMenu() => void- Close the dropdown menu.
inputProps- Props passed to the input element (search bar).onChange(e: ChangeEvent<HTMLInputElement>) => void- Event handler to handle input change.
onKeyDown(e: KeyboardEvent<HTMLInputElement>) => void- Event handler to handle key down events on the input.
- Includes handling various keys:
Enter,Escape,Backspace,ArrowDown, and field selection (:).
onPaste(e: ClipboardEvent<HTMLInputElement>) => void- Event handler to handle paste events on the input.
- Primarily for separating search field and string.
onFocus() => void- Event handler to handle focus events on the input.
- Used for opening the dropdown menu.
onBlur() => void- Event handler to handle blur events on the input.
valuestring- Same as
searchString.
refRefObject<HTMLInputElement>- Ref forwarded to the input element.
anchorRef- Ref forwarded to the anchor element (search bar wrapper).listRef- Ref forwarded to the dropdown menu list element (ul).
Search Query
fieldkeyof T | '_default'- The field to search.
fieldLabelstring- The label to display.
valuestring- Query string to match.
Contributing
Contributions are welcome! Feel free to open an issue or submit a pull request.
Before submitting a pull request, please make sure to test your changes and update the documentation if necessary.
License
This project is licensed under the MIT License