Cursors, Esc to cancel active tool (#946)

* basic drawing cursors

* Esc to cancel tool, I shortcut bug fix

* panel btns hover animation

* minor changes

* Linter fix

* update packages

* remove NYC coverage causing build errors
would be nice to find out a replacement or a fix

---------

Co-authored-by: JFH <20402845+jfhenon@users.noreply.github.com>
master
olekhshch 2024-01-17 15:19:37 +01:00 committed by GitHub
parent 24f78d3d0f
commit f75d1a83a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 438 additions and 997 deletions

View File

@ -24,12 +24,12 @@ jobs:
npm run test
env:
CI: true
- name: Report NYC coverage
uses: sidx1024/report-nyc-coverage-github-action@v1.2.6
with:
# Path to coverage file generated by "nyc report".
coverage_file: "coverage/coverage-summary.json"
base_coverage_file: "coverage/coverage-summary.json"
comment_template_file: ".github/comment-template.md"
# - name: Report NYC coverage - removed for now
# uses: sidx1024/report-nyc-coverage-github-action@v1.2.6
# with:
# # Path to coverage file generated by "nyc report".
# coverage_file: "coverage/coverage-summary.json"
# base_coverage_file: "coverage/coverage-summary.json"
# comment_template_file: ".github/comment-template.md"

1229
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -85,22 +85,22 @@
"dependencies": {
"@svgedit/svgcanvas": "7.2.2",
"browser-fs-access": "0.35.0",
"core-js": "3.34.0",
"core-js": "3.35.0",
"elix": "15.0.1",
"html2canvas": "1.4.1",
"i18next": "23.7.8",
"i18next": "23.7.16",
"jspdf": "2.5.1",
"pathseg": "1.2.1",
"regenerator-runtime": "0.14.0",
"replace-in-file": "^7.0.2",
"svg2pdf.js": "2.2.2"
"regenerator-runtime": "0.14.1",
"replace-in-file": "^7.1.0",
"svg2pdf.js": "2.2.3"
},
"devDependencies": {
"@babel/core": "7.23.5",
"@babel/preset-env": "7.23.5",
"@babel/register": "7.22.15",
"@babel/runtime-corejs3": "7.23.5",
"@cypress/code-coverage": "3.12.13",
"@babel/core": "7.23.7",
"@babel/preset-env": "7.23.8",
"@babel/register": "7.23.7",
"@babel/runtime-corejs3": "7.23.8",
"@cypress/code-coverage": "3.12.18",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-dynamic-import-vars": "2.1.2",
@ -112,23 +112,23 @@
"@web/dev-server-rollup": "0.6.1",
"babel-plugin-istanbul": "^6.1.1",
"babel-plugin-transform-object-rest-spread": "7.0.0-beta.3",
"core-js-bundle": "3.34.0",
"core-js-bundle": "3.35.0",
"cp-cli": "2.0.0",
"cypress": "13.6.1",
"cypress": "13.6.3",
"cypress-multi-reporters": "1.6.4",
"jamilih": "0.58.2",
"jsdoc": "4.0.2",
"node-static": "0.7.11",
"npm-run-all": "4.1.5",
"nyc": "15.1.0",
"open-cli": "7.2.0",
"open-cli": "8.0.0",
"promise-fs": "2.1.1",
"qr-manipulation": "0.7.0",
"query-result": "1.0.5",
"remark-cli": "12.0.0",
"remark-lint-ordered-list-marker-value": "3.1.2",
"rimraf": "5.0.5",
"rollup": "4.7.0",
"rollup": "4.9.5",
"rollup-plugin-copy": "3.5.0",
"rollup-plugin-filesize": "10.0.0",
"rollup-plugin-html": "0.2.1",

View File

@ -96,6 +96,7 @@ class Editor extends EditorStartup {
'zh-CN',
'zh-TW'
]
const modKey = isMac() ? 'meta+' : 'ctrl+'
this.shortcuts = [
// Shortcuts not associated with buttons
@ -304,6 +305,14 @@ class Editor extends EditorStartup {
fn: () => {
this.pasteInCenter()
}
},
{
key: 'escape',
fn: () => {
if (this.enableToolCancel) {
this.cancelTool()
}
}
}
]
this.leftPanel = new LeftPanel(this)

View File

@ -122,6 +122,11 @@ class EditorStartup {
this.modeEvent = this.svgCanvas.modeEvent
document.addEventListener('modeChange', (evt) => this.modeListener(evt))
/** if true - selected tool can be cancelled with Esc key
* disables on dragging (mousedown) to avoid changing mode in the middle of drawing
*/
this.enableToolCancel = true
this.leftPanel.init()
this.bottomPanel.init()
this.topPanel.init()
@ -309,6 +314,7 @@ class EditorStartup {
return false
})
$id('svgcanvas').addEventListener('mousedown', (evt) => {
this.enableToolCancel = false
if (evt.button === 1 || keypan === true) {
// prDefault to avoid firing of browser's panning on mousewheel
evt.preventDefault()
@ -331,6 +337,7 @@ class EditorStartup {
})
window.addEventListener('mouseup', (evt) => {
this.enableToolCancel = true
if (evt.button === 1) {
this.svgCanvas.setMode(previousMode ?? 'select')
}
@ -747,12 +754,36 @@ class EditorStartup {
case 'zoom':
cs = 'crosshair'
break
case 'circle':
case 'ellipse':
case 'rect':
case 'square':
case 'star':
case 'polygon':
cs = `url("./images/cursors/${mode}_cursor.svg"), crosshair`
break
case 'text':
// #TODO: Cursor should be changed back to default after text element was created
cs = 'text'
break
default:
cs = 'auto'
}
this.workarea.style.cursor = cs
}
/**
* Listens for Esc key to be pressed to cancel active mode, sets mode to Select
*/
cancelTool () {
const mode = this.svgCanvas.getMode()
// list of modes that are currently save to cancel
const modesToCancel = ['zoom', 'rect', 'square', 'circle', 'ellipse', 'line', 'text', 'star', 'polygon', 'eyedropper']
if (modesToCancel.includes(mode)) {
this.leftPanel.clickSelect()
}
}
}
export default EditorStartup

View File

@ -3,9 +3,18 @@ import { t } from '../locale.js'
const template = document.createElement('template')
template.innerHTML = `
<style>
@keyframes btnHover {
from {
background-color: var(--main-bg-color);
}
to {
background-color: var(--icon-bg-color-hover);
}
}
:host(:hover) :not(.disabled)
{
background-color: var(--icon-bg-color-hover);
animation: btnHover 0.2s forwards;
}
div
{

View File

@ -39,10 +39,18 @@ export class FlyingButton extends HTMLElement {
:host {
position:relative;
}
.overall:hover *
{
@keyframes btnHover {
from {
background-color: transparent;
}
to {
background-color: var(--icon-bg-color-hover);
}
}
.overall .menu-button:hover {
animation: btnHover 0.2s forwards;
}
img {
border: none;
width: 24px;

View File

@ -44,6 +44,23 @@ template.innerHTML = `
margin-top: 2px;
margin-bottom: 1px;
}
#arrow-up, #arrow-down {
user-select: none;
}
@keyframes hover-arrows {
from {
background: transparent;
color: var(--icon-bg-color-hover);
}
to {
background: var(--icon-bg-color-hover);
color: var(--orange-color);
}
}
#arrow-up:hover, #arrow-down:hover {
animation: hover-arrows 0.2s forwards;
}
#down{
width:18px;
height:23px;

View File

@ -83,6 +83,7 @@ export default {
callback () {
// Add the button and its handler(s)
const title = `${name}:buttons.0.title`
// #TODO: Come up with another shortcut (?) because 'I' is reserved for italic
const key = `${name}:buttons.0.key`
const buttonTemplate = `
<se-button id="tool_eyedropper" title="${title}" src="eye_dropper.svg" shortcut=${key}></se-button>

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<svg width="20" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<title>circle_cursor</title>
<g class="layer">
<title>Layer 1</title>
<g id="layer2">
<path d="m0.1,7.76l7.7,-7.66l-7.7,0l0,7.66z" fill="#2b3c45" id="path9" stroke="#2b3c45"
stroke-dashoffset="0" stroke-width="0.19" />
</g>
<circle cx="9.44" cy="9.06" display="inline" fill="#ffffff" id="path10" r="5.98" stroke="#2b3c45"
stroke-dashoffset="0" stroke-width="1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 599 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<svg width="20" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<title>circle_cursor</title>
<g class="layer">
<title>Layer 1</title>
<g class="layer" id="svg_2">
<g id="layer2">
<path d="m0.1,6.37l6.3,-6.27l-6.3,0l0,6.27z" fill="#2b3c45" id="path9" stroke="#2b3c45"
stroke-dashoffset="0" stroke-width="0.2" />
</g>
<ellipse cx="11.0" cy="7" fill="#ffffff" id="svg_1" rx="7.5" ry="3.43" stroke="#2b3c45" stroke-width="1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 623 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<svg width="20" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<title>circle_cursor</title>
<g class="layer">
<title>Layer 1</title>
<g class="layer" id="svg_2">
<path d="m0.1,6.37l6.3,-6.27l-6.3,0l0,6.27z" fill="#2b3c45" id="path9" stroke="#2b3c45"
stroke-dashoffset="0" stroke-width="0.2" />
</g>
<rect fill="#ffffff" height="6.4" id="svg_3" stroke="#2b3c45" width="13.6" x="4.54" y="4.53" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 556 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<svg width="20" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<title>circle_cursor</title>
<g class="layer">
<title>Layer 1</title>
<g class="layer" id="svg_2">
<g id="layer2">
<path d="m0.1,6.37l6.3,-6.27l-6.3,0l0,6.27z" fill="#2b3c45" id="path9" stroke="#2b3c45" stroke-dashoffset="0" stroke-width="0.2"/>
</g>
</g>
<rect fill="#ffffff" height="8" id="svg_3" stroke="#2b3c45" width="8" x="5.04" y="4.43"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 517 B

View File

@ -764,25 +764,38 @@ class TopPanel {
}
}
/**
* Checks if there are currently selected text elements to avoid firing of bold,italic when no text selected
* @returns {boolean}
*/
get anyTextSelected () {
const selected = this.editor.svgCanvas.getSelectedElements()
return selected.filter(el => el.tagName === 'text').length > 0
}
/**
*
* @returns {false}
*/
clickBold () {
if (this.anyTextSelected) {
this.editor.svgCanvas.setBold(!this.editor.svgCanvas.getBold())
this.updateContextPanel()
return false
}
}
/**
*
* @returns {false}
*/
clickItalic () {
if (this.anyTextSelected) {
this.editor.svgCanvas.setItalic(!this.editor.svgCanvas.getItalic())
this.updateContextPanel()
return false
}
}
/**
* Handles the click on the text decoration buttons

View File

@ -8,6 +8,7 @@
--icon-bg-color: #72797A;
--icon-bg-color-hover: #2B3C45;
--input-color: #B2B2B2;
--orange-color: #f9bc01;
--global-se-spin-input-width: 82px;
}