History tracker

The History tracker in JSS automatically logs all changes made to the sheet. So, the user can undo (CTRL+Z) their actions and restore the sheet to a previous state or redo (CTRL+Y) their changes. This feature provides valuable safety for users, allowing them to restore changes and prevent losing their work.

If you are migrating from previous versions The new JSS version 10 significantly improves the history of changes. The change tracker is now decoupled from a specific grid instance and will track changes across all sheets on the screen. The new feature aligns the changes-tracking capabilities with other spreadsheet applications, allowing for safer cross-worksheet operations and providing a more comprehensive history of changes made to the worksheets available on the screen.

Documentation

Methods

The undo and redo methods are normally invoked by the CTRL+Z, CTRL+Y keyboard shortcut. The following methods can be called programmatically, as follows:

Method Description
history.undo() Undo the last spreadsheet changes.
jspreadsheet.history.undo() : void
history.redo() Redo the most recent spreadsheet changes.
jspreadsheet.history.redo() : void
history.reset() Remove all history entries.
jspreadsheet.history.reset() : void
Advance usage
history(object) Add a new entry on the history tracker.
jspreadsheet.history(changes: Object) : void

Events

Events related to the history changes tracker.

Event Description
onredo onredo(worksheet: Object, info: Object) : null
The info array contains all necessary information about the history and depends on which change was performed.
onundo onundo(worksheet: Object, info: Object) : null
The info array contains all necessary information about the history and depends on which change was performed.

Interface

The history tracker interface is composed of the following attributes:

Event Description
index: number; History index cursor
actions: []; History items
cascade: boolean; When true the next item is cascaded in the same existing history element
ignore: boolean; When true no history will be added to the tracker
progress: string; Tell if a history process is ongoing.
undo: () => void; Undo last action
redo: () => void; Redo most recent action
reset: () => void; Reset history tracker

Examples

Controlling the changes programmatically

As explained above, the history actions are available on the spreadsheet level.

<html>
<script src="https://jspreadsheet.com/v10/jspreadsheet.js"></script>
<script src="https://jsuites.net/v5/jsuites.js"></script>
<link rel="stylesheet" href="https://jspreadsheet.com/v10/jspreadsheet.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v5/jsuites.css" type="text/css" />

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons" />

<div id='spreadsheet'></div>

<br/>
<input type="button" value="Undo" id="undobtn" />
<input type="button" value="Redo" id="redobtn" />
<input type="button" value="Reset" id="resetbtn" />

<script>
// Set your JSS license key (The following key only works for one day)
jspreadsheet.setLicense('NmNkY2YwMDFjOWE4MmUzMWI3NmUxY2Q3MTg3ODUyZWQzNWFlZGJlZjVlM2JkMjQzMjcyODFiMTQ3YzdlMDgzZDE5MzUwZmMyYTRlYWExNjI0NTQyZmFiZjI5MzA4Y2IzZmE3MmNjYTdkNTIyZWZiOWQzZDczY2E2NjNiZjVlMmIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpJeE5qTTJNVGN4TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0ozWldJaUxDSnNiMk5oYkdodmMzUWlYU3dpY0d4aGJpSTZJak0wSWl3aWMyTnZjR1VpT2xzaWRqY2lMQ0oyT0NJc0luWTVJaXdpZGpFd0lpd2lkakV4SWl3aVkyaGhjblJ6SWl3aVptOXliWE1pTENKbWIzSnRkV3hoSWl3aWNHRnljMlZ5SWl3aWNtVnVaR1Z5SWl3aVkyOXRiV1Z1ZEhNaUxDSnBiWEJ2Y25SbGNpSXNJbUpoY2lJc0luWmhiR2xrWVhScGIyNXpJaXdpYzJWaGNtTm9JaXdpY0hKcGJuUWlMQ0p6YUdWbGRITWlMQ0pqYkdsbGJuUWlMQ0p6WlhKMlpYSWlMQ0p6YUdGd1pYTWlYU3dpWkdWdGJ5STZkSEoxWlgwPQ==');

