ngx-vflow 0.4.0 → 0.5.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.
@@ -1,10 +1,10 @@
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, computed, effect, Input, TemplateRef, EventEmitter, Output, untracked, runInInjectionContext, Injector, NgZone, Component, ChangeDetectionStrategy, ViewChild, HostListener, ContentChild, NgModule } from '@angular/core';
4
+ import { signal, Injectable, inject, ElementRef, Directive, computed, effect, Input, TemplateRef, EventEmitter, Output, untracked, runInInjectionContext, HostListener, Injector, NgZone, Component, ChangeDetectionStrategy, ViewChild, ContentChild, NgModule } from '@angular/core';
5
5
  import { select } from 'd3-selection';
6
6
  import { zoomIdentity, zoom } from 'd3-zoom';
7
- import { Subject, tap, switchMap, merge, skip, map, pairwise, filter, distinctUntilChanged, observeOn, asyncScheduler, zip, Observable, Subscription, startWith, fromEvent } from 'rxjs';
7
+ import { Subject, tap, switchMap, merge, skip, map, pairwise, filter, distinctUntilChanged, observeOn, asyncScheduler, zip, Observable, fromEvent, animationFrameScheduler, share, Subscription, startWith } from 'rxjs';
8
8
  import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
9
9
  import { drag } from 'd3-drag';
10
10
  import { path } from 'd3-path';
@@ -812,20 +812,50 @@ class ChangesControllerDirective {
812
812
  /**
813
813
  * Watch nodes change
814
814
  */
815
- this.onNodesChange = new EventEmitter();
815
+ this.onNodesChange = this.nodesChangeService.changes$;
816
+ this.onNodesChangePosition = this.nodeChangesOfType('position');
817
+ this.onNodesChangePositionSignle = this.singleChange(this.nodeChangesOfType('position'));
818
+ this.onNodesChangePositionMany = this.manyChanges(this.nodeChangesOfType('position'));
819
+ this.onNodesChangeAdd = this.nodeChangesOfType('add');
820
+ this.onNodesChangeAddSingle = this.singleChange(this.nodeChangesOfType('add'));
821
+ this.onNodesChangeAddMany = this.manyChanges(this.nodeChangesOfType('add'));
822
+ this.onNodesChangeRemove = this.nodeChangesOfType('remove');
823
+ this.onNodesChangeRemoveSingle = this.singleChange(this.nodeChangesOfType('remove'));
824
+ this.onNodesChangeRemoveMany = this.manyChanges(this.nodeChangesOfType('remove'));
825
+ this.onNodesChangeSelect = this.nodeChangesOfType('select');
826
+ this.onNodesChangeSelectSingle = this.singleChange(this.nodeChangesOfType('select'));
827
+ this.onNodesChangeSelectMany = this.manyChanges(this.nodeChangesOfType('select'));
816
828
  /**
817
- * Watch nodes change
829
+ * Watch edges change
818
830
  */
819
- this.onEdgesChange = new EventEmitter();
820
- this.nodesChangeProxySubscription = this.nodesChangeService.changes$
821
- .pipe(tap((changes) => this.onNodesChange.emit(changes)), takeUntilDestroyed())
822
- .subscribe();
823
- this.edgesChangeProxySubscription = this.edgesChangeService.changes$
824
- .pipe(tap((changes) => this.onEdgesChange.emit(changes)), takeUntilDestroyed())
825
- .subscribe();
831
+ this.onEdgesChange = this.edgesChangeService.changes$;
832
+ this.onNodesChangeDetached = this.edgeChangesOfType('detached');
833
+ this.onNodesChangeDetachedSingle = this.singleChange(this.edgeChangesOfType('detached'));
834
+ this.onNodesChangeDetachedMany = this.manyChanges(this.edgeChangesOfType('detached'));
835
+ this.onEdgesChangeAdd = this.edgeChangesOfType('add');
836
+ this.onEdgeChangeAddSingle = this.singleChange(this.edgeChangesOfType('add'));
837
+ this.onEdgeChangeAddMany = this.manyChanges(this.edgeChangesOfType('add'));
838
+ this.onEdgeChangeRemove = this.edgeChangesOfType('remove');
839
+ this.onEdgeChangeRemoveSingle = this.singleChange(this.edgeChangesOfType('remove'));
840
+ this.onEdgeChangeRemoveMany = this.manyChanges(this.edgeChangesOfType('remove'));
841
+ this.onEdgeChangeSelect = this.edgeChangesOfType('select');
842
+ this.onEdgeChangeSelectSingle = this.singleChange(this.edgeChangesOfType('select'));
843
+ this.onEdgeChangeSelectMany = this.manyChanges(this.edgeChangesOfType('select'));
844
+ }
845
+ nodeChangesOfType(type) {
846
+ return this.nodesChangeService.changes$.pipe(map(changes => changes.filter((c) => c.type === type)), filter(changes => !!changes.length));
847
+ }
848
+ edgeChangesOfType(type) {
849
+ return this.edgesChangeService.changes$.pipe(map(changes => changes.filter((c) => c.type === type)), filter(changes => !!changes.length));
850
+ }
851
+ singleChange(changes$) {
852
+ return changes$.pipe(filter(changes => changes.length === 1), map(([first]) => first));
853
+ }
854
+ manyChanges(changes$) {
855
+ return changes$.pipe(filter(changes => changes.length > 1));
826
856
  }
