TooltipContainer - use resize observer instead of getClientBoundingRect (#31937)

* TooltipContainer - use resize observer instead of getClientBoundingRect

* Well, let's screw Safari, shall we?

* Memoize resize observer

* Make ts happy happy happy
This commit is contained in:
Dominik Prokop
2021-03-15 18:08:43 +01:00
committed by GitHub
parent 50e5342379
commit b0b2ba0157
2 changed files with 42 additions and 4 deletions

View File

@@ -5,3 +5,10 @@ export interface CartesianCoords2D {
x: number;
y: number;
}
/**
* 2d object dimensions.
*/
export interface Dimensions2D {
width: number;
height: number;
}

View File

@@ -1,9 +1,9 @@
import React, { useState, useLayoutEffect, useRef, HTMLAttributes } from 'react';
import React, { useState, useLayoutEffect, useRef, HTMLAttributes, useMemo } from 'react';
import { stylesFactory } from '../../themes/stylesFactory';
import { css, cx } from 'emotion';
import { useTheme } from '../../themes/ThemeContext';
import useWindowSize from 'react-use/lib/useWindowSize';
import { GrafanaTheme } from '@grafana/data';
import { Dimensions2D, GrafanaTheme } from '@grafana/data';
interface TooltipContainerProps extends HTMLAttributes<HTMLDivElement> {
position: { x: number; y: number };
@@ -20,18 +20,49 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({
}) => {
const theme = useTheme();
const tooltipRef = useRef<HTMLDivElement>(null);
const tooltipMeasurementRef = useRef<Dimensions2D>({ width: 0, height: 0 });
const { width, height } = useWindowSize();
const [placement, setPlacement] = useState({
x: positionX + offsetX,
y: positionY + offsetY,
});
const resizeObserver = useMemo(
() =>
// TS has hard time playing games with @types/resize-observer-browser, hence the ignore
// @ts-ignore
new ResizeObserver((entries) => {
for (let entry of entries) {
const tW = Math.floor(entry.contentRect.width + 2 * 8); // adding padding until Safari supports borderBoxSize
const tH = Math.floor(entry.contentRect.height + 2 * 8);
if (tooltipMeasurementRef.current.width !== tW || tooltipMeasurementRef.current.height !== tH) {
tooltipMeasurementRef.current = {
width: tW,
height: tH,
};
}
}
}),
[]
);
useLayoutEffect(() => {
if (tooltipRef.current) {
resizeObserver.observe(tooltipRef.current);
}
return () => {
resizeObserver.disconnect();
};
}, [resizeObserver]);
// Make sure tooltip does not overflow window
useLayoutEffect(() => {
let xO = 0,
yO = 0;
if (tooltipRef && tooltipRef.current) {
const measurement = tooltipRef.current.getBoundingClientRect();
const measurement = tooltipMeasurementRef.current;
const xOverflow = width - (positionX + measurement.width);
const yOverflow = height - (positionY + measurement.height);
if (xOverflow < 0) {
@@ -47,7 +78,7 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({
x: positionX + offsetX - xO,
y: positionY + offsetY - yO,
});
}, [tooltipRef, width, height, positionX, offsetX, positionY, offsetY]);
}, [width, height, positionX, offsetX, positionY, offsetY]);
const styles = getTooltipContainerStyles(theme);