diff --git a/src/editor/LayersPanel.js b/src/editor/LayersPanel.js index e82569bd..fb0e7a6b 100644 --- a/src/editor/LayersPanel.js +++ b/src/editor/LayersPanel.js @@ -23,42 +23,6 @@ class LayersPanel { 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)); - - const lmenuFunc = (action, el, pos) => { - switch (action) { - case 'dupe': - /* await */ this.cloneLayer(); - break; - case 'delete': - this.deleteLayer(); - break; - case 'merge_down': - this.mergeLayer(); - break; - case 'merge_all': - this.svgCanvas.mergeAllLayers(); - this.updateContextPanel(); - this.populateLayers(); - break; - } - }; - - $('#layer_moreopts').contextMenu( - { - menu: 'cmenu_layers', - inSpeed: 0, - allowLeft: true - }, - lmenuFunc - ); - - $('#layerlist').contextMenu( - { - menu: 'cmenu_layers', - inSpeed: 0 - }, - lmenuFunc - ); } /** * @returns {void} diff --git a/src/editor/dialogs/cmenuDialog.js b/src/editor/dialogs/cmenuDialog.js new file mode 100644 index 00000000..6d6750ef --- /dev/null +++ b/src/editor/dialogs/cmenuDialog.js @@ -0,0 +1,276 @@ +const template = document.createElement('template'); +template.innerHTML = ` + + +`; +/** + * @class SeCMenuDialog + */ +export class SeCMenuDialog extends HTMLElement { + /** + * @function constructor + */ + constructor () { + super(); + // create the shadowDom and insert the template + this._shadowRoot = this.attachShadow({mode: 'open'}); + this._shadowRoot.appendChild(template.content.cloneNode(true)); + this._workarea = document.getElementById('workarea'); + this.$dialog = this._shadowRoot.querySelector('#cmenu_canvas'); + this.$copyLink = this._shadowRoot.querySelector('#se-copy'); + this.$cutLink = this._shadowRoot.querySelector('#se-cut'); + this.$pasteLink = this._shadowRoot.querySelector('#se-paste'); + this.$pasteInPlaceLink = this._shadowRoot.querySelector('#se-paste-in-place'); + this.$deleteLink = this._shadowRoot.querySelector('#se-delete'); + this.$groupLink = this._shadowRoot.querySelector('#se-group'); + this.$ungroupLink = this._shadowRoot.querySelector('#se-ungroup'); + this.$moveFrontLink = this._shadowRoot.querySelector('#se-move-front'); + this.$moveUpLink = this._shadowRoot.querySelector('#se-move-up'); + this.$moveDownLink = this._shadowRoot.querySelector('#se-move-down'); + this.$moveBackLink = this._shadowRoot.querySelector('#se-move-back'); + } + /** + * @function observedAttributes + * @returns {any} observed + */ + static get observedAttributes () { + return ['disableallmenu', 'enablemenuitems', 'disablemenuitems']; + } + /** + * @function attributeChangedCallback + * @param {string} name + * @param {string} oldValue + * @param {string} newValue + * @returns {void} + */ + attributeChangedCallback (name, oldValue, newValue) { + let eles = []; + const sdowRoot = this._shadowRoot; + switch (name) { + case 'disableallmenu': + if (newValue === 'true') { + const elesli = sdowRoot.querySelectorAll('li'); + elesli.forEach(function (eleli) { + eleli.classList.add('disabled'); + }); + } + break; + case 'enablemenuitems': + eles = newValue.split(','); + eles.forEach(function (ele) { + const selEle = sdowRoot.querySelector('a[href*="' + ele + '"]'); + selEle.parentElement.classList.remove('disabled'); + }); + break; + case 'disablemenuitems': + eles = newValue.split(','); + eles.forEach(function (ele) { + const selEle = sdowRoot.querySelector('a[href*="' + ele + '"]'); + selEle.parentElement.classList.add('disabled'); + }); + break; + default: + super.attributeChangedCallback(name, oldValue, newValue); + break; + } + } + /** + * @function get + * @returns {any} + */ + get disableallmenu () { + return this.getAttribute('disableallmenu'); + } + + /** + * @function set + * @returns {void} + */ + set disableallmenu (value) { + this.setAttribute('disableallmenu', value); + } + /** + * @function get + * @returns {any} + */ + get enablemenuitems () { + return this.getAttribute('enablemenuitems'); + } + + /** + * @function set + * @returns {void} + */ + set enablemenuitems (value) { + this.setAttribute('enablemenuitems', value); + } + /** + * @function get + * @returns {any} + */ + get disablemenuitems () { + return this.getAttribute('disablemenuitems'); + } + + /** + * @function set + * @returns {void} + */ + set disablemenuitems (value) { + this.setAttribute('disablemenuitems', value); + } + /** + * @function connectedCallback + * @returns {void} + */ + connectedCallback () { + const current = this; + const onMenuOpenHandler = (e) => { + e.preventDefault(); + current.$dialog.style.top = e.pageY + 'px'; + current.$dialog.style.left = e.pageX + 'px'; + current.$dialog.style.display = 'block'; + }; + const onMenuCloseHandler = (e) => { + if (e.button !== 2) { + current.$dialog.style.display = 'none'; + } + }; + const onMenuClickHandler = (e, action) => { + const triggerEvent = new CustomEvent('change', {detail: { + trigger: action + }}); + this.dispatchEvent(triggerEvent); + }; + this._workarea.addEventListener('contextmenu', onMenuOpenHandler); + this._workarea.addEventListener('mousedown', onMenuCloseHandler); + this.$cutLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'cut')); + this.$copyLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'copy')); + this.$pasteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'paste')); + this.$pasteInPlaceLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'paste_in_place')); + this.$deleteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'delete')); + this.$groupLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'group')); + this.$ungroupLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'ungroup')); + this.$moveFrontLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_front')); + this.$moveUpLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_up')); + this.$moveDownLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_down')); + this.$moveBackLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'move_back')); + } +} + +// Register +customElements.define('se-cmenu_canvas-dialog', SeCMenuDialog); diff --git a/src/editor/dialogs/cmenuLayersDialog.js b/src/editor/dialogs/cmenuLayersDialog.js new file mode 100644 index 00000000..94707834 --- /dev/null +++ b/src/editor/dialogs/cmenuLayersDialog.js @@ -0,0 +1,193 @@ +const template = document.createElement('template'); +template.innerHTML = ` + + +`; +/** + * @class SeCMenuLayerDialog + */ +export class SeCMenuLayerDialog extends HTMLElement { + /** + * @function constructor + */ + constructor () { + super(); + // create the shadowDom and insert the template + this._shadowRoot = this.attachShadow({mode: 'open'}); + this._shadowRoot.appendChild(template.content.cloneNode(true)); + this.source = ''; + this._workarea = undefined; + this.$sidePanels = document.getElementById('sidepanels'); + this.$dialog = this._shadowRoot.querySelector('#cmenu_layers'); + this.$duplicateLink = this._shadowRoot.querySelector('#se-dupe'); + this.$deleteLink = this._shadowRoot.querySelector('#se-layer-delete'); + this.$mergeDownLink = this._shadowRoot.querySelector('#se-merge-down'); + this.$mergeAllLink = this._shadowRoot.querySelector('#se-merge-all'); + } + /** + * @function observedAttributes + * @returns {any} observed + */ + static get observedAttributes () { + return ['value', 'leftclick']; + } + /** + * @function attributeChangedCallback + * @param {string} name + * @param {string} oldValue + * @param {string} newValue + * @returns {void} + */ + attributeChangedCallback (name, oldValue, newValue) { + if (oldValue === newValue) return; + switch (name) { + case 'value': + this.source = newValue; + if (newValue !== '' && newValue !== undefined) { + this._workarea = document.getElementById(this.source); + } + break; + default: + super.attributeChangedCallback(name, oldValue, newValue); + break; + } + } + /** + * @function get + * @returns {any} + */ + get value () { + return this.getAttribute('value'); + } + + /** + * @function set + * @returns {void} + */ + set value (value) { + this.setAttribute('value', value); + } + /** + * @function get + * @returns {any} + */ + get leftclick () { + return this.getAttribute('leftclick'); + } + + /** + * @function set + * @returns {void} + */ + set leftclick (value) { + this.setAttribute('leftclick', value); + } + /** + * @function connectedCallback + * @returns {void} + */ + connectedCallback () { + const current = this; + const onMenuOpenHandler = (e) => { + e.preventDefault(); + current.$dialog.style.top = e.pageY + 'px'; + current.$dialog.style.left = e.pageX + 'px'; + current.$dialog.style.display = 'block'; + }; + const onMenuCloseHandler = (e) => { + if (e.button !== 2) { + current.$dialog.style.display = 'none'; + } + }; + const onMenuClickHandler = (e, action, id) => { + const triggerEvent = new CustomEvent('change', {detail: { + trigger: action, + source: id + }}); + this.dispatchEvent(triggerEvent); + current.$dialog.style.display = 'none'; + }; + if (this._workarea !== undefined) { + this._workarea.addEventListener('contextmenu', onMenuOpenHandler); + if (this.getAttribute('leftclick') === 'true') { + this._workarea.addEventListener('click', onMenuOpenHandler); + } + this._workarea.addEventListener('mousedown', onMenuCloseHandler); + this.$sidePanels.addEventListener('mousedown', onMenuCloseHandler); + } + this.$duplicateLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'dupe', this.source)); + this.$deleteLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'delete', this.source)); + this.$mergeDownLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'merge_down', this.source)); + this.$mergeAllLink.addEventListener('click', (evt) => onMenuClickHandler(evt, 'merge_all', this.source)); + } +} + +// Register +customElements.define('se-cmenu-layers', SeCMenuLayerDialog); diff --git a/src/editor/dialogs/index.js b/src/editor/dialogs/index.js index efa4b307..faa5f538 100644 --- a/src/editor/dialogs/index.js +++ b/src/editor/dialogs/index.js @@ -1,3 +1,5 @@ import './imagePropertiesDialog.js'; import './editorPreferencesDialog.js'; import './svgSourceDialog.js'; +import './cmenuDialog.js'; +import './cmenuLayersDialog.js'; diff --git a/src/editor/index.html b/src/editor/index.html index aa31af93..ee28888b 100644 --- a/src/editor/index.html +++ b/src/editor/index.html @@ -406,26 +406,6 @@
- - - diff --git a/src/editor/svgedit.css b/src/editor/svgedit.css index 79935b71..79282d4f 100644 --- a/src/editor/svgedit.css +++ b/src/editor/svgedit.css @@ -1075,66 +1075,3 @@ ul li.current { background: #B0B0B0; border: 1px solid #000; } - -/* Generic context menu styles */ -.contextMenu { - position: absolute; - z-index: 99999; - border: solid 1px rgba(0,0,0,.33); - background: rgba(255,255,255,.95); - padding: 5px 0; - margin: 0px; - display: none; - font: 12px/15px Lucida Sans, Helvetica, Verdana, sans-serif; - border-radius: 5px; - -moz-border-radius: 5px; - -moz-box-shadow: 2px 5px 10px rgba(0,0,0,.3); - -webkit-box-shadow: 2px 5px 10px rgba(0,0,0,.3); - box-shadow: 2px 5px 10px rgba(0,0,0,.3); -} - -.contextMenu LI { - list-style: none; - padding: 0px; - margin: 0px; -} - -.contextMenu .shortcut { - width: 115px; - text-align:right; - float:right; -} - -.contextMenu A { - -moz-user-select: none; - -webkit-user-select: none; - color: #222; - text-decoration: none; - display: block; - line-height: 20px; - height: 20px; - background-position: 6px center; - background-repeat: no-repeat; - outline: none; - padding: 0px 15px 1px 20px; -} - -.contextMenu LI.hover A { - background-color: #2e5dea; - color: white; - cursor: default; -} - -.contextMenu LI.disabled A { - color: #999; -} - -.contextMenu LI.hover.disabled A { - background-color: transparent; -} - -.contextMenu LI.separator { - border-top: solid 1px #E3E3E3; - padding-top: 5px; - margin-top: 5px; -} diff --git a/src/editor/svgedit.js b/src/editor/svgedit.js index 15833e17..4a111b95 100644 --- a/src/editor/svgedit.js +++ b/src/editor/svgedit.js @@ -232,6 +232,21 @@ editor.init = () => { const newSeEditorDialog = document.createElement('se-svg-source-editor-dialog'); newSeEditorDialog.setAttribute('id', 'se-svg-editor-dialog'); document.body.append(newSeEditorDialog); + // canvas menu added to DOM + const dialogBox = document.createElement('se-cmenu_canvas-dialog'); + dialogBox.setAttribute('id', 'se-cmenu_canvas'); + document.body.append(dialogBox); + // 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); } catch (err) {} configObj.load(); @@ -358,7 +373,7 @@ editor.init = () => { const unit = configObj.curConfig.baseUnit !== 'px' ? configObj.curConfig.baseUnit : null; const isNode = currentMode === 'pathedit'; // elem ? (elem.id && elem.id.startsWith('pathpointgrip')) : false; - const menuItems = $('#cmenu_canvas li'); + const menuItems = document.getElementById('se-cmenu_canvas'); $('#selected_panel, #multiselected_panel, #g_panel, #rect_panel, #circle_panel,' + '#ellipse_panel, #line_panel, #text_panel, #image_panel, #container_panel,' + ' #use_panel, #a_panel').hide(); @@ -519,16 +534,16 @@ editor.init = () => { $('#g_title').prop('disabled', tagName === 'use'); } } - menuItems[(tagName === 'g' ? 'en' : 'dis') + 'ableContextMenuItems']('#ungroup'); - menuItems[((tagName === 'g' || !multiselected) ? 'dis' : 'en') + 'ableContextMenuItems']('#group'); + menuItems.setAttribute((tagName === 'g' ? 'en' : 'dis') + 'ablemenuitems', '#ungroup'); + menuItems.setAttribute(((tagName === 'g' || !multiselected) ? 'dis' : 'en') + 'ablemenuitems', '#group'); + // if (!Utils.isNullish(elem)) } else if (multiselected) { $('#multiselected_panel').show(); - menuItems - .enableContextMenuItems('#group') - .disableContextMenuItems('#ungroup'); + menuItems.setAttribute('enablemenuitems', '#group'); + menuItems.setAttribute('disablemenuitems', '#ungroup'); } else { - menuItems.disableContextMenuItems('#delete,#cut,#copy,#group,#ungroup,#move_front,#move_up,#move_down,#move_back'); + menuItems.setAttribute('disablemenuitems', '#delete,#cut,#copy,#group,#ungroup,#move_front,#move_up,#move_down,#move_back'); } // update history buttons @@ -542,9 +557,8 @@ editor.init = () => { $('#selLayerNames').removeAttr('disabled').val(currentLayerName); // Enable regular menu options - canvMenu.enableContextMenuItems( - '#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back' - ); + const canCMenu = document.getElementById('se-cmenu_canvas'); + canCMenu.setAttribute('enablemenuitems', '#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back'); } else { $('#selLayerNames').attr('disabled', 'disabled'); } @@ -556,8 +570,7 @@ editor.init = () => { const path = svgCanvas.pathActions; const {undoMgr} = svgCanvas; const workarea = $('#workarea'); - const canvMenu = $('#cmenu_canvas'); - + const canvMenu = document.getElementById('se-cmenu_canvas'); let exportWindow = null; let defaultImageURL = configObj.curConfig.imgPath + 'logo.svg'; const zoomInIcon = 'crosshair'; @@ -3009,6 +3022,25 @@ editor.init = () => { changeSidePanelWidth(deltaX); }; + const lmenuFunc = (action, el) => { + switch (action) { + case 'dupe': + /* await */ layersPanel.cloneLayer(); + break; + case 'delete': + layersPanel.deleteLayer(); + break; + case 'merge_down': + layersPanel.mergeLayer(); + break; + case 'merge_all': + layersPanel.svgCanvas.mergeAllLayers(); + layersPanel.updateContextPanel(); + layersPanel.populateLayers(); + break; + } + }; + /** * If width is non-zero, then fully close it; otherwise fully open it. * @param {boolean} close Forces the side panel closed @@ -3206,6 +3238,56 @@ editor.init = () => { saveSourceEditor(e); } }); + $id('se-cmenu_canvas').addEventListener('change', function (e) { + const action = e?.detail?.trigger; + switch (action) { + case 'delete': + deleteSelected(); + break; + case 'cut': + cutSelected(); + break; + case 'copy': + copySelected(); + break; + case 'paste': + svgCanvas.pasteElements(); + break; + case 'paste_in_place': + svgCanvas.pasteElements('in_place'); + break; + case 'group': + case 'group_elements': + svgCanvas.groupSelectedElements(); + break; + case 'ungroup': + svgCanvas.ungroupSelectedElement(); + break; + case 'move_front': + moveToTopSelected(); + break; + case 'move_up': + moveUpDownSelected('Up'); + break; + case 'move_down': + moveUpDownSelected('Down'); + break; + case 'move_back': + moveToBottomSelected(); + break; + default: + if (hasCustomHandler(action)) { + getCustomHandler(action).call(); + } + break; + } + }); + $id('se-cmenu-layers-more').addEventListener('change', function (e) { + lmenuFunc(e?.detail?.trigger, e?.detail?.source); + }); + $id('se-cmenu-layers-list').addEventListener('change', function (e) { + lmenuFunc(e?.detail?.trigger, e?.detail?.source); + }); layersPanel.addEvents(); const toolButtons = [ // Shortcuts not associated with buttons @@ -3404,63 +3486,8 @@ editor.init = () => { // zoom $id('zoom').value = (svgCanvas.getZoom() * 100).toFixed(1); - - $('#workarea').contextMenu( - { - menu: 'cmenu_canvas', - inSpeed: 0 - }, - function (action, el, pos) { - switch (action) { - case 'delete': - deleteSelected(); - break; - case 'cut': - cutSelected(); - break; - case 'copy': - copySelected(); - break; - case 'paste': - svgCanvas.pasteElements(); - break; - case 'paste_in_place': - svgCanvas.pasteElements('in_place'); - break; - case 'group': - case 'group_elements': - svgCanvas.groupSelectedElements(); - break; - case 'ungroup': - svgCanvas.ungroupSelectedElement(); - break; - case 'move_front': - moveToTopSelected(); - break; - case 'move_up': - moveUpDownSelected('Up'); - break; - case 'move_down': - moveUpDownSelected('Down'); - break; - case 'move_back': - moveToBottomSelected(); - break; - default: - if (hasCustomHandler(action)) { - getCustomHandler(action).call(); - } - break; - } - } - ); - - $('.contextMenu li').mousedown(function (ev) { - ev.preventDefault(); - }); - - $('#cmenu_canvas li').disableContextMenu(); - canvMenu.enableContextMenuItems('#delete,#cut,#copy'); + canvMenu.setAttribute('disableallmenu', true); + canvMenu.setAttribute('enablemenuitems', '#delete,#cut,#copy'); /** * @returns {void} */ @@ -3469,9 +3496,8 @@ editor.init = () => { try { svgeditClipboard = localStorage.getItem('svgedit_clipboard'); } catch (err) {} - canvMenu[(svgeditClipboard ? 'en' : 'dis') + 'ableContextMenuItems']( - '#paste,#paste_in_place' - ); + // eslint-disable-next-line max-len + canvMenu.setAttribute((svgeditClipboard ? 'en' : 'dis') + 'ablemenuitems', '#paste,#paste_in_place'); } enableOrDisableClipboard(); diff --git a/src/svgcanvas/selected-elem.js b/src/svgcanvas/selected-elem.js index 016de056..7cfc4fef 100644 --- a/src/svgcanvas/selected-elem.js +++ b/src/svgcanvas/selected-elem.js @@ -421,11 +421,9 @@ export const copySelectedElements = function () { sessionStorage.setItem(elementContext_.getClipboardID(), data); elementContext_.flashStorage(); - const menu = $('#cmenu_canvas'); // Context menu might not exist (it is provided by editor.js). - if (menu.enableContextMenuItems) { - menu.enableContextMenuItems('#paste,#paste_in_place'); - } + const canvMenu = document.getElementById('se-cmenu_canvas'); + canvMenu.setAttribute('enablemenuitems', '#paste,#paste_in_place'); }; /**