// tslint:disable:max-classes-per-file import * as React from 'react'; import { MenuItemProps, Overlay, Popover, PopoverProps } from 'react-bootstrap'; export interface ContextMenuProps { key?: string | number; id: string; header?: string; onSelect?: (item: MenuItemProps) => void; } export interface ContextMenuState { isVisible: boolean; left?: number; top?: number; } // this is necessary to prevent a js error due to the Overlay trying to position // the Popup. // The Overlay attempts to pass on a bunch of props to its child and these props // are not supported by the child node. We end up with a React 'Unknown props' // error. So to circumvent this issue, we use this container wrapper that consumes // all those Overlay injected props and only passes on the className and children. // We still need to 'support' all the PopoverProps to eliminate the error. class ContextMenuContainer extends React.Component { render() { return (
{this.props.children}
); } } const ArrowOffset = 20; export class ContextMenu extends React.Component< ContextMenuProps, ContextMenuState > { public static displayName = 'ContextMenu'; constructor(props: any) { super(props); this.handleClick = this.handleClick.bind(this); this.handleHide = this.handleHide.bind(this); this.state = { isVisible: false, } as ContextMenuState; } private handleClick(e: React.MouseEvent) { // check if the right mouse button was clicked const isVisible = e.button === 2; if (isVisible === true) { // prevent other actions from happening e.stopPropagation(); e.preventDefault(); const left = e.pageX; const top = e.pageY - ArrowOffset; // update our state this.setState((prevState, props) => { return { isVisible, left, top, }; }); } } private handleHide() { this.setState((prevState, props) => { return { isVisible: false, left: undefined, top: undefined, }; }); } render() { const menuItems: React.ReactChild[] = React.Children.toArray( this.props.children, ); return (
{menuItems.shift()}
{this.renderMenu(menuItems)}
); } private renderMenu(menuItems: React.ReactChild[]) { return this.wxr.renderConditional(this.state.isVisible === true, () => { return (
    {this.renderMenuItems(menuItems)}
); }); } protected renderMenuItems(menuItems: React.ReactChild[]) { const onSelect = this.props.onSelect; if (onSelect) { // if onSelect is provided we need to inject it into all the menu items return React.Children.map(menuItems, (x: React.ReactElement) => React.cloneElement(x, { onSelect: () => onSelect(x.props) }), ); } return menuItems; } }