Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | import React, { JSX, ReactNode, useCallback, useEffect, useMemo, useState, } from "react"; import { MaybePromise } from "shared/types"; import { ContextMenu } from "ui/menu/ContextMenu"; const CLOSE_CONTEXT_MENUS_EVENT = "close-context-menus"; const closeAllContextMenus = () => { window.dispatchEvent(new Event(CLOSE_CONTEXT_MENUS_EVENT)); }; interface ContextMenuState { x: number; y: number; menu: JSX.Element[]; } interface UseContextMenuOptions { enabled?: boolean; getIsEnabled?: (e: React.MouseEvent) => boolean; getMenu: (args: { closeMenu: () => void; event: React.MouseEvent; }) => MaybePromise<JSX.Element[] | undefined>; } interface UseContextMenuResult { onContextMenu: (e: React.MouseEvent) => void; closeMenu: () => void; contextMenuElement: ReactNode; isOpen: boolean; } export const useContextMenu = ({ enabled = true, getIsEnabled, getMenu, }: UseContextMenuOptions): UseContextMenuResult => { const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null); const closeMenu = useCallback(() => { setContextMenu(null); }, []); useEffect(() => { window.addEventListener(CLOSE_CONTEXT_MENUS_EVENT, closeMenu); return () => { window.removeEventListener(CLOSE_CONTEXT_MENUS_EVENT, closeMenu); }; }, [closeMenu]); const onContextMenu = useCallback( async (e: React.MouseEvent) => { Iif (!enabled || (getIsEnabled !== undefined && !getIsEnabled(e))) { return; } e.preventDefault(); e.stopPropagation(); closeAllContextMenus(); const menu = await getMenu({ closeMenu, event: e }); Iif (!menu || menu.length === 0) { return; } setContextMenu({ x: e.pageX, y: e.pageY, menu, }); }, [enabled, getIsEnabled, getMenu, closeMenu], ); const contextMenuElement = useMemo(() => { Iif (!contextMenu) { return null; } return ( <ContextMenu x={contextMenu.x} y={contextMenu.y} onClose={closeMenu}> {contextMenu.menu} </ContextMenu> ); }, [contextMenu, closeMenu]); return { onContextMenu, closeMenu, contextMenuElement, isOpen: contextMenu !== null, }; }; |