noogle/components/searchInput/searchInput.tsx
2022-12-18 18:28:04 +01:00

261 lines
6.4 KiB
TypeScript

import React, { useState, useMemo, useEffect } from "react";
import Paper from "@mui/material/Paper";
import InputBase from "@mui/material/InputBase";
import IconButton from "@mui/material/IconButton";
import ClearIcon from "@mui/icons-material/Clear";
import {
Box,
debounce,
FormControl,
FormControlLabel,
FormLabel,
Grid,
Radio,
RadioGroup,
Tooltip,
Typography,
} from "@mui/material";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { NixType, nixTypes } from "../../types/nix";
import { useRouter } from "next/router";
import ShareIcon from "@mui/icons-material/Share";
import { useSnackbar } from "notistack";
interface SelectOptionProps {
label: string;
handleChange: (value: string) => void;
value: string;
options: {
value: string;
label: string;
}[];
}
const SelectOption = (props: SelectOptionProps) => {
const { label, handleChange, options, value } = props;
const _handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newVal = (event.target as HTMLInputElement).value as NixType;
handleChange(newVal);
};
return (
<FormControl
sx={{
flexDirection: "row",
}}
>
<FormLabel
sx={{
width: "7rem",
wordWrap: "unset",
margin: "auto",
padding: 1,
}}
>
<Typography sx={{ minWidth: "max-content" }}>{label}</Typography>
</FormLabel>
<RadioGroup
sx={{
width: "100%",
"&.MuiFormGroup-root": {
flexDirection: "row",
},
}}
value={value}
onChange={_handleChange}
>
{options.map(({ value, label }) => (
<FormControlLabel
key={value}
value={value}
control={<Radio />}
label={label}
/>
))}
</RadioGroup>
</FormControl>
);
};
export type Filter = { from: NixType; to: NixType };
export interface SearchInputProps {
handleSearch: (term: string) => void;
handleFilter: (filter: Filter | ((curr: Filter) => Filter)) => void;
clearSearch: () => void;
placeholder: string;
page: number;
}
export function SearchInput(props: SearchInputProps) {
const { handleSearch, clearSearch, placeholder, handleFilter, page } = props;
const { enqueueSnackbar } = useSnackbar();
const [term, setTerm] = useState("");
const [to, setTo] = useState<NixType>("any");
const [from, setFrom] = useState<NixType>("any");
const router = useRouter();
useEffect(() => {
const { query } = router;
if (query?.search) {
if (typeof query.search === "string") {
const search = query.search;
setTerm(search);
handleSearch(search);
}
}
if (query?.to) {
if (typeof query.to === "string") {
const to = query.to as NixType;
setTo(to);
handleFilter((curr) => ({ ...curr, to }));
}
}
if (query?.from) {
if (typeof query.from === "string") {
const from = query.from as NixType;
setFrom(from);
handleFilter((curr) => ({ ...curr, from }));
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [router]);
const handleShare = () => {
const queries = [];
if (term) {
queries.push(`search=${term}`);
}
queries.push(`page=${page}`);
queries.push(`to=${to}&from=${from}`);
const handle = `https://noogle.dev?${queries.join("&")}`;
navigator.clipboard.writeText(handle);
enqueueSnackbar("link copied to clipboard", { variant: "default" });
};
const handleSubmit = React.useRef((input: string) => {
handleSearch(input);
router.query[term] = term;
}).current;
const debouncedSubmit = useMemo(
() => debounce(handleSubmit, 300),
[handleSubmit]
);
const _handleFilter = (t: NixType, mode: "from" | "to") => {
if (mode === "to") {
setTo(t);
handleFilter({ to: t, from });
} else {
setFrom(t);
handleFilter({ to, from: t });
}
};
const handleClear = () => {
setTerm("");
setFrom("any");
setTo("any");
clearSearch();
};
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setTerm(e.target.value);
debouncedSubmit(e.target.value);
};
return (
<>
<Paper
component="form"
elevation={0}
sx={{
width: "100%",
p: 1,
my: 2,
display: "flex",
alignItems: "center",
}}
onSubmit={(e: React.FormEvent) => {
e.preventDefault();
handleSubmit(term);
}}
>
<IconButton aria-label="clear-button" onClick={handleClear}>
<ClearIcon />
</IconButton>
<InputBase
autoFocus
sx={{
ml: 1,
flex: 1,
backgroundColor: "paper.main",
p: 1,
}}
placeholder={placeholder}
inputProps={{ "aria-label": "search-input" }}
value={term}
onChange={(e) => handleChange(e)}
/>
<Tooltip title="share search result">
<IconButton
sx={{
p: 1,
color: "common.black",
}}
onClick={handleShare}
>
<ShareIcon fontSize="inherit" />
</IconButton>
</Tooltip>
</Paper>
<Box>
<Grid container>
<Grid item xs={12} md={5}>
<SelectOption
value={from}
label="from type"
handleChange={(value) => {
_handleFilter(value as NixType, "from");
}}
options={nixTypes.map((v) => ({ value: v, label: v }))}
/>
</Grid>
<Grid
item
md={2}
sx={{
display: {
md: "flex",
xs: "none",
},
justifyContent: "center",
alignItems: "center",
}}
>
<Typography>
<ChevronRightIcon />
</Typography>
</Grid>
<Grid item xs={12} md={5}>
<SelectOption
value={to}
label="to type"
handleChange={(value) => {
_handleFilter(value as NixType, "to");
}}
options={nixTypes.map((v) => ({ value: v, label: v }))}
/>
</Grid>
</Grid>
</Box>
</>
);
}