SearchInput.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
  2. import { useRef, FC, useState, useEffect, useMemo } from 'react';
  3. import { useTranslation } from 'react-i18next';
  4. import { useSearchParams } from 'react-router-dom';
  5. import Icons from '../icons/Icons';
  6. import { SearchType } from './Types';
  7. const useSearchStyles = makeStyles(theme => ({
  8. wrapper: {
  9. display: 'flex',
  10. },
  11. input: {
  12. backgroundColor: '#fff',
  13. borderRadius: '4px',
  14. padding: theme.spacing(1),
  15. width: '240px',
  16. border: '1px solid #e9e9ed',
  17. fontSize: '14px',
  18. transition: 'all 0.2s',
  19. '& .MuiAutocomplete-endAdornment': {
  20. right: theme.spacing(0.5),
  21. },
  22. '& .MuiInput-underline:before': {
  23. border: 'none',
  24. },
  25. '& .MuiInput-underline:after': {
  26. border: 'none',
  27. },
  28. /**
  29. * when input focus
  30. * 1. change parent wrapper border color
  31. * 2. hide input start search icon
  32. */
  33. '&:focus-within': {
  34. border: `1px solid ${theme.palette.primary.main}`,
  35. '& $searchIcon': {
  36. width: 0,
  37. },
  38. },
  39. },
  40. textfield: {
  41. padding: 0,
  42. height: '16px',
  43. '&:focus': {
  44. caretColor: theme.palette.primary.main,
  45. },
  46. },
  47. searchIcon: {
  48. color: theme.palette.attuGrey.main,
  49. cursor: 'pointer',
  50. fontSize: '20px',
  51. width: (props: { searched: boolean }) => `${props.searched ? 0 : '20px'}`,
  52. transition: 'width 0.2s',
  53. },
  54. clearIcon: {
  55. color: theme.palette.primary.main,
  56. cursor: 'pointer',
  57. },
  58. iconWrapper: {
  59. opacity: (props: { searched: boolean }) => `${props.searched ? 1 : 0}`,
  60. transition: 'opacity 0.2s',
  61. },
  62. searchWrapper: {
  63. display: 'flex',
  64. justifyContent: 'center',
  65. alignItems: 'center',
  66. },
  67. }));
  68. const SearchInput: FC<SearchType> = props => {
  69. const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
  70. const [searchParams, setSearchParams] = useSearchParams();
  71. const [searchValue, setSearchValue] = useState<string>(searchText || '');
  72. const searched = useMemo(() => searchValue !== '', [searchValue]);
  73. const classes = useSearchStyles({ searched });
  74. const { t: commonTrans } = useTranslation();
  75. const inputRef = useRef<any>(null);
  76. const handleSearch = (value: string) => {
  77. onSearch(value);
  78. };
  79. useEffect(() => {
  80. searchParams[searchValue ? 'set' : 'delete']('search', searchValue);
  81. setSearchParams(searchParams);
  82. handleSearch(searchValue);
  83. }, [searchValue]);
  84. return (
  85. <div className={classes.wrapper}>
  86. <TextField
  87. inputRef={inputRef}
  88. variant="standard"
  89. classes={{ root: classes.input }}
  90. InputProps={{
  91. disableUnderline: true,
  92. classes: { input: classes.textfield },
  93. endAdornment: (
  94. <InputAdornment position="end">
  95. <span
  96. data-testid="clear-icon"
  97. className={`flex-center ${classes.iconWrapper}`}
  98. onClick={e => {
  99. setSearchValue('');
  100. inputRef.current.focus();
  101. onClear();
  102. }}
  103. >
  104. {Icons.clear({ classes: { root: classes.clearIcon } })}
  105. </span>
  106. </InputAdornment>
  107. ),
  108. startAdornment: (
  109. <InputAdornment position="start">
  110. <span
  111. className={classes.searchWrapper}
  112. onClick={() => handleSearch(searchValue)}
  113. >
  114. {Icons.search({ classes: { root: classes.searchIcon } })}
  115. </span>
  116. </InputAdornment>
  117. ),
  118. }}
  119. onChange={e => {
  120. const value = e.target.value.trim();
  121. setSearchValue(value);
  122. if (value === '') {
  123. onClear();
  124. }
  125. }}
  126. onKeyPress={e => {
  127. if (e.key === 'Enter') {
  128. // Do code here
  129. handleSearch(searchValue);
  130. e.preventDefault();
  131. }
  132. }}
  133. value={searchValue || ''}
  134. placeholder={commonTrans('search')}
  135. />
  136. </div>
  137. );
  138. };
  139. export default SearchInput;