Tiny·Engine (Core)

Recipe

Modal

A modal capsule controlled by data API triggers.

Markup

modal.html
<button data-ui-toggle="modal" data-ui-action="open" data-target="#confirm">
  Delete account
</button>

<dialog ui-modal id="confirm">
  <h2>Are you sure?</h2>
  <p>This cannot be undone.</p>
  <button ref="cancel">Cancel</button>
  <button ref="confirm">Delete</button>
</dialog>

Capsule

modal.ts
import { UI, Capsule } from "tiny-engine-core";

class Modal extends Capsule {
  constructor(el, options) {
    super(el, options);
    this.lastFocus = null;

    this.on(this.refs.cancel, "click", () => this.close());
    this.on(this.refs.confirm, "click", () => {
      const event = this.emit("modal:confirm", null, { cancelable: true });
      if (!event.defaultPrevented) this.close();
    });
    this.on(document, "keydown", (event) => {
      if (event.key === "Escape" && this.el.open) this.close();
    });
  }

  open() {
    this.lastFocus = document.activeElement;
    this.el.showModal();
    this.refs.confirm.focus();
  }

  close() {
    this.el.close();
    this.lastFocus?.focus();
  }
}

UI.register("modal", Modal);
UI.init();
UI.observe();

The dialog host mounts from ui-modal. The trigger calls the public open() method through data-ui-toggle, data-ui-action, and data-target. Confirmation emits a cancellable modal:confirm event.