ngx-vflow 1.4.2 → 1.6.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.
Files changed (47) hide show
  1. package/esm2022/lib/vflow/components/connection/connection.component.mjs +4 -3
  2. package/esm2022/lib/vflow/components/edge/edge.component.mjs +29 -26
  3. package/esm2022/lib/vflow/components/edge-label/edge-label.component.mjs +2 -1
  4. package/esm2022/lib/vflow/components/node/node.component.mjs +6 -4
  5. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +15 -3
  6. package/esm2022/lib/vflow/directives/connection-controller.directive.mjs +55 -37
  7. package/esm2022/lib/vflow/directives/root-svg-context.directive.mjs +2 -2
  8. package/esm2022/lib/vflow/directives/selectable.directive.mjs +14 -11
  9. package/esm2022/lib/vflow/directives/template.directive.mjs +16 -1
  10. package/esm2022/lib/vflow/interfaces/connection.interface.mjs +1 -1
  11. package/esm2022/lib/vflow/interfaces/contextable.interface.mjs +2 -0
  12. package/esm2022/lib/vflow/interfaces/edge.interface.mjs +1 -1
  13. package/esm2022/lib/vflow/interfaces/template-context.interface.mjs +1 -1
  14. package/esm2022/lib/vflow/models/edge.model.mjs +50 -23
  15. package/esm2022/lib/vflow/models/node.model.mjs +22 -1
  16. package/esm2022/lib/vflow/public-components/custom-template-edge/custom-template-edge.component.mjs +29 -0
  17. package/esm2022/lib/vflow/services/edge-rendering.service.mjs +28 -0
  18. package/esm2022/lib/vflow/services/flow-settings.service.mjs +2 -1
  19. package/esm2022/lib/vflow/services/flow-status.service.mjs +13 -1
  20. package/esm2022/lib/vflow/testing-utils/component-mocks/vflow-mock.component.mjs +4 -2
  21. package/esm2022/lib/vflow/testing-utils/directive-mocks/connection-controller-mock.directive.mjs +6 -2
  22. package/esm2022/lib/vflow/vflow.mjs +3 -1
  23. package/esm2022/public-api.mjs +2 -1
  24. package/fesm2022/ngx-vflow.mjs +358 -193
  25. package/fesm2022/ngx-vflow.mjs.map +1 -1
  26. package/lib/vflow/components/connection/connection.component.d.ts +2 -6
  27. package/lib/vflow/components/edge/edge.component.d.ts +10 -7
  28. package/lib/vflow/components/edge-label/edge-label.component.d.ts +2 -6
  29. package/lib/vflow/components/vflow/vflow.component.d.ts +6 -1
  30. package/lib/vflow/directives/connection-controller.directive.d.ts +5 -2
  31. package/lib/vflow/directives/selectable.directive.d.ts +4 -1
  32. package/lib/vflow/directives/template.directive.d.ts +6 -1
  33. package/lib/vflow/interfaces/connection.interface.d.ts +5 -0
  34. package/lib/vflow/interfaces/contextable.interface.d.ts +5 -0
  35. package/lib/vflow/interfaces/edge.interface.d.ts +1 -0
  36. package/lib/vflow/interfaces/template-context.interface.d.ts +37 -1
  37. package/lib/vflow/models/edge.model.d.ts +24 -1
  38. package/lib/vflow/models/node.model.d.ts +8 -1
  39. package/lib/vflow/public-components/custom-template-edge/custom-template-edge.component.d.ts +17 -0
  40. package/lib/vflow/services/edge-rendering.service.d.ts +10 -0
  41. package/lib/vflow/services/flow-settings.service.d.ts +1 -0
  42. package/lib/vflow/services/flow-status.service.d.ts +24 -1
  43. package/lib/vflow/testing-utils/component-mocks/vflow-mock.component.d.ts +2 -1
  44. package/lib/vflow/testing-utils/directive-mocks/connection-controller-mock.directive.d.ts +4 -2
  45. package/lib/vflow/vflow.d.ts +2 -1
  46. package/package.json +1 -1
  47. package/public-api.d.ts +1 -0
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, DestroyRef, EventEmitter, OutputEmitterRef, input, NgZone, viewChild, Component, ChangeDetectionStrategy, Injector, output, HostListener, runInInjectionContext, contentChild, Input, forwardRef } from '@angular/core';
2
+ import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, DestroyRef, EventEmitter, OutputEmitterRef, input, NgZone, viewChild, Component, ChangeDetectionStrategy, output, HostListener, Injector, runInInjectionContext, contentChild, Input, forwardRef } from '@angular/core';
3
3
  import { select } from 'd3-selection';
4
4
  import { zoomIdentity, zoom } from 'd3-zoom';
5
5
  import { switchMap, merge, fromEvent, tap, Subject, Observable, skip, map, pairwise, filter, distinctUntilChanged, observeOn, asyncScheduler, zip, animationFrameScheduler, share, startWith, of } from 'rxjs';
@@ -151,6 +151,7 @@ class FlowSettingsService {
151
151
  constructor() {
152
152
  this.entitiesSelectable = signal(true);
153
153
  this.elevateNodesOnSelect = signal(true);
154
+ this.elevateEdgesOnSelect = signal(true);
154
155
  /**
155
156
  * @see {VflowComponent.view}
156
157
  */
@@ -575,6 +576,9 @@ class EdgeTemplateDirective {
575
576
  constructor() {
576
577
  this.templateRef = inject(TemplateRef);
577
578
  }
579
+ static ngTemplateContextGuard(dir, ctx) {
580
+ return true;
581
+ }
578
582
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
579
583
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: EdgeTemplateDirective, isStandalone: true, selector: "ng-template[edge]", ngImport: i0 }); }
580
584
  }
@@ -589,6 +593,9 @@ class ConnectionTemplateDirective {
589
593
  constructor() {
590
594
  this.templateRef = inject(TemplateRef);
591
595
  }
596
+ static ngTemplateContextGuard(dir, ctx) {
597
+ return true;
598
+ }
592
599
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
593
600
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionTemplateDirective, isStandalone: true, selector: "ng-template[connection]", ngImport: i0 }); }
594
601
  }
@@ -603,6 +610,9 @@ class EdgeLabelHtmlTemplateDirective {
603
610
  constructor() {
604
611
  this.templateRef = inject(TemplateRef);
605
612
  }
613
+ static ngTemplateContextGuard(dir, ctx) {
614
+ return true;
615
+ }
606
616
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeLabelHtmlTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
607
617
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: EdgeLabelHtmlTemplateDirective, isStandalone: true, selector: "ng-template[edgeLabelHtml]", ngImport: i0 }); }
608
618
  }
@@ -617,6 +627,9 @@ class NodeHtmlTemplateDirective {
617
627
  constructor() {
618
628
  this.templateRef = inject(TemplateRef);
619
629
  }
630
+ static ngTemplateContextGuard(dir, ctx) {
631
+ return true;
632
+ }
620
633
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeHtmlTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
621
634
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NodeHtmlTemplateDirective, isStandalone: true, selector: "ng-template[nodeHtml]", ngImport: i0 }); }
622
635
  }
@@ -631,6 +644,9 @@ class GroupNodeTemplateDirective {
631
644
  constructor() {
632
645
  this.templateRef = inject(TemplateRef);
633
646
  }
647
+ static ngTemplateContextGuard(dir, ctx) {
648
+ return true;
649
+ }
634
650
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: GroupNodeTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
635
651
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: GroupNodeTemplateDirective, isStandalone: true, selector: "ng-template[groupNode]", ngImport: i0 }); }
636
652
  }
