import { useLayoutEffect, useRef, useState } from 'react' import { useClickAway } from 'react-use' import useLockMainScroll from '@/web/hooks/useLockMainScroll' import useMeasure from 'react-use-measure' import { ContextMenuItem } from './types' import MenuPanel from './MenuPanel' import { createPortal } from 'react-dom' import { ContextMenuPosition } from './types' const BasicContextMenu = ({ onClose, items, target, cursorPosition, options, classNames, }: { onClose: (e: MouseEvent) => void items: ContextMenuItem[] target: HTMLElement cursorPosition: { x: number; y: number } options?: { useCursorPosition?: boolean fixedPosition?: `${'top' | 'bottom'}-${'left' | 'right'}` } | null classNames?: string }) => { const menuRef = useRef(null) const [measureRef, menu] = useMeasure() const [position, setPosition] = useState(null) useClickAway(menuRef, onClose) useLockMainScroll(!!position) useLayoutEffect(() => { if (options?.useCursorPosition) { const leftX = cursorPosition.x const rightX = cursorPosition.x - menu.width const bottomY = cursorPosition.y const topY = cursorPosition.y - menu.height const position = { x: leftX + menu.width < window.innerWidth ? leftX : rightX, y: bottomY + menu.height < window.innerHeight ? bottomY : topY, } setPosition(position) } else if (options?.fixedPosition) { const [vertical, horizontal] = options.fixedPosition.split('-') as [ 'top' | 'bottom', 'left' | 'right' ] const button = target.getBoundingClientRect() const leftX = button.x const rightX = button.x - menu.width + button.width const bottomY = button.y + button.height + 8 const topY = button.y - menu.height - 8 const position: ContextMenuPosition = { x: horizontal === 'left' ? leftX : rightX, y: vertical === 'bottom' ? bottomY : topY, transformOrigin: `origin-${options.fixedPosition}`, } setPosition(position) } else { const button = target.getBoundingClientRect() const leftX = button.x const rightX = button.x - menu.width + button.width const bottomY = button.y + button.height + 8 const topY = button.y - menu.height - 8 const position = { x: leftX + menu.width < window.innerWidth ? leftX : rightX, y: bottomY + menu.height < window.innerHeight ? bottomY : topY, } setPosition(position) } }, [target, menu, options?.useCursorPosition, cursorPosition]) return createPortal( <> { // }} forMeasure={true} classNames={classNames} /> {position && ( )} , document.body ) } export default BasicContextMenu