[Info Bubble] Refine positioning, add comments

Refine positioning to behave correctly when mouse is on
right and/or lower part of screen; listen for scope
destruction to avoid cases where an element is removed
before mouseleave is ever called. WTD-884.
This commit is contained in:
Victor Woeltjen
2015-05-22 13:19:37 -07:00
parent eafaa04c06
commit d9a2a7f4c3
3 changed files with 24 additions and 5 deletions

View File

@@ -1,4 +1,4 @@
<div class="t-infobubble s-infobubble l-infobubble-wrapper {{bubble.bubbleLayout}}"> <div class="t-infobubble s-infobubble l-infobubble-wrapper {{bubble.bubbleLayout}}" style="position: relative;">
<div class="l-infobubble"> <div class="l-infobubble">
<div ng-show="bubble.bubbleTitle.length > 0" <div ng-show="bubble.bubbleTitle.length > 0"
class="title"> class="title">

View File

@@ -8,28 +8,37 @@ define(
function InfoGesture($timeout, infoService, DELAY, element, domainObject) { function InfoGesture($timeout, infoService, DELAY, element, domainObject) {
var dismissBubble, var dismissBubble,
pendingBubble, pendingBubble,
mousePosition; mousePosition,
scopeOff;
function hideBubble() { function hideBubble() {
// If a bubble is showing, dismiss it
if (dismissBubble) { if (dismissBubble) {
dismissBubble(); dismissBubble();
element.off('mouseleave', hideBubble); element.off('mouseleave', hideBubble);
dismissBubble = undefined; dismissBubble = undefined;
} }
// If a bubble will be shown on a timeout, cancel that
if (pendingBubble) { if (pendingBubble) {
$timeout.cancel(pendingBubble); $timeout.cancel(pendingBubble);
pendingBubble = undefined; pendingBubble = undefined;
} }
// Also clear mouse position so we don't have a ton of tiny
// arrays allocated while user mouses over things
mousePosition = undefined; mousePosition = undefined;
} }
function trackPosition(event) { function trackPosition(event) {
// Record mouse position, so bubble can be shown at latest
// mouse position (not just where the mouse entered)
mousePosition = [ event.clientX, event.clientY ]; mousePosition = [ event.clientX, event.clientY ];
} }
function showBubble(event) { function showBubble(event) {
trackPosition(event); trackPosition(event);
// Show the bubble, after a suitable delay (if mouse has
// left before this time is up, this will be canceled.)
pendingBubble = $timeout(function () { pendingBubble = $timeout(function () {
dismissBubble = infoService.display( dismissBubble = infoService.display(
"info-table", "info-table",
@@ -45,13 +54,23 @@ define(
element.on('mouseleave', hideBubble); element.on('mouseleave', hideBubble);
} }
element.on('mousemove', trackPosition); // Show bubble (on a timeout) on mouse over
element.on('mouseenter', showBubble); element.on('mouseenter', showBubble);
// Also need to track position during hover
element.on('mousemove', trackPosition);
// Also make sure we dismiss bubble if representation is destroyed
// before the mouse actually leaves it
scopeOff = element.scope().$on('$destroy', hideBubble);
return { return {
destroy: function () { destroy: function () {
// Dismiss any active bubble...
hideBubble(); hideBubble();
// ...and detach listeners
element.off('mouseenter', showBubble); element.off('mouseenter', showBubble);
scopeOff();
} }
}; };
} }

View File

@@ -40,12 +40,12 @@ define(
// Position the bubble // Position the bubble
bubble.css('position', 'absolute'); bubble.css('position', 'absolute');
if (goLeft) { if (goLeft) {
bubble.css('right', (winDim[0] - position[0] + OFFSET[0]) + 'px'); bubble.css('right', (winDim[0] - position[0] - OFFSET[0]) + 'px');
} else { } else {
bubble.css('left', position[0] - OFFSET[0] + 'px'); bubble.css('left', position[0] - OFFSET[0] + 'px');
} }
if (goUp) { if (goUp) {
bubble.css('bottom', (winDim[1] - position[1] + OFFSET[1]) + 'px'); bubble.css('bottom', (winDim[1] - position[1] - OFFSET[1]) + 'px');
} else { } else {
bubble.css('top', position[1] - OFFSET[1] + 'px'); bubble.css('top', position[1] - OFFSET[1] + 'px');
} }