import * as React from 'react' import { MutableRefObject, useCallback, useEffect, useRef, useState, } from 'react' import { useEventListener, Portal, Menu, MenuButton, PortalProps, MenuButtonProps, MenuProps, } from '@chakra-ui/react' export interface ContextMenuProps { renderMenu: () => JSX.Element | null children: ( ref: MutableRefObject, isOpened: boolean ) => JSX.Element | null menuProps?: MenuProps portalProps?: PortalProps menuButtonProps?: MenuButtonProps isDisabled?: boolean } export function ContextMenu( props: ContextMenuProps ) { const [isOpened, setIsOpened] = useState(false) const [isRendered, setIsRendered] = useState(false) const [isDeferredOpen, setIsDeferredOpen] = useState(false) const [position, setPosition] = useState<[number, number]>([0, 0]) const targetRef = useRef(null) useEffect(() => { if (isOpened) { setTimeout(() => { setIsRendered(true) setTimeout(() => { setIsDeferredOpen(true) }) }) } else { setIsDeferredOpen(false) const timeout = setTimeout(() => { setIsRendered(isOpened) }, 1000) return () => clearTimeout(timeout) } }, [isOpened]) useEventListener( 'contextmenu', (e) => { if (props.isDisabled) return if (e.currentTarget === targetRef.current) { e.preventDefault() e.stopPropagation() setIsOpened(true) setPosition([e.pageX, e.pageY]) } else { setIsOpened(false) } }, targetRef.current ) const onCloseHandler = useCallback(() => { props.menuProps?.onClose?.() setIsOpened(false) // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.menuProps?.onClose, setIsOpened]) return ( <> {props.children(targetRef, isOpened)} {isRendered && ( {props.renderMenu()} )} ) }