view design_system/src/components/link.js @ 272:41a49c29a28f

polish JRPG conversation experience Integrate desktop conversations into the utility panel, simplify the mobile frame, add modal destinations and a reusable Zenbu composer lab, and preserve explicit conversation resume behavior. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 16:05:29 -0700
parents 60a876c4587a
children
line wrap: on
line source

/**
 * Adds an optional reusable effect to one direct native link.
 *
 * @extends {HTMLElement}
 */
export class ZenLink extends HTMLElement {
  static get observedAttributes() {
    return ["effect"];
  }

  connectedCallback() {
    this._observer ||= new MutationObserver(() => this.sync());
    this._observer.observe(this, { childList: true });
    this.sync();
  }

  disconnectedCallback() {
    this._observer?.disconnect();
    this.removeGeneratedDecoration();
  }

  attributeChangedCallback() {
    if (this.isConnected) this.sync();
  }

  removeGeneratedDecoration() {
    this.querySelector(
      ':scope > a > zen-icon[data-zen-link-decoration][data-zen-generated="true"]',
    )?.remove();
  }

  sync() {
    const link = this.querySelector(":scope > a");
    if (!link || this.getAttribute("effect") !== "paw") {
      this.removeGeneratedDecoration();
      return;
    }
    let decoration = link.querySelector(
      ":scope > zen-icon[data-zen-link-decoration]",
    );
    if (!decoration) {
      decoration = document.createElement("zen-icon");
      decoration.dataset.zenLinkDecoration = "";
      decoration.dataset.zenGenerated = "true";
      decoration.setAttribute("name", "paw");
      link.prepend(decoration);
    }
  }
}

if (!customElements.get("zen-link")) {
  customElements.define("zen-link", ZenLink);
}