зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1270462 - part1: extract devtools tooltip toggle logic to separate file;r=bgrins,jsnajdr
The code used to make the tooltip appear/disappear when hovering targets has been extracted to a separate class that can be shared between the current Tooltip.js implementation and the upcoming HTMLTooltip. MozReview-Commit-ID: UYSjPFeMYK --HG-- extra : rebase_source : 5dcca2d5887ffc98fec621092640073a0909c13f
This commit is contained in:
Родитель
e87387e53d
Коммит
87fe331949
|
@ -23,8 +23,6 @@ const SEARCH_TOKEN_FLAG = "#";
|
|||
const SEARCH_LINE_FLAG = ":";
|
||||
const SEARCH_VARIABLE_FLAG = "*";
|
||||
const SEARCH_AUTOFILL = [SEARCH_GLOBAL_FLAG, SEARCH_FUNCTION_FLAG, SEARCH_TOKEN_FLAG];
|
||||
const EDITOR_VARIABLE_HOVER_DELAY = 750; // ms
|
||||
const EDITOR_VARIABLE_POPUP_POSITION = "topcenter bottomleft";
|
||||
const TOOLBAR_ORDER_POPUP_POSITION = "topcenter bottomleft";
|
||||
const RESIZE_REFRESH_RATE = 50; // ms
|
||||
const PROMISE_DEBUGGER_URL =
|
||||
|
|
|
@ -846,7 +846,7 @@ function intendOpenVarPopup(aPanel, aPosition, aButtonPushed) {
|
|||
deferred.resolve(true);
|
||||
}
|
||||
},
|
||||
tooltip.defaultShowDelay + 1000
|
||||
bubble.TOOLTIP_SHOW_DELAY + 1000
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
|
|
|
@ -25,6 +25,17 @@ function VariableBubbleView(DebuggerController, DebuggerView) {
|
|||
}
|
||||
|
||||
VariableBubbleView.prototype = {
|
||||
/**
|
||||
* Delay before showing the variables bubble tooltip when hovering a valid
|
||||
* target.
|
||||
*/
|
||||
TOOLTIP_SHOW_DELAY: 750,
|
||||
|
||||
/**
|
||||
* Tooltip position for the variables bubble tooltip.
|
||||
*/
|
||||
TOOLTIP_POSITION: "topcenter bottomleft",
|
||||
|
||||
/**
|
||||
* Initialization function, called when the debugger is started.
|
||||
*/
|
||||
|
@ -49,8 +60,7 @@ VariableBubbleView.prototype = {
|
|||
event: "keydown"
|
||||
}]
|
||||
});
|
||||
this._tooltip.defaultPosition = EDITOR_VARIABLE_POPUP_POSITION;
|
||||
this._tooltip.defaultShowDelay = EDITOR_VARIABLE_HOVER_DELAY;
|
||||
this._tooltip.defaultPosition = this.TOOLTIP_POSITION;
|
||||
this._tooltip.panel.addEventListener("popuphiding", this._onPopupHiding);
|
||||
},
|
||||
|
||||
|
@ -275,7 +285,7 @@ VariableBubbleView.prototype = {
|
|||
// Allow events to settle down first. If the mouse hovers over
|
||||
// a certain point in the editor long enough, try showing a variable bubble.
|
||||
setNamedTimeout("editor-mouse-move",
|
||||
EDITOR_VARIABLE_HOVER_DELAY, () => this._findIdentifier(e.clientX, e.clientY));
|
||||
this.TOOLTIP_SHOW_DELAY, () => this._findIdentifier(e.clientX, e.clientY));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,7 +16,7 @@ add_task(function* () {
|
|||
let target = img.editor.getAttributeElement("src").querySelector(".link");
|
||||
|
||||
info("Check that the src attribute of the image is a valid tooltip target");
|
||||
let isValid = yield markup.tooltip.isValidHoverTarget(target);
|
||||
let isValid = yield isHoverTooltipTarget(markup.tooltip, target);
|
||||
ok(isValid, "The element is a valid tooltip target");
|
||||
|
||||
info("Start dragging the test div");
|
||||
|
@ -24,7 +24,7 @@ add_task(function* () {
|
|||
|
||||
info("Now check that the src attribute of the image isn't a valid target");
|
||||
try {
|
||||
yield markup.tooltip.isValidHoverTarget(target);
|
||||
yield isHoverTooltipTarget(markup.tooltip, target);
|
||||
isValid = true;
|
||||
} catch (e) {
|
||||
isValid = false;
|
||||
|
@ -35,6 +35,6 @@ add_task(function* () {
|
|||
yield simulateNodeDrop(inspector, "div");
|
||||
|
||||
info("Check again the src attribute of the image");
|
||||
isValid = yield markup.tooltip.isValidHoverTarget(target);
|
||||
isValid = yield isHoverTooltipTarget(markup.tooltip, target);
|
||||
ok(isValid, "The element is a valid tooltip target");
|
||||
});
|
||||
|
|
|
@ -43,7 +43,7 @@ function* getImageTooltipTarget({selector}, inspector) {
|
|||
|
||||
function* assertTooltipShownOn(element, {markup}) {
|
||||
info("Is the element a valid hover target");
|
||||
let isValid = yield markup.tooltip.isValidHoverTarget(element);
|
||||
let isValid = yield isHoverTooltipTarget(markup.tooltip, element);
|
||||
ok(isValid, "The element is a valid hover target for the image tooltip");
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ add_task(function* () {
|
|||
ok(target, "Found the src attribute in the markup view.");
|
||||
|
||||
info("Showing tooltip on the src link.");
|
||||
yield inspector.markup.tooltip.isValidHoverTarget(target);
|
||||
yield isHoverTooltipTarget(inspector.markup.tooltip, target);
|
||||
|
||||
checkImageTooltip(INITIAL_SRC_SIZE, inspector);
|
||||
|
||||
|
@ -48,7 +48,7 @@ add_task(function* () {
|
|||
ok(target, "Found the src attribute in the markup view after mutation.");
|
||||
|
||||
info("Showing tooltip on the src link.");
|
||||
yield inspector.markup.tooltip.isValidHoverTarget(target);
|
||||
yield isHoverTooltipTarget(inspector.markup.tooltip, target);
|
||||
|
||||
info("Checking that the new image was shown.");
|
||||
checkImageTooltip(UPDATED_SRC_SIZE, inspector);
|
||||
|
|
|
@ -169,36 +169,6 @@ var focusEditableField = Task.async(function* (ruleView, editable, xOffset = 1,
|
|||
return onEdit;
|
||||
});
|
||||
|
||||
/**
|
||||
* Given a tooltip object instance (see Tooltip.js), checks if it is set to
|
||||
* toggle and hover and if so, checks if the given target is a valid hover
|
||||
* target. This won't actually show the tooltip (the less we interact with XUL
|
||||
* panels during test runs, the better).
|
||||
*
|
||||
* @return a promise that resolves when the answer is known
|
||||
*/
|
||||
function isHoverTooltipTarget(tooltip, target) {
|
||||
if (!tooltip._basedNode || !tooltip.panel) {
|
||||
return promise.reject(new Error(
|
||||
"The tooltip passed isn't set to toggle on hover or is not a tooltip"));
|
||||
}
|
||||
return tooltip.isValidHoverTarget(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as isHoverTooltipTarget except that it will fail the test if there is no
|
||||
* tooltip defined on hover of the given element
|
||||
*
|
||||
* @return a promise
|
||||
*/
|
||||
function assertHoverTooltipOn(tooltip, element) {
|
||||
return isHoverTooltipTarget(tooltip, element).then(() => {
|
||||
ok(true, "A tooltip is defined on hover of the given element");
|
||||
}, () => {
|
||||
ok(false, "No tooltip is defined on hover of the given element");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* When a tooltip is closed, this ends up "commiting" the value changed within
|
||||
* the tooltip (e.g. the color in case of a colorpicker) which, in turn, ends up
|
||||
|
|
|
@ -198,36 +198,6 @@ var focusEditableField = Task.async(function*(ruleView, editable, xOffset=1,
|
|||
return onEdit;
|
||||
});
|
||||
|
||||
/**
|
||||
* Given a tooltip object instance (see Tooltip.js), checks if it is set to
|
||||
* toggle and hover and if so, checks if the given target is a valid hover
|
||||
* target. This won't actually show the tooltip (the less we interact with XUL
|
||||
* panels during test runs, the better).
|
||||
*
|
||||
* @return a promise that resolves when the answer is known
|
||||
*/
|
||||
function isHoverTooltipTarget(tooltip, target) {
|
||||
if (!tooltip._basedNode || !tooltip.panel) {
|
||||
return promise.reject(new Error(
|
||||
"The tooltip passed isn't set to toggle on hover or is not a tooltip"));
|
||||
}
|
||||
return tooltip.isValidHoverTarget(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as isHoverTooltipTarget except that it will fail the test if there is no
|
||||
* tooltip defined on hover of the given element
|
||||
*
|
||||
* @return a promise
|
||||
*/
|
||||
function assertHoverTooltipOn(tooltip, element) {
|
||||
return isHoverTooltipTarget(tooltip, element).then(() => {
|
||||
ok(true, "A tooltip is defined on hover of the given element");
|
||||
}, () => {
|
||||
ok(false, "No tooltip is defined on hover of the given element");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls a given function waiting for it to return true.
|
||||
*
|
||||
|
|
|
@ -777,3 +777,33 @@ function synthesizeKeys(input, win) {
|
|||
EventUtils.synthesizeKey(key, {}, win);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a tooltip object instance (see Tooltip.js), checks if it is set to
|
||||
* toggle and hover and if so, checks if the given target is a valid hover
|
||||
* target. This won't actually show the tooltip (the less we interact with XUL
|
||||
* panels during test runs, the better).
|
||||
*
|
||||
* @return a promise that resolves when the answer is known
|
||||
*/
|
||||
function isHoverTooltipTarget(tooltip, target) {
|
||||
if (!tooltip._toggle._baseNode || !tooltip.panel) {
|
||||
return promise.reject(new Error(
|
||||
"The tooltip passed isn't set to toggle on hover or is not a tooltip"));
|
||||
}
|
||||
return tooltip._toggle.isValidHoverTarget(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as isHoverTooltipTarget except that it will fail the test if there is no
|
||||
* tooltip defined on hover of the given element
|
||||
*
|
||||
* @return a promise
|
||||
*/
|
||||
function assertHoverTooltipOn(tooltip, element) {
|
||||
return isHoverTooltipTarget(tooltip, element).then(() => {
|
||||
ok(true, "A tooltip is defined on hover of the given element");
|
||||
}, () => {
|
||||
ok(false, "No tooltip is defined on hover of the given element");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ function test() {
|
|||
*/
|
||||
function showTooltipOn(tooltip, element) {
|
||||
return Task.spawn(function*() {
|
||||
let isTarget = yield tooltip.isValidHoverTarget(element);
|
||||
let isValidTarget = yield tooltip._toggle.isValidHoverTarget(element);
|
||||
ok(isValidTarget, "Element is a valid tooltip target");
|
||||
let onShown = tooltip.once("shown");
|
||||
tooltip.show();
|
||||
yield onShown;
|
||||
|
|
|
@ -11,6 +11,7 @@ const {CubicBezierWidget} =
|
|||
require("devtools/client/shared/widgets/CubicBezierWidget");
|
||||
const {MdnDocsWidget} = require("devtools/client/shared/widgets/MdnDocsWidget");
|
||||
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
|
||||
const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {colorUtils} = require("devtools/client/shared/css-color");
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
|
@ -188,8 +189,11 @@ function Tooltip(doc, options) {
|
|||
}, options);
|
||||
this.panel = PanelFactory.get(doc, this.options);
|
||||
|
||||
// Used for namedTimeouts in the mouseover handling
|
||||
this.uid = "tooltip-" + Date.now();
|
||||
// Create tooltip toggle helper and decorate the Tooltip instance with
|
||||
// shortcut methods.
|
||||
this._toggle = new TooltipToggle(this);
|
||||
this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
|
||||
this.stopTogglingOnHover = this._toggle.stop.bind(this._toggle);
|
||||
|
||||
// Emit show/hide events when the panel does.
|
||||
for (let eventName of POPUP_EVENTS) {
|
||||
|
@ -242,7 +246,6 @@ Tooltip.prototype = {
|
|||
// px
|
||||
defaultOffsetY: 0,
|
||||
// px
|
||||
defaultShowDelay: 50,
|
||||
|
||||
/**
|
||||
* Show the tooltip. It might be wise to append some content first if you
|
||||
|
@ -333,9 +336,7 @@ Tooltip.prototype = {
|
|||
|
||||
this.content = null;
|
||||
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
this._toggle.destroy();
|
||||
|
||||
this.doc = null;
|
||||
|
||||
|
@ -343,134 +344,6 @@ Tooltip.prototype = {
|
|||
this.panel = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show/hide the tooltip when the mouse hovers over particular nodes.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This works by tracking mouse movements on a base container node (baseNode)
|
||||
* and showing the tooltip when the mouse stops moving. The targetNodeCb
|
||||
* callback is used to know whether or not the particular element being
|
||||
* hovered over should indeed receive the tooltip. If you don't provide it
|
||||
* it's equivalent to a function that always returns true.
|
||||
*
|
||||
* Note that if you call this function a second time, it will itself call
|
||||
* stopTogglingOnHover 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 returns true or false
|
||||
* (or a promise that resolves or rejects) to signify if the tooltip
|
||||
* should be shown on that node or not.
|
||||
* If the promise rejects, it must reject `false` as value.
|
||||
* Any other value is going to be logged as unexpected error.
|
||||
* Additionally, the function receives a second argument which is the
|
||||
* tooltip instance itself, to be used to add/modify the content of the
|
||||
* tooltip if needed. If omitted, the tooltip will be shown everytime.
|
||||
* @param {Number} showDelay
|
||||
* An optional delay that will be observed before showing the tooltip.
|
||||
* Defaults to this.defaultShowDelay.
|
||||
*/
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb,
|
||||
showDelay=this.defaultShowDelay) {
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
if (!baseNode) {
|
||||
// Calling tool is in the process of being destroyed.
|
||||
return;
|
||||
}
|
||||
|
||||
this._basedNode = baseNode;
|
||||
this._showDelay = showDelay;
|
||||
this._targetNodeCb = targetNodeCb || (() => true);
|
||||
|
||||
this._onBaseNodeMouseMove = this._onBaseNodeMouseMove.bind(this);
|
||||
this._onBaseNodeMouseLeave = this._onBaseNodeMouseLeave.bind(this);
|
||||
|
||||
baseNode.addEventListener("mousemove", this._onBaseNodeMouseMove, false);
|
||||
baseNode.addEventListener("mouseleave", this._onBaseNodeMouseLeave, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* If the startTogglingOnHover function has been used previously, and you want
|
||||
* to get rid of this behavior, then call this function to remove the mouse
|
||||
* movement tracking
|
||||
*/
|
||||
stopTogglingOnHover: function() {
|
||||
clearNamedTimeout(this.uid);
|
||||
|
||||
if (!this._basedNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._basedNode.removeEventListener("mousemove",
|
||||
this._onBaseNodeMouseMove, false);
|
||||
this._basedNode.removeEventListener("mouseleave",
|
||||
this._onBaseNodeMouseLeave, false);
|
||||
|
||||
this._basedNode = null;
|
||||
this._targetNodeCb = null;
|
||||
this._lastHovered = null;
|
||||
},
|
||||
|
||||
_onBaseNodeMouseMove: function(event) {
|
||||
if (event.target !== this._lastHovered) {
|
||||
this.hide();
|
||||
this._lastHovered = event.target;
|
||||
setNamedTimeout(this.uid, this._showDelay, () => {
|
||||
this.isValidHoverTarget(event.target).then(target => {
|
||||
this.show(target);
|
||||
}, reason => {
|
||||
if (reason === false) {
|
||||
// isValidHoverTarget rejects with false if the tooltip should
|
||||
// not be shown. This can be safely ignored.
|
||||
return;
|
||||
}
|
||||
// Report everything else. Reason might be error that should not be
|
||||
// hidden.
|
||||
console.error("isValidHoverTarget rejected with an unexpected reason:");
|
||||
console.error(reason);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the given target DOMNode a valid node for toggling the tooltip on hover.
|
||||
* This delegates to the user-defined _targetNodeCb callback.
|
||||
* @return a promise that resolves or rejects depending if the tooltip should
|
||||
* be shown or not. If it resolves, it does to the actual anchor to be used
|
||||
*/
|
||||
isValidHoverTarget: function(target) {
|
||||
// Execute the user-defined callback which should return either true/false
|
||||
// or a promise that resolves or rejects
|
||||
let res = this._targetNodeCb(target, this);
|
||||
|
||||
// The callback can additionally return a DOMNode to replace the anchor of
|
||||
// the tooltip when shown
|
||||
if (res && res.then) {
|
||||
return res.then(arg => {
|
||||
return arg instanceof Ci.nsIDOMNode ? arg : target;
|
||||
});
|
||||
}
|
||||
let newTarget = res instanceof Ci.nsIDOMNode ? res : target;
|
||||
return res ? promise.resolve(newTarget) : promise.reject(false);
|
||||
},
|
||||
|
||||
_onBaseNodeMouseLeave: function() {
|
||||
clearNamedTimeout(this.uid);
|
||||
this._lastHovered = null;
|
||||
this.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content of this tooltip. Will first empty the tooltip and then
|
||||
* append the new content element.
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
'tooltip',
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
'AbstractTreeItem.jsm',
|
||||
'BarGraphWidget.js',
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript 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";
|
||||
|
||||
const DEFAULT_SHOW_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._onMouseLeave = this._onMouseLeave.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 returns true or false
|
||||
* (or a promise that resolves or rejects) to signify if the tooltip
|
||||
* should be shown on that node or not.
|
||||
* If the promise rejects, it must reject `false` as value.
|
||||
* Any other value is going to be logged as unexpected error.
|
||||
* Additionally, the function receives a second argument which is the
|
||||
* tooltip instance itself, to be used to add/modify the content of the
|
||||
* tooltip if needed. If omitted, the tooltip will be shown everytime.
|
||||
* @param {Number} showDelay
|
||||
* An optional delay that will be observed before showing the tooltip.
|
||||
* Defaults to DEFAULT_SHOW_DELAY.
|
||||
*/
|
||||
start: function (baseNode, targetNodeCb, showDelay = DEFAULT_SHOW_DELAY) {
|
||||
this.stop();
|
||||
|
||||
if (!baseNode) {
|
||||
// Calling tool is in the process of being destroyed.
|
||||
return;
|
||||
}
|
||||
|
||||
this._baseNode = baseNode;
|
||||
this._showDelay = showDelay;
|
||||
this._targetNodeCb = targetNodeCb || (() => true);
|
||||
|
||||
baseNode.addEventListener("mousemove", this._onMouseMove, false);
|
||||
baseNode.addEventListener("mouseleave", this._onMouseLeave, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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, false);
|
||||
this._baseNode.removeEventListener("mouseleave", this._onMouseLeave, false);
|
||||
|
||||
this._baseNode = null;
|
||||
this._targetNodeCb = null;
|
||||
this._lastHovered = null;
|
||||
},
|
||||
|
||||
_onMouseMove: function (event) {
|
||||
if (event.target !== this._lastHovered) {
|
||||
this.tooltip.hide();
|
||||
this._lastHovered = event.target;
|
||||
|
||||
this.win.clearTimeout(this.toggleTimer);
|
||||
this.toggleTimer = this.win.setTimeout(() => {
|
||||
this.isValidHoverTarget(event.target).then(target => {
|
||||
this.tooltip.show(target);
|
||||
}, reason => {
|
||||
if (reason === false) {
|
||||
// isValidHoverTarget rejects with false if the tooltip should
|
||||
// not be shown. This can be safely ignored.
|
||||
return;
|
||||
}
|
||||
console.error("isValidHoverTarget rejected with unexpected reason:");
|
||||
console.error(reason);
|
||||
});
|
||||
}, this._showDelay);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the given target DOMNode a valid node for toggling the tooltip on hover.
|
||||
* This delegates to the user-defined _targetNodeCb callback.
|
||||
* @return a promise that resolves or rejects depending if the tooltip should
|
||||
* be shown or not. If it resolves, it does to the actual anchor to be used
|
||||
*/
|
||||
isValidHoverTarget: function (target) {
|
||||
// Execute the user-defined callback which should return either true/false
|
||||
// or a promise that resolves or rejects
|
||||
let res = this._targetNodeCb(target, this.tooltip);
|
||||
|
||||
// The callback can additionally return a DOMNode to replace the anchor of
|
||||
// the tooltip when shown
|
||||
if (res && res.then) {
|
||||
return res.then(arg => {
|
||||
return arg && arg.nodeName ? arg : target;
|
||||
});
|
||||
}
|
||||
let newTarget = res && res.nodeName ? res : target;
|
||||
return new Promise((resolve, reject) => {
|
||||
res ? resolve(newTarget) : reject(false);
|
||||
});
|
||||
},
|
||||
|
||||
_onMouseLeave: function () {
|
||||
this.win.clearTimeout(this.toggleTimer);
|
||||
this._lastHovered = null;
|
||||
this.tooltip.hide();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.stop();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'TooltipToggle.js',
|
||||
)
|
Загрузка…
Ссылка в новой задаче