ngx-vflow 0.14.0 → 0.15.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 (35) hide show
  1. package/esm2022/lib/vflow/components/custom-node-base/custom-node-base.component.mjs +5 -7
  2. package/esm2022/lib/vflow/components/defs/defs.component.mjs +1 -1
  3. package/esm2022/lib/vflow/components/handle/handle.component.mjs +16 -14
  4. package/esm2022/lib/vflow/components/node/node.component.mjs +8 -3
  5. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +8 -5
  6. package/esm2022/lib/vflow/directives/drag-handle.directive.mjs +27 -0
  7. package/esm2022/lib/vflow/directives/map-context.directive.mjs +24 -17
  8. package/esm2022/lib/vflow/math/edge-path/bezier-path.mjs +14 -17
  9. package/esm2022/lib/vflow/math/edge-path/straigh-path.mjs +2 -6
  10. package/esm2022/lib/vflow/models/handle.model.mjs +3 -2
  11. package/esm2022/lib/vflow/models/node.model.mjs +2 -1
  12. package/esm2022/lib/vflow/models/toolbar.model.mjs +36 -0
  13. package/esm2022/lib/vflow/public-components/minimap/minimap.component.mjs +1 -1
  14. package/esm2022/lib/vflow/public-components/node-toolbar/node-toolbar.component.mjs +66 -0
  15. package/esm2022/lib/vflow/services/draggable.service.mjs +13 -15
  16. package/esm2022/lib/vflow/services/overlays.service.mjs +34 -0
  17. package/esm2022/lib/vflow/testing-utils/provide-custom-node-mocks.mjs +67 -0
  18. package/esm2022/lib/vflow/vflow.module.mjs +21 -9
  19. package/esm2022/public-api.mjs +5 -1
  20. package/fesm2022/ngx-vflow.mjs +308 -87
  21. package/fesm2022/ngx-vflow.mjs.map +1 -1
  22. package/lib/vflow/components/handle/handle.component.d.ts +3 -3
  23. package/lib/vflow/components/node/node.component.d.ts +2 -0
  24. package/lib/vflow/directives/drag-handle.directive.d.ts +8 -0
  25. package/lib/vflow/directives/map-context.directive.d.ts +3 -2
  26. package/lib/vflow/models/handle.model.d.ts +1 -0
  27. package/lib/vflow/models/node.model.d.ts +1 -0
  28. package/lib/vflow/models/toolbar.model.d.ts +19 -0
  29. package/lib/vflow/public-components/node-toolbar/node-toolbar.component.d.ts +22 -0
  30. package/lib/vflow/services/draggable.service.d.ts +0 -5
  31. package/lib/vflow/services/overlays.service.d.ts +11 -0
  32. package/lib/vflow/testing-utils/provide-custom-node-mocks.d.ts +2 -0
  33. package/lib/vflow/vflow.module.d.ts +14 -12
  34. package/package.json +1 -3
  35. package/public-api.d.ts +3 -0
@@ -1,13 +1,12 @@
1
1
  import * as i1 from '@angular/common';
2
- import { CommonModule } from '@angular/common';
2
+ import { NgIf, NgFor, NgTemplateOutlet, NgComponentOutlet, KeyValuePipe } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, EventEmitter, Output, DestroyRef, Input, runInInjectionContext, Component, Injector, ChangeDetectionStrategy, HostListener, ViewChild, NgZone, ContentChild, NgModule } from '@angular/core';
5
5
  import { select } from 'd3-selection';
6
6
  import { zoomIdentity, zoom } from 'd3-zoom';
7
- import { switchMap, merge, fromEvent, tap, Subject, observeOn, animationFrameScheduler, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, share, Observable, startWith } from 'rxjs';
7
+ import { switchMap, merge, fromEvent, tap, Subject, observeOn, animationFrameScheduler, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, share, Observable, startWith, of } from 'rxjs';
8
8
  import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
9
9
  import { drag } from 'd3-drag';
10
- import { path } from 'd3-path';
11
10
  import { __decorate } from 'tslib';
12
11
 
13
12
  function getNodesBounds(nodes) {
@@ -407,30 +406,37 @@ class MapContextDirective {
407
406
  this.viewportService.readableViewport.set(mapTransformToViewportState(transform));
408
407
  this.zoomableSelection.attr('transform', transform.toString());
409
408
  };
409
+ this.handleZoomStart = ({ transform }) => {
410
+ this.viewportForSelection = {
411
+ start: mapTransformToViewportState(transform)
412
+ };
413
+ };
414
+ this.handleZoomEnd = ({ transform, sourceEvent }) => {
415
+ this.viewportForSelection = {
416
+ ...this.viewportForSelection,
417
+ end: mapTransformToViewportState(transform),
418
+ target: evTarget(sourceEvent)
419
+ };
420
+ this.selectionService.setViewport(this.viewportForSelection);
421
+ };
422
+ this.filterCondition = (event) => {
423
+ if (event.type === 'mousedown' || event.type === 'touchstart') {
424
+ return event.target.closest('.vflow-node') === null;
425
+ }
426
+ return true;
427
+ };
410
428
  }
411
429
  ngOnInit() {
412
430
  this.zoomBehavior = zoom()
413
431
  .scaleExtent([this.flowSettingsService.minZoom(), this.flowSettingsService.maxZoom()])
414
- .on('start', (event) => this.onD3zoomStart(event))
415
- .on('zoom', (event) => this.handleZoom(event))
416
- .on('end', (event) => this.onD3zoomEnd(event));
432
+ .filter(this.filterCondition)
433
+ .on('start', this.handleZoomStart)
434
+ .on('zoom', this.handleZoom)
435
+ .on('end', this.handleZoomEnd);
417
436
  this.rootSvgSelection
418
437
  .call(this.zoomBehavior)
419
438
  .on('dblclick.zoom', null);
420
439
  }
