Modify undo manager to return a value and use it on undo (#1616)
* Modify undo manager to return a value and use it on undo * Storing the last redo value internally to correctly run undo * Fix types * Improve test ensuring redo didn't add original block Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
parent
9f74287802
commit
d22fe7fbc0
2 changed files with 69 additions and 8 deletions
|
@ -47,6 +47,59 @@ test('Basic undo/redo', async () => {
|
||||||
expect(undoManager.redoDescription).toBe(undefined)
|
expect(undoManager.redoDescription).toBe(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Basic undo/redo response dependant', async () => {
|
||||||
|
expect(undoManager.canUndo).toBe(false)
|
||||||
|
expect(undoManager.canRedo).toBe(false)
|
||||||
|
expect(undoManager.currentCheckpoint).toBe(0)
|
||||||
|
|
||||||
|
const blockIds = [2, 1]
|
||||||
|
const blocks: Record<string, any> = {}
|
||||||
|
|
||||||
|
const newBlock = await undoManager.perform(
|
||||||
|
async () => {
|
||||||
|
const responseId = blockIds.pop() // every time we run the action a new ID is obtained
|
||||||
|
const block: Record<string, any> = {id: responseId, title: 'Sample'}
|
||||||
|
blocks[block.id] = block
|
||||||
|
return block
|
||||||
|
},
|
||||||
|
async (block: Record<string, any>) => {
|
||||||
|
delete blocks[block.id]
|
||||||
|
},
|
||||||
|
'test',
|
||||||
|
)
|
||||||
|
|
||||||
|
// should insert the block and return the new block for its use
|
||||||
|
expect(undoManager.canUndo).toBe(true)
|
||||||
|
expect(undoManager.canRedo).toBe(false)
|
||||||
|
expect(blocks).toHaveProperty('1')
|
||||||
|
expect(blocks[1]).toEqual(newBlock)
|
||||||
|
|
||||||
|
// should correctly remove the block based on the info gathered in
|
||||||
|
// the redo function
|
||||||
|
await undoManager.undo()
|
||||||
|
expect(undoManager.canUndo).toBe(false)
|
||||||
|
expect(undoManager.canRedo).toBe(true)
|
||||||
|
expect(blocks).not.toHaveProperty('1')
|
||||||
|
|
||||||
|
// when redoing, as the function has side effects the new id will
|
||||||
|
// be different
|
||||||
|
await undoManager.redo()
|
||||||
|
expect(undoManager.canUndo).toBe(true)
|
||||||
|
expect(undoManager.canRedo).toBe(false)
|
||||||
|
expect(blocks).not.toHaveProperty('1')
|
||||||
|
expect(blocks).toHaveProperty('2')
|
||||||
|
expect(blocks[2].id).toEqual(2)
|
||||||
|
|
||||||
|
// when undoing, the undo manager has saved the new id internally
|
||||||
|
// and it removes the right block
|
||||||
|
await undoManager.undo()
|
||||||
|
expect(undoManager.canUndo).toBe(false)
|
||||||
|
expect(undoManager.canRedo).toBe(true)
|
||||||
|
expect(blocks).not.toHaveProperty('2')
|
||||||
|
|
||||||
|
await undoManager.clear()
|
||||||
|
})
|
||||||
|
|
||||||
test('Grouped undo/redo', async () => {
|
test('Grouped undo/redo', async () => {
|
||||||
expect(undoManager.canUndo).toBe(false)
|
expect(undoManager.canUndo).toBe(false)
|
||||||
expect(undoManager.canRedo).toBe(false)
|
expect(undoManager.canRedo).toBe(false)
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
interface UndoCommand {
|
interface UndoCommand {
|
||||||
checkpoint: number
|
checkpoint: number
|
||||||
undo: () => Promise<void>
|
undo: (value?: any) => Promise<void>
|
||||||
redo: () => Promise<void>
|
redo: () => Promise<void>
|
||||||
description?: string
|
description?: string
|
||||||
groupId?: string
|
groupId?: string
|
||||||
|
value?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -50,30 +51,36 @@ class UndoManager {
|
||||||
}
|
}
|
||||||
this.isExecuting = true
|
this.isExecuting = true
|
||||||
|
|
||||||
await command[action]()
|
if (action === 'redo') {
|
||||||
|
command.value = await command[action]()
|
||||||
|
} else {
|
||||||
|
await command[action](command.value)
|
||||||
|
}
|
||||||
|
|
||||||
this.isExecuting = false
|
this.isExecuting = false
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
async perform(
|
async perform(
|
||||||
redo: () => Promise<void>,
|
redo: () => Promise<any>,
|
||||||
undo: () => Promise<void>,
|
undo: (value?: any) => Promise<void>,
|
||||||
description?: string,
|
description?: string,
|
||||||
groupId?: string,
|
groupId?: string,
|
||||||
isDiscardable = false,
|
isDiscardable = false,
|
||||||
): Promise<UndoManager> {
|
): Promise<any> {
|
||||||
await redo()
|
const value = await redo()
|
||||||
return this.registerUndo({undo, redo}, description, groupId, isDiscardable)
|
this.registerUndo({undo, redo}, description, groupId, value, isDiscardable)
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
registerUndo(
|
registerUndo(
|
||||||
command: {
|
command: {
|
||||||
undo: () => Promise<void>,
|
undo: (value?: any) => Promise<void>,
|
||||||
redo: () => Promise<void>
|
redo: () => Promise<void>
|
||||||
},
|
},
|
||||||
description?: string,
|
description?: string,
|
||||||
groupId?: string,
|
groupId?: string,
|
||||||
|
value?: any,
|
||||||
isDiscardable = false,
|
isDiscardable = false,
|
||||||
): UndoManager {
|
): UndoManager {
|
||||||
if (this.isExecuting) {
|
if (this.isExecuting) {
|
||||||
|
@ -97,6 +104,7 @@ class UndoManager {
|
||||||
redo: command.redo,
|
redo: command.redo,
|
||||||
description,
|
description,
|
||||||
groupId,
|
groupId,
|
||||||
|
value,
|
||||||
}
|
}
|
||||||
this.commands.push(internalCommand)
|
this.commands.push(internalCommand)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue