Skip to content
v3.3.0

Undo-redo

HyperFormula supports undo-redo for CRUD and move operations. By default, you can undo 20 actions. The undoLimit can be changed inside the configuration options so you can adapt that number to your needs. Be careful when setting undoLimit to large numbers. It may result in performance issues.

Undo and redo work together as a synced pair, so each time you undo some action it is put onto a redo stack.

Named expressions behave just like any other CRUD operation.

There are two methods which can be used to check the actual state of the undo-redo stack:isThereSomethingToUndo and isThereSomethingToRedo.

When you batch several operations remember that undo-redo will recognize them as a single cumulative operation.

 

Name Value
Source code
/**
* Initial table data.
*/
const tableData = [
['Greg', '2'],
['Chris', '4'],
];
// Create an empty HyperFormula instance.
const hf = HyperFormula.buildEmpty({
licenseKey: 'gpl-v3',
});
// Add a new sheet and get its id.
const sheetName = hf.addSheet('main');
const sheetId = hf.getSheetId(sheetName);
// Fill the HyperFormula sheet with data.
hf.setCellContents(
{
row: 0,
col: 0,
sheet: sheetId,
},
tableData,
);
// Clear the undo stack to prevent undoing the initialization steps.
hf.clearUndoStack();
/**
* Fill the HTML table with data.
*/
function renderTable() {
const tbodyDOM = document.querySelector('.example tbody');
const updatedCellClass = ANIMATION_ENABLED ? 'updated-cell' : '';
const { height, width } = hf.getSheetDimensions(sheetId);
let newTbodyHTML = '';
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const cellAddress = { sheet: sheetId, col, row };
const cellValue = hf.getCellValue(cellAddress);
newTbodyHTML += `<td class="${updatedCellClass}"><span>
${cellValue}
</span></td>`;
}
newTbodyHTML += '</tr>';
}
tbodyDOM.innerHTML = newTbodyHTML;
}
/**
* Clear the existing information.
*/
function clearInfo() {
const infoBoxDOM = document.querySelector('.example #info-box');
infoBoxDOM.innerHTML = '&nbsp;';
}
/**
* Display the provided message in the info box.
*
* @param {string} message Message to display.
*/
function displayInfo(message) {
const infoBoxDOM = document.querySelector('.example #info-box');
infoBoxDOM.innerText = message;
}
/**
* Bind the events to the buttons.
*/
function bindEvents() {
const removeRowButton = document.querySelector('.example #remove-row');
const undoButton = document.querySelector('.example #undo');
removeRowButton.addEventListener('click', () => {
removeSecondRow();
});
undoButton.addEventListener('click', () => {
undo();
});
}
/**
* Remove the second row from the table.
*/
function removeSecondRow() {
const filledRowCount = hf.getSheetDimensions(sheetId).height;
clearInfo();
if (filledRowCount < 2) {
displayInfo("There's not enough filled rows to perform this action.");
return;
}
hf.removeRows(sheetId, [1, 1]);
renderTable();
}
/**
* Run the HF undo action.
*/
function undo() {
clearInfo();
if (!hf.isThereSomethingToUndo()) {
displayInfo("There's nothing to undo.");
return;
}
hf.undo();
renderTable();
}
const ANIMATION_ENABLED = true;
// Bind the button events.
bindEvents();
// Render the table.
renderTable();