// Create the spreadsheet
let spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {
    worksheets: [{
        minDimensions: [8, 8],
    }],
});

document.getElementById("undobtn").onclick = () => jspreadsheet.history.undo()
document.getElementById("redobtn").onclick = () => jspreadsheet.history.redo()
document.getElementById("resetbtn").onclick = () => jspreadsheet.history.reset()
</script>
</html>
import React, { useRef } from "react";
import { Spreadsheet, Worksheet } from "@jspreadsheet/react";
import jspreadsheet from "jspreadsheet";

const license = 'NmNkY2YwMDFjOWE4MmUzMWI3NmUxY2Q3MTg3ODUyZWQzNWFlZGJlZjVlM2JkMjQzMjcyODFiMTQ3YzdlMDgzZDE5MzUwZmMyYTRlYWExNjI0NTQyZmFiZjI5MzA4Y2IzZmE3MmNjYTdkNTIyZWZiOWQzZDczY2E2NjNiZjVlMmIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpJeE5qTTJNVGN4TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0ozWldJaUxDSnNiMk5oYkdodmMzUWlYU3dpY0d4aGJpSTZJak0wSWl3aWMyTnZjR1VpT2xzaWRqY2lMQ0oyT0NJc0luWTVJaXdpZGpFd0lpd2lkakV4SWl3aVkyaGhjblJ6SWl3aVptOXliWE1pTENKbWIzSnRkV3hoSWl3aWNHRnljMlZ5SWl3aWNtVnVaR1Z5SWl3aVkyOXRiV1Z1ZEhNaUxDSnBiWEJ2Y25SbGNpSXNJbUpoY2lJc0luWmhiR2xrWVhScGIyNXpJaXdpYzJWaGNtTm9JaXdpY0hKcGJuUWlMQ0p6YUdWbGRITWlMQ0pqYkdsbGJuUWlMQ0p6WlhKMlpYSWlMQ0p6YUdGd1pYTWlYU3dpWkdWdGJ5STZkSEoxWlgwPQ==';

export default function App() {
    // Spreadsheet array of worksheets
    const spreadsheet = useRef();

    // Render component
    return (
        <>
            <Spreadsheet ref={spreadsheet} license={license}>
                <Worksheet minDimensions={[8,8]} />
            </Spreadsheet>
            <input type="button" value="Undo" onClick={() => jspreadsheet.history.undo()} />
            <input type="button" value="Redo" onClick={() => jspreadsheet.history.redo()} />
            <input type="button" value="Reset" onClick={() => jspreadsheet.history.reset()} />
        </>
    );
}
<template>
    <Spreadsheet ref="spreadsheet" :license="license">
        <Worksheet :minDimensions="[8,8]" />
    </Spreadsheet>
    <input type="button" value="Undo" @click="jspreadsheet.history.undo()" />
    <input type="button" value="Redo" @click="jspreadsheet.history.redo()" />
    <input type="button" value="Reset" @click="jspreadsheet.history.reset()" />
</template>

<script>
import { Spreadsheet, Worksheet, jspreadsheet } from "@jspreadsheet/vue";

const license = 'NmNkY2YwMDFjOWE4MmUzMWI3NmUxY2Q3MTg3ODUyZWQzNWFlZGJlZjVlM2JkMjQzMjcyODFiMTQ3YzdlMDgzZDE5MzUwZmMyYTRlYWExNjI0NTQyZmFiZjI5MzA4Y2IzZmE3MmNjYTdkNTIyZWZiOWQzZDczY2E2NjNiZjVlMmIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpJeE5qTTJNVGN4TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0ozWldJaUxDSnNiMk5oYkdodmMzUWlYU3dpY0d4aGJpSTZJak0wSWl3aWMyTnZjR1VpT2xzaWRqY2lMQ0oyT0NJc0luWTVJaXdpZGpFd0lpd2lkakV4SWl3aVkyaGhjblJ6SWl3aVptOXliWE1pTENKbWIzSnRkV3hoSWl3aWNHRnljMlZ5SWl3aWNtVnVaR1Z5SWl3aVkyOXRiV1Z1ZEhNaUxDSnBiWEJ2Y25SbGNpSXNJbUpoY2lJc0luWmhiR2xrWVhScGIyNXpJaXdpYzJWaGNtTm9JaXdpY0hKcGJuUWlMQ0p6YUdWbGRITWlMQ0pqYkdsbGJuUWlMQ0p6WlhKMlpYSWlMQ0p6YUdGd1pYTWlYU3dpWkdWdGJ5STZkSEoxWlgwPQ==';

