import { create } from 'zustand'; import { persist } from 'zustand/middleware'; /** * رنگ‌های ثابت (hex) برای گزینه‌هایی که باید دقیقاً یک رنگ مشخص باشند و نه * محاسبه‌شده از hue (فرمول oklch با L ثابت نمی‌تواند رنگ روشن دقیق بدهد). */ export interface BrandFixed { primary: string; p600: string; p700: string; soft: string; soft2: string; softDark: string; soft2Dark: string; ring: string; } export interface BrandHue { name: string; h: number; c: number; fixed?: BrandFixed; } export const HUES: BrandHue[] = [ { name: 'بنفش', h: 277, c: 0.177 }, { name: 'آبی پزشکی', h: 256, c: 0.15 }, { name: 'فیروزه‌ای', h: 205, c: 0.12 }, { name: 'سبز سلامت', h: 162, c: 0.13 }, { name: 'نیلی', h: 272, c: 0.16 }, { name: 'نارنجی', h: 45, c: 0.16, // رنگ اختصاصی rgb(241,119,50) — با انتخابش رنگ اصلی کل سایت دقیقاً همین می‌شود fixed: { primary: 'rgb(241, 119, 50)', p600: '#db5a1f', p700: '#b74d16', soft: '#fdeee4', soft2: '#fbd9c4', softDark: '#3a2b23', soft2Dark: '#4a3527', ring: 'rgba(241, 119, 50, 0.32)', }, }, ]; interface UiState { sidebarOpen: boolean; darkMode: boolean; density: 'comfortable' | 'compact'; brandHue: BrandHue; settingsPanelOpen: boolean; toggleSidebar: () => void; setSidebarOpen: (open: boolean) => void; toggleDarkMode: () => void; setDensity: (density: 'comfortable' | 'compact') => void; setBrandHue: (hue: BrandHue) => void; toggleSettingsPanel: () => void; } const FIXED_VAR_KEYS = [ '--primary', '--primary-600', '--primary-700', '--primary-soft', '--primary-soft2', '--ring', ]; /** * اعمال رنگ برند روی ریشه. اگر hue رنگ ثابت داشته باشد، مقادیر hex را مستقیماً * inline می‌گذارد (روی oklch/base غالب می‌شود)؛ وگرنه override را پاک می‌کند تا * فرمول hue‌محورِ stylesheet دوباره فعال شود. hue/chroma همیشه ست می‌شوند. */ export function applyBrand(root: HTMLElement, brandHue: BrandHue, darkMode: boolean) { root.style.setProperty('--brand-h', String(brandHue.h)); root.style.setProperty('--brand-c', String(brandHue.c)); const f = brandHue.fixed; if (f) { root.style.setProperty('--primary', f.primary); root.style.setProperty('--primary-600', f.p600); root.style.setProperty('--primary-700', f.p700); root.style.setProperty('--primary-soft', darkMode ? f.softDark : f.soft); root.style.setProperty('--primary-soft2', darkMode ? f.soft2Dark : f.soft2); root.style.setProperty('--ring', f.ring); } else { FIXED_VAR_KEYS.forEach((k) => root.style.removeProperty(k)); } } function applyTheme(state: Pick) { const root = document.documentElement; root.classList.toggle('dark', state.darkMode); root.setAttribute('data-theme', state.darkMode ? 'dark' : 'light'); root.setAttribute('data-density', state.density); applyBrand(root, state.brandHue, state.darkMode); } export const useUiStore = create()( persist( (set, get) => ({ sidebarOpen: true, darkMode: false, density: 'comfortable', brandHue: HUES[0], settingsPanelOpen: false, toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })), setSidebarOpen: (open) => set({ sidebarOpen: open }), toggleDarkMode: () => { const next = !get().darkMode; set({ darkMode: next }); applyTheme({ ...get(), darkMode: next }); }, setDensity: (density) => { set({ density }); applyTheme({ ...get(), density }); }, setBrandHue: (brandHue) => { set({ brandHue }); applyTheme({ ...get(), brandHue }); }, toggleSettingsPanel: () => set((s) => ({ settingsPanelOpen: !s.settingsPanelOpen })), }), { name: 'clinicpro-ui', onRehydrateStorage: () => (state) => { if (state) { applyTheme({ darkMode: state.darkMode ?? false, density: state.density ?? 'comfortable', brandHue: state.brandHue ?? HUES[0], }); } }, }, ), );