mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -5,3 +5,10 @@ export interface CartesianCoords2D {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
/**
|
||||
* 2d object dimensions.
|
||||
*/
|
||||
export interface Dimensions2D {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user