setting-theme.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import useSWR from "swr";
  2. import { useEffect, useState } from "react";
  3. import { useLockFn } from "ahooks";
  4. import { useTranslation } from "react-i18next";
  5. import {
  6. Button,
  7. Dialog,
  8. DialogActions,
  9. DialogContent,
  10. DialogTitle,
  11. List,
  12. ListItem,
  13. ListItemText,
  14. styled,
  15. TextField,
  16. } from "@mui/material";
  17. import { getVergeConfig, patchVergeConfig } from "../../services/cmds";
  18. import { defaultTheme } from "../../pages/_theme";
  19. interface Props {
  20. open: boolean;
  21. onClose: () => void;
  22. onError?: (err: Error) => void;
  23. }
  24. const Item = styled(ListItem)(() => ({
  25. padding: "5px 2px",
  26. }));
  27. const Round = styled("div")(() => ({
  28. width: "24px",
  29. height: "24px",
  30. borderRadius: "18px",
  31. display: "inline-block",
  32. marginRight: "8px",
  33. }));
  34. const SettingTheme = (props: Props) => {
  35. const { open, onClose, onError } = props;
  36. const { t } = useTranslation();
  37. const { data: vergeConfig, mutate } = useSWR(
  38. "getVergeConfig",
  39. getVergeConfig
  40. );
  41. const { theme_setting } = vergeConfig ?? {};
  42. const [theme, setTheme] = useState(theme_setting || {});
  43. useEffect(() => {
  44. setTheme({ ...theme_setting } || {});
  45. }, [theme_setting]);
  46. const textProps = {
  47. size: "small",
  48. autoComplete: "off",
  49. sx: { width: 135 },
  50. } as const;
  51. const handleChange = (field: keyof typeof theme) => (e: any) => {
  52. setTheme((t) => ({ ...t, [field]: e.target.value }));
  53. };
  54. const onSave = useLockFn(async () => {
  55. try {
  56. await patchVergeConfig({ theme_setting: theme });
  57. mutate();
  58. } catch (err: any) {
  59. onError?.(err);
  60. }
  61. });
  62. return (
  63. <Dialog open={open} onClose={onClose}>
  64. <DialogTitle>{t("Theme Setting")}</DialogTitle>
  65. <DialogContent
  66. sx={{ width: 400, maxHeight: 300, overflow: "auto", pb: 0 }}
  67. >
  68. <List sx={{ pt: 0 }}>
  69. <Item>
  70. <ListItemText primary="Primary Color" />
  71. <Round
  72. sx={{
  73. background: theme.primary_color || defaultTheme.primary_color,
  74. }}
  75. />
  76. <TextField
  77. {...textProps}
  78. value={theme.primary_color ?? ""}
  79. placeholder={defaultTheme.primary_color}
  80. onChange={handleChange("primary_color")}
  81. />
  82. </Item>
  83. <Item>
  84. <ListItemText primary="Secondary Color" />
  85. <Round
  86. sx={{
  87. background:
  88. theme.secondary_color || defaultTheme.secondary_color,
  89. }}
  90. />
  91. <TextField
  92. {...textProps}
  93. value={theme.secondary_color ?? ""}
  94. placeholder={defaultTheme.secondary_color}
  95. onChange={handleChange("secondary_color")}
  96. />
  97. </Item>
  98. <Item>
  99. <ListItemText primary="Info Color" />
  100. <Round
  101. sx={{
  102. background: theme.info_color || defaultTheme.info_color,
  103. }}
  104. />
  105. <TextField
  106. {...textProps}
  107. value={theme.info_color ?? ""}
  108. placeholder={defaultTheme.info_color}
  109. onChange={handleChange("info_color")}
  110. />
  111. </Item>
  112. <Item>
  113. <ListItemText primary="Error Color" />
  114. <Round
  115. sx={{
  116. background: theme.error_color || defaultTheme.error_color,
  117. }}
  118. />
  119. <TextField
  120. {...textProps}
  121. value={theme.error_color ?? ""}
  122. placeholder={defaultTheme.error_color}
  123. onChange={handleChange("error_color")}
  124. />
  125. </Item>
  126. <Item>
  127. <ListItemText primary="Warning Color" />
  128. <Round
  129. sx={{
  130. background: theme.warning_color || defaultTheme.warning_color,
  131. }}
  132. />
  133. <TextField
  134. {...textProps}
  135. value={theme.warning_color ?? ""}
  136. placeholder={defaultTheme.warning_color}
  137. onChange={handleChange("warning_color")}
  138. />
  139. </Item>
  140. <Item>
  141. <ListItemText primary="Success Color" />
  142. <Round
  143. sx={{
  144. background: theme.success_color || defaultTheme.success_color,
  145. }}
  146. />
  147. <TextField
  148. {...textProps}
  149. value={theme.success_color ?? ""}
  150. placeholder={defaultTheme.success_color}
  151. onChange={handleChange("success_color")}
  152. />
  153. </Item>
  154. <Item>
  155. <ListItemText primary="Font Family" />
  156. <TextField
  157. {...textProps}
  158. value={theme.font_family ?? ""}
  159. onChange={handleChange("font_family")}
  160. />
  161. </Item>
  162. <Item>
  163. <ListItemText primary="Font Face" />
  164. <TextField
  165. {...textProps}
  166. value={theme.font_face ?? ""}
  167. onChange={handleChange("font_face")}
  168. />
  169. </Item>
  170. </List>
  171. </DialogContent>
  172. <DialogActions>
  173. <Button onClick={onClose}>{t("Cancel")}</Button>
  174. <Button onClick={onSave} variant="contained">
  175. {t("Save")}
  176. </Button>
  177. </DialogActions>
  178. </Dialog>
  179. );
  180. };
  181. export default SettingTheme;