diff --git a/src/components/lab/ColorPicker/ColorPicker.tsx b/src/components/lab/ColorPicker/ColorPicker.tsx index 96cf30c069..f4a31e63a4 100644 --- a/src/components/lab/ColorPicker/ColorPicker.tsx +++ b/src/components/lab/ColorPicker/ColorPicker.tsx @@ -1,16 +1,17 @@ import * as React from 'react'; import type {HsvaColor} from '@uiw/react-color'; -import {Alpha, Hue, Saturation, hsvaToHex, hsvaToHexa} from '@uiw/react-color'; +import {Alpha, Hue, Saturation, hsvaToHex, hsvaToHexa, validHex} from '@uiw/react-color'; import {useControlledState} from '../../../hooks/useControlledState'; import {Popup} from '../../Popup'; +import type {PopupPlacement} from '../../Popup'; import {Select} from '../../Select'; import {ColorDisplay, ColorPointer, HexInput, RgbInputs} from './components'; import {DEFAULT_COLOR, b} from './constants'; import {Modes} from './types'; -import {convertSelectedModeColorToHsva, getTextValueByMode} from './utils'; +import {convertSelectedModeColorToHsva, getTextValueByMode, isValidHsva} from './utils'; export interface ColorPickerProps { /* @@ -51,6 +52,20 @@ export interface ColorPickerProps { compact?: boolean; } +const POPUP_PLACEMENTS: PopupPlacement = [ + 'bottom-start', + 'bottom-end', + 'left-start', + 'left-end', + 'top-start', + 'top-end', +]; + +const MODE_OPTIONS = Object.values(Modes).map((val) => ({ + content: val, + value: val, +})); + export const ColorPicker = ({ size, value, @@ -94,10 +109,21 @@ export const ColorPicker = ({ const updateHsva = React.useCallback( (updates: Partial) => { const newHsva = {...hsva, ...updates}; + + // Validate HSVA before applying + if (!isValidHsva(newHsva)) { + return; + } + setHsva(newHsva); const newHexValue = withAlpha ? hsvaToHexa(newHsva) : hsvaToHex(newHsva); + // Validate HEX before calling onUpdate + if (!validHex(newHexValue)) { + return; + } + isInternalUpdateRef.current = true; setColor(newHexValue); }, @@ -113,13 +139,35 @@ export const ColorPicker = ({ }; const applyInputValue = React.useCallback(() => { - const newHsva = convertSelectedModeColorToHsva(inputValue, modeState, withAlpha); - setHsva(newHsva); + const raw = inputValue.trim(); + + if (!raw) { + setInputValue(color); + return; + } + + if (modeState === Modes.Hex && !validHex(raw)) { + setInputValue(color); + return; + } + const newHsva = convertSelectedModeColorToHsva(raw, modeState, withAlpha); + + if (!isValidHsva(newHsva)) { + setInputValue(color); + return; + } const newHexValue = withAlpha ? hsvaToHexa(newHsva) : hsvaToHex(newHsva); + + if (!validHex(newHexValue)) { + setInputValue(color); + return; + } + isInternalUpdateRef.current = true; + setHsva(newHsva); setColor(newHexValue); - }, [inputValue, modeState, withAlpha, setColor]); + }, [inputValue, modeState, withAlpha, setColor, color]); return ( @@ -136,14 +184,7 @@ export const ColorPicker = ({