127 lines
4.6 KiB
JavaScript
Raw Normal View History

// retoor <retoor@molodetz.nl>
import { Component } from "./Component.js";
export class AppDialog extends Component {
connectedCallback() {
if (this._built) {
return;
}
this._built = true;
this.resolver = null;
this.mode = "confirm";
this.lastFocus = null;
this.build();
}
build() {
const overlay = document.createElement("div");
overlay.className = "dialog-overlay";
overlay.setAttribute("role", "dialog");
overlay.setAttribute("aria-modal", "true");
overlay.innerHTML =
'<div class="card dialog-card">' +
'<div class="modal-header"><h3 class="dialog-title"></h3>' +
'<button type="button" class="modal-close btn-ghost btn-icon dialog-close">&times;</button></div>' +
'<p class="dialog-message"></p>' +
'<div class="dialog-field" hidden><label class="dialog-field-label"></label>' +
'<input type="text" class="dialog-input" autocomplete="off"></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-secondary dialog-cancel">Cancel</button>' +
'<button type="button" class="btn btn-primary dialog-confirm">OK</button>' +
"</div></div>";
this.appendChild(overlay);
this.overlay = overlay;
this.titleEl = overlay.querySelector(".dialog-title");
this.messageEl = overlay.querySelector(".dialog-message");
this.field = overlay.querySelector(".dialog-field");
this.fieldLabel = overlay.querySelector(".dialog-field-label");
this.input = overlay.querySelector(".dialog-input");
this.cancelBtn = overlay.querySelector(".dialog-cancel");
this.confirmBtn = overlay.querySelector(".dialog-confirm");
overlay.addEventListener("click", (e) => {
if (e.target === overlay) this.dismiss();
});
overlay.querySelector(".dialog-close").addEventListener("click", () => this.dismiss());
this.cancelBtn.addEventListener("click", () => this.dismiss());
this.confirmBtn.addEventListener("click", () => this.accept());
this.input.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
e.preventDefault();
this.accept();
}
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && this.overlay.classList.contains("visible")) {
e.preventDefault();
this.dismiss();
}
});
}
open(mode, options) {
const opts = options || {};
this.mode = mode;
this.titleEl.textContent = opts.title || (mode === "prompt" ? "Rename" : mode === "alert" ? "Notice" : "Confirm");
this.messageEl.textContent = opts.message || "";
this.messageEl.hidden = !opts.message;
this.confirmBtn.textContent = opts.confirmLabel || (mode === "alert" ? "OK" : mode === "prompt" ? "Save" : "Confirm");
this.cancelBtn.textContent = opts.cancelLabel || "Cancel";
this.cancelBtn.hidden = mode === "alert";
this.confirmBtn.classList.toggle("dialog-danger", !!opts.danger);
if (mode === "prompt") {
this.field.hidden = false;
this.fieldLabel.textContent = opts.label || "";
this.fieldLabel.hidden = !opts.label;
this.input.value = opts.value || "";
this.input.placeholder = opts.placeholder || "";
} else {
this.field.hidden = true;
}
this.lastFocus = document.activeElement;
this.overlay.classList.add("visible");
const focusTarget = mode === "prompt" ? this.input : this.confirmBtn;
setTimeout(() => {
focusTarget.focus();
if (mode === "prompt") this.input.select();
}, 20);
return new Promise((resolve) => { this.resolver = resolve; });
}
accept() {
const value = this.mode === "prompt" ? this.input.value : true;
this.close(value);
}
dismiss() {
this.close(this.mode === "prompt" ? null : this.mode === "alert" ? undefined : false);
}
close(value) {
this.overlay.classList.remove("visible");
if (this.lastFocus && this.lastFocus.focus) this.lastFocus.focus();
const resolve = this.resolver;
this.resolver = null;
if (resolve) resolve(value);
}
confirm(options) {
return this.open("confirm", options);
}
prompt(options) {
return this.open("prompt", options);
}
alert(options) {
return this.open("alert", options);
}
}
customElements.define("dp-dialog", AppDialog);