diff --git a/web/css/form.css b/web/css/form.css index c5a9f9b..0e80897 100644 --- a/web/css/form.css +++ b/web/css/form.css @@ -76,6 +76,6 @@ input[type="radio"] { } dialog { - max-width: calc(min(50%, 80ch)); + max-width: calc(min(100% - 16px, 80ch)); color: var(--main-text-color); } \ No newline at end of file diff --git a/web/css/style.css b/web/css/style.css index dbffbfa..e013dd7 100644 --- a/web/css/style.css +++ b/web/css/style.css @@ -114,6 +114,12 @@ hr { align-items: center; } +.column-reverse { + flex-direction: column-reverse; + row-gap: 10px; + align-items: center; +} + .wrap { flex-wrap: wrap; row-gap: 10px; diff --git a/web/scripts/dialog.js b/web/scripts/dialog.js index 291e83f..a9048bf 100644 --- a/web/scripts/dialog.js +++ b/web/scripts/dialog.js @@ -42,23 +42,114 @@ export function dialog (template, onclose = async (result, form) => { }) { } export function alert (message) { - const dialog = document.createElement("dialog"); - dialog.innerHTML = ` -
-

${message}

-
- -
-
- `; - dialog.className = "w3-container w3-card w3-border-0"; + const dialog = document.querySelector("#alert-dialog"); + if (dialog == null) { + const dialog = document.createElement("dialog"); + dialog.id = "alert-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; + } + else { + console.error("Attempted to create a new alert while one already exists!"); + return null; + } +} - document.body.append(dialog); - dialog.showModal(); +class ErrorDialog extends HTMLElement { + shadowRoot = null; + dialog = null; + errors = null; - dialog.addEventListener("close", () => { - dialog.parentElement.removeChild(dialog); - }); + constructor () { + super(); + this.shadowRoot = this.attachShadow({ mode: "open" }); + this.shadowRoot.innerHTML = ` + + + + + +
+

Error

+
+
+ + +
+
+
+ `; + this.dialog = this.shadowRoot.querySelector("dialog"); + this.errors = this.shadowRoot.querySelector("#errors") + for (const control of this.shadowRoot.querySelector("#controls").childNodes) { + control.addEventListener("click", async (e) => { + e.preventDefault(); + this.dialog.close(e.target.value); + }); + } + + this.dialog.addEventListener("close", () => { + if (this.dialog.returnValue == "ok") {} + else if (this.dialog.returnValue == "copy") { + let errors = "" + for (const error of this.errors.childNodes) { + errors += `${error.innerText}\n` + } + navigator.clipboard.writeText(errors) + } + this.parentElement.removeChild(this); + }); + } + + appendError (error) { + error = `${(new Date()).toUTCString()}: ${error}`; + const p = document.createElement("p"); + p.innerText = error; + this.errors.appendChild(p); + } + + showModal () { + this.dialog.showModal(); + } +} + +customElements.define("error-dialog", ErrorDialog); + +export function error (message) { + let dialog = document.querySelector("error-dialog"); + if (dialog == null) { + dialog = document.createElement("error-dialog"); + document.body.append(dialog); + dialog.appendError(message); + dialog.showModal(); + } + else { + dialog.appendError(message); + dialog.showModal(); + } return dialog; } diff --git a/web/scripts/index.js b/web/scripts/index.js index 63b22c6..2685365 100644 --- a/web/scripts/index.js +++ b/web/scripts/index.js @@ -219,7 +219,7 @@ async function getInstancesFragment () { async function refreshInstances () { let instances = await getInstancesFragment(); if (instances.status !== 200) { - alert(`Error fetching instances: ${instances.status} ${instances.error !== undefined ? instances.error : ""}`); + error(`Error fetching instances: ${instances.status} ${instances.error !== undefined ? instances.error : ""}`); } else { instances = instances.data;