export default {
    components: {
        Spreadsheet,
        Worksheet,
    },
    data() {
        return {
            license,
            jspreadsheet,
        }
    }
}
</script>
import { Component, ViewChild, ElementRef } from "@angular/core";
import jspreadsheet from "jspreadsheet";

import "jspreadsheet/dist/jspreadsheet.css"
import "jsuites/dist/jsuites.css"

// Set your JSS license key (The following key only works for one day)
jspreadsheet.setLicense('NmNkY2YwMDFjOWE4MmUzMWI3NmUxY2Q3MTg3ODUyZWQzNWFlZGJlZjVlM2JkMjQzMjcyODFiMTQ3YzdlMDgzZDE5MzUwZmMyYTRlYWExNjI0NTQyZmFiZjI5MzA4Y2IzZmE3MmNjYTdkNTIyZWZiOWQzZDczY2E2NjNiZjVlMmIsZXlKamJHbGxiblJKWkNJNklpSXNJbTVoYldVaU9pSktjM0J5WldGa2MyaGxaWFFpTENKa1lYUmxJam94TnpJeE5qTTJNVGN4TENKa2IyMWhhVzRpT2xzaWFuTndjbVZoWkhOb1pXVjBMbU52YlNJc0ltTnZaR1Z6WVc1a1ltOTRMbWx2SWl3aWFuTm9aV3hzTG01bGRDSXNJbU56WWk1aGNIQWlMQ0ozWldJaUxDSnNiMk5oYkdodmMzUWlYU3dpY0d4aGJpSTZJak0wSWl3aWMyTnZjR1VpT2xzaWRqY2lMQ0oyT0NJc0luWTVJaXdpZGpFd0lpd2lkakV4SWl3aVkyaGhjblJ6SWl3aVptOXliWE1pTENKbWIzSnRkV3hoSWl3aWNHRnljMlZ5SWl3aWNtVnVaR1Z5SWl3aVkyOXRiV1Z1ZEhNaUxDSnBiWEJ2Y25SbGNpSXNJbUpoY2lJc0luWmhiR2xrWVhScGIyNXpJaXdpYzJWaGNtTm9JaXdpY0hKcGJuUWlMQ0p6YUdWbGRITWlMQ0pqYkdsbGJuUWlMQ0p6WlhKMlpYSWlMQ0p6YUdGd1pYTWlYU3dpWkdWdGJ5STZkSEoxWlgwPQ==');

// Create component
@Component({
    selector: "app-root",
    template: `<div #spreadsheet></div>
        <input type="button" value="Undo" (click)="jspreadsheet.history.undo()" />
        <input type="button" value="Redo" (click)="jspreadsheet.history.redo()" />
        <input type="button" value="Reset" (click)="jspreadsheet.history.reset()" />`,
})
export class AppComponent {
    @ViewChild("spreadsheet") spreadsheet: ElementRef;
    // Worksheets
    worksheets: jspreadsheet.worksheetInstance[];
    // Create a new data grid
    ngAfterViewInit() {
        // Create spreadsheet
        this.worksheets = jspreadsheet(this.spreadsheet.nativeElement, {
            worksheets: [{
                minDimensions: [8, 8],
            }],
        });
    }
}

Releases notes

Differences from version 9

As previously mentioned, the history tracker in JSS version 10 can register changes across all spreadsheets on the screen, regardless of the worksheet instance. Therefore, the syntax to access this feature need revision to the following:

Attribute Description
worksheet.resetHistory() Please use jspreadsheet.history.reset()
worksheet.setHistory() Please use jspreadsheet.history()