ngx-vflow 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/esm2022/lib/vflow/components/node/node.component.mjs +24 -54
  2. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +5 -11
  3. package/esm2022/lib/vflow/directives/changes-controller.directive.mjs +55 -1
  4. package/esm2022/lib/vflow/directives/connection-controller.directive.mjs +37 -47
  5. package/esm2022/lib/vflow/directives/node-handles-controller.directive.mjs +33 -0
  6. package/esm2022/lib/vflow/directives/node-resize-controller.directive.mjs +35 -0
  7. package/esm2022/lib/vflow/directives/selectable.directive.mjs +2 -2
  8. package/esm2022/lib/vflow/models/handle.model.mjs +33 -33
  9. package/esm2022/lib/vflow/models/node.model.mjs +30 -67
  10. package/esm2022/lib/vflow/public-components/handle/handle.component.mjs +3 -3
  11. package/esm2022/lib/vflow/public-components/resizable/resizable.component.mjs +4 -3
  12. package/esm2022/lib/vflow/services/draggable.service.mjs +2 -2
  13. package/esm2022/lib/vflow/services/flow-status.service.mjs +1 -15
  14. package/esm2022/lib/vflow/services/handle.service.mjs +1 -1
  15. package/esm2022/lib/vflow/testing-utils/component-mocks/handle-mock.component.mjs +22 -0
  16. package/esm2022/lib/vflow/testing-utils/component-mocks/minimap-mock.component.mjs +22 -0
  17. package/esm2022/lib/vflow/testing-utils/component-mocks/node-toolbar-mock.component.mjs +19 -0
  18. package/esm2022/lib/vflow/testing-utils/component-mocks/resizable-mock.component.mjs +21 -0
  19. package/esm2022/lib/vflow/testing-utils/component-mocks/vflow-mock.component.mjs +251 -0
  20. package/esm2022/lib/vflow/testing-utils/directive-mocks/connection-controller-mock.directive.mjs +15 -0
  21. package/esm2022/lib/vflow/testing-utils/directive-mocks/drag-handle-mock.directive.mjs +11 -0
  22. package/esm2022/lib/vflow/testing-utils/directive-mocks/selectable-mock.directive.mjs +14 -0
  23. package/esm2022/lib/vflow/testing-utils/directive-mocks/template-mock.directive.mjs +87 -0
  24. package/esm2022/lib/vflow/testing-utils/provide-custom-node-mocks.mjs +9 -8
  25. package/esm2022/lib/vflow/testing-utils/vflow-mocks.mjs +26 -0
  26. package/esm2022/lib/vflow/vflow.mjs +3 -1
  27. package/esm2022/public-api.mjs +11 -1
  28. package/fesm2022/ngx-vflow.mjs +857 -378
  29. package/fesm2022/ngx-vflow.mjs.map +1 -1
  30. package/lib/vflow/components/node/node.component.d.ts +6 -12
  31. package/lib/vflow/components/vflow/vflow.component.d.ts +4 -5
  32. package/lib/vflow/directives/changes-controller.directive.d.ts +54 -0
  33. package/lib/vflow/directives/connection-controller.directive.d.ts +4 -5
  34. package/lib/vflow/directives/node-handles-controller.directive.d.ts +11 -0
  35. package/lib/vflow/directives/node-resize-controller.directive.d.ts +11 -0
  36. package/lib/vflow/models/handle.model.d.ts +8 -23
  37. package/lib/vflow/models/node.model.d.ts +17 -13
  38. package/lib/vflow/services/flow-status.service.d.ts +0 -6
  39. package/lib/vflow/services/handle.service.d.ts +1 -1
  40. package/lib/vflow/testing-utils/component-mocks/handle-mock.component.d.ts +11 -0
  41. package/lib/vflow/testing-utils/component-mocks/minimap-mock.component.d.ts +10 -0
  42. package/lib/vflow/testing-utils/component-mocks/node-toolbar-mock.component.d.ts +7 -0
  43. package/lib/vflow/testing-utils/component-mocks/resizable-mock.component.d.ts +8 -0
  44. package/lib/vflow/testing-utils/component-mocks/vflow-mock.component.d.ts +48 -0
  45. package/lib/vflow/testing-utils/directive-mocks/connection-controller-mock.directive.d.ts +7 -0
  46. package/lib/vflow/testing-utils/directive-mocks/drag-handle-mock.directive.d.ts +5 -0
  47. package/lib/vflow/testing-utils/directive-mocks/selectable-mock.directive.d.ts +5 -0
  48. package/lib/vflow/testing-utils/directive-mocks/template-mock.directive.d.ts +32 -0
  49. package/lib/vflow/testing-utils/vflow-mocks.d.ts +10 -0
  50. package/lib/vflow/vflow.d.ts +2 -1
  51. package/package.json +1 -1
  52. package/public-api.d.ts +10 -0
@@ -1,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, output, DestroyRef, EventEmitter, OutputEmitterRef, input, viewChild, Component, ChangeDetectionStrategy, Injector, runInInjectionContext, HostListener, NgZone, contentChild, Input, forwardRef } from '@angular/core';
2
+ import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, DestroyRef, EventEmitter, OutputEmitterRef, input, viewChild, Component, ChangeDetectionStrategy, Injector, output, HostListener, runInInjectionContext, NgZone, contentChild, Input, forwardRef } from '@angular/core';
3
3
  import { select } from 'd3-selection';
4
4
  import { zoomIdentity, zoom } from 'd3-zoom';
5
- import { switchMap, merge, fromEvent, tap, Subject, Observable, observeOn, animationFrameScheduler, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, share, startWith, of } from 'rxjs';
6
- import { toObservable, takeUntilDestroyed, toSignal, outputFromObservable } from '@angular/core/rxjs-interop';
5
+ import { switchMap, merge, fromEvent, tap, Subject, Observable, skip, map, pairwise, filter, distinctUntilChanged, observeOn, asyncScheduler, zip, animationFrameScheduler, share, startWith, of } from 'rxjs';
6
+ import { toObservable, takeUntilDestroyed, outputFromObservable, toSignal } from '@angular/core/rxjs-interop';
7
7
  import { drag } from 'd3-drag';
8
8
  import { __decorate } from 'tslib';
9
9
  import { NgTemplateOutlet, NgComponentOutlet, KeyValuePipe } from '@angular/common';
@@ -545,7 +545,7 @@ function moveNode(model, point) {
545
545
  point.y = Math.min(parent.size().height - model.size().height, point.y);
546
546
  point.y = Math.max(0, point.y);
547
547
  }
548
- model.setPoint(point, true);
548
+ model.setPoint(point);
549
549
  }
550
550
 
551
551
  class EdgeTemplateDirective {
@@ -666,173 +666,6 @@ class FlowStatusService {
666
666
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FlowStatusService, decorators: [{
667
667
  type: Injectable
668
668
  }] });
669
- /**
670
- * Batch status changes together to call them one after another
671
- *
672
- * @param changes list of set[FlowStatus.state]Status() calls
673
- */
674
- function batchStatusChanges(...changes) {
675
- if (changes.length) {
676
- const [firstChange, ...restChanges] = changes;
677
- // first change is sync
678
- firstChange();
679
- // without timer, subscribed effects/comuted signals only get latest value
680
- restChanges.forEach((change) => setTimeout(() => change()));
681
- }
682
- }
683
-
684
- /**
685
- * This function contains a hack-y behavior.
686
- * If the handles are of the same type (source-source or target-target),
687
- * it returns nodes where source === target and
688
- * handles where sourceHandle === targetHandle
689
- *
690
- * This leads to that notSelfValidator returns false for these cases,
691
- * exactly what we need for strict connection type
692
- */
693
- function adjustDirection(connection) {
694
- const result = {};
695
- if (connection.sourceHandle.rawHandle.type === 'source') {
696
- result.source = connection.source;
697
- result.sourceHandle = connection.sourceHandle;
698
- }
699
- else {
700
- result.source = connection.target;
701
- result.sourceHandle = connection.targetHandle;
702
- }
703
- if (connection.targetHandle.rawHandle.type === 'target') {
704
- result.target = connection.target;
705
- result.targetHandle = connection.targetHandle;
706
- }
707
- else {
708
- result.target = connection.source;
709
- result.targetHandle = connection.sourceHandle;
710
- }
711
- return result;
712
- }
713
-
714
- class ConnectionControllerDirective {
715
- constructor() {
716
- /**
717
- * This event fires when user tries to create new Edge.
718
- *
719
- * `Connection` is an entity that contains data about source and target nodes.
720
- *
721
- * Also it's important to note, that this event only fires when connection is valid by validator function in `ConnectionSettings`,
722
- * by default without passing the validator every connection concidered valid.
723
- *
724
- * @todo add connect event and deprecate onConnect
725
- */
726
- // eslint-disable-next-line @angular-eslint/no-output-on-prefix
727
- this.onConnect = output();
728
- this.statusService = inject(FlowStatusService);
729
- this.flowEntitiesService = inject(FlowEntitiesService);
730
- this.connectEffect = effect(() => {
731
- const status = this.statusService.status();
732
- if (status.state === 'connection-end') {
733
- let source = status.payload.source;
734
- let target = status.payload.target;
735
- let sourceHandle = status.payload.sourceHandle;
736
- let targetHandle = status.payload.targetHandle;
737
- if (this.isStrictMode()) {
738
- const adjusted = adjustDirection({
739
- source: status.payload.source,
740
- sourceHandle: status.payload.sourceHandle,
741
- target: status.payload.target,
742
- targetHandle: status.payload.targetHandle,
743
- });
744
- source = adjusted.source;
745
- target = adjusted.target;
746
- sourceHandle = adjusted.sourceHandle;
747
- targetHandle = adjusted.targetHandle;
748
- }
749
- const sourceId = source.node.id;
750
- const targetId = target.node.id;
751
- const sourceHandleId = sourceHandle.rawHandle.id;
752
- const targetHandleId = targetHandle.rawHandle.id;
753
- const connectionModel = this.flowEntitiesService.connection();
754
- const connection = {
755
- source: sourceId,
756
- target: targetId,
757
- sourceHandle: sourceHandleId,
758
- targetHandle: targetHandleId,
759
- };
760
- if (connectionModel.validator(connection)) {
761
- this.onConnect.emit(connection);
762
- }
763
- }
764
- }, { allowSignalWrites: true });
765
- this.isStrictMode = computed(() => this.flowEntitiesService.connection().mode === 'strict');
766
- }
767
- startConnection(handle) {
768
- this.statusService.setConnectionStartStatus(handle.parentNode, handle);
769
- }
770
- validateConnection(handle) {
771
- const status = this.statusService.status();
772
- if (status.state === 'connection-start') {
773
- let source = status.payload.source;
774
- let target = handle.parentNode;
775
- let sourceHandle = status.payload.sourceHandle;
776
- let targetHandle = handle;
777
- if (this.isStrictMode()) {
778
- // swap direction (if needed) according to actual source and target of strict mode
779
- const adjusted = adjustDirection({
780
- source: status.payload.source,
781
- sourceHandle: status.payload.sourceHandle,
782
- target: handle.parentNode,
783
- targetHandle: handle,
784
- });
785
- source = adjusted.source;
786
- target = adjusted.target;
787
- sourceHandle = adjusted.sourceHandle;
788
- targetHandle = adjusted.targetHandle;
789
- }
790
- const valid = this.flowEntitiesService.connection().validator({
791
- source: source.node.id,
792
- target: target.node.id,
793
- sourceHandle: sourceHandle.rawHandle.id,
794
- targetHandle: targetHandle.rawHandle.id,
795
- });
796
- // TODO: check how react flow handles highlight of handle
797
- // if direction changes
798
- handle.state.set(valid ? 'valid' : 'invalid');
799
- // status is about how we draw connection, so we don't need
800
- // swapped diretion here
801
- this.statusService.setConnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle);
802
- }
803
- }
804
- resetValidateConnection(targetHandle) {
805
- targetHandle.state.set('idle');
806
- // drop back to start status
807
- const status = this.statusService.status();
808
- if (status.state === 'connection-validation') {
809
- this.statusService.setConnectionStartStatus(status.payload.source, status.payload.sourceHandle);
810
- }
811
- }
812
- endConnection() {
813
- const status = this.statusService.status();
814
- if (status.state === 'connection-validation') {
815
- const source = status.payload.source;
816
- const sourceHandle = status.payload.sourceHandle;
817
- const target = status.payload.target;
818
- const targetHandle = status.payload.targetHandle;
819
- batchStatusChanges(
820
- // call to create connection
821
- () => this.statusService.setConnectionEndStatus(source, target, sourceHandle, targetHandle),
822
- // when connection created, we need go back to idle status
823
- () => this.statusService.setIdleStatus());
824
- }
825
- }
826
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
827
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[connectionController]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
828
- }
829
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerDirective, decorators: [{
830
- type: Directive,
831
- args: [{
832
- selector: '[connectionController]',
833
- standalone: true,
834
- }]
835
- }] });
836
669
 
