ngx-vflow 1.6.1 → 1.7.1

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 (45) hide show
  1. package/esm2022/lib/vflow/components/custom-node-base/custom-node-base.component.mjs +2 -2
  2. package/esm2022/lib/vflow/components/node/node.component.mjs +5 -4
  3. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +11 -10
  4. package/esm2022/lib/vflow/directives/connection-controller.directive.mjs +5 -5
  5. package/esm2022/lib/vflow/directives/node-resize-controller.directive.mjs +4 -1
  6. package/esm2022/lib/vflow/directives/template.directive.mjs +18 -1
  7. package/esm2022/lib/vflow/interfaces/node.interface.mjs +7 -1
  8. package/esm2022/lib/vflow/interfaces/template-context.interface.mjs +1 -1
  9. package/esm2022/lib/vflow/models/handle.model.mjs +2 -2
  10. package/esm2022/lib/vflow/models/node.model.mjs +55 -79
  11. package/esm2022/lib/vflow/public-components/minimap/minimap.component.mjs +5 -5
  12. package/esm2022/lib/vflow/public-components/node-toolbar/node-toolbar.component.mjs +12 -2
  13. package/esm2022/lib/vflow/public-components/resizable/resizable.component.mjs +105 -44
  14. package/esm2022/lib/vflow/services/draggable.service.mjs +2 -4
  15. package/esm2022/lib/vflow/services/flow-entities.service.mjs +2 -2
  16. package/esm2022/lib/vflow/services/node-changes.service.mjs +6 -6
  17. package/esm2022/lib/vflow/services/overlays.service.mjs +4 -3
  18. package/esm2022/lib/vflow/services/viewport.service.mjs +2 -2
  19. package/esm2022/lib/vflow/testing-utils/directive-mocks/template-mock.directive.mjs +15 -1
  20. package/esm2022/lib/vflow/utils/add-nodes-to-edges.mjs +2 -2
  21. package/esm2022/lib/vflow/utils/align-number.mjs +4 -0
  22. package/esm2022/lib/vflow/utils/identity-checker/reference-identity-checker.mjs +31 -0
  23. package/esm2022/lib/vflow/utils/is-group-node.mjs +2 -2
  24. package/esm2022/lib/vflow/utils/to-unified-node.mjs +24 -0
  25. package/esm2022/lib/vflow/vflow.mjs +3 -2
  26. package/fesm2022/ngx-vflow.mjs +265 -156
  27. package/fesm2022/ngx-vflow.mjs.map +1 -1
  28. package/lib/vflow/components/node/node.component.d.ts +3 -2
  29. package/lib/vflow/components/vflow/vflow.component.d.ts +4 -3
  30. package/lib/vflow/directives/node-resize-controller.directive.d.ts +3 -0
  31. package/lib/vflow/directives/template.directive.d.ts +7 -1
  32. package/lib/vflow/interfaces/node.interface.d.ts +16 -2
  33. package/lib/vflow/interfaces/template-context.interface.d.ts +8 -0
  34. package/lib/vflow/models/node.model.d.ts +20 -23
  35. package/lib/vflow/public-components/minimap/minimap.component.d.ts +1 -1
  36. package/lib/vflow/public-components/node-toolbar/node-toolbar.component.d.ts +3 -0
  37. package/lib/vflow/public-components/resizable/resizable.component.d.ts +2 -0
  38. package/lib/vflow/services/overlays.service.d.ts +2 -2
  39. package/lib/vflow/testing-utils/directive-mocks/template-mock.directive.d.ts +5 -0
  40. package/lib/vflow/utils/align-number.d.ts +1 -0
  41. package/lib/vflow/utils/{reference-keeper.d.ts → identity-checker/reference-identity-checker.d.ts} +5 -5
  42. package/lib/vflow/utils/to-unified-node.d.ts +2 -0
  43. package/lib/vflow/vflow.d.ts +2 -2
  44. package/package.json +1 -1
  45. package/esm2022/lib/vflow/utils/reference-keeper.mjs +0 -31
@@ -120,7 +120,7 @@ class FlowEntitiesService {
120
120
  this.minimap = signal(null);
121
121
  }
122
122
  getNode(id) {
123
- return this.nodes().find(({ node }) => node.id === id);
123
+ return this.nodes().find(({ rawNode }) => rawNode.id === id);
124
124
  }
125
125
  getDetachedEdges() {
126
126
  return this.edges().filter((e) => e.detached());
@@ -217,7 +217,7 @@ class ViewportService {
217
217
  this.entitiesService.nodes()
218
218
  : // Otherwise fit to specific nodes
219
219
  nodeIds
220
- .map((nodeId) => this.entitiesService.nodes().find(({ node }) => node.id === nodeId))
220
+ .map((nodeId) => this.entitiesService.nodes().find(({ rawNode }) => rawNode.id === nodeId))
221
221
  .filter((node) => !!node);
222
222
  }
223
223
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ViewportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
@@ -459,6 +459,10 @@ const evTarget = (anyEvent) => {
459
459
 
460
460
  const round = (num) => Math.round(num * 100) / 100;
461
461
 
462
+ function align(num, constant) {
463
+ return Math.ceil(num / constant) * constant;
464
+ }
465
+
462
466
  class DraggableService {
463
467
  constructor() {
464
468
  this.entitiesService = inject(FlowEntitiesService);
@@ -568,9 +572,6 @@ class DraggableService {
568
572
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DraggableService, decorators: [{
569
573
  type: Injectable
570
574
  }] });
571
- function align(num, constant) {
572
- return Math.ceil(num / constant) * constant;
573
- }
574
575
 
575
576
  class EdgeTemplateDirective {
576
577
  constructor() {
@@ -640,6 +641,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
640
641
  selector: 'ng-template[nodeHtml]',
641
642
  }]
642
643
  }] });
