base-search-box.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { Box, SvgIcon, TextField, Theme, styled } from "@mui/material";
  2. import Tooltip from "@mui/material/Tooltip";
  3. import { ChangeEvent, useState } from "react";
  4. import { useTranslation } from "react-i18next";
  5. import matchCaseIcon from "@/assets/image/component/match_case.svg?react";
  6. import matchWholeWordIcon from "@/assets/image/component/match_whole_word.svg?react";
  7. import useRegularExpressionIcon from "@/assets/image/component/use_regular_expression.svg?react";
  8. type SearchProps = {
  9. placeholder?: string;
  10. onSearch: (
  11. match: (content: string) => boolean,
  12. state: {
  13. text: string;
  14. matchCase: boolean;
  15. matchWholeWord: boolean;
  16. useRegularExpression: boolean;
  17. }
  18. ) => void;
  19. };
  20. export const BaseSearchBox = styled((props: SearchProps) => {
  21. const { t } = useTranslation();
  22. const [matchCase, setMatchCase] = useState(true);
  23. const [matchWholeWord, setMatchWholeWord] = useState(false);
  24. const [useRegularExpression, setUseRegularExpression] = useState(false);
  25. const [errorMessage, setErrorMessage] = useState("");
  26. const iconStyle = {
  27. style: {
  28. height: "24px",
  29. width: "24px",
  30. cursor: "pointer",
  31. } as React.CSSProperties,
  32. inheritViewBox: true,
  33. };
  34. const active = "var(--primary-main)";
  35. const onChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
  36. props.onSearch(
  37. (content) => doSearch([content], e.target?.value ?? "").length > 0,
  38. {
  39. text: e.target?.value ?? "",
  40. matchCase,
  41. matchWholeWord,
  42. useRegularExpression,
  43. }
  44. );
  45. };
  46. const doSearch = (searchList: string[], searchItem: string) => {
  47. setErrorMessage("");
  48. return searchList.filter((item) => {
  49. try {
  50. let searchItemCopy = searchItem;
  51. if (!matchCase) {
  52. item = item.toLowerCase();
  53. searchItemCopy = searchItemCopy.toLowerCase();
  54. }
  55. if (matchWholeWord) {
  56. const regex = new RegExp(`\\b${searchItemCopy}\\b`);
  57. if (useRegularExpression) {
  58. const regexWithOptions = new RegExp(searchItemCopy);
  59. return regexWithOptions.test(item) && regex.test(item);
  60. } else {
  61. return regex.test(item);
  62. }
  63. } else if (useRegularExpression) {
  64. const regex = new RegExp(searchItemCopy);
  65. return regex.test(item);
  66. } else {
  67. return item.includes(searchItemCopy);
  68. }
  69. } catch (err) {
  70. setErrorMessage(`${err}`);
  71. }
  72. });
  73. };
  74. return (
  75. <Tooltip title={errorMessage} placement="bottom-start">
  76. <TextField
  77. hiddenLabel
  78. fullWidth
  79. size="small"
  80. autoComplete="off"
  81. variant="outlined"
  82. spellCheck="false"
  83. placeholder={props.placeholder ?? t("Filter conditions")}
  84. sx={{ input: { py: 0.65, px: 1.25 } }}
  85. onChange={onChange}
  86. InputProps={{
  87. sx: { pr: 1 },
  88. endAdornment: (
  89. <Box display="flex">
  90. <Tooltip title={t("Match Case")}>
  91. <div>
  92. <SvgIcon
  93. component={matchCaseIcon}
  94. {...iconStyle}
  95. sx={{ fill: matchCase ? active : undefined }}
  96. onClick={() => {
  97. setMatchCase(!matchCase);
  98. }}
  99. />
  100. </div>
  101. </Tooltip>
  102. <Tooltip title={t("Match Whole Word")}>
  103. <div>
  104. <SvgIcon
  105. component={matchWholeWordIcon}
  106. {...iconStyle}
  107. sx={{ fill: matchWholeWord ? active : undefined }}
  108. onClick={() => {
  109. setMatchWholeWord(!matchWholeWord);
  110. }}
  111. />
  112. </div>
  113. </Tooltip>
  114. <Tooltip title={t("Use Regular Expression")}>
  115. <div>
  116. <SvgIcon
  117. component={useRegularExpressionIcon}
  118. sx={{ fill: useRegularExpression ? active : undefined }}
  119. {...iconStyle}
  120. onClick={() => {
  121. setUseRegularExpression(!useRegularExpression);
  122. }}
  123. />{" "}
  124. </div>
  125. </Tooltip>
  126. </Box>
  127. ),
  128. }}
  129. />
  130. </Tooltip>
  131. );
  132. })(({ theme }) => ({
  133. "& .MuiInputBase-root": {
  134. background: theme.palette.mode === "light" ? "#fff" : undefined,
  135. },
  136. }));