827
857
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ChangesControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
828
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ChangesControllerDirective, isStandalone: true, selector: "[changesController]", outputs: { onNodesChange: "onNodesChange", onEdgesChange: "onEdgesChange" }, ngImport: i0 }); }
858
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ChangesControllerDirective, isStandalone: true, selector: "[changesController]", outputs: { onNodesChange: "onNodesChange", onNodesChangePosition: "onNodesChange.position", onNodesChangePositionSignle: "onNodesChange.position.single", onNodesChangePositionMany: "onNodesChange.position.many", onNodesChangeAdd: "onNodesChange.add", onNodesChangeAddSingle: "onNodesChange.add.single", onNodesChangeAddMany: "onNodesChange.add.many", onNodesChangeRemove: "onNodesChange.remove", onNodesChangeRemoveSingle: "onNodesChange.remove.single", onNodesChangeRemoveMany: "onNodesChange.remove.many", onNodesChangeSelect: "onNodesChange.select", onNodesChangeSelectSingle: "onNodesChange.select.single", onNodesChangeSelectMany: "onNodesChange.select.many", onEdgesChange: "onEdgesChange", onNodesChangeDetached: "onEdgesChange.detached", onNodesChangeDetachedSingle: "onEdgesChange.detached.single", onNodesChangeDetachedMany: "onEdgesChange.detached.many", onEdgesChangeAdd: "onEdgesChange.add", onEdgeChangeAddSingle: "onEdgesChange.add.single", onEdgeChangeAddMany: "onEdgesChange.add.many", onEdgeChangeRemove: "onEdgesChange.remove", onEdgeChangeRemoveSingle: "onEdgesChange.remove.single", onEdgeChangeRemoveMany: "onEdgesChange.remove.many", onEdgeChangeSelect: "onEdgesChange.select", onEdgeChangeSelectSingle: "onEdgesChange.select.single", onEdgeChangeSelectMany: "onEdgesChange.select.many" }, ngImport: i0 }); }
829
859
  }
830
860
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ChangesControllerDirective, decorators: [{
831
861
  type: Directive,
@@ -835,8 +865,80 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
835
865
  }]
