import React, { useEffect, useRef } from 'react' import type { ContextMenuProps } from './contextmenu' import { classNames } from '../../utils/classNames' import { on } from '../../utils/DOMUtils' import styles from './contextmenu.module.scss' export type Props = Omit & { Element?: React.ElementType children: React.ReactNode context: React.ReactNode } const ContextMenu = ({ Element = 'div', className, children, context, ...rest }: Props) => { const ctx = useRef(null) const content = useRef(null) const classes = classNames([ styles.ctx, className ]) const showContext = (event: MouseEvent) => { event.preventDefault() if (content.current) { const target = event.currentTarget as HTMLElement const rect = target.getBoundingClientRect() const x = event.clientX - rect.left const y = event.clientY - rect.top content.current.style.top = `${y}px` content.current.style.left = `${x}px` content.current.dataset.show = 'true' } } const hideContext = (event: MouseEvent) => { if (content.current) { if (content.current.contains(event.target as Node)) { return } content.current.dataset.show = 'false' setTimeout(() => { if (content.current) { content.current.style.top = '' content.current.style.left = '' } }, 200) } } useEffect(() => { if (ctx.current) { on(ctx.current, 'contextmenu', showContext) on(document, 'click', hideContext) } return () => { ctx.current?.removeEventListener('contextmenu', showContext) document.removeEventListener('click', hideContext) } }, []) if (!context) { // eslint-disable-next-line no-console console.error('Missing `context` prop. Add `context={...}` to your component.') } return ( {children}
event.stopPropagation()}> {context}
) } export default ContextMenu