UNPKG

undox

Version:

Redux implementation of Undo/Redo based on storing actions instead of states.

124 lines (123 loc) 6.53 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; exports.__esModule = true; exports.undox = exports.createSelectors = void 0; var undox_action_1 = require("./undox.action"); // helper used to flatten the history if grouped actions are in it var flatten = function (x) { return [].concat.apply([], x); }; // actions can be an array of arrays because of grouped actions, so we flatten it first var calculateState = function (reducer, actions, state) { return flatten(actions).reduce(reducer, state); }; var getFutureActions = function (state) { return state.history.slice(state.index + 1); }; var getPastActionsWithPresent = function (state, isPastLimitExceeded) { var startIdx = isPastLimitExceeded ? 2 : 1; return __spreadArrays([state.history[0]], state.history.slice(startIdx, state.index + 1)); }; var getPastActions = function (state) { return state.history.slice(0, state.index); }; var getPresentState = function (state) { return state.present; }; exports.createSelectors = function (reducer) { return { getPastStates: function (state) { return getPastActions(state) .reduce(function (states, a, i) { return Array.isArray(a) ? __spreadArrays(states, [a.reduce(reducer, states[i - 1])]) : __spreadArrays(states, [reducer(states[i - 1], a)]); }, []); }, getFutureStates: function (state) { return getFutureActions(state) .reduce(function (states, a, i) { return Array.isArray(a) ? __spreadArrays(states, [a.reduce(reducer, states[i])]) : __spreadArrays(states, [reducer(states[i], a)]); }, [getPresentState(state)]).slice(1); }, getPresentState: getPresentState, getPastActions: function (state) { return flatten(getPastActions(state)); }, getPresentAction: function (state) { return state.history[state.index]; }, getFutureActions: function (state) { return flatten(getFutureActions(state)); } }; }; var doNPastStatesExist = function (_a, nStates) { var index = _a.index; return index >= nStates; }; var doNFutureStatesExist = function (_a, nStates) { var history = _a.history, index = _a.index; return history.length - 1 - index >= nStates; }; var group = function (state, action, reducer, comparator, limit) { var presentState = getPresentState(state); var nextState = action.payload.reduce(reducer, state.present); if (comparator(presentState, nextState)) return state; var isPastLimitExceeded = pastLimitExceeded(state.index, limit); return __assign(__assign({}, state), { history: __spreadArrays(getPastActionsWithPresent(state, isPastLimitExceeded), [action.payload]), index: state.index + 1, present: nextState }); }; var undo = function (reducer, state, _a, limit) { var _b = _a.payload, payload = _b === void 0 ? 1 : _b; var nPastStatesExist = doNPastStatesExist(state, payload); var index = nPastStatesExist ? state.index - payload : 0; var futureExceeded = (state.history.length - state.index) > limit.future; var history = futureExceeded ? state.history.slice(0, -payload) : state.history; var isPastLimitExceeded = pastLimitExceeded(state.index, limit); var newState = __assign(__assign({}, state), { history: history, index: index }); return __assign(__assign({}, newState), { present: calculateState(reducer, getPastActionsWithPresent(!isPastLimitExceeded ? newState : __assign(__assign({}, newState), { index: state.index }), isPastLimitExceeded), newState.initial) }); }; var redo = function (reducer, state, _a) { var _b = _a.payload, payload = _b === void 0 ? 1 : _b; var latestFuture = state.history.slice(state.index + 1, state.index + 1 + payload); return __assign(__assign({}, state), { index: doNFutureStatesExist(state, payload) ? state.index + payload : state.history.length - 1, present: calculateState(reducer, latestFuture, getPresentState(state)) }); }; var pastLimitExceeded = function (index, limit) { return index >= limit.past; }; var delegate = function (state, action, reducer, comparator, limit) { var nextPresent = reducer(state.present, action); var index = state.index; var isPastLimitExceeded = pastLimitExceeded(index, limit); if (comparator(state.present, nextPresent)) return state; var newHistory = __spreadArrays(getPastActionsWithPresent(state, isPastLimitExceeded), [action]); var newState = __assign(__assign({}, state), { history: newHistory, index: isPastLimitExceeded ? state.index : state.index + 1, present: nextPresent, initial: isPastLimitExceeded ? reducer(state.initial, state.history[1]) : state.initial }); return newState; }; exports.undox = function (reducer, initAction, comparator, limit) { if (initAction === void 0) { initAction = { type: 'undox/INIT' }; } if (comparator === void 0) { comparator = function (s1, s2) { return s1 === s2; }; } var noLimit = { past: Infinity, future: Infinity }; var initial = reducer(undefined, initAction); var initialState = { history: [initAction], present: initial, index: 0, initial: initial }; return function (state, action) { if (state === void 0) { state = initialState; } switch (action.type) { case undox_action_1.UndoxTypes.UNDO: return undo(reducer, state, action, limit || noLimit); case undox_action_1.UndoxTypes.REDO: return redo(reducer, state, action, limit || noLimit); case undox_action_1.UndoxTypes.GROUP: return group(state, action, reducer, comparator, limit || noLimit); default: return delegate(state, action, reducer, comparator, limit || noLimit); } }; };