diff options
| author | Petri Hienonen <petri.hienonen@gmail.com> | 2025-11-08 22:05:01 +0200 |
|---|---|---|
| committer | Petri Hienonen <petri.hienonen@gmail.com> | 2025-11-08 22:05:01 +0200 |
| commit | 277ffe2cab8c711427b979fbc057c7d04932398e (patch) | |
| tree | cf14de72a58ac1c0712590cef7805518604cdb89 /app/dom.js | |
| parent | a9a0070662c2494b37528d27d7420f3da33e749d (diff) | |
| download | housing-277ffe2cab8c711427b979fbc057c7d04932398e.tar.zst | |
Update DOM
Diffstat (limited to 'app/dom.js')
| -rw-r--r-- | app/dom.js | 200 |
1 files changed, 59 insertions, 141 deletions
@@ -34,10 +34,6 @@ export const ToastType = { warning: "warning", }; -/** - * DOM element creation class – every creator applies its own options. - * @class - */ export class Dom { /** * Create a `<div>` @@ -216,29 +212,36 @@ export class Dom { if (o.children) p.append(...o.children); return p; } +} +export class Widgets { /** * Build a modal dialog + * @param {object} style * @param {() => void} onClose * @returns {HTMLDialogElement} */ - static buildModal(onClose) { + 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", - }); + 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", @@ -264,7 +267,7 @@ export class Dom { /** * Build modal content for a house - * @param {import("./models.js").House} house + * @param {House} house * @returns {DocumentFragment} */ static buildModalContent(house) { @@ -507,130 +510,45 @@ export class Dom { } /** - * Build modal content for a house - * @param {House} house - * @returns {DocumentFragment} + * Create a number filter input + * @param {string} id + * @param {string} labelText + * @param {(value: number | null) => void} onChange + * @returns {HTMLElement} */ - static buildHouseModalContent(house) { - const frag = document.createDocumentFragment(); - - /* Header */ - const header = Dom.div( - new DomOptions({ - styles: { - alignItems: "center", - display: "flex", - justifyContent: "space-between", - marginBottom: "20px", - }, - }), - ); - const title = Dom.heading( - 2, - house.address, - new DomOptions({ - styles: { color: "#333", fontSize: "20px", margin: "0" }, - }), - ); - const score = Dom.span( - `Score: ${house.scores.current}`, - new DomOptions({ - styles: { - background: "#e8f5e9", - borderRadius: "4px", - color: "#2e7d32", - fontSize: "16px", - fontWeight: "bold", - padding: "4px 8px", - }, - }), - ); - header.append(title, score); - frag.appendChild(header); - - /* Details grid */ - const grid = Dom.div( + static addNumberFilter(id, labelText, onChange) { + return Dom.div( new DomOptions({ - styles: { - display: "grid", - gap: "15px", - gridTemplateColumns: "repeat(2,1fr)", - marginBottom: "20px", - }, - }), - ); - const details = [ - { label: "Price", value: `€${house.price.toLocaleString()}` }, - { label: "Building Type", value: house.buildingType }, - { label: "Construction Year", value: house.constructionYear?.toString() ?? "N/A" }, - { label: "Living Area", value: `${house.livingArea} m²` }, - { label: "District", value: house.district }, - { label: "Rooms", value: house.rooms?.toString() ?? "N/A" }, - ]; - for (const { label, value } of details) { - const item = Dom.div( - new DomOptions({ - children: [ - Dom.span( - label, - new DomOptions({ - styles: { fontSize: "14px", fontWeight: "bold", marginBottom: "4px" }, - }), - ), - Dom.span(value, new DomOptions({ styles: { color: "#333", fontSize: "14px" } })), - ], - }), - ); - grid.appendChild(item); - } - frag.appendChild(grid); - - /* Description */ - const descSect = Dom.div(new DomOptions({ styles: { marginBottom: "20px" } })); - const descTitle = Dom.span( - "Description", - new DomOptions({ - styles: { fontSize: "14px", fontWeight: "bold", marginBottom: "5px" }, - }), - ); - const descText = Dom.p( - house.description || "No description available.", - new DomOptions({ - styles: { color: "#333", fontSize: "14px", lineHeight: "1.4", marginTop: "5px" }, - }), - ); - descSect.append(descTitle, descText); - frag.appendChild(descSect); - - /* Images */ - if (house.images?.length) { - const imgSect = Dom.div(new DomOptions({ styles: { marginBottom: "20px" } })); - const imgTitle = Dom.span( - "Images", - new DomOptions({ - styles: { fontSize: "14px", fontWeight: "bold", marginBottom: "10px" }, - }), - ); - const imgCont = Dom.div( - new DomOptions({ - styles: { display: "flex", gap: "10px", overflowX: "auto", paddingBottom: "5px" }, - }), - ); - for (const src of house.images.slice(0, 3)) { - imgCont.appendChild( - Dom.img( - src, + children: [ + Dom.label( + id, + labelText, new DomOptions({ - attributes: { loading: "lazy" }, - styles: { borderRadius: "4px", flexShrink: "0", height: "100px" }, + styles: { fontSize: "0.85rem", fontWeight: "bold", marginBottom: "0.25rem" }, }), ), - ); - } - imgSect.append(imgTitle, imgCont); - frag.appendChild(imgSect); - } - - return frag; + Dom.input( + "number", + (e) => { + const target = /** @type {HTMLInputElement} */ (e.target); + const raw = target.value.trim(); + onChange(raw === "" ? null : Number(raw)); + }, + "any", + "", + new DomOptions({ + id, + styles: { + border: "1px solid #ddd", + borderRadius: "4px", + fontSize: "0.9rem", + padding: "0.5rem", + }, + }), + ), + ], + styles: { display: "flex", flexDirection: "column", marginBottom: "1.75rem" }, + }), + ); } } |
