summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scripts/gui-common/color.js4
-rw-r--r--scripts/gui-common/widget-set.js95
-rw-r--r--scripts/gui-common/widgets.js371
-rw-r--r--scripts/main.js5
-rw-r--r--styles/themes/base.css14
-rw-r--r--styles/widgets.css127
6 files changed, 535 insertions, 81 deletions
diff --git a/scripts/gui-common/color.js b/scripts/gui-common/color.js
index eae3971..489096a 100644
--- a/scripts/gui-common/color.js
+++ b/scripts/gui-common/color.js
@@ -1,3 +1,5 @@
+'use strict';
+
class Color
{
/** @type {Array<number>} */
@@ -227,6 +229,8 @@ const BLUE = new Color(0, 0, 1, 1);
const MAGENTA = new Color(1, 0, 1, 1);
const WHITE = new Color(1, 1, 1, 1);
const BLACK = new Color(0, 0, 0, 1);
+const TRANSPARENT_WHITE = new Color(1,1,1,0);
+const TRANSPARENT_BLACK = new Color(0,0,0,0);
const PI_THIRDS = Math.PI / 3;
const TWO_PI = Math.PI * 2;
diff --git a/scripts/gui-common/widget-set.js b/scripts/gui-common/widget-set.js
index b515831..bd6ccd8 100644
--- a/scripts/gui-common/widget-set.js
+++ b/scripts/gui-common/widget-set.js
@@ -1,9 +1,13 @@
+'use strict';
+
/**
* JSDoc types
*/
/**
- * @typedef {"button" | "toggle" | "checkbox" | "slider" | "color-temp" | "color-light" | "color-wheel" | "thermostat" | "scrubber"} DescType
+ * @typedef {"button" | "toggle" | "checkbox" | "radio" |
+ * "slider" | "color-temp" | "color-light" |
+ * "color-wheel" | "thermostat" | "multi-select" | "scrubber"} DescType
*/
/**
@@ -41,6 +45,14 @@
*/
/**
+ * @typedef OSmMultiSelectProps
+ * @property {Array<srting>} labels
+ * @property {Array<any>} values
+ * @property {number} max
+ * @property {boolean} remove
+ */
+
+/**
* @typedef OSmScrubberProps
* @property {number} max
* @property {number} min
@@ -51,7 +63,13 @@
*/
/**
- * @typedef {OSmButtonProps | OSmToggleProps | OSmSliderProps | OSmThermostatProps | OSmScrubberProps} OSmAnyProps
+ * @typedef OSmRadioSelectProps
+ * @property {Array<any>} values
+ * @property {Array<string>} labels
+ */
+
+/**
+ * @typedef {OSmButtonProps | OSmToggleProps | OSmSliderProps | OSmThermostatProps | OSmMultiSelectProps | OSmScrubberProps | OSmRadioSelectProps} OSmAnyProps
*/
/**
@@ -89,11 +107,19 @@
*/
/**
+ * @typedef {DescBase & {type: "multi-select", props: OSmMultiSelectProps, state: Array<number>}} DescMultiSelect
+ */
+
+/**
* @typedef {DescBase & {type: "scrubber", props: OSmScrubberProps, state: number}} DescScrubber
*/
/**
- * @typedef {DescButton | DescToggle | DescSlider | DescColorTemp | DescColorLight | DescColorWheel | DescThermostat | DescScrubber} DescAny
+ * @typedef {DescBase & {type: "radio", props: OSmRadioSelectProps, state: number | null}} DescRadioSelect
+ */
+
+/**
+ * @typedef {DescButton | DescToggle | DescSlider | DescColorTemp | DescColorLight | DescColorWheel | DescThermostat | DescMultiSelect | DescScrubber | DescRadioSelect} DescAny
*/
class WidgetSet extends EventTarget{
@@ -130,7 +156,7 @@ class WidgetSet extends EventTarget{
{
let merge = {...{tv: true, fv: false, text: ""}, ...desc.props};
let out = new WidgetButton(merge.tv, merge.fv);
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
out.element.innerText = merge.text;
return out;
}
@@ -151,7 +177,7 @@ class WidgetSet extends EventTarget{
out = new WidgetToggle(desc.state ? merge.tv : merge.fv, merge.tv, merge.fv);
out.element.innerText = merge.text;
}
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
return out;
}
@@ -164,7 +190,7 @@ class WidgetSet extends EventTarget{
{
let merge = {...{step: 0.1, precision: 1, percent: false}, ...desc.props};
let out = new WidgetSlider(desc.state, merge.min, merge.max, merge.step, merge.precision, merge.percent);
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
return out;
}
@@ -175,7 +201,7 @@ class WidgetSet extends EventTarget{
static parse_color_light(desc)
{
let out = new WidgetColorLight(Math.max(Math.min(desc.state, 1), 0));
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
return out;
}
@@ -186,7 +212,7 @@ class WidgetSet extends EventTarget{
static parse_color_temp(desc)
{
let out = new WidgetColorTemp(Math.max(Math.min(desc.state, 6000), 2700));
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
return out;
}
@@ -198,7 +224,7 @@ class WidgetSet extends EventTarget{
{
let value = new Color(...desc.state.channels);
let out = new WidgetColorWheel(value);
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
return out;
}
@@ -222,7 +248,26 @@ class WidgetSet extends EventTarget{
let cw = new Color(...merge.color_warm.channels);
let out = new WidgetThermostat(desc.state, merge.gague, merge.deviation, cc, ct, cw, merge.ct, merge.tw);
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
+ return out;
+ }
+
+ /**
+ * @param {DescMultiSelect} desc
+ * @returns {WidgetSelectButton}
+ */
+ static parse_multi_select(desc)
+ {
+ let merge = {...{max: 1, remove: true, labels: [], values: []}, ...desc.props};
+
+ let out = new WidgetSelectButton(merge.max, merge.remove);
+
+ for(let i = 0; i < Math.min(merge.labels.length, merge.values.length); i++) {
+ out.add_option(merge.labels[i], merge.values[i]);
+ }
+
+ out.set_indices(desc.state);
+
return out;
}
@@ -239,7 +284,30 @@ class WidgetSet extends EventTarget{
}, ...desc.props};
let out = new WidgetScrubber(desc.state, merge.max, merge.min, merge.step, merge.zones, merge.speed, merge.spring);
- out.setId("name", desc.name);
+ out.set_by_id("name", desc.name);
+ return out;
+ }
+
+ /**
+ *
+ * @param {DescRadioSelect} desc
+ * @returns {WidgetRadio}
+ */
+ static parse_radio(desc)
+ {
+ let merge = {...{
+
+ }, ...desc.props};
+ let out = new WidgetRadioGroup();
+
+ for (let i = 0; i < Math.min(merge.labels.length, merge.values.length); i++) {
+ out.add_option(merge.labels[i], merge.values[i]);
+ }
+
+ if (desc.state !== null)
+ out.set_by_index(desc.state);
+
+ out.set_by_id("name", desc.name);
return out;
}
@@ -276,9 +344,14 @@ class WidgetSet extends EventTarget{
case "thermostat":
out = WidgetSet.parse_thermostat(i);
break;
+ case "multi-select":
+ out = WidgetSet.parse_multi_select(i);
+ break;
case "scrubber":
out = WidgetSet.parse_scrubber(i);
break;
+ case "radio":
+ out = WidgetSet.parse_radio(i);
}
this.widgets[i.name] = out;
}
diff --git a/scripts/gui-common/widgets.js b/scripts/gui-common/widgets.js
index f77c89c..458aabd 100644
--- a/scripts/gui-common/widgets.js
+++ b/scripts/gui-common/widgets.js
@@ -1,14 +1,4 @@
-( function () {
- window.SCROLLING = false;
- function scroll(event) {
- window.SCROLLING = true;
- }
- function unscroll(event) {
- window.SCROLLING = false;
- }
- window.addEventListener("scroll", scroll);
- window.addEventListener("scrollend", unscroll);
-})();
+'use strict';
/**
* The base Widget class. Represents an interactible
@@ -72,7 +62,7 @@ class Widget extends EventTarget{
* @param {*} v
* @param {boolean} [emit]
*/
- setId(id, v, emit = false)
+ set_by_id(id, v, emit = false)
{
this.#value[id] = v;
if (emit)
@@ -166,8 +156,8 @@ class WidgetButton extends Widget
this.addEventListener("touchstart", this.#touchstart);
this.addEventListener("touchend", this.#touchend);
this.#bound = this.#unpress.bind(this);
- this.setId("true", tv);
- this.setId("false", fv);
+ this.set_by_id("true", tv);
+ this.set_by_id("false", fv);
this.set(fv);
}
@@ -246,14 +236,14 @@ class WidgetButton extends Widget
}
}
- setId(id, v, emit = false)
+ set_by_id(id, v, emit = false)
{
if (id == "false" || id == "true")
{
- if (this.get() == this.get(id))
- super.setId("value", v);
+ if (this.get() === this.get(id))
+ super.set_by_id("value", v);
}
- super.setId(id, v, emit);
+ super.set_by_id(id, v, emit);
}
}
@@ -284,8 +274,8 @@ class WidgetToggle extends Widget
this.addEventListener("change", this.update);
this.addEventListener("touchend", this.#touchend);
this.#bound = this.#toggle.bind(this);
- super.setId("false", fv);
- super.setId("true", tv);
+ super.set_by_id("false", fv);
+ super.set_by_id("true", tv);
this.set(value);
}
@@ -307,7 +297,7 @@ class WidgetToggle extends Widget
if (((1 << event.button) & this.#primed) == 0)
return;
- if (this.get() == this.get("true"))
+ if (this.get() === this.get("true"))
this.set(this.get("false"));
else
this.set(this.get("true"));
@@ -318,7 +308,7 @@ class WidgetToggle extends Widget
update()
{
- this.element.classList.toggle("active", this.get() == this.get("true"));
+ this.element.classList.toggle("active", this.get() === this.get("true"));
}
/** @param {MouseEvent} event */
@@ -354,7 +344,7 @@ class WidgetToggle extends Widget
i.clientY > rect.top &&
i.clientY < rect.bottom
) {
- if (this.get() == this.get("true"))
+ if (this.get() === this.get("true"))
this.set(this.get("false"));
else
this.set(this.get("true"));
@@ -363,14 +353,19 @@ class WidgetToggle extends Widget
}
}
- setId(id, v, emit = false)
+ /**
+ * @param {string} id
+ * @param {*} v
+ * @param {boolean} [emit]
+ */
+ set_by_id(id, v, emit = false)
{
if (id == "false" || id == "true")
{
- if (this.get() == this.get(id))
- super.setId("value", v);
+ if (this.get() === this.get(id))
+ super.set_by_id("value", v);
}
- super.setId(id, v, emit);
+ super.set_by_id(id, v, emit);
}
}
@@ -567,11 +562,11 @@ class WidgetSlider extends WidgetDragable
this.addEventListener("change", this.#change);
- super.setId("max", max);
- super.setId("min", min);
- super.setId("step", step);
- super.setId("prec", precision);
- super.setId("perc", percent);
+ super.set_by_id("max", max);
+ super.set_by_id("min", min);
+ super.set_by_id("step", step);
+ super.set_by_id("prec", precision);
+ super.set_by_id("perc", percent);
this.set(value);
}
@@ -651,12 +646,12 @@ class WidgetSlider extends WidgetDragable
update()
{
- let min = this.get("min"), max = this.get("max"), perc = this.get("perc");
+ let min = this.get("min"), max = this.get("max");
if (this.#tmpNum < min || max < this.#tmpNum)
this.#tmpNum = Math.min(Math.max(this.#tmpNum, min), max);
let percent = (this.#tmpNum - min) / (max - min);
- this.#u_detail(this.#detail, this.#tmpNum, percent, perc);
+ this.#u_detail(this.#detail, this.#tmpNum, percent);
this.element.style.setProperty("--percent", percent);
}
@@ -674,9 +669,14 @@ class WidgetSlider extends WidgetDragable
this.#u_detail = updater;
}
- setId(id, v, emit = false)
+ /**
+ * @param {string} id
+ * @param {*} v
+ * @param {boolean} [emit]
+ */
+ set_by_id(id, v, emit = false)
{
- super.setId(id, v, emit);
+ super.set_by_id(id, v, emit);
if (id == "max" || id == "min" || id == "step")
{
this.update();
@@ -729,9 +729,9 @@ class WidgetColorTemp extends WidgetSlider
class WidgetColorLight extends WidgetSlider
{
/** @type {Color} */
- WHITE = new Color(1, 1, 1);
+ static WHITE = new Color(1, 1, 1);
/** @type {Color} */
- BLACK = new Color(0, 0, 0);
+ static BLACK = new Color(0, 0, 0);
constructor (value = 1)
{
@@ -749,7 +749,7 @@ class WidgetColorLight extends WidgetSlider
*/
#update_detail(el, val, percent)
{
- let out = this.BLACK.interpolate(this.WHITE, percent);
+ let out = WidgetColorLight.BLACK.interpolate(WidgetColorLight.WHITE, percent);
this.element.style.setProperty("--detail", out.rgb());
}
}
@@ -926,13 +926,13 @@ class WidgetThermostat extends Widget
this.addEventListener("change", this.update);
- super.setId("deviation", dev);
- super.setId("cold", cold_col);
- super.setId("temp", temp_col);
- super.setId("warm", warm_col);
- super.setId("ct", ct);
- super.setId("tw", tw);
- super.setId("gague", gague);
+ super.set_by_id("deviation", dev);
+ super.set_by_id("cold", cold_col);
+ super.set_by_id("temp", temp_col);
+ super.set_by_id("warm", warm_col);
+ super.set_by_id("ct", ct);
+ super.set_by_id("tw", tw);
+ super.set_by_id("gague", gague);
this.set(value);
}
@@ -963,9 +963,14 @@ class WidgetThermostat extends Widget
this.element.style.setProperty("--detail", color.rgb());
}
- setId(id, v, emit = false)
+ /**
+ * @param {string} id
+ * @param {*} v
+ * @param {boolean} [emit]
+ */
+ set_by_id(id, v, emit = false)
{
- super.setId(id, v, emit);
+ super.set_by_id(id, v, emit);
if (id == "deviation" || id == "cold" || id == "temp" || id == "warm" || id == "ct" || id == "tw" || id == "gague")
{
this.update();
@@ -1035,9 +1040,10 @@ class WidgetSelectButton extends Widget
{
if(this.#selected.length > 0)
{
+ /** @type {number} */
let swap = this.#selected.shift();
this.#swgs[swap].removeEventListener("change", this.#binder);
- this.#swgs[swap].set(-swap - 1);
+ this.#swgs[swap].set_by_id("value", -swap - 1, false);
this.#swgs[swap].addEventListener("change", this.#binder);
}
this.#selected.push(idx);
@@ -1045,7 +1051,7 @@ class WidgetSelectButton extends Widget
else
{
this.#swgs[idx].removeEventListener("change", this.#binder);
- this.#swgs[idx].set(-idx - 1);
+ this.#swgs[idx].set_by_id("value", -swap - 1, false);
this.#swgs[idx].addEventListener("change", this.#binder);
}
@@ -1108,21 +1114,21 @@ class WidgetSelectButton extends Widget
* @param {*} val Value emitted when this value is selected
* @returns {number} The index of the new option
*/
- addOption(inner, val)
+ add_option(inner, val)
{
this.#swgs.push(new WidgetToggle());
let idx = this.#swgs.length;
- this.#swgs[idx - 1].setId("true", idx);
- this.#swgs[idx - 1].setId("false", -idx);
+ this.#swgs[idx - 1].set_by_id("true", idx);
+ this.#swgs[idx - 1].set_by_id("false", -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);
- return idx;
+ return idx - 1;
}
- delOption(index)
+ delete_option(index)
{
this.#swgs[index].element.remove();
this.#swgs[index].removeEventListener("change", this.#binder);
@@ -1144,7 +1150,7 @@ class WidgetSelectButton extends Widget
}
}
- if (needs_update)
+ if (need_update)
{
if (!this.#removeLastOver && this.#selected.length == this.#maxSelections - 1)
{
@@ -1161,6 +1167,25 @@ class WidgetSelectButton extends Widget
{
return this.#svls.indexOf(val);
}
+
+ /**
+ *
+ * @param {Array<number>} indices
+ */
+ set_indices(indices)
+ {
+ for(let i of this.#selected) {
+ this.#swgs[i].set(-i - 1);
+ }
+
+ for(let i of indices) {
+ this.#swgs.set(i + 1);
+ }
+
+ this.#selected = indices;
+
+ this.#update_val();
+ }
}
/**
@@ -1210,13 +1235,13 @@ class WidgetScrubber extends WidgetDragable
this.element.style.setProperty("--percent", 0);
- super.setId("max", max);
- super.setId("min", min);
- super.setId("step", step);
- super.setId("zones", zones);
- super.setId("speed", speed);
- super.setId("spring", spring);
- super.setId("value", value);
+ super.set_by_id("max", max);
+ super.set_by_id("min", min);
+ super.set_by_id("step", step);
+ super.set_by_id("zones", zones);
+ super.set_by_id("speed", speed);
+ super.set_by_id("spring", spring);
+ super.set_by_id("value", value);
this.#binder = this.#i_update.bind(this);
this.#detail.innerText = this.get();
@@ -1364,9 +1389,14 @@ class WidgetScrubber extends WidgetDragable
this.#detail.innerText = this.#tmpNum;
}
- setId(id, v, emit = false)
+ /**
+ * @param {string} id
+ * @param {*} v
+ * @param {boolean} [emit]
+ */
+ set_by_id(id, v, emit = false)
{
- super.setId(id, v, emit);
+ super.set_by_id(id, v, emit);
if (id == "max" || id == "min" || id == "step" || id == "zones" || id == "spring")
{
this.update();
@@ -1379,3 +1409,214 @@ class WidgetScrubber extends WidgetDragable
}
}
}
+
+/**
+ * A toggle widget, similar to a WidgetCheckbox, but meant
+ * to be used for on/off power states.
+ * @extends Widget<*>
+ */
+class WidgetRadio extends Widget {
+ /** @type {number} */
+ #primed = 0;
+
+ /** @type {number} */
+ #gone = 0;
+
+ #bound = null;
+
+ constructor (value = false, tv = true, fv = false)
+ {
+ super();
+ this.element.classList.add("button");
+ this.element.classList.add("radio");
+ this.addEventListener("mousedown", this.#prime);
+ this.addEventListener("mouseup", this.#toggle);
+ this.addEventListener("mouseleave", this.#leave);
+ this.addEventListener("mouseenter", this.#enter);
+ this.addEventListener("change", this.update);
+ this.addEventListener("touchend", this.#touchend);
+ this.#bound = this.#toggle.bind(this);
+ super.set_by_id("false", fv);
+ super.set_by_id("true", tv);
+ this.set(value);
+ }
+
+ /** @param {MouseEvent} event */
+ #prime(event)
+ {
+ this.#primed |= (1 << event.button);
+ }
+
+ /** @param {MouseEvent} event */
+ #toggle(event)
+ {
+ if (this.#gone)
+ {
+ this.#primed = 0;
+ this.#gone = 0;
+ window.removeEventListener("mouseup", this.#bound);
+ }
+ if (((1 << event.button) & this.#primed) == 0)
+ return;
+
+ this.set(this.get("true"));
+
+ this.update();
+ this.#primed -= (1 << event.button);
+ }
+
+ update()
+ {
+ this.element.classList.toggle("active", this.get() === this.get("true"));
+ }
+
+ /** @param {MouseEvent} event */
+ #leave(event)
+ {
+ if (this.#primed == 0)
+ return;
+ this.#gone = 1;
+ window.addEventListener("mouseup", this.#bound);
+ }
+
+ /** @param {MouseEvent} event */
+ #enter(event)
+ {
+ if (this.#primed == 0)
+ return;
+ this.#gone = 0;
+ window.removeEventListener("mouseup", this.#bound);
+ }
+
+ /** @param {TouchEvent} event */
+ #touchend(event)
+ {
+ if (event.changedTouches.length < 1)
+ return;
+
+ let rect = this.element.getBoundingClientRect();
+
+ for (let i of event.changedTouches)
+ {
+ if (i.clientX < rect.right &&
+ i.clientX > rect.left &&
+ i.clientY > rect.top &&
+ i.clientY < rect.bottom
+ ) {
+ this.set(this.get("true"));
+ return;
+ }
+ }
+ }
+
+ /**
+ * @param {string} id
+ * @param {*} v
+ * @param {boolean} [emit]
+ */
+ set_by_id(id, v, emit = false)
+ {
+ if (id == "false" || id == "true")
+ {
+ if (this.get() === this.get(id))
+ super.set_by_id("value", v);
+ }
+ super.set_by_id(id, v, emit);
+ }
+}
+
+class WidgetRadioGroup extends Widget {
+ /** @type {Array<WidgetRadio>} */
+ #radios = [];
+
+ /** @type {Array<any>} */
+ #vals = [];
+
+ /** @type {number} */
+ #selected = null;
+
+ /** @type {(e: CustomEvent) => void} */
+ #binder = null;
+
+ /**
+ * Constructor
+ */
+ constructor()
+ {
+ super();
+ this.element.classList.add("radio-group");
+
+ this.#binder = this.#select.bind(this);
+ }
+
+ #select(event)
+ {
+ let idx = event.detail.widget.get();
+ if (idx == this.#selected)
+ return;
+
+ if (this.#selected !== null)
+ this.#radios[this.#selected].set_by_id("value", false, false);
+ this.#selected = idx;
+ this.set(this.#vals[idx]);
+ }
+
+ /**
+ * Add a new selection with the given
+ * innerHTML and value when selected
+ * @param {string} label Inner HTML of the radio's label
+ * @param {*} val Value emitted when this value is selected
+ * @returns {number} The index of the new option
+ */
+ add_option(labelInner, val)
+ {
+ // setup radio and value
+ let idx = this.#radios.length;
+ this.#radios.push(new WidgetRadio());
+ this.#radios[idx].set_by_id("true", idx);
+ this.#radios[idx].addEventListener("change", this.#binder);
+ this.#vals.push(val);
+
+ // setup label and radio container
+ let label = document.createElement("div");
+ label.className = "radio-label";
+ label.innerHTML = labelInner;
+
+ let el = document.createElement("div");
+ el.className = "radio-option";
+ el.appendChild(this.#radios[idx].element);
+ el.appendChild(label);
+
+ // Append the option
+ this.element.appendChild(el);
+
+ return idx;
+ }
+
+ delete_option(index)
+ {
+ this.#radios[index].element.remove();
+ this.#radios[index].removeEventListener("change", this.#binder);
+ this.#radios.splice(index, 1);
+ this.#vals.splice(index, 1);
+
+ if (this.#selected == index) {
+ this.set(null);
+ }
+ }
+
+ indexOf(val)
+ {
+ return this.#vals.indexOf(val);
+ }
+
+ set_by_index(idx) {
+ if (idx == this.#selected)
+ return;
+ if (this.#selected !== null)
+ this.#radios[this.#selected].set_by_id("value", false, false);
+ this.#radios[idx].set_by_id("value", idx, false);
+ this.#selected = idx;
+ this.set(this.#vals[idx]);
+ }
+} \ No newline at end of file
diff --git a/scripts/main.js b/scripts/main.js
index 9f384ff..cd213a2 100644
--- a/scripts/main.js
+++ b/scripts/main.js
@@ -15,7 +15,9 @@ class Client {
{ type: "color-temp", name: "ctp", state: 3000 },
{ type: "color-wheel", name: "cwh", state: Color.from_rgb(255, 200, 200) },
{ type: "thermostat", name: "thm", props: {gague: 69}, state: 72 },
- { type: "scrubber", name: "scb", props: {min: 60, max: 80, step: 1}, state: 72 }
+ { type: "multi-select", name: "msl", props: {max: 2, values: ["aeiou", "sometimes y"], labels: ["vowels", "extra vowels"]}, state: []},
+ { type: "scrubber", name: "scb", props: {min: 60, max: 80, step: 1}, state: 72 },
+ { type: "radio", name: "rad", props: {values: ["a", "b"], labels: ["a", "b"]}, state: null}
];
this.set = new WidgetSet(desc, "set");
@@ -33,6 +35,7 @@ class Client {
}
}
+let OSmClient = null;
let contents = document.getElementsByTagName("content");
if (contents.length > 0)
OSmClient = new Client(contents[0]);
diff --git a/styles/themes/base.css b/styles/themes/base.css
index 53ac83f..27b9b3e 100644
--- a/styles/themes/base.css
+++ b/styles/themes/base.css
@@ -15,9 +15,9 @@
--w-tg-inactive-bg-hover: #3ea2ff;
--w-tg-inactive-bg-active: #3ea2ff;
--w-tg-active-color: rgb(255, 128, 0);
- --w-tg-active-bg: #dd3;
- --w-tg-active-bg-hover: #ee7;
- --w-tg-active-bg-active: #ee7;
+ --w-tg-active-bg: #fff134;
+ --w-tg-active-bg-hover: #fff674;
+ --w-tg-active-bg-active: #fff674;
/* Slider colors */
--w-sl-fill: #0084ff;
@@ -49,4 +49,12 @@
--w-scr-back: rgba(255, 255, 255, 0);
--w-scr-back-other: rgba(255, 255, 255, 0.3);
--w-scr-back-active: rgba(62, 162, 255, 0.5);
+
+ /* Radio */
+ --w-radio-inactive: #ccc;
+ --w-radio-inactive-hover: #eee;
+ --w-radio-inactive-active: #eee;
+ --w-radio-active: #0084ff;
+ --w-radio-active-hover: #2696ff;
+ --w-radio-active-active: #3ea2ff;
} \ No newline at end of file
diff --git a/styles/widgets.css b/styles/widgets.css
index 53f3568..1ca4ce2 100644
--- a/styles/widgets.css
+++ b/styles/widgets.css
@@ -630,7 +630,6 @@
border-radius: 5px;
background-color: var(--w-scr-nub);
text-align: center;
- vertical-align: middle;
position: absolute;
@@ -723,4 +722,130 @@
background-color: rgba(0, 132, 255, 1);
color: rgba(255, 255, 255, 1);
transition-duration: 0s;
+}
+
+/**
+ Radio widget
+*/
+
+.radio {
+ --w-bg: rgba(0, 0, 0, 0);
+ --w-bg-hover: rgba(0, 0, 0, 0);
+ --w-bg-active: rgba(0, 0, 0, 0);
+
+ width: var(--base-unit);
+ height: var(--base-unit);
+
+ border: 7px solid var(--w-radio-inactive);
+ border-radius: 50%;
+ box-shadow: 5px 5px inset var(--w-shadow), 1px 1px var(--w-shadow), 3px 3px var(--w-shadow), 5px 5px var(--w-shadow);
+ padding: 0;
+
+ overflow: hidden;
+}
+
+.radio:hover {
+ border-color: var(--w-radio-inactive-hover);
+}
+
+.radio:active, .radio.touch {
+ border: 7px solid var(--w-radio-inactive-active);
+}
+
+.radio.inactive, .radio:disabled {
+ box-shadow: 5px 5px inset var(--w-shadow-inactive), 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive);
+}
+
+.radio.inactive::before, .radio:disabled::before {
+ width: calc(100% + 14px);
+ height: calc(100% + 14px);
+ top: -7px;
+ left: -7px;
+}
+
+.radio.inactive:hover, .radio:disabled:hover {
+ border-color: var(--w-radio-inactive);
+}
+
+.radio.inactive:active, .radio:disabled:active, .radio.inactive.touch, .radio.touch:disabled {
+ border: 7px solid var(--w-radio-inactive);
+ box-shadow: 5px 5px inset var(--w-shadow-inactive), 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive) !important;
+}
+
+.radio::after
+{
+ height: calc(var(--base-unit) * 0.4);
+ width: calc(var(--base-unit) * 0.4);
+
+ box-sizing: border-box;
+
+ position: absolute;
+
+ display: block;
+
+ background-color: rgba(0, 0, 0, 0);
+ border-radius: 50%;
+ box-shadow: none;
+ padding: 0;
+
+ top: calc(50% - var(--base-unit) * 0.4 / 2);
+ left: calc(50% - var(--base-unit) * 0.4 / 2);
+
+ transition-duration: 0.35s;
+
+ content: '';
+}
+
+.radio:checked, .radio.active {
+ border-color: var(--w-radio-active);
+}
+
+.radio:checked:hover, .radio.active:hover {
+ border-color: var(--w-radio-active-hover);
+}
+
+.radio:checked:hover::after, .radio.active:hover::after {
+ background-color: var(--w-radio-active-hover);
+}
+
+.radio:checked::after, .radio.active::after
+{
+ background-color: var(--w-radio-active);
+ box-shadow: 1px 1px var(--w-shadow), 3px 3px var(--w-shadow), 5px 5px var(--w-shadow);
+}
+
+.radio:checked:active,
+.radio:checked.touch,
+.radio.active:active,
+.radio.active.touch {
+ border-color: var(--w-radio-active-active);
+}
+
+.radio:checked:active::after,
+.radio:checked.touch::after,
+.radio.active:active::after,
+.radio.active.touch::after {
+ background-color: var(--w-radio-active-active);
+ box-shadow: none;
+}
+
+
+.radio.inactive:checked, .radio.active.inactive, .radio:disabled:checked {
+ box-shadow: 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive);
+}
+
+.radio.inactive:checked:active,
+.radio.active.inactive:active,
+.radio:disabled:checked:active,
+.radio.inactive.touch:checked,
+.radio.active.inactive.touch,
+.radio:disabled.touch:checked {
+ box-shadow: 1px 1px var(--w-shadow-inactive), 3px 3px var(--w-shadow-inactive), 5px 5px var(--w-shadow-inactive) !important;
+}
+
+.radio.inactive:checked::before, .radio.active.inactive::before, .radio:disabled:checked::before {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
} \ No newline at end of file