From c2cf6b494d1e5535f3c0a4b7ec5c742cfc95ae5f Mon Sep 17 00:00:00 2001 From: CircleShift Date: Thu, 19 Jun 2025 21:04:03 -0400 Subject: Radio group --- scripts/gui-common/color.js | 4 + scripts/gui-common/widget-set.js | 95 ++++++++-- scripts/gui-common/widgets.js | 371 ++++++++++++++++++++++++++++++++------- scripts/main.js | 5 +- styles/themes/base.css | 14 +- styles/widgets.css | 127 +++++++++++++- 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} */ @@ -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 */ /** @@ -40,6 +44,14 @@ * @property {number} [tw] */ +/** + * @typedef OSmMultiSelectProps + * @property {Array} labels + * @property {Array} values + * @property {number} max + * @property {boolean} remove + */ + /** * @typedef OSmScrubberProps * @property {number} max @@ -51,7 +63,13 @@ */ /** - * @typedef {OSmButtonProps | OSmToggleProps | OSmSliderProps | OSmThermostatProps | OSmScrubberProps} OSmAnyProps + * @typedef OSmRadioSelectProps + * @property {Array} values + * @property {Array} labels + */ + +/** + * @typedef {OSmButtonProps | OSmToggleProps | OSmSliderProps | OSmThermostatProps | OSmMultiSelectProps | OSmScrubberProps | OSmRadioSelectProps} OSmAnyProps */ /** @@ -88,12 +106,20 @@ * @typedef {DescBase & {type: "thermostat", props: OSmThermostatProps, state: number}} DescThermostat */ +/** + * @typedef {DescBase & {type: "multi-select", props: OSmMultiSelectProps, state: Array}} 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} 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} */ + #radios = []; + + /** @type {Array} */ + #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 -- cgit v1.2.3