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 };