Skip to content

Commit 48f9621

Browse files
committed
handle activation of buttons in top level
1 parent ab79283 commit 48f9621

File tree

9 files changed

+147
-89
lines changed

9 files changed

+147
-89
lines changed

js/dom.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,26 @@ export function getPanelTools() {
1212
return document.getElementById("tools");
1313
}
1414

15+
export function getToolButtons() {
16+
return getPanelTools().querySelectorAll("tool-button");
17+
}
18+
19+
export function getToolButtonById(id) {
20+
return Array.from(getToolButtons()).find((button) => button.id === id);
21+
}
22+
1523
export function getPanelToolVariants() {
1624
return document.getElementById("variants");
1725
}
1826

27+
export function getVariantButtons() {
28+
return getPanelToolVariants().querySelectorAll("variant-button");
29+
}
30+
31+
export function getVariantButtonById(id) {
32+
return Array.from(getVariantButtons()).find((button) => button.id === id);
33+
}
34+
1935
export function getPanelToolActions() {
2036
return document.getElementById("actions");
2137
}
@@ -24,6 +40,14 @@ export function getPanelColors() {
2440
return document.getElementById("colors");
2541
}
2642

43+
export function getColorButtons() {
44+
return getPanelColors().querySelectorAll("color-button");
45+
}
46+
47+
export function getColorButtonByColor(color) {
48+
return Array.from(getColorButtons()).find((button) => button.color === color);
49+
}
50+
2751
export function getPanel() {
2852
return document.getElementById("panel");
2953
}

