comparison design_system/src/components/button.js @ 251:117c4d53c9a4

[ui] Add HTML-first Web Component system Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 09:14:57 -0700
parents
children 2b6e732087ff
comparison
equal deleted inserted replaced
250:745fd127b2a1 251:117c4d53c9a4
1 export class ZenButton extends HTMLElement {
2 static get observedAttributes() {
3 return ["disabled", "loading"];
4 }
5
6 connectedCallback() {
7 if (!this._blockInteraction) {
8 this._blockInteraction = event => {
9 if (!this.hasAttribute("disabled") &&
10 !this.hasAttribute("loading")) return;
11 event.preventDefault();
12 event.stopImmediatePropagation();
13 };
14 }
15 this.addEventListener("click", this._blockInteraction, true);
16 if (!this._observer) {
17 this._observer = new MutationObserver(records => {
18 for (const record of records) {
19 for (const node of record.removedNodes) {
20 if (node instanceof Element &&
21 node.matches("button, a")) {
22 this.restoreManagedState(node);
23 }
24 }
25 }
26 this.sync();
27 });
28 }
29 this._observer.observe(this, { childList: true });
30 this.sync();
31 }
32
33 disconnectedCallback() {
34 this._observer?.disconnect();
35 this.removeEventListener("click", this._blockInteraction, true);
36 }
37
38 attributeChangedCallback() {
39 this.sync();
40 }
41
42 sync() {
43 const control = this.querySelector(":scope > button, :scope > a");
44 if (!control) return;
45
46 const disabled =
47 this.hasAttribute("disabled") ||
48 this.hasAttribute("loading");
49
50 if (disabled) {
51 if (control.dataset.zenManagedAriaDisabled !== "true") {
52 control.dataset.zenOriginalAriaDisabled =
53 control.getAttribute("aria-disabled") ?? "__zen_missing__";
54 control.dataset.zenManagedAriaDisabled = "true";
55 }
56 control.setAttribute("aria-disabled", "true");
57 } else {
58 this.restoreDisabledState(control);
59 }
60
61 if (this.hasAttribute("loading")) {
62 if (control.dataset.zenManagedAriaBusy !== "true") {
63 control.dataset.zenOriginalAriaBusy =
64 control.getAttribute("aria-busy") ?? "__zen_missing__";
65 control.dataset.zenManagedAriaBusy = "true";
66 }
67 control.setAttribute("aria-busy", "true");
68 } else if (control.dataset.zenManagedAriaBusy === "true") {
69 if (control.dataset.zenOriginalAriaBusy === "__zen_missing__") {
70 control.removeAttribute("aria-busy");
71 } else {
72 control.setAttribute(
73 "aria-busy",
74 control.dataset.zenOriginalAriaBusy,
75 );
76 }
77 delete control.dataset.zenManagedAriaBusy;
78 delete control.dataset.zenOriginalAriaBusy;
79 }
80 }
81
82 restoreDisabledState(control) {
83 if (control.dataset.zenManagedAriaDisabled === "true") {
84 if (control.dataset.zenOriginalAriaDisabled === "__zen_missing__") {
85 control.removeAttribute("aria-disabled");
86 } else {
87 control.setAttribute(
88 "aria-disabled",
89 control.dataset.zenOriginalAriaDisabled,
90 );
91 }
92 delete control.dataset.zenManagedAriaDisabled;
93 delete control.dataset.zenOriginalAriaDisabled;
94 }
95 }
96
97 restoreManagedState(control) {
98 this.restoreDisabledState(control);
99 if (control.dataset.zenManagedAriaBusy === "true") {
100 if (control.dataset.zenOriginalAriaBusy === "__zen_missing__") {
101 control.removeAttribute("aria-busy");
102 } else {
103 control.setAttribute(
104 "aria-busy",
105 control.dataset.zenOriginalAriaBusy,
106 );
107 }
108 delete control.dataset.zenManagedAriaBusy;
109 delete control.dataset.zenOriginalAriaBusy;
110 }
111 }
112 }
113
114 if (!customElements.get("zen-button")) {
115 customElements.define("zen-button", ZenButton);
116 }