Skip to content

Commit a4e19e7

Browse files
committed
convert tool button to web components
1 parent d396f0a commit a4e19e7

File tree

8 files changed

+121
-55
lines changed

8 files changed

+121
-55
lines changed

css/panel.css

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,29 +37,19 @@
3737
justify-content: space-between;
3838
}
3939

40-
.panel-row color-button {
40+
.panel-row color-button,
41+
.panel-row tool-button {
4142
margin-right: var(--button-space);
4243
}
4344

44-
.panel-row color-button:last-child {
45+
.panel-row color-button:last-child,
46+
.panel-row tool-button:last-child {
4547
margin-right: 0px;
4648
}
4749

48-
.panel-row button {
49-
margin-right: var(--button-space);
50-
width: var(--button-size);
51-
height: var(--button-size);
52-
border: 2px solid black;
53-
border-radius: 6px;
54-
}
55-
56-
.panel-row button:last-child {
57-
margin-right: 0px;
58-
}
59-
60-
.panel-row button.active {
61-
border: 2px solid white;
62-
box-shadow: 0px 0px 0px 4px orange;
50+
#tools {
51+
display: flex;
52+
flex: 1;
6353
}
6454

6555
#actions {

js/boot.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import { attachGamepadBlockListeners } from "./controls/general.mjs";
2323
import { attachGamepadListeners } from "./controls/gamepad.mjs";
2424
import { initializeCursor } from "./cursor.mjs";
2525
import { ColorButton } from "./ui/color.mjs";
26+
import { ToolButton } from "./ui/tool.mjs";
2627

2728
function registerComponents() {
2829
customElements.define("color-button", ColorButton);
30+
customElements.define("tool-button", ToolButton);
2931
}
3032

3133
function attachResizeListeners() {

js/ui/color.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export class ColorButton extends HTMLElement {
4848
this.#button.dispatchEvent(e);
4949
}
5050

51+
isColorEqual(color) {
52+
return this.color === color;
53+
}
54+
5155
set isActive(value) {
5256
if (value) {
5357
this.#button.setAttribute("aria-pressed", "true");

js/ui/panel.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import {
55
import { getPanel } from "../dom.mjs";
66
import { isCursorWithinPanelBounds } from "./utils.mjs";
77
import { ColorButton } from "./color.mjs";
8+
import { ToolButton } from "./tool.mjs";
89

910
function getPanelButtonByCoordinates(x, y, panel) {
10-
const buttons = panel.querySelectorAll("button,color-button");
11+
const buttons = panel.querySelectorAll("button,color-button,tool-button");
1112

1213
for (let i = 0; i < buttons.length; i++) {
1314
const button = buttons[i];
@@ -44,7 +45,7 @@ function activatePanelButtonOnCoordinates(x, y) {
4445
bubbles: false,
4546
});
4647

47-
if (button instanceof ColorButton) {
48+
if (button instanceof ColorButton || button instanceof ToolButton) {
4849
button.click(clickEvent);
4950

5051
return;

js/ui/tool.mjs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { loadIcon } from "./utils.mjs";
2+
/**
3+
* Represents tool/action button.
4+
*/
5+
export class ToolButton extends HTMLElement {
6+
constructor({ id, iconUrl, onClick }) {
7+
super();
8+
9+
this.#description = id.description;
10+
this.#iconUrl = iconUrl;
11+
this.id = id;
12+
this.#onClick = onClick;
13+
this.attachShadow({ mode: "open" });
14+
}
15+
16+
id = "";
17+
#iconUrl = "";
18+
#description = "";
19+
#onClick = () => {};
20+
21+
connectedCallback() {
22+
this.shadowRoot.innerHTML = `
23+
<style>
24+
button {
25+
width: var(--button-size);
26+
height: var(--button-size);
27+
border: 2px solid black;
28+
border-radius: 6px;
29+
}
30+
31+
button[aria-pressed="true"] {
32+
border: 2px solid white;
33+
box-shadow: 0px 0px 0px 4px orange;
34+
}
35+
</style>
36+
<button
37+
data-id="${this.id.description}"
38+
data-value="${this.#description}"
39+
aria-label="${this.#description.toLocaleLowerCase()} tool"
40+
aria-pressed="${this.isActive ? "true" : "false"}"
41+
>
42+
</button>
43+
`;
44+
45+
this.#button.addEventListener("click", this.#handleClick, true);
46+
this.isActive = false;
47+
48+
loadIcon(this.#iconUrl)
49+
.then((icon) => {
50+
this.#button.innerHTML = icon;
51+
})
52+
.catch((error) => {
53+
console.error(error);
54+
this.#button.innerText = this.#description;
55+
});
56+
}
57+
58+
click(e) {
59+
this.#button.dispatchEvent(e);
60+
}
61+
62+
set isActive(value) {
63+
if (value) {
64+
this.#button.setAttribute("aria-pressed", "true");
65+
} else {
66+
this.#button.removeAttribute("aria-pressed", "false");
67+
}
68+
}
69+
70+
get #button() {
71+
return this.shadowRoot.querySelector("button");
72+
}
73+
74+
#handleClick = (e) => {
75+
this.#onClick(e);
76+
};
77+
78+
static compare(id, description) {
79+
return id.description === description;
80+
}
81+
}

js/ui/tools.mjs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { getPanelTools } from "../dom.mjs";
22
import { setTool } from "../state/actions/tool.mjs";
33
import { TOOL_LIST } from "../state/constants.mjs";
4-
import { loadIcon, updateActivatedButton } from "./utils.mjs";
4+
import { updateActivatedButton } from "./utils.mjs";
55
import { buildToolActions } from "./actions.mjs";
66
import { buildToolVariants } from "./variants.mjs";
7+
import { ToolButton } from "./tool.mjs";
78

89
export function createToolPanel({ state }) {
910
const tools = getPanelTools();
@@ -37,26 +38,10 @@ export function createToolPanel({ state }) {
3738
});
3839

3940
TOOL_LIST.forEach((tool) => {
40-
const button = document.createElement("button");
41-
42-
button.addEventListener(
43-
"click",
44-
() => {
45-
setTool(tool, { state });
46-
},
47-
true,
48-
);
49-
50-
button.dataset.value = tool.id.description;
51-
52-
loadIcon(tool.iconUrl)
53-
.then((icon) => {
54-
button.innerHTML = icon;
55-
})
56-
.catch((error) => {
57-
console.error(error);
58-
button.innerText = tool.id.description;
59-
});
41+
const button = new ToolButton({
42+
...tool,
43+
onClick: () => setTool(tool, { state }),
44+
});
6045

6146
tools.appendChild(button);
6247
});

js/ui/utils.mjs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ColorButton } from "./color.mjs";
1+
import { ToolButton } from "./tool.mjs";
22

33
export async function loadIcon(url) {
44
const response = await fetch(url);
@@ -30,25 +30,27 @@ export function ensureCallbacksRemoved(listeners) {
3030
}
3131

3232
export function updateActivatedButton(buttonContainer, value) {
33-
const buttons = buttonContainer.querySelectorAll("button,color-button");
33+
const buttons = buttonContainer.querySelectorAll("button");
3434

3535
Array.from(buttons).forEach((button) => {
36-
const isColorButton = button instanceof ColorButton;
37-
const buttonColor = isColorButton ? button.color : button.dataset.value;
38-
const isActive = buttonColor === value;
39-
40-
if (isColorButton) {
41-
button.isActive = isActive;
42-
43-
return;
36+
if (button.dataset.value === value) {
37+
button.classList.add("active");
4438
} else {
45-
if (button.dataset.value === value) {
46-
button.classList.add("active");
47-
} else {
48-
button.classList.remove("active");
49-
}
39+
button.classList.remove("active");
5040
}
5141
});
42+
43+
const colorButtons = buttonContainer.querySelectorAll("color-button");
44+
45+
Array.from(colorButtons).forEach((button) => {
46+
button.isActive = button.isColorEqual(value);
47+
});
48+
49+
const toolButtons = buttonContainer.querySelectorAll("tool-button");
50+
51+
Array.from(toolButtons).forEach((button) => {
52+
button.isActive = ToolButton.compare(button.id, value);
53+
});
5254
}
5355

5456
/**

service-worker.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const STATIC_ASSETS = [
4444
"js/ui/global.mjs",
4545
"js/ui/panel.mjs",
4646
"js/ui/tools.mjs",
47+
"js/ui/tool.mjs",
4748
"js/ui/toast.mjs",
4849
"js/ui/utils.mjs",
4950
"js/ui/variants.mjs",

0 commit comments

Comments
 (0)