Skip to content

Commit

Permalink
Changing some prop function signatures to reduce duplicate or unused …
Browse files Browse the repository at this point in the history
…code

Adding ability to pass function to buttonProps so mobile mode state is available
Updating comments to reflect changes
Updating README
  • Loading branch information
FoxtrotPerry committed Jun 13, 2023
1 parent 6b5ca14 commit 4c3c16d
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 71 deletions.
6 changes: 6 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
"browser": true,
"es2021": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"prettier"
],
"parserOptions": {
Expand Down
30 changes: 23 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
# React Search Dialog🔎 ![Version](https://badgen.net/npm/v/react-search-dialog?icon=npm) ![NPM bundle size](https://img.shields.io/bundlephobia/minzip/react-search-dialog?color=brightgreen)

A batteries included search component that aims to make implementing a modern search experience in your application as easy as possible. With a focus on performance and refraining from reinventing the wheel, **React Search Dialog** is built on top of [Fuse.js](https://fusejs.io/) and [react-window](https://github.com/bvaughn/react-window) so that no matter the data size, you'll get the results you're looking for near instantly!
A batteries included search component that aims to make implementing a modern search experience in your application as easy as possible. With a focus on performance and not reinventing the wheel, **React Search Dialog** is built on top of battle tested libraries [Fuse.js](https://fusejs.io/) and [react-window](https://github.com/bvaughn/react-window) so that no matter the item list, you'll get the results you're looking for near instantly!

## Features

- ✅ Handles massive data sets with ease
- ✅ Live results as you type
- ✅ Out of the box styling, with ability to customize as needed
- ✅ Mobile friendly
- ✅ Optional render functions for search results and recent items
- ✅ Build in recent search history (can be disabled)
- ✅ Optional quick select section for common search items (fully customizable)

## Examples

## Features
🚧 _Coming soon!_

## Props

Below is a table of all props exposed by the `Search` component. The majority of this table is a pared down, more digestable version of the exported type `SearchProps` so
Below is a table of all props exposed by the `Search` component. The majority of this table is a pared down, more digestible version of the exported type `SearchProps` so
if more detail is needed, please feel free to refer to the type definitions directly!

| Key | |
| :-----------: | ------------- |
|| Required |
|| Conditionally Optional |
|| Optional |

| Required | Prop Name | Prop Type | Description |
| :-----------: | ------------- | ------------- | ------------- |
|| `items` | Array of `T` (generic) | The items to search through. **IMPORTANT: `T` must have a `label` property OR be a string.** |
| | `onItemSelect` | `(item: T) => void` | Callback to fire when an item is selected |
|| `buttonProps` | `ButtonProps` | Props to pass to the button that opens the search dialog. [Click here to read more about the props available to the Button component](https://mui.com/api/button/) |
| | `onItemSelect` | `(item: T) => void` | Callback to fire when an item is selected. **IMPORTANT: When NOT passing in your own render function with `renderResult`, this prop is required so that an action can be taken on search result / recent click.** |
|| `buttonProps` | `ButtonProps OR (isSmallScreen: boolean) => ButtonProps` | Props to pass to the button that opens the search dialog. [Click here to read more about the props available to the Button component](https://mui.com/api/button/) |
|| `placeholder` | `string` | The placeholder text to display in the search input |
|| `itemHeight` | `number` or [`ItemHeightPreset`](./react-search-dialog/Search.tsx) | Height of each item in the search results |
|| `quickFillItems` | Array of `T` (generic) | Items to display in the quick fill section |
|| `maxHeight` | `string` or `number` | Maximum height of the search dialog |
|| `maxWidth` | `string` or `number` | Maximum width of the search dialog |
|| `noHistory` | `boolean` | Whether or not to record or display recent search history |
|| `renderResult` | `(result: T, onItemSelectCallback: () => void) => JSX.Element` | Callback to render a single search result **IMPORTANT: Because you're supplying the render function for each search result, it's up to you to fire the provided `onItemSelectCallback()` function at the moment a user has selected a specific search item. This is what allows for search history tracking (if enabled) in custom components and handles the firing of your `onItemSelect` function, if defined!** |
|| `renderRecent` | `(recent: T, onItemSelectCallback: () => void) => JSX.Element` | Callback to render a single recent search item **IMPORTANT: Because you're supplying the render function for each recent search item, it's up to you to fire the provided `onItemSelectCallback()` function at the moment a user has selected a specific recent item. This is what allows for search history tracking (if enabled) in custom components and also handles the firing of your `onItemSelect` function, if defined!** |
|| `renderResult` | `({ item, closeDialog, addToRecents }) => JSX.Element` | Callback to render a single search result **IMPORTANT: Because you're supplying the render function for each search result, it's up to you to use the `addToRecents` function when a selection is made by the user, and the `closeDialog` function when you see it appropriate for the search dialog to close.** |
|| `renderRecent` | `({ item, closeDialog, addToRecents }) => JSX.Element` | Callback to render a single recent search item **IMPORTANT: Because you're supplying the render function for each recent search item, it's up to you to use the `addToRecents` function when a selection is made by the user, and the `closeDialog` function when you see it appropriate for the search dialog to close.** |
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"url": "https://github.com/foxtrotperry",
"email": "[email protected]"
},
"repository": {
"type": "git",
"url": "https://github.com/foxtrotperry/react-search-dialog.git"
},
"files": [
"dist"
],
Expand Down Expand Up @@ -49,6 +53,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0",
"path": "^0.12.7",
"prettier": "^2.7.1",
"react": "^18.2.0",
Expand Down
24 changes: 14 additions & 10 deletions react-search-dialog/FilteredSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { useLayoutEffect, useState } from 'react';
import { VariableSizeList } from 'react-window';
import { ListSubheader } from '@mui/material';
import { ItemHeight, SearchItemRequirements, SearchProps } from './Search';
import { ItemHeight, RenderItemArgs, SearchItemRequirements, SearchProps } from './Search';
import { DefaultListItem } from './DefaultListItem';
import { getItemLabel } from './helpers';

type FilteredSearch<T> = {
searchResults: T[];
itemHeight: ItemHeight;
getAddToRecentsFunc: (item: T) => RenderItemArgs<T>['addToRecents'];
onItemSelect: SearchProps<T>['onItemSelect'];
renderResult?: SearchProps<T>['renderResult'];
closeDialog: () => void;
};

type RenderWindowListItemParams = {
Expand All @@ -22,8 +24,8 @@ type ContainerLengthWidth = {
w: number;
};

const ALLEZ_SEARCH_RESULTS_HEADER_ID = 'allez-search-results-header';
const ALLEZ_SEARCH_RESULTS_CONTAINER_ID = 'allez-search-results-container';
const RSD_SEARCH_RESULTS_HEADER_ID = 'rsd-search-results-header';
const RSD_SEARCH_RESULTS_CONTAINER_ID = 'rsd-search-results-container';

/**
* Returns a list of search results with a basic header.
Expand All @@ -44,11 +46,11 @@ export const FilteredSearch = <T extends SearchItemRequirements>(props: Filtered
useLayoutEffect(() => {
const updateSize = () => {
const containerDimensions = document
.getElementById(ALLEZ_SEARCH_RESULTS_CONTAINER_ID)
.getElementById(RSD_SEARCH_RESULTS_CONTAINER_ID)
?.getBoundingClientRect();

const headerDimensions = document
.getElementById(ALLEZ_SEARCH_RESULTS_HEADER_ID)
.getElementById(RSD_SEARCH_RESULTS_HEADER_ID)
?.getBoundingClientRect();

containerDimensions &&
Expand Down Expand Up @@ -78,15 +80,17 @@ export const FilteredSearch = <T extends SearchItemRequirements>(props: Filtered
{
// If the user has provided a custom render function, use it.
props.renderResult ? (
props.renderResult(result, () => {
props.onItemSelect(result);
props.renderResult({
item: result,
addToRecents: props.getAddToRecentsFunc(result),
closeDialog: () => props.closeDialog(),
})
) : (
// Otherwise, use the default list item.
<DefaultListItem
item={result}
onClick={() => {
props.onItemSelect(result);
props.onItemSelect && props.onItemSelect(result);
}}
/>
)
Expand All @@ -101,9 +105,9 @@ export const FilteredSearch = <T extends SearchItemRequirements>(props: Filtered
width: '100%',
height: '100%',
}}
id={ALLEZ_SEARCH_RESULTS_CONTAINER_ID}
id={RSD_SEARCH_RESULTS_CONTAINER_ID}
>
<ListSubheader component="div" id={ALLEZ_SEARCH_RESULTS_HEADER_ID}>
<ListSubheader component="div" id={RSD_SEARCH_RESULTS_HEADER_ID}>
Search Results
</ListSubheader>
<VariableSizeList
Expand Down
2 changes: 1 addition & 1 deletion react-search-dialog/QuickFill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const QuickFill = <T extends SearchItemRequirements>(props: QuickFillProp
label={typeof item === 'string' ? item : item.label}
quickItem={item}
onClick={props.onQuickItemClick}
key={`allez-list-item-${i}`}
key={`rsd-list-item-${i}`}
/>
);
})}
Expand Down
23 changes: 18 additions & 5 deletions react-search-dialog/RecentSearches.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ListSubheader, Stack, Typography } from '@mui/material';
import { ItemHeight, SearchItemRequirements, SearchProps } from './Search';
import { ItemHeight, RenderItemArgs, SearchItemRequirements, SearchProps } from './Search';
import { DefaultListItem } from './DefaultListItem';

import AllInclusiveIcon from '@mui/icons-material/AllInclusive';
Expand All @@ -14,13 +14,21 @@ type RecentSearchProps<T> = {
*/
itemHeight: ItemHeight;
/**
* Callback for when an item is selected.
* Function to add an item to the list of recents.
*/
getAddToRecentsFunc: (item: T) => RenderItemArgs<T>['addToRecents'];
/**
* Function to call when an item is selected.
*/
onItemSelect: SearchProps<T>['onItemSelect'];
/**
* Optional render function for recent selections.
*/
renderRecent?: SearchProps<T>['renderResult'];
/**
* Function to close the search dialog.
*/
closeDialog: () => void;
/**
* Optional toolbar elements to display above the list of recents.
*/
Expand All @@ -31,7 +39,8 @@ type RecentSearchProps<T> = {
* Returns list of recent searches with a header and optional toolbar elements.
*/
export const RecentSearches = <T extends SearchItemRequirements>(props: RecentSearchProps<T>) => {
const { recents, toolbarElements, onItemSelect, renderRecent } = props;
const { recents, toolbarElements, getAddToRecentsFunc, onItemSelect, renderRecent, closeDialog } =
props;
return (
<div
style={{
Expand All @@ -48,12 +57,16 @@ export const RecentSearches = <T extends SearchItemRequirements>(props: RecentSe
recents.map((item) => (
<div key={typeof item === 'string' ? `${item}` : `${item.label}`}>
{renderRecent ? (
renderRecent(item, () => props.onItemSelect(item))
renderRecent({
item,
addToRecents: getAddToRecentsFunc(item),
closeDialog,
})
) : (
<DefaultListItem
item={item}
onClick={() => {
onItemSelect(item);
onItemSelect && onItemSelect(item);
}}
/>
)}
Expand Down
Loading

0 comments on commit 4c3c16d

Please sign in to comment.