/** * GlobalColorPicker — color picker with global token popover * * Wraps the standard color-picker + hex-input pattern. * A brand-colored Globe icon inside the hex input opens a popover * listing all global color tokens. Selecting one sets the value * and shows the global name in the field. * Users can still click the color swatch to pick a custom color. * * Works for both Free and Pro users: * - Free: standard picker, no globe icon (globals not available) * - Pro: picker + globe popover + global name display */ import { useState } from 'react' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Globe, Check } from 'lucide-react' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' import { usePro } from '@/contexts/pro-context' import { GlobalColor } from '@/features/globals/config' interface GlobalColorPickerProps { /** Label shown above the picker */ label: string /** Current color value */ value: string /** Called when the user picks a new colour */ onChange: (value: string) => void /** Which global token this maps to (e.g. 'primary', 'text', 'background') */ globalTokenId?: string /** The feature's original hardcoded default (used to detect "unchanged") */ featureDefault?: string /** Span full width (sm:col-span-2) */ fullWidth?: boolean } /** * Get the global styles from the window data (passed from PHP). */ function getGlobalColors(): GlobalColor[] { const data = (window as any).swiftCommerceData return data?.globalStyles?.colors ?? [] } /** * Find a global color token by value (case-insensitive hex match). */ function findGlobalByValue(hex: string): GlobalColor | undefined { if (!hex) return undefined return getGlobalColors().find( (c: GlobalColor) => c.value.toLowerCase() === hex.toLowerCase(), ) } /** * Find a global color token by ID. */ function findGlobalById(id: string): GlobalColor | undefined { return getGlobalColors().find((c: GlobalColor) => c.id === id) } /** * Resolve a feature color exactly as the Pro storefront resolver does. * Default feature values inherit their mapped Global Style token, while an * explicitly customized value remains local to the feature. */ export function resolveGlobalColor( value: string, globalTokenId: string, featureDefault: string, ): string { const mappedToken = findGlobalById(globalTokenId) if (!mappedToken) return value || featureDefault const normalizedValue = value.toLowerCase() if (!value || normalizedValue === featureDefault.toLowerCase()) { return mappedToken.value } return value } export function GlobalColorPicker({ label, value, onChange, globalTokenId, featureDefault, fullWidth, }: GlobalColorPickerProps) { const { isPro } = usePro() const [popoverOpen, setPopoverOpen] = useState(false) const [editingCustomColor, setEditingCustomColor] = useState(false) const globalColors = getGlobalColors() const hasGlobals = isPro && globalColors.length > 0 // Resolve which global token is currently active const mappedToken = globalTokenId ? findGlobalById(globalTokenId) : undefined const matchedByValue = value ? findGlobalByValue(value) : undefined // Determine if the current value matches a global token const activeGlobal = editingCustomColor ? undefined : matchedByValue || (mappedToken && (!value || value.toLowerCase() === featureDefault?.toLowerCase() || value.toLowerCase() === mappedToken.value.toLowerCase()) ? mappedToken : undefined) const handleSelectGlobal = (color: GlobalColor) => { onChange(color.value) setEditingCustomColor(false) setPopoverOpen(false) } const handleHexChange = (newValue: string) => { setEditingCustomColor(true) onChange(newValue) } return (
{/* Color swatch picker */} { setEditingCustomColor(true) onChange(e.target.value) }} className="w-9 h-9 p-0 cursor-pointer shrink-0 rounded-lg" /> {/* Hex input with optional Globe button */}
{activeGlobal ? ( // Show global name as a styled read-only display
{activeGlobal.name} {activeGlobal.value}
) : ( handleHexChange(e.target.value)} placeholder={mappedToken?.value || '#000000'} className="h-9 pr-9" /> )} {/* Globe icon — opens popover */} {hasGlobals && (

Global Colors

{globalColors.map((gc: GlobalColor) => { const isActive = activeGlobal?.id === gc.id return ( ) })} {/* Clear / use custom option */} {activeGlobal && ( <>
)}
)}
) }