results.webkit.org: Move legend into sidebar
[WebKit-https.git] / Tools / resultsdbpy / resultsdbpy / view / static / js / tooltip.js
index 7390e8e..0f452a3 100644 (file)
@@ -28,7 +28,7 @@ function isPointInElement(element, point)
     if (!element || element.style.display == 'none')
         return false;
     const bounds = element.getBoundingClientRect();
-    return point.x >= bounds.left && point.x <= bounds.right && point.y >= bounds.top && point.y <= bounds.bottom;
+    return point.x >= bounds.left - 1 && point.x <= bounds.right + 1 && point.y >= bounds.top - 1 && point.y <= bounds.bottom + 1;
 }
 
 class _ToolTip {
@@ -36,6 +36,9 @@ class _ToolTip {
         this.ref = null;
         this.arrow = null;
         this.onArrowClick = null;
+
+        this.VERTICAL = 0;
+        this.HORIZONTAL = 1;
     }
     toString() {
         const self = this;
@@ -43,6 +46,8 @@ class _ToolTip {
             state: {content: null, points: null},
             onElementMount: (element) => {
                 element.addEventListener('mouseleave', (event) => {
+                    if (element.style.display === 'none')
+                        return;
                     if (!isPointInElement(self.arrow.element, event))
                         this.unset()
                 });
@@ -51,11 +56,11 @@ class _ToolTip {
                 if (stateDiff.content) {
                     DOM.inject(element, stateDiff.content);
                     element.style.display = null;
-                }
-                if (!state.content && !element.style.display) {
+                } else {
                     element.style.display = 'none';
                     DOM.inject(element, '');
                 }
+
                 if (stateDiff.points) {
                     element.style.left = '0px';
                     element.style.top = '0px';
@@ -67,23 +72,48 @@ class _ToolTip {
                     const viewportWitdh = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
                     const viewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
 
-                    // Make an effort to place the tooltip in the center of the viewport.
                     let direction = 'down';
-                    let tipY = upperPoint.y - 8 - bounds.height;
                     let point = upperPoint;
-                    if (tipY < scrollDelta || tipY + bounds.height + (lowerPoint.y - upperPoint.y) / 2 < scrollDelta + viewportHeight / 2) {
-                        direction = 'up';
-                        tipY = lowerPoint.y + 16;
-                        point = lowerPoint;
-                    }
-                    element.style.top = `${tipY}px`;
 
-                    let tipX = point.x - bounds.width / 2;
-                    if (tipX + bounds.width > viewportWitdh)
-                        tipX = viewportWitdh - bounds.width;
-                    if (tipX < 0)
-                        tipX = 0;
-                    element.style.left = `${tipX}px`;
+                    if (upperPoint.y == lowerPoint.y) {
+                        // Horizontal tooltip
+                        const leftPoint = stateDiff.points.length > 1 && stateDiff.points[0].x > stateDiff.points[1].x ? stateDiff.points[1] : stateDiff.points[0];
+                        const rightPoint = stateDiff.points.length > 1 && stateDiff.points[1].x > stateDiff.points[0].x ? stateDiff.points[1] : stateDiff.points[0];
+
+                        direction = 'left';
+                        let tipX = leftPoint.x - 12 - bounds.width;
+                        point = rightPoint;
+                        if (tipX < 0 || tipX + bounds.width + (rightPoint.x - leftPoint.x) / 2 < viewportWitdh / 2) {
+                            direction = 'right';
+                            tipX = rightPoint.x + 16;
+                            point = rightPoint;
+                        }
+                        element.style.left = `${tipX}px`;
+
+                        let tipY = point.y - bounds.height / 2;
+                        if (tipY + bounds.height > scrollDelta + viewportHeight)
+                            tipY = scrollDelta + viewportHeight - bounds.height;
+                        if (tipY < 0)
+                            tipY = 0;
+                        element.style.top = `${tipY}px`;
+                    } else {
+                        // Make an effort to place the tooltip in the center of the viewport.
+                        let tipY = upperPoint.y - 8 - bounds.height;
+                        point = upperPoint;
+                        if (tipY < scrollDelta || tipY + bounds.height + (lowerPoint.y - upperPoint.y) / 2 < scrollDelta + viewportHeight / 2) {
+                            direction = 'up';
+                            tipY = lowerPoint.y + 16;
+                            point = lowerPoint;
+                        }
+                        element.style.top = `${tipY}px`;
+
+                        let tipX = point.x - bounds.width / 2;
+                        if (tipX + bounds.width > viewportWitdh)
+                            tipX = viewportWitdh - bounds.width;
+                        if (tipX < 0)
+                            tipX = 0;
+                        element.style.left = `${tipX}px`;
+                    }
 
                     self.arrow.setState({direction: direction, location: point});
                 }
@@ -93,12 +123,14 @@ class _ToolTip {
             state: {direction: null, location: null},
             onElementMount: (element) => {
                 element.addEventListener('mouseleave', (event) => {
-                    if (!isPointInElement(self.ref.element, event))
+                    if (element.style.display === 'none')
+                        return;
+                    if (!isPointInElement(self.ref.element, event) && !isPointInElement(element, event))
                         this.unset()
                 });
             },
-            onStateUpdate: (element, stateDiff, state) => {
-                if (!state.direction || !state.location) {
+            onStateUpdate: (element, stateDiff) => {
+                if (!stateDiff.direction || !stateDiff.location) {
                     element.style.display = 'none';
                     element.onclick = null;
                     element.style.cursor = null;
@@ -113,12 +145,21 @@ class _ToolTip {
                     element.style.cursor = null;
                 }
 
-                element.classList = [`tooltip arrow-${state.direction}`];
-                element.style.left = `${state.location.x - 15}px`;
-                if (state.direction == 'down')
-                    element.style.top = `${state.location.y - 8}px`;
-                else
-                    element.style.top = `${state.location.y - 13}px`;
+                element.classList = [`tooltip arrow-${stateDiff.direction}`];
+                
+                if (stateDiff.direction == 'down') {
+                    element.style.left = `${stateDiff.location.x - 15}px`;
+                    element.style.top = `${stateDiff.location.y - 8}px`;
+                } else if (stateDiff.direction == 'left') {
+                    element.style.left = `${stateDiff.location.x - 30}px`;
+                    element.style.top = `${stateDiff.location.y - 15}px`;
+                } else if (stateDiff.direction == 'right') {
+                    element.style.left = `${stateDiff.location.x - 13}px`;
+                    element.style.top = `${stateDiff.location.y - 15}px`;
+                } else {
+                    element.style.left = `${stateDiff.location.x - 15}px`;
+                    element.style.top = `${stateDiff.location.y - 13}px`;
+                }
                 element.style.display = null;
             },
 
@@ -139,6 +180,28 @@ class _ToolTip {
         this.onArrowClick = onArrowClick;
         this.ref.setState({content: content, points: points});
     }
+    setByElement(content, element, options) {
+        const bound = element.getBoundingClientRect();
+        const orientation = options.orientation ? options.orientation : this.VERTICAL;
+        const onArrowClick = options.onArrowClick ? options.onArrowClick : null;
+
+        // Manage the scroll delta
+        let scrollDelta = 0;
+        if (window.getComputedStyle(element.offsetParent).getPropertyValue('position') == 'fixed')
+            scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
+
+        if (options.orientation) {
+            this.set(content, [
+                {x: bound.right, y: (bound.top + bound.bottom) / 2 + scrollDelta},
+                {x: bound.left, y: (bound.top + bound.bottom) / 2 + scrollDelta},
+            ], onArrowClick);
+        } else {
+            this.set(content, [
+                {x: (bound.right + bound.left) / 2, y: bound.top + scrollDelta},
+                {x: (bound.right + bound.left) / 2, y: bound.bottom + scrollDelta},
+            ], onArrowClick);
+        }
+    }
     unset() {
         if (this.ref)
             this.ref.setState({content: null, points: null});