mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
StateTimeline: Refactor hover markers (#75326)
This commit is contained in:
parent
e72b5c54f8
commit
0668820259
@ -1,4 +1,4 @@
|
||||
import uPlot, { Cursor, Series } from 'uplot';
|
||||
import uPlot, { Series } from 'uplot';
|
||||
|
||||
import { GrafanaTheme2, TimeRange } from '@grafana/data';
|
||||
import { alpha } from '@grafana/data/src/themes/colorManipulator';
|
||||
@ -83,17 +83,6 @@ export function getConfig(opts: TimelineCoreOptions) {
|
||||
|
||||
let qt: Quadtree;
|
||||
|
||||
const hoverMarks = Array(numSeries)
|
||||
.fill(null)
|
||||
.map(() => {
|
||||
let mark = document.createElement('div');
|
||||
mark.classList.add('bar-mark');
|
||||
mark.style.position = 'absolute';
|
||||
mark.style.background = 'rgba(255,255,255,0.2)';
|
||||
mark.style.pointerEvents = 'none';
|
||||
return mark;
|
||||
});
|
||||
|
||||
// Needed for to calculate text positions
|
||||
let boxRectsBySeries: TimelineBoxRect[][];
|
||||
|
||||
@ -105,6 +94,7 @@ export function getConfig(opts: TimelineCoreOptions) {
|
||||
|
||||
const font = `500 ${Math.round(12 * devicePixelRatio)}px ${theme.typography.fontFamily}`;
|
||||
const hovered: Array<Rect | null> = Array(numSeries).fill(null);
|
||||
let hoveredAtCursor: Rect | null = null;
|
||||
|
||||
const size = [colWidth, Infinity];
|
||||
const gapFactor = 1 - size[0];
|
||||
@ -378,8 +368,8 @@ export function getConfig(opts: TimelineCoreOptions) {
|
||||
pxPerChar += 2.5;
|
||||
|
||||
over.style.overflow = 'hidden';
|
||||
hoverMarks.forEach((m) => {
|
||||
over.appendChild(m);
|
||||
u.root.querySelectorAll<HTMLDivElement>('.u-cursor-pt').forEach((el) => {
|
||||
el.style.borderRadius = '0';
|
||||
});
|
||||
};
|
||||
|
||||
@ -396,112 +386,77 @@ export function getConfig(opts: TimelineCoreOptions) {
|
||||
});
|
||||
};
|
||||
|
||||
function setHoverMark(i: number, o: Rect | null) {
|
||||
let h = hoverMarks[i];
|
||||
function setHovered(cx: number, cy: number, cys: number[]) {
|
||||
hovered.fill(null);
|
||||
hoveredAtCursor = null;
|
||||
|
||||
let pxRatio = uPlot.pxRatio;
|
||||
|
||||
if (o) {
|
||||
h.style.display = '';
|
||||
h.style.left = round(o.x / pxRatio) + 'px';
|
||||
h.style.top = round(o.y / pxRatio) + 'px';
|
||||
h.style.width = round(o.w / pxRatio) + 'px';
|
||||
h.style.height = round(o.h / pxRatio) + 'px';
|
||||
} else {
|
||||
h.style.display = 'none';
|
||||
if (cx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
hovered[i] = o;
|
||||
for (let i = 0; i < cys.length; i++) {
|
||||
let cy2 = cys[i];
|
||||
|
||||
qt.get(cx, cy2, 1, 1, (o) => {
|
||||
if (pointWithin(cx, cy2, o.x, o.y, o.x + o.w, o.y + o.h)) {
|
||||
hovered[o.sidx] = o;
|
||||
|
||||
if (Math.abs(cy - cy2) <= o.h / 2) {
|
||||
hoveredAtCursor = o;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let hoveredAtCursor: Rect | undefined;
|
||||
const hoverMulti = mode === TimelineMode.Changes;
|
||||
|
||||
function hoverMulti(cx: number, cy: number) {
|
||||
let foundAtCursor: Rect | undefined;
|
||||
|
||||
for (let i = 0; i < numSeries; i++) {
|
||||
let found: Rect | undefined;
|
||||
|
||||
if (cx >= 0) {
|
||||
let cy2 = yMids[i];
|
||||
|
||||
qt.get(cx, cy2, 1, 1, (o) => {
|
||||
if (pointWithin(cx, cy2, o.x, o.y, o.x + o.w, o.y + o.h)) {
|
||||
found = o;
|
||||
|
||||
if (Math.abs(cy - cy2) <= o.h / 2) {
|
||||
foundAtCursor = o;
|
||||
const cursor: uPlot.Cursor = {
|
||||
x: mode === TimelineMode.Changes,
|
||||
y: false,
|
||||
dataIdx: (u, seriesIdx) => {
|
||||
if (seriesIdx === 1) {
|
||||
// if quadtree is empty, fill it
|
||||
if (qt.o.length === 0 && qt.q == null) {
|
||||
for (const seriesRects of boxRectsBySeries) {
|
||||
for (const rect of seriesRects) {
|
||||
rect && qt.add(rect);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (found !== hovered[i]) {
|
||||
setHoverMark(i, found);
|
||||
}
|
||||
} else if (hovered[i] != null) {
|
||||
setHoverMark(i, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAtCursor) {
|
||||
if (foundAtCursor !== hoveredAtCursor) {
|
||||
hoveredAtCursor = foundAtCursor;
|
||||
onHover(foundAtCursor.sidx, foundAtCursor.didx, foundAtCursor);
|
||||
}
|
||||
} else if (hoveredAtCursor) {
|
||||
hoveredAtCursor = undefined;
|
||||
onLeave();
|
||||
}
|
||||
}
|
||||
let cx = u.cursor.left! * uPlot.pxRatio;
|
||||
let cy = u.cursor.top! * uPlot.pxRatio;
|
||||
|
||||
function hoverOne(cx: number, cy: number) {
|
||||
let foundAtCursor: Rect | undefined;
|
||||
let prevHovered = hoveredAtCursor;
|
||||
|
||||
qt.get(cx, cy, 1, 1, (o) => {
|
||||
if (pointWithin(cx, cy, o.x, o.y, o.x + o.w, o.y + o.h)) {
|
||||
foundAtCursor = o;
|
||||
}
|
||||
});
|
||||
setHovered(cx, cy, hoverMulti ? yMids : [cy]);
|
||||
|
||||
if (foundAtCursor) {
|
||||
setHoverMark(0, foundAtCursor);
|
||||
|
||||
if (foundAtCursor !== hoveredAtCursor) {
|
||||
hoveredAtCursor = foundAtCursor;
|
||||
onHover(foundAtCursor.sidx, foundAtCursor.didx, foundAtCursor);
|
||||
}
|
||||
} else if (hoveredAtCursor) {
|
||||
setHoverMark(0, null);
|
||||
hoveredAtCursor = undefined;
|
||||
onLeave();
|
||||
}
|
||||
}
|
||||
|
||||
const doHover = mode === TimelineMode.Changes ? hoverMulti : hoverOne;
|
||||
|
||||
const setCursor = (u: uPlot) => {
|
||||
let cx = round(u.cursor.left! * uPlot.pxRatio);
|
||||
let cy = round(u.cursor.top! * uPlot.pxRatio);
|
||||
|
||||
// if quadtree is empty, fill it
|
||||
if (!qt.o.length && qt.q == null) {
|
||||
for (const seriesRects of boxRectsBySeries) {
|
||||
for (const rect of seriesRects) {
|
||||
rect && qt.add(rect);
|
||||
if (hoveredAtCursor != null) {
|
||||
if (hoveredAtCursor !== prevHovered) {
|
||||
onHover(hoveredAtCursor.sidx, hoveredAtCursor.didx, hoveredAtCursor);
|
||||
}
|
||||
} else if (prevHovered != null) {
|
||||
onLeave();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doHover(cx, cy);
|
||||
};
|
||||
return hovered[seriesIdx]?.didx;
|
||||
},
|
||||
points: {
|
||||
fill: 'rgba(255,255,255,0.2)',
|
||||
bbox: (u, seriesIdx) => {
|
||||
let hRect = hovered[seriesIdx];
|
||||
let isHovered = hRect != null;
|
||||
|
||||
// hide y crosshair & hover points
|
||||
const cursor: Partial<Cursor> = {
|
||||
y: false,
|
||||
x: mode === TimelineMode.Changes,
|
||||
points: { show: false },
|
||||
return {
|
||||
left: isHovered ? hRect!.x / uPlot.pxRatio : -10,
|
||||
top: isHovered ? hRect!.y / uPlot.pxRatio : -10,
|
||||
width: isHovered ? hRect!.w / uPlot.pxRatio : 0,
|
||||
height: isHovered ? hRect!.h / uPlot.pxRatio : 0,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const yMids: number[] = Array(numSeries).fill(0);
|
||||
@ -576,7 +531,6 @@ export function getConfig(opts: TimelineCoreOptions) {
|
||||
// hooks
|
||||
init,
|
||||
drawClear,
|
||||
setCursor,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,6 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<UPlotConfigOptions> = (
|
||||
|
||||
builder.addHook('init', coreConfig.init);
|
||||
builder.addHook('drawClear', coreConfig.drawClear);
|
||||
builder.addHook('setCursor', coreConfig.setCursor);
|
||||
|
||||
// in TooltipPlugin, this gets invoked and the result is bound to a setCursor hook
|
||||
// which fires after the above setCursor hook, so can take advantage of hoveringOver
|
||||
|
@ -445,12 +445,11 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
|
||||
const init = (u: uPlot) => {
|
||||
let over = u.over;
|
||||
over.style.overflow = 'hidden';
|
||||
u.root.querySelectorAll('.u-cursor-pt').forEach((el) => {
|
||||
if (el instanceof HTMLElement) {
|
||||
el.style.borderRadius = '0';
|
||||
if (opts.fullHighlight) {
|
||||
el.style.zIndex = '-1';
|
||||
}
|
||||
u.root.querySelectorAll<HTMLDivElement>('.u-cursor-pt').forEach((el) => {
|
||||
el.style.borderRadius = '0';
|
||||
|
||||
if (opts.fullHighlight) {
|
||||
el.style.zIndex = '-1';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user