Diff
Modified: trunk/Tools/ChangeLog (248909 => 248910)
--- trunk/Tools/ChangeLog 2019-08-20 18:46:36 UTC (rev 248909)
+++ trunk/Tools/ChangeLog 2019-08-20 18:55:34 UTC (rev 248910)
@@ -1,3 +1,36 @@
+2019-08-20 Jonathan Bedard <jbed...@apple.com>
+
+ results.webkit.org: Add ToolTips
+ https://bugs.webkit.org/show_bug.cgi?id=200801
+
+ Rubber-stamped by Aakash Jain.
+
+ When dots or scale labels are hovered over, we should display a tool tip with additional information about
+ The specific element.
+
+ * resultsdbpy/resultsdbpy/view/static/css/tooltip.css: Added.
+ (.tooltip): Add class for ToolTip text box.
+ (.tooltip-arrow-up): Add class for ToolTip arrow pointing up.
+ (.tooltip-arrow-down): Add class for ToolTip arrow pointing down.
+ * resultsdbpy/resultsdbpy/view/static/js/commit.js:
+ (_CommitBank.prototype.commitsDuringUuid): Return a list of commits which were the trunk of their respective
+ repositories at a given time.
+ * resultsdbpy/resultsdbpy/view/static/js/timeline.js:
+ (xAxisFromScale): Add callbacks triggered when the mouse enters or leaves elements in the scale canvas.
+ (TimelineFromEndpoint.render): Add callbacks triggered when the mouse enters or leaves dot elements.
+ * resultsdbpy/resultsdbpy/view/static/js/tooltip.js: Added.
+ (isPointInElement): Given an element and a point, return true if that point is within the bounds of the element.
+ (_ToolTip):
+ (_ToolTip.prototype.set): Set the content and location of the ToolTip.
+ (_ToolTip.prototype.toString): Return the html needed to render the ToolTip.
+ (_ToolTip.prototype.unset): Clear and hide the ToolTip.
+ (_ToolTip.prototype.isIn): Check if a given point is contained within the ToolTip.
+ * resultsdbpy/resultsdbpy/view/static/library/js/components/TimelineComponents.js:
+ (Timeline.CanvasSeriesComponent): Convert onHover events to onEnter/onLeave events. Add toolTips points to both
+ dot and scale elements.
+ * resultsdbpy/resultsdbpy/view/templates/search.html: Add ToolTip.
+ * resultsdbpy/resultsdbpy/view/templates/suite_results.html: Ditto.
+
2019-08-20 Justin Michaud <justin_mich...@apple.com>
Fix InBounds speculation of typed array PutByVal and add extra step to integer range optimization to search for equality relationships on the RHS value
Added: trunk/Tools/resultsdbpy/resultsdbpy/view/static/css/tooltip.css (0 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/css/tooltip.css (rev 0)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/css/tooltip.css 2019-08-20 18:55:34 UTC (rev 248910)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.tooltip {
+ z-index: 50;
+ position: absolute;
+ opacity: 80%;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 15px;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+.tooltip.arrow-up {
+ border-color: transparent transparent #cccd transparent;
+}
+
+.tooltip.arrow-down {
+ border-color: #cccd transparent transparent transparent;
+}
+
+.tooltip-content {
+ z-index: 50;
+ position: absolute;
+ -webkit-backdrop-filter: blur(10px) brightness(88%);
+ backdrop-filter: blur(10px) brightness(88%);
+ color: var(--boldInverseColor);
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ padding: 5px;
+ font-size: var(--smallSize);
+ list-style: none;
+ max-width: 600px;
+}
Modified: trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/commit.js (248909 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/commit.js 2019-08-20 18:46:36 UTC (rev 248909)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/commit.js 2019-08-20 18:55:34 UTC (rev 248910)
@@ -162,6 +162,48 @@
}
return null;
}
+ commitsDuringUuid(uuid) {
+ let commits = [];
+ let begin = 0;
+ let end = this.commits.length - 1;
+ let index = this.commits.length - 1;
+ while (begin <= end) {
+ const mid = Math.ceil((begin + end) / 2);
+ const candidate = this.commits[mid];
+ if (candidate.uuid === uuid) {
+ commits.push(candidate);
+ index = mid - 1;
+ break;
+ }
+ if (candidate.uuid < uuid)
+ begin = mid + 1;
+ else
+ end = mid - 1;
+ }
+
+ let repositories = new Set();
+ if (commits.length)
+ repositories.add(commits[0].repository_id);
+
+ while (index >= 0) {
+ if (repositories.has(this.commits[index].repository_id)) {
+ --index;
+ continue;
+ }
+ if (this._repositories.length && !this._repositories.has(this.commits[index].repository_id)) {
+ --index;
+ continue;
+ }
+
+ commits.push(this.commits[index]);
+ repositories.add(this.commits[index].repository_id);
+ if (repositories.length == this._repositories.length)
+ break;
+
+ --index;
+ }
+ return commits.sort(function(a, b) {return a.repository_id.localeCompare(b.repository_id)});
+ }
_loadSiblings(commit) {
const query = paramsToQuery({
branch: [commit.branch],
Modified: trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/timeline.js (248909 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/timeline.js 2019-08-20 18:46:36 UTC (rev 248909)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/timeline.js 2019-08-20 18:55:34 UTC (rev 248910)
@@ -23,9 +23,10 @@
import {CommitBank} from '/assets/js/commit.js';
import {Configuration} from '/assets/js/configuration.js';
-import {deepCompare, ErrorDisplay, paramsToQuery, queryToParams} from '/assets/js/common.js';
+import {deepCompare, ErrorDisplay, escapeHTML, paramsToQuery, queryToParams} from '/assets/js/common.js';
+import {ToolTip} from '/assets/js/tooltip.js';
+import {Timeline} from '/library/js/components/TimelineComponents.js';
import {DOM, EventStream, REF, FP} from '/library/js/Ref.js';
-import {Timeline} from '/library/js/components/TimelineComponents.js';
const DEFAULT_LIMIT = 100;
@@ -219,6 +220,25 @@
const query = paramsToQuery(params);
window.open(`/commit?${query}`, '_blank');
},
+ onScaleEnter: (node, event, canvas) => {
+ const scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
+ ToolTip.set(
+ `<div class="content">
+ Time: ${new Date(node.label.timestamp * 1000).toLocaleString()}<br>
+ Repository: ${node.label.repository_id}<br>
+ Branch: ${node.label.branch}<br>
+ Committer: ${node.label.committer}
+ ${node.label.message ? `<br><div>${escapeHTML(node.label.message.split('\n')[0])}</div>` : ''}
+ </div>`,
+ node.tipPoints.map((point) => {
+ return {x: canvas.x + point.x, y: canvas.y + scrollDelta + point.y};
+ })
+ );
+ },
+ onScaleLeave: (event, canvas) => {
+ if (!ToolTip.isIn({x: event.x, y: event.y}))
+ ToolTip.unset();
+ },
// Per the birthday paradox, 10% change of collision with 7.7 million commits with 12 character commits
getLabelFunc: (commit) => {return commit ? commit.id.substring(0,12) : '?';},
getScaleFunc: (commit) => commit.uuid,
@@ -570,6 +590,48 @@
}
}
+ function onDotEnterFactory(configuration) {
+ return (data, event, canvas) => {
+ const scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
+ ToolTip.set(
+ `<div class="content">
+ ${data.start_time ? `<a href="" () {
+ let buildParams = configuration.toParams();
+ buildParams['suite'] = [self.suite];
+ buildParams['uuid'] = [data.uuid];
+ buildParams['after_time'] = [data.start_time];
+ buildParams['before_time'] = [data.start_time];
+ if (branch)
+ buildParams['branch'] = branch;
+ return buildParams;
+ } ())}" target="_blank">Test run</a> @ ${new Date(data.start_time * 1000).toLocaleString()}<br>` : ''}
+ Commits: ${CommitBank.commitsDuringUuid(data.uuid).map((commit) => {
+ let params = {
+ branch: commit.branch ? [commit.branch] : branch,
+ uuid: [commit.uuid],
+ }
+ if (!params.branch)
+ delete params.branch;
+ const query = paramsToQuery(params);
+ return `<a href="" target="_blank">${commit.id.substring(0,12)}</a>`;
+ }).join(', ')}
+ <br>
+
+ ${data.expected ? `Expected: ${data.expected}<br>` : ''}
+ ${data.actual ? `Actual: ${data.actual}<br>` : ''}
+ </div>`,
+ data.tipPoints.map((point) => {
+ return {x: canvas.x + point.x, y: canvas.y + scrollDelta + point.y};
+ })
+ );
+ }
+ }
+
+ function onDotLeave(event, canvas) {
+ if (!ToolTip.isIn({x: event.pageX, y: event.pageY}))
+ ToolTip.unset();
+ }
+
function exporterFactory(data) {
return (updateFunction) => {
self.updates.push((scale) => {updateFunction(data, scale);});
@@ -624,6 +686,8 @@
renderFactory: options.renderFactory,
exporter: options.exporter,
onDotClick: onDotClickFactory(config),
+ onDotEnter: onDotEnterFactory(config),
+ onDotLeave: onDotLeave,
exporter: exporterFactory(resultsForConfig),
}));
@@ -638,6 +702,8 @@
renderFactory: options.renderFactory,
exporter: options.exporter,
onDotClick: onDotClickFactory(sdkConfig),
+ onDotEnter: onDotEnterFactory(sdkConfig),
+ onDotLeave: onDotLeave,
exporter: exporterFactory(resultsByKey[sdkConfig.toKey()]),
})));
});
@@ -663,6 +729,8 @@
compareFunc: options.compareFunc,
renderFactory: options.renderFactory,
onDotClick: onDotClickFactory(configuration),
+ onDotEnter: onDotEnterFactory(configuration),
+ onDotLeave: onDotLeave,
exporter: exporterFactory(allResults),
})),
...collapsedTimelines
Added: trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/tooltip.js (0 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/tooltip.js (rev 0)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/js/tooltip.js 2019-08-20 18:55:34 UTC (rev 248910)
@@ -0,0 +1,144 @@
+// Copyright (C) 2019 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+
+import {DOM, REF} from '/library/js/Ref.js';
+
+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;
+}
+
+class _ToolTip {
+ constructor() {
+ this.ref = null;
+ this.arrow = null;
+ }
+ toString() {
+ const self = this;
+ this.ref = REF.createRef({
+ state: {content: null, points: null},
+ onElementMount: (element) => {
+ element.addEventListener('mouseleave', (event) => {
+ if (!isPointInElement(self.arrow.element, event))
+ this.unset()
+ });
+ },
+ onStateUpdate: (element, stateDiff, state) => {
+ if (stateDiff.content) {
+ DOM.inject(element, stateDiff.content);
+ element.style.display = null;
+ }
+ if (!state.content && !element.style.display) {
+ element.style.display = 'none';
+ DOM.inject(element, '');
+ }
+ if (stateDiff.points) {
+ element.style.left = '0px';
+ element.style.top = '0px';
+
+ const upperPoint = stateDiff.points.length > 1 && stateDiff.points[0].y > stateDiff.points[1].y ? stateDiff.points[1] : stateDiff.points[0];
+ const lowerPoint = stateDiff.points.length > 1 && stateDiff.points[1].y > stateDiff.points[0].y ? stateDiff.points[1] : stateDiff.points[0];
+ const scrollDelta = document.documentElement.scrollTop || document.body.scrollTop;
+ const bounds = element.getBoundingClientRect();
+ 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`;
+
+ self.arrow.setState({direction: direction, location: point});
+ }
+ },
+ });
+ this.arrow = REF.createRef({
+ state: {direction: null, location: null},
+ onElementMount: (element) => {
+ element.addEventListener('mouseleave', (event) => {
+ if (!isPointInElement(self.ref.element, event))
+ this.unset()
+ });
+ },
+ onStateUpdate: (element, stateDiff, state) => {
+ if (!state.direction || !state.location) {
+ element.style.display = 'none';
+ return;
+ }
+
+ 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.style.display = null;
+ },
+
+ });
+ return `<div class="tooltip arrow-up" ref="${this.arrow}"></div>
+ <div class="tooltip-content" ref="${this.ref}">
+ </div>`;
+ }
+ set(content, points) {
+ if (!this.ref) {
+ console.error('Cannot set ToolTip content, no tooltip on the page');
+ return;
+ }
+ if (!points || points.length == 0) {
+ console.error('Tool tips require a location');
+ return;
+ }
+
+ this.ref.setState({content: content, points: points});
+ }
+ unset() {
+ if (this.ref)
+ this.ref.setState({content: null, points: null});
+ if (this.arrow)
+ this.arrow.setState({direction: null, points: null});
+ }
+ isIn(point) {
+ return isPointInElement(this.ref.element, point) || isPointInElement(this.arrow.element, point);
+ }
+}
+
+const ToolTip = new _ToolTip();
+
+export {ToolTip};
Modified: trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/components/TimelineComponents.js (248909 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/components/TimelineComponents.js 2019-08-20 18:46:36 UTC (rev 248909)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/static/library/js/components/TimelineComponents.js 2019-08-20 18:55:34 UTC (rev 248910)
@@ -223,7 +223,8 @@
const getScale = typeof option.getScaleFunc === "function" ? option.getScaleFunc : (a) => a;
const comp = typeof option.compareFunc === "function" ? option.compareFunc : (a, b) => a - b;
const _onDotClick_ = typeof option._onDotClick_ === "function" ? option.onDotClick : null;
- const _onDotHover_ = typeof option._onDotHover_ === "function" ? option.onDotHover : null;
+ const _onDotEnter_ = typeof option._onDotEnter_ === "function" ? option.onDotEnter : null;
+ const _onDotLeave_ = typeof option._onDotLeave_ === "function" ? option.onDotLeave : null;
const tagHeight = defaultFontSize;
const height = option.height ? option.height : 2 * radius + tagHeight;
const colorBatchRender = new ColorBatchRender();
@@ -360,8 +361,26 @@
};
return ListProviderReceiver((updateContainerWidth, onContainerScroll, onResize) => {
+ const mouseMove = (e) => {
+ let dots = getMouseEventTirggerDots(e, canvasRef.state.scrollLeft, canvasRef.element);
+ if (dots.length) {
+ if (onDotEnter) {
+ dots[0].tipPoints = [
+ {x: dots[0]._dotCenter.x, y: dots[0]._dotCenter.y - 3 * radius / 2},
+ {x: dots[0]._dotCenter.x, y: dots[0]._dotCenter.y + radius / 2},
+ ];
+ onDotEnter(dots[0], e, canvasRef.element.getBoundingClientRect());
+ }
+ canvasRef.element.style.cursor = "pointer";
+ } else {
+ if (onDotLeave)
+ onDotLeave(e, canvasRef.element.getBoundingClientRect());
+ canvasRef.element.style.cursor = "default";
+ }
+ }
const _onScrollAction_ = (e) => {
canvasRef.setState({scrollLeft: e.target.scrollLeft / getDevicePixelRatio()});
+ mouseMove(e);
};
const _onResizeAction_ = (width) => {
canvasRef.setState({width: width});
@@ -386,17 +405,10 @@
});
}
- if (onDotClick || onDotHover) {
- element.addEventListener('mousemove', (e) => {
- let dots = getMouseEventTirggerDots(e, canvasRef.state.scrollLeft, element);
- if (dots.length) {
- if (onDotHover)
- onDotHover(dots[0], e);
- element.style.cursor = "pointer";
- } else
- element.style.cursor = "default";
- });
- }
+ if (onDotClick || onDotEnter || onDotLeave)
+ element.addEventListener('mousemove', mouseMove);
+ if (onDotLeave)
+ element.addEventListener('mouseleave', (e) => onDotLeave(e, element.getBoundingClientRect()));
createInsertionObservers(element, (entries) => {
canvasRef.setState({onScreen: entries[0].isIntersecting});
@@ -533,7 +545,8 @@
const getScaleKey = typeof option.getScaleFunc === "function" ? option.getScaleFunc : (a) => a;
const comp = typeof option.compareFunc === "function" ? option.compareFunc : (a, b) => a - b;
const _onScaleClick_ = typeof option._onScaleClick_ === "function" ? option.onScaleClick : null;
- const _onScaleHover_ = typeof option._onScaleHover_ === "function" ? option.onScaleHover : null;
+ const _onScaleEnter_ = typeof option._onScaleEnter_ === "function" ? option.onScaleEnter : null;
+ const _onScaleLeave_ = typeof option._onScaleLeave_ === "function" ? option.onScaleLeave : null;
const sortData = option.sortData === true ? option.sortData : false;
const getLabel = typeof option.getLabelFunc === "function" ? option.getLabelFunc : (a) => a;
const isTop = typeof option.isTop === "boolean" ? option.isTop : false;
@@ -626,8 +639,9 @@
const getMouseEventTirggerScales = (e, scrollLeft, element) => {
const {x, y} = getMousePosInCanvas(e, element);
return onScreenScales.filter(scale => {
- const width = scale.label.toString().length * fontSizeNumber / 2;
- const height = scale.label.toString().length * fontSizeNumber / 2 * sqrt3;
+ const labelLength = getLabel(scale.label).length;
+ const width = labelLength * fontSizeNumber / 2;
+ const height = labelLength * fontSizeNumber / 2 * sqrt3;
const point1 = {
x: scale._tagTop.x - scrollLeft - (isTop ? fontSizeNumber / 2 * sqrt3 : 0),
y: scale._tagTop.y + (fontSizeNumber / 2 + scaleTagLineHeight) * (isTop ? -1 : 1),
@@ -725,8 +739,30 @@
return {
series: ListProviderReceiver((updateContainerWidth, onContainerScroll, onResize) => {
+ const mouseMove = (e) => {
+ let scales = getMouseEventTirggerScales(e, canvasRef.state.scrollLeft, canvasRef.element);
+ if (scales.length) {
+ if (onScaleEnter) {
+ const labelLength = getLabel(scales[0].label).length;
+ scales[0].tipPoints = [{
+ x: scales[0]._tagTop.x - canvasRef.state.scrollLeft,
+ y: scales[0]._tagTop.y + scaleTagLineHeight * (isTop ? -1 : 0),
+ }, {
+ x: scales[0]._tagTop.x - canvasRef.state.scrollLeft + labelLength * fontSizeNumber / 3 - scaleTagLineHeight * (isTop ? 1 : .25),
+ y: scales[0]._tagTop.y + (labelLength * fontSizeNumber / 2 * sqrt3) * (isTop ? -1 : 1) + scaleTagLineHeight * (isTop ? 1 : 0),
+ }];
+ onScaleEnter(scales[0], e, canvasRef.element.getBoundingClientRect());
+ }
+ canvasRef.element.style.cursor = "pointer";
+ } else {
+ if (onScaleEnter)
+ onScaleLeave(e, canvasRef.element.getBoundingClientRect());
+ canvasRef.element.style.cursor = "default";
+ }
+ }
const _onScrollAction_ = (e) => {
canvasRef.setState({scrollLeft: e.target.scrollLeft / getDevicePixelRatio()});
+ mouseMove(e);
};
const _onResizeAction_ = (width) => {
canvasRef.setState({width: width});
@@ -750,18 +786,10 @@
});
}
- if (onScaleClick || onScaleHover) {
- element.addEventListener('mousemove', (e) => {
- let scales = getMouseEventTirggerScales(e, canvasRef.state.scrollLeft, element);
- if (scales.length) {
- if (onScaleHover)
- onScaleHover(scales[0], e);
- element.style.cursor = "pointer";
- } else {
- element.style.cursor = "default";
- }
- });
- }
+ if (onScaleClick || onScaleEnter || onScaleLeave)
+ element.addEventListener('mousemove', mouseMove);
+ if (onScaleLeave)
+ element.addEventListener('mouseleave', (e) => onScaleLeave(e, element.getBoundingClientRect()));
},
onElementUnmount: (element) => {
onContainerScroll.stopAction(onScrollAction);
Modified: trunk/Tools/resultsdbpy/resultsdbpy/view/templates/search.html (248909 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/templates/search.html 2019-08-20 18:46:36 UTC (rev 248909)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/templates/search.html 2019-08-20 18:55:34 UTC (rev 248910)
@@ -28,6 +28,7 @@
<link rel="stylesheet" type="text/css" href=""
<link rel="stylesheet" type="text/css" href=""
<link rel="stylesheet" type="text/css" href=""
+<link rel="stylesheet" type="text/css" href=""
<script type="module">
import {CommitBank} from '/assets/js/commit.js';
@@ -34,9 +35,10 @@
import {deepCompare, ErrorDisplay, queryToParams, paramsToQuery} from '/assets/js/common.js';
import {Configuration} from '/assets/js/configuration.js';
import {Drawer, BranchSelector, ConfigurationSelectors, LimitSlider} from '/assets/js/drawer.js';
-import {DOM, REF} from '/library/js/Ref.js';
import {SearchBar} from '/assets/js/search.js';
import {Legend, TimelineFromEndpoint} from '/assets/js/timeline.js';
+import {ToolTip} from '/assets/js/tooltip.js';
+import {DOM, REF} from '/library/js/Ref.js';
const DEFAULT_LIMIT = 100;
const SUITES = JSON.parse('{{ suites|safe }}');
@@ -215,7 +217,8 @@
DOM.inject(
document.getElementById('app'),
- `${Drawer([
+ `${ToolTip}
+ ${Drawer([
LimitSlider(() => {view.reload()}),
BranchSelector(() => {
CommitBank.reload();
Modified: trunk/Tools/resultsdbpy/resultsdbpy/view/templates/suite_results.html (248909 => 248910)
--- trunk/Tools/resultsdbpy/resultsdbpy/view/templates/suite_results.html 2019-08-20 18:46:36 UTC (rev 248909)
+++ trunk/Tools/resultsdbpy/resultsdbpy/view/templates/suite_results.html 2019-08-20 18:55:34 UTC (rev 248910)
@@ -28,6 +28,7 @@
<link rel="stylesheet" type="text/css" href=""
<link rel="stylesheet" type="text/css" href=""
<link rel="stylesheet" type="text/css" href=""
+<link rel="stylesheet" type="text/css" href=""
<script type="module">
import {CommitBank} from '/assets/js/commit.js';
@@ -34,8 +35,9 @@
import {deepCompare, ErrorDisplay, queryToParams, paramsToQuery} from '/assets/js/common.js';
import {Configuration} from '/assets/js/configuration.js';
import {Drawer, BranchSelector, ConfigurationSelectors, LimitSlider} from '/assets/js/drawer.js';
+import {Legend, TimelineFromEndpoint} from '/assets/js/timeline.js';
+import {ToolTip} from '/assets/js/tooltip.js';
import {DOM, REF} from '/library/js/Ref.js';
-import {Legend, TimelineFromEndpoint} from '/assets/js/timeline.js';
const DEFAULT_LIMIT = 100;
const SUITES = JSON.parse('{{ suites|safe }}');
@@ -159,7 +161,8 @@
let view = new MainView();
-DOM.inject(document.getElementById('app'), `${Drawer([
+DOM.inject(document.getElementById('app'), `${ToolTip}
+${Drawer([
LimitSlider(() => {view.reload()}),
BranchSelector(() => {
CommitBank.reload();