diff design_system/src/components/alert.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/alert.js	Tue Aug 04 09:14:57 2026 -0700
@@ -0,0 +1,73 @@
+export class ZenAlert extends HTMLElement {
+  static get observedAttributes() {
+    return ["dismissible", "tone"];
+  }
+
+  connectedCallback() {
+    if (!this._observer) {
+      this._observer = new MutationObserver(() => this.sync());
+    }
+    this._observer.observe(this, { childList: true });
+    this.sync();
+  }
+
+  disconnectedCallback() {
+    this._observer?.disconnect();
+    this.unbindDismissControl();
+  }
+
+  attributeChangedCallback() {
+    this.sync();
+  }
+
+  unbindDismissControl() {
+    if (this._dismissControl && this._dismissHandler) {
+      this._dismissControl.removeEventListener(
+        "click",
+        this._dismissHandler,
+      );
+    }
+    this._dismissControl = null;
+    this._dismissHandler = null;
+  }
+
+  sync() {
+    this.setAttribute(
+      "role",
+      this.getAttribute("tone") === "danger" ? "alert" : "status",
+    );
+
+    let dismiss = this.querySelector(":scope > [data-zen-dismiss]");
+    if (!this.hasAttribute("dismissible")) {
+      this.unbindDismissControl();
+      if (dismiss?.dataset.zenGenerated === "true") dismiss.remove();
+      return;
+    }
+
+    if (!dismiss) {
+      dismiss = document.createElement("button");
+      dismiss.type = "button";
+      dismiss.dataset.zenDismiss = "";
+      dismiss.dataset.zenGenerated = "true";
+      dismiss.setAttribute("aria-label", "Dismiss message");
+      dismiss.textContent = "×";
+      this.append(dismiss);
+    }
+    if (dismiss === this._dismissControl) return;
+
+    this.unbindDismissControl();
+    this._dismissControl = dismiss;
+    this._dismissHandler = () => {
+      this.dispatchEvent(new CustomEvent("zen-dismiss", {
+        bubbles: true,
+        composed: true,
+      }));
+      this.remove();
+    };
+    dismiss.addEventListener("click", this._dismissHandler);
+  }
+}
+
+if (!customElements.get("zen-alert")) {
+  customElements.define("zen-alert", ZenAlert);
+}