Mercurial
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/design_system/src/components/button.js Tue Aug 04 09:14:57 2026 -0700 @@ -0,0 +1,116 @@ +export class ZenButton extends HTMLElement { + static get observedAttributes() { + return ["disabled", "loading"]; + } + + connectedCallback() { + if (!this._blockInteraction) { + this._blockInteraction = event => { + if (!this.hasAttribute("disabled") && + !this.hasAttribute("loading")) return; + event.preventDefault(); + event.stopImmediatePropagation(); + }; + } + this.addEventListener("click", this._blockInteraction, true); + if (!this._observer) { + this._observer = new MutationObserver(records => { + for (const record of records) { + for (const node of record.removedNodes) { + if (node instanceof Element && + node.matches("button, a")) { + this.restoreManagedState(node); + } + } + } + this.sync(); + }); + } + this._observer.observe(this, { childList: true }); + this.sync(); + } + + disconnectedCallback() { + this._observer?.disconnect(); + this.removeEventListener("click", this._blockInteraction, true); + } + + attributeChangedCallback() { + this.sync(); + } + + sync() { + const control = this.querySelector(":scope > button, :scope > a"); + if (!control) return; + + const disabled = + this.hasAttribute("disabled") || + this.hasAttribute("loading"); + + if (disabled) { + if (control.dataset.zenManagedAriaDisabled !== "true") { + control.dataset.zenOriginalAriaDisabled = + control.getAttribute("aria-disabled") ?? "__zen_missing__"; + control.dataset.zenManagedAriaDisabled = "true"; + } + control.setAttribute("aria-disabled", "true"); + } else { + this.restoreDisabledState(control); + } + + if (this.hasAttribute("loading")) { + if (control.dataset.zenManagedAriaBusy !== "true") { + control.dataset.zenOriginalAriaBusy = + control.getAttribute("aria-busy") ?? "__zen_missing__"; + control.dataset.zenManagedAriaBusy = "true"; + } + control.setAttribute("aria-busy", "true"); + } else if (control.dataset.zenManagedAriaBusy === "true") { + if (control.dataset.zenOriginalAriaBusy === "__zen_missing__") { + control.removeAttribute("aria-busy"); + } else { + control.setAttribute( + "aria-busy", + control.dataset.zenOriginalAriaBusy, + ); + } + delete control.dataset.zenManagedAriaBusy; + delete control.dataset.zenOriginalAriaBusy; + } + } + + restoreDisabledState(control) { + if (control.dataset.zenManagedAriaDisabled === "true") { + if (control.dataset.zenOriginalAriaDisabled === "__zen_missing__") { + control.removeAttribute("aria-disabled"); + } else { + control.setAttribute( + "aria-disabled", + control.dataset.zenOriginalAriaDisabled, + ); + } + delete control.dataset.zenManagedAriaDisabled; + delete control.dataset.zenOriginalAriaDisabled; + } + } + + restoreManagedState(control) { + this.restoreDisabledState(control); + if (control.dataset.zenManagedAriaBusy === "true") { + if (control.dataset.zenOriginalAriaBusy === "__zen_missing__") { + control.removeAttribute("aria-busy"); + } else { + control.setAttribute( + "aria-busy", + control.dataset.zenOriginalAriaBusy, + ); + } + delete control.dataset.zenManagedAriaBusy; + delete control.dataset.zenOriginalAriaBusy; + } + } +} + +if (!customElements.get("zen-button")) { + customElements.define("zen-button", ZenButton); +}