Summary: Ref T12733. - While editing a comment, show a pink star ({icon star, color=pink}) with a tooltip. - Slight UI tweaks, including draft comments getting an indigo pencil ({icon pencil, color=indigo}). Test Plan: {F4968470} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12733 Differential Revision: https://secure.phabricator.com/D17977
243 lines
6.1 KiB
JavaScript
243 lines
6.1 KiB
JavaScript
/**
|
|
* @requires javelin-install
|
|
* javelin-util
|
|
* javelin-dom
|
|
* javelin-vector
|
|
* @provides phabricator-tooltip
|
|
* @javelin
|
|
*/
|
|
|
|
JX.install('Tooltip', {
|
|
|
|
statics: {
|
|
_node: null,
|
|
_last: null,
|
|
_lock: 0,
|
|
|
|
show : function(root, scale, align, content) {
|
|
var self = JX.Tooltip;
|
|
|
|
if (self._lock) {
|
|
return;
|
|
}
|
|
|
|
if (content === null) {
|
|
return;
|
|
}
|
|
|
|
if (__DEV__) {
|
|
switch (align) {
|
|
case 'N':
|
|
case 'E':
|
|
case 'S':
|
|
case 'W':
|
|
break;
|
|
default:
|
|
JX.$E(
|
|
'Only alignments "N" (north), "E" (east), "S" (south), ' +
|
|
'and "W" (west) are supported.'
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
var node_inner = JX.$N(
|
|
'div',
|
|
{ className: 'jx-tooltip-inner' },
|
|
[
|
|
JX.$N('div', { className: 'jx-tooltip' }, content),
|
|
JX.$N('div', { className: 'jx-tooltip-anchor' })
|
|
]);
|
|
|
|
var node = JX.$N(
|
|
'div',
|
|
{ className: 'jx-tooltip-container' },
|
|
node_inner);
|
|
|
|
if (scale == 'auto') {
|
|
node.style.maxWidth = '';
|
|
} else {
|
|
node.style.maxWidth = scale + 'px';
|
|
}
|
|
|
|
JX.Tooltip.hide();
|
|
self._node = node;
|
|
|
|
// Append the tip to the document, but offscreen, so we can measure it.
|
|
node.style.left = '-10000px';
|
|
document.body.appendChild(node);
|
|
|
|
// Jump through some hoops trying to auto-position the tooltip
|
|
var pos = self._getSmartPosition(align, root, node);
|
|
pos.setPos(node);
|
|
|
|
// Animate the tip if we haven't shown any tips recently. If we are
|
|
// already showing a tip (or hid one very recently) just show the tip
|
|
// immediately. This makes hunting for a particular item by mousing
|
|
// through tips smoother: you only have to sit through the animation
|
|
// once, at the beginning.
|
|
|
|
var is_recent = false;
|
|
|
|
var last_tip = self._last;
|
|
if (last_tip) {
|
|
// If we recently hid a tip, compute how many milliseconds ago we
|
|
// hid it.
|
|
var last_tip_age = (new Date().getTime() - self._last);
|
|
if (last_tip_age < 500) {
|
|
is_recent = true;
|
|
}
|
|
}
|
|
|
|
if (!is_recent) {
|
|
JX.DOM.alterClass(node, 'jx-tooltip-appear', true);
|
|
}
|
|
},
|
|
|
|
_getSmartPosition: function (align, root, node) {
|
|
var self = JX.Tooltip;
|
|
|
|
// Figure out how to position the tooltip on screen. We will try the
|
|
// configured aligment first.
|
|
var try_alignments = [align];
|
|
|
|
// If the configured alignment does not fit, we'll try the opposite
|
|
// alignment.
|
|
var opposites = {
|
|
N: 'S',
|
|
S: 'N',
|
|
E: 'W',
|
|
W: 'E'
|
|
};
|
|
try_alignments.push(opposites[align]);
|
|
|
|
// Then we'll try the other alignments, in arbitrary order.
|
|
for (var k in opposites) {
|
|
try_alignments.push(k);
|
|
}
|
|
|
|
var use_alignment = null;
|
|
var use_pos = null;
|
|
for (var ii = 0; ii < try_alignments.length; ii++) {
|
|
var try_alignment = try_alignments[ii];
|
|
|
|
var pos = self._proposePosition(try_alignment, root, node);
|
|
if (self.isOnScreen(pos, node)) {
|
|
use_alignment = try_alignment;
|
|
use_pos = pos;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we don't come up with a good answer, default to the configured
|
|
// alignment.
|
|
if (use_alignment === null) {
|
|
use_alignment = align;
|
|
use_pos = self._proposePosition(use_alignment, root, node);
|
|
}
|
|
|
|
self._setAnchor(use_alignment);
|
|
|
|
return pos;
|
|
},
|
|
|
|
_proposePosition: function (align, root, node) {
|
|
var p = JX.$V(root);
|
|
var d = JX.Vector.getDim(root);
|
|
var n = JX.Vector.getDim(node);
|
|
var l = 0;
|
|
var t = 0;
|
|
|
|
// Caculate the tip so it's nicely aligned.
|
|
switch (align) {
|
|
case 'N':
|
|
l = parseInt(p.x - ((n.x - d.x) / 2), 10);
|
|
t = parseInt(p.y - n.y, 10);
|
|
break;
|
|
case 'E':
|
|
l = parseInt(p.x + d.x, 10);
|
|
t = parseInt(p.y - ((n.y - d.y) / 2), 10);
|
|
break;
|
|
case 'S':
|
|
l = parseInt(p.x - ((n.x - d.x) / 2), 10);
|
|
t = parseInt(p.y + d.y + 5, 10);
|
|
break;
|
|
case 'W':
|
|
l = parseInt(p.x - n.x - 5, 10);
|
|
t = parseInt(p.y - ((n.y - d.y) / 2), 10);
|
|
break;
|
|
}
|
|
|
|
return new JX.Vector(l, t);
|
|
},
|
|
|
|
isOnScreen: function (a, node) {
|
|
var view = this._getViewBoundaries();
|
|
var corners = this._getNodeCornerPositions(a, node);
|
|
|
|
// Check if any of the corners are offscreen.
|
|
for (var i = 0; i < corners.length; i++) {
|
|
var corner = corners[i];
|
|
if (corner.x < view.w ||
|
|
corner.y < view.n ||
|
|
corner.x > view.e ||
|
|
corner.y > view.s) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
_getNodeCornerPositions: function(pos, node) {
|
|
// Get positions of all four corners of a node.
|
|
var n = JX.Vector.getDim(node);
|
|
return [new JX.Vector(pos.x, pos.y),
|
|
new JX.Vector(pos.x + n.x, pos.y),
|
|
new JX.Vector(pos.x, pos.y + n.y),
|
|
new JX.Vector(pos.x + n.x, pos.y + n.y)];
|
|
},
|
|
|
|
_getViewBoundaries: function() {
|
|
var s = JX.Vector.getScroll();
|
|
var v = JX.Vector.getViewport();
|
|
var max_x = s.x + v.x;
|
|
var max_y = s.y + v.y;
|
|
|
|
// Even if the corner is technically on the screen, don't allow the
|
|
// tip to display too close to the edge of the screen.
|
|
var margin = 16;
|
|
|
|
return {
|
|
w: s.x + margin,
|
|
e: max_x - margin,
|
|
n: s.y + margin,
|
|
s: max_y - margin
|
|
};
|
|
},
|
|
|
|
_setAnchor: function (align) {
|
|
// Orient the little tail
|
|
JX.DOM.alterClass(this._node, 'jx-tooltip-align-' + align, true);
|
|
},
|
|
|
|
hide : function() {
|
|
if (this._node) {
|
|
JX.DOM.remove(this._node);
|
|
this._node = null;
|
|
this._last = new Date().getTime();
|
|
}
|
|
},
|
|
|
|
lock: function() {
|
|
var self = JX.Tooltip;
|
|
self.hide();
|
|
self._lock++;
|
|
},
|
|
|
|
unlock: function() {
|
|
var self = JX.Tooltip;
|
|
self._lock--;
|
|
}
|
|
}
|
|
});
|