836
866
  }], propDecorators: { onNodesChange: [{
837
867
  type: Output
868
+ }], onNodesChangePosition: [{
869
+ type: Output,
870
+ args: ['onNodesChange.position']
871
+ }], onNodesChangePositionSignle: [{
872
+ type: Output,
873
+ args: ['onNodesChange.position.single']
874
+ }], onNodesChangePositionMany: [{
875
+ type: Output,
876
+ args: ['onNodesChange.position.many']
877
+ }], onNodesChangeAdd: [{
878
+ type: Output,
879
+ args: ['onNodesChange.add']
880
+ }], onNodesChangeAddSingle: [{
881
+ type: Output,
882
+ args: ['onNodesChange.add.single']
883
+ }], onNodesChangeAddMany: [{
884
+ type: Output,
885
+ args: ['onNodesChange.add.many']
886
+ }], onNodesChangeRemove: [{
887
+ type: Output,
888
+ args: ['onNodesChange.remove']
889
+ }], onNodesChangeRemoveSingle: [{
890
+ type: Output,
891
+ args: ['onNodesChange.remove.single']
892
+ }], onNodesChangeRemoveMany: [{
893
+ type: Output,
894
+ args: ['onNodesChange.remove.many']
895
+ }], onNodesChangeSelect: [{
896
+ type: Output,
897
+ args: ['onNodesChange.select']
898
+ }], onNodesChangeSelectSingle: [{
899
+ type: Output,
900
+ args: ['onNodesChange.select.single']
901
+ }], onNodesChangeSelectMany: [{
902
+ type: Output,
903
+ args: ['onNodesChange.select.many']
838
904
  }], onEdgesChange: [{
839
905
  type: Output
906
+ }], onNodesChangeDetached: [{
907
+ type: Output,
908
+ args: ['onEdgesChange.detached']
909
+ }], onNodesChangeDetachedSingle: [{
910
+ type: Output,
911
+ args: ['onEdgesChange.detached.single']
912
+ }], onNodesChangeDetachedMany: [{
913
+ type: Output,
914
+ args: ['onEdgesChange.detached.many']
915
+ }], onEdgesChangeAdd: [{
916
+ type: Output,
917
+ args: ['onEdgesChange.add']
918
+ }], onEdgeChangeAddSingle: [{
919
+ type: Output,
920
+ args: ['onEdgesChange.add.single']
921
+ }], onEdgeChangeAddMany: [{
922
+ type: Output,
923
+ args: ['onEdgesChange.add.many']
924
+ }], onEdgeChangeRemove: [{
925
+ type: Output,
926
+ args: ['onEdgesChange.remove']
927
+ }], onEdgeChangeRemoveSingle: [{
928
+ type: Output,
929
+ args: ['onEdgesChange.remove.single']
930
+ }], onEdgeChangeRemoveMany: [{
931
+ type: Output,
932
+ args: ['onEdgesChange.remove.many']
933
+ }], onEdgeChangeSelect: [{
934
+ type: Output,
935
+ args: ['onEdgesChange.select']
936
+ }], onEdgeChangeSelectSingle: [{
937
+ type: Output,
938
+ args: ['onEdgesChange.select.single']
939
+ }], onEdgeChangeSelectMany: [{
940
+ type: Output,
941
+ args: ['onEdgesChange.select.many']
840
942
  }] } });
841
943
 
