aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2024-01-19 04:00:22 +0100
committerGuilhem Moulin <guilhem@fripost.org>2024-01-20 11:09:23 +0100
commitfd88c1d218347602b00398b1e5d326c8078992e7 (patch)
tree1159fae7a7e64d1be760f268af0e4cafee4ef4a8
parent47c0acada856eaa92f474ff0e87c5f4c5578aad3 (diff)
Add accordions to the layer selection panel.
-rw-r--r--main.js271
-rw-r--r--style.css40
2 files changed, 252 insertions, 59 deletions
diff --git a/main.js b/main.js
index c98179f..ce7c5cc 100644
--- a/main.js
+++ b/main.js
@@ -466,75 +466,84 @@ view.on('change', function(event) {
});
-const styles = {
- 'svk_lines':
- [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) {
- return new Style({
- zIndex: 2,
- stroke: new Stroke({
- color: 'black',
- width: width,
- }),
- });
- }),
- 'svk_pylons':
- [undefined, undefined, undefined, undefined, undefined]
- .concat([3, 4, 5, 6, 8, 10, 15].map(function(radius) {
+const layers = {
+ svk_lines: {
+ style: [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) {
+ return new Style({
+ zIndex: 2,
+ stroke: new Stroke({
+ color: 'black',
+ width: width,
+ }),
+ });
+ }),
+ },
+ svk_pylons: {
+ style: [undefined, undefined, undefined, undefined, undefined]
+ .concat([3, 4, 5, 6, 8, 10, 15].map(function(radius) {
+ return new Style({
+ zIndex: 1,
+ image: new CircleStyle({
+ radius: radius,
+ fill: new Fill({
+ color: 'black',
+ }),
+ }),
+ });
+ })),
+ },
+ svk_stations: {
+ style: [5, 6, 8, 8, 10, 12, 12].map(function(radius) {
return new Style({
- zIndex: 1,
- image: new CircleStyle({
+ zIndex: 0,
+ image: new RegularShape({
radius: radius,
+ points: 4,
+ angle: Math.PI/4,
fill: new Fill({
color: 'black',
}),
}),
});
- })),
- 'svk_stations':
- [5, 6, 8, 8, 10, 12, 12].map(function(radius) {
- return new Style({
- zIndex: 0,
- image: new RegularShape({
- radius: radius,
- points: 4,
- angle: Math.PI/4,
+ })
+ .concat([.5, 1, 1.5, 2, 2].map(function(width) {
+ return new Style({
+ zIndex: 0,
fill: new Fill({
- color: 'black',
+ color: 'rgba(128, 128, 128, .7)',
}),
- }),
- });
- })
- .concat([.5, 1, 1.5, 2, 2].map(function(width) {
- return new Style({
- zIndex: 0,
- fill: new Fill({
- color: 'rgba(128, 128, 128, .7)',
- }),
- stroke: new Stroke({
- width: width,
- color: 'rgb(0, 0, 0)',
- }),
- });
- })),
+ stroke: new Stroke({
+ width: width,
+ color: 'rgb(0, 0, 0)',
+ }),
+ });
+ })),
+ },
};
-map.addLayer(new VectorTileLayer({
- source: new VectorTile({
- url: '/public/xyztiles/{z}/{x}/{y}.pbf',
- format: new MVT({
- layers: Object.keys(styles),
- }),
- projection: projection,
- wrapX: false,
- transition: 0,
- tileGrid: createXYZ({
- extent: extent,
- tileSize: 1024,
- maxResolution: 1024, /* = 1048576/1024 */
- minZoom: 0,
- maxZoom: 9,
- }),
+const vectorSource = new VectorTile({
+ url: '/public/xyztiles/{z}/{x}/{y}.pbf',
+ format: new MVT({
+ layers: Object.keys(layers),
+ }),
+ projection: projection,
+ wrapX: false,
+ transition: 0,
+ tileGrid: createXYZ({
+ extent: extent,
+ tileSize: 1024,
+ maxResolution: 1024, /* = 1048576/1024 */
+ minZoom: 0,
+ maxZoom: 9,
}),
+});
+
+const styles = Object.keys(layers).reduce(function(result, key) {
+ result[key] = layers[key].style;
+ return result;
+}, {});
+map.addLayer(new VectorTileLayer({
+ source: vectorSource,
/* XXX switch to 'hybrid' if there are perf issues; but that seems to
* put lines above points regardless of their respective z-index */
renderMode: 'vector',
@@ -569,9 +578,153 @@ map.addLayer(new VectorTileLayer({
content.appendChild(body);
body.classList.add('modal-body');
+ const accordion = document.createElement('div');
+ body.appendChild(accordion);
+ accordion.id = 'layer-selection-accordion';
+ accordion.classList.add('accordion', 'accordion-flush');
+
+ const setIndeterminateAndChecked = function(input, children) {
+ const childrenStyles = Object.values(children.reduce(function(result, child) {
+ if (Array.isArray(child.layerName)) {
+ child.layerName.forEach(function(layerName) {
+ result[layerName] = (styles[layerName] !== undefined);
+ });
+ } else {
+ result[child.layerName] = (styles[child.layerName] !== undefined);
+ }
+ return result;
+ }, {}));
+ input.indeterminate = children.length <= 1 ? false : childrenStyles.slice(1).some((v) => v !== childrenStyles[0]);
+ input.checked = childrenStyles.every((v) => v);
+ };
+
+ let accordionCollapseId = 0, inputId = 0;
+ const addAccordionItem = function(headerText, children) {
+ const item = document.createElement('div');
+ accordion.appendChild(item);
+ item.classList.add('accordion-item');
+
+ const header = document.createElement('div');
+ item.appendChild(header);
+ header.classList.add('accordion-header');
+
+ const btn = document.createElement('button');
+ header.appendChild(btn);
+
+ const collapse = document.createElement('div');
+ item.appendChild(collapse);
+ collapse.id = 'accordion-collapse-' + accordionCollapseId++;
+ collapse.classList.add('accordion-collapse', 'collapse');
+ // collapse.setAttribute('data-bs-parent', '#' + accordion.id);
+
+ btn.type = 'button';
+ btn.setAttribute('data-bs-toggle', 'collapse');
+ btn.setAttribute('data-bs-target', '#' + collapse.id);
+ btn.setAttribute('aria-expanded', 'false');
+ btn.setAttribute('aria-controls', collapse.id);
+ btn.classList.add('accordion-button', 'collapsed');
+
+ const span0 = document.createElement('span');
+ btn.appendChild(span0);
+ span0.classList.add('form-check');
+ span0.setAttribute('data-bs-toggle', 'collapse');
+ span0.setAttribute('data-bs-target', '');
+
+ const input0 = document.createElement('input');
+ span0.appendChild(input0);
+ input0.classList.add('form-check-input');
+ input0.type = 'checkbox';
+ input0.id = 'layer' + inputId++;
+
+ const label0 = document.createElement('label');
+ span0.appendChild(label0);
+ label0.classList.add('form-check-label');
+ label0.setAttribute('for', input0.id);
+
+ const text0 = document.createTextNode(headerText);
+ label0.appendChild(text0);
+
+ setIndeterminateAndChecked(input0, children);
+
+ const inputs = Object.values(children).map(function(child) {
+ const input = document.createElement('input');
+ setIndeterminateAndChecked(input, [child]);
+ return input;
+ });
+
+ const body = document.createElement('div');
+ collapse.appendChild(body);
+ body.classList.add('accordion-body');
+
+ const group = document.createElement('ul');
+ body.appendChild(group);
+ group.classList.add('list-group', 'list-group-flush');
+
+ children.forEach(function(child, i) {
+ const li = document.createElement('li');
+ group.appendChild(li);
+ li.classList.add('list-group-item');
+
+ const input = inputs[i];
+ li.appendChild(input);
+ input.classList.add('form-check-input');
+ input.type = 'checkbox';
+ input.id = 'layer' + inputId++;
+
+ const label = document.createElement('label');
+ li.appendChild(label);
+ label.classList.add('form-check-label');
+ label.setAttribute('for', input.id);
+
+ const text = document.createTextNode(child.text);
+ label.appendChild(text);
+ });
+
+ input0.onclick = function(event) {
+ children.forEach(function(child, i) {
+ const layerNames = Array.isArray(child.layerName) ? child.layerName : [child.layerName];
+ layerNames.forEach(function(layerName) {
+ if (input0.checked) {
+ styles[layerName] = layers[layerName].style;
+ inputs[i].checked = true;
+ } else {
+ delete styles[layerName];
+ inputs[i].checked = false;
+ }
+ });
+ });
+ vectorSource.changed();
+ };
+
+ inputs.forEach(function(input, i) {
+ input.onclick = function(event) {
+ const layerNames = Array.isArray(children[i].layerName) ? children[i].layerName : [children[i].layerName];
+ layerNames.forEach(function(layerName) {
+ if (input.checked) {
+ styles[layerName] = layers[layerName].style;
+ } else {
+ delete styles[layerName];
+ }
+ });
+
+ setIndeterminateAndChecked(input0, children);
+ vectorSource.changed();
+ };
+ });
+ };
+
+ addAccordionItem('Transmissionsnät för el', [
+ {layerName: ['svk_lines', 'svk_pylons'], text: 'Kraftledningar'},
+ {layerName: 'svk_stations', text: 'Stationer'},
+ ]);
+
(function() {
+ const item = document.createElement('div');
+ accordion.appendChild(item);
+ item.classList.add('accordion-item');
+
const div = document.createElement('div');
- body.appendChild(div);
+ item.appendChild(div);
div.classList.add('form-check', 'form-switch');
const input = document.createElement('input');
@@ -579,7 +732,7 @@ map.addLayer(new VectorTileLayer({
input.classList.add('form-check-input');
input.type = 'checkbox';
input.setAttribute('role', 'switch');
- input.id = 'layer-basemap';
+ input.id = 'layer' + inputId++;
const label = document.createElement('label');
div.appendChild(label);
diff --git a/style.css b/style.css
index cce906c..5838586 100644
--- a/style.css
+++ b/style.css
@@ -255,3 +255,43 @@ html, body {
.ol-overlaycontainer-stopevent .modal-backdrop.show {
pointer-events: auto;
}
+
+#layer-selection-panel .accordion {
+ --bs-accordion-active-bg: var(--bs-accordion-bg);
+ --bs-accordion-active-color: var(--bs-body-color);
+ --bs-accordion-btn-padding-x: 0;
+ --bs-accordion-btn-padding-y: 0;
+ --bs-accordion-btn-focus-box-shadow: none;
+ --bs-accordion-body-padding-x: 0;
+ --bs-accordion-body-padding-y: 0;
+}
+#layer-selection-panel .accordion-item {
+ border: none;
+}
+#layer-selection-panel .accordion-body {
+ padding-left: 1em;
+}
+#layer-selection-panel .accordion .list-group.list-group-flush > .list-group-item {
+ padding: 0;
+ border: none;
+}
+#layer-selection-panel .accordion-item .form-check {
+ padding-left: 0;
+ margin: 0.125rem 0 0 0;
+}
+#layer-selection-panel .accordion-item:first-child .form-check {
+ margin-top: 0;
+}
+#layer-selection-panel .accordion-header > .accordion-button[aria-expanded="true"] {
+ padding-bottom: 0.125rem;
+ margin-bottom: 0.125rem;
+}
+#layer-selection-panel .accordion-header > .accordion-button > .form-check > .form-check-input,
+#layer-selection-panel .accordion > .accordion-item > .form-check > .form-check-input {
+ margin-left: 0;
+ float: none;
+}
+#layer-selection-panel .accordion-item .form-check > .form-check-input,
+#layer-selection-panel .accordion-item .list-group-item > .form-check-input {
+ margin-right: .5em !important;
+}