ngx-vflow 0.13.0-0 → 0.14.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 (42) hide show
  1. package/esm2022/lib/vflow/components/background/background.component.mjs +7 -16
  2. package/esm2022/lib/vflow/components/default-node/default-node.component.mjs +18 -0
  3. package/esm2022/lib/vflow/components/node/node.component.mjs +8 -7
  4. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +23 -10
  5. package/esm2022/lib/vflow/directives/root-pointer.directive.mjs +1 -1
  6. package/esm2022/lib/vflow/directives/selectable.directive.mjs +5 -2
  7. package/esm2022/lib/vflow/models/minimap.model.mjs +7 -0
  8. package/esm2022/lib/vflow/public-components/minimap/minimap.component.mjs +119 -0
  9. package/esm2022/lib/vflow/public-components/resizable/resizable.component.mjs +124 -180
  10. package/esm2022/lib/vflow/services/draggable.service.mjs +40 -19
  11. package/esm2022/lib/vflow/services/flow-entities.service.mjs +6 -5
  12. package/esm2022/lib/vflow/services/flow-settings.service.mjs +2 -1
  13. package/esm2022/lib/vflow/services/keyboard.service.mjs +45 -0
  14. package/esm2022/lib/vflow/services/node-changes.service.mjs +6 -7
  15. package/esm2022/lib/vflow/services/selection.service.mjs +12 -5
  16. package/esm2022/lib/vflow/types/keyboard-action.type.mjs +2 -0
  17. package/esm2022/lib/vflow/utils/get-os.mjs +24 -0
  18. package/esm2022/lib/vflow/utils/transform-background.mjs +6 -0
  19. package/esm2022/lib/vflow/vflow.module.mjs +11 -3
  20. package/esm2022/public-api.mjs +3 -1
  21. package/fesm2022/ngx-vflow.mjs +423 -242
  22. package/fesm2022/ngx-vflow.mjs.map +1 -1
  23. package/lib/vflow/components/background/background.component.d.ts +3 -6
  24. package/lib/vflow/components/default-node/default-node.component.d.ts +6 -0
  25. package/lib/vflow/components/vflow/vflow.component.d.ts +6 -2
  26. package/lib/vflow/directives/root-pointer.directive.d.ts +10 -16
  27. package/lib/vflow/directives/space-point-context.directive.d.ts +1 -15
  28. package/lib/vflow/models/edge.model.d.ts +17 -1
  29. package/lib/vflow/models/minimap.model.d.ts +4 -0
  30. package/lib/vflow/public-components/minimap/minimap.component.d.ts +49 -0
  31. package/lib/vflow/public-components/resizable/resizable.component.d.ts +6 -11
  32. package/lib/vflow/services/draggable.service.d.ts +2 -0
  33. package/lib/vflow/services/flow-entities.service.d.ts +7 -5
  34. package/lib/vflow/services/flow-settings.service.d.ts +2 -0
  35. package/lib/vflow/services/keyboard.service.d.ts +11 -0
  36. package/lib/vflow/services/selection.service.d.ts +1 -0
  37. package/lib/vflow/types/keyboard-action.type.d.ts +2 -0
  38. package/lib/vflow/utils/get-os.d.ts +1 -0
  39. package/lib/vflow/utils/transform-background.d.ts +2 -0
  40. package/lib/vflow/vflow.module.d.ts +21 -19
  41. package/package.json +1 -1
  42. package/public-api.d.ts +2 -0
@@ -1,11 +1,11 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, EventEmitter, Output, DestroyRef, Input, runInInjectionContext, Injector, Component, ChangeDetectionStrategy, HostListener, ViewChild, NgZone, ContentChild, NgModule } from '@angular/core';
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 { Subject, tap, merge, observeOn, animationFrameScheduler, switchMap, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, fromEvent, share, Observable, startWith } from 'rxjs';
8
- import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
7
+ import { switchMap, merge, fromEvent, tap, Subject, observeOn, animationFrameScheduler, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, share, Observable, startWith } from 'rxjs';
8
+ import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
9
9
  import { drag } from 'd3-drag';
10
10
  import { path } from 'd3-path';
11
11
  import { __decorate } from 'tslib';
@@ -94,6 +94,10 @@ class FlowEntitiesService {
94
94
  // empty arrays considered equal, other arrays may not be equal
95
95
  equal: (a, b) => !a.length && !b.length ? true : a === b
96
96
  });
97
+ this.validEdges = computed(() => {
98
+ const nodes = this.nodes();
99
+ return this.edges().filter(e => nodes.includes(e.source()) && nodes.includes(e.target()));
100
+ });
97
101
  this.connection = signal(new ConnectionModel({}));
98
102
  this.markers = computed(() => {
99
103
  const markersMap = new Map();
@@ -114,14 +118,11 @@ class FlowEntitiesService {
114
118
  }
115
119
  return markersMap;
116
120
  });
117
- this.validEdges = computed(() => {
118
- const nodes = this.nodes();
119
- return this.edges().filter(e => nodes.includes(e.source()) && nodes.includes(e.target()));
120
- });
121
121
  this.entities = computed(() => [
122
122
  ...this.nodes(),
123
123
  ...this.edges()
124
124
  ]);
125
+ this.minimap = signal(null);
125
126
  }
126
127
  getNode(id) {
127
128
  return this.nodes().find(({ node }) => node.id === id);
@@ -174,6 +175,7 @@ class FlowSettingsService {
174
175
  this.computedFlowHeight = signal(0);
175
176
  this.minZoom = signal(0.5);
176
177
  this.maxZoom = signal(3);
178
+ this.background = signal({ type: 'solid', color: '#fff' });
177
179
  }
178
180
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
179
181
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService }); }
@@ -249,9 +251,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
249
251
  }]
250
252
  }] });
251
253
 
