import { html, LitElement, type TemplateResult } from 'lit';
import { property } from 'lit/decorators.js';
/**
* Renders a template into a portal. Defaults to `document.body`.
*
* Note that every time the parent component re-renders, the portal will be re-called.
*
* See https://lit.dev/docs/components/rendering/#writing-a-good-render()-method
*
* @example
* ```ts
* render() {
* return html`${showPortal
* ? html``
* : null}`;
* };
* ```
*/
export class Portal extends LitElement {
private _portalRoot: HTMLElement | null = null;
override createRenderRoot() {
const portalRoot = document.createElement('div');
const renderRoot = this.shadowDom
? portalRoot.attachShadow({
mode: 'open',
...(typeof this.shadowDom !== 'boolean' ? this.shadowDom : {}),
})
: portalRoot;
portalRoot.classList.add('blocksuite-portal');
this.container.append(portalRoot);
this._portalRoot = portalRoot;
return renderRoot;
}
override disconnectedCallback(): void {
super.disconnectedCallback();
this._portalRoot?.remove();
}
override render() {
return this.template;
}
@property({ attribute: false })
accessor container = document.body;
@property({ attribute: false })
accessor shadowDom: boolean | ShadowRootInit = true;
@property({ attribute: false })
accessor template: TemplateResult | undefined = html``;
}
declare global {
interface HTMLElementTagNameMap {
'blocksuite-portal': Portal;
}
}