@@ -677,12 +693,24 @@ class FlowStatusService {
677
693
  setConnectionStartStatus(source, sourceHandle) {
678
694
  this.status.set({ state: 'connection-start', payload: { source, sourceHandle } });
679
695
  }
696
+ setReconnectionStartStatus(source, sourceHandle, oldEdge) {
697
+ this.status.set({ state: 'reconnection-start', payload: { source, sourceHandle, oldEdge } });
698
+ }
680
699
  setConnectionValidationStatus(valid, source, target, sourceHandle, targetHandle) {
681
700
  this.status.set({ state: 'connection-validation', payload: { source, target, sourceHandle, targetHandle, valid } });
682
701
  }
702
+ setReconnectionValidationStatus(valid, source, target, sourceHandle, targetHandle, oldEdge) {
703
+ this.status.set({
704
+ state: 'reconnection-validation',
705
+ payload: { source, target, sourceHandle, targetHandle, valid, oldEdge },
706
+ });
707
+ }
683
708
  setConnectionEndStatus(source, target, sourceHandle, targetHandle) {
684
709
  this.status.set({ state: 'connection-end', payload: { source, target, sourceHandle, targetHandle } });
685
710
  }
711
+ setReconnectionEndStatus(source, target, sourceHandle, targetHandle, oldEdge) {
712
+ this.status.set({ state: 'reconnection-end', payload: { source, target, sourceHandle, targetHandle, oldEdge } });
713
+ }
686
714
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FlowStatusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
687
715
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FlowStatusService }); }
688
716
  }
@@ -914,6 +942,9 @@ class NodeModel {
914
942
  this.resizable = signal(false);
915
943
  this.resizing = signal(false);
916
944
  this.resizerTemplate = signal(null);
945
+ this.context = {
946
+ $implicit: {},
947
+ };
917
948
  this.parentId = signal(null);
918
949
  if (isDefined(node.draggable)) {
919
950
  if (isDynamicNode(node)) {
@@ -947,6 +978,24 @@ class NodeModel {
947
978
  this.resizable.set(node.resizable);
948
979
  }
949
980
  }
981
+ if (node.type === 'html-template') {
982
+ this.context = {
983
+ $implicit: {
984
+ node: node,
985
+ selected: this.selected,
986
+ },
987
+ };
988
+ }
989
+ else if (node.type === 'template-group') {
990
+ this.context = {
991
+ $implicit: {
992
+ node: node,
993
+ selected: this.selected.asReadonly(),
994
+ width: this.width,
995
+ height: this.height,
996
+ },
997
+ };
998
+ }
950
999
  }
951
1000
  setPoint(point) {
952
1001
  this.point.set(point);
@@ -1254,6 +1303,7 @@ class EdgeModel {
1254
1303
  this.target = signal(undefined);
1255
1304
  this.selected = signal(false);
1256
1305
  this.selected$ = toObservable(this.selected);
1306
+ this.renderOrder = signal(0);
1257
1307
  this.detached = computed(() => {
1258
1308
  const source = this.source();
1259
1309
  const target = this.target();
@@ -1278,28 +1328,8 @@ class EdgeModel {
1278
1328
  });
1279
1329
  this.detached$ = toObservable(this.detached);
1280
1330
  this.path = computed(() => {
1281
- let source;
1282
- if (this.edge.sourceHandle) {
1283
- source = this.source()
1284
- ?.handles()
1285
- .find((handle) => handle.rawHandle.id === this.edge.sourceHandle);
1286
- }
1287
- else {
1288
- source = this.source()
1289
- ?.handles()
1290
- .find((handle) => handle.rawHandle.type === 'source');
1291
- }
1292
- let target;
1293
- if (this.edge.targetHandle) {
1294
- target = this.target()
1295
- ?.handles()
1296
- .find((handle) => handle.rawHandle.id === this.edge.targetHandle);
1297
- }
1298
- else {
1299
- target = this.target()
1300
- ?.handles()
1301
- .find((handle) => handle.rawHandle.type === 'target');
1302
- }
1331
+ const source = this.sourceHandle();
1332
+ const target = this.targetHandle();
1303
1333
  // TODO: don't like this
1304
1334
  if (!source || !target) {
1305
1335
  return {
@@ -1322,9 +1352,54 @@ class EdgeModel {
1322
1352
  return smoothStepPath(source.pointAbsolute(), target.pointAbsolute(), source.rawHandle.position, target.rawHandle.position, 0);
1323
1353
  }
1324
1354
  });
1355
+ this.sourceHandle = computed(() => {
1356
+ if (this.edge.sourceHandle) {
1357
+ return (this.source()
1358
+ ?.handles()
1359
+ .find((handle) => handle.rawHandle.id === this.edge.sourceHandle) ?? null);
1360
+ }
1361
+ return (this.source()
1362
+ ?.handles()
1363
+ .find((handle) => handle.rawHandle.type === 'source') ?? null);
1364
+ });
1365
+ this.targetHandle = computed(() => {
1366
+ if (this.edge.targetHandle) {
1367
+ return (this.target()
1368
+ ?.handles()
1369
+ .find((handle) => handle.rawHandle.id === this.edge.targetHandle) ?? null);
1370
+ }
1371
+ return (this.target()
1372
+ ?.handles()
1373
+ .find((handle) => handle.rawHandle.type === 'target') ?? null);
1374
+ });
1375
+ /**
1376
+ * TODO: not reactive
1377
+ */
1378
+ this.markerStartUrl = computed(() => {
1379
+ const marker = this.edge.markers?.start;
1380
+ return marker ? `url(#${hashCode(JSON.stringify(marker))})` : '';
1381
+ });
1382
+ /**
1383
+ * TODO: not reactive
1384
+ */
1385
+ this.markerEndUrl = computed(() => {
1386
+ const marker = this.edge.markers?.end;
1387
+ return marker ? `url(#${hashCode(JSON.stringify(marker))})` : '';
1388
+ });
1389
+ this.context = {
1390
+ $implicit: {
1391
+ // TODO: check if edge could change
1392
+ edge: this.edge,
1393
+ path: computed(() => this.path().path),
1394
+ markerStart: this.markerStartUrl,
1395
+ markerEnd: this.markerEndUrl,
1396
+ selected: this.selected.asReadonly(),
1397
+ },
1398
+ };
1325
1399
  this.edgeLabels = {};
1326
1400
  this.type = edge.type ?? 'default';
1327
1401
  this.curve = edge.curve ?? 'bezier';
1402
+ this.reconnectable = edge.reconnectable ?? false;
1328
1403
  if (edge.edgeLabels?.start)
1329
1404
  this.edgeLabels.start = new EdgeLabelModel(edge.edgeLabels.start);
1330
1405
  if (edge.edgeLabels?.center)
@@ -1829,6 +1904,7 @@ class EdgeLabelComponent {
1829
1904
  }), takeUntilDestroyed(this.destroyRef))
1830
1905
  .subscribe();
1831
1906
  }
1907
+ // TODO: move to model with Contextable interface
1832
1908
  getLabelContext() {
1833
1909
  return {
1834
1910
  $implicit: {
@@ -1845,76 +1921,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1845
1921
  args: [{ standalone: true, selector: 'g[edgeLabel]', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], template: "@if (model(); as model) {\n @if (model.edgeLabel.type === 'html-template' && htmlTemplate()) {\n @if (htmlTemplate(); as htmlTemplate) {\n <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 <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\" />\n </div>\n </svg:foreignObject>\n }\n }\n\n @if (model.edgeLabel.type === 'default') {\n <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 <div #edgeLabelWrapper class=\"edge-label-wrapper\" [style]=\"edgeLabelStyle()\">\n {{ model.edgeLabel.text }}\n </div>\n </svg:foreignObject>\n }\n}\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"] }]
1846
1922
  }] });