js/ui/button.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export class UiButton extends HTMLElement {
99
// Can be built also declaratively with HTML.
1010
constructor(options = {}) {
1111
const {
12+
isActive,
1213
id,
1314
ariaLabel,
1415
dataset,
@@ -20,6 +21,7 @@ export class UiButton extends HTMLElement {
2021

2122
super();
2223

24+
this.#isActive = isActive ?? false;
2325
this.#id = id;
2426
this.#onClick = onClick;
2527
this.#signal = signal;
@@ -31,6 +33,7 @@ export class UiButton extends HTMLElement {
3133
this.attachShadow({ mode: "open" });
3234
}
3335

36+
#isActive = false;
3437
#id = "";
3538
#ariaLabel = "";
3639
#dataset = {};
@@ -94,9 +97,14 @@ export class UiButton extends HTMLElement {
9497
}
9598

9699
set isActive(value) {
100+
this.#isActive = value;
97101
this.button.setAttribute("aria-pressed", value ? "true" : "false");
98102
}
99103

104+
get isActive() {
105+
return this.#isActive;
106+
}
107+
100108
#setContents() {
101109
const iconUrl = this.#iconUrl ?? this.getAttribute("icon-url");
102110
const dataset = this.#dataset ?? this.dataset;

js/ui/color.mjs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import { UiButton } from "./button.mjs";
55
* Represents a button for selecting colors
66
*/
77
export class ColorButton extends HTMLElement {
8-
constructor({ onClick, color, signal }) {
8+
constructor({ onClick, color, signal, isActive }) {
99
super();
1010

1111
this.color = color;
12+
this.#isActive = isActive;
1213
this.#onClick = onClick;
1314
this.#signal = signal;
1415
this.attachShadow({ mode: "open" });
@@ -17,6 +18,7 @@ export class ColorButton extends HTMLElement {
1718
color = "#000000";
1819
#onClick = () => {};
1920
#signal = null;
21+
#isActive = false;
2022

2123
connectedCallback() {
2224
const button = new UiButton({
@@ -32,6 +34,7 @@ export class ColorButton extends HTMLElement {
3234
});
3335

3436
this.shadowRoot.appendChild(button);
37+
this.isActive = this.#isActive;
3538
}
3639

3740
click(e) {
@@ -42,11 +45,11 @@ export class ColorButton extends HTMLElement {
4245
this.shadowRoot.querySelector("ui-button").isActive = value;
4346
}
4447

45-
get #button() {
46-
return this.shadowRoot.querySelector("ui-button").button;
48+
get isActive() {
49+
return this.shadowRoot.querySelector("ui-button").isActive;
4750
}
4851

49-
isColorEqual(color) {
50-
return this.color === color;
52+
get #button() {
53+
return this.shadowRoot.querySelector("ui-button").button;
5154
}
5255
}

js/ui/colors.mjs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import {
99
isLeftShoulderGamepadButtonPressed,
1010
getGamepad,
1111
} from "../controls/gamepad.mjs";
12-
import { getPanelColors } from "../dom.mjs";
13-
import { updateActivatedButton } from "./utils.mjs";
12+
import {
13+
getPanelColors,
14+
getColorButtons,
15+
getColorButtonByColor,
16+
} from "../dom.mjs";
1417
import { ColorButton } from "./color.mjs";
1518

1619
const GAMEPAD_BUTTON_ACTIVATION_DELAY_IN_MS = 300;
@@ -84,7 +87,18 @@ export function createColorPanel({ state }) {
8487
return;
8588
}
8689

87-
updateActivatedButton(colors, nextState.color);
90+
const prevActiveButton = Array.from(getColorButtons()).find(
91+
(b) => b.isActive,
92+
);
93+
prevActiveButton.isActive = false;
94+
95+
const nextActiveButton = getColorButtonByColor(nextState.color);
96+
97+
if (!nextActiveButton) {
98+
throw new Error(`Color button not found for color: ${nextState.color}`);
99+
}
100+
101+
nextActiveButton.isActive = true;
88102
});
89103

90104
state.addListener((nextState, prevState) => {
@@ -104,21 +118,16 @@ export function createColorPanel({ state }) {
104118
});
105119

106120
COLOR_LIST.forEach((color) => {
107-
const button = new ColorButton({
121+
const colorButton = new ColorButton({
108122
color,
109123
onClick: () => setColor(color, { state }),
110124
signal: controller.signal,
125+
isActive: state.get((prevState) => prevState.color) === color,
111126
});
112127

113-
colors.appendChild(button);
128+
colors.appendChild(colorButton);
114129
});
115130

116-
const selectedColor = state.get((prevState) => prevState.color);
117-
118-
if (selectedColor) {
119-
updateActivatedButton(colors, selectedColor);
120-
}
121-
122131
attachKeyboardListeners({ state, signal: controller.signal });
123132
attachGamepadListeners(state);
124133
}

js/ui/tool.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import { UiButton } from "./button.mjs";
44
* Represents tool/action button.
55
*/
66
export class ToolButton extends HTMLElement {
7-
constructor({ id, iconUrl, onClick, signal }) {
7+
constructor({ id, iconUrl, onClick, signal, isActive }) {
88
super();
99

10+
this.#isActive = isActive;
1011
this.#description = id.description;
1112
this.#iconUrl = iconUrl;
1213
this.id = id;
@@ -15,6 +16,7 @@ export class ToolButton extends HTMLElement {
1516
this.attachShadow({ mode: "open" });
1617
}
1718

19+
#isActive = false;
1820
id = "";
1921
#iconUrl = "";
2022
#description = "";
@@ -34,6 +36,7 @@ export class ToolButton extends HTMLElement {
3436
});
3537

3638
this.shadowRoot.appendChild(button);
39+
button.isActive = this.#isActive;
3740
}
3841

3942
click(e) {
@@ -44,6 +47,10 @@ export class ToolButton extends HTMLElement {
4447
this.shadowRoot.querySelector("ui-button").isActive = value;
4548
}
4649

50+
get isActive() {
51+
return this.shadowRoot.querySelector("ui-button").isActive;
52+
}
53+
4754
get #button() {
4855
return this.shadowRoot.querySelector("ui-button").button;
4956
}

js/ui/tools.mjs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { getPanelTools } from "../dom.mjs";
1+
import { getPanelTools, getToolButtons, getToolButtonById } from "../dom.mjs";
22
import { setTool } from "../state/actions/tool.mjs";
33
import { TOOL_LIST } from "../state/constants.mjs";
4-
import { updateActivatedButton } from "./utils.mjs";
54
import { buildToolActions } from "./actions.mjs";
65
import { buildToolVariants } from "./variants.mjs";
76
import { ToolButton } from "./tool.mjs";
@@ -27,7 +26,19 @@ export function createToolPanel({ state }) {
2726
variantsController = new AbortController();
2827
actionsController = new AbortController();
2928

30-
updateActivatedButton(tools, updatedState.tool.id.description);
29+
const prevActiveButton = Array.from(getToolButtons()).find(
30+
(b) => b.isActive,
31+
);
32+
prevActiveButton.isActive = false;
33+
34+
const toolId = updatedState.tool.id;
35+
const nextActiveButton = getToolButtonById(toolId);
36+
37+
if (!nextActiveButton) {
38+
throw new Error(`Tool button not found for tool: ${toolId}`);
39+
}
40+
41+
nextActiveButton.isActive = true;
3142

3243
if (updatedState.tool.variants) {
3344
buildToolVariants(updatedState.tool, {
@@ -44,20 +55,19 @@ export function createToolPanel({ state }) {
4455
}
4556
});
4657

58+
const selectedTool = state.get((prevState) => prevState.tool);
59+
4760
TOOL_LIST.forEach((tool) => {
4861
const button = new ToolButton({
4962
...tool,
5063
onClick: () => setTool(tool, { state }),
64+
isActive: selectedTool.id === tool.id,
5165
});
5266

5367
tools.appendChild(button);
5468
});
5569

56-
const selectedTool = state.get((prevState) => prevState.tool);
57-
5870
if (selectedTool) {
59-
updateActivatedButton(tools, selectedTool.id.description);
60-
6171
if (selectedTool.variants) {
6272
buildToolVariants(selectedTool, {
6373
state,

js/ui/utils.mjs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import { ToolButton } from "./tool.mjs";
2-
import { VariantButton } from "./variant.mjs";
3-
41
export async function loadIcon(url) {
52
const response = await fetch(url);
63

@@ -30,36 +27,6 @@ export function ensureCallbacksRemoved(listeners) {
3027
throw new Error("Not all listeners were removed!");
3128
}
3229

33-
export function updateActivatedButton(buttonContainer, value) {
34-
const buttons = buttonContainer.querySelectorAll("button");
35-
36-
Array.from(buttons).forEach((button) => {
37-
if (button.dataset.value === value) {
38-
button.classList.add("active");
39-
} else {
40-
button.classList.remove("active");
41-
}
42-
});
43-
44-
const colorButtons = buttonContainer.querySelectorAll("color-button");
45-
46-
Array.from(colorButtons).forEach((button) => {
47-
button.isActive = button.isColorEqual(value);
48-
});
49-
50-
const toolButtons = buttonContainer.querySelectorAll("tool-button");
51-
52-
Array.from(toolButtons).forEach((button) => {
53-
button.isActive = ToolButton.compare(button.id, value);
54-
});
55-
56-
const variantButtons = buttonContainer.querySelectorAll("variant-button");
57-
58-
Array.from(variantButtons).forEach((button) => {
59-
button.isActive = VariantButton.compare(button.id, value);
60-
});
61-
}
62-
6330
/**
6431
* Is cursor located on UI panel?
6532
*

js/ui/variant.mjs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { UiButton } from "./button.mjs";
22

33
export class VariantButton extends HTMLElement {
4-
constructor({ id, onClick, iconUrl, signal }) {
4+
constructor({ id, onClick, iconUrl, signal, isActive }) {
55
super();
66

7+
this.#isActive = isActive;
78
this.id = id;
89
this.#onClick = onClick;
910
this.#iconUrl = iconUrl;
@@ -12,6 +13,7 @@ export class VariantButton extends HTMLElement {
1213
this.attachShadow({ mode: "open" });
1314
}
1415

16+
#isActive = false;
1517
id = "";
1618
#onClick = () => {};
1719
#iconUrl = "";
@@ -29,6 +31,7 @@ export class VariantButton extends HTMLElement {
2931
});
3032

3133
this.shadowRoot.appendChild(button);
34+
this.isActive = this.#isActive;
3235
}
3336

3437
click(e) {
@@ -47,6 +50,10 @@ export class VariantButton extends HTMLElement {
4750
this.shadowRoot.querySelector("ui-button").isActive = value;
4851
}
4952

53+
get isActive() {
54+
return this.shadowRoot.querySelector("ui-button").isActive;
55+
}
56+
5057
static compare(id, value) {
5158
return id.description === value;
5259
}

0 commit comments

Comments
 (0)