@blueprintjs/core
Version:
Core styles & components
113 lines • 5.34 kB
JavaScript
/*
* Copyright 2022 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 });
exports.Portal = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const ReactDOM = tslib_1.__importStar(require("react-dom"));
const common_1 = require("../../common");
const portalProvider_1 = require("../../context/portal/portalProvider");
/**
* Portal component.
*
* This component detaches its contents and re-attaches them to document.body.
* Use it when you need to circumvent DOM z-stacking (for dialogs, popovers, etc.).
* Any class names passed to this element will be propagated to the new container element on document.body.
*
* @see https://blueprintjs.com/docs/#core/components/portal
*/
function Portal(
// eslint-disable-next-line @typescript-eslint/no-deprecated
{ className, stopPropagationEvents, container, onChildrenMount, children }) {
var _a;
const context = React.useContext(portalProvider_1.PortalContext);
const portalContainer = (_a = container !== null && container !== void 0 ? container : context.portalContainer) !== null && _a !== void 0 ? _a : (typeof document !== "undefined" ? document.body : undefined);
const [portalElement, setPortalElement] = React.useState();
const createPortalElement = React.useCallback(() => {
const newPortalElement = document.createElement("div");
newPortalElement.classList.add(common_1.Classes.PORTAL);
maybeAddClass(newPortalElement.classList, className); // directly added to this portal element
maybeAddClass(newPortalElement.classList, context.portalClassName); // added via PortalProvider context
addStopPropagationListeners(newPortalElement, stopPropagationEvents);
return newPortalElement;
}, [className, context.portalClassName, stopPropagationEvents]);
// create the container element & attach it to the DOM
React.useEffect(() => {
if (portalContainer == null) {
return;
}
const newPortalElement = createPortalElement();
portalContainer.appendChild(newPortalElement);
setPortalElement(newPortalElement);
return () => {
removeStopPropagationListeners(newPortalElement, stopPropagationEvents);
newPortalElement.remove();
setPortalElement(undefined);
};
}, [portalContainer, createPortalElement, stopPropagationEvents]);
// wait until next successful render to invoke onChildrenMount callback
React.useEffect(() => {
if (portalElement != null) {
onChildrenMount === null || onChildrenMount === void 0 ? void 0 : onChildrenMount();
}
}, [portalElement, onChildrenMount]);
React.useEffect(() => {
if (portalElement != null) {
maybeAddClass(portalElement.classList, className);
return () => maybeRemoveClass(portalElement.classList, className);
}
return undefined;
}, [className, portalElement]);
React.useEffect(() => {
if (portalElement != null) {
addStopPropagationListeners(portalElement, stopPropagationEvents);
return () => removeStopPropagationListeners(portalElement, stopPropagationEvents);
}
return undefined;
}, [portalElement, stopPropagationEvents]);
// Only render `children` once this component has mounted in a browser environment, so they are
// immediately attached to the DOM tree and can do DOM things like measuring or `autoFocus`.
// See long comment on componentDidMount in https://reactjs.org/docs/portals.html#event-bubbling-through-portals
if (typeof document === "undefined" || portalElement == null) {
return null;
}
else {
return ReactDOM.createPortal(children, portalElement);
}
}
exports.Portal = Portal;
Portal.displayName = `${common_1.DISPLAYNAME_PREFIX}.Portal`;
function maybeRemoveClass(classList, className) {
if (className != null && className !== "") {
classList.remove(...className.split(" "));
}
}
function maybeAddClass(classList, className) {
if (className != null && className !== "") {
classList.add(...className.split(" "));
}
}
function addStopPropagationListeners(portalElement, eventNames) {
eventNames === null || eventNames === void 0 ? void 0 : eventNames.forEach(event => portalElement.addEventListener(event, handleStopProgation));
}
function removeStopPropagationListeners(portalElement, events) {
events === null || events === void 0 ? void 0 : events.forEach(event => portalElement.removeEventListener(event, handleStopProgation));
}
function handleStopProgation(e) {
e.stopPropagation();
}
//# sourceMappingURL=portal.js.map
;