Skip to content

Commit a2064ab

Browse files
committed
Fix menu hover delays
1 parent 0608c02 commit a2064ab

1 file changed

Lines changed: 46 additions & 22 deletions

File tree

src/components/BorderedApp/BorderedAppMenu/BorderedAppMenu.tsx

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,64 @@ function BorderedAppMenuItem({
2323
itemNo: number;
2424
position: { x: number; y: number };
2525
}) {
26+
const hoverDelayMs = 400;
2627
const elementRef = useRef<HTMLDivElement>(null);
2728
const elementPosition = useRef({ ...position });
28-
const hoverRevealDelayRef = useRef<NodeJS.Timeout>();
29+
const hoverOpenDelayRef = useRef<NodeJS.Timeout>();
30+
const hoverCloseDelayRef = useRef<NodeJS.Timeout>();
31+
const [open, setOpen] = useState<boolean>(false);
2932

3033
useEffect(() => {
31-
const rect = elementRef.current?.getBoundingClientRect();
34+
return () => {
35+
clearTimeout(hoverCloseDelayRef.current);
36+
clearTimeout(hoverOpenDelayRef.current);
37+
};
38+
}, []);
3239

33-
// Since we're adding to the ref value, we first check if it's
34-
// still it's initial value. This avoids incorrectly adding
35-
// multiple times when the effect fires multiple times.
36-
if (elementPosition.current.x === position.x) {
37-
elementPosition.current.x = rect?.width ?? 0;
38-
}
39-
if (elementPosition.current.y === position.y) {
40-
elementPosition.current.y = (rect?.height ?? 0) * (itemNo - 1);
41-
}
40+
useEffect(() => {
41+
const rect = elementRef.current?.getBoundingClientRect();
42+
elementPosition.current.x = rect?.width ?? 0;
43+
elementPosition.current.y = (rect?.height ?? 0) * (itemNo - 1);
4244
}, [elementRef, itemNo, position]);
43-
const [open, setOpen] = useState<boolean>(false);
4445

4546
function handleOnMouseEnter() {
46-
if (!open) {
47-
hoverRevealDelayRef.current = setTimeout(() => {
48-
if (!open) setOpen(true);
49-
}, 400);
47+
// Now that we've entered the item, if it's open
48+
// and there's a pending timeout to close it,
49+
// we want to cancel that
50+
if (open && hoverCloseDelayRef.current) {
51+
clearTimeout(hoverCloseDelayRef.current);
52+
hoverCloseDelayRef.current = undefined;
53+
}
54+
55+
// If it's not open yet and there's no pending
56+
// timeout to open it, lets start one.
57+
if (!open && !hoverOpenDelayRef.current) {
58+
hoverOpenDelayRef.current = setTimeout(() => {
59+
if (!open) {
60+
setOpen(true);
61+
hoverOpenDelayRef.current = undefined;
62+
}
63+
}, hoverDelayMs);
5064
}
5165
}
5266
function handleOnMouseLeave() {
53-
if (hoverRevealDelayRef.current) {
54-
clearTimeout(hoverRevealDelayRef.current);
67+
// Now that we've left the item, if it's closed
68+
// and there's a pending timeout to open it,
69+
// we want to cancel that
70+
if (!open && hoverOpenDelayRef.current) {
71+
clearTimeout(hoverOpenDelayRef.current);
72+
hoverOpenDelayRef.current = undefined;
5573
}
56-
if (open) {
57-
hoverRevealDelayRef.current = setTimeout(() => {
58-
if (open) setOpen(false);
59-
});
74+
75+
// If it's not closed yet and there's no pending
76+
// timeout to close it, lets start one.
77+
if (open && !hoverCloseDelayRef.current) {
78+
hoverCloseDelayRef.current = setTimeout(() => {
79+
if (open) {
80+
setOpen(false);
81+
hoverCloseDelayRef.current = undefined;
82+
}
83+
}, hoverDelayMs);
6084
}
6185
}
6286

0 commit comments

Comments
 (0)