Migration to angular 11 + Better responsiveness + Better shortcuts
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
|
||||
8157
package-lock.json
generated
8157
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
73
src/app/export/export-dialog.component.scss
Normal file
73
src/app/export/export-dialog.component.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user