Files
clash-proxy/src/components/base/base-search-box.tsx
Tunglies 475a09bb54 feat: comprehensive oxlint cleanup - remove unused code
🧹 Cleanup Summary:
- Fixed 83 oxlint warnings across 50+ files
- Removed unused imports, variables, and functions
- Maintained all functional code and error handling
- Improved bundle size and code maintainability

📝 Key Changes:
- Cleaned unused React hooks (useState, useEffect, useClashInfo)
- Removed unused Material-UI imports (useTheme, styled components)
- Deleted unused interfaces and type definitions
- Fixed spread operator usage and boolean casting
- Simplified catch parameters where appropriate

🎯 Files Modified:
- React components: home.tsx, settings, profiles, etc.
- Custom hooks: use-*.ts files
- Utility functions and type definitions
- Configuration files

 Result: 0 oxlint warnings (from 83 warnings)
🔧 All functionality preserved
📦 Reduced bundle size through dead code elimination
2025-08-22 18:48:56 +08:00

194 lines
5.9 KiB
TypeScript

import { Box, SvgIcon, TextField, styled } from "@mui/material";
import Tooltip from "@mui/material/Tooltip";
import { ChangeEvent, useEffect, useMemo, useRef, useState } from "react";
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";
import { useTranslation } from "react-i18next";
export type SearchState = {
text: string;
matchCase: boolean;
matchWholeWord: boolean;
useRegularExpression: boolean;
};
type SearchProps = {
placeholder?: string;
matchCase?: boolean;
matchWholeWord?: boolean;
useRegularExpression?: boolean;
onSearch: (match: (content: string) => boolean, state: SearchState) => void;
};
const StyledTextField = styled(TextField)(({ theme }) => ({
"& .MuiInputBase-root": {
background: theme.palette.mode === "light" ? "#fff" : undefined,
paddingRight: "4px",
},
"& .MuiInputBase-root svg[aria-label='active'] path": {
fill: theme.palette.primary.light,
},
"& .MuiInputBase-root svg[aria-label='inactive'] path": {
fill: "#A7A7A7",
},
}));
export const BaseSearchBox = (props: SearchProps) => {
const { t } = useTranslation();
const inputRef = useRef<HTMLInputElement>(null);
const [matchCase, setMatchCase] = useState(props.matchCase ?? false);
const [matchWholeWord, setMatchWholeWord] = useState(
props.matchWholeWord ?? false,
);
const [useRegularExpression, setUseRegularExpression] = useState(
props.useRegularExpression ?? false,
);
const [errorMessage, setErrorMessage] = useState("");
const iconStyle = {
style: {
height: "24px",
width: "24px",
cursor: "pointer",
} as React.CSSProperties,
inheritViewBox: true,
};
// 验证正则表达式的辅助函数
const validateRegex = (pattern: string) => {
if (!pattern) return true;
try {
new RegExp(pattern);
return true;
} catch (e) {
console.warn("[BaseSearchBox] validateRegex error:", e);
return false;
}
};
const createMatcher = useMemo(() => {
return (searchText: string) => {
try {
// 当启用正则表达式验证是否合规
if (useRegularExpression && searchText) {
const isValid = validateRegex(searchText);
if (!isValid) {
throw new Error(t("Invalid regular expression"));
}
}
return (content: string) => {
if (!searchText) return true;
let item = !matchCase ? content.toLowerCase() : content;
let searchItem = !matchCase ? searchText.toLowerCase() : searchText;
if (useRegularExpression) {
return new RegExp(searchItem).test(item);
}
if (matchWholeWord) {
return new RegExp(`\\b${searchItem}\\b`).test(item);
}
return item.includes(searchItem);
};
} catch (err) {
setErrorMessage(err instanceof Error ? err.message : `${err}`);
return () => false; // 无效正则规则 不匹配值
}
};
}, [matchCase, matchWholeWord, useRegularExpression, t]);
useEffect(() => {
if (!inputRef.current) return;
const value = inputRef.current.value;
props.onSearch(createMatcher(value), {
text: value,
matchCase,
matchWholeWord,
useRegularExpression,
});
}, [matchCase, matchWholeWord, useRegularExpression, createMatcher]);
const onChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const value = e.target?.value ?? "";
setErrorMessage("");
// 验证正则表达式
if (useRegularExpression && value) {
const isValid = validateRegex(value);
if (!isValid) {
setErrorMessage(t("Invalid regular expression"));
}
}
props.onSearch(createMatcher(value), {
text: value,
matchCase,
matchWholeWord,
useRegularExpression,
});
};
return (
<Tooltip title={errorMessage || ""} placement="bottom-start">
<StyledTextField
autoComplete="new-password"
inputRef={inputRef}
hiddenLabel
fullWidth
size="small"
variant="outlined"
spellCheck="false"
placeholder={props.placeholder ?? t("Filter conditions")}
sx={{ input: { py: 0.65, px: 1.25 } }}
onChange={onChange}
error={!!errorMessage}
slotProps={{
input: {
sx: { pr: 1 },
endAdornment: (
<Box display="flex">
<Tooltip title={t("Match Case")}>
<div>
<SvgIcon
component={matchCaseIcon}
{...iconStyle}
aria-label={matchCase ? "active" : "inactive"}
onClick={() => setMatchCase(!matchCase)}
/>
</div>
</Tooltip>
<Tooltip title={t("Match Whole Word")}>
<div>
<SvgIcon
component={matchWholeWordIcon}
{...iconStyle}
aria-label={matchWholeWord ? "active" : "inactive"}
onClick={() => setMatchWholeWord(!matchWholeWord)}
/>
</div>
</Tooltip>
<Tooltip title={t("Use Regular Expression")}>
<div>
<SvgIcon
component={useRegularExpressionIcon}
aria-label={useRegularExpression ? "active" : "inactive"}
{...iconStyle}
onClick={() =>
setUseRegularExpression(!useRegularExpression)
}
/>{" "}
</div>
</Tooltip>
</Box>
),
},
}}
/>
</Tooltip>
);
};