Bottom panel styling, more adaptive palette, shortcuts (#937)

This commit is contained in:
olekhshch
2023-12-24 01:48:27 +01:00
committed by GitHub
parent 2f9f6e2f71
commit d502525cc0
6 changed files with 494 additions and 134 deletions

View File

@@ -1,6 +1,9 @@
name: Node CI name: Node CI
on: [pull_request] on: [pull_request]
# To be able to write in the PR, we need write permission.
permissions:
pull-requests: write
jobs: jobs:
build: build:

View File

@@ -71,49 +71,240 @@ class Editor extends EditorStartup {
this.docprops = false this.docprops = false
this.configObj.preferences = false this.configObj.preferences = false
this.canvMenu = null this.canvMenu = null
this.goodLangs = ['ar', 'cs', 'de', 'en', 'es', 'fa', 'fr', 'fy', 'hi', 'it', 'ja', 'nl', 'pl', 'pt-BR', 'ro', 'ru', 'sk', 'sl', 'sv', 'tr', 'uk', 'zh-CN', 'zh-TW'] this.goodLangs = [
const modKey = (isMac() ? 'meta+' : 'ctrl+') 'ar',
'cs',
'de',
'en',
'es',
'fa',
'fr',
'fy',
'hi',
'it',
'ja',
'nl',
'pl',
'pt-BR',
'ro',
'ru',
'sk',
'sl',
'sv',
'tr',
'uk',
'zh-CN',
'zh-TW'
]
const modKey = isMac() ? 'meta+' : 'ctrl+'
this.shortcuts = [ this.shortcuts = [
// Shortcuts not associated with buttons // Shortcuts not associated with buttons
{ key: 'ctrl+arrowleft', fn: () => { this.rotateSelected(0, 1) } }, {
{ key: 'ctrl+arrowright', fn: () => { this.rotateSelected(1, 1) } }, key: ['ctrl+arrowleft', true],
{ key: 'ctrl+shift+arrowleft', fn: () => { this.rotateSelected(0, 5) } }, fn: () => {
{ key: 'ctrl+shift+arrowright', fn: () => { this.rotateSelected(1, 5) } }, this.rotateSelected(0, 1)
{ key: 'shift+o', fn: () => { this.svgCanvas.cycleElement(0) } }, }
{ key: 'shift+p', fn: () => { this.svgCanvas.cycleElement(1) } }, },
{ key: 'tab', fn: () => { this.svgCanvas.cycleElement(0) } }, {
{ key: 'shift+tab', fn: () => { this.svgCanvas.cycleElement(1) } }, key: 'ctrl+arrowright',
{ key: [modKey + 'arrowup', true], fn: () => { this.zoomImage(2) } }, fn: () => {
{ key: [modKey + 'arrowdown', true], fn: () => { this.zoomImage(0.5) } }, this.rotateSelected(1, 1)
{ key: [modKey + ']', true], fn: () => { this.moveUpDownSelected('Up') } }, }
{ key: [modKey + '[', true], fn: () => { this.moveUpDownSelected('Down') } }, },
{ key: ['arrowup', true], fn: () => { this.moveSelected(0, -1) } }, {
{ key: ['arrowdown', true], fn: () => { this.moveSelected(0, 1) } }, key: ['ctrl+shift+arrowleft', true],
{ key: ['arrowleft', true], fn: () => { this.moveSelected(-1, 0) } }, fn: () => {
{ key: ['arrowright', true], fn: () => { this.moveSelected(1, 0) } }, this.rotateSelected(0, 5)
{ key: 'shift+arrowup', fn: () => { this.moveSelected(0, -10) } }, }
{ key: 'shift+arrowdown', fn: () => { this.moveSelected(0, 10) } }, },
{ key: 'shift+arrowleft', fn: () => { this.moveSelected(-10, 0) } }, {
{ key: 'shift+arrowright', fn: () => { this.moveSelected(10, 0) } }, key: 'ctrl+shift+arrowright',
{ key: ['alt+arrowup', true], fn: () => { this.svgCanvas.cloneSelectedElements(0, -1) } }, fn: () => {
{ key: ['alt+arrowdown', true], fn: () => { this.svgCanvas.cloneSelectedElements(0, 1) } }, this.rotateSelected(1, 5)
{ key: ['alt+arrowleft', true], fn: () => { this.svgCanvas.cloneSelectedElements(-1, 0) } }, }
{ key: ['alt+arrowright', true], fn: () => { this.svgCanvas.cloneSelectedElements(1, 0) } }, },
{ key: ['alt+shift+arrowup', true], fn: () => { this.svgCanvas.cloneSelectedElements(0, -10) } }, {
{ key: ['alt+shift+arrowdown', true], fn: () => { this.svgCanvas.cloneSelectedElements(0, 10) } }, key: 'shift+o',
{ key: ['alt+shift+arrowleft', true], fn: () => { this.svgCanvas.cloneSelectedElements(-10, 0) } }, fn: () => {
{ key: ['alt+shift+arrowright', true], fn: () => { this.svgCanvas.cloneSelectedElements(10, 0) } }, this.svgCanvas.cycleElement(0)
}
},
{
key: 'shift+p',
fn: () => {
this.svgCanvas.cycleElement(1)
}
},
{
key: 'tab',
fn: () => {
this.svgCanvas.cycleElement(0)
}
},
{
key: 'shift+tab',
fn: () => {
this.svgCanvas.cycleElement(1)
}
},
{
key: [modKey + 'arrowup', true],
fn: () => {
this.zoomImage(2)
}
},
{
key: [modKey + 'arrowdown', true],
fn: () => {
this.zoomImage(0.5)
}
},
{
key: [modKey + ']', true],
fn: () => {
this.moveUpDownSelected('Up')
}
},
{
key: [modKey + '[', true],
fn: () => {
this.moveUpDownSelected('Down')
}
},
{
key: ['arrowup', true],
fn: () => {
this.moveSelected(0, -1)
}
},
{
key: ['arrowdown', true],
fn: () => {
this.moveSelected(0, 1)
}
},
{
key: ['arrowleft', true],
fn: () => {
this.moveSelected(-1, 0)
}
},
{
key: ['arrowright', true],
fn: () => {
this.moveSelected(1, 0)
}
},
{
key: 'shift+arrowup',
fn: () => {
this.moveSelected(0, -10)
}
},
{
key: 'shift+arrowdown',
fn: () => {
this.moveSelected(0, 10)
}
},
{
key: 'shift+arrowleft',
fn: () => {
this.moveSelected(-10, 0)
}
},
{
key: 'shift+arrowright',
fn: () => {
this.moveSelected(10, 0)
}
},
{
key: ['alt+arrowup', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(0, -1)
}
},
{
key: ['alt+arrowdown', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(0, 1)
}
},
{
key: ['alt+arrowleft', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(-1, 0)
}
},
{
key: ['alt+arrowright', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(1, 0)
}
},
{
key: ['alt+shift+arrowup', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(0, -10)
}
},
{
key: ['alt+shift+arrowdown', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(0, 10)
}
},
{
key: ['alt+shift+arrowleft', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(-10, 0)
}
},
{
key: ['alt+shift+arrowright', true],
fn: () => {
this.svgCanvas.cloneSelectedElements(10, 0)
}
},
{ {
key: ['delete/backspace', true], key: ['delete/backspace', true],
fn: () => { fn: () => {
if (this.selectedElement || this.multiselected) { this.svgCanvas.deleteSelectedElements() } if (this.selectedElement || this.multiselected) {
this.svgCanvas.deleteSelectedElements()
}
} }
}, },
{ key: 'a', fn: () => { this.svgCanvas.selectAllInCurrentLayer() } }, {
{ key: modKey + 'a', fn: () => { this.svgCanvas.selectAllInCurrentLayer() } }, key: 'a',
{ key: modKey + 'x', fn: () => { this.cutSelected() } }, fn: () => {
{ key: modKey + 'c', fn: () => { this.copySelected() } }, this.svgCanvas.selectAllInCurrentLayer()
{ key: modKey + 'v', fn: () => { this.pasteInCenter() } } }
},
{
key: [modKey + 'a', true],
fn: () => {
this.svgCanvas.selectAllInCurrentLayer()
}
},
{
key: modKey + 'x',
fn: () => {
this.cutSelected()
}
},
{
key: modKey + 'c',
fn: () => {
this.copySelected()
}
},
{
key: modKey + 'v',
fn: () => {
this.pasteInCenter()
}
}
] ]
this.leftPanel = new LeftPanel(this) this.leftPanel = new LeftPanel(this)
this.bottomPanel = new BottomPanel(this) this.bottomPanel = new BottomPanel(this)
@@ -204,7 +395,7 @@ class Editor extends EditorStartup {
setAll () { setAll () {
const keyHandler = {} // will contain the action for each pressed key const keyHandler = {} // will contain the action for each pressed key
this.shortcuts.forEach(shortcut => { this.shortcuts.forEach((shortcut) => {
// Bind function to shortcut key // Bind function to shortcut key
if (shortcut.key) { if (shortcut.key) {
// Set shortcut based on options // Set shortcut based on options
@@ -218,14 +409,14 @@ class Editor extends EditorStartup {
} }
keyval = String(keyval) keyval = String(keyval)
const { fn } = shortcut const { fn } = shortcut
keyval.split('/').forEach(key => { keyval.split('/').forEach((key) => {
keyHandler[key] = { fn, pd } keyHandler[key] = { fn, pd }
}) })
} }
return true return true
}) })
// register the keydown event // register the keydown event
document.addEventListener('keydown', e => { document.addEventListener('keydown', (e) => {
// only track keyboard shortcuts for the body containing the SVG-Editor // only track keyboard shortcuts for the body containing the SVG-Editor
if (e.target.nodeName !== 'BODY') return if (e.target.nodeName !== 'BODY') return
// normalize key // normalize key
@@ -282,7 +473,7 @@ class Editor extends EditorStartup {
* @returns {module:SVGthis.ToolButton} * @returns {module:SVGthis.ToolButton}
*/ */
getButtonData (sel) { getButtonData (sel) {
return Object.values(this.shortcuts).find(btn => { return Object.values(this.shortcuts).find((btn) => {
return btn.sel === sel return btn.sel === sel
}) })
} }
@@ -350,7 +541,9 @@ class Editor extends EditorStartup {
const { workarea } = this const { workarea } = this
const cnvs = $id('svgcanvas') const cnvs = $id('svgcanvas')
let w = parseFloat(getComputedStyle(workarea, null).width.replace('px', '')) let w = parseFloat(
getComputedStyle(workarea, null).width.replace('px', '')
)
let h = parseFloat( let h = parseFloat(
getComputedStyle(workarea, null).height.replace('px', '') getComputedStyle(workarea, null).height.replace('px', '')
) )
@@ -444,9 +637,8 @@ class Editor extends EditorStartup {
} }
` `
if (document.querySelectorAll('#wireframe_rules').length > 0) { if (document.querySelectorAll('#wireframe_rules').length > 0) {
document.querySelector( document.querySelector('#wireframe_rules').textContent =
'#wireframe_rules' this.workarea.classList.contains('wireframe') ? rule : ''
).textContent = this.workarea.classList.contains('wireframe') ? rule : ''
} }
} }
@@ -538,7 +730,7 @@ class Editor extends EditorStartup {
this.leftPanel.clickSelect() this.leftPanel.clickSelect()
} }
elems.forEach(elem => { elems.forEach((elem) => {
const isSvgElem = elem?.tagName === 'svg' const isSvgElem = elem?.tagName === 'svg'
if (isSvgElem || this.svgCanvas.isLayer(elem)) { if (isSvgElem || this.svgCanvas.isLayer(elem)) {
this.layersPanel.populateLayers() this.layersPanel.populateLayers()
@@ -593,18 +785,14 @@ class Editor extends EditorStartup {
* @returns {void} * @returns {void}
*/ */
afterClear (win) { afterClear (win) {
this.svgCanvas.runExtensions( this.svgCanvas.runExtensions('afterClear')
'afterClear'
)
} }
/** /**
* @returns {void} * @returns {void}
*/ */
beforeClear (win) { beforeClear (win) {
this.svgCanvas.runExtensions( this.svgCanvas.runExtensions('beforeClear')
'beforeClear'
)
} }
/** /**
@@ -1135,7 +1323,7 @@ class Editor extends EditorStartup {
return this.ready(() => { return this.ready(() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fetch(url, { cache: cache ? 'force-cache' : 'no-cache' }) fetch(url, { cache: cache ? 'force-cache' : 'no-cache' })
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
if (noAlert) { if (noAlert) {
reject(new Error('URLLoadFail')) reject(new Error('URLLoadFail'))
@@ -1146,16 +1334,18 @@ class Editor extends EditorStartup {
} }
return response.text() return response.text()
}) })
.then(str => { .then((str) => {
this.loadSvgString(str, { noAlert }) this.loadSvgString(str, { noAlert })
return str return str
}) })
.catch(error => { .catch((error) => {
if (noAlert) { if (noAlert) {
reject(new Error('URLLoadFail')) reject(new Error('URLLoadFail'))
return return
} }
seAlert(this.i18next.t('notification.URLLoadFail') + ': \n' + error) seAlert(
this.i18next.t('notification.URLLoadFail') + ': \n' + error
)
resolve() resolve()
}) })
}) })

View File

@@ -634,7 +634,7 @@ div.jGraduate_Slider img {
} }
#color_picker { #color_picker {
z-index: 1000; z-index: 1000;
top: 60%; bottom: 0;
} }
</style> </style>
<div id="picker"> <div id="picker">
@@ -651,8 +651,8 @@ div.jGraduate_Slider img {
*/ */
export class SeColorPicker extends HTMLElement { export class SeColorPicker extends HTMLElement {
/** /**
* @function constructor * @function constructor
*/ */
constructor () { constructor () {
super() super()
// create the shadowDom and insert the template // create the shadowDom and insert the template

View File

@@ -2,16 +2,48 @@
/* eslint-disable max-len */ /* eslint-disable max-len */
const palette = [ const palette = [
// Todo: Make into configuration item? // Todo: Make into configuration item?
'none', '#000000', '#3f3f3f', '#7f7f7f', '#bfbfbf', '#ffffff', 'none',
'#ff0000', '#ff7f00', '#ffff00', '#7fff00', '#000000',
'#00ff00', '#00ff7f', '#00ffff', '#007fff', '#3f3f3f',
'#0000ff', '#7f00ff', '#ff00ff', '#ff007f', '#7f7f7f',
'#7f0000', '#7f3f00', '#7f7f00', '#3f7f00', '#bfbfbf',
'#007f00', '#007f3f', '#007f7f', '#003f7f', '#ffffff',
'#00007f', '#3f007f', '#7f007f', '#7f003f', '#ff0000',
'#ffaaaa', '#ffd4aa', '#ffffaa', '#d4ffaa', '#ff7f00',
'#aaffaa', '#aaffd4', '#aaffff', '#aad4ff', '#ffff00',
'#aaaaff', '#d4aaff', '#ffaaff', '#ffaad4' '#7fff00',
'#00ff00',
'#00ff7f',
'#00ffff',
'#007fff',
'#0000ff',
'#7f00ff',
'#ff00ff',
'#ff007f',
'#7f0000',
'#7f3f00',
'#7f7f00',
'#3f7f00',
'#007f00',
'#007f3f',
'#007f7f',
'#003f7f',
'#00007f',
'#3f007f',
'#7f007f',
'#7f003f',
'#ffaaaa',
'#ffd4aa',
'#ffffaa',
'#d4ffaa',
'#aaffaa',
'#aaffd4',
'#aaffff',
'#aad4ff',
'#aaaaff',
'#d4aaff',
'#ffaaff',
'#ffaad4'
] ]
const template = document.createElement('template') const template = document.createElement('template')
@@ -32,8 +64,9 @@ template.innerHTML = `
#js-se-palette { #js-se-palette {
float: left; float: left;
width: 632px; min-width: 30px;
height: 16px; height: 15px;
overflow: hidden;
} }
div.palette_item { div.palette_item {
@@ -46,11 +79,39 @@ template.innerHTML = `
background: white; background: white;
} }
.palette_expand_btn {
background: none;
border: none;
width: 14px;
height: 14px;
font-size: 12px;
cursor: pointer;
user-select:none;
}
#palette_popup {
padding: 4px;
margin-left: 24px;
background: white;
min-width: 180px;
max-width: 360px;
min-height: 14px;
flex-wrap: wrap;
border-radius: 4px;
position: absolute;
bottom: 36px;
right: 30px;
}
</style> </style>
<div id="palette_holder" title=""> <div id="palette_holder" title="">
<div id="js-se-palette"> <div id="js-se-palette">
</div> </div>
</div> </div>
<button class="palette_expand_btn" title="Show whole palette">▼</button>
<!-- hidden div -->
<div id="palette_popup" style="display:none"/>
` `
/** /**
@@ -58,20 +119,38 @@ template.innerHTML = `
*/ */
export class SEPalette extends HTMLElement { export class SEPalette extends HTMLElement {
/** /**
* @function constructor * @function constructor
*/ */
constructor () { constructor () {
super() super()
// create the shadowDom and insert the template // create the shadowDom and insert the template
this._shadowRoot = this.attachShadow({ mode: 'open' }) this._shadowRoot = this.attachShadow({ mode: 'open' })
this._shadowRoot.append(template.content.cloneNode(true)) this._shadowRoot.append(template.content.cloneNode(true))
this.$strip = this._shadowRoot.querySelector('#js-se-palette') this.$strip = this._shadowRoot.querySelector('#js-se-palette')
this.expand_btn = this._shadowRoot.querySelector(
'button.palette_expand_btn'
)
this.popUp = this._shadowRoot.getElementById('palette_popup')
svgEditor.$click(this.expand_btn, (e) => {
e.stopPropagation()
const { display } = this.popUp.style
if (display === 'none') {
this.showPopUp()
} else {
this.hidePopUp()
}
})
svgEditor.svgCanvas.container.addEventListener('click', () =>
this.hidePopUp()
)
palette.forEach((rgb) => { palette.forEach((rgb) => {
const newDiv = document.createElement('div') const newDiv = document.createElement('div')
newDiv.classList.add('square') newDiv.classList.add('square')
if (rgb === 'none') { if (rgb === 'none') {
const img = document.createElement('img') const img = document.createElement('img')
img.src = 'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InN2Z19pY29uIj48c3ZnIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICA8bGluZSBmaWxsPSJub25lIiBzdHJva2U9IiNkNDAwMDAiIGlkPSJzdmdfOTAiIHkyPSIyNCIgeDI9IjI0IiB5MT0iMCIgeDE9IjAiLz4KICAgIDxsaW5lIGlkPSJzdmdfOTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q0MDAwMCIgeTI9IjI0IiB4Mj0iMCIgeTE9IjAiIHgxPSIyNCIvPgogIDwvc3ZnPjwvc3ZnPg==' img.src =
'data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgY2xhc3M9InN2Z19pY29uIj48c3ZnIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+CiAgICA8bGluZSBmaWxsPSJub25lIiBzdHJva2U9IiNkNDAwMDAiIGlkPSJzdmdfOTAiIHkyPSIyNCIgeDI9IjI0IiB5MT0iMCIgeDE9IjAiLz4KICAgIDxsaW5lIGlkPSJzdmdfOTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2Q0MDAwMCIgeTI9IjI0IiB4Mj0iMCIgeTE9IjAiIHgxPSIyNCIvPgogIDwvc3ZnPjwvc3ZnPg=='
img.style.width = '15px' img.style.width = '15px'
img.style.height = '15px' img.style.height = '15px'
newDiv.append(img) newDiv.append(img)
@@ -79,19 +158,31 @@ export class SEPalette extends HTMLElement {
newDiv.style.backgroundColor = rgb newDiv.style.backgroundColor = rgb
} }
newDiv.dataset.rgb = rgb newDiv.dataset.rgb = rgb
svgEditor.$click(newDiv, (evt) => { const clickCb = (evt) => {
evt.preventDefault() evt.preventDefault()
// shift key or right click for stroke // shift key or right click for stroke
const picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill' const picker = evt.shiftKey || evt.button === 2 ? 'stroke' : 'fill'
let color = newDiv.dataset.rgb let color = newDiv.dataset.rgb
// Webkit-based browsers returned 'initial' here for no stroke // Webkit-based browsers returned 'initial' here for no stroke
if (color === 'none' || color === 'transparent' || color === 'initial') { if (
color === 'none' ||
color === 'transparent' ||
color === 'initial'
) {
color = 'none' color = 'none'
} }
const paletteEvent = new CustomEvent('change', { detail: { picker, color }, bubbles: false }) const paletteEvent = new CustomEvent('change', {
detail: { picker, color },
bubbles: false
})
this.dispatchEvent(paletteEvent) this.dispatchEvent(paletteEvent)
}) }
svgEditor.$click(newDiv, clickCb)
this.$strip.append(newDiv) this.$strip.append(newDiv)
const divDialog = newDiv.cloneNode(true)
svgEditor.$click(divDialog, clickCb)
this.popUp.append(divDialog)
}) })
} }
@@ -131,7 +222,21 @@ export class SEPalette extends HTMLElement {
* @function connectedCallback * @function connectedCallback
* @returns {void} * @returns {void}
*/ */
connectedCallback () { connectedCallback () {}
/**
* Shows popUp window with the whole palette
*/
showPopUp () {
this.popUp.style.display = 'flex'
this.expand_btn.textContent = '▲'
this.expand_btn.setAttribute('title', 'Hide palette window')
}
hidePopUp () {
this.popUp.style.display = 'none'
this.expand_btn.textContent = '▼'
this.expand_btn.setAttribute('title', 'Show palette window')
} }
} }

View File

@@ -8,12 +8,12 @@ const { $id } = SvgCanvas
* register actions for left panel * register actions for left panel
*/ */
/** /**
* @type {module} * @type {module}
*/ */
class BottomPanel { class BottomPanel {
/** /**
* @param {PlainObject} editor svgedit handler * @param {PlainObject} editor svgedit handler
*/ */
constructor (editor) { constructor (editor) {
this.editor = editor this.editor = editor
} }
@@ -26,26 +26,30 @@ class BottomPanel {
} }
/** /**
* @type {module} * @type {module}
*/ */
get multiselected () { get multiselected () {
return this.editor.multiselected return this.editor.multiselected
} }
/** /**
* @type {module} * @type {module}
*/ */
changeStrokeWidth (e) { changeStrokeWidth (e) {
let val = e.target.value let val = e.target.value
if (val === 0 && this.editor.selectedElement && ['line', 'polyline'].includes(this.editor.selectedElement.nodeName)) { if (
val === 0 &&
this.editor.selectedElement &&
['line', 'polyline'].includes(this.editor.selectedElement.nodeName)
) {
val = 1 val = 1
} }
this.editor.svgCanvas.setStrokeWidth(val) this.editor.svgCanvas.setStrokeWidth(val)
} }
/** /**
* @type {module} * @type {module}
*/ */
changeZoom (value) { changeZoom (value) {
switch (value) { switch (value) {
case 'canvas': case 'canvas':
@@ -54,34 +58,51 @@ class BottomPanel {
case 'content': case 'content':
this.editor.zoomChanged(window, value) this.editor.zoomChanged(window, value)
break break
default: default: {
{
const zoomlevel = Number(value) > 0.1 ? Number(value) * 0.01 : 0.1 const zoomlevel = Number(value) > 0.1 ? Number(value) * 0.01 : 0.1
const zoom = this.editor.svgCanvas.getZoom() const zoom = this.editor.svgCanvas.getZoom()
const { workarea } = this.editor const { workarea } = this.editor
this.editor.zoomChanged(window, { this.editor.zoomChanged(
width: 0, window,
height: 0, {
// center pt of scroll position width: 0,
x: (workarea.scrollLeft + parseFloat(getComputedStyle(workarea, null).width.replace('px', '')) / 2) / zoom, height: 0,
y: (workarea.scrollTop + parseFloat(getComputedStyle(workarea, null).height.replace('px', '')) / 2) / zoom, // center pt of scroll position
zoom: zoomlevel x:
}, true) (workarea.scrollLeft +
parseFloat(
getComputedStyle(workarea, null).width.replace('px', '')
) /
2) /
zoom,
y:
(workarea.scrollTop +
parseFloat(
getComputedStyle(workarea, null).height.replace('px', '')
) /
2) /
zoom,
zoom: zoomlevel
},
true
)
} }
} }
} }
/** /**
* @fires module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate * @fires module:svgcanvas.SvgCanvas#event:ext_toolButtonStateUpdate
* @returns {void} * @returns {void}
*/ */
updateToolButtonState () { updateToolButtonState () {
const bNoFill = (this.editor.svgCanvas.getColor('fill') === 'none') const bNoFill = this.editor.svgCanvas.getColor('fill') === 'none'
const bNoStroke = (this.editor.svgCanvas.getColor('stroke') === 'none') const bNoStroke = this.editor.svgCanvas.getColor('stroke') === 'none'
const buttonsNeedingStroke = ['tool_fhpath', 'tool_line'] const buttonsNeedingStroke = ['tool_fhpath', 'tool_line']
const buttonsNeedingFillAndStroke = [ const buttonsNeedingFillAndStroke = [
'tools_rect', 'tools_ellipse', 'tools_rect',
'tool_text', 'tool_path' 'tools_ellipse',
'tool_text',
'tool_path'
] ]
if (bNoStroke) { if (bNoStroke) {
@@ -120,8 +141,8 @@ class BottomPanel {
} }
/** /**
* @type {module} * @type {module}
*/ */
handleColorPicker (type, evt) { handleColorPicker (type, evt) {
const { paint } = evt.detail const { paint } = evt.detail
this.editor.svgCanvas.setPaint(type, paint) this.editor.svgCanvas.setPaint(type, paint)
@@ -129,44 +150,50 @@ class BottomPanel {
} }
/** /**
* @type {module} * @type {module}
*/ */
handleStrokeAttr (type, evt) { handleStrokeAttr (type, evt) {
this.editor.svgCanvas.setStrokeAttr(type, evt.detail.value) this.editor.svgCanvas.setStrokeAttr(type, evt.detail.value)
} }
/** /**
* @type {module} * @type {module}
*/ */
handleOpacity (evt) { handleOpacity (evt) {
const val = Number.parseInt(evt.currentTarget.value.split('%')[0]) const val = Number.parseInt(evt.currentTarget.value.split('%')[0])
this.editor.svgCanvas.setOpacity(val / 100) this.editor.svgCanvas.setOpacity(val / 100)
} }
/** /**
* @type {module} * @type {module}
*/ */
handlePalette (e) { handlePalette (e) {
e.preventDefault() e.preventDefault()
// shift key or right click for stroke // shift key or right click for stroke
const { picker, color } = e.detail const { picker, color } = e.detail
// Webkit-based browsers returned 'initial' here for no stroke // Webkit-based browsers returned 'initial' here for no stroke
const paint = color === 'none' ? new jGraduate.Paint() : new jGraduate.Paint({ alpha: 100, solidColor: color.substr(1) }) const paint =
color === 'none'
? new jGraduate.Paint()
: new jGraduate.Paint({ alpha: 100, solidColor: color.substr(1) })
if (picker === 'fill') { if (picker === 'fill') {
$id('fill_color').setPaint(paint) $id('fill_color').setPaint(paint)
} else { } else {
$id('stroke_color').setPaint(paint) $id('stroke_color').setPaint(paint)
} }
this.editor.svgCanvas.setColor(picker, color) this.editor.svgCanvas.setColor(picker, color)
if (color !== 'none' && this.editor.svgCanvas.getPaintOpacity(picker) !== 1) { if (
color !== 'none' &&
this.editor.svgCanvas.getPaintOpacity(picker) !== 1
) {
this.editor.svgCanvas.setPaintOpacity(picker, 1.0) this.editor.svgCanvas.setPaintOpacity(picker, 1.0)
} }
this.updateToolButtonState() this.updateToolButtonState()
} }
/** /**
* @type {module} * @type {module}
*/ */
init () { init () {
// register actions for Bottom panel // register actions for Bottom panel
const template = document.createElement('template') const template = document.createElement('template')
@@ -177,26 +204,56 @@ class BottomPanel {
$id('palette').addEventListener('change', this.handlePalette.bind(this)) $id('palette').addEventListener('change', this.handlePalette.bind(this))
$id('palette').init(i18next) $id('palette').init(i18next)
const { curConfig } = this.editor.configObj const { curConfig } = this.editor.configObj
$id('fill_color').setPaint(new jGraduate.Paint({ alpha: 100, solidColor: curConfig.initFill.color })) $id('fill_color').setPaint(
$id('stroke_color').setPaint(new jGraduate.Paint({ alpha: 100, solidColor: curConfig.initStroke.color })) new jGraduate.Paint({ alpha: 100, solidColor: curConfig.initFill.color })
$id('zoom').addEventListener('change', (e) => this.changeZoom.bind(this)(e.detail.value)) )
$id('stroke_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('stroke', evt)) $id('stroke_color').setPaint(
$id('fill_color').addEventListener('change', (evt) => this.handleColorPicker.bind(this)('fill', evt)) new jGraduate.Paint({
$id('stroke_width').addEventListener('change', this.changeStrokeWidth.bind(this)) alpha: 100,
$id('stroke_style').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-dasharray', evt)) solidColor: curConfig.initStroke.color
$id('stroke_linejoin').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linejoin', evt)) })
$id('stroke_linecap').addEventListener('change', (evt) => this.handleStrokeAttr.bind(this)('stroke-linecap', evt)) )
$id('zoom').addEventListener('change', (e) =>
this.changeZoom.bind(this)(e.detail.value)
)
$id('stroke_color').addEventListener('change', (evt) =>
this.handleColorPicker.bind(this)('stroke', evt)
)
$id('fill_color').addEventListener('change', (evt) =>
this.handleColorPicker.bind(this)('fill', evt)
)
$id('stroke_width').addEventListener(
'change',
this.changeStrokeWidth.bind(this)
)
$id('stroke_style').addEventListener('change', (evt) =>
this.handleStrokeAttr.bind(this)('stroke-dasharray', evt)
)
$id('stroke_linejoin').addEventListener('change', (evt) =>
this.handleStrokeAttr.bind(this)('stroke-linejoin', evt)
)
$id('stroke_linecap').addEventListener('change', (evt) =>
this.handleStrokeAttr.bind(this)('stroke-linecap', evt)
)
$id('opacity').addEventListener('change', this.handleOpacity.bind(this)) $id('opacity').addEventListener('change', this.handleOpacity.bind(this))
$id('fill_color').init(i18next) $id('fill_color').init(i18next)
$id('stroke_color').init(i18next) $id('stroke_color').init(i18next)
} }
/** /**
* @type {module} * @type {module}
*/ */
updateColorpickers (apply) { updateColorpickers (apply) {
$id('fill_color').update(this.editor.svgCanvas, this.editor.selectedElement, apply) $id('fill_color').update(
$id('stroke_color').update(this.editor.svgCanvas, this.editor.selectedElement, apply) this.editor.svgCanvas,
this.editor.selectedElement,
apply
)
$id('stroke_color').update(
this.editor.svgCanvas,
this.editor.selectedElement,
apply
)
} }
} }

View File

@@ -323,14 +323,16 @@ hr {
#tools_bottom { #tools_bottom {
grid-area: bottom; grid-area: bottom;
overflow: auto; overflow-x: auto;
overflow-y: hidden;
display: flex; display: flex;
align-items: center; align-items: center;
scrollbar-width: thin; /* Firefox */ scrollbar-width: thin; /* Firefox */
} }
#tools_bottom ::-webkit-scrollbar { /* Chrome, Edge, and Safari */ #tools_bottom::-webkit-scrollbar { /* Chrome, Edge, and Safari */
width: 3px; width: 3px;
height: 3px;
} }
#tools_bottom se-list, #tools_bottom se-select { #tools_bottom se-list, #tools_bottom se-select {
@@ -358,11 +360,11 @@ hr {
width: 3px; width: 3px;
} }
#tools_left::-webkit-scrollbar-track { #tools_left::-webkit-scrollbar-track, #tools_bottom::-webkit-scrollbar-track{
background: transparent; background: transparent;
} }
#tools_left::-webkit-scrollbar-thumb { #tools_left::-webkit-scrollbar-thumb, #tools_bottom::-webkit-scrollbar-thumb {
background-color:rgb(70, 70, 70); background-color:rgb(70, 70, 70);
} }
@@ -530,6 +532,9 @@ input[type=text] {
#palette { #palette {
margin-left: auto; margin-left: auto;
margin-right: 16px;
display: flex;
align-items: center;
} }
#stroke_expand { #stroke_expand {