fixed theme viewer

This commit is contained in:
coolcoala
2025-07-09 04:49:05 +03:00
parent 149bdd5175
commit d5266fa003
3 changed files with 58 additions and 40 deletions

View File

@@ -86,6 +86,7 @@
"peggy": "^5.0.3", "peggy": "^5.0.3",
"react": "19.1.0", "react": "19.1.0",
"react-chartjs-2": "^5.3.0", "react-chartjs-2": "^5.3.0",
"react-colorful": "^5.6.1",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-error-boundary": "6.0.0", "react-error-boundary": "6.0.0",
"react-hook-form": "^7.57.0", "react-hook-form": "^7.57.0",

14
pnpm-lock.yaml generated
View File

@@ -191,6 +191,9 @@ importers:
react-chartjs-2: react-chartjs-2:
specifier: ^5.3.0 specifier: ^5.3.0
version: 5.3.0(chart.js@4.5.0)(react@19.1.0) version: 5.3.0(chart.js@4.5.0)(react@19.1.0)
react-colorful:
specifier: ^5.6.1
version: 5.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-dom: react-dom:
specifier: 19.1.0 specifier: 19.1.0
version: 19.1.0(react@19.1.0) version: 19.1.0(react@19.1.0)
@@ -3329,6 +3332,12 @@ packages:
chart.js: ^4.1.1 chart.js: ^4.1.1
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-colorful@5.6.1:
resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
react-dom@19.1.0: react-dom@19.1.0:
resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
peerDependencies: peerDependencies:
@@ -7031,6 +7040,11 @@ snapshots:
chart.js: 4.5.0 chart.js: 4.5.0
react: 19.1.0 react: 19.1.0
react-colorful@5.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
react-dom@19.1.0(react@19.1.0): react-dom@19.1.0(react@19.1.0):
dependencies: dependencies:
react: 19.1.0 react: 19.1.0

View File

@@ -1,9 +1,7 @@
import { forwardRef, useImperativeHandle, useState, useMemo } from "react"; import { forwardRef, useImperativeHandle, useState, useEffect, useCallback } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material/styles"; // Оставляем для получения дефолтных цветов темы
// Новые импорты
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme"; import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
import { DialogRef } from "@/components/base"; import { DialogRef } from "@/components/base";
@@ -14,54 +12,63 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogC
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Edit } from "lucide-react"; import { Edit } from "lucide-react";
import { useThemeMode } from "@/services/states"; // Наш хук для получения текущего режима
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import {HexColorPicker} from "react-colorful";
interface Props {} interface Props {}
// Дочерний компонент для одной строки настройки цвета
const ColorSettingRow = ({ label, value, placeholder, onChange }: { const ColorSettingRow = ({ label, value, placeholder, onChange }: {
label: string; label: string;
value: string; value: string;
placeholder: string; placeholder: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void; onChange: (e: { target: { value: string } }) => void; // Адаптируем тип для совместимости
}) => ( }) => {
<div className="flex items-center justify-between"> const color = value || placeholder;
<Label>{label}</Label>
<div className="flex items-center gap-2"> return (
{/* --- НАЧАЛО ИЗМЕНЕНИЙ --- */} <div className="flex items-center justify-between">
{/* Этот контейнер теперь позиционирован, чтобы спрятать input внутри */} <Label>{label}</Label>
<div className="relative h-6 w-6 cursor-pointer"> <div className="flex items-center gap-2">
{/* Видимый образец цвета */} <Popover>
<div <PopoverTrigger asChild>
className="h-full w-full rounded-full border" <Button
style={{ backgroundColor: value || placeholder }} variant="outline"
/> className="h-8 w-8 p-0 border-2"
{/* Невидимый input, который и открывает палитру */} style={{ backgroundColor: color }}
aria-label={`Choose ${label}`}
/>
</PopoverTrigger>
<PopoverContent className="w-auto p-0 border-0">
<HexColorPicker color={color} onChange={(newColor) => onChange({ target: { value: newColor } })} />
</PopoverContent>
</Popover>
<Input <Input
type="color" className="w-32 h-8 font-mono text-sm"
className="absolute inset-0 h-full w-full cursor-pointer opacity-0" value={value ?? ""}
value={value || placeholder} placeholder={placeholder}
onChange={onChange} onChange={onChange}
/> />
</div> </div>
{/* --- КОНЕЦ ИЗМЕНЕНИЙ --- */}
<Input
className="w-32 h-8 font-mono text-sm"
value={value ?? ""}
placeholder={placeholder}
onChange={onChange}
/>
</div> </div>
</div> );
); };
export const ThemeViewer = forwardRef<DialogRef>((props, ref) => { export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [editorOpen, setEditorOpen] = useState(false); const [editorOpen, setEditorOpen] = useState(false);
const { verge, patchVerge } = useVerge(); const { verge, patchVerge, mutateVerge } = useVerge();
const { theme_setting } = verge ?? {}; const { theme_setting } = verge ?? {};
const [theme, setTheme] = useState(theme_setting || {}); const [theme, setTheme] = useState(theme_setting || {});
const mode = useThemeMode();
const resolvedMode = mode === 'system'
? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light")
: mode;
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
open: () => { open: () => {
setOpen(true); setOpen(true);
@@ -70,32 +77,30 @@ export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
close: () => setOpen(false), close: () => setOpen(false),
})); }));
const handleChange = (field: keyof typeof theme) => (e: any) => { const handleChange = (field: keyof typeof theme) => (e: { target: { value: string } }) => {
setTheme((t) => ({ ...t, [field]: e.target.value })); setTheme((t) => ({ ...t, [field]: e.target.value }));
}; };
const onSave = useLockFn(async () => { const onSave = useLockFn(async () => {
try { try {
await patchVerge({ theme_setting: theme }); await patchVerge({ theme_setting: theme });
await mutateVerge();
setOpen(false); setOpen(false);
showNotice("success", t("Saved Successfully, please restart the app to take effect")); showNotice("success", t("Theme updated successfully"));
} catch (err: any) { } catch (err: any) {
showNotice("error", err.toString()); showNotice("error", err.toString());
} }
}); });
const muiTheme = useTheme();
const dt = muiTheme.palette.mode === "light" ? defaultTheme : defaultDarkTheme; const dt = resolvedMode === "light" ? defaultTheme : defaultDarkTheme;
type ThemeKey = keyof typeof theme & keyof typeof defaultTheme; type ThemeKey = keyof typeof theme & keyof typeof defaultTheme;
const renderItem = (label: string, key: ThemeKey) => { const renderItem = (label: string, key: ThemeKey) => {
return ( return (
<ColorSettingRow <ColorSettingRow
label={label} label={label}
// --- НАЧАЛО ИСПРАВЛЕНИЯ ---
// Добавляем `?? ''` чтобы value всегда был строкой
value={theme[key] ?? ""} value={theme[key] ?? ""}
// --- КОНЕЦ ИСПРАВЛЕНИЯ ---
placeholder={dt[key]} placeholder={dt[key]}
onChange={handleChange(key)} onChange={handleChange(key)}
/> />
@@ -124,9 +129,7 @@ export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
<Label>{t("Font Family")}</Label> <Label>{t("Font Family")}</Label>
<Input <Input
className="w-48 h-8" className="w-48 h-8"
// --- НАЧАЛО ИСПРАВЛЕНИЯ ---
value={theme.font_family ?? ""} value={theme.font_family ?? ""}
// --- КОНЕЦ ИСПРАВЛЕНИЯ ---
onChange={handleChange("font_family")} onChange={handleChange("font_family")}
/> />
</div> </div>