UNPKG

@ihatecode/react-context-menu

Version:

A context menu component written in React.

1 lines 4.02 kB
"use strict";Object.defineProperty(exports,"__esModule",{value:true});const React=require("react");const ReactDOM=require("react-dom");const classnames_1=require("classnames");const ContextMenu=props=>{const containerRef=React.useRef(null);const[position,setPosition]=React.useState({top:0,left:0});React.useEffect((()=>{const containerWidth=containerRef.current?.offsetWidth||0;const containerHeight=containerRef.current?.offsetHeight||0;const positionY=props.contextMenu?.position.y===undefined?-9999:props.contextMenu?.position.y;const positionX=props.contextMenu?.position.x===undefined?-9999:props.contextMenu?.position.x;setPosition({top:positionY+containerHeight>window.innerHeight?positionY-containerHeight:positionY,left:positionX+containerWidth>window.innerWidth?positionX-containerWidth:positionX})}),[containerRef.current,props.contextMenu.visible,props.contextMenu?.position.x,props.contextMenu?.position.y]);const eventPreprocess=e=>{e.preventDefault();e.stopPropagation()};const handleMouseEnter=(e,item)=>{eventPreprocess(e);if(item.disabled||!item.children?.length)return;const rect=e.currentTarget.getBoundingClientRect();const subMenu=e.currentTarget.lastElementChild;const subMenuWidth=subMenu?.clientWidth||0;const subMenuHeight=subMenu?.clientHeight||0;const x=window.innerWidth-rect.right>subMenuWidth?"left:calc(100% - 8px)":`right:calc(100% - 8px)`;const y=window.innerHeight-rect.bottom>subMenuHeight?"top:-8px":`bottom:${-(window.innerHeight-rect.bottom-8)}px`;e.currentTarget.lastElementChild?.setAttribute("style",`${x};${y}`)};const handleMouseLeave=e=>{eventPreprocess(e);e.currentTarget.lastElementChild?.removeAttribute("style")};const handleClick=(e,item)=>{eventPreprocess(e);if(item.disabled||item.children?.length)return;props.onClick?.(item.key);props.contextMenu.onClose()};const renderItems=React.useCallback(((items,withIcon)=>{const hasIcon=withIcon||items.flat().some((item=>item.icon!==undefined));return items.map((item=>{if(Array.isArray(item)){const key=item.map((i=>i.key)).join("-");return item.length>0?React.createElement("div",{className:"ihc-context-menu-group",key:key},renderItems(item,hasIcon)):null}const subItems=item.children||[];return React.createElement("div",{key:item.key,className:(0,classnames_1.default)("ihc-context-menu-item",{"ihc-item-disabled":item.disabled}),onMouseEnter:e=>handleMouseEnter(e,item),onMouseLeave:handleMouseLeave,onClick:e=>handleClick(e,item)},hasIcon&&React.createElement("div",{className:"ihc-menu-item-icon"},item.icon),React.createElement("div",{className:"ihc-menu-item-label"},item.label),React.createElement("div",{className:"ihc-menu-item-icon-right"},subItems.length>0&&React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"1em",height:"1em",viewBox:"0 0 24 24"},React.createElement("path",{fill:"none",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"1.5",d:"m10 17l5-5l-5-5"}))),subItems.length>0&&React.createElement("div",{className:"ihc-sub-context-menu"},renderItems(subItems)))}))}),[]);const style={position:"fixed",zIndex:props.zIndex,...position};return ReactDOM.createPortal(React.createElement("div",{ref:containerRef,style:style,className:(0,classnames_1.default)("ihc-context-menu",{"ihc-visible":props.contextMenu.visible},props.className),onClick:eventPreprocess,onContextMenu:eventPreprocess},renderItems(props.items)),document.body)};const useContextMenu=()=>{const _defaultState={visible:false,position:{x:-9999,y:-9999}};const[state,setState]=React.useState({..._defaultState});React.useEffect((()=>{const onClick=()=>setState({..._defaultState});window.addEventListener("click",onClick);return()=>window.removeEventListener("click",onClick)}),[]);const onContextMenu=e=>{e.preventDefault();e.stopPropagation();setState({visible:true,position:{x:e.clientX,y:e.clientY}})};const onClose=()=>setState({..._defaultState});return[{...state,onClose:onClose,onContextMenu:onContextMenu}]};ContextMenu.useContextMenu=useContextMenu;exports.default=ContextMenu;