setting-theme.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  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. useTheme,
  17. } from "@mui/material";
  18. import { getVergeConfig, patchVergeConfig } from "../../services/cmds";
  19. import { defaultTheme, defaultDarkTheme } from "../../pages/_theme";
  20. interface Props {
  21. open: boolean;
  22. onClose: () => void;
  23. onError?: (err: Error) => void;
  24. }
  25. const Item = styled(ListItem)(() => ({
  26. padding: "5px 2px",
  27. }));
  28. const Round = styled("div")(() => ({
  29. width: "24px",
  30. height: "24px",
  31. borderRadius: "18px",
  32. display: "inline-block",
  33. marginRight: "8px",
  34. }));
  35. const SettingTheme = (props: Props) => {
  36. const { open, onClose, onError } = props;
  37. const { t } = useTranslation();
  38. const { data: vergeConfig, mutate } = useSWR(
  39. "getVergeConfig",
  40. getVergeConfig
  41. );
  42. const { theme_setting } = vergeConfig ?? {};
  43. const [theme, setTheme] = useState(theme_setting || {});
  44. useEffect(() => {
  45. setTheme({ ...theme_setting } || {});
  46. }, [theme_setting]);
  47. const textProps = {
  48. size: "small",
  49. autoComplete: "off",
  50. sx: { width: 135 },
  51. } as const;
  52. const handleChange = (field: keyof typeof theme) => (e: any) => {
  53. setTheme((t) => ({ ...t, [field]: e.target.value }));
  54. };
  55. const onSave = useLockFn(async () => {
  56. try {
  57. await patchVergeConfig({ theme_setting: theme });
  58. mutate();
  59. onClose();
  60. } catch (err: any) {
  61. onError?.(err);
  62. }
  63. });
  64. // default theme
  65. const { palette } = useTheme();
  66. const dt = palette.mode === "light" ? defaultTheme : defaultDarkTheme;
  67. type ThemeKey = keyof typeof theme & keyof typeof defaultTheme;
  68. const renderItem = (label: string, key: ThemeKey) => {
  69. return (
  70. <Item>
  71. <ListItemText primary={label} />
  72. <Round sx={{ background: theme[key] || dt[key] }} />
  73. <TextField
  74. {...textProps}
  75. value={theme[key] ?? ""}
  76. placeholder={dt[key]}
  77. onChange={handleChange(key)}
  78. onKeyDown={(e) => e.key === "Enter" && onSave()}
  79. />
  80. </Item>
  81. );
  82. };
  83. return (
  84. <Dialog open={open} onClose={onClose}>
  85. <DialogTitle>{t("Theme Setting")}</DialogTitle>
  86. <DialogContent
  87. sx={{ width: 400, maxHeight: 300, overflow: "auto", pb: 0 }}
  88. >
  89. <List sx={{ pt: 0 }}>
  90. {renderItem("Primary Color", "primary_color")}
  91. {renderItem("Secondary Color", "secondary_color")}
  92. {renderItem("Primary Text", "primary_text")}
  93. {renderItem("Secondary Text", "secondary_text")}
  94. {renderItem("Info Color", "info_color")}
  95. {renderItem("Error Color", "error_color")}
  96. {renderItem("Warning Color", "warning_color")}
  97. {renderItem("Success Color", "success_color")}
  98. <Item>
  99. <ListItemText primary="Font Family" />
  100. <TextField
  101. {...textProps}
  102. value={theme.font_family ?? ""}
  103. onChange={handleChange("font_family")}
  104. onKeyDown={(e) => e.key === "Enter" && onSave()}
  105. />
  106. </Item>
  107. <Item>
  108. <ListItemText primary="CSS Injection" />
  109. <TextField
  110. {...textProps}
  111. value={theme.css_injection ?? ""}
  112. onChange={handleChange("css_injection")}
  113. onKeyDown={(e) => e.key === "Enter" && onSave()}
  114. />
  115. </Item>
  116. </List>
  117. </DialogContent>
  118. <DialogActions>
  119. <Button onClick={onClose}>{t("Cancel")}</Button>
  120. <Button onClick={onSave} variant="contained">
  121. {t("Save")}
  122. </Button>
  123. </DialogActions>
  124. </Dialog>
  125. );
  126. };
  127. export default SettingTheme;