'use client'; import { useRef, useState, useEffect, useCallback } from 'react'; import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react'; import { isLightColor, type CustomThemeColors } from '@/contexts/ThemeContext'; import { PaletteIcon } from '@/components/icons/Icons'; /** * Curated swatch palettes per color role, sourced from existing themes * plus hand-picked pastels and bold tones for variety. */ /* 18 swatches each → 3 clean rows of 6 */ const ROLE_SWATCHES: Record = { background: [ '#111111', '#020617', '#0a0f0c', '#1a0f0f', '#0c1922', '#0f1916', '#ffffff', '#faf8ff', '#fff8f8', '#fdfbf7', '#f6faff', '#e8ecf0', '#fef3c7', '#fce7f3', '#e0e7ff', '#d1fae5', '#f5f3ff', '#fff1f2', ], base: [ '#171717', '#0f172a', '#111a15', '#2c1810', '#102c3d', '#132d27', '#f7fafc', '#f3effb', '#fef1f1', '#f7f2e8', '#edf4fc', '#dde2e8', '#fefce8', '#fdf2f8', '#eef2ff', '#ecfdf5', '#faf5ff', '#fff5f5', ], offbase: [ '#343434', '#1e293b', '#1a2820', '#3d1f14', '#1a3c52', '#1c3d35', '#e2e8f0', '#e4daf0', '#f5dada', '#e8dfc9', '#d5e3f5', '#c8ced6', '#fde68a', '#fbcfe8', '#c7d2fe', '#a7f3d0', '#e9d5ff', '#fecdd3', ], accent: [ '#ef4444', '#f87171', '#38bdf8', '#4ade80', '#ff6b6b', '#06b6d4', '#2dd4bf', '#7c3aed', '#e11d48', '#b45309', '#2563eb', '#e94560', '#f59e0b', '#ec4899', '#8b5cf6', '#10b981', '#f97316', '#5b7a9d', ], secondaryAccent: [ '#ed6868', '#eb6262', '#22d3ee', '#22c55e', '#f59e0b', '#0ea5e9', '#10b981', '#a78bfa', '#f472b6', '#d97706', '#3b82f6', '#f78da7', '#fbbf24', '#f9a8d4', '#34d399', '#fb923c', '#7393b0', '#c084fc', ], foreground: [ '#2d3748', '#ededed', '#e2e8f0', '#d4e8d0', '#ffe4d6', '#e0f2fe', '#dcfce7', '#3b2e5a', '#4a2c2c', '#44392a', '#1e3a5f', '#2c3440', '#1c1917', '#18181b', '#1e293b', '#064e3b', '#4c1d95', '#881337', ], muted: [ '#718096', '#a3a3a3', '#94a3b8', '#7c8f85', '#bc8f8f', '#7ca7c4', '#75a99c', '#8e7bab', '#b08a8a', '#9a8b74', '#6b8db5', '#7a8694', '#d4d4d8', '#a1a1aa', '#78716c', '#6b7280', '#9ca3af', '#64748b', ], }; interface ColorPickerProps { value: string; field: keyof CustomThemeColors; label: string; onChange: (color: string) => void; } export function ColorPicker({ value, field, label, onChange }: ColorPickerProps) { const [hexInput, setHexInput] = useState(value); const nativeRef = useRef(null); useEffect(() => { setHexInput(value); }, [value]); const handleHexCommit = useCallback((raw: string) => { let hex = raw.trim(); if (!hex.startsWith('#')) hex = '#' + hex; if (/^#[0-9a-fA-F]{6}$/.test(hex)) { onChange(hex.toLowerCase()); } else { // revert to current value setHexInput(value); } }, [onChange, value]); const swatches = ROLE_SWATCHES[field]; return (
{/* Label */}
{label} {/* Eyedropper / native picker */}
onChange(e.target.value)} className="absolute inset-0 w-full h-full opacity-0 pointer-events-none" tabIndex={-1} aria-hidden />
{/* Swatch grid */}
{swatches.map((color) => { const selected = color.toLowerCase() === value.toLowerCase(); return ( ); })}
{/* Hex input */}
setHexInput(e.target.value)} onBlur={(e) => handleHexCommit(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') handleHexCommit(hexInput); }} spellCheck={false} maxLength={7} className="flex-1 rounded-lg px-2 py-1 text-xs font-mono border border-offbase bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-accent" />
); }