Files
exui/static/controls.js
2023-11-05 06:06:30 +01:00

511 lines
16 KiB
JavaScript

import * as util from "./util.js";
export class Label {
constructor(className, data, data_id) {
this.data = data;
this.data_id = data_id;
this.element = util.newDiv(null, className);
this.element.innerHTML = "|";
}
refresh() {
this.element.innerHTML = this.data[this.data_id];
}
}
export class LabelTextbox {
constructor(classNameLabel, textLabel, className, placeholder, data, data_id, validateFunc, updateFunc, cb_auto_id = null, multiline = false) {
this.data = data;
this.data_id = data_id;
this.element = util.newVFlex("vflex_line");
if (textLabel) {
this.label = util.newDiv(null, classNameLabel);
this.label.innerHTML = textLabel;
}
if (!multiline) {
this.tb = document.createElement("input");
this.tb.className = className;
this.tb.type = "text";
} else {
this.tb = document.createElement("textarea");
this.tb.className = className;
this.tb.rows = 5;
}
if (placeholder) this.tb.placeholder = placeholder;
this.tb.spellcheck = false;
this.tb.value = this.data[this.data_id] ? this.data[this.data_id] : "";
this.tb.addEventListener("focus", () => {
//console.log(this.data[this.data_id]);
this.textbox_initial = this.tb.value;
});
this.tb.addEventListener("focusout", () => {
if (this.textbox_initial != this.tb.value) {
if (!validateFunc || validateFunc(this.tb.value)) {
this.data[this.data_id] = this.interpret(this.tb.value);
if (updateFunc) updateFunc();
} else {
this.tb.value = this.textbox_initial;
}
}
});
this.tb.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
this.tb.value = this.textbox_initial;
this.tb.blur();
event.preventDefault();
}
if (event.key === "Enter" && !event.shiftKey) {
this.tb.blur();
}
});
if (textLabel) this.element.appendChild(this.label);
this.element.appendChild(this.tb);
this.cb_auto_id = cb_auto_id;
if (this.cb_auto_id) {
this.chkb = document.createElement("input");
this.chkb.type = "checkbox";
this.chkb.id = "checkbox_" + cb_auto_id;
this.chkb.className = "checkbox";
this.element.appendChild(this.chkb);
this.chkb_label = document.createElement("label");
this.chkb_label.htmlFor = this.chkb.id;
this.chkb_label.className = "checkbox-label";
this.chkb_label.innerHTML = "Auto";
this.element.appendChild(this.chkb_label);
this.chkb.addEventListener("change", () => {
this.data[this.cb_auto_id] = this.chkb.checked;
if (updateFunc) updateFunc();
});
}
}
interpret(value) {
return value;
}
refresh() {
let v = this.data[this.data_id] ? this.data[this.data_id] : null;
this.tb.value = v;
this.refreshCB();
}
refreshCB() {
if (this.cb_auto_id) {
this.chkb.checked = this.data[this.cb_auto_id];
if (this.chkb.checked)
this.tb.classList.add("hidden");
else
this.tb.classList.remove("hidden");
}
}
setVisible(visible) {
if (visible)
this.element.classList.remove("hidden");
else
this.element.classList.add("hidden");
}
}
export class LargeTextbox extends LabelTextbox {
constructor(className, placeholder, data, data_id, validateFunc, updateFunc) {
super(null, null, className, placeholder, data, data_id, validateFunc, updateFunc, null, true);
}
}
export class LabelTextboxButton extends LabelTextbox {
constructor(classNameLabel, textLabel, className, placeholder, data, data_id, validateFunc, updateFunc, cb_auto_id = null, buttonText, buttonFunc) {
super(classNameLabel, textLabel, className, placeholder, data, data_id, validateFunc, updateFunc, cb_auto_id);
this.buttonFunc = buttonFunc;
this.button = document.createElement("span");
this.button.className = "linkbutton enabled";
this.button.innerHTML = buttonText;
this.element.appendChild(this.button);
this.button.addEventListener("click", () => {
if (this.buttonFunc) this.buttonFunc();
});
}
}
export class LabelNumbox extends LabelTextbox {
constructor(classNameLabel, textLabel, className, placeholder, data, data_id, min, max, decimals, updateFunc, cb_auto_id = null) {
super(classNameLabel, textLabel, className, placeholder, data, data_id, null, updateFunc, cb_auto_id);
this.min = min;
this.max = max;
this.decimals = decimals;
}
interpret(value) {
let num = parseFloat(value);
if (isNaN(num)) num = 0;
if (num < this.min) num = this.min;
if (num > this.max) num = this.max;
num = parseFloat(num.toFixed(this.decimals));
return num;
}
refresh() {
this.tb.value = this.data[this.data_id].toFixed(this.decimals);
this.refreshCB();
}
}
export class LabelText {
constructor(classNameLabel, textLabel, className, data, data_id) {
this.data = data;
this.data_id = data_id;
this.element = util.newVFlex("vflex_line");
this.label = util.newDiv(null, classNameLabel);
this.label.innerHTML = textLabel;
this.text = util.newDiv(null, className);
this.element.appendChild(this.label);
this.element.appendChild(this.text);
}
refresh() {
this.text.innerHTML = this.data[this.data_id];
}
}
export class LabelCombobox {
constructor(classNameLabel, textLabel, className, options, data, data_id, updateFunc) {
this.data = data;
this.data_id = data_id;
this.element = util.newVFlex("vflex_line");
this.label = util.newDiv(null, classNameLabel);
this.label.innerHTML = textLabel;
this.cb = document.createElement("select");
this.cb.className = className;
for (let i = 0; i < options.length; i++) this.cb.add(new Option(options[i], options[i]));
this.cb.addEventListener("change", (event) => {
this.data[this.data_id] = this.cb.value;
if (updateFunc) updateFunc();
} );
this.element.appendChild(this.label);
this.element.appendChild(this.cb);
this.refresh();
}
refresh() {
this.cb.value = this.data[this.data_id];
}
}
export class LabelCheckbox {
constructor(classNameLabel, textLabel, className, text, data, data_id, updateFunc) {
this.data = data;
this.data_id = data_id;
this.element = util.newVFlex("vflex_line");
if (textLabel) {
this.label = util.newDiv(null, classNameLabel);
this.label.innerHTML = textLabel;
this.element.appendChild(this.label);
}
this.chkb = document.createElement("input");
this.chkb.type = "checkbox";
this.chkb.id = "checkbox_" + data_id;
this.chkb.className = "checkbox";
this.element.appendChild(this.chkb);
this.chkb_label = document.createElement("label");
this.chkb_label.htmlFor = this.chkb.id;
this.chkb_label.className = className;
this.chkb_label.innerHTML = text;
this.element.appendChild(this.chkb_label);
this.chkb.addEventListener("change", () => {
this.data[this.data_id] = this.chkb.checked;
if (updateFunc) updateFunc();
});
this.refresh();
}
refresh() {
this.chkb.checked = this.data[this.data_id];
}
}
export class Button {
constructor(text, clickFunc, extra_style = null) {
this.element = util.newDiv(null, "textbutton", text);
if (extra_style) this.element.classList.add(extra_style);
this.enabled = true;
this.clickFunc = clickFunc;
this.hidden = false;
this.element.addEventListener("click", () => {
if (!this.enabled) return;
if (this.clickFunc) this.clickFunc();
});
}
setEnabled(enabled) {
this.enabled = enabled;
this.refresh();
}
setHidden(hidden) {
this.hidden = hidden;
this.refresh();
}
refresh() {
if (this.enabled) {
this.element.classList.add("enabled");
this.element.classList.remove("disabled");
} else {
this.element.classList.remove("enabled");
this.element.classList.add("disabled");
}
if (this.hidden) {
this.element.classList.add("hidden");
} else {
this.element.classList.remove("hidden");
}
}
}
export class LinkButton {
constructor(text, confirmText, clickFunc, extraStyle = null) {
this.text = text;
this.confirmText = confirmText;
this.clickFunc = clickFunc;
this.element = util.newDiv(null, null);
this.inner = document.createElement("span");
this.inner.classList.add("linkbutton");
this.inner.classList.add("no-select");
if (extraStyle) this.inner.classList.add(extraStyle);
this.element.appendChild(this.inner);
this.enabled = true;
this.refresh();
this.inner.innerHTML = this.text;
this.inner.addEventListener("click", () => {
if (!this.enabled) return;
if (!this.confirmText) {
if (this.clickFunc) this.clickFunc();
} else {
if (this.inner.classList.contains("danger")) {
this.inner.innerHTML = this.text;
this.inner.classList.remove("danger");
if (this.clickFunc) this.clickFunc();
clearTimeout(this.timeoutID);
} else {
this.inner.innerHTML = this.confirmText;
this.inner.classList.add("danger");
this.timeoutID = setTimeout(() => {
this.inner.classList.remove("danger");
this.inner.innerHTML = this.text;
}, 2000);
}
}
});
}
setEnabled(enabled) {
this.enabled = enabled;
this.refresh();
}
refresh() {
if (this.enabled) {
this.inner.classList.add("enabled");
this.inner.classList.remove("disabled");
} else {
this.inner.classList.remove("enabled");
this.inner.classList.add("disabled");
}
}
setVisible(visible) {
if (visible)
this.element.classList.remove("hidden");
else
this.element.classList.add("hidden");
}
}
export class EditableLabel {
constructor(value, allow_empty = false, editFunc = null) {
this.value = value;
this.element = util.newDiv(null, null);
this.inner = document.createElement("p");
this.inner.innerHTML = value;
this.element.appendChild(this.inner);
this.setEditable(false);
this.inner.addEventListener("click", () => {
if (this.inner.classList.contains("editable-label"))
this.beginEdit();
});
this.allow_empty = allow_empty;
this.editFunc = editFunc;
}
setEditable(editable) {
if (editable) {
this.inner.classList.add("editable-label");
} else {
this.inner.classList.remove("editable-label");
}
}
beginEdit() {
util.makeEditable(this.inner, (value) => {
if (!this.allow_empty && value.trim() == "") return false;
return true;
}, (id, value) => {
if (this.editFunc)
this.editFunc(value);
});
}
}
export class CollapsibleSection {
constructor(className, heading) {
this.element = util.newDiv(null, "block");
let div_head = util.newDiv(null, (className ? className + " " : "") + "block-header no-select");
div_head.innerHTML = "<span class='arrow'>⯆</span>" + heading;
this.element.appendChild(div_head);
div_head.addEventListener('click', function() {
let c = this.nextElementSibling;
let a = this.children[0];
if (c.classList.contains("shown")) {
a.innerHTML = "⯈";
c.classList.remove("shown");
} else {
a.innerHTML = "⯆";
c.classList.add("shown");
}
});
this.inner = util.newDiv(null, (className ? className + " " : "") + "block-contents shown");
this.element.appendChild(this.inner);
}
setVisible(visible) {
if (visible)
this.element.classList.remove("hidden");
else
this.element.classList.add("hidden");
}
}
export class SettingsSlider {
constructor(classNameLabel, textLabel, classNameSlider, classNameTextbox, decimals, min, max, alt_display, data, data_id, updateFunc = null) {
this.data = data;
this.data_id = data_id;
this.decimals = decimals;
this.min = min;
this.max = max;
this.alt_display = alt_display;
this.updateFunc = updateFunc;
this.element = util.newDiv(null, "item-split");
let label = util.newDiv(null, classNameLabel, textLabel);
this.exp = Math.pow(10, decimals);
this.slider = document.createElement('input');
this.slider.type= "range";
this.slider.className = classNameSlider;
this.slider.min = this.exp * this.min;
this.slider.max = this.exp * this.max;
this.slider.value = this.slider.min;
this.textbox = document.createElement('input');
this.textbox.type= "text";
this.textbox.className = classNameTextbox;
this.textbox.rows = 1;
this.textbox.spellcheck = false;
this.slider.addEventListener("input", (event) => {
this.sliderInput();
});
this.slider.addEventListener("change", (event) => {
if (updateFunc) updateFunc();
});
this.textbox.addEventListener("focus", (event) => {
this.originalValue = this.textbox.value;
});
this.textbox.addEventListener("blur", (event) => {
let nvalue = parseFloat(this.textbox.value);
if (isNaN(nvalue)) {
this.textbox.value = this.originalValue;
return;
}
if (nvalue < this.min) nvalue = this.min;
if (nvalue > this.max) nvalue = this.max;
nvalue *= this.exp;
this.slider.value = "" + nvalue;
this.sliderInput();
if (updateFunc) updateFunc();
});
this.refresh();
this.element.appendChild(label);
this.element.appendChild(this.slider);
this.element.appendChild(this.textbox);
}
sliderInput() {
let v = parseFloat(this.slider.value) / this.exp;
this.data[this.data_id] = v;
let t = v.toFixed(this.decimals);
let st = "";
if (this.alt_display) {
if (Object.keys(this.alt_display).includes(t)) st = this.alt_display[t];
}
if (st != "") { this.textbox.value = st; this.textbox.classList.add("special"); }
else { this.textbox.value = t; this.textbox.classList.remove("special"); }
}
refresh() {
let v = this.data[this.data_id];
v *= this.exp;
this.slider.value = "" + v;
this.sliderInput();
}
}
export class CheckboxLabel extends LabelCheckbox {
constructor(className, text, data, data_id, updateFunc) {
super(null, null, className, text, data, data_id, updateFunc);
}
}