undo-redo-vuex
Version:
A Vuex plugin for module namespaced undo and redo functionality. This plugin takes inspiration from and extends the work of [vuex-undo-redo](https://github.com/anthonygore/vuex-undo-redo).
115 lines (107 loc) • 3.36 kB
text/typescript
import { EMPTY_STATE } from "./constants";
import {
getConfig,
pipeActions,
setConfig,
updateCanUndoRedo
} from "./utils-undo-redo";
/**
* The Undo function - pushes the latest done mutation to the top of the undone
* stack by popping the done stack and 'replays' all mutations in the done stack
*
* @module store/plugins/undoRedo:undo
* @function
*/
export default ({
paths,
store
}: {
paths: UndoRedoOptions[];
store: any;
}) => async (namespace: string) => {
const config = getConfig(paths)(namespace);
if (Object.keys(config).length) {
/**
* @var {Array} done - The updated done stack
* @var {Array} commits - The list of mutations which are undone
* NB: The reduceRight operation is used to identify the mutation(s) from the
* top of the done stack to be undone
*/
const { done, commits } = config.done.reduceRight(
(
{
commits,
done,
proceed
}: {
commits: Array<Mutation> | [];
done: Array<Mutation> | [];
proceed: boolean;
},
m: Mutation
) => {
if (!commits.length) {
// The "topmost" mutation from the done stack
commits = [m];
// Do not find more mutations if the mutations does not belong to a group
proceed = !!m.payload.actionGroup;
} else if (!proceed) {
// Unshift the mutation to the done stack
done = [m, ...done];
} else {
const lastUndone = commits[commits.length - 1];
const { actionGroup } = lastUndone.payload;
// Unshift to commits if mutation belongs to the same actionGroup,
// otherwise unshift to the done stack
proceed =
m.payload.actionGroup && m.payload.actionGroup === actionGroup;
commits = [...(proceed ? [m] : []), ...commits];
done = [...(proceed ? [] : [m]), ...done];
}
return { done, commits, proceed };
},
{
done: [],
commits: [],
proceed: true
}
);
// Check if there are any undo callback actions
const undoCallbacks = commits.map(({ payload }: { payload: any }) => ({
action: payload.undoCallback ? `${namespace}${payload.undoCallback}` : "",
payload
}));
await pipeActions(store)(undoCallbacks);
const undone = [...config.undone, ...commits];
config.newMutation = false;
store.commit(`${namespace}${EMPTY_STATE}`);
const redoCallbacks = done
.filter((mutation: Mutation) => mutation.type && mutation.payload)
.map(async (mutation: Mutation) => {
store.commit(
mutation.type,
Array.isArray(mutation.payload)
? [...mutation.payload]
: mutation.payload.constructor(mutation.payload)
);
// Check if there is an undo callback action
const { redoCallback } = mutation.payload;
return {
action: redoCallback ? `${namespace}${redoCallback}` : "",
payload: mutation.payload
};
});
await pipeActions(store)(await Promise.all(redoCallbacks));
config.newMutation = true;
setConfig(paths)(
namespace,
{
...config,
done,
undone
},
store
);
updateCanUndoRedo({ paths, store })(namespace);
}
};