837
670
  class ComponentEventBusService {
838
671
  constructor() {
@@ -994,26 +827,29 @@ function isTemplateDynamicGroupNode(node) {
994
827
  return node.type === 'template-group';
995
828
  }
996
829
 
997
- // TODO bad naming around points
998
830
  class NodeModel {
999
831
  static { this.defaultWidth = 100; }
1000
832
  static { this.defaultHeight = 50; }
1001
833
  static { this.defaultColor = '#1b262c'; }
1002
834
  constructor(node) {
1003
835
  this.node = node;
1004
- this.flowSettingsService = inject(FlowSettingsService);
1005
836
  this.entitiesService = inject(FlowEntitiesService);
1006
- this.internalPoint = this.createInternalPointSignal();
1007
- this.throttledPoint$ = toObservable(this.internalPoint).pipe(observeOn(animationFrameScheduler));
1008
- this.notThrottledPoint$ = new Subject();
1009
- this.point = toSignal(merge(this.throttledPoint$, this.notThrottledPoint$), {
1010
- initialValue: this.internalPoint(),
1011
- });
1012
- this.point$ = this.throttledPoint$;
1013
- this.size = signal({ width: 0, height: 0 });
837
+ this.point = this.createInternalPointSignal();
838
+ this.point$ = toObservable(this.point);
839
+ this.width = this.createWidthSignal(NodeModel.defaultWidth);
840
+ this.width$ = toObservable(this.width);
841
+ this.height = this.createHeightSignal(NodeModel.defaultHeight);
842
+ this.height$ = toObservable(this.height);
843
+ /**
844
+ * @deprecated use width or height signals
845
+ */
846
+ this.size = computed(() => ({ width: this.width(), height: this.height() }));
847
+ /**
848
+ * @deprecated use width$ or height$
849
+ */
1014
850
  this.size$ = toObservable(this.size);
1015
- this.width = computed(() => this.size().width);
1016
- this.height = computed(() => this.size().height);
851
+ this.styleWidth = computed(() => `${this.width()}px`);
852
+ this.styleHeight = computed(() => `${this.height()}px`);
1017
853
  this.renderOrder = signal(0);
1018
854
  this.selected = signal(false);
1019
855
  this.selected$ = toObservable(this.selected);
@@ -1083,56 +919,8 @@ class NodeModel {
1083
919
  }
1084
920
  }
1085
921
  }
1086
- setPoint(point, throttle) {
1087
- if (throttle) {
1088
- this.internalPoint.set(point);
1089
- }
1090
- else {
1091
- this.notThrottledPoint$.next(point);
1092
- }
1093
- }
1094
- /**
1095
- * TODO find the way to implement this better
1096
- */
1097
- linkDefaultNodeSizeWithModelSize() {
1098
- const node = this.node;
1099
- switch (node.type) {
1100
- case 'default':
1101
- case 'default-group':
1102
- case 'template-group': {
1103
- if (isDynamicNode(node)) {
1104
- effect(() => {
1105
- this.size.set({
1106
- width: node.width?.() ?? NodeModel.defaultWidth,
1107
- height: node.height?.() ?? NodeModel.defaultHeight,
1108
- });
1109
- }, { allowSignalWrites: true });
1110
- }
1111
- else {
1112
- this.size.set({
1113
- width: node.width ?? NodeModel.defaultWidth,
1114
- height: node.height ?? NodeModel.defaultHeight,
1115
- });
1116
- }
1117
- }
1118
- }
1119
- if (node.type === 'html-template' || this.isComponentType) {
1120
- if (isDynamicNode(node)) {
1121
- effect(() => {
1122
- if (node.width && node.height) {
1123
- this.size.set({
1124
- width: node.width(),
1125
- height: node.height(),
1126
- });
1127
- }
1128
- }, { allowSignalWrites: true });
1129
- }
1130
- else {
1131
- if (node.width && node.height) {
1132
- this.size.set({ width: node.width, height: node.height });
1133
- }
1134
- }
1135
- }
922
+ setPoint(point) {
923
+ this.point.set(point);
1136
924
  }
1137
925
  createTextSignal() {
1138
926
  const node = this.node;
@@ -1149,6 +937,16 @@ class NodeModel {
1149
937
  createInternalPointSignal() {
1150
938
  return isDynamicNode(this.node) ? this.node.point : signal({ x: this.node.point.x, y: this.node.point.y });
1151
939
  }
940
+ createWidthSignal(defaultValue) {
941
+ return isDynamicNode(this.node)
942
+ ? (this.node.width ?? signal(defaultValue))
943
+ : signal(this.node.width ?? defaultValue);
944
+ }
945
+ createHeightSignal(defaultValue) {
946
+ return isDynamicNode(this.node)
947
+ ? (this.node.height ?? signal(defaultValue))
948
+ : signal(this.node.height ?? defaultValue);
949
+ }
1152
950
  }
1153
951
 
1154
952
  class EdgeLabelModel {
@@ -1618,37 +1416,67 @@ class ChangesControllerDirective {
1618
1416
  this.onNodesChangePosition = outputFromObservable(this.nodeChangesOfType('position'), {
1619
1417
  alias: 'onNodesChange.position',
1620
1418
  });
1419
+ /**
1420
+ * @deprecated use `onNodesChange.position` instead
1421
+ */
1621
1422
  this.onNodesChangePositionSignle = outputFromObservable(this.singleChange(this.nodeChangesOfType('position')), { alias: 'onNodesChange.position.single' });
1423
+ /**
1424
+ * @deprecated use `onNodesChange.position` instead
1425
+ */
1622
1426
  this.onNodesChangePositionMany = outputFromObservable(this.manyChanges(this.nodeChangesOfType('position')), { alias: 'onNodesChange.position.many' });
1623
1427
  this.onNodesChangeSize = outputFromObservable(this.nodeChangesOfType('size'), {
1624
1428
  alias: 'onNodesChange.size',
1625
1429
  });
1430
+ /**
1431
+ * @deprecated use `onNodesChange.size` instead
1432
+ */
1626
1433
  this.onNodesChangeSizeSingle = outputFromObservable(this.singleChange(this.nodeChangesOfType('size')), {
1627
1434
  alias: 'onNodesChange.size.single',
1628
1435
  });
1436
+ /**
1437
+ * @deprecated use `onNodesChange.size` instead
1438
+ */
1629
1439
  this.onNodesChangeSizeMany = outputFromObservable(this.manyChanges(this.nodeChangesOfType('size')), {
1630
1440
  alias: 'onNodesChange.size.many',
1631
1441
  });
1632
1442
  this.onNodesChangeAdd = outputFromObservable(this.nodeChangesOfType('add'), {
1633
1443
  alias: 'onNodesChange.add',
1634
1444
  });
1445
+ /**
1446
+ * @deprecated use `onNodesChange.add` instead
1447
+ */
1635
1448
  this.onNodesChangeAddSingle = outputFromObservable(this.singleChange(this.nodeChangesOfType('add')), {
1636
1449
  alias: 'onNodesChange.add.single',
1637
1450
  });
1451
+ /**
1452
+ * @deprecated use `onNodesChange.add` instead
1453
+ */
1638
1454
  this.onNodesChangeAddMany = outputFromObservable(this.manyChanges(this.nodeChangesOfType('add')), {
1639
1455
  alias: 'onNodesChange.add.many',
1640
1456
  });
1641
1457
  this.onNodesChangeRemove = outputFromObservable(this.nodeChangesOfType('remove'), {
1642
1458
  alias: 'onNodesChange.remove',
1643
1459
  });
1460
+ /**
1461
+ * @deprecated use `onNodesChange.remove` instead
1462
+ */
1644
1463
  this.onNodesChangeRemoveSingle = outputFromObservable(this.singleChange(this.nodeChangesOfType('remove')), { alias: 'onNodesChange.remove.single' });
1464
+ /**
1465
+ * @deprecated use `onNodesChange.remove` instead
1466
+ */
1645
1467
  this.onNodesChangeRemoveMany = outputFromObservable(this.manyChanges(this.nodeChangesOfType('remove')), {
1646
1468
  alias: 'onNodesChange.remove.many',
1647
1469
  });
1648
1470
  this.onNodesChangeSelect = outputFromObservable(this.nodeChangesOfType('select'), {
1649
1471
  alias: 'onNodesChange.select',
1650
1472
  });
1473
+ /**
1474
+ * @deprecated use `onNodesChange.select` instead
1475
+ */
1651
1476
  this.onNodesChangeSelectSingle = outputFromObservable(this.singleChange(this.nodeChangesOfType('select')), { alias: 'onNodesChange.select.single' });
1477
+ /**
1478
+ * @deprecated use `onNodesChange.select` instead
1479
+ */
1652
1480
  this.onNodesChangeSelectMany = outputFromObservable(this.manyChanges(this.nodeChangesOfType('select')), {
1653
1481
  alias: 'onNodesChange.select.many',
1654
1482
  });
@@ -1659,32 +1487,56 @@ class ChangesControllerDirective {
1659
1487
  this.onNodesChangeDetached = outputFromObservable(this.edgeChangesOfType('detached'), {
1660
1488
  alias: 'onEdgesChange.detached',
1661
1489
  });
1490
+ /**
1491
+ * @deprecated use `onEdgesChange.detached` instead
1492
+ */
1662
1493
  this.onNodesChangeDetachedSingle = outputFromObservable(this.singleChange(this.edgeChangesOfType('detached')), { alias: 'onEdgesChange.detached.single' });
1494
+ /**
1495
+ * @deprecated use `onEdgesChange.detached` instead
1496
+ */
1663
1497
  this.onNodesChangeDetachedMany = outputFromObservable(this.manyChanges(this.edgeChangesOfType('detached')), { alias: 'onEdgesChange.detached.many' });
1664
1498
  this.onEdgesChangeAdd = outputFromObservable(this.edgeChangesOfType('add'), {
1665
1499
  alias: 'onEdgesChange.add',
1666
1500
  });
1501
+ /**
1502
+ * @deprecated use `onEdgesChange.add` instead
1503
+ */
1667
1504
  this.onEdgeChangeAddSingle = outputFromObservable(this.singleChange(this.edgeChangesOfType('add')), {
1668
1505
  alias: 'onEdgesChange.add.single',
1669
1506
  });
1507
+ /**
1508
+ * @deprecated use `onEdgesChange.add` instead
1509
+ */
1670
1510
  this.onEdgeChangeAddMany = outputFromObservable(this.manyChanges(this.edgeChangesOfType('add')), {
1671
1511
  alias: 'onEdgesChange.add.many',
1672
1512
  });
1673
1513
  this.onEdgeChangeRemove = outputFromObservable(this.edgeChangesOfType('remove'), {
1674
1514
  alias: 'onEdgesChange.remove',
1675
1515
  });
1516
+ /**
1517
+ * @deprecated use `onEdgesChange.remove` instead
1518
+ */
1676
1519
  this.onEdgeChangeRemoveSingle = outputFromObservable(this.singleChange(this.edgeChangesOfType('remove')), {
1677
1520
  alias: 'onEdgesChange.remove.single',
1678
1521
  });
1522
+ /**
1523
+ * @deprecated use `onEdgesChange.remove` instead
1524
+ */
1679
1525
  this.onEdgeChangeRemoveMany = outputFromObservable(this.manyChanges(this.edgeChangesOfType('remove')), {
1680
1526
  alias: 'onEdgesChange.remove.many',
1681
1527
  });
1682
1528
  this.onEdgeChangeSelect = outputFromObservable(this.edgeChangesOfType('select'), {
1683
1529
  alias: 'onEdgesChange.select',
1684
1530
  });
1531
+ /**
1532
+ * @deprecated use `onEdgesChange.select` instead
1533
+ */
1685
1534
  this.onEdgeChangeSelectSingle = outputFromObservable(this.singleChange(this.edgeChangesOfType('select')), {
1686
1535
  alias: 'onEdgesChange.select.single',
1687
1536
  });
1537
+ /**
1538
+ * @deprecated use `onEdgesChange.select` instead
1539
+ */
1688
1540
  this.onEdgeChangeSelectMany = outputFromObservable(this.manyChanges(this.edgeChangesOfType('select')), {
1689
1541
  alias: 'onEdgesChange.select.many',
1690
1542
  });
@@ -2005,55 +1857,169 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2005
1857
  type: Injectable
2006
1858
  }], propDecorators: { createHandle: [] } });
2007
1859
 
2008
- function resizable(elems, zone) {
2009
- return new Observable((subscriber) => {
2010
- const ro = new ResizeObserver((entries) => {
2011
- zone.run(() => subscriber.next(entries));
2012
- });
2013
- elems.forEach((e) => ro.observe(e));
2014
- return () => ro.disconnect();
2015
- });
2016
- }
2017
-
2018
- function InjectionContext(target, key, descriptor) {
2019
- const originalMethod = descriptor.value;
2020
- descriptor.value = function (...args) {
2021
- if (implementsWithInjector(this)) {
2022
- return runInInjectionContext(this.injector, () => originalMethod.apply(this, args));
2023
- }
2024
- else {
2025
- throw new Error('Class that contains decorated method must extends WithInjectorDirective class');
2026
- }
2027
- };
2028
- // Return the modified descriptor
2029
- return descriptor;
2030
- }
2031
- const implementsWithInjector = (instance) => {
2032
- return 'injector' in instance && 'get' in instance.injector;
2033
- };
2034
-
2035
- class HandleSizeControllerDirective {
2036
- constructor() {
2037
- this.handleModel = input.required({
2038
- alias: 'handleSizeController',
2039
- });
2040
- this.handleWrapper = inject(ElementRef);
1860
+ /**
1861
+ * This function contains a hack-y behavior.
1862
+ * If the handles are of the same type (source-source or target-target),
1863
+ * it returns nodes where source === target and
1864
+ * handles where sourceHandle === targetHandle
1865
+ *
1866
+ * This leads to that notSelfValidator returns false for these cases,
1867
+ * exactly what we need for strict connection type
1868
+ */
1869
+ function adjustDirection(connection) {
1870
+ const result = {};
1871
+ if (connection.sourceHandle.rawHandle.type === 'source') {
1872
+ result.source = connection.source;
1873
+ result.sourceHandle = connection.sourceHandle;
2041
1874
  }
2042
- ngAfterViewInit() {
2043
- const element = this.handleWrapper.nativeElement;
2044
- const rect = element.getBBox();
2045
- const stroke = getChildStrokeWidth(element);
2046
- this.handleModel().size.set({
2047
- width: rect.width + stroke,
2048
- height: rect.height + stroke,
2049
- });
1875
+ else {
1876
+ result.source = connection.target;
1877
+ result.sourceHandle = connection.targetHandle;
2050
1878
  }
2051
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2052
- 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 }); }
1879
+ if (connection.targetHandle.rawHandle.type === 'target') {
1880
+ result.target = connection.target;
1881
+ result.targetHandle = connection.targetHandle;
1882
+ }
1883
+ else {
1884
+ result.target = connection.source;
1885
+ result.targetHandle = connection.sourceHandle;
1886
+ }
1887
+ return result;
2053
1888
  }
2054
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, decorators: [{
2055
- type: Directive,
2056
- args: [{
1889
+
1890
+ class ConnectionControllerDirective {
1891
+ constructor() {
1892
+ this.statusService = inject(FlowStatusService);
1893
+ this.flowEntitiesService = inject(FlowEntitiesService);
1894
+ /**
1895
+ * This event fires when user tries to create new Edge.
1896
+ *
1897
+ * `Connection` is an entity that contains data about source and target nodes.
1898
+ *
1899
+ * Also it's important to note, that this event only fires when connection is valid by validator function in `ConnectionSettings`,
1900
+ * by default without passing the validator every connection concidered valid.
1901
+ *
1902
+ * @todo add connect event and deprecate onConnect
1903
+ */
1904
+ // eslint-disable-next-line @angular-eslint/no-output-on-prefix
1905
+ this.onConnect = outputFromObservable(toObservable(this.statusService.status).pipe(filter((status) => status.state === 'connection-end'), map((status) => {
1906
+ let source = status.payload.source;
1907
+ let target = status.payload.target;
1908
+ let sourceHandle = status.payload.sourceHandle;
1909
+ let targetHandle = status.payload.targetHandle;
1910
+ if (this.isStrictMode()) {
1911
+ const adjusted = adjustDirection({
1912
+ source: status.payload.source,
1913
+ sourceHandle: status.payload.sourceHandle,
1914
+ target: status.payload.target,
1915
+ targetHandle: status.payload.targetHandle,
1916
+ });
1917
+ source = adjusted.source;
1918
+ target = adjusted.target;
1919
+ sourceHandle = adjusted.sourceHandle;
1920
+ targetHandle = adjusted.targetHandle;
1921
+ }
1922
+ const sourceId = source.node.id;
1923
+ const targetId = target.node.id;
1924
+ const sourceHandleId = sourceHandle.rawHandle.id;
1925
+ const targetHandleId = targetHandle.rawHandle.id;
1926
+ return {
1927
+ source: sourceId,
1928
+ target: targetId,
1929
+ sourceHandle: sourceHandleId,
1930
+ targetHandle: targetHandleId,
1931
+ };
1932
+ }), tap(() => this.statusService.setIdleStatus()), filter((connection) => this.flowEntitiesService.connection().validator(connection))));
1933
+ this.isStrictMode = computed(() => this.flowEntitiesService.connection().mode === 'strict');
1934
+ }
1935
+ startConnection(handle) {
1936
+ this.statusService.setConnectionStartStatus(handle.parentNode, handle);
1937
+ }
1938
+ validateConnection(handle) {
1939
+ const status = this.statusService.status();
1940
+ if (status.state === 'connection-start') {
1941
+ let source = status.payload.source;
1942
+ let target = handle.parentNode;
1943
+ let sourceHandle = status.payload.sourceHandle;
1944
+ let targetHandle = handle;
1945
+ if (this.isStrictMode()) {
1946
+ // swap direction (if needed) according to actual source and target of strict mode
1947
+ const adjusted = adjustDirection({
1948
+ source: status.payload.source,
1949
+ sourceHandle: status.payload.sourceHandle,
1950
+ target: handle.parentNode,
1951
+ targetHandle: handle,
1952
+ });
1953
+ source = adjusted.source;
1954
+ target = adjusted.target;
1955
+ sourceHandle = adjusted.sourceHandle;
1956
+ targetHandle = adjusted.targetHandle;
1957
+ }
1958
+ const valid = this.flowEntitiesService.connection().validator({
1959
+ source: source.node.id,
1960
+ target: target.node.id,
1961
+ sourceHandle: sourceHandle.rawHandle.id,
1962
+ targetHandle: targetHandle.rawHandle.id,
1963
+ });
1964
+ // TODO: check how react flow handles highlight of handle
1965
+ // if direction changes
1966
+ handle.state.set(valid ? 'valid' : 'invalid');
1967
+ // status is about how we draw connection, so we don't need
1968
+ // swapped diretion here
1969
+ this.statusService.setConnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle);
1970
+ }
1971
+ }
1972
+ resetValidateConnection(targetHandle) {
1973
+ targetHandle.state.set('idle');
1974
+ // drop back to start status
1975
+ const status = this.statusService.status();
1976
+ if (status.state === 'connection-validation') {
1977
+ this.statusService.setConnectionStartStatus(status.payload.source, status.payload.sourceHandle);
1978
+ }
1979
+ }
1980
+ endConnection() {
1981
+ const status = this.statusService.status();
1982
+ if (status.state === 'connection-validation') {
1983
+ const source = status.payload.source;
1984
+ const sourceHandle = status.payload.sourceHandle;
1985
+ const target = status.payload.target;
1986
+ const targetHandle = status.payload.targetHandle;
1987
+ this.statusService.setConnectionEndStatus(source, target, sourceHandle, targetHandle);
1988
+ }
1989
+ }
1990
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1991
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[onConnect]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
1992
+ }
1993
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerDirective, decorators: [{
1994
+ type: Directive,
1995
+ args: [{
1996
+ selector: '[onConnect]',
1997
+ standalone: true,
1998
+ }]
1999
+ }] });
2000
+
2001
+ class HandleSizeControllerDirective {
2002
+ constructor() {
2003
+ this.handleModel = input.required({
2004
+ alias: 'handleSizeController',
2005
+ });
2006
+ this.handleWrapper = inject(ElementRef);
2007
+ }
2008
+ ngAfterViewInit() {
2009
+ const element = this.handleWrapper.nativeElement;
2010
+ const rect = element.getBBox();
2011
+ const stroke = getChildStrokeWidth(element);
2012
+ this.handleModel().size.set({
2013
+ width: rect.width + stroke,
2014
+ height: rect.height + stroke,
2015
+ });
2016
+ }
2017
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2018
+ 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 }); }
2019
+ }
2020
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleSizeControllerDirective, decorators: [{
2021
+ type: Directive,
2022
+ args: [{
2057
2023
  standalone: true,
2058
2024
  selector: '[handleSizeController]',
2059
2025
  }]
@@ -2219,8 +2185,9 @@ class ResizableComponent {
2219
2185
  return;
2220
2186
  const offset = calcOffset(event.movementX, event.movementY, this.zoom());
2221
2187
  const { x, y, width, height } = constrainRect(applyResize(this.resizeSide, this.model, offset), this.model, this.resizeSide, this.minWidth, this.minHeight);
2222
- this.model.setPoint({ x, y }, false);
2223
- this.model.size.set({ width, height });
2188
+ this.model.setPoint({ x, y });
2189
+ this.model.width.set(width);
2190
+ this.model.height.set(height);
2224
2191
  }
2225
2192
  endResize() {
2226
2193
  this.resizeSide = null;
@@ -2381,26 +2348,43 @@ class HandleModel {
2381
2348
  width: 10 + 2 * this.strokeWidth,
2382
2349
  height: 10 + 2 * this.strokeWidth,
2383
2350
  });
2384
- this.offset = computed(() => {
2351
+ this.pointAbsolute = computed(() => {
2352
+ return {
2353
+ x: this.parentNode.globalPoint().x + this.hostOffset().x + this.sizeOffset().x,
2354
+ y: this.parentNode.globalPoint().y + this.hostOffset().y + this.sizeOffset().y,
2355
+ };
2356
+ });
2357
+ this.state = signal('idle');
2358
+ this.updateHostSizeAndPosition$ = new Subject();
2359
+ this.hostSize = toSignal(this.updateHostSizeAndPosition$.pipe(map(() => this.getHostSize())), {
2360
+ initialValue: { width: 0, height: 0 },
2361
+ });
2362
+ this.hostPosition = toSignal(this.updateHostSizeAndPosition$.pipe(map(() => ({
2363
+ x: this.hostReference instanceof HTMLElement ? this.hostReference.offsetLeft : 0, // for now just 0 for group nodes
2364
+ y: this.hostReference instanceof HTMLElement ? this.hostReference.offsetTop : 0, // for now just 0 for group nodes
2365
+ }))), {
2366
+ initialValue: { x: 0, y: 0 },
2367
+ });
2368
+ this.hostOffset = computed(() => {
2385
2369
  switch (this.rawHandle.position) {
2386
2370
  case 'left':
2387
2371
  return {
2388
2372
  x: 0,
2389
- y: this.parentPosition().y + this.parentSize().height / 2,
2373
+ y: this.hostPosition().y + this.hostSize().height / 2,
2390
2374
  };
2391
2375
  case 'right':
2392
2376
  return {
2393
2377
  x: this.parentNode.size().width,
2394
- y: this.parentPosition().y + this.parentSize().height / 2,
2378
+ y: this.hostPosition().y + this.hostSize().height / 2,
2395
2379
  };
2396
2380
  case 'top':
2397
2381
  return {
2398
- x: this.parentPosition().x + this.parentSize().width / 2,
2382
+ x: this.hostPosition().x + this.hostSize().width / 2,
2399
2383
  y: 0,
2400
2384
  };
2401
2385
  case 'bottom':
2402
2386
  return {
2403
- x: this.parentPosition().x + this.parentSize().width / 2,
2387
+ x: this.hostPosition().x + this.hostSize().width / 2,
2404
2388
  y: this.parentNode.size().height,
2405
2389
  };
2406
2390
  }
@@ -2417,50 +2401,50 @@ class HandleModel {
2417
2401
  return { x: 0, y: this.size().height / 2 };
2418
2402
  }
2419
2403
  });
2420
- this.pointAbsolute = computed(() => {
2421
- return {
2422
- x: this.parentNode.globalPoint().x + this.offset().x + this.sizeOffset().x,
2423
- y: this.parentNode.globalPoint().y + this.offset().y + this.sizeOffset().y,
2424
- };
2425
- });
2426
- this.state = signal('idle');
2427
- this.updateParentSizeAndPosition$ = new Subject();
2428
- this.parentSize = toSignal(this.updateParentSizeAndPosition$.pipe(map(() => this.getParentSize())), {
2429
- initialValue: { width: 0, height: 0 },
2430
- });
2431
- this.parentPosition = toSignal(this.updateParentSizeAndPosition$.pipe(map(() => ({
2432
- x: this.parentReference instanceof HTMLElement ? this.parentReference.offsetLeft : 0, // for now just 0 for group nodes
2433
- y: this.parentReference instanceof HTMLElement ? this.parentReference.offsetTop : 0, // for now just 0 for group nodes
2434
- }))), {
2435
- initialValue: { x: 0, y: 0 },
2436
- });
2437
- this.parentReference = this.rawHandle.parentReference;
2404
+ this.hostReference = this.rawHandle.hostReference;
2438
2405
  this.template = this.rawHandle.template;
2439
2406
  this.templateContext = {
2440
2407
  $implicit: {
2441
- point: this.offset,
2408
+ point: this.hostOffset,
2442
2409
  state: this.state,
2443
2410
  node: this.parentNode.node,
2444
2411
  },
2445
2412
  };
2446
2413
  }
2447
- updateParent() {
2448
- this.updateParentSizeAndPosition$.next();
2414
+ updateHost() {
2415
+ this.updateHostSizeAndPosition$.next();
2449
2416
  }
2450
- getParentSize() {
2451
- if (this.parentReference instanceof HTMLElement) {
2417
+ getHostSize() {
2418
+ if (this.hostReference instanceof HTMLElement) {
2452
2419
  return {
2453
- width: this.parentReference.offsetWidth,
2454
- height: this.parentReference.offsetHeight,
2420
+ width: this.hostReference.offsetWidth,
2421
+ height: this.hostReference.offsetHeight,
2455
2422
  };
2456
2423
  }
2457
- else if (this.parentReference instanceof SVGGraphicsElement) {
2458
- return this.parentReference.getBBox();
2424
+ else if (this.hostReference instanceof SVGGraphicsElement) {
2425
+ return this.hostReference.getBBox();
2459
2426
  }
2460
2427
  return { width: 0, height: 0 };
2461
2428
  }
2462
2429
  }
2463
2430
 
2431
+ function InjectionContext(target, key, descriptor) {
2432
+ const originalMethod = descriptor.value;
2433
+ descriptor.value = function (...args) {
2434
+ if (implementsWithInjector(this)) {
2435
+ return runInInjectionContext(this.injector, () => originalMethod.apply(this, args));
2436
+ }
2437
+ else {
2438
+ throw new Error('Class that contains decorated method must extends WithInjectorDirective class');
2439
+ }
2440
+ };
2441
+ // Return the modified descriptor
2442
+ return descriptor;
2443
+ }
2444
+ const implementsWithInjector = (instance) => {
2445
+ return 'injector' in instance && 'get' in instance.injector;
2446
+ };
2447
+
2464
2448
  class HandleComponent {
2465
2449
  constructor() {
2466
2450
  this.injector = inject(Injector);
@@ -2488,11 +2472,11 @@ class HandleComponent {
2488
2472
  position: this.position(),
2489
2473
  type: this.type(),
2490
2474
  id: this.id(),
2491
- parentReference: this.element.parentElement,
2475
+ hostReference: this.element.parentElement,
2492
2476
  template: this.template(),
2493
2477
  }, node);
2494
2478
  this.handleService.createHandle(this.model);
2495
- requestAnimationFrame(() => this.model.updateParent());
2479
+ requestAnimationFrame(() => this.model.updateHost());
2496
2480
  this.destroyRef.onDestroy(() => this.handleService.destroyHandle(this.model));
2497
2481
  }
2498
2482
  }
@@ -2507,6 +2491,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2507
2491
  args: [{ standalone: true, selector: 'handle', changeDetection: ChangeDetectionStrategy.OnPush, template: "" }]
2508
2492
  }], propDecorators: { ngOnInit: [] } });
2509
2493
 
2494
+ function resizable(elems, zone) {
2495
+ return new Observable((subscriber) => {
2496
+ const ro = new ResizeObserver((entries) => {
2497
+ zone.run(() => subscriber.next(entries));
2498
+ });
2499
+ elems.forEach((e) => ro.observe(e));
2500
+ return () => ro.disconnect();
2501
+ });
2502
+ }
2503
+
2504
+ class NodeHandlesControllerDirective {
2505
+ constructor() {
2506
+ this.nodeAccessor = inject(NodeAccessorService);
2507
+ this.zone = inject(NgZone);
2508
+ this.destroyRef = inject(DestroyRef);
2509
+ this.hostElementRef = inject(ElementRef);
2510
+ }
2511
+ ngOnInit() {
2512
+ const model = this.nodeAccessor.model();
2513
+ model.handles$
2514
+ .pipe(switchMap((handles) => resizable([...handles.map((h) => h.hostReference), this.hostElementRef.nativeElement], this.zone).pipe(map(() => handles))), tap((handles) => {
2515
+ // TODO (performance) inspect how to avoid calls of this when flow initially rendered
2516
+ handles.forEach((h) => h.updateHost());
2517
+ }), takeUntilDestroyed(this.destroyRef))
2518
+ .subscribe();
2519
+ }
2520
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeHandlesControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2521
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NodeHandlesControllerDirective, isStandalone: true, selector: "[nodeHandlesController]", ngImport: i0 }); }
2522
+ }
2523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeHandlesControllerDirective, decorators: [{
2524
+ type: Directive,
2525
+ args: [{
2526
+ selector: '[nodeHandlesController]',
2527
+ standalone: true,
2528
+ }]
2529
+ }] });
2530
+
2531
+ class NodeResizeControllerDirective {
2532
+ constructor() {
2533
+ this.nodeAccessor = inject(NodeAccessorService);
2534
+ this.zone = inject(NgZone);
2535
+ this.destroyRef = inject(DestroyRef);
2536
+ this.hostElementRef = inject(ElementRef);
2537
+ }
2538
+ ngOnInit() {
2539
+ const model = this.nodeAccessor.model();
2540
+ resizable([this.hostElementRef.nativeElement], this.zone)
2541
+ .pipe(startWith(null), filter(() => !model.resizing()), tap(() => {
2542
+ const width = this.hostElementRef.nativeElement.clientWidth;
2543
+ const height = this.hostElementRef.nativeElement.clientHeight;
2544
+ model.width.set(width);
2545
+ model.height.set(height);
2546
+ }), takeUntilDestroyed(this.destroyRef))
2547
+ .subscribe();
2548
+ }
2549
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeResizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2550
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NodeResizeControllerDirective, isStandalone: true, selector: "[nodeResizeController]", ngImport: i0 }); }
2551
+ }
2552
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeResizeControllerDirective, decorators: [{
2553
+ type: Directive,
2554
+ args: [{
2555
+ selector: '[nodeResizeController]',
2556
+ standalone: true,
2557
+ }]
2558
+ }] });
2559
+
2510
2560
  class NodeComponent {
2511
2561
  constructor() {
2512
2562
  this.injector = inject(Injector);
@@ -2517,51 +2567,28 @@ class NodeComponent {
2517
2567
  this.flowSettingsService = inject(FlowSettingsService);
2518
2568
  this.selectionService = inject(SelectionService);
2519
2569
  this.hostRef = inject(ElementRef);
2520
- this.connectionController = inject(ConnectionControllerDirective);
2521
2570
  this.nodeAccessor = inject(NodeAccessorService);
2522
2571
  this.overlaysService = inject(OverlaysService);
2523
- this.zone = inject(NgZone);
2524
- this.nodeModel = input.required();
2572
+ // TODO remove dependency from this directive
2573
+ this.connectionController = inject(ConnectionControllerDirective, { optional: true });
2574
+ this.model = input.required();
2525
2575
  this.nodeTemplate = input();
2526
2576
  this.groupNodeTemplate = input();
2527
- this.htmlWrapperRef = viewChild.required('htmlWrapper');
2528
2577
  this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
2529
2578
  this.flowStatusService.status().state === 'connection-validation');
2530
- this.styleWidth = computed(() => `${this.nodeModel().size().width}px`);
2531
- this.styleHeight = computed(() => `${this.nodeModel().size().height}px`);
2532
- this.toolbar = computed(() => this.overlaysService.nodeToolbars().get(this.nodeModel()));
2579
+ this.toolbar = computed(() => this.overlaysService.nodeToolbars().get(this.model()));
2533
2580
  }
2534
2581
  ngOnInit() {
2535
- this.nodeAccessor.model.set(this.nodeModel());
2536
- this.handleService.node.set(this.nodeModel());
2582
+ this.nodeAccessor.model.set(this.model());
2583
+ this.handleService.node.set(this.model());
2537
2584
  effect(() => {
2538
- if (this.nodeModel().draggable()) {
2539
- this.draggableService.enable(this.hostRef.nativeElement, this.nodeModel());
2585
+ if (this.model().draggable()) {
2586
+ this.draggableService.enable(this.hostRef.nativeElement, this.model());
2540
2587
  }
2541
2588
  else {
2542
2589
  this.draggableService.disable(this.hostRef.nativeElement);
2543
2590
  }
2544
- });
2545
- this.nodeModel()
2546
- .handles$.pipe(switchMap((handles) => resizable(handles.map((h) => h.parentReference), this.zone).pipe(map(() => handles))), tap((handles) => {
2547
- // TODO (performance) inspect how to avoid calls of this when flow initially rendered
2548
- handles.forEach((h) => h.updateParent());
2549
- }), takeUntilDestroyed())
2550
- .subscribe();
2551
- }
2552
- ngAfterViewInit() {
2553
- this.nodeModel().linkDefaultNodeSizeWithModelSize();
2554
- if (this.nodeModel().node.type === 'html-template' || this.nodeModel().isComponentType) {
2555
- resizable([this.htmlWrapperRef().nativeElement], this.zone)
2556
- .pipe(startWith(null), tap(() => this.nodeModel()
2557
- .handles()
2558
- .forEach((h) => h.updateParent())), filter(() => !this.nodeModel().resizing()), tap(() => {
2559
- const width = this.htmlWrapperRef().nativeElement.clientWidth;
2560
- const height = this.htmlWrapperRef().nativeElement.clientHeight;
2561
- this.nodeModel().size.set({ width, height });
2562
- }), takeUntilDestroyed())
2563
- .subscribe();
2564
- }
2591
+ }, { injector: this.injector });
2565
2592
  }
