diff --git a/modules/ContextMenu.js b/modules/ContextMenu.js index 2f8821393f15a1ce85132385688f08cd6e390bf4..41e47ea740636f81bdd484a7093d0ea3169eb9d5 100644 --- a/modules/ContextMenu.js +++ b/modules/ContextMenu.js @@ -81,6 +81,11 @@ var ContextMenu = function (_AbstractMenu) { x = _e$detail$position.x, y = _e$detail$position.y; + if (x === undefined) { + var rect = e.detail.target.getBoundingClientRect(); + x = rect.x; + y = rect.y; + } _this.setState({ isVisible: true, x: x, y: y }); _this.registerHandlers(); @@ -226,6 +231,9 @@ var ContextMenu = function (_AbstractMenu) { var wrapper = window.requestAnimationFrame || setTimeout; if (this.state.isVisible) { + if (!this.previousFocus) { + this.previousFocus = document.activeElement; + } wrapper(function () { var _state = _this2.state, x = _state.x, @@ -240,7 +248,10 @@ var ContextMenu = function (_AbstractMenu) { _this2.menu.style.top = top + 'px'; _this2.menu.style.left = left + 'px'; _this2.menu.style.opacity = 1; + _this2.menu.style.visibility = 'visible'; _this2.menu.style.pointerEvents = 'auto'; + + _this2.menu.focus(); }); }); } else { @@ -248,6 +259,16 @@ var ContextMenu = function (_AbstractMenu) { if (!_this2.menu) return; _this2.menu.style.opacity = 0; _this2.menu.style.pointerEvents = 'none'; + + // Return to the previous focus state when dismissing the menu, unless the + // menu option focused another element. This is important for keyboard mode. + if (_this2.props.avoidFocusRestoreOnBlur) return; + + var isFocusWithinMenu = _this2.menu.contains(document.activeElement); + if (isFocusWithinMenu && _this2.previousFocus && _this2.previousFocus.focus) { + _this2.previousFocus.focus(); + _this2.previousFocus = null; + } }); } } diff --git a/modules/SubMenu.js b/modules/SubMenu.js index ad1dc7043c13cbc30f1659d5b82696ed974c996a..c919be8d12329dd5bcf77d3660b85edb83714bb8 100644 --- a/modules/SubMenu.js +++ b/modules/SubMenu.js @@ -129,6 +129,7 @@ var SubMenu = function (_AbstractMenu) { if (_this.props.disabled || _this.state.visible) return; + if (_this.opentimer) clearTimeout(_this.opentimer); _this.opentimer = setTimeout(function () { return _this.setState({ visible: true, @@ -142,6 +143,7 @@ var SubMenu = function (_AbstractMenu) { if (!_this.state.visible) return; + if (_this.closetimer) clearTimeout(_this.closetimer); _this.closetimer = setTimeout(function () { return _this.setState({ visible: false, @@ -170,6 +172,15 @@ var SubMenu = function (_AbstractMenu) { } }; + _this.cleanup = function () { + _this.subMenu.removeEventListener('transitionend', _this.cleanup); + _this.subMenu.style.removeProperty('bottom'); + _this.subMenu.style.removeProperty('right'); + _this.subMenu.style.top = 0; + _this.subMenu.style.left = '100%'; + _this.unregisterHandlers(); + }; + _this.state = (0, _objectAssign2.default)({}, _this.state, { visible: false }); @@ -202,32 +213,28 @@ var SubMenu = function (_AbstractMenu) { if (this.props.forceOpen || this.state.visible) { var wrapper = window.requestAnimationFrame || setTimeout; wrapper(function () { - var styles = _this2.props.rtl ? _this2.getRTLMenuPosition() : _this2.getMenuPosition(); - - _this2.subMenu.style.removeProperty('top'); - _this2.subMenu.style.removeProperty('bottom'); - _this2.subMenu.style.removeProperty('left'); - _this2.subMenu.style.removeProperty('right'); - - if ((0, _helpers.hasOwnProp)(styles, 'top')) _this2.subMenu.style.top = styles.top; - if ((0, _helpers.hasOwnProp)(styles, 'left')) _this2.subMenu.style.left = styles.left; - if ((0, _helpers.hasOwnProp)(styles, 'bottom')) _this2.subMenu.style.bottom = styles.bottom; - if ((0, _helpers.hasOwnProp)(styles, 'right')) _this2.subMenu.style.right = styles.right; - _this2.subMenu.classList.add(_helpers.cssClasses.menuVisible); - - _this2.registerHandlers(); - _this2.setState({ selectedItem: null }); + if (_this2.props.forceOpen || _this2.state.visible) { + _this2.subMenu.removeEventListener('transitionend', _this2.cleanup); + var styles = _this2.props.rtl ? _this2.getRTLMenuPosition() : _this2.getMenuPosition(); + + _this2.subMenu.style.removeProperty('top'); + _this2.subMenu.style.removeProperty('bottom'); + _this2.subMenu.style.removeProperty('left'); + _this2.subMenu.style.removeProperty('right'); + + if ((0, _helpers.hasOwnProp)(styles, 'top')) _this2.subMenu.style.top = styles.top; + if ((0, _helpers.hasOwnProp)(styles, 'left')) _this2.subMenu.style.left = styles.left; + if ((0, _helpers.hasOwnProp)(styles, 'bottom')) _this2.subMenu.style.bottom = styles.bottom; + if ((0, _helpers.hasOwnProp)(styles, 'right')) _this2.subMenu.style.right = styles.right; + _this2.subMenu.classList.add(_helpers.cssClasses.menuVisible); + + _this2.registerHandlers(); + _this2.setState({ selectedItem: null }); + } }); } else { - var cleanup = function cleanup() { - _this2.subMenu.removeEventListener('transitionend', cleanup); - _this2.subMenu.style.removeProperty('bottom'); - _this2.subMenu.style.removeProperty('right'); - _this2.subMenu.style.top = 0; - _this2.subMenu.style.left = '100%'; - _this2.unregisterHandlers(); - }; - this.subMenu.addEventListener('transitionend', cleanup); + this.subMenu.removeEventListener('transitionend', this.cleanup); + this.subMenu.addEventListener('transitionend', this.cleanup); this.subMenu.classList.remove(_helpers.cssClasses.menuVisible); } } diff --git a/src/index.d.ts b/src/index.d.ts index 753ce9081490fd90b4354e6e73937dc85957689c..e8a8b0815a7ca61ef9bc488299cda08e181267bc 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -14,6 +14,8 @@ declare module "react-contextmenu" { preventHideOnResize?: boolean, preventHideOnScroll?: boolean, style?: React.CSSProperties, + avoidFocusRestoreOnBlur?: boolean; + children?: React.ReactNode; } export interface ContextMenuTriggerProps { @@ -25,6 +27,7 @@ declare module "react-contextmenu" { renderTag?: React.ElementType, mouseButton?: number, disableIfShiftIsPressed?: boolean, + children?: React.ReactNode, } export interface MenuItemProps { @@ -35,6 +38,7 @@ declare module "react-contextmenu" { divider?: boolean, preventClose?: boolean, onClick?: {(event: React.TouchEvent | React.MouseEvent, data: Object, target: HTMLElement): void} | Function, + children?: React.ReactNode, } export interface SubMenuProps { @@ -45,11 +49,13 @@ declare module "react-contextmenu" { rtl?: boolean, preventCloseOnClick?: boolean, onClick?: {(event: React.TouchEvent | React.MouseEvent, data: Object, target: HTMLElement): void} | Function, + children?: React.ReactNode, } export interface ConnectMenuProps { id: string; trigger: any; + children?: React.ReactNode; } export const ContextMenu: React.ComponentClass;