Repository navigation
Expand file tree
/
Copy pathundoSteps.ts
More file actions
37 lines (32 loc) · 2.21 KB
/
Copy pathundoSteps.ts
File metadata and controls
37 lines (32 loc) · 2.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import moize from 'moize'
import Patch from '../@types/Patch'
import State from '../@types/State'
import getUndoStepCount from '../util/getUndoStepCount'
/** A step of the undo history: the patches that are undone and redone together, in chronological order. */
export interface UndoStep {
patches: Patch[]
}
/** Groups patches, ordered newest first, into undo steps. Uses the same step-boundary policy as undoRedoEnhancer: a navigation patch is grouped with the undoable patch before it, and a patch that follows a newThought is grouped with it, e.g. typing the value of a new thought, unless it holds formatting for the empty thought (see formatSelection). Unlike undoReducer, a formatting-only edit of a newly created thought is also grouped with the newThought, since telling it apart would require the thought's value at that point in the history. */
const groupSteps = (patches: Patch[]): UndoStep[] =>
patches.reduce<UndoStep[]>((steps, patch, i) => {
// skip a patch that was grouped with the newer patch before it
if (steps.at(-1)?.patches[0] === patch) return steps
const older = patches[i + 1]
const grouped = getUndoStepCount(patch, older) === 2
return [...steps, { patches: grouped ? [older, patch] : [patch] }]
}, [])
/** Returns the steps of the undo history, newest first, spanning the redo stack (the steps ahead of the current state) and the undo stack. The position is the index of the current state in the steps, i.e. the number of steps that have been undone. The steps on either side of the current state are grouped separately, so the current state always sits on a step boundary. */
const undoSteps = (state: State): { steps: UndoStep[]; position: number } => {
// redoPatches[0] is the oldest action that was undone, i.e. the newest in the history
const redoSteps = groupSteps(state.redoPatches)
return {
steps: [...redoSteps, ...groupSteps([...state.undoPatches].reverse())],
position: redoSteps.length,
}
}
/** Memoize undoSteps by the undo and redo stacks, so that components re-render only when the history changes. */
const undoStepsMemoized = moize(undoSteps, {
maxSize: 1,
transformArgs: ([state]) => [state.undoPatches, state.redoPatches],
})
export default undoStepsMemoized