842
944
  class NodeRenderingService {
@@ -1030,6 +1132,124 @@ function getChildStrokeWidth(element) {
1030
1132
  return 0;
1031
1133
  }
1032
1134
 
1135
+ class RootPointerDirective {
1136
+ constructor() {
1137
+ this.host = inject(ElementRef).nativeElement;
1138
+ this.initialTouch$ = new Subject();
1139
+ // TODO: do not emit if mouse not down
1140
+ this.mouseMovement$ = fromEvent(this.host, 'mousemove').pipe(map(event => ({
1141
+ x: event.clientX,
1142
+ y: event.clientY,
1143
+ originalEvent: event
1144
+ })), observeOn(animationFrameScheduler), share());
1145
+ this.touchMovement$ = merge(this.initialTouch$, fromEvent(this.host, 'touchmove')).pipe(tap((event) => event.preventDefault()), map((originalEvent) => {
1146
+ const x = originalEvent.touches[0]?.clientX ?? 0;
1147
+ const y = originalEvent.touches[0]?.clientY ?? 0;
1148
+ const target = document.elementFromPoint(x, y);
1149
+ return { x, y, target, originalEvent };
1150
+ }), observeOn(animationFrameScheduler), share());
1151
+ this.touchEnd$ = fromEvent(this.host, 'touchend').pipe(map((originalEvent) => {
1152
+ const x = originalEvent.changedTouches[0]?.clientX ?? 0;
1153
+ const y = originalEvent.changedTouches[0]?.clientY ?? 0;
1154
+ const target = document.elementFromPoint(x, y);
1155
+ return { x, y, target, originalEvent };
1156
+ }), share());
1157
+ this.pointerMovement$ = merge(this.mouseMovement$, this.touchMovement$);
1158
+ }
1159
+ /**
1160
+ * We should know when user started a touch in order to not
1161
+ * show old touch position when connection creation is started
1162
+ */
1163
+ setInitialTouch(event) {
1164
+ this.initialTouch$.next(event);
1165
+ }
1166
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1167
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootPointerDirective, selector: "svg[rootPointer]", ngImport: i0 }); }
1168
+ }
1169
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, decorators: [{
1170
+ type: Directive,
1171
+ args: [{ selector: 'svg[rootPointer]' }]
1172
+ }] });
1173
+
1174
+ class PointerDirective {
1175
+ constructor() {
1176
+ this.hostElement = inject(ElementRef).nativeElement;
1177
+ this.pointerMovementDirective = inject(RootPointerDirective);
1178
+ this.pointerOver = new EventEmitter();
1179
+ this.pointerOut = new EventEmitter();
1180
+ this.pointerStart = new EventEmitter();
1181
+ this.pointerEnd = new EventEmitter();
1182
+ this.wasPointerOver = false;
1183
+ // TODO check if i could avoid global touch end
1184
+ this.touchEnd = this.pointerMovementDirective.touchEnd$
1185
+ .pipe(filter(({ target }) => target === this.hostElement), tap(({ originalEvent }) => this.pointerEnd.emit(originalEvent)), takeUntilDestroyed())
1186
+ .subscribe();
1187
+ this.touchOverOut = this.pointerMovementDirective.touchMovement$
1188
+ .pipe(tap(({ target, originalEvent }) => {
1189
+ this.handleTouchOverAndOut(target, originalEvent);
1190
+ }), takeUntilDestroyed())
1191
+ .subscribe();
1192
+ }
1193
+ onPointerStart(event) {
1194
+ this.pointerStart.emit(event);
1195
+ if (event instanceof TouchEvent) {
1196
+ this.pointerMovementDirective.setInitialTouch(event);
1197
+ }
1198
+ }
1199
+ onPointerEnd(event) {
1200
+ this.pointerEnd.emit(event);
1201
+ }
1202
+ onMouseOver(event) {
1203
+ this.pointerOver.emit(event);
1204
+ }
1205
+ onMouseOut(event) {
1206
+ this.pointerOut.emit(event);
1207
+ }
1208
+ // TODO: dirty imperative implementation
1209
+ handleTouchOverAndOut(target, event) {
1210
+ if (target === this.hostElement) {
1211
+ this.pointerOver.emit(event);
1212
+ this.wasPointerOver = true;
1213
+ }
1214
+ else {
1215
+ // should not emit before pointerOver
1216
+ if (this.wasPointerOver) {
1217
+ this.pointerOut.emit(event);
1218
+ }
1219
+ this.wasPointerOver = false;
1220
+ }
1221
+ }
1222
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PointerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1223
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: { pointerOver: "pointerOver", pointerOut: "pointerOut", pointerStart: "pointerStart", pointerEnd: "pointerEnd" }, host: { listeners: { "mousedown": "onPointerStart($event)", "touchstart": "onPointerStart($event)", "mouseup": "onPointerEnd($event)", "mouseover": "onMouseOver($event)", "mouseout": "onMouseOut($event)" } }, ngImport: i0 }); }
1224
+ }
1225
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PointerDirective, decorators: [{
1226
+ type: Directive,
1227
+ args: [{ selector: '[pointerStart], [pointerEnd], [pointerOver], [pointerOut]' }]
1228
+ }], propDecorators: { pointerOver: [{
1229
+ type: Output
1230
+ }], pointerOut: [{
1231
+ type: Output
1232
+ }], pointerStart: [{
1233
+ type: Output
1234
+ }], pointerEnd: [{
1235
+ type: Output
1236
+ }], onPointerStart: [{
1237
+ type: HostListener,
1238
+ args: ['mousedown', ['$event']]
1239
+ }, {
1240
+ type: HostListener,
1241
+ args: ['touchstart', ['$event']]
1242
+ }], onPointerEnd: [{
1243
+ type: HostListener,
1244
+ args: ['mouseup', ['$event']]
1245
+ }], onMouseOver: [{
1246
+ type: HostListener,
1247
+ args: ['mouseover', ['$event']]
1248
+ }], onMouseOut: [{
1249
+ type: HostListener,
1250
+ args: ['mouseout', ['$event']]
1251
+ }] } });
1252
+
1033
1253
  class NodeComponent {
1034
1254
  constructor() {
1035
1255
  this.injector = inject(Injector);
@@ -1149,7 +1369,7 @@ class NodeComponent {
1149
1369
  }
1150
1370
  }
1151
1371
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1152
- 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 }], ngImport: i0, template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n (mousedown)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_selected]=\"nodeModel.selected()\"\n [innerHTML]=\"nodeModel.node.text ?? ''\"\n ></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\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 class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\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:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\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 }); }
1372
+ 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 }], ngImport: i0, template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n (mousedown)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_selected]=\"nodeModel.selected()\"\n [innerHTML]=\"nodeModel.node.text ?? ''\"\n ></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\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 class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"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 (pointerEnd)=\"endConnection(); resetValidateTargetHandle(handle)\"\n (pointerOver)=\"validateTargetHandle(handle)\"\n (pointerOut)=\"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:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\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"] }, { kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1153
1373
  }
