import { css, cx } from '@emotion/css' import { ForwardedRef, forwardRef, useLayoutEffect, useRef, useState, } from 'react' import { motion } from 'framer-motion' import MenuItem, { ContextMenuItem } from './MenuItem' interface PanelProps { position: { x: number y: number transformOrigin?: `origin-${'top' | 'bottom'}-${'left' | 'right'}` } items: ContextMenuItem[] onClose: (e: MouseEvent) => void forMeasure?: boolean classNames?: string isSubmenu?: boolean } interface SubmenuProps { itemRect: DOMRect index: number } const MenuPanel = forwardRef( ( { position, items, onClose, forMeasure, classNames, isSubmenu }: PanelProps, ref: ForwardedRef ) => { const [submenuProps, setSubmenuProps] = useState(null) return ( // Container (to add padding for submenus) {/* The real panel */}
{items.map((item, index) => ( setSubmenuProps(props)} onSubmenuClose={() => setSubmenuProps(null)} className={isSubmenu ? 'submenu' : ''} /> ))}
{/* Submenu */}
) } ) MenuPanel.displayName = 'Menu' export default MenuPanel const SubMenu = ({ items, itemRect, onClose, }: { items?: ContextMenuItem[] itemRect?: DOMRect onClose: (e: MouseEvent) => void }) => { const submenuRef = useRef(null) const [position, setPosition] = useState<{ x: number y: number transformOrigin: `origin-${'top' | 'bottom'}-${'left' | 'right'}` }>() useLayoutEffect(() => { if (!itemRect || !submenuRef.current) { return } const item = itemRect const submenu = submenuRef.current.getBoundingClientRect() const isRightSide = item.x + item.width + submenu.width <= window.innerWidth const x = isRightSide ? item.x + item.width : item.x - submenu.width const isTopSide = item.y - 10 + submenu.height <= window.innerHeight const y = isTopSide ? item.y - 10 : item.y + item.height + 10 - submenu.height const transformOriginTable = { top: { right: 'origin-top-left', left: 'origin-top-right', }, bottom: { right: 'origin-bottom-left', left: 'origin-bottom-right', }, } as const setPosition({ x, y, transformOrigin: transformOriginTable[isTopSide ? 'top' : 'bottom'][ isRightSide ? 'right' : 'left' ], }) }, [itemRect]) if (!items || !itemRect) { return <> } return ( <> { // Do nothing }} forMeasure={true} isSubmenu={true} /> ) }