1847
1923
 
1848
- class EdgeComponent {
1849
- constructor() {
1850
- this.injector = inject(Injector);
1851
- this.selectionService = inject(SelectionService);
1852
- this.flowSettingsService = inject(FlowSettingsService);
1853
- this.model = input.required();
1854
- this.edgeTemplate = input();
1855
- this.edgeLabelHtmlTemplate = input();
1856
- this.markerStartUrl = computed(() => {
1857
- const marker = this.model().edge.markers?.start;
1858
- return marker ? `url(#${hashCode(JSON.stringify(marker))})` : '';
1859
- });
1860
- this.markerEndUrl = computed(() => {
1861
- const marker = this.model().edge.markers?.end;
1862
- return marker ? `url(#${hashCode(JSON.stringify(marker))})` : '';
1863
- });
1864
- }
1865
- ngOnInit() {
1866
- this.edgeContext = {
1867
- $implicit: {
1868
- // TODO: check if edge could change
1869
- edge: this.model().edge,
1870
- path: computed(() => this.model().path().path),
1871
- markerStart: this.markerStartUrl,
1872
- markerEnd: this.markerEndUrl,
1873
- selected: this.model().selected.asReadonly(),
1874
- },
1875
- };
1876
- }
1877
- onEdgeMouseDown() {
1878
- if (this.flowSettingsService.entitiesSelectable()) {
1879
- this.selectionService.select(this.model());
1880
- }
1881
- }
1882
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1883
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EdgeComponent, isStandalone: true, selector: "g[edge]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, edgeTemplate: { classPropertyName: "edgeTemplate", publicName: "edgeTemplate", isSignal: true, isRequired: false, transformFunction: null }, edgeLabelHtmlTemplate: { classPropertyName: "edgeLabelHtmlTemplate", publicName: "edgeLabelHtmlTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "selectable" }, ngImport: i0, template: "@if (model().type === 'default') {\n <svg:path\n class=\"edge\"\n [attr.d]=\"model().path().path\"\n [attr.marker-start]=\"markerStartUrl()\"\n [attr.marker-end]=\"markerEndUrl()\"\n [class.edge_selected]=\"model().selected()\"\n (mousedown)=\"onEdgeMouseDown()\" />\n}\n\n@if (model().type === 'template' && edgeTemplate()) {\n @if (edgeTemplate(); as edgeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"edgeTemplate\"\n [ngTemplateOutletContext]=\"edgeContext\"\n [ngTemplateOutletInjector]=\"injector\" />\n }\n}\n\n@if (model().edgeLabels.start; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.start\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.center; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.center\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.end; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.end\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n", styles: [".edge{fill:none;stroke-width:2;stroke:#b1b1b7}.edge_selected{stroke-width:2.5;stroke:#0f4c75}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EdgeLabelComponent, selector: "g[edgeLabel]", inputs: ["model", "edgeModel", "point", "htmlTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1884
- }
1885
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeComponent, decorators: [{
1886
- type: Component,
1887
- args: [{ standalone: true, selector: 'g[edge]', changeDetection: ChangeDetectionStrategy.OnPush, host: {
1888
- class: 'selectable',
1889
- }, imports: [NgTemplateOutlet, EdgeLabelComponent], template: "@if (model().type === 'default') {\n <svg:path\n class=\"edge\"\n [attr.d]=\"model().path().path\"\n [attr.marker-start]=\"markerStartUrl()\"\n [attr.marker-end]=\"markerEndUrl()\"\n [class.edge_selected]=\"model().selected()\"\n (mousedown)=\"onEdgeMouseDown()\" />\n}\n\n@if (model().type === 'template' && edgeTemplate()) {\n @if (edgeTemplate(); as edgeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"edgeTemplate\"\n [ngTemplateOutletContext]=\"edgeContext\"\n [ngTemplateOutletInjector]=\"injector\" />\n }\n}\n\n@if (model().edgeLabels.start; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.start\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.center; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.center\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.end; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.end\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n", styles: [".edge{fill:none;stroke-width:2;stroke:#b1b1b7}.edge_selected{stroke-width:2.5;stroke:#0f4c75}\n"] }]
1890
- }] });
1891
-
1892
- class HandleService {
1893
- constructor() {
1894
- this.node = signal(null);
1895
- }
1896
- createHandle(newHandle) {
1897
- const node = this.node();
1898
- if (node) {
1899
- node.handles.update((handles) => [...handles, newHandle]);
1900
- }
1901
- }
1902
- destroyHandle(handleToDestoy) {
1903
- const node = this.node();
1904
- if (node) {
1905
- node.handles.update((handles) => handles.filter((handle) => handle !== handleToDestoy));
1906
- }
1907
- }
1908
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1909
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleService }); }
1910
- }
1911
- __decorate([
1912
- Microtask // TODO fixes rendering of handle for group node
1913
- ], HandleService.prototype, "createHandle", null);
1914
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleService, decorators: [{
1915
- type: Injectable
1916
- }], propDecorators: { createHandle: [] } });
1917
-
1918
1924
  /**
1919
1925
  * This function contains a hack-y behavior.
1920
1926
  * If the handles are of the same type (source-source or target-target),
@@ -1960,42 +1966,24 @@ class ConnectionControllerDirective {
1960
1966
  * @todo add connect event and deprecate onConnect
1961
1967
  */
1962
1968
  // eslint-disable-next-line @angular-eslint/no-output-on-prefix
1963
- this.onConnect = outputFromObservable(toObservable(this.statusService.status).pipe(filter((status) => status.state === 'connection-end'), map((status) => {
1964
- let source = status.payload.source;
1965
- let target = status.payload.target;
1966
- let sourceHandle = status.payload.sourceHandle;
1967
- let targetHandle = status.payload.targetHandle;
1968
- if (this.isStrictMode()) {
1969
- const adjusted = adjustDirection({
1970
- source: status.payload.source,
1971
- sourceHandle: status.payload.sourceHandle,
1972
- target: status.payload.target,
1973
- targetHandle: status.payload.targetHandle,
1974
- });
1975
- source = adjusted.source;
1976
- target = adjusted.target;
1977
- sourceHandle = adjusted.sourceHandle;
1978
- targetHandle = adjusted.targetHandle;
1979
- }
1980
- const sourceId = source.node.id;
1981
- const targetId = target.node.id;
1982
- const sourceHandleId = sourceHandle.rawHandle.id;
1983
- const targetHandleId = targetHandle.rawHandle.id;
1984
- return {
1985
- source: sourceId,
1986
- target: targetId,
1987
- sourceHandle: sourceHandleId,
1988
- targetHandle: targetHandleId,
1989
- };
1990
- }), tap(() => this.statusService.setIdleStatus()), filter((connection) => this.flowEntitiesService.connection().validator(connection))));
1969
+ this.onConnect = outputFromObservable(toObservable(this.statusService.status).pipe(filter((status) => status.state === 'connection-end'), map((status) => statusToConnection(status, this.isStrictMode())), tap(() => this.statusService.setIdleStatus()), filter((connection) => this.flowEntitiesService.connection().validator(connection))));
1970
+ this.onReconnect = outputFromObservable(toObservable(this.statusService.status).pipe(filter((status) => status.state === 'reconnection-end'), map((status) => {
1971
+ const connection = statusToConnection(status, this.isStrictMode());
1972
+ const oldEdge = status.payload.oldEdge.edge;
1973
+ return { connection, oldEdge };
1974
+ }), tap(() => this.statusService.setIdleStatus()), filter(({ connection }) => this.flowEntitiesService.connection().validator(connection))));
1991
1975
  this.isStrictMode = computed(() => this.flowEntitiesService.connection().mode === 'strict');
