From f9a96ff91476cf3909bd1ac848dc82968f10e34d Mon Sep 17 00:00:00 2001 From: GyDi Date: Thu, 31 Mar 2022 23:38:00 +0800 Subject: [PATCH] feat: support theme setting --- src/components/layout/use-custom-theme.ts | 115 +++++++----- src/components/setting/setting-theme.tsx | 210 ++++++++++++++++++++++ src/components/setting/setting-verge.tsx | 17 ++ src/pages/_theme.tsx | 12 ++ src/services/types.ts | 12 ++ 5 files changed, 317 insertions(+), 49 deletions(-) create mode 100644 src/components/setting/setting-theme.tsx create mode 100644 src/pages/_theme.tsx diff --git a/src/components/layout/use-custom-theme.ts b/src/components/layout/use-custom-theme.ts index fc72a5e..de936a9 100644 --- a/src/components/layout/use-custom-theme.ts +++ b/src/components/layout/use-custom-theme.ts @@ -1,49 +1,66 @@ -import useSWR from "swr"; -import { useMemo } from "react"; -import { createTheme } from "@mui/material"; -import { getVergeConfig } from "../../services/cmds"; - -/** - * wip: custome theme - */ -export default function useCustomTheme() { - const { data } = useSWR("getVergeConfig", getVergeConfig); - const mode = data?.theme_mode ?? "light"; - - const theme = useMemo(() => { - // const background = mode === "light" ? "#f5f5f5" : "#000"; - const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5"; - - const rootEle = document.documentElement; - rootEle.style.background = "transparent"; - rootEle.style.setProperty("--selection-color", selectColor); - - const theme = createTheme({ - breakpoints: { - values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 }, - }, - palette: { - mode, - primary: { main: "#5b5c9d" }, - text: { primary: "#637381", secondary: "#909399" }, - }, - }); - - const { palette } = theme; - - setTimeout(() => { - const dom = document.querySelector("#Gradient2"); - if (dom) { - dom.innerHTML = ` - - - - `; - } - }, 0); - - return theme; - }, [mode]); - - return { theme }; -} +import useSWR from "swr"; +import { useMemo } from "react"; +import { createTheme } from "@mui/material"; +import { getVergeConfig } from "../../services/cmds"; +import { defaultTheme as dt } from "../../pages/_theme"; + +/** + * custome theme + */ +export default function useCustomTheme() { + const { data } = useSWR("getVergeConfig", getVergeConfig); + const { theme_mode, theme_setting } = data ?? {}; + + const theme = useMemo(() => { + const mode = theme_mode ?? "light"; + // const background = mode === "light" ? "#f5f5f5" : "#000"; + const selectColor = mode === "light" ? "#f5f5f5" : "#d5d5d5"; + + const rootEle = document.documentElement; + rootEle.style.background = "transparent"; + rootEle.style.setProperty("--selection-color", selectColor); + + const setting = theme_setting || {}; + + const theme = createTheme({ + breakpoints: { + values: { xs: 0, sm: 650, md: 900, lg: 1200, xl: 1536 }, + }, + palette: { + mode, + primary: { main: setting.primary_color || dt.primary_color }, + secondary: { main: setting.secondary_color || dt.secondary_color }, + info: { main: setting.info_color || dt.info_color }, + error: { main: setting.error_color || dt.error_color }, + warning: { main: setting.warning_color || dt.warning_color }, + success: { main: setting.success_color || dt.success_color }, + text: { + primary: setting.primary_text || dt.primary_text, + secondary: setting.secondary_text || dt.secondary_text, + }, + }, + typography: { + fontFamily: setting.font_family + ? `"${setting.font_family}", ${dt.font_family}` + : dt.font_family, + }, + }); + + const { palette } = theme; + + setTimeout(() => { + const dom = document.querySelector("#Gradient2"); + if (dom) { + dom.innerHTML = ` + + + + `; + } + }, 0); + + return theme; + }, [theme_mode, theme_setting]); + + return { theme }; +} diff --git a/src/components/setting/setting-theme.tsx b/src/components/setting/setting-theme.tsx new file mode 100644 index 0000000..106896e --- /dev/null +++ b/src/components/setting/setting-theme.tsx @@ -0,0 +1,210 @@ +import useSWR from "swr"; +import { useEffect, useState } from "react"; +import { useLockFn } from "ahooks"; +import { useTranslation } from "react-i18next"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + List, + ListItem, + ListItemText, + styled, + TextField, +} from "@mui/material"; +import { getVergeConfig, patchVergeConfig } from "../../services/cmds"; +import { defaultTheme } from "../../pages/_theme"; + +interface Props { + open: boolean; + onClose: () => void; + onError?: (err: Error) => void; +} + +const Item = styled(ListItem)(() => ({ + padding: "5px 2px", +})); + +const Round = styled("div")(() => ({ + width: "24px", + height: "24px", + borderRadius: "18px", + display: "inline-block", + marginRight: "8px", +})); + +const SettingTheme = (props: Props) => { + const { open, onClose, onError } = props; + + const { t } = useTranslation(); + const { data: vergeConfig, mutate } = useSWR( + "getVergeConfig", + getVergeConfig + ); + + const { theme_setting } = vergeConfig ?? {}; + const [theme, setTheme] = useState(theme_setting || {}); + + useEffect(() => { + setTheme({ ...theme_setting } || {}); + }, [theme_setting]); + + const textProps = { + size: "small", + autoComplete: "off", + sx: { width: 135 }, + } as const; + + const handleChange = (field: keyof typeof theme) => (e: any) => { + setTheme((t) => ({ ...t, [field]: e.target.value })); + }; + + const onSave = useLockFn(async () => { + try { + await patchVergeConfig({ theme_setting: theme }); + mutate(); + } catch (err: any) { + onError?.(err); + } + }); + + return ( + + {t("Theme Setting")} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default SettingTheme; diff --git a/src/components/setting/setting-verge.tsx b/src/components/setting/setting-verge.tsx index 19f19cd..2241b96 100644 --- a/src/components/setting/setting-verge.tsx +++ b/src/components/setting/setting-verge.tsx @@ -1,4 +1,5 @@ import useSWR, { useSWRConfig } from "swr"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; import { IconButton, @@ -20,6 +21,7 @@ import { CmdType } from "../../services/types"; import { version } from "../../../package.json"; import PaletteSwitch from "./palette-switch"; import GuardState from "./guard-state"; +import SettingTheme from "./setting-theme"; interface Props { onError?: (err: Error) => void; @@ -32,6 +34,8 @@ const SettingVerge = ({ onError }: Props) => { const { theme_mode, theme_blur, traffic_graph, language } = vergeConfig ?? {}; + const [themeOpen, setThemeOpen] = useState(false); + const onSwitchFormat = (_e: any, value: boolean) => value; const onChangeData = (patch: Partial) => { mutate("getVergeConfig", { ...vergeConfig, ...patch }, false); @@ -99,6 +103,17 @@ const SettingVerge = ({ onError }: Props) => { + + + setThemeOpen(true)} + > + + + + @@ -117,6 +132,8 @@ const SettingVerge = ({ onError }: Props) => { v{version} + + setThemeOpen(false)} /> ); }; diff --git a/src/pages/_theme.tsx b/src/pages/_theme.tsx new file mode 100644 index 0000000..4b92a03 --- /dev/null +++ b/src/pages/_theme.tsx @@ -0,0 +1,12 @@ +// default theme setting +export const defaultTheme = { + primary_color: "#5b5c9d", + secondary_color: "#9c27b0", + primary_text: "#637381", + secondary_text: "#909399", + info_color: "#0288d1", + error_color: "#d32f2f", + warning_color: "#ed6c02", + success_color: "#2e7d32", + font_family: `"Roboto", "Helvetica", "Arial", sans-serif`, +}; diff --git a/src/services/types.ts b/src/services/types.ts index 37e2e14..b098c0f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -130,6 +130,18 @@ export namespace CmdType { enable_system_proxy?: boolean; enable_proxy_guard?: boolean; system_proxy_bypass?: string; + theme_setting?: { + primary_color?: string; + secondary_color?: string; + primary_text?: string; + secondary_text?: string; + info_color?: string; + error_color?: string; + warning_color?: string; + success_color?: string; + font_face?: string; + font_family?: string; + }; } type ClashConfigValue = any;