diff options
-rw-r--r-- | index.html | 5 | ||||
-rw-r--r-- | scripts/gui-common/widgets.js | 208 | ||||
-rw-r--r-- | scripts/main.js | 5 | ||||
-rw-r--r-- | styles/widgets.css | 6 |
4 files changed, 213 insertions, 11 deletions
@@ -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); |