mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
8c395520d9
Since these are just interpreted comments, there's 0 impact on actual code. This removes all lines that match /* vim: set(.*)tw=80: */ with S&R -- there are a few others scattered around which will be removed manually in a second part.
182 lines
6.1 KiB
JavaScript
182 lines
6.1 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* 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 {Task} = require("devtools/shared/task");
|
|
|
|
const DEFAULT_TOGGLE_DELAY = 50;
|
|
|
|
/**
|
|
* Tooltip helper designed to show/hide the tooltip when the mouse hovers over
|
|
* particular nodes.
|
|
*
|
|
* This works by tracking mouse movements on a base container node (baseNode)
|
|
* and showing the tooltip when the mouse stops moving. A callback can be
|
|
* provided to the start() method to know whether or not the node being
|
|
* hovered over should indeed receive the tooltip.
|
|
*/
|
|
function TooltipToggle(tooltip) {
|
|
this.tooltip = tooltip;
|
|
this.win = tooltip.doc.defaultView;
|
|
|
|
this._onMouseMove = this._onMouseMove.bind(this);
|
|
this._onMouseOut = this._onMouseOut.bind(this);
|
|
|
|
this._onTooltipMouseOver = this._onTooltipMouseOver.bind(this);
|
|
this._onTooltipMouseOut = this._onTooltipMouseOut.bind(this);
|
|
}
|
|
|
|
module.exports.TooltipToggle = TooltipToggle;
|
|
|
|
TooltipToggle.prototype = {
|
|
/**
|
|
* Start tracking mouse movements on the provided baseNode to show the
|
|
* tooltip.
|
|
*
|
|
* 2 Ways to make this work:
|
|
* - Provide a single node to attach the tooltip to, as the baseNode, and
|
|
* omit the second targetNodeCb argument
|
|
* - Provide a baseNode that is the container of possibly numerous children
|
|
* elements that may receive a tooltip. In this case, provide the second
|
|
* targetNodeCb argument to decide wether or not a child should receive
|
|
* a tooltip.
|
|
*
|
|
* Note that if you call this function a second time, it will itself call
|
|
* stop() before adding mouse tracking listeners again.
|
|
*
|
|
* @param {node} baseNode
|
|
* The container for all target nodes
|
|
* @param {Function} targetNodeCb
|
|
* A function that accepts a node argument and that checks if a tooltip
|
|
* should be displayed. Possible return values are:
|
|
* - false (or a falsy value) if the tooltip should not be displayed
|
|
* - true if the tooltip should be displayed
|
|
* - a DOM node to display the tooltip on the returned anchor
|
|
* The function can also return a promise that will resolve to one of
|
|
* the values listed above.
|
|
* If omitted, the tooltip will be shown everytime.
|
|
* @param {Object} options
|
|
Set of optional arguments:
|
|
* - {Number} toggleDelay
|
|
* An optional delay (in ms) that will be observed before showing
|
|
* and before hiding the tooltip. Defaults to DEFAULT_TOGGLE_DELAY.
|
|
* - {Boolean} interactive
|
|
* If enabled, the tooltip is not hidden when mouse leaves the
|
|
* target element and enters the tooltip. Allows the tooltip
|
|
* content to be interactive.
|
|
*/
|
|
start: function (baseNode, targetNodeCb,
|
|
{toggleDelay = DEFAULT_TOGGLE_DELAY, interactive = false} = {}) {
|
|
this.stop();
|
|
|
|
if (!baseNode) {
|
|
// Calling tool is in the process of being destroyed.
|
|
return;
|
|
}
|
|
|
|
this._baseNode = baseNode;
|
|
this._targetNodeCb = targetNodeCb || (() => true);
|
|
this._toggleDelay = toggleDelay;
|
|
this._interactive = interactive;
|
|
|
|
baseNode.addEventListener("mousemove", this._onMouseMove);
|
|
baseNode.addEventListener("mouseout", this._onMouseOut);
|
|
|
|
if (this._interactive) {
|
|
this.tooltip.container.addEventListener("mouseover", this._onTooltipMouseOver);
|
|
this.tooltip.container.addEventListener("mouseout", this._onTooltipMouseOut);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* If the start() function has been used previously, and you want to get rid
|
|
* of this behavior, then call this function to remove the mouse movement
|
|
* tracking
|
|
*/
|
|
stop: function () {
|
|
this.win.clearTimeout(this.toggleTimer);
|
|
|
|
if (!this._baseNode) {
|
|
return;
|
|
}
|
|
|
|
this._baseNode.removeEventListener("mousemove", this._onMouseMove);
|
|
this._baseNode.removeEventListener("mouseout", this._onMouseOut);
|
|
|
|
if (this._interactive) {
|
|
this.tooltip.container.removeEventListener("mouseover", this._onTooltipMouseOver);
|
|
this.tooltip.container.removeEventListener("mouseout", this._onTooltipMouseOut);
|
|
}
|
|
|
|
this._baseNode = null;
|
|
this._targetNodeCb = null;
|
|
this._lastHovered = null;
|
|
},
|
|
|
|
_onMouseMove: function (event) {
|
|
if (event.target !== this._lastHovered) {
|
|
this._lastHovered = event.target;
|
|
|
|
this.win.clearTimeout(this.toggleTimer);
|
|
this.toggleTimer = this.win.setTimeout(() => {
|
|
this.tooltip.hide();
|
|
this.isValidHoverTarget(event.target).then(target => {
|
|
if (target === null) {
|
|
return;
|
|
}
|
|
this.tooltip.show(target);
|
|
}, reason => {
|
|
console.error("isValidHoverTarget rejected with unexpected reason:");
|
|
console.error(reason);
|
|
});
|
|
}, this._toggleDelay);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Is the given target DOMNode a valid node for toggling the tooltip on hover.
|
|
* This delegates to the user-defined _targetNodeCb callback.
|
|
* @return {Promise} a promise that will resolve the anchor to use for the
|
|
* tooltip or null if no valid target was found.
|
|
*/
|
|
isValidHoverTarget: Task.async(function* (target) {
|
|
let res = yield this._targetNodeCb(target, this.tooltip);
|
|
if (res) {
|
|
return res.nodeName ? res : target;
|
|
}
|
|
|
|
return null;
|
|
}),
|
|
|
|
_onMouseOut: function (event) {
|
|
// Only hide the tooltip if the mouse leaves baseNode.
|
|
if (event && this._baseNode && !this._baseNode.contains(event.relatedTarget)) {
|
|
return;
|
|
}
|
|
|
|
this._lastHovered = null;
|
|
this.win.clearTimeout(this.toggleTimer);
|
|
this.toggleTimer = this.win.setTimeout(() => {
|
|
this.tooltip.hide();
|
|
}, this._toggleDelay);
|
|
},
|
|
|
|
_onTooltipMouseOver() {
|
|
this.win.clearTimeout(this.toggleTimer);
|
|
},
|
|
|
|
_onTooltipMouseOut() {
|
|
this.win.clearTimeout(this.toggleTimer);
|
|
this.toggleTimer = this.win.setTimeout(() => {
|
|
this.tooltip.hide();
|
|
}, this._toggleDelay);
|
|
},
|
|
|
|
destroy: function () {
|
|
this.stop();
|
|
}
|
|
};
|