ngx-svg-graphics 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ng-package.json +7 -0
- package/package.json +4 -21
- package/src/lib/components/arrows/arrow-between-boxes/arrow-between-boxes.component.css +0 -0
- package/src/lib/components/arrows/arrow-between-boxes/arrow-between-boxes.component.spec.ts +32 -0
- package/src/lib/components/arrows/arrow-between-boxes/arrow-between-boxes.component.svg +12 -0
- package/src/lib/components/arrows/arrow-between-boxes/arrow-between-boxes.component.ts +60 -0
- package/src/lib/components/arrows/arrow-between-elems/arrow-between-elems.component.css +0 -0
- package/src/lib/components/arrows/arrow-between-elems/arrow-between-elems.component.spec.ts +23 -0
- package/src/lib/components/arrows/arrow-between-elems/arrow-between-elems.component.svg +9 -0
- package/src/lib/components/arrows/arrow-between-elems/arrow-between-elems.component.ts +96 -0
- package/src/lib/components/arrows/arrow-between-points/arrow-between-points.component.css +0 -0
- package/src/lib/components/arrows/arrow-between-points/arrow-between-points.component.spec.ts +23 -0
- package/src/lib/components/arrows/arrow-between-points/arrow-between-points.component.svg +15 -0
- package/src/lib/components/arrows/arrow-between-points/arrow-between-points.component.ts +39 -0
- package/src/lib/components/draggable/draggable.component.css +0 -0
- package/src/lib/components/draggable/draggable.component.spec.ts +33 -0
- package/src/lib/components/draggable/draggable.component.svg +8 -0
- package/src/lib/components/draggable/draggable.component.ts +49 -0
- package/src/lib/components/rectangle/rectangle.component.css +0 -0
- package/src/lib/components/rectangle/rectangle.component.spec.ts +23 -0
- package/src/lib/components/rectangle/rectangle.component.svg +3 -0
- package/src/lib/components/rectangle/rectangle.component.ts +14 -0
- package/src/lib/components/text-area-svg/text-area-svg.component.css +0 -0
- package/src/lib/components/text-area-svg/text-area-svg.component.spec.ts +59 -0
- package/src/lib/components/text-area-svg/text-area-svg.component.svg +6 -0
- package/src/lib/components/text-area-svg/text-area-svg.component.ts +57 -0
- package/src/lib/models/arrow-style-configuration.ts +8 -0
- package/src/lib/models/bounding-box.ts +7 -0
- package/src/lib/models/dragger.spec.ts +9 -0
- package/src/lib/models/dragger.ts +54 -0
- package/{lib/models/identifiable.d.ts → src/lib/models/identifiable.ts} +1 -1
- package/src/lib/models/positionable.ts +8 -0
- package/src/lib/services/arrow-style-configuration.service.spec.ts +16 -0
- package/{lib/services/arrow-style-configuration.service.d.ts → src/lib/services/arrow-style-configuration.service.ts} +16 -7
- package/src/lib/services/svg-access.service.spec.ts +16 -0
- package/src/lib/services/svg-access.service.ts +35 -0
- package/src/lib/utils/path-layouter.spec.ts +7 -0
- package/src/lib/utils/path-layouter.ts +41 -0
- package/src/lib/utils/position-helper.spec.ts +140 -0
- package/src/lib/utils/position-helper.ts +45 -0
- package/src/lib/utils/text-distributor.spec.ts +96 -0
- package/src/lib/utils/text-distributor.ts +68 -0
- package/{public-api.d.ts → src/public-api.ts} +12 -5
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +15 -0
- package/fesm2022/ngx-svg-graphics.mjs +0 -541
- package/fesm2022/ngx-svg-graphics.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/lib/components/arrows/arrow-between-boxes/arrow-between-boxes.component.d.ts +0 -24
- package/lib/components/arrows/arrow-between-elems/arrow-between-elems.component.d.ts +0 -33
- package/lib/components/arrows/arrow-between-points/arrow-between-points.component.d.ts +0 -20
- package/lib/components/draggable/draggable.component.d.ts +0 -19
- package/lib/components/text-area-svg/text-area-svg.component.d.ts +0 -19
- package/lib/models/arrow-style-configuration.d.ts +0 -6
- package/lib/models/bounding-box.d.ts +0 -6
- package/lib/models/dragger.d.ts +0 -13
- package/lib/models/positionable.d.ts +0 -6
- package/lib/services/svg-access.service.d.ts +0 -13
- package/lib/utils/path-layouter.d.ts +0 -7
- package/lib/utils/position-helper.d.ts +0 -9
- package/lib/utils/text-distributor.d.ts +0 -7
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-svg-graphics",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Small svg library to link components or svg elements with arrows and allow to drag components.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"Drag",
|
|
7
|
-
"Arrows",
|
|
8
|
-
"SVG",
|
|
9
|
-
"Angular",
|
|
10
|
-
"EMF"
|
|
11
|
-
],
|
|
5
|
+
"keywords": ["Drag","Arrows", "SVG", "Angular", "EMF"],
|
|
12
6
|
"author": {
|
|
13
7
|
"name": "Susanne Göbel",
|
|
14
8
|
"email": "goebel@uni-koblenz.de"
|
|
@@ -25,16 +19,5 @@
|
|
|
25
19
|
"dependencies": {
|
|
26
20
|
"tslib": "^2.3.0"
|
|
27
21
|
},
|
|
28
|
-
"sideEffects": false
|
|
29
|
-
|
|
30
|
-
"typings": "index.d.ts",
|
|
31
|
-
"exports": {
|
|
32
|
-
"./package.json": {
|
|
33
|
-
"default": "./package.json"
|
|
34
|
-
},
|
|
35
|
-
".": {
|
|
36
|
-
"types": "./index.d.ts",
|
|
37
|
-
"default": "./fesm2022/ngx-svg-graphics.mjs"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
22
|
+
"sideEffects": false
|
|
23
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import {ArrowBetweenBoxesComponent} from './arrow-between-boxes.component';
|
|
4
|
+
import {NO_ERRORS_SCHEMA} from "@angular/core";
|
|
5
|
+
import {BoundingBox} from "../../../models/bounding-box";
|
|
6
|
+
|
|
7
|
+
describe('ArrowBetweenBoxesComponent', () => {
|
|
8
|
+
let component: ArrowBetweenBoxesComponent;
|
|
9
|
+
let fixture: ComponentFixture<ArrowBetweenBoxesComponent>;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
await TestBed.configureTestingModule({
|
|
13
|
+
imports: [ArrowBetweenBoxesComponent],
|
|
14
|
+
schemas: [NO_ERRORS_SCHEMA],
|
|
15
|
+
})
|
|
16
|
+
.compileComponents();
|
|
17
|
+
|
|
18
|
+
let start: BoundingBox = {x: 50, y: -20, w: 200, h: 80}
|
|
19
|
+
let end: BoundingBox = {x: 80, y: 20, w: 100, h: 200}
|
|
20
|
+
|
|
21
|
+
fixture = TestBed.createComponent(ArrowBetweenBoxesComponent);
|
|
22
|
+
component = fixture.componentInstance;
|
|
23
|
+
component.start = start
|
|
24
|
+
component.end = end
|
|
25
|
+
fixture.detectChanges();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should create', () => {
|
|
29
|
+
expect(component).toBeTruthy();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { AfterViewInit, ChangeDetectorRef, Component, Input, OnChanges,} from '@angular/core';
|
|
2
|
+
import { Point } from "@angular/cdk/drag-drop";
|
|
3
|
+
import {v4 as uuidv4} from "uuid";
|
|
4
|
+
import {BoundingBox} from "../../../models/bounding-box";
|
|
5
|
+
import {PathLayouter} from "../../../utils/path-layouter";
|
|
6
|
+
import {ArrowBetweenPointsComponent} from "../arrow-between-points/arrow-between-points.component";
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: '[arrow-between-boxes]',
|
|
10
|
+
templateUrl: './arrow-between-boxes.component.svg',
|
|
11
|
+
styleUrl: './arrow-between-boxes.component.css',
|
|
12
|
+
imports: [ArrowBetweenPointsComponent]
|
|
13
|
+
})
|
|
14
|
+
export class ArrowBetweenBoxesComponent implements OnChanges, AfterViewInit {
|
|
15
|
+
@Input() start!: BoundingBox;
|
|
16
|
+
@Input() end!: BoundingBox;
|
|
17
|
+
@Input() arrowType?: string;
|
|
18
|
+
@Input() text?: string;
|
|
19
|
+
@Input() style?: string;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
x1: number = 0;
|
|
23
|
+
y1: number = 0;
|
|
24
|
+
x2: number = 5;
|
|
25
|
+
y2: number = 5;
|
|
26
|
+
|
|
27
|
+
id = uuidv4();
|
|
28
|
+
|
|
29
|
+
positioned= false;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private cdr: ChangeDetectorRef,
|
|
33
|
+
) {}
|
|
34
|
+
|
|
35
|
+
ngAfterViewInit() {
|
|
36
|
+
this.computePositions()
|
|
37
|
+
this.positioned = true;
|
|
38
|
+
this.cdr.detectChanges();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ngOnChanges() {
|
|
42
|
+
if(this.positioned) {
|
|
43
|
+
this.computePositions()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
private computePositions() {
|
|
49
|
+
let res = PathLayouter.bestPoints(this.start, this.end);
|
|
50
|
+
this.applyBestPoints(res)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private applyBestPoints(res: Point[]) {
|
|
54
|
+
this.x1 = res[0].x;
|
|
55
|
+
this.y1 = res[0].y;
|
|
56
|
+
this.x2 = res[1].x;
|
|
57
|
+
this.y2 = res[1].y;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { ArrowBetweenElemsComponent } from './arrow-between-elems.component';
|
|
4
|
+
|
|
5
|
+
describe('ArrowBetweenElemsComponent', () => {
|
|
6
|
+
let component: ArrowBetweenElemsComponent;
|
|
7
|
+
let fixture: ComponentFixture<ArrowBetweenElemsComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [ArrowBetweenElemsComponent]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(ArrowBetweenElemsComponent);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AfterViewInit,
|
|
3
|
+
ChangeDetectorRef,
|
|
4
|
+
Component, ElementRef,
|
|
5
|
+
Input, OnChanges, OnDestroy,
|
|
6
|
+
OnInit, SimpleChanges,
|
|
7
|
+
ViewChild
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
import {Observable, Subscription} from "rxjs";
|
|
10
|
+
import { NgIf } from '@angular/common';
|
|
11
|
+
import {BoundingBox} from "../../../models/bounding-box";
|
|
12
|
+
import {ArrowBetweenBoxesComponent} from "../arrow-between-boxes/arrow-between-boxes.component";
|
|
13
|
+
import {SVGAccessService} from "../../../services/svg-access.service";
|
|
14
|
+
|
|
15
|
+
@Component({
|
|
16
|
+
selector: '[arrowElems]',
|
|
17
|
+
templateUrl: './arrow-between-elems.component.svg',
|
|
18
|
+
styleUrl: './arrow-between-elems.component.css',
|
|
19
|
+
imports: [NgIf, ArrowBetweenBoxesComponent]
|
|
20
|
+
})
|
|
21
|
+
export class ArrowBetweenElemsComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy {
|
|
22
|
+
|
|
23
|
+
@Input() startGID!: string;
|
|
24
|
+
@Input() startSuffix!: string;
|
|
25
|
+
@Input() endGID!: string;
|
|
26
|
+
@Input() endSuffix!: string;
|
|
27
|
+
@Input() arrowType?: string;
|
|
28
|
+
|
|
29
|
+
@Input() breaks: BoundingBox[] = [];
|
|
30
|
+
@Input() text?: string;
|
|
31
|
+
@Input() style?: string; //todo move into ArrowStyleConfig?
|
|
32
|
+
|
|
33
|
+
startId!: string;
|
|
34
|
+
endId!: string;
|
|
35
|
+
|
|
36
|
+
start?: BoundingBox;
|
|
37
|
+
end?: BoundingBox;
|
|
38
|
+
|
|
39
|
+
positioned= false;
|
|
40
|
+
@ViewChild('arrow') node!: ElementRef<SVGGraphicsElement>;
|
|
41
|
+
|
|
42
|
+
changeNotifier: Observable<string>;
|
|
43
|
+
changeSubscription: Subscription;
|
|
44
|
+
|
|
45
|
+
//idea: compute the two input positions as relative to the current elem
|
|
46
|
+
constructor(
|
|
47
|
+
private svgAccessService: SVGAccessService,
|
|
48
|
+
private cdr: ChangeDetectorRef) {
|
|
49
|
+
this.changeNotifier = this.svgAccessService.listenToPositionChange()
|
|
50
|
+
this.changeSubscription = this.changeNotifier.subscribe(nextString => {
|
|
51
|
+
if (nextString == this.startGID || nextString == this.endGID) {
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
this.computePositionsByIds()
|
|
54
|
+
this.cdr.detectChanges()
|
|
55
|
+
}, 0)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
ngOnInit() {
|
|
61
|
+
this.startId = this.startGID+this.startSuffix;
|
|
62
|
+
this.endId = this.endGID+this.endSuffix;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ngOnChanges(_: SimpleChanges) {
|
|
66
|
+
this.startId = this.startGID+this.startSuffix;
|
|
67
|
+
this.endId = this.endGID+this.endSuffix;
|
|
68
|
+
this.computePositionsByIds()
|
|
69
|
+
this.cdr.detectChanges()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
ngAfterViewInit() {
|
|
73
|
+
this.positioned = true;
|
|
74
|
+
this.computePositionsByIds()
|
|
75
|
+
this.cdr.detectChanges()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private computePositionsByIds() {
|
|
79
|
+
if (this.node?.nativeElement){
|
|
80
|
+
let rel = this.node.nativeElement as SVGGraphicsElement
|
|
81
|
+
let startOpt = this.svgAccessService.getRelativePosition(this.startId, rel)
|
|
82
|
+
if (startOpt) {
|
|
83
|
+
this.start = startOpt
|
|
84
|
+
}
|
|
85
|
+
let endOpt = this.svgAccessService.getRelativePosition(this.endId, rel)
|
|
86
|
+
if (endOpt) {
|
|
87
|
+
this.end = endOpt
|
|
88
|
+
}
|
|
89
|
+
} else console.log('No native element yet')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ngOnDestroy() {
|
|
93
|
+
this.changeSubscription.unsubscribe();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { ArrowBetweenPointsComponent } from './arrow-between-points.component';
|
|
4
|
+
|
|
5
|
+
describe('ArrowBetweenPointsComponent', () => {
|
|
6
|
+
let component: ArrowBetweenPointsComponent;
|
|
7
|
+
let fixture: ComponentFixture<ArrowBetweenPointsComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [ArrowBetweenPointsComponent]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(ArrowBetweenPointsComponent);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg:g>
|
|
2
|
+
<path [attr.id]="id+'-path'"
|
|
3
|
+
[attr.d]="'M '+startX+','+startY+' L '+endX+','+endY"
|
|
4
|
+
[attr.stroke]="arrowStyleConfiguration.color"
|
|
5
|
+
[attr.stroke-dasharray]="arrowStyleConfiguration.dashed"
|
|
6
|
+
[attr.marker-start]='"url(#"+arrowStyleConfiguration.startPointer+")"'
|
|
7
|
+
[attr.marker-end]='"url(#"+arrowStyleConfiguration.endPointer+")"'
|
|
8
|
+
style="{{style}}">
|
|
9
|
+
</path>
|
|
10
|
+
<text *ngIf="text">
|
|
11
|
+
<textPath [attr.href]="'#'+id+'-path'">
|
|
12
|
+
{{text}}
|
|
13
|
+
</textPath>
|
|
14
|
+
</text>
|
|
15
|
+
</svg:g>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {Component, Input, OnChanges} from '@angular/core';
|
|
2
|
+
import {NgIf} from "@angular/common";
|
|
3
|
+
import {ArrowStyleConfigurationService} from "../../../services/arrow-style-configuration.service";
|
|
4
|
+
import {ArrowStyleConfiguration} from "../../../models/arrow-style-configuration";
|
|
5
|
+
import {v4 as uuidv4} from "uuid";
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: '[arrow-between-points]',
|
|
9
|
+
imports: [
|
|
10
|
+
NgIf
|
|
11
|
+
],
|
|
12
|
+
templateUrl: './arrow-between-points.component.svg',
|
|
13
|
+
styleUrl: './arrow-between-points.component.css'
|
|
14
|
+
})
|
|
15
|
+
export class ArrowBetweenPointsComponent implements OnChanges {
|
|
16
|
+
|
|
17
|
+
@Input() startX!: number;
|
|
18
|
+
@Input() startY!: number;
|
|
19
|
+
@Input() endX!: number;
|
|
20
|
+
@Input() endY!: number;
|
|
21
|
+
@Input() text?: string;
|
|
22
|
+
@Input() style?: string;
|
|
23
|
+
|
|
24
|
+
@Input() arrowType?: string;
|
|
25
|
+
|
|
26
|
+
arrowStyleConfiguration: ArrowStyleConfiguration;
|
|
27
|
+
id = uuidv4();
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
private arrowStyleConfigService: ArrowStyleConfigurationService,) {
|
|
31
|
+
|
|
32
|
+
this.arrowStyleConfiguration = this.arrowStyleConfigService.styleArrow()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
ngOnChanges() {
|
|
36
|
+
this.arrowStyleConfiguration = this.arrowStyleConfigService.styleArrow(this.arrowType)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { DraggableComponent } from './draggable.component';
|
|
4
|
+
import {Draggable} from "../../models/positionable";
|
|
5
|
+
import {SVGAccessService} from "../../services/svg-access.service";
|
|
6
|
+
|
|
7
|
+
describe('DraggableComponent', () => {
|
|
8
|
+
let example = {$gId: "id", position: { x: 1, y: 2, w: 2, h: 1 }};
|
|
9
|
+
let component: DraggableComponent<Draggable>;
|
|
10
|
+
let fixture: ComponentFixture<DraggableComponent<Draggable>>;
|
|
11
|
+
|
|
12
|
+
class DraggableTestComponent<T extends Draggable> extends DraggableComponent<T> {
|
|
13
|
+
constructor(svgAccessService: SVGAccessService) {
|
|
14
|
+
super(svgAccessService);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
await TestBed.configureTestingModule({
|
|
20
|
+
imports: [DraggableComponent]
|
|
21
|
+
})
|
|
22
|
+
.compileComponents();
|
|
23
|
+
|
|
24
|
+
fixture = TestBed.createComponent(DraggableTestComponent<Draggable>);
|
|
25
|
+
component = fixture.componentInstance;
|
|
26
|
+
component.elem = example;
|
|
27
|
+
fixture.detectChanges();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should create', () => {
|
|
31
|
+
expect(component).toBeTruthy();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {AfterViewInit, Component, EventEmitter, Output} from '@angular/core';
|
|
2
|
+
import {SVGAccessService} from '../../services/svg-access.service';
|
|
3
|
+
import { Draggable } from '../../models/positionable';
|
|
4
|
+
import {Dragger} from "../../models/dragger";
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
imports: [],
|
|
8
|
+
selector: '[draggable]',
|
|
9
|
+
templateUrl: './draggable.component.svg',
|
|
10
|
+
styleUrl: './draggable.component.css'
|
|
11
|
+
})
|
|
12
|
+
export abstract class DraggableComponent<T extends Draggable> implements AfterViewInit {
|
|
13
|
+
|
|
14
|
+
@Output() chooseElem = new EventEmitter<T>();
|
|
15
|
+
//the caller must initialize both required elements (elem and elementDragger) either in the constructor
|
|
16
|
+
// (or if they are inputs) in the ngOnInit life cycle hook
|
|
17
|
+
elem!: T;
|
|
18
|
+
elemDragger!: Dragger<T>;
|
|
19
|
+
|
|
20
|
+
protected constructor(
|
|
21
|
+
protected svgAccessService: SVGAccessService
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
ngAfterViewInit() {
|
|
26
|
+
this.svgAccessService.notifyPositionChange(this.elem.$gId)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
startDrag(event: MouseEvent) {
|
|
30
|
+
this.elemDragger.startDrag(event);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
drag(event: MouseEvent) {
|
|
34
|
+
if (this.elemDragger.drag(event)) {
|
|
35
|
+
this.svgAccessService.notifyPositionChange(this.elem.$gId)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
endDrag(event: MouseEvent) {
|
|
40
|
+
this.elemDragger.endDrag(event);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
clickElem(event: MouseEvent) {
|
|
44
|
+
if (this.elemDragger.clickElem(event)) {
|
|
45
|
+
this.chooseElem.emit(this.elem);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { RectangleComponent } from './rectangle.component';
|
|
4
|
+
|
|
5
|
+
describe('RectangleComponent', () => {
|
|
6
|
+
let component: RectangleComponent;
|
|
7
|
+
let fixture: ComponentFixture<RectangleComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [RectangleComponent]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(RectangleComponent);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Component, Input} from '@angular/core';
|
|
2
|
+
import {BoundingBox} from "../../models/bounding-box";
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: '[rectangleG]',
|
|
6
|
+
imports: [],
|
|
7
|
+
templateUrl: './rectangle.component.svg',
|
|
8
|
+
styleUrl: './rectangle.component.css'
|
|
9
|
+
})
|
|
10
|
+
export class RectangleComponent {
|
|
11
|
+
@Input() position!: BoundingBox
|
|
12
|
+
@Input() color: string = '#ccffff';
|
|
13
|
+
|
|
14
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { TextAreaSvgComponent } from './text-area-svg.component';
|
|
4
|
+
|
|
5
|
+
describe('TextAreaSvgComponent', () => {
|
|
6
|
+
let component: TextAreaSvgComponent;
|
|
7
|
+
let fixture: ComponentFixture<TextAreaSvgComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [TextAreaSvgComponent]
|
|
12
|
+
}).compileComponents();
|
|
13
|
+
|
|
14
|
+
fixture = TestBed.createComponent(TextAreaSvgComponent);
|
|
15
|
+
component = fixture.componentInstance;
|
|
16
|
+
fixture.detectChanges();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should create', () => {
|
|
20
|
+
expect(component).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function initializeDistributedText() {
|
|
24
|
+
component.x = 20
|
|
25
|
+
component.y = 40
|
|
26
|
+
component.h = 50
|
|
27
|
+
component.w = 200
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function verifyDistributedText(text: string, distributedText: string[]) {
|
|
32
|
+
component.text = text;
|
|
33
|
+
component.distributeText()
|
|
34
|
+
expect(component.distributedText).toEqual(distributedText)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
it('Should set a two line distributed text', () => {
|
|
38
|
+
initializeDistributedText();
|
|
39
|
+
|
|
40
|
+
verifyDistributedText(
|
|
41
|
+
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam',
|
|
42
|
+
['Lorem ipsum dolor sit', 'amet, consetetur ...']
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('Should set a short distributed text after setting a longer one', () => {
|
|
47
|
+
initializeDistributedText();
|
|
48
|
+
|
|
49
|
+
verifyDistributedText(
|
|
50
|
+
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam',
|
|
51
|
+
['Lorem ipsum dolor sit', 'amet, consetetur ...']
|
|
52
|
+
) //first set two lines, next set a shorter one
|
|
53
|
+
verifyDistributedText(
|
|
54
|
+
'Short',
|
|
55
|
+
['Short']
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg:text [attr.x]="x" [attr.y]="y" [attr.width]="w" [attr.height]="h" dy="0" [attr.style]="" (click)="handleClick()">
|
|
2
|
+
<tspan dy="1.2em" [attr.x]="x" *ngFor="let line of distributedText">{{line}}</tspan>
|
|
3
|
+
</svg:text>
|
|
4
|
+
<svg:foreignObject *ngIf="isActive" [attr.x]="x" [attr.y]="y" [attr.width]="w" [attr.height]="h">
|
|
5
|
+
<input id="text-area" type="text" [(ngModel)]="this.text" (focusout)="leaveTextInput()"/>
|
|
6
|
+
</svg:foreignObject>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
EventEmitter,
|
|
4
|
+
Input,
|
|
5
|
+
OnChanges,
|
|
6
|
+
Output,
|
|
7
|
+
} from '@angular/core';
|
|
8
|
+
import { FormsModule } from '@angular/forms';
|
|
9
|
+
import { NgFor, NgIf } from '@angular/common';
|
|
10
|
+
import {TextDistributor} from "../../utils/text-distributor";
|
|
11
|
+
|
|
12
|
+
@Component({
|
|
13
|
+
selector: '[text-area-svg]',
|
|
14
|
+
templateUrl: './text-area-svg.component.svg',
|
|
15
|
+
styleUrl: './text-area-svg.component.css',
|
|
16
|
+
imports: [NgFor, NgIf, FormsModule]
|
|
17
|
+
})
|
|
18
|
+
export class TextAreaSvgComponent implements OnChanges {
|
|
19
|
+
/*
|
|
20
|
+
a fixed size svg. If the text exceeds the possible size, we will do a ... for now
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
@Input() text!: string
|
|
24
|
+
@Input() x!: number
|
|
25
|
+
@Input() y!: number
|
|
26
|
+
@Input() w!: number
|
|
27
|
+
@Input() h!: number
|
|
28
|
+
@Input() singleEdit: boolean = false
|
|
29
|
+
@Output() textChange = new EventEmitter<string>();
|
|
30
|
+
//only with singleEdit since that opens an overlay where one can change the text in place
|
|
31
|
+
|
|
32
|
+
distributedText: string[] = []
|
|
33
|
+
isActive: boolean = false;
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
ngOnChanges() {
|
|
37
|
+
this.distributeText();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
handleClick() {
|
|
41
|
+
if(this.singleEdit) {
|
|
42
|
+
this.isActive = true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
leaveTextInput() {
|
|
47
|
+
this.textChange.emit(this.text)
|
|
48
|
+
this.isActive = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
distributeText(){
|
|
52
|
+
this.distributedText = TextDistributor.distributeText(
|
|
53
|
+
this.text, this.w, this.h
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|