import { html, LitElement } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { createRef, ref } from 'lit/directives/ref.js'; import { styleMap } from 'lit/directives/style-map.js'; import type { DialogEmbedProps } from '.'; import type { MediaPlayer } from '@vouchfor/media-player-legacy'; import './DialogOverlay'; @customElement('vouch-embed-legacy-dialog-portal') class DialogPortal extends LitElement { @property({ type: String }) vouchId: DialogEmbedProps['vouchId']; @property({ type: String }) templateId: DialogEmbedProps['templateId']; @property({ type: Array }) questions: DialogEmbedProps['questions']; @property({ type: String }) env: DialogEmbedProps['env'] = 'prod'; @property({ type: String }) apiKey: DialogEmbedProps['apiKey'] = ''; @property({ type: Boolean }) disableTracking: DialogEmbedProps['disableTracking'] = false; @property({ type: String }) trackingSource: DialogEmbedProps['trackingSource'] = 'embedded_player'; @property({ type: Array }) controls: DialogEmbedProps['controls']; @property({ type: String }) preload: DialogEmbedProps['preload'] = 'none'; @property({ type: Boolean }) disableAutoplay: DialogEmbedProps['disableAutoplay'] = false; @property({ type: Number }) aspectRatio: DialogEmbedProps['aspectRatio'] = 0; private _mediaPlayerRef = createRef(); @state() open = false; private _handleToggle = ({ detail }: CustomEvent) => { // Because we have to attach this listener to the document since this element is portalled outside of the button, // we also have to make sure that this player is actually the one we want to open and play by passing in an ID // from the button wrapper parent and checking against that same ID we pass as the event detail if (this.id === detail) { this.open = !this.open; if (this.open) { if (!this.disableAutoplay && this._mediaPlayerRef?.value) { this._mediaPlayerRef.value.muted = false; this._mediaPlayerRef.value.play(); } } else { this._mediaPlayerRef?.value?.pause(); } } }; // We could do the same thing on close and check for the correct ID but it doesn't really matter private _handleClose = () => { this.open = false; this._mediaPlayerRef?.value?.pause(); }; private _handleDocumentKeyUp = (e: KeyboardEvent) => { if (e.key === 'Escape') { this._handleClose(); } }; connectedCallback(): void { super.connectedCallback(); document.addEventListener('dialogembed:click', this._handleToggle); document.addEventListener('keyup', this._handleDocumentKeyUp); document.addEventListener('close:click', this._handleClose); document.addEventListener('overlay:click', this._handleClose); } disconnectedCallback(): void { super.disconnectedCallback(); document.removeEventListener('dialogembed:click', this._handleToggle); document.removeEventListener('keyup', this._handleDocumentKeyUp); document.removeEventListener('close:click', this._handleClose); document.removeEventListener('overlay:click', this._handleClose); } protected createRenderRoot(): HTMLElement | DocumentFragment { // We must create a new node here because portalling into the same node (document.body) causes the second // element to overwrite the first for some reason (not behaviour really stated in the docs) // I am fairly certain this function is only run once as the default behaviour creates the open shadow root // and returns that shadow root in this function: https://lit.dev/docs/components/shadow-dom/#implementing-createrenderroot const newNode = document.createElement('div'); document.body.appendChild(newNode); return newNode; } render() { return html` `; } } declare global { interface HTMLElementTagNameMap { 'vouch-embed-legacy-dialog-portal': DialogPortal; } namespace JSX { interface IntrinsicElements { 'vouch-embed-legacy-dialog-portal': DialogPortal; } } } export { DialogPortal };