diff --git a/src/components/CellWithPopover/CellWithPopover.tsx b/src/components/CellWithPopover/CellWithPopover.tsx index 272df0dd78..14529d8b42 100644 --- a/src/components/CellWithPopover/CellWithPopover.tsx +++ b/src/components/CellWithPopover/CellWithPopover.tsx @@ -11,6 +11,8 @@ interface CellWithPopoverProps extends PopoverProps { wrapperClassName?: string; } +const DELAY_TIMEOUT = 100; + export function CellWithPopover({ children, className, @@ -19,7 +21,12 @@ export function CellWithPopover({ }: CellWithPopoverProps) { return (
- + {children}
diff --git a/src/components/PDiskPopup/PDiskPopup.tsx b/src/components/PDiskPopup/PDiskPopup.tsx index c73f4e5a9c..0bca72c7f9 100644 --- a/src/components/PDiskPopup/PDiskPopup.tsx +++ b/src/components/PDiskPopup/PDiskPopup.tsx @@ -70,6 +70,14 @@ export const PDiskPopup = ({data, ...props}: PDiskPopupProps) => { const nodeHost = valueIsDefined(data.NodeId) ? nodeHostsMap?.get(data.NodeId) : undefined; const info = React.useMemo(() => preparePDiskData(data, nodeHost), [data, nodeHost]); + const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false); + const onMouseLeave = React.useCallback(() => { + setIsPopupContentHovered(false); + }, []); + const onMouseEnter = React.useCallback(() => { + setIsPopupContentHovered(true); + }, []); + return ( { // bigger offset for easier switching to neighbour nodes // matches the default offset for popup with arrow out of a sense of beauty offset={[0, 12]} + onMouseLeave={onMouseLeave} + onMouseEnter={onMouseEnter} {...props} + open={isPopupContentHovered || props.open} > diff --git a/src/components/VDisk/VDisk.tsx b/src/components/VDisk/VDisk.tsx index cc59cffb8a..c369719c67 100644 --- a/src/components/VDisk/VDisk.tsx +++ b/src/components/VDisk/VDisk.tsx @@ -1,5 +1,7 @@ import React from 'react'; +import {debounce} from 'lodash'; + import {cn} from '../../utils/cn'; import type {PreparedVDisk} from '../../utils/disks/types'; import {DiskStateProgressBar} from '../DiskStateProgressBar/DiskStateProgressBar'; @@ -12,6 +14,8 @@ import './VDisk.scss'; const b = cn('ydb-vdisk-component'); +const DEBOUNCE_TIMEOUT = 100; + export interface VDiskProps { data?: PreparedVDisk; compact?: boolean; @@ -35,15 +39,15 @@ export const VDisk = ({ const anchor = React.useRef(null); - const handleShowPopup = () => { + const debouncedHandleShowPopup = debounce(() => { setIsPopupVisible(true); onShowPopup?.(); - }; + }, DEBOUNCE_TIMEOUT); - const handleHidePopup = () => { + const debouncedHandleHidePopup = debounce(() => { setIsPopupVisible(false); onHidePopup?.(); - }; + }, DEBOUNCE_TIMEOUT); const vDiskPath = getVDiskLink(data); @@ -52,8 +56,11 @@ export const VDisk = ({
{ + debouncedHandleShowPopup.cancel(); + debouncedHandleHidePopup(); + }} > { const isFullData = isFullVDiskData(data); + const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false); + const onMouseLeave = React.useCallback(() => { + setIsPopupContentHovered(false); + }, []); + const onMouseEnter = React.useCallback(() => { + setIsPopupContentHovered(true); + }, []); + const vdiskInfo = React.useMemo( () => (isFullData ? prepareVDiskData(data) : prepareUnavailableVDiskData(data)), [data, isFullData], @@ -181,7 +189,10 @@ export const VDiskPopup = ({data, ...props}: VDiskPopupProps) => { // bigger offset for easier switching to neighbour nodes // matches the default offset for popup with arrow out of a sense of beauty offset={[0, 12]} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} {...props} + open={isPopupContentHovered || props.open} > {data.DonorMode && } diff --git a/src/containers/Storage/PDisk/PDisk.tsx b/src/containers/Storage/PDisk/PDisk.tsx index f0ff784ae0..a48162925a 100644 --- a/src/containers/Storage/PDisk/PDisk.tsx +++ b/src/containers/Storage/PDisk/PDisk.tsx @@ -1,5 +1,7 @@ import React from 'react'; +import {debounce} from 'lodash'; + import {DiskStateProgressBar} from '../../../components/DiskStateProgressBar/DiskStateProgressBar'; import {InternalLink} from '../../../components/InternalLink'; import {PDiskPopup} from '../../../components/PDiskPopup/PDiskPopup'; @@ -16,6 +18,8 @@ import './PDisk.scss'; const b = cn('pdisk-storage'); +const DEBOUNCE_TIMEOUT = 100; + interface PDiskProps { data?: PreparedPDisk; vDisks?: PreparedVDisk[]; @@ -44,15 +48,15 @@ export const PDisk = ({ const {NodeId, PDiskId} = data; const pDiskIdsDefined = valueIsDefined(NodeId) && valueIsDefined(PDiskId); - const handleShowPopup = () => { + const debouncedHandleShowPopup = debounce(() => { setIsPopupVisible(true); onShowPopup?.(); - }; + }, DEBOUNCE_TIMEOUT); - const handleHidePopup = () => { + const debouncedHandleHidePopup = debounce(() => { setIsPopupVisible(false); onHidePopup?.(); - }; + }, DEBOUNCE_TIMEOUT); const renderVDisks = () => { if (!vDisks?.length) { @@ -101,8 +105,11 @@ export const PDisk = ({ { + debouncedHandleShowPopup.cancel(); + debouncedHandleHidePopup(); + }} >