/** * Custom modal dialog with form support. Assumes the following structure: * * Where prompt is the modal dialog's prompt or header, * body contains an optional form or other information, * and controls contains a series of buttons which controls the form */ class ModalDialog extends HTMLElement { shadowRoot = null; dialog = null; constructor () { super(); // setup shadowDOM const internals = this.attachInternals(); this.shadowRoot = internals.shadowRoot; this.dialog = this.shadowRoot.querySelector("dialog"); // add dialog handler to each control button with the return value corresponding to their value attribute const controls = this.shadowRoot.querySelector("#controls"); for (const button of controls.childNodes) { button.addEventListener("click", async (e) => { e.preventDefault(); this.dialog.close(e.target.value); }); } this.setOnClose(); // default behavior to just close the dialog, should call setOnClose to override this behavior } showModal () { this.dialog.showModal(); } querySelector (query) { return this.shadowRoot.querySelector(query); } querySelectorAll (query) { return this.shadowRoot.querySelectorAll(query); } setOnClose (callback = (result, form) => {}) { this.dialog.addEventListener("close", () => { const formElem = this.dialog.querySelector("form"); const formData = formElem ? new FormData(formElem) : null; callback(this.dialog.returnValue, formData); formElem.reset(); this.dialog.close(); }); } } customElements.define("modal-dialog", ModalDialog); export function dialog (header, body, onclose = async (result, form) => { }) { const dialog = document.createElement("dialog"); dialog.innerHTML = `

`; dialog.className = "w3-container w3-card w3-border-0"; dialog.querySelector("#prompt").innerText = header; dialog.querySelector("#body").innerHTML = body; dialog.addEventListener("close", async () => { const formElem = dialog.querySelector("form"); const formData = formElem ? new FormData(formElem) : null; await onclose(dialog.returnValue, formData); dialog.parentElement.removeChild(dialog); }); if (!dialog.querySelector("form")) { dialog.querySelector("#confirm").addEventListener("click", async (e) => { e.preventDefault(); dialog.close(e.target.value); }); dialog.querySelector("#cancel").addEventListener("click", async (e) => { e.preventDefault(); dialog.close(e.target.value); }); } document.body.append(dialog); dialog.showModal(); return dialog; } export function alert (message) { const dialog = document.createElement("dialog"); dialog.innerHTML = `

${message}

`; dialog.className = "w3-container w3-card w3-border-0"; document.body.append(dialog); dialog.showModal(); dialog.addEventListener("close", () => { dialog.parentElement.removeChild(dialog); }); return dialog; }