Merge branch 'addTool' into addEraser

This commit is contained in:
DD Liu 2017-07-27 11:45:41 -04:00
commit c7471d26ab
19 changed files with 136 additions and 123 deletions

4
.babelrc Normal file
View file

@ -0,0 +1,4 @@
{
"plugins": ["transform-object-rest-spread"],
"presets": ["es2015", "react"]
}

View file

@ -9,8 +9,8 @@
"deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"",
"lint": "eslint . --ext .js,.jsx",
"start": "webpack-dev-server",
"tap": "./node_modules/.bin/tap ./test/*.js",
"test": "npm run lint && npm run build && npm run tap",
"test": "npm run lint && npm run build && npm run unit",
"unit": "jest",
"watch": "webpack --progress --colors --watch"
},
"author": "Massachusetts Institute of Technology",
@ -28,35 +28,51 @@
"autoprefixer": "7.1.1",
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-jest": "^20.0.3",
"babel-loader": "^7.0.0",
"babel-plugin-transform-object-rest-spread": "^6.22.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.22.0",
"classnames": "2.2.5",
"css-loader": "0.28.3",
"enzyme": "^2.8.2",
"eslint": "^3.16.1",
"eslint-config-import": "^0.13.0",
"eslint-config-scratch": "^3.0.0",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-react": "^7.0.1",
"gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder",
"html-webpack-plugin": "2.28.0",
"jest": "^20.0.4",
"keymirror": "0.1.1",
"lodash.bindall": "4.4.0",
"lodash.defaultsdeep": "4.6.0",
"minilog": "3.1.0",
"mkdirp": "^0.5.1",
"paper": "^0.11.4",
"paper": "0.11.4",
"postcss-import": "^10.0.0",
"postcss-loader": "^2.0.5",
"postcss-simple-vars": "^4.0.0",
"prop-types": "^15.5.10",
"react": "15.5.4",
"react": "15.6.1",
"react-dom": "15.5.4",
"react-intl": "2.3.0",
"react-redux": "5.0.5",
"react-test-renderer": "^15.5.4",
"redux": "3.6.0",
"redux-mock-store": "^1.2.3",
"redux-throttle": "0.1.1",
"regenerator-runtime": "^0.10.5",
"rimraf": "^2.6.1",
"style-loader": "^0.18.0",
"tap": "^10.2.0",
"webpack": "^2.4.1",
"webpack-dev-server": "^2.4.1"
},
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js"
}
}
}

View file

@ -1,7 +1,13 @@
module.exports = {
root: true,
extends: ['scratch', 'scratch/es6', 'scratch/react'],
extends: ['scratch', 'scratch/es6', 'scratch/react', 'import'],
env: {
browser: true
},
rules: {
'import/no-mutable-exports': 'error',
'import/no-commonjs': 'error',
'import/no-amd': 'error',
'import/no-nodejs-modules': 'error'
}
};

View file

@ -3,24 +3,27 @@ import React from 'react';
import PaperCanvas from '../containers/paper-canvas.jsx';
import BrushTool from '../containers/tools/brush-tool.jsx';
import EraserTool from '../containers/tools/eraser-tool.jsx';
import ToolTypes from '../tools/tool-types.js';
const PaintEditorComponent = props => (
<div>
<PaperCanvas
canvasId={props.canvasId}
tool={props.tool}
/>
<BrushTool canvasId={props.canvasId} />
<EraserTool canvasId={props.canvasId} />
</div>
);
class PaintEditorComponent extends React.Component {
render () {
return (
<div>
<PaperCanvas
ref={canvas => {
this.canvas = canvas;
}}
tool={this.props.tool}
/>
<BrushTool canvas={this.canvas} />
<EraserTool canvas={this.canvas} />
</div>
);
}
}
PaintEditorComponent.propTypes = {
canvasId: PropTypes.string.isRequired,
tool: PropTypes.shape({
name: PropTypes.string.isRequired
})
tool: PropTypes.oneOf(Object.keys(ToolTypes)).isRequired
};
module.exports = PaintEditorComponent;
export default PaintEditorComponent;

View file

