mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 17:15:38 +08:00
chore: component base-search-box
This commit is contained in:
145
src/components/base/base-search-box.tsx
Normal file
145
src/components/base/base-search-box.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Box, SvgIcon, TextField, Theme, styled } from "@mui/material";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import matchCaseIcon from "@/assets/image/component/match_case.svg?react";
|
||||
import matchWholeWordIcon from "@/assets/image/component/match_whole_word.svg?react";
|
||||
import useRegularExpressionIcon from "@/assets/image/component/use_regular_expression.svg?react";
|
||||
|
||||
type SearchProps = {
|
||||
placeholder?: string;
|
||||
onSearch: (
|
||||
match: (content: string) => boolean,
|
||||
search: (contents: string[]) => string[],
|
||||
state: {
|
||||
input: string;
|
||||
matchCase: boolean;
|
||||
matchWholeWord: boolean;
|
||||
useRegularExpression: boolean;
|
||||
}
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const BaseSearchBox = styled((props: SearchProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [matchCase, setMatchCase] = useState(true);
|
||||
const [matchWholeWord, setMatchWholeWord] = useState(false);
|
||||
const [useRegularExpression, setUseRegularExpression] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
const iconStyle = {
|
||||
style: {
|
||||
height: "24px",
|
||||
width: "24px",
|
||||
cursor: "pointer",
|
||||
} as React.CSSProperties,
|
||||
inheritViewBox: true,
|
||||
};
|
||||
const active = "var(--primary-main)";
|
||||
|
||||
const onChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
props.onSearch(
|
||||
(content) => doSearch([content], e.target?.value ?? "").length > 0,
|
||||
(contents: string[]) => doSearch(contents, e.target?.value ?? ""),
|
||||
{
|
||||
input: e.target?.value ?? "",
|
||||
matchCase,
|
||||
matchWholeWord,
|
||||
useRegularExpression,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const doSearch = (searchList: string[], searchItem: string) => {
|
||||
setErrorMessage("");
|
||||
return searchList.filter((item) => {
|
||||
try {
|
||||
let searchItemCopy = searchItem;
|
||||
if (!matchCase) {
|
||||
item = item.toLowerCase();
|
||||
searchItemCopy = searchItemCopy.toLowerCase();
|
||||
}
|
||||
if (matchWholeWord) {
|
||||
const regex = new RegExp(`\\b${searchItemCopy}\\b`);
|
||||
if (useRegularExpression) {
|
||||
const regexWithOptions = new RegExp(searchItemCopy);
|
||||
return regexWithOptions.test(item) && regex.test(item);
|
||||
} else {
|
||||
return regex.test(item);
|
||||
}
|
||||
} else if (useRegularExpression) {
|
||||
const regex = new RegExp(searchItemCopy);
|
||||
return regex.test(item);
|
||||
} else {
|
||||
return item.includes(searchItemCopy);
|
||||
}
|
||||
} catch (e) {
|
||||
setErrorMessage(`${e}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title={errorMessage} placement="bottom-start">
|
||||
<TextField
|
||||
hiddenLabel
|
||||
fullWidth
|
||||
size="small"
|
||||
autoComplete="off"
|
||||
variant="outlined"
|
||||
spellCheck="false"
|
||||
placeholder={props.placeholder ?? t("Filter conditions")}
|
||||
sx={{ input: { py: 0.65, px: 1.25 } }}
|
||||
onChange={onChange}
|
||||
InputProps={{
|
||||
sx: { pr: 1 },
|
||||
endAdornment: (
|
||||
<Box display="flex">
|
||||
<Tooltip title={t("Match Case")}>
|
||||
<div>
|
||||
<SvgIcon
|
||||
component={matchCaseIcon}
|
||||
{...iconStyle}
|
||||
sx={{ fill: matchCase ? active : undefined }}
|
||||
onClick={() => {
|
||||
setMatchCase(!matchCase);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("Match Whole Word")}>
|
||||
<div>
|
||||
<SvgIcon
|
||||
component={matchWholeWordIcon}
|
||||
{...iconStyle}
|
||||
sx={{ fill: matchWholeWord ? active : undefined }}
|
||||
onClick={() => {
|
||||
setMatchWholeWord(!matchWholeWord);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("Use Regular Expression")}>
|
||||
<div>
|
||||
<SvgIcon
|
||||
component={useRegularExpressionIcon}
|
||||
sx={{ fill: useRegularExpression ? active : undefined }}
|
||||
{...iconStyle}
|
||||
onClick={() => {
|
||||
setUseRegularExpression(!useRegularExpression);
|
||||
}}
|
||||
/>{" "}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
})(({ theme }) => ({
|
||||
"& .MuiInputBase-root": {
|
||||
background: theme.palette.mode === "light" ? "#fff" : undefined,
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user