summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--index.html5
-rw-r--r--scripts/gui-common/widgets.js208
-rw-r--r--scripts/main.js5
-rw-r--r--styles/widgets.css6
4 files changed, 213 insertions, 11 deletions
diff --git a/index.html b/index.html
index 81722a9..c075753 100644
--- a/index.html
+++ b/index.html
@@ -33,11 +33,6 @@
</nav>
<content>
- <div class="widget sel-button h">
- <div class="widget button">Heat</div>
- <div class="widget button">Cool</div>
- <div class="widget button" selected>Eco</div>
- </div>
</content>
<script src="scripts/gui-common/color.js"></script>
diff --git a/scripts/gui-common/widgets.js b/scripts/gui-common/widgets.js
index 34660f4..0f92d08 100644
--- a/scripts/gui-common/widgets.js
+++ b/scripts/gui-common/widgets.js
@@ -175,6 +175,10 @@ class WidgetToggle extends Widget
#bound = null;
+ #falseVal = false;
+
+ #trueVal = true;
+
constructor ()
{
super();
@@ -184,6 +188,7 @@ class WidgetToggle extends Widget
this.addEventListener("mouseup", this.#toggle);
this.addEventListener("mouseleave", this.#leave);
this.addEventListener("mouseenter", this.#enter);
+ this.addEventListener("change", this.#update_ui);
this.#bound = this.#toggle.bind(this);
}
@@ -204,11 +209,21 @@ class WidgetToggle extends Widget
}
if (((1 << event.button) & this.primed) == 0)
return;
- this.set(!this.get());
- this.element.classList.toggle("active", this.get());
+
+ if (this.get() == this.#trueVal)
+ this.set(this.#falseVal);
+ else
+ this.set(this.#trueVal);
+
+ this.#update_ui();
this.primed -= (1 << event.button);
}
+ #update_ui()
+ {
+ this.element.classList.toggle("active", this.get() == this.#trueVal);
+ }
+
/** @param {MouseEvent} event */
#leave(event)
{
@@ -226,6 +241,20 @@ class WidgetToggle extends Widget
this.gone = 0;
window.removeEventListener("mouseup", this.#bound);
}
+
+ setFalseVal(fv)
+ {
+ if (this.get() == this.#falseVal)
+ this.set(fv)
+ this.#falseVal = fv;
+ }
+
+ setTrueVal(tv)
+ {
+ if (this.get() == this.#trueVal)
+ this.set(tv)
+ this.#trueVal = tv;
+ }
}
class WidgetCheckbox extends WidgetToggle {
@@ -786,7 +815,180 @@ class WidgetThermostat extends Widget
}
}
+class WidgetSelectButton extends Widget
+{
+ /** @type {Array<WidgetToggle>} */
+ #swgs = [];
+
+ /** @type {Array<any>} */
+ #svls = [];
+
+ /** @type {Array<number>} */
+ #selected = [];
+
+ /** @type {number} */
+ #maxSelections = 1;
+
+ /** @type {boolean} */
+ #removeLastOver = true;
+
+
+ /** @type {(e: CustomEvent) => void} */
+ #binder = null;
+
+ /**
+ * Constructor
+ * @param {number} max Maximum allowed to select
+ * @param {boolean} remove Behaviour when user selects over the maximum. If true, remove the oldest selection to make room. If false, lock out selections until user removes one.
+ */
+ constructor(max = 1, remove = true)
+ {
+ super();
+ this.element.classList.add("sel-button");
+
+ this.#binder = this.#selection_change.bind(this);
+
+ this.#maxSelections = max;
+ this.#removeLastOver = remove;
+ }
+
+ #update_val()
+ {
+ let out = [];
+ for(let i = 0; i < this.#selected.length; i++)
+ {
+ out.push(this.#svls[this.#selected[i]]);
+ }
+ this.set(out);
+ }
+
+ #selection_add(idx)
+ {
+ if (this.#selected.length < this.#maxSelections)
+ {
+ this.#selected.push(idx);
+ }
+ else if (this.#removeLastOver)
+ {
+ if(this.#selected.length > 0)
+ {
+ let swap = this.#selected.shift();
+ this.#swgs[swap].set(-swap - 1);
+ }
+ this.#selected.push(idx);
+ }
+ else
+ {
+ this.#swgs[idx].set(-idx - 1);
+ }
+
+ if (!this.#removeLastOver && this.#selected.length >= this.#maxSelections)
+ {
+ for (let i = 0; i < this.#swgs.length; i++)
+ {
+ if (this.#swgs[i].get() < 0)
+ {
+ this.#swgs[i].setInactive(true);
+ }
+ }
+ }
+
+ this.#update_val();
+ }
+
+ #selection_remove(idx)
+ {
+ for(let i = 0; i < this.#selected.length; i++)
+ {
+ if (this.#selected[i] == idx)
+ {
+ this.#selected.splice(i, 1);
+ i--;
+ }
+ }
+
+ if (this.#selected.length == this.#maxSelections - 1 && this.#removeLastOver == false)
+ {
+ for (let i = 0; i < this.#swgs.length; i++)
+ {
+ if (this.#swgs[i].get() < 0)
+ {
+ this.#swgs[i].setInactive(false);
+ }
+ }
+ }
+
+ this.#update_val();
+ }
+
+ #selection_change(event)
+ {
+ let idx = Math.abs(event.detail.get()) - 1;
+ if (event.detail.get() < 0)
+ {
+ this.#selection_remove(idx);
+ }
+ else
+ {
+ this.#selection_add(idx);
+ }
+ }
+
+ /**
+ * Add a new selection with the given
+ * innerHTML and value when selected
+ * @param {string} inner Inner HTML of the selection button
+ * @param {*} val Value emitted when this value is selected
+ * @returns {number} The index of the new option
+ */
+ addOption(inner, val)
+ {
+ this.#swgs.push(new WidgetToggle());
+ let idx = this.#swgs.length;
+ this.#swgs[idx - 1].setTrueVal(idx);
+ this.#swgs[idx - 1].setFalseVal(-idx);
+ this.element.appendChild(this.#swgs[idx - 1].element);
+ this.#swgs[idx - 1].element.innerHTML = inner;
+ this.#swgs[idx - 1].element.classList.remove("toggle");
+ this.#swgs[idx - 1].addEventListener("change", this.#binder);
+ this.#svls.push(val);
+ }
+
+ delOption(index)
+ {
+ this.#swgs[index].element.remove();
+ this.#swgs[index].removeEventListener("change", this.#binder);
+ this.#swgs.splice(index, 1);
+ this.#svls.splice(index, 1);
+
+ let need_update = false;
+ for (let i = 0; i < this.#selected.length; i++)
+ {
+ if (this.#selected[i] == index)
+ {
+ this.#selected.splice(index, 1);
+ need_update = true;
+ i--;
+ }
+ else if (this.#selected[i] > index)
+ {
+ this.#selected[i]--;
+ }
+ }
+
+ if (needs_update)
+ {
+ this.#update_val();
+ }
+ }
+
+ indexOf(val)
+ {
+ return this.#svls.indexOf(val);
+ }
+}
+
/** @typedef {Widget<number> & {getGague: () => number, setGague: (value: number) => void}} WidgetThermostat */
/** @typedef {Widget<any> & {addSelection: (name: string, value: any) => void, setSelection: (name: string) => boolean, removeSelection: (name: string) => void, getSelection: () => string}} WidgetSelectButton */
-// export { Widget }; \ No newline at end of file
+// export { Widget };
diff --git a/scripts/main.js b/scripts/main.js
index cedfb79..0ed7fd1 100644
--- a/scripts/main.js
+++ b/scripts/main.js
@@ -20,6 +20,11 @@ class Client {
content.appendChild(this.wheel.element);
this.therm = new WidgetThermostat({temp: 72, gague: 69});
content.appendChild(this.therm.element);
+ this.sel_b = new WidgetSelectButton(2);
+ this.sel_b.addOption("Heat", "heat");
+ this.sel_b.addOption("Cool", "cool");
+ this.sel_b.addOption("Eco", "eco");
+ content.appendChild(this.sel_b.element);
// content.appendChild(Widget("button").el);
// content.appendChild(Widget("checkbox").el);
// content.appendChild(Widget("slider").el);
diff --git a/styles/widgets.css b/styles/widgets.css
index 79ef3ff..feeeb73 100644
--- a/styles/widgets.css
+++ b/styles/widgets.css
@@ -532,7 +532,7 @@ input {
overflow-x: hidden;
}
-.sel-button > div {
+.sel-button > .button {
margin: 5px 5px;
padding: 7px;
@@ -547,13 +547,13 @@ input {
width: fit-content;
}
-.sel-button.h > div {
+.sel-button.h > .button {
height: auto;
width: calc(100% - 12px);
}
-.sel-button > div[selected] {
+.sel-button > .button.active {
--w-bg: var(--w-sel-button-selected);
--w-bg-hover: var(--w-sel-button-selected-hover);
--w-bg-active: var(--w-sel-button-selected-active);