mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-23 05:52:42 -05:00
Merge pull request #112 from paulkaplan/transparent-state
Fix several color state inconsistencies
This commit is contained in:
commit
c1ce433f72
17 changed files with 185 additions and 12 deletions
|
@ -5,6 +5,7 @@
|
|||
}
|
||||
|
||||
.color-button-swatch {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-basis: 2rem;
|
||||
flex-shrink: 0;
|
||||
|
@ -30,3 +31,23 @@
|
|||
color: #575e75;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.swatch-icon {
|
||||
width: 1.75rem;
|
||||
margin: auto;
|
||||
/* Make sure it appears above the outline box */
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.outline-swatch:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: calc(0.5rem + 1px);
|
||||
left: calc(0.5rem + 1px);
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
background: white;
|
||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||
/* Make sure it appears below the transparent icon */
|
||||
z-index: 1;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,55 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {MIXED} from '../../helper/style-path';
|
||||
|
||||
import noFillIcon from './no-fill.svg';
|
||||
import mixedFillIcon from './mixed-fill.svg';
|
||||
import styles from './color-button.css';
|
||||
|
||||
const colorToBackground = color => {
|
||||
if (color === MIXED || color === null) return 'white';
|
||||
return color;
|
||||
};
|
||||
|
||||
const ColorButtonComponent = props => (
|
||||
<div
|
||||
className={styles.colorButton}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<div
|
||||
className={styles.colorButtonSwatch}
|
||||
className={classNames(styles.colorButtonSwatch, {
|
||||
[styles.outlineSwatch]: props.outline
|
||||
})}
|
||||
style={{
|
||||
background: props.color
|
||||
background: colorToBackground(props.color)
|
||||
}}
|
||||
>
|
||||
{props.color === null ? (
|
||||
<img
|
||||
className={styles.swatchIcon}
|
||||
src={noFillIcon}
|
||||
/>
|
||||
) : ((props.color === MIXED ? (
|
||||
<img
|
||||
className={styles.swatchIcon}
|
||||
src={mixedFillIcon}
|
||||
/>
|
||||
) : null))}
|
||||
</div>
|
||||
<div className={styles.colorButtonArrow}>▾</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
ColorButtonComponent.propTypes = {
|
||||
color: PropTypes.string,
|
||||
onClick: PropTypes.func.isRequired
|
||||
onClick: PropTypes.func.isRequired,
|
||||
outline: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
ColorButtonComponent.defaultProps = {
|
||||
outline: false
|
||||
};
|
||||
|
||||
export default ColorButtonComponent;
|
||||
|
|
16
src/components/color-button/mixed-fill.svg
Normal file
16
src/components/color-button/mixed-fill.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>mixed-fill</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" fill-opacity="0.75">
|
||||
<g id="mixed-fill">
|
||||
<g id="mixed-fill-icon" transform="translate(2.000000, 2.500000)">
|
||||
<circle id="blue" fill="#4C97FF" cx="4.5" cy="10.5" r="4.5"></circle>
|
||||
<circle id="red" fill="#FF5500" cx="8" cy="4.5" r="4.5"></circle>
|
||||
<circle id="yellow" fill="#FFBF00" cx="11.4099998" cy="10.5" r="4.5"></circle>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 888 B |
12
src/components/color-button/no-fill.svg
Normal file
12
src/components/color-button/no-fill.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>no-fill</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="square">
|
||||
<g id="no-fill" stroke="#FF661A" stroke-width="2">
|
||||
<path d="M3,17 L17,3" id="Line"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 612 B |
|
@ -48,3 +48,7 @@
|
|||
border: 1px solid #4C97FF;
|
||||
box-shadow: 0px 0px 0px 3px hsla(215, 100%, 65%, 0.2);
|
||||
}
|
||||
|
||||
.swatch > img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import {MIXED} from '../../helper/style-path';
|
|||
|
||||
import Slider from '../forms/slider.jsx';
|
||||
import styles from './color-picker.css';
|
||||
import noFillIcon from '../color-button/no-fill.svg';
|
||||
|
||||
const colorStringToHsv = hexString => {
|
||||
const hsv = parseColor(hexString).hsv;
|
||||
|
@ -179,7 +180,9 @@ class ColorPickerComponent extends React.Component {
|
|||
[styles.activeSwatch]: this.props.color === null
|
||||
})}
|
||||
onClick={this.handleTransparent}
|
||||
/>
|
||||
>
|
||||
<img src={noFillIcon} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
const FillColorIndicatorComponent = props => (
|
||||
<InputGroup>
|
||||
<InputGroup disabled={props.disabled}>
|
||||
<Popover
|
||||
body={
|
||||
<ColorPicker
|
||||
|
@ -40,6 +40,7 @@ const FillColorIndicatorComponent = props => (
|
|||
);
|
||||
|
||||
FillColorIndicatorComponent.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
fillColor: PropTypes.string,
|
||||
fillColorModalVisible: PropTypes.bool.isRequired,
|
||||
intl: intlShape,
|
||||
|
|
|
@ -3,3 +3,9 @@
|
|||
.input-group + .input-group {
|
||||
margin-left: calc(3 * $grid-unit);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.3;
|
||||
/* Prevent any user actions */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
|
@ -5,14 +5,19 @@ import PropTypes from 'prop-types';
|
|||
import styles from './input-group.css';
|
||||
|
||||
const InputGroup = props => (
|
||||
<div className={classNames(props.className, styles.inputGroup)}>
|
||||
<div
|
||||
className={classNames(props.className, styles.inputGroup, {
|
||||
[styles.disabled]: props.disabled
|
||||
})}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
||||
InputGroup.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool
|
||||
};
|
||||
|
||||
export default InputGroup;
|
||||
|
|
|
@ -17,7 +17,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
const StrokeColorIndicatorComponent = props => (
|
||||
<InputGroup>
|
||||
<InputGroup disabled={props.disabled}>
|
||||
<Popover
|
||||
body={
|
||||
<ColorPicker
|
||||
|
@ -31,6 +31,7 @@ const StrokeColorIndicatorComponent = props => (
|
|||
>
|
||||
<Label text={props.intl.formatMessage(messages.stroke)}>
|
||||
<ColorButton
|
||||
outline
|
||||
color={props.strokeColor}
|
||||
onClick={props.onOpenStrokeColor}
|
||||
/>
|
||||
|
@ -40,6 +41,7 @@ const StrokeColorIndicatorComponent = props => (
|
|||
);
|
||||
|
||||
StrokeColorIndicatorComponent.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
intl: intlShape,
|
||||
onChangeStrokeColor: PropTypes.func.isRequired,
|
||||
onCloseStrokeColor: PropTypes.func.isRequired,
|
||||
|
|
|
@ -9,9 +9,10 @@ import {MAX_STROKE_WIDTH} from '../reducers/stroke-width';
|
|||
|
||||
const BufferedInput = BufferedInputHOC(Input);
|
||||
const StrokeWidthIndicatorComponent = props => (
|
||||
<InputGroup>
|
||||
<InputGroup disabled={props.disabled}>
|
||||
<BufferedInput
|
||||
small
|
||||
disabled={props.disabled}
|
||||
max={MAX_STROKE_WIDTH}
|
||||
min="0"
|
||||
type="number"
|
||||
|
@ -22,6 +23,7 @@ const StrokeWidthIndicatorComponent = props => (
|
|||
);
|
||||
|
||||
StrokeWidthIndicatorComponent.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
onChangeStrokeWidth: PropTypes.func.isRequired,
|
||||
strokeWidth: PropTypes.number
|
||||
};
|
||||
|
|
|
@ -4,7 +4,9 @@ import {connect} from 'react-redux';
|
|||
import bindAll from 'lodash.bindall';
|
||||
import Modes from '../modes/modes';
|
||||
import Blobbiness from '../helper/blob-tools/blob';
|
||||
import {MIXED} from '../helper/style-path';
|
||||
|
||||
import {changeFillColor} from '../reducers/fill-color';
|
||||
import {changeBrushSize} from '../reducers/brush-mode';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
|
@ -13,6 +15,9 @@ import {clearSelection} from '../helper/selection';
|
|||
import BrushModeComponent from '../components/brush-mode/brush-mode.jsx';
|
||||
|
||||
class BrushMode extends React.Component {
|
||||
static get DEFAULT_COLOR () {
|
||||
return '#9966FF';
|
||||
}
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
|
@ -49,6 +54,12 @@ class BrushMode extends React.Component {
|
|||
// analogous to how selection works with eraser
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
|
||||
// Force the default brush color if fill is MIXED or transparent
|
||||
const {fillColor} = this.props.colorState;
|
||||
if (fillColor === MIXED || fillColor === null) {
|
||||
this.props.onChangeFillColor(BrushMode.DEFAULT_COLOR);
|
||||
}
|
||||
|
||||
// TODO: This is temporary until a component that provides the brush size is hooked up
|
||||
this.props.canvas.addEventListener('mousewheel', this.onScroll);
|
||||
this.blob.activateTool({
|
||||
|
@ -93,6 +104,7 @@ BrushMode.propTypes = {
|
|||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isBrushModeActive: PropTypes.bool.isRequired,
|
||||
onChangeFillColor: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
@ -110,6 +122,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.BRUSH));
|
||||
},
|
||||
onChangeFillColor: fillColor => {
|
||||
dispatch(changeFillColor(fillColor));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import bindAll from 'lodash.bindall';
|
||||
import {changeFillColor} from '../reducers/fill-color';
|
||||
import {openFillColor, closeFillColor} from '../reducers/modals';
|
||||
import Modes from '../modes/modes';
|
||||
|
||||
import FillColorIndicatorComponent from '../components/fill-color-indicator.jsx';
|
||||
import {applyFillColorToSelection} from '../helper/style-path';
|
||||
|
@ -30,6 +31,7 @@ class FillColorIndicator extends React.Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
disabled: state.scratchPaint.mode === Modes.PEN,
|
||||
fillColor: state.scratchPaint.color.fillColor,
|
||||
fillColorModalVisible: state.scratchPaint.modals.fillColor
|
||||
});
|
||||
|
@ -47,6 +49,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
});
|
||||
|
||||
FillColorIndicator.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
fillColor: PropTypes.string,
|
||||
onChangeFillColor: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
|
|
|
@ -8,8 +8,11 @@ import {clearSelection} from '../helper/selection';
|
|||
import {endPointHit, touching} from '../helper/snapping';
|
||||
import {drawHitPoint, removeHitPoint} from '../helper/guides';
|
||||
import {stylePath} from '../helper/style-path';
|
||||
import {changeStrokeColor} from '../reducers/stroke-color';
|
||||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
import {MIXED} from '../helper/style-path';
|
||||
|
||||
import LineModeComponent from '../components/line-mode/line-mode.jsx';
|
||||
|
||||
|
@ -17,6 +20,9 @@ class LineMode extends React.Component {
|
|||
static get SNAP_TOLERANCE () {
|
||||
return 6;
|
||||
}
|
||||
static get DEFAULT_COLOR () {
|
||||
return '#000000';
|
||||
}
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
|
@ -46,6 +52,16 @@ class LineMode extends React.Component {
|
|||
}
|
||||
activateTool () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
|
||||
// Force the default line color if stroke is MIXED or transparent
|
||||
const {strokeColor} = this.props.colorState;
|
||||
if (strokeColor === MIXED || strokeColor === null) {
|
||||
this.props.onChangeStrokeColor(LineMode.DEFAULT_COLOR);
|
||||
}
|
||||
// Force a minimum stroke width
|
||||
if (!this.props.colorState.strokeWidth) {
|
||||
this.props.onChangeStrokeWidth(1);
|
||||
}
|
||||
this.tool = new paper.Tool();
|
||||
|
||||
this.path = null;
|
||||
|
@ -220,6 +236,8 @@ LineMode.propTypes = {
|
|||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isLineModeActive: PropTypes.bool.isRequired,
|
||||
onChangeStrokeColor: PropTypes.func.isRequired,
|
||||
onChangeStrokeWidth: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
@ -233,6 +251,12 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.LINE));
|
||||
},
|
||||
onChangeStrokeColor: strokeColor => {
|
||||
dispatch(changeStrokeColor(strokeColor));
|
||||
},
|
||||
onChangeStrokeWidth: strokeWidth => {
|
||||
dispatch(changeStrokeWidth(strokeWidth));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -4,14 +4,20 @@ import {connect} from 'react-redux';
|
|||
import bindAll from 'lodash.bindall';
|
||||
import Modes from '../modes/modes';
|
||||
|
||||
import {changeStrokeColor} from '../reducers/stroke-color';
|
||||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
import {MIXED} from '../helper/style-path';
|
||||
|
||||
import {clearSelection} from '../helper/selection';
|
||||
import PenTool from '../helper/tools/pen-tool';
|
||||
import PenModeComponent from '../components/pen-mode/pen-mode.jsx';
|
||||
|
||||
class PenMode extends React.Component {
|
||||
static get DEFAULT_COLOR () {
|
||||
return '#000000';
|
||||
}
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
|
@ -42,6 +48,15 @@ class PenMode extends React.Component {
|
|||
}
|
||||
activateTool () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
// Force the default pen color if stroke is MIXED or transparent
|
||||
const {strokeColor} = this.props.colorState;
|
||||
if (strokeColor === MIXED || strokeColor === null) {
|
||||
this.props.onChangeStrokeColor(PenMode.DEFAULT_COLOR);
|
||||
}
|
||||
// Force a minimum stroke width
|
||||
if (!this.props.colorState.strokeWidth) {
|
||||
this.props.onChangeStrokeWidth(1);
|
||||
}
|
||||
this.tool = new PenTool(
|
||||
this.props.clearSelectedItems,
|
||||
this.props.onUpdateSvg
|
||||
|
@ -73,6 +88,8 @@ PenMode.propTypes = {
|
|||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isPenModeActive: PropTypes.bool.isRequired,
|
||||
onChangeStrokeColor: PropTypes.func.isRequired,
|
||||
onChangeStrokeWidth: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
@ -89,6 +106,12 @@ const mapDispatchToProps = dispatch => ({
|
|||
dispatch(changeMode(Modes.PEN));
|
||||
},
|
||||
deactivateTool () {
|
||||
},
|
||||
onChangeStrokeColor: strokeColor => {
|
||||
dispatch(changeStrokeColor(strokeColor));
|
||||
},
|
||||
onChangeStrokeWidth: strokeWidth => {
|
||||
dispatch(changeStrokeWidth(strokeWidth));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import bindAll from 'lodash.bindall';
|
||||
import {changeStrokeColor} from '../reducers/stroke-color';
|
||||
import {openStrokeColor, closeStrokeColor} from '../reducers/modals';
|
||||
import Modes from '../modes/modes';
|
||||
|
||||
import StrokeColorIndicatorComponent from '../components/stroke-color-indicator.jsx';
|
||||
import {applyStrokeColorToSelection} from '../helper/style-path';
|
||||
|
@ -30,6 +31,7 @@ class StrokeColorIndicator extends React.Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
disabled: state.scratchPaint.mode === Modes.BRUSH,
|
||||
strokeColor: state.scratchPaint.color.strokeColor,
|
||||
strokeColorModalVisible: state.scratchPaint.modals.strokeColor
|
||||
});
|
||||
|
@ -47,6 +49,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
});
|
||||
|
||||
StrokeColorIndicator.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
onChangeStrokeColor: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
strokeColor: PropTypes.string
|
||||
|
|
|
@ -5,6 +5,7 @@ import bindAll from 'lodash.bindall';
|
|||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import StrokeWidthIndicatorComponent from '../components/stroke-width-indicator.jsx';
|
||||
import {applyStrokeWidthToSelection} from '../helper/style-path';
|
||||
import Modes from '../modes/modes';
|
||||
|
||||
class StrokeWidthIndicator extends React.Component {
|
||||
constructor (props) {
|
||||
|
@ -20,6 +21,7 @@ class StrokeWidthIndicator extends React.Component {
|
|||
render () {
|
||||
return (
|
||||
<StrokeWidthIndicatorComponent
|
||||
disabled={this.props.disabled}
|
||||
strokeWidth={this.props.strokeWidth}
|
||||
onChangeStrokeWidth={this.handleChangeStrokeWidth}
|
||||
/>
|
||||
|
@ -28,6 +30,7 @@ class StrokeWidthIndicator extends React.Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
disabled: state.scratchPaint.mode === Modes.BRUSH,
|
||||
strokeWidth: state.scratchPaint.color.strokeWidth
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
@ -37,6 +40,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
});
|
||||
|
||||
StrokeWidthIndicator.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
onChangeStrokeWidth: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
strokeWidth: PropTypes.number
|
||||
|
|
Loading…
Reference in a new issue