1992
1976
  }
1993
1977
  startConnection(handle) {
1994
1978
  this.statusService.setConnectionStartStatus(handle.parentNode, handle);
1995
1979
  }
1980
+ startReconnection(handle, oldEdge) {
1981
+ this.statusService.setReconnectionStartStatus(handle.parentNode, handle, oldEdge);
1982
+ }
1996
1983
  validateConnection(handle) {
1997
1984
  const status = this.statusService.status();
1998
- if (status.state === 'connection-start') {
1985
+ if (status.state === 'connection-start' || status.state === 'reconnection-start') {
1986
+ const isReconnection = status.state === 'reconnection-start';
1999
1987
  let source = status.payload.source;
2000
1988
  let target = handle.parentNode;
2001
1989
  let sourceHandle = status.payload.sourceHandle;
@@ -2024,89 +2012,97 @@ class ConnectionControllerDirective {
2024
2012
  handle.state.set(valid ? 'valid' : 'invalid');
2025
2013
  // status is about how we draw connection, so we don't need
2026
2014
  // swapped diretion here
2027
- this.statusService.setConnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle);
2015
+ isReconnection
2016
+ ? this.statusService.setReconnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle, status.payload.oldEdge)
2017
+ : this.statusService.setConnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle);
2028
2018
  }
2029
2019
  }
2030
2020
  resetValidateConnection(targetHandle) {
2031
2021
  targetHandle.state.set('idle');
2032
2022
  // drop back to start status
2033
2023
  const status = this.statusService.status();
2034
- if (status.state === 'connection-validation') {
2035
- this.statusService.setConnectionStartStatus(status.payload.source, status.payload.sourceHandle);
2024
+ if (status.state === 'connection-validation' || status.state === 'reconnection-validation') {
2025
+ const isReconnection = status.state === 'reconnection-validation';
2026
+ isReconnection
2027
+ ? this.statusService.setReconnectionStartStatus(status.payload.source, status.payload.sourceHandle, status.payload.oldEdge)
2028
+ : this.statusService.setConnectionStartStatus(status.payload.source, status.payload.sourceHandle);
2036
2029
  }
2037
2030
  }
2038
2031
  endConnection() {
2039
2032
  const status = this.statusService.status();
2040
- if (status.state === 'connection-validation') {
2033
+ if (status.state === 'connection-validation' || status.state === 'reconnection-validation') {
2034
+ const isReconnection = status.state === 'reconnection-validation';
2041
2035
  const source = status.payload.source;
2042
2036
  const sourceHandle = status.payload.sourceHandle;
2043
2037
  const target = status.payload.target;
2044
2038
  const targetHandle = status.payload.targetHandle;
2045
- this.statusService.setConnectionEndStatus(source, target, sourceHandle, targetHandle);
2039
+ isReconnection
2040
+ ? this.statusService.setReconnectionEndStatus(source, target, sourceHandle, targetHandle, status.payload.oldEdge)
2041
+ : this.statusService.setConnectionEndStatus(source, target, sourceHandle, targetHandle);
2046
2042
  }
2047
2043
  }
2048
2044
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2049
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[onConnect]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
2045
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[onConnect], [onReconnect]", outputs: { onConnect: "onConnect", onReconnect: "onReconnect" }, ngImport: i0 }); }
2050
2046
  }
2051
2047
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerDirective, decorators: [{
2052
2048
  type: Directive,
2053
2049
  args: [{
2054
- selector: '[onConnect]',
2050
+ selector: '[onConnect], [onReconnect]',
2055
2051
  standalone: true,
2056
2052
  }]
2057
2053
  }] });
2054
+ function statusToConnection(status, isStrictMode) {
2055
+ let source = status.payload.source;
2056
+ let target = status.payload.target;
2057
+ let sourceHandle = status.payload.sourceHandle;
2058
+ let targetHandle = status.payload.targetHandle;
2059
+ if (isStrictMode) {
2060
+ const adjusted = adjustDirection({
2061
+ source: status.payload.source,
2062
+ sourceHandle: status.payload.sourceHandle,
2063
+ target: status.payload.target,
2064
+ targetHandle: status.payload.targetHandle,
2065
+ });
2066
+ source = adjusted.source;
2067
+ target = adjusted.target;
2068
+ sourceHandle = adjusted.sourceHandle;
2069
+ targetHandle = adjusted.targetHandle;
2070
+ }
2071
+ const sourceId = source.node.id;
2072
+ const targetId = target.node.id;
2073
+ const sourceHandleId = sourceHandle.rawHandle.id;
2074
+ const targetHandleId = targetHandle.rawHandle.id;
2075
+ return {
2076
+ source: sourceId,
2077
+ target: targetId,
2078
+ sourceHandle: sourceHandleId,
2079
+ targetHandle: targetHandleId,
2080
+ };
2081
+ }
2058
2082
 
