Extended Context Menu

The SpreadJS built-in context menu supports adding/removing custom menu options. Each option follows the defined menu data in the JSON schema.

Description
app.component.ts
index.html
app.component.html
styles.css
Copy to CodeMine

You can define the menuData property as shown here:

    var openDialog = {
        text: 'Open Dialog',
        name: 'openDialog',
        command: showLoginDialog,
        workArea: 'viewport'
    };
    spread.contextMenu.menuData.push(openDialog);
    function showLoginDialog() {
        //pop up login dialog
    }

You can add/remove the custom menu options by using the spread.contextMenu.menuData property:

    $.each(spread.contextMenu.menuData, function (p, v) {
        if (v.name === 'openDialog') { //openDialog is a command's name
            spread.contextMenu.menuData.splice(p, 1)
        }
    });
You can define the menuData property as shown here: You can add/remove the custom menu options by using the spread.contextMenu.menuData property:
import { Component, NgModule, enableProdMode } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { SpreadSheetsModule } from '@mescius/spread-sheets-angular'; import GC from '@mescius/spread-sheets'; import './styles.css'; const spreadNS = GC.Spread.Sheets, SheetArea = spreadNS.SheetArea; function showLoginDialog() { if (document.getElementsByClassName('loginBox').length === 0) { let loginBox = document.createElement('div'); loginBox.className = 'loginBox'; loginBox.innerHTML = '<label for="Dialog" class="loginBoxLabel">Dialog:</label>' + '<input type="button" id="okBtn" class="btn-primary button" value="OK">' + '<input type="button" id="cancelBtn" class="btn-primary button" value="Cancel">'; document.body.appendChild(loginBox); document.getElementById('okBtn').onclick = function (){ loginBox.style.display = 'none'; }; document.getElementById('cancelBtn').onclick = function (){ loginBox.style.display = 'none'; }; } let loginBoxEle = document.getElementsByClassName('loginBox')[0]; if (loginBoxEle.style.display === 'none') { loginBoxEle.style.display = 'block'; } } @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { spread: GC.Spread.Sheets.Workbook; hostStyle = { width: 'calc(100% - 280px)', height: '100%', overflow: 'hidden', float: 'left' }; constructor () { } initSpread ($event: any) { this.spread = $event.spread; } addFormatCell() { let spread = this.spread; let commandRemoved = false; spread.contextMenu.menuData.forEach(function (item, index) { if (item && item.name === "markWithRedBg") { spread.contextMenu.menuData.splice(index, 1); commandRemoved = true; } }); if (commandRemoved) { return; } let commandManager = spread.commandManager(); let markWithRedBg = { text: "Format Cells", name: "markWithRedBg", command: "markWithRedBg", workArea: "viewport" }; spread.contextMenu.menuData.push(markWithRedBg); let markWithRedBgCommand = { canUndo: false, execute: function () { let style = new GC.Spread.Sheets.Style(); style.name = 'style1'; style.backColor = 'red'; let sheet = spread.getActiveSheet(); sheet.suspendPaint(); let selections = sheet.getSelections(); let selectionIndex = 0, selectionCount = selections.length; for (; selectionIndex < selectionCount; selectionIndex++) { let selection = selections[selectionIndex]; for (let i = selection.row; i < (selection.row + selection.rowCount); i++) { for (let j = selection.col; j < (selection.col + selection.colCount); j++) { sheet.setStyle(i, j, style, GC.Spread.Sheets.SheetArea.viewport); } } } sheet.resumePaint(); } }; commandManager.register("markWithRedBg", markWithRedBgCommand, null, false, false, false, false); } addOpenDialog() { let spread = this.spread; let commandRemoved = false; spread.contextMenu.menuData.forEach(function (item, index) { if (item && item.name === "openDialog") { spread.contextMenu.menuData.splice(index, 1); commandRemoved = true; } }); if (commandRemoved) { return; } let openDialog = { text: "Open Dialog", name: "openDialog", command: showLoginDialog, workArea: "viewport" }; spread.contextMenu.menuData.push(openDialog); } } @NgModule({ imports: [BrowserModule, SpreadSheetsModule, FormsModule], declarations: [AppComponent], exports: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);
<!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/angular/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/spread/source/data/data.js" type="text/javascript"></script> <!-- Polyfills --> <script src="$DEMOROOT$/en/angular/node_modules/core-js/client/shim.min.js"></script> <script src="$DEMOROOT$/en/angular/node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="$DEMOROOT$/en/angular/node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.import('@angular/compiler'); System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('$DEMOROOT$/en/lib/angular/license.ts'); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<div class="sample-tutorial"> <gc-spread-sheets [hostStyle]="hostStyle" (workbookInitialized)="initSpread($event)"> <gc-worksheet> </gc-worksheet> </gc-spread-sheets> <div class="options-container"> <div class="options-row"> <p>Right click any cell to bring up its context menu. You can then add or remove one of the demo options below:</p> </div> <div class="options-row"> <input type="checkbox" id="addFormatCell" (change)="addFormatCell()"> <label for="addFormatCell">Add "Format Cells" menu item</label> </div> <div class="options-row"> <input type="checkbox" id="addOpenDialog" (change)="addOpenDialog()"> <label for="addOpenDialog">Add "Open Dialog" menu item</label> </div> </div> </div>
.sample-tutorial { position: relative; height: 100%; overflow: hidden; } .options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .options-row { font-size: 14px; padding: 5px; margin-top: 10px; } p{ padding:2px 10px; background-color:#F4F8EB; } input { display: inline-block; } input[type="text"] { width: 200px; } input[type=button] { margin-top: 6px; } label { margin-bottom: 6px; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .loginBox { position: absolute; left: 35%; top: 30%; background-color: white; padding: 20px; border: 1px solid #c6c6c6; box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 5px; z-index: 2000; height: 100px; width: 200px; } .loginBoxLabel { display: inline-block; width: 200px; text-align: center; } .button{ width: 30%; margin: 60px 10%; text-align: center; }
(function (global) { System.config({ transpiler: 'ts', typescriptOptions: { tsconfig: true }, meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'core-js': 'npm:core-js/client/shim.min.js', 'zone': 'npm:zone.js/fesm2015/zone.min.js', 'rxjs': 'npm:rxjs/dist/bundles/rxjs.umd.min.js', '@angular/core': 'npm:@angular/core/fesm2022', '@angular/common': 'npm:@angular/common/fesm2022/common.mjs', '@angular/compiler': 'npm:@angular/compiler/fesm2022/compiler.mjs', '@angular/platform-browser': 'npm:@angular/platform-browser/fesm2022/platform-browser.mjs', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/fesm2022/platform-browser-dynamic.mjs', '@angular/common/http': 'npm:@angular/common/fesm2022/http.mjs', '@angular/router': 'npm:@angular/router/fesm2022/router.mjs', '@angular/forms': 'npm:@angular/forms/fesm2022/forms.mjs', 'jszip': 'npm:jszip/dist/jszip.min.js', 'typescript': 'npm:typescript/lib/typescript.js', 'ts': './plugin.js', 'tslib':'npm:tslib/tslib.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', '@mescius/spread-sheets': 'npm:@mescius/spread-sheets/index.js', '@mescius/spread-sheets-angular': 'npm:@mescius/spread-sheets-angular/fesm2020/mescius-spread-sheets-angular.mjs', '@grapecity/jsob-test-dependency-package/react-components': 'npm:@grapecity/jsob-test-dependency-package/react-components/index.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'ts' }, rxjs: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, "node_modules/@angular": { defaultExtension: 'mjs' }, "@mescius/spread-sheets-angular": { defaultExtension: 'mjs' }, '@angular/core': { defaultExtension: 'mjs', main: 'core.mjs' } } }); })(this);