-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: popup closes on context menu copy #1453
Changes from all commits
6ebd399
991db52
f5aa709
8d376e2
64d8b66
f5f3a4b
0a7d79e
79d1fcc
c6cdae4
f2a330e
23dbd4c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.hover-popup { | ||
padding: var(--g-spacing-3); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import React from 'react'; | ||
|
||
import {Popup} from '@gravity-ui/uikit'; | ||
import debounce from 'lodash/debounce'; | ||
|
||
import {cn} from '../../utils/cn'; | ||
|
||
import './HoverPopup.scss'; | ||
|
||
const b = cn('hover-popup'); | ||
|
||
const DEBOUNCE_TIMEOUT = 100; | ||
|
||
interface HoverPopupProps { | ||
children: React.ReactNode; | ||
popupContent: React.ReactNode; | ||
showPopup?: boolean; | ||
offset?: [number, number]; | ||
anchorRef?: React.RefObject<HTMLElement>; | ||
onShowPopup?: VoidFunction; | ||
onHidePopup?: VoidFunction; | ||
} | ||
|
||
export const HoverPopup = ({ | ||
children, | ||
popupContent, | ||
showPopup, | ||
offset, | ||
anchorRef, | ||
onShowPopup, | ||
onHidePopup, | ||
}: HoverPopupProps) => { | ||
const [isPopupVisible, setIsPopupVisible] = React.useState(false); | ||
const anchor = React.useRef<HTMLDivElement>(null); | ||
|
||
const debouncedHandleShowPopup = React.useMemo( | ||
() => | ||
debounce(() => { | ||
setIsPopupVisible(true); | ||
onShowPopup?.(); | ||
}, DEBOUNCE_TIMEOUT), | ||
[onShowPopup], | ||
); | ||
|
||
const hidePopup = React.useCallback(() => { | ||
setIsPopupVisible(false); | ||
onHidePopup?.(); | ||
}, [onHidePopup]); | ||
|
||
const debouncedHandleHidePopup = React.useMemo( | ||
() => debounce(hidePopup, DEBOUNCE_TIMEOUT), | ||
[hidePopup], | ||
); | ||
|
||
const onMouseEnter = debouncedHandleShowPopup; | ||
|
||
const onMouseLeave = () => { | ||
debouncedHandleShowPopup.cancel(); | ||
debouncedHandleHidePopup(); | ||
}; | ||
|
||
const [isPopupContentHovered, setIsPopupContentHovered] = React.useState(false); | ||
const [isFocused, setIsFocused] = React.useState(false); | ||
|
||
const onPopupMouseEnter = React.useCallback(() => { | ||
setIsPopupContentHovered(true); | ||
}, []); | ||
|
||
const onPopupMouseLeave = React.useCallback(() => { | ||
setIsPopupContentHovered(false); | ||
}, []); | ||
|
||
const onPopupContextMenu = React.useCallback(() => { | ||
setIsFocused(true); | ||
}, []); | ||
|
||
const onPopupBlur = React.useCallback(() => { | ||
setIsFocused(false); | ||
}, []); | ||
|
||
const onPopupEscapeKeyDown = React.useCallback(() => { | ||
setIsFocused(false); | ||
setIsPopupContentHovered(false); | ||
hidePopup(); | ||
}, [hidePopup]); | ||
|
||
const open = isPopupVisible || showPopup || isPopupContentHovered || isFocused; | ||
|
||
return ( | ||
<React.Fragment> | ||
<div ref={anchor} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}> | ||
{children} | ||
</div> | ||
<Popup | ||
contentClassName={b()} | ||
anchorRef={anchorRef || anchor} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we allow to pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We allow to pass anchorRef for custom anchors if they are needed |
||
open={open} | ||
onMouseEnter={onPopupMouseEnter} | ||
onMouseLeave={onPopupMouseLeave} | ||
onEscapeKeyDown={onPopupEscapeKeyDown} | ||
onBlur={onPopupBlur} | ||
placement={['top', 'bottom']} | ||
hasArrow | ||
// bigger offset for easier switching to neighbour nodes | ||
// matches the default offset for popup with arrow out of a sense of beauty | ||
offset={offset || [0, 12]} | ||
> | ||
<div onContextMenu={onPopupContextMenu}>{popupContent}</div> | ||
</Popup> | ||
</React.Fragment> | ||
); | ||
}; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,4 @@ | ||
.vdisk-storage-popup { | ||
padding: 12px; | ||
|
||
.info-viewer + .info-viewer { | ||
margin-top: 8px; | ||
padding-top: 8px; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need all these
useCallback
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
because we pass this function as prop to external component which may use this function as prop