421
- onD3zoomStart({ transform }) {
422
- this.viewportForSelection = {
423
- start: mapTransformToViewportState(transform)
424
- };
425
- }
426
- onD3zoomEnd({ transform, sourceEvent }) {
427
- this.viewportForSelection = {
428
- ...this.viewportForSelection,
429
- end: mapTransformToViewportState(transform),
430
- target: evTarget(sourceEvent)
431
- };
432
- this.selectionService.setViewport(this.viewportForSelection);
433
- }
434
440
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MapContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
435
441
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: MapContextDirective, selector: "g[mapContext]", ngImport: i0 }); }
436
442
  }
@@ -459,8 +465,8 @@ class DraggableService {
459
465
  * @param model model with data for this element
460
466
  */
461
467
  enable(element, model) {
462
- const d3Element = select(element);
463
- d3Element.call(this.getDragBehavior(model));
468
+ select(element)
469
+ .call(this.getDragBehavior(model));
464
470
  }
465
471
  /**
466
472
  * Disable draggable behavior for element.
@@ -469,8 +475,8 @@ class DraggableService {
469
475
  * @param model model with data for this element
470
476
  */
471
477
  disable(element) {
472
- const d3Element = select(element);
473
- d3Element.call(this.getIgnoreDragBehavior());
478
+ select(element)
479
+ .call(drag().on('drag', null));
474
480
  }
475
481
  /**
476
482
  * TODO: not shure if this work, need to check
@@ -489,7 +495,15 @@ class DraggableService {
489
495
  getDragBehavior(model) {
490
496
  let dragNodes = [];
491
497
  let initialPositions = [];
498
+ const filterCondition = (event) => {
499
+ // if there is at least one drag handle, we should check if we are dragging it
500
+ if (model.dragHandlesCount()) {
501
+ return !!event.target.closest('.vflow-drag-handle');
502
+ }
503
+ return true;
504
+ };
492
505
  return drag()
506
+ .filter(filterCondition)
493
507
  .on('start', (event) => {
494
508
  dragNodes = this.getDragNodes(model);
495
509
  initialPositions = dragNodes.map(node => ({
@@ -507,16 +521,6 @@ class DraggableService {
507
521
  });
508
522
  });
509
523
  }
510
- /**
511
- * Specify ignoring drag behavior. It's responsible for not moving the map when user tries to drag node
512
- * with disabled drag behavior
513
- */
514
- getIgnoreDragBehavior() {
515
- return drag()
516
- .on('drag', (event) => {
517
- event.sourceEvent.stopPropagation();
518
- });
519
- }
520
524
  getDragNodes(model) {
521
525
  return model.selected()
522
526
  ? this.entitiesService
@@ -826,7 +830,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
826
830
 
827
831
  class CustomNodeBaseComponent {
828
832
  constructor() {
829
- this.eventBus = inject(ComponentEventBusService, { optional: true });
833
+ this.eventBus = inject(ComponentEventBusService);
830
834
  this.destroyRef = inject(DestroyRef);
831
835
  /**
832
836
  * Signal with selected state of node
@@ -838,11 +842,9 @@ class CustomNodeBaseComponent {
838
842
  this.selected.set(value);
839
843
  }
840
844
  ngOnInit() {
841
- if (this.eventBus) {
842
- this.trackEvents()
843
- .pipe(takeUntilDestroyed(this.destroyRef))
844
- .subscribe();
845
- }
845
+ this.trackEvents()
846
+ .pipe(takeUntilDestroyed(this.destroyRef))
847
+ .subscribe();
846
848
  }
847
849
  trackEvents() {
848
850
  const props = Object.getOwnPropertyNames(this);
@@ -981,6 +983,7 @@ class NodeModel {
981
983
  this.handles = signal([]);
982
984
  this.handles$ = toObservable(this.handles);
983
985
  this.draggable = signal(true);
986
+ this.dragHandlesCount = signal(0);
984
987
  // disabled for configuration for now
985
988
  this.magnetRadius = 20;
986
989
  // TODO: not sure if we need to statically store it
@@ -1127,11 +1130,8 @@ function getPointOnLineByRatio(start, end, ratio) {
1127
1130
  function straightPath(source, target, usingPoints = [false, false, false]) {
1128
1131
  const [start, center, end] = usingPoints;
1129
1132
  const nullPoint = { x: 0, y: 0 };
1130
- const path$1 = path();
1131
- path$1.moveTo(source.x, source.y);
1132
- path$1.lineTo(target.x, target.y);
1133
1133
  return {
1134
- path: path$1.toString(),
1134
+ path: `M ${source.x},${source.y}L ${target.x},${target.y}`,
1135
1135
  points: {
1136
1136
  start: start ? getPointOnLineByRatio(source, target, .15) : nullPoint,
1137
1137
  center: center ? getPointOnLineByRatio(source, target, .50) : nullPoint,
@@ -1141,13 +1141,11 @@ function straightPath(source, target, usingPoints = [false, false, false]) {
1141
1141
  }
1142
1142
 
1143
1143
  function bezierPath(source, target, sourcePosition, targetPosition, usingPoints = [false, false, false]) {
1144
- const path$1 = path();
1145
- path$1.moveTo(source.x, source.y);
1146
1144
  const distanceVector = { x: source.x - target.x, y: source.y - target.y };
1147
- const firstControl = calcControlPoint(source, sourcePosition, distanceVector);
1148
- const secondControl = calcControlPoint(target, targetPosition, distanceVector);
1149
- path$1.bezierCurveTo(firstControl.x, firstControl.y, secondControl.x, secondControl.y, target.x, target.y);
1150
- return getPathData(path$1, source, target, firstControl, secondControl, usingPoints);
1145
+ const sourceControl = calcControlPoint(source, sourcePosition, distanceVector);
1146
+ const targetControl = calcControlPoint(target, targetPosition, distanceVector);
1147
+ const path = `M${source.x},${source.y} C${sourceControl.x},${sourceControl.y} ${targetControl.x},${targetControl.y} ${target.x},${target.y}`;
1148
+ return getPathData(path, source, target, sourceControl, targetControl, usingPoints);
1151
1149
  }
1152
1150
  /**
1153
1151
  * Calculate control point based on provided point
@@ -1187,20 +1185,20 @@ function calcControlPoint(point, pointPosition, distanceVector) {
1187
1185
  y: point.y - factorPoint.y * controlOffset,
1188
1186
  };
1189
1187
  }
1190
- function getPathData(path, source, target, firstControl, secondControl, usingPoints) {
1188
+ function getPathData(path, source, target, sourceControl, targetControl, usingPoints) {
1191
1189
  const [start, center, end] = usingPoints;
1192
1190
  const nullPoint = { x: 0, y: 0 };
1193
1191
  return {
1194
- path: path.toString(),
1192
+ path,
1195
1193
  points: {
1196
1194
  start: start
1197
- ? getPointOnBezier(source, target, firstControl, secondControl, 0.1)
1195
+ ? getPointOnBezier(source, target, sourceControl, targetControl, 0.1)
1198
1196
  : nullPoint,
1199
1197
  center: center
1200
- ? getPointOnBezier(source, target, firstControl, secondControl, 0.5)
1198
+ ? getPointOnBezier(source, target, sourceControl, targetControl, 0.5)
1201
1199
  : nullPoint,
1202
1200
  end: end
1203
- ? getPointOnBezier(source, target, firstControl, secondControl, 0.9)
1201
+ ? getPointOnBezier(source, target, sourceControl, targetControl, 0.9)
1204
1202
  : nullPoint,
1205
1203
  },
1206
1204
  };
@@ -1208,10 +1206,10 @@ function getPathData(path, source, target, firstControl, secondControl, usingPoi
1208
1206
  /**
1209
1207
  * Get point on bezier curve by ratio
1210
1208
  */
1211
- function getPointOnBezier(sourcePoint, targetPoint, controlPoint1, controlPoint2, ratio) {
1212
- const fromSourceToFirstControl = getPointOnLineByRatio(sourcePoint, controlPoint1, ratio);
1213
- const fromFirstControlToSecond = getPointOnLineByRatio(controlPoint1, controlPoint2, ratio);
1214
- const fromSecondControlToTarget = getPointOnLineByRatio(controlPoint2, targetPoint, ratio);
1209
+ function getPointOnBezier(sourcePoint, targetPoint, sourceControl, targetControl, ratio) {
1210
+ const fromSourceToFirstControl = getPointOnLineByRatio(sourcePoint, sourceControl, ratio);
1211
+ const fromFirstControlToSecond = getPointOnLineByRatio(sourceControl, targetControl, ratio);
1212
+ const fromSecondControlToTarget = getPointOnLineByRatio(targetControl, targetPoint, ratio);
1215
1213
  return getPointOnLineByRatio(getPointOnLineByRatio(fromSourceToFirstControl, fromFirstControlToSecond, ratio), getPointOnLineByRatio(fromFirstControlToSecond, fromSecondControlToTarget, ratio), ratio);
1216
1214
  }
1217
1215
 
@@ -1695,6 +1693,36 @@ function Microtask(target, key, descriptor) {
1695
1693
  return descriptor;
1696
1694
  }
1697
1695
 
1696
+ class OverlaysService {
1697
+ constructor() {
1698
+ this.toolbars = signal([]);
1699
+ this.nodeToolbars = computed(() => {
1700
+ const map = new Map();
1701
+ this.toolbars().forEach((toolbar) => {
1702
+ map.set(toolbar.node, toolbar);
1703
+ });
1704
+ return map;
1705
+ });
1706
+ }
1707
+ addToolbar(toolbar) {
1708
+ this.toolbars.update((toolbars) => [...toolbars, toolbar]);
1709
+ }
1710
+ removeToolbar(toolbar) {
1711
+ this.toolbars.update((toolbars) => toolbars.filter(t => t !== toolbar));
1712
+ }
1713
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: OverlaysService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1714
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: OverlaysService }); }
1715
+ }
1716
+ __decorate([
1717
+ Microtask
1718
+ ], OverlaysService.prototype, "addToolbar", null);
1719
+ __decorate([
1720
+ Microtask
1721
+ ], OverlaysService.prototype, "removeToolbar", null);
1722
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: OverlaysService, decorators: [{
1723
+ type: Injectable
1724
+ }], propDecorators: { addToolbar: [], removeToolbar: [] } });
1725
+
1698
1726
  class HandleService {
1699
1727
  constructor() {
1700
1728
  this.node = signal(null);
@@ -1845,7 +1873,8 @@ class HandleModel {
1845
1873
  this.templateContext = {
1846
1874
  $implicit: {
1847
1875
  point: this.offset,
1848
- state: this.state
1876
+ state: this.state,
1877
+ node: this.parentNode.node
1849
1878
  }
1850
1879
  };
1851
1880
  }
@@ -1871,20 +1900,22 @@ class HandleComponent {
1871
1900
  this.injector = inject(Injector);
1872
1901
  this.handleService = inject(HandleService);
1873
1902
  this.element = inject(ElementRef).nativeElement;
1903
+ this.destroyRef = inject(DestroyRef);
1874
1904
  }
1875
1905
  ngOnInit() {
1876
- this.model = new HandleModel({
1877
- position: this.position,
1878
- type: this.type,
1879
- id: this.id,
1880
- parentReference: this.element.parentElement,
1881
- template: this.template
1882
- }, this.handleService.node());
1883
- this.handleService.createHandle(this.model);
1884
- requestAnimationFrame(() => this.model.updateParent());
1885
- }
1886
- ngOnDestroy() {
1887
- this.handleService.destroyHandle(this.model);
1906
+ const node = this.handleService.node();
1907
+ if (node) {
1908
+ this.model = new HandleModel({
1909
+ position: this.position,
1910
+ type: this.type,
1911
+ id: this.id,
1912
+ parentReference: this.element.parentElement,
1913
+ template: this.template
1914
+ }, node);
1915
+ this.handleService.createHandle(this.model);
1916
+ requestAnimationFrame(() => this.model.updateParent());
1917
+ this.destroyRef.onDestroy(() => this.handleService.destroyHandle(this.model));
1918
+ }
1888
1919
  }
1889
1920
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1890
1921
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HandleComponent, selector: "handle", inputs: { position: "position", type: "type", id: "id", template: "template" }, ngImport: i0, template: "", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
@@ -2230,11 +2261,13 @@ class NodeComponent {
2230
2261
  this.hostRef = inject(ElementRef);
2231
2262
  this.connectionController = inject(ConnectionControllerDirective);
2232
2263
  this.nodeAccessor = inject(NodeAccessorService);
2264
+ this.overlaysService = inject(OverlaysService);
2233
2265
  this.zone = inject(NgZone);
2234
2266
  this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
2235
2267
  this.flowStatusService.status().state === 'connection-validation');
2236
2268
  this.styleWidth = computed(() => `${this.nodeModel.size().width}px`);
2237
2269
  this.styleHeight = computed(() => `${this.nodeModel.size().height}px`);
2270
+ this.toolbar = computed(() => this.overlaysService.nodeToolbars().get(this.nodeModel));
2238
2271
  }
2239
2272
  ngOnInit() {
2240
2273
  this.nodeAccessor.model.set(this.nodeModel);
@@ -2292,7 +2325,7 @@ class NodeComponent {
2292
2325
  }
2293
2326
  }
2294
2327
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2295
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeTemplate: "nodeTemplate", groupNodeTemplate: "groupNodeTemplate" }, providers: [HandleService, NodeAccessorService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], ngImport: i0, template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode(); selectNode()\"\n>\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 >\n <div [outerHTML]=\"nodeModel.text()\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </default-node>\n</svg:foreignObject>\n\n<!-- Template node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Component node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.isComponentType\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Default group node -->\n<svg:rect\n *ngIf=\"nodeModel.node.type === 'default-group'\"\n [resizable]=\"nodeModel.resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel.color()\"\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\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<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (pointerStart)=\"pullNode()\"\n>\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected, width: nodeModel.width, height: nodeModel.height } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n</svg:g>\n\n<!-- Resizer -->\n<ng-container *ngIf=\"nodeModel.resizerTemplate() as template\">\n <ng-container *ngIf=\"nodeModel.resizable()\">\n <ng-template [ngTemplateOutlet]=\"template\" />\n </ng-container>\n</ng-container>\n\n<!-- Handles -->\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(handle); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\"\n />\n</ng-container>\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: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2328
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeTemplate: "nodeTemplate", groupNodeTemplate: "groupNodeTemplate" }, host: { classAttribute: "vflow-node" }, providers: [HandleService, NodeAccessorService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], ngImport: i0, template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode(); selectNode()\"\n>\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 >\n <div [outerHTML]=\"nodeModel.text()\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </default-node>\n</svg:foreignObject>\n\n<!-- Template node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Component node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.isComponentType\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Default group node -->\n<svg:rect\n *ngIf=\"nodeModel.node.type === 'default-group'\"\n [resizable]=\"nodeModel.resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel.color()\"\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\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<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (pointerStart)=\"pullNode()\"\n>\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected, width: nodeModel.width, height: nodeModel.height } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n</svg:g>\n\n<!-- Resizer -->\n<ng-container *ngIf=\"nodeModel.resizerTemplate() as template\">\n <ng-container *ngIf=\"nodeModel.resizable()\">\n <ng-template [ngTemplateOutlet]=\"template\" />\n </ng-container>\n</ng-container>\n\n<!-- Handles -->\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(handle); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\"\n />\n</ng-container>\n\n<!-- Toolbar -->\n<svg:foreignObject\n *ngIf=\"toolbar() as toolbar\"\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\"\n>\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n</svg:foreignObject>\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: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2296
2329
  }
