var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } import React from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; import assign from 'object-assign'; import listener from './globalEventListener'; import AbstractMenu from './AbstractMenu'; import SubMenu from './SubMenu'; import { hideMenu } from './actions'; import { cssClasses, callIfExists, store } from './helpers'; var ContextMenu = function (_AbstractMenu) { _inherits(ContextMenu, _AbstractMenu); function ContextMenu(props) { _classCallCheck(this, ContextMenu); var _this = _possibleConstructorReturn(this, (ContextMenu.__proto__ || Object.getPrototypeOf(ContextMenu)).call(this, props)); _this.registerHandlers = function () { document.addEventListener('mousedown', _this.handleOutsideClick); document.addEventListener('touchstart', _this.handleOutsideClick); if (!_this.props.preventHideOnScroll) document.addEventListener('scroll', _this.handleHide); if (!_this.props.preventHideOnContextMenu) document.addEventListener('contextmenu', _this.handleHide); document.addEventListener('keydown', _this.handleKeyNavigation); if (!_this.props.preventHideOnResize) window.addEventListener('resize', _this.handleHide); }; _this.unregisterHandlers = function () { document.removeEventListener('mousedown', _this.handleOutsideClick); document.removeEventListener('touchstart', _this.handleOutsideClick); document.removeEventListener('scroll', _this.handleHide); document.removeEventListener('contextmenu', _this.handleHide); document.removeEventListener('keydown', _this.handleKeyNavigation); window.removeEventListener('resize', _this.handleHide); }; _this.handleShow = function (e) { if (e.detail.id !== _this.props.id || _this.state.isVisible) return; var _e$detail$position = e.detail.position, x = _e$detail$position.x, y = _e$detail$position.y; _this.setState({ isVisible: true, x: x, y: y }); _this.registerHandlers(); callIfExists(_this.props.onShow, e); }; _this.handleHide = function (e) { if (_this.state.isVisible && (!e.detail || !e.detail.id || e.detail.id === _this.props.id)) { _this.unregisterHandlers(); _this.setState({ isVisible: false, selectedItem: null, forceSubMenuOpen: false }); callIfExists(_this.props.onHide, e); } }; _this.handleOutsideClick = function (e) { if (!_this.menu.contains(e.target)) hideMenu(); }; _this.handleMouseLeave = function (event) { event.preventDefault(); callIfExists(_this.props.onMouseLeave, event, assign({}, _this.props.data, store.data), store.target); if (_this.props.hideOnLeave) hideMenu(); }; _this.handleContextMenu = function (e) { if (process.env.NODE_ENV === 'production') { e.preventDefault(); } _this.handleHide(e); }; _this.hideMenu = function (e) { if (e.keyCode === 27 || e.keyCode === 13) { // ECS or enter hideMenu(); } }; _this.getMenuPosition = function () { var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var menuStyles = { top: y, left: x }; if (!_this.menu) return menuStyles; var _window = window, innerWidth = _window.innerWidth, innerHeight = _window.innerHeight; var rect = _this.menu.getBoundingClientRect(); if (y + rect.height > innerHeight) { menuStyles.top -= rect.height; } if (x + rect.width > innerWidth) { menuStyles.left -= rect.width; } if (menuStyles.top < 0) { menuStyles.top = rect.height < innerHeight ? (innerHeight - rect.height) / 2 : 0; } if (menuStyles.left < 0) { menuStyles.left = rect.width < innerWidth ? (innerWidth - rect.width) / 2 : 0; } return menuStyles; }; _this.getRTLMenuPosition = function () { var x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var menuStyles = { top: y, left: x }; if (!_this.menu) return menuStyles; var _window2 = window, innerWidth = _window2.innerWidth, innerHeight = _window2.innerHeight; var rect = _this.menu.getBoundingClientRect(); // Try to position the menu on the left side of the cursor menuStyles.left = x - rect.width; if (y + rect.height > innerHeight) { menuStyles.top -= rect.height; } if (menuStyles.left < 0) { menuStyles.left += rect.width; } if (menuStyles.top < 0) { menuStyles.top = rect.height < innerHeight ? (innerHeight - rect.height) / 2 : 0; } if (menuStyles.left + rect.width > innerWidth) { menuStyles.left = rect.width < innerWidth ? (innerWidth - rect.width) / 2 : 0; } return menuStyles; }; _this.menuRef = function (c) { _this.menu = c; }; _this.state = assign({}, _this.state, { x: 0, y: 0, isVisible: false }); return _this; } _createClass(ContextMenu, [{ key: 'getSubMenuType', value: function getSubMenuType() { // eslint-disable-line class-methods-use-this return SubMenu; } }, { key: 'componentDidMount', value: function componentDidMount() { this.listenId = listener.register(this.handleShow, this.handleHide); } }, { key: 'componentDidUpdate', value: function componentDidUpdate() { var _this2 = this; var wrapper = window.requestAnimationFrame || setTimeout; if (this.state.isVisible) { wrapper(function () { var _state = _this2.state, x = _state.x, y = _state.y; var _ref = _this2.props.rtl ? _this2.getRTLMenuPosition(x, y) : _this2.getMenuPosition(x, y), top = _ref.top, left = _ref.left; wrapper(function () { if (!_this2.menu) return; _this2.menu.style.top = top + 'px'; _this2.menu.style.left = left + 'px'; _this2.menu.style.opacity = 1; _this2.menu.style.pointerEvents = 'auto'; }); }); } else { wrapper(function () { if (!_this2.menu) return; _this2.menu.style.opacity = 0; _this2.menu.style.pointerEvents = 'none'; }); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { if (this.listenId) { listener.unregister(this.listenId); } this.unregisterHandlers(); } }, { key: 'render', value: function render() { var _props = this.props, children = _props.children, className = _props.className, style = _props.style; var isVisible = this.state.isVisible; var inlineStyle = assign({}, style, { position: 'fixed', opacity: 0, pointerEvents: 'none' }); var menuClassnames = cx(cssClasses.menu, className, _defineProperty({}, cssClasses.menuVisible, isVisible)); return React.createElement( 'nav', { role: 'menu', tabIndex: '-1', ref: this.menuRef, style: inlineStyle, className: menuClassnames, onContextMenu: this.handleContextMenu, onMouseLeave: this.handleMouseLeave }, this.renderChildren(children) ); } }]); return ContextMenu; }(AbstractMenu); ContextMenu.propTypes = { id: PropTypes.string.isRequired, children: PropTypes.node.isRequired, data: PropTypes.object, className: PropTypes.string, hideOnLeave: PropTypes.bool, rtl: PropTypes.bool, onHide: PropTypes.func, onMouseLeave: PropTypes.func, onShow: PropTypes.func, preventHideOnContextMenu: PropTypes.bool, preventHideOnResize: PropTypes.bool, preventHideOnScroll: PropTypes.bool, style: PropTypes.object }; ContextMenu.defaultProps = { className: '', data: {}, hideOnLeave: false, rtl: false, onHide: function onHide() { return null; }, onMouseLeave: function onMouseLeave() { return null; }, onShow: function onShow() { return null; }, preventHideOnContextMenu: false, preventHideOnResize: false, preventHideOnScroll: false, style: {} }; export default ContextMenu;