Switch state to track hover item ID instead of item itself

This commit is contained in:
DD 2017-09-22 13:48:18 -04:00
parent 05ad64fd01
commit e5303784a0
7 changed files with 58 additions and 45 deletions

View file

@ -37,7 +37,6 @@
"babel-plugin-transform-object-rest-spread": "^6.22.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.22.0",
"canvas-prebuilt": "^1.6.5-prerelease.1",
"classnames": "2.2.5",
"css-loader": "0.28.3",
"enzyme": "^2.8.2",

View file

@ -9,7 +9,6 @@ import {setHoveredItem, clearHoveredItem} from '../reducers/hover';
import SelectTool from '../helper/selection-tools/select-tool';
import SelectModeComponent from '../components/select-mode.jsx';
import paper from 'paper';
class SelectMode extends React.Component {
constructor (props) {
@ -25,8 +24,8 @@ class SelectMode extends React.Component {
}
}
componentWillReceiveProps (nextProps) {
if (this.tool && nextProps.hoveredItem !== this.props.hoveredItem) {
this.tool.setPrevHoveredItem(nextProps.hoveredItem);
if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {
this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);
}
if (nextProps.isSelectModeActive && !this.props.isSelectModeActive) {
@ -57,7 +56,7 @@ class SelectMode extends React.Component {
SelectMode.propTypes = {
clearHoveredItem: PropTypes.func.isRequired,
handleMouseDown: PropTypes.func.isRequired,
hoveredItem: PropTypes.instanceOf(paper.Item),
hoveredItemId: PropTypes.number,
isSelectModeActive: PropTypes.bool.isRequired,
onUpdateSvg: PropTypes.func.isRequired,
setHoveredItem: PropTypes.func.isRequired
@ -65,11 +64,11 @@ SelectMode.propTypes = {
const mapStateToProps = state => ({
isSelectModeActive: state.scratchPaint.mode === Modes.SELECT,
hoveredItem: state.scratchPaint.hoveredItem
hoveredItemId: state.scratchPaint.hoveredItemId
});
const mapDispatchToProps = dispatch => ({
setHoveredItem: hoveredItem => {
dispatch(setHoveredItem(hoveredItem));
setHoveredItem: hoveredItemId => {
dispatch(setHoveredItem(hoveredItemId));
},
clearHoveredItem: () => {
dispatch(clearHoveredItem());

View file

@ -1,29 +1,43 @@
import PropTypes from 'prop-types';
import React from 'react';
import {connect} from 'react-redux';
import bindAll from 'lodash.bindall';
import paper from 'paper';
const SelectionHOC = function (WrappedComponent) {
class SelectionComponent extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'removeItemById'
]);
}
componentDidMount () {
if (this.props.hoveredItem) {
if (this.props.hoveredItemId) {
paper.view.update();
}
}
componentDidUpdate (prevProps) {
if (this.props.hoveredItem && this.props.hoveredItem !== prevProps.hoveredItem) {
// A hover item has been added. Update the view
if (prevProps.hoveredItem) {
prevProps.hoveredItem.remove();
// Hovered item has changed
if ((this.props.hoveredItemId && this.props.hoveredItemId !== prevProps.hoveredItemId) ||
(!this.props.hoveredItemId && prevProps.hoveredItemId)) {
// Remove the old hover item if any
this.removeItemById(prevProps.hoveredItemId);
}
}
removeItemById (itemId) {
if (itemId) {
const match = paper.project.getItem({
match: item => (item.id === itemId)
});
if (match) {
match.remove();
}
} else if (!this.props.hoveredItem && prevProps.hoveredItem) {
// Remove the hover item
prevProps.hoveredItem.remove();
}
}
render () {
const {
hoveredItem, // eslint-disable-line no-unused-vars
hoveredItemId, // eslint-disable-line no-unused-vars
...props
} = this.props;
return (
@ -32,11 +46,11 @@ const SelectionHOC = function (WrappedComponent) {
}
}
SelectionComponent.propTypes = {
hoveredItem: PropTypes.instanceOf(paper.Item)
hoveredItemId: PropTypes.number
};
const mapStateToProps = state => ({
hoveredItem: state.scratchPaint.hoveredItem
hoveredItemId: state.scratchPaint.hoveredItemId
});
return connect(
mapStateToProps

View file

@ -47,11 +47,11 @@ class SelectTool extends paper.Tool {
* To be called when the hovered item changes. When the select tool hovers over a
* new item, it compares against this to see if a hover item change event needs to
* be fired.
* @param {paper.Item} prevHoveredItem The highlight that indicates the mouse is over
* a given item currently
* @param {paper.Item} prevHoveredItemId ID of the highlight item that indicates the mouse is
* over a given item currently
*/
setPrevHoveredItem (prevHoveredItem) {
this.prevHoveredItem = prevHoveredItem;
setPrevHoveredItemId (prevHoveredItemId) {
this.prevHoveredItemId = prevHoveredItemId;
}
/**
* Returns the hit options to use when conducting hit tests.
@ -92,11 +92,11 @@ class SelectTool extends paper.Tool {
}
handleMouseMove (event) {
const hoveredItem = getHoveredItem(event, this.getHitOptions());
if ((!hoveredItem && this.prevHoveredItem) || // There is no longer a hovered item
(hoveredItem && !this.prevHoveredItem) || // There is now a hovered item
(hoveredItem && this.prevHoveredItem &&
hoveredItem.id !== this.prevHoveredItem.id)) { // hovered item changed
this.setHoveredItem(hoveredItem);
if ((!hoveredItem && this.prevHoveredItemId) || // There is no longer a hovered item
(hoveredItem && !this.prevHoveredItemId) || // There is now a hovered item
(hoveredItem && this.prevHoveredItemId &&
hoveredItem.id !== this.prevHoveredItemId)) { // hovered item changed
this.setHoveredItem(hoveredItem ? hoveredItem.id : null);
}
}
handleMouseDrag (event) {

View file

@ -1,4 +1,3 @@
import paper from 'paper';
import log from '../log/log';
const CHANGE_HOVERED = 'scratch-paint/hover/CHANGE_HOVERED';
@ -8,29 +7,36 @@ const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState;
switch (action.type) {
case CHANGE_HOVERED:
if (typeof action.hoveredItem === 'undefined' ||
(action.hoveredItem !== null && !(action.hoveredItem instanceof paper.Item))) {
if (typeof action.hoveredItemId === 'undefined') {
log.warn(`Hovered item should not be set to undefined. Use null.`);
return state;
} else if (typeof action.hoveredItemId === 'undefined' || isNaN(action.hoveredItemId)) {
log.warn(`Hovered item should be an item ID number. Got: ${action.hoveredItemId}`);
return state;
}
return action.hoveredItem;
return action.hoveredItemId;
default:
return state;
}
};
// Action creators ==================================
const setHoveredItem = function (hoveredItem) {
/**
* Set the hovered item state to the given item ID
* @param {number} hoveredItemId The paper.Item ID of the hover indicator item.
* @return {object} Redux action to change the hovered item.
*/
const setHoveredItem = function (hoveredItemId) {
return {
type: CHANGE_HOVERED,
hoveredItem: hoveredItem
hoveredItemId: hoveredItemId
};
};
const clearHoveredItem = function () {
return {
type: CHANGE_HOVERED,
hoveredItem: null
hoveredItemId: null
};
};

View file

@ -10,5 +10,5 @@ export default combineReducers({
brushMode: brushModeReducer,
eraserMode: eraserModeReducer,
color: colorReducer,
hoveredItem: hoverReducer
hoveredItemId: hoverReducer
});

View file

@ -1,12 +1,7 @@
/* eslint-env jest */
import paper from 'paper';
import reducer from '../../src/reducers/hover';
import {clearHoveredItem, setHoveredItem} from '../../src/reducers/hover';
beforeEach(() => {
paper.setup();
});
test('initialState', () => {
let defaultState;
expect(reducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeNull();
@ -14,22 +9,22 @@ test('initialState', () => {
test('setHoveredItem', () => {
let defaultState;
const item1 = new paper.Path();
const item2 = new paper.Path();
const item1 = 1;
const item2 = 2;
expect(reducer(defaultState /* state */, setHoveredItem(item1) /* action */)).toBe(item1);
expect(reducer(item1 /* state */, setHoveredItem(item2) /* action */)).toBe(item2);
});
test('clearHoveredItem', () => {
let defaultState;
const item = new paper.Path();
const item = 1;
expect(reducer(defaultState /* state */, clearHoveredItem() /* action */)).toBeNull();
expect(reducer(item /* state */, clearHoveredItem() /* action */)).toBeNull();
});
test('invalidSetHoveredItem', () => {
let defaultState;
const item = new paper.Path();
const item = 1;
const nonItem = {random: 'object'};
let undef;
expect(reducer(defaultState /* state */, setHoveredItem(nonItem) /* action */)).toBeNull();