Bug 1518512 - (Part 1) Add basic context menu to Changes panel. r=gl

Adds context menu with options to select all and copy text content from
the Changes panel.

Differential Revision: https://phabricator.services.mozilla.com/D17255
This commit is contained in:
Razvan Caliman
2019-01-23 14:00:10 +00:00
parent b5d26a7493
commit 84c7100017
5 changed files with 142 additions and 3 deletions

View File

@@ -0,0 +1,99 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
loader.lazyRequireGetter(this, "Menu", "devtools/client/framework/menu");
loader.lazyRequireGetter(this, "MenuItem", "devtools/client/framework/menu-item");
loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
const { getStr } = require("./utils/l10n");
/**
* Context menu for the Changes panel with options to select, copy and export CSS changes.
*/
class ChangesContextMenu {
/**
* @param {ChangesView} view
*/
constructor(view) {
this.view = view;
this.inspector = this.view.inspector;
// Document object to which the Changes panel belongs to.
this.document = this.view.document;
// DOM element container for the Changes panel content.
this.panel = this.document.getElementById("sidebar-panel-changes");
// Window object to which the Changes panel belongs to.
this.window = this.document.defaultView;
this._onCopy = this._onCopy.bind(this);
this._onSelectAll = this._onSelectAll.bind(this);
}
show(event) {
this._openMenu({
target: event.explicitOriginalTarget,
screenX: event.screenX,
screenY: event.screenY,
});
}
_openMenu({ target, screenX = 0, screenY = 0 } = {}) {
this.window.focus();
const menu = new Menu();
// Copy option
const menuitemCopy = new MenuItem({
label: getStr("changes.contextmenu.copy"),
accesskey: getStr("changes.contextmenu.copy.accessKey"),
click: this._onCopy,
disabled: !this._hasTextSelected(),
});
menu.append(menuitemCopy);
// Select All option
const menuitemSelectAll = new MenuItem({
label: getStr("changes.contextmenu.selectAll"),
accesskey: getStr("changes.contextmenu.selectAll.accessKey"),
click: this._onSelectAll,
});
menu.append(menuitemSelectAll);
menu.popup(screenX, screenY, this.inspector.toolbox);
return menu;
}
_hasTextSelected() {
const selection = this.window.getSelection();
return selection.toString() && !selection.isCollapsed;
}
/**
* Select all text.
*/
_onSelectAll() {
const selection = this.window.getSelection();
selection.selectAllChildren(this.panel);
}
/**
* Copy the selected text to clipboard.
*/
_onCopy() {
const text = this.window.getSelection().toString();
clipboardHelper.copyString(text);
}
destroy() {
this.inspector = null;
this.panel = null;
this.view = null;
this.window = null;
}
}
module.exports = ChangesContextMenu;

View File

@@ -9,6 +9,8 @@
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
loader.lazyRequireGetter(this, "ChangesContextMenu", "devtools/client/inspector/changes/ChangesContextMenu");
const ChangesApp = createFactory(require("./components/ChangesApp"));
const {
@@ -21,18 +23,28 @@ class ChangesView {
this.document = window.document;
this.inspector = inspector;
this.store = this.inspector.store;
this.toolbox = this.inspector.toolbox;
this.onAddChange = this.onAddChange.bind(this);
this.onClearChanges = this.onClearChanges.bind(this);
this.onChangesFront = this.onChangesFront.bind(this);
this.onContextMenu = this.onContextMenu.bind(this);
this.destroy = this.destroy.bind(this);
this.init();
}
get contextMenu() {
if (!this._contextMenu) {
this._contextMenu = new ChangesContextMenu(this);
}
return this._contextMenu;
}
init() {
const changesApp = ChangesApp({});
const changesApp = ChangesApp({
onContextMenu: this.onContextMenu,
});
// listen to the front for initialization, add listeners
// when it is ready
@@ -88,6 +100,10 @@ class ChangesView {
this.store.dispatch(resetChanges());
}
onContextMenu(e) {
this.contextMenu.show(e);
}
/**
* Destruction function called when the inspector is destroyed.
*/
@@ -102,7 +118,11 @@ class ChangesView {
this.document = null;
this.inspector = null;
this.store = null;
this.toolbox = null;
if (this._contextMenu) {
this._contextMenu.destroy();
this._contextMenu = null;
}
}
}

View File

@@ -19,6 +19,8 @@ class ChangesApp extends PureComponent {
return {
// Nested CSS rule tree structure of CSS changes grouped by source (stylesheet)
changesTree: PropTypes.object.isRequired,
// Event handler for "contextmenu" event
onContextMenu: PropTypes.func.isRequired,
};
}
@@ -140,6 +142,7 @@ class ChangesApp extends PureComponent {
{
className: "theme-sidebar inspector-tabpanel",
id: "sidebar-panel-changes",
onContextMenu: this.props.onContextMenu,
},
!hasChanges && this.renderEmptyState(),
hasChanges && this.renderDiff(this.props.changesTree)

View File

@@ -13,6 +13,7 @@ DIRS += [
]
DevToolsModules(
'ChangesContextMenu.js',
'ChangesView.js',
)

View File

@@ -24,3 +24,19 @@ changes.elementStyleLabel=Element
# LOCALIZATION NOTE (changes.iframeLabel): This label appears next to URLs of stylesheets
# and element inline styles hosted by iframes. Lowercase intentional.
changes.iframeLabel=iframe
# LOCALIZATION NOTE (changes.contextmenu.copy): Label for "Copy" option in Changes panel
# context menu
changes.contextmenu.copy=Copy
# LOCALIZATION NOTE (changes.contextmenu.copy.accessKey): Access key for "Copy"
# option in the Changes panel.
changes.contextmenu.copy.accessKey=C
# LOCALIZATION NOTE (changes.contextmenu.selectAll): Label for "Select All" option in the
# Changes panel context menu to select all text content.
changes.contextmenu.selectAll=Select All
# LOCALIZATION NOTE (changes.contextmenu.selectAll.accessKey): Access key for "Select All"
# option in the Changes panel.
changes.contextmenu.selectAll.accessKey=A