UNPKG

@firefox-devtools/react-contextmenu

Version:
257 lines (256 loc) 9.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireDefault(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _classnames = _interopRequireDefault(require("classnames")); var _objectAssign = _interopRequireDefault(require("object-assign")); var _actions = require("./actions"); var _AbstractMenu = _interopRequireDefault(require("./AbstractMenu")); var _helpers = require("./helpers"); var _globalEventListener = _interopRequireDefault(require("./globalEventListener")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } class SubMenu extends _AbstractMenu.default { static propTypes = { children: _propTypes.default.node.isRequired, attributes: _propTypes.default.object, title: _propTypes.default.node.isRequired, className: _propTypes.default.string, disabled: _propTypes.default.bool, hoverDelay: _propTypes.default.number, rtl: _propTypes.default.bool, selected: _propTypes.default.bool, onMouseMove: _propTypes.default.func, onMouseOut: _propTypes.default.func, forceOpen: _propTypes.default.bool, forceClose: _propTypes.default.func, parentKeyNavigationHandler: _propTypes.default.func, onClick: _propTypes.default.func, data: _propTypes.default.object, preventCloseOnClick: _propTypes.default.bool }; static defaultProps = { disabled: false, hoverDelay: 500, attributes: {}, className: '', rtl: false, selected: false, onMouseMove: () => null, onMouseOut: () => null, forceOpen: false, forceClose: () => null, parentKeyNavigationHandler: () => null, onClick: () => null, data: {}, preventCloseOnClick: false }; constructor(props) { super(props); this.state = (0, _objectAssign.default)({}, this.state, { visible: false }); } componentDidMount() { this.listenId = _globalEventListener.default.register(() => {}, this.hideSubMenu); } getSubMenuType() { // eslint-disable-line class-methods-use-this return SubMenu; } shouldComponentUpdate(nextProps, nextState) { this.isVisibilityChange = (this.state.visible !== nextState.visible || this.props.forceOpen !== nextProps.forceOpen) && !(this.state.visible && nextProps.forceOpen) && !(this.props.forceOpen && nextState.visible); return true; } componentDidUpdate() { if (!this.isVisibilityChange) return; if (this.props.forceOpen || this.state.visible) { const wrapper = window.requestAnimationFrame || setTimeout; wrapper(() => { if (!this.subMenu) return; const styles = this.props.rtl ? this.getRTLMenuPosition() : this.getMenuPosition(); this.subMenu.style.removeProperty('top'); this.subMenu.style.removeProperty('bottom'); this.subMenu.style.removeProperty('left'); this.subMenu.style.removeProperty('right'); if ((0, _helpers.hasOwnProp)(styles, 'top')) this.subMenu.style.top = styles.top; if ((0, _helpers.hasOwnProp)(styles, 'left')) this.subMenu.style.left = styles.left; if ((0, _helpers.hasOwnProp)(styles, 'bottom')) this.subMenu.style.bottom = styles.bottom; if ((0, _helpers.hasOwnProp)(styles, 'right')) this.subMenu.style.right = styles.right; this.subMenu.classList.add(_helpers.cssClasses.menuVisible); this.registerHandlers(); this.setState({ selectedItem: null }); }); } else { const cleanup = () => { if (!this.subMenu) return; this.subMenu.removeEventListener('transitionend', cleanup); this.subMenu.style.removeProperty('bottom'); this.subMenu.style.removeProperty('right'); this.subMenu.style.top = 0; this.subMenu.style.left = '100%'; this.unregisterHandlers(); }; this.subMenu.addEventListener('transitionend', cleanup); this.subMenu.classList.remove(_helpers.cssClasses.menuVisible); } } componentWillUnmount() { if (this.listenId) { _globalEventListener.default.unregister(this.listenId); } if (this.opentimer) clearTimeout(this.opentimer); if (this.closetimer) clearTimeout(this.closetimer); this.unregisterHandlers(true); } getMenuPosition = () => { const { innerWidth, innerHeight } = window; const rect = this.subMenu.getBoundingClientRect(); const position = {}; if (rect.bottom > innerHeight) { position.bottom = 0; } else { position.top = 0; } if (rect.right < innerWidth) { position.left = '100%'; } else { position.right = '100%'; } return position; }; getRTLMenuPosition = () => { const { innerHeight } = window; const rect = this.subMenu.getBoundingClientRect(); const position = {}; if (rect.bottom > innerHeight) { position.bottom = 0; } else { position.top = 0; } if (rect.left < 0) { position.left = '100%'; } else { position.right = '100%'; } return position; }; hideMenu = e => { e.preventDefault(); this.hideSubMenu(e); }; hideSubMenu = e => { // avoid closing submenus of a different menu tree if (e.detail && e.detail.id && this.menu && e.detail.id !== this.menu.id) { return; } if (this.props.forceOpen) { this.props.forceClose(); } this.setState({ visible: false, selectedItem: null }); this.unregisterHandlers(); }; handleClick = event => { event.preventDefault(); if (this.props.disabled) return; (0, _helpers.callIfExists)(this.props.onClick, event, (0, _objectAssign.default)({}, this.props.data, _helpers.store.data), _helpers.store.target); if (!this.props.onClick || this.props.preventCloseOnClick) return; (0, _actions.hideMenu)(); }; handleMouseEnter = () => { if (this.closetimer) clearTimeout(this.closetimer); if (this.props.disabled || this.state.visible) return; this.opentimer = setTimeout(() => this.setState({ visible: true, selectedItem: null }), this.props.hoverDelay); }; handleMouseLeave = () => { if (this.opentimer) clearTimeout(this.opentimer); if (!this.state.visible) return; this.closetimer = setTimeout(() => this.setState({ visible: false, selectedItem: null }), this.props.hoverDelay); }; menuRef = c => { this.menu = c; }; subMenuRef = c => { this.subMenu = c; }; registerHandlers = () => { document.removeEventListener('keydown', this.props.parentKeyNavigationHandler); document.addEventListener('keydown', this.handleKeyNavigation); }; unregisterHandlers = dismounting => { document.removeEventListener('keydown', this.handleKeyNavigation); if (!dismounting) { document.addEventListener('keydown', this.props.parentKeyNavigationHandler); } }; render() { const { children, attributes, disabled, title, selected } = this.props; const { visible } = this.state; const menuProps = { ref: this.menuRef, onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, className: (0, _classnames.default)(_helpers.cssClasses.menuItem, _helpers.cssClasses.subMenu, attributes.listClassName), style: { position: 'relative' } }; const menuItemProps = { className: (0, _classnames.default)(_helpers.cssClasses.menuItem, attributes.className, { [(0, _classnames.default)(_helpers.cssClasses.menuItemDisabled, attributes.disabledClassName)]: disabled, [(0, _classnames.default)(_helpers.cssClasses.menuItemActive, attributes.visibleClassName)]: visible, [(0, _classnames.default)(_helpers.cssClasses.menuItemSelected, attributes.selectedClassName)]: selected }), onMouseMove: this.props.onMouseMove, onMouseOut: this.props.onMouseOut, onClick: this.handleClick }; const subMenuProps = { ref: this.subMenuRef, style: { position: 'absolute', transition: 'opacity 1ms', // trigger transitionend event top: 0, left: '100%' }, className: (0, _classnames.default)(_helpers.cssClasses.menu, this.props.className) }; return /*#__PURE__*/_react.default.createElement("nav", _extends({}, menuProps, { role: "menuitem", tabIndex: "-1", "aria-haspopup": "true" }), /*#__PURE__*/_react.default.createElement("div", _extends({}, attributes, menuItemProps), title), /*#__PURE__*/_react.default.createElement("nav", _extends({}, subMenuProps, { role: "menu", tabIndex: "-1" }), this.renderChildren(children))); } } exports.default = SubMenu;