summaryrefslogtreecommitdiff
path: root/src/Map/Layer.js
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-06-02 00:53:26 -0500
committersanine <sanine.not@pm.me>2022-06-02 00:53:26 -0500
commite69dfc1b29f2fed1d1fd26dbeb9b248b1377c64c (patch)
tree44c6c389646d6e65f43930a97ac65acbaf0f7e52 /src/Map/Layer.js
parentf82f540a0cf07e8fd95e36dea10a49aa839832d0 (diff)
add basic layer ui
Diffstat (limited to 'src/Map/Layer.js')
-rw-r--r--src/Map/Layer.js187
1 files changed, 187 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 };