Question regarding registering commands to control +Z and undo and redo buttons

Posted by: georgeg on 31 July 2025, 6:12 pm EST

    • Post Options:
    • Link

    Posted 31 July 2025, 6:12 pm EST - Updated 31 July 2025, 6:27 pm EST

    How would I connect my custom command for my quick math window to the undo and redo controls in addition to cntrol+Z and control+Y buttons.

    In my initialization function that initializes the SpreadJS and Designer controls:

     //Configuration for added features goes here:
     var config = GC.Spread.Sheets.Designer.DefaultConfig;
     config.commandMap = config.commandMap || {};
     config.commandMap["applyMath"] = applyMathCommand();
     config.contextMenu = config.contextMenu || [];
     config.contextMenu.push("applyMath");
    
     // Initialize a workbook
     // Initialize the SpreadJS Designer inside the HTML element with ID 'spreadJsSpreadsheet' using the specified configuration,
     // then retrieve the associated workbook instance for further interaction or manipulation.
     //var spread = new GC.Spread.Sheets.Workbook(document.getElementById('spreadJsSpreadsheet'), { sheetCount: 1 });
     var designer = new GC.Spread.Sheets.Designer.Designer(document.getElementById('spreadJsSpreadsheet'), config);
     var spread = designer.getWorkbook();
     spread.options.tabEditable = false;
     spread.options.newTabVisible = false;
    
     spread.bind(GC.Spread.Sheets.Events.SelectionChanged, function () {
         designer.refresh(); // Ensures command is re-evaluated
     });

    then in my applyMathCommand() function I have:

    //------------------------------------------------------------------------------
    // applyMathCommand()
    // Defines a custom SpreadJS Designer command named "Apply Math Operation".
    // This command becomes available in the context menu when right-clicking on
    // the viewport area. It checks if the current selection contains any non-empty
    // cells and enables the command accordingly.
    //
    // When executed, it applies a simple math operation (incrementing numeric cells
    // by 1) and supports undo by saving the original cell values to restore them
    // if the user triggers Undo (e.g., Ctrl+Z).
    //
    // Returns:
    //   A command object compatible with GC.Spread.Sheets.Designer.commandMap.
    //
    // Usage:
    //   config.commandMap["applyMath"] = applyMathCommand();
    //------------------------------------------------------------------------------
    function applyMathCommand() {
        return {
            title: "Apply Math Operation",
            text: "Apply Math Operation",
            iconClass: "custom-icon",
            commandName: "applyMath",
            visibleContext: "ClickViewport",
            canUndo: true,
            isEnabled: function (context) {
                var sheet = context.getWorkbook().getActiveSheet();
                var selections = sheet.getSelections();
                if (!selections || selections.length === 0) return false;
    
                for (var s = 0; s < selections.length; s++) {
                    var sel = selections[s];
                    for (var r = sel.row; r < sel.row + sel.rowCount; r++) {
                        for (var c = sel.col; c < sel.col + sel.colCount; c++) {
                            let val = sheet.getValue(r, c);
                            if (val !== null && val !== "") {
                                return true;
                            }
                        }
                    }
                }
                return false;
            },
            execute: function (context, options) {
                var sheet = context.getWorkbook().getActiveSheet();
    
                // Double-check that selection has data before proceeding
                if (!selectionHasData(sheet)) {
                    console.warn("Selected cells are blank. Command aborted.");
                    return;
                }
    
                // Open the popup window (assuming it's already initialized as a Kendo Window)
                let mathOps = $("#math-ops-pop-up-window").data("kendoWindow");
                if (mathOps) {
                    mathOps.center();
                    mathOps.open();
                } else {
                    console.warn("Popup window 'math-ops-pop-up-window' not found or not initialized.");
                }
            }
        };
    }//end applyMathCommmand().
    
    //HELPER FUNCTION FOR ABOVE USED IN EXECUTE:
    // Checks if the current selection in the worksheet contains any non-empty cell values.
    // This function is useful for validating whether a command should proceed,
    // ensuring there's actual data to operate on (e.g. before applying math operations).
    // 
    // It iterates over all selected ranges in the sheet, then over each cell within those ranges,
    // checking if any cell contains a value that is not null and not an empty string.
    // 
    // Returns true if at least one cell has usable data; false otherwise.
    function selectionHasData(sheet) {
        var selections = sheet.getSelections(); // Get all selected ranges
        if (!selections || selections.length === 0) return false; // Return false if nothing is selected
    
        for (var s = 0; s < selections.length; s++) {
            var sel = selections[s]; // Current selection range
            for (var r = sel.row; r < sel.row + sel.rowCount; r++) {
                for (var c = sel.col; c < sel.col + sel.colCount; c++) {
                    let val = sheet.getValue(r, c); // Get cell value
                    if (val !== null && val !== "") { // Check if value is not null or empty
                        return true; // Found valid data, return true
                    }
                }
            }
        }
        return false; // No data found in selection
    }
    

    Now I think there is a way to connect to the listeners or something for the redo or undo buttons in the Designer and the Cntrol+Z command. I tried CoPilot on this and it took me down a terrible path where if froze up the selection box for the SpreadJS…Oof.

    how do I link the custom command I have working in the context menu to trigger or have the undo, redo, and Control+Z to responed?

  • Posted 1 August 2025, 5:08 am EST - Updated 1 August 2025, 5:14 am EST

    Hi,

    Based on your description and the original implementation, we understand that the goal is to:

    • Execute a math operation (eg, incrementing numeric cells by 1),
    • Register the action as undoable,
    • Ensure that Ctrl+Z/Ctrl+Y and the Designer’s Undo/Redo buttons behave as expected.

    To fulfill this, we recommend separating the UI trigger (Designer command) from the logic (SpreadJS Command). The Designer command should call a SpreadJS command that:

    • Uses Commands. startTransaction and Commands.endTransaction to wrap changes and include the actions in the Undo/Redo context.
    • Performs a mathematical operation.

    Please refer to the following code snippet that demonstrates the above algorithm:

    execute: function (context, options) {
        const spread = context.getWorkbook();
        const sheet = spread.getActiveSheet();
        if (!selectionHasData(sheet)) {
            console.warn("Selected cells are blank. Command aborted.");
            return;
        }
        spread.commandManager().execute({cmd: "applyMathSpread", sheetName: sheet.name(), selections: sheet.getSelections()});
    }
    function addCommand(spread) {
        const command = {
            canUndo: true,
            execute: function (context, options, isUndo) {
                const Commands = GC.Spread.Sheets.Commands;
                options.cmd = "applyMathSpread";
                if (isUndo) {
                    Commands.undoTransaction(context, options);
                    return true;
                } else {
                    const sheet = context.getSheetFromName(options.sheetName);
                    const selections = options.selections;
                    Commands.startTransaction(context, options);
                    sheet.suspendPaint();
                    sheet.suspendEvent();
                    sheet.suspendCalcService();
                    for (let s = 0; s < selections.length; s++) {
                        const sel = selections[s];
                        const shArray = sheet.getArray(sel.row, sel.col, sel.rowCount, sel.colCount);
                        let isNumber = false;
                        for(let r = 0; r < sel.rowCount; r++) {
                            for(let c = 0; c < sel.colCount; c++) {
                                let val = shArray[r][c];
                                if(typeof val === "number" && !isNaN(val)) {
                                    shArray[r][c] = val + 1;
                                    if(!isNumber) isNumber = true;
                                }
                            }
                        }
                        if(isNumber) sheet.setArray(sel.row, sel.col, shArray);
                    }
                    sheet.resumeCalcService();
                    sheet.resumeEvent();
                    sheet.resumePaint();
                    Commands.endTransaction(context, options);
                    return true;
                }
            }
        };
        spread.commandManager().register("applyMathSpread", command);
    }

    You can further refer to the attached sample that uses the above code snippets and registers the math command under an Undo/Redo context (see below).

    Please let us know if you encounter any further issues or require additional assistance.

    Kind Regards,

    Chirag Gupta

    Attachment: https://jscodemine.mescius.io/share/lZ24ZEcF40WyWw2BWGDvRg/?defaultOpen={"OpenedFileName"%3A["%2Findex.html"%2C"%2Fapp.js"]%2C"ActiveFile"%3A"%2Fapp.js"}

    References:

    Working:

  • Posted 1 August 2025, 11:47 am EST - Updated 1 August 2025, 12:01 pm EST

    Ok this is tricky because my solution opens a kendoWindow to do select from three math operations all the execute handler in my: applyMathCommand(); does is check if the sheet has data or if the selected cells have values then opens up a kendoWindow where the user can select their math operation:

            execute: function (context, options) {
                var sheet = context.getWorkbook().getActiveSheet();
    
                // Double-check that selection has data before proceeding
                if (!selectionHasData(sheet)) {
                    console.warn("Selected cells are blank. Command aborted.");
                    return;
                }
    
                // Open the popup window (assuming it's already initialized as a Kendo Window)<<-- Here.
                let mathOps = $("#math-ops-pop-up-window").data("kendoWindow");
                if (mathOps) {
                    mathOps.center();
                    mathOps.open();
                } else {
                    console.warn("Popup window 'math-ops-pop-up-window' not found or not initialized.");
                }
            }

    If the sheet is not empty, and the cells selected have values then—> let mathOps = $(“#math-ops-pop-up-window”).data(“kendoWindow”);

    When the user selects the context menu option…

    …the above execute code does its thing.



    …and the kendoWindow (pop-up) appears (figure 2.)…And the user:

    1. Selects what math op they want…
    2. types in the amount in the kendoNumericTextBox
    3. optionally selects whether the numeric amount is a percent value of the operand being operated upon.
    4. clicks Apply if happy.( the Cancel button makes the window disappear).

    Once the user clicks Apply the kendoButton click event handler calls this function:

    // Executes a mathematical operation (Add, Subtract, Multiply) on selected cells
    // in the active worksheet using a numeric input provided by the user.
    //
    // Retrieves the SpreadJS Designer and workbook instance.
    // Validates the user's input to ensure it is a valid number.
    // Determines whether the input should be treated as a percentage.
    // Applies the selected operation to numeric cells in the active selection.
    // Displays alerts if non-numeric values are encountered.
    // Resets UI controls and closes the math operation popup on completion.
    function performMathOp() {
        var designer = GC.Spread.Sheets.Designer.findControl(document.getElementById('spreadJsSpreadsheet'));
        var spread = designer.getWorkbook();
        var sheet = spread.getActiveSheet();
        var number = $("#numberInput").data("kendoNumericTextBox").value();
    
        // Detect if percentage toggle is active -- GREY not selected and BLUE == selected.
        var isPercent = _view.get("percentSelected");
    
    
        
    
        //Validate Input:
        // Check if value is null, undefined, or not a number
        if (number === null || number === undefined || isNaN(number)) {
    
            pksUI.spreadJSAlertDialog("Validation Error:", "<span style='color: red;'>Please enter a valid number before applying the operation.</span>");
            // Reset controls to default state
            console.error("In performMathOp: Mathamatical opperation attempted on a non-numeric value.");
            JL("jsLogger").error("In performMathOp: Mathamatical opperation attempted on a non-numeric value.");
            resetControls();
    
            return; // Exit early to prevent execution
        }
    
        //Select the arithmetic operation from the selected radio button.
        var selectedOp = document.querySelector('input[name="math_op"]:checked');
        var operation = selectedOp ? selectedOp.value : null;
        var retVal = true;
        var selectedRanges = spread.getActiveSheet().getSelections();
    
         selectedRanges.forEach(function (range) {
            for (var r = range.row; r < range.row + range.rowCount; r++) {
                for (var c = range.col; c < range.col + range.colCount; c++) {
                    var val = sheet.getValue(r, c);
                    if (typeof val === "number") {
    
                        //If the percent toggle button in true/BLUE then convert.
                        //Ergo...modify to make the number in the input a percent value 30 is percent then it needs to be converted to .30.
                        var modifier = isPercent ? val * (number / 100) : number;//ternary operator.
    
                        //Now do operation:
                        if (operation === "Multiply") {
                            sheet.setValue(r, c, val * modifier);
                        } else if (operation === "Add") {
                            sheet.setValue(r, c, val + modifier);
                        } else if (operation === "Subtract") {
                            sheet.setValue(r, c, val - modifier);
                        }
                    } else if (val !== null && val !== undefined) {
    
                        var columnName = sheet.getValue(0, c); // Column name from first row
    
                        pksUI.spreadJSAlertDialog(
                            "Validation Error:",
                            `<span style='color: red;'> Cannot perform arithmetic on a non-numeric value at  row ${r + 1} of column ${c + 1}: <strong><i>${columnName}</i></strong>.</span>`
                        );
                        // Reset controls to default state
                        resetControls();
                        return;
                    }
                }
            }
    
        });
    
        let mathOps = $("#math-ops-pop-up-window").data("kendoWindow");
        mathOps.close();
    
        return;
    }

    Seems like its not so easy as registering the commands separately. How can I salvage this? The kendo Click action actually calls this function:

        //Kendo related controls:
        $("#Apply").kendoButton({
            icon: "check-circle",
            themeColor: "primary",
            click: function () {
                performMathOp();
    
            }
        });

    Big challenge I have is this is all tangled up (tightly coupled with the Kendo controls. Let me see if I can’t peice this together from what you have, and in the mean time if there is a simple solution to this please share it.

    Thanks!

    -George

  • Posted 1 August 2025, 12:25 pm EST - Updated 1 August 2025, 12:30 pm EST

    Also… like I said I wasn’t clear enough to reveal the intermediate step of the pop-up window (see above)…so another aproach is necessary…because selecting the cells then applying a static addition operation or any other operation directly isn’t what I am doing. This:

    won’t work because the user has to set up his arithmetic operation in a pop-up window. It doesn’t trigger the operation directly.

  • Posted 2 August 2025, 1:57 am EST - Updated 2 August 2025, 2:02 am EST

    Hi,

    We understand that the popup window is triggered when the math operation is selected from the SpreadJS context menu, and the actual mathematical operation to be performed on the selection is chosen from the popup. The previously shared solution was a basic prototype of a single increment operation. But as per your request, we have implemented a multi-operation command executor.

    Please refer to the attached sample that demonstrates the working of the SpreadJS command manager with a popup command integrating undo/redo context (see below).

    Please feel free to reach out if you encounter any further issues.

    Kind Regards,

    Chirag Gupta

    Attachment: https://jscodemine.mescius.io/share/lZ24ZEcF40WyWw2BWGDvRg/?defaultOpen={"OpenedFileName"%3A["%2Findex.html"%2C"%2Fapp.js"%2C"%2Fstyles.css"]%2C"ActiveFile"%3A"%2Fapp.js"}

    Working:

  • Posted 5 January 2026, 7:14 pm EST - Updated 6 January 2026, 1:57 pm EST

    Ok I have managed to implement/ integrate your code with my code including the kendo for jQuery controls. The problem I am having now is that once I apply my changes… if it is the first operation on a spreadsheet that has been loaded from an API … and not typed in by hand as shown in your video… the undo button does not enable… so I cannot click the undo button.



    I must be forgetting something that registers the changes, then enables the undo button and gives it a label for the action?.

    Now if I follow your steps above on a blank sheet I can click the undo button and it will revert the applied math operation change… but if I load in a sheet and do the math operation on multiple cells the undo button does not get enabled.

    What am I missing?

    George

    George

  • Posted 6 January 2026, 6:21 pm EST - Updated 6 January 2026, 6:26 pm EST

    UPDATE:

    Ok I have the undo button activated… I think the designer needs to be refreshed after the changes are applied (see below)

    But now I cannot get the undoText to appear in the undo / redo list.

    I think I covered all my bases.

    On initialization:

        //Configuration for added features goes here:
        var config = GC.Spread.Sheets.Designer.DefaultConfig;
        config.commandMap = config.commandMap || {};
        config.commandMap["applyMath"] = applyMathCommand();//this is the configuration block.
        config.contextMenu = config.contextMenu || [];
        config.contextMenu.push("applyMath");
    
        //Second mapping for the workbook command (applyMathSpread) so the Designer’s undo list has a label to render.
        config.commandMap["applyMathSpread"] = {
            text: "Apply Math Operation",
            title: "Apply Math Operation",
            commandName: "applyMathSpread"
        };
        ``
    
    
        addEmailButton(config);
    
        // Initialize a workbook
        // Initialize the SpreadJS Designer inside the HTML element with ID 'spreadJsSpreadsheet' using the specified configuration,
        // then retrieve the associated workbook instance for further interaction or manipulation.
        //var spread = new GC.Spread.Sheets.Workbook(document.getElementById('spreadJsSpreadsheet'), { sheetCount: 1 });
        var designer = new GC.Spread.Sheets.Designer.Designer(document.getElementById('spreadJsSpreadsheet'), config);
    
        var spread = designer.getWorkbook();
        spread.options.tabEditable = false;
        spread.options.newTabVisible = false;
        addCommand(spread);
    
        spread.bind(GC.Spread.Sheets.Events.SelectionChanged, function () {
            designer.refresh(); // Ensures command is re-evaluated
        });
    
    

    In addCommand():

    function addCommand(spread) {
        const command = {
            canUndo: true,
            undoText: "Apply Math Operation", // <---THIS...does this conflict? maybe?
            execute: function (context, options, isUndo) {
                const Commands = GC.Spread.Sheets.Commands;
                //options.cmd = "applyMathSpread"; //removed this.cmd 
    										// must originate from the execute call, not be overwritten inside 
                                                                                    // the command (correct?). 
                if (isUndo) {
                    Commands.undoTransaction(context, options);
                    return true;
                } else {
    
    
                    // Inject a label in the transaction options... SANITY ...still doesn't help.
                    options.undoText = options.undoText || "Apply Math Operation"; 
                    // (Some builds also accept options.title)
                    options.title = "Apply Math Operation";//doesn't seem to do anything for 18.0.3.
    
                    const sheet = context.getSheetFromName(options.sheetName);
                    const value = options.value;
                    const percentDivider = options.isPercent ? 100 : 1;
                    const operation = options.operation;
                    const selections = options.selections;
                    Commands.startTransaction(context, options);
                    sheet.suspendPaint();
                    sheet.suspendEvent();
                    sheet.suspendCalcService();
                    for (let s = 0; s < selections.length; s++) {
                        const sel = selections[s];
                        const shArray = sheet.getArray(sel.row, sel.col, sel.rowCount, sel.colCount);
                        let isChanged = false;
                        for (let r = 0; r < sel.rowCount; r++) {
                            for (let c = 0; c < sel.colCount; c++) {
                                let val = shArray[r][c];
                                if (typeof val === "number" && !isNaN(val)) {// skips cell if value operated on is not of number type.
                                    switch (operation) {
                                        case "Add":
                                            shArray[r][c] += (value / percentDivider);
                                            break;
                                        case "Multiply":
                                            shArray[r][c] *= (value / percentDivider);
                                            break;
                                        case "Subtract":
                                            shArray[r][c] -= (value / percentDivider);
                                    }
                                    if (!isChanged) isChanged = true;
                                }
                            }
                        }
                        if (isChanged) sheet.setArray(sel.row, sel.col, shArray);
                    }
                    sheet.resumeCalcService();
                    sheet.resumeEvent();
                    sheet.resumePaint();
                    Commands.endTransaction(context, options);
                    return true;
                }
            }
        };
        console.log("REGISTERING COMMAND: applyMathSpread:",command);
        spread.commandManager().register("applyMathSpread", command);
    
    
    }
  • Posted 7 January 2026, 2:28 am EST - Updated 7 January 2026, 2:33 am EST

    Hi George,

    We can confirm that the applyMathSpread command is not visible in the Designer when it is executed for the first time, even though the command is registered in the SpreadJS undo stack. Please refer to the attached video:

    To resolve this issue, we recommend refreshing the designer only after the command is executed, not when the SpreadJS selection change event is triggered, as below:

    spread.commandManager().execute(options);
    designer.refresh();

    Additionally, the code snippet you shared above does not resolve the issue because of the following reasons:

    1. It is not required to map the workbook commands with the Designer command map. The workbook commands are required to be registered with the workbook’s command manager only. Therefore, we strongly recommend you omit the code below from your application:
         config.commandMap["applyMathSpread"] = {
              text: "Apply Math Operation",
              title: "Apply Math Operation",
              commandName: "applyMathSpread"
         };
    2. The undoText, options.undoText and options.title do not conflict with the working of the workbook command; however, there is no use of these properties in the command execution, therefore, we strongly recommend you omit these properties from the command execution definition.

    You can further refer to the attached sample that demonstrates the correct updation of the designer undo UI (see below).

    In case the issue does not resolve on your end, please share a stripped-down sample replicating the issue. You can also fork, modify, and share the attached sample.

    Please let us know if you encounter any further issues or require additional assistance.

    Kind Regards,

    Chirag

    Attachment: https://jscodemine.mescius.io/share/lZ24ZEcF40WyWw2BWGDvRg

    Working:

  • Posted 7 January 2026, 7:19 pm EST

    Hi,

    Ok:

    • removed the code that maps the workbook commands with the Designer command map.

      *added the designer.refresh() … did this in between our correspondences.

    Here is my updated function with verbose commenting … hopefully this will be helpful and maybe uncover some gaffs I have:

    function applyMathConfirmed() {
        // ---------------------------------------------------------------------------------------------
        // 1) Collect inputs safely from the DOM/UI.
        //    - Radio button group 'math_op' provides the operation string.
        //    - Kendo NumericTextBox provides the numeric value.
        //    - View-model '_view' provides the percent toggle state.
        // ---------------------------------------------------------------------------------------------
        const opEl = document.querySelector('input[name="math_op"]:checked');
        const operation = opEl?.value;   // Expected: "Add" | "Subtract" | "Multiply"
        const value = $("#numberInput").data("kendoNumericTextBox").value(); // Raw number from Kendo
        const isPercent = _view.get("percentSelected"); // Boolean from view-model
    
        // ---------------------------------------------------------------------------------------------
        // 2) Validate inputs:
        //    - Operation must be present (a radio button is selected).
        //    - Value must be a number (not null/undefined and not NaN).
        //    If invalid, show alert, log error, reset UI controls, and exit early.
        // ---------------------------------------------------------------------------------------------
        if (!operation || value == null || isNaN(value)) {
            pksUI.spreadJSAlertDialog(
                "Validation Error:",
                "<span style='color: red;'>Please enter a valid number before applying the operation.</span>"
            );
            JL("jsLogger").error("In performMathOp: Mathematical operation attempted on a non-numeric value.");
            resetControls();
            return;
        }
    
        // ---------------------------------------------------------------------------------------------
        // 3) Resolve Designer and workbook context, then capture the current selection from the active
        //    sheet. Selections may include multiple non-contiguous ranges, and are passed through to
        //    the command as-is.
        // ---------------------------------------------------------------------------------------------
        const designer = GC.Spread.Sheets.Designer.findControl(document.getElementById('spreadJsSpreadsheet'));
        const spread = designer.getWorkbook();
        const sheet = spread.getActiveSheet();
        const selections = sheet.getSelections(); // Array<GC.Spread.Sheets.Range>
    
        // Defensive guard: if no selections or no sheet, abort gracefully.
        if (!sheet || !selections || selections.length === 0) {
            pksUI.spreadJSAlertDialog(
                "Selection Error:",
                "<span style='color: red;'>Please select at least one cell range to apply the operation.</span>"
            );
            JL("jsLogger").warn("applyMathConfirmed: No active sheet or no selections present.");
            resetControls();
            return;
        }
    
        // ---------------------------------------------------------------------------------------------
        // 4) Ensure undo is allowed (guarantees the undo manager is active). This is important to make
        //    sure the command will be tracked and can be undone/redone by the user.
        // ---------------------------------------------------------------------------------------------
        spread.options.allowUndo = true;
    
        // ---------------------------------------------------------------------------------------------
        // 5) Execute the custom command exactly once, passing all required context/data. This pushes
        //    an entry onto the undo stack with the label configured in the command registration.
        //    - cmd: must match the name used in spread.commandManager().register()
        // ---------------------------------------------------------------------------------------------
        spread.commandManager().execute({
            cmd: "applyMathSpread",            // Registered in addCommand(spread)
            sheetName: sheet.name(),           // Target sheet name (string)
            selections: selections,            // Current selections (array of ranges)
            value: parseFloat(value),          // Numeric operand (ensure number type)
            operation: operation,              // "Add" | "Subtract" | "Multiply"
            isPercent: isPercent               // Boolean for percent-mode behavior
        });
    
    
    
        // ---------------------------------------------------------------------------------------------
        // Diagnostics (v18.0.3):
        // - Inspect the undo stack to confirm the command was recorded. Useful in dev/verification
        //   builds; consider removing or gating behind an environment flag in production.
        // ---------------------------------------------------------------------------------------------
        const stack = spread.undoManager().getUndoStack();
        console.table(stack.map(item => ({
            cmd: item.cmd,
            sheetName: item.sheetName
        })));
    
        // ---------------------------------------------------------------------------------------------
        // Designer toolbar sometimes needs a refresh to enable Undo immediately after execution.
        // Forcing a refresh here ensures the UI state (Undo button enabled) matches the model.
        // ---------------------------------------------------------------------------------------------
        designer.refresh();
    
        // ---------------------------------------------------------------------------------------------
        // 6) Cleanup UI:
        //    - Close the math-ops popup window.
        //    - Reset the UI controls to their default state for the next operation.
        // ---------------------------------------------------------------------------------------------
        let mathOps = $("#math-ops-pop-up-window").data("kendoWindow");
        mathOps && mathOps.close();
        resetControls();
    }
    

    At this point in time the undo / redo works but darn it… still no text (Apply Math Operation) in the list.

    Thanks again for your patience,

    George

  • Posted 8 January 2026, 4:31 am EST

    Hi George,

    Thank you for sharing the complete details.

    We were able to reproduce the issue using SpreadJS versions 18.0.3 through 18.0.7. This issue has already been resolved in SpreadJS v18.1.0. We therefore recommend upgrading from v18.0.3 to v18.1.0 or above to resolve the issue on your end.

    Please let us know if you encounter any further issues or need additional assistance.

    Kind Regards,

    Chirag

  • Posted 8 January 2026, 2:08 pm EST

    I need to incrementally update eventually to 19 I guess but I’d like to see what 18.1 does… having difficulty finding the repository or SpreadJS versions …I can see v19 but can’t find 18.1 …like to start with 18.1 where are the repos to update the version.

    George

  • Posted 8 January 2026, 11:52 pm EST

    Hi George,

    Yes, you can absolutely upgrade incrementally.

    To help you evaluate SpreadJS v18.1 before moving to v19, please refer to the following official resources:

    1. All SpreadJS releases (including older versions): https://developer.mescius.com/spreadjs/releases
    2. npm package migration guide: https://developer.mescius.com/spreadjs/docs/getstarted/npm-package-migration-guide
    3. Release notes (version-wise details and changes): https://developer.mescius.com/spreadjs/docs/rnotes
    4. SpreadJS 18 hotfix information: https://developer.mescius.com/spreadjs/releases/spreadjs-18-hotfixes

    To obtain SpreadJS v18.1.0, you can use any of the following options:

    1. Install the specific version directly from npm - https://www.npmjs.com/package/@mescius/spread-sheets/v/18.1.0
    2. Download the full distribution package from the CDN: https://cdn.mescius.com/spreadjs/18.1.0/Files/SpreadJS.Release.18.1.0.zip

    This should allow you to test v18.1 in your environment before proceeding with further upgrades.

    Please let us know if you require any further assistance.

    Kind regards,

    Chirag

  • Posted 9 January 2026, 1:48 pm EST - Updated 9 January 2026, 1:53 pm EST

    Thanks for the links Chirag… ok now I have 18.1 loaded…and I am seeing something:

    However I would rather see the undoText:

    undoText: "Apply Math Operation", // <---THIS.

    as it appears in the addCommand() function (see previous message).

    I will poke around on this a bit and see if I can make this happen on my own, in the mean time any suggests you all have would be much appreciated.

    Thanks again for your help and patience ^___^

    George

  • Posted 11 January 2026, 10:30 pm EST

    Hi George,

    Apologies for the delay caused over the weekend.

    By default, the SpreadJS Designer displays the underlying SpreadJS command name in the Undo history.

    To display a custom Undo text in the Designer, you need to map the SpreadJS command name to a custom label in the Designer resources. The following code snippet demonstrates this approach:

    const designerResouces = GC.Spread.Sheets.Designer.getResources();
    designerResouces.commandMap["applyMathSpread"] = "Apply Math Operation";
    GC.Spread.Sheets.Designer.setResources(designerResouces);

    You can also refer to the updated code sample below, which illustrates the complete working of this implementation: https://jscodemine.mescius.io/share/lZ24ZEcF40WyWw2BWGDvRg.

    Please let us know if you need any further assistance.

    Kind Regards,

    Chirag

Need extra support?

Upgrade your support plan and get personal unlimited phone support with our customer engagement team

Learn More

Forum Channels