1154
1374
  __decorate([
1155
1375
  Microtask
@@ -1159,7 +1379,7 @@ __decorate([
1159
1379
  ], NodeComponent.prototype, "setInitialHandles", null);
1160
1380
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
1161
1381
  type: Component,
1162
- args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService], template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n (mousedown)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_selected]=\"nodeModel.selected()\"\n [innerHTML]=\"nodeModel.node.text ?? ''\"\n ></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\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 class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\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:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
1382
+ args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService], template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n (mousedown)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_selected]=\"nodeModel.selected()\"\n [innerHTML]=\"nodeModel.node.text ?? ''\"\n ></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\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 class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"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 (pointerEnd)=\"endConnection(); resetValidateTargetHandle(handle)\"\n (pointerOver)=\"validateTargetHandle(handle)\"\n (pointerOut)=\"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:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
1163
1383
  }], propDecorators: { nodeModel: [{
1164
1384
  type: Input
1165
1385
  }], nodeHtmlTemplate: [{
@@ -1277,19 +1497,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1277
1497
 
1278
1498
  class SpacePointContextDirective {
1279
1499
  constructor() {
1500
+ this.pointerMovementDirective = inject(RootPointerDirective);
1501
+ this.rootSvg = inject(RootSvgReferenceDirective).element;
1502
+ this.host = inject(ElementRef).nativeElement;
1280
1503
  /**
1281
1504
  * Signal with current mouse position in svg space
1282
1505
  */
1283
1506
  this.svgCurrentSpacePoint = computed(() => {
1284
- const movement = this.mouseMovement();
1507
+ const movement = this.pointerMovement();
1508
+ if (!movement) {
1509
+ return { x: 0, y: 0 };
1510
+ }
1285
1511
  const point = this.rootSvg.createSVGPoint();
1286
1512
  point.x = movement.x;
1287
1513
  point.y = movement.y;
1288
1514
  return point.matrixTransform(this.host.getScreenCTM().inverse());
1289
1515
  });
1290
- this.rootSvg = inject(RootSvgReferenceDirective).element;
1291
- this.host = inject(ElementRef).nativeElement;
1292
- this.mouseMovement = toSignal(fromEvent(this.rootSvg, 'mousemove').pipe(map(event => ({ x: event.clientX, y: event.clientY }))), { initialValue: { x: 0, y: 0 } });
1516
+ this.pointerMovement = toSignal(this.pointerMovementDirective.pointerMovement$);
1293
1517
  }
1294
1518
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1295
1519
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SpacePointContextDirective, selector: "g[spacePointContext]", ngImport: i0 }); }
@@ -1440,7 +1664,7 @@ class RootSvgContextDirective {
1440
1664
  }
1441
1665
  }
