import { useState , useRef , useEffect , useMemo } from "react" ; import ContextMenu from "./ContextMenu" ; interface Props extends React . HTMLAttributes < HTMLDivElement > { renderMenu : ( ) => React . ReactNode ; top ? : boolean ; bottom ? : boolean ; } function getNodeOffset ( node ) { if ( node == null ) { return { top : 0 , left : 0 } ; } const rect = node . getBoundingClientRect ( ) ; const scrollLeft = window . pageXOffset || document . documentElement ! . scrollLeft ; const scrollTop = window . pageYOffset || document . documentElement ! . scrollTop ; return { top : rect . top + scrollTop , left : rect . left + scrollLeft } ; } function ContextMenuTarget ( props ) { const handleNode = useRef < HTMLDivElement > ( null ) ; const [ selected , setSelected ] = useState ( false ) ; useEffect ( ( ) => { if ( ! selected ) { return ; } function handleGlobalClick ( e ) { if ( selected && e . target instanceof Element && selected && handleNode . current && ! handleNode . current . contains ( e . target ! ) ) { setSelected ( false ) ; } } document . addEventListener ( "click" , handleGlobalClick ) ; return ( ) => { document . removeEventListener ( "click" , handleGlobalClick ) ; } ; } , [ selected ] ) ; const offset = useMemo ( ( ) => { return selected ? getNodeOffset ( handleNode . current ) : { top : 0 , left : 0 } ; } , [ selected ] ) ; const { renderMenu , children , top , bottom , ... passThroughProps } = props ; return ( < div { ... passThroughProps } > < div className = "handle" style = { { width : "100%" , height : "100%" } } ref = { handleNode } onClick = { ( ) => setSelected ( ! selected ) } > { children } < /div> < ContextMenu selected = { selected } offsetTop = { offset . top } offsetLeft = { offset . left } top = { top } bottom = { bottom } > { renderMenu ( ) } < /ContextMenu> < /div>); } export default ContextMenuTarget ;