|
@@ -1,5 +1,7 @@
|
|
import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
|
|
import { InputAdornment, makeStyles, TextField } from '@material-ui/core';
|
|
-import { useRef, FC, useState } from 'react';
|
|
|
|
|
|
+import { useRef, FC, useState, useEffect, useMemo } from 'react';
|
|
|
|
+import { useTranslation } from 'react-i18next';
|
|
|
|
+import { useHistory } from 'react-router-dom';
|
|
import Icons from '../icons/Icons';
|
|
import Icons from '../icons/Icons';
|
|
import { SearchType } from './Types';
|
|
import { SearchType } from './Types';
|
|
|
|
|
|
@@ -9,10 +11,12 @@ const useSearchStyles = makeStyles(theme => ({
|
|
},
|
|
},
|
|
input: {
|
|
input: {
|
|
backgroundColor: '#fff',
|
|
backgroundColor: '#fff',
|
|
- borderRadius: '4px 4px 0 0',
|
|
|
|
- padding: (props: any) => `${props.showInput ? theme.spacing(0, 1) : 0}`,
|
|
|
|
- boxSizing: 'border-box',
|
|
|
|
- width: (props: any) => `${props.showInput ? '255px' : 0}`,
|
|
|
|
|
|
+ borderRadius: '4px',
|
|
|
|
+ padding: theme.spacing(1),
|
|
|
|
+ width: '240px',
|
|
|
|
+ border: '1px solid #e9e9ed',
|
|
|
|
+ fontSize: '14px',
|
|
|
|
+
|
|
transition: 'all 0.2s',
|
|
transition: 'all 0.2s',
|
|
|
|
|
|
'& .MuiAutocomplete-endAdornment': {
|
|
'& .MuiAutocomplete-endAdornment': {
|
|
@@ -26,73 +30,126 @@ const useSearchStyles = makeStyles(theme => ({
|
|
'& .MuiInput-underline:after': {
|
|
'& .MuiInput-underline:after': {
|
|
border: 'none',
|
|
border: 'none',
|
|
},
|
|
},
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * when input focus
|
|
|
|
+ * 1. change parent wrapper border color
|
|
|
|
+ * 2. hide input start search icon
|
|
|
|
+ */
|
|
|
|
+ '&:focus-within': {
|
|
|
|
+ border: `1px solid ${theme.palette.primary.main}`,
|
|
|
|
+
|
|
|
|
+ '& $searchIcon': {
|
|
|
|
+ width: 0,
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ textfield: {
|
|
|
|
+ padding: 0,
|
|
|
|
+ height: '16px',
|
|
|
|
+
|
|
|
|
+ '&:focus': {
|
|
|
|
+ caretColor: theme.palette.primary.main,
|
|
|
|
+ },
|
|
},
|
|
},
|
|
searchIcon: {
|
|
searchIcon: {
|
|
- paddingLeft: theme.spacing(1),
|
|
|
|
- color: theme.palette.primary.main,
|
|
|
|
|
|
+ color: '#aeaebb',
|
|
cursor: 'pointer',
|
|
cursor: 'pointer',
|
|
- fontSize: '24px',
|
|
|
|
|
|
+ fontSize: '20px',
|
|
|
|
+ width: (props: { searched: boolean }) => `${props.searched ? 0 : '20px'}`,
|
|
|
|
+
|
|
|
|
+ transition: 'width 0.2s',
|
|
},
|
|
},
|
|
clearIcon: {
|
|
clearIcon: {
|
|
- color: 'rgba(0, 0, 0, 0.6)',
|
|
|
|
|
|
+ color: theme.palette.primary.main,
|
|
cursor: 'pointer',
|
|
cursor: 'pointer',
|
|
},
|
|
},
|
|
iconWrapper: {
|
|
iconWrapper: {
|
|
- opacity: (props: any) => `${props.searched ? 1 : 0}`,
|
|
|
|
|
|
+ opacity: (props: { searched: boolean }) => `${props.searched ? 1 : 0}`,
|
|
transition: 'opacity 0.2s',
|
|
transition: 'opacity 0.2s',
|
|
},
|
|
},
|
|
searchWrapper: {
|
|
searchWrapper: {
|
|
- display: (props: any) => `${props.showInput ? 'flex' : 'none'}`,
|
|
|
|
|
|
+ display: 'flex',
|
|
justifyContent: 'center',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
},
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
+let timer: NodeJS.Timeout | null = null;
|
|
|
|
+
|
|
const SearchInput: FC<SearchType> = props => {
|
|
const SearchInput: FC<SearchType> = props => {
|
|
const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
|
|
const { searchText = '', onClear = () => {}, onSearch = () => {} } = props;
|
|
- const [searchValue, setSearchValue] = useState<string>(searchText);
|
|
|
|
- const searched = searchValue !== '';
|
|
|
|
- const [showInput, setShowInput] = useState<boolean>(searchValue !== '');
|
|
|
|
|
|
+ const [searchValue, setSearchValue] = useState<string | null>(
|
|
|
|
+ searchText || null
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const [isInit, setIsInit] = useState<boolean>(true);
|
|
|
|
+
|
|
|
|
+ const searched = useMemo(
|
|
|
|
+ () => searchValue !== '' && searchValue !== null,
|
|
|
|
+ [searchValue]
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const classes = useSearchStyles({ searched });
|
|
|
|
+ const { t: commonTrans } = useTranslation();
|
|
|
|
|
|
- const classes = useSearchStyles({ searched, showInput });
|
|
|
|
|
|
+ const history = useHistory();
|
|
|
|
|
|
const inputRef = useRef<any>(null);
|
|
const inputRef = useRef<any>(null);
|
|
|
|
|
|
- const onIconClick = () => {
|
|
|
|
- setShowInput(true);
|
|
|
|
- if (inputRef.current) {
|
|
|
|
- inputRef.current.focus();
|
|
|
|
|
|
+ const savedSearchFn = useRef<(value: string) => void>(() => {});
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ savedSearchFn.current = onSearch;
|
|
|
|
+ }, [onSearch]);
|
|
|
|
+
|
|
|
|
+ useEffect(() => {
|
|
|
|
+ if (timer) {
|
|
|
|
+ clearTimeout(timer);
|
|
}
|
|
}
|
|
|
|
+ if (searchValue !== null && !isInit) {
|
|
|
|
+ timer = setTimeout(() => {
|
|
|
|
+ // save other params data and remove last time search info
|
|
|
|
+ const location = history.location;
|
|
|
|
+ const params = new URLSearchParams(location.search);
|
|
|
|
+ params.delete('search');
|
|
|
|
+
|
|
|
|
+ if (searchValue) {
|
|
|
|
+ params.append('search', searchValue);
|
|
|
|
+ }
|
|
|
|
+ // add search value in url
|
|
|
|
+ history.push({ search: params.toString() });
|
|
|
|
|
|
- if (searched) {
|
|
|
|
- onSearch(searchValue);
|
|
|
|
|
|
+ savedSearchFn.current(searchValue);
|
|
|
|
+ }, 300);
|
|
}
|
|
}
|
|
- };
|
|
|
|
|
|
|
|
- const handleInputBlur = () => {
|
|
|
|
- setShowInput(searched);
|
|
|
|
|
|
+ return () => {
|
|
|
|
+ timer && clearTimeout(timer);
|
|
|
|
+ };
|
|
|
|
+ }, [searchValue, history, isInit]);
|
|
|
|
+
|
|
|
|
+ const handleSearch = (value: string | null) => {
|
|
|
|
+ if (value !== null) {
|
|
|
|
+ onSearch(value);
|
|
|
|
+ }
|
|
};
|
|
};
|
|
|
|
|
|
return (
|
|
return (
|
|
<div className={classes.wrapper}>
|
|
<div className={classes.wrapper}>
|
|
- {!showInput && (
|
|
|
|
- <div className={classes.searchIcon} onClick={onIconClick}>
|
|
|
|
- {Icons.search()}
|
|
|
|
- </div>
|
|
|
|
- )}
|
|
|
|
<TextField
|
|
<TextField
|
|
inputRef={inputRef}
|
|
inputRef={inputRef}
|
|
- autoFocus={true}
|
|
|
|
variant="standard"
|
|
variant="standard"
|
|
classes={{ root: classes.input }}
|
|
classes={{ root: classes.input }}
|
|
InputProps={{
|
|
InputProps={{
|
|
disableUnderline: true,
|
|
disableUnderline: true,
|
|
|
|
+ classes: { input: classes.textfield },
|
|
endAdornment: (
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<InputAdornment position="end">
|
|
<span
|
|
<span
|
|
className={`flex-center ${classes.iconWrapper}`}
|
|
className={`flex-center ${classes.iconWrapper}`}
|
|
onClick={e => {
|
|
onClick={e => {
|
|
setSearchValue('');
|
|
setSearchValue('');
|
|
|
|
+ setIsInit(false);
|
|
inputRef.current.focus();
|
|
inputRef.current.focus();
|
|
onClear();
|
|
onClear();
|
|
}}
|
|
}}
|
|
@@ -105,31 +162,30 @@ const SearchInput: FC<SearchType> = props => {
|
|
<InputAdornment position="start">
|
|
<InputAdornment position="start">
|
|
<span
|
|
<span
|
|
className={classes.searchWrapper}
|
|
className={classes.searchWrapper}
|
|
- onClick={() => onSearch(searchValue)}
|
|
|
|
|
|
+ onClick={() => handleSearch(searchValue)}
|
|
>
|
|
>
|
|
{Icons.search({ classes: { root: classes.searchIcon } })}
|
|
{Icons.search({ classes: { root: classes.searchIcon } })}
|
|
</span>
|
|
</span>
|
|
</InputAdornment>
|
|
</InputAdornment>
|
|
),
|
|
),
|
|
}}
|
|
}}
|
|
- onBlur={handleInputBlur}
|
|
|
|
onChange={e => {
|
|
onChange={e => {
|
|
- // console.log('change', e.target.value);
|
|
|
|
- const value = e.target.value;
|
|
|
|
|
|
+ const value = e.target.value.trim();
|
|
setSearchValue(value);
|
|
setSearchValue(value);
|
|
|
|
+ setIsInit(false);
|
|
if (value === '') {
|
|
if (value === '') {
|
|
onClear();
|
|
onClear();
|
|
}
|
|
}
|
|
}}
|
|
}}
|
|
onKeyPress={e => {
|
|
onKeyPress={e => {
|
|
- // console.log(`Pressed keyCode ${e.key}`);
|
|
|
|
if (e.key === 'Enter') {
|
|
if (e.key === 'Enter') {
|
|
// Do code here
|
|
// Do code here
|
|
- onSearch(searchValue);
|
|
|
|
|
|
+ handleSearch(searchValue);
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}}
|
|
}}
|
|
- value={searchValue}
|
|
|
|
|
|
+ value={searchValue || ''}
|
|
|
|
+ placeholder={commonTrans('search')}
|
|
/>
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
);
|