diff options
author | Guilhem Moulin <guilhem@fripost.org> | 2024-01-20 15:53:52 +0100 |
---|---|---|
committer | Guilhem Moulin <guilhem@fripost.org> | 2024-01-20 15:55:50 +0100 |
commit | b6924ae728eafdba7436e1da776467cf6edd8c7c (patch) | |
tree | 8ce429a62402b92574fcdc1e31b519b7a64759f3 | |
parent | fd88c1d218347602b00398b1e5d326c8078992e7 (diff) |
Support layer hierarchy with arbitary depth.
-rw-r--r-- | main.js | 188 | ||||
-rw-r--r-- | style.css | 10 |
2 files changed, 109 insertions, 89 deletions
@@ -521,6 +521,22 @@ const layers = { }, }; +const layerHierarchy = [ + { + text: 'Transmissionsnät för el', + children: [ + { + text: 'Kraftledningar', + layer: ['svk_lines', 'svk_pylons'], + }, + { + text: 'Stationer', + layer: 'svk_stations', + }, + ], + }, +]; + const vectorSource = new VectorTile({ url: '/public/xyztiles/{z}/{x}/{y}.pbf', format: new MVT({ @@ -583,23 +599,85 @@ map.addLayer(new VectorTileLayer({ 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); + (function collectLayers(list) { + list.forEach(function(elem) { + elem._layers = elem.layer === undefined ? [] + : Array.isArray(elem.layer) ? elem.layer + : [elem.layer]; + if (elem.children !== undefined && elem.children.length > 0) { + collectLayers(elem.children); + elem.children.forEach(function(child) { + child._layers.forEach((l) => elem._layers.push(l)); + }); + }; + }); + })(layerHierarchy); + + const setIndeterminateAndChecked = function(list) { + return list.forEach(function(elem) { + const layerStyles = elem._layers.map((lyr) => styles[lyr] !== undefined); + elem._input.indeterminate = elem._layers.length <= 1 ? false + : layerStyles.slice(1).some((v) => v !== layerStyles[0]); + if (!elem._input.indeterminate) { + /* keep checked value if indeterminate */ + elem._input.checked = layerStyles.every((v) => v); + } + if (elem.children !== undefined && elem.children.length > 0) { + setIndeterminateAndChecked(elem.children); + } + }); }; + const onClickFunction = function(layerList, event) { + layerList.forEach(function(lyr) { + if (event.target.checked) { + styles[lyr] = layers[lyr].style; + } else { + delete styles[lyr]; + } + }); + setIndeterminateAndChecked(layerHierarchy); + vectorSource.changed(); + }; + + let layerId = 0; + const addAccordionGroup = function(parentNode, children) { + const ul = document.createElement('ul'); + parentNode.appendChild(ul); + ul.classList.add('list-group', 'list-group-flush'); - let accordionCollapseId = 0, inputId = 0; - const addAccordionItem = function(headerText, children) { + children.forEach(function(child) { + const li = document.createElement('li'); + ul.appendChild(li); + li.classList.add('list-group-item'); + + const div = document.createElement('div'); + li.appendChild(div); + div.classList.add('d-inline-flex'); + + const input = child._input = document.createElement('input'); + div.appendChild(input); + input.classList.add('form-check-input'); + input.type = 'checkbox'; + input.id = 'layer' + layerId++; + + const label = document.createElement('label'); + div.appendChild(label); + label.classList.add('form-check-label'); + label.setAttribute('for', input.id); + + const textNode = document.createTextNode(child.text); + label.appendChild(textNode); + + if (child.children !== undefined && child.children.length > 0) { + addAccordionGroup(li, child.children); + }; + + input.onclick = function(event) { + return onClickFunction(child._layers, event); + }; + }); + }; + layerHierarchy.forEach(function(x, idx) { const item = document.createElement('div'); accordion.appendChild(item); item.classList.add('accordion-item'); @@ -613,7 +691,7 @@ map.addLayer(new VectorTileLayer({ const collapse = document.createElement('div'); item.appendChild(collapse); - collapse.id = 'accordion-collapse-' + accordionCollapseId++; + collapse.id = 'accordion-collapse-' + idx; collapse.classList.add('accordion-collapse', 'collapse'); // collapse.setAttribute('data-bs-parent', '#' + accordion.id); @@ -630,93 +708,31 @@ map.addLayer(new VectorTileLayer({ span0.setAttribute('data-bs-toggle', 'collapse'); span0.setAttribute('data-bs-target', ''); - const input0 = document.createElement('input'); + const input0 = x._input = document.createElement('input'); span0.appendChild(input0); input0.classList.add('form-check-input'); input0.type = 'checkbox'; - input0.id = 'layer' + inputId++; + input0.id = 'layer' + layerId++; const label0 = document.createElement('label'); span0.appendChild(label0); label0.classList.add('form-check-label'); label0.setAttribute('for', input0.id); - const text0 = document.createTextNode(headerText); + const text0 = document.createTextNode(x.text); 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); - }); + addAccordionGroup(body, x.children); 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(); + return onClickFunction(x._layers, event); }; - - 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'}, - ]); + }); + setIndeterminateAndChecked(layerHierarchy); (function() { const item = document.createElement('div'); @@ -732,7 +748,7 @@ map.addLayer(new VectorTileLayer({ input.classList.add('form-check-input'); input.type = 'checkbox'; input.setAttribute('role', 'switch'); - input.id = 'layer' + inputId++; + input.id = 'layer' + layerId++; const label = document.createElement('label'); div.appendChild(label); @@ -268,8 +268,8 @@ html, body { #layer-selection-panel .accordion-item { border: none; } -#layer-selection-panel .accordion-body { - padding-left: 1em; +#layer-selection-panel .accordion-body .list-group { + padding-left: 1.5em; } #layer-selection-panel .accordion .list-group.list-group-flush > .list-group-item { padding: 0; @@ -292,6 +292,10 @@ html, body { float: none; } #layer-selection-panel .accordion-item .form-check > .form-check-input, -#layer-selection-panel .accordion-item .list-group-item > .form-check-input { +#layer-selection-panel .accordion-item .list-group-item > .d-inline-flex > .form-check-input { margin-right: .5em !important; } +#layer-selection-panel .accordion-header > .accordion-button > .form-check > .form-check-label, +#layer-selection-panel .accordion-item .form-check > .form-check-label { + display: inline; +} |