SearchInput.tsx
1 import { SearchIcon } from '@heroicons/react/outline'; 2 import { XCircleIcon } from '@heroicons/react/solid'; 3 import { Box, BoxProps, IconButton, InputBase, useMediaQuery, useTheme } from '@mui/material'; 4 import debounce from 'lodash/debounce'; 5 import { useMemo, useRef, useState } from 'react'; 6 7 interface SearchInputProps { 8 onSearchTermChange: (value: string) => void; 9 wrapperSx?: BoxProps; 10 placeholder: string; 11 disableFocus?: boolean; 12 } 13 14 export const SearchInput = ({ 15 onSearchTermChange, 16 wrapperSx, 17 placeholder, 18 disableFocus, 19 }: SearchInputProps) => { 20 const inputEl = useRef<HTMLInputElement>(null); 21 const [searchTerm, setSearchTerm] = useState(''); 22 23 const { breakpoints } = useTheme(); 24 const sm = useMediaQuery(breakpoints.down('sm')); 25 26 const handleClear = () => { 27 setSearchTerm(''); 28 onSearchTermChange(''); 29 inputEl.current?.focus(); 30 }; 31 32 const debounchedChangeHandler = useMemo(() => { 33 return debounce((value: string) => { 34 onSearchTermChange(value); 35 }, 300); 36 }, [onSearchTermChange]); 37 return ( 38 <Box 39 sx={(theme) => ({ 40 display: 'flex', 41 alignItems: 'center', 42 gap: 2, 43 border: `1px solid ${theme.palette.divider}`, 44 borderRadius: '6px', 45 height: '36px', 46 ...wrapperSx, 47 })} 48 > 49 <Box sx={{ ml: 2, mt: 1 }}> 50 <SearchIcon height={16} /> 51 </Box> 52 <InputBase 53 autoFocus={sm} 54 inputRef={inputEl} 55 sx={{ width: '100%', fontSize: { xs: 16, sm: 14 } }} 56 placeholder={placeholder} 57 value={searchTerm} 58 onChange={(e) => { 59 setSearchTerm(e.target.value); 60 debounchedChangeHandler(e.target.value); 61 }} 62 onKeyDown={(event) => { 63 if (disableFocus) event.stopPropagation(); 64 }} 65 /> 66 <IconButton 67 sx={{ p: 0, mr: 2, visibility: searchTerm ? 'visible' : 'hidden' }} 68 onClick={() => handleClear()} 69 > 70 <XCircleIcon height={16} /> 71 </IconButton> 72 </Box> 73 ); 74 };