| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- import BasePlugin from './../_base';
- import Hooks from './../../pluginHooks';
- import {arrayEach} from './../../helpers/array';
- import CommandExecutor from './commandExecutor';
- import EventManager from './../../eventManager';
- import ItemsFactory from './itemsFactory';
- import Menu from './menu';
- import {registerPlugin} from './../../plugins';
- import {stopPropagation, pageX, pageY} from './../../helpers/dom/event';
- import {getWindowScrollLeft, getWindowScrollTop, hasClass} from './../../helpers/dom/element';
- import {
- ROW_ABOVE,
- ROW_BELOW,
- COLUMN_LEFT,
- COLUMN_RIGHT,
- REMOVE_ROW,
- REMOVE_COLUMN,
- UNDO,
- REDO,
- READ_ONLY,
- ALIGNMENT,
- SEPARATOR
- } from './predefinedItems';
- import './contextMenu.css';
- Hooks.getSingleton().register('afterContextMenuDefaultOptions');
- Hooks.getSingleton().register('afterContextMenuShow');
- Hooks.getSingleton().register('afterContextMenuHide');
- Hooks.getSingleton().register('afterContextMenuExecute');
- /**
- * @description
- * This plugin creates the Handsontable Context Menu. It allows to create a new row or
- * column at any place in the grid among [other features](http://docs.handsontable.com/demo-context-menu.html).
- * Possible values:
- * * `true` (to enable default options),
- * * `false` (to disable completely)
- *
- * or array of any available strings:
- * * `["row_above", "row_below", "col_left", "col_right",
- * "remove_row", "remove_col", "---------", "undo", "redo"]`.
- *
- * See [the context menu demo](http://docs.handsontable.com/demo-context-menu.html) for examples.
- *
- * @example
- * ```js
- * ...
- * // as a boolean
- * contextMenu: true
- * ...
- * // as a array
- * contextMenu: ['row_above', 'row_below', '---------', 'undo', 'redo']
- * ...
- * ```
- *
- * @plugin ContextMenu
- */
- class ContextMenu extends BasePlugin {
- /**
- * Default menu items order when `contextMenu` is enabled by `true`.
- *
- * @returns {Array}
- */
- static get DEFAULT_ITEMS() {
- return [
- ROW_ABOVE, ROW_BELOW,
- SEPARATOR,
- COLUMN_LEFT, COLUMN_RIGHT,
- SEPARATOR,
- REMOVE_ROW, REMOVE_COLUMN,
- SEPARATOR,
- UNDO, REDO,
- SEPARATOR,
- READ_ONLY,
- SEPARATOR,
- ALIGNMENT,
- ];
- }
- constructor(hotInstance) {
- super(hotInstance);
- /**
- * Instance of {@link EventManager}.
- *
- * @type {EventManager}
- */
- this.eventManager = new EventManager(this);
- /**
- * Instance of {@link CommandExecutor}.
- *
- * @type {CommandExecutor}
- */
- this.commandExecutor = new CommandExecutor(this.hot);
- /**
- * Instance of {@link ItemsFactory}.
- *
- * @type {ItemsFactory}
- */
- this.itemsFactory = null;
- /**
- * Instance of {@link Menu}.
- *
- * @type {Menu}
- */
- this.menu = null;
- }
- /**
- * Check if the plugin is enabled in the Handsontable settings.
- *
- * @returns {Boolean}
- */
- isEnabled() {
- return this.hot.getSettings().contextMenu;
- }
- /**
- * Enable plugin for this Handsontable instance.
- */
- enablePlugin() {
- if (this.enabled) {
- return;
- }
- this.itemsFactory = new ItemsFactory(this.hot, ContextMenu.DEFAULT_ITEMS);
- const settings = this.hot.getSettings().contextMenu;
- let predefinedItems = {
- items: this.itemsFactory.getItems(settings)
- };
- this.registerEvents();
- if (typeof settings.callback === 'function') {
- this.commandExecutor.setCommonCallback(settings.callback);
- }
- super.enablePlugin();
- this.callOnPluginsReady(() => {
- this.hot.runHooks('afterContextMenuDefaultOptions', predefinedItems);
- this.itemsFactory.setPredefinedItems(predefinedItems.items);
- let menuItems = this.itemsFactory.getItems(settings);
- this.menu = new Menu(this.hot, {
- className: 'htContextMenu',
- keepInViewport: true
- });
- this.hot.runHooks('beforeContextMenuSetItems', menuItems);
- this.menu.setMenuItems(menuItems);
- this.menu.addLocalHook('afterOpen', () => this.onMenuAfterOpen());
- this.menu.addLocalHook('afterClose', () => this.onMenuAfterClose());
- this.menu.addLocalHook('executeCommand', (...params) => this.executeCommand.apply(this, params));
- // Register all commands. Predefined and added by user or by plugins
- arrayEach(menuItems, (command) => this.commandExecutor.registerCommand(command.key, command));
- });
- }
- /**
- * Updates the plugin to use the latest options you have specified.
- */
- updatePlugin() {
- this.disablePlugin();
- this.enablePlugin();
- super.updatePlugin();
- }
- /**
- * Disable plugin for this Handsontable instance.
- */
- disablePlugin() {
- this.close();
- if (this.menu) {
- this.menu.destroy();
- this.menu = null;
- }
- super.disablePlugin();
- }
- /**
- * Register dom listeners.
- *
- * @private
- */
- registerEvents() {
- this.eventManager.addEventListener(this.hot.rootElement, 'contextmenu', (event) => this.onContextMenu(event));
- }
- /**
- * Open menu and re-position it based on dom event object.
- *
- * @param {Event} event The event object.
- */
- open(event) {
- if (!this.menu) {
- return;
- }
- this.menu.open();
- this.menu.setPosition({
- top: parseInt(pageY(event), 10) - getWindowScrollTop(),
- left: parseInt(pageX(event), 10) - getWindowScrollLeft(),
- });
- // ContextMenu is not detected HotTableEnv correctly because is injected outside hot-table
- this.menu.hotMenu.isHotTableEnv = this.hot.isHotTableEnv;
- // Handsontable.eventManager.isHotTableEnv = this.hot.isHotTableEnv;
- }
- /**
- * Close menu.
- */
- close() {
- if (!this.menu) {
- return;
- }
- this.menu.close();
- }
- /**
- * Execute context menu command.
- *
- * You can execute all predefined commands:
- * * `'row_above'` - Insert row above
- * * `'row_below'` - Insert row below
- * * `'col_left'` - Insert column on the left
- * * `'col_right'` - Insert column on the right
- * * `'clear_column'` - Clear selected column
- * * `'remove_row'` - Remove row
- * * `'remove_col'` - Remove column
- * * `'undo'` - Undo last action
- * * `'redo'` - Redo last action
- * * `'make_read_only'` - Make cell read only
- * * `'alignment:left'` - Alignment to the left
- * * `'alignment:top'` - Alignment to the top
- * * `'alignment:right'` - Alignment to the right
- * * `'alignment:bottom'` - Alignment to the bottom
- * * `'alignment:middle'` - Alignment to the middle
- * * `'alignment:center'` - Alignment to the center (justify)
- *
- * Or you can execute command registered in settings where `key` is your command name.
- *
- * @param {String} commandName
- * @param {*} params
- */
- executeCommand(...params) {
- this.commandExecutor.execute.apply(this.commandExecutor, params);
- }
- /**
- * On context menu listener.
- *
- * @private
- * @param {Event} event
- */
- onContextMenu(event) {
- let settings = this.hot.getSettings();
- let showRowHeaders = settings.rowHeaders;
- let showColHeaders = settings.colHeaders;
- function isValidElement(element) {
- return element.nodeName === 'TD' || element.parentNode.nodeName === 'TD';
- }
- // if event is from hot-table we must get web component element not element inside him
- let element = event.realTarget;
- this.close();
- if (hasClass(element, 'handsontableInput')) {
- return;
- }
- event.preventDefault();
- stopPropagation(event);
- if (!(showRowHeaders || showColHeaders)) {
- if (!isValidElement(element) && !(hasClass(element, 'current') && hasClass(element, 'wtBorder'))) {
- return;
- }
- }
- this.open(event);
- }
- /**
- * On menu after open listener.
- *
- * @private
- */
- onMenuAfterOpen() {
- this.hot.runHooks('afterContextMenuShow', this);
- }
- /**
- * On menu after close listener.
- *
- * @private
- */
- onMenuAfterClose() {
- this.hot.listen();
- this.hot.runHooks('afterContextMenuHide', this);
- }
- /**
- * Destroy instance.
- */
- destroy() {
- this.close();
- if (this.menu) {
- this.menu.destroy();
- }
- super.destroy();
- }
- }
- ContextMenu.SEPARATOR = {
- name: SEPARATOR
- };
- registerPlugin('contextMenu', ContextMenu);
- export default ContextMenu;
|