mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2025-01-10 06:32:07 -05:00
159 lines
7.1 KiB
JavaScript
159 lines
7.1 KiB
JavaScript
import PropTypes from 'prop-types';
|
|
import React from 'react';
|
|
import bindAll from 'lodash.bindall';
|
|
import parseColor from 'parse-color';
|
|
import {injectIntl, intlShape} from 'react-intl';
|
|
|
|
import {getSelectedLeafItems} from '../helper/selection';
|
|
import Formats from '../lib/format';
|
|
import {isBitmap} from '../lib/format';
|
|
import GradientTypes from '../lib/gradient-types';
|
|
|
|
import ColorIndicatorComponent from '../components/color-indicator.jsx';
|
|
import {applyColorToSelection,
|
|
applyGradientTypeToSelection,
|
|
applyStrokeWidthToSelection,
|
|
getRotatedColor,
|
|
swapColorsInSelection,
|
|
MIXED} from '../helper/style-path';
|
|
|
|
const makeColorIndicator = (label, isStroke) => {
|
|
class ColorIndicator extends React.Component {
|
|
constructor (props) {
|
|
super(props);
|
|
bindAll(this, [
|
|
'handleChangeColor',
|
|
'handleChangeGradientType',
|
|
'handleCloseColor',
|
|
'handleSwap'
|
|
]);
|
|
|
|
// Flag to track whether an svg-update-worthy change has been made
|
|
this._hasChanged = false;
|
|
}
|
|
componentWillReceiveProps (newProps) {
|
|
const {colorModalVisible, onUpdateImage} = this.props;
|
|
if (colorModalVisible && !newProps.colorModalVisible) {
|
|
// Submit the new SVG, which also stores a single undo/redo action.
|
|
if (this._hasChanged) onUpdateImage();
|
|
this._hasChanged = false;
|
|
}
|
|
}
|
|
handleChangeColor (newColor) {
|
|
// 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.
|
|
if (isStroke) {
|
|
if (this.props.color === null && newColor !== null) {
|
|
this._hasChanged = applyStrokeWidthToSelection(1, this.props.textEditTarget) || this._hasChanged;
|
|
this.props.onChangeStrokeWidth(1);
|
|
} else if (this.props.color !== null && newColor === null) {
|
|
this._hasChanged = applyStrokeWidthToSelection(0, this.props.textEditTarget) || this._hasChanged;
|
|
this.props.onChangeStrokeWidth(0);
|
|
}
|
|
}
|
|
|
|
const formatIsBitmap = isBitmap(this.props.format);
|
|
// Apply color and update redux, but do not update svg until picker closes.
|
|
const isDifferent = applyColorToSelection(
|
|
newColor,
|
|
this.props.colorIndex,
|
|
this.props.gradientType === GradientTypes.SOLID,
|
|
// In bitmap mode, only the fill color selector is used, but it applies to stroke if fillBitmapShapes
|
|
// is set to true via the "Fill"/"Outline" selector button
|
|
isStroke || (formatIsBitmap && !this.props.fillBitmapShapes),
|
|
this.props.textEditTarget);
|
|
this._hasChanged = this._hasChanged || isDifferent;
|
|
this.props.onChangeColor(newColor, this.props.colorIndex);
|
|
}
|
|
handleChangeGradientType (gradientType) {
|
|
const formatIsBitmap = isBitmap(this.props.format);
|
|
// Apply color and update redux, but do not update svg until picker closes.
|
|
const isDifferent = applyGradientTypeToSelection(
|
|
gradientType,
|
|
isStroke || (formatIsBitmap && !this.props.fillBitmapShapes),
|
|
this.props.textEditTarget);
|
|
this._hasChanged = this._hasChanged || isDifferent;
|
|
const hasSelectedItems = getSelectedLeafItems().length > 0;
|
|
if (hasSelectedItems) {
|
|
if (isDifferent) {
|
|
// Recalculates the swatch colors
|
|
this.props.setSelectedItems(this.props.format);
|
|
}
|
|
}
|
|
if (this.props.gradientType === GradientTypes.SOLID && gradientType !== GradientTypes.SOLID) {
|
|
// Generate color 2 and change to the 2nd swatch when switching from solid to gradient
|
|
if (!hasSelectedItems) {
|
|
this.props.onChangeColor(getRotatedColor(this.props.color), 1);
|
|
}
|
|
this.props.onChangeColorIndex(1);
|
|
}
|
|
if (this.props.onChangeGradientType) this.props.onChangeGradientType(gradientType);
|
|
}
|
|
handleCloseColor () {
|
|
// If the eyedropper is currently being used, don't
|
|
// close the color menu.
|
|
if (this.props.isEyeDropping) return;
|
|
|
|
// Otherwise, close the color menu and
|
|
// also reset the color index to indicate
|
|
// that `color1` is selected.
|
|
this.props.onCloseColor();
|
|
this.props.onChangeColorIndex(0);
|
|
}
|
|
handleSwap () {
|
|
if (getSelectedLeafItems().length) {
|
|
const formatIsBitmap = isBitmap(this.props.format);
|
|
const isDifferent = swapColorsInSelection(
|
|
isStroke || (formatIsBitmap && !this.props.fillBitmapShapes),
|
|
this.props.textEditTarget);
|
|
this.props.setSelectedItems(this.props.format);
|
|
this._hasChanged = this._hasChanged || isDifferent;
|
|
} else {
|
|
let color1 = this.props.color;
|
|
let color2 = this.props.color2;
|
|
color1 = color1 === null || color1 === MIXED ? color1 : parseColor(color1).hex;
|
|
color2 = color2 === null || color2 === MIXED ? color2 : parseColor(color2).hex;
|
|
this.props.onChangeColor(color1, 1);
|
|
this.props.onChangeColor(color2, 0);
|
|
}
|
|
}
|
|
render () {
|
|
return (
|
|
<ColorIndicatorComponent
|
|
{...this.props}
|
|
label={this.props.intl.formatMessage(label)}
|
|
outline={isStroke}
|
|
onChangeColor={this.handleChangeColor}
|
|
onChangeGradientType={this.handleChangeGradientType}
|
|
onCloseColor={this.handleCloseColor}
|
|
onSwap={this.handleSwap}
|
|
/>
|
|
);
|
|
}
|
|
}
|
|
|
|
ColorIndicator.propTypes = {
|
|
colorIndex: PropTypes.number.isRequired,
|
|
disabled: PropTypes.bool.isRequired,
|
|
color: PropTypes.string,
|
|
color2: PropTypes.string,
|
|
colorModalVisible: PropTypes.bool.isRequired,
|
|
fillBitmapShapes: PropTypes.bool.isRequired,
|
|
format: PropTypes.oneOf(Object.keys(Formats)),
|
|
gradientType: PropTypes.oneOf(Object.keys(GradientTypes)).isRequired,
|
|
intl: intlShape,
|
|
isEyeDropping: PropTypes.bool.isRequired,
|
|
onChangeColorIndex: PropTypes.func.isRequired,
|
|
onChangeColor: PropTypes.func.isRequired,
|
|
onChangeGradientType: PropTypes.func,
|
|
onChangeStrokeWidth: PropTypes.func,
|
|
onCloseColor: PropTypes.func.isRequired,
|
|
onUpdateImage: PropTypes.func.isRequired,
|
|
setSelectedItems: PropTypes.func.isRequired,
|
|
textEditTarget: PropTypes.number
|
|
};
|
|
|
|
return injectIntl(ColorIndicator);
|
|
};
|
|
|
|
export default makeColorIndicator;
|