2025-01-24 10:36:24 +01:00
|
|
|
// Generic state changer with visual feedback
|
2025-01-29 17:35:54 +01:00
|
|
|
// Tooltips require boostrap.Tooltip
|
2025-01-24 10:36:24 +01:00
|
|
|
class BrickChanger {
|
|
|
|
constructor(prefix, id, url, parent = undefined) {
|
|
|
|
this.prefix = prefix
|
|
|
|
this.html_element = document.getElementById(`${prefix}-${id}`);
|
2025-01-29 16:14:52 +01:00
|
|
|
this.html_clear = document.getElementById(`clear-${prefix}-${id}`);
|
2025-01-24 10:36:24 +01:00
|
|
|
this.html_status = document.getElementById(`status-${prefix}-${id}`);
|
2025-01-29 17:35:54 +01:00
|
|
|
this.html_status_tooltip = undefined;
|
2025-02-03 16:08:11 +01:00
|
|
|
this.html_type = undefined;
|
2025-01-24 10:36:24 +01:00
|
|
|
this.url = url;
|
|
|
|
|
|
|
|
if (parent) {
|
|
|
|
this.html_parent = document.getElementById(`${parent}-${id}`);
|
|
|
|
this.parent_dataset = `data-${prefix}`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register an event depending on the type
|
2025-02-03 16:08:11 +01:00
|
|
|
let listener = undefined;
|
|
|
|
switch (this.html_element.tagName) {
|
|
|
|
case "INPUT":
|
|
|
|
this.html_type = this.html_element.getAttribute("type");
|
|
|
|
|
|
|
|
switch (this.html_type) {
|
|
|
|
case "checkbox":
|
|
|
|
case "text":
|
|
|
|
listener = "change";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw Error(`Unsupported input type for BrickChanger: ${this.html_type}`);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "SELECT":
|
|
|
|
this.html_type = "select";
|
|
|
|
listener = "change";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw Error(`Unsupported HTML tag type for BrickChanger: ${this.html_element.tagName}`);
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.html_element.addEventListener(listener, ((changer) => (e) => {
|
|
|
|
changer.change();
|
|
|
|
})(this));
|
2025-01-29 16:14:52 +01:00
|
|
|
|
|
|
|
if (this.html_clear) {
|
|
|
|
this.html_clear.addEventListener("click", ((changer) => (e) => {
|
|
|
|
changer.html_element.value = "";
|
|
|
|
changer.change();
|
|
|
|
})(this));
|
|
|
|
}
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Clean the status
|
|
|
|
status_clean() {
|
|
|
|
if (this.html_status) {
|
|
|
|
const to_remove = Array.from(
|
|
|
|
this.html_status.classList.values()
|
|
|
|
).filter(
|
|
|
|
(name) => name.startsWith('ri-') || name.startsWith('text-') || name.startsWith('bg-')
|
|
|
|
);
|
|
|
|
|
|
|
|
if (to_remove.length) {
|
|
|
|
this.html_status.classList.remove(...to_remove);
|
|
|
|
}
|
2025-01-29 17:35:54 +01:00
|
|
|
|
|
|
|
if (this.html_status_tooltip) {
|
|
|
|
this.html_status_tooltip.dispose();
|
|
|
|
this.html_status_tooltip = undefined;
|
|
|
|
}
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the status to Error
|
2025-01-29 17:35:54 +01:00
|
|
|
status_error(message) {
|
2025-01-24 10:36:24 +01:00
|
|
|
if (this.html_status) {
|
|
|
|
this.status_clean();
|
|
|
|
this.html_status.classList.add("ri-alert-line", "text-danger");
|
2025-01-29 17:35:54 +01:00
|
|
|
|
|
|
|
this.html_status_tooltip = new bootstrap.Tooltip(this.html_status, {
|
|
|
|
"title": message,
|
|
|
|
})
|
|
|
|
this.html_status_tooltip.show();
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the status to OK
|
|
|
|
status_ok() {
|
|
|
|
if (this.html_status) {
|
|
|
|
this.status_clean();
|
|
|
|
this.html_status.classList.add("ri-checkbox-circle-line", "text-success");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the status to Unknown
|
|
|
|
status_unknown() {
|
|
|
|
if (this.html_status) {
|
|
|
|
this.status_clean();
|
|
|
|
this.html_status.classList.add("ri-question-line", "text-warning");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async change() {
|
|
|
|
try {
|
|
|
|
this.status_unknown();
|
|
|
|
|
|
|
|
// Grab the value depending on the type
|
2025-02-03 16:08:11 +01:00
|
|
|
let value = undefined;
|
|
|
|
|
|
|
|
switch(this.html_type) {
|
|
|
|
case "checkbox":
|
|
|
|
value = this.html_element.checked;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "text":
|
|
|
|
case "select":
|
|
|
|
value = this.html_element.value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw Error("Unsupported input type for BrickChanger");
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const response = await fetch(this.url, {
|
|
|
|
method: "POST",
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
name: this.prefix,
|
|
|
|
value: value,
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!response.ok) {
|
2025-01-29 17:35:54 +01:00
|
|
|
throw new Error(`Response status: ${response.status} (${response.statusText})`);
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const json = await response.json();
|
|
|
|
|
|
|
|
if ("error" in json) {
|
|
|
|
throw new Error(`Error received: ${json.error}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
this.status_ok();
|
|
|
|
|
|
|
|
// Update the parent
|
|
|
|
if (this.html_parent) {
|
|
|
|
if (this.html_type == "checkbox") {
|
|
|
|
value = Number(value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not going through dataset to avoid converting
|
|
|
|
this.html_parent.setAttribute(this.parent_dataset, value);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.log(error.message);
|
|
|
|
|
2025-01-29 17:35:54 +01:00
|
|
|
this.status_error(error.message);
|
2025-01-24 10:36:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper to setup the changer
|
2025-01-24 15:55:15 +01:00
|
|
|
const setup_changers = () => document.querySelectorAll("*[data-changer-id]").forEach(
|
|
|
|
el => new BrickChanger(
|
|
|
|
el.dataset.changerPrefix,
|
|
|
|
el.dataset.changerId,
|
|
|
|
el.dataset.changerUrl,
|
|
|
|
el.dataset.changerParent
|
|
|
|
)
|
|
|
|
);
|