644
+ class NodeSvgTemplateDirective {
645
+ constructor() {
646
+ this.templateRef = inject(TemplateRef);
647
+ }
648
+ static ngTemplateContextGuard(dir, ctx) {
649
+ return true;
650
+ }
651
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeSvgTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
652
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NodeSvgTemplateDirective, isStandalone: true, selector: "ng-template[nodeSvg]", ngImport: i0 }); }
653
+ }
654
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeSvgTemplateDirective, decorators: [{
655
+ type: Directive,
656
+ args: [{
657
+ standalone: true,
658
+ selector: 'ng-template[nodeSvg]',
659
+ }]
660
+ }] });
643
661
  class GroupNodeTemplateDirective {
644
662
  constructor() {
645
663
  this.templateRef = inject(TemplateRef);
@@ -674,7 +692,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
674
692
 
675
693
  function addNodesToEdges(nodes, edges) {
676
694
  const nodesById = nodes.reduce((acc, n) => {
677
- acc[n.node.id] = n;
695
+ acc[n.rawNode.id] = n;
678
696
  return acc;
679
697
  }, {});
680
698
  edges.forEach((e) => {
@@ -775,7 +793,7 @@ class CustomNodeBaseComponent {
775
793
  }
776
794
  return merge(...Array.from(emittersOrRefs.keys()).map((emitter) => emitter.pipe(tap((event) => {
777
795
  this.eventBus.pushEvent({
778
- nodeId: this.nodeService.model()?.node.id ?? '',
796
+ nodeId: this.nodeService.model()?.rawNode.id ?? '',
779
797
  eventName: emittersOrRefs.get(emitter),
780
798
  eventPayload: event,
781
799
  });
@@ -859,6 +877,12 @@ function isTemplateStaticNode(node) {
859
877
  function isTemplateDynamicNode(node) {
860
878
  return node.type === 'html-template';
861
879
  }
880
+ function isSvgTemplateStaticNode(node) {
881
+ return node.type === 'svg-template';
882
+ }
883
+ function isSvgTemplateDynamicNode(node) {
884
+ return node.type === 'html-template';
885
+ }
862
886
  function isDefaultStaticNode(node) {
863
887
  return node.type === 'default';
864
888
  }
@@ -882,34 +906,48 @@ function isTemplateDynamicGroupNode(node) {
882
906
  // the bug reproduces if edgeLabelWrapperRef size fully matched the size of parent foreignObject
883
907
  const MAGIC_NUMBER_TO_FIX_GLITCH_IN_CHROME = 2;
884
908
 
909
+ function toUnifiedNode(node) {
910
+ if (isDynamicNode(node)) {
911
+ return node;
912
+ }
913
+ return {
914
+ ...toSignalProperties(node),
915
+ // non-signal props below
916
+ id: node.id,
917
+ // TODO this actually of incorrect type for component nodes
918
+ type: node.type,
919
+ };
920
+ }
921
+ function toSignalProperties(obj) {
922
+ const newObj = {};
923
+ for (const key in obj) {
924
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
925
+ newObj[key] = signal(obj[key]);
926
+ }
927
+ }
928
+ return newObj;
929
+ }
930
+
885
931
  class NodeModel {
886
932
  static { this.defaultWidth = 100; }
887
933
  static { this.defaultHeight = 50; }
888
934
  static { this.defaultColor = '#1b262c'; }
889
- constructor(node) {
890
- this.node = node;
935
+ constructor(rawNode) {
936
+ this.rawNode = rawNode;
891
937
  this.entitiesService = inject(FlowEntitiesService);
892
- this.point = this.createInternalPointSignal();
893
- this.point$ = toObservable(this.point);
894
- this.width = this.createWidthSignal(NodeModel.defaultWidth);
895
- this.width$ = toObservable(this.width);
896
- this.height = this.createHeightSignal(NodeModel.defaultHeight);
897
- this.height$ = toObservable(this.height);
938
+ this.point = signal({ x: 0, y: 0 });
939
+ this.width = signal(NodeModel.defaultWidth);
940
+ this.height = signal(NodeModel.defaultHeight);
898
941
  /**
899
942
  * @deprecated use width or height signals
900
943
  */
901
944
  this.size = computed(() => ({ width: this.width(), height: this.height() }));
902
- /**
903
- * @deprecated use width$ or height$
904
- */
905
- this.size$ = toObservable(this.size);
906
945
  this.styleWidth = computed(() => `${this.width()}px`);
907
946
  this.styleHeight = computed(() => `${this.height()}px`);
908
947
  this.foWidth = computed(() => this.width() + MAGIC_NUMBER_TO_FIX_GLITCH_IN_CHROME);
909
948
  this.foHeight = computed(() => this.height() + MAGIC_NUMBER_TO_FIX_GLITCH_IN_CHROME);
910
949
  this.renderOrder = signal(0);
911
950
  this.selected = signal(false);
912
- this.selected$ = toObservable(this.selected);
913
951
  this.globalPoint = computed(() => {
914
952
  let parent = this.parent();
915
953
  let x = this.point().x;
@@ -923,21 +961,20 @@ class NodeModel {
923
961
  });
924
962
  this.pointTransform = computed(() => `translate(${this.globalPoint().x}, ${this.globalPoint().y})`);
925
963
  this.handles = signal([]);
926
- this.handles$ = toObservable(this.handles);
927
964
  this.draggable = signal(true);
928
965
  this.dragHandlesCount = signal(0);
929
966
  // disabled for configuration for now
930
967
  this.magnetRadius = 20;
931
968
  // TODO: not sure if we need to statically store it
932
- this.isComponentType = isComponentStaticNode(this.node) || isComponentDynamicNode(this.node);
969
+ this.isComponentType = isComponentStaticNode(this.rawNode) || isComponentDynamicNode(this.rawNode);
933
970
  // Default node specific thing
934
- this.text = this.createTextSignal();
971
+ this.text = signal('');
935
972
  // Component node specific thing
936
973
  this.componentTypeInputs = {
937
- node: this.node,
974
+ node: this.rawNode,
938
975
  };
939
- this.parent = computed(() => this.entitiesService.nodes().find((n) => n.node.id === this.parentId()) ?? null);
940
- this.children = computed(() => this.entitiesService.nodes().filter((n) => n.parentId() === this.node.id));
976
+ this.parent = computed(() => this.entitiesService.nodes().find((n) => n.rawNode.id === this.parentId()) ?? null);
977
+ this.children = computed(() => this.entitiesService.nodes().filter((n) => n.parentId() === this.rawNode.id));
941
978
  this.color = signal(NodeModel.defaultColor);
942
979
  this.resizable = signal(false);
943
980
  this.resizing = signal(false);
@@ -946,85 +983,70 @@ class NodeModel {
946
983
  $implicit: {},
947
984
  };
948
985
  this.parentId = signal(null);
949
- if (isDefined(node.draggable)) {
950
- if (isDynamicNode(node)) {
951
- this.draggable = node.draggable;
952
- }
953
- else {
954
- this.draggable.set(node.draggable);
955
- }
986
+ const internalNode = toUnifiedNode(rawNode);
987
+ if (internalNode.point) {
988
+ this.point = internalNode.point;
956
989
  }
957
- if (isDefined(node.parentId)) {
958
- if (isDynamicNode(node)) {
959
- this.parentId = node.parentId;
960
- }
961
- else {
962
- this.parentId.set(node.parentId);
963
- }
990
+ if (internalNode.width) {
991
+ this.width = internalNode.width;
964
992
  }
965
- if (node.type === 'default-group' && node.color) {
966
- if (isDynamicNode(node)) {
967
- this.color = node.color;
968
- }
969
- else {
970
- this.color.set(node.color);
971
- }
993
+ if (internalNode.height) {
994
+ this.height = internalNode.height;
972
995
  }
973
- if (node.type === 'default-group' && node.resizable) {
974
- if (isDynamicNode(node)) {
975
- this.resizable = node.resizable;
976
- }
977
- else {
978
- this.resizable.set(node.resizable);
979
- }
996
+ if (internalNode.draggable) {
997
+ this.draggable = internalNode.draggable;
998
+ }
999
+ if (internalNode.parentId) {
1000
+ this.parentId = internalNode.parentId;
1001
+ }
1002
+ if (internalNode.type === 'default-group' && internalNode.color) {
1003
+ this.color = internalNode.color;
1004
+ }
1005
+ if (internalNode.type === 'default-group' && internalNode.resizable) {
1006
+ this.resizable = internalNode.resizable;
980
1007
  }
981
- if (node.type === 'html-template') {
1008
+ if (internalNode.type === 'default' && internalNode.text) {
1009
+ this.text = internalNode.text;
1010
+ }
1011
+ if (internalNode.type === 'html-template') {
982
1012
  this.context = {
983
1013
  $implicit: {
984
- node: node,
1014
+ node: rawNode,
985
1015
  selected: this.selected,
986
1016
  },
987
1017
  };
988
1018
  }
989
- else if (node.type === 'template-group') {
1019
+ if (internalNode.type === 'svg-template') {
990
1020
  this.context = {
991
1021
  $implicit: {
992
- node: node,
1022
+ node: rawNode,
1023
+ selected: this.selected,
1024
+ width: this.width,
1025
+ height: this.height,
1026
+ },
1027
+ };
1028
+ }
1029
+ if (internalNode.type === 'template-group') {
1030
+ this.context = {
1031
+ $implicit: {
1032
+ node: rawNode,
993
1033
  selected: this.selected.asReadonly(),
994
1034
  width: this.width,
995
1035
  height: this.height,
996
1036
  },
997
1037
  };
998
1038
  }
1039
+ // Initialize Observables after all signal assignments
1040
+ this.point$ = toObservable(this.point);
1041
+ this.width$ = toObservable(this.width);
1042
+ this.height$ = toObservable(this.height);
1043
+ this.size$ = toObservable(this.size);
1044
+ this.selected$ = toObservable(this.selected);
1045
+ this.handles$ = toObservable(this.handles);
999
1046
  }
1000
1047
  setPoint(point) {
1001
1048
  this.point.set(point);
1002
1049
  }
1003
- createTextSignal() {
1004
- const node = this.node;
1005
- if (node.type === 'default') {
1006
- if (isDynamicNode(node)) {
1007
- return node.text ?? signal('');
1008
- }
1009
- else {
1010
- return signal(node.text ?? '');
1011
- }
1012
- }
1013
- return signal('');
1014
- }
1015
- createInternalPointSignal() {
1016
- return isDynamicNode(this.node) ? this.node.point : signal({ x: this.node.point.x, y: this.node.point.y });
1017
- }
1018
- createWidthSignal(defaultValue) {
1019
- return isDynamicNode(this.node)
1020
- ? (this.node.width ?? signal(defaultValue))
1021
- : signal(this.node.width ?? defaultValue);
1022
- }
1023
- createHeightSignal(defaultValue) {
1024
- return isDynamicNode(this.node)
1025
- ? (this.node.height ?? signal(defaultValue))
1026
- : signal(this.node.height ?? defaultValue);
1027
- }
1028
1050
  }
1029
1051
 
1030
1052
  class EdgeLabelModel {
@@ -1410,13 +1432,13 @@ class EdgeModel {
1410
1432
  }
1411
1433
  }
1412
1434
 
1413
- class ReferenceKeeper {
1435
+ class ReferenceIdentityChecker {
1414
1436
  /**
1415
1437
  * Create new models for new node references and keep old models for old node references
1416
1438
  */
1417
1439
  static nodes(newNodes, oldNodeModels) {
1418
1440
  const oldNodesMap = new Map();
1419
- oldNodeModels.forEach((model) => oldNodesMap.set(model.node, model));
1441
+ oldNodeModels.forEach((model) => oldNodesMap.set(model.rawNode, model));
1420
1442
  return newNodes.map((newNode) => {
1421
1443
  if (oldNodesMap.has(newNode))
1422
1444
  return oldNodesMap.get(newNode);
@@ -1453,12 +1475,12 @@ class NodesChangeService {
1453
1475
  return this.entitiesService
1454
1476
  .nodes()
1455
1477
  .filter((node) => node === changedNode || node.selected())
1456
- .map((node) => ({ type: 'position', id: node.node.id, point: node.point() }));
1478
+ .map((node) => ({ type: 'position', id: node.rawNode.id, point: node.point() }));
1457
1479
  }));
1458
- this.nodeSizeChange$ = toObservable(this.entitiesService.nodes).pipe(switchMap((nodes) => merge(...nodes.map((node) => node.size$.pipe(skip(1), map(() => node))))), map((changedNode) => [{ type: 'size', id: changedNode.node.id, size: changedNode.size() }]));
1459
- this.nodeAddChange$ = toObservable(this.entitiesService.nodes).pipe(pairwise(), map(([oldList, newList]) => newList.filter((node) => !oldList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map((node) => ({ type: 'add', id: node.node.id }))));
1460
- this.nodeRemoveChange$ = toObservable(this.entitiesService.nodes).pipe(pairwise(), map(([oldList, newList]) => oldList.filter((node) => !newList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map((node) => ({ type: 'remove', id: node.node.id }))));
1461
- this.nodeSelectedChange$ = toObservable(this.entitiesService.nodes).pipe(switchMap((nodes) => merge(...nodes.map((node) => node.selected$.pipe(distinctUntilChanged(), skip(1), map(() => node))))), map((changedNode) => [{ type: 'select', id: changedNode.node.id, selected: changedNode.selected() }]));
1480
+ this.nodeSizeChange$ = toObservable(this.entitiesService.nodes).pipe(switchMap((nodes) => merge(...nodes.map((node) => node.size$.pipe(skip(1), map(() => node))))), map((changedNode) => [{ type: 'size', id: changedNode.rawNode.id, size: changedNode.size() }]));
1481
+ this.nodeAddChange$ = toObservable(this.entitiesService.nodes).pipe(pairwise(), map(([oldList, newList]) => newList.filter((node) => !oldList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map((node) => ({ type: 'add', id: node.rawNode.id }))));
1482
+ this.nodeRemoveChange$ = toObservable(this.entitiesService.nodes).pipe(pairwise(), map(([oldList, newList]) => oldList.filter((node) => !newList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map((node) => ({ type: 'remove', id: node.rawNode.id }))));
1483
+ this.nodeSelectedChange$ = toObservable(this.entitiesService.nodes).pipe(switchMap((nodes) => merge(...nodes.map((node) => node.selected$.pipe(distinctUntilChanged(), skip(1), map(() => node))))), map((changedNode) => [{ type: 'select', id: changedNode.rawNode.id, selected: changedNode.selected() }]));
1462
1484
  this.changes$ = merge(this.nodesPositionChange$, this.nodeSizeChange$, this.nodeAddChange$, this.nodeRemoveChange$, this.nodeSelectedChange$).pipe(
1463
1485
  // this fixes a bug when on fire node event change,
1464
1486
  // you can't get valid list of detached edges
@@ -1669,7 +1691,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
1669
1691
  }] });
1670
1692
 
1671
1693
  function isGroupNode(node) {
1672
- return node.node.type === 'default-group' || node.node.type === 'template-group';
1694
+ return node.rawNode.type === 'default-group' || node.rawNode.type === 'template-group';
1673
1695
  }
1674
1696
 
1675
1697
  class NodeRenderingService {
@@ -1816,10 +1838,11 @@ function Microtask(target, key, descriptor) {
1816
1838
  class OverlaysService {
1817
1839
  constructor() {
1818
1840
  this.toolbars = signal([]);
1819
- this.nodeToolbars = computed(() => {
1841
+ this.nodeToolbarsMap = computed(() => {
1820
1842
  const map = new Map();
1821
1843
  this.toolbars().forEach((toolbar) => {
1822
- map.set(toolbar.node, toolbar);
1844
+ const existing = map.get(toolbar.node) ?? [];
1845
+ map.set(toolbar.node, [...existing, toolbar]);
1823
1846
  });
1824
1847
  return map;
1825
1848
  });
@@ -2002,8 +2025,8 @@ class ConnectionControllerDirective {
2002
2025
  targetHandle = adjusted.targetHandle;
2003
2026
  }
2004
2027
  const valid = this.flowEntitiesService.connection().validator({
2005
- source: source.node.id,
2006
- target: target.node.id,
2028
+ source: source.rawNode.id,
2029
+ target: target.rawNode.id,
2007
2030
  sourceHandle: sourceHandle.rawHandle.id,
2008
2031
  targetHandle: targetHandle.rawHandle.id,
2009
2032
  });
@@ -2068,8 +2091,8 @@ function statusToConnection(status, isStrictMode) {
2068
2091
  sourceHandle = adjusted.sourceHandle;
2069
2092
  targetHandle = adjusted.targetHandle;
2070
2093
  }
2071
- const sourceId = source.node.id;
2072
- const targetId = target.node.id;
2094
+ const sourceId = source.rawNode.id;
2095
+ const targetId = target.rawNode.id;
2073
2096
  const sourceHandleId = sourceHandle.rawHandle.id;
2074
2097
  const targetHandleId = targetHandle.rawHandle.id;
2075
2098
  return {
@@ -2319,6 +2342,7 @@ class ResizableComponent {
2319
2342
  this.rootPointer = inject(RootPointerDirective);
2320
2343
  this.viewportService = inject(ViewportService);
2321
2344
  this.spacePointContext = inject(SpacePointContextDirective);
2345
+ this.settingsService = inject(FlowSettingsService);
2322
2346
  this.hostRef = inject(ElementRef);
2323
2347
  this.resizable = input();
2324
2348
  this.resizerColor = input('#2e414c');
@@ -2363,7 +2387,7 @@ class ResizableComponent {
2363
2387
  if (!this.resizeSide)
2364
2388
  return;
2365
2389
  const offset = calcOffset(event.movementX, event.movementY, this.zoom());
2366
- const resized = applyResize(this.resizeSide, this.model, offset, this.getDistanceToEdge(event));
2390
+ const resized = this.applyResize(this.resizeSide, this.model, offset, this.getDistanceToEdge(event));
2367
2391
  const { x, y, width, height } = constrainRect(resized, this.model, this.resizeSide, this.minWidth, this.minHeight);
2368
2392
  this.model.setPoint({ x, y });
2369
2393
  this.model.width.set(width);
@@ -2383,6 +2407,104 @@ class ResizableComponent {
2383
2407
  bottom: flowPoint.y - (y + this.model.height()),
2384
2408
  };
2385
2409
  }
2410
+ applyResize(side, model, offset, distanceToEdge) {
2411
+ const { x, y } = model.point();
2412
+ const width = model.width();
2413
+ const height = model.height();
2414
+ const [snapX, snapY] = this.settingsService.snapGrid();
2415
+ switch (side) {
2416
+ case 'left': {
2417
+ const movementX = offset.x + distanceToEdge.left;
2418
+ const newX = align(x + movementX, snapX);
2419
+ const deltaX = newX - x;
2420
+ return {
2421
+ x: newX,
2422
+ y,
2423
+ width: width - deltaX,
2424
+ height,
2425
+ };
2426
+ }
2427
+ case 'right': {
2428
+ const movementX = offset.x + distanceToEdge.right;
2429
+ const newWidth = align(width + movementX, snapX);
2430
+ return {
2431
+ x,
2432
+ y,
2433
+ width: newWidth,
2434
+ height,
2435
+ };
2436
+ }
2437
+ case 'top': {
2438
+ const movementY = offset.y + distanceToEdge.top;
2439
+ const newY = align(y + movementY, snapY);
2440
+ const deltaY = newY - y;
2441
+ return {
2442
+ x,
2443
+ y: newY,
2444
+ width,
2445
+ height: height - deltaY,
2446
+ };
2447
+ }
2448
+ case 'bottom': {
2449
+ const movementY = offset.y + distanceToEdge.bottom;
2450
+ const newHeight = align(height + movementY, snapY);
2451
+ return {
2452
+ x,
2453
+ y,
2454
+ width,
2455
+ height: newHeight,
2456
+ };
2457
+ }
2458
+ case 'top-left': {
2459
+ const movementX = offset.x + distanceToEdge.left;
2460
+ const movementY = offset.y + distanceToEdge.top;
2461
+ const newX = align(x + movementX, snapX);
2462
+ const newY = align(y + movementY, snapY);
2463
+ const deltaX = newX - x;
2464
+ const deltaY = newY - y;
2465
+ return {
2466
+ x: newX,
2467
+ y: newY,
2468
+ width: width - deltaX,
2469
+ height: height - deltaY,
2470
+ };
2471
+ }
2472
+ case 'top-right': {
2473
+ const movementX = offset.x + distanceToEdge.right;
2474
+ const movementY = offset.y + distanceToEdge.top;
2475
+ const newY = align(y + movementY, snapY);
2476
+ const deltaY = newY - y;
2477
+ return {
2478
+ x,
2479
+ y: newY,
2480
+ width: align(width + movementX, snapX),
2481
+ height: height - deltaY,
2482
+ };
2483
+ }
2484
+ case 'bottom-left': {
2485
+ const movementX = offset.x + distanceToEdge.left;
2486
+ const movementY = offset.y + distanceToEdge.bottom;
2487
+ const newX = align(x + movementX, snapX);
2488
+ const deltaX = newX - x;
2489
+ return {
2490
+ x: newX,
2491
+ y,
2492
+ width: width - deltaX,
2493
+ height: align(height + movementY, snapY),
2494
+ };
2495
+ }
2496
+ case 'bottom-right': {
2497
+ const movementX = offset.x + distanceToEdge.right;
2498
+ const movementY = offset.y + distanceToEdge.bottom;
2499
+ return {
2500
+ x,
2501
+ y,
2502
+ width: align(width + movementX, snapX),
2503
+ height: align(height + movementY, snapY),
2504
+ };
2505
+ }
2506
+ }
2507
+ }
2386
2508
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ResizableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2387
2509
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "17.3.12", type: ResizableComponent, 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 } }, viewQueries: [{ propertyName: "resizer", first: true, predicate: ["resizer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #resizer>\n <svg:g>\n <!-- top line -->\n <svg:line\n class=\"top\"\n stroke-width=\"2\"\n [attr.x1]=\"lineGap\"\n [attr.y1]=\"-gap()\"\n [attr.x2]=\"model.size().width - lineGap\"\n [attr.y2]=\"-gap()\"\n [attr.stroke]=\"resizerColor()\"\n (pointerStart)=\"startResize('top', $event)\" />\n <!-- Left line -->\n <svg:line\n class=\"left\"\n stroke-width=\"2\"\n [attr.x1]=\"-gap()\"\n [attr.y1]=\"lineGap\"\n [attr.x2]=\"-gap()\"\n [attr.y2]=\"model.size().height - lineGap\"\n [attr.stroke]=\"resizerColor()\"\n (pointerStart)=\"startResize('left', $event)\" />\n <!-- Bottom line -->\n <svg:line\n class=\"bottom\"\n stroke-width=\"2\"\n [attr.x1]=\"lineGap\"\n [attr.y1]=\"model.size().height + gap()\"\n [attr.x2]=\"model.size().width - lineGap\"\n [attr.y2]=\"model.size().height + gap()\"\n [attr.stroke]=\"resizerColor()\"\n (pointerStart)=\"startResize('bottom', $event)\" />\n <!-- Right line -->\n <svg:line\n class=\"right\"\n stroke-width=\"2\"\n [attr.x1]=\"model.size().width + gap()\"\n [attr.y1]=\"lineGap\"\n [attr.x2]=\"model.size().width + gap()\"\n [attr.y2]=\"model.size().height - lineGap\"\n [attr.stroke]=\"resizerColor()\"\n (pointerStart)=\"startResize('right', $event)\" />\n\n <!-- Top Left -->\n <svg:rect\n class=\"top-left\"\n [attr.x]=\"-(handleSize / 2) - gap()\"\n [attr.y]=\"-(handleSize / 2) - gap()\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor()\"\n (pointerStart)=\"startResize('top-left', $event)\" />\n\n <!-- Top right -->\n <svg:rect\n class=\"top-right\"\n [attr.x]=\"model.size().width - handleSize / 2 + gap()\"\n [attr.y]=\"-(handleSize / 2) - gap()\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor()\"\n (pointerStart)=\"startResize('top-right', $event)\" />\n\n <!-- Bottom left -->\n <svg:rect\n class=\"bottom-left\"\n [attr.x]=\"-(handleSize / 2) - gap()\"\n [attr.y]=\"model.size().height - handleSize / 2 + gap()\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor()\"\n (pointerStart)=\"startResize('bottom-left', $event)\" />\n\n <!-- Bottom right -->\n <svg:rect\n class=\"bottom-right\"\n [attr.x]=\"model.size().width - handleSize / 2 + gap()\"\n [attr.y]=\"model.size().height - handleSize / 2 + gap()\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor()\"\n (pointerStart)=\"startResize('bottom-right', $event)\" />\n </svg:g>\n</ng-template>\n\n<ng-content />\n", styles: [".top{cursor:n-resize}.left{cursor:w-resize}.right{cursor:e-resize}.bottom{cursor:s-resize}.top-left{cursor:nw-resize}.top-right{cursor:ne-resize}.bottom-left{cursor:sw-resize}.bottom-right{cursor:se-resize}\n"], dependencies: [{ kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2388
2510
  }
@@ -2395,50 +2517,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2395
2517
  }], ctorParameters: () => [], propDecorators: { ngAfterViewInit: [] } });
2396
2518
  function calcOffset(movementX, movementY, zoom) {
2397
2519
  return {
2398
- offsetX: round(movementX / zoom),
2399
- offsetY: round(movementY / zoom),
2520
+ x: round(movementX / zoom),
2521
+ y: round(movementY / zoom),
2400
2522
  };
2401
2523
  }
2402
- function applyResize(side, model, offset, distanceToEdge) {
2403
- const { offsetX, offsetY } = offset;
2404
- const { x, y } = model.point();
2405
- const width = model.width();
2406
- const height = model.height();
2407
- // Handle each case of resizing (top, bottom, left, right, corners)
2408
- switch (side) {
2409
- case 'left':
2410
- return { x: x + offsetX + distanceToEdge.left, y, width: width - offsetX - distanceToEdge.left, height };
2411
- case 'right':
2412
- return { x, y, width: width + offsetX + distanceToEdge.right, height };
2413
- case 'top':
2414
- return { x, y: y + offsetY + distanceToEdge.top, width, height: height - offsetY - distanceToEdge.top };
2415
- case 'bottom':
2416
- return { x, y, width, height: height + offsetY + distanceToEdge.bottom };
2417
- case 'top-left':
2418
- return {
2419
- x: x + offsetX + distanceToEdge.left,
2420
- y: y + offsetY + distanceToEdge.top,
2421
- width: width - offsetX - distanceToEdge.left,
2422
- height: height - offsetY - distanceToEdge.top,
2423
- };
2424
- case 'top-right':
2425
- return {
2426
- x,
2427
- y: y + offsetY + distanceToEdge.top,
2428
- width: width + offsetX + distanceToEdge.right,
2429
- height: height - offsetY - distanceToEdge.top,
2430
- };
2431
- case 'bottom-left':
2432
- return {
2433
- x: x + offsetX + distanceToEdge.left,
2434
- y,
2435
- width: width - offsetX - distanceToEdge.left,
2436
- height: height + offsetY + distanceToEdge.bottom,
2437
- };
2438
- case 'bottom-right':
2439
- return { x, y, width: width + offsetX + distanceToEdge.right, height: height + offsetY + distanceToEdge.bottom };
2440
- }
2441
- }
2442
2524
  function constrainRect(rect, model, side, minWidth, minHeight) {
2443
2525
  let { x, y, width, height } = rect;
2444
2526
  // 1. Prevent negative dimensions
@@ -2568,7 +2650,7 @@ class HandleModel {
2568
2650
  $implicit: {
2569
2651
  point: this.hostOffset,
2570
2652
  state: this.state,
2571
- node: this.parentNode.node,
2653
+ node: this.parentNode.rawNode,
2572
2654
  },
2573
2655
  };
2574
2656
  }
@@ -2661,6 +2743,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2661
2743
  }]
2662
2744
  }] });
2663
2745
 
2746
+ /**
2747
+ * Only suitable for HTML nodes
2748
+ */
2664
2749
  class NodeResizeControllerDirective {
2665
2750
  constructor() {
2666
2751
  this.nodeAccessor = inject(NodeAccessorService);
@@ -2706,12 +2791,13 @@ class NodeComponent {
2706
2791
  this.connectionController = inject(ConnectionControllerDirective, { optional: true });
2707
2792
  this.model = input.required();
2708
2793
  this.nodeTemplate = input();
2794
+ this.nodeSvgTemplate = input();
2709
2795
  this.groupNodeTemplate = input();
2710
2796
  this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
2711
2797
  this.flowStatusService.status().state === 'connection-validation' ||
2712
2798
  this.flowStatusService.status().state === 'reconnection-start' ||
2713
2799
  this.flowStatusService.status().state === 'reconnection-validation');
2714
- this.toolbar = computed(() => this.overlaysService.nodeToolbars().get(this.model()));
2800
+ this.toolbars = computed(() => this.overlaysService.nodeToolbarsMap().get(this.model()));
2715
2801
  }
2716
2802
  ngOnInit() {
2717
2803
  this.nodeAccessor.model.set(this.model());
@@ -2753,7 +2839,7 @@ class NodeComponent {
2753
2839
  }
2754
2840
  }
2755
2841
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2756
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NodeComponent, isStandalone: true, selector: "g[node]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, groupNodeTemplate: { classPropertyName: "groupNodeTemplate", publicName: "groupNodeTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "vflow-node" }, providers: [HandleService, NodeAccessorService], ngImport: i0, template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- Template node -->\n@if (model().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().node.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@if (toolbar(); as toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"], dependencies: [{ kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: NodeHandlesControllerDirective, selector: "[nodeHandlesController]" }, { kind: "directive", type: NodeResizeControllerDirective, selector: "[nodeResizeController]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2842
+ 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 }, nodeSvgTemplate: { classPropertyName: "nodeSvgTemplate", publicName: "nodeSvgTemplate", 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().rawNode.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- HTML Template node -->\n@if (model().rawNode.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- SVG Template node -->\n@if (model().rawNode.type === 'svg-template' && nodeSvgTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeSvgTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().rawNode.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().rawNode.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().rawNode.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@for (toolbar of toolbars(); track 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 }); }
2757
2843
  }
2758
2844
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, decorators: [{
2759
2845
  type: Component,
@@ -2769,7 +2855,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2769
2855
  HandleSizeControllerDirective,
2770
2856
  NodeHandlesControllerDirective,
2771
2857
  NodeResizeControllerDirective,
2772
- ], template: "<!-- Default node -->\n@if (model().node.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- Template node -->\n@if (model().node.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().node.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().node.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (pointerStart)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().node.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@if (toolbar(); as toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
2858
+ ], template: "<!-- Default node -->\n@if (model().rawNode.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- HTML Template node -->\n@if (model().rawNode.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- SVG Template node -->\n@if (model().rawNode.type === 'svg-template' && nodeSvgTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeSvgTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (pointerStart)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(model().rawNode.type)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- Default group node -->\n@if (model().rawNode.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().rawNode.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (pointerStart)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (!handle.template) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@for (toolbar of toolbars(); track 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"] }]
2773
2859
  }] });
2774
2860
 
2775
2861
  class ConnectionComponent {
@@ -3161,6 +3247,7 @@ class VflowComponent {
3161
3247
  // #endregion
3162
3248
  // #region TEMPLATES
3163
3249
  this.nodeTemplateDirective = contentChild(NodeHtmlTemplateDirective);
3250
+ this.nodeSvgTemplateDirective = contentChild(NodeSvgTemplateDirective);
3164
3251
  this.groupNodeTemplateDirective = contentChild(GroupNodeTemplateDirective);
3165
3252
  this.edgeTemplateDirective = contentChild(EdgeTemplateDirective);
3166
3253
  this.edgeLabelHtmlDirective = contentChild(EdgeLabelHtmlTemplateDirective);
@@ -3279,7 +3366,7 @@ class VflowComponent {
3279
3366
  * Nodes to render
3280
3367
  */
3281
3368
  set nodes(newNodes) {
3282
- const newModels = runInInjectionContext(this.injector, () => ReferenceKeeper.nodes(newNodes, this.flowEntitiesService.nodes()));
3369
+ const newModels = runInInjectionContext(this.injector, () => ReferenceIdentityChecker.nodes(newNodes, this.flowEntitiesService.nodes()));
3283
3370
  // quick and dirty binding nodes to edges
3284
3371
  addNodesToEdges(newModels, this.flowEntitiesService.edges());
3285
3372
  this.flowEntitiesService.nodes.set(newModels);
@@ -3288,7 +3375,7 @@ class VflowComponent {
3288
3375
  * Edges to render
3289
3376
  */
3290
3377
  set edges(newEdges) {
3291
- const newModels = runInInjectionContext(this.injector, () => ReferenceKeeper.edges(newEdges, this.flowEntitiesService.edges()));
3378
+ const newModels = runInInjectionContext(this.injector, () => ReferenceIdentityChecker.edges(newEdges, this.flowEntitiesService.edges()));
3292
3379
  // quick and dirty binding nodes to edges
3293
3380
  addNodesToEdges(this.nodeModels(), newModels);
3294
3381
  this.flowEntitiesService.edges.set(newModels);
@@ -3342,7 +3429,7 @@ class VflowComponent {
3342
3429
  * @param id node id
3343
3430
  */
3344
3431
  getNode(id) {
3345
- return this.flowEntitiesService.getNode(id)?.node;
3432
+ return this.flowEntitiesService.getNode(id)?.rawNode;
3346
3433
  }
3347
3434
  /**
3348
3435
  * Sync method to get detached edges
@@ -3357,7 +3444,7 @@ class VflowComponent {
3357
3444
  return this.spacePointContext().documentPointToFlowPoint(point);
3358
3445
  }
3359
3446
  // #endregion
3360
- trackNodes(idx, { node }) {
3447
+ trackNodes(idx, { rawNode: node }) {
3361
3448
  return node;
3362
3449
  }
3363
3450
  trackEdges(idx, { edge }) {
@@ -3365,7 +3452,7 @@ class VflowComponent {
3365
3452
  }
3366
3453
  setInitialNodesOrder() {
3367
3454
  this.nodeModels().forEach((model) => {
3368
- switch (model.node.type) {
3455
+ switch (model.rawNode.type) {
3369
3456
  case 'default-group':
3370
3457
  case 'template-group': {
3371
3458
  this.nodeRenderingService.pullNode(model);
@@ -3388,7 +3475,7 @@ class VflowComponent {
3388
3475
  ComponentEventBusService,
3389
3476
  KeyboardService,
3390
3477
  OverlaysService,
3391
- ], 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 }); }
3478
+ ], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "nodeSvgTemplateDirective", first: true, predicate: NodeSvgTemplateDirective, 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 [nodeSvgTemplate]=\"nodeSvgTemplateDirective()?.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 [nodeSvgTemplate]=\"nodeSvgTemplateDirective()?.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", "nodeSvgTemplate", "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 }); }
3392
3479
  }
3393
3480
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: VflowComponent, decorators: [{
3394
3481
  type: Component,
@@ -3419,7 +3506,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3419
3506
  NodeComponent,
3420
3507
  EdgeComponent,
3421
3508
  NgTemplateOutlet,
3422
- ], 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"] }]
3509
+ ], 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 [nodeSvgTemplate]=\"nodeSvgTemplateDirective()?.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 [nodeSvgTemplate]=\"nodeSvgTemplateDirective()?.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"] }]
3423
3510
  }], propDecorators: { view: [{
3424
3511
  type: Input
3425
3512
  }], minZoom: [{
@@ -3609,15 +3696,15 @@ class MiniMapComponent {
3609
3696
  model.template.set(this.minimap());
3610
3697
  this.entitiesService.minimap.set(model);
3611
3698
  }
3612
- trackNodes(idx, { node }) {
3613
- return node;
3699
+ trackNodes(idx, { rawNode }) {
3700
+ return rawNode;
3614
3701
  }
3615
3702
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MiniMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3616
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MiniMapComponent, 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 } }, viewQueries: [{ propertyName: "minimap", first: true, predicate: ["minimap"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #minimap>\n <svg:rect\n fill=\"none\"\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.stroke]=\"strokeColor()\" />\n\n <svg:svg\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n (mouseover)=\"hovered.set(true)\"\n (mouseleave)=\"hovered.set(false)\">\n <svg:rect [attr.width]=\"minimapWidth()\" [attr.height]=\"minimapHeight()\" [attr.fill]=\"maskColor()\" />\n\n <svg:g [attr.transform]=\"minimapTransform()\">\n <svg:rect\n [attr.fill]=\"viewportColor()\"\n [attr.transform]=\"viewportTransform()\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\" />\n\n @for (model of entitiesService.nodes(); track trackNodes($index, model)) {\n <ng-container>\n @if (model.node.type === 'default' || model.node.type === 'html-template' || model.isComponentType) {\n <svg:foreignObject\n [attr.transform]=\"model.pointTransform()\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <default-node\n [selected]=\"model.selected()\"\n [style.width.px]=\"model.size().width\"\n [style.height.px]=\"model.size().height\"\n [style.max-width.px]=\"model.size().width\"\n [style.max-height.px]=\"model.size().height\">\n <div [outerHTML]=\"model.text()\"></div>\n </default-node>\n </svg:foreignObject>\n }\n @if (model.node.type === 'default-group' || model.node.type === 'template-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [attr.transform]=\"model.pointTransform()\"\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 }\n </ng-container>\n }\n </svg:g>\n </svg:svg>\n</ng-template>\n", styles: [".default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}\n"], dependencies: [{ kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3703
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MiniMapComponent, 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 } }, viewQueries: [{ propertyName: "minimap", first: true, predicate: ["minimap"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #minimap>\n <svg:rect\n fill=\"none\"\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.stroke]=\"strokeColor()\" />\n\n <svg:svg\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n (mouseover)=\"hovered.set(true)\"\n (mouseleave)=\"hovered.set(false)\">\n <svg:rect [attr.width]=\"minimapWidth()\" [attr.height]=\"minimapHeight()\" [attr.fill]=\"maskColor()\" />\n\n <svg:g [attr.transform]=\"minimapTransform()\">\n <svg:rect\n [attr.fill]=\"viewportColor()\"\n [attr.transform]=\"viewportTransform()\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\" />\n\n @for (model of entitiesService.nodes(); track trackNodes($index, model)) {\n <ng-container>\n @if (model.rawNode.type === 'default' || model.rawNode.type === 'html-template' || model.isComponentType) {\n <svg:foreignObject\n [attr.transform]=\"model.pointTransform()\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <default-node\n [selected]=\"model.selected()\"\n [style.width.px]=\"model.size().width\"\n [style.height.px]=\"model.size().height\"\n [style.max-width.px]=\"model.size().width\"\n [style.max-height.px]=\"model.size().height\">\n <div [outerHTML]=\"model.text()\"></div>\n </default-node>\n </svg:foreignObject>\n }\n @if (model.rawNode.type === 'default-group' || model.rawNode.type === 'template-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [attr.transform]=\"model.pointTransform()\"\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 }\n </ng-container>\n }\n </svg:g>\n </svg:svg>\n</ng-template>\n", styles: [".default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}\n"], dependencies: [{ kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3617
3704
  }
3618
3705
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MiniMapComponent, decorators: [{
3619
3706
  type: Component,
3620
- args: [{ standalone: true, selector: 'mini-map', imports: [DefaultNodeComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #minimap>\n <svg:rect\n fill=\"none\"\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.stroke]=\"strokeColor()\" />\n\n <svg:svg\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n (mouseover)=\"hovered.set(true)\"\n (mouseleave)=\"hovered.set(false)\">\n <svg:rect [attr.width]=\"minimapWidth()\" [attr.height]=\"minimapHeight()\" [attr.fill]=\"maskColor()\" />\n\n <svg:g [attr.transform]=\"minimapTransform()\">\n <svg:rect\n [attr.fill]=\"viewportColor()\"\n [attr.transform]=\"viewportTransform()\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\" />\n\n @for (model of entitiesService.nodes(); track trackNodes($index, model)) {\n <ng-container>\n @if (model.node.type === 'default' || model.node.type === 'html-template' || model.isComponentType) {\n <svg:foreignObject\n [attr.transform]=\"model.pointTransform()\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <default-node\n [selected]=\"model.selected()\"\n [style.width.px]=\"model.size().width\"\n [style.height.px]=\"model.size().height\"\n [style.max-width.px]=\"model.size().width\"\n [style.max-height.px]=\"model.size().height\">\n <div [outerHTML]=\"model.text()\"></div>\n </default-node>\n </svg:foreignObject>\n }\n @if (model.node.type === 'default-group' || model.node.type === 'template-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [attr.transform]=\"model.pointTransform()\"\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 }\n </ng-container>\n }\n </svg:g>\n </svg:svg>\n</ng-template>\n", styles: [".default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}\n"] }]
3707
+ args: [{ standalone: true, selector: 'mini-map', imports: [DefaultNodeComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #minimap>\n <svg:rect\n fill=\"none\"\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.stroke]=\"strokeColor()\" />\n\n <svg:svg\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n (mouseover)=\"hovered.set(true)\"\n (mouseleave)=\"hovered.set(false)\">\n <svg:rect [attr.width]=\"minimapWidth()\" [attr.height]=\"minimapHeight()\" [attr.fill]=\"maskColor()\" />\n\n <svg:g [attr.transform]=\"minimapTransform()\">\n <svg:rect\n [attr.fill]=\"viewportColor()\"\n [attr.transform]=\"viewportTransform()\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\" />\n\n @for (model of entitiesService.nodes(); track trackNodes($index, model)) {\n <ng-container>\n @if (model.rawNode.type === 'default' || model.rawNode.type === 'html-template' || model.isComponentType) {\n <svg:foreignObject\n [attr.transform]=\"model.pointTransform()\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\">\n <default-node\n [selected]=\"model.selected()\"\n [style.width.px]=\"model.size().width\"\n [style.height.px]=\"model.size().height\"\n [style.max-width.px]=\"model.size().width\"\n [style.max-height.px]=\"model.size().height\">\n <div [outerHTML]=\"model.text()\"></div>\n </default-node>\n </svg:foreignObject>\n }\n @if (model.rawNode.type === 'default-group' || model.rawNode.type === 'template-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [attr.transform]=\"model.pointTransform()\"\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 }\n </ng-container>\n }\n </svg:g>\n </svg:svg>\n</ng-template>\n", styles: [".default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}\n"] }]
3621
3708
  }] });
3622
3709
 
3623
3710
  class ToolbarModel {
@@ -3693,9 +3780,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3693
3780
  class NodeToolbarWrapperDirective {
3694
3781
  constructor() {
3695
3782
  this.element = inject(ElementRef);
3783
+ this.zone = inject(NgZone);
3784
+ this.destroyRef = inject(DestroyRef);
3696
3785
  this.model = input.required();
3697
3786
  }
3698
3787
  ngOnInit() {
3788
+ resizable([this.element.nativeElement], this.zone)
3789
+ .pipe(tap(() => this.setSize()), takeUntilDestroyed(this.destroyRef))
3790
+ .subscribe();
3791
+ }
3792
+ setSize() {
3699
3793
  this.model().size.set({
3700
3794
  width: this.element.nativeElement.clientWidth,
3701
3795
  height: this.element.nativeElement.clientHeight,
@@ -3747,6 +3841,7 @@ const Vflow = [
3747
3841
  DragHandleDirective,
3748
3842
  ConnectionControllerDirective,
3749
3843
  NodeHtmlTemplateDirective,
3844
+ NodeSvgTemplateDirective,
3750
3845
  GroupNodeTemplateDirective,
3751
3846
  EdgeLabelHtmlTemplateDirective,
3752
3847
  EdgeTemplateDirective,
@@ -3866,6 +3961,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
3866
3961
  selector: 'ng-template[nodeHtml]',
3867
3962
  }]
3868
3963
  }] });
3964
+ class NodeSvgTemplateMockDirective {
3965
+ constructor() {
3966
+ this.templateRef = inject(TemplateRef);
3967
+ }
3968
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeSvgTemplateMockDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3969
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: NodeSvgTemplateMockDirective, isStandalone: true, selector: "ng-template[nodeSvg]", ngImport: i0 }); }
3970
+ }
3971
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeSvgTemplateMockDirective, decorators: [{
3972
+ type: Directive,
3973
+ args: [{
3974
+ standalone: true,
3975
+ selector: 'ng-template[nodeSvg]',
3976
+ }]
3977
+ }] });
3869
3978
  class GroupNodeTemplateMockDirective {
3870
3979
  constructor() {
3871
3980
  this.templateRef = inject(TemplateRef);
@@ -4325,5 +4434,5 @@ const VflowMocks = [
4325
4434
  * Generated bundle index. Do not edit.
4326
4435
  */
4327
4436
 
4328
- export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionControllerMockDirective, ConnectionTemplateDirective, ConnectionTemplateMockDirective, CustomDynamicNodeComponent, CustomNodeComponent, CustomTemplateEdgeComponent, DragHandleDirective, DragHandleMockDirective, EdgeLabelHtmlTemplateDirective, EdgeLabelHtmlTemplateMockDirective, EdgeTemplateDirective, EdgeTemplateMockDirective, GroupNodeTemplateDirective, GroupNodeTemplateMockDirective, HandleComponent, HandleMockComponent, HandleTemplateDirective, HandleTemplateMockDirective, MiniMapComponent, MiniMapMockComponent, NodeHtmlTemplateDirective, NodeHtmlTemplateMockDirective, NodeToolbarComponent, NodeToolbarMockComponent, NodeToolbarWrapperDirective, ResizableComponent, ResizableMockComponent, SelectableDirective, SelectableMockDirective, Vflow, VflowComponent, VflowMockComponent, VflowMocks, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, provideCustomNodeMocks };
4437
+ export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionControllerMockDirective, ConnectionTemplateDirective, ConnectionTemplateMockDirective, CustomDynamicNodeComponent, CustomNodeComponent, CustomTemplateEdgeComponent, DragHandleDirective, DragHandleMockDirective, EdgeLabelHtmlTemplateDirective, EdgeLabelHtmlTemplateMockDirective, EdgeTemplateDirective, EdgeTemplateMockDirective, GroupNodeTemplateDirective, GroupNodeTemplateMockDirective, HandleComponent, HandleMockComponent, HandleTemplateDirective, HandleTemplateMockDirective, MiniMapComponent, MiniMapMockComponent, NodeHtmlTemplateDirective, NodeHtmlTemplateMockDirective, NodeSvgTemplateDirective, NodeSvgTemplateMockDirective, NodeToolbarComponent, NodeToolbarMockComponent, NodeToolbarWrapperDirective, ResizableComponent, ResizableMockComponent, SelectableDirective, SelectableMockDirective, Vflow, VflowComponent, VflowMockComponent, VflowMocks, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isSvgTemplateDynamicNode, isSvgTemplateStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, provideCustomNodeMocks };
4329
4438
  //# sourceMappingURL=ngx-vflow.mjs.map