Undo Manager

SpreadJS allows you to have better control over the undo/redo stack, including setting the maximum size of actions to record.

Description
app.jsx
app-func.jsx
app-class.jsx
index.html
styles.css
data.jsx
Copy to CodeMine

You can get the redoStack of SpreadJS using the getRedoStack method.

    var spread = GC.Spread.Sheets.findControl(document.getElementById('ss'));
    let redoStack = spread.undoManager().getRedoStack();

You can get the undoStack of SpreadJS using the getUndoStack method.

    var spread = GC.Spread.Sheets.findControl(document.getElementById('ss'));
    let undoStack = spread.undoManager().getUndoStack();

You can get or set the max size of the redoStack/undoStack using the maxSize method.

    var spread = GC.Spread.Sheets.findControl(document.getElementById('ss'));
    let undoManager = spread.undoManager();
    undoManager.maxSize(20); // set max size.
    let maxSize = undoManager.maxSize(); // get max size. 
You can get the redoStack of SpreadJS using the getRedoStack method. You can get the undoStack of SpreadJS using the getUndoStack method. You can get or set the max size of the redoStack/undoStack using the maxSize method.
import * as React from 'react'; import * as ReactDOM from 'react-dom'; import './styles.css'; import { AppFunc } from './app-func'; import { App } from './app-class'; // 1. Functional Component sample ReactDOM.render(<AppFunc />, document.getElementById('app')); // 2. Class Component sample // ReactDOM.render(<App />, document.getElementById('app'));
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import { SpreadSheets, Worksheet } from '@mescius/spread-sheets-react'; import './styles.css'; import { getData } from './data'; function _getElementById(id) { return document.getElementById(id); } export function AppFunc() { const initSpread = (spread) => { spread.fromJSON(getData()[0]); attachEvents(spread); executeCommand(spread); _getElementById("gc-max-size").value = 10; updateInfo(spread); } const attachEvents = (spread) => { const events = GC.Spread.Sheets.Events; spread.bind(events.ValueChanged, () => updateInfo(spread)); spread.bind(events.ViewZoomed, () => updateInfo(spread)); spread.bind(events.ColumnChanged, () => updateInfo(spread)); spread.bind(events.ColumnWidthChanged, () => updateInfo(spread)); spread.bind(events.RowChanged, () => updateInfo(spread)); spread.bind(events.RowHeightChanged, () => updateInfo(spread)); const setButton = _getElementById("gc-max-size-set"); const maxSizeDom = _getElementById("gc-max-size"); const redoButton = _getElementById("gc-redo-button"); const undoButton = _getElementById("gc-undo-button"); const undoManager = spread.undoManager(); setButton.addEventListener("click", () => { const value = Number(maxSizeDom.value); if (!isNaN(value)) { spread.undoManager().maxSize(value); } }); redoButton.addEventListener("click", () => { undoManager.redo(); updateInfo(spread); }); undoButton.addEventListener("click", () => { undoManager.undo(); updateInfo(spread); }); } const executeCommand = (spread) => { const commandManager = spread.commandManager(); const sheet = spread.getActiveSheet(); const sheetName = sheet.name(); commandManager.execute({ cmd: "resizeColumn", sheetName: sheetName, size: 70, rowHeader: false, columns: [{ firstCol: 0, lastCol: 5 }], }); commandManager.execute({ cmd: "editCell", sheetName: sheetName, row: 0, col: 0, newValue: "Edit Cell", autoFormat: true, }); commandManager.execute({ cmd: "resizeRow", sheetName: sheetName, rows: [{ firstRow: 0, lastRow: 5, }], size: 30, columnHeader: false, }) spread.undoManager().undo(); } const updateInfo = (spread) => { const undoManager = spread.undoManager(); setTimeout(() => { updateUndoInfo(undoManager); updateRedoInfo(undoManager); }, 0); } const updateUndoInfo = (undoManager) => { const undoStack = undoManager.getUndoStack(); const description = getDescription(undoStack); const undoContainer = _getElementById("gc-undo-stack-container"); undoContainer.value = description; } const updateRedoInfo = (undoManager) => { const redoStack = undoManager.getRedoStack(); const description = getDescription(redoStack); const redoContainer = _getElementById("gc-redo-stack-container"); redoContainer.value = description; } const getDescription = (actionStack) => { let description = ""; for (let i = 0; i < actionStack.length; i++) { description = description + (i + 1) + "." + actionStack[i].cmd + "\r\n"; } return description; } return <div class="sample-tutorial"> <div id="ss" class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => initSpread(spread)}> <Worksheet></Worksheet> </SpreadSheets> </div> <div class="options-container"> <label>Make any changes such as editing text or resizing row and cols then undo or redo your actions using the below buttons.</label> <div class="option-row"> <input type="button" value="Redo" class="gc-redo" id="gc-redo-button" /> <input type="button" value="Undo" class="gc-undo" id="gc-undo-button" /> </div> <div class="gc-undo-stack-container option-row"> <label>The undoStack includes the following command:</label> <textarea type="text" class="gc-stack-list" id="gc-undo-stack-container" cols="30"></textarea> </div> <div class="gc-redo-stack-container option-row"> <label>The redoStack includes the following command:</label> <textarea type="text" class="gc-stack-list" id="gc-redo-stack-container" cols="30"></textarea> </div> <div class="option-row"> <label>Undo/Redo Stack Max Size</label> <input type="number" min="0" max="2147483647" id="gc-max-size" class="gc-max-size" /> <input type="button" id="gc-max-size-set" value="set" /> </div> </div> </div> }
import * as React from 'react'; import GC from '@mescius/spread-sheets'; import { SpreadSheets, Worksheet } from '@mescius/spread-sheets-react'; import './styles.css'; import { getData } from './data'; const Component = React.Component; function _getElementById(id) { return document.getElementById(id); } export class App extends Component { constructor(props) { super(props); this.spread = null; } render() { return <div class="sample-tutorial"> <div id="ss" class="sample-spreadsheets"> <SpreadSheets workbookInitialized={spread => this.initSpread(spread)}> <Worksheet></Worksheet> </SpreadSheets> </div> <div class="options-container"> <label>Make any changes such as editing text or resizing row and cols then undo or redo your actions using the below buttons.</label> <div class="option-row"> <input type="button" value="Redo" class="gc-redo" id="gc-redo-button" /> <input type="button" value="Undo" class="gc-undo" id="gc-undo-button" /> </div> <div class="gc-undo-stack-container option-row"> <label>The undoStack includes the following command:</label> <textarea type="text" class="gc-stack-list" id="gc-undo-stack-container" cols="30"></textarea> </div> <div class="gc-redo-stack-container option-row"> <label>The redoStack includes the following command:</label> <textarea type="text" class="gc-stack-list" id="gc-redo-stack-container" cols="30"></textarea> </div> <div class="option-row"> <label>Undo/Redo Stack Max Size</label> <input type="number" min="0" max="2147483647" id="gc-max-size" class="gc-max-size" /> <input type="button" id="gc-max-size-set" value="set" /> </div> </div> </div> } initSpread(spread) { this.spread = spread; spread.fromJSON(getData()[0]); this.attachEvents(); this.executeCommand(); _getElementById("gc-max-size").value = 10; this.updateInfo(); } attachEvents() { var spread = this.spread; var events = GC.Spread.Sheets.Events; spread.bind(events.ValueChanged, this.updateInfo.bind(this)); spread.bind(events.ViewZoomed, this.updateInfo.bind(this)); spread.bind(events.ColumnChanged, this.updateInfo.bind(this)); spread.bind(events.ColumnWidthChanged, this.updateInfo.bind(this)); spread.bind(events.RowChanged, this.updateInfo.bind(this)); spread.bind(events.RowHeightChanged, this.updateInfo.bind(this)); let setButton = _getElementById("gc-max-size-set"); let maxSizeDom = _getElementById("gc-max-size"); let redoButton = _getElementById("gc-redo-button"); let undoButton = _getElementById("gc-undo-button"); let undoManager = spread.undoManager(); setButton.addEventListener("click", () => { let value = Number(maxSizeDom.value); if (!isNaN(value)) { spread.undoManager().maxSize(value); } }); redoButton.addEventListener("click", () => { undoManager.redo(); this.updateInfo(); }); undoButton.addEventListener("click", () => { undoManager.undo(); this.updateInfo(); }); } executeCommand() { var spread = this.spread; var commandManager = spread.commandManager(); let sheet = spread.getActiveSheet(); let sheetName = sheet.name(); commandManager.execute({ cmd: "resizeColumn", sheetName: sheetName, size: 70, rowHeader: false, columns: [{ firstCol: 0, lastCol: 5 }], }); commandManager.execute({ cmd: "editCell", sheetName: sheetName, row: 0, col: 0, newValue: "Edit Cell", autoFormat: true, }); commandManager.execute({ cmd: "resizeRow", sheetName: sheetName, rows: [{ firstRow: 0, lastRow: 5, }], size: 30, columnHeader: false, }) spread.undoManager().undo(); } updateInfo() { var undoManager = this.spread.undoManager(); setTimeout(() => { this.updateUndoInfo(undoManager); this.updateRedoInfo(undoManager); }, 0); } updateUndoInfo(undoManager) { var undoStack = undoManager.getUndoStack(); var description = this.getDescription(undoStack); let undoContainer = _getElementById("gc-undo-stack-container"); undoContainer.value = description; } updateRedoInfo(undoManager) { var redoStack = undoManager.getRedoStack(); let description = this.getDescription(redoStack); let redoContainer = _getElementById("gc-redo-stack-container"); redoContainer.value = description; } getDescription(actionStack) { let description = ""; for (var i = 0; i < actionStack.length; i++) { description = description + (i + 1) + "." + actionStack[i].cmd + "\r\n"; } return description; } }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/react/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <!-- SystemJS --> <script src="$DEMOROOT$/en/react/node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('$DEMOROOT$/en/lib/react/license.js').then(function () { System.import('./src/app'); }); </script> </head> <body> <div id="app"></div> </body> </html>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .sample-spreadsheets { width: calc(100% - 280px); height: 100%; overflow: hidden; float: left; } .options-container{ float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .option-row { font-size: 14px; padding: 5px; margin-top: 10px; } .gc-stack-list{ width: 100%; height: 150px; margin-top: 10px; } #app { height: 100%; } .gc-stack-button{ margin: 0 5px; }
export function getData(){ var data = [{ "version":"12.0.0", "tabStripRatio":0.6, "sheetCount":1, "sheets":{ "Sheet1":{ "name":"Sheet1", "rowCount":114, "columnCount":21, "activeRow":2, "activeCol":2, "data":{ "dataTable":{ "2":{ "2":{ "value":"Student", "style": { "foreColor" : "#FFFFFF", "backColor" : "#82BC00", "hAlign" : 1 } }, "3":{ "value":"Language", "style": { "foreColor" : "#FFFFFF", "backColor" : "#82BC00", "hAlign" : 1 } }, "4":{ "value":"Math", "style": { "foreColor" : "#FFFFFF", "backColor" : "#82BC00", "hAlign" : 1 } }, "5":{ "value":"Science", "style": { "foreColor" : "#FFFFFF", "backColor" : "#82BC00", "hAlign" : 1 } }, "6":{ "value":"Music", "style": { "foreColor" : "#FFFFFF", "backColor" : "#82BC00", "hAlign" : 1 } }, "7":{ "value":"History", "style": { "foreColor" : "#FFFFFF", "backColor" : "#82BC00", "hAlign" : 1 } } }, "3":{ "2":{ "value":"Ally", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":65.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":75.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":91.8, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } }, "4":{ "2":{ "value":"Tom", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":81.8, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":95, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":75.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":88, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } }, "5":{ "2":{ "value":"Jack", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":68.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":65.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":88, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":79, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } }, "6":{ "2":{ "value":"John", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":68.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":81.8, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":86, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":75, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":86, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } }, "7":{ "2":{ "value":"Lily", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":65.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":91.8, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } }, "8":{ "2":{ "value":"Linda", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":75.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":88, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":65.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":65.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } }, "9":{ "2":{ "value":"Will", "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "3":{ "value":95.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "4":{ "value":94.8, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "5":{ "value":94, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "6":{ "value":55.2, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} }, "7":{ "value":88, "style":{"backColor" : "#F4F8EB", "hAlign" : 1} } } } }, } } }]; return data; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, react: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-react': 'npm:@mescius/spread-sheets-react/index.js', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js', 'react': 'npm:react/umd/react.production.min.js', 'react-dom': 'npm:react-dom/umd/react-dom.production.min.js', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);