Migration to angular 11 + Better responsiveness + Better shortcuts

This commit is contained in:
Yann Armelin
2021-02-28 18:31:00 +01:00
parent e2611df71e
commit 01cad71533
12 changed files with 4236 additions and 4216 deletions

View File

@@ -30,11 +30,13 @@ Edit or create SVG paths in browser: https://yqnn.github.io/svg-path-editor/
- Convert all commands to relative or absolute coordinates with **Convert to relative** or **Convert to absolute** button
##### Shortcuts:
- Press **m**, **l**, **v**, **h**, **c**, **s**, **q**, **t**, **a** or **z** to insert a relative command after the selected one
- Press **shift** + **m**, **l**, **v**, **h**, **c**, **s**, **q**, **t**, **a** or **z** to insert an absolute command after the selected one
- Press **m**, **l**, **v**, **h**, **c**, **s**, **q**, **t**, **a** or **z** to insert a command after the selected one
- Press **shift** + **m**, **l**, **v**, **h**, **c**, **s**, **q**, **t**, **a** or **z** to convert selected command to a new type
- Press **echap** to delete the command being created, or the undo the current dragging operation
- Press **delete** or **backspace** to delete the selected command
- Press **ctrl** + **z** or **cmd** + **z** to undo
- Press **ctrl** + **shift** + **z** or **cmd** + **shift** + **z** to redo
- Press **ctrl** while dragging to ignore `snap to grid` constraint
## Running Locally

View File

@@ -40,7 +40,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,

