Vue Embedded AnalyticsUndo & Redo

Version 3.0.0

Studio records each change made to a report, and can step back and forward through them.

Undo and redo cover the durable document: widgets, layout, filters and schema. View state such as the selected page and the current selection is left where it is, then brought back into view for the restored change.

Keyboard Shortcuts Copy Link

While focus is inside Studio:

  • ⌃ Ctrl/⌘ Cmd + Z - Undo the last change.
  • ⌃ Ctrl/⌘ Cmd + ⇧ Shift + Z - Redo.

The shortcuts are bound to Studio's own element, so they work only while focus is inside it. Studio listens nowhere else: with focus in one of your application's inputs or controls, the key press is yours, and a text field inside Studio keeps the shortcut for its own editing history. See Keyboard Shortcuts for Studio's other shortcuts.

Undoing From Outside Studio Copy Link

To undo while focus is elsewhere in your application, bind the shortcut yourself and call undo() or redo():

document.addEventListener('keydown', (event) => {
    if (!(event.ctrlKey || event.metaKey) || event.code !== 'KeyZ') return;

    event.preventDefault();
    if (event.shiftKey) {
        api.redo();
    } else {
        api.undo();
    }
});

How far that binding reaches, and which of your own components it should leave alone, is yours to decide. Studio makes no assumptions about the page around it.

A binding on the document also fires for a press made inside Studio, because Studio does not stop the event propagating. Suppress the shortcut in Studio, as below, so one press does not undo twice.

Suppressing the Shortcuts Copy Link

suppressKeyboard stops Studio handling a shortcut, leaving the key press for the application to bind:

<ag-studio
    :suppressKeyboard="suppressKeyboard"
    /* other studio properties ... */>
</ag-studio>

this.suppressKeyboard = {
    undo: true,
    redo: true,
};

Each is independent, so { redo: true } keeps undo on ⌃ Ctrl/⌘ Cmd + Z. A suppressed shortcut is left untouched rather than swallowed, so a handler of your own still receives the event.

Undoing and Redoing in Code Copy Link

undo() and redo() each step one change, and do nothing at the end of their stack:

api.undo();
api.redo();

Making a new change discards whatever was waiting to be redone.

Reading the History Copy Link

Studio ships no undo controls of its own, so an application that wants them builds them from getHistory(). It returns the live state alongside the changes each action would step through:

const { undo, redo } = api.getHistory();
undoButton.disabled = undo.length === 0;
redoButton.disabled = redo.length === 0;

Refresh the controls from the stateUpdated event, which fires after every committed change - undo and redo included - and also when the history is discarded, whether or not the live state itself changed. A host persisting on this event sees one write per clearHistory() call, and one per mode switch once it opts into history.onModeChange: 'clear'.

The last entry of each stack is the one that action takes next, and its label names the change. Labels come from the locale, so they follow the configured language:

const next = api.getHistory().undo.at(-1);
undoButton.title = next ? `Undo ${next.label}` : 'Nothing to undo';

Each entry also carries an id. Passing one to undo() or redo() steps through every change up to that entry as a single step, which is what a history list needs:

const { undo } = api.getHistory();
api.undo(undo[0].id); // back to the start of the history

The history holds the last 100 changes by default, and is cleared when Studio loads initialState. A later setState() is recorded as a change like any other, so it can be undone. It can also be cleared explicitly with api.clearHistory(), or automatically on a mode change with history.onModeChange: 'clear', both covered below.

Limiting the History Copy Link

Each entry holds the whole state either side of its change, so a long history on a large dashboard costs memory. Set history.maxEntries to trade how far back a user can undo against what that retention costs:

<ag-studio
    :history="history"
    /* other studio properties ... */>
</ag-studio>

this.history = {
    maxEntries: 20,
};

Once the history holds that many changes, recording a new one discards the oldest, which can no longer be undone. Undone changes do not add to the count: undoing moves a change from the undo side to the redo side rather than creating another one, so the two stacks together never exceed the limit.

A value below one keeps no history, leaving undo() and redo() with nothing to do. Changes still apply as normal; only the ability to reverse them is given up.

Clearing the History Copy Link

Call clearHistory() to discard the undo and redo stacks without touching the live state:

api.clearHistory();

getHistory() then returns empty stacks, and undo()/redo() are no-ops until the next change. The live state is untouched, so the changes themselves remain - only the ability to step back over them goes.

Set history.onModeChange to have this happen automatically whenever mode changes:

<ag-studio
    :history="history"
    /* other studio properties ... */>
</ag-studio>

this.history = {
    onModeChange: 'clear',
};

The default, 'preserve', carries the history across a mode switch. 'clear' discards it on a switch in either direction, so a change made while in view mode (possible when filter editing is enabled there) is discarded too on the way back to edit mode. Mode does not otherwise gate undo or redo - both work in either mode.

Add a chart or two below, then clear the history explicitly or switch mode with onModeChange set to 'clear'. The counts show both stacks emptying while the charts stay where they are:

Undo & Redo API Copy Link

Properties Copy Link

suppressKeyboardCopy Link
AgSuppressKeyboard
Keyboard shortcuts Studio should not handle, so an application can bind them itself. Each shortcut is suppressed independently, e.g. { redo: true } keeps undo on ctrl/cmd+Z and leaves ctrl/cmd+shift+Z to the application.
historyCopy Link
AgHistoryOptions
How Studio's undo and redo history behaves.

API Methods Copy Link

Function
Undo the last change to the durable document state (widgets, layout, filters, schema). View state such as the selected page or selection is left as it is, then brought back into view for the restored change. No-op when there is nothing to undo (getHistory().undo is empty). Pass a getHistory().undo entry's id to undo every change back through that entry in a single step; an unknown id is a no-op.
Function
Redo the change most recently undone. No-op when there is nothing to redo (getHistory().redo is empty). Making a fresh change discards the redo branch. Pass a getHistory().redo entry's id to redo every change forward through that entry in a single step; an unknown id is a no-op.
getHistoryCopy Link
Function
The editing history: the live state plus what undo() and redo() would step through, each entry labelled and timestamped. Drives control enablement (an empty undo stack means there is nothing to undo) and a history list. History does not survive loading new state.
clearHistoryCopy Link
Function
Discard the undo and redo history, so getHistory() returns empty stacks and undo()/redo() become no-ops until the next change. The live state is untouched.