@teipublisher/pb-components
Version:
Collection of webcomponents underlying TEI Publisher
120 lines (110 loc) • 3.58 kB
JavaScript
import { LitElement, css, html } from 'lit-element';
import { unsafeHTML } from 'lit-html/directives/unsafe-html';
import { pbMixin } from './pb-mixin';
import { themableMixin } from './theming.js';
/**
* A simple dialog component using the HTML5 <dialog> element.
*
* Any button with the attribute `rel="prev"` will close the dialog when clicked.
*
* @slot - Content of the dialog
* @slot title - Title of the dialog
* @fires pb-dialog-opened - Fired when the dialog is opened
* @fires pb-dialog-closed - Fired when the dialog is closed
*/
export class PbDialog extends themableMixin(pbMixin(LitElement)) {
static get properties() {
return {
...super.properties,
open: { type: Boolean, reflect: true },
modal: { type: Boolean, reflect: true },
title: { type: String, reflect: true },
message: { type: String, reflect: true },
};
}
constructor() {
super();
this.open = false;
this.modal = true;
this._escListener = this._onEsc.bind(this);
this.title = null;
this.message = null;
}
_onEsc(e) {
if (e.key === 'Escape' && this.open) {
this.closeDialog();
}
}
openDialog() {
if (!this.open) {
if (this.modal) {
this._dialog.showModal();
} else {
this._dialog.show();
}
this.dispatchEvent(new CustomEvent('pb-dialog-opened', { bubbles: true, composed: true }));
this.open = true;
}
}
closeDialog() {
if (this.open) {
this._dialog.close();
this.dispatchEvent(new CustomEvent('pb-dialog-closed', { bubbles: true, composed: true }));
this.open = false;
}
}
render() {
return html`
<dialog @click="${e => e.target === this._dialog && this.modal && this.closeDialog()}">
<article>
<header>
${this.title ? unsafeHTML(this.title) : html`<slot name="title"></slot>`}
<button rel="prev" aria-label="Close"></button>
</header>
${this.message ? unsafeHTML(this.message) : html`<slot></slot>`}
<footer>
<slot name="footer"></slot>
</footer>
</article>
</dialog>
`;
}
firstUpdated() {
this._dialog = this.renderRoot.querySelector('dialog');
// Add click listeners to close buttons in both shadow and light DOM
[
...this._dialog.querySelectorAll('button[rel="prev"]'),
...this.querySelectorAll('button[rel="prev"]'),
].forEach(button => button.addEventListener('click', this.closeDialog.bind(this)));
}
static get styles() {
return css`
header {
display: flex;
justify-content: space-between;
align-items: center;
}
button[rel='prev'] {
display: block;
height: 1rem;
width: 1rem;
background-image: var(
--pb-dialog-background-image,
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E")
);
background-position: center;
background-size: auto 1rem;
background-repeat: no-repeat;
background-color: transparent;
border: none;
}
footer {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 16px;
}
`;
}
}
customElements.define('pb-dialog', PbDialog);