123 lines
3 KiB
TypeScript
123 lines
3 KiB
TypeScript
import {
|
|
alpha,
|
|
Grid2,
|
|
IconButton,
|
|
InputAdornment,
|
|
InputBase,
|
|
Theme
|
|
} from "@mui/material";
|
|
import { makeStyles } from "@mui/styles";
|
|
import { Search as SearchIcon, Cancel as CancelIcon } from "@mui/icons-material";
|
|
import classNames from "classnames";
|
|
import { useDebounce, usePrevious } from "hooks";
|
|
import { useEffect, useState } from "react";
|
|
|
|
const useStyles = makeStyles((theme: Theme) => {
|
|
return ({
|
|
search: {
|
|
position: "relative",
|
|
width: "100%",
|
|
marginLeft: 0,
|
|
backgroundColor: alpha(theme.palette.common.white, 0.15),
|
|
"&:hover": {
|
|
backgroundColor: alpha(theme.palette.common.white, 0.25)
|
|
},
|
|
[theme.breakpoints.up("sm")]: {
|
|
width: "auto"
|
|
}
|
|
},
|
|
inputRoot: {
|
|
position: "relative",
|
|
color: "inherit",
|
|
width: "100%"
|
|
},
|
|
searchInput: {
|
|
width: "100%"
|
|
},
|
|
spacingLeft: {
|
|
marginLeft: theme.spacing(1)
|
|
},
|
|
spacingRight: {
|
|
marginRight: theme.spacing(1)
|
|
},
|
|
mutedIcon: {
|
|
color: theme.palette.text.secondary
|
|
},
|
|
roundBar: {
|
|
borderRadius: theme.shape.borderRadius
|
|
},
|
|
squareBar: {
|
|
borderRadius: 0
|
|
}
|
|
});
|
|
});
|
|
|
|
interface Props {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
shape?: "round" | "square";
|
|
}
|
|
|
|
export default function SearchBar(props: Props) {
|
|
const classes = useStyles();
|
|
const { value, onChange, shape } = props;
|
|
const prevValue = usePrevious(value);
|
|
const [search, setSearch] = useState(value);
|
|
const debouncedSearch = useDebounce(search, 500);
|
|
const prevDebouncedSearch = usePrevious(debouncedSearch);
|
|
|
|
useEffect(() => {
|
|
if (value !== prevValue) {
|
|
setSearch(value);
|
|
}
|
|
}, [prevValue, value]);
|
|
|
|
useEffect(() => {
|
|
if (debouncedSearch !== prevDebouncedSearch) {
|
|
onChange(debouncedSearch);
|
|
}
|
|
}, [debouncedSearch, prevDebouncedSearch, onChange]);
|
|
|
|
const handleChange = (newSearch: string) => {
|
|
setSearch(newSearch);
|
|
};
|
|
|
|
return (
|
|
<Grid2
|
|
container
|
|
direction="row"
|
|
className={classNames(
|
|
classes.search,
|
|
shape === "square" ? classes.squareBar : classes.roundBar
|
|
)}>
|
|
<InputBase
|
|
autoFocus={false}
|
|
placeholder="Search…"
|
|
classes={{
|
|
root: classes.inputRoot,
|
|
input: classes.searchInput
|
|
}}
|
|
onChange={event => handleChange(event.target.value)}
|
|
value={search}
|
|
fullWidth
|
|
inputProps={{ "aria-label": "Search" }}
|
|
startAdornment={
|
|
<InputAdornment position="start">
|
|
<SearchIcon className={classes.spacingLeft} />
|
|
</InputAdornment>
|
|
}
|
|
endAdornment={
|
|
<InputAdornment position="end">
|
|
<IconButton
|
|
size="small"
|
|
aria-label="Cancel Search"
|
|
onClick={() => handleChange("")}
|
|
className={classes.spacingRight}>
|
|
<CancelIcon fontSize="small" className={classes.mutedIcon} />
|
|
</IconButton>
|
|
</InputAdornment>
|
|
}
|
|
/>
|
|
</Grid2>
|
|
);
|
|
}
|