diff --git a/src/editor/components/index.js b/src/editor/components/index.js index f26f829e..a29c8d01 100644 --- a/src/editor/components/index.js +++ b/src/editor/components/index.js @@ -5,3 +5,5 @@ import './seDropdown.js'; import './seInput.js'; import './seSpinInput.js'; import './sePalette.js'; +import './seMenu.js'; +import './seMenuItem.js'; diff --git a/src/editor/components/seMenu.js b/src/editor/components/seMenu.js new file mode 100644 index 00000000..2c766e31 --- /dev/null +++ b/src/editor/components/seMenu.js @@ -0,0 +1,119 @@ +/* eslint-disable node/no-unpublished-import */ +import 'elix/define/MenuButton.js'; +import 'elix/define/MenuItem.js'; + +const template = document.createElement('template'); +template.innerHTML = ` + + + + + + +`; +/** + * @class SeMenu + */ +export class SeMenu 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.$menu = this._shadowRoot.querySelector('elix-menu-button'); + this.$label = this.$menu.shadowRoot.querySelector('#popupToggle').shadowRoot; + } + /** + * @function observedAttributes + * @returns {any} observed + */ + static get observedAttributes () { + return ['label', 'src']; + } + + /** + * @function attributeChangedCallback + * @param {string} name + * @param {string} oldValue + * @param {string} newValue + * @returns {void} + */ + attributeChangedCallback (name, oldValue, newValue) { + var image = new Image(); + if (oldValue === newValue) return; + switch (name) { + case 'src': + image.src = newValue; + image.width = 18; + image.height = 18; + this.$label.prepend(image); + break; + case 'label': + this.$label.prepend(newValue); + break; + default: + // eslint-disable-next-line no-console + console.error(`unknown attribute: ${name}`); + break; + } + } + /** + * @function get + * @returns {any} + */ + get label () { + return this.getAttribute('label'); + } + + /** + * @function set + * @returns {void} + */ + set label (value) { + this.setAttribute('label', value); + } + /** + * @function get + * @returns {any} + */ + get src () { + return this.getAttribute('src'); + } + + /** + * @function set + * @returns {void} + */ + set src (value) { + this.setAttribute('src', value); + } + /** + * @function connectedCallback + * @returns {void} + */ + connectedCallback () { + this.$menu.addEventListener('openedchange', (e) => { + e.preventDefault(); + const selectedItem = e?.detail?.closeResult; + if (selectedItem !== undefined && selectedItem?.id !== undefined) { + document.getElementById(selectedItem.id).click(); + } + }); + } +} + +// Register +customElements.define('se-menu', SeMenu); diff --git a/src/editor/components/seMenuItem.js b/src/editor/components/seMenuItem.js new file mode 100644 index 00000000..d93a0860 --- /dev/null +++ b/src/editor/components/seMenuItem.js @@ -0,0 +1,121 @@ +/* eslint-disable node/no-unpublished-import */ +import 'elix/define/Menu.js'; +import 'elix/define/MenuItem.js'; + +const template = document.createElement('template'); +template.innerHTML = ` + + +
+ icon + +
+
+`; +/** + * @class SeMenuItem + */ +export class SeMenuItem 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.$img = this._shadowRoot.querySelector('img'); + this.$label = this._shadowRoot.querySelector('span'); + this.$menuitem = this._shadowRoot.querySelector('elix-menu-item'); + } + /** + * @function observedAttributes + * @returns {any} observed + */ + static get observedAttributes () { + return ['label', 'src']; + } + /** + * @function attributeChangedCallback + * @param {string} name + * @param {string} oldValue + * @param {string} newValue + * @returns {void} + */ + attributeChangedCallback (name, oldValue, newValue) { + let shortcut = ''; + if (oldValue === newValue) return; + switch (name) { + case 'src': + this.$img.setAttribute('src', newValue); + this.$img.style.display = 'inline-block'; + break; + case 'label': + shortcut = this.getAttribute('shortcut'); + this.$label.textContent = `${newValue} ${shortcut ? `(${shortcut})` : ''}`; + break; + default: + // eslint-disable-next-line no-console + console.error(`unknown attribute: ${name}`); + break; + } + } + /** + * @function get + * @returns {any} + */ + get label () { + return this.getAttribute('label'); + } + + /** + * @function set + * @returns {void} + */ + set label (value) { + this.setAttribute('label', value); + } + /** + * @function get + * @returns {any} + */ + get src () { + return this.getAttribute('src'); + } + + /** + * @function set + * @returns {void} + */ + set src (value) { + this.setAttribute('src', value); + } + + /** + * @function connectedCallback + * @returns {void} + */ + connectedCallback () { + // capture shortcuts + const shortcut = this.getAttribute('shortcut'); + if (shortcut) { + // register the keydown event + document.addEventListener('keydown', (e) => { + // only track keyboard shortcuts for the body containing the SVG-Editor + if (e.target.nodeName !== 'BODY') return; + // normalize key + const key = `${(e.metaKey) ? 'meta+' : ''}${(e.ctrlKey) ? 'ctrl+' : ''}${e.key.toUpperCase()}`; + if (shortcut !== key) return; + // launch the click event + if (this.id) { + document.getElementById(this.id).click(); + } + e.preventDefault(); + }); + } + } +} + +// Register +customElements.define('se-menu-item', SeMenuItem); diff --git a/src/editor/images/docprop.svg b/src/editor/images/docprop.svg new file mode 100644 index 00000000..ab7fc559 --- /dev/null +++ b/src/editor/images/docprop.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/images/editPref.svg b/src/editor/images/editPref.svg new file mode 100644 index 00000000..96c7cd21 --- /dev/null +++ b/src/editor/images/editPref.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/images/export.svg b/src/editor/images/export.svg new file mode 100644 index 00000000..95e730c2 --- /dev/null +++ b/src/editor/images/export.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/images/imagelib.svg b/src/editor/images/imagelib.svg new file mode 100644 index 00000000..e4f821c6 --- /dev/null +++ b/src/editor/images/imagelib.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/images/importImg.svg b/src/editor/images/importImg.svg new file mode 100644 index 00000000..5a1908a6 --- /dev/null +++ b/src/editor/images/importImg.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/images/open.svg b/src/editor/images/open.svg new file mode 100644 index 00000000..4afb58fd --- /dev/null +++ b/src/editor/images/open.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/images/saveImg.svg b/src/editor/images/saveImg.svg new file mode 100644 index 00000000..23a517f5 --- /dev/null +++ b/src/editor/images/saveImg.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/images/svg-edit-home.svg b/src/editor/images/svg-edit-home.svg new file mode 100644 index 00000000..a2acbe49 --- /dev/null +++ b/src/editor/images/svg-edit-home.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/editor/index.html b/src/editor/index.html index 34e1b580..3303c0eb 100644 --- a/src/editor/index.html +++ b/src/editor/index.html @@ -74,14 +74,24 @@
L a y e r s
-
+ + + + + + + + + + + + -
- + + --> +
diff --git a/src/editor/svgedit.js b/src/editor/svgedit.js index 734d1d58..1650f2cd 100644 --- a/src/editor/svgedit.js +++ b/src/editor/svgedit.js @@ -4680,18 +4680,28 @@ editor.init = () => { $id('tool_italic').addEventListener('click', clickItalic); $id('palette').addEventListener('change', handlePalette); + $id('tool_clear').addEventListener('click', clickClear); + $id('tool_open').addEventListener('click', function (e) { + clickOpen(); + window.dispatchEvent(new CustomEvent('openImage')); + }); + $id('tool_import').addEventListener('click', function (e) { + clickImport(); + window.dispatchEvent(new CustomEvent('importImage')); + }); + $id('tool_save').addEventListener('click', function (e) { + if (editingsource) { + saveSourceEditor(); + } else { + clickSave(); + } + }); + $id('tool_export').addEventListener('click', clickExport); + $id('tool_docprops').addEventListener('click', showDocProperties); + $id('tool_editor_prefs').addEventListener('click', showPreferences); + $id('tool_editor_homepage').addEventListener('click', openHomePage); + const toolButtons = [ - {sel: '#tool_clear', fn: clickClear, evt: 'mouseup', key: ['N', true]}, - {sel: '#tool_save', fn () { - if (editingsource) { - saveSourceEditor(); - } else { - clickSave(); - } - }, evt: 'mouseup', key: ['S', true]}, - {sel: '#tool_export', fn: clickExport, evt: 'mouseup'}, - {sel: '#tool_open', fn: clickOpen, evt: 'mouseup', key: ['O', true]}, - {sel: '#tool_import', fn: clickImport, evt: 'mouseup'}, { key: ['esc', false, false], fn () { @@ -4707,12 +4717,7 @@ editor.init = () => { key: ['esc', false, false], hidekey: true}, {sel: '#tool_source_save', fn: saveSourceEditor, evt: 'click'}, {sel: '#tool_docprops_save', fn: saveDocProperties, evt: 'click'}, - {sel: '#tool_docprops', fn: showDocProperties, evt: 'click'}, {sel: '#tool_prefs_save', fn: savePreferences, evt: 'click'}, - {sel: '#tool_editor_prefs', fn: showPreferences, evt: 'click'}, - {sel: '#tool_editor_homepage', fn: openHomePage, evt: 'click'}, - {sel: '#tool_open', fn () { window.dispatchEvent(new CustomEvent('openImage')); }, evt: 'click'}, - {sel: '#tool_import', fn () { window.dispatchEvent(new CustomEvent('importImage')); }, evt: 'click'}, {sel: '#tool_node_link', fn: linkControlPoints, evt: 'click'}, {sel: '#tool_ungroup', fn: clickGroup, evt: 'click'}, {sel: '#tool_unlink_use', fn: clickGroup, evt: 'click'},