UNPKG

svgedit

Version:

Powerful SVG-Editor for your browser

346 lines (325 loc) 11.7 kB
/* eslint-disable no-alert */ import SvgCanvas from "../../svgcanvas/svgcanvas.js"; const { $id } = SvgCanvas; /** * */ class LayersPanel { /** * @param {PlainObject} editor */ constructor(editor) { this.updateContextPanel = editor.topPanel.updateContextPanel.bind(editor.topPanel); this.editor = editor; } /** * @param {PlainObject} e event * @returns {void} */ lmenuFunc(e) { const action = e?.detail?.trigger; switch (action) { case "dupe": this.cloneLayer(); break; case "delete": this.deleteLayer(); break; case "merge_down": this.mergeLayer(); break; case "merge_all": this.editor.svgCanvas.mergeAllLayers(); this.updateContextPanel(); this.populateLayers(); break; } } /** * @returns {void} */ init() { const template = document.createElement("template"); const { i18next } = this.editor; // eslint-disable-next-line no-unsanitized/property template.innerHTML = ` <div id="sidepanels"> <se-text id="sidepanel_handle" title="ui.panel_action" text="ui.panel"></se-text> <div id="sidepanel_content"> <div id="layerpanel"> <se-text id="layersLabel" text="layers.layers"></se-text> <fieldset id="layerbuttons"> <se-button id="layer_new" title="layers.new" size="small" src="new.svg"></se-button> <se-button id="layer_delete" title="layers.del" size="small" src="delete.svg"></se-button> <se-button id="layer_rename" title="layers.rename" size="small" src="text.svg"></se-button> <se-button id="layer_up" title="layers.move_up" size="small" src="go_up.svg"></se-button> <se-button id="layer_down" title="layers.move_down" size="small" src="go_down.svg"></se-button> <se-button id="layer_moreopts" title="common.more_opts" size="small" src="context_menu.svg"> </se-button> </fieldset> <table id="layerlist"> <tr class="layer"> <td class="layervis"></td> <td class="layername">Layer 1</td> </tr> </table> <se-select id="selLayerNames" title="layers.move_selected" label="layers.move_elems_to" options="Layer 1" values="layer1" value="layer1" disabled="disabled"> </se-select> </div> </div> </div> `; this.editor.$svgEditor.append(template.content.cloneNode(true)); // layer menu added to DOM const menuMore = document.createElement("se-cmenu-layers"); menuMore.setAttribute("id", "se-cmenu-layers-more"); menuMore.value = "layer_moreopts"; menuMore.setAttribute("leftclick", true); this.editor.$container.append(menuMore); menuMore.init(i18next); const menuLayerBox = document.createElement("se-cmenu-layers"); menuLayerBox.setAttribute("id", "se-cmenu-layers-list"); menuLayerBox.value = "layerlist"; menuLayerBox.setAttribute("leftclick", false); this.editor.$container.append(menuLayerBox); menuLayerBox.init(i18next); $id("layer_new").addEventListener("click", this.newLayer.bind(this)); $id("layer_delete").addEventListener("click", this.deleteLayer.bind(this)); $id("layer_up").addEventListener("click", () => this.moveLayer.bind(this)(-1)); $id("layer_down").addEventListener("click", () => this.moveLayer.bind(this)(1)); $id("layer_rename").addEventListener("click", this.layerRename.bind(this)); $id("se-cmenu-layers-more").addEventListener("change", this.lmenuFunc.bind(this)); $id("se-cmenu-layers-list").addEventListener("change", (e) => { this.lmenuFunc(e); }); $id("sidepanel_handle").addEventListener("click", () => this.toggleSidePanel()); this.toggleSidePanel(this.editor.configObj.curConfig.showlayers); } toggleSidePanel(displayFlag) { if (displayFlag === undefined) { this.editor.$svgEditor.classList.toggle('open'); } else if (displayFlag) { this.editor.$svgEditor.classList.add('open'); } else { this.editor.$svgEditor.classList.remove('open'); } } /** * @returns {void} */ newLayer() { let uniqName; let i = this.editor.svgCanvas.getCurrentDrawing().getNumLayers(); do { uniqName = this.editor.i18next.t("layers.layer") + " " + ++i; } while (this.editor.svgCanvas.getCurrentDrawing().hasLayer(uniqName)); const newName = prompt( this.editor.i18next.t('notification.enterUniqueLayerName'), uniqName ); if (!newName) { return; } if (this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)) { alert(this.editor.i18next.t('notification.dupeLayerName')); return; } this.editor.svgCanvas.createLayer(newName); this.updateContextPanel(); this.populateLayers(); } /** * * @returns {void} */ deleteLayer() { if (this.editor.svgCanvas.deleteCurrentLayer()) { this.updateContextPanel(); this.populateLayers(); // This matches what this.editor.svgCanvas does // TODO: make this behavior less brittle (svg-editor should get which // layer is selected from the canvas and then select that one in the UI) const elements = document.querySelectorAll('#layerlist tr.layer'); Array.prototype.forEach.call(elements, function(el){ el.classList.remove('layersel'); }); document.querySelector('#layerlist tr.layer').classList.add('layersel'); } } /** * * @returns {void} */ cloneLayer() { const name = this.editor.svgCanvas.getCurrentDrawing().getCurrentLayerName() + " copy"; const newName = prompt( this.editor.i18next.t('notification.enterUniqueLayerName'), name ); if (!newName) { return; } if (this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)) { alert(this.editor.i18next.t('notification.dupeLayerName')); return; } this.editor.svgCanvas.cloneLayer(newName); this.updateContextPanel(); this.populateLayers(); } index(el) { if (!el) return -1; let i = 0; do { i++; } while (el == el.previousElementSibling); return i; } /** * * @returns {void} */ mergeLayer() { if ( (this.index(document.querySelector("#layerlist tr.layersel"))-1) === this.editor.svgCanvas.getCurrentDrawing().getNumLayers() - 1 ) { return; } this.editor.svgCanvas.mergeLayer(); this.updateContextPanel(); this.populateLayers(); } /** * @param {Integer} pos * @returns {void} */ moveLayer(pos) { const total = this.editor.svgCanvas.getCurrentDrawing().getNumLayers(); let curIndex = (this.index(document.querySelector("#layerlist tr.layersel"))-1); if (curIndex > 0 || curIndex < total - 1) { curIndex += pos; this.editor.svgCanvas.setCurrentLayerPosition(total - curIndex - 1); this.populateLayers(); } } /** * @returns {void} */ layerRename() { const ele = document.querySelector("#layerlist tr.layersel td.layername"); const oldName = (ele) ? ele.textContent : ''; const newName = prompt(this.editor.i18next.t('notification.enterNewLayerName'), ""); if (!newName) { return; } if ( oldName === newName || this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName) ) { alert(this.editor.i18next.t('notification.layerHasThatName')); return; } this.editor.svgCanvas.renameCurrentLayer(newName); this.populateLayers(); } /** * This function highlights the layer passed in (by fading out the other layers). * If no layer is passed in, this function restores the other layers. * @param {string} [layerNameToHighlight] * @returns {void} */ toggleHighlightLayer(layerNameToHighlight) { let i; const curNames = []; const numLayers = this.editor.svgCanvas.getCurrentDrawing().getNumLayers(); for (i = 0; i < numLayers; i++) { curNames[i] = this.editor.svgCanvas.getCurrentDrawing().getLayerName(i); } if (layerNameToHighlight) { curNames.forEach((curName) => { if (curName !== layerNameToHighlight) { this.editor.svgCanvas .getCurrentDrawing() .setLayerOpacity(curName, 0.5); } }); } else { curNames.forEach((curName) => { this.editor.svgCanvas.getCurrentDrawing().setLayerOpacity(curName, 1.0); }); } } /** * @returns {void} */ populateLayers() { this.editor.svgCanvas.clearSelection(); const self = this; const layerlist = $id("layerlist").querySelector('tbody'); while(layerlist.firstChild) layerlist.removeChild(layerlist.firstChild); $id("selLayerNames").setAttribute("options", ""); const drawing = this.editor.svgCanvas.getCurrentDrawing(); const currentLayerName = drawing.getCurrentLayerName(); let layer = this.editor.svgCanvas.getCurrentDrawing().getNumLayers(); // we get the layers in the reverse z-order (the layer rendered on top is listed first) let values = ""; let text = ""; while (layer--) { const name = drawing.getLayerName(layer); const layerTr = document.createElement("tr"); layerTr.className = (name === currentLayerName) ? 'layer layersel' : 'layer'; const layerVis = document.createElement("td"); layerVis.className = (!drawing.getLayerVisibility(name)) ? "layerinvis layervis" : 'layervis'; const layerName = document.createElement("td"); layerName.className = 'layername'; layerName.textContent = name; layerTr.appendChild(layerVis); layerTr.appendChild(layerName); layerlist.appendChild(layerTr); values = (values) ? values + "::" + name : name; text = (text) ? text + "," + name : name; } $id("selLayerNames").setAttribute("options", text); $id("selLayerNames").setAttribute("values", values); // handle selection of layer const nelements = $id('layerlist').querySelectorAll("td.layername"); Array.from(nelements).forEach(function(element) { element.addEventListener('mouseup', function(evt) { const trElements = $id('layerlist').querySelectorAll("tr.layer"); Array.from(trElements).forEach(function(element) { element.classList.remove("layersel"); }); evt.currentTarget.parentNode.classList.add("layersel"); self.editor.svgCanvas.setCurrentLayer(evt.currentTarget.textContent); evt.preventDefault(); }); element.addEventListener('mouseup', (evt) => { self.toggleHighlightLayer(evt.currentTarget.textContent); }); element.addEventListener('mouseout', (_evt) => { self.toggleHighlightLayer(); }); }); const elements = $id('layerlist').querySelectorAll("td.layervis"); Array.from(elements).forEach(function(element) { element.addEventListener('click', function(evt) { const ele = evt.currentTarget.parentNode.querySelector("td.layername"); const name = (ele)? ele.textContent : ''; const vis = evt.currentTarget.classList.contains("layerinvis"); self.editor.svgCanvas.setLayerVisibility(name, vis); evt.currentTarget.classList.toggle("layerinvis"); }); }); // if there were too few rows, let's add a few to make it not so lonely let num = 5 - $id('layerlist').querySelectorAll("tr.layer").length; while (num-- > 0) { // TODO: there must a better way to do this const tlayer = document.createElement("tr"); tlayer.innerHTML = '<td style="color:white">_</td><td/>'; layerlist.append(tlayer); } } } export default LayersPanel;