支持 6 套预设主题(默认、海洋、森林、日落、玫瑰、紫罗兰)和自定义主色色相滑块, 通过动态注入 CSS 变量实现主题切换,使用 localStorage 持久化存储, 添加 SSR 初始化脚本避免首次加载颜色闪烁。 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist, createJSONStorage } from 'zustand/middleware';
|
|
|
|
import type { UIStore } from './types';
|
|
|
|
import { STORAGE_KEYS } from '@/config/constants';
|
|
import {
|
|
DEFAULT_THEME_CONFIG,
|
|
getHueFromConfig,
|
|
getPresetById,
|
|
} from '@/config/theme-presets';
|
|
import { applyTheme } from '@/lib/theme-service';
|
|
|
|
export const useUIStore = create<UIStore>()(
|
|
persist(
|
|
(set, get) => ({
|
|
theme: 'system',
|
|
themeConfig: DEFAULT_THEME_CONFIG,
|
|
sidebarOpen: false, // 移动端侧边栏默认关闭
|
|
sidebarCollapsed: false,
|
|
|
|
setTheme: (theme) => set({ theme }),
|
|
|
|
setThemeConfig: (config) => {
|
|
set({ themeConfig: config });
|
|
// 应用主题
|
|
const hue = getHueFromConfig(config);
|
|
applyTheme(hue);
|
|
},
|
|
|
|
applyPreset: (presetId) => {
|
|
// 验证预设是否存在
|
|
const preset = getPresetById(presetId);
|
|
if (!preset) {
|
|
console.warn(`Theme preset "${presetId}" not found, using default`);
|
|
presetId = 'default';
|
|
}
|
|
get().setThemeConfig({ type: 'preset', presetId });
|
|
},
|
|
|
|
applyCustomPrimaryHue: (hue) => {
|
|
// 确保色相在有效范围内
|
|
const normalizedHue = ((hue % 360) + 360) % 360;
|
|
get().setThemeConfig({ type: 'custom', primaryHue: normalizedHue });
|
|
},
|
|
|
|
toggleSidebar: () =>
|
|
set((state) => ({ sidebarOpen: !state.sidebarOpen })),
|
|
|
|
setSidebarOpen: (open) => set({ sidebarOpen: open }),
|
|
|
|
setSidebarCollapsed: (collapsed) =>
|
|
set({ sidebarCollapsed: collapsed }),
|
|
}),
|
|
{
|
|
name: STORAGE_KEYS.UI,
|
|
storage: createJSONStorage(() => localStorage),
|
|
partialize: (state) => ({
|
|
theme: state.theme,
|
|
themeConfig: state.themeConfig,
|
|
sidebarCollapsed: state.sidebarCollapsed,
|
|
}),
|
|
// hydration 完成后应用主题
|
|
onRehydrateStorage: () => (state) => {
|
|
if (state?.themeConfig) {
|
|
const hue = getHueFromConfig(state.themeConfig);
|
|
applyTheme(hue);
|
|
}
|
|
},
|
|
}
|
|
)
|
|
);
|
|
|
|
// Selector hooks
|
|
export const useTheme = () => useUIStore((state) => state.theme);
|
|
export const useThemeConfig = () => useUIStore((state) => state.themeConfig);
|
|
export const useSidebarOpen = () => useUIStore((state) => state.sidebarOpen);
|
|
export const useSidebarCollapsed = () =>
|
|
useUIStore((state) => state.sidebarCollapsed);
|