azure-devops-ui
Version:
React components for building web UI in Azure DevOps
67 lines (66 loc) • 3.1 kB
JavaScript
import "../../CommonImports";
import "../../Core/core.css";
import "./Portal.css";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { ObservableValue } from '../../Core/Observable';
import { Observer } from '../../Observer';
/**
* The Portal component is used to create a React Portal through a well known component.
* This component allows the platform to control where portals are rooted in the document
* and ensure these are managed properly.
*/
export class Portal extends React.Component {
constructor(props) {
super(props);
this.mounted = new ObservableValue(false);
this.focusElement = document.activeElement;
// Determine the element that will host the portal.
let parentElement = this.props.portalElement;
if (!parentElement && this.props.portalSelector) {
parentElement = document.querySelector(this.props.portalSelector);
}
if (!parentElement) {
parentElement = document.querySelector(".bolt-portal-host");
if (!parentElement) {
parentElement = document.createElement("div");
parentElement.className = "bolt-portal-host absolute-fill no-events scroll-hidden";
document.body.appendChild(parentElement);
}
}
if (this.props.parentClassName && !parentElement.classList.contains(this.props.parentClassName)) {
parentElement.classList.add(this.props.parentClassName);
}
this.parentElement = parentElement;
// Create the hosting element for the portal.
this.hostElement = document.createElement("div");
}
render() {
this.hostElement.className = "";
this.hostElement.classList.add("bolt-portal");
this.hostElement.classList.add("absolute-fill");
// If custom class's are supplied add them (1 at a time since IE doesnt support multiple args).
if (this.props.className) {
const classNames = this.props.className.split(" ");
for (let className of classNames) {
this.hostElement.classList.add(className);
}
}
// NOTE: We dont render the children until after we have mounted the portal.
// If the caller needs to access the document while mounting the content this
// will ensure the children of the portal are not mounted until the portal
// is attached to the DOM.
return ReactDOM.createPortal(React.createElement(Observer, { mounted: this.mounted }, (props) => (props.mounted ? this.props.children : null)), this.hostElement);
}
componentDidMount() {
this.parentElement.appendChild(this.hostElement);
this.mounted.value = true;
}
componentWillUnmount() {
var _a;
this.parentElement.removeChild(this.hostElement);
if (!this.props.bypassActiveElementFocusOnUnmount) {
(_a = this.focusElement) === null || _a === void 0 ? void 0 : _a.focus();
}
}
}