mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 13:32:28 -05:00
call undo
This commit is contained in:
parent
8baf731328
commit
28464b237b
25 changed files with 243 additions and 97 deletions
|
@ -58,11 +58,13 @@ class PaintEditorComponent extends React.Component {
|
|||
<div className={styles.buttonGroup}>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={this.props.onUndo}
|
||||
>
|
||||
Undo
|
||||
</button>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={this.props.onRedo}
|
||||
>
|
||||
Redo
|
||||
</button>
|
||||
|
@ -154,6 +156,8 @@ class PaintEditorComponent extends React.Component {
|
|||
|
||||
PaintEditorComponent.propTypes = {
|
||||
intl: intlShape,
|
||||
onRedo: PropTypes.func.isRequired,
|
||||
onUndo: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
rotationCenterX: PropTypes.number,
|
||||
rotationCenterY: PropTypes.number,
|
||||
|
|
|
@ -4,6 +4,7 @@ import BroadBrushHelper from './broad-brush-helper';
|
|||
import SegmentBrushHelper from './segment-brush-helper';
|
||||
import {MIXED, styleCursorPreview} from '../../helper/style-path';
|
||||
import {clearSelection} from '../../helper/selection';
|
||||
import {performSnapshot} from '../../helper/undo';
|
||||
|
||||
/**
|
||||
* Shared code for the brush and eraser mode. Adds functions on the paper tool object
|
||||
|
@ -29,11 +30,12 @@ class Blobbiness {
|
|||
* @param {function} updateCallback call when the drawing has changed to let listeners know
|
||||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
*/
|
||||
constructor (updateCallback, clearSelectedItems) {
|
||||
constructor (updateCallback, clearSelectedItems, undoSnapshot) {
|
||||
this.broadBrushHelper = new BroadBrushHelper();
|
||||
this.segmentBrushHelper = new SegmentBrushHelper();
|
||||
this.updateCallback = updateCallback;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
|
||||
// The following are stored to check whether these have changed and the cursor preview needs to be redrawn.
|
||||
this.strokeColor = null;
|
||||
|
@ -144,6 +146,7 @@ class Blobbiness {
|
|||
|
||||
blob.cursorPreview.visible = false;
|
||||
blob.updateCallback();
|
||||
performSnapshot(blob.undoSnapshot);
|
||||
blob.cursorPreview.visible = true;
|
||||
blob.cursorPreview.bringToFront();
|
||||
blob.cursorPreview.position = event.point;
|
||||
|
@ -234,8 +237,6 @@ class Blobbiness {
|
|||
paths.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// TODO: Add back undo
|
||||
// pg.undo.snapshot('broadbrush');
|
||||
}
|
||||
|
||||
mergeEraser (lastPath) {
|
||||
|
@ -284,8 +285,6 @@ class Blobbiness {
|
|||
}
|
||||
}
|
||||
lastPath.remove();
|
||||
// TODO add back undo
|
||||
// pg.undo.snapshot('eraser');
|
||||
continue;
|
||||
}
|
||||
// Erase
|
||||
|
@ -358,8 +357,6 @@ class Blobbiness {
|
|||
items[i].remove();
|
||||
}
|
||||
lastPath.remove();
|
||||
// TODO: Add back undo handling
|
||||
// pg.undo.snapshot('eraser');
|
||||
}
|
||||
|
||||
colorMatch (existingPath, addedPath) {
|
||||
|
|
|
@ -4,10 +4,13 @@ import {connect} from 'react-redux';
|
|||
import bindAll from 'lodash.bindall';
|
||||
import Modes from '../modes/modes';
|
||||
import Blobbiness from './blob/blob';
|
||||
|
||||
import {changeBrushSize} from '../reducers/brush-mode';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
import {clearSelection} from '../helper/selection';
|
||||
|
||||
import BrushModeComponent from '../components/brush-mode.jsx';
|
||||
|
||||
class BrushMode extends React.Component {
|
||||
|
@ -18,7 +21,8 @@ class BrushMode extends React.Component {
|
|||
'deactivateTool',
|
||||
'onScroll'
|
||||
]);
|
||||
this.blob = new Blobbiness(this.props.onUpdateSvg, this.props.clearSelectedItems);
|
||||
this.blob = new Blobbiness(
|
||||
this.props.onUpdateSvg, this.props.clearSelectedItems, this.props.undoSnapshot);
|
||||
}
|
||||
componentDidMount () {
|
||||
if (this.props.isBrushModeActive) {
|
||||
|
@ -87,7 +91,8 @@ BrushMode.propTypes = {
|
|||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isBrushModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
undoSnapshot: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -104,6 +109,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.BRUSH));
|
||||
},
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import Modes from '../modes/modes';
|
|||
import Blobbiness from './blob/blob';
|
||||
import {changeBrushSize} from '../reducers/eraser-mode';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
import EraserModeComponent from '../components/eraser-mode.jsx';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
|
||||
|
@ -17,7 +18,8 @@ class EraserMode extends React.Component {
|
|||
'deactivateTool',
|
||||
'onScroll'
|
||||
]);
|
||||
this.blob = new Blobbiness(this.props.onUpdateSvg, this.props.clearSelectedItems);
|
||||
this.blob = new Blobbiness(
|
||||
this.props.onUpdateSvg, this.props.clearSelectedItems, this.props.undoSnapshot);
|
||||
}
|
||||
componentDidMount () {
|
||||
if (this.props.isEraserModeActive) {
|
||||
|
@ -72,7 +74,8 @@ EraserMode.propTypes = {
|
|||
}),
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isEraserModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
undoSnapshot: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -88,6 +91,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.ERASER));
|
||||
},
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -2,13 +2,16 @@ import {connect} from 'react-redux';
|
|||
import {changeFillColor} from '../reducers/fill-color';
|
||||
import FillColorIndicatorComponent from '../components/fill-color-indicator.jsx';
|
||||
import {applyFillColorToSelection} from '../helper/style-path';
|
||||
import {performSnapshot} from '../helper/undo';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
fillColor: state.scratchPaint.color.fillColor
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChangeFillColor: fillColor => {
|
||||
applyFillColorToSelection(fillColor);
|
||||
applyFillColorToSelection(fillColor, undoSnapshot);
|
||||
performSnapshot(snapshot => dispatch(undoSnapshot(snapshot)));
|
||||
dispatch(changeFillColor(fillColor));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import paper from 'paper';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
|
@ -6,10 +7,13 @@ import Modes from '../modes/modes';
|
|||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import {clearSelection, getSelectedLeafItems} from '../helper/selection';
|
||||
import {MIXED} from '../helper/style-path';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import LineModeComponent from '../components/line-mode.jsx';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import paper from 'paper';
|
||||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import {performSnapshot} from '../helper/undo';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
import LineModeComponent from '../components/line-mode.jsx';
|
||||
|
||||
class LineMode extends React.Component {
|
||||
static get SNAP_TOLERANCE () {
|
||||
|
@ -209,10 +213,9 @@ class LineMode extends React.Component {
|
|||
this.props.onUpdateSvg();
|
||||
this.props.setSelectedItems();
|
||||
|
||||
// TODO add back undo
|
||||
// if (this.path) {
|
||||
// pg.undo.snapshot('line');
|
||||
// }
|
||||
if (this.path) {
|
||||
performSnapshot(this.props.undoSnapshot);
|
||||
}
|
||||
}
|
||||
toleranceSquared () {
|
||||
return Math.pow(LineMode.SNAP_TOLERANCE / paper.view.zoom, 2);
|
||||
|
@ -284,7 +287,8 @@ LineMode.propTypes = {
|
|||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isLineModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
setSelectedItems: PropTypes.func.isRequired
|
||||
setSelectedItems: PropTypes.func.isRequired,
|
||||
undoSnapshot: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -303,6 +307,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.LINE));
|
||||
},
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import PaintEditorComponent from '../components/paint-editor.jsx';
|
||||
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {undo, redo} from '../reducers/undo';
|
||||
|
||||
import {getGuideLayer} from '../helper/layer';
|
||||
import {performUndo, performRedo} from '../helper/undo';
|
||||
|
||||
import Modes from '../modes/modes';
|
||||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
|
@ -12,7 +17,9 @@ class PaintEditor extends React.Component {
|
|||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleUpdateSvg'
|
||||
'handleUpdateSvg',
|
||||
'handleUndo',
|
||||
'handleRedo'
|
||||
]);
|
||||
}
|
||||
componentDidMount () {
|
||||
|
@ -34,12 +41,20 @@ class PaintEditor extends React.Component {
|
|||
paper.project.view.center.y - bounds.y);
|
||||
getGuideLayer().visible = true;
|
||||
}
|
||||
handleUndo () {
|
||||
performUndo(this.props.undoState, this.props.onUndo);
|
||||
}
|
||||
handleRedo () {
|
||||
performRedo(this.props.undoState, this.props.onRedo);
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<PaintEditorComponent
|
||||
rotationCenterX={this.props.rotationCenterX}
|
||||
rotationCenterY={this.props.rotationCenterY}
|
||||
svg={this.props.svg}
|
||||
onRedo={this.handleRedo}
|
||||
onUndo={this.handleUndo}
|
||||
onUpdateSvg={this.handleUpdateSvg}
|
||||
/>
|
||||
);
|
||||
|
@ -48,12 +63,21 @@ class PaintEditor extends React.Component {
|
|||
|
||||
PaintEditor.propTypes = {
|
||||
onKeyPress: PropTypes.func.isRequired,
|
||||
onRedo: PropTypes.func.isRequired,
|
||||
onUndo: PropTypes.func.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
rotationCenterX: PropTypes.number,
|
||||
rotationCenterY: PropTypes.number,
|
||||
svg: PropTypes.string
|
||||
svg: PropTypes.string,
|
||||
undoState: PropTypes.shape({
|
||||
stack: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
pointer: PropTypes.number.isRequired
|
||||
})
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
undoState: state.scratchPaint.undo
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onKeyPress: event => {
|
||||
if (event.key === 'e') {
|
||||
|
@ -65,10 +89,16 @@ const mapDispatchToProps = dispatch => ({
|
|||
} else if (event.key === 's') {
|
||||
dispatch(changeMode(Modes.SELECT));
|
||||
}
|
||||
},
|
||||
onUndo: () => {
|
||||
dispatch(undo());
|
||||
},
|
||||
onRedo: () => {
|
||||
dispatch(redo());
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PaintEditor);
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import bindAll from 'lodash.bindall';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import paper from 'paper';
|
||||
|
||||
import {performSnapshot} from '../helper/undo';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
import styles from './paper-canvas.css';
|
||||
|
||||
class PaperCanvas extends React.Component {
|
||||
|
@ -20,6 +24,7 @@ class PaperCanvas extends React.Component {
|
|||
if (this.props.svg) {
|
||||
this.importSvg(this.props.svg, this.props.rotationCenterX, this.props.rotationCenterY);
|
||||
}
|
||||
performSnapshot(this.props.undoSnapshot);
|
||||
}
|
||||
componentWillReceiveProps (newProps) {
|
||||
paper.project.activeLayer.removeChildren();
|
||||
|
@ -85,7 +90,16 @@ PaperCanvas.propTypes = {
|
|||
canvasRef: PropTypes.func,
|
||||
rotationCenterX: PropTypes.number,
|
||||
rotationCenterY: PropTypes.number,
|
||||
svg: PropTypes.string
|
||||
svg: PropTypes.string,
|
||||
undoSnapshot: PropTypes.func.isRequired
|
||||
};
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
}
|
||||
});
|
||||
|
||||
export default PaperCanvas;
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(PaperCanvas);
|
||||
|
|
|
@ -8,6 +8,7 @@ import {changeMode} from '../reducers/modes';
|
|||
import {clearHoveredItem, setHoveredItem} from '../reducers/hover';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
import ReshapeTool from '../helper/selection-tools/reshape-tool';
|
||||
import ReshapeModeComponent from '../components/reshape-mode.jsx';
|
||||
|
@ -45,7 +46,9 @@ class ReshapeMode extends React.Component {
|
|||
this.props.clearHoveredItem,
|
||||
this.props.setSelectedItems,
|
||||
this.props.clearSelectedItems,
|
||||
this.props.onUpdateSvg);
|
||||
this.props.onUpdateSvg,
|
||||
this.props.undoSnapshot
|
||||
);
|
||||
this.tool.setPrevHoveredItemId(this.props.hoveredItemId);
|
||||
this.tool.activate();
|
||||
}
|
||||
|
@ -70,7 +73,8 @@ ReshapeMode.propTypes = {
|
|||
isReshapeModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
setHoveredItem: PropTypes.func.isRequired,
|
||||
setSelectedItems: PropTypes.func.isRequired
|
||||
setSelectedItems: PropTypes.func.isRequired,
|
||||
undoSnapshot: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -92,6 +96,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.RESHAPE));
|
||||
},
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import Modes from '../modes/modes';
|
|||
import {changeMode} from '../reducers/modes';
|
||||
import {clearHoveredItem, setHoveredItem} from '../reducers/hover';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import SelectTool from '../helper/selection-tools/select-tool';
|
||||
|
@ -45,7 +46,9 @@ class SelectMode extends React.Component {
|
|||
this.props.clearHoveredItem,
|
||||
this.props.setSelectedItems,
|
||||
this.props.clearSelectedItems,
|
||||
this.props.onUpdateSvg);
|
||||
this.props.onUpdateSvg,
|
||||
this.props.undoSnapshot
|
||||
);
|
||||
this.tool.activate();
|
||||
}
|
||||
deactivateTool () {
|
||||
|
@ -68,7 +71,8 @@ SelectMode.propTypes = {
|
|||
isSelectModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
setHoveredItem: PropTypes.func.isRequired,
|
||||
setSelectedItems: PropTypes.func.isRequired
|
||||
setSelectedItems: PropTypes.func.isRequired,
|
||||
undoSnapshot: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
|
@ -90,6 +94,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.SELECT));
|
||||
},
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -2,13 +2,16 @@ import {connect} from 'react-redux';
|
|||
import {changeStrokeColor} from '../reducers/stroke-color';
|
||||
import StrokeColorIndicatorComponent from '../components/stroke-color-indicator.jsx';
|
||||
import {applyStrokeColorToSelection} from '../helper/style-path';
|
||||
import {performSnapshot} from '../helper/undo';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
strokeColor: state.scratchPaint.color.strokeColor
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChangeStrokeColor: strokeColor => {
|
||||
applyStrokeColorToSelection(strokeColor);
|
||||
applyStrokeColorToSelection(strokeColor, undoSnapshot);
|
||||
performSnapshot(snapshot => dispatch(undoSnapshot(snapshot)));
|
||||
dispatch(changeStrokeColor(strokeColor));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,13 +2,16 @@ import {connect} from 'react-redux';
|
|||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import StrokeWidthIndicatorComponent from '../components/stroke-width-indicator.jsx';
|
||||
import {applyStrokeWidthToSelection} from '../helper/style-path';
|
||||
import {performSnapshot} from '../helper/undo';
|
||||
import {undoSnapshot} from '../reducers/undo';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
strokeWidth: state.scratchPaint.color.strokeWidth
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onChangeStrokeWidth: strokeWidth => {
|
||||
applyStrokeWidthToSelection(strokeWidth);
|
||||
applyStrokeWidthToSelection(strokeWidth, undoSnapshot);
|
||||
performSnapshot(snapshot => dispatch(undoSnapshot(snapshot)));
|
||||
dispatch(changeStrokeWidth(strokeWidth));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -36,16 +36,16 @@ class BoundingBoxTool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot) {
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.mode = null;
|
||||
this.boundsPath = null;
|
||||
this.boundsScaleHandles = [];
|
||||
this.boundsRotHandles = [];
|
||||
this._modeMap = {};
|
||||
this._modeMap[Modes.SCALE] = new ScaleTool(onUpdateSvg);
|
||||
this._modeMap[Modes.ROTATE] = new RotateTool(onUpdateSvg);
|
||||
this._modeMap[Modes.MOVE] = new MoveTool(setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
this._modeMap[Modes.SCALE] = new ScaleTool(onUpdateSvg, undoSnapshot);
|
||||
this._modeMap[Modes.ROTATE] = new RotateTool(onUpdateSvg, undoSnapshot);
|
||||
this._modeMap[Modes.MOVE] = new MoveTool(setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {clearSelection, getSelectedLeafItems} from '../selection';
|
||||
import {performSnapshot} from '../undo';
|
||||
|
||||
/** Sub tool of the Reshape tool for moving handles, which adjust bezier curves. */
|
||||
class HandleTool {
|
||||
|
@ -7,11 +8,13 @@ class HandleTool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot) {
|
||||
this.hitType = null;
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
this.selectedItems = [];
|
||||
}
|
||||
/**
|
||||
* @param {!object} hitProperties Describes the mouse event
|
||||
|
@ -28,9 +31,9 @@ class HandleTool {
|
|||
this.hitType = hitProperties.hitResult.type;
|
||||
}
|
||||
onMouseDrag (event) {
|
||||
const selectedItems = getSelectedLeafItems();
|
||||
this.selectedItems = getSelectedLeafItems();
|
||||
|
||||
for (const item of selectedItems) {
|
||||
for (const item of this.selectedItems) {
|
||||
for (const seg of item.segments) {
|
||||
// add the point of the segment before the drag started
|
||||
// for later use in the snap calculation
|
||||
|
@ -66,9 +69,25 @@ class HandleTool {
|
|||
}
|
||||
}
|
||||
onMouseUp () {
|
||||
// @todo add back undo
|
||||
// resetting the items and segments origin points for the next usage
|
||||
let moved = false;
|
||||
for (const item of this.selectedItems) {
|
||||
if (!item.segments) {
|
||||
return;
|
||||
}
|
||||
for (const seg of item.segments) {
|
||||
if (seg.origPoint && !seg.equals(seg.origPoint)) {
|
||||
moved = true;
|
||||
}
|
||||
seg.origPoint = null;
|
||||
}
|
||||
}
|
||||
if (moved) {
|
||||
performSnapshot(this.undoSnapshot);
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
this.selectedItems = [];
|
||||
}
|
||||
}
|
||||
|
||||
export default HandleTool;
|
||||
|
|
|
@ -2,6 +2,7 @@ import {isGroup} from '../group';
|
|||
import {isCompoundPathItem, getRootItem} from '../item';
|
||||
import {snapDeltaToAngle} from '../math';
|
||||
import {clearSelection, cloneSelection, getSelectedLeafItems, setItemSelection} from '../selection';
|
||||
import {performSnapshot} from '../undo';
|
||||
|
||||
/**
|
||||
* Tool to handle dragging an item to reposition it in a selection mode.
|
||||
|
@ -12,11 +13,12 @@ class MoveTool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot) {
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.selectedItems = null;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +53,7 @@ class MoveTool {
|
|||
}
|
||||
this._select(item, true, hitProperties.subselect);
|
||||
}
|
||||
if (hitProperties.clone) cloneSelection(hitProperties.subselect);
|
||||
if (hitProperties.clone) cloneSelection(hitProperties.subselect, this.undoSnapshot);
|
||||
this.selectedItems = getSelectedLeafItems();
|
||||
}
|
||||
/**
|
||||
|
@ -94,16 +96,21 @@ class MoveTool {
|
|||
}
|
||||
}
|
||||
onMouseUp () {
|
||||
let moved = false;
|
||||
// resetting the items origin point for the next usage
|
||||
for (const item of this.selectedItems) {
|
||||
if (item.data.origPos && !item.position.equals(item.data.origPos)) {
|
||||
moved = true;
|
||||
}
|
||||
item.data.origPos = null;
|
||||
}
|
||||
this.selectedItems = null;
|
||||
|
||||
// @todo add back undo
|
||||
// pg.undo.snapshot('moveSelection');
|
||||
if (moved) {
|
||||
performSnapshot(this.undoSnapshot);
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MoveTool;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import paper from 'paper';
|
||||
import {snapDeltaToAngle} from '../math';
|
||||
import {clearSelection, getSelectedLeafItems} from '../selection';
|
||||
import {performSnapshot} from '../undo';
|
||||
|
||||
/** Subtool of ReshapeTool for moving control points. */
|
||||
class PointTool {
|
||||
|
@ -9,7 +10,7 @@ class PointTool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot) {
|
||||
/**
|
||||
* Deselection often does not happen until mouse up. If the mouse is dragged before
|
||||
* mouse up, deselection is cancelled. This variable keeps track of which paper.Item to deselect.
|
||||
|
@ -29,6 +30,7 @@ class PointTool {
|
|||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,11 +168,15 @@ class PointTool {
|
|||
}
|
||||
onMouseUp () {
|
||||
// resetting the items and segments origin points for the next usage
|
||||
let moved = false;
|
||||
for (const item of this.selectedItems) {
|
||||
if (!item.segments) {
|
||||
return;
|
||||
}
|
||||
for (const seg of item.segments) {
|
||||
if (seg.origPoint && !seg.equals(seg.origPoint)) {
|
||||
moved = true;
|
||||
}
|
||||
seg.origPoint = null;
|
||||
}
|
||||
}
|
||||
|
@ -193,9 +199,11 @@ class PointTool {
|
|||
}
|
||||
this.selectedItems = null;
|
||||
this.setSelectedItems();
|
||||
// @todo add back undo
|
||||
if (moved) {
|
||||
performSnapshot(this.undoSnapshot);
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PointTool;
|
||||
|
|
|
@ -45,18 +45,19 @@ class ReshapeTool extends paper.Tool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot) {
|
||||
super();
|
||||
this.setHoveredItem = setHoveredItem;
|
||||
this.clearHoveredItem = clearHoveredItem;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
this.prevHoveredItemId = null;
|
||||
this.lastEvent = null;
|
||||
this.mode = ReshapeModes.SELECTION_BOX;
|
||||
this._modeMap = {};
|
||||
this._modeMap[ReshapeModes.FILL] = new MoveTool(setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
this._modeMap[ReshapeModes.POINT] = new PointTool(setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
this._modeMap[ReshapeModes.HANDLE] = new HandleTool(setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
this._modeMap[ReshapeModes.FILL] = new MoveTool(setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot);
|
||||
this._modeMap[ReshapeModes.POINT] = new PointTool(setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot);
|
||||
this._modeMap[ReshapeModes.HANDLE] = new HandleTool(setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot);
|
||||
this._modeMap[ReshapeModes.SELECTION_BOX] =
|
||||
new SelectionBoxTool(Modes.RESHAPE, setSelectedItems, clearSelectedItems);
|
||||
|
||||
|
@ -221,7 +222,7 @@ class ReshapeTool extends paper.Tool {
|
|||
handleKeyUp (event) {
|
||||
// Backspace, delete
|
||||
if (event.key === 'delete' || event.key === 'backspace') {
|
||||
deleteSelection(Modes.RESHAPE);
|
||||
deleteSelection(Modes.RESHAPE, this.undoSnapshot);
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import paper from 'paper';
|
||||
import {performSnapshot} from '../undo';
|
||||
|
||||
/**
|
||||
* Tool to handle rotation when dragging the rotation handle in the bounding box tool.
|
||||
|
@ -7,11 +8,12 @@ class RotateTool {
|
|||
/**
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (onUpdateSvg) {
|
||||
constructor (onUpdateSvg, undoSnapshot) {
|
||||
this.rotItems = [];
|
||||
this.rotGroupPivot = null;
|
||||
this.prevRot = [];
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +65,7 @@ class RotateTool {
|
|||
this.rotGroupPivot = null;
|
||||
this.prevRot = [];
|
||||
|
||||
// @todo add back undo
|
||||
performSnapshot(this.undoSnapshot);
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import paper from 'paper';
|
||||
import {performSnapshot} from '../undo';
|
||||
|
||||
/**
|
||||
* Tool to handle scaling items by pulling on the handles around the edges of the bounding
|
||||
|
@ -8,7 +9,7 @@ class ScaleTool {
|
|||
/**
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (onUpdateSvg) {
|
||||
constructor (onUpdateSvg, undoSnapshot) {
|
||||
this.pivot = null;
|
||||
this.origPivot = null;
|
||||
this.corner = null;
|
||||
|
@ -22,6 +23,7 @@ class ScaleTool {
|
|||
this.boundsScaleHandles = [];
|
||||
this.boundsRotHandles = [];
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.undoSnapshot = undoSnapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,7 +159,7 @@ class ScaleTool {
|
|||
}
|
||||
this.itemGroup.remove();
|
||||
|
||||
// @todo add back undo
|
||||
performSnapshot(this.undoSnapshot);
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
_getRectCornerNameByIndex (index) {
|
||||
|
|
|
@ -25,12 +25,12 @@ class SelectTool extends paper.Tool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot) {
|
||||
super();
|
||||
this.setHoveredItem = setHoveredItem;
|
||||
this.clearHoveredItem = clearHoveredItem;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.boundingBoxTool = new BoundingBoxTool(setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
this.boundingBoxTool = new BoundingBoxTool(setSelectedItems, clearSelectedItems, onUpdateSvg, undoSnapshot);
|
||||
this.selectionBoxTool = new SelectionBoxTool(Modes.SELECT, setSelectedItems, clearSelectedItems);
|
||||
this.selectionBoxMode = false;
|
||||
this.prevHoveredItemId = null;
|
||||
|
@ -126,7 +126,7 @@ class SelectTool extends paper.Tool {
|
|||
handleKeyUp (event) {
|
||||
// Backspace, delete
|
||||
if (event.key === 'delete' || event.key === 'backspace') {
|
||||
deleteSelection(Modes.SELECT);
|
||||
deleteSelection(Modes.SELECT, this.undoSnapshot);
|
||||
this.clearHoveredItem();
|
||||
this.boundingBoxTool.removeBoundsPath();
|
||||
this.onUpdateSvg();
|
||||
|
|
|
@ -4,6 +4,7 @@ import Modes from '../modes/modes';
|
|||
import {getItemsGroup, isGroup} from './group';
|
||||
import {getRootItem, isCompoundPathItem, isBoundsItem, isPathItem, isPGTextItem} from './item';
|
||||
import {getItemsCompoundPath, isCompoundPath, isCompoundPathChild} from './compound-path';
|
||||
import {performSnapshot} from './undo';
|
||||
|
||||
/**
|
||||
* @param {boolean} includeGuides True if guide layer items like the bounding box should
|
||||
|
@ -165,20 +166,18 @@ const getSelectedLeafItems = function () {
|
|||
return items;
|
||||
};
|
||||
|
||||
const deleteItemSelection = function (items) {
|
||||
const deleteItemSelection = function (items, undoSnapshot) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
items[i].remove();
|
||||
}
|
||||
|
||||
// @todo: Update toolbar state on change
|
||||
paper.project.view.update();
|
||||
// @todo add back undo
|
||||
// pg.undo.snapshot('deleteItemSelection');
|
||||
performSnapshot(undoSnapshot);
|
||||
};
|
||||
|
||||
const removeSelectedSegments = function (items) {
|
||||
// @todo add back undo
|
||||
// pg.undo.snapshot('removeSelectedSegments');
|
||||
const removeSelectedSegments = function (items, undoSnapshot) {
|
||||
performSnapshot(undoSnapshot);
|
||||
|
||||
const segmentsToRemove = [];
|
||||
|
||||
|
@ -201,16 +200,16 @@ const removeSelectedSegments = function (items) {
|
|||
return removedSegments;
|
||||
};
|
||||
|
||||
const deleteSelection = function (mode) {
|
||||
const deleteSelection = function (mode, undoSnapshot) {
|
||||
if (mode === Modes.RESHAPE) {
|
||||
const selectedItems = getSelectedLeafItems();
|
||||
// If there are points selected remove them. If not delete the item selected.
|
||||
if (!removeSelectedSegments(selectedItems)) {
|
||||
deleteItemSelection(selectedItems);
|
||||
if (!removeSelectedSegments(selectedItems, undoSnapshot)) {
|
||||
deleteItemSelection(selectedItems, undoSnapshot);
|
||||
}
|
||||
} else {
|
||||
const selectedItems = getSelectedRootItems();
|
||||
deleteItemSelection(selectedItems);
|
||||
deleteItemSelection(selectedItems, undoSnapshot);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -281,11 +280,11 @@ const splitPathAtSelectedSegments = function () {
|
|||
}
|
||||
};
|
||||
|
||||
const deleteSegments = function (item) {
|
||||
const deleteSegments = function (item, undoSnapshot) {
|
||||
if (item.children) {
|
||||
for (let i = 0; i < item.children.length; i++) {
|
||||
const child = item.children[i];
|
||||
deleteSegments(child);
|
||||
deleteSegments(child, undoSnapshot);
|
||||
}
|
||||
} else {
|
||||
const segments = item.segments;
|
||||
|
@ -299,7 +298,7 @@ const deleteSegments = function (item) {
|
|||
!segment.previous.selected)) {
|
||||
|
||||
splitPathRetainSelection(item, j);
|
||||
deleteSelection();
|
||||
deleteSelection(Modes.RESHAPE, undoSnapshot);
|
||||
return;
|
||||
|
||||
} else if (!item.closed) {
|
||||
|
@ -316,26 +315,24 @@ const deleteSegments = function (item) {
|
|||
}
|
||||
};
|
||||
|
||||
const deleteSegmentSelection = function (items) {
|
||||
const deleteSegmentSelection = function (items, undoSnapshot) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
deleteSegments(items[i]);
|
||||
deleteSegments(items[i], undoSnapshot);
|
||||
}
|
||||
|
||||
// @todo: Update toolbar state on change
|
||||
paper.project.view.update();
|
||||
// @todo add back undo
|
||||
// pg.undo.snapshot('deleteSegmentSelection');
|
||||
performSnapshot(undoSnapshot);
|
||||
};
|
||||
|
||||
const cloneSelection = function (recursive) {
|
||||
const cloneSelection = function (recursive, undoSnapshot) {
|
||||
const selectedItems = recursive ? getSelectedLeafItems() : getSelectedRootItems();
|
||||
for (let i = 0; i < selectedItems.length; i++) {
|
||||
const item = selectedItems[i];
|
||||
item.clone();
|
||||
item.selected = false;
|
||||
}
|
||||
// @todo add back undo
|
||||
// pg.undo.snapshot('cloneSelection');
|
||||
performSnapshot(undoSnapshot);
|
||||
};
|
||||
|
||||
// Only returns paths, no compound paths, groups or any other stuff
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {getSelectedLeafItems} from './selection';
|
||||
import {isPGTextItem, isPointTextItem} from './item';
|
||||
import {isGroup} from './group';
|
||||
import {performSnapshot} from './undo';
|
||||
|
||||
const MIXED = 'scratch-paint/style-path/mixed';
|
||||
|
||||
|
@ -8,7 +9,7 @@ const MIXED = 'scratch-paint/style-path/mixed';
|
|||
* Called when setting fill color
|
||||
* @param {string} colorString New color, css format
|
||||
*/
|
||||
const applyFillColorToSelection = function (colorString) {
|
||||
const applyFillColorToSelection = function (colorString, undoSnapshot) {
|
||||
const items = getSelectedLeafItems();
|
||||
for (const item of items) {
|
||||
if (isPGTextItem(item)) {
|
||||
|
@ -30,14 +31,14 @@ const applyFillColorToSelection = function (colorString) {
|
|||
item.fillColor = colorString;
|
||||
}
|
||||
}
|
||||
// @todo add back undo
|
||||
performSnapshot(undoSnapshot);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when setting stroke color
|
||||
* @param {string} colorString New color, css format
|
||||
*/
|
||||
const applyStrokeColorToSelection = function (colorString) {
|
||||
const applyStrokeColorToSelection = function (colorString, undoSnapshot) {
|
||||
const items = getSelectedLeafItems();
|
||||
|
||||
for (const item of items) {
|
||||
|
@ -61,14 +62,14 @@ const applyStrokeColorToSelection = function (colorString) {
|
|||
item.strokeColor = colorString;
|
||||
}
|
||||
}
|
||||
// @todo add back undo
|
||||
performSnapshot(undoSnapshot);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when setting stroke width
|
||||
* @param {number} value New stroke width
|
||||
*/
|
||||
const applyStrokeWidthToSelection = function (value) {
|
||||
const applyStrokeWidthToSelection = function (value, undoSnapshot) {
|
||||
const items = getSelectedLeafItems();
|
||||
for (const item of items) {
|
||||
if (isGroup(item)) {
|
||||
|
@ -77,7 +78,7 @@ const applyStrokeWidthToSelection = function (value) {
|
|||
item.strokeWidth = value;
|
||||
}
|
||||
}
|
||||
// @todo add back undo
|
||||
performSnapshot(undoSnapshot);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -168,7 +169,12 @@ const stylePath = function (path, options) {
|
|||
if (options.isEraser) {
|
||||
path.fillColor = 'white';
|
||||
} else {
|
||||
if (options.fillColor) {
|
||||
path.fillColor = options.fillColor;
|
||||
} else {
|
||||
// Make sure something visible is drawn
|
||||
path.fillColor = 'black';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -178,7 +184,12 @@ const styleCursorPreview = function (path, options) {
|
|||
path.strokeColor = 'cornflowerblue';
|
||||
path.strokeWidth = 1;
|
||||
} else {
|
||||
if (options.fillColor) {
|
||||
path.fillColor = options.fillColor;
|
||||
} else {
|
||||
// Make sure something visible is drawn
|
||||
path.fillColor = 'black';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import eraserModeReducer from './eraser-mode';
|
|||
import colorReducer from './color';
|
||||
import hoverReducer from './hover';
|
||||
import selectedItemReducer from './selected-items';
|
||||
import undoReducer from './undo';
|
||||
|
||||
export default combineReducers({
|
||||
mode: modeReducer,
|
||||
|
@ -12,5 +13,6 @@ export default combineReducers({
|
|||
eraserMode: eraserModeReducer,
|
||||
color: colorReducer,
|
||||
hoveredItemId: hoverReducer,
|
||||
selectedItems: selectedItemReducer
|
||||
selectedItems: selectedItemReducer,
|
||||
undo: undoReducer
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ const reducer = function (state, action) {
|
|||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case UNDO:
|
||||
if (state.pointer === -1) {
|
||||
if (state.pointer <= 0) {
|
||||
log.warn(`Can't undo, undo stack is empty`);
|
||||
return state;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ const reducer = function (state, action) {
|
|||
pointer: state.pointer - 1
|
||||
};
|
||||
case REDO:
|
||||
if (state.pointer === state.stack.length - 1) {
|
||||
if (state.pointer <= -1 || state.pointer === state.stack.length - 1) {
|
||||
log.warn(`Can't redo, redo stack is empty`);
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -52,12 +52,20 @@ test('clearUndoState', () => {
|
|||
|
||||
test('cantUndo', () => {
|
||||
let defaultState;
|
||||
const state1 = {state: 1};
|
||||
|
||||
// Undo when there's no undo stack
|
||||
const reduxState = undoReducer(defaultState /* state */, undo() /* action */);
|
||||
let reduxState = undoReducer(defaultState /* state */, undo() /* action */);
|
||||
|
||||
expect(reduxState.pointer).toEqual(-1);
|
||||
expect(reduxState.stack).toHaveLength(0);
|
||||
|
||||
// Undo when there's only one state
|
||||
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state1]) /* action */);
|
||||
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||
|
||||
expect(reduxState.pointer).toEqual(0);
|
||||
expect(reduxState.stack).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('cantRedo', () => {
|
||||
|
@ -111,23 +119,23 @@ test('undoSnapshotCantRedo', () => {
|
|||
let defaultState;
|
||||
const state1 = {state: 1};
|
||||
const state2 = {state: 2};
|
||||
const state3 = {state: 3};
|
||||
|
||||
// Push 2 states then undo twice
|
||||
// Push 2 states then undo
|
||||
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([state1]) /* action */);
|
||||
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
||||
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||
|
||||
expect(reduxState.pointer).toEqual(-1);
|
||||
expect(reduxState.pointer).toEqual(0);
|
||||
expect(reduxState.stack).toHaveLength(2);
|
||||
|
||||
// Snapshot
|
||||
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state2]) /* action */);
|
||||
reduxState = undoReducer(reduxState /* state */, undoSnapshot([state3]) /* action */);
|
||||
// Redo should do nothing
|
||||
const newReduxState = undoReducer(reduxState /* state */, redo() /* action */);
|
||||
|
||||
expect(newReduxState.pointer).toEqual(reduxState.pointer);
|
||||
expect(newReduxState.stack).toHaveLength(reduxState.stack.length);
|
||||
expect(newReduxState.stack[0]).toEqual(reduxState.stack[0]);
|
||||
expect(newReduxState.stack[0]).toEqual(state2);
|
||||
expect(newReduxState.stack[1]).toEqual(state3);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue