webview/src/shared/components/contextMenu/ContextMenu.tsx
import React from 'react'
import { Instance } from 'tippy.js'
import Tooltip from '../tooltip/Tooltip'
const positionContextMenuAndDisableEvents = (
instance: Instance,
event: PointerEvent
) => {
event.preventDefault()
instance.setProps({
getReferenceClientRect() {
const { top, bottom, height } = instance.reference.getBoundingClientRect()
return {
bottom,
height,
left: event.clientX,
right: event.clientX,
top,
width: 0
} as DOMRect
}
})
const handleDefaultEvent = (event: Event) => {
event.preventDefault()
}
instance.reference.removeEventListener('contextmenu', handleDefaultEvent)
instance.reference.addEventListener('contextmenu', handleDefaultEvent)
const hideOnClick = (event: Event) => {
if (
event.target instanceof HTMLElement &&
!event.target.closest('[aria-disabled=true]')
) {
!instance.state.isDestroyed && instance.hide()
}
}
instance.popper.removeEventListener('click', hideOnClick)
instance.popper.addEventListener('click', hideOnClick)
window.addEventListener('click', hideOnClick, { once: true })
}
export interface ContextMenuProps {
children: React.ReactElement
content?: React.ReactNode
disabled?: boolean
onShow?: () => void
onHide?: () => void
trigger?: string
}
export const ContextMenu: React.FC<ContextMenuProps> = ({
children,
content,
disabled,
onShow,
onHide,
trigger = 'contextmenu'
}) => (
<Tooltip
trigger={trigger}
delay={[100, 200]}
placement="bottom-start"
interactive
isContextMenu={true}
onTrigger={positionContextMenuAndDisableEvents}
onClickOutside={(instance: Instance) => instance.hide()}
hideOnClick={false}
content={content}
onShow={onShow}
onHide={onHide}
disabled={!content || disabled}
appendTo="parent"
followCursor="initial"
offset={[0, 0]}
>
{children}
</Tooltip>
)