aboutsummaryrefslogtreecommitdiffstats
path: root/app/dom.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/dom.js')
-rw-r--r--app/dom.js166
1 files changed, 116 insertions, 50 deletions
diff --git a/app/dom.js b/app/dom.js
index cf69e95..50768da 100644
--- a/app/dom.js
+++ b/app/dom.js
@@ -1,3 +1,4 @@
+// dom.js
import { House } from "models";
export class DomOptions {
@@ -216,56 +217,6 @@ export class Dom {
export class Widgets {
/**
- * Build a modal dialog
- * @param {object} style
- * @param {() => void} onClose
- * @returns {HTMLDialogElement}
- */
- static buildModal(style, onClose) {
- const modal = document.createElement("dialog");
- Object.assign(
- modal.style,
- {
- background: "white",
- border: "none",
- borderRadius: "8px",
- boxShadow: "0 4px 20px rgba(0,0,0,0.2)",
- maxHeight: "80vh",
- maxWidth: "600px",
- overflowY: "auto",
- padding: "20px",
- position: "fixed",
- top: "50%",
- transform: "translateY(-50%)",
- width: "90%",
- zIndex: "1000",
- },
- style,
- );
-
- const closeBtn = Dom.button(
- "x",
- onClose,
- new DomOptions({
- styles: {
- background: "none",
- border: "none",
- color: "#666",
- cursor: "pointer",
- fontSize: "24px",
- position: "absolute",
- right: "10px",
- top: "10px",
- },
- }),
- );
-
- modal.appendChild(closeBtn);
- modal.addEventListener("close", onClose);
- return modal;
- }
-
- /**
* Build modal content for a house
* @param {House} house
* @returns {DocumentFragment}
@@ -552,3 +503,118 @@ export class Widgets {
);
}
}
+
+export class Modal {
+ /** @type {HTMLDialogElement} */
+ #dialog;
+ /** @type {AbortController} */
+ #abortController;
+ /** @type {number | undefined} */
+ #timer;
+ /** @type {boolean} */
+ #persistent;
+ /** @type {House} */
+ #house;
+ /** @type {() => void} */
+ #onHide;
+ /** @type {() => void} */
+ #onClearMapTimer;
+
+ /**
+ * @param {House} house
+ * @param {boolean} persistent
+ * @param {object} positionStyles
+ * @param {() => void} onHide
+ * @param {() => void} onClearMapTimer
+ */
+ constructor(house, persistent, positionStyles, onHide, onClearMapTimer) {
+ this.#house = house;
+ this.#persistent = persistent;
+ this.#onHide = onHide;
+ this.#onClearMapTimer = onClearMapTimer;
+ this.#abortController = new AbortController();
+ this.#dialog = document.createElement("dialog");
+
+ Object.assign(
+ this.#dialog.style,
+ {
+ background: "white",
+ border: "none",
+ borderRadius: "8px",
+ boxShadow: "0 4px 20px rgba(0,0,0,0.2)",
+ maxHeight: "80vh",
+ maxWidth: "600px",
+ overflowY: "auto",
+ padding: "20px",
+ position: "fixed",
+ top: "50%",
+ transform: "translateY(-50%)",
+ width: "90%",
+ zIndex: "1000",
+ },
+ positionStyles,
+ );
+
+ const closeBtn = Dom.button(
+ "x",
+ () => this.hide(),
+ new DomOptions({
+ styles: {
+ background: "none",
+ border: "none",
+ color: "#666",
+ cursor: "pointer",
+ fontSize: "24px",
+ position: "absolute",
+ right: "10px",
+ top: "10px",
+ },
+ }),
+ );
+
+ this.#dialog.appendChild(closeBtn);
+ this.#dialog.appendChild(Widgets.buildModalContent(house));
+
+ // Add event listeners with AbortController
+ this.#dialog.addEventListener("close", () => this.hide(), {
+ signal: this.#abortController.signal,
+ });
+ this.#dialog.addEventListener(
+ "mouseenter",
+ () => {
+ clearTimeout(this.#timer);
+ this.#onClearMapTimer();
+ },
+ { signal: this.#abortController.signal },
+ );
+ this.#dialog.addEventListener(
+ "mouseleave",
+ () => {
+ if (!this.#persistent) {
+ this.#timer = setTimeout(() => this.hide(), 200);
+ }
+ },
+ { signal: this.#abortController.signal },
+ );
+ }
+
+ render() {
+ return this.#dialog;
+ }
+
+ show() {
+ if (this.#persistent) {
+ this.#dialog.showModal();
+ } else {
+ this.#dialog.show();
+ }
+ }
+
+ hide() {
+ clearTimeout(this.#timer);
+ this.#dialog.close();
+ this.#dialog.remove();
+ this.#abortController.abort();
+ this.#onHide();
+ }
+}