2297
2330
  __decorate([
2298
2331
  InjectionContext
@@ -2302,7 +2335,9 @@ __decorate([
2302
2335
  ], NodeComponent.prototype, "ngAfterViewInit", null);
2303
2336
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
2304
2337
  type: Component,
2305
- args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService, NodeAccessorService], template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode(); selectNode()\"\n>\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 >\n <div [outerHTML]=\"nodeModel.text()\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </default-node>\n</svg:foreignObject>\n\n<!-- Template node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Component node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.isComponentType\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Default group node -->\n<svg:rect\n *ngIf=\"nodeModel.node.type === 'default-group'\"\n [resizable]=\"nodeModel.resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel.color()\"\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\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<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (pointerStart)=\"pullNode()\"\n>\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected, width: nodeModel.width, height: nodeModel.height } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n</svg:g>\n\n<!-- Resizer -->\n<ng-container *ngIf=\"nodeModel.resizerTemplate() as template\">\n <ng-container *ngIf=\"nodeModel.resizable()\">\n <ng-template [ngTemplateOutlet]=\"template\" />\n </ng-container>\n</ng-container>\n\n<!-- Handles -->\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(handle); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\"\n />\n</ng-container>\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"] }]
2338
+ args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService, NodeAccessorService], host: {
2339
+ 'class': 'vflow-node',
2340
+ }, template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode(); selectNode()\"\n>\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 >\n <div [outerHTML]=\"nodeModel.text()\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </default-node>\n</svg:foreignObject>\n\n<!-- Template node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Component node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.isComponentType\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Default group node -->\n<svg:rect\n *ngIf=\"nodeModel.node.type === 'default-group'\"\n [resizable]=\"nodeModel.resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel.color()\"\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\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<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (pointerStart)=\"pullNode()\"\n>\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected, width: nodeModel.width, height: nodeModel.height } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n</svg:g>\n\n<!-- Resizer -->\n<ng-container *ngIf=\"nodeModel.resizerTemplate() as template\">\n <ng-container *ngIf=\"nodeModel.resizable()\">\n <ng-template [ngTemplateOutlet]=\"template\" />\n </ng-container>\n</ng-container>\n\n<!-- Handles -->\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(handle); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\"\n />\n</ng-container>\n\n<!-- Toolbar -->\n<svg:foreignObject\n *ngIf=\"toolbar() as toolbar\"\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\"\n>\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n</svg:foreignObject>\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"] }]
2306
2341
  }], propDecorators: { nodeModel: [{
2307
2342
  type: Input
2308
2343
  }], nodeTemplate: [{
@@ -2538,7 +2573,7 @@ class DefsComponent {
2538
2573
  this.defaultColor = 'rgb(177, 177, 183)';
2539
2574
  }
2540
2575
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DefsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2541
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DefsComponent, selector: "defs[flowDefs]", inputs: { markers: "markers" }, ngImport: i0, template: "<svg:marker\n *ngFor=\"let marker of markers | keyvalue\"\n [attr.id]=\"marker.key\"\n [attr.markerWidth]=\"marker.value.width ?? 16.5\"\n [attr.markerHeight]=\"marker.value.height ?? 16.5\"\n [attr.orient]=\"marker.value.orient ?? 'auto-start-reverse'\"\n viewBox=\"-10 -10 20 20\"\n [attr.markerUnits]=\"marker.value.markerUnits ?? 'userSpaceOnUse'\"\n refX=\"0\"\n refY=\"0\"\n>\n <polyline\n *ngIf=\"marker.value.type === 'arrow-closed' || !marker.value.type\"\n class=\"marker__arrow_closed\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n [style.fill]=\"marker.value.color ?? defaultColor\"\n points=\"-5,-4 1,0 -5,4 -5,-4\"\n />\n\n <polyline\n *ngIf=\"marker.value.type === 'arrow'\"\n class=\"marker__arrow_default\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n points=\"-5,-4 0,0 -5,4\"\n />\n</svg:marker>\n", styles: [".marker__arrow_default{stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;fill:none}.marker__arrow_closed{stroke-linecap:round;stroke-linejoin:round}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2576
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DefsComponent, selector: "defs[flowDefs]", inputs: { markers: "markers" }, ngImport: i0, template: "<svg:marker\n *ngFor=\"let marker of markers | keyvalue\"\n [attr.id]=\"marker.key\"\n [attr.markerWidth]=\"marker.value.width ?? 16.5\"\n [attr.markerHeight]=\"marker.value.height ?? 16.5\"\n [attr.orient]=\"marker.value.orient ?? 'auto-start-reverse'\"\n viewBox=\"-10 -10 20 20\"\n [attr.markerUnits]=\"marker.value.markerUnits ?? 'userSpaceOnUse'\"\n refX=\"0\"\n refY=\"0\"\n>\n <polyline\n *ngIf=\"marker.value.type === 'arrow-closed' || !marker.value.type\"\n class=\"marker__arrow_closed\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n [style.fill]=\"marker.value.color ?? defaultColor\"\n points=\"-5,-4 1,0 -5,4 -5,-4\"\n />\n\n <polyline\n *ngIf=\"marker.value.type === 'arrow'\"\n class=\"marker__arrow_default\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n points=\"-5,-4 0,0 -5,4\"\n />\n</svg:marker>\n", styles: [".marker__arrow_default{stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;fill:none}.marker__arrow_closed{stroke-linecap:round;stroke-linejoin:round}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2542
2577
  }
2543
2578
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DefsComponent, decorators: [{
2544
2579
  type: Component,
@@ -2915,8 +2950,9 @@ class VflowComponent {
2915
2950
  SelectionService,
2916
2951
  FlowSettingsService,
2917
2952
  ComponentEventBusService,
2918
- KeyboardService
2919
- ], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateDirective, descendants: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true }, { propertyName: "spacePointContext", first: true, predicate: SpacePointContextDirective, descendants: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onNodesChange.position", "onNodesChange.position", "onNodesChange.position.single", "onNodesChange.position.single", "onNodesChange.position.many", "onNodesChange.position.many", "onNodesChange.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\n rootSvgRef\n rootSvgContext\n rootPointer\n flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g background />\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n <!-- Minimap -->\n <ng-container *ngIf=\"minimap() as minimap\">\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n </ng-container>\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeTemplate", "groupNodeTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "component", type: BackgroundComponent, selector: "g[background]" }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]" }, { 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]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2953
+ KeyboardService,
2954
+ OverlaysService
2955
+ ], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateDirective, descendants: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true }, { propertyName: "spacePointContext", first: true, predicate: SpacePointContextDirective, descendants: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onNodesChange.position", "onNodesChange.position", "onNodesChange.position.single", "onNodesChange.position.single", "onNodesChange.position.many", "onNodesChange.position.many", "onNodesChange.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\n rootSvgRef\n rootSvgContext\n rootPointer\n flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g background />\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n <!-- Minimap -->\n <ng-container *ngIf=\"minimap() as minimap\">\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n </ng-container>\n</svg:svg>\n\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeTemplate", "groupNodeTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "component", type: BackgroundComponent, selector: "g[background]" }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]" }, { 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]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2920
2956
  }
2921
2957
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
2922
2958
  type: Component,
@@ -2931,11 +2967,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2931
2967
  SelectionService,
2932
2968
  FlowSettingsService,
2933
2969
  ComponentEventBusService,
2934
- KeyboardService
2970
+ KeyboardService,
2971
+ OverlaysService
2935
2972
  ], hostDirectives: [
2936
2973
  connectionControllerHostDirective,
2937
2974
  changesControllerHostDirective
2938
- ], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g background />\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n <!-- Minimap -->\n <ng-container *ngIf=\"minimap() as minimap\">\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n </ng-container>\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"] }]
2975
+ ], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g background />\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n <!-- Minimap -->\n <ng-container *ngIf=\"minimap() as minimap\">\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n </ng-container>\n</svg:svg>\n\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"] }]
2939
2976
  }], propDecorators: { view: [{
2940
2977
  type: Input
2941
2978
  }], minZoom: [{
@@ -3118,7 +3155,7 @@ class MiniMapComponent {
3118
3155
  return node;
3119
3156
  }
3120
3157
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MiniMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3121
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MiniMapComponent, selector: "mini-map", inputs: { position: "position", maskColor: "maskColor", strokeColor: "strokeColor", scaleOnHover: "scaleOnHover" }, viewQueries: [{ propertyName: "minimap", first: true, predicate: ["minimap"], descendants: true, static: true }], ngImport: i0, template: "<ng-template #minimap>\n <svg:rect\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.stroke]=\"strokeColor\"\n fill=\"none\"\n />\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 >\n <svg:rect\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.fill]=\"maskColor\"\n />\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\n <ng-container\n *ngFor=\"let model of entitiesService.nodes(); trackBy: trackNodes\"\n >\n <svg:foreignObject\n *ngIf=\"model.node.type === 'default' || model.node.type === 'html-template' || model.isComponentType\"\n [attr.transform]=\"model.pointTransform()\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\"\n >\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 >\n <div [outerHTML]=\"model.text()\"></div>\n </default-node>\n </svg:foreignObject>\n\n <svg:rect\n *ngIf=\"model.node.type === 'default-group' || model.node.type === 'template-group'\"\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\n </ng-container>\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: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }] }); }
3158
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: MiniMapComponent, selector: "mini-map", inputs: { position: "position", maskColor: "maskColor", strokeColor: "strokeColor", scaleOnHover: "scaleOnHover" }, viewQueries: [{ propertyName: "minimap", first: true, predicate: ["minimap"], descendants: true, static: true }], ngImport: i0, template: "<ng-template #minimap>\n <svg:rect\n [attr.x]=\"minimapPoint().x\"\n [attr.y]=\"minimapPoint().y\"\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.stroke]=\"strokeColor\"\n fill=\"none\"\n />\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 >\n <svg:rect\n [attr.width]=\"minimapWidth()\"\n [attr.height]=\"minimapHeight()\"\n [attr.fill]=\"maskColor\"\n />\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\n <ng-container\n *ngFor=\"let model of entitiesService.nodes(); trackBy: trackNodes\"\n >\n <svg:foreignObject\n *ngIf=\"model.node.type === 'default' || model.node.type === 'html-template' || model.isComponentType\"\n [attr.transform]=\"model.pointTransform()\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\"\n >\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 >\n <div [outerHTML]=\"model.text()\"></div>\n </default-node>\n </svg:foreignObject>\n\n <svg:rect\n *ngIf=\"model.node.type === 'default-group' || model.node.type === 'template-group'\"\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\n </ng-container>\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: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }] }); }
3122
3159
  }
3123
3160
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MiniMapComponent, decorators: [{
3124
3161
  type: Component,
@@ -3136,6 +3173,125 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
3136
3173
  args: ['minimap', { static: true }]
3137
3174
  }] } });