1442
1666
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1443
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootSvgContextDirective, selector: "svg[rootSvgContext]", host: { listeners: { "document:mouseup": "resetConnection()" } }, ngImport: i0 }); }
1667
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootSvgContextDirective, selector: "svg[rootSvgContext]", host: { listeners: { "document:mouseup": "resetConnection()", "document:touchend": "resetConnection()" } }, ngImport: i0 }); }
1444
1668
  }
1445
1669
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgContextDirective, decorators: [{
1446
1670
  type: Directive,
@@ -1448,6 +1672,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1448
1672
  }], propDecorators: { resetConnection: [{
1449
1673
  type: HostListener,
1450
1674
  args: ['document:mouseup']
1675
+ }, {
1676
+ type: HostListener,
1677
+ args: ['document:touchend']
1451
1678
  }] } });
1452
1679
 
1453
1680
  const connectionControllerHostDirective = {
@@ -1456,7 +1683,34 @@ const connectionControllerHostDirective = {
1456
1683
  };
1457
1684
  const changesControllerHostDirective = {
1458
1685
  directive: ChangesControllerDirective,
1459
- outputs: ['onNodesChange', 'onEdgesChange']
1686
+ outputs: [
1687
+ 'onNodesChange',
1688
+ 'onNodesChange.position',
1689
+ 'onNodesChange.position.single',
1690
+ 'onNodesChange.position.many',
1691
+ 'onNodesChange.add',
1692
+ 'onNodesChange.add.single',
1693
+ 'onNodesChange.add.many',
1694
+ 'onNodesChange.remove',
1695
+ 'onNodesChange.remove.single',
1696
+ 'onNodesChange.remove.many',
1697
+ 'onNodesChange.select',
1698
+ 'onNodesChange.select.single',
1699
+ 'onNodesChange.select.many',
1700
+ 'onEdgesChange',
1701
+ 'onEdgesChange.detached',
1702
+ 'onEdgesChange.detached.single',
1703
+ 'onEdgesChange.detached.many',
1704
+ 'onEdgesChange.add',
1705
+ 'onEdgesChange.add.single',
1706
+ 'onEdgesChange.add.many',
1707
+ 'onEdgesChange.remove',
1708
+ 'onEdgesChange.remove.single',
1709
+ 'onEdgesChange.remove.many',
1710
+ 'onEdgesChange.select',
1711
+ 'onEdgesChange.select.single',
1712
+ 'onEdgesChange.select.many',
1713
+ ]
1460
1714
  };
1461
1715
  class VflowComponent {
1462
1716
  constructor() {
@@ -1508,7 +1762,7 @@ class VflowComponent {
1508
1762
  */
1509
1763
  this.nodesChange$ = this.nodesChangeService.changes$;
1510
1764
  /**
1511
- * Observable with nodes change
1765
+ * Observable with edges change
1512
1766
  */
1513
1767
  this.edgesChange$ = this.edgesChangeService.changes$;
1514
1768
  // #endregion
@@ -1629,7 +1883,7 @@ class VflowComponent {
1629
1883
  NodeRenderingService,
1630
1884
  SelectionService,
1631
1885
  FlowSettingsService
1632
- ], queries: [{ propertyName: "nodeHtmlDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onEdgesChange", "onEdgesChange"] }], ngImport: i0, template: "<svg:svg\n rootSvgRef\n rootSvgContext\n class=\"root-svg\"\n #flow\n [style.backgroundColor]=\"background\"\n [attr.width]=\"flowWidth()\"\n [attr.height]=\"flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <svg:g\n mapContext\n spacePointContext\n [minZoom]=\"minZoom\"\n [maxZoom]=\"maxZoom\"\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeHtmlTemplate]=\"nodeHtmlDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeHtmlTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]", inputs: ["minZoom", "maxZoom"] }, { kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1886
+ ], queries: [{ propertyName: "nodeHtmlDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onNodesChange.position", "onNodesChange.position", "onNodesChange.position.single", "onNodesChange.position.single", "onNodesChange.position.many", "onNodesChange.position.many", "onNodesChange.add", "onNodesChange.add", "onNodesChange.add.single", "onNodesChange.add.single", "onNodesChange.add.many", "onNodesChange.add.many", "onNodesChange.remove", "onNodesChange.remove", "onNodesChange.remove.single", "onNodesChange.remove.single", "onNodesChange.remove.many", "onNodesChange.remove.many", "onNodesChange.select", "onNodesChange.select", "onNodesChange.select.single", "onNodesChange.select.single", "onNodesChange.select.many", "onNodesChange.select.many", "onEdgesChange", "onEdgesChange", "onEdgesChange.detached", "onEdgesChange.detached", "onEdgesChange.detached.single", "onEdgesChange.detached.single", "onEdgesChange.detached.many", "onEdgesChange.detached.many", "onEdgesChange.add", "onEdgesChange.add", "onEdgesChange.add.single", "onEdgesChange.add.single", "onEdgesChange.add.many", "onEdgesChange.add.many", "onEdgesChange.remove", "onEdgesChange.remove", "onEdgesChange.remove.single", "onEdgesChange.remove.single", "onEdgesChange.remove.many", "onEdgesChange.remove.many", "onEdgesChange.select", "onEdgesChange.select", "onEdgesChange.select.single", "onEdgesChange.select.single", "onEdgesChange.select.many", "onEdgesChange.select.many"] }], ngImport: i0, template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n class=\"root-svg\"\n #flow\n [style.backgroundColor]=\"background\"\n [attr.width]=\"flowWidth()\"\n [attr.height]=\"flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <svg:g\n mapContext\n spacePointContext\n [minZoom]=\"minZoom\"\n [maxZoom]=\"maxZoom\"\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeHtmlTemplate]=\"nodeHtmlDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeHtmlTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]", inputs: ["minZoom", "maxZoom"] }, { kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1633
1887
  }
