mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-02-03 03:10:50 +08:00
- Use unique ID prefix for clipPath to avoid duplicate ID issues - Add cleanup function to initializeTheme to prevent memory leak - Change tooltip to show action description instead of current theme name
87 lines
2.3 KiB
TypeScript
87 lines
2.3 KiB
TypeScript
/**
|
|
* 主题状态管理
|
|
* 从原项目 src/modules/theme.js 迁移
|
|
*/
|
|
|
|
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
import type { Theme } from '@/types';
|
|
import { STORAGE_KEY_THEME } from '@/utils/constants';
|
|
|
|
type ResolvedTheme = 'light' | 'dark';
|
|
|
|
interface ThemeState {
|
|
theme: Theme;
|
|
resolvedTheme: ResolvedTheme;
|
|
setTheme: (theme: Theme) => void;
|
|
cycleTheme: () => void;
|
|
initializeTheme: () => () => void;
|
|
}
|
|
|
|
const getSystemTheme = (): ResolvedTheme => {
|
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
return 'dark';
|
|
}
|
|
return 'light';
|
|
};
|
|
|
|
const applyTheme = (resolved: ResolvedTheme) => {
|
|
if (resolved === 'dark') {
|
|
document.documentElement.setAttribute('data-theme', 'dark');
|
|
} else {
|
|
document.documentElement.removeAttribute('data-theme');
|
|
}
|
|
};
|
|
|
|
export const useThemeStore = create<ThemeState>()(
|
|
persist(
|
|
(set, get) => ({
|
|
theme: 'auto',
|
|
resolvedTheme: 'light',
|
|
|
|
setTheme: (theme) => {
|
|
const resolved: ResolvedTheme = theme === 'auto' ? getSystemTheme() : theme;
|
|
applyTheme(resolved);
|
|
set({ theme, resolvedTheme: resolved });
|
|
},
|
|
|
|
cycleTheme: () => {
|
|
const { theme, setTheme } = get();
|
|
const order: Theme[] = ['light', 'dark', 'auto'];
|
|
const currentIndex = order.indexOf(theme);
|
|
const nextTheme = order[(currentIndex + 1) % order.length];
|
|
setTheme(nextTheme);
|
|
},
|
|
|
|
initializeTheme: () => {
|
|
const { theme, setTheme } = get();
|
|
|
|
// 应用已保存的主题
|
|
setTheme(theme);
|
|
|
|
// 监听系统主题变化(仅在 auto 模式下生效)
|
|
if (!window.matchMedia) {
|
|
return () => {};
|
|
}
|
|
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
const listener = () => {
|
|
const { theme: currentTheme } = get();
|
|
if (currentTheme === 'auto') {
|
|
const resolved = getSystemTheme();
|
|
applyTheme(resolved);
|
|
set({ resolvedTheme: resolved });
|
|
}
|
|
};
|
|
|
|
mediaQuery.addEventListener('change', listener);
|
|
|
|
return () => mediaQuery.removeEventListener('change', listener);
|
|
},
|
|
}),
|
|
{
|
|
name: STORAGE_KEY_THEME,
|
|
}
|
|
)
|
|
);
|