summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js')
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js209
1 files changed, 209 insertions, 0 deletions
diff --git a/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
new file mode 100644
index 000000000..52bf565e2
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
@@ -0,0 +1,209 @@
+/* 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";
+
+const EventEmitter = require("devtools/shared/event-emitter");
+const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
+const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+
+/**
+ * Base class for all (color, gradient, ...)-swatch based value editors inside
+ * tooltips
+ *
+ * @param {Document} document
+ * The document to attach the SwatchBasedEditorTooltip. This is either the toolbox
+ * document if the tooltip is a popup tooltip or the panel's document if it is an
+ * inline editor.
+ */
+function SwatchBasedEditorTooltip(document, stylesheet) {
+ EventEmitter.decorate(this);
+ // Creating a tooltip instance
+ // This one will consume outside clicks as it makes more sense to let the user
+ // close the tooltip by clicking out
+ // It will also close on <escape> and <enter>
+ this.tooltip = new HTMLTooltip(document, {
+ type: "arrow",
+ consumeOutsideClicks: true,
+ useXulWrapper: true,
+ stylesheet
+ });
+
+ // By default, swatch-based editor tooltips revert value change on <esc> and
+ // commit value change on <enter>
+ this.shortcuts = new KeyShortcuts({
+ window: this.tooltip.topWindow
+ });
+ this.shortcuts.on("Escape", (name, event) => {
+ if (!this.tooltip.isVisible()) {
+ return;
+ }
+ this.revert();
+ this.hide();
+ event.stopPropagation();
+ event.preventDefault();
+ });
+ this.shortcuts.on("Return", (name, event) => {
+ if (!this.tooltip.isVisible()) {
+ return;
+ }
+ this.commit();
+ this.hide();
+ event.stopPropagation();
+ event.preventDefault();
+ });
+
+ // All target swatches are kept in a map, indexed by swatch DOM elements
+ this.swatches = new Map();
+
+ // When a swatch is clicked, and for as long as the tooltip is shown, the
+ // activeSwatch property will hold the reference to the swatch DOM element
+ // that was clicked
+ this.activeSwatch = null;
+
+ this._onSwatchClick = this._onSwatchClick.bind(this);
+}
+
+SwatchBasedEditorTooltip.prototype = {
+ /**
+ * Show the editor tooltip for the currently active swatch.
+ *
+ * @return {Promise} a promise that resolves once the editor tooltip is displayed, or
+ * immediately if there is no currently active swatch.
+ */
+ show: function () {
+ if (this.activeSwatch) {
+ let onShown = this.tooltip.once("shown");
+ this.tooltip.show(this.activeSwatch, "topcenter bottomleft");
+
+ // When the tooltip is closed by clicking outside the panel we want to
+ // commit any changes.
+ this.tooltip.once("hidden", () => {
+ if (!this._reverted && !this.eyedropperOpen) {
+ this.commit();
+ }
+ this._reverted = false;
+
+ // Once the tooltip is hidden we need to clean up any remaining objects.
+ if (!this.eyedropperOpen) {
+ this.activeSwatch = null;
+ }
+ });
+
+ return onShown;
+ }
+
+ return Promise.resolve();
+ },
+
+ hide: function () {
+ this.tooltip.hide();
+ },
+
+ /**
+ * Add a new swatch DOM element to the list of swatch elements this editor
+ * tooltip knows about. That means from now on, clicking on that swatch will
+ * toggle the editor.
+ *
+ * @param {node} swatchEl
+ * The element to add
+ * @param {object} callbacks
+ * Callbacks that will be executed when the editor wants to preview a
+ * value change, or revert a change, or commit a change.
+ * - onShow: will be called when one of the swatch tooltip is shown
+ * - onPreview: will be called when one of the sub-classes calls
+ * preview
+ * - onRevert: will be called when the user ESCapes out of the tooltip
+ * - onCommit: will be called when the user presses ENTER or clicks
+ * outside the tooltip.
+ */
+ addSwatch: function (swatchEl, callbacks = {}) {
+ if (!callbacks.onShow) {
+ callbacks.onShow = function () {};
+ }
+ if (!callbacks.onPreview) {
+ callbacks.onPreview = function () {};
+ }
+ if (!callbacks.onRevert) {
+ callbacks.onRevert = function () {};
+ }
+ if (!callbacks.onCommit) {
+ callbacks.onCommit = function () {};
+ }
+
+ this.swatches.set(swatchEl, {
+ callbacks: callbacks
+ });
+ swatchEl.addEventListener("click", this._onSwatchClick, false);
+ },
+
+ removeSwatch: function (swatchEl) {
+ if (this.swatches.has(swatchEl)) {
+ if (this.activeSwatch === swatchEl) {
+ this.hide();
+ this.activeSwatch = null;
+ }
+ swatchEl.removeEventListener("click", this._onSwatchClick, false);
+ this.swatches.delete(swatchEl);
+ }
+ },
+
+ _onSwatchClick: function (event) {
+ let swatch = this.swatches.get(event.target);
+
+ if (event.shiftKey) {
+ event.stopPropagation();
+ return;
+ }
+ if (swatch) {
+ this.activeSwatch = event.target;
+ this.show();
+ swatch.callbacks.onShow();
+ event.stopPropagation();
+ }
+ },
+
+ /**
+ * Not called by this parent class, needs to be taken care of by sub-classes
+ */
+ preview: function (value) {
+ if (this.activeSwatch) {
+ let swatch = this.swatches.get(this.activeSwatch);
+ swatch.callbacks.onPreview(value);
+ }
+ },
+
+ /**
+ * This parent class only calls this on <esc> keypress
+ */
+ revert: function () {
+ if (this.activeSwatch) {
+ this._reverted = true;
+ let swatch = this.swatches.get(this.activeSwatch);
+ this.tooltip.once("hidden", () => {
+ swatch.callbacks.onRevert();
+ });
+ }
+ },
+
+ /**
+ * This parent class only calls this on <enter> keypress
+ */
+ commit: function () {
+ if (this.activeSwatch) {
+ let swatch = this.swatches.get(this.activeSwatch);
+ swatch.callbacks.onCommit();
+ }
+ },
+
+ destroy: function () {
+ this.swatches.clear();
+ this.activeSwatch = null;
+ this.tooltip.off("keypress", this._onTooltipKeypress);
+ this.tooltip.destroy();
+ this.shortcuts.destroy();
+ }
+};
+
+module.exports = SwatchBasedEditorTooltip;