import { useState, useRef, useCallback, useEffect } from 'react' import { isValidCssColor, toHexColor } from '../utils/colorUtils' /** * Inline color swatch button that opens a native color picker. * Shows nothing if the value is not a valid CSS color. */ export function ColorSwatch({ color, onChange }: { color: string onChange?: (hex: string) => void }) { const hex = toHexColor(color) ?? '#000000' const inputRef = useRef(null) const handleClick = useCallback(() => { inputRef.current?.click() }, []) const handleChange = useCallback((e: React.ChangeEvent) => { onChange?.(e.target.value) }, [onChange]) const handleKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() inputRef.current?.click() } }, []) return ( ) } /** * Editable text field with an inline color swatch. * The swatch only appears when the value is a valid CSS color. */ interface ColorEditableValueProps { value: string isEditing: boolean onStartEdit: () => void onSave: (newValue: string) => void onCancel: () => void } export function ColorEditableValue({ value, isEditing, onStartEdit, onSave, onCancel }: ColorEditableValueProps) { const [editValue, setEditValue] = useState(value) const showSwatch = isValidCssColor(isEditing ? editValue : value) const handlePickerChange = useCallback((hex: string) => { if (isEditing) setEditValue(hex) onSave(hex) }, [isEditing, onSave]) if (isEditing) { return ( ) } return ( ) } function EditableColorValue({ editValue, onCancel, onChange, onPickerChange, onSave, showSwatch, value, }: { editValue: string onCancel: () => void onChange: (value: string) => void onPickerChange: (hex: string) => void onSave: (newValue: string) => void showSwatch: boolean value: string }) { const textInputRef = useRef(null) const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') onSave(editValue) else if (e.key === 'Escape') { onChange(value) onCancel() } } useEffect(() => { textInputRef.current?.focus() }, []) return ( {showSwatch && } onChange(e.target.value)} onKeyDown={handleKeyDown} onBlur={() => onSave(editValue)} data-testid="color-text-input" /> ) } function ReadonlyColorValue({ onPickerChange, onStartEdit, showSwatch, value, }: { onPickerChange: (hex: string) => void onStartEdit: () => void showSwatch: boolean value: string }) { return ( {showSwatch && } ) }