ngx-vflow 0.2.1 → 0.3.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/esm2022/lib/vflow/components/edge-label/edge-label.component.mjs +13 -10
- package/esm2022/lib/vflow/components/handle/handle.component.mjs +26 -28
- package/esm2022/lib/vflow/components/node/node.component.mjs +58 -25
- package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +10 -4
- package/esm2022/lib/vflow/decorators/microtask.decorator.mjs +11 -0
- package/esm2022/lib/vflow/decorators/run-in-injection-context.decorator.mjs +26 -0
- package/esm2022/lib/vflow/directives/handle-size-controller.directive.mjs +38 -0
- package/esm2022/lib/vflow/models/edge.model.mjs +36 -6
- package/esm2022/lib/vflow/models/handle.model.mjs +36 -5
- package/esm2022/lib/vflow/models/node.model.mjs +3 -22
- package/esm2022/lib/vflow/services/edge-changes.service.mjs +14 -7
- package/esm2022/lib/vflow/services/flow-entities.service.mjs +5 -2
- package/esm2022/lib/vflow/services/handle.service.mjs +10 -4
- package/esm2022/lib/vflow/services/node-changes.service.mjs +6 -3
- package/esm2022/lib/vflow/types/edge-change.type.mjs +1 -1
- package/esm2022/lib/vflow/utils/add-nodes-to-edges.mjs +3 -3
- package/esm2022/lib/vflow/utils/resizable.mjs +11 -0
- package/esm2022/lib/vflow/vflow.module.mjs +5 -2
- package/fesm2022/ngx-vflow.mjs +317 -146
- package/fesm2022/ngx-vflow.mjs.map +1 -1
- package/lib/vflow/components/handle/handle.component.d.ts +8 -4
- package/lib/vflow/components/node/node.component.d.ts +7 -6
- package/lib/vflow/components/vflow/vflow.component.d.ts +6 -2
- package/lib/vflow/decorators/microtask.decorator.d.ts +1 -0
- package/lib/vflow/decorators/run-in-injection-context.decorator.d.ts +8 -0
- package/lib/vflow/directives/handle-size-controller.directive.d.ts +10 -0
- package/lib/vflow/models/edge.model.d.ts +21 -3
- package/lib/vflow/models/handle.model.d.ts +33 -0
- package/lib/vflow/models/node.model.d.ts +2 -3
- package/lib/vflow/services/flow-entities.service.d.ts +1 -0
- package/lib/vflow/services/handle.service.d.ts +8 -10
- package/lib/vflow/types/edge-change.type.d.ts +3 -0
- package/lib/vflow/utils/resizable.d.ts +3 -0
- package/lib/vflow/vflow.module.d.ts +4 -3
- package/package.json +1 -1
package/fesm2022/ngx-vflow.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as i1 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { signal, Injectable, inject, ElementRef, Directive, effect, Input, TemplateRef, computed, EventEmitter, Output, untracked, Injector,
|
|
4
|
+
import { signal, Injectable, inject, ElementRef, Directive, effect, Input, TemplateRef, computed, EventEmitter, Output, untracked, runInInjectionContext, Injector, NgZone, Component, ChangeDetectionStrategy, ViewChild, HostListener, ContentChild, NgModule } from '@angular/core';
|
|
5
5
|
import { select } from 'd3-selection';
|
|
6
6
|
import { zoomIdentity, zoom } from 'd3-zoom';
|
|
7
7
|
import { drag } from 'd3-drag';
|
|
8
8
|
import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
9
|
-
import { switchMap, merge, skip, map, pairwise, filter, observeOn, asyncScheduler, tap, fromEvent } from 'rxjs';
|
|
9
|
+
import { switchMap, merge, skip, map, pairwise, filter, observeOn, asyncScheduler, zip, distinctUntilChanged, tap, Subject, Observable, Subscription, startWith, fromEvent } from 'rxjs';
|
|
10
10
|
import { path } from 'd3-path';
|
|
11
|
+
import { __decorate } from 'tslib';
|
|
11
12
|
|
|
12
13
|
class ViewportService {
|
|
13
14
|
constructor() {
|
|
@@ -237,8 +238,8 @@ function addNodesToEdges(nodes, edges) {
|
|
|
237
238
|
return acc;
|
|
238
239
|
}, {});
|
|
239
240
|
edges.forEach(e => {
|
|
240
|
-
e.source
|
|
241
|
-
e.target
|
|
241
|
+
e.source.set(nodesById[e.edge.source]);
|
|
242
|
+
e.target.set(nodesById[e.edge.target]);
|
|
242
243
|
});
|
|
243
244
|
}
|
|
244
245
|
|
|
@@ -344,12 +345,15 @@ class FlowEntitiesService {
|
|
|
344
345
|
});
|
|
345
346
|
this.validEdges = computed(() => {
|
|
346
347
|
const nodes = this.nodes();
|
|
347
|
-
return this.edges().filter(e => nodes.includes(e.source) && nodes.includes(e.target));
|
|
348
|
+
return this.edges().filter(e => nodes.includes(e.source()) && nodes.includes(e.target()));
|
|
348
349
|
});
|
|
349
350
|
}
|
|
350
351
|
getNode(id) {
|
|
351
352
|
return this.nodes().find(({ node }) => node.id === id);
|
|
352
353
|
}
|
|
354
|
+
getDetachedEdges() {
|
|
355
|
+
return this.edges().filter(e => e.detached());
|
|
356
|
+
}
|
|
353
357
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowEntitiesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
354
358
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowEntitiesService }); }
|
|
355
359
|
}
|
|
@@ -399,52 +403,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
399
403
|
type: Output
|
|
400
404
|
}] } });
|
|
401
405
|
|
|
402
|
-
class HandleModel {
|
|
403
|
-
constructor(rawHandle, parentNode) {
|
|
404
|
-
this.rawHandle = rawHandle;
|
|
405
|
-
this.parentNode = parentNode;
|
|
406
|
-
this.strokeWidth = 2;
|
|
407
|
-
this.size = signal({
|
|
408
|
-
width: 10 + (2 * this.strokeWidth),
|
|
409
|
-
height: 10 + (2 * this.strokeWidth)
|
|
410
|
-
});
|
|
411
|
-
this.offset = computed(() => {
|
|
412
|
-
switch (this.rawHandle.position) {
|
|
413
|
-
case 'left': return {
|
|
414
|
-
x: 0,
|
|
415
|
-
y: this.rawHandle.parentPosition().y + (this.rawHandle.parentSize().height / 2)
|
|
416
|
-
};
|
|
417
|
-
case 'right': return {
|
|
418
|
-
x: this.parentNode.size().width,
|
|
419
|
-
y: this.rawHandle.parentPosition().y + (this.rawHandle.parentSize().height / 2)
|
|
420
|
-
};
|
|
421
|
-
case 'top': return {
|
|
422
|
-
x: this.rawHandle.parentPosition().x + (this.rawHandle.parentSize().width / 2),
|
|
423
|
-
y: 0
|
|
424
|
-
};
|
|
425
|
-
case 'bottom': return {
|
|
426
|
-
x: this.rawHandle.parentPosition().x + this.rawHandle.parentSize().width / 2,
|
|
427
|
-
y: this.parentNode.size().height
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
this.sizeOffset = computed(() => {
|
|
432
|
-
switch (this.rawHandle.position) {
|
|
433
|
-
case 'left': return { x: -(this.size().width / 2), y: 0 };
|
|
434
|
-
case 'right': return { x: this.size().width / 2, y: 0 };
|
|
435
|
-
case 'top': return { x: 0, y: -(this.size().height / 2) };
|
|
436
|
-
case 'bottom': return { x: 0, y: this.size().height / 2 };
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
this.pointAbsolute = computed(() => {
|
|
440
|
-
return {
|
|
441
|
-
x: this.parentNode.point().x + this.offset().x + this.sizeOffset().x,
|
|
442
|
-
y: this.parentNode.point().y + this.offset().y + this.sizeOffset().y,
|
|
443
|
-
};
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
406
|
class NodeModel {
|
|
449
407
|
constructor(node) {
|
|
450
408
|
this.node = node;
|
|
@@ -455,26 +413,8 @@ class NodeModel {
|
|
|
455
413
|
// Now source and handle positions derived from parent flow
|
|
456
414
|
this.sourcePosition = computed(() => this.flow.handlePositions().source);
|
|
457
415
|
this.targetPosition = computed(() => this.flow.handlePositions().target);
|
|
458
|
-
this.handles =
|
|
459
|
-
|
|
460
|
-
return this.rawHandles().map((handle => new HandleModel(handle, this)));
|
|
461
|
-
}
|
|
462
|
-
return [
|
|
463
|
-
new HandleModel({
|
|
464
|
-
position: this.sourcePosition(),
|
|
465
|
-
type: 'source',
|
|
466
|
-
parentPosition: signal({ x: 0, y: 0 }),
|
|
467
|
-
parentSize: signal(this.size())
|
|
468
|
-
}, this),
|
|
469
|
-
new HandleModel({
|
|
470
|
-
position: this.targetPosition(),
|
|
471
|
-
type: 'target',
|
|
472
|
-
parentPosition: signal({ x: 0, y: 0 }),
|
|
473
|
-
parentSize: signal(this.size())
|
|
474
|
-
}, this),
|
|
475
|
-
];
|
|
476
|
-
});
|
|
477
|
-
this.rawHandles = signal([]);
|
|
416
|
+
this.handles = signal([]);
|
|
417
|
+
this.handles$ = toObservable(this.handles);
|
|
478
418
|
this.draggable = true;
|
|
479
419
|
// disabled for configuration for now
|
|
480
420
|
this.magnetRadius = 20;
|
|
@@ -605,23 +545,52 @@ function getPointOnBezier(sourcePoint, targetPoint, controlPoint1, controlPoint2
|
|
|
605
545
|
class EdgeModel {
|
|
606
546
|
constructor(edge) {
|
|
607
547
|
this.edge = edge;
|
|
548
|
+
this.source = signal(undefined);
|
|
549
|
+
this.target = signal(undefined);
|
|
550
|
+
this.detached = computed(() => {
|
|
551
|
+
const source = this.source();
|
|
552
|
+
const target = this.target();
|
|
553
|
+
if (!source || !target) {
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
let existsSourceHandle = false;
|
|
557
|
+
let existsTargetHandle = false;
|
|
558
|
+
if (this.edge.sourceHandle) {
|
|
559
|
+
existsSourceHandle = !!source.handles()
|
|
560
|
+
.find(handle => handle.rawHandle.id === this.edge.sourceHandle);
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
existsSourceHandle = !!source.handles()
|
|
564
|
+
.find(handle => handle.rawHandle.type === 'source');
|
|
565
|
+
}
|
|
566
|
+
if (this.edge.targetHandle) {
|
|
567
|
+
existsTargetHandle = !!target.handles()
|
|
568
|
+
.find(handle => handle.rawHandle.id === this.edge.targetHandle);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
existsTargetHandle = !!target.handles()
|
|
572
|
+
.find(handle => handle.rawHandle.type === 'target');
|
|
573
|
+
}
|
|
574
|
+
return !existsSourceHandle || !existsTargetHandle;
|
|
575
|
+
});
|
|
576
|
+
this.detached$ = toObservable(this.detached);
|
|
608
577
|
this.path = computed(() => {
|
|
609
578
|
let source;
|
|
610
579
|
if (this.edge.sourceHandle) {
|
|
611
|
-
source = this.source
|
|
580
|
+
source = this.source()?.handles()
|
|
612
581
|
.find(handle => handle.rawHandle.id === this.edge.sourceHandle);
|
|
613
582
|
}
|
|
614
583
|
else {
|
|
615
|
-
source = this.source
|
|
584
|
+
source = this.source()?.handles()
|
|
616
585
|
.find(handle => handle.rawHandle.type === 'source');
|
|
617
586
|
}
|
|
618
587
|
let target;
|
|
619
588
|
if (this.edge.targetHandle) {
|
|
620
|
-
target = this.target
|
|
589
|
+
target = this.target()?.handles()
|
|
621
590
|
.find(handle => handle.rawHandle.id === this.edge.targetHandle);
|
|
622
591
|
}
|
|
623
592
|
else {
|
|
624
|
-
target = this.target
|
|
593
|
+
target = this.target()?.handles()
|
|
625
594
|
.find(handle => handle.rawHandle.type === 'target');
|
|
626
595
|
}
|
|
627
596
|
// TODO: don't like this
|
|
@@ -702,7 +671,10 @@ class NodesChangeService {
|
|
|
702
671
|
.pipe(pairwise(), map(([oldList, newList]) => newList.filter(node => !oldList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map(node => ({ type: 'add', id: node.node.id }))));
|
|
703
672
|
this.nodeRemoveChange$ = toObservable(this.entitiesService.nodes)
|
|
704
673
|
.pipe(pairwise(), map(([oldList, newList]) => oldList.filter(node => !newList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map(node => ({ type: 'remove', id: node.node.id }))));
|
|
705
|
-
this.changes$ = merge(this.nodesPositionChange$, this.nodeAddChange$, this.nodeRemoveChange$)
|
|
674
|
+
this.changes$ = merge(this.nodesPositionChange$, this.nodeAddChange$, this.nodeRemoveChange$).pipe(
|
|
675
|
+
// this fixes a bug when on fire node event change,
|
|
676
|
+
// you can't get valid list of detached edges
|
|
677
|
+
observeOn(asyncScheduler));
|
|
706
678
|
}
|
|
707
679
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
708
680
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService }); }
|
|
@@ -711,16 +683,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
711
683
|
type: Injectable
|
|
712
684
|
}] });
|
|
713
685
|
|
|
686
|
+
const haveSameContents = (a, b) => a.length === b.length &&
|
|
687
|
+
[...new Set([...a, ...b])].every(v => a.filter(e => e === v).length === b.filter(e => e === v).length);
|
|
714
688
|
class EdgeChangesService {
|
|
715
689
|
constructor() {
|
|
716
690
|
this.entitiesService = inject(FlowEntitiesService);
|
|
717
|
-
this.edgeDetachedChange$ = toObservable(computed(() => {
|
|
691
|
+
this.edgeDetachedChange$ = merge(toObservable(computed(() => {
|
|
718
692
|
const nodes = this.entitiesService.nodes();
|
|
719
693
|
const edges = untracked(this.entitiesService.edges);
|
|
720
|
-
return edges.filter(({ source, target }) => !nodes.includes(source) || !nodes.includes(target));
|
|
721
|
-
})).pipe(
|
|
722
|
-
|
|
723
|
-
|
|
694
|
+
return edges.filter(({ source, target }) => !nodes.includes(source()) || !nodes.includes(target()));
|
|
695
|
+
})), toObservable(this.entitiesService.edges).pipe(switchMap((edges) => {
|
|
696
|
+
return zip(...edges.map(e => e.detached$.pipe(map(() => e))));
|
|
697
|
+
}), map((edges) => edges.filter(e => e.detached())),
|
|
698
|
+
// TODO check why there are 2 emits
|
|
699
|
+
skip(2))).pipe(
|
|
700
|
+
// here we check if 2 approaches to detect detached edges emits same
|
|
701
|
+
// and same values (this may happen on node delete)
|
|
702
|
+
distinctUntilChanged(haveSameContents), filter(edges => !!edges.length), map((edges) => edges.map(({ edge }) => ({ type: 'detached', id: edge.id }))));
|
|
724
703
|
this.edgeAddChange$ = toObservable(this.entitiesService.edges)
|
|
725
704
|
.pipe(pairwise(), map(([oldList, newList]) => {
|
|
726
705
|
return newList.filter(edge => !oldList.includes(edge));
|
|
@@ -780,13 +759,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
780
759
|
|
|
781
760
|
class HandleService {
|
|
782
761
|
constructor() {
|
|
783
|
-
this.
|
|
762
|
+
this.node = signal(null);
|
|
784
763
|
}
|
|
785
764
|
createHandle(newHandle) {
|
|
786
|
-
this.
|
|
765
|
+
const node = this.node();
|
|
766
|
+
if (node) {
|
|
767
|
+
node.handles.update(handles => [...handles, newHandle]);
|
|
768
|
+
}
|
|
787
769
|
}
|
|
788
770
|
destroyHandle(handleToDestoy) {
|
|
789
|
-
|
|
771
|
+
const node = this.node();
|
|
772
|
+
if (node) {
|
|
773
|
+
node.handles.update(handles => handles.filter(handle => handle !== handleToDestoy));
|
|
774
|
+
}
|
|
790
775
|
}
|
|
791
776
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
792
777
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleService }); }
|
|
@@ -795,41 +780,203 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
795
780
|
type: Injectable
|
|
796
781
|
}] });
|
|
797
782
|
|
|
798
|
-
class
|
|
783
|
+
class HandleModel {
|
|
784
|
+
constructor(rawHandle, parentNode) {
|
|
785
|
+
this.rawHandle = rawHandle;
|
|
786
|
+
this.parentNode = parentNode;
|
|
787
|
+
this.strokeWidth = 2;
|
|
788
|
+
/**
|
|
789
|
+
* Pre-computed size for default handle, changed dynamically
|
|
790
|
+
* for custom handles
|
|
791
|
+
*/
|
|
792
|
+
this.size = signal({
|
|
793
|
+
width: 10 + (2 * this.strokeWidth),
|
|
794
|
+
height: 10 + (2 * this.strokeWidth)
|
|
795
|
+
});
|
|
796
|
+
this.offset = computed(() => {
|
|
797
|
+
switch (this.rawHandle.position) {
|
|
798
|
+
case 'left': return {
|
|
799
|
+
x: 0,
|
|
800
|
+
y: this.parentPosition().y + (this.parentSize().height / 2)
|
|
801
|
+
};
|
|
802
|
+
case 'right': return {
|
|
803
|
+
x: this.parentNode.size().width,
|
|
804
|
+
y: this.parentPosition().y + (this.parentSize().height / 2)
|
|
805
|
+
};
|
|
806
|
+
case 'top': return {
|
|
807
|
+
x: this.parentPosition().x + (this.parentSize().width / 2),
|
|
808
|
+
y: 0
|
|
809
|
+
};
|
|
810
|
+
case 'bottom': return {
|
|
811
|
+
x: this.parentPosition().x + this.parentSize().width / 2,
|
|
812
|
+
y: this.parentNode.size().height
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
this.sizeOffset = computed(() => {
|
|
817
|
+
switch (this.rawHandle.position) {
|
|
818
|
+
case 'left': return { x: -(this.size().width / 2), y: 0 };
|
|
819
|
+
case 'right': return { x: this.size().width / 2, y: 0 };
|
|
820
|
+
case 'top': return { x: 0, y: -(this.size().height / 2) };
|
|
821
|
+
case 'bottom': return { x: 0, y: this.size().height / 2 };
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
this.pointAbsolute = computed(() => {
|
|
825
|
+
return {
|
|
826
|
+
x: this.parentNode.point().x + this.offset().x + this.sizeOffset().x,
|
|
827
|
+
y: this.parentNode.point().y + this.offset().y + this.sizeOffset().y,
|
|
828
|
+
};
|
|
829
|
+
});
|
|
830
|
+
this.state = signal('idle');
|
|
831
|
+
this.updateParentSizeAndPosition$ = new Subject();
|
|
832
|
+
this.parentSize = toSignal(this.updateParentSizeAndPosition$.pipe(map(() => ({
|
|
833
|
+
width: this.parentReference.offsetWidth,
|
|
834
|
+
height: this.parentReference.offsetHeight
|
|
835
|
+
}))), {
|
|
836
|
+
initialValue: { width: 0, height: 0 }
|
|
837
|
+
});
|
|
838
|
+
this.parentPosition = toSignal(this.updateParentSizeAndPosition$.pipe(map(() => ({
|
|
839
|
+
x: this.parentReference.offsetLeft,
|
|
840
|
+
y: this.parentReference.offsetTop
|
|
841
|
+
}))), {
|
|
842
|
+
initialValue: { x: 0, y: 0 }
|
|
843
|
+
});
|
|
844
|
+
this.parentReference = this.rawHandle.parentReference;
|
|
845
|
+
this.template = this.rawHandle.template;
|
|
846
|
+
this.templateContext = {
|
|
847
|
+
$implicit: {
|
|
848
|
+
point: this.offset,
|
|
849
|
+
state: this.state
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
updateParent() {
|
|
854
|
+
this.updateParentSizeAndPosition$.next();
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function resizable(elems, zone) {
|
|
859
|
+
return new Observable((subscriber) => {
|
|
860
|
+
let ro = new ResizeObserver((entries) => {
|
|
861
|
+
zone.run(() => subscriber.next(entries));
|
|
862
|
+
});
|
|
863
|
+
elems.forEach(e => ro.observe(e));
|
|
864
|
+
return () => ro.disconnect();
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function InjectionContext(target, key, descriptor) {
|
|
869
|
+
const originalMethod = descriptor.value;
|
|
870
|
+
descriptor.value = function (...args) {
|
|
871
|
+
if (this instanceof WithInjectorDirective) {
|
|
872
|
+
return runInInjectionContext(this.injector, () => originalMethod.apply(this, args));
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
throw new Error('Class that contains decorated method must extends WithInjectorDirective class');
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
// Return the modified descriptor
|
|
879
|
+
return descriptor;
|
|
880
|
+
}
|
|
881
|
+
class WithInjectorDirective {
|
|
799
882
|
constructor() {
|
|
800
|
-
this.handleService = inject(HandleService);
|
|
801
883
|
this.injector = inject(Injector);
|
|
884
|
+
}
|
|
885
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WithInjectorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
886
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: WithInjectorDirective, ngImport: i0 }); }
|
|
887
|
+
}
|
|
888
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WithInjectorDirective, decorators: [{
|
|
889
|
+
type: Directive
|
|
890
|
+
}] });
|
|
891
|
+
|
|
892
|
+
function Microtask(target, key, descriptor) {
|
|
893
|
+
const originalMethod = descriptor.value;
|
|
894
|
+
descriptor.value = function (...args) {
|
|
895
|
+
queueMicrotask(() => {
|
|
896
|
+
originalMethod?.apply(this, args);
|
|
897
|
+
});
|
|
898
|
+
};
|
|
899
|
+
// Return the modified descriptor
|
|
900
|
+
return descriptor;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
class HandleSizeControllerDirective {
|
|
904
|
+
constructor() {
|
|
905
|
+
this.handleWrapper = inject(ElementRef);
|
|
906
|
+
}
|
|
907
|
+
ngAfterViewInit() {
|
|
908
|
+
const element = this.handleWrapper.nativeElement;
|
|
909
|
+
const rect = element.getBBox();
|
|
910
|
+
const stroke = getChildStrokeWidth(element);
|
|
911
|
+
this.handleModel.size.set({
|
|
912
|
+
width: rect.width + stroke,
|
|
913
|
+
height: rect.height + stroke
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
917
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: { handleModel: ["handleSizeController", "handleModel"] }, ngImport: i0 }); }
|
|
918
|
+
}
|
|
919
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleSizeControllerDirective, decorators: [{
|
|
920
|
+
type: Directive,
|
|
921
|
+
args: [{ selector: '[handleSizeController]' }]
|
|
922
|
+
}], propDecorators: { handleModel: [{
|
|
923
|
+
type: Input,
|
|
924
|
+
args: [{ required: true, alias: 'handleSizeController' }]
|
|
925
|
+
}] } });
|
|
926
|
+
function getChildStrokeWidth(element) {
|
|
927
|
+
const child = element.firstElementChild;
|
|
928
|
+
if (child) {
|
|
929
|
+
const stroke = getComputedStyle(child).strokeWidth;
|
|
930
|
+
const strokeAsNumber = Number(stroke.replace('px', ''));
|
|
931
|
+
if (isNaN(strokeAsNumber)) {
|
|
932
|
+
return 0;
|
|
933
|
+
}
|
|
934
|
+
return strokeAsNumber;
|
|
935
|
+
}
|
|
936
|
+
return 0;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
class NodeComponent extends WithInjectorDirective {
|
|
940
|
+
constructor() {
|
|
941
|
+
super(...arguments);
|
|
942
|
+
this.handleService = inject(HandleService);
|
|
943
|
+
this.zone = inject(NgZone);
|
|
802
944
|
this.draggableService = inject(DraggableService);
|
|
803
945
|
this.flowStatusService = inject(FlowStatusService);
|
|
804
946
|
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
805
947
|
this.hostRef = inject(ElementRef);
|
|
806
948
|
this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
|
|
807
949
|
this.flowStatusService.status().state === 'connection-validation');
|
|
808
|
-
this.
|
|
809
|
-
this.targetHandleState = signal('idle');
|
|
950
|
+
this.subscription = new Subscription();
|
|
810
951
|
}
|
|
811
952
|
ngOnInit() {
|
|
812
|
-
|
|
813
|
-
effect(() => this.nodeModel.rawHandles.set(this.handleService.handles()), { allowSignalWrites: true });
|
|
814
|
-
});
|
|
953
|
+
this.handleService.node.set(this.nodeModel);
|
|
815
954
|
this.draggableService.toggleDraggable(this.hostRef.nativeElement, this.nodeModel);
|
|
955
|
+
const sub = this.nodeModel.handles$
|
|
956
|
+
.pipe(switchMap((handles) => resizable(handles.map(h => h.parentReference), this.zone)
|
|
957
|
+
.pipe(map(() => handles))), tap((handles) => handles.forEach(h => h.updateParent())))
|
|
958
|
+
.subscribe();
|
|
959
|
+
this.subscription.add(sub);
|
|
816
960
|
}
|
|
817
961
|
ngAfterViewInit() {
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
962
|
+
this.setInitialHandles();
|
|
963
|
+
if (this.nodeModel.node.type === 'default') {
|
|
964
|
+
const { width, height } = this.nodeContentRef.nativeElement.getBBox();
|
|
965
|
+
this.nodeModel.size.set({ width, height });
|
|
966
|
+
}
|
|
967
|
+
if (this.nodeModel.node.type === 'html-template') {
|
|
968
|
+
const sub = resizable([this.htmlWrapperRef.nativeElement], this.zone)
|
|
969
|
+
.pipe(startWith(null), tap(() => {
|
|
825
970
|
const width = this.htmlWrapperRef.nativeElement.clientWidth;
|
|
826
971
|
const height = this.htmlWrapperRef.nativeElement.clientHeight;
|
|
827
972
|
this.nodeModel.size.set({ width, height });
|
|
828
|
-
}
|
|
829
|
-
|
|
973
|
+
})).subscribe();
|
|
974
|
+
this.subscription.add(sub);
|
|
975
|
+
}
|
|
830
976
|
}
|
|
831
977
|
ngOnDestroy() {
|
|
832
978
|
this.draggableService.destroy(this.hostRef.nativeElement);
|
|
979
|
+
this.subscription.unsubscribe();
|
|
833
980
|
}
|
|
834
981
|
startConnection(event, handle) {
|
|
835
982
|
// ignore drag by stopping propagation
|
|
@@ -867,27 +1014,47 @@ class NodeComponent {
|
|
|
867
1014
|
sourceHandle: sourceHandle.rawHandle.id,
|
|
868
1015
|
targetHandle: targetHandle.rawHandle.id
|
|
869
1016
|
});
|
|
870
|
-
|
|
1017
|
+
targetHandle.state.set(valid ? 'valid' : 'invalid');
|
|
871
1018
|
this.flowStatusService.setConnectionValidationStatus(valid, sourceNode, targetNode, sourceHandle, targetHandle);
|
|
872
1019
|
}
|
|
873
1020
|
}
|
|
874
1021
|
/**
|
|
875
1022
|
* TODO srp
|
|
876
1023
|
*/
|
|
877
|
-
resetValidateTargetHandle() {
|
|
878
|
-
|
|
1024
|
+
resetValidateTargetHandle(targetHandle) {
|
|
1025
|
+
targetHandle.state.set('idle');
|
|
879
1026
|
// drop back to start status
|
|
880
1027
|
const status = this.flowStatusService.status();
|
|
881
1028
|
if (status.state === 'connection-validation') {
|
|
882
1029
|
this.flowStatusService.setConnectionStartStatus(status.payload.sourceNode, status.payload.sourceHandle);
|
|
883
1030
|
}
|
|
884
1031
|
}
|
|
885
|
-
|
|
886
|
-
|
|
1032
|
+
setInitialHandles() {
|
|
1033
|
+
if (this.nodeModel.node.type === 'default') {
|
|
1034
|
+
this.handleService.createHandle(new HandleModel({
|
|
1035
|
+
position: this.nodeModel.sourcePosition(),
|
|
1036
|
+
type: 'source',
|
|
1037
|
+
parentReference: this.htmlWrapperRef.nativeElement
|
|
1038
|
+
}, this.nodeModel));
|
|
1039
|
+
this.handleService.createHandle(new HandleModel({
|
|
1040
|
+
position: this.nodeModel.targetPosition(),
|
|
1041
|
+
type: 'target',
|
|
1042
|
+
parentReference: this.htmlWrapperRef.nativeElement
|
|
1043
|
+
}, this.nodeModel));
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1047
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeHtmlTemplate: "nodeHtmlTemplate" }, providers: [HandleService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n>\n <div #htmlWrapper class=\"default-node\" [innerHTML]=\"nodeModel.node.text ?? ''\"></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n stroke=\"white\"\n fill=\"black\"\n (mousedown)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (mouseup)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (mousedown)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (mouseup)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"handle.rawHandle.type === 'target' && showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (mouseup)=\"endConnection(); resetValidateTargetHandle(handle)\"\n (mouseover)=\"validateTargetHandle(handle)\"\n (mouseout)=\"resetValidateTargetHandle(handle)\"\n />\n</ng-container>\n\n", styles: [".wrapper{width:max-content}.magnet{opacity:0}.default-node{max-width:100px;max-height:100px;width:100px;height:50px;border:2px solid black;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
887
1048
|
}
|
|
1049
|
+
__decorate([
|
|
1050
|
+
Microtask
|
|
1051
|
+
], NodeComponent.prototype, "ngAfterViewInit", null);
|
|
1052
|
+
__decorate([
|
|
1053
|
+
InjectionContext
|
|
1054
|
+
], NodeComponent.prototype, "setInitialHandles", null);
|
|
888
1055
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
|
|
889
1056
|
type: Component,
|
|
890
|
-
args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService], template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n>\n <div class=\"default-node\" [innerHTML]=\"nodeModel.node.text ?? ''\"></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n
|
|
1057
|
+
args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService], template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n>\n <div #htmlWrapper class=\"default-node\" [innerHTML]=\"nodeModel.node.text ?? ''\"></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n stroke=\"white\"\n fill=\"black\"\n (mousedown)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (mouseup)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (mousedown)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (mouseup)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"handle.rawHandle.type === 'target' && showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (mouseup)=\"endConnection(); resetValidateTargetHandle(handle)\"\n (mouseover)=\"validateTargetHandle(handle)\"\n (mouseout)=\"resetValidateTargetHandle(handle)\"\n />\n</ng-container>\n\n", styles: [".wrapper{width:max-content}.magnet{opacity:0}.default-node{max-width:100px;max-height:100px;width:100px;height:50px;border:2px solid black;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}\n"] }]
|
|
891
1058
|
}], propDecorators: { nodeModel: [{
|
|
892
1059
|
type: Input
|
|
893
1060
|
}], nodeHtmlTemplate: [{
|
|
@@ -898,7 +1065,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
898
1065
|
}], htmlWrapperRef: [{
|
|
899
1066
|
type: ViewChild,
|
|
900
1067
|
args: ['htmlWrapper']
|
|
901
|
-
}] } });
|
|
1068
|
+
}], ngAfterViewInit: [], setInitialHandles: [] } });
|
|
902
1069
|
|
|
903
1070
|
class EdgeLabelComponent {
|
|
904
1071
|
constructor() {
|
|
@@ -919,14 +1086,12 @@ class EdgeLabelComponent {
|
|
|
919
1086
|
}
|
|
920
1087
|
set point(point) { this.pointSignal.set(point); }
|
|
921
1088
|
ngAfterViewInit() {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
this.model.size.set({ width, height });
|
|
929
|
-
});
|
|
1089
|
+
// this is a fix for visual artifact in chrome that for some reason adresses only for edge label.
|
|
1090
|
+
// the bug reproduces if edgeLabelWrapperRef size fully matched the size of parent foreignObject
|
|
1091
|
+
const MAGIC_VALUE_TO_FIX_GLITCH_IN_CHROME = 2;
|
|
1092
|
+
const width = this.edgeLabelWrapperRef.nativeElement.clientWidth + MAGIC_VALUE_TO_FIX_GLITCH_IN_CHROME;
|
|
1093
|
+
const height = this.edgeLabelWrapperRef.nativeElement.clientHeight + MAGIC_VALUE_TO_FIX_GLITCH_IN_CHROME;
|
|
1094
|
+
this.model.size.set({ width, height });
|
|
930
1095
|
}
|
|
931
1096
|
getLabelContext() {
|
|
932
1097
|
return {
|
|
@@ -939,6 +1104,9 @@ class EdgeLabelComponent {
|
|
|
939
1104
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
940
1105
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: EdgeLabelComponent, selector: "g[edgeLabel]", inputs: { model: "model", edgeModel: "edgeModel", point: "point", htmlTemplate: "htmlTemplate" }, viewQueries: [{ propertyName: "edgeLabelWrapperRef", first: true, predicate: ["edgeLabelWrapper"], descendants: true }], ngImport: i0, template: "<svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\"\n *ngIf=\"model.edgeLabel.type === 'html-template' && htmlTemplate\"\n>\n <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container\n *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\"\n />\n </div>\n</svg:foreignObject>\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
941
1106
|
}
|
|
1107
|
+
__decorate([
|
|
1108
|
+
Microtask
|
|
1109
|
+
], EdgeLabelComponent.prototype, "ngAfterViewInit", null);
|
|
942
1110
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeLabelComponent, decorators: [{
|
|
943
1111
|
type: Component,
|
|
944
1112
|
args: [{ selector: 'g[edgeLabel]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\"\n *ngIf=\"model.edgeLabel.type === 'html-template' && htmlTemplate\"\n>\n <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container\n *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\"\n />\n </div>\n</svg:foreignObject>\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"] }]
|
|
@@ -953,7 +1121,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
953
1121
|
}], edgeLabelWrapperRef: [{
|
|
954
1122
|
type: ViewChild,
|
|
955
1123
|
args: ['edgeLabelWrapper']
|
|
956
|
-
}] } });
|
|
1124
|
+
}], ngAfterViewInit: [] } });
|
|
957
1125
|
|
|
958
1126
|
class EdgeComponent {
|
|
959
1127
|
constructor() {
|
|
@@ -1277,7 +1445,7 @@ class VflowComponent {
|
|
|
1277
1445
|
* Edges to render
|
|
1278
1446
|
*/
|
|
1279
1447
|
set edges(newEdges) {
|
|
1280
|
-
const newModels = ReferenceKeeper.edges(newEdges, this.flowEntitiesService.edges());
|
|
1448
|
+
const newModels = runInInjectionContext(this.injector, () => ReferenceKeeper.edges(newEdges, this.flowEntitiesService.edges()));
|
|
1281
1449
|
// quick and dirty binding nodes to edges
|
|
1282
1450
|
addNodesToEdges(this.nodeModels, newModels);
|
|
1283
1451
|
this.flowEntitiesService.edges.set(newModels);
|
|
@@ -1316,12 +1484,18 @@ class VflowComponent {
|
|
|
1316
1484
|
getNode(id) {
|
|
1317
1485
|
return this.flowEntitiesService.getNode(id)?.node;
|
|
1318
1486
|
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Sync method to get detached edges
|
|
1489
|
+
*/
|
|
1490
|
+
getDetachedEdges() {
|
|
1491
|
+
return this.flowEntitiesService.getDetachedEdges().map(e => e.edge);
|
|
1492
|
+
}
|
|
1319
1493
|
// #endregion
|
|
1320
1494
|
trackNodes(idx, { node }) {
|
|
1321
|
-
return node
|
|
1495
|
+
return node;
|
|
1322
1496
|
}
|
|
1323
1497
|
trackEdges(idx, { edge }) {
|
|
1324
|
-
return edge
|
|
1498
|
+
return edge;
|
|
1325
1499
|
}
|
|
1326
1500
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1327
1501
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "16.2.12", type: VflowComponent, selector: "vflow", inputs: { view: "view", minZoom: "minZoom", maxZoom: "maxZoom", handlePositions: "handlePositions", background: "background", connection: ["connection", "connection", (settings) => new ConnectionModel(settings)], nodes: "nodes", edges: "edges" }, providers: [
|
|
@@ -1384,39 +1558,32 @@ function bindFlowToNodes(flow, nodes) {
|
|
|
1384
1558
|
nodes.forEach(n => n.bindFlow(flow));
|
|
1385
1559
|
}
|
|
1386
1560
|
|
|
1387
|
-
class HandleComponent {
|
|
1561
|
+
class HandleComponent extends WithInjectorDirective {
|
|
1388
1562
|
constructor() {
|
|
1563
|
+
super(...arguments);
|
|
1389
1564
|
this.handleService = inject(HandleService);
|
|
1390
1565
|
this.element = inject(ElementRef).nativeElement;
|
|
1391
1566
|
}
|
|
1392
1567
|
ngOnInit() {
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
this.
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
});
|
|
1568
|
+
this.model = new HandleModel({
|
|
1569
|
+
position: this.position,
|
|
1570
|
+
type: this.type,
|
|
1571
|
+
id: this.id,
|
|
1572
|
+
parentReference: this.element.parentElement,
|
|
1573
|
+
template: this.template
|
|
1574
|
+
}, this.handleService.node());
|
|
1575
|
+
this.handleService.createHandle(this.model);
|
|
1576
|
+
queueMicrotask(() => this.model.updateParent());
|
|
1403
1577
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
const fo = this.element.closest('foreignObject');
|
|
1407
|
-
const parent = this.element.parentElement;
|
|
1408
|
-
const foRect = fo.getBoundingClientRect();
|
|
1409
|
-
const parentRect = parent.getBoundingClientRect();
|
|
1410
|
-
return {
|
|
1411
|
-
x: parentRect.left - foRect.left,
|
|
1412
|
-
y: parentRect.top - foRect.top,
|
|
1413
|
-
width: parentRect.width,
|
|
1414
|
-
height: parentRect.height
|
|
1415
|
-
};
|
|
1578
|
+
ngOnDestroy() {
|
|
1579
|
+
this.handleService.destroyHandle(this.model);
|
|
1416
1580
|
}
|
|
1417
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, deps:
|
|
1418
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HandleComponent, selector: "handle", inputs: { position: "position", type: "type", id: "id" }, ngImport: i0, template: "" }); }
|
|
1581
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1582
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HandleComponent, selector: "handle", inputs: { position: "position", type: "type", id: "id", template: "template" }, usesInheritance: true, ngImport: i0, template: "" }); }
|
|
1419
1583
|
}
|
|
1584
|
+
__decorate([
|
|
1585
|
+
InjectionContext
|
|
1586
|
+
], HandleComponent.prototype, "ngOnInit", null);
|
|
1420
1587
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, decorators: [{
|
|
1421
1588
|
type: Component,
|
|
1422
1589
|
args: [{ selector: 'handle', template: "" }]
|
|
@@ -1428,7 +1595,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
1428
1595
|
args: [{ required: true }]
|
|
1429
1596
|
}], id: [{
|
|
1430
1597
|
type: Input
|
|
1431
|
-
}]
|
|
1598
|
+
}], template: [{
|
|
1599
|
+
type: Input
|
|
1600
|
+
}], ngOnInit: [] } });
|
|
1432
1601
|
|
|
1433
1602
|
const components = [
|
|
1434
1603
|
VflowComponent,
|
|
@@ -1444,6 +1613,7 @@ const directives = [
|
|
|
1444
1613
|
MapContextDirective,
|
|
1445
1614
|
RootSvgReferenceDirective,
|
|
1446
1615
|
RootSvgContextDirective,
|
|
1616
|
+
HandleSizeControllerDirective
|
|
1447
1617
|
];
|
|
1448
1618
|
const templateDirectives = [
|
|
1449
1619
|
NodeHtmlTemplateDirective,
|
|
@@ -1463,7 +1633,8 @@ class VflowModule {
|
|
|
1463
1633
|
DefsComponent, SpacePointContextDirective,
|
|
1464
1634
|
MapContextDirective,
|
|
1465
1635
|
RootSvgReferenceDirective,
|
|
1466
|
-
RootSvgContextDirective,
|
|
1636
|
+
RootSvgContextDirective,
|
|
1637
|
+
HandleSizeControllerDirective, NodeHtmlTemplateDirective,
|
|
1467
1638
|
EdgeLabelHtmlTemplateDirective,
|
|
1468
1639
|
EdgeTemplateDirective,
|
|
1469
1640
|
ConnectionTemplateDirective,
|