Custom MenuView

You can customize the built-in context menu by adding or removing items from the MenuView.

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

You can rewrite the defined context menu style class to apply different styles. For example:

    .gc-ui-contextmenu-group-header {
            font-weight:normal;
            background-color:none;
    }

You can customize the menu item's view structure by overwriting the MenuView's createMenuItemElement function:

    var spread = new GC.Spread.Sheets.Workbook(document.getElementById('ss'));
    function CustomMenuView() {}
    CustomMenuView.prototype = new GC.Spread.Sheets.ContextMenu.MenuView();
    CustomMenuView.prototype.createMenuItemElement = function (menuItemData) {
        // create menu item view by your self
        // you can call super's createMenuItemElement here and only customize a few of menu item
        // should return menu item view back
    };
    spread.contextMenu.menuView = new CustomMenuView();

You can overwrite the MenuView's getCommandOptions function to show the options when the command is executed:

    var spread = new GC.Spread.Sheets.Workbook(document.getElementById('ss'));
    function CustomMenuView() {}
    CustomMenuView.prototype = new GC.Spread.Sheets.ContextMenu.MenuView();
    CustomMenuView.prototype.getCommandOptions = function (menuItemData, host, event) {
        // For most of the menu items, only the selections on sheets or the active sheet’s name are needed
        // but there may be some menu items need special parameters,like color picker,change color
        // To change colors, the command needs to know which color the user clicked on, so overwrite this function and return this
        // parameter's value here.
    };
    spread.contextMenu.menuView = new CustomMenuView();
You can rewrite the defined context menu style class to apply different styles. For example: You can customize the menu item's view structure by overwriting the MenuView's createMenuItemElement function: You can overwrite the MenuView's getCommandOptions function to show the options when the command is executed:
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, colors = ['rgb(255,255,255)', 'rgb(0,255,255)', 'rgb(255,0,255)', 'rgb(255,255,0)', 'rgb(255,0,0)', 'rgb(0,255,0)', 'rgb(0,0,255)', 'rgb(0,0,0)']; function createColorpicker () { let colorPicker = document.createElement('div'); colorPicker.className = 'colorPickerContent'; for (let j = 0; j < 8; j++) { let colorDom = document.createElement("div"); colorDom.className = 'colorDom'; colorDom.style['backgroundColor'] = colors[j]; colorPicker.appendChild(colorDom); } return colorPicker; } function isMenuItemExist (menuData, menuItemName) { let i = 0, count = menuData.length; for (; i < count; i++) { if (menuItemName === menuData[i].name) { return true; } } } function createInput() { var inputBlock = document.createElement('input'); inputBlock.className = 'inputBlock'; inputBlock.type = 'text'; inputBlock.value = '1'; inputBlock.setAttribute('gcUIElement', 'gcContextMenu'); inputBlock.style = 'width: 20px'; inputBlock.onclick = function (ev) { if (ev.target) { ev.stopPropagation() } } return inputBlock; } @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; } addMoreMenuItems () { let spread = this.spread; if (isMenuItemExist(spread.contextMenu.menuData, "selectColorWithBg") || isMenuItemExist(spread.contextMenu.menuData, "insertRows")) { spread.contextMenu.menuData.forEach(function (item, index) { if (item && (item.name === "selectColorWithBg" || item.name === "insertRows")) { spread.contextMenu.menuData.splice(index, 1); } }); return; } let commandManager = spread.commandManager(); // prepair select with a background color command and menu item let selectWithABackgroundColor = { text: "Select Color", name: "selectColorWithBg", workArea: "viewport", subMenu: [ { name: "selectColorPicker", command: "selectWithBg" } ] }; spread.contextMenu.menuData.push(selectWithABackgroundColor); let selectWithABackgroundColorCommand = { canUndo: false, execute: function (spread, options) { if (options.commandOptions) { let style = new GC.Spread.Sheets.Style(); style.name = 'style1'; style.backColor = options.commandOptions; 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("selectWithBg", selectWithABackgroundColorCommand, null, false, false, false, false); // prepair insert rows by specified row count command and menu item let insertRows = { text: "insertRows", name: "insertRows", command: "insertRows", workArea: "rowHeader" }; spread.contextMenu.menuData.push(insertRows); let insertRowsCommand = { canUndo: false, execute: function (spread, options) { var rowCount = parseInt(options.commandOptions); if (!isNaN(rowCount)) { var sheet = spread.getSheetFromName(options.sheetName); sheet.suspendPaint(); sheet.addRows(options.activeRow, rowCount); sheet.resumePaint(); } } }; commandManager.register("insertRows", insertRowsCommand, null, false, false, false, false); // customize context menu function CustomMenuView () { } CustomMenuView.prototype = new GC.Spread.Sheets.ContextMenu.MenuView(); CustomMenuView.prototype.createMenuItemElement = function (menuItemData) { let self = this; if (menuItemData.name === "selectColorPicker") { let containers = GC.Spread.Sheets.ContextMenu.MenuView.prototype.createMenuItemElement.call(self, menuItemData); let supMenuItemContainer = containers[0]; while (supMenuItemContainer.firstChild) { supMenuItemContainer.removeChild(supMenuItemContainer.firstChild); } let colorPicker = createColorpicker(); supMenuItemContainer.appendChild(colorPicker); return supMenuItemContainer; } else if (menuItemData.name === "insertRows") { var containers = GC.Spread.Sheets.ContextMenu.MenuView.prototype.createMenuItemElement.call(self, menuItemData); var supMenuItemContainer = containers[0]; supMenuItemContainer.appendChild(createInput()); return supMenuItemContainer; } else { let menuItemView = GC.Spread.Sheets.ContextMenu.MenuView.prototype.createMenuItemElement.call(self, menuItemData); return menuItemView; } }; CustomMenuView.prototype.getCommandOptions = function (menuItemData, host, event) { if (menuItemData && menuItemData.name === "selectColorPicker") { let ele = event.target || event.srcElement; return ele.style.backgroundColor; } else if (menuItemData && menuItemData.name === "insertRows") { return host.getElementsByClassName("inputBlock")[0].value; } }; spread.contextMenu.menuView = new CustomMenuView(); } } @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"> Right click any cell to bring up the context menu.You can easily modify the context menu depending on your needs. Select the option below to add a new context menu item on viewport that will allow the user to change the background color of the cell and a new context menu item on rowHeader that will allow the user to add row count by specified number. </div> <div class="options-row"> <input type="checkbox" id="addMoreMenuItems" (change)="addMoreMenuItems()"> <label for="addMoreMenuItems">Add background color menu selection</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; } input { display: inline-block; } input[type="text"] { width: 200px; } label { margin-bottom: 6px; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .colorPickerContent{ width: 100%; background-color:white; } .colorDom{ width: 14px; height: 14px; margin:0 0 0 6px; display: inline-block; border: solid 1px #333333; vertical-align: top; }
(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);