2566
2593
  ngOnDestroy() {
2567
2594
  this.draggableService.destroy(this.hostRef.nativeElement);
@@ -2569,34 +2596,28 @@ class NodeComponent {
2569
2596
  startConnection(event, handle) {
2570
2597
  // ignore drag by stopping propagation
2571
2598
  event.stopPropagation();
2572
- this.connectionController.startConnection(handle);
2599
+ this.connectionController?.startConnection(handle);
2573
2600
  }
2574
2601
  validateConnection(handle) {
2575
- this.connectionController.validateConnection(handle);
2602
+ this.connectionController?.validateConnection(handle);
2576
2603
  }
2577
2604
  resetValidateConnection(targetHandle) {
2578
- this.connectionController.resetValidateConnection(targetHandle);
2605
+ this.connectionController?.resetValidateConnection(targetHandle);
2579
2606
  }
2580
2607
  endConnection() {
2581
- this.connectionController.endConnection();
2608
+ this.connectionController?.endConnection();
2582
2609
  }
2583
2610
  pullNode() {
2584
- this.nodeRenderingService.pullNode(this.nodeModel());
2611
+ this.nodeRenderingService.pullNode(this.model());
2585
2612
  }
2586
2613
  selectNode() {
2587
2614
  if (this.flowSettingsService.entitiesSelectable()) {
2588
- this.selectionService.select(this.nodeModel());
2615
+ this.selectionService.select(this.model());
2589
2616
  }
2590
2617
  }
2591
2618
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2592
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NodeComponent, isStandalone: true, selector: "g[node]", inputs: { nodeModel: { classPropertyName: "nodeModel", publicName: "nodeModel", 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], viewQueries: [{ propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Default node -->\n@if (nodeModel().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n #htmlWrapper\n [selected]=\"nodeModel().selected()\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n [style.max-width]=\"styleWidth()\"\n [style.max-height]=\"styleHeight()\">\n <div [outerHTML]=\"nodeModel().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 (nodeModel().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n (pointerStart)=\"pullNode()\">\n <div #htmlWrapper class=\"wrapper\" [style.width]=\"styleWidth()\" [style.height]=\"styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: { node: nodeModel().node, selected: nodeModel().selected },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (nodeModel().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n (pointerStart)=\"pullNode()\">\n <div #htmlWrapper class=\"wrapper\" [style.width]=\"styleWidth()\" [style.height]=\"styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel().node.type)\"\n [ngComponentOutletInputs]=\"nodeModel().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (nodeModel().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"nodeModel().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel().color()\"\n [class.default-group-node_selected]=\"nodeModel().selected()\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n [style.stroke]=\"nodeModel().color()\"\n [style.fill]=\"nodeModel().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (nodeModel().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n node: nodeModel().node,\n selected: nodeModel().selected,\n width: nodeModel().width,\n height: nodeModel().height,\n },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (nodeModel().resizerTemplate(); as template) {\n @if (nodeModel().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of nodeModel().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().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]=\"nodeModel().magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2619
+ 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().size().width\"\n [attr.height]=\"model().size().height\"\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().size().width\"\n [attr.height]=\"model().size().height\"\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().size().width\"\n [attr.height]=\"model().size().height\"\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 }); }
2593
2620
  }
2594
- __decorate([
2595
- InjectionContext
2596
- ], NodeComponent.prototype, "ngOnInit", null);
2597
- __decorate([
2598
- InjectionContext
2599
- ], NodeComponent.prototype, "ngAfterViewInit", null);
2600
2621
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, decorators: [{
2601
2622
  type: Component,
2602
2623
  args: [{ standalone: true, selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService, NodeAccessorService], host: {
@@ -2609,8 +2630,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2609
2630
  NgComponentOutlet,
2610
2631
  ResizableComponent,
2611
2632
  HandleSizeControllerDirective,
2612
- ], template: "<!-- Default node -->\n@if (nodeModel().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n #htmlWrapper\n [selected]=\"nodeModel().selected()\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n [style.max-width]=\"styleWidth()\"\n [style.max-height]=\"styleHeight()\">\n <div [outerHTML]=\"nodeModel().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 (nodeModel().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n (pointerStart)=\"pullNode()\">\n <div #htmlWrapper class=\"wrapper\" [style.width]=\"styleWidth()\" [style.height]=\"styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: { node: nodeModel().node, selected: nodeModel().selected },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (nodeModel().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n (pointerStart)=\"pullNode()\">\n <div #htmlWrapper class=\"wrapper\" [style.width]=\"styleWidth()\" [style.height]=\"styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel().node.type)\"\n [ngComponentOutletInputs]=\"nodeModel().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (nodeModel().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"nodeModel().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel().color()\"\n [class.default-group-node_selected]=\"nodeModel().selected()\"\n [attr.width]=\"nodeModel().size().width\"\n [attr.height]=\"nodeModel().size().height\"\n [style.stroke]=\"nodeModel().color()\"\n [style.fill]=\"nodeModel().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (nodeModel().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"{\n $implicit: {\n node: nodeModel().node,\n selected: nodeModel().selected,\n width: nodeModel().width,\n height: nodeModel().height,\n },\n }\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (nodeModel().resizerTemplate(); as template) {\n @if (nodeModel().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of nodeModel().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().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]=\"nodeModel().magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().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"] }]
2613
- }], propDecorators: { ngOnInit: [], ngAfterViewInit: [] } });
2633
+ NodeHandlesControllerDirective,
2634
+ NodeResizeControllerDirective,
2635
+ ], template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\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().size().width\"\n [attr.height]=\"model().size().height\"\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().size().width\"\n [attr.height]=\"model().size().height\"\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"] }]
2636
+ }] });
2614
2637
 
