feat: custom accent color via color wheel in settings
Add ColorWheel component (hue/saturation wheel + lightness strip + hex input). themeStore gains accentIdx=-1 for custom mode and setCustom action. ThemeApplier uses getActiveAccent to support both presets and custom hex. Settings page shows "Свой" button; clicking reveals the color wheel inline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,22 +17,50 @@ export const ACCENT_PRESETS: AccentPreset[] = [
|
||||
{ name: 'Минт', accent: '#00FFB2', rgb: '0,255,178' },
|
||||
]
|
||||
|
||||
const STORAGE_KEY = 'pm_accent'
|
||||
function hexToRgbStr(hex: string): string {
|
||||
const h = hex.replace('#', '')
|
||||
const r = parseInt(h.substring(0, 2), 16)
|
||||
const g = parseInt(h.substring(2, 4), 16)
|
||||
const b = parseInt(h.substring(4, 6), 16)
|
||||
return `${r},${g},${b}`
|
||||
}
|
||||
|
||||
const KEY_IDX = 'pm_accent'
|
||||
const KEY_CUSTOM = 'pm_accent_custom'
|
||||
|
||||
interface ThemeStore {
|
||||
accentIdx: number
|
||||
accentIdx: number // -1 = custom color
|
||||
customHex: string
|
||||
setAccent: (idx: number) => void
|
||||
setCustom: (hex: string) => void
|
||||
}
|
||||
|
||||
export const useThemeStore = create<ThemeStore>((set) => ({
|
||||
accentIdx: (() => {
|
||||
if (typeof window === 'undefined') return 0
|
||||
const saved = localStorage.getItem(STORAGE_KEY)
|
||||
const saved = localStorage.getItem(KEY_IDX)
|
||||
const idx = saved !== null ? parseInt(saved, 10) : 0
|
||||
return idx >= 0 && idx < ACCENT_PRESETS.length ? idx : 0
|
||||
return idx >= -1 && idx < ACCENT_PRESETS.length ? idx : 0
|
||||
})(),
|
||||
customHex: (() => {
|
||||
if (typeof window === 'undefined') return '#ff00ff'
|
||||
return localStorage.getItem(KEY_CUSTOM) || '#ff00ff'
|
||||
})(),
|
||||
setAccent: (idx) => {
|
||||
if (typeof window !== 'undefined') localStorage.setItem(STORAGE_KEY, String(idx))
|
||||
if (typeof window !== 'undefined') localStorage.setItem(KEY_IDX, String(idx))
|
||||
set({ accentIdx: idx })
|
||||
},
|
||||
setCustom: (hex) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(KEY_CUSTOM, hex)
|
||||
localStorage.setItem(KEY_IDX, '-1')
|
||||
}
|
||||
set({ accentIdx: -1, customHex: hex })
|
||||
},
|
||||
}))
|
||||
|
||||
export function getActiveAccent(accentIdx: number, customHex: string): { accent: string; rgb: string } {
|
||||
if (accentIdx === -1) return { accent: customHex, rgb: hexToRgbStr(customHex) }
|
||||
const preset = ACCENT_PRESETS[accentIdx] ?? ACCENT_PRESETS[0]
|
||||
return { accent: preset.accent, rgb: preset.rgb }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user