mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05: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;
|
x: number;
|
||||||
y: 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 { stylesFactory } from '../../themes/stylesFactory';
|
||||||
import { css, cx } from 'emotion';
|
import { css, cx } from 'emotion';
|
||||||
import { useTheme } from '../../themes/ThemeContext';
|
import { useTheme } from '../../themes/ThemeContext';
|
||||||
import useWindowSize from 'react-use/lib/useWindowSize';
|
import useWindowSize from 'react-use/lib/useWindowSize';
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { Dimensions2D, GrafanaTheme } from '@grafana/data';
|
||||||
|
|
||||||
interface TooltipContainerProps extends HTMLAttributes<HTMLDivElement> {
|
interface TooltipContainerProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
position: { x: number; y: number };
|
position: { x: number; y: number };
|
||||||
@@ -20,18 +20,49 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
const tooltipRef = useRef<HTMLDivElement>(null);
|
||||||
|
const tooltipMeasurementRef = useRef<Dimensions2D>({ width: 0, height: 0 });
|
||||||
const { width, height } = useWindowSize();
|
const { width, height } = useWindowSize();
|
||||||
const [placement, setPlacement] = useState({
|
const [placement, setPlacement] = useState({
|
||||||
x: positionX + offsetX,
|
x: positionX + offsetX,
|
||||||
y: positionY + offsetY,
|
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
|
// Make sure tooltip does not overflow window
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
let xO = 0,
|
let xO = 0,
|
||||||
yO = 0;
|
yO = 0;
|
||||||
if (tooltipRef && tooltipRef.current) {
|
if (tooltipRef && tooltipRef.current) {
|
||||||
const measurement = tooltipRef.current.getBoundingClientRect();
|
const measurement = tooltipMeasurementRef.current;
|
||||||
const xOverflow = width - (positionX + measurement.width);
|
const xOverflow = width - (positionX + measurement.width);
|
||||||
const yOverflow = height - (positionY + measurement.height);
|
const yOverflow = height - (positionY + measurement.height);
|
||||||
if (xOverflow < 0) {
|
if (xOverflow < 0) {
|
||||||
@@ -47,7 +78,7 @@ export const TooltipContainer: React.FC<TooltipContainerProps> = ({
|
|||||||
x: positionX + offsetX - xO,
|
x: positionX + offsetX - xO,
|
||||||
y: positionY + offsetY - yO,
|
y: positionY + offsetY - yO,
|
||||||
});
|
});
|
||||||
}, [tooltipRef, width, height, positionX, offsetX, positionY, offsetY]);
|
}, [width, height, positionX, offsetX, positionY, offsetY]);
|
||||||
|
|
||||||
const styles = getTooltipContainerStyles(theme);
|
const styles = getTooltipContainerStyles(theme);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user