TooltipPlugin2: Improve containment inside ancestors that hide overflow (#81442)

This commit is contained in:
Leon Sorokin 2024-01-28 22:36:28 -06:00 committed by GitHub
parent d34bd1dc90
commit 6a61c3da2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -146,20 +146,23 @@ export const TooltipPlugin2 = ({
let _plot = plot;
let _isHovering = isHovering;
let _someSeriesIdx = false;
let _isPinned = isPinned;
let _style = style;
const updateHovering = () => {
_isHovering = closestSeriesIdx != null || (hoverMode === TooltipHoverMode.xAll && _someSeriesIdx);
};
let offsetX = 0;
let offsetY = 0;
let htmlEl = document.documentElement;
let winWidth = htmlEl.clientWidth - 16;
let winHeight = htmlEl.clientHeight - 16;
window.addEventListener('resize', (e) => {
winWidth = htmlEl.clientWidth - 16;
winHeight = htmlEl.clientHeight - 16;
});
let containRect = {
lft: 0,
top: 0,
rgt: screen.width,
btm: screen.height,
};
let selectedRange: TimeRange2 | null = null;
let seriesIdxs: Array<number | null> = plot?.cursor.idxs!.slice()!;
@ -290,6 +293,51 @@ export const TooltipPlugin2 = ({
}
}
});
const haltAncestorId = 'pageContent';
const scrollbarWidth = 16;
// if we're in a container that can clip the tooltip, we should try to stay within that rather than window edges
u.over.addEventListener(
'mouseenter',
() => {
// clamp to viewport bounds
let htmlEl = document.documentElement;
let winWid = htmlEl.clientWidth - scrollbarWidth;
let winHgt = htmlEl.clientHeight - scrollbarWidth;
let lft = 0,
top = 0,
rgt = winWid,
btm = winHgt;
// find nearest scrollable container where overflow is not visible, (stop at #pageContent)
let par: HTMLElement | null = u.root;
while (par != null && par.id !== haltAncestorId) {
let style = getComputedStyle(par);
let overflowX = style.getPropertyValue('overflow-x');
let overflowY = style.getPropertyValue('overflow-y');
if (overflowX !== 'visible' || overflowY !== 'visible') {
let rect = par.getBoundingClientRect();
lft = Math.max(rect.x, lft);
top = Math.max(rect.y, top);
rgt = Math.min(lft + rect.width, rgt);
btm = Math.min(top + rect.height, btm);
break;
}
par = par.parentElement;
}
containRect.lft = lft;
containRect.top = top;
containRect.rgt = rgt;
containRect.btm = btm;
},
{ capture: true }
);
});
config.addHook('setSelect', (u) => {
@ -372,44 +420,22 @@ export const TooltipPlugin2 = ({
yDrag = false;
});
// fires on data value hovers/unhovers (before setSeries)
config.addHook('setLegend', (u) => {
seriesIdxs = _plot?.cursor!.idxs!.slice()!;
let hoveredSeriesIdx = seriesIdxs.findIndex((v, i) => i > 0 && v != null);
let _isHoveringNow = hoveredSeriesIdx !== -1;
// setSeries may not fire if focus.prox is not set, so we set closestSeriesIdx here instead
if (hoverMode === TooltipHoverMode.xyOne) {
closestSeriesIdx = hoveredSeriesIdx;
}
if (_isHoveringNow) {
// create
if (!_isHovering) {
_isHovering = true;
}
} else {
// destroy...TODO: debounce this
if (_isHovering) {
_isHovering = false;
}
}
scheduleRender();
});
// fires on series focus/proximity changes
// e.g. to highlight the hovered/closest series
// TODO: we only need this for multi/all mode?
config.addHook('setSeries', (u, seriesIdx) => {
// don't jiggle focused series styling when there's only one series
// const isMultiSeries = u.series.length > 2;
// if (hoverModeRef.current === TooltipHoverMode.xAll && closestSeriesIdx !== seriesIdx) {
closestSeriesIdx = seriesIdx;
updateHovering();
scheduleRender();
});
// fires on data value hovers/unhovers
config.addHook('setLegend', (u) => {
seriesIdxs = _plot?.cursor!.idxs!.slice()!;
_someSeriesIdx = seriesIdxs.some((v, i) => i > 0 && v != null);
updateHovering();
scheduleRender();
// }
});
// fires on mousemoves
@ -435,25 +461,25 @@ export const TooltipPlugin2 = ({
let clientY = u.rect.top + top;
if (offsetY !== 0) {
if (clientY + height < winHeight || clientY - height < 0) {
if (clientY + height < containRect.btm || clientY - height < 0) {
offsetY = 0;
} else if (offsetY !== -height) {
offsetY = -height;
}
} else {
if (clientY + height > winHeight && clientY - height >= 0) {
if (clientY + height > containRect.btm && clientY - height >= 0) {
offsetY = -height;
}
}
if (offsetX !== 0) {
if (clientX + width < winWidth || clientX - width < 0) {
if (clientX + width < containRect.rgt || clientX - width < 0) {
offsetX = 0;
} else if (offsetX !== -width) {
offsetX = -width;
}
} else {
if (clientX + width > winWidth && clientX - width >= 0) {
if (clientX + width > containRect.rgt && clientX - width >= 0) {
offsetX = -width;
}
}