UNPKG

@blueprintjs/core

Version:

Core styles & components

120 lines 5.58 kB
/* * Copyright 2023 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. */ import * as React from "react"; import * as ReactDOM from "react-dom"; import { Classes } from "../../common"; import { OverlaysProvider } from "../../context/overlays/overlaysProvider"; import { ContextMenuPopover } from "./contextMenuPopover"; /** State which contains the context menu singleton instance for the imperative ContextMenu APIs. */ let contextMenuState; /** * Show a context menu at a particular offset from the top-left corner of the document. * The menu will appear below-right of this point and will flip to below-left if there is not enough * room onscreen. Additional props like `onClose`, `isDarkTheme`, etc. can be forwarded to the `<ContextMenuPopover>`. * * Context menus created with this API will automatically close when a user clicks outside the popover. * You may force them to close by using `hideContextMenu()`. * * Note that this API relies on global state in the @blueprintjs/core package, and should be used with caution, * especially if your build system allows multiple copies of Blueprint libraries to be bundled into an application at * once. * * Alternative APIs to consider which do not have the limitations of global state: * - `<ContextMenu>` * - `<ContextMenuPopover>` * * @see https://blueprintjs.com/docs/#core/components/context-menu-popover.imperative-api */ export function showContextMenu(props, // eslint-disable-next-line @typescript-eslint/no-deprecated options = {}) { const { container, render } = maybeMigrateShowContextOptions(options); if (contextMenuState == null) { const element = document.createElement("div"); element.classList.add(Classes.CONTEXT_MENU); container.appendChild(element); contextMenuState = { element, unmount: undefined }; } else { // N.B. It's important to unmount previous instances of the ContextMenuPopover rendered by this function. // Otherwise, React will detect no change in props sent to the already-mounted component, and therefore // do nothing after the first call to this function, leading to bugs like https://github.com/palantir/blueprint/issues/5949 contextMenuState.unmount(); } contextMenuState.unmount = render(React.createElement(OverlaysProvider, null, React.createElement(UncontrolledContextMenuPopover, { ...props })), contextMenuState.element); } function maybeMigrateShowContextOptions( // eslint-disable-next-line @typescript-eslint/no-deprecated options) { if ("render" in options) { return { container: options.container ?? document.body, render: options.render, }; } return { container: options.container ?? document.body, render: (element, container) => { // TODO(React 18): Replace deprecated ReactDOM methods. See: https://github.com/palantir/blueprint/issues/7165 // eslint-disable-next-line @typescript-eslint/no-deprecated const render = options.domRenderer ?? ReactDOM.render; // eslint-disable-next-line @typescript-eslint/no-deprecated render(element, container); return () => { // TODO(React 18): Replace deprecated ReactDOM methods. See: https://github.com/palantir/blueprint/issues/7165 // eslint-disable-next-line @typescript-eslint/no-deprecated const unmount = options.domUnmounter ?? ReactDOM.unmountComponentAtNode; unmount(container); }; }, }; } /** * Hide a context menu that was created using `showContextMenu()`. * * Note that this API relies on global state in the @blueprintjs/core package, and should be used with caution. * * @see https://blueprintjs.com/docs/#core/components/context-menu-popover.imperative-api */ // eslint-disable-next-line @typescript-eslint/no-deprecated export function hideContextMenu(options = {}) { // TODO(React 18): Replace deprecated ReactDOM methods. See: https://github.com/palantir/blueprint/issues/7165 // eslint-disable-next-line @typescript-eslint/no-deprecated const { domUnmounter = ReactDOM.unmountComponentAtNode } = options; if (contextMenuState != null) { if (domUnmounter != null) { domUnmounter(contextMenuState.element); } else { contextMenuState.unmount(); } contextMenuState = undefined; } } /** * A simple wrapper around `ContextMenuPopover` which is open by default and uncontrolled. * It closes when a user clicks outside the popover. */ function UncontrolledContextMenuPopover({ onClose, ...props }) { const [isOpen, setIsOpen] = React.useState(true); const handleClose = React.useCallback(() => { setIsOpen(false); onClose?.(); }, [onClose]); return React.createElement(ContextMenuPopover, { isOpen: isOpen, ...props, onClose: handleClose }); } //# sourceMappingURL=contextMenuSingleton.js.map