Skip to content

Commit a0bb878

Browse files
ui: Fixed flickering tooltip issues by upgrading the lib to the latest (#5738)
* Fixced flickering tooltip issues by upgrading the lib to the latest * [pre-commit.ci lite] apply automatic fixes * Linter fix --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent a9985f2 commit a0bb878

File tree

3 files changed

+62
-80
lines changed

3 files changed

+62
-80
lines changed

ui/packages/shared/profile/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.19.5",
44
"description": "Profile viewing libraries",
55
"dependencies": {
6+
"@floating-ui/react": "^0.27.12",
67
"@headlessui/react": "^1.7.19",
78
"@iconify/react": "^4.0.0",
89
"@parca/client": "workspace:*",

ui/packages/shared/profile/src/GraphTooltipArrow/index.tsx

Lines changed: 44 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,114 +13,78 @@
1313

1414
import React, {useEffect, useState} from 'react';
1515

16+
import {flip, offset, shift, useFloating, type VirtualElement} from '@floating-ui/react';
1617
import {pointer} from 'd3-selection';
17-
import {usePopper} from 'react-popper';
1818

1919
interface GraphTooltipProps {
2020
children: React.ReactNode;
21-
x?: number;
22-
y?: number;
2321
contextElement: Element | null;
24-
isFixed?: boolean;
25-
virtualContextElement?: boolean;
26-
isContextMenuOpen?: boolean;
2722
}
2823

29-
const virtualElement = {
30-
getBoundingClientRect: () =>
31-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
32-
({
33-
width: 0,
34-
height: 0,
35-
top: 0,
36-
left: 0,
37-
right: 0,
38-
bottom: 0,
39-
} as DOMRect),
40-
};
41-
42-
function generateGetBoundingClientRect(contextElement: Element, x = 0, y = 0): () => DOMRect {
24+
function createPositionedVirtualElement(contextElement: Element, x = 0, y = 0): VirtualElement {
4325
const domRect = contextElement.getBoundingClientRect();
44-
return () =>
45-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
46-
({
26+
return {
27+
getBoundingClientRect: () => ({
4728
width: 0,
4829
height: 0,
4930
top: domRect.y + y,
5031
left: domRect.x + x,
5132
right: domRect.x + x,
5233
bottom: domRect.y + y,
53-
} as DOMRect);
34+
x: domRect.x + x,
35+
y: domRect.y + y,
36+
toJSON: () => ({}),
37+
}),
38+
};
5439
}
5540

56-
const GraphTooltip = ({
57-
children,
58-
x,
59-
y,
60-
contextElement,
61-
isFixed = false,
62-
virtualContextElement = true,
63-
isContextMenuOpen = false,
64-
}: GraphTooltipProps): React.JSX.Element => {
65-
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
41+
const GraphTooltip = ({children, contextElement}: GraphTooltipProps): React.JSX.Element => {
42+
const [isPositioned, setIsPositioned] = useState(false);
6643

67-
const {styles, attributes, ...popperProps} = usePopper(
68-
virtualContextElement ? virtualElement : contextElement,
69-
popperElement,
70-
{
71-
placement: 'bottom-start',
72-
strategy: 'absolute',
73-
modifiers: [
74-
{
75-
name: 'preventOverflow',
76-
options: {
77-
tether: false,
78-
altAxis: true,
79-
},
80-
},
81-
{
82-
name: 'offset',
83-
options: {
84-
offset: [30, 30],
85-
},
86-
},
87-
],
88-
}
89-
);
44+
const {refs, floatingStyles, update} = useFloating({
45+
placement: 'bottom-start',
46+
strategy: 'absolute',
47+
middleware: [
48+
offset({
49+
mainAxis: 30,
50+
crossAxis: 30,
51+
}),
52+
flip(),
53+
shift({
54+
padding: 5,
55+
}),
56+
],
57+
whileElementsMounted: undefined,
58+
});
9059

9160
useEffect(() => {
9261
if (contextElement === null) return;
93-
const onMouseMove: EventListenerOrEventListenerObject = (e: Event) => {
94-
if (isContextMenuOpen) {
95-
return;
96-
}
9762

98-
let tooltipX = x;
99-
let tooltipY = y;
100-
if (tooltipX == null || tooltipY == null) {
101-
const rel = pointer(e);
102-
tooltipX = rel[0];
103-
tooltipY = rel[1];
104-
}
105-
virtualElement.getBoundingClientRect = generateGetBoundingClientRect(
106-
contextElement,
107-
tooltipX,
108-
tooltipY
109-
);
110-
111-
void popperProps.update?.();
63+
const onMouseMove: EventListenerOrEventListenerObject = (e: Event) => {
64+
const rel = pointer(e);
65+
const tooltipX = rel[0];
66+
const tooltipY = rel[1];
67+
const virtualElement = createPositionedVirtualElement(contextElement, tooltipX, tooltipY);
68+
refs.setReference(virtualElement);
69+
setIsPositioned(true);
70+
update();
11271
};
11372

11473
contextElement.addEventListener('mousemove', onMouseMove);
11574
return () => {
11675
contextElement.removeEventListener('mousemove', onMouseMove);
11776
};
118-
}, [contextElement, popperProps, x, y, isContextMenuOpen]);
77+
}, [contextElement, update, refs]);
11978

120-
return isFixed ? (
121-
<>{children}</>
122-
) : (
123-
<div ref={setPopperElement} style={styles.popper} {...attributes.popper} className="z-50">
79+
return (
80+
<div
81+
ref={refs.setFloating}
82+
style={{
83+
...floatingStyles,
84+
visibility: !isPositioned ? 'hidden' : 'visible',
85+
}}
86+
className="z-50 w-max"
87+
>
12488
{children}
12589
</div>
12690
);

ui/pnpm-lock.yaml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)