254
+ function getOS() {
255
+ const userAgent = window.navigator.userAgent.toLowerCase();
256
+ const macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos)/i;
257
+ const windowsPlatforms = /(win32|win64|windows|wince)/i;
258
+ const iosPlatforms = /(iphone|ipad|ipod)/i;
259
+ let os = null;
260
+ if (macosPlatforms.test(userAgent)) {
261
+ os = "macos";
262
+ }
263
+ else if (iosPlatforms.test(userAgent)) {
264
+ os = "ios";
265
+ }
266
+ else if (windowsPlatforms.test(userAgent)) {
267
+ os = "windows";
268
+ }
269
+ else if (/android/.test(userAgent)) {
270
+ os = "android";
271
+ }
272
+ else if (!os && /linux/.test(userAgent)) {
273
+ os = "linux";
274
+ }
275
+ return os;
276
+ }
277
+
278
+ class KeyboardService {
279
+ constructor() {
280
+ this.actions = signal({
281
+ multiSelection: [
282
+ getOS() === 'macos' ? 'MetaLeft' : 'ControlLeft',
283
+ getOS() === 'macos' ? 'MetaRight' : 'ControlRight',
284
+ ]
285
+ });
286
+ this.actionsActive = {
287
+ multiSelection: false
288
+ };
289
+ toObservable(this.actions).pipe(switchMap(() => merge(fromEvent(document, 'keydown').pipe(tap(event => {
290
+ for (const action in this.actions()) {
291
+ const keyCodes = this.actions()[action] ?? [];
292
+ if (keyCodes.includes(event.code)) {
293
+ this.actionsActive[action] = true;
294
+ }
295
+ }
296
+ })), fromEvent(document, 'keyup').pipe(tap(event => {
297
+ for (const action in this.actions()) {
298
+ const keyCodes = this.actions()[action] ?? [];
299
+ if (keyCodes.includes(event.code)) {
300
+ this.actionsActive[action] = false;
301
+ }
302
+ }
303
+ })))), takeUntilDestroyed()).subscribe();
304
+ }
305
+ setShortcuts(newActions) {
306
+ this.actions.update((actions) => ({ ...actions, ...newActions }));
307
+ }
308
+ isActiveAction(action) {
309
+ return this.actionsActive[action];
310
+ }
311
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: KeyboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
312
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: KeyboardService }); }
313
+ }
314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: KeyboardService, decorators: [{
315
+ type: Injectable
316
+ }], ctorParameters: function () { return []; } });
317
+
252
318
  class SelectionService {
253
319
  constructor() {
254
320
  this.flowEntitiesService = inject(FlowEntitiesService);
321
+ this.keyboardService = inject(KeyboardService);
255
322
  this.viewport$ = new Subject();
256
323
  this.resetSelection = this.viewport$.pipe(tap(({ start, end, target }) => {
257
324
  if (start && end && target) {
@@ -273,10 +340,15 @@ class SelectionService {
273
340
  this.viewport$.next(viewport);
274
341
  }
275
342
  select(entity) {
276
- // undo select for previously selected nodes
277
- this.flowEntitiesService.entities()
278
- .filter(n => n.selected)
279
- .forEach(n => n.selected.set(false));
343
+ // ? May be not a responsibility of this method
344
+ // if entity already selected - do nothing
345
+ if (entity?.selected()) {
346
+ return;
347
+ }
348
+ if (!this.keyboardService.isActiveAction('multiSelection')) {
349
+ // undo select for previously selected nodes
350
+ this.flowEntitiesService.entities().forEach(n => n.selected.set(false));
351
+ }
280
352
  if (entity) {
281
353
  // select passed entity
282
354
  entity.selected.set(true);
@@ -377,6 +449,9 @@ const evTarget = (anyEvent) => {
377
449
  const round = (num) => Math.round(num * 100) / 100;
378
450
 
379
451
  class DraggableService {
452
+ constructor() {
453
+ this.entitiesService = inject(FlowEntitiesService);
454
+ }
380
455
  /**
381
456
  * Enable draggable behavior for element.
382
457
  *
@@ -412,27 +487,24 @@ class DraggableService {
412
487
  * @returns
413
488
  */
414
489
  getDragBehavior(model) {
415
- let deltaX;
416
- let deltaY;
490
+ let dragNodes = [];
491
+ let initialPositions = [];
417
492
  return drag()
418
493
  .on('start', (event) => {
419
- deltaX = model.point().x - event.x;
420
- deltaY = model.point().y - event.y;
494
+ dragNodes = this.getDragNodes(model);
495
+ initialPositions = dragNodes.map(node => ({
496
+ x: node.point().x - event.x,
497
+ y: node.point().y - event.y
498
+ }));
421
499
  })
422
500
  .on('drag', (event) => {
423
- let point = {
424
- x: round(event.x + deltaX),
425
- y: round(event.y + deltaY)
426
- };
427
- const parent = model.parent();
428
- // keep node in bounds of parent
429
- if (parent) {
430
- point.x = Math.min(parent.size().width - model.size().width, point.x);
431
- point.x = Math.max(0, point.x);
432
- point.y = Math.min(parent.size().height - model.size().height, point.y);
433
- point.y = Math.max(0, point.y);
434
- }
435
- model.setPoint(point, true);
501
+ dragNodes.forEach((model, index) => {
502
+ let point = {
503
+ x: round(event.x + initialPositions[index].x),
504
+ y: round(event.y + initialPositions[index].y)
505
+ };
506
+ moveNode(model, point);
507
+ });
436
508
  });
437
509
  }
438
510
  /**
@@ -445,12 +517,32 @@ class DraggableService {
445
517
  event.sourceEvent.stopPropagation();
446
518
  });
447
519
  }
520
+ getDragNodes(model) {
521
+ return model.selected()
522
+ ? this.entitiesService
523
+ .nodes()
524
+ // selected draggable nodes (with current node)
525
+ .filter(node => node.selected() && node.draggable())
526
+ // we only can move current node if it's not selected
527
+ : [model];
528
+ }
448
529
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
449
530
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService }); }
450
531
  }
451
532
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService, decorators: [{
452
533
  type: Injectable
453
534
  }] });
535
+ function moveNode(model, point) {
536
+ const parent = model.parent();
537
+ // keep node in bounds of parent
538
+ if (parent) {
539
+ point.x = Math.min(parent.size().width - model.size().width, point.x);
540
+ point.x = Math.max(0, point.x);
541
+ point.y = Math.min(parent.size().height - model.size().height, point.y);
542
+ point.y = Math.max(0, point.y);
543
+ }
544
+ model.setPoint(point, true);
545
+ }
454
546
 
455
547
  class EdgeTemplateDirective {
456
548
  constructor() {
@@ -1247,12 +1339,11 @@ class NodesChangeService {
1247
1339
  // Check for nodes list change and watch for specific node from this list change its position
1248
1340
  switchMap((nodes) => merge(...nodes.map(node => node.point$.pipe(
1249
1341
  // skip initial position from signal
1250
- skip(1), map(() => node))))),
1251
- // For now it's a single node, later this list will also be filled
1252
- // with child node position changes
1253
- map(changedNode => [
1254
- { type: 'position', id: changedNode.node.id, point: changedNode.point() }
1255
- ]));
1342
+ skip(1), map(() => node))))), map((changedNode) => {
1343
+ return this.entitiesService.nodes()
1344
+ .filter(node => node === changedNode || node.selected())
1345
+ .map(node => ({ type: 'position', id: node.node.id, point: node.point() }));
1346
+ }));
1256
1347
  this.nodeSizeChange$ = toObservable(this.entitiesService.nodes)
1257
1348
  .pipe(switchMap((nodes) => merge(...nodes.map(node => node.size$.pipe(skip(1), map(() => node))))), map(changedNode => [
1258
1349
  { type: 'size', id: changedNode.node.id, size: changedNode.size() }
@@ -1587,6 +1678,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1587
1678
  args: [{ selector: 'g[spacePointContext]' }]
1588
1679
  }] });
1589
1680
 
1681
+ function transformBackground(background) {
1682
+ return typeof background === 'string'
1683
+ ? { type: 'solid', color: background }
1684
+ : background;
1685
+ }
1686
+
1590
1687
  function Microtask(target, key, descriptor) {
1591
1688
  const originalMethod = descriptor.value;
1592
1689
  descriptor.value = function (...args) {
@@ -1665,6 +1762,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1665
1762
  type: Injectable
1666
1763
  }] });
1667
1764
 
1765
+ class DefaultNodeComponent {
1766
+ constructor() {
1767
+ this.selected = false;
1768
+ }
1769
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DefaultNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1770
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DefaultNodeComponent, selector: "default-node", inputs: { selected: "selected" }, host: { properties: { "class.selected": "selected" } }, ngImport: i0, template: "<ng-content />\n", styles: [":host{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}:host(.selected){border-width:2px}\n"] }); }
1771
+ }
1772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DefaultNodeComponent, decorators: [{
1773
+ type: Component,
1774
+ args: [{ selector: 'default-node', host: {
1775
+ '[class.selected]': 'selected'
1776
+ }, template: "<ng-content />\n", styles: [":host{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}:host(.selected){border-width:2px}\n"] }]
1777
+ }], propDecorators: { selected: [{
1778
+ type: Input
1779
+ }] } });
1780
+
1668
1781
  class HandleModel {
1669
1782
  constructor(rawHandle, parentNode) {
1670
1783
  this.rawHandle = rawHandle;
@@ -1878,6 +1991,7 @@ class ResizableComponent {
1878
1991
  this.nodeAccessor = inject(NodeAccessorService);
1879
1992
  this.rootPointer = inject(RootPointerDirective);
1880
1993
  this.viewportService = inject(ViewportService);
1994
+ this.spacePointContext = inject(SpacePointContextDirective);
1881
1995
  this.hostRef = inject(ElementRef);
1882
1996
  this.resizerColor = '#2e414c';
1883
1997
  this.gap = 1.5;
@@ -1889,7 +2003,7 @@ class ResizableComponent {
1889
2003
  this.minHeight = 0;
1890
2004
  // TODO: allow reszie beside the flow
1891
2005
  this.resizeOnGlobalMouseMove = this.rootPointer.pointerMovement$
1892
- .pipe(filter(() => this.resizeSide !== null), tap((event) => this.resize(event)), takeUntilDestroyed())
2006
+ .pipe(filter(() => this.resizeSide !== null), filter((event) => event.movementX !== 0 || event.movementY !== 0), tap((event) => this.resize(event)), takeUntilDestroyed())
1893
2007
  .subscribe();
1894
2008
  this.endResizeOnGlobalMouseUp = this.rootPointer.documentPointerEnd$
1895
2009
  .pipe(tap(() => this.endResize()), takeUntilDestroyed())
@@ -1918,193 +2032,55 @@ class ResizableComponent {
1918
2032
  this.resizeSide = side;
1919
2033
  this.model.resizing.set(true);
1920
2034
  }
1921
- resize({ movementX, movementY }) {
1922
- const offsetX = round(movementX / this.zoom());
1923
- const offsetY = round(movementY / this.zoom());
1924
- switch (this.resizeSide) {
1925
- case 'left':
1926
- let x = this.model.point().x + offsetX;
1927
- x = Math.max(x, this.getMinX());
1928
- x = Math.min(x, this.getMaxX());
1929
- // TODO this fixes increasing width when current node hits the parent
1930
- if (x === this.getMinX() || x === this.getMaxX()) {
1931
- return;
1932
- }
1933
- this.model.setPoint({ x, y: this.model.point().y }, false);
1934
- this.model.size.update(({ height, width }) => {
1935
- width -= offsetX;
1936
- width = Math.max(width, this.minWidth);
1937
- width = Math.min(width, this.getMaxWidth());
1938
- return { height, width: width };
1939
- });
1940
- return;
1941
- case 'right':
1942
- this.model.size.update(({ height, width }) => {
1943
- width += offsetX;
1944
- width = Math.max(width, this.minWidth);
1945
- width = Math.min(width, this.getMaxWidth());
1946
- const bounds = getNodesBounds(this.model.children());
1947
- width = Math.max(width, bounds.x + bounds.width);
1948
- return { height, width };
1949
- });
1950
- return;
1951
- case 'top':
1952
- let y = this.model.point().y + offsetY;
1953
- y = Math.max(y, this.getMinY());
1954
- y = Math.min(y, this.getMaxY());
1955
- if (y === this.getMinY() || y === this.getMaxY()) {
1956
- return;
1957
- }
1958
- this.model.setPoint({ x: this.model.point().x, y }, false);
1959
- this.model.size.update(({ height, width }) => {
1960
- height -= offsetY;
1961
- height = Math.max(height, this.minHeight);
1962
- height = Math.min(height, this.getMaxHeight());
1963
- return { width, height };
1964
- });
1965
- return;
1966
- case 'bottom':
1967
- this.model.size.update(({ height, width }) => {
1968
- height += offsetY;
1969
- height = Math.max(height, this.minHeight);
1970
- height = Math.min(height, this.getMaxHeight());
1971
- const bounds = getNodesBounds(this.model.children());
1972
- height = Math.max(height, bounds.y + bounds.height);
1973
- return { width, height };
1974
- });
1975
- return;
1976
- case 'top-left': {
1977
- let x = this.model.point().x + offsetX;
1978
- x = Math.max(x, this.getMinX());
1979
- x = Math.min(x, this.getMaxX());
1980
- let y = this.model.point().y + offsetY;
1981
- y = Math.max(y, this.getMinY());
1982
- y = Math.min(y, this.getMaxY());
1983
- if (x === this.getMinX() || y === this.getMinY() ||
1984
- x === this.getMaxX() || y === this.getMaxY()) {
1985
- return;
1986
- }
1987
- this.model.setPoint({ x, y }, false);
1988
- this.model.size.update(({ height, width }) => {
1989
- width -= offsetX;
1990
- width = Math.max(width, this.minWidth);
1991
- width = Math.min(width, this.getMaxWidth());
1992
- height -= offsetY;
1993
- height = Math.max(height, this.minHeight);
1994
- height = Math.min(height, this.getMaxHeight());
1995
- return { height, width };
1996
- });
1997
- return;
1998
- }
1999
- case 'top-right': {
2000
- let y = this.model.point().y + offsetY;
2001
- y = Math.max(y, this.getMinY());
2002
- y = Math.min(y, this.getMaxY());
2003
- if (y === this.getMinX() || y === this.getMaxY()) {
2004
- return;
2005
- }
2006
- this.model.setPoint({ x: this.model.point().x, y }, false);
2007
- this.model.size.update(({ height, width }) => {
2008
- const bounds = getNodesBounds(this.model.children());
2009
- width += offsetX;
2010
- width = Math.max(width, this.minWidth);
2011
- width = Math.min(width, this.getMaxWidth());
2012
- width = Math.max(width, bounds.x + bounds.width);
2013
- height -= offsetY;
2014
- height = Math.max(height, this.minHeight);
2015
- height = Math.min(height, this.getMaxHeight());
2016
- return { height, width };
2017
- });
2018
- return;
2019
- }
2020
- case 'bottom-left': {
2021
- let x = this.model.point().x + offsetX;
2022
- x = Math.max(x, this.getMinX());
2023
- x = Math.min(x, this.getMaxX());
2024
- if (x === this.getMinX() || x === this.getMaxX()) {
2025
- return;
2026
- }
2027
- this.model.setPoint({ x, y: this.model.point().y }, false);
2028
- this.model.size.update(({ height, width }) => {
2029
- width -= offsetX;
2030
- width = Math.max(width, this.minWidth);
2031
- width = Math.min(width, this.getMaxWidth());
2032
- height += offsetY;
2033
- height = Math.max(height, this.minHeight);
2034
- height = Math.min(height, this.getMaxHeight());
2035
- const bounds = getNodesBounds(this.model.children());
2036
- height = Math.max(height, bounds.y + bounds.height);
2037
- return { height, width };
2038
- });
2039
- return;
2040
- }
2041
- case 'bottom-right': {
2042
- this.model.size.update(({ height, width }) => {
2043
- const bounds = getNodesBounds(this.model.children());
2044
- width += offsetX;
2045
- width = Math.max(width, this.minWidth);
2046
- width = Math.min(width, this.getMaxWidth());
2047
- width = Math.max(width, bounds.x + bounds.width);
2048
- height += offsetY;
2049
- height = Math.max(height, this.minHeight);
2050
- height = Math.min(height, this.getMaxHeight());
2051
- height = Math.max(height, bounds.y + bounds.height);
2052
- return { height, width };
2053
- });
2054
- }
2055
- }
2035
+ resize(event) {
2036
+ if (!this.resizeSide)
2037
+ return;
2038
+ if (this.isResizeConstrained(event))
2039
+ return;
2040
+ const offset = calcOffset(event.movementX, event.movementY, this.zoom());
2041
+ const { x, y, width, height } = constrainRect(applyResize(this.resizeSide, this.model, offset), this.model, this.resizeSide, this.minWidth, this.minHeight);
2042
+ this.model.setPoint({ x, y }, false);
2043
+ this.model.size.set({ width, height });
2056
2044
  }
2057
2045
  endResize() {
2058
2046
  this.resizeSide = null;
2059
2047
  this.model.resizing.set(false);
2060
2048
  }
2061
- getMaxWidth() {
2062
- const parent = this.model.parent();
2063
- if (parent) {
2064
- return parent.size().width - this.model.point().x;
2065
- }
2066
- return Infinity;
2067
- }
2068
- getMaxHeight() {
2069
- const parent = this.model.parent();
2070
- if (parent) {
2071
- return parent.size().height - this.model.point().y;
2072
- }
2073
- return Infinity;
2074
- }
2075
- getMinX() {
2076
- const parent = this.model.parent();
2077
- if (parent) {
2078
- return 0;
2049
+ isResizeConstrained({ x, y, movementX, movementY }) {
2050
+ const flowPoint = this.spacePointContext.documentPointToFlowPoint({ x, y });
2051
+ if (this.resizeSide?.includes('right')) {
2052
+ if (movementX > 0 && flowPoint.x < (this.model.point().x + this.model.size().width)) {
2053
+ return true;
2054
+ }
2055
+ if (movementX < 0 && flowPoint.x > this.model.point().x + this.model.size().width) {
2056
+ return true;
2057
+ }
2079
2058
  }
2080
- return -Infinity;
2081
- }
2082
- getMinY() {
2083
- const parent = this.model.parent();
2084
- if (parent) {
2085
- return 0;
2059
+ if (this.resizeSide?.includes('left')) {
2060
+ if (movementX < 0 && flowPoint.x > this.model.point().x) {
2061
+ return true;
2062
+ }
2063
+ if (movementX > 0 && flowPoint.x < this.model.point().x) {
2064
+ return true;
2065
+ }
2086
2066
  }
2087
- return -Infinity;
2088
- }
2089
- getMaxX() {
2090
- const x = this.model.point().x;
2091
- const width = this.model.size().width;
2092
- const children = this.model.children();
2093
- if (children) {
2094
- const bounds = getNodesBounds(children);
2095
- return x + (bounds.x + bounds.width) >= x + width ? x : (width - this.minWidth) + x;
2067
+ if (this.resizeSide?.includes('bottom')) {
2068
+ if (movementY > 0 && flowPoint.y < (this.model.point().y + this.model.size().height)) {
2069
+ return true;
2070
+ }
2071
+ if (movementY < 0 && flowPoint.y > this.model.point().y + this.model.size().height) {
2072
+ return true;
2073
+ }
2096
2074
  }
2097
- return (width - this.minWidth) + x;
2098
- }
2099
- getMaxY() {
2100
- const y = this.model.point().y;
2101
- const height = this.model.size().height;
2102
- const children = this.model.children();
2103
- if (children) {
2104
- const bounds = getNodesBounds(children);
2105
- return y + (bounds.y + bounds.height) >= y + height ? y : (height - this.minHeight) + y;
2075
+ if (this.resizeSide?.includes('top')) {
2076
+ if (movementY < 0 && flowPoint.y > this.model.point().y) {
2077
+ return true;
2078
+ }
2079
+ if (movementY > 0 && flowPoint.y < this.model.point().y) {
2080
+ return true;
2081
+ }
2106
2082
  }
2107
- return (height - this.minHeight) + y;
2083
+ return false;
2108
2084
  }
2109
2085
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ResizableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2110
2086
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ResizableComponent, selector: "[resizable]", inputs: { resizable: "resizable", resizerColor: "resizerColor", gap: "gap" }, viewQueries: [{ propertyName: "resizer", first: true, predicate: ["resizer"], descendants: true, static: true }], ngImport: i0, template: "<ng-template #resizer>\n <svg:g>\n <!-- top line -->\n <svg:line\n class=\"top\"\n [attr.x1]=\"lineGap\"\n [attr.y1]=\"-gap\"\n [attr.x2]=\"model.size().width - lineGap\"\n [attr.y2]=\"-gap\"\n [attr.stroke]=\"resizerColor\"\n stroke-width=\"2\"\n (pointerStart)=\"startResize('top', $event)\"\n />\n <!-- Left line -->\n <svg:line\n class=\"left\"\n [attr.x1]=\"-gap\"\n [attr.y1]=\"lineGap\"\n [attr.x2]=\"-gap\"\n [attr.y2]=\"model.size().height - lineGap\"\n [attr.stroke]=\"resizerColor\"\n stroke-width=\"2\"\n (pointerStart)=\"startResize('left', $event)\"\n />\n <!-- Bottom line -->\n <svg:line\n class=\"bottom\"\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 stroke-width=\"2\"\n (pointerStart)=\"startResize('bottom', $event)\"\n />\n <!-- Right line -->\n <svg:line\n class=\"right\"\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 stroke-width=\"2\"\n (pointerStart)=\"startResize('right', $event)\"\n />\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\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\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\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 />\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"] }] }); }
@@ -2125,6 +2101,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2125
2101
  type: ViewChild,
2126
2102
  args: ['resizer', { static: true }]
2127
2103
  }], ngAfterViewInit: [] } });
2104
+ function calcOffset(movementX, movementY, zoom) {
2105
+ return {
2106
+ offsetX: round(movementX / zoom),
2107
+ offsetY: round(movementY / zoom)
2108
+ };
2109
+ }
2110
+ function applyResize(side, model, offset) {
2111
+ const { offsetX, offsetY } = offset;
2112
+ const { x, y } = model.point();
2113
+ const { width, height } = model.size();
2114
+ // Handle each case of resizing (top, bottom, left, right, corners)
2115
+ switch (side) {
2116
+ case 'left':
2117
+ return { x: x + offsetX, y, width: width - offsetX, height };
2118
+ case 'right':
2119
+ return { x, y, width: width + offsetX, height };
2120
+ case 'top':
2121
+ return { x, y: y + offsetY, width, height: height - offsetY };
2122
+ case 'bottom':
2123
+ return { x, y, width, height: height + offsetY };
2124
+ case 'top-left':
2125
+ return { x: x + offsetX, y: y + offsetY, width: width - offsetX, height: height - offsetY };
2126
+ case 'top-right':
2127
+ return { x, y: y + offsetY, width: width + offsetX, height: height - offsetY };
2128
+ case 'bottom-left':
2129
+ return { x: x + offsetX, y, width: width - offsetX, height: height + offsetY };
2130
+ case 'bottom-right':
2131
+ return { x, y, width: width + offsetX, height: height + offsetY };
2132
+ }
2133
+ }
2134
+ function constrainRect(rect, model, side, minWidth, minHeight) {
2135
+ let { x, y, width, height } = rect;
2136
+ // 1. Prevent negative dimensions
2137
+ width = Math.max(width, 0);
2138
+ height = Math.max(height, 0);
2139
+ // 2. Apply minimum size constraints
2140
+ width = Math.max(minWidth, width);
2141
+ height = Math.max(minHeight, height);
2142
+ // Apply left/top constraints based on minimum size
2143
+ x = Math.min(x, model.point().x + model.size().width - minWidth);
2144
+ y = Math.min(y, model.point().y + model.size().height - minHeight);
2145
+ const parent = model.parent();
2146
+ // 3. Apply maximum size constraints based on parent size (if exists)
2147
+ if (parent) {
2148
+ x = Math.max(x, 0); // Left boundary of the parent
2149
+ y = Math.max(y, 0); // Top boundary of the parent
2150
+ if (x === 0) {
2151
+ width = model.point().x + model.size().width;
2152
+ }
2153
+ if (y === 0) {
2154
+ height = model.point().y + model.size().height;
2155
+ }
2156
+ width = Math.min(width, parent.size().width - model.point().x);
2157
+ height = Math.min(height, parent.size().height - model.point().y);
2158
+ }
2159
+ const bounds = getNodesBounds(model.children());
2160
+ // 4. Apply child node constraints (if children exist)
2161
+ if (bounds) {
2162
+ if (side.includes('left')) {
2163
+ x = Math.min(x, (model.point().x + model.size().width) - (bounds.x + bounds.width));
2164
+ width = Math.max(width, bounds.x + bounds.width);
2165
+ }
2166
+ if (side.includes('right')) {
2167
+ width = Math.max(width, bounds.x + bounds.width);
2168
+ }
2169
+ if (side.includes('bottom')) {
2170
+ height = Math.max(height, bounds.y + bounds.height);
2171
+ }
2172
+ if (side.includes('top')) {
2173
+ y = Math.min(y, (model.point().y + model.size().height) - (bounds.y + bounds.height));
2174
+ height = Math.max(height, bounds.y + bounds.height);
2175
+ }
2176
+ }
2177
+ return {
2178
+ x,
2179
+ y,
2180
+ width,
2181
+ height
2182
+ };
2183
+ }
2128
2184
 
2129
2185
  class HandleSizeControllerDirective {
2130
2186
  constructor() {
@@ -2236,7 +2292,7 @@ class NodeComponent {
2236
2292
  }
2237
2293
  }
2238
2294
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2239
- 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 (mousedown)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_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 </div>\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 (mousedown)=\"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 (mousedown)=\"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 (mousedown)=\"pullNode(); selectNode()\"\n/>\n\n<!-- Template group node -->\n<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (mousedown)=\"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-node{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-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: 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 }); }
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 }); }
2240
2296
  }
2241
2297
  __decorate([
2242
2298
  InjectionContext
@@ -2246,7 +2302,7 @@ __decorate([
2246
2302
  ], NodeComponent.prototype, "ngAfterViewInit", null);
2247
2303
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
2248
2304
  type: Component,
2249
- 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 (mousedown)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_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 </div>\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 (mousedown)=\"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 (mousedown)=\"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 (mousedown)=\"pullNode(); selectNode()\"\n/>\n\n<!-- Template group node -->\n<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (mousedown)=\"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-node{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
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"] }]
2250
2306
  }], propDecorators: { nodeModel: [{
2251
2307
  type: Input
2252
2308
  }], nodeTemplate: [{
@@ -2502,13 +2558,11 @@ const defaultGap = 20;
2502
2558
  const defaultDotSize = 2;
2503
2559
  const defaultDotColor = 'rgb(177, 177, 183)';
2504
2560
  class BackgroundComponent {
2505
- set background(value) {
2506
- this.backgroundSignal.set(value);
2507
- }
2508
2561
  constructor() {
2509
2562
  this.viewportService = inject(ViewportService);
2510
2563
  this.rootSvg = inject(RootSvgReferenceDirective).element;
2511
- this.backgroundSignal = signal({ type: 'solid', color: defaultBg });
2564
+ this.settingsService = inject(FlowSettingsService);
2565
+ this.backgroundSignal = this.settingsService.background;
2512
2566
  this.scaledGap = computed(() => {
2513
2567
  const background = this.backgroundSignal();
2514
2568
  if (background.type === 'dots') {
@@ -2542,20 +2596,12 @@ class BackgroundComponent {
2542
2596
  });
2543
2597
  }
2544
2598
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BackgroundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2545
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "16.2.12", type: BackgroundComponent, selector: "g[background]", inputs: { background: ["background", "background", transform] }, ngImport: i0, template: "<ng-container *ngIf=\"backgroundSignal().type === 'dots'\">\n <svg:pattern\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\"\n patternUnits=\"userSpaceOnUse\"\n >\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\"\n />\n </svg:pattern>\n\n <svg:rect\n x=\"0\"\n y=\"0\"\n width=\"100%\"\n height=\"100%\"\n [attr.fill]=\"patternUrl\"\n />\n</ng-container>\n", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2599
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: BackgroundComponent, selector: "g[background]", ngImport: i0, template: "<ng-container *ngIf=\"backgroundSignal().type === 'dots'\">\n <svg:pattern\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\"\n patternUnits=\"userSpaceOnUse\"\n >\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\"\n />\n </svg:pattern>\n\n <svg:rect\n x=\"0\"\n y=\"0\"\n width=\"100%\"\n height=\"100%\"\n [attr.fill]=\"patternUrl\"\n />\n</ng-container>\n", dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2546
2600
  }
2547
2601
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BackgroundComponent, decorators: [{
2548
2602
  type: Component,
2549
2603
  args: [{ selector: 'g[background]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"backgroundSignal().type === 'dots'\">\n <svg:pattern\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\"\n patternUnits=\"userSpaceOnUse\"\n >\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\"\n />\n </svg:pattern>\n\n <svg:rect\n x=\"0\"\n y=\"0\"\n width=\"100%\"\n height=\"100%\"\n [attr.fill]=\"patternUrl\"\n />\n</ng-container>\n" }]
2550
- }], ctorParameters: function () { return []; }, propDecorators: { background: [{
2551
- type: Input,
2552
- args: [{ required: true, transform }]
2553
- }] } });
2554
- function transform(background) {
2555
- return typeof background === 'string'
2556
- ? { type: 'solid', color: background }
2557
- : background;
2558
- }
2604
+ }], ctorParameters: function () { return []; } });
2559
2605
 
2560
2606
  // TODO: too general purpose nane
2561
2607
  class RootSvgContextDirective {
@@ -2662,11 +2708,8 @@ class VflowComponent {
2662
2708
  this.nodeRenderingService = inject(NodeRenderingService);
2663
2709
  this.flowSettingsService = inject(FlowSettingsService);
2664
2710
  this.componentEventBusService = inject(ComponentEventBusService);
2711
+ this.keyboardService = inject(KeyboardService);
2665
2712
  this.injector = inject(Injector);
2666
- /**
2667
- * Background for flow
2668
- */
2669
- this.background = '#fff';
2670
2713
  this.optimization = {
2671
2714
  computeLayersOnInit: true
2672
2715
  };
@@ -2711,6 +2754,7 @@ class VflowComponent {
2711
2754
  this.edgesChange$ = this.edgesChangeService.changes$;
2712
2755
  // #endregion
2713
2756
  this.markers = this.flowEntitiesService.markers;
2757
+ this.minimap = this.flowEntitiesService.minimap;
2714
2758
  }
2715
2759
  // #endregion
2716
2760
  // #region SETTINGS
@@ -2747,12 +2791,21 @@ class VflowComponent {
2747
2791
  set handlePositions(handlePositions) {
2748
2792
  this.flowSettingsService.handlePositions.set(handlePositions);
2749
2793
  }
2794
+ /**
2795
+ * Background for flow
2796
+ */
2797
+ set background(value) {
2798
+ this.flowSettingsService.background.set(transformBackground(value));
2799
+ }
2750
2800
  /**
2751
2801
  * Global rule if you can or can't select entities
2752
2802
  */
2753
2803
  set entitiesSelectable(value) {
2754
2804
  this.flowSettingsService.entitiesSelectable.set(value);
2755
2805
  }
2806
+ set keyboardShortcuts(value) {
2807
+ this.keyboardService.setShortcuts(value);
2808
+ }
2756
2809
  /**
2757
2810
  * Settings for connection (it renders when user tries to create edge between nodes)
2758
2811
  *
@@ -2851,7 +2904,7 @@ class VflowComponent {
2851
2904
  }
2852
2905
  }
2853
2906
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2854
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "16.2.12", type: VflowComponent, selector: "vflow", inputs: { view: "view", minZoom: "minZoom", maxZoom: "maxZoom", handlePositions: "handlePositions", background: "background", optimization: "optimization", entitiesSelectable: "entitiesSelectable", connection: ["connection", "connection", (settings) => new ConnectionModel(settings)], nodes: "nodes", edges: "edges" }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
2907
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "16.2.12", type: VflowComponent, selector: "vflow", inputs: { view: "view", minZoom: "minZoom", maxZoom: "maxZoom", handlePositions: "handlePositions", background: "background", optimization: "optimization", entitiesSelectable: "entitiesSelectable", keyboardShortcuts: "keyboardShortcuts", connection: ["connection", "connection", (settings) => new ConnectionModel(settings)], nodes: "nodes", edges: "edges" }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
2855
2908
  DraggableService,
2856
2909
  ViewportService,
2857
2910
  FlowStatusService,
@@ -2861,8 +2914,9 @@ class VflowComponent {
2861
2914
  NodeRenderingService,
2862
2915
  SelectionService,
2863
2916
  FlowSettingsService,
2864
- ComponentEventBusService
2865
- ], 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]=\"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</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "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]", inputs: ["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 }); }
2917
+ 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 }); }
2866
2920
  }
2867
2921
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
2868
2922
  type: Component,
@@ -2876,11 +2930,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2876
2930
  NodeRenderingService,
2877
2931
  SelectionService,
2878
2932
  FlowSettingsService,
2879
- ComponentEventBusService
2933
+ ComponentEventBusService,
2934
+ KeyboardService
2880
2935
  ], hostDirectives: [
2881
2936
  connectionControllerHostDirective,
2882
2937
  changesControllerHostDirective
2883
- ], 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]=\"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</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"] }]
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"] }]
2884
2939
  }], propDecorators: { view: [{
2885
2940
  type: Input
2886
2941
  }], minZoom: [{
@@ -2895,6 +2950,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2895
2950
  type: Input
2896
2951
  }], entitiesSelectable: [{
2897
2952
  type: Input
2953
+ }], keyboardShortcuts: [{
2954
+ type: Input
2898
2955
  }], connection: [{
2899
2956
  type: Input,
2900
2957
  args: [{ transform: (settings) => new ConnectionModel(settings) }]
@@ -2951,7 +3008,7 @@ class SelectableDirective {
2951
3008
  return null;
2952
3009
  }
2953
3010
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SelectableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2954
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SelectableDirective, selector: "[selectable]", host: { listeners: { "mousedown": "onMousedown()" } }, ngImport: i0 }); }
3011
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SelectableDirective, selector: "[selectable]", host: { listeners: { "mousedown": "onMousedown()", "touchstart": "onMousedown()" } }, ngImport: i0 }); }
2955
3012
  }
2956
3013
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SelectableDirective, decorators: [{
2957
3014
  type: Directive,
@@ -2959,11 +3016,130 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2959
3016
  }], propDecorators: { onMousedown: [{
2960
3017
  type: HostListener,
2961
3018
  args: ['mousedown']
3019
+ }, {
3020
+ type: HostListener,
3021
+ args: ['touchstart']
3022
+ }] } });
3023
+
3024
+ class MinimapModel {
3025
+ constructor() {
3026
+ this.template = signal(null);
3027
+ }
3028
+ }
3029
+
3030
+ class MiniMapComponent {
3031
+ constructor() {
3032
+ this.entitiesService = inject(FlowEntitiesService);
3033
+ this.flowSettingsService = inject(FlowSettingsService);
3034
+ this.viewportService = inject(ViewportService);
3035
+ this.injector = inject(Injector);
3036
+ /**
3037
+ * The color outside the viewport (invisible area)
3038
+ */
3039
+ this.maskColor = `rgba(215, 215, 215, 0.6)`;
3040
+ /**
3041
+ * The minimap stroke color
3042
+ */
3043
+ this.strokeColor = `rgb(200, 200, 200)`;
3044
+ this.minimapOffset = 10;
3045
+ this.minimapScale = computed(() => {
3046
+ if (this.scaleOnHoverSignal()) {
3047
+ return this.hovered() ? 0.4 : 0.2;
3048
+ }
3049
+ return 0.2;
3050
+ });
3051
+ this.viewportColor = computed(() => this.flowSettingsService.background().color ?? '#fff');
3052
+ this.hovered = signal(false);
3053
+ this.minimapPoint = computed(() => {
3054
+ switch (this.minimapPosition()) {
3055
+ case 'top-left':
3056
+ return { x: this.minimapOffset, y: this.minimapOffset };
3057
+ case 'top-right':
3058
+ return {
3059
+ x: this.flowSettingsService.computedFlowWidth() - this.minimapWidth() - this.minimapOffset,
3060
+ y: this.minimapOffset
3061
+ };
3062
+ case 'bottom-left':
3063
+ return {
3064
+ x: this.minimapOffset,
3065
+ y: this.flowSettingsService.computedFlowHeight() - this.minimapHeight() - this.minimapOffset
3066
+ };
3067
+ case 'bottom-right':
3068
+ return {
3069
+ x: this.flowSettingsService.computedFlowWidth() - this.minimapWidth() - this.minimapOffset,
3070
+ y: this.flowSettingsService.computedFlowHeight() - this.minimapHeight() - this.minimapOffset
3071
+ };
3072
+ }
3073
+ });
3074
+ this.minimapWidth = computed(() => this.flowSettingsService.computedFlowWidth() * this.minimapScale());
3075
+ this.minimapHeight = computed(() => this.flowSettingsService.computedFlowHeight() * this.minimapScale());
3076
+ this.viewportTransform = computed(() => {
3077
+ const viewport = this.viewportService.readableViewport();
3078
+ let scale = 1 / viewport.zoom;
3079
+ let x = -(viewport.x * this.minimapScale()) * scale;
3080
+ x /= this.minimapScale();
3081
+ let y = -(viewport.y * this.minimapScale()) * scale;
3082
+ y /= this.minimapScale();
3083
+ scale /= this.minimapScale();
3084
+ return `translate(${x}, ${y}) scale(${scale})`;
3085
+ });
3086
+ this.boundsViewport = computed(() => {
3087
+ const nodes = this.entitiesService.nodes();
3088
+ return getViewportForBounds(getNodesBounds(nodes), this.flowSettingsService.computedFlowWidth(), this.flowSettingsService.computedFlowHeight(), -Infinity, 1.5, 0);
3089
+ });
3090
+ this.minimapTransform = computed(() => {
3091
+ const vport = this.boundsViewport();
3092
+ const x = vport.x * this.minimapScale();
3093
+ const y = vport.y * this.minimapScale();
3094
+ const scale = vport.zoom * this.minimapScale();
3095
+ return `translate(${x} ${y}) scale(${scale})`;
3096
+ });
3097
+ this.minimapPosition = signal('bottom-right');
3098
+ this.scaleOnHoverSignal = signal(false);
3099
+ }
3100
+ /**
3101
+ * The corner of the flow where to render a mini-map
3102
+ */
3103
+ set position(value) {
3104
+ this.minimapPosition.set(value);
3105
+ }
3106
+ /**
3107
+ * Make a minimap bigger on hover
3108
+ */
3109
+ set scaleOnHover(value) {
3110
+ this.scaleOnHoverSignal.set(value);
3111
+ }
3112
+ ngOnInit() {
3113
+ const model = new MinimapModel();
3114
+ model.template.set(this.minimap);
3115
+ this.entitiesService.minimap.set(model);
3116
+ }
3117
+ trackNodes(idx, { node }) {
3118
+ return node;
3119
+ }
3120
+ 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"] }] }); }
3122
+ }
3123
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MiniMapComponent, decorators: [{
3124
+ type: Component,
3125
+ args: [{ selector: 'mini-map', 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"] }]
3126
+ }], propDecorators: { position: [{
3127
+ type: Input
3128
+ }], maskColor: [{
3129
+ type: Input
3130
+ }], strokeColor: [{
3131
+ type: Input
3132
+ }], scaleOnHover: [{
3133
+ type: Input
3134
+ }], minimap: [{
3135
+ type: ViewChild,
3136
+ args: ['minimap', { static: true }]
2962
3137
  }] } });
2963
3138
 
2964
3139
  const components = [
2965
3140
  VflowComponent,
2966
3141
  NodeComponent,
3142
+ DefaultNodeComponent,
2967
3143
  EdgeComponent,
2968
3144
  EdgeLabelComponent,
2969
3145
  ConnectionComponent,
@@ -2971,6 +3147,7 @@ const components = [
2971
3147
  DefsComponent,
2972
3148
  BackgroundComponent,
2973
3149
  ResizableComponent,
3150
+ MiniMapComponent
2974
3151
  ];
2975
3152
  const directives = [
2976
3153
  SpacePointContextDirective,
@@ -2995,13 +3172,15 @@ class VflowModule {
2995
3172
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2996
3173
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, declarations: [VflowComponent,
2997
3174
  NodeComponent,
3175
+ DefaultNodeComponent,
2998
3176
  EdgeComponent,
2999
3177
  EdgeLabelComponent,
3000
3178
  ConnectionComponent,
3001
3179
  HandleComponent,
3002
3180
  DefsComponent,
3003
3181
  BackgroundComponent,
3004
- ResizableComponent, SpacePointContextDirective,
3182
+ ResizableComponent,
3183
+ MiniMapComponent, SpacePointContextDirective,
3005
3184
  MapContextDirective,
3006
3185
  RootSvgReferenceDirective,
3007
3186
  RootSvgContextDirective,
@@ -3017,7 +3196,8 @@ class VflowModule {
3017
3196
  HandleTemplateDirective], imports: [CommonModule], exports: [VflowComponent,
3018
3197
  HandleComponent,
3019
3198
  ResizableComponent,
3020
- SelectableDirective, NodeHtmlTemplateDirective,
3199
+ SelectableDirective,
3200
+ MiniMapComponent, NodeHtmlTemplateDirective,
3021
3201
  GroupNodeTemplateDirective,
3022
3202
  EdgeLabelHtmlTemplateDirective,
3023
3203
  EdgeTemplateDirective,
@@ -3034,6 +3214,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
3034
3214
  HandleComponent,
3035
3215
  ResizableComponent,
3036
3216
  SelectableDirective,
3217
+ MiniMapComponent,
3037
3218
  ...templateDirectives
3038
3219
  ],
3039
3220
  declarations: [...components, ...directives, ...templateDirectives],
@@ -3046,5 +3227,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
3046
3227
  * Generated bundle index. Do not edit.
3047
3228
  */
3048
3229
 
3049
- export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, CustomDynamicNodeComponent, CustomNodeComponent, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, GroupNodeTemplateDirective, HandleComponent, HandleTemplateDirective, NodeHtmlTemplateDirective, ResizableComponent, SelectableDirective, VflowComponent, VflowModule, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode };
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 };
3050
3231
  //# sourceMappingURL=ngx-vflow.mjs.map