2615
2638
  class ConnectionComponent {
2616
2639
  constructor() {
@@ -2932,10 +2955,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2932
2955
  args: ['document:touchend']
2933
2956
  }] } });
2934
2957
 
2935
- const connectionControllerHostDirective = {
2936
- directive: ConnectionControllerDirective,
2937
- outputs: ['onConnect'],
2938
- };
2939
2958
  const changesControllerHostDirective = {
2940
2959
  directive: ChangesControllerDirective,
2941
2960
  outputs: [
@@ -3208,7 +3227,7 @@ class VflowComponent {
3208
3227
  ComponentEventBusService,
3209
3228
  KeyboardService,
3210
3229
  OverlaysService,
3211
- ], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateDirective, descendants: true, isSignal: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true, isSignal: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true, isSignal: true }, { propertyName: "spacePointContext", first: true, predicate: SpacePointContextDirective, descendants: true, isSignal: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onNodesChange.position", "onNodesChange.position", "onNodesChange.position.single", "onNodesChange.position.single", "onNodesChange.position.many", "onNodesChange.position.many", "onNodesChange.size", "onNodesChange.size", "onNodesChange.size.single", "onNodesChange.size.single", "onNodesChange.size.many", "onNodesChange.size.many", "onNodesChange.add", "onNodesChange.add", "onNodesChange.add.single", "onNodesChange.add.single", "onNodesChange.add.many", "onNodesChange.add.many", "onNodesChange.remove", "onNodesChange.remove", "onNodesChange.remove.single", "onNodesChange.remove.single", "onNodesChange.remove.many", "onNodesChange.remove.many", "onNodesChange.select", "onNodesChange.select", "onNodesChange.select.single", "onNodesChange.select.single", "onNodesChange.select.many", "onNodesChange.select.many", "onEdgesChange", "onEdgesChange", "onEdgesChange.detached", "onEdgesChange.detached", "onEdgesChange.detached.single", "onEdgesChange.detached.single", "onEdgesChange.detached.many", "onEdgesChange.detached.many", "onEdgesChange.add", "onEdgesChange.add", "onEdgesChange.add.single", "onEdgesChange.add.single", "onEdgesChange.add.many", "onEdgesChange.add.many", "onEdgesChange.remove", "onEdgesChange.remove", "onEdgesChange.remove.single", "onEdgesChange.remove.single", "onEdgesChange.remove.many", "onEdgesChange.remove.many", "onEdgesChange.select", "onEdgesChange.select", "onEdgesChange.select.single", "onEdgesChange.select.single", "onEdgesChange.select.many", "onEdgesChange.select.many"] }], ngImport: i0, template: "<svg:svg #flow rootSvgRef rootSvgContext rootPointer flowSizeController class=\"root-svg\">\n <defs flowDefs [markers]=\"markers()\" />\n\n <g background />\n\n <svg:g mapContext spacePointContext>\n <!-- Connection -->\n <svg:g connection [model]=\"connection\" [template]=\"connectionTemplateDirective()?.templateRef\" />\n\n @if (optimization().detachedGroupsLayer) {\n <!-- Groups -->\n @for (model of groups(); track trackNodes($index, model)) {\n <svg:g\n node\n [nodeModel]=\"model\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nonGroups(); track trackNodes($index, model)) {\n <svg:g\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n\n @if (!optimization().detachedGroupsLayer) {\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nodeModels(); track trackNodes($index, model)) {\n <svg:g\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n </svg:g>\n\n <!-- Minimap -->\n @if (minimap(); as minimap) {\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n }\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }, { kind: "directive", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]" }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "component", type: BackgroundComponent, selector: "g[background]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]" }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeTemplate", "groupNodeTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3230
+ ], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateDirective, descendants: true, isSignal: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true, isSignal: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true, isSignal: true }, { propertyName: "spacePointContext", first: true, predicate: SpacePointContextDirective, descendants: true, isSignal: true }], hostDirectives: [{ directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onNodesChange.position", "onNodesChange.position", "onNodesChange.position.single", "onNodesChange.position.single", "onNodesChange.position.many", "onNodesChange.position.many", "onNodesChange.size", "onNodesChange.size", "onNodesChange.size.single", "onNodesChange.size.single", "onNodesChange.size.many", "onNodesChange.size.many", "onNodesChange.add", "onNodesChange.add", "onNodesChange.add.single", "onNodesChange.add.single", "onNodesChange.add.many", "onNodesChange.add.many", "onNodesChange.remove", "onNodesChange.remove", "onNodesChange.remove.single", "onNodesChange.remove.single", "onNodesChange.remove.many", "onNodesChange.remove.many", "onNodesChange.select", "onNodesChange.select", "onNodesChange.select.single", "onNodesChange.select.single", "onNodesChange.select.many", "onNodesChange.select.many", "onEdgesChange", "onEdgesChange", "onEdgesChange.detached", "onEdgesChange.detached", "onEdgesChange.detached.single", "onEdgesChange.detached.single", "onEdgesChange.detached.many", "onEdgesChange.detached.many", "onEdgesChange.add", "onEdgesChange.add", "onEdgesChange.add.single", "onEdgesChange.add.single", "onEdgesChange.add.many", "onEdgesChange.add.many", "onEdgesChange.remove", "onEdgesChange.remove", "onEdgesChange.remove.single", "onEdgesChange.remove.single", "onEdgesChange.remove.many", "onEdgesChange.remove.many", "onEdgesChange.select", "onEdgesChange.select", "onEdgesChange.select.single", "onEdgesChange.select.single", "onEdgesChange.select.many", "onEdgesChange.select.many"] }], ngImport: i0, template: "<svg:svg #flow rootSvgRef rootSvgContext rootPointer flowSizeController class=\"root-svg\">\n <defs flowDefs [markers]=\"markers()\" />\n\n <g background />\n\n <svg:g mapContext spacePointContext>\n <!-- Connection -->\n <svg:g connection [model]=\"connection\" [template]=\"connectionTemplateDirective()?.templateRef\" />\n\n @if (optimization().detachedGroupsLayer) {\n <!-- Groups -->\n @for (model of groups(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nonGroups(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n\n @if (!optimization().detachedGroupsLayer) {\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nodeModels(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n </svg:g>\n\n <!-- Minimap -->\n @if (minimap(); as minimap) {\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n }\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }, { kind: "directive", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]" }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "component", type: BackgroundComponent, selector: "g[background]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]" }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["model", "nodeTemplate", "groupNodeTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3212
3231
  }
3213
3232
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowComponent, decorators: [{
3214
3233
  type: Component,
@@ -3225,7 +3244,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3225
3244
  ComponentEventBusService,
3226
3245
  KeyboardService,
3227
3246
  OverlaysService,
3228
- ], hostDirectives: [connectionControllerHostDirective, changesControllerHostDirective], imports: [
3247
+ ], hostDirectives: [changesControllerHostDirective], imports: [
3229
3248
  RootSvgReferenceDirective,
3230
3249
  RootSvgContextDirective,
3231
3250
  RootPointerDirective,
@@ -3238,7 +3257,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3238
3257
  NodeComponent,
3239
3258
  EdgeComponent,
3240
3259
  NgTemplateOutlet,
3241
- ], template: "<svg:svg #flow rootSvgRef rootSvgContext rootPointer flowSizeController class=\"root-svg\">\n <defs flowDefs [markers]=\"markers()\" />\n\n <g background />\n\n <svg:g mapContext spacePointContext>\n <!-- Connection -->\n <svg:g connection [model]=\"connection\" [template]=\"connectionTemplateDirective()?.templateRef\" />\n\n @if (optimization().detachedGroupsLayer) {\n <!-- Groups -->\n @for (model of groups(); track trackNodes($index, model)) {\n <svg:g\n node\n [nodeModel]=\"model\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nonGroups(); track trackNodes($index, model)) {\n <svg:g\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n\n @if (!optimization().detachedGroupsLayer) {\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nodeModels(); track trackNodes($index, model)) {\n <svg:g\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n </svg:g>\n\n <!-- Minimap -->\n @if (minimap(); as minimap) {\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n }\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"] }]
3260
+ ], template: "<svg:svg #flow rootSvgRef rootSvgContext rootPointer flowSizeController class=\"root-svg\">\n <defs flowDefs [markers]=\"markers()\" />\n\n <g background />\n\n <svg:g mapContext spacePointContext>\n <!-- Connection -->\n <svg:g connection [model]=\"connection\" [template]=\"connectionTemplateDirective()?.templateRef\" />\n\n @if (optimization().detachedGroupsLayer) {\n <!-- Groups -->\n @for (model of groups(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nonGroups(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n\n @if (!optimization().detachedGroupsLayer) {\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nodeModels(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n </svg:g>\n\n <!-- Minimap -->\n @if (minimap(); as minimap) {\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n }\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"] }]
3242
3261
  }], propDecorators: { view: [{
3243
3262
  type: Input
3244
3263
  }], minZoom: [{
@@ -3303,7 +3322,7 @@ class SelectableDirective {
3303
3322
  }
3304
3323
  entity() {
3305
3324
  if (this.parentNode) {
3306
- return this.parentNode.nodeModel();
3325
+ return this.parentNode.model();
3307
3326
  }
3308
3327
  else if (this.parentEdge) {
3309
3328
  return this.parentEdge.model();
@@ -3532,6 +3551,7 @@ const Vflow = [
3532
3551
  MiniMapComponent,
3533
3552
  NodeToolbarComponent,
3534
3553
  DragHandleDirective,
3554
+ ConnectionControllerDirective,
3535
3555
  NodeHtmlTemplateDirective,
3536
3556
  GroupNodeTemplateDirective,
3537
3557
  EdgeLabelHtmlTemplateDirective,
@@ -3549,6 +3569,14 @@ function provideCustomNodeMocks() {
3549
3569
  pushEvent: () => { },
3550
3570
  },
3551
3571
  },
3572
+ {
3573
+ provide: NodeAccessorService,
3574
+ useFactory: () => ({
3575
+ model: signal(mockModel()),
3576
+ }),
3577
+ },
3578
+ FlowEntitiesService,
3579
+ // TODO: mocks below should be removed after the major release
3552
3580
  {
3553
3581
  provide: HandleService,
3554
3582
  useFactory: () => ({
@@ -3577,12 +3605,6 @@ function provideCustomNodeMocks() {
3577
3605
  documentPointToFlowPoint: (point) => point,
3578
3606
  },
3579
3607
  },
3580
- {
3581
- provide: NodeAccessorService,
3582
- useFactory: () => ({
3583
- model: signal(mockModel()),
3584
- }),
3585
- },
3586
3608
  {
3587
3609
  provide: SelectionService,
3588
3610
  useValue: {
@@ -3590,16 +3612,473 @@ function provideCustomNodeMocks() {
3590
3612
  },
3591
3613
  },
3592
3614
  FlowSettingsService,
3593
- FlowEntitiesService,
3594
3615
  ViewportService,
3595
3616
  ];
3596
3617
  }
3597
3618
 
3619
+ class EdgeTemplateMockDirective {
3620
+ constructor() {
3621
+ this.templateRef = inject(TemplateRef);
3622
+ }
3623
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3624
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: EdgeTemplateMockDirective, isStandalone: true, selector: "ng-template[edge]", ngImport: i0 }); }
3625
+ }
3626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeTemplateMockDirective, decorators: [{
3627
+ type: Directive,
3628
+ args: [{
3629
+ standalone: true,
3630
+ selector: 'ng-template[edge]',
3631
+ }]
3632
+ }] });
3633
+ class ConnectionTemplateMockDirective {
3634
+ constructor() {
3635
+ this.templateRef = inject(TemplateRef);
3636
+ }
3637
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3638
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionTemplateMockDirective, isStandalone: true, selector: "ng-template[connection]", ngImport: i0 }); }
3639
+ }
3640
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionTemplateMockDirective, decorators: [{
3641
+ type: Directive,
3642
+ args: [{
3643
+ standalone: true,
3644
+ selector: 'ng-template[connection]',
3645
+ }]
3646
+ }] });
3647
+ class EdgeLabelHtmlTemplateMockDirective {
3648
+ constructor() {
3649
+ this.templateRef = inject(TemplateRef);
3650
+ }
3651
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeLabelHtmlTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3652
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: EdgeLabelHtmlTemplateMockDirective, isStandalone: true, selector: "ng-template[edgeLabelHtml]", ngImport: i0 }); }
3653
+ }
3654
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EdgeLabelHtmlTemplateMockDirective, decorators: [{
3655
+ type: Directive,
3656
+ args: [{
3657
+ standalone: true,
3658
+ selector: 'ng-template[edgeLabelHtml]',
3659
+ }]
3660
+ }] });
3661
+ class NodeHtmlTemplateMockDirective {
3662
+ constructor() {
3663
+ this.templateRef = inject(TemplateRef);
3664
+ }
3665
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeHtmlTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3666
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NodeHtmlTemplateMockDirective, isStandalone: true, selector: "ng-template[nodeHtml]", ngImport: i0 }); }
3667
+ }
3668
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeHtmlTemplateMockDirective, decorators: [{
3669
+ type: Directive,
3670
+ args: [{
3671
+ standalone: true,
3672
+ selector: 'ng-template[nodeHtml]',
3673
+ }]
3674
+ }] });
3675
+ class GroupNodeTemplateMockDirective {
3676
+ constructor() {
3677
+ this.templateRef = inject(TemplateRef);
3678
+ }
3679
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: GroupNodeTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3680
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: GroupNodeTemplateMockDirective, isStandalone: true, selector: "ng-template[groupNode]", ngImport: i0 }); }
3681
+ }
3682
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: GroupNodeTemplateMockDirective, decorators: [{
3683
+ type: Directive,
3684
+ args: [{
3685
+ standalone: true,
3686
+ selector: 'ng-template[groupNode]',
3687
+ }]
3688
+ }] });
3689
+ class HandleTemplateMockDirective {
3690
+ constructor() {
3691
+ this.templateRef = inject(TemplateRef);
3692
+ }
3693
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3694
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: HandleTemplateMockDirective, isStandalone: true, selector: "ng-template[handle]", ngImport: i0 }); }
3695
+ }
3696
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleTemplateMockDirective, decorators: [{
3697
+ type: Directive,
3698
+ args: [{
3699
+ standalone: true,
3700
+ selector: 'ng-template[handle]',
3701
+ }]
3702
+ }] });
3703
+
3704
+ class VflowMockComponent {
3705
+ constructor() {
3706
+ this.nodes = input.required();
3707
+ this.edges = input.required();
3708
+ this.view = input([400, 400]);
3709
+ this.minZoom = input(0.5);
3710
+ this.maxZoom = input(3);
3711
+ this.background = input('#fff');
3712
+ this.optimization = input({
3713
+ detachedGroupsLayer: false,
3714
+ });
3715
+ this.entitiesSelectable = input(true);
3716
+ this.keyboardShortcuts = input({
3717
+ multiSelection: null,
3718
+ });
3719
+ this.connection = input();
3720
+ // eslint-disable-next-line @angular-eslint/no-output-on-prefix
3721
+ this.onComponentNodeEvent = output();
3722
+ this.nodeTemplateDirective = contentChild(NodeHtmlTemplateMockDirective);
3723
+ this.groupNodeTemplateDirective = contentChild(GroupNodeTemplateMockDirective);
3724
+ this.edgeTemplateDirective = contentChild(EdgeTemplateMockDirective);
3725
+ this.edgeLabelHtmlDirective = contentChild(EdgeLabelHtmlTemplateMockDirective);
3726
+ this.connectionTemplateDirective = contentChild(ConnectionTemplateMockDirective);
3727
+ this.viewport = signal({
3728
+ x: 0,
3729
+ y: 0,
3730
+ zoom: 1,
3731
+ });
3732
+ this.nodesChange = signal([]);
3733
+ this.edgesChange = signal([]);
3734
+ this.viewportChange$ = toObservable(this.viewport);
3735
+ this.nodesChange$ = toObservable(this.nodesChange);
3736
+ this.edgesChange$ = toObservable(this.edgesChange);
3737
+ }
3738
+ viewportTo(viewport) {
3739
+ this.viewport.set(viewport);
3740
+ }
3741
+ zoomTo(zoom) {
3742
+ this.viewport.update((prev) => ({ ...prev, zoom }));
3743
+ }
3744
+ panTo(point) {
3745
+ this.viewport.update((prev) => ({ ...prev, x: point.x, y: point.y }));
3746
+ }
3747
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3748
+ fitView(options) { }
3749
+ documentPointToFlowPoint(point) {
3750
+ return point;
3751
+ }
3752
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3753
+ getNode(id) {
3754
+ return this.nodes().find((node) => node.id === id);
3755
+ }
3756
+ getDetachedEdges() {
3757
+ return [];
3758
+ }
3759
+ createSignal(value) {
3760
+ return signal(value);
3761
+ }
3762
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowMockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3763
+ 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: true, isRequired: true, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: true, isRequired: true, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: true, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: true, isRequired: false, transformFunction: null }, background: { classPropertyName: "background", publicName: "background", isSignal: true, isRequired: false, transformFunction: null }, optimization: { classPropertyName: "optimization", publicName: "optimization", isSignal: true, isRequired: false, transformFunction: null }, entitiesSelectable: { classPropertyName: "entitiesSelectable", publicName: "entitiesSelectable", isSignal: true, isRequired: false, transformFunction: null }, keyboardShortcuts: { classPropertyName: "keyboardShortcuts", publicName: "keyboardShortcuts", isSignal: true, isRequired: false, transformFunction: null }, connection: { classPropertyName: "connection", publicName: "connection", isSignal: true, 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: `
3764
+ <ng-content />
3765
+
3766
+ @for (node of nodes(); track $index) {
3767
+ @if (node.type === 'html-template') {
3768
+ <ng-component
3769
+ [ngTemplateOutlet]="nodeTemplateDirective()?.templateRef ?? null"
3770
+ [ngTemplateOutletContext]="{
3771
+ $implicit: {
3772
+ node: node,
3773
+ selected: createSignal(false),
3774
+ },
3775
+ }" />
3776
+ }
3777
+
3778
+ @if (node.type === 'template-group') {
3779
+ <ng-component
3780
+ [ngTemplateOutlet]="groupNodeTemplateDirective()?.templateRef ?? null"
3781
+ [ngTemplateOutletContext]="{
3782
+ $implicit: {
3783
+ node: node,
3784
+ selected: createSignal(false),
3785
+ width: createSignal(node.width),
3786
+ height: createSignal(node.height),
3787
+ },
3788
+ }" />
3789
+ }
3790
+ }
3791
+
3792
+ @for (edge of edges(); track $index) {
3793
+ @if (edge.type === 'template') {
3794
+ <ng-component
3795
+ [ngTemplateOutlet]="edgeTemplateDirective()?.templateRef ?? null"
3796
+ [ngTemplateOutletContext]="{
3797
+ $implicit: {
3798
+ edge: edge,
3799
+ selected: createSignal(false),
3800
+ path: createSignal(''),
3801
+ markerStart: createSignal(''),
3802
+ markerEnd: createSignal(''),
3803
+ },
3804
+ }" />
3805
+
3806
+ @if (edge.edgeLabels?.start) {
3807
+ <ng-component
3808
+ [ngTemplateOutlet]="edgeLabelHtmlDirective()?.templateRef ?? null"
3809
+ [ngTemplateOutletContext]="{
3810
+ $implicit: {
3811
+ edge: edge,
3812
+ },
3813
+ }" />
3814
+ }
3815
+
3816
+ @if (edge.edgeLabels?.center) {
3817
+ <ng-component
3818
+ [ngTemplateOutlet]="edgeLabelHtmlDirective()?.templateRef ?? null"
3819
+ [ngTemplateOutletContext]="{
3820
+ $implicit: {
3821
+ edge: edge,
3822
+ label: edge.edgeLabels?.center,
3823
+ },
3824
+ }" />
3825
+ }
3826
+
3827
+ @if (edge.edgeLabels?.end) {
3828
+ <ng-component
3829
+ [ngTemplateOutlet]="edgeLabelHtmlDirective()?.templateRef ?? null"
3830
+ [ngTemplateOutletContext]="{
3831
+ $implicit: {
3832
+ edge: edge,
3833
+ label: edge.edgeLabels?.end,
3834
+ },
3835
+ }" />
3836
+ }
3837
+ }
3838
+ }
3839
+
3840
+ @if (connection()?.type === 'template') {
3841
+ <ng-component
3842
+ [ngTemplateOutlet]="connectionTemplateDirective()?.templateRef ?? null"
3843
+ [ngTemplateOutletContext]="{
3844
+ $implicit: {
3845
+ path: createSignal(''),
3846
+ marker: createSignal(''),
3847
+ },
3848
+ }" />
3849
+ }
3850
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3851
+ }
3852
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowMockComponent, decorators: [{
3853
+ type: Component,
3854
+ args: [{
3855
+ selector: 'vflow',
3856
+ template: `
3857
+ <ng-content />
3858
+
3859
+ @for (node of nodes(); track $index) {
3860
+ @if (node.type === 'html-template') {
3861
+ <ng-component
3862
+ [ngTemplateOutlet]="nodeTemplateDirective()?.templateRef ?? null"
3863
+ [ngTemplateOutletContext]="{
3864
+ $implicit: {
3865
+ node: node,
3866
+ selected: createSignal(false),
3867
+ },
3868
+ }" />
3869
+ }
3870
+
3871
+ @if (node.type === 'template-group') {
3872
+ <ng-component
3873
+ [ngTemplateOutlet]="groupNodeTemplateDirective()?.templateRef ?? null"
3874
+ [ngTemplateOutletContext]="{
3875
+ $implicit: {
3876
+ node: node,
3877
+ selected: createSignal(false),
3878
+ width: createSignal(node.width),
3879
+ height: createSignal(node.height),
3880
+ },
3881
+ }" />
3882
+ }
3883
+ }
3884
+
3885
+ @for (edge of edges(); track $index) {
3886
+ @if (edge.type === 'template') {
3887
+ <ng-component
3888
+ [ngTemplateOutlet]="edgeTemplateDirective()?.templateRef ?? null"
3889
+ [ngTemplateOutletContext]="{
3890
+ $implicit: {
3891
+ edge: edge,
3892
+ selected: createSignal(false),
3893
+ path: createSignal(''),
3894
+ markerStart: createSignal(''),
3895
+ markerEnd: createSignal(''),
3896
+ },
3897
+ }" />
3898
+
3899
+ @if (edge.edgeLabels?.start) {
3900
+ <ng-component
3901
+ [ngTemplateOutlet]="edgeLabelHtmlDirective()?.templateRef ?? null"
3902
+ [ngTemplateOutletContext]="{
3903
+ $implicit: {
3904
+ edge: edge,
3905
+ },
3906
+ }" />
3907
+ }
3908
+
3909
+ @if (edge.edgeLabels?.center) {
3910
+ <ng-component
3911
+ [ngTemplateOutlet]="edgeLabelHtmlDirective()?.templateRef ?? null"
3912
+ [ngTemplateOutletContext]="{
3913
+ $implicit: {
3914
+ edge: edge,
3915
+ label: edge.edgeLabels?.center,
3916
+ },
3917
+ }" />
3918
+ }
3919
+
3920
+ @if (edge.edgeLabels?.end) {
3921
+ <ng-component
3922
+ [ngTemplateOutlet]="edgeLabelHtmlDirective()?.templateRef ?? null"
3923
+ [ngTemplateOutletContext]="{
3924
+ $implicit: {
3925
+ edge: edge,
3926
+ label: edge.edgeLabels?.end,
3927
+ },
3928
+ }" />
3929
+ }
3930
+ }
3931
+ }
3932
+
3933
+ @if (connection()?.type === 'template') {
3934
+ <ng-component
3935
+ [ngTemplateOutlet]="connectionTemplateDirective()?.templateRef ?? null"
3936
+ [ngTemplateOutletContext]="{
3937
+ $implicit: {
3938
+ path: createSignal(''),
3939
+ marker: createSignal(''),
3940
+ },
3941
+ }" />
3942
+ }
3943
+ `,
3944
+ changeDetection: ChangeDetectionStrategy.OnPush,
3945
+ standalone: true,
3946
+ imports: [NgTemplateOutlet],
3947
+ }]
3948
+ }] });
3949
+
3950
+ class HandleMockComponent {
3951
+ constructor() {
3952
+ this.position = input.required();
3953
+ this.type = input.required();
3954
+ this.id = input();
3955
+ this.template = input();
3956
+ }
3957
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleMockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3958
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: HandleMockComponent, isStandalone: true, selector: "handle", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: true, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, template: { classPropertyName: "template", publicName: "template", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3959
+ }
3960
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: HandleMockComponent, decorators: [{
3961
+ type: Component,
3962
+ args: [{
3963
+ selector: 'handle',
3964
+ template: '',
3965
+ changeDetection: ChangeDetectionStrategy.OnPush,
3966
+ standalone: true,
3967
+ }]
3968
+ }] });
3969
+
3970
+ class ResizableMockComponent {
3971
+ constructor() {
3972
+ this.resizable = input();
3973
+ this.resizerColor = input('#2e414c');
3974
+ this.gap = input(1.5);
3975
+ }
3976
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ResizableMockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3977
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: ResizableMockComponent, isStandalone: true, selector: "[resizable]", inputs: { resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resizerColor: { classPropertyName: "resizerColor", publicName: "resizerColor", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3978
+ }
3979
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ResizableMockComponent, decorators: [{
3980
+ type: Component,
3981
+ args: [{
3982
+ selector: '[resizable]',
3983
+ template: '<ng-content />',
3984
+ standalone: true,
3985
+ changeDetection: ChangeDetectionStrategy.OnPush,
3986
+ }]
3987
+ }] });
3988
+
3989
+ class MiniMapMockComponent {
3990
+ constructor() {
3991
+ this.maskColor = input(`rgba(215, 215, 215, 0.6)`);
3992
+ this.strokeColor = input(`rgb(200, 200, 200)`);
3993
+ this.position = input('bottom-right');
3994
+ this.scaleOnHover = input(false);
3995
+ }
3996
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MiniMapMockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3997
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: MiniMapMockComponent, isStandalone: true, selector: "mini-map", inputs: { maskColor: { classPropertyName: "maskColor", publicName: "maskColor", isSignal: true, isRequired: false, transformFunction: null }, strokeColor: { classPropertyName: "strokeColor", publicName: "strokeColor", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, scaleOnHover: { classPropertyName: "scaleOnHover", publicName: "scaleOnHover", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3998
+ }
3999
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MiniMapMockComponent, decorators: [{
4000
+ type: Component,
4001
+ args: [{
4002
+ selector: 'mini-map',
4003
+ template: '',
4004
+ standalone: true,
4005
+ changeDetection: ChangeDetectionStrategy.OnPush,
4006
+ }]
4007
+ }] });
4008
+
4009
+ class NodeToolbarMockComponent {
4010
+ constructor() {
4011
+ this.position = input('top');
4012
+ }
4013
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeToolbarMockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4014
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "17.3.12", type: NodeToolbarMockComponent, isStandalone: true, selector: "node-toolbar", inputs: { position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: '<ng-content />', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4015
+ }
4016
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeToolbarMockComponent, decorators: [{
4017
+ type: Component,
4018
+ args: [{
4019
+ selector: 'node-toolbar',
4020
+ template: '<ng-content />',
4021
+ standalone: true,
4022
+ changeDetection: ChangeDetectionStrategy.OnPush,
4023
+ }]
4024
+ }] });
4025
+
4026
+ class ConnectionControllerMockDirective {
4027
+ constructor() {
4028
+ // eslint-disable-next-line @angular-eslint/no-output-on-prefix
4029
+ this.onConnect = output();
4030
+ }
4031
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
4032
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: ConnectionControllerMockDirective, isStandalone: true, selector: "[onConnect]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
4033
+ }
4034
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionControllerMockDirective, decorators: [{
4035
+ type: Directive,
4036
+ args: [{ selector: '[onConnect]', standalone: true }]
4037
+ }] });
4038
+
4039
+ class DragHandleMockDirective {
4040
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DragHandleMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
4041
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: DragHandleMockDirective, isStandalone: true, selector: "[dragHandle]", ngImport: i0 }); }
4042
+ }
4043
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DragHandleMockDirective, decorators: [{
4044
+ type: Directive,
4045
+ args: [{ selector: '[dragHandle]', standalone: true }]
4046
+ }] });
4047
+
4048
+ class SelectableMockDirective {
4049
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectableMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
4050
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: SelectableMockDirective, isStandalone: true, selector: "[selectable]", ngImport: i0 }); }
4051
+ }
4052
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectableMockDirective, decorators: [{
4053
+ type: Directive,
4054
+ args: [{
4055
+ selector: '[selectable]',
4056
+ standalone: true,
4057
+ }]
4058
+ }] });
4059
+
4060
+ const VflowMocks = [
4061
+ VflowMockComponent,
4062
+ HandleMockComponent,
4063
+ ResizableMockComponent,
4064
+ SelectableMockDirective,
4065
+ MiniMapMockComponent,
4066
+ NodeToolbarMockComponent,
4067
+ DragHandleMockDirective,
4068
+ ConnectionControllerMockDirective,
4069
+ NodeHtmlTemplateMockDirective,
4070
+ GroupNodeTemplateMockDirective,
4071
+ EdgeLabelHtmlTemplateMockDirective,
4072
+ EdgeTemplateMockDirective,
4073
+ ConnectionTemplateMockDirective,
4074
+ HandleTemplateMockDirective,
4075
+ ];
4076
+
3598
4077
  // Standalone Util
3599
4078
 
3600
4079
  /**
3601
4080
  * Generated bundle index. Do not edit.
3602
4081
  */
3603
4082
 
3604
- export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, CustomDynamicNodeComponent, CustomNodeComponent, DragHandleDirective, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, GroupNodeTemplateDirective, HandleComponent, HandleTemplateDirective, MiniMapComponent, NodeHtmlTemplateDirective, NodeToolbarComponent, NodeToolbarWrapperDirective, ResizableComponent, SelectableDirective, Vflow, VflowComponent, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, provideCustomNodeMocks };
4083
+ 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 };
3605
4084
  //# sourceMappingURL=ngx-vflow.mjs.map