8157
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,32 +11,32 @@
},
"private": true,
"dependencies": {
"@angular/animations": "~10.2.1",
"@angular/cdk": "^10.2.6",
"@angular/common": "~10.2.1",
"@angular/compiler": "~10.2.1",
"@angular/core": "~10.2.1",
"@angular/forms": "~10.2.1",
"@angular/material": "^10.2.6",
"@angular/platform-browser": "~10.2.1",
"@angular/platform-browser-dynamic": "~10.2.1",
"@angular/router": "~10.2.1",
"@angular/animations": "~11.2.3",
"@angular/cdk": "^11.2.2",
"@angular/common": "~11.2.3",
"@angular/compiler": "~11.2.3",
"@angular/core": "~11.2.3",
"@angular/forms": "~11.2.3",
"@angular/material": "^11.2.2",
"@angular/platform-browser": "~11.2.3",
"@angular/platform-browser-dynamic": "~11.2.3",
"@angular/router": "~11.2.3",
"rxjs": "~6.5.4",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1002.0",
"@angular/cli": "~10.2.0",
"@angular/compiler-cli": "~10.2.1",
"@angular/language-service": "~10.2.1",
"@angular-devkit/build-angular": "~0.1102.2",
"@angular/cli": "~11.2.2",
"@angular/compiler-cli": "~11.2.3",
"@angular/language-service": "~11.2.3",
"@types/node": "^12.11.1",
"@types/jasmine": "~3.5.0",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.5.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma": "~6.1.1",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",

View File

@@ -1,5 +1,10 @@
<div style="display:flex;flex-direction:row;height: 100%;" @leftColumnParent>
<div *ngIf="isLeftPanelOpened" @leftColumn (@leftColumn.done)="canvas.refreshCanvasSize()" style="width:300px">
<div *ngIf="isLeftPanelOpened" @leftColumn (@leftColumn.done)="canvas.refreshCanvasSize()" class="left-column-wrapper">
<button mat-raised-button class="left-column-close"
(click)="toggleLeftPanel()"
>
<mat-icon>arrow_left</mat-icon>
</button>
<div class="left-column" cdk-scrollable>
<app-expandable panelTitle="Path" [opened]="true">
<div class="row">
@@ -256,7 +261,13 @@
<div class="spacer"></div>
</div>
</div>
<div style="flex:1; align-self:stretch;position: relative;overflow:hidden" class="drawings">
<div class="drawings">
<button mat-raised-button class="left-column-open" *ngIf="!isLeftPanelOpened"
(click)="toggleLeftPanel()"
>
<mat-icon>arrow_right</mat-icon>
</button>
<button mat-mini-fab *ngIf="(!draggedPoint && focusedItem && !preview) || parsedPath.path.length===0"
color="primary"
class="add-button"
@@ -274,13 +285,6 @@
<mat-icon *ngIf="!focusedItem">add</mat-icon>
</button>
<button mat-raised-button
style="position: absolute; left:0; top:8px; border-radius: 0; min-width:16px; padding:8px 2px"
(click)="toggleLeftPanel()"
>
<mat-icon>{{isLeftPanelOpened?'arrow_left':'arrow_right'}}</mat-icon>
</button>
<div class="scene-top-actions">
<button mat-mini-fab
color="basic"
@@ -364,8 +368,9 @@
[tickInterval]="tickInterval"
(afertModelChange)="afertModelChange()"
(dragging)="setIsDragging($event)"
(canvasWidthChange)="canvasWidth = $event"
(canvasWidthChange)="canvasWidth = $event || canvasWidth"
(canvasHeightChange)="canvasHeight = $event"
(emptyCanvas)="isLeftPanelOpened = false"
(viewPort)="updateViewPort($event.x, $event.y, $event.w, $event.h, $event.force)"
></svg>
</div>

View File

@@ -3,6 +3,7 @@
height: 100%;
}
* {
box-sizing: border-box;
}
@@ -37,12 +38,30 @@
/* UI */
.left-column-wrapper {
width:300px;
position: relative;
}
.left-column {
width:300px;
overflow:auto;
background-color:#252526;
height: calc(100% - 28px);
}
.left-column-close, .left-column-open {
position: absolute;
top:8px;
border-radius: 0;
min-width:16px;
padding:8px 2px;
z-index:10;
}
.left-column-close {
right:-28px;
}
.left-column-open {
left:0;
}
input.svg-value {
width:38px;
@@ -84,6 +103,11 @@ div.dragged {
/* Drawings */
.drawings {
flex:1;
align-self:stretch;
position: relative;
overflow:hidden;
&:hover .add-button, .add-button.opened {
display: block;
}
@@ -113,4 +137,25 @@ div.dragged {
margin-top:8px;
}
}
}
@media screen and (max-width: 500px) {
.left-column {
width:100%;
}
.left-column-wrapper {
width:100% !important;
max-width:none !important;
}
.left-column-close, .left-column-open {
right:0;
}
.left-column-close {
top: unset;
bottom: 4px;
padding:2px 6px;
}
.footer {
width: 100%;
}
}

View File

@@ -104,8 +104,13 @@ export class AppComponent implements AfterViewInit {
this.undo();
$event.preventDefault();
} else if (!$event.metaKey && !$event.ctrlKey && /^[mlvhcsqtaz]$/i.test($event.key)) {
if (this.canInsertAfter(this.focusedItem, $event.key)) {
this.insert($event.key, this.focusedItem, false);
const isLower = $event.key === $event.key.toLowerCase();
const key = $event.key.toUpperCase();
if (isLower && this.canInsertAfter(this.focusedItem, key)) {
this.insert(key, this.focusedItem, false);
$event.preventDefault();
} else if (!isLower && this.canConvert(this.focusedItem, key)) {
this.insert(key, this.focusedItem, true);
$event.preventDefault();
}
} else if (!$event.metaKey && !$event.ctrlKey && $event.key === 'Escape') {
@@ -116,6 +121,12 @@ export class AppComponent implements AfterViewInit {
// stopDrag will unselect selected item if any
this.canvas.stopDrag();
}
$event.preventDefault();
} else if (!$event.metaKey && !$event.ctrlKey && ($event.key === 'Delete' || $event.key === 'Backspace')) {
if (this.focusedItem && this.canDelete(this.focusedItem)) {
this.delete(this.focusedItem);
$event.preventDefault();
}
}
}
}

View File

