// // entityListContextMenu.js // // exampleContextMenus.js was originally created by David Rowe on 22 Aug 2018. // Modified to entityListContextMenu.js by Thijs Wenker on 10 Oct 2018 // Copyright 2018 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* eslint-env browser */ const CONTEXT_MENU_CLASS = "context-menu"; /** * ContextMenu class for EntityList * @constructor */ function EntityListContextMenu() { this._elContextMenu = null; this._onSelectedCallback = null; this._listItems = []; this._initialize(); } EntityListContextMenu.prototype = { /** * @private */ _elContextMenu: null, /** * @private */ _onSelectedCallback: null, /** * @private */ _selectedEntityID: null, /** * @private */ _listItems: null, /** * Close the context menu */ close: function() { if (this.isContextMenuOpen()) { this._elContextMenu.style.display = "none"; } }, isContextMenuOpen: function() { return this._elContextMenu.style.display === "block"; }, /** * Open the context menu * @param clickEvent * @param selectedEntityID * @param enabledOptions */ open: function(clickEvent, selectedEntityID, enabledOptions) { this._selectedEntityID = selectedEntityID; this._listItems.forEach(function(listItem) { let enabled = enabledOptions.includes(listItem.label); listItem.enabled = enabled; listItem.element.setAttribute('class', enabled ? '' : 'disabled'); }); this._elContextMenu.style.display = "block"; this._elContextMenu.style.left = Math.min(clickEvent.pageX, document.body.offsetWidth - this._elContextMenu.offsetWidth).toString() + "px"; this._elContextMenu.style.top = Math.min(clickEvent.pageY, document.body.clientHeight - this._elContextMenu.offsetHeight).toString() + "px"; clickEvent.stopPropagation(); }, /** * Set the callback for when a menu item is selected * @param onSelectedCallback */ setOnSelectedCallback: function(onSelectedCallback) { this._onSelectedCallback = onSelectedCallback; }, /** * Add a labeled item to the context menu * @param itemLabel * @private */ _addListItem: function(itemLabel) { let elListItem = document.createElement("li"); elListItem.innerText = itemLabel; let listItem = { label: itemLabel, element: elListItem, enabled: false }; elListItem.addEventListener("click", function () { if (listItem.enabled && this._onSelectedCallback) { this._onSelectedCallback.call(this, itemLabel, this._selectedEntityID); } }.bind(this), false); elListItem.setAttribute('class', 'disabled'); this._listItems.push(listItem); this._elContextMenu.appendChild(elListItem); }, /** * Add a separator item to the context menu * @private */ _addListSeparator: function() { let elListItem = document.createElement("li"); elListItem.setAttribute('class', 'separator'); this._elContextMenu.appendChild(elListItem); }, /** * Initialize the context menu. * @private */ _initialize: function() { this._elContextMenu = document.createElement("ul"); this._elContextMenu.setAttribute("class", CONTEXT_MENU_CLASS); document.body.appendChild(this._elContextMenu); this._addListItem("Cut"); this._addListItem("Copy"); this._addListItem("Paste"); this._addListSeparator(); this._addListItem("Rename"); this._addListItem("Duplicate"); this._addListItem("Delete"); // Ignore clicks on context menu background or separator. this._elContextMenu.addEventListener("click", function(event) { // Sink clicks on context menu background or separator but let context menu item clicks through. if (event.target.classList.contains(CONTEXT_MENU_CLASS)) { event.stopPropagation(); } }); // Provide means to close context menu without clicking menu item. document.body.addEventListener("click", this.close.bind(this)); document.body.addEventListener("keydown", function(event) { // Close context menu with Esc key. if (this.isContextMenuOpen() && event.key === "Escape") { this.close(); } }.bind(this)); } };