Skip to content

Transaction

Transactions represent atomic state changes. They contain a sequence of Steps that transform the document.

Use the TransactionBuilder from an EditorState:

const state = editor.getState();
const tr = state.transaction('command')
.insertText(blockId, offset, 'hello', [])
.build();
editor.dispatch(tr);

The origin parameter is optional and defaults to 'api':

// Equivalent — origin defaults to 'api'
const tr = state.transaction()
.setBlockType(blockId, nodeType('heading'), { level: 1 })
.build();
// Insert text with marks at a position
builder.insertText(blockId, offset, text, marks, segments?)
// Delete text with explicit undo data
builder.deleteText(blockId, from, to, deletedText, deletedMarks, deletedSegments?)
// Delete text — auto-derives undo data from working document
builder.deleteTextAt(blockId, from, to)
builder.addMark(blockId, from, to, mark)
builder.removeMark(blockId, from, to, mark)
// Split a block at offset, creating a new block with the given ID
builder.splitBlock(blockId, offset, newBlockId)
// Merge two blocks with explicit target length
builder.mergeBlocks(targetBlockId, sourceBlockId, targetLengthBefore)
// Merge two blocks — auto-derives target length from working document
builder.mergeBlocksAt(targetBlockId, sourceBlockId)
// Change a block's type and optionally its attributes
builder.setBlockType(blockId, nodeType, attrs?)
// Insert a child node at index under the parent path
builder.insertNode(parentPath, index, node)
// Remove a child node at index under the parent path
builder.removeNode(parentPath, index)
// Set attributes on a node at the given path
builder.setNodeAttr(path, attrs)
// Insert an InlineNode at offset within a block
builder.insertInlineNode(blockId, offset, node)
// Remove an InlineNode at offset (auto-derives from working document)
builder.removeInlineNode(blockId, offset)
// Set attributes on an InlineNode at offset
// attrs: Readonly<Record<string, string | number | boolean>>
builder.setInlineNodeAttr(blockId, offset, attrs)
builder.setSelection(selection)
builder.setNodeSelection(nodeId, path)
builder.setStoredMarks(marks, previousMarks)
const transaction = builder.build();

Each transaction has an origin that describes where it came from:

type TransactionOrigin = 'input' | 'paste' | 'command' | 'history' | 'api';
OriginDescription
inputUser typing / input events
pastePaste operations
commandProgrammatic command execution
historyUndo/redo operations
apiExternal API calls (setHTML, setJSON) — this is the default

Every step is invertible for undo support:

StepDescription
InsertTextStepInsert text at position
DeleteTextStepDelete text range
SplitBlockStepSplit block at offset
MergeBlocksStepMerge two adjacent blocks
AddMarkStepAdd inline mark to range
RemoveMarkStepRemove inline mark from range
SetBlockTypeStepChange block type
SetStoredMarksStepSet stored marks (for mark continuity) — internal, not exported
InsertNodeStepInsert a block node into a parent
RemoveNodeStepRemove a block node from a parent
SetNodeAttrStepChange a node’s attributes
InsertInlineNodeStepInsert an inline node at offset
RemoveInlineNodeStepRemove an inline node at offset
SetInlineNodeAttrStepChange an inline node’s attributes

Every transaction can be inverted for undo:

import { invertTransaction } from '@notectl/core';
const inverse = invertTransaction(transaction);
// Applying inverse undoes the original transaction

Individual steps can also be inverted:

import { invertStep } from '@notectl/core';
const invertedStep = invertStep(step);

Transactions pass through a middleware chain before being applied:

context.registerMiddleware((tr, state, next) => {
// Inspect or modify the transaction
console.log(`${tr.steps.length} steps`);
next(tr); // Call next to continue, or skip to cancel
}, 100);