diff options
author | sanine <sanine.not@pm.me> | 2022-06-02 00:53:26 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-06-02 00:53:26 -0500 |
commit | e69dfc1b29f2fed1d1fd26dbeb9b248b1377c64c (patch) | |
tree | 44c6c389646d6e65f43930a97ac65acbaf0f7e52 /src/Map | |
parent | f82f540a0cf07e8fd95e36dea10a49aa839832d0 (diff) |
add basic layer ui
Diffstat (limited to 'src/Map')
-rw-r--r-- | src/Map/Layer.js | 187 | ||||
-rw-r--r-- | src/Map/Map.js | 1 |
2 files changed, 188 insertions, 0 deletions
diff --git a/src/Map/Layer.js b/src/Map/Layer.js new file mode 100644 index 0000000..93e5799 --- /dev/null +++ b/src/Map/Layer.js @@ -0,0 +1,187 @@ +import h from '../Util/DomUtil.js'; + +class Layer { + constructor(id, name) { + this.id = id; + this.name = name; + this.subLayers = []; + this.visible = true; + } +} + +class LayerData { + constructor() { + this.layers = []; + this.idPool = 0; + } + + _getNewId() { + const id = this.idPool; + this.idPool += 1; + return `layer${id}`; + } + + _getContainerAndId(idChain) { + if (idChain.length === 0) + return [ this.layers, null ]; + + let container = this.layers; + while (idChain.length > 1) { + const id = idChain.pop(); + console.log(container, id); + container = container.find(layer => layer.id === id).subLayers; + console.log(idChain); + } + + return [ container, idChain[0] ]; + } + + _idToIndex(container, id) { + return container.findIndex(layer => layer.id === id); + } + + insertLayer(idChain) { + const layerId = this._getNewId(); + const newLayer = new Layer(layerId, `Layer ${layerId}`); + + const [ container, id ] = this._getContainerAndId(idChain); + if (id === null) { + container.push(newLayer); + return; + } + + const index = this._idToIndex(container, id); + container.splice(index+1, 0, newLayer); + } + + removeLayer(idChain) { + const [ container, id ] = this._getContainerAndId(idChain); + if (id === null) return; + + const index = this._idToIndex(container, id); + container.splice(index, 1); + } + + pushLayer(idChain) { + const [ container, id ] = this._getContainerAndId(idChain); + if (id === null) return; // nothing to do + + const index = this._idToIndex(container, id); + const after = container[index+1]; + if (!after) return; // no successor to push to, do nothing + after.subLayers.push(container[index]); + container.splice(index, 1); + } +} + + +class LayerView { + constructor(parentId) { + const parentElement = document.getElementById(parentId); + + const button = (label, f) => { + const b = h('button', label, { 'style': 'margin: 0 5px' }); + b.onclick = f; + return b; + } + + this.layersList = h('ul'); + + const root = h('fieldset', { 'class': 'layer-view' }, [ + h('legend', 'Layers'), + this.layersList, + button('Move Up', () => this.onMoveUp(this._getIdChain())), + button('Move Down', () => this.onMoveDown(this._getIdChain())), + button('Toggle Visible', () => this.onShowHideLayer(this._getIdChain())), + button('Rename', () => this.onRenameLayer(this._getIdChain())), + h('br'), h('br'), + button('Add Layer', () => this.onAddLayer(this._getIdChain())), + button('Remove Layer', () => this.onRemoveLayer(this._getIdChain())), + button('Push', () => this.onPushLayer(this._getIdChain())), + button('Pop', () => this.onPopLayer(this._getIdChain())), + ]); + + parentElement.appendChild(root); + + this.selectedId = null; + + this.onAddLayer = () => {}; + this.onRemoveLayer = () => {}; + this.onPushLayer = () => {}; + this.onPopLayer = () => {}; + + this.onMoveUp = () => {}; + this.onMoveDown = () => {}; + this.onShowHideLayer = () => {}; + this.onRenameLayer = () => {}; + } + + _getIdChain() { + const chain = []; + let el = this.selected; + + if (el === null || el === undefined) return []; + while (el !== this.layersList && el !== null) { + if (el.nodeName.toLowerCase() === 'li') + chain.push(el.id); + el = el.parentNode; + } + return chain; + } + + renderLayers(layers, root=this.layersList) { + let selectedId = ''; + if (this.selected) selectedId = this.selected.id; + if (root === this.layersList) this.layersList.replaceChildren(); + for (let layer of layers) { + let classList = ''; + if (layer.visible) classList += 'visible '; + const el = h( + 'li', + layer.name, + { + id: layer.id, + 'class': classList, + } + ); + + if (selectedId === layer.id) { + el.classList.add('selected'); + this.selected = el; + } + el.onclick = e => { + e.stopPropagation(); + if (this.selected) this.selected.classList.remove('selected'); + this.selected = el; + el.classList.add('selected'); + }; + if (layer.subLayers.length !== 0) { + const ul = h('ul'); + this.renderLayers(layer.subLayers, ul); + el.appendChild(ul); + } + root.prepend(el); + } + } +} + + +class LayerController { + constructor(data, view) { + this.data = data; + this.view = view; + + const respond = f => ( idChain => { f(idChain); this.updateView(); } ); + + this.view.onAddLayer = idChain => { this.data.insertLayer(idChain); this.updateView(); }; + this.view.onPushLayer = idChain => { this.data.pushLayer(idChain); this.updateView(); }; + this.view.onRemoveLayer = idChain => { this.data.removeLayer(idChain); this.updateView(); } + } + + updateView() { + this.view.renderLayers(this.data.layers); + } +} + + +export { LayerData, LayerView, LayerController }; diff --git a/src/Map/Map.js b/src/Map/Map.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/Map/Map.js @@ -0,0 +1 @@ + |