import { Box, Menu } from "@mantine/core"; import { createContext, useEffect, useState } from "react"; interface ContextMenuProviderContextType { openContextMenu: ( items : ContextMenuItem[], noRenderStandardItems?: boolean, noRenderDisabledItems?: boolean) => void; } export const ContextMenuContext = createContext(null); interface ContextMenuProviderProps { children: React.ReactNode; } export interface ContextMenuItem { label: string; action: () => void; icon: React.ReactNode; cond?: () => boolean | Promise; __reserved_prerender_condition?: boolean; } const standardMenuItems: ContextMenuItem[] = []; const animationDelay = 40; export function ContextMenuProvider(props : ContextMenuProviderProps) { const [coords, setCoords] = useState({ x: 0, y: 0 }); const [open, setOpen] = useState(false); const [items, setItems] = useState([]); const [noRenderStandardItems, setNoRenderStandardItems] = useState(false); const [standardItemsReady, setStandardItemsReady] = useState([]); useEffect(() => { document.removeEventListener('contextmenu', contextMenuHandler); document.removeEventListener('click', clickHandler); document.addEventListener('contextmenu',contextMenuHandler); document.addEventListener('click', clickHandler); return () => { document.removeEventListener('contextmenu', contextMenuHandler); document.removeEventListener('click', clickHandler); } }, [open]); useEffect(() => { (async () => { setStandardItemsReady(await translateConditionsToReservedField(standardMenuItems)); })(); }, [open]); const contextMenuHandler = (event) => { event.preventDefault(); setOpen(true); setCoords({ x: event.clientX, y: event.clientY }); } const clickHandler = () => { if(open){ setOpen(false); setTimeout(() => { /** * Ждем завершения анимации */ setItems([]); }, animationDelay); } } const translateConditionsToReservedField = async (fromItems : ContextMenuItem[], noRenderDisabledItems: boolean = false) : Promise => { const newItems: ContextMenuItem[] = []; for(const item of fromItems){ if(!item.cond){ newItems.push({ ...item, __reserved_prerender_condition: true }); continue; } const condResult = await item.cond(); if(!condResult && noRenderDisabledItems){ continue; } newItems.push({ ...item, __reserved_prerender_condition: condResult }); } return newItems; } const openContextMenu = async ( items : ContextMenuItem[], noRenderStandardItems: boolean = false, noRenderDisabledItems: boolean = false ) => { setItems(await translateConditionsToReservedField(items, noRenderDisabledItems)); setNoRenderStandardItems(noRenderStandardItems); setOpen(true); } return ( <> {standardItemsReady.length > 0 || items.length > 0 && ( window.innerHeight - (items.concat(standardMenuItems).length * 45) ? (window.innerHeight - (items.concat(standardMenuItems).length * 45)) + 'px' : coords.y + 'px', left: coords.x > window.innerWidth - 210 ? (window.innerWidth - 210) + 'px' : coords.x + 'px', }}> window.innerHeight - (items.concat(standardMenuItems).length * 45) ? (window.innerHeight - (items.concat(standardMenuItems).length * 45)) + 'px' : coords.y + 'px', left: coords.x > window.innerWidth - 210 ? (window.innerWidth - 210) + 'px' : coords.x + 'px', } }} width={150}> {items.map((item, index) => ( { item.action(); setOpen(false); }}> {item.label} ))} {items.length > 0 && !noRenderStandardItems && standardMenuItems.length > 0 && } {!noRenderStandardItems && standardItemsReady.map((item, index) => ( { item.action(); setOpen(false); }}> {item.label} ))} )} {props.children} ); }