1634
1888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
1635
1889
  type: Component,
@@ -1646,7 +1900,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1646
1900
  ], hostDirectives: [
1647
1901
  connectionControllerHostDirective,
1648
1902
  changesControllerHostDirective
1649
- ], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n class=\"root-svg\"\n #flow\n [style.backgroundColor]=\"background\"\n [attr.width]=\"flowWidth()\"\n [attr.height]=\"flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <svg:g\n mapContext\n spacePointContext\n [minZoom]=\"minZoom\"\n [maxZoom]=\"maxZoom\"\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeHtmlTemplate]=\"nodeHtmlDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"] }]
1903
+ ], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n class=\"root-svg\"\n #flow\n [style.backgroundColor]=\"background\"\n [attr.width]=\"flowWidth()\"\n [attr.height]=\"flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <svg:g\n mapContext\n spacePointContext\n [minZoom]=\"minZoom\"\n [maxZoom]=\"maxZoom\"\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeHtmlTemplate]=\"nodeHtmlDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"] }]
1650
1904
  }], propDecorators: { view: [{
1651
1905
  type: Input
1652
1906
  }], minZoom: [{
@@ -1773,7 +2027,9 @@ const directives = [
1773
2027
  RootSvgReferenceDirective,
1774
2028
  RootSvgContextDirective,
1775
2029
  HandleSizeControllerDirective,
1776
- SelectableDirective
2030
+ SelectableDirective,
2031
+ PointerDirective,
2032
+ RootPointerDirective
1777
2033
  ];
1778
2034
  const templateDirectives = [
1779
2035
  NodeHtmlTemplateDirective,
@@ -1795,7 +2051,9 @@ class VflowModule {
1795
2051
  RootSvgReferenceDirective,
1796
2052
  RootSvgContextDirective,
1797
2053
  HandleSizeControllerDirective,
1798
- SelectableDirective, NodeHtmlTemplateDirective,
2054
+ SelectableDirective,
2055
+ PointerDirective,
2056
+ RootPointerDirective, NodeHtmlTemplateDirective,
1799
2057
  EdgeLabelHtmlTemplateDirective,
1800
2058
  EdgeTemplateDirective,
1801
2059
  ConnectionTemplateDirective,