Merge pull request #112 from paulkaplan/transparent-state

Fix several color state inconsistencies
This commit is contained in:
Paul Kaplan 2017-10-27 08:20:09 -04:00 committed by GitHub
commit c1ce433f72
17 changed files with 185 additions and 12 deletions

View file

@ -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;
}

View file

@ -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;

View 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

View 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

View file

@ -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%;
}

View file

@ -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>

View file

@ -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,

View file

@ -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;
}

View file

@ -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;

View file

@ -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,

View file

@ -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
};

View file

@ -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));
}
});

View file

@ -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

View file

@ -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));
}
});

View file

@ -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));
}
});

View file

@ -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

View file

@ -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