JavaScript is required

Undo & Redo

Undo/Redo System - Track and reverse graph modifications

Undo/Redo Functionality

Functional Overview

This example demonstrates implementation of undo/redo functionality for graph operations. Every modification (add node, remove node, move node, add line, remove line) is recorded in a history stack, allowing users to revert or replay changes.

Implementation of Key Features

History Stack Management

interface GraphAction {
    type: 'addNode' | 'removeNode' | 'moveNode' | 'addLine' | 'removeLine';
    data: any;
    timestamp: number;
}

const historyRef = useRef<GraphAction[]>([]);
const historyIndexRef = useRef(-1);

const recordAction = (action: GraphAction) => {
    // Remove any future history if we're not at the end
    historyRef.current = historyRef.current.slice(0, historyIndexRef.current + 1);
    historyRef.current.push(action);
    historyIndexRef.current++;
};

Action Wrappers

const addNodeWithUndo = async (node: JsonNode) => {
    await graphInstance.addNodes([node]);
    recordAction({
        type: 'addNode',
        data: { node },
        timestamp: Date.now()
    });
};

const removeNodeWithUndo = async (nodeId: string) => {
    const node = graphInstance.getNodeById(nodeId);
    await graphInstance.removeNodeById(nodeId);
    recordAction({
        type: 'removeNode',
        data: { node },
        timestamp: Date.now()
    });
};

Undo/Redo Implementation

const undo = async () => {
    if (historyIndexRef.current < 0) return;

    const action = historyRef.current[historyIndexRef.current];
    switch (action.type) {
        case 'addNode':
            await graphInstance.removeNodeById(action.data.node.id);
            break;
        case 'removeNode':
            await graphInstance.addNodes([action.data.node]);
            break;
        case 'moveNode':
            await graphInstance.updateNode(action.data.node.id, {
                x: action.data.oldX,
                y: action.data.oldY
            });
            break;
    }
    historyIndexRef.current--;
};

const redo = async () => {
    if (historyIndexRef.current >= historyRef.current.length - 1) return;

    historyIndexRef.current++;
    const action = historyRef.current[historyIndexRef.current];
    // Replay action...
};

Creative Use Cases

Graph Editors: Allow users to experiment freely and revert mistakes.

Collaborative Editing: Track changes for conflict resolution.

Step-by-Step Tutorials: Guide users through graph construction.

Error Recovery: Protect against accidental deletions or modifications.

Animation Recording: Record and replay graph creation process.