aboutsummaryrefslogtreecommitdiffstats
path: root/app/components.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/components.js')
-rw-r--r--app/components.js910
1 files changed, 482 insertions, 428 deletions
diff --git a/app/components.js b/app/components.js
index 72c0595..27ad900 100644
--- a/app/components.js
+++ b/app/components.js
@@ -617,56 +617,46 @@ export class Widgets {
}
}
-export class Sidebar {
+export class LeftSidebar {
+ /** @type {HTMLElement} */
+ #rootElement;
/** @type {Histogram} */
#histogram;
/** @type {House[]} */
#allHouses;
- /** @type {HTMLElement} */
- #rootElement;
- /** @type {boolean} */
- #collapsed = false;
/** @type {Filters} */
#filters;
/** @type {AreaParam} */
#areaParam;
/** @type {HouseParameter} */
#houseParam;
- /** @type {Weights} */
- #weights;
+ /** @type {boolean} */
+ #collapsed = true;
/** @type {() => void} */
#onFilterChange;
- /** @type {(key: string, value: number) => void} */
- #onWeightChange;
/** @type {(param: string) => void} */
#onColorChange;
/** @type {(param: string) => void} */
#onAreaColorChange;
- /** @type {HTMLElement|null} */
- #filtersSectionElement;
/**
* @param {Object} options
* @param {House[]} options.allHouses
- * @param {AreaParam} options.areaParam *
- * @param {AreaParam} options.houseParam
+ * @param {AreaParam} options.areaParam
+ * @param {HouseParameter} options.houseParam
* @param {Filters} options.filters
- * @param {Weights} options.weights
* @param {() => void} options.onFilterChange
- * @param {(key: string, value: number) => void} options.onWeightChange
- * @param {(param: string) => void} options.onHouseParamChange
- * @param {(param: string) => void} options.onAreaParamChange
+ * @param {(param: string) => void} options.onColorChange
+ * @param {(param: string) => void} options.onAreaColorChange
*/
constructor(options) {
this.#areaParam = options.areaParam;
this.#houseParam = options.houseParam;
this.#allHouses = options.allHouses;
this.#filters = options.filters;
- this.#weights = options.weights;
this.#onFilterChange = options.onFilterChange;
- this.#onWeightChange = options.onWeightChange;
- this.#onColorChange = options.onHouseParamChange;
- this.#onAreaColorChange = options.onAreaParamChange;
+ this.#onColorChange = options.onColorChange;
+ this.#onAreaColorChange = options.onAreaColorChange;
const initialValues = this.#allHouses?.map((house) => house.get(this.#houseParam));
this.#histogram = new Histogram({
@@ -676,452 +666,549 @@ export class Sidebar {
values: initialValues || [],
});
- this.#filtersSectionElement = null;
this.#rootElement = this.#render();
}
/**
- * @param {Weights} weights
- * @param {(key: string, value: number) => void} onChange
+ * Handle histogram bar click
+ * @param {number} min
+ * @param {number} max
*/
- static weightSection(weights, onChange) {
- return Dom.div(
- new DomOptions({
- children: [
- Dom.heading(
- 3,
- "Scoring Weights",
- new DomOptions({
- styles: {
- color: "#333",
- fontSize: "1.1rem",
- margin: "1rem 0 1rem 0",
- },
- }),
- ),
- // Basic house properties
- Widgets.slider("w-price", "Price", "price", weights.price, onChange),
- Widgets.slider(
- "w-year",
- "Construction Year",
- "constructionYear",
- weights.constructionYear,
- onChange,
- ),
- Widgets.slider("w-area", "Living Area", "livingArea", weights.livingArea, onChange),
-
- // Location factors
- Widgets.slider(
- "w-market",
- "Market Distance",
- "distanceMarket",
- weights.distanceMarket,
- onChange,
- ),
- Widgets.slider(
- "w-school",
- "School Distance",
- "distanceSchool",
- weights.distanceSchool,
- onChange,
- ),
-
- // Transit distances
- Widgets.slider(
- "w-train",
- "Train Distance",
- "distanceTrain",
- weights.distanceTrain,
- onChange,
- ),
- Widgets.slider(
- "w-lightrail",
- "Light Rail Distance",
- "distanceLightRail",
- weights.distanceLightRail,
- onChange,
- ),
- Widgets.slider("w-tram", "Tram Distance", "distanceTram", weights.distanceTram, onChange),
+ #handleHistogramClick(min, max) {
+ const param = this.#houseParam;
- // Statistical area factors
- Widgets.slider(
- "w-foreign",
- "Foreign Speakers",
- "foreignSpeakers",
- weights.foreignSpeakers,
- onChange,
- ),
- Widgets.slider(
- "w-unemployment",
- "Unemployment Rate",
- "unemploymentRate",
- weights.unemploymentRate,
- onChange,
- ),
- Widgets.slider(
- "w-income",
- "Average Income",
- "averageIncome",
- weights.averageIncome,
- onChange,
- ),
- Widgets.slider(
- "w-education",
- "Higher Education",
- "higherEducation",
- weights.higherEducation,
- onChange,
- ),
- ],
- }),
- );
+ switch (param) {
+ case HouseParameter.price: {
+ this.#filters.minPrice = min;
+ this.#filters.maxPrice = max;
+ break;
+ }
+ case HouseParameter.area: {
+ this.#filters.minArea = min;
+ this.#filters.maxArea = max;
+ break;
+ }
+ case HouseParameter.year: {
+ this.#filters.minYear = min;
+ this.#filters.maxYear = max;
+ break;
+ }
+ case HouseParameter.score: {
+ // Handle score filtering if needed
+ console.log(`Score range: ${min} - ${max}`);
+ break;
+ }
+ }
+ this.#onFilterChange();
}
/**
- * @param {AreaParam} areaParam
+ * Update histogram when house parameter changes
* @param {HouseParameter} houseParam
- * @param {(param: string) => void} onHouseChange
- * @param {(param: string) => void} onAreaChange
*/
- static dataSection(areaParam, houseParam, onHouseChange, onAreaChange) {
- return Dom.div(
- new DomOptions({
- children: [
- Dom.heading(
- 3,
- "Visualisation parameters",
- new DomOptions({
- styles: {
- color: "#333",
- fontSize: "1.1rem",
- margin: "0 0 1rem 0",
- },
- }),
- ),
- Widgets.dropdown(
- "Color houses by",
- [
- { text: "Price", value: HouseParameter.price },
- { text: "Score", value: HouseParameter.score },
- { text: "Construction Year", value: HouseParameter.year },
- { text: "Living Area", value: HouseParameter.area },
- ],
- houseParam,
- (value) => onHouseChange(value),
- new DomOptions({
- id: "color-parameter",
- styles: {
- marginBottom: "1rem",
- },
- }),
- ),
- Widgets.dropdown(
- "Color areas by",
- [
- { text: "None", value: AreaParam.none },
- { text: "Foreign speakers", value: AreaParam.foreignSpeakers },
- { text: "Unemployment rate", value: AreaParam.unemploymentRate },
- { text: "Average income", value: AreaParam.averageIncome },
- { text: "Higher education", value: AreaParam.higherEducation },
- ],
- areaParam,
- (value) => onAreaChange(value),
- new DomOptions({
- id: "area-color-parameter",
- }),
- ),
- ],
- styles: {
- borderBottom: "1px solid #eee",
- paddingBottom: "1rem",
- },
- }),
- );
+ #updateHistogram(houseParam) {
+ this.#houseParam = houseParam;
+ const values = this.#allHouses.map((house) => house.get(houseParam));
+ this.#histogram.update(values, houseParam);
}
+
/**
- * @param {Filters} filters
- * @param {House[]} houses
- * @param {() => void} onChange
+ * Render sidebar content
+ * @returns {HTMLElement}
*/
- static filtersSection(filters, houses, onChange) {
- const priceRange = {
- max: filters.maxPrice,
- min: filters.minPrice,
- step: 10000,
- };
-
- const yearRange = {
- max: filters.maxYear,
- min: filters.minYear,
- step: 1,
- };
-
- const areaRange = {
- max: filters.maxArea,
- min: filters.minArea,
- step: 10,
- };
-
- const lotRange = {
- max: filters.maxArea,
- min: filters.minArea,
- step: 100,
- };
-
+ #renderContent() {
return Dom.div(
new DomOptions({
children: [
- Dom.heading(
- 3,
- "Filters",
+ // Histogram section
+ Dom.div(
new DomOptions({
+ children: [
+ Dom.heading(
+ 3,
+ "Distribution",
+ new DomOptions({
+ styles: {
+ color: "#333",
+ fontSize: "1.1rem",
+ margin: "0 0 0.5rem 0",
+ },
+ }),
+ ),
+ this.#histogram.render(),
+ ],
styles: {
- color: "#333",
- fontSize: "1.1rem",
- margin: "1rem 0 1rem 0",
+ borderBottom: "1px solid #eee",
+ paddingBottom: "1rem",
},
}),
),
- Widgets.range(
- "Price Range (€)",
- priceRange.min,
- priceRange.max,
- filters.minPrice,
- filters.maxPrice,
- (min, max) => {
- filters.minPrice = min;
- filters.maxPrice = max === priceRange.max ? Number.POSITIVE_INFINITY : max;
- onChange();
- },
- priceRange.step,
- new DomOptions({
- id: "price-range",
- }),
- ),
- Widgets.range(
- "Construction Year",
- yearRange.min,
- yearRange.max,
- filters.minYear,
- filters.maxYear,
- (min, max) => {
- filters.minYear = min;
- filters.maxYear = max === yearRange.max ? Number.POSITIVE_INFINITY : max;
- onChange();
- },
- yearRange.step,
- new DomOptions({
- id: "year-range",
- }),
- ),
-
- Widgets.range(
- "Living Area (m²)",
- areaRange.min,
- areaRange.max,
- filters.minArea,
- filters.maxArea,
- (min, max) => {
- filters.minArea = min;
- filters.maxArea = max === areaRange.max ? Number.POSITIVE_INFINITY : max;
- onChange();
- },
- areaRange.step,
- new DomOptions({
- id: "area-range",
- }),
- ),
- Widgets.range(
- "Lot Size (m²)",
- lotRange.min,
- lotRange.max,
- filters.minLot,
- filters.maxLot,
- (min, max) => {
- filters.minLot = min;
- filters.maxLot = max === lotRange.max ? Number.POSITIVE_INFINITY : max;
- onChange();
- },
- lotRange.step,
+ // Data visualization parameters
+ Dom.div(
new DomOptions({
- id: "lot-range",
+ children: [
+ Dom.heading(
+ 3,
+ "Visualisation parameters",
+ new DomOptions({
+ styles: {
+ color: "#333",
+ fontSize: "1.1rem",
+ margin: "0 0 1rem 0",
+ },
+ }),
+ ),
+ Widgets.dropdown(
+ "Color houses by",
+ [
+ { text: "Price", value: HouseParameter.price },
+ { text: "Score", value: HouseParameter.score },
+ { text: "Construction Year", value: HouseParameter.year },
+ { text: "Living Area", value: HouseParameter.area },
+ ],
+ this.#houseParam,
+ (value) => {
+ this.#onColorChange(value);
+ this.#updateHistogram(value);
+ },
+ new DomOptions({
+ id: "color-parameter",
+ styles: {
+ marginBottom: "1rem",
+ },
+ }),
+ ),
+ Widgets.dropdown(
+ "Color areas by",
+ [
+ { text: "None", value: AreaParam.none },
+ { text: "Foreign speakers", value: AreaParam.foreignSpeakers },
+ { text: "Unemployment rate", value: AreaParam.unemploymentRate },
+ { text: "Average income", value: AreaParam.averageIncome },
+ { text: "Higher education", value: AreaParam.higherEducation },
+ ],
+ this.#areaParam,
+ (value) => this.#onAreaColorChange(value),
+ new DomOptions({
+ id: "area-color-parameter",
+ }),
+ ),
+ ],
+ styles: {
+ borderBottom: "1px solid #eee",
+ paddingBottom: "1rem",
+ },
}),
),
- // Districts Multi-select
+ // Filters section
Dom.div(
new DomOptions({
children: [
- Dom.label(
- "district-select",
- "Districts",
+ Dom.heading(
+ 3,
+ "Filters",
new DomOptions({
styles: {
- fontSize: "0.85rem",
- fontWeight: "bold",
- marginBottom: "0.25rem",
+ color: "#333",
+ fontSize: "1.1rem",
+ margin: "1rem 0 1rem 0",
},
}),
),
- Dom.select(
- undefined,
- (e) => {
- const target = /** @type {HTMLSelectElement} */ (e.target);
- const selectedOptions = Array.from(target.selectedOptions).map(
- (opt) => opt.value,
- );
- filters.districts = selectedOptions;
- onChange();
+
+ // Price filter
+ Widgets.range(
+ "Price Range (€)",
+ this.#filters.minPrice,
+ this.#filters.maxPrice,
+ this.#filters.minPrice,
+ this.#filters.maxPrice,
+ (min, max) => {
+ this.#filters.minPrice = min;
+ this.#filters.maxPrice =
+ max === this.#filters.maxPrice ? Number.POSITIVE_INFINITY : max;
+ this.#onFilterChange();
+ },
+ 10000,
+ new DomOptions({
+ id: "price-range",
+ }),
+ ),
+
+ // Construction year filter
+ Widgets.range(
+ "Construction Year",
+ this.#filters.minYear,
+ this.#filters.maxYear,
+ this.#filters.minYear,
+ this.#filters.maxYear,
+ (min, max) => {
+ this.#filters.minYear = min;
+ this.#filters.maxYear =
+ max === this.#filters.maxYear ? Number.POSITIVE_INFINITY : max;
+ this.#onFilterChange();
+ },
+ 1,
+ new DomOptions({
+ id: "year-range",
+ }),
+ ),
+
+ // Living area filter
+ Widgets.range(
+ "Living Area (m²)",
+ this.#filters.minArea,
+ this.#filters.maxArea,
+ this.#filters.minArea,
+ this.#filters.maxArea,
+ (min, max) => {
+ this.#filters.minArea = min;
+ this.#filters.maxArea =
+ max === this.#filters.maxArea ? Number.POSITIVE_INFINITY : max;
+ this.#onFilterChange();
+ },
+ 10,
+ new DomOptions({
+ id: "area-range",
+ }),
+ ),
+
+ // Lot size filter
+ Widgets.range(
+ "Lot Size (m²)",
+ this.#filters.minLot,
+ this.#filters.maxLot,
+ this.#filters.minLot,
+ this.#filters.maxLot,
+ (min, max) => {
+ this.#filters.minLot = min;
+ this.#filters.maxLot =
+ max === this.#filters.maxLot ? Number.POSITIVE_INFINITY : max;
+ this.#onFilterChange();
+ },
+ 100,
+ new DomOptions({
+ id: "lot-range",
+ }),
+ ),
+
+ // Districts Multi-select
+ Dom.div(
+ new DomOptions({
+ children: [
+ Dom.label(
+ "district-select",
+ "Districts",
+ new DomOptions({
+ styles: {
+ fontSize: "0.85rem",
+ fontWeight: "bold",
+ marginBottom: "0.25rem",
+ },
+ }),
+ ),
+ Dom.select(
+ undefined,
+ (e) => {
+ const target = /** @type {HTMLSelectElement} */ (e.target);
+ const selectedOptions = Array.from(target.selectedOptions).map(
+ (opt) => opt.value,
+ );
+ this.#filters.districts = selectedOptions;
+ this.#onFilterChange();
+ },
+ new DomOptions({
+ attributes: { multiple: "true" },
+ children: [...this.#renderDistrictOptions()],
+ id: "district-select",
+ styles: {
+ border: "1px solid #ddd",
+ borderRadius: "4px",
+ minHeight: "120px",
+ padding: "0.5rem",
+ width: "100%",
+ },
+ }),
+ ),
+ ],
+ styles: {
+ display: "flex",
+ flexDirection: "column",
+ },
+ }),
+ ),
+
+ // Clear Filters Button
+ Dom.button(
+ "Clear All Filters",
+ () => {
+ this.#filters.reset();
+ this.#onFilterChange();
},
new DomOptions({
- attributes: { multiple: "true" },
- children: [...Sidebar.#renderDistrictOptions(houses)],
- id: "district-select",
styles: {
- border: "1px solid #ddd",
+ background: "#f44336",
+ border: "none",
borderRadius: "4px",
- minHeight: "120px",
- padding: "0.5rem",
+ color: "white",
+ cursor: "pointer",
+ fontSize: "0.85rem",
+ marginTop: "1rem",
+ padding: "0.5rem 1rem",
width: "100%",
},
}),
),
],
styles: {
- display: "flex",
- flexDirection: "column",
+ borderBottom: "1px solid #eee",
+ paddingBottom: "1rem",
},
}),
),
+ ],
+ id: "left-sidebar-content",
+ styles: {
+ display: this.#collapsed ? "none" : "block",
+ height: "100%",
+ overflowY: "auto",
+ },
+ }),
+ );
+ }
- // Clear Filters Button
- Dom.button(
- "Clear All Filters",
- () => {
- // Reset all ranges to their maximum possible values
- filters.reset();
+ /**
+ * Render district options for multi-select
+ * @returns {HTMLOptionElement[]}
+ */
+ #renderDistrictOptions() {
+ const houseDistricts = [
+ ...new Set(this.#allHouses.map((h) => h.district).filter((d) => d)),
+ ].sort();
+ return houseDistricts.map((districtName) => Dom.option(districtName, districtName));
+ }
- // Update the UI by triggering onChange
- onChange();
- },
+ /**
+ * Render the complete sidebar
+ * @returns {HTMLElement}
+ */
+ #render() {
+ return Dom.div(
+ new DomOptions({
+ children: [
+ // Toggle button
+ Dom.button(
+ "☰",
+ () => this.toggle(),
new DomOptions({
+ id: "left-sidebar-toggle",
styles: {
- background: "#f44336",
+ background: "none",
border: "none",
- borderRadius: "4px",
- color: "white",
+ color: "#333",
cursor: "pointer",
- fontSize: "0.85rem",
- marginTop: "1rem",
- padding: "0.5rem 1rem",
- width: "100%",
+ fontSize: "1.5rem",
+ left: "0.5rem",
+ padding: "0.5rem",
+ position: "absolute",
+ top: "0.5rem",
+ zIndex: "10",
},
}),
),
+ this.#renderContent(),
],
+ id: "left-sidebar",
styles: {
- borderBottom: "1px solid #eee",
- paddingBottom: "1rem",
+ background: "#fff",
+ borderRight: "1px solid #ddd",
+ display: "flex",
+ flexDirection: "column",
+ flexShrink: "0",
+ height: "100%",
+ overflowY: "auto",
+ padding: this.#collapsed ? "0" : "1rem",
+ position: "relative",
+ transition: "width 0.3s ease, padding 0.3s ease",
+ width: this.#collapsed ? "0" : "300px",
},
}),
);
}
/**
- * Update sidebar with new data
- * @param {HouseParameter} houseParam
+ * Get the root DOM element
+ * @returns {HTMLElement}
*/
- update(houseParam) {
- const values = this.#allHouses.map((house) => house.get(houseParam));
- this.#histogram.update(values, houseParam);
- this.#updateFiltersSection(this.#allHouses);
+ render() {
+ return this.#rootElement;
}
- /**
- * Update filters section with current data
- * @param {House[]} houses
- */
- #updateFiltersSection(houses) {
- if (!houses || houses.length === 0) return;
+ /** Toggle sidebar visibility */
+ toggle() {
+ this.#collapsed = !this.#collapsed;
+ const sidebarContent = this.#rootElement.querySelector("#left-sidebar-content");
- const newFiltersSection = Sidebar.filtersSection(
- this.#filters,
- this.#allHouses,
- this.#onFilterChange,
- );
+ if (this.#collapsed) {
+ this.#rootElement.style.width = "0";
+ this.#rootElement.style.padding = "0";
+ if (sidebarContent) sidebarContent.style.display = "none";
+ } else {
+ this.#rootElement.style.width = "300px";
+ this.#rootElement.style.padding = "1rem";
+ if (sidebarContent) sidebarContent.style.display = "block";
+ }
+ }
- if (this.#filtersSectionElement) {
- this.#filtersSectionElement.replaceWith(newFiltersSection);
+ /** Show the sidebar */
+ show() {
+ if (this.#collapsed) {
+ this.toggle();
}
+ }
- this.#filtersSectionElement = newFiltersSection;
+ /** Hide the sidebar */
+ hide() {
+ if (!this.#collapsed) {
+ this.toggle();
+ }
}
+}
+
+export class RightSidebar {
+ /** @type {HTMLElement} */
+ #rootElement;
+ /** @type {Weights} */
+ #weights;
+ /** @type {boolean} */
+ #collapsed = true;
+ /** @type {(key: string, value: number) => void} */
+ #onWeightChange;
/**
- * Handle histogram bar click
- * @param {number} min
- * @param {number} max
+ * @param {Object} options
+ * @param {Weights} options.weights
+ * @param {(key: string, value: number) => void} options.onWeightChange
*/
- #handleHistogramClick(min, max) {
- const param = this.#houseParam;
-
- switch (param) {
- case HouseParameter.price: {
- this.#filters.minPrice = min;
- this.#filters.maxPrice = max;
- break;
- }
- case HouseParameter.area: {
- this.#filters.minArea = min;
- this.#filters.maxArea = max;
- break;
- }
- case HouseParameter.year: {
- this.#filters.minYear = min;
- this.#filters.maxYear = max;
- break;
- }
- case HouseParameter.score: {
- // Handle score filtering if needed
- console.log(`Score range: ${min} - ${max}`);
- break;
- }
- }
- // Trigger the filter change to update the application
- this.#onFilterChange();
+ constructor(options) {
+ this.#weights = options.weights;
+ this.#onWeightChange = options.onWeightChange;
+ this.#rootElement = this.#render();
}
/**
- * @param {Histogram} histogram
+ * Render weights section
+ * @returns {HTMLElement}
*/
- static histogramSection(histogram) {
+ #renderWeights() {
return Dom.div(
new DomOptions({
children: [
Dom.heading(
3,
- "Distribution",
+ "Scoring Weights",
new DomOptions({
styles: {
color: "#333",
fontSize: "1.1rem",
- margin: "0 0 0.5rem 0",
+ margin: "1rem 0 1rem 0",
},
}),
),
- histogram.render(),
+ // Basic house properties
+ Widgets.slider("w-price", "Price", "price", this.#weights.price, this.#onWeightChange),
+ Widgets.slider(
+ "w-year",
+ "Construction Year",
+ "constructionYear",
+ this.#weights.constructionYear,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-area",
+ "Living Area",
+ "livingArea",
+ this.#weights.livingArea,
+ this.#onWeightChange,
+ ),
+
+ // Location factors
+ Widgets.slider(
+ "w-market",
+ "Market Distance",
+ "distanceMarket",
+ this.#weights.distanceMarket,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-school",
+ "School Distance",
+ "distanceSchool",
+ this.#weights.distanceSchool,
+ this.#onWeightChange,
+ ),
+
+ // Transit distances
+ Widgets.slider(
+ "w-train",
+ "Train Distance",
+ "distanceTrain",
+ this.#weights.distanceTrain,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-lightrail",
+ "Light Rail Distance",
+ "distanceLightRail",
+ this.#weights.distanceLightRail,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-tram",
+ "Tram Distance",
+ "distanceTram",
+ this.#weights.distanceTram,
+ this.#onWeightChange,
+ ),
+
+ // Statistical area factors
+ Widgets.slider(
+ "w-foreign",
+ "Foreign Speakers",
+ "foreignSpeakers",
+ this.#weights.foreignSpeakers,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-unemployment",
+ "Unemployment Rate",
+ "unemploymentRate",
+ this.#weights.unemploymentRate,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-income",
+ "Average Income",
+ "averageIncome",
+ this.#weights.averageIncome,
+ this.#onWeightChange,
+ ),
+ Widgets.slider(
+ "w-education",
+ "Higher Education",
+ "higherEducation",
+ this.#weights.higherEducation,
+ this.#onWeightChange,
+ ),
],
- styles: {
- borderBottom: "1px solid #eee",
- paddingBottom: "1rem",
- },
}),
);
}
/**
- * Render sidebar container
+ * Render the complete sidebar
* @returns {HTMLElement}
*/
#render() {
@@ -1130,10 +1217,10 @@ export class Sidebar {
children: [
// Toggle button
Dom.button(
- "☰",
+ "⚙️",
() => this.toggle(),
new DomOptions({
- id: "sidebar-toggle",
+ id: "right-sidebar-toggle",
styles: {
background: "none",
border: "none",
@@ -1150,33 +1237,29 @@ export class Sidebar {
),
Dom.div(
new DomOptions({
- children: [
- Sidebar.histogramSection(this.#histogram),
- Sidebar.dataSection(
- this.#areaParam,
- this.#houseParam,
- this.#onColorChange,
- this.#onAreaColorChange,
- ),
- Sidebar.filtersSection(this.#filters, this.#allHouses, this.#onFilterChange),
- Sidebar.weightSection(this.#weights, this.#onWeightChange),
- ],
- id: "sidebar-content",
+ children: [this.#renderWeights()],
+ id: "right-sidebar-content",
+ styles: {
+ display: this.#collapsed ? "none" : "block",
+ height: "100%",
+ overflowY: "auto",
+ },
}),
),
],
- id: "sidebar",
+ id: "right-sidebar",
styles: {
background: "#fff",
- borderRight: "1px solid #ddd",
+ borderLeft: "1px solid #ddd",
display: "flex",
flexDirection: "column",
flexShrink: "0",
+ height: "100%",
overflowY: "auto",
- padding: "1rem",
+ padding: this.#collapsed ? "0" : "1rem",
position: "relative",
- transition: "width 0.3s ease",
- width: "300px",
+ transition: "width 0.3s ease, padding 0.3s ease",
+ width: this.#collapsed ? "0" : "300px",
},
}),
);
@@ -1190,63 +1273,34 @@ export class Sidebar {
return this.#rootElement;
}
- /** Show the sidebar */
- show() {
- if (this.#collapsed) {
- this.toggle();
- }
- }
-
- /** Hide the sidebar */
- hide() {
- if (!this.#collapsed) {
- this.toggle();
- }
- }
-
+ /** Toggle sidebar visibility */
toggle() {
this.#collapsed = !this.#collapsed;
- const sidebarContent = this.#rootElement.querySelector("#sidebar-content");
- const toggleButton = this.#rootElement.querySelector("#sidebar-toggle");
+ const sidebarContent = this.#rootElement.querySelector("#right-sidebar-content");
if (this.#collapsed) {
this.#rootElement.style.width = "0";
this.#rootElement.style.padding = "0";
if (sidebarContent) sidebarContent.style.display = "none";
- if (toggleButton) {
- toggleButton.textContent = "☰";
- toggleButton.style.right = "0.5rem";
- }
} else {
this.#rootElement.style.width = "300px";
this.#rootElement.style.padding = "1rem";
if (sidebarContent) sidebarContent.style.display = "block";
- if (toggleButton) {
- toggleButton.textContent = "☰";
- toggleButton.style.right = "0.5rem";
- }
}
}
- /**
- * Set the area color parameter in the dropdown
- * @param {string} param
- */
- setAreaParameter(param) {
- const areaColorSelect = this.#rootElement.querySelector("#area-color-parameter");
- if (areaColorSelect) {
- areaColorSelect.value = param;
+ /** Show the sidebar */
+ show() {
+ if (this.#collapsed) {
+ this.toggle();
}
}
- /**
- * Render district options for multi-select
- * @param {House[]} houses
- * @returns {HTMLOptionElement[]}
- */
- static #renderDistrictOptions(houses) {
- const houseDistricts = [...new Set(houses.map((h) => h.district).filter((d) => d))].sort();
- return houseDistricts.map((districtName) => Dom.option(districtName, districtName));
+ /** Hide the sidebar */
+ hide() {
+ if (!this.#collapsed) {
+ this.toggle();
+ }
}
}