54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
export interface TooltipPositionInput {
|
|
/** Cursor x in viewport coordinates. */
|
|
cx: number;
|
|
/** Cursor y in viewport coordinates. */
|
|
cy: number;
|
|
/** Measured tooltip width. */
|
|
tipW: number;
|
|
/** Measured tooltip height. */
|
|
tipH: number;
|
|
/** Gap between the cursor and the tooltip. */
|
|
offset: number;
|
|
/** Viewport width (window.innerWidth). */
|
|
viewportW: number;
|
|
/** Viewport height (window.innerHeight). */
|
|
viewportH: number;
|
|
}
|
|
|
|
/**
|
|
* Computes the fixed-position coordinates for the chart tooltip, flipping it to
|
|
* the opposite side of the cursor when it would overflow the viewport and
|
|
* clamping it to an 8px margin. The result is rounded to whole pixels so that
|
|
* sub-pixel jitter from `getBoundingClientRect` collapses to the same
|
|
* coordinate. (The effect's dependency array in chart.tsx is what actually
|
|
* stops the render loop; rounding only reduces how often the equality guard is
|
|
* defeated by a fractional difference.)
|
|
*/
|
|
export function computeTooltipPosition({
|
|
cx,
|
|
cy,
|
|
tipW,
|
|
tipH,
|
|
offset,
|
|
viewportW,
|
|
viewportH,
|
|
}: TooltipPositionInput): { x: number; y: number } {
|
|
let x = cx + offset;
|
|
let y = cy + offset;
|
|
|
|
if (x + tipW > viewportW - 8) {
|
|
x = cx - tipW - offset;
|
|
}
|
|
if (y + tipH > viewportH - 8) {
|
|
y = cy - tipH - offset;
|
|
}
|
|
if (x < 8) {
|
|
x = 8;
|
|
}
|
|
if (y < 8) {
|
|
y = 8;
|
|
}
|
|
|
|
return { x: Math.round(x), y: Math.round(y) };
|
|
}
|