@@ -47,6 +47,8 @@ export class CanvasComponent implements OnInit, OnChanges, AfterViewInit {
@Output() viewPort = new EventEmitter<{x: number, y: number, w: number, h: number, force?: boolean}>();
@Output() emptyCanvas = new EventEmitter<void>();
_canvasWidth: number;
@Output() canvasWidthChange = new EventEmitter<number>();
@@ -84,10 +86,10 @@ export class CanvasComponent implements OnInit, OnChanges, AfterViewInit {
ngAfterViewInit() {
setTimeout(() => {
this.refreshCanvasSize();
this.refreshCanvasSize(true);
});
window.addEventListener('resize', () => {
this.refreshCanvasSize();
this.refreshCanvasSize(true);
});
}
@@ -131,8 +133,11 @@ export class CanvasComponent implements OnInit, OnChanges, AfterViewInit {
}
refreshCanvasSize() {
refreshCanvasSize(emitEmptyCanvas = false) {
const rect = this.canvas.nativeElement.parentNode.getBoundingClientRect();
if (rect.width === 0 && emitEmptyCanvas) {
this.emptyCanvas.emit();
}
this.canvasWidth = rect.width;
this.canvasHeight = rect.height;
@@ -250,8 +255,9 @@ export class CanvasComponent implements OnInit, OnChanges, AfterViewInit {
event.stopPropagation();
const pt = this.eventToLocation(event);
if (this.draggedPoint) {
pt.x = parseFloat(pt.x.toFixed(this.decimals));
pt.y = parseFloat(pt.y.toFixed(this.decimals));
const decimals = event.ctrlKey ? (this.decimals ? 0 : 3) : this.decimals;
pt.x = parseFloat(pt.x.toFixed(decimals));
pt.y = parseFloat(pt.y.toFixed(decimals));
this.parsedPath.setLocation(this.draggedPoint, pt as Point);
if (this.draggedIsNew) {
const previousIdx = this.parsedPath.path.indexOf(this.draggedPoint.itemReference) - 1;

View File

@@ -1,59 +1,69 @@
<h1 mat-dialog-title>Export as SVG</h1>
<div mat-dialog-content>
<div style="display:flex">
<div style="flex:1">
<div style="margin-bottom:8px">
<div class="export-content">
<div class="export-form">
<div class="style-form-title">
Style
</div>
<div style="display:flex">
<div style="margin-right: 32px;">
<div>
<mat-checkbox class="example-margin" [ngModel]="fill" (ngModelChange)="fill=$event;stroke=stroke||!$event">Fill</mat-checkbox>
</div>
<mat-form-field floatLabel="always" appearance="fill" style="width:121px; margin-right:8px">
<mat-label>Fill Color</mat-label>
<input matInput [(ngModel)]="fillColor" [disabled]="!fill">
</mat-form-field>
</div>
<div class="style-form-fill">
<div>
<div>
<mat-checkbox class="example-margin" [ngModel]="stroke" (ngModelChange)="stroke=$event;fill=fill||!$event">Stroke</mat-checkbox>
<mat-checkbox class="example-margin" [ngModel]="fill" (ngModelChange)="fill=$event;stroke=stroke||!$event">Fill</mat-checkbox>
</div>
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>Fill Color</mat-label>
<input matInput [(ngModel)]="fillColor" [disabled]="!fill">
</mat-form-field>
</div>
<div class="style-form-stroke">
<div>
<mat-checkbox class="example-margin" [ngModel]="stroke" (ngModelChange)="stroke=$event;fill=fill||!$event">Stroke</mat-checkbox>
</div>
<div class="style-form-stroke-fields">
<div class="style-form-stroke-field">
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>Stroke Color</mat-label>
<input matInput [(ngModel)]="strokeColor" [disabled]="!stroke">
</mat-form-field>
</div>
<div class="style-form-stroke-field">
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>Stroke width</mat-label>
<input matInput [(ngModel)]="strokeWidth" [disabled]="!stroke">
</mat-form-field>
</div>
<mat-form-field floatLabel="always" appearance="fill" style="width:121px; margin-right:8px">
<mat-label>Stroke Color</mat-label>
<input matInput [(ngModel)]="strokeColor" [disabled]="!stroke">
</mat-form-field>
<mat-form-field floatLabel="always" appearance="fill" style="width:121px;">
<mat-label>Stroke width</mat-label>
<input matInput [(ngModel)]="strokeWidth" [disabled]="!stroke">
</mat-form-field>
</div>
</div>
<div style="display: inline-block; position: relative;">
<div style="margin:8px 0">ViewBox</div>
<button mat-flat-button (click)="refreshViewbox()" color="basic" style="position: absolute;top:0;right:0;">Reset</button>
<mat-form-field floatLabel="always" appearance="fill" style="width:97px; margin-right:8px">
<div class="viewbox-form-title">
<div>ViewBox</div>
<button mat-flat-button (click)="refreshViewbox()" color="basic" class="reset-button">Reset</button>
</div>
<div class="viewbox-form-field">
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>X</mat-label>
<input matInput [(ngModel)]="x">
</mat-form-field>
<mat-form-field floatLabel="always" appearance="fill" style="width:97px; margin-right:8px">
</div>
<div class="viewbox-form-field">
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>Y</mat-label>
<input matInput [(ngModel)]="y">
</mat-form-field>
<mat-form-field floatLabel="always" appearance="fill" style="width:97px; margin-right:8px">
</div>
<div class="viewbox-form-field">
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>Width</mat-label>
<input matInput [(ngModel)]="width">
</mat-form-field>
<mat-form-field floatLabel="always" appearance="fill" style="width:97px;">
</div>
<div class="viewbox-form-field">
<mat-form-field floatLabel="always" appearance="fill">
<mat-label>Height</mat-label>
<input matInput [(ngModel)]="height">
</mat-form-field>
</div>
</div>
<div>
<div class="preview">
<svg [attr.viewBox]="x+' '+y+' '+width+' '+height"
style="margin:0 0 0 24px;"
width="300" height="300"
xmlns="http://www.w3.org/2000/svg"
>

View File

@@ -0,0 +1,73 @@
mat-form-field {
width:100%;
}
.export-content {
display:flex;
align-items: flex-start;
flex-wrap: wrap;
.export-form {
flex:1;
display:flex;
flex-wrap: wrap;
justify-content:space-between;
.style-form-title {
width: 100%;
margin-bottom:8px;
}
.style-form-fill {
width: calc(33% - 4px);
}
.style-form-stroke {
width: calc(67% - 4px);
.style-form-stroke-fields {
display: flex;
flex-wrap: wrap;
justify-content:space-between;
.style-form-stroke-field {
width: calc(50% - 4px);
}
}
}
.viewbox-form-title {
width: 100%;
margin:8px 0;
display:flex;
align-items: center;
div {
flex:1;
}
}
.viewbox-form-field {
width:calc( 25% - 6px);
}
}
.preview {
width:324px;
svg {
margin:0 0 0 24px;
}
}
}
@media screen and (max-width: 850px) {
.style-form-fill {
width: 100% !important;
}
.style-form-stroke {
width: 100% !important;
}
.viewbox-form-field {
width:calc( 50% - 4px) !important;
}
}
@media screen and (max-width: 700px) {
.preview {
width:100% !important;
svg {
width: 100% !important;
margin: 0 !important;
}
}
}

View File

@@ -11,6 +11,7 @@ class DialogData {
@Component({
selector: 'app-export-dialog',
templateUrl: 'export-dialog.component.html',
styleUrls: ['./export-dialog.component.scss']
})
export class ExportDialogComponent {
x = 0;
@@ -82,8 +83,7 @@ export class ExportDialogComponent {
@Component({
selector: 'app-export',
templateUrl: './export.component.html',
styleUrls: ['./export.component.css']
templateUrl: './export.component.html'
})
export class ExportComponent {
@Input() path: string;