diff options
Diffstat (limited to 'app/dom.js')
| -rw-r--r-- | app/dom.js | 166 |
1 files changed, 116 insertions, 50 deletions
@@ -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(); + } +} |
