Skip to main content

Undo/Redo

In 5.x, the undo/redo functionality has been pulled into the Toolkit core, in order to provide a more cohesive experience for users of the library. There are several advantages that come from doing this:

  • repositioning caused by the magnetizer and by snapping to a grid is now seamlessly integrated into the undo/redo stack
  • it is possible for users of the library to bundle a set of operations into transactions, which can be undone/redone as an atomic unit

Transactions

At the core of the undo/redo mechanism now is the concept of a Transaction - a set of operations which should be treated as an atomic unit. Certain internal methods in the Toolkit and Surface use Transactions, and it is also possible for a library user to declare their own.

Opening a transaction

To open a new transaction the basic call is:

toolkit.openTransaction()

Assuming you do not have a transaction already open this will create a new transaction in the Toolkit. If you do have a transaction already open the method call as shown will throw an Error. The Toolkit offers three flags you can use to instruct it what to do if a transaction is already open:

Commit the current transaction and open a new one

import { COMMIT_CURRENT } from '@jsplumbtoolkit/core'

toolkit.openTransaction(COMMIT_CURRENT)

Rollback the current transaction and open a new one

import { ROLLBACK_CURRENT } from '@jsplumbtoolkit/core'

toolkit.openTransaction(ROLLBACK_CURRENT)

Append to the current transaction without opening a new one

import { APPEND_TO_CURRENT } from '@jsplumbtoolkit/core'

toolkit.openTransaction(APPEND_TO_CURRENT)

Closing a transaction

To close the current transaction you can either commit it or roll it back:

toolkit.commitTransaction()
toolkit.rollbackTransaction()

If there is no current transaction when these methods are called no action is taken.

Closing a transaction that has been appended to

If you use APPEND_TO_CURRENT when opening a transaction, and there was a current transaction at the time, you cannot directly commit or rollback the transaction: calling either of the above methods will result in a log message, but the transaction will remain open. The Toolkit maintains a stack of append calls for the current transaction, and if you call commitTransaction() or rollbackTransaction() when there are append calls on the stack, the Toolkit pops the stack instead of closing the transaction. Only when the append stack is empty will a commitTransaction() or rollbackTransaction() call succeed.


Internal transactions

As mentioned above, the Toolkit/Surface performs a few key operations inside a transaction, These occur without user intervention and are included here purely for information.

  • When a drag starts, a transaction is opened, and when the drag stops the transaction is closed. Since a drag cannot start in the middle of the execution of some other code (except perhaps in the case of a testing scenario), this transaction is opened with no flag to define how it should behave if there is an existing transaction: no transaction should already exist. If you see an error logged about opening a transaction when a drag starts then you have forgotten to close a transaction somewhere.

  • setMagnetizedPosition operates within a transaction. All repositioning that occurs when a vertex is set and magnetized is bundled up into a single transaction (as more than one vertex generally moves when using the magnetizer).


Constants

If you're using Javascript instead of Typescript, the constants you need are:

export const COMMIT_CURRENT = "commitCurrent"
export const ROLLBACK_CURRENT = "rollbackCurrent"
export const APPEND_TO_CURRENT = "appendToCurrent"