diff --git a/js/components/ContextMenuWrapper.tsx b/js/components/ContextMenuWrapper.tsx index db187b58..774c95ba 100644 --- a/js/components/ContextMenuWrapper.tsx +++ b/js/components/ContextMenuWrapper.tsx @@ -1,8 +1,9 @@ -import React, { ReactNode } from "react"; +import React, { ReactNode, useState, useEffect, useCallback } from "react"; import ContextMenu from "./ContextMenu"; interface Props { renderContents(): ReactNode; + children: ReactNode; } interface State { @@ -11,74 +12,71 @@ interface State { offsetLeft: number | null; } -export default class ContextMenuWraper extends React.Component { - constructor(props: Props) { - super(props); - this.state = { - selected: false, - offsetTop: null, - offsetLeft: null, - }; - } +// TODO: Consider using nested contexts to ensure we don't ever have multiple +// non-nested context menus open at a time. +export default function ContextMenuWraper({ + children, + renderContents, + ...passThroughProps +}: Props) { + const [openPosition, setOpenPosition] = useState<{ + x: number; + y: number; + } | null>(null); - componentWillUnmount() { - this._closeMenu(); - } + const closeMenu = useCallback(() => { + setOpenPosition(null); + }, []); - _closeMenu() { - this.setState({ selected: false, offsetTop: null, offsetLeft: null }); - document.removeEventListener("click", this._handleGlobalClick); - document.body.removeEventListener( - "contextmenu", - this._handleGlobalRightClick - ); - } + const handleGlobalClick = useCallback( + (e: MouseEvent) => { + if (e.button !== 2) { + closeMenu(); + } + }, + [closeMenu] + ); - _handleGlobalRightClick = () => { - this._closeMenu(); - }; - - _handleGlobalClick = (e: MouseEvent) => { - if (e.button === 2) { - return; - } - this._closeMenu(); - }; - - _handleRightClick = (e: React.MouseEvent) => { - const { pageX, pageY } = e; - this.setState({ - selected: true, + const handleRightClick = useCallback( + (e: React.MouseEvent) => { + const { pageX, pageY } = e; // TODO: We could do an initial render to see if the menu fits here // and do a second render if it does not. - offsetTop: pageY, - offsetLeft: pageX, - }); - // Even if you right click multiple times before closeing, - // we should only end up with one global listener. - document.addEventListener("click", this._handleGlobalClick); - document.body.addEventListener("contextmenu", this._handleGlobalRightClick); - e.preventDefault(); - e.stopPropagation(); - }; + setOpenPosition({ x: pageX, y: pageY }); + e.preventDefault(); + e.stopPropagation(); + }, + [] + ); - render() { - const { children, renderContents, ...passThroughProps } = this.props; - return ( -
{ + if (openPosition == null) { + return; + } + document.addEventListener("click", handleGlobalClick); + document.body.addEventListener("contextmenu", closeMenu); + + return () => { + document.removeEventListener("click", handleGlobalClick); + document.body.removeEventListener("contextmenu", closeMenu); + }; + }, [openPosition, closeMenu, handleGlobalClick]); + + return ( +
+ - - {renderContents()} - - {children} -
- ); - } + {renderContents()} + + {children} +
+ ); }