aboutsummaryrefslogtreecommitdiffstats
path: root/app/dom.js
diff options
context:
space:
mode:
authorPetri Hienonen <petri.hienonen@gmail.com>2025-11-08 22:05:01 +0200
committerPetri Hienonen <petri.hienonen@gmail.com>2025-11-08 22:05:01 +0200
commit277ffe2cab8c711427b979fbc057c7d04932398e (patch)
treecf14de72a58ac1c0712590cef7805518604cdb89 /app/dom.js
parenta9a0070662c2494b37528d27d7420f3da33e749d (diff)
downloadhousing-277ffe2cab8c711427b979fbc057c7d04932398e.tar.zst
Update DOM
Diffstat (limited to 'app/dom.js')
-rw-r--r--app/dom.js200
1 files changed, 59 insertions, 141 deletions
diff --git a/app/dom.js b/app/dom.js
index b1a8e8a..cf69e95 100644
--- a/app/dom.js
+++ b/app/dom.js
@@ -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" },
+ }),
+ );
}
}