Spaces:
Runtime error
Runtime error
| "use client"; | |
| import { create } from "zustand"; | |
| import { persist } from "zustand/middleware"; | |
| import { THEME_CONFIG } from "@/shared/constants/appConfig"; | |
| interface ThemeState { | |
| theme: string; | |
| colorTheme: string; | |
| customColor: string; | |
| setTheme: (theme: string) => void; | |
| setColorTheme: (colorTheme: string) => void; | |
| setCustomColorTheme: (color: string) => void; | |
| toggleTheme: () => void; | |
| initTheme: () => void; | |
| } | |
| const useThemeStore = create<ThemeState>()( | |
| persist( | |
| (set, get) => ({ | |
| theme: THEME_CONFIG.defaultTheme, | |
| colorTheme: "coral", | |
| customColor: "#3b82f6", | |
| setTheme: (theme) => { | |
| set({ theme }); | |
| applyTheme(theme); | |
| }, | |
| setColorTheme: (colorTheme) => { | |
| set({ colorTheme }); | |
| applyColorTheme(colorTheme, get().customColor); | |
| }, | |
| setCustomColorTheme: (color) => { | |
| const normalized = normalizeHexColor(color); | |
| set({ colorTheme: "custom", customColor: normalized }); | |
| applyColorTheme("custom", normalized); | |
| }, | |
| toggleTheme: () => { | |
| const currentTheme = get().theme; | |
| const newTheme = currentTheme === "dark" ? "light" : "dark"; | |
| set({ theme: newTheme }); | |
| applyTheme(newTheme); | |
| }, | |
| initTheme: () => { | |
| const { theme, colorTheme, customColor } = get(); | |
| applyTheme(theme); | |
| applyColorTheme(colorTheme, customColor); | |
| }, | |
| }), | |
| { | |
| name: THEME_CONFIG.storageKey, | |
| } | |
| ) | |
| ); | |
| export const COLOR_THEMES: Record<string, string> = { | |
| coral: "#e54d5e", | |
| blue: "#3b82f6", | |
| red: "#ef4444", | |
| green: "#22c55e", | |
| violet: "#8b5cf6", | |
| orange: "#f97316", | |
| cyan: "#06b6d4", | |
| }; | |
| // Apply light/dark theme to document | |
| function applyTheme(theme: string) { | |
| if (typeof window === "undefined") return; | |
| const root = document.documentElement; | |
| const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; | |
| const effectiveTheme = theme === "system" ? systemTheme : theme; | |
| if (effectiveTheme === "dark") { | |
| root.classList.add("dark"); | |
| } else { | |
| root.classList.remove("dark"); | |
| } | |
| } | |
| function applyColorTheme(colorTheme: string, customColor: string) { | |
| if (typeof window === "undefined") return; | |
| const root = document.documentElement; | |
| const baseColor = | |
| colorTheme === "custom" | |
| ? normalizeHexColor(customColor) | |
| : COLOR_THEMES[colorTheme] || COLOR_THEMES.coral; | |
| const hoverColor = shadeHexColor(baseColor, -0.14); | |
| root.style.setProperty("--color-primary", baseColor); | |
| root.style.setProperty("--color-primary-hover", hoverColor); | |
| } | |
| function normalizeHexColor(color: string) { | |
| const value = (color || "").trim(); | |
| const hex = value.startsWith("#") ? value : `#${value}`; | |
| const valid = /^#([0-9a-fA-F]{6})$/.test(hex); | |
| return valid ? hex.toLowerCase() : "#3b82f6"; | |
| } | |
| function shadeHexColor(hex: string, percent: number) { | |
| const normalized = normalizeHexColor(hex).slice(1); | |
| const r = parseInt(normalized.slice(0, 2), 16); | |
| const g = parseInt(normalized.slice(2, 4), 16); | |
| const b = parseInt(normalized.slice(4, 6), 16); | |
| const shade = (channel: number) => { | |
| const target = percent < 0 ? 0 : 255; | |
| const amount = Math.round((target - channel) * Math.abs(percent)); | |
| const next = percent < 0 ? channel - amount : channel + amount; | |
| return Math.max(0, Math.min(255, next)); | |
| }; | |
| const toHex = (channel: number) => channel.toString(16).padStart(2, "0"); | |
| return `#${toHex(shade(r))}${toHex(shade(g))}${toHex(shade(b))}`; | |
| } | |
| export default useThemeStore; | |