mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2025-06-30 07:10:58 -04:00
Merge 5803c1d540
into a5fa94166a
This commit is contained in:
commit
29e7c4eeb4
9 changed files with 129 additions and 17 deletions
src
components
containers
helper/tools
lib
|
@ -17,6 +17,7 @@ const ColorIndicatorComponent = props => (
|
||||||
<Popover
|
<Popover
|
||||||
body={
|
body={
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
|
allowAlpha={props.allowAlpha}
|
||||||
color={props.color}
|
color={props.color}
|
||||||
color2={props.color2}
|
color2={props.color2}
|
||||||
gradientType={props.gradientType}
|
gradientType={props.gradientType}
|
||||||
|
@ -44,6 +45,7 @@ const ColorIndicatorComponent = props => (
|
||||||
);
|
);
|
||||||
|
|
||||||
ColorIndicatorComponent.propTypes = {
|
ColorIndicatorComponent.propTypes = {
|
||||||
|
allowAlpha: PropTypes.bool,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
disabled: PropTypes.bool.isRequired,
|
disabled: PropTypes.bool.isRequired,
|
||||||
color: PropTypes.string,
|
color: PropTypes.string,
|
||||||
|
|
BIN
src/components/color-picker/checkerboard.png
Normal file
BIN
src/components/color-picker/checkerboard.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 145 B |
|
@ -19,12 +19,21 @@ import fillRadialIcon from './icons/fill-radial-enabled.svg';
|
||||||
import fillSolidIcon from './icons/fill-solid-enabled.svg';
|
import fillSolidIcon from './icons/fill-solid-enabled.svg';
|
||||||
import fillVertGradientIcon from './icons/fill-vert-gradient-enabled.svg';
|
import fillVertGradientIcon from './icons/fill-vert-gradient-enabled.svg';
|
||||||
import swapIcon from './icons/swap.svg';
|
import swapIcon from './icons/swap.svg';
|
||||||
|
import checkerboard from './checkerboard.png'
|
||||||
import Modes from '../../lib/modes';
|
import Modes from '../../lib/modes';
|
||||||
|
|
||||||
const hsvToHex = (h, s, v) =>
|
const hsvToHex = (h, s, v, alpha = 100) => {
|
||||||
|
// Scale alpha from [0, 100] to [0, 1]
|
||||||
|
const alphaNormalized = alpha / 100;
|
||||||
// Scale hue back up to [0, 360] from [0, 100]
|
// Scale hue back up to [0, 360] from [0, 100]
|
||||||
parseColor(`hsv(${3.6 * h}, ${s}, ${v})`).hex
|
const color = parseColor(`hsv(${3.6 * h}, ${s}, ${v})`);
|
||||||
;
|
// Get the hex value without the alpha channel
|
||||||
|
const hex = color.hex;
|
||||||
|
// Calculate the alpha value in hex (0-255)
|
||||||
|
const alphaHex = Math.round(alphaNormalized * 255).toString(16).padStart(2, '0');
|
||||||
|
// Return the hex value with the alpha channel
|
||||||
|
return `${hex}${alphaHex}`;
|
||||||
|
};
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
swap: {
|
swap: {
|
||||||
|
@ -41,13 +50,16 @@ class ColorPickerComponent extends React.Component {
|
||||||
for (let n = 100; n >= 0; n -= 10) {
|
for (let n = 100; n >= 0; n -= 10) {
|
||||||
switch (channel) {
|
switch (channel) {
|
||||||
case 'hue':
|
case 'hue':
|
||||||
stops.push(hsvToHex(n, this.props.saturation, this.props.brightness));
|
stops.push(hsvToHex(n, this.props.saturation, this.props.brightness, this.props.alpha));
|
||||||
break;
|
break;
|
||||||
case 'saturation':
|
case 'saturation':
|
||||||
stops.push(hsvToHex(this.props.hue, n, this.props.brightness));
|
stops.push(hsvToHex(this.props.hue, n, this.props.brightness, this.props.alpha));
|
||||||
break;
|
break;
|
||||||
case 'brightness':
|
case 'brightness':
|
||||||
stops.push(hsvToHex(this.props.hue, this.props.saturation, n));
|
stops.push(hsvToHex(this.props.hue, this.props.saturation, n, this.props.alpha));
|
||||||
|
break;
|
||||||
|
case 'alpha':
|
||||||
|
stops.push(hsvToHex(this.props.hue, this.props.saturation, this.props.brightness, n));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown channel for color sliders: ${channel}`);
|
throw new Error(`Unknown channel for color sliders: ${channel}`);
|
||||||
|
@ -63,7 +75,7 @@ class ColorPickerComponent extends React.Component {
|
||||||
stops[0] += ` 0 ${halfHandleWidth}px`;
|
stops[0] += ` 0 ${halfHandleWidth}px`;
|
||||||
stops[stops.length - 1] += ` ${CONTAINER_WIDTH - halfHandleWidth}px 100%`;
|
stops[stops.length - 1] += ` ${CONTAINER_WIDTH - halfHandleWidth}px 100%`;
|
||||||
|
|
||||||
return `linear-gradient(to left, ${stops.join(',')})`;
|
return `linear-gradient(to left, ${stops.join(',')}), url("${checkerboard}")`;
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
|
@ -245,13 +257,37 @@ class ColorPickerComponent extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.rowSlider}>
|
<div className={styles.rowSlider}>
|
||||||
<Slider
|
<Slider
|
||||||
lastSlider
|
lastSlider={!this.props.allowAlpha}
|
||||||
background={this._makeBackground('brightness')}
|
background={this._makeBackground('brightness')}
|
||||||
value={this.props.brightness}
|
value={this.props.brightness}
|
||||||
onChange={this.props.onBrightnessChange}
|
onChange={this.props.onBrightnessChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{this.props.allowAlpha && (
|
||||||
|
<div className={styles.row}>
|
||||||
|
<div className={styles.rowHeader}>
|
||||||
|
<span className={styles.labelName}>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Opacity"
|
||||||
|
description="Label for the opacity component in the color picker"
|
||||||
|
id="paint.paintEditor.alpha"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span className={styles.labelReadout}>
|
||||||
|
{Math.round(this.props.alpha)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.rowSlider}>
|
||||||
|
<Slider
|
||||||
|
lastSlider
|
||||||
|
background={this._makeBackground('alpha')}
|
||||||
|
value={this.props.alpha}
|
||||||
|
onChange={this.props.onAlphaChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className={styles.swatchRow}>
|
<div className={styles.swatchRow}>
|
||||||
<div className={styles.swatches}>
|
<div className={styles.swatches}>
|
||||||
{this.props.mode === Modes.BIT_LINE ||
|
{this.props.mode === Modes.BIT_LINE ||
|
||||||
|
@ -299,7 +335,9 @@ class ColorPickerComponent extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorPickerComponent.propTypes = {
|
ColorPickerComponent.propTypes = {
|
||||||
|
allowAlpha: PropTypes.bool,
|
||||||
brightness: PropTypes.number.isRequired,
|
brightness: PropTypes.number.isRequired,
|
||||||
|
alpha: PropTypes.number.isRequired,
|
||||||
color: PropTypes.string,
|
color: PropTypes.string,
|
||||||
color2: PropTypes.string,
|
color2: PropTypes.string,
|
||||||
colorIndex: PropTypes.number.isRequired,
|
colorIndex: PropTypes.number.isRequired,
|
||||||
|
@ -310,6 +348,7 @@ ColorPickerComponent.propTypes = {
|
||||||
mode: PropTypes.oneOf(Object.keys(Modes)),
|
mode: PropTypes.oneOf(Object.keys(Modes)),
|
||||||
onActivateEyeDropper: PropTypes.func.isRequired,
|
onActivateEyeDropper: PropTypes.func.isRequired,
|
||||||
onBrightnessChange: PropTypes.func.isRequired,
|
onBrightnessChange: PropTypes.func.isRequired,
|
||||||
|
onAlphaChange: PropTypes.func.isRequired,
|
||||||
onChangeGradientTypeHorizontal: PropTypes.func.isRequired,
|
onChangeGradientTypeHorizontal: PropTypes.func.isRequired,
|
||||||
onChangeGradientTypeRadial: PropTypes.func.isRequired,
|
onChangeGradientTypeRadial: PropTypes.func.isRequired,
|
||||||
onChangeGradientTypeSolid: PropTypes.func.isRequired,
|
onChangeGradientTypeSolid: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -88,10 +88,12 @@ const PaintEditorComponent = props => (
|
||||||
<FillColorIndicatorComponent
|
<FillColorIndicatorComponent
|
||||||
className={styles.modMarginAfter}
|
className={styles.modMarginAfter}
|
||||||
onUpdateImage={props.onUpdateImage}
|
onUpdateImage={props.onUpdateImage}
|
||||||
|
allowAlpha
|
||||||
/>
|
/>
|
||||||
{/* stroke */}
|
{/* stroke */}
|
||||||
<StrokeColorIndicatorComponent
|
<StrokeColorIndicatorComponent
|
||||||
onUpdateImage={props.onUpdateImage}
|
onUpdateImage={props.onUpdateImage}
|
||||||
|
allowAlpha
|
||||||
/>
|
/>
|
||||||
{/* stroke width */}
|
{/* stroke width */}
|
||||||
<StrokeWidthIndicatorComponent
|
<StrokeWidthIndicatorComponent
|
||||||
|
|
|
@ -30,6 +30,16 @@ const makeColorIndicator = (label, isStroke) => {
|
||||||
// Flag to track whether an svg-update-worthy change has been made
|
// Flag to track whether an svg-update-worthy change has been made
|
||||||
this._hasChanged = false;
|
this._hasChanged = false;
|
||||||
}
|
}
|
||||||
|
componentDidMount () {
|
||||||
|
if (!this.props.allowAlpha) {
|
||||||
|
this.removeAlpha();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (!this.props.allowAlpha && prevProps.allowAlpha) {
|
||||||
|
this.removeAlpha();
|
||||||
|
}
|
||||||
|
}
|
||||||
componentWillReceiveProps (newProps) {
|
componentWillReceiveProps (newProps) {
|
||||||
const {colorModalVisible, onUpdateImage} = this.props;
|
const {colorModalVisible, onUpdateImage} = this.props;
|
||||||
if (colorModalVisible && !newProps.colorModalVisible) {
|
if (colorModalVisible && !newProps.colorModalVisible) {
|
||||||
|
@ -38,6 +48,14 @@ const makeColorIndicator = (label, isStroke) => {
|
||||||
this._hasChanged = false;
|
this._hasChanged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
removeAlpha() {
|
||||||
|
const parsedColor1 = parseColor(this.props.color)
|
||||||
|
if (parsedColor1?.hex)
|
||||||
|
this.props.onChangeColor(parsedColor1.hex.substr(0, 7), 0)
|
||||||
|
const parsedColor2 = parseColor(this.props.color2)
|
||||||
|
if (parsedColor2?.hex)
|
||||||
|
this.props.onChangeColor(parsedColor2.hex.substr(0, 7), 1)
|
||||||
|
}
|
||||||
handleChangeColor (newColor) {
|
handleChangeColor (newColor) {
|
||||||
// Stroke-selector-specific logic: if we change the stroke color from "none" to something visible, ensure
|
// Stroke-selector-specific logic: if we change the stroke color from "none" to something visible, ensure
|
||||||
// there's a nonzero stroke width. If we change the stroke color to "none", set the stroke width to zero.
|
// there's a nonzero stroke width. If we change the stroke color to "none", set the stroke width to zero.
|
||||||
|
|
|
@ -26,10 +26,19 @@ const colorStringToHsv = hexString => {
|
||||||
return hsv;
|
return hsv;
|
||||||
};
|
};
|
||||||
|
|
||||||
const hsvToHex = (h, s, v) =>
|
const hsvToHex = (h, s, v, alpha = 100) => {
|
||||||
|
// Scale alpha from [0, 100] to [0, 1]
|
||||||
|
const alphaNormalized = alpha / 100;
|
||||||
// Scale hue back up to [0, 360] from [0, 100]
|
// Scale hue back up to [0, 360] from [0, 100]
|
||||||
parseColor(`hsv(${3.6 * h}, ${s}, ${v})`).hex
|
const color = parseColor(`hsv(${3.6 * h}, ${s}, ${v})`);
|
||||||
;
|
// Get the hex value without the alpha channel
|
||||||
|
const hex = color.hex;
|
||||||
|
// Calculate the alpha value in hex (0-255)
|
||||||
|
const alphaHex = Math.round(alphaNormalized * 255).toString(16).padStart(2, '0');
|
||||||
|
// Return the hex value with the alpha channel
|
||||||
|
return `${hex}${alphaHex}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Important! This component ignores new color props except when isEyeDropping
|
// Important! This component ignores new color props except when isEyeDropping
|
||||||
// This is to make the HSV <=> RGB conversion stable. The sliders manage their
|
// This is to make the HSV <=> RGB conversion stable. The sliders manage their
|
||||||
|
@ -46,16 +55,20 @@ class ColorPicker extends React.Component {
|
||||||
'handleHueChange',
|
'handleHueChange',
|
||||||
'handleSaturationChange',
|
'handleSaturationChange',
|
||||||
'handleBrightnessChange',
|
'handleBrightnessChange',
|
||||||
|
'handleAlphaChange',
|
||||||
'handleTransparent',
|
'handleTransparent',
|
||||||
'handleActivateEyeDropper'
|
'handleActivateEyeDropper'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const color = props.colorIndex === 0 ? props.color : props.color2;
|
const color = props.colorIndex === 0 ? props.color : props.color2;
|
||||||
const hsv = this.getHsv(color);
|
const hsv = this.getHsv(color);
|
||||||
|
const alpha = this.getAlpha(color);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hue: hsv[0],
|
hue: hsv[0],
|
||||||
saturation: hsv[1],
|
saturation: hsv[1],
|
||||||
brightness: hsv[2]
|
brightness: hsv[2],
|
||||||
|
alpha: alpha * 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
componentWillReceiveProps (newProps) {
|
componentWillReceiveProps (newProps) {
|
||||||
|
@ -64,10 +77,13 @@ class ColorPicker extends React.Component {
|
||||||
const colorSetByEyedropper = this.props.isEyeDropping && color !== newColor;
|
const colorSetByEyedropper = this.props.isEyeDropping && color !== newColor;
|
||||||
if (colorSetByEyedropper || this.props.colorIndex !== newProps.colorIndex) {
|
if (colorSetByEyedropper || this.props.colorIndex !== newProps.colorIndex) {
|
||||||
const hsv = this.getHsv(newColor);
|
const hsv = this.getHsv(newColor);
|
||||||
|
const alpha = this.getAlpha(newColor);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
hue: hsv[0],
|
hue: hsv[0],
|
||||||
saturation: hsv[1],
|
saturation: hsv[1],
|
||||||
brightness: hsv[2]
|
brightness: hsv[2],
|
||||||
|
alpha: alpha * 100
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,6 +93,26 @@ class ColorPicker extends React.Component {
|
||||||
return isTransparent || isMixed ?
|
return isTransparent || isMixed ?
|
||||||
[50, 100, 100] : colorStringToHsv(color);
|
[50, 100, 100] : colorStringToHsv(color);
|
||||||
}
|
}
|
||||||
|
getAlpha(color) {
|
||||||
|
// TODO: need to find a way to get the alpha from all kinds of color strings (rgb, rgba, hex, hex with alpha, etc.)
|
||||||
|
// parse-color returns a range of 0-255 for hex inputs, but 0-1 for any other input
|
||||||
|
// (for hex codes without an alpha value, parse-color returns an alpha of 1)
|
||||||
|
|
||||||
|
if (!color) return 0; // transparent swatch
|
||||||
|
|
||||||
|
const result = parseColor(color)
|
||||||
|
if (!result?.rgba) return 1; // no alpha value
|
||||||
|
|
||||||
|
let alpha = result.rgba[3]
|
||||||
|
|
||||||
|
if (color.startsWith('#') && alpha !== 1) {
|
||||||
|
// We used a hex color, divide parse-color alpha value by 255
|
||||||
|
|
||||||
|
alpha = alpha / 255
|
||||||
|
}
|
||||||
|
|
||||||
|
return alpha
|
||||||
|
}
|
||||||
handleHueChange (hue) {
|
handleHueChange (hue) {
|
||||||
this.setState({hue: hue}, () => {
|
this.setState({hue: hue}, () => {
|
||||||
this.handleColorChange();
|
this.handleColorChange();
|
||||||
|
@ -92,15 +128,24 @@ class ColorPicker extends React.Component {
|
||||||
this.handleColorChange();
|
this.handleColorChange();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
handleAlphaChange (alpha) {
|
||||||
|
this.setState({alpha: alpha}, () => {
|
||||||
|
this.handleColorChange();
|
||||||
|
});
|
||||||
|
}
|
||||||
handleColorChange () {
|
handleColorChange () {
|
||||||
this.props.onChangeColor(hsvToHex(
|
this.props.onChangeColor(hsvToHex(
|
||||||
this.state.hue,
|
this.state.hue,
|
||||||
this.state.saturation,
|
this.state.saturation,
|
||||||
this.state.brightness
|
this.state.brightness,
|
||||||
|
this.state.alpha
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
handleTransparent () {
|
handleTransparent () {
|
||||||
this.props.onChangeColor(null);
|
// TODO: UX - should this reset all sliders, or just the alpha?
|
||||||
|
this.setState({alpha: 0}, () => {
|
||||||
|
this.handleColorChange();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
handleActivateEyeDropper () {
|
handleActivateEyeDropper () {
|
||||||
this.props.onActivateEyeDropper(
|
this.props.onActivateEyeDropper(
|
||||||
|
@ -123,7 +168,9 @@ class ColorPicker extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
<ColorPickerComponent
|
<ColorPickerComponent
|
||||||
|
allowAlpha={this.props.allowAlpha}
|
||||||
brightness={this.state.brightness}
|
brightness={this.state.brightness}
|
||||||
|
alpha={this.state.alpha}
|
||||||
color={this.props.color}
|
color={this.props.color}
|
||||||
color2={this.props.color2}
|
color2={this.props.color2}
|
||||||
colorIndex={this.props.colorIndex}
|
colorIndex={this.props.colorIndex}
|
||||||
|
@ -136,6 +183,7 @@ class ColorPicker extends React.Component {
|
||||||
shouldShowGradientTools={this.props.shouldShowGradientTools}
|
shouldShowGradientTools={this.props.shouldShowGradientTools}
|
||||||
onActivateEyeDropper={this.handleActivateEyeDropper}
|
onActivateEyeDropper={this.handleActivateEyeDropper}
|
||||||
onBrightnessChange={this.handleBrightnessChange}
|
onBrightnessChange={this.handleBrightnessChange}
|
||||||
|
onAlphaChange={this.handleAlphaChange}
|
||||||
onChangeGradientTypeHorizontal={this.handleChangeGradientTypeHorizontal}
|
onChangeGradientTypeHorizontal={this.handleChangeGradientTypeHorizontal}
|
||||||
onChangeGradientTypeRadial={this.handleChangeGradientTypeRadial}
|
onChangeGradientTypeRadial={this.handleChangeGradientTypeRadial}
|
||||||
onChangeGradientTypeSolid={this.handleChangeGradientTypeSolid}
|
onChangeGradientTypeSolid={this.handleChangeGradientTypeSolid}
|
||||||
|
@ -152,6 +200,7 @@ class ColorPicker extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorPicker.propTypes = {
|
ColorPicker.propTypes = {
|
||||||
|
allowAlpha: PropTypes.bool,
|
||||||
color: PropTypes.string,
|
color: PropTypes.string,
|
||||||
color2: PropTypes.string,
|
color2: PropTypes.string,
|
||||||
colorIndex: PropTypes.number.isRequired,
|
colorIndex: PropTypes.number.isRequired,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import UpdateImageHOC from '../hocs/update-image-hoc.jsx';
|
||||||
|
|
||||||
import {changeMode} from '../reducers/modes';
|
import {changeMode} from '../reducers/modes';
|
||||||
import {changeFormat} from '../reducers/format';
|
import {changeFormat} from '../reducers/format';
|
||||||
|
import {changeFillColor} from '../reducers/fill-style';
|
||||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||||
import {deactivateEyeDropper} from '../reducers/eye-dropper';
|
import {deactivateEyeDropper} from '../reducers/eye-dropper';
|
||||||
import {setTextEditTarget} from '../reducers/text-edit-target';
|
import {setTextEditTarget} from '../reducers/text-edit-target';
|
||||||
|
|
|
@ -77,6 +77,7 @@ class EyeDropperTool extends paper.Tool {
|
||||||
const r = colorInfo.color[0];
|
const r = colorInfo.color[0];
|
||||||
const g = colorInfo.color[1];
|
const g = colorInfo.color[1];
|
||||||
const b = colorInfo.color[2];
|
const b = colorInfo.color[2];
|
||||||
|
const a = colorInfo.color[3] / 255; // Normalize alpha to range [0, 1]
|
||||||
|
|
||||||
// from https://github.com/LLK/scratch-gui/blob/77e54a80a31b6cd4684d4b2a70f1aeec671f229e/src/containers/stage.jsx#L218-L222
|
// from https://github.com/LLK/scratch-gui/blob/77e54a80a31b6cd4684d4b2a70f1aeec671f229e/src/containers/stage.jsx#L218-L222
|
||||||
// formats the color info from the canvas into hex for parsing by the color picker
|
// formats the color info from the canvas into hex for parsing by the color picker
|
||||||
|
@ -84,7 +85,7 @@ class EyeDropperTool extends paper.Tool {
|
||||||
const hex = c.toString(16);
|
const hex = c.toString(16);
|
||||||
return hex.length === 1 ? `0${hex}` : hex;
|
return hex.length === 1 ? `0${hex}` : hex;
|
||||||
};
|
};
|
||||||
this.colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}`;
|
this.colorString = `#${componentToString(r)}${componentToString(g)}${componentToString(b)}${Math.round(a * 255).toString(16).padStart(2, '0')}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getColorInfo (x, y, hideLoupe) {
|
getColorInfo (x, y, hideLoupe) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {getColorsFromSelection, MIXED} from '../helper/style-path';
|
||||||
import GradientTypes from './gradient-types';
|
import GradientTypes from './gradient-types';
|
||||||
|
|
||||||
// Matches hex colors
|
// Matches hex colors
|
||||||
const hexRegex = /^#([0-9a-f]{3}){1,2}$/i;
|
const hexRegex = /^#([0-9a-f]{3}){1,2}([0-9a-f]{2})?$/i;
|
||||||
|
|
||||||
const isValidHexColor = color => {
|
const isValidHexColor = color => {
|
||||||
if (!hexRegex.test(color) && color !== null && color !== MIXED) {
|
if (!hexRegex.test(color) && color !== null && color !== MIXED) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue