Skip to main content

assets/js/skua/hooks/dialog.js

/* SkuaDialog — native <dialog> + showModal().

   Markup (from Skua.Components.Overlay.dialog/1):
     <dialog id="confirm" phx-hook="SkuaDialog"
             phx-mounted={JS.ignore_attributes("open")} data-on-close={…}>
       …<button data-sk-close>Cancel</button>…
     </dialog>

   showModal() gives us the inert backdrop, focus trap, and Esc-to-close for
   free. We listen for skua:open / skua:close events (dispatched by
   open_dialog/close_dialog), wire any [data-sk-close] buttons, and run the
   optional on_close JS command. JS.ignore_attributes("open") (applied in the
   component) keeps the dialog open across LiveView re-renders.
*/
export default {
  mounted() {
    this.open = () => {
      if (!this.el.open) this.el.showModal();
    };
    this.close = () => {
      if (this.el.open) this.el.close();
    };

    this.onOpen = () => this.open();
    this.onCloseEvt = () => this.close();
    this.el.addEventListener("skua:open", this.onOpen);
    this.el.addEventListener("skua:close", this.onCloseEvt);

    // [data-sk-close] anywhere inside closes the dialog (the × and Cancel).
    this.onClick = (e) => {
      if (e.target.closest("[data-sk-close]")) this.close();
    };
    this.el.addEventListener("click", this.onClick);

    // Native close (Esc / requestClose) → run the author's on_close JS.
    this.onNativeClose = () => {
      const cmd = this.el.dataset.onClose;
      if (cmd && cmd !== "[]") this.liveSocket.execJS(this.el, cmd);
    };
    this.el.addEventListener("close", this.onNativeClose);
  },

  destroyed() {
    this.el.removeEventListener("skua:open", this.onOpen);
    this.el.removeEventListener("skua:close", this.onCloseEvt);
    this.el.removeEventListener("click", this.onClick);
    this.el.removeEventListener("close", this.onNativeClose);
  },
};