summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCircleShift <kgunger12@gmail.com>2025-06-22 12:56:15 -0400
committerCircleShift <kgunger12@gmail.com>2025-06-22 12:56:15 -0400
commitf354bdfb5f2ca881347c2d2bcf1c2df8cbda875d (patch)
tree1c1b2fd047d7950d702f4736e3ee0256452b4ebc
parentd7c9a05a1945fe2f404628f2cb6ec5e2b74fa48a (diff)
Add keyboard support to CSS, Button widget
-rw-r--r--scripts/gui-common/widgets.js95
-rw-r--r--styles/themes/base.css6
-rw-r--r--styles/widgets.css85
3 files changed, 159 insertions, 27 deletions
diff --git a/scripts/gui-common/widgets.js b/scripts/gui-common/widgets.js
index b8fe01f..2c5ed26 100644
--- a/scripts/gui-common/widgets.js
+++ b/scripts/gui-common/widgets.js
@@ -24,6 +24,7 @@ class Widget extends EventTarget{
this.element = document.createElement("div");
this.element.classList.add("widget");
this.element.classList.add("gridlock");
+ this.element.setAttribute("tabindex", 0);
this.element.addEventListener("mousedown", this.#emitMouseEvent.bind(this));
this.element.addEventListener("mouseup", this.#emitMouseEvent.bind(this));
@@ -38,6 +39,12 @@ class Widget extends EventTarget{
this.element.addEventListener("touchmove", this.#emitTouchEvent.bind(this));
this.element.addEventListener("touchcancel", this.#emitTouchEvent.bind(this));
+ this.element.addEventListener("focusin", this.#emitFocusEvent.bind(this));
+ this.element.addEventListener("focusout", this.#emitFocusEvent.bind(this));
+
+ this.element.addEventListener("keydown", this.#emitKeyboardEvent.bind(this));
+ this.element.addEventListener("keyup", this.#emitKeyboardEvent.bind(this));
+
this.element.addEventListener("contextmenu", this.#emitContextEvent.bind(this));
}
@@ -118,6 +125,34 @@ class Widget extends EventTarget{
event.preventDefault();
}
+ /** @param {KeyboardEvent} event */
+ #emitKeyboardEvent(event) {
+ if (this.#inactive)
+ this.dispatchEvent(new CustomEvent("inactive", {detail: {widget: this, event: new KeyboardEvent(event.type, event)}}));
+ else
+ this.dispatchEvent(new KeyboardEvent(event.type, event));
+
+ if (event.key == " " || event.key == "Enter") {
+ if (event.type == "keydown")
+ this.element.classList.add("touch");
+ else if (event.type == "keyup")
+ this.element.classList.remove("touch");
+
+ event.preventDefault();
+ }
+ }
+
+ /** @param {FocusEvent} event */
+ #emitFocusEvent(event) {
+ if (this.#inactive)
+ this.dispatchEvent(new CustomEvent("inactive", {detail: {widget: this, event: new FocusEvent(event.type, event)}}));
+ else
+ this.dispatchEvent(new FocusEvent(event.type, event));
+
+ if(event.type == "focusout")
+ this.element.classList.remove("touch");
+ }
+
/** @param {Event} event */
#emitContextEvent(event)
{
@@ -156,6 +191,9 @@ class WidgetButton extends Widget
this.addEventListener("mouseenter", this.#enter);
this.addEventListener("touchstart", this.#touchstart);
this.addEventListener("touchend", this.#touchend);
+ this.addEventListener("keydown", this.#keystart);
+ this.addEventListener("keyup", this.#keyend);
+ this.addEventListener("focusout", this.#focusend);
this.#bound = this.#unpress.bind(this);
this.set_by_id("true", tv);
this.set_by_id("false", fv);
@@ -165,7 +203,8 @@ class WidgetButton extends Widget
/** @param {MouseEvent} event */
#press(event)
{
- this.#pressing |= (1 << event.button);
+ if (event.button == 0)
+ this.#pressing |= 1;
this.set(this.get("true"));
}
@@ -198,8 +237,6 @@ class WidgetButton extends Widget
/** @param {MouseEvent} event */
#enter(event)
{
- if (this.primed == 0)
- return;
this.#gone = 0;
window.removeEventListener("mouseup", this.#bound);
}
@@ -237,6 +274,56 @@ class WidgetButton extends Widget
}
}
+ /** @param {KeyboardEvent} event */
+ #keystart(event)
+ {
+ let button = 0;
+ if (event.key == " ")
+ button = 2;
+ else if (event.key == "Enter")
+ button = 4;
+
+ if (button != 0 && (this.#pressing & button) == 0) {
+ if (this.#pressing == 0)
+ this.set(this.get("true"));
+ this.#pressing |= button;
+ }
+ }
+
+ /** @param {KeyboardEvent} event */
+ #keyend(event)
+ {
+ let button = 0;
+ if (event.key == " ")
+ button = 2;
+ else if (event.key == "Enter")
+ button = 4;
+
+ if ((button & this.#pressing) !== 0) {
+ this.#pressing -= button;
+ if (this.#pressing == 0)
+ this.set(this.get("false"));
+ }
+ }
+
+ /** @param {FocusEvent} event */
+ #focusend(event)
+ {
+ let button = 6;
+
+ if ((button & this.#pressing) !== 0) {
+ this.#pressing -= (button & this.#pressing);
+ if (this.#pressing == 0)
+ this.set(this.get("false"));
+ }
+ }
+
+ /**
+ *
+ * @param {string} id
+ * @param {any} v
+ * @param {boolean} [emit]
+ */
set_by_id(id, v, emit = false)
{
if (id == "false" || id == "true")
@@ -704,6 +791,7 @@ class WidgetColorTemp extends WidgetSlider
this.element.classList.replace("slider", "color-temp");
this.setDetailUpdater(this.#update_detail.bind(this));
+ this.#update_detail(null, null, (value - 2700) / (6000 - 2700));
}
/**
@@ -740,6 +828,7 @@ class WidgetColorLight extends WidgetSlider
this.element.classList.replace("slider", "color-light");
this.setDetailUpdater(this.#update_detail.bind(this));
+ this.#update_detail(null, null, value);
}
/**
diff --git a/styles/themes/base.css b/styles/themes/base.css
index 27b9b3e..591d272 100644
--- a/styles/themes/base.css
+++ b/styles/themes/base.css
@@ -51,9 +51,9 @@
--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-inactive: #eee;
+ --w-radio-inactive-hover: #def;
+ --w-radio-inactive-active: #def;
--w-radio-active: #0084ff;
--w-radio-active-hover: #2696ff;
--w-radio-active-active: #3ea2ff;
diff --git a/styles/widgets.css b/styles/widgets.css
index 870d2aa..dbb8383 100644
--- a/styles/widgets.css
+++ b/styles/widgets.css
@@ -36,7 +36,7 @@
height: calc(var(--height) * var(--base-unit) + (var(--height) - 1) * var(--alt-unit));
}
-.widget:hover {
+.widget:hover, .widget:focus-within {
background-color: var(--w-bg-hover);
}
@@ -63,11 +63,13 @@
z-index: 1;
}
-.widget.inactive:hover::before, .widget:disabled:hover::before {
+.widget.inactive:hover::before, .widget:disabled:hover::before,
+.widget.inactive:focus-within::before, .widget:disabled:focus-within::before {
background-color: rgba(0, 0, 0, 0);
}
-.widget.inactive:hover, .widget:disabled:hover {
+.widget.inactive:hover, .widget:disabled:hover,
+.widget.inactive:focus-within, .widget:disabled:focus-within {
background-color: var(--w-bg);
}
@@ -165,7 +167,8 @@
height: 100%;
}
-.slider:hover > .fill::before {
+.slider:hover > .fill::before,
+.slider:focus-within > .fill::before {
background-color: var(--w-sl-fill-hover);
}
@@ -271,7 +274,7 @@
overflow: unset;
}
-.checkbox:hover {
+.checkbox:hover, .checkbox:focus-within {
border-color: var(--w-cb-inactive-outline-hover);
}
@@ -290,7 +293,8 @@
left: -7px;
}
-.checkbox.inactive:hover, .checkbox:disabled:hover {
+.checkbox.inactive:hover, .checkbox:disabled:hover,
+.checkbox.inactive:focus-within, .checkbox:disabled:focus-within {
border-color: var(--w-cb-inactive-outline);
}
@@ -391,7 +395,8 @@
transition-duration: 0s;
}
-.color-wheel:hover::after {
+.color-wheel:hover::after,
+.color-wheel:focus-within:after {
border-color: #888;
}
@@ -476,7 +481,8 @@
transition-duration: 0s;
}
-.color-temp:hover > .fill::after {
+.color-temp:hover > .fill::after,
+.color-temp:focus-within > .fill::after {
border-color: #888;
}
@@ -493,6 +499,9 @@
align-content: center;
text-align: center;
+ --w-bg-hover: var(--w-bg);
+ --w-bg-active: var(--w-bg);
+
--width: 5;
--height: 3;
}
@@ -571,6 +580,14 @@
overflow: visible;
}
+.thermostat > .unit:focus {
+ outline: none;
+}
+
+.thermostat > .unit:hover, .thermostat > .unit:focus {
+ background-color: #cef;
+}
+
.thermostat > .unit::after {
content: '';
@@ -590,7 +607,7 @@
}
.thermostat > .unit:active, .thermostat > .unit.touch {
- box-shadow: inset -5px -5px var(--w-bg-hover) !important;
+ box-shadow: inset -5px -5px var(--w-bg) !important;
}
/*
@@ -694,7 +711,7 @@
bottom: calc(50% - (var(--base-unit) - 10px) / 2);
}
-.scrubber:hover > .fill::after, .scrubber.touch > .fill::after {
+.scrubber:hover > .fill::after, .scrubber.touch > .fill::after, .scrubber:focus-within > .fill::after {
background-color: var(--w-scr-nub-hover);
}
@@ -778,17 +795,16 @@
*/
.radio {
- --width: 1;
- --height: 1;
+ min-width: 0;
+ min-height: 0;
+ --width: 0.9;
+ --height: 0.9;
--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: 5px 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;
@@ -796,12 +812,12 @@
overflow: hidden;
}
-.radio:hover {
+.radio:hover, .radio:focus-within {
border-color: var(--w-radio-inactive-hover);
}
.radio:active, .radio.touch {
- border: 7px solid var(--w-radio-inactive-active);
+ border: 5px solid var(--w-radio-inactive-active);
}
.radio.inactive, .radio:disabled {
@@ -815,7 +831,7 @@
left: -7px;
}
-.radio.inactive:hover, .radio:disabled:hover {
+.radio.inactive:hover, .radio:disabled:hover, .radio.inactive:focus-within, .radio:disabled:focus-within {
border-color: var(--w-radio-inactive);
}
@@ -852,11 +868,13 @@
border-color: var(--w-radio-active);
}
-.radio:checked:hover, .radio.active:hover {
+.radio:checked:hover, .radio.active:hover,
+.radio:checked:focus-within, .radio.active:focus-within {
border-color: var(--w-radio-active-hover);
}
-.radio:checked:hover::after, .radio.active:hover::after {
+.radio:checked:hover::after, .radio.active:hover::after,
+.radio:checked:focus-within::after, .radio.active:focus-within::after {
background-color: var(--w-radio-active-hover);
}
@@ -901,3 +919,28 @@
top: 0;
left: 0;
}
+
+/*
+ Radio group
+*/
+
+.radio-group {
+ display: flex;
+ flex-direction: column;
+ box-shadow: inset 5px 5px var(--w-shadow);
+ --w-bg-hover: var(--w-bg);
+ --w-bg-active: var(--w-bg);
+ --width: 3;
+ --height: 2;
+ justify-content: space-around;
+}
+
+.radio-group.h {
+ flex-direction: row;
+}
+
+.radio-group > .radio-option {
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+} \ No newline at end of file