3138
3175
 
3176
+ class ToolbarModel {
3177
+ constructor(node) {
3178
+ this.node = node;
3179
+ this.position = signal('top');
3180
+ this.template = signal(null);
3181
+ this.offset = signal(10);
3182
+ this.point = computed(() => {
3183
+ switch (this.position()) {
3184
+ case 'top':
3185
+ return {
3186
+ x: this.node.size().width / 2 - this.size().width / 2,
3187
+ y: -this.size().height - this.offset()
3188
+ };
3189
+ case 'bottom':
3190
+ return {
3191
+ x: this.node.size().width / 2 - this.size().width / 2,
3192
+ y: this.node.size().height + this.offset()
3193
+ };
3194
+ case 'left':
3195
+ return {
3196
+ x: -this.size().width - this.offset(),
3197
+ y: this.node.size().height / 2 - this.size().height / 2
3198
+ };
3199
+ case 'right':
3200
+ return {
3201
+ x: this.node.size().width + this.offset(),
3202
+ y: this.node.size().height / 2 - this.size().height / 2
3203
+ };
3204
+ }
3205
+ });
3206
+ this.transform = computed(() => `translate(${this.point().x}, ${this.point().y})`);
3207
+ this.size = signal({ width: 0, height: 0 });
3208
+ }
3209
+ }
3210
+
3211
+ class NodeToolbarComponent {
3212
+ constructor() {
3213
+ this.overlaysService = inject(OverlaysService);
3214
+ this.nodeService = inject(NodeAccessorService);
3215
+ this.model = new ToolbarModel(this.nodeService.model());
3216
+ }
3217
+ set position(value) {
3218
+ this.model.position.set(value);
3219
+ }
3220
+ ngOnInit() {
3221
+ this.model.template.set(this.toolbarContentTemplate);
3222
+ this.overlaysService.addToolbar(this.model);
3223
+ }
3224
+ ngOnDestroy() {
3225
+ this.overlaysService.removeToolbar(this.model);
3226
+ }
3227
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3228
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeToolbarComponent, selector: "node-toolbar", inputs: { position: "position" }, viewQueries: [{ propertyName: "toolbarContentTemplate", first: true, predicate: ["toolbar"], descendants: true, static: true }], ngImport: i0, template: `
3229
+ <ng-template #toolbar>
3230
+ <div class="wrapper" nodeToolbarWrapper [model]="model">
3231
+ <ng-content />
3232
+ </div>
3233
+ </ng-template>
3234
+ `, isInline: true, styles: [".wrapper{width:max-content}\n"], dependencies: [{ kind: "directive", type: i0.forwardRef(function () { return NodeToolbarWrapperDirective; }), selector: "[nodeToolbarWrapper]", inputs: ["model"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3235
+ }
3236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeToolbarComponent, decorators: [{
3237
+ type: Component,
3238
+ args: [{ selector: 'node-toolbar', template: `
3239
+ <ng-template #toolbar>
3240
+ <div class="wrapper" nodeToolbarWrapper [model]="model">
3241
+ <ng-content />
3242
+ </div>
3243
+ </ng-template>
3244
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".wrapper{width:max-content}\n"] }]
3245
+ }], propDecorators: { position: [{
3246
+ type: Input
3247
+ }], toolbarContentTemplate: [{
3248
+ type: ViewChild,
3249
+ args: ['toolbar', { static: true }]
3250
+ }] } });
3251
+ class NodeToolbarWrapperDirective {
3252
+ constructor() {
3253
+ this.element = inject(ElementRef);
3254
+ }
3255
+ ngOnInit() {
3256
+ this.model.size.set({
3257
+ width: this.element.nativeElement.clientWidth,
3258
+ height: this.element.nativeElement.clientHeight
3259
+ });
3260
+ }
3261
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeToolbarWrapperDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3262
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: NodeToolbarWrapperDirective, selector: "[nodeToolbarWrapper]", inputs: { model: "model" }, ngImport: i0 }); }
3263
+ }
3264
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeToolbarWrapperDirective, decorators: [{
3265
+ type: Directive,
3266
+ args: [{ selector: '[nodeToolbarWrapper]' }]
3267
+ }], propDecorators: { model: [{
3268
+ type: Input
3269
+ }] } });
3270
+
3271
+ class DragHandleDirective {
3272
+ get model() {
3273
+ return this.nodeAccessor.model();
3274
+ }
3275
+ constructor() {
3276
+ this.nodeAccessor = inject(NodeAccessorService);
3277
+ this.model.dragHandlesCount.update((count) => count + 1);
3278
+ inject(DestroyRef).onDestroy(() => {
3279
+ this.model.dragHandlesCount.update(count => count - 1);
3280
+ });
3281
+ }
3282
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragHandleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3283
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DragHandleDirective, selector: "[dragHandle]", host: { classAttribute: "vflow-drag-handle" }, ngImport: i0 }); }
3284
+ }
3285
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DragHandleDirective, decorators: [{
3286
+ type: Directive,
3287
+ args: [{
3288
+ selector: '[dragHandle]',
3289
+ host: {
3290
+ 'class': 'vflow-drag-handle'
3291
+ }
3292
+ }]
3293
+ }], ctorParameters: function () { return []; } });
3294
+
3139
3295
  const components = [
3140
3296
  VflowComponent,
3141
3297
  NodeComponent,
@@ -3147,7 +3303,8 @@ const components = [
3147
3303
  DefsComponent,
3148
3304
  BackgroundComponent,
3149
3305
  ResizableComponent,
3150
- MiniMapComponent
3306
+ MiniMapComponent,
3307
+ NodeToolbarComponent
3151
3308
  ];
3152
3309
  const directives = [
3153
3310
  SpacePointContextDirective,
@@ -3156,9 +3313,11 @@ const directives = [
3156
3313
  RootSvgContextDirective,
3157
3314
  HandleSizeControllerDirective,
3158
3315
  SelectableDirective,
3316
+ DragHandleDirective,
3159
3317
  PointerDirective,
3160
3318
  RootPointerDirective,
3161
3319
  FlowSizeControllerDirective,
3320
+ NodeToolbarWrapperDirective
3162
3321
  ];
3163
3322
  const templateDirectives = [
3164
3323
  NodeHtmlTemplateDirective,
@@ -3180,52 +3339,114 @@ class VflowModule {
3180
3339
  DefsComponent,
3181
3340
  BackgroundComponent,
3182
3341
  ResizableComponent,
3183
- MiniMapComponent, SpacePointContextDirective,
3342
+ MiniMapComponent,
3343
+ NodeToolbarComponent, SpacePointContextDirective,
3184
3344
  MapContextDirective,
3185
3345
  RootSvgReferenceDirective,
3186
3346
  RootSvgContextDirective,
3187
3347
  HandleSizeControllerDirective,
3188
3348
  SelectableDirective,
3349
+ DragHandleDirective,
3189
3350
  PointerDirective,
3190
3351
  RootPointerDirective,
3191
- FlowSizeControllerDirective, NodeHtmlTemplateDirective,
3352
+ FlowSizeControllerDirective,
3353
+ NodeToolbarWrapperDirective, NodeHtmlTemplateDirective,
3192
3354
  GroupNodeTemplateDirective,
3193
3355
  EdgeLabelHtmlTemplateDirective,
3194
3356
  EdgeTemplateDirective,
3195
3357
  ConnectionTemplateDirective,
3196
- HandleTemplateDirective], imports: [CommonModule], exports: [VflowComponent,
3358
+ HandleTemplateDirective], imports: [NgIf, NgFor, NgTemplateOutlet, NgComponentOutlet, KeyValuePipe], exports: [VflowComponent,
3197
3359
  HandleComponent,
3198
3360
  ResizableComponent,
3199
3361
  SelectableDirective,
3200
- MiniMapComponent, NodeHtmlTemplateDirective,
3362
+ MiniMapComponent,
3363
+ NodeToolbarComponent,
3364
+ DragHandleDirective, NodeHtmlTemplateDirective,
3201
3365
  GroupNodeTemplateDirective,
3202
3366
  EdgeLabelHtmlTemplateDirective,
3203
3367
  EdgeTemplateDirective,
3204
3368
  ConnectionTemplateDirective,
3205
3369
  HandleTemplateDirective] }); }
3206
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, imports: [CommonModule] }); }
3370
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule }); }
3207
3371
  }
3208
3372
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, decorators: [{
3209
3373
  type: NgModule,
3210
3374
  args: [{
3211
- imports: [CommonModule],
3375
+ imports: [NgIf, NgFor, NgTemplateOutlet, NgComponentOutlet, KeyValuePipe],
3212
3376
  exports: [
3213
3377
  VflowComponent,
3214
3378
  HandleComponent,
3215
3379
  ResizableComponent,
3216
3380
  SelectableDirective,
3217
3381
  MiniMapComponent,
3382
+ NodeToolbarComponent,
3383
+ DragHandleDirective,
3218
3384
  ...templateDirectives
3219
3385
  ],
3220
3386
  declarations: [...components, ...directives, ...templateDirectives],
3221
3387
  }]
3222
3388
  }] });
3223
3389
 
3390
+ const mockModel = () => new NodeModel({ id: 'mock', type: 'default', point: { x: 0, y: 0 } });
3391
+ function provideCustomNodeMocks() {
3392
+ return [
3393
+ {
3394
+ provide: ComponentEventBusService,
3395
+ useValue: {
3396
+ pushEvent: () => { }
3397
+ }
3398
+ },
3399
+ {
3400
+ provide: HandleService,
3401
+ useFactory: () => ({
3402
+ node: signal(mockModel()),
3403
+ createHandle: () => { },
3404
+ destroyHandle: () => { },
3405
+ })
3406
+ },
3407
+ {
3408
+ provide: RootPointerDirective,
3409
+ useValue: {
3410
+ pointerMovement$: of({
3411
+ x: 0,
3412
+ y: 0,
3413
+ movementX: 0,
3414
+ movementY: 0,
3415
+ target: null,
3416
+ originalEvent: null
3417
+ }),
3418
+ documentPointerEnd$: of(null)
3419
+ }
3420
+ },
3421
+ {
3422
+ provide: SpacePointContextDirective,
3423
+ useValue: {
3424
+ documentPointToFlowPoint: (point) => point
3425
+ }
3426
+ },
3427
+ {
3428
+ provide: NodeAccessorService,
3429
+ useFactory: () => ({
3430
+ model: signal(mockModel())
3431
+ })
3432
+ },
3433
+ {
3434
+ provide: SelectionService,
3435
+ useValue: {
3436
+ select: () => { },
3437
+ }
3438
+ },
3439
+ FlowSettingsService,
3440
+ FlowEntitiesService,
3441
+ ViewportService
3442
+ ];
3443
+ }
3444
+
3224
3445
  // Modules
3225
3446
 
3226
3447
  /**
3227
3448
  * Generated bundle index. Do not edit.
3228
3449
  */
3229
3450
 
3230
- export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, CustomDynamicNodeComponent, CustomNodeComponent, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, GroupNodeTemplateDirective, HandleComponent, HandleTemplateDirective, MiniMapComponent, NodeHtmlTemplateDirective, ResizableComponent, SelectableDirective, VflowComponent, VflowModule, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode };
3451
+ export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, CustomDynamicNodeComponent, CustomNodeComponent, DragHandleDirective, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, GroupNodeTemplateDirective, HandleComponent, HandleTemplateDirective, MiniMapComponent, NodeHtmlTemplateDirective, NodeToolbarComponent, NodeToolbarWrapperDirective, ResizableComponent, SelectableDirective, VflowComponent, VflowModule, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, provideCustomNodeMocks };
3231
3452
  //# sourceMappingURL=ngx-vflow.mjs.map