2059
- class HandleSizeControllerDirective {
2083
+ class EdgeRenderingService {
2060
2084
  constructor() {
2061
- this.handleModel = input.required({
2062
- alias: 'handleSizeController',
2085
+ this.flowEntitiesService = inject(FlowEntitiesService);
2086
+ this.edges = computed(() => {
2087
+ return this.flowEntitiesService.validEdges().sort((aNode, bNode) => aNode.renderOrder() - bNode.renderOrder());
2063
2088
  });
2064
- this.handleWrapper = inject(ElementRef);
2065
- }
2066
- ngAfterViewInit() {
2067
- const element = this.handleWrapper.nativeElement;
2068
- const rect = element.getBBox();
2069
- const stroke = getChildStrokeWidth(element);
2070
- this.handleModel().size.set({
2071
- width: rect.width + stroke,
2072
- height: rect.height + stroke,
2089
+ this.maxOrder = computed(() => {
2090
+ return Math.max(...this.flowEntitiesService.validEdges().map((n) => n.renderOrder()));
2073
2091
  });
2074
2092
  }
2075
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2076
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type: HandleSizeControllerDirective, isStandalone: true, selector: "[handleSizeController]", inputs: { handleModel: { classPropertyName: "handleModel", publicName: "handleSizeController", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
2077
- }
2078
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, decorators: [{
2079
- type: Directive,
2080
- args: [{
2081
- standalone: true,
2082
- selector: '[handleSizeController]',
2083
- }]
2084
- }] });
2085
- function getChildStrokeWidth(element) {
2086
- const child = element.firstElementChild;
2087
- if (child) {
2088
- const stroke = getComputedStyle(child).strokeWidth;
2089
- const strokeAsNumber = Number(stroke.replace('px', ''));
2090
- if (isNaN(strokeAsNumber)) {
2091
- return 0;
2093
+ pull(edge) {
2094
+ const isAlreadyOnTop = edge.renderOrder() !== 0 && this.maxOrder() === edge.renderOrder();
2095
+ if (isAlreadyOnTop) {
2096
+ return;
2092
2097
  }
2093
- return strokeAsNumber;
2094
- }
2095
- return 0;
2096
- }
2097
-
2098
- class DefaultNodeComponent {
2099
- constructor() {
2100
- this.selected = input(false);
2098
+ // pull node
2099
+ edge.renderOrder.set(this.maxOrder() + 1);
2101
2100
  }
2102
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2103
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: DefaultNodeComponent, isStandalone: true, selector: "default-node", inputs: { selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.selected": "selected()" } }, ngImport: i0, template: "<ng-content />\n", styles: [":host{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}:host(.selected){border-width:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2101
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeRenderingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2102
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeRenderingService }); }
2104
2103
  }
2105
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultNodeComponent, decorators: [{
2106
- type: Component,
2107
- args: [{ standalone: true, selector: 'default-node', host: {
2108
- '[class.selected]': 'selected()',
2109
- }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content />\n", styles: [":host{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}:host(.selected){border-width:2px}\n"] }]
2104
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeRenderingService, decorators: [{
2105
+ type: Injectable
2110
2106
  }] });
2111
2107
 
2112
2108
  function isTouchEvent(event) {
@@ -2191,6 +2187,129 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2191
2187
  args: ['mouseout', ['$event']]
2192
2188
  }] } });
2193
2189
 
2190
+ class EdgeComponent {
2191
+ constructor() {
2192
+ this.injector = inject(Injector);
2193
+ this.selectionService = inject(SelectionService);
2194
+ this.flowSettingsService = inject(FlowSettingsService);
2195
+ this.flowStatusService = inject(FlowStatusService);
2196
+ this.edgeRenderingService = inject(EdgeRenderingService);
2197
+ // TODO remove dependency from this directive
2198
+ this.connectionController = inject(ConnectionControllerDirective, { optional: true });
2199
+ this.model = input.required();
2200
+ this.edgeTemplate = input();
2201
+ this.edgeLabelHtmlTemplate = input();
2202
+ this.isReconnecting = computed(() => {
2203
+ const status = this.flowStatusService.status();
2204
+ const isReconnecting = status.state === 'reconnection-start' || status.state === 'reconnection-validation';
2205
+ return isReconnecting && status.payload.oldEdge === this.model();
2206
+ });
2207
+ }
2208
+ select() {
2209
+ if (this.flowSettingsService.entitiesSelectable()) {
2210
+ this.selectionService.select(this.model());
2211
+ }
2212
+ }
2213
+ pull() {
2214
+ if (this.flowSettingsService.elevateEdgesOnSelect()) {
2215
+ this.edgeRenderingService.pull(this.model());
2216
+ }
2217
+ }
2218
+ startReconnection(event, handle) {
2219
+ // ignore drag by stopping propagation
2220
+ event.stopPropagation();
2221
+ this.connectionController?.startReconnection(handle, this.model());
2222
+ }
2223
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2224
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EdgeComponent, isStandalone: true, selector: "g[edge]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, edgeTemplate: { classPropertyName: "edgeTemplate", publicName: "edgeTemplate", isSignal: true, isRequired: false, transformFunction: null }, edgeLabelHtmlTemplate: { classPropertyName: "edgeLabelHtmlTemplate", publicName: "edgeLabelHtmlTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.visibility": "isReconnecting() ? \"hidden\" : \"visible\"" }, classAttribute: "selectable" }, ngImport: i0, template: "@if (model().type === 'default') {\n <svg:path\n class=\"edge\"\n [attr.d]=\"model().path().path\"\n [attr.marker-start]=\"model().markerStartUrl()\"\n [attr.marker-end]=\"model().markerStartUrl()\"\n [class.edge_selected]=\"model().selected()\" />\n\n <svg:path class=\"interactive-edge\" [attr.d]=\"model().path().path\" (pointerStart)=\"select(); pull()\" />\n}\n\n@if (model().type === 'template' && edgeTemplate()) {\n @if (edgeTemplate(); as edgeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"edgeTemplate\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n }\n}\n\n@if (model().edgeLabels.start; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.start\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.center; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.center\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.end; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.end\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().sourceHandle() && model().targetHandle()) {\n @if (model().reconnectable === true || model().reconnectable === 'source') {\n <svg:circle\n class=\"reconnect-handle\"\n r=\"10\"\n [attr.cx]=\"model().sourceHandle()!.pointAbsolute().x\"\n [attr.cy]=\"model().sourceHandle()!.pointAbsolute().y\"\n (pointerStart)=\"startReconnection($event, model().targetHandle()!)\" />\n }\n\n @if (model().reconnectable === true || model().reconnectable === 'target') {\n <svg:circle\n class=\"reconnect-handle\"\n r=\"10\"\n [attr.cx]=\"model().targetHandle()!.pointAbsolute().x\"\n [attr.cy]=\"model().targetHandle()!.pointAbsolute().y\"\n (pointerStart)=\"startReconnection($event, model().sourceHandle()!)\" />\n }\n}\n", styles: [".edge{fill:none;stroke-width:2;stroke:#b1b1b7}.edge_selected{stroke-width:2.5;stroke:#0f4c75}.interactive-edge{fill:none;stroke-width:20;stroke:transparent}.reconnect-handle{fill:transparent;cursor:move}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: EdgeLabelComponent, selector: "g[edgeLabel]", inputs: ["model", "edgeModel", "point", "htmlTemplate"] }, { kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2225
+ }
2226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeComponent, decorators: [{
2227
+ type: Component,
2228
+ args: [{ standalone: true, selector: 'g[edge]', changeDetection: ChangeDetectionStrategy.OnPush, host: {
2229
+ class: 'selectable',
2230
+ '[style.visibility]': 'isReconnecting() ? "hidden" : "visible"',
2231
+ }, imports: [NgTemplateOutlet, EdgeLabelComponent, PointerDirective], template: "@if (model().type === 'default') {\n <svg:path\n class=\"edge\"\n [attr.d]=\"model().path().path\"\n [attr.marker-start]=\"model().markerStartUrl()\"\n [attr.marker-end]=\"model().markerStartUrl()\"\n [class.edge_selected]=\"model().selected()\" />\n\n <svg:path class=\"interactive-edge\" [attr.d]=\"model().path().path\" (pointerStart)=\"select(); pull()\" />\n}\n\n@if (model().type === 'template' && edgeTemplate()) {\n @if (edgeTemplate(); as edgeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"edgeTemplate\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n }\n}\n\n@if (model().edgeLabels.start; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.start\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.center; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.center\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().edgeLabels.end; as label) {\n <svg:g\n edgeLabel\n [model]=\"label\"\n [point]=\"model().path().points.end\"\n [edgeModel]=\"model()\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate()\" />\n}\n\n@if (model().sourceHandle() && model().targetHandle()) {\n @if (model().reconnectable === true || model().reconnectable === 'source') {\n <svg:circle\n class=\"reconnect-handle\"\n r=\"10\"\n [attr.cx]=\"model().sourceHandle()!.pointAbsolute().x\"\n [attr.cy]=\"model().sourceHandle()!.pointAbsolute().y\"\n (pointerStart)=\"startReconnection($event, model().targetHandle()!)\" />\n }\n\n @if (model().reconnectable === true || model().reconnectable === 'target') {\n <svg:circle\n class=\"reconnect-handle\"\n r=\"10\"\n [attr.cx]=\"model().targetHandle()!.pointAbsolute().x\"\n [attr.cy]=\"model().targetHandle()!.pointAbsolute().y\"\n (pointerStart)=\"startReconnection($event, model().sourceHandle()!)\" />\n }\n}\n", styles: [".edge{fill:none;stroke-width:2;stroke:#b1b1b7}.edge_selected{stroke-width:2.5;stroke:#0f4c75}.interactive-edge{fill:none;stroke-width:20;stroke:transparent}.reconnect-handle{fill:transparent;cursor:move}\n"] }]
2232
+ }] });
2233
+
2234
+ class HandleService {
2235
+ constructor() {
2236
+ this.node = signal(null);
2237
+ }
2238
+ createHandle(newHandle) {
2239
+ const node = this.node();
2240
+ if (node) {
2241
+ node.handles.update((handles) => [...handles, newHandle]);
2242
+ }
2243
+ }
2244
+ destroyHandle(handleToDestoy) {
2245
+ const node = this.node();
2246
+ if (node) {
2247
+ node.handles.update((handles) => handles.filter((handle) => handle !== handleToDestoy));
2248
+ }
2249
+ }
2250
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2251
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleService }); }
2252
+ }
2253
+ __decorate([
2254
+ Microtask // TODO fixes rendering of handle for group node
2255
+ ], HandleService.prototype, "createHandle", null);
2256
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleService, decorators: [{
2257
+ type: Injectable
2258
+ }], propDecorators: { createHandle: [] } });
2259
+
2260
+ class HandleSizeControllerDirective {
2261
+ constructor() {
2262
+ this.handleModel = input.required({
2263
+ alias: 'handleSizeController',
2264
+ });
2265
+ this.handleWrapper = inject(ElementRef);
2266
+ }
2267
+ ngAfterViewInit() {
2268
+ const element = this.handleWrapper.nativeElement;
2269
+ const rect = element.getBBox();
2270
+ const stroke = getChildStrokeWidth(element);
2271
+ this.handleModel().size.set({
2272
+ width: rect.width + stroke,
2273
+ height: rect.height + stroke,
2274
+ });
2275
+ }
2276
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2277
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type: HandleSizeControllerDirective, isStandalone: true, selector: "[handleSizeController]", inputs: { handleModel: { classPropertyName: "handleModel", publicName: "handleSizeController", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
2278
+ }
2279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, decorators: [{
2280
+ type: Directive,
2281
+ args: [{
2282
+ standalone: true,
2283
+ selector: '[handleSizeController]',
2284
+ }]
2285
+ }] });
2286
+ function getChildStrokeWidth(element) {
2287
+ const child = element.firstElementChild;
2288
+ if (child) {
2289
+ const stroke = getComputedStyle(child).strokeWidth;
2290
+ const strokeAsNumber = Number(stroke.replace('px', ''));
2291
+ if (isNaN(strokeAsNumber)) {
2292
+ return 0;
2293
+ }
2294
+ return strokeAsNumber;
2295
+ }
2296
+ return 0;
2297
+ }
2298
+
2299
+ class DefaultNodeComponent {
2300
+ constructor() {
2301
+ this.selected = input(false);
2302
+ }
2303
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2304
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: DefaultNodeComponent, isStandalone: true, selector: "default-node", inputs: { selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.selected": "selected()" } }, ngImport: i0, template: "<ng-content />\n", styles: [":host{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}:host(.selected){border-width:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2305
+ }
2306
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultNodeComponent, decorators: [{
2307
+ type: Component,
2308
+ args: [{ standalone: true, selector: 'default-node', host: {
2309
+ '[class.selected]': 'selected()',
2310
+ }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content />\n", styles: [":host{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}:host(.selected){border-width:2px}\n"] }]
2311
+ }] });
2312
+
2194
2313
  class ResizableComponent {
2195
2314
  get model() {
2196
2315
  return this.nodeAccessor.model();
@@ -2589,7 +2708,9 @@ class NodeComponent {
2589
2708
  this.nodeTemplate = input();
2590
2709
  this.groupNodeTemplate = input();
2591
2710
  this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
2592
- this.flowStatusService.status().state === 'connection-validation');
2711
+ this.flowStatusService.status().state === 'connection-validation' ||
2712
+ this.flowStatusService.status().state === 'reconnection-start' ||
2713
+ this.flowStatusService.status().state === 'reconnection-validation');
2593
2714
  this.toolbar = computed(() => this.overlaysService.nodeToolbars().get(this.model()));
2594
2715
  }
2595
2716
  ngOnInit() {
@@ -2632,7 +2753,7 @@ class NodeComponent {
2632
2753
  }
2633
2754
  }
2634
2755
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2635
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NodeComponent, isStandalone: true, selector: "g[node]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, groupNodeTemplate: { classPropertyName: "groupNodeTemplate", publicName: "groupNodeTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "vflow-node" }, providers: [HandleService, NodeAccessorService], ngImport: i0, template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- Template node -->\n@if (model().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: { node: model().node, selected: model().selected },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().node.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n node: model().node,\n selected: model().selected,\n width: model().width,\n height: model().height,\n },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@if (toolbar(); as toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"], dependencies: [{ kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: NodeHandlesControllerDirective, selector: "[nodeHandlesController]" }, { kind: "directive", type: NodeResizeControllerDirective, selector: "[nodeResizeController]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2756
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NodeComponent, isStandalone: true, selector: "g[node]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, groupNodeTemplate: { classPropertyName: "groupNodeTemplate", publicName: "groupNodeTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "vflow-node" }, providers: [HandleService, NodeAccessorService], ngImport: i0, template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- Template node -->\n@if (model().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().node.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@if (toolbar(); as toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"], dependencies: [{ kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: NodeHandlesControllerDirective, selector: "[nodeHandlesController]" }, { kind: "directive", type: NodeResizeControllerDirective, selector: "[nodeResizeController]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2636
2757
  }
2637
2758
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, decorators: [{
2638
2759
  type: Component,
@@ -2648,7 +2769,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2648
2769
  HandleSizeControllerDirective,
2649
2770
  NodeHandlesControllerDirective,
2650
2771
  NodeResizeControllerDirective,
2651
- ], template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- Template node -->\n@if (model().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: { node: model().node, selected: model().selected },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().node.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n node: model().node,\n selected: model().selected,\n width: model().width,\n height: model().height,\n },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@if (toolbar(); as toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
2772
+ ], template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- Template node -->\n@if (model().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().node.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@if (toolbar(); as toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
2652
2773
  }] });
2653
2774
 
2654
2775
  class ConnectionComponent {
@@ -2659,7 +2780,7 @@ class ConnectionComponent {
2659
2780
  this.spacePointContext = inject(SpacePointContextDirective);
2660
2781
  this.path = computed(() => {
2661
2782
  const status = this.flowStatusService.status();
2662
- if (status.state === 'connection-start') {
2783
+ if (status.state === 'connection-start' || status.state === 'reconnection-start') {
2663
2784
  const sourceHandle = status.payload.sourceHandle;
2664
2785
  const sourcePoint = sourceHandle.pointAbsolute();
2665
2786
  const sourcePosition = sourceHandle.rawHandle.position;
@@ -2676,7 +2797,7 @@ class ConnectionComponent {
2676
2797
  return smoothStepPath(sourcePoint, targetPoint, sourcePosition, targetPosition, 0).path;
2677
2798
  }
2678
2799
  }
2679
- if (status.state === 'connection-validation') {
2800
+ if (status.state === 'connection-validation' || status.state === 'reconnection-validation') {
2680
2801
  const sourceHandle = status.payload.sourceHandle;
2681
2802
  const sourcePoint = sourceHandle.pointAbsolute();
2682
2803
  const sourcePosition = sourceHandle.rawHandle.position;
@@ -2710,6 +2831,7 @@ class ConnectionComponent {
2710
2831
  });
2711
2832
  this.defaultColor = 'rgb(177, 177, 183)';
2712
2833
  }
2834
+ // TODO: move context to model
2713
2835
  getContext() {
2714
2836
  return {
2715
2837
  $implicit: {
@@ -2950,7 +3072,7 @@ class RootSvgContextDirective {
2950
3072
  // TODO: check for multiple instances on page
2951
3073
  resetConnection() {
2952
3074
  const status = this.flowStatusService.status();
2953
- if (status.state === 'connection-start') {
3075
+ if (status.state === 'connection-start' || status.state === 'reconnection-start') {
2954
3076
  this.flowStatusService.setIdleStatus();
2955
3077
  }
2956
3078
  }
@@ -3016,6 +3138,7 @@ class VflowComponent {
3016
3138
  this.nodesChangeService = inject(NodesChangeService);
3017
3139
  this.edgesChangeService = inject(EdgeChangesService);
3018
3140
  this.nodeRenderingService = inject(NodeRenderingService);
3141
+ this.edgeRenderingService = inject(EdgeRenderingService);
3019
3142
  this.flowSettingsService = inject(FlowSettingsService);
3020
3143
  this.componentEventBusService = inject(ComponentEventBusService);
3021
3144
  this.keyboardService = inject(KeyboardService);
@@ -3026,7 +3149,7 @@ class VflowComponent {
3026
3149
  this.nodeModels = computed(() => this.nodeRenderingService.nodes());
3027
3150
  this.groups = computed(() => this.nodeRenderingService.groups());
3028
3151
  this.nonGroups = computed(() => this.nodeRenderingService.nonGroups());
3029
- this.edgeModels = computed(() => this.flowEntitiesService.validEdges());
3152
+ this.edgeModels = computed(() => this.edgeRenderingService.edges());
3030
3153
  // #endregion
3031
3154
  // #region OUTPUTS
3032
3155
  /**
@@ -3144,6 +3267,12 @@ class VflowComponent {
3144
3267
  set elevateNodesOnSelect(value) {
3145
3268
  this.flowSettingsService.elevateNodesOnSelect.set(value);
3146
3269
  }
3270
+ /**
3271
+ * Raizing z-index for selected edge
3272
+ */
3273
+ set elevateEdgesOnSelect(value) {
3274
+ this.flowSettingsService.elevateEdgesOnSelect.set(value);
3275
+ }
3147
3276
  // #endregion
3148
3277
  // #region MAIN_INPUTS
3149
3278
  /**
@@ -3245,7 +3374,7 @@ class VflowComponent {
3245
3374
  });
3246
3375
  }
3247
3376
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3248
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: VflowComponent, isStandalone: true, selector: "vflow", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: false, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: false, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: false, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: false, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: false, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: false, isRequired: false, transformFunction: (settings) => new ConnectionModel(settings) }, snapGrid: { classPropertyName: "snapGrid", publicName: "snapGrid", isSignal: false, isRequired: false, transformFunction: null }, elevateNodesOnSelect: { classPropertyName: "elevateNodesOnSelect", publicName: "elevateNodesOnSelect", isSignal: false, isRequired: false, transformFunction: null }, nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: false, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
3377
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: VflowComponent, isStandalone: true, selector: "vflow", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: false, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: false, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: false, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: false, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: false, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: false, isRequired: false, transformFunction: (settings) => new ConnectionModel(settings) }, snapGrid: { classPropertyName: "snapGrid", publicName: "snapGrid", isSignal: false, isRequired: false, transformFunction: null }, elevateNodesOnSelect: { classPropertyName: "elevateNodesOnSelect", publicName: "elevateNodesOnSelect", isSignal: false, isRequired: false, transformFunction: null }, elevateEdgesOnSelect: { classPropertyName: "elevateEdgesOnSelect", publicName: "elevateEdgesOnSelect", isSignal: false, isRequired: false, transformFunction: null }, nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: false, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
3249
3378
  DraggableService,
3250
3379
  ViewportService,
3251
3380
  FlowStatusService,
@@ -3253,6 +3382,7 @@ class VflowComponent {
3253
3382
  NodesChangeService,
3254
3383
  EdgeChangesService,
3255
3384
  NodeRenderingService,
3385
+ EdgeRenderingService,
3256
3386
  SelectionService,
3257
3387
  FlowSettingsService,
3258
3388
  ComponentEventBusService,
@@ -3270,6 +3400,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3270
3400
  NodesChangeService,
3271
3401
  EdgeChangesService,
3272
3402
  NodeRenderingService,
3403
+ EdgeRenderingService,
3273
3404
  SelectionService,
3274
3405
  FlowSettingsService,
3275
3406
  ComponentEventBusService,
@@ -3310,6 +3441,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3310
3441
  type: Input
3311
3442
  }], elevateNodesOnSelect: [{
3312
3443
  type: Input
3444
+ }], elevateEdgesOnSelect: [{
3445
+ type: Input
3313
3446
  }], nodes: [{
3314
3447
  type: Input,
3315
3448
  args: [{ required: true }]
@@ -3348,8 +3481,12 @@ class SelectableDirective {
3348
3481
  this.selectionService = inject(SelectionService);
3349
3482
  this.parentEdge = inject(EdgeComponent, { optional: true });
3350
3483
  this.parentNode = inject(NodeComponent, { optional: true });
3484
+ this.host = inject(ElementRef);
3485
+ this.selectOnEvent = this.getEvent$()
3486
+ .pipe(tap(() => this.select()), takeUntilDestroyed())
3487
+ .subscribe();
3351
3488
  }
3352
- onMousedown() {
3489
+ select() {
3353
3490
  const entity = this.entity();
3354
3491
  if (entity && this.flowSettingsService.entitiesSelectable()) {
3355
3492
  this.selectionService.select(entity);
@@ -3364,8 +3501,11 @@ class SelectableDirective {
3364
3501
  }
3365
3502
  return null;
3366
3503
  }
3504
+ getEvent$() {
3505
+ return merge(fromEvent(this.host.nativeElement, 'mousedown'), fromEvent(this.host.nativeElement, 'touchstart'));
3506
+ }
3367
3507
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3368
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: SelectableDirective, isStandalone: true, selector: "[selectable]", host: { listeners: { "mousedown": "onMousedown()", "touchstart": "onMousedown()" } }, ngImport: i0 }); }
3508
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: SelectableDirective, isStandalone: true, selector: "[selectable]", ngImport: i0 }); }
3369
3509
  }
3370
3510
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectableDirective, decorators: [{
3371
3511
  type: Directive,
@@ -3373,13 +3513,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3373
3513
  standalone: true,
3374
3514
  selector: '[selectable]',
3375
3515
  }]
3376
- }], propDecorators: { onMousedown: [{
3377
- type: HostListener,
3378
- args: ['mousedown']
3379
- }, {
3380
- type: HostListener,
3381
- args: ['touchstart']
3382
- }] } });
3516
+ }] });
3383
3517
 
3384
3518
  class MinimapModel {
3385
3519
  constructor() {
@@ -3585,6 +3719,7 @@ const Vflow = [
3585
3719
  SelectableDirective,
3586
3720
  MiniMapComponent,
3587
3721
  NodeToolbarComponent,
3722
+ CustomTemplateEdgeComponent,
3588
3723
  DragHandleDirective,
3589
3724
  ConnectionControllerDirective,
3590
3725
  NodeHtmlTemplateDirective,
@@ -3595,6 +3730,30 @@ const Vflow = [
3595
3730
  HandleTemplateDirective,
3596
3731
  ];
3597
3732
 
3733
+ class CustomTemplateEdgeComponent {
3734
+ constructor() {
3735
+ this.edge = inject(EdgeComponent);
3736
+ this.flowSettingsService = inject(FlowSettingsService);
3737
+ this.edgeRenderingService = inject(EdgeRenderingService);
3738
+ this.model = this.edge.model();
3739
+ this.context = this.model.context.$implicit;
3740
+ }
3741
+ pull() {
3742
+ if (this.flowSettingsService.elevateEdgesOnSelect()) {
3743
+ this.edgeRenderingService.pull(this.model);
3744
+ }
3745
+ }
3746
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CustomTemplateEdgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3747
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: CustomTemplateEdgeComponent, isStandalone: true, selector: "g[customTemplateEdge]", host: { listeners: { "mousedown": "pull()", "touchstart": "pull()" } }, ngImport: i0, template: "<ng-content />\n\n<svg:path #interactiveEdge class=\"interactive-edge\" [attr.d]=\"context.path()\" />\n", styles: [".interactive-edge{fill:none;stroke-width:20;stroke:transparent}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3748
+ }
3749
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CustomTemplateEdgeComponent, decorators: [{
3750
+ type: Component,
3751
+ args: [{ selector: 'g[customTemplateEdge]', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, host: {
3752
+ '(mousedown)': 'pull()',
3753
+ '(touchstart)': 'pull()',
3754
+ }, template: "<ng-content />\n\n<svg:path #interactiveEdge class=\"interactive-edge\" [attr.d]=\"context.path()\" />\n", styles: [".interactive-edge{fill:none;stroke-width:20;stroke:transparent}\n"] }]
3755
+ }] });
3756
+
3598
3757
  const mockModel = () => new NodeModel({ id: 'mock', type: 'default', point: { x: 0, y: 0 } });
3599
3758
  function provideCustomNodeMocks() {
3600
3759
  return [
@@ -3794,7 +3953,7 @@ class VflowMockComponent {
3794
3953
  return signal(value);
3795
3954
  }
3796
3955
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowMockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3797
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: VflowMockComponent, isStandalone: true, selector: "vflow", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: false, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: false, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: false, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: false, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: false, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: false, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: false, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: false, isRequired: false, transformFunction: (settings) => new ConnectionModel(settings) }, snapGrid: { classPropertyName: "snapGrid", publicName: "snapGrid", isSignal: false, isRequired: false, transformFunction: null }, elevateNodesOnSelect: { classPropertyName: "elevateNodesOnSelect", publicName: "elevateNodesOnSelect", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateMockDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
3956
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: VflowMockComponent, isStandalone: true, selector: "vflow", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: false, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: false, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: false, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: false, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: false, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: false, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: false, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: false, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: false, isRequired: false, transformFunction: (settings) => new ConnectionModel(settings) }, snapGrid: { classPropertyName: "snapGrid", publicName: "snapGrid", isSignal: false, isRequired: false, transformFunction: null }, elevateNodesOnSelect: { classPropertyName: "elevateNodesOnSelect", publicName: "elevateNodesOnSelect", isSignal: false, isRequired: false, transformFunction: null }, elevateEdgesOnSelect: { classPropertyName: "elevateEdgesOnSelect", publicName: "elevateEdgesOnSelect", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateMockDirective, descendants: true, isSignal: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateMockDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
3798
3957
  <ng-content />
3799
3958
 
3800
3959
  @for (node of nodes; track $index) {
@@ -4005,6 +4164,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
4005
4164
  type: Input
4006
4165
  }], elevateNodesOnSelect: [{
4007
4166
  type: Input
4167
+ }], elevateEdgesOnSelect: [{
4168
+ type: Input
4008
4169
  }] } });
4009
4170
 
4010
4171
  class HandleMockComponent {
@@ -4099,17 +4260,21 @@ class ConnectionControllerMockDirective {
4099
4260
  constructor() {
4100
4261
  // eslint-disable-next-line @angular-eslint/no-output-on-prefix
4101
4262
  this.onConnect = output();
4263
+ // eslint-disable-next-line @angular-eslint/no-output-on-prefix
4264
+ this.onReconnect = output();
4102
4265
  }
4103
4266
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
4104
4267
  startConnection(handle) { }
4105
4268
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
4269
+ startReconnection(handle) { }
4270
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4106
4271
  validateConnection(handle) { }
4107
4272
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
4108
4273
  resetValidateConnection(targetHandle) { }
4109
4274
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
4110
4275
  endConnection() { }
4111
4276
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
4112
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerMockDirective, isStandalone: true, selector: "[onConnect]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
4277
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerMockDirective, isStandalone: true, selector: "[onConnect]", outputs: { onConnect: "onConnect", onReconnect: "onReconnect" }, ngImport: i0 }); }
4113
4278
  }
4114
4279
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerMockDirective, decorators: [{
4115
4280
  type: Directive,
@@ -4160,5 +4325,5 @@ const VflowMocks = [
4160
4325
  * Generated bundle index. Do not edit.
4161
4326
  */
4162
4327
 
4163
- export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionControllerMockDirective, ConnectionTemplateDirective, ConnectionTemplateMockDirective, CustomDynamicNodeComponent, CustomNodeComponent, DragHandleDirective, DragHandleMockDirective, EdgeLabelHtmlTemplateDirective, EdgeLabelHtmlTemplateMockDirective, EdgeTemplateDirective, EdgeTemplateMockDirective, GroupNodeTemplateDirective, GroupNodeTemplateMockDirective, HandleComponent, HandleMockComponent, HandleTemplateDirective, HandleTemplateMockDirective, MiniMapComponent, MiniMapMockComponent, NodeHtmlTemplateDirective, NodeHtmlTemplateMockDirective, NodeToolbarComponent, NodeToolbarMockComponent, NodeToolbarWrapperDirective, ResizableComponent, ResizableMockComponent, SelectableDirective, SelectableMockDirective, Vflow, VflowComponent, VflowMockComponent, VflowMocks, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, provideCustomNodeMocks };
4328
+ export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionControllerMockDirective, ConnectionTemplateDirective, ConnectionTemplateMockDirective, CustomDynamicNodeComponent, CustomNodeComponent, CustomTemplateEdgeComponent, DragHandleDirective, DragHandleMockDirective, EdgeLabelHtmlTemplateDirective, EdgeLabelHtmlTemplateMockDirective, EdgeTemplateDirective, EdgeTemplateMockDirective, GroupNodeTemplateDirective, GroupNodeTemplateMockDirective, HandleComponent, HandleMockComponent, HandleTemplateDirective, HandleTemplateMockDirective, MiniMapComponent, MiniMapMockComponent, NodeHtmlTemplateDirective, NodeHtmlTemplateMockDirective, NodeToolbarComponent, NodeToolbarMockComponent, NodeToolbarWrapperDirective, ResizableComponent, ResizableMockComponent, SelectableDirective, SelectableMockDirective, Vflow, VflowComponent, VflowMockComponent, VflowMocks, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, provideCustomNodeMocks };
4164
4329
  //# sourceMappingURL=ngx-vflow.mjs.map