Skip to content

Commit ac70e8d

Browse files
authored
fix: do not hide pdisk and vdisk popups if mouse on popup content (#1435)
1 parent 78cd713 commit ac70e8d

File tree

5 files changed

+56
-13
lines changed

5 files changed

+56
-13
lines changed

src/components/CellWithPopover/CellWithPopover.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ interface CellWithPopoverProps extends PopoverProps {
1111
wrapperClassName?: string;
1212
}
1313

14+
const DELAY_TIMEOUT = 100;
15+
1416
export function CellWithPopover({
1517
children,
1618
className,
@@ -19,7 +21,12 @@ export function CellWithPopover({
1921
}: CellWithPopoverProps) {
2022
return (
2123
<div className={b(null, wrapperClassName)}>
22-
<Popover className={b('popover', className)} {...props}>
24+
<Popover
25+
delayClosing={DELAY_TIMEOUT}
26+
delayOpening={DELAY_TIMEOUT}
27+
className={b('popover', className)}
28+
{...props}
29+
>
2330
{children}
2431
</Popover>
2532
</div>

src/components/PDiskPopup/PDiskPopup.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ export const PDiskPopup = ({data, ...props}: PDiskPopupProps) => {
7070
const nodeHost = valueIsDefined(data.NodeId) ? nodeHostsMap?.get(data.NodeId) : undefined;
7171
const info = React.useMemo(() => preparePDiskData(data, nodeHost), [data, nodeHost]);
7272

73+
const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false);
74+
const onMouseLeave = React.useCallback(() => {
75+
setIsPopupContentHovered(false);
76+
}, []);
77+
const onMouseEnter = React.useCallback(() => {
78+
setIsPopupContentHovered(true);
79+
}, []);
80+
7381
return (
7482
<Popup
7583
contentClassName={b()}
@@ -78,7 +86,10 @@ export const PDiskPopup = ({data, ...props}: PDiskPopupProps) => {
7886
// bigger offset for easier switching to neighbour nodes
7987
// matches the default offset for popup with arrow out of a sense of beauty
8088
offset={[0, 12]}
89+
onMouseLeave={onMouseLeave}
90+
onMouseEnter={onMouseEnter}
8191
{...props}
92+
open={isPopupContentHovered || props.open}
8293
>
8394
<InfoViewer title="PDisk" info={info} size="s" />
8495
</Popup>

src/components/VDisk/VDisk.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22

3+
import {debounce} from 'lodash';
4+
35
import {cn} from '../../utils/cn';
46
import type {PreparedVDisk} from '../../utils/disks/types';
57
import {DiskStateProgressBar} from '../DiskStateProgressBar/DiskStateProgressBar';
@@ -12,6 +14,8 @@ import './VDisk.scss';
1214

1315
const b = cn('ydb-vdisk-component');
1416

17+
const DEBOUNCE_TIMEOUT = 100;
18+
1519
export interface VDiskProps {
1620
data?: PreparedVDisk;
1721
compact?: boolean;
@@ -35,15 +39,15 @@ export const VDisk = ({
3539

3640
const anchor = React.useRef(null);
3741

38-
const handleShowPopup = () => {
42+
const debouncedHandleShowPopup = debounce(() => {
3943
setIsPopupVisible(true);
4044
onShowPopup?.();
41-
};
45+
}, DEBOUNCE_TIMEOUT);
4246

43-
const handleHidePopup = () => {
47+
const debouncedHandleHidePopup = debounce(() => {
4448
setIsPopupVisible(false);
4549
onHidePopup?.();
46-
};
50+
}, DEBOUNCE_TIMEOUT);
4751

4852
const vDiskPath = getVDiskLink(data);
4953

@@ -52,8 +56,11 @@ export const VDisk = ({
5256
<div
5357
className={b()}
5458
ref={anchor}
55-
onMouseEnter={handleShowPopup}
56-
onMouseLeave={handleHidePopup}
59+
onMouseEnter={debouncedHandleShowPopup}
60+
onMouseLeave={() => {
61+
debouncedHandleShowPopup.cancel();
62+
debouncedHandleHidePopup();
63+
}}
5764
>
5865
<InternalLink to={vDiskPath} className={b('content')}>
5966
<DiskStateProgressBar

src/components/VDiskPopup/VDiskPopup.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ interface VDiskPopupProps extends PopupProps {
137137
export const VDiskPopup = ({data, ...props}: VDiskPopupProps) => {
138138
const isFullData = isFullVDiskData(data);
139139

140+
const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false);
141+
const onMouseLeave = React.useCallback(() => {
142+
setIsPopupContentHovered(false);
143+
}, []);
144+
const onMouseEnter = React.useCallback(() => {
145+
setIsPopupContentHovered(true);
146+
}, []);
147+
140148
const vdiskInfo = React.useMemo(
141149
() => (isFullData ? prepareVDiskData(data) : prepareUnavailableVDiskData(data)),
142150
[data, isFullData],
@@ -181,7 +189,10 @@ export const VDiskPopup = ({data, ...props}: VDiskPopupProps) => {
181189
// bigger offset for easier switching to neighbour nodes
182190
// matches the default offset for popup with arrow out of a sense of beauty
183191
offset={[0, 12]}
192+
onMouseEnter={onMouseEnter}
193+
onMouseLeave={onMouseLeave}
184194
{...props}
195+
open={isPopupContentHovered || props.open}
185196
>
186197
{data.DonorMode && <Label className={b('donor-label')}>Donor</Label>}
187198
<InfoViewer title="VDisk" info={vdiskInfo} size="s" />

src/containers/Storage/PDisk/PDisk.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22

3+
import {debounce} from 'lodash';
4+
35
import {DiskStateProgressBar} from '../../../components/DiskStateProgressBar/DiskStateProgressBar';
46
import {InternalLink} from '../../../components/InternalLink';
57
import {PDiskPopup} from '../../../components/PDiskPopup/PDiskPopup';
@@ -16,6 +18,8 @@ import './PDisk.scss';
1618

1719
const b = cn('pdisk-storage');
1820

21+
const DEBOUNCE_TIMEOUT = 100;
22+
1923
interface PDiskProps {
2024
data?: PreparedPDisk;
2125
vDisks?: PreparedVDisk[];
@@ -44,15 +48,15 @@ export const PDisk = ({
4448
const {NodeId, PDiskId} = data;
4549
const pDiskIdsDefined = valueIsDefined(NodeId) && valueIsDefined(PDiskId);
4650

47-
const handleShowPopup = () => {
51+
const debouncedHandleShowPopup = debounce(() => {
4852
setIsPopupVisible(true);
4953
onShowPopup?.();
50-
};
54+
}, DEBOUNCE_TIMEOUT);
5155

52-
const handleHidePopup = () => {
56+
const debouncedHandleHidePopup = debounce(() => {
5357
setIsPopupVisible(false);
5458
onHidePopup?.();
55-
};
59+
}, DEBOUNCE_TIMEOUT);
5660

5761
const renderVDisks = () => {
5862
if (!vDisks?.length) {
@@ -101,8 +105,11 @@ export const PDisk = ({
101105
<InternalLink
102106
to={pDiskPath}
103107
className={b('content')}
104-
onMouseEnter={handleShowPopup}
105-
onMouseLeave={handleHidePopup}
108+
onMouseEnter={debouncedHandleShowPopup}
109+
onMouseLeave={() => {
110+
debouncedHandleShowPopup.cancel();
111+
debouncedHandleHidePopup();
112+
}}
106113
>
107114
<DiskStateProgressBar
108115
diskAllocatedPercent={data.AllocatedPercent}

0 commit comments

Comments
 (0)