@ -7,27 +7,25 @@ import {connect} from 'react-redux';
class PaintEditor extends React.Component {
componentDidMount () {
const onKeyPress = this.props.onKeyPress;
document.onkeydown = function (e) {
e = e || window.event;
onKeyPress(e);
};
document.addEventListener('keydown', this.props.onKeyPress);
}
componentWillUnmount () {
document.removeEventListener('keydown', this.props.onKeyPress);
}
render () {
const {
onKeyPress, // eslint-disable-line no-unused-vars
...props
} = this.props;
return (
<PaintEditorComponent
canvasId="paper-canvas"
tool={this.props.tool}
/>
<PaintEditorComponent {...props} />
);
}
}
PaintEditor.propTypes = {
onKeyPress: PropTypes.func.isRequired,
tool: PropTypes.shape({
name: PropTypes.string.isRequired
})
tool: PropTypes.oneOf(Object.keys(ToolTypes)).isRequired
};
const mapStateToProps = state => ({
@ -43,7 +41,7 @@ const mapDispatchToProps = dispatch => ({
}
});
module.exports = connect(
export default connect(
mapStateToProps,
mapDispatchToProps
)(PaintEditor);

View file

@ -4,13 +4,8 @@ import paper from 'paper';
import ToolTypes from '../tools/tool-types.js';
class PaperCanvas extends React.Component {
constructor (props) {
super(props);
this.state = {
};
}
componentDidMount () {
paper.setup('paper-canvas');
paper.setup(this.canvas);
// Create a Paper.js Path to draw a line into it:
const path = new paper.Path();
// Give the stroke a color
@ -24,25 +19,22 @@ class PaperCanvas extends React.Component {
// Draw the view now:
paper.view.draw();
}
componentWillReceiveProps (nextProps) {
if (nextProps.tool !== this.props.tool && nextProps.tool instanceof ToolTypes) {
// TODO switch tool
}
}
componentWillUnmount () {
paper.remove();
}
render () {
return (
<canvas id={this.props.canvasId} />
<canvas
ref={canvas => {
this.canvas = canvas;
}}
/>
);
}
}
PaperCanvas.propTypes = {
canvasId: PropTypes.string.isRequired,
tool: PropTypes.shape({
name: PropTypes.string.isRequired
})
tool: PropTypes.oneOf(Object.keys(ToolTypes)).isRequired
};
module.exports = PaperCanvas;
export default PaperCanvas;

View file

@ -22,6 +22,7 @@ class BrushTool extends React.Component {
}
componentDidMount () {
if (this.props.tool === BrushTool.TOOL_TYPE) {
debugger;
this.activateTool();
}
}
@ -38,8 +39,7 @@ class BrushTool extends React.Component {
return false; // Logic only component
}
activateTool () {
document.getElementById(this.props.canvasId)
.addEventListener('mousewheel', this.onScroll);
this.props.canvas.addEventListener('mousewheel', this.onScroll);
this.tool = new paper.Tool();
this.blob.activateTool(false /* isEraser */, this.tool, this.props.brushToolState);
@ -56,8 +56,7 @@ class BrushTool extends React.Component {
this.tool.activate();
}
deactivateTool () {
document.getElementById(this.props.canvasId)
.removeEventListener('mousewheel', this.onScroll);
this.props.canvas.removeEventListener('mousewheel', this.onScroll);
}
onScroll (event) {
if (event.deltaY < 0) {
@ -69,7 +68,7 @@ class BrushTool extends React.Component {
}
render () {
return (
<div />
<div>Brush Tool </div>
);
}
}
@ -78,7 +77,7 @@ BrushTool.propTypes = {
brushToolState: PropTypes.shape({
brushSize: PropTypes.number.isRequired
}),
canvasId: PropTypes.string.isRequired,
canvas: PropTypes.element,
changeBrushSize: PropTypes.func.isRequired,
tool: PropTypes.shape({
name: PropTypes.string.isRequired
@ -95,7 +94,7 @@ const mapDispatchToProps = dispatch => ({
}
});
module.exports = connect(
export default connect(
mapStateToProps,
mapDispatchToProps
)(BrushTool);

View file

@ -38,16 +38,14 @@ class EraserTool extends React.Component {
return false; // Logic only component
}
activateTool () {
document.getElementById(this.props.canvasId)
.addEventListener('mousewheel', this.onScroll);
this.props.canvas.addEventListener('mousewheel', this.onScroll);
this.tool = new paper.Tool();
this.blob.activateTool(true /* isEraser */, this.tool, this.props.eraserToolState);
this.tool.activate();
}
deactivateTool () {
document.getElementById(this.props.canvasId)
.removeEventListener('mousewheel', this.onScroll);
this.props.canvas.removeEventListener('mousewheel', this.onScroll);
this.blob.deactivateTool();
}
onScroll (event) {
@ -60,13 +58,13 @@ class EraserTool extends React.Component {
}
render () {
return (
<div />
<div>Eraser Tool </div>
);
}
}
EraserTool.propTypes = {
canvasId: PropTypes.string.isRequired,
canvas: PropTypes.element,
changeBrushSize: PropTypes.func.isRequired,
eraserToolState: PropTypes.shape({
brushSize: PropTypes.number.isRequired
@ -86,7 +84,7 @@ const mapDispatchToProps = dispatch => ({
}
});
module.exports = connect(
export default connect(
mapStateToProps,
mapDispatchToProps
)(EraserTool);

View file

@ -1,4 +1,4 @@
const minilog = require('minilog');
import minilog from 'minilog';
minilog.enable();
module.exports = minilog('paint-editor');
export default minilog('scratch-paint');

View file

@ -2,15 +2,12 @@ import React from 'react';
import ReactDOM from 'react-dom';
import PaintEditor from '..';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import throttle from 'redux-throttle';
import {createStore} from 'redux';
import reducer from '../reducers/combine-reducers';
const appTarget = document.createElement('div');
document.body.appendChild(appTarget);
const store = applyMiddleware(
throttle(300, {leading: true, trailing: true})
)(createStore)(
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

View file

@ -22,4 +22,4 @@ reducer.changeBrushSize = function (brushSize) {
};
};
module.exports = reducer;
export default reducer;

View file

@ -1,7 +1,10 @@
import {combineReducers} from 'redux';
import toolReducer from './tools';
import brushToolReducer from './brush-tool';
import eraserToolReducer from './eraser-tool';
module.exports = combineReducers({
tool: require('./tools'),
brushTool: require('./brush-tool'),
eraserTool: require('./eraser-tool')
export default combineReducers({
tool: toolReducer,
brushTool: brushToolReducer,
eraserTool: eraserToolReducer
});

View file

@ -22,4 +22,4 @@ reducer.changeBrushSize = function (brushSize) {
};
};
module.exports = reducer;
export default reducer;

View file

@ -1,5 +1,5 @@
const ToolTypes = require('../tools/tool-types');
const log = require('../log/log');
import ToolTypes from '../tools/tool-types';
import log from '../log/log';
const CHANGE_TOOL = 'scratch-paint/tools/CHANGE_TOOL';
const initialState = ToolTypes.BRUSH;
@ -8,10 +8,10 @@ const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState;
switch (action.type) {
case CHANGE_TOOL:
if (action.tool instanceof ToolTypes) {
if (action.tool in ToolTypes) {
return action.tool;
}
log.warn(`Warning: Tool type does not exist: ${action.tool}`);
log.warn(`Tool type does not exist: ${action.tool}`);
/* falls through */
default:
return state;
@ -22,11 +22,8 @@ const reducer = function (state, action) {
reducer.changeTool = function (tool) {
return {
type: CHANGE_TOOL,
tool: tool,
meta: {
throttle: 30
}
tool: tool
};
};
module.exports = reducer;
export default reducer;

View file

@ -1,12 +1,8 @@
class ToolTypes {
constructor (name) {
this.name = name;
}
toString () {
return `ToolTypes.${this.name}`;
}
}
ToolTypes.BRUSH = new ToolTypes('BRUSH');
ToolTypes.ERASER = new ToolTypes('ERASER');
import keyMirror from 'keymirror';
module.exports = ToolTypes;
const ToolTypes = keyMirror({
BRUSH: null,
ERASER: null
});
export default ToolTypes;

View file

@ -0,0 +1,3 @@
// __mocks__/fileMock.js
module.exports = 'test-file-stub';

View file

@ -0,0 +1,3 @@
// __mocks__/styleMock.js
module.exports = {};

View file

@ -1,25 +0,0 @@
const test = require('tap').test;
const ToolTypes = require('../src/tools/tool-types');
const reducer = require('../src/reducers/tools');
test('initialState', t => {
let defaultState;
t.assert(reducer(defaultState /* state */, {type: 'anything'} /* action */) instanceof ToolTypes);
t.end();
});
test('changeTool', t => {
let defaultState;
t.assert(reducer(defaultState /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */), ToolTypes.ERASER);
t.assert(
reducer(ToolTypes.ERASER /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */), ToolTypes.ERASER);
t.assert(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */), ToolTypes.ERASER);
t.end();
});
test('invalidChangeTool', t => {
t.assert(
reducer(ToolTypes.BRUSH /* state */, reducer.changeTool('non-existant tool') /* action */), ToolTypes.BRUSH);
t.assert(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool() /* action */), ToolTypes.BRUSH);
t.end();
});

View file

@ -0,0 +1,23 @@
/* eslint-env jest */
import ToolTypes from '../../src/tools/tool-types';
import reducer from '../../src/reducers/tools';
test('initialState', () => {
let defaultState;
expect(reducer(defaultState /* state */, {type: 'anything'} /* action */) in ToolTypes).toBeTruthy();
});
test('changeTool', () => {
let defaultState;
expect(reducer(defaultState /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */)).toBe(ToolTypes.ERASER);
expect(reducer(ToolTypes.ERASER /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */))
.toBe(ToolTypes.ERASER);
expect(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool(ToolTypes.ERASER) /* action */))
.toBe(ToolTypes.ERASER);
});
test('invalidChangeTool', () => {
expect(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool('non-existant tool') /* action */))
.toBe(ToolTypes.BRUSH);
expect(reducer(ToolTypes.BRUSH /* state */, reducer.changeTool() /* action */)).toBe(ToolTypes.BRUSH);
});