@blueprintjs/core
Version:
Core styles & components
129 lines • 6.34 kB
JavaScript
/*
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var classnames_1 = tslib_1.__importDefault(require("classnames"));
var React = tslib_1.__importStar(require("react"));
var ReactDOM = tslib_1.__importStar(require("react-dom"));
var react_lifecycles_compat_1 = require("react-lifecycles-compat");
var common_1 = require("../../common");
var utils_1 = require("../../common/utils");
var popover_1 = require("../popover/popover");
var POPPER_MODIFIERS = {
preventOverflow: { boundariesElement: "viewport" },
};
var TRANSITION_DURATION = 100;
/* istanbul ignore next */
var ContextMenu = /** @class */ (function (_super) {
tslib_1.__extends(ContextMenu, _super);
function ContextMenu() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = {
isDarkTheme: false,
isOpen: false,
menu: null,
offset: null,
};
_this.cancelContextMenu = function (e) { return e.preventDefault(); };
_this.handleBackdropContextMenu = function (e) {
// React function to remove from the event pool, useful when using a event within a callback
e.persist();
e.preventDefault();
// wait for backdrop to disappear so we can find the "real" element at event coordinates.
// timeout duration is equivalent to transition duration so we know it's animated out.
_this.setTimeout(function () {
// retrigger context menu event at the element beneath the backdrop.
// if it has a `contextmenu` event handler then it'll be invoked.
// if it doesn't, no native menu will show (at least on OSX) :(
var newTarget = document.elementFromPoint(e.clientX, e.clientY);
newTarget.dispatchEvent(new MouseEvent("contextmenu", e));
}, TRANSITION_DURATION);
};
_this.handlePopoverInteraction = function (nextOpenState) {
if (!nextOpenState) {
// delay the actual hiding till the event queue clears
// to avoid flicker of opening twice
requestAnimationFrame(function () { return _this.hide(); });
}
};
return _this;
}
ContextMenu.prototype.render = function () {
var _a;
// prevent right-clicking in a context menu
var content = React.createElement("div", { onContextMenu: this.cancelContextMenu }, this.state.menu);
var popoverClassName = classnames_1.default((_a = {}, _a[common_1.Classes.DARK] = this.state.isDarkTheme, _a));
// HACKHACK: workaround until we have access to Popper#scheduleUpdate().
// https://github.com/palantir/blueprint/issues/692
// Generate key based on offset so a new Popover instance is created
// when offset changes, to force recomputing position.
var key = this.state.offset == null ? "" : this.state.offset.left + "x" + this.state.offset.top;
// wrap the popover in a positioned div to make sure it is properly
// offset on the screen.
return (React.createElement("div", { className: common_1.Classes.CONTEXT_MENU_POPOVER_TARGET, style: this.state.offset },
React.createElement(popover_1.Popover, tslib_1.__assign({}, this.props, { backdropProps: { onContextMenu: this.handleBackdropContextMenu }, content: content, enforceFocus: false, key: key, hasBackdrop: true, isOpen: this.state.isOpen, minimal: true, modifiers: POPPER_MODIFIERS, onInteraction: this.handlePopoverInteraction, position: common_1.Position.RIGHT_TOP, popoverClassName: popoverClassName, target: React.createElement("div", null), transitionDuration: TRANSITION_DURATION }))));
};
ContextMenu.prototype.show = function (menu, offset, onClose, isDarkTheme) {
this.setState({ isOpen: true, menu: menu, offset: offset, onClose: onClose, isDarkTheme: isDarkTheme });
};
ContextMenu.prototype.hide = function () {
utils_1.safeInvoke(this.state.onClose);
this.setState({ isOpen: false, onClose: undefined });
};
ContextMenu = tslib_1.__decorate([
react_lifecycles_compat_1.polyfill
], ContextMenu);
return ContextMenu;
}(common_1.AbstractPureComponent2));
var contextMenuElement;
var contextMenu;
/**
* Show the given menu element at the given offset from the top-left corner of the viewport.
* The menu will appear below-right of this point and will flip to below-left if there is not enough
* room onscreen. The optional callback will be invoked when this menu closes.
*/
function show(menu, offset, onClose, isDarkTheme) {
if (contextMenuElement == null) {
contextMenuElement = document.createElement("div");
contextMenuElement.classList.add(common_1.Classes.CONTEXT_MENU);
document.body.appendChild(contextMenuElement);
contextMenu = ReactDOM.render(React.createElement(ContextMenu, { onClosed: remove }), contextMenuElement);
}
contextMenu.show(menu, offset, onClose, isDarkTheme);
}
exports.show = show;
/** Hide the open context menu. */
function hide() {
if (contextMenu != null) {
contextMenu.hide();
}
}
exports.hide = hide;
/** Return whether a context menu is currently open. */
function isOpen() {
return contextMenu != null && contextMenu.state.isOpen;
}
exports.isOpen = isOpen;
function remove() {
if (contextMenuElement != null) {
ReactDOM.unmountComponentAtNode(contextMenuElement);
contextMenuElement.remove();
contextMenuElement = null;
contextMenu = null;
}
}
//# sourceMappingURL=contextMenu.js.map
;