From 4474ec3aa14ff426a479d009c644d53ef61b18be Mon Sep 17 00:00:00 2001 From: DD Liu Date: Wed, 29 Aug 2018 15:29:13 -0400 Subject: [PATCH] Keyboard shortcuts (#623) --- package.json | 1 + src/components/mode-tools/mode-tools.jsx | 2 +- src/containers/copy-paste-hoc.jsx | 138 +++++++++++++++++++++++ src/containers/font-dropdown.jsx | 6 +- src/containers/mode-tools.jsx | 63 ++--------- src/containers/paint-editor.jsx | 98 ++++++++++------ src/containers/paper-canvas.jsx | 27 +---- src/helper/bitmap.js | 22 +++- src/helper/selection.js | 24 ++-- src/index.js | 5 +- 10 files changed, 255 insertions(+), 131 deletions(-) create mode 100644 src/containers/copy-paste-hoc.jsx diff --git a/package.json b/package.json index 303a6c72..eddab59a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "classnames": "2.2.5", "keymirror": "0.1.1", "lodash.bindall": "4.4.0", + "lodash.omit": "4.5.0", "minilog": "3.1.0", "parse-color": "1.0.0", "prop-types": "^15.5.10", diff --git a/src/components/mode-tools/mode-tools.jsx b/src/components/mode-tools/mode-tools.jsx index e5950cc2..a755db99 100644 --- a/src/components/mode-tools/mode-tools.jsx +++ b/src/components/mode-tools/mode-tools.jsx @@ -317,7 +317,7 @@ ModeToolsComponent.propTypes = { clipboardItems: PropTypes.arrayOf(PropTypes.array), eraserValue: PropTypes.number, fillBitmapShapes: PropTypes.bool, - format: PropTypes.oneOf(Object.keys(Formats)).isRequired, + format: PropTypes.oneOf(Object.keys(Formats)), hasSelectedUncurvedPoints: PropTypes.bool, hasSelectedUnpointedPoints: PropTypes.bool, intl: intlShape.isRequired, diff --git a/src/containers/copy-paste-hoc.jsx b/src/containers/copy-paste-hoc.jsx new file mode 100644 index 00000000..542f4d01 --- /dev/null +++ b/src/containers/copy-paste-hoc.jsx @@ -0,0 +1,138 @@ +import paper from '@scratch/paper'; +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import omit from 'lodash.omit'; +import {connect} from 'react-redux'; + +import { + clearSelection, + getSelectedLeafItems, + getSelectedRootItems +} from '../helper/selection'; +import {isBitmap} from '../lib/format'; +import Formats from '../lib/format'; +import Modes from '../lib/modes'; + +import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items'; +import {incrementPasteOffset, setClipboardItems} from '../reducers/clipboard'; + +const CopyPasteHOC = function (WrappedComponent) { + class CopyPasteWrapper extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleCopy', + 'handlePaste' + ]); + } + handleCopy () { + let selectedItems = []; + if (this.props.mode === Modes.RESHAPE) { + const leafItems = getSelectedLeafItems(); + // Copy root of compound paths + for (const item of leafItems) { + if (item.parent && item.parent instanceof paper.CompoundPath) { + selectedItems.push(item.parent); + } else { + selectedItems.push(item); + } + } + } else { + selectedItems = getSelectedRootItems(); + } + if (selectedItems.length > 0) { + const clipboardItems = []; + for (let i = 0; i < selectedItems.length; i++) { + const jsonItem = selectedItems[i].exportJSON({asString: false}); + clipboardItems.push(jsonItem); + } + this.props.setClipboardItems(clipboardItems); + } + } + // Returns true if anything was pasted, false if nothing changed + handlePaste () { + clearSelection(this.props.clearSelectedItems); + + if (this.props.clipboardItems.length === 0) return false; + + let items = []; + for (let i = 0; i < this.props.clipboardItems.length; i++) { + const item = paper.Base.importJSON(this.props.clipboardItems[i]); + if (item) { + items.push(item); + } + } + if (!items.length) return false; + // If pasting a group or non-raster to bitmap, rasterize first + if (isBitmap(this.props.format) && !(items.length === 1 && items[0] instanceof paper.Raster)) { + const group = new paper.Group(items); + items = [group.rasterize()]; + group.remove(); + } + for (const item of items) { + const placedItem = paper.project.getActiveLayer().addChild(item); + placedItem.selected = true; + placedItem.position.x += 10 * this.props.pasteOffset; + placedItem.position.y += 10 * this.props.pasteOffset; + } + this.props.incrementPasteOffset(); + this.props.setSelectedItems(this.props.format); + return true; + } + render () { + const componentProps = omit(this.props, [ + 'clearSelectedItems', + 'clipboardItems', + 'incrementPasteOffset', + 'pasteOffset', + 'setClipboardItems', + 'setSelectedItems']); + return ( + + ); + } + } + + CopyPasteWrapper.propTypes = { + clearSelectedItems: PropTypes.func.isRequired, + clipboardItems: PropTypes.arrayOf(PropTypes.array), + format: PropTypes.oneOf(Object.keys(Formats)), + incrementPasteOffset: PropTypes.func.isRequired, + mode: PropTypes.oneOf(Object.keys(Modes)), + pasteOffset: PropTypes.number, + setClipboardItems: PropTypes.func.isRequired, + setSelectedItems: PropTypes.func.isRequired + }; + const mapStateToProps = state => ({ + clipboardItems: state.scratchPaint.clipboard.items, + format: state.scratchPaint.format, + mode: state.scratchPaint.mode, + pasteOffset: state.scratchPaint.clipboard.pasteOffset + }); + const mapDispatchToProps = dispatch => ({ + setClipboardItems: items => { + dispatch(setClipboardItems(items)); + }, + incrementPasteOffset: () => { + dispatch(incrementPasteOffset()); + }, + clearSelectedItems: () => { + dispatch(clearSelectedItems()); + }, + setSelectedItems: format => { + dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format))); + } + }); + + return connect( + mapStateToProps, + mapDispatchToProps + )(CopyPasteWrapper); +}; + +export default CopyPasteHOC; diff --git a/src/containers/font-dropdown.jsx b/src/containers/font-dropdown.jsx index 543186d9..f0eecdfa 100644 --- a/src/containers/font-dropdown.jsx +++ b/src/containers/font-dropdown.jsx @@ -10,7 +10,7 @@ import {changeFont} from '../reducers/font'; import {getSelectedLeafItems} from '../helper/selection'; import styles from '../components/font-dropdown/font-dropdown.css'; -class ModeToolsComponent extends React.Component { +class FontDropdown extends React.Component { constructor (props) { super(props); bindAll(this, [ @@ -164,7 +164,7 @@ class ModeToolsComponent extends React.Component { } } -ModeToolsComponent.propTypes = { +FontDropdown.propTypes = { changeFont: PropTypes.func.isRequired, font: PropTypes.string, onUpdateImage: PropTypes.func.isRequired @@ -182,4 +182,4 @@ const mapDispatchToProps = dispatch => ({ export default connect( mapStateToProps, mapDispatchToProps -)(ModeToolsComponent); +)(FontDropdown); diff --git a/src/containers/mode-tools.jsx b/src/containers/mode-tools.jsx index a1481b1e..02265c06 100644 --- a/src/containers/mode-tools.jsx +++ b/src/containers/mode-tools.jsx @@ -4,11 +4,11 @@ import PropTypes from 'prop-types'; import {connect} from 'react-redux'; import bindAll from 'lodash.bindall'; +import CopyPasteHOC from './copy-paste-hoc.jsx'; import ModeToolsComponent from '../components/mode-tools/mode-tools.jsx'; import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items'; import {incrementPasteOffset, setClipboardItems} from '../reducers/clipboard'; import { - clearSelection, deleteSelection, getSelectedLeafItems, getSelectedRootItems, @@ -29,7 +29,6 @@ class ModeTools extends React.Component { '_getSelectedUnpointedPoints', 'hasSelectedUncurvedPoints', 'hasSelectedUnpointedPoints', - 'handleCopyToClipboard', 'handleCurvePoints', 'handleFlipHorizontal', 'handleFlipVertical', @@ -191,57 +190,22 @@ class ModeTools extends React.Component { this._handleFlip(1, -1, selectedItems); } } + handlePasteFromClipboard () { + if (this.props.onPasteFromClipboard()) { + this.props.onUpdateImage(); + } + } handleDelete () { if (deleteSelection(this.props.mode, this.props.onUpdateImage)) { this.props.setSelectedItems(this.props.format); } } - handleCopyToClipboard () { - const selectedItems = getSelectedRootItems(); - if (selectedItems.length > 0) { - const clipboardItems = []; - for (let i = 0; i < selectedItems.length; i++) { - const jsonItem = selectedItems[i].exportJSON({asString: false}); - clipboardItems.push(jsonItem); - } - this.props.setClipboardItems(clipboardItems); - } - } - handlePasteFromClipboard () { - clearSelection(this.props.clearSelectedItems); - - if (this.props.clipboardItems.length > 0) { - let items = []; - for (let i = 0; i < this.props.clipboardItems.length; i++) { - const item = paper.Base.importJSON(this.props.clipboardItems[i]); - if (item) { - items.push(item); - } - } - if (!items.length) return; - // If pasting a group or non-raster to bitmap, rasterize firsts - if (isBitmap(this.props.format) && !(items.length === 1 && items[0] instanceof paper.Raster)) { - const group = new paper.Group(items); - items = [group.rasterize()]; - group.remove(); - } - for (const item of items) { - const placedItem = paper.project.getActiveLayer().addChild(item); - placedItem.selected = true; - placedItem.position.x += 10 * this.props.pasteOffset; - placedItem.position.y += 10 * this.props.pasteOffset; - } - this.props.incrementPasteOffset(); - this.props.setSelectedItems(this.props.format); - this.props.onUpdateImage(); - } - } render () { return ( ({ } }); -export default connect( +export default CopyPasteHOC(connect( mapStateToProps, mapDispatchToProps -)(ModeTools); +)(ModeTools)); diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx index 59396d64..008601a3 100644 --- a/src/containers/paint-editor.jsx +++ b/src/containers/paint-editor.jsx @@ -5,6 +5,8 @@ import log from '../log/log'; import React from 'react'; import {connect} from 'react-redux'; import PaintEditorComponent from '../components/paint-editor/paint-editor.jsx'; +import CopyPasteHOC from './copy-paste-hoc.jsx'; +import SelectionHOC from './selection-hoc.jsx'; import {changeMode} from '../reducers/modes'; import {changeFormat} from '../reducers/format'; @@ -16,12 +18,14 @@ import {updateViewBounds} from '../reducers/view-bounds'; import {setLayout} from '../reducers/layout'; import {getRaster, hideGuideLayers, showGuideLayers} from '../helper/layer'; -import {commitSelectionToBitmap, convertToBitmap, convertToVector, getHitBounds} from '../helper/bitmap'; +import {commitSelectionToBitmap, convertToBitmap, convertToVector, getHitBounds, + selectAllBitmap} from '../helper/bitmap'; import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo'; import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order'; import {groupSelection, ungroupSelection} from '../helper/group'; import {scaleWithStrokes} from '../helper/math'; -import {getSelectedLeafItems} from '../helper/selection'; +import {clearSelection, deleteSelection, getSelectedLeafItems, + selectAllItems, selectAllSegments} from '../helper/selection'; import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, SVG_ART_BOARD_WIDTH, SVG_ART_BOARD_HEIGHT} from '../helper/view'; import {resetZoom, zoomOnSelection} from '../helper/view'; import EyeDropperTool from '../helper/tools/eye-dropper'; @@ -57,6 +61,7 @@ class PaintEditor extends React.Component { 'canRedo', 'canUndo', 'switchMode', + 'onKeyPress', 'onMouseDown', 'setCanvas', 'setTextArea', @@ -73,14 +78,7 @@ class PaintEditor extends React.Component { this.props.setLayout(this.props.rtl ? 'rtl' : 'ltr'); } componentDidMount () { - document.addEventListener('keydown', (/* event */) => { - // Don't activate keyboard shortcuts during text editing - if (!this.props.textEditing) { - // @todo disabling keyboard shortcuts because there is a bug - // that is interfering with text editing. - // this.props.onKeyPress(event); - } - }); + document.addEventListener('keydown', this.onKeyPress); // document listeners used to detect if a mouse is down outside of the // canvas, and should therefore stop the eye dropper document.addEventListener('mousedown', this.onMouseDown); @@ -119,7 +117,7 @@ class PaintEditor extends React.Component { } } componentWillUnmount () { - document.removeEventListener('keydown', this.props.onKeyPress); + document.removeEventListener('keydown', this.onKeyPress); this.stopEyeDroppingLoop(); document.removeEventListener('mousedown', this.onMouseDown); document.removeEventListener('touchstart', this.onMouseDown); @@ -313,6 +311,56 @@ class PaintEditor extends React.Component { setTextArea (element) { this.setState({textArea: element}); } + onKeyPress (event) { + // Don't activate keyboard shortcuts during text editing + if (this.props.textEditing) return; + + if (event.key === 'Escape') { + event.preventDefault(); + clearSelection(this.props.clearSelectedItems); + } else if (event.key === 'Delete' || event.key === 'Backspace') { + if (deleteSelection(this.props.mode, this.handleUpdateImage)) { + this.handleSetSelectedItems(); + } + } else if (event.metaKey || event.ctrlKey) { + if (event.shiftKey && event.key === 'z') { + this.handleRedo(); + } else if (event.key === 'z') { + this.handleUndo(); + } else if (event.key === 'c') { + this.props.onCopyToClipboard(); + } else if (event.key === 'v') { + this.changeToASelectMode(); + if (this.props.onPasteFromClipboard()) { + this.handleUpdateImage(); + } + } else if (event.key === 'a') { + this.changeToASelectMode(); + event.preventDefault(); + this.selectAll(); + } + } + } + changeToASelectMode () { + if (isBitmap(this.props.format)) { + if (this.props.mode !== Modes.BIT_SELECT) { + this.props.changeMode(Modes.BIT_SELECT); + } + } else if (this.props.mode !== Modes.SELECT && this.props.mode !== Modes.RESHAPE) { + this.props.changeMode(Modes.SELECT); + } + } + selectAll () { + if (isBitmap(this.props.format)) { + selectAllBitmap(this.props.clearSelectedItems); + this.handleSetSelectedItems(); + } else if (this.props.mode === Modes.RESHAPE) { + if (selectAllSegments()) this.handleSetSelectedItems(); + } else { + // Disable lint for easier to read logic + if (selectAllItems()) this.handleSetSelectedItems(); // eslint-disable-line no-lonely-if + } + } onMouseDown (event) { if (event.target === paper.view.element && document.activeElement instanceof HTMLInputElement) { @@ -431,8 +479,9 @@ PaintEditor.propTypes = { isEyeDropping: PropTypes.bool, mode: PropTypes.oneOf(Object.keys(Modes)).isRequired, name: PropTypes.string, + onCopyToClipboard: PropTypes.func.isRequired, onDeactivateEyeDropper: PropTypes.func.isRequired, - onKeyPress: PropTypes.func.isRequired, + onPasteFromClipboard: PropTypes.func.isRequired, onRedo: PropTypes.func.isRequired, onUndo: PropTypes.func.isRequired, onUpdateImage: PropTypes.func.isRequired, @@ -471,27 +520,6 @@ const mapStateToProps = state => ({ viewBounds: state.scratchPaint.viewBounds }); const mapDispatchToProps = dispatch => ({ - onKeyPress: event => { - if (event.key === 'e') { - dispatch(changeMode(Modes.ERASER)); - } else if (event.key === 'b') { - dispatch(changeMode(Modes.BRUSH)); - } else if (event.key === 'l') { - dispatch(changeMode(Modes.LINE)); - } else if (event.key === 's') { - dispatch(changeMode(Modes.SELECT)); - } else if (event.key === 'w') { - dispatch(changeMode(Modes.RESHAPE)); - } else if (event.key === 'f') { - dispatch(changeMode(Modes.FILL)); - } else if (event.key === 't') { - dispatch(changeMode(Modes.TEXT)); - } else if (event.key === 'c') { - dispatch(changeMode(Modes.OVAL)); - } else if (event.key === 'r') { - dispatch(changeMode(Modes.RECT)); - } - }, changeMode: mode => { dispatch(changeMode(mode)); }, @@ -531,7 +559,7 @@ const mapDispatchToProps = dispatch => ({ } }); -export default connect( +export default SelectionHOC(CopyPasteHOC(connect( mapStateToProps, mapDispatchToProps -)(PaintEditor); +)(PaintEditor))); diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index 9aa9c6e3..8aa65ff8 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -4,16 +4,13 @@ import React from 'react'; import {connect} from 'react-redux'; import paper from '@scratch/paper'; import Formats from '../lib/format'; -import {isBitmap} from '../lib/format'; -import Modes from '../lib/modes'; import log from '../log/log'; import {performSnapshot} from '../helper/undo'; import {undoSnapshot, clearUndoState} from '../reducers/undo'; import {isGroup, ungroupItems} from '../helper/group'; import {clearRaster, getRaster, setupLayers} from '../helper/layer'; -import {deleteSelection, getSelectedLeafItems} from '../helper/selection'; -import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items'; +import {clearSelectedItems} from '../reducers/selected-items'; import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, resetZoom} from '../helper/view'; import {ensureClockwise, scaleWithStrokes} from '../helper/math'; import {clearHoveredItem} from '../reducers/hover'; @@ -28,12 +25,10 @@ class PaperCanvas extends React.Component { bindAll(this, [ 'setCanvas', 'importSvg', - 'handleKeyDown', 'switchCostume' ]); } componentDidMount () { - document.addEventListener('keydown', this.handleKeyDown); paper.setup(this.canvas); resetZoom(); this.props.updateViewBounds(paper.view.matrix); @@ -57,19 +52,6 @@ class PaperCanvas extends React.Component { } componentWillUnmount () { paper.remove(); - document.removeEventListener('keydown', this.handleKeyDown); - } - handleKeyDown (event) { - if (event.target instanceof HTMLInputElement) { - // Ignore delete if a text input field is focused - return; - } - // Backspace, delete - if (event.key === 'Delete' || event.key === 'Backspace') { - if (deleteSelection(this.props.mode, this.props.onUpdateImage)) { - this.props.setSelectedItems(this.props.format); - } - } } switchCostume (format, image, rotationCenterX, rotationCenterY) { for (const layer of paper.project.layers) { @@ -231,18 +213,14 @@ PaperCanvas.propTypes = { clearPasteOffset: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, clearUndo: PropTypes.func.isRequired, - format: PropTypes.oneOf(Object.keys(Formats)), // Internal, up-to-date data format image: PropTypes.oneOfType([ PropTypes.string, PropTypes.instanceOf(HTMLImageElement) ]), imageFormat: PropTypes.string, // The incoming image's data format, used during import. The user could switch this. imageId: PropTypes.string, - mode: PropTypes.oneOf(Object.keys(Modes)), - onUpdateImage: PropTypes.func.isRequired, rotationCenterX: PropTypes.number, rotationCenterY: PropTypes.number, - setSelectedItems: PropTypes.func.isRequired, undoSnapshot: PropTypes.func.isRequired, updateViewBounds: PropTypes.func.isRequired }; @@ -257,9 +235,6 @@ const mapDispatchToProps = dispatch => ({ clearUndo: () => { dispatch(clearUndoState()); }, - setSelectedItems: format => { - dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format))); - }, clearSelectedItems: () => { dispatch(clearSelectedItems()); }, diff --git a/src/helper/bitmap.js b/src/helper/bitmap.js index 4e227b87..8eb670cc 100644 --- a/src/helper/bitmap.js +++ b/src/helper/bitmap.js @@ -1,6 +1,7 @@ import paper from '@scratch/paper'; import {createCanvas, clearRaster, getRaster, hideGuideLayers, showGuideLayers} from './layer'; import {getGuideColor} from './guides'; +import {clearSelection} from './selection'; import {inlineSvgFonts} from 'scratch-svg-renderer'; const forEachLinePoint = function (point1, point2, callback) { @@ -347,7 +348,7 @@ const convertToBitmap = function (clearSelectedItems, onUpdateImage) { // @todo if the active layer contains only rasters, drawing them directly to the raster layer // would be more efficient. - clearSelectedItems(); + clearSelection(clearSelectedItems); // Export svg const guideLayers = hideGuideLayers(true /* includeRaster */); @@ -392,7 +393,7 @@ const convertToBitmap = function (clearSelectedItems, onUpdateImage) { }; const convertToVector = function (clearSelectedItems, onUpdateImage) { - clearSelectedItems(); + clearSelection(clearSelectedItems); const trimmedRaster = trim_(getRaster()); if (trimmedRaster) { paper.project.activeLayer.addChild(trimmedRaster); @@ -722,6 +723,20 @@ const commitSelectionToBitmap = function (selection, bitmap) { commitArbitraryTransformation_(selection, bitmap); }; +const selectAllBitmap = function (clearSelectedItems) { + clearSelection(clearSelectedItems); + + // Pull raster to active layer + const raster = getRaster(); + raster.guide = false; + raster.locked = false; + raster.parent = paper.project.activeLayer; + raster.selected = true; + + // Clear raster layer + clearRaster(); +}; + export { commitSelectionToBitmap, convertToBitmap, @@ -736,5 +751,6 @@ export { forEachLinePoint, flipBitmapHorizontal, flipBitmapVertical, - scaleBitmap + scaleBitmap, + selectAllBitmap }; diff --git a/src/helper/selection.js b/src/helper/selection.js index dc6c65a5..20536141 100644 --- a/src/helper/selection.js +++ b/src/helper/selection.js @@ -108,30 +108,34 @@ const setItemSelection = function (item, state, fullySelected) { } _setGroupSelection(item, state, fullySelected); } - // @todo: Update toolbar state on change }; +/** @return {boolean} true if anything was selected */ const selectAllItems = function () { const items = getAllSelectableRootItems(); + if (items.length === 0) return false; for (let i = 0; i < items.length; i++) { setItemSelection(items[i], true); } + return true; }; +/** @return {boolean} true if anything was selected */ const selectAllSegments = function () { const items = getAllSelectableRootItems(); + if (items.length === 0) return false; for (let i = 0; i < items.length; i++) { selectItemSegments(items[i], true); } + return true; }; /** @param {!function} dispatchClearSelect Function to update the Redux select state */ const clearSelection = function (dispatchClearSelect) { paper.project.deselectAll(); - // @todo: Update toolbar state on change dispatchClearSelect(); }; @@ -148,6 +152,14 @@ const getSelectedRootItems = function () { for (const item of allItems) { if (item.selected) { items.push(item); + } else if (item instanceof paper.CompoundPath) { + // Consider a compound path selected if any of its paths are selected + for (const child of item.children) { + if (child.selected) { + items.push(item); + break; + } + } } } @@ -412,13 +424,10 @@ const selectRootItem = function () { } }; -const shouldShowSelectAll = function () { - return paper.project.getItems({class: paper.PathItem}).length > 0; -}; - export { getItems, getAllRootItems, + getAllSelectableRootItems, selectAllItems, selectAllSegments, clearSelection, @@ -429,6 +438,5 @@ export { getSelectedRootItems, getSelectedSegments, processRectangularSelection, - selectRootItem, - shouldShowSelectAll + selectRootItem }; diff --git a/src/index.js b/src/index.js index 9ed86c0e..b7d91a70 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,7 @@ import PaintEditor from './containers/paint-editor.jsx'; -import SelectionHOC from './containers/selection-hoc.jsx'; import ScratchPaintReducer from './reducers/scratch-paint-reducer'; -const Wrapped = SelectionHOC(PaintEditor); - export { - Wrapped as default, + PaintEditor as default, ScratchPaintReducer };