phorge/webroot/rsrc/js/core/ToolTip.js
epriestley 61318a8119 Improve minor workboard drag behaviors
Summary:
Ref T5240.

  - Add proper class when dropping cards.
  - Add proper class when creating new cards.
  - Make X-drag explicit so that it works if there's only one column.
  - Stop tootips when dragging, resume them after dropping.
  - Move CSS rule for consistency.
  - Allow user to hit "Escape" to cancel an in-progress drag.

Test Plan:
  - Dropped cards.
  - Created new cards.
  - X-dragged on a workboard with one column and a dashboard.
  - Dragged over a tooltip (no tip), dropped, moused over tooltip (tip).
  - Hit escape during a drag.

Reviewers: chad

Reviewed By: chad

Subscribers: cspeckmim

Maniphest Tasks: T5240

Differential Revision: https://secure.phabricator.com/D15163
2016-02-02 06:42:41 -08:00

192 lines
4.7 KiB
JavaScript

/**
* @requires javelin-install
* javelin-util
* javelin-dom
* javelin-vector
* @provides phabricator-tooltip
* @javelin
*/
JX.install('Tooltip', {
statics: {
_node: null,
_lock: 0,
show : function(root, scale, align, content) {
var self = JX.Tooltip;
if (self._lock) {
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);
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);
},
_getSmartPosition: function (align, root, node) {
var self = JX.Tooltip;
var pos = self._proposePosition(align, root, node);
// If toolip is offscreen, try to be clever
if (!JX.Tooltip.isOnScreen(pos, node)) {
align = self._getImprovedOrientation(pos, node);
pos = self._proposePosition(align, root, node);
}
self._setAnchor(align);
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 s = JX.Vector.getScroll();
var v = JX.Vector.getViewport();
var max_x = s.x + v.x;
var max_y = s.y + v.y;
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 < s.x ||
corner.y < s.y ||
corner.x > max_x ||
corner.y > max_y) {
return false;
}
}
return true;
},
_getImprovedOrientation: function (a, node) {
// Try to predict the "more correct" orientation
var s = JX.Vector.getScroll();
var v = JX.Vector.getViewport();
var max_x = s.x + v.x;
var max_y = s.y + v.y;
var corners = this._getNodeCornerPositions(a, node);
for (var i = 0; i < corners.length; i++) {
var corner = corners[i];
if (corner.y < v.y) {
return 'S';
} else
if (corner.x < v.x) {
return 'E';
} else
if (corner.y > max_y) {
return 'N';
} else
if (corner.x > max_x) {
return 'W';
} else {
return 'N';
}
}
},
_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)];
},
_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;
}
},
lock: function() {
var self = JX.Tooltip;
self.hide();
self._lock++;
},
unlock: function() {
var self = JX.Tooltip;
self._lock--;
}
}
});