/* eslint-disable max-len */ /* eslint-disable no-alert */ import SvgCanvas from "../../svgcanvas/svgcanvas.js"; const SIDEPANEL_MAXWIDTH = 300; const SIDEPANEL_OPENWIDTH = 150; const { $id } = SvgCanvas; /** * */ class LayersPanel { /** * @param {PlainObject} editor */ constructor(editor) { this.uiStrings = editor.uiStrings; this.updateContextPanel = editor.topPanel.updateContextPanel; this.sidedrag = -1; this.sidedragging = false; this.allowmove = false; this.editor = editor; } /** * @param {Float} delta * @fires module:svgcanvas.SvgCanvas#event:ext_workareaResized * @returns {void} */ changeSidePanelWidth(delta) { const rulerX = document.querySelector("#ruler_x"); $id("sidepanels").style.width = (parseFloat(getComputedStyle($id("sidepanels"), null).width.replace("px", "")) + delta) + "px"; $id("layerpanel").style.width = (parseFloat(getComputedStyle($id("layerpanel"), null).width.replace("px", "")) + delta) + "px"; rulerX.style.right = parseFloat(getComputedStyle(rulerX, null).right.replace("px", "")) + delta + "px"; this.editor.workarea.style.right = parseFloat( getComputedStyle(this.editor.workarea, null).right.replace("px", "") ) + delta + "px"; this.editor.svgCanvas.runExtensions("workareaResized"); } /** * @param {Event} evt * @returns {void} */ resizeSidePanel(evt) { if (!this.allowmove) { return; } if (this.sidedrag === -1) { return; } this.sidedragging = true; let deltaX = this.sidedrag - evt.pageX; const sideWidth = parseFloat(getComputedStyle($id("sidepanels"), null).width.replace("px", "")); if (sideWidth + deltaX > SIDEPANEL_MAXWIDTH) { deltaX = SIDEPANEL_MAXWIDTH - sideWidth; // sideWidth = SIDEPANEL_MAXWIDTH; } else if (sideWidth + deltaX < 2) { deltaX = 2 - sideWidth; // sideWidth = 2; } if (deltaX === 0) { return; } this.sidedrag -= deltaX; this.changeSidePanelWidth(deltaX); } /** * If width is non-zero, then fully close it; otherwise fully open it. * @param {boolean} close Forces the side panel closed * @returns {void} */ toggleSidePanel(close) { const dpr = window.devicePixelRatio || 1; const w = parseFloat(getComputedStyle($id("sidepanels"), null).width.replace("px", "")) const isOpened = (dpr < 1 ? w : w / dpr) > 2; const zoomAdjustedSidepanelWidth = (dpr < 1 ? 1 : dpr) * SIDEPANEL_OPENWIDTH; const deltaX = (isOpened || close ? 0 : zoomAdjustedSidepanelWidth) - w; this.changeSidePanelWidth(deltaX); } /** * @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"); template.innerHTML = `

Layers

Layer 1
Move elements to:
L a y e r s
`; 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); document.body.append(menuMore); const menuLayerBox = document.createElement("se-cmenu-layers"); menuLayerBox.setAttribute("id", "se-cmenu-layers-list"); menuLayerBox.value = "layerlist"; menuLayerBox.setAttribute("leftclick", false); document.body.append(menuLayerBox); document .getElementById("layer_new") .addEventListener("click", this.newLayer.bind(this)); document .getElementById("layer_delete") .addEventListener("click", this.deleteLayer.bind(this)); document .getElementById("layer_up") .addEventListener("click", () => this.moveLayer.bind(this)(-1)); document .getElementById("layer_down") .addEventListener("click", () => this.moveLayer.bind(this)(1)); document .getElementById("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.bind(this) ); if (this.editor.configObj.curConfig.showlayers) { this.toggleSidePanel(); } $id("sidepanel_handle").addEventListener("mousedown", evt => { this.sidedrag = evt.pageX; window.addEventListener("mousemove", this.resizeSidePanel.bind(this)); this.allowmove = false; // Silly hack for Chrome, which always runs mousemove right after mousedown setTimeout(() => { this.allowmove = true; }, 20); }); $id("sidepanel_handle").addEventListener("mouseup", _evt => { if (!this.sidedragging) { this.toggleSidePanel(); } this.sidedrag = -1; this.sidedragging = false; }); window.addEventListener("mouseup", _evt => { this.sidedrag = -1; this.sidedragging = false; $id("svg_editor").removeEventListener( "mousemove", this.resizeSidePanel.bind(this) ); }); } /** * @returns {void} */ newLayer() { let uniqName; let i = this.editor.svgCanvas.getCurrentDrawing().getNumLayers(); do { uniqName = this.uiStrings.layers.layer + " " + ++i; } while (this.editor.svgCanvas.getCurrentDrawing().hasLayer(uniqName)); const newName = prompt( this.uiStrings.notification.enterUniqueLayerName, uniqName ); if (!newName) { return; } if (this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)) { alert(this.uiStrings.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.uiStrings.notification.enterUniqueLayerName, name ); if (!newName) { return; } if (this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName)) { alert(this.uiStrings.notification.dupeLayerName); return; } this.editor.svgCanvas.cloneLayer(newName); this.updateContextPanel(); this.populateLayers(); } index(el) { if (!el) return -1; var 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 oldName = document.querySelector("#layerlist tr.layersel td.layername").textContent; const newName = prompt(this.uiStrings.notification.enterNewLayerName, ""); if (!newName) { return; } if ( oldName === newName || this.editor.svgCanvas.getCurrentDrawing().hasLayer(newName) ) { alert(this.uiStrings.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 = [], 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); const selLayerNames = $id("selLayerNames"); // empty() ref: http://youmightnotneedjquery.com/#empty while(selLayerNames.firstChild) selLayerNames.removeChild(selLayerNames.firstChild); 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) 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); // eslint-disable-next-line no-unsanitized/property selLayerNames.innerHTML += ''; } // 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', function(evt) { self.toggleHighlightLayer( self.editor.svgCanvas, evt.currentTarget.textContent ); }); element.addEventListener('mouseout', function(_evt) { self.toggleHighlightLayer(self.editor.svgCanvas); }); }); const elements = $id('layerlist').querySelectorAll("td.layervis"); Array.from(elements).forEach(function(element) { element.addEventListener('click', function(evt) { const name = evt.currentTarget.parentNode.querySelector("td.layername").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 = '_'; layerlist.append(tlayer); } } } export default LayersPanel;