diff --git a/package-lock.json b/package-lock.json index ae7d260..8cef737 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7154,6 +7154,11 @@ } } }, + "keycode": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz", + "integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo=" + }, "killable": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.0.tgz", diff --git a/package.json b/package.json index f542f31..84288be 100755 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "bowser": "^1.8.1", "fit-curve": "^0.1.6", "imports-loader": "^0.7.1", + "keycode": "^2.1.9", "memoizee": "^0.3.9", "pouchdb": "^6.3.4", "proptypes": "^1.1.0", @@ -39,6 +40,7 @@ "react-notification-system-redux": "^1.2.0", "react-redux": "^5.0.6", "react-resize-detector": "^1.1.0", + "react-svg-inline": "^2.0.1", "redux-form": "^7.1.2", "redux-undo": "^1.0.0-beta9-9-7", "reselect": "^3.0.1", @@ -46,8 +48,7 @@ "shortid": "^2.2.8", "three": "^0.88.0", "three-js-csg": "github:Doodle3D/three-js-csg", - "valid-url": "^1.0.9", - "react-svg-inline": "^2.0.1" + "valid-url": "^1.0.9" }, "devDependencies": { "babel-cli": "6.24.1", diff --git a/src/components/App.js b/src/components/App.js index ca605d3..29f2c9d 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -13,6 +13,9 @@ import btnUndoImageURL from '../../img/mainmenu/btnUndo.png'; import btnRedoImageURL from '../../img/mainmenu/btnRedo.png'; import InlineIconsLoader from './InlineIconsLoader.js'; import JSONToSketchData from '../shape/JSONToSketchData.js'; +import keycode from 'keycode'; +import bowser from 'bowser'; +import * as d2Tools from '../constants/d2Tools.js'; const styles = { container: { @@ -63,7 +66,12 @@ class App extends React.Component { addImage: PropTypes.func.isRequired, undo: PropTypes.func.isRequired, redo: PropTypes.func.isRequired, - classes: PropTypes.objectOf(PropTypes.string) + classes: PropTypes.objectOf(PropTypes.string), + deleteSelection: PropTypes.func.isRequired, + selectAll: PropTypes.func.isRequired, + d2ChangeTool: PropTypes.func.isRequired, + moveSelection: PropTypes.func.isRequired, + selectedPen: PropTypes.string.isRequired }; componentDidMount() { @@ -71,6 +79,13 @@ class App extends React.Component { container.addEventListener('dragover', event => event.preventDefault()); container.addEventListener('drop', this.onDrop); + window.addEventListener('keydown', this.onKeyDown); + } + + componentWillUnmount() { + const { container } = this.refs; + container.removeEventListener('drop', this.onDrop); + window.removeEventListener('keydown', this.onKeyDown); } onDrop = async event => { @@ -100,9 +115,65 @@ class App extends React.Component { } }; - componentWillUnmount() { - const { container } = this.refs; - container.removeEventListener('drop', this.onDrop); + onKeyDown = (event) => { + const { undo, redo, deleteSelection, selectAll, d2ChangeTool, moveSelection, selectedPen } = this.props; + const { metaKey, ctrlKey, shiftKey } = event; + const key = keycode(event); + const commandKey = bowser.mac ? metaKey : ctrlKey; + + const targetTag = event.target.tagName.toLowerCase(); + if (targetTag === 'input' || targetTag === 'textarea') return; + + switch (key) { + case 'backspace': + case 'delete': + event.preventDefault(); + deleteSelection(); + break; + + case 'a': + if (commandKey) selectAll(); + break; + + case 'z': + if (commandKey) { + if (shiftKey) { + redo(); + } else { + undo(); + } + } + break; + + case 't': { + if (!commandKey) d2ChangeTool(d2Tools.TEXT); + break; + } + + case 'b': { + if (!commandKey) d2ChangeTool(selectedPen); + break; + } + + case 'left': + case 'right': + case 'up': + case 'down': { + const delta = shiftKey ? 10 : 1; + const deltas = { + left: { deltaX: -delta, deltaY: 0 }, + right: { deltaX: delta, deltaY: 0 }, + up: { deltaX: 0, deltaY: -delta }, + down: { deltaX: 0, deltaY: delta } + }; + + const { deltaX, deltaY } = deltas[key]; + moveSelection(deltaX, deltaY); + } + + default: + break; + } } render() { @@ -126,9 +197,15 @@ class App extends React.Component { } } -export default injectSheet(styles)(connect(null, { +export default injectSheet(styles)(connect(state => ({ + selectedPen: state.sketcher.present.menus['pen-tools'].selected +}), { undo: actions.undo.undo, redo: actions.undo.redo, openSketch: actions.openSketch, - addImage: actions.addImage + addImage: actions.addImage, + deleteSelection: actions.deleteSelection, + selectAll: actions.selectAll, + d2ChangeTool: actions.d2ChangeTool, + moveSelection: actions.moveSelection })(App));