ngx-vflow 0.7.0 → 0.8.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 (32) hide show
  1. package/esm2022/lib/vflow/components/background/background.component.mjs +4 -4
  2. package/esm2022/lib/vflow/components/connection/connection.component.mjs +2 -2
  3. package/esm2022/lib/vflow/components/handle/handle.component.mjs +5 -5
  4. package/esm2022/lib/vflow/components/node/node.component.mjs +31 -82
  5. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +12 -2
  6. package/esm2022/lib/vflow/directives/connection-controller.directive.mjs +91 -12
  7. package/esm2022/lib/vflow/directives/space-point-context.directive.mjs +11 -5
  8. package/esm2022/lib/vflow/interfaces/connection-settings.interface.mjs +1 -1
  9. package/esm2022/lib/vflow/interfaces/connection.internal.interface.mjs +2 -0
  10. package/esm2022/lib/vflow/models/connection.model.mjs +28 -6
  11. package/esm2022/lib/vflow/models/handle.model.mjs +1 -1
  12. package/esm2022/lib/vflow/services/flow-entities.service.mjs +2 -2
  13. package/esm2022/lib/vflow/services/flow-status.service.mjs +7 -7
  14. package/esm2022/lib/vflow/services/handle.service.mjs +1 -1
  15. package/esm2022/lib/vflow/types/connection-mode.type.mjs +2 -0
  16. package/esm2022/lib/vflow/utils/adjust-direction.mjs +30 -0
  17. package/esm2022/public-api.mjs +2 -1
  18. package/fesm2022/ngx-vflow.mjs +339 -246
  19. package/fesm2022/ngx-vflow.mjs.map +1 -1
  20. package/lib/vflow/components/node/node.component.d.ts +4 -12
  21. package/lib/vflow/components/vflow/vflow.component.d.ts +6 -0
  22. package/lib/vflow/directives/connection-controller.directive.d.ts +6 -0
  23. package/lib/vflow/directives/space-point-context.directive.d.ts +1 -0
  24. package/lib/vflow/interfaces/connection-settings.interface.d.ts +2 -0
  25. package/lib/vflow/interfaces/connection.internal.interface.d.ts +8 -0
  26. package/lib/vflow/models/connection.model.d.ts +5 -2
  27. package/lib/vflow/models/handle.model.d.ts +1 -1
  28. package/lib/vflow/services/flow-status.service.d.ts +7 -18
  29. package/lib/vflow/types/connection-mode.type.d.ts +1 -0
  30. package/lib/vflow/utils/adjust-direction.d.ts +11 -0
  31. package/package.json +1 -1
  32. package/public-api.d.ts +1 -0
@@ -1,10 +1,10 @@
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, Injectable, inject, ElementRef, Directive, computed, effect, untracked, Input, TemplateRef, EventEmitter, Output, DestroyRef, runInInjectionContext, HostListener, Injector, Component, ChangeDetectionStrategy, ViewChild, ContentChild, NgModule } from '@angular/core';
4
+ import { signal, Injectable, inject, ElementRef, Directive, computed, effect, untracked, Input, TemplateRef, EventEmitter, Output, DestroyRef, runInInjectionContext, Injector, Component, ChangeDetectionStrategy, HostListener, ViewChild, 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, BehaviorSubject, observeOn, animationFrameScheduler, switchMap, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, Observable, fromEvent, share, Subscription, startWith } from 'rxjs';
7
+ import { Subject, tap, merge, BehaviorSubject, observeOn, animationFrameScheduler, switchMap, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, fromEvent, share, Observable, startWith } from 'rxjs';
8
8
  import { takeUntilDestroyed, toSignal, toObservable } from '@angular/core/rxjs-interop';
9
9
  import { drag } from 'd3-drag';
10
10
  import { path } from 'd3-path';
@@ -59,13 +59,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
59
59
  }] });
60
60
 
61
61
  class ConnectionModel {
62
- constructor(connection) {
63
- this.connection = connection;
64
- this.curve = connection.curve ?? 'bezier';
65
- this.type = connection.type ?? 'default';
66
- this.validator = connection.validator ?? (() => true);
62
+ constructor(settings) {
63
+ this.settings = settings;
64
+ this.curve = settings.curve ?? 'bezier';
65
+ this.type = settings.type ?? 'default';
66
+ this.mode = settings.mode ?? 'strict';
67
+ const validatorsToRun = this.getValidators(settings);
68
+ this.validator = (connection) => validatorsToRun.every((v) => v(connection));
69
+ }
70
+ getValidators(settings) {
71
+ const validators = [];
72
+ validators.push(notSelfValidator);
73
+ if (this.mode === 'loose') {
74
+ validators.push(hasSourceAndTargetHandleValidator);
75
+ }
76
+ if (settings.validator) {
77
+ validators.push(settings.validator);
78
+ }
79
+ return validators;
67
80
  }
68
81
  }
82
+ /**
83
+ * Internal validator that not allows self connections
84
+ */
85
+ const notSelfValidator = (connection) => {
86
+ return connection.source !== connection.target;
87
+ };
88
+ const hasSourceAndTargetHandleValidator = (connection) => {
89
+ return connection.sourceHandle !== undefined && connection.targetHandle !== undefined;
90
+ };
69
91
 
70
92
  function hashCode(str) {
71
93
  return str.split('').reduce((a, b) => {
@@ -97,7 +119,7 @@ class FlowEntitiesService {
97
119
  markersMap.set(hash, e.edge.markers.end);
98
120
  }
99
121
  });
100
- const connectionMarker = this.connection().connection.marker;
122
+ const connectionMarker = this.connection().settings.marker;
101
123
  if (connectionMarker) {
102
124
  const hash = hashCode(JSON.stringify(connectionMarker));
103
125
  markersMap.set(hash, connectionMarker);
@@ -385,14 +407,14 @@ class FlowStatusService {
385
407
  setIdleStatus() {
386
408
  this.status.set({ state: 'idle', payload: null });
387
409
  }
388
- setConnectionStartStatus(sourceNode, sourceHandle) {
389
- this.status.set({ state: 'connection-start', payload: { sourceNode, sourceHandle } });
410
+ setConnectionStartStatus(source, sourceHandle) {
411
+ this.status.set({ state: 'connection-start', payload: { source, sourceHandle } });
390
412
  }
391
- setConnectionValidationStatus(valid, sourceNode, targetNode, sourceHandle, targetHandle) {
392
- this.status.set({ state: 'connection-validation', payload: { sourceNode, targetNode, sourceHandle, targetHandle, valid } });
413
+ setConnectionValidationStatus(valid, source, target, sourceHandle, targetHandle) {
414
+ this.status.set({ state: 'connection-validation', payload: { source, target, sourceHandle, targetHandle, valid } });
393
415
  }
394
- setConnectionEndStatus(sourceNode, targetNode, sourceHandle, targetHandle) {
395
- this.status.set({ state: 'connection-end', payload: { sourceNode, targetNode, sourceHandle, targetHandle } });
416
+ setConnectionEndStatus(source, target, sourceHandle, targetHandle) {
417
+ this.status.set({ state: 'connection-end', payload: { source, target, sourceHandle, targetHandle } });
396
418
  }
397
419
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
398
420
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService }); }
@@ -415,6 +437,36 @@ function batchStatusChanges(...changes) {
415
437
  }
416
438
  }
417
439
 
440
+ /**
441
+ * This function contains a hack-y behavior.
442
+ * If the handles are of the same type (source-source or target-target),
443
+ * it returns nodes where source === target and
444
+ * handles where sourceHandle === targetHandle
445
+ *
446
+ * This leads to that notSelfValidator returns false for these cases,
447
+ * exactly what we need for strict connection type
448
+ */
449
+ function adjustDirection(connection) {
450
+ const result = {};
451
+ if (connection.sourceHandle.rawHandle.type === 'source') {
452
+ result.source = connection.source;
453
+ result.sourceHandle = connection.sourceHandle;
454
+ }
455
+ else {
456
+ result.source = connection.target;
457
+ result.sourceHandle = connection.targetHandle;
458
+ }
459
+ if (connection.targetHandle.rawHandle.type === 'target') {
460
+ result.target = connection.target;
461
+ result.targetHandle = connection.targetHandle;
462
+ }
463
+ else {
464
+ result.target = connection.source;
465
+ result.targetHandle = connection.sourceHandle;
466
+ }
467
+ return result;
468
+ }
469
+
418
470
  class ConnectionControllerDirective {
419
471
  constructor() {
420
472
  /**
@@ -431,18 +483,96 @@ class ConnectionControllerDirective {
431
483
  this.connectEffect = effect(() => {
432
484
  const status = this.statusService.status();
433
485
  if (status.state === 'connection-end') {
434
- const sourceModel = status.payload.sourceNode;
435
- const targetModel = status.payload.targetNode;
436
- const source = sourceModel.node.id;
437
- const target = targetModel.node.id;
438
- const sourceHandle = status.payload.sourceHandle.rawHandle.id;
439
- const targetHandle = status.payload.targetHandle.rawHandle.id;
440
- const connection = this.flowEntitiesService.connection();
441
- if (connection.validator({ source, target, sourceHandle, targetHandle })) {
442
- this.onConnect.emit({ source, target, sourceHandle, targetHandle });
486
+ let source = status.payload.source;
487
+ let target = status.payload.target;
488
+ let sourceHandle = status.payload.sourceHandle;
489
+ let targetHandle = status.payload.targetHandle;
490
+ if (this.isStrictMode()) {
491
+ const adjusted = adjustDirection({
492
+ source: status.payload.source,
493
+ sourceHandle: status.payload.sourceHandle,
494
+ target: status.payload.target,
495
+ targetHandle: status.payload.targetHandle
496
+ });
497
+ source = adjusted.source;
498
+ target = adjusted.target;
499
+ sourceHandle = adjusted.sourceHandle;
500
+ targetHandle = adjusted.targetHandle;
501
+ }
502
+ const sourceId = source.node.id;
503
+ const targetId = target.node.id;
504
+ const sourceHandleId = sourceHandle.rawHandle.id;
505
+ const targetHandleId = targetHandle.rawHandle.id;
506
+ const connectionModel = this.flowEntitiesService.connection();
507
+ const connection = {
508
+ source: sourceId, target: targetId,
509
+ sourceHandle: sourceHandleId, targetHandle: targetHandleId
510
+ };
511
+ if (connectionModel.validator(connection)) {
512
+ this.onConnect.emit(connection);
443
513
  }
444
514
  }
445
515
  }, { allowSignalWrites: true });
516
+ this.isStrictMode = computed(() => this.flowEntitiesService.connection().mode === 'strict');
517
+ }
518
+ startConnection(handle) {
519
+ this.statusService.setConnectionStartStatus(handle.parentNode, handle);
520
+ }
521
+ validateConnection(handle) {
522
+ const status = this.statusService.status();
523
+ if (status.state === 'connection-start') {
524
+ let source = status.payload.source;
525
+ let target = handle.parentNode;
526
+ let sourceHandle = status.payload.sourceHandle;
527
+ let targetHandle = handle;
528
+ if (this.isStrictMode()) {
529
+ // swap direction (if needed) according to actual source and target of strict mode
530
+ const adjusted = adjustDirection({
531
+ source: status.payload.source,
532
+ sourceHandle: status.payload.sourceHandle,
533
+ target: handle.parentNode,
534
+ targetHandle: handle
535
+ });
536
+ source = adjusted.source;
537
+ target = adjusted.target;
538
+ sourceHandle = adjusted.sourceHandle;
539
+ targetHandle = adjusted.targetHandle;
540
+ }
541
+ const valid = this.flowEntitiesService.connection().validator({
542
+ source: source.node.id,
543
+ target: target.node.id,
544
+ sourceHandle: sourceHandle.rawHandle.id,
545
+ targetHandle: targetHandle.rawHandle.id
546
+ });
547
+ // TODO: check how react flow handles highlight of handle
548
+ // if direction changes
549
+ handle.state.set(valid ? 'valid' : 'invalid');
550
+ // status is about how we draw connection, so we don't need
551
+ // swapped diretion here
552
+ this.statusService.setConnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle);
553
+ }
554
+ }
555
+ resetValidateConnection(targetHandle) {
556
+ targetHandle.state.set('idle');
557
+ // drop back to start status
558
+ const status = this.statusService.status();
559
+ if (status.state === 'connection-validation') {
560
+ this.statusService.setConnectionStartStatus(status.payload.source, status.payload.sourceHandle);
561
+ }
562
+ }
563
+ endConnection(handle) {
564
+ const status = this.statusService.status();
565
+ if (status.state === 'connection-validation') {
566
+ const source = status.payload.source;
567
+ const sourceHandle = status.payload.sourceHandle;
568
+ const target = status.payload.target;
569
+ const targetHandle = status.payload.targetHandle;
570
+ batchStatusChanges(
571
+ // call to create connection
572
+ () => this.statusService.setConnectionEndStatus(source, target, sourceHandle, targetHandle),
573
+ // when connection created, we need go back to idle status
574
+ () => this.statusService.setIdleStatus());
575
+ }
446
576
  }
447
577
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
448
578
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[connectionController]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
@@ -1040,6 +1170,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1040
1170
  type: Injectable
1041
1171
  }] });
1042
1172
 
1173
+ class RootPointerDirective {
1174
+ constructor() {
1175
+ this.host = inject(ElementRef).nativeElement;
1176
+ this.initialTouch$ = new Subject();
1177
+ // TODO: do not emit if mouse not down
1178
+ this.mouseMovement$ = fromEvent(this.host, 'mousemove').pipe(map(event => ({
1179
+ x: event.clientX,
1180
+ y: event.clientY,
1181
+ originalEvent: event
1182
+ })), observeOn(animationFrameScheduler), share());
1183
+ this.touchMovement$ = merge(this.initialTouch$, fromEvent(this.host, 'touchmove')).pipe(tap((event) => event.preventDefault()), map((originalEvent) => {
1184
+ const x = originalEvent.touches[0]?.clientX ?? 0;
1185
+ const y = originalEvent.touches[0]?.clientY ?? 0;
1186
+ const target = document.elementFromPoint(x, y);
1187
+ return { x, y, target, originalEvent };
1188
+ }), observeOn(animationFrameScheduler), share());
1189
+ this.touchEnd$ = fromEvent(this.host, 'touchend').pipe(map((originalEvent) => {
1190
+ const x = originalEvent.changedTouches[0]?.clientX ?? 0;
1191
+ const y = originalEvent.changedTouches[0]?.clientY ?? 0;
1192
+ const target = document.elementFromPoint(x, y);
1193
+ return { x, y, target, originalEvent };
1194
+ }), share());
1195
+ this.pointerMovement$ = merge(this.mouseMovement$, this.touchMovement$);
1196
+ }
1197
+ /**
1198
+ * We should know when user started a touch in order to not
1199
+ * show old touch position when connection creation is started
1200
+ */
1201
+ setInitialTouch(event) {
1202
+ this.initialTouch$.next(event);
1203
+ }
1204
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1205
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootPointerDirective, selector: "svg[rootPointer]", ngImport: i0 }); }
1206
+ }
1207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, decorators: [{
1208
+ type: Directive,
1209
+ args: [{ selector: 'svg[rootPointer]' }]
1210
+ }] });
1211
+
1212
+ class SpacePointContextDirective {
1213
+ constructor() {
1214
+ this.pointerMovementDirective = inject(RootPointerDirective);
1215
+ this.rootSvg = inject(RootSvgReferenceDirective).element;
1216
+ this.host = inject(ElementRef).nativeElement;
1217
+ /**
1218
+ * Signal with current mouse position in svg space
1219
+ */
1220
+ this.svgCurrentSpacePoint = computed(() => {
1221
+ const movement = this.pointerMovement();
1222
+ if (!movement) {
1223
+ return { x: 0, y: 0 };
1224
+ }
1225
+ return this.documentPointToFlowPoint({
1226
+ x: movement.x,
1227
+ y: movement.y
1228
+ });
1229
+ });
1230
+ this.pointerMovement = toSignal(this.pointerMovementDirective.pointerMovement$);
1231
+ }
1232
+ documentPointToFlowPoint(documentPoint) {
1233
+ const point = this.rootSvg.createSVGPoint();
1234
+ point.x = documentPoint.x;
1235
+ point.y = documentPoint.y;
1236
+ return point.matrixTransform(this.host.getScreenCTM().inverse());
1237
+ }
1238
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1239
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SpacePointContextDirective, selector: "g[spacePointContext]", ngImport: i0 }); }
1240
+ }
1241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, decorators: [{
1242
+ type: Directive,
1243
+ args: [{ selector: 'g[spacePointContext]' }]
1244
+ }] });
1245
+
1043
1246
  class HandleService {
1044
1247
  constructor() {
1045
1248
  this.node = signal(null);
@@ -1063,6 +1266,44 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1063
1266
  type: Injectable
1064
1267
  }] });
1065
1268
 
1269
+ function resizable(elems) {
1270
+ return new Observable((subscriber) => {
1271
+ let ro = new ResizeObserver((entries) => {
1272
+ subscriber.next(entries);
1273
+ });
1274
+ elems.forEach(e => ro.observe(e));
1275
+ return () => ro.disconnect();
1276
+ });
1277
+ }
1278
+
1279
+ function InjectionContext(target, key, descriptor) {
1280
+ const originalMethod = descriptor.value;
1281
+ descriptor.value = function (...args) {
1282
+ if (implementsWithInjector(this)) {
1283
+ return runInInjectionContext(this.injector, () => originalMethod.apply(this, args));
1284
+ }
1285
+ else {
1286
+ throw new Error('Class that contains decorated method must extends WithInjectorDirective class');
1287
+ }
1288
+ };
1289
+ // Return the modified descriptor
1290
+ return descriptor;
1291
+ }
1292
+ const implementsWithInjector = (instance) => {
1293
+ return 'injector' in instance && 'get' in instance.injector;
1294
+ };
1295
+
1296
+ function Microtask(target, key, descriptor) {
1297
+ const originalMethod = descriptor.value;
1298
+ descriptor.value = function (...args) {
1299
+ queueMicrotask(() => {
1300
+ originalMethod?.apply(this, args);
1301
+ });
1302
+ };
1303
+ // Return the modified descriptor
1304
+ return descriptor;
1305
+ }
1306
+
1066
1307
  class HandleModel {
1067
1308
  constructor(rawHandle, parentNode) {
1068
1309
  this.rawHandle = rawHandle;
@@ -1138,43 +1379,46 @@ class HandleModel {
1138
1379
  }
1139
1380
  }
1140
1381
 
1141
- function resizable(elems) {
1142
- return new Observable((subscriber) => {
1143
- let ro = new ResizeObserver((entries) => {
1144
- subscriber.next(entries);
1145
- });
1146
- elems.forEach(e => ro.observe(e));
1147
- return () => ro.disconnect();
1148
- });
1149
- }
1150
-
1151
- function InjectionContext(target, key, descriptor) {
1152
- const originalMethod = descriptor.value;
1153
- descriptor.value = function (...args) {
1154
- if (implementsWithInjector(this)) {
1155
- return runInInjectionContext(this.injector, () => originalMethod.apply(this, args));
1156
- }
1157
- else {
1158
- throw new Error('Class that contains decorated method must extends WithInjectorDirective class');
1159
- }
1160
- };
1161
- // Return the modified descriptor
1162
- return descriptor;
1163
- }
1164
- const implementsWithInjector = (instance) => {
1165
- return 'injector' in instance && 'get' in instance.injector;
1166
- };
1167
-
1168
- function Microtask(target, key, descriptor) {
1169
- const originalMethod = descriptor.value;
1170
- descriptor.value = function (...args) {
1171
- queueMicrotask(() => {
1172
- originalMethod?.apply(this, args);
1173
- });
1174
- };
1175
- // Return the modified descriptor
1176
- return descriptor;
1382
+ class HandleComponent {
1383
+ constructor() {
1384
+ this.injector = inject(Injector);
1385
+ this.handleService = inject(HandleService);
1386
+ this.element = inject(ElementRef).nativeElement;
1387
+ }
1388
+ ngOnInit() {
1389
+ this.model = new HandleModel({
1390
+ position: this.position,
1391
+ type: this.type,
1392
+ id: this.id,
1393
+ parentReference: this.element.parentElement,
1394
+ template: this.template
1395
+ }, this.handleService.node());
1396
+ this.handleService.createHandle(this.model);
1397
+ requestAnimationFrame(() => this.model.updateParent());
1398
+ }
1399
+ ngOnDestroy() {
1400
+ this.handleService.destroyHandle(this.model);
1401
+ }
1402
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1403
+ 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 }); }
1177
1404
  }
1405
+ __decorate([
1406
+ InjectionContext
1407
+ ], HandleComponent.prototype, "ngOnInit", null);
1408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, decorators: [{
1409
+ type: Component,
1410
+ args: [{ selector: 'handle', changeDetection: ChangeDetectionStrategy.OnPush, template: "" }]
1411
+ }], propDecorators: { position: [{
1412
+ type: Input,
1413
+ args: [{ required: true }]
1414
+ }], type: [{
1415
+ type: Input,
1416
+ args: [{ required: true }]
1417
+ }], id: [{
1418
+ type: Input
1419
+ }], template: [{
1420
+ type: Input
1421
+ }], ngOnInit: [] } });
1178
1422
 
1179
1423
  class HandleSizeControllerDirective {
1180
1424
  constructor() {
@@ -1212,45 +1456,6 @@ function getChildStrokeWidth(element) {
1212
1456
  return 0;
1213
1457
  }
1214
1458
 
1215
- class RootPointerDirective {
1216
- constructor() {
1217
- this.host = inject(ElementRef).nativeElement;
1218
- this.initialTouch$ = new Subject();
1219
- // TODO: do not emit if mouse not down
1220
- this.mouseMovement$ = fromEvent(this.host, 'mousemove').pipe(map(event => ({
1221
- x: event.clientX,
1222
- y: event.clientY,
1223
- originalEvent: event
1224
- })), observeOn(animationFrameScheduler), share());
1225
- this.touchMovement$ = merge(this.initialTouch$, fromEvent(this.host, 'touchmove')).pipe(tap((event) => event.preventDefault()), map((originalEvent) => {
1226
- const x = originalEvent.touches[0]?.clientX ?? 0;
1227
- const y = originalEvent.touches[0]?.clientY ?? 0;
1228
- const target = document.elementFromPoint(x, y);
1229
- return { x, y, target, originalEvent };
1230
- }), observeOn(animationFrameScheduler), share());
1231
- this.touchEnd$ = fromEvent(this.host, 'touchend').pipe(map((originalEvent) => {
1232
- const x = originalEvent.changedTouches[0]?.clientX ?? 0;
1233
- const y = originalEvent.changedTouches[0]?.clientY ?? 0;
1234
- const target = document.elementFromPoint(x, y);
1235
- return { x, y, target, originalEvent };
1236
- }), share());
1237
- this.pointerMovement$ = merge(this.mouseMovement$, this.touchMovement$);
1238
- }
1239
- /**
1240
- * We should know when user started a touch in order to not
1241
- * show old touch position when connection creation is started
1242
- */
1243
- setInitialTouch(event) {
1244
- this.initialTouch$.next(event);
1245
- }
1246
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1247
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootPointerDirective, selector: "svg[rootPointer]", ngImport: i0 }); }
1248
- }
1249
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, decorators: [{
1250
- type: Directive,
1251
- args: [{ selector: 'svg[rootPointer]' }]
1252
- }] });
1253
-
1254
1459
  class PointerDirective {
1255
1460
  constructor() {
1256
1461
  this.hostElement = inject(ElementRef).nativeElement;
@@ -1336,28 +1541,27 @@ class NodeComponent {
1336
1541
  this.handleService = inject(HandleService);
1337
1542
  this.draggableService = inject(DraggableService);
1338
1543
  this.flowStatusService = inject(FlowStatusService);
1339
- this.flowEntitiesService = inject(FlowEntitiesService);
1340
1544
  this.nodeRenderingService = inject(NodeRenderingService);
1341
1545
  this.flowSettingsService = inject(FlowSettingsService);
1342
1546
  this.selectionService = inject(SelectionService);
1343
1547
  this.hostRef = inject(ElementRef);
1548
+ this.connectionController = inject(ConnectionControllerDirective);
1344
1549
  this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
1345
1550
  this.flowStatusService.status().state === 'connection-validation');
1346
1551
  this.styleWidth = computed(() => `${this.nodeModel.size().width}px`);
1347
1552
  this.styleHeight = computed(() => `${this.nodeModel.size().height}px`);
1348
- this.subscription = new Subscription();
1349
1553
  }
1350
1554
  ngOnInit() {
1351
1555
  this.handleService.node.set(this.nodeModel);
1352
1556
  this.draggableService.toggleDraggable(this.hostRef.nativeElement, this.nodeModel);
1353
- const sub = this.nodeModel.handles$
1354
- .pipe(switchMap((handles) => resizable(handles.map(h => h.parentReference))
1355
- .pipe(map(() => handles))), tap((handles) => handles.forEach(h => h.updateParent())))
1557
+ this.nodeModel.handles$
1558
+ .pipe(switchMap((handles) => resizable(handles.map(h => h.parentReference)).pipe(map(() => handles))), tap((handles) => {
1559
+ // TODO (performance) inspect how to avoid calls of this when flow initially rendered
1560
+ handles.forEach(h => h.updateParent());
1561
+ }), takeUntilDestroyed())
1356
1562
  .subscribe();
1357
- this.subscription.add(sub);
1358
1563
  }
1359
1564
  ngAfterViewInit() {
1360
- this.setInitialHandles();
1361
1565
  if (this.nodeModel.node.type === 'default') {
1362
1566
  this.nodeModel.size.set({
1363
1567
  width: this.nodeModel.node.width ?? NodeModel.defaultTypeSize.width,
@@ -1365,69 +1569,30 @@ class NodeComponent {
1365
1569
  });
1366
1570
  }
1367
1571
  if (this.nodeModel.node.type === 'html-template' || this.nodeModel.isComponentType) {
1368
- const sub = resizable([this.htmlWrapperRef.nativeElement])
1572
+ resizable([this.htmlWrapperRef.nativeElement])
1369
1573
  .pipe(startWith(null), tap(() => {
1370
1574
  const width = this.htmlWrapperRef.nativeElement.clientWidth;
1371
1575
  const height = this.htmlWrapperRef.nativeElement.clientHeight;
1372
1576
  this.nodeModel.size.set({ width, height });
1373
- })).subscribe();
1374
- this.subscription.add(sub);
1577
+ }), takeUntilDestroyed()).subscribe();
1375
1578
  }
1376
1579
  }
1377
1580
  ngOnDestroy() {
1378
1581
  this.draggableService.destroy(this.hostRef.nativeElement);
1379
- this.subscription.unsubscribe();
1380
1582
  }
1381
1583
  startConnection(event, handle) {
1382
1584
  // ignore drag by stopping propagation
1383
1585
  event.stopPropagation();
1384
- this.flowStatusService.setConnectionStartStatus(this.nodeModel, handle);
1586
+ this.connectionController.startConnection(handle);
1385
1587
  }
1386
- endConnection() {
1387
- const status = this.flowStatusService.status();
1388
- if (status.state === 'connection-validation') {
1389
- const sourceNode = status.payload.sourceNode;
1390
- const targetNode = this.nodeModel;
1391
- const sourceHandle = status.payload.sourceHandle;
1392
- const targetHandle = status.payload.targetHandle;
1393
- batchStatusChanges(
1394
- // call to create connection
1395
- () => this.flowStatusService.setConnectionEndStatus(sourceNode, targetNode, sourceHandle, targetHandle),
1396
- // when connection created, we need go back to idle status
1397
- () => this.flowStatusService.setIdleStatus());
1398
- }
1588
+ validateConnection(handle) {
1589
+ this.connectionController.validateConnection(handle);
1399
1590
  }
1400
- /**
1401
- * TODO srp
1402
- */
1403
- validateTargetHandle(targetHandle) {
1404
- const status = this.flowStatusService.status();
1405
- if (status.state === 'connection-start') {
1406
- const sourceNode = status.payload.sourceNode;
1407
- const sourceHandle = status.payload.sourceHandle;
1408
- const source = sourceNode.node.id;
1409
- const targetNode = this.nodeModel;
1410
- const target = targetNode.node.id;
1411
- const valid = this.flowEntitiesService.connection().validator({
1412
- source,
1413
- target,
1414
- sourceHandle: sourceHandle.rawHandle.id,
1415
- targetHandle: targetHandle.rawHandle.id
1416
- });
1417
- targetHandle.state.set(valid ? 'valid' : 'invalid');
1418
- this.flowStatusService.setConnectionValidationStatus(valid, sourceNode, targetNode, sourceHandle, targetHandle);
1419
- }
1591
+ resetValidateConnection(targetHandle) {
1592
+ this.connectionController.resetValidateConnection(targetHandle);
1420
1593
  }
1421
- /**
1422
- * TODO srp
1423
- */
1424
- resetValidateTargetHandle(targetHandle) {
1425
- targetHandle.state.set('idle');
1426
- // drop back to start status
1427
- const status = this.flowStatusService.status();
1428
- if (status.state === 'connection-validation') {
1429
- this.flowStatusService.setConnectionStartStatus(status.payload.sourceNode, status.payload.sourceHandle);
1430
- }
1594
+ endConnection(handle) {
1595
+ this.connectionController.endConnection(handle);
1431
1596
  }
1432
1597
  pullNode() {
1433
1598
  this.nodeRenderingService.pullNode(this.nodeModel);
@@ -1437,32 +1602,20 @@ class NodeComponent {
1437
1602
  this.selectionService.select(this.nodeModel);
1438
1603
  }
1439
1604
  }
1440
- setInitialHandles() {
1441
- if (this.nodeModel.node.type === 'default') {
1442
- this.handleService.createHandle(new HandleModel({
1443
- position: this.nodeModel.sourcePosition(),
1444
- type: 'source',
1445
- parentReference: this.htmlWrapperRef.nativeElement
1446
- }, this.nodeModel));
1447
- this.handleService.createHandle(new HandleModel({
1448
- position: this.nodeModel.targetPosition(),
1449
- type: 'target',
1450
- parentReference: this.htmlWrapperRef.nativeElement
1451
- }, this.nodeModel));
1452
- }
1453
- }
1454
1605
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1455
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeHtmlTemplate: "nodeHtmlTemplate" }, providers: [HandleService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], ngImport: i0, template: "<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 [innerHTML]=\"nodeModel.node.text ?? ''\"\n ></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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 #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"handle.rawHandle.type === 'target' && showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(); resetValidateTargetHandle(handle)\"\n (pointerOver)=\"validateTargetHandle(handle)\"\n (pointerOut)=\"resetValidateTargetHandle(handle)\"\n />\n</ng-container>\n", styles: [".wrapper{width:max-content}.magnet{opacity:0}.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-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: "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 }); }
1606
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeHtmlTemplate: "nodeHtmlTemplate" }, providers: [HandleService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], ngImport: i0, template: "<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.node.text ?? ''\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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 #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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: [".wrapper{width:max-content}.magnet{opacity:0}.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-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: "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 }); }
1456
1607
  }
1457
1608
  __decorate([
1458
- Microtask
1459
- ], NodeComponent.prototype, "ngAfterViewInit", null);
1609
+ InjectionContext
1610
+ ], NodeComponent.prototype, "ngOnInit", null);
1460
1611
  __decorate([
1612
+ Microtask // TODO (performance) check if we need microtask here
1613
+ ,
1461
1614
  InjectionContext
1462
- ], NodeComponent.prototype, "setInitialHandles", null);
1615
+ ], NodeComponent.prototype, "ngAfterViewInit", null);
1463
1616
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
1464
1617
  type: Component,
1465
- args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService], template: "<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 [innerHTML]=\"nodeModel.node.text ?? ''\"\n ></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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 #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"handle.rawHandle.type === 'source' ? startConnection($event, handle) : null\"\n (pointerEnd)=\"handle.rawHandle.type === 'target' ? endConnection() : null\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"handle.rawHandle.type === 'target' && showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(); resetValidateTargetHandle(handle)\"\n (pointerOver)=\"validateTargetHandle(handle)\"\n (pointerOut)=\"resetValidateTargetHandle(handle)\"\n />\n</ng-container>\n", styles: [".wrapper{width:max-content}.magnet{opacity:0}.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-handle{stroke:#fff;fill:#1b262c}\n"] }]
1618
+ args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService], template: "<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.node.text ?? ''\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (mousedown)=\"pullNode()\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngTemplateOutlet]=\"nodeHtmlTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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 #htmlWrapper class=\"wrapper\">\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\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: [".wrapper{width:max-content}.magnet{opacity:0}.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-handle{stroke:#fff;fill:#1b262c}\n"] }]
1466
1619
  }], propDecorators: { nodeModel: [{
1467
1620
  type: Input
1468
1621
  }], nodeHtmlTemplate: [{
@@ -1473,7 +1626,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1473
1626
  }], htmlWrapperRef: [{
1474
1627
  type: ViewChild,
1475
1628
  args: ['htmlWrapper']
1476
- }], ngAfterViewInit: [], setInitialHandles: [] } });
1629
+ }], ngOnInit: [], ngAfterViewInit: [] } });
1477
1630
 
1478
1631
  class EdgeLabelComponent {
1479
1632
  constructor() {
@@ -1578,34 +1731,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
1578
1731
  type: Input
1579
1732
  }] } });
1580
1733
 
1581
- class SpacePointContextDirective {
1582
- constructor() {
1583
- this.pointerMovementDirective = inject(RootPointerDirective);
1584
- this.rootSvg = inject(RootSvgReferenceDirective).element;
1585
- this.host = inject(ElementRef).nativeElement;
1586
- /**
1587
- * Signal with current mouse position in svg space
1588
- */
1589
- this.svgCurrentSpacePoint = computed(() => {
1590
- const movement = this.pointerMovement();
1591
- if (!movement) {
1592
- return { x: 0, y: 0 };
1593
- }
1594
- const point = this.rootSvg.createSVGPoint();
1595
- point.x = movement.x;
1596
- point.y = movement.y;
1597
- return point.matrixTransform(this.host.getScreenCTM().inverse());
1598
- });
1599
- this.pointerMovement = toSignal(this.pointerMovementDirective.pointerMovement$);
1600
- }
1601
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1602
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SpacePointContextDirective, selector: "g[spacePointContext]", ngImport: i0 }); }
1603
- }
1604
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, decorators: [{
1605
- type: Directive,
1606
- args: [{ selector: 'g[spacePointContext]' }]
1607
- }] });
1608
-
1609
1734
  class ConnectionComponent {
1610
1735
  constructor() {
1611
1736
  this.flowStatusService = inject(FlowStatusService);
@@ -1643,7 +1768,7 @@ class ConnectionComponent {
1643
1768
  return null;
1644
1769
  });
1645
1770
  this.markerUrl = computed(() => {
1646
- const marker = this.model.connection.marker;
1771
+ const marker = this.model.settings.marker;
1647
1772
  if (marker) {
1648
1773
  return `url(#${hashCode(JSON.stringify(marker))})`;
1649
1774
  }
@@ -1784,11 +1909,11 @@ class BackgroundComponent {
1784
1909
  });
1785
1910
  }
1786
1911
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BackgroundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1787
- 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"] }] }); }
1912
+ 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 }); }
1788
1913
  }
1789
1914
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BackgroundComponent, decorators: [{
1790
1915
  type: Component,
1791
- args: [{ selector: 'g[background]', 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" }]
1916
+ 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" }]
1792
1917
  }], ctorParameters: function () { return []; }, propDecorators: { background: [{
1793
1918
  type: Input,
1794
1919
  args: [{ required: true, transform }]
@@ -2022,6 +2147,12 @@ class VflowComponent {
2022
2147
  getDetachedEdges() {
2023
2148
  return this.flowEntitiesService.getDetachedEdges().map(e => e.edge);
2024
2149
  }
2150
+ /**
2151
+ * Convert point received from document to point on the flow
2152
+ */
2153
+ documentPointToFlowPoint(point) {
2154
+ return this.spacePointContext.documentPointToFlowPoint(point);
2155
+ }
2025
2156
  // #endregion
2026
2157
  trackNodes(idx, { node }) {
2027
2158
  return node;
@@ -2041,7 +2172,7 @@ class VflowComponent {
2041
2172
  SelectionService,
2042
2173
  FlowSettingsService,
2043
2174
  ComponentEventBusService
2044
- ], queries: [{ propertyName: "nodeHtmlDirective", first: true, predicate: NodeHtmlTemplateDirective, 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 }], 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.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 class=\"root-svg\"\n #flow\n [attr.width]=\"flowWidth()\"\n [attr.height]=\"flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g [background]=\"background\"/>\n\n <svg:g\n mapContext\n spacePointContext\n [minZoom]=\"minZoom\"\n [maxZoom]=\"maxZoom\"\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 [nodeHtmlTemplate]=\"nodeHtmlDirective?.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", "nodeHtmlTemplate"] }, { 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]", inputs: ["minZoom", "maxZoom"] }, { kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2175
+ ], queries: [{ propertyName: "nodeHtmlDirective", first: true, predicate: NodeHtmlTemplateDirective, 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.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 class=\"root-svg\"\n #flow\n [attr.width]=\"flowWidth()\"\n [attr.height]=\"flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g [background]=\"background\"/>\n\n <svg:g\n mapContext\n spacePointContext\n [minZoom]=\"minZoom\"\n [maxZoom]=\"maxZoom\"\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 [nodeHtmlTemplate]=\"nodeHtmlDirective?.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", "nodeHtmlTemplate"] }, { 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]", inputs: ["minZoom", "maxZoom"] }, { kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2045
2176
  }
2046
2177
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
2047
2178
  type: Component,
@@ -2097,49 +2228,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2097
2228
  }], mapContext: [{
2098
2229
  type: ViewChild,
2099
2230
  args: [MapContextDirective]
2231
+ }], spacePointContext: [{
2232
+ type: ViewChild,
2233
+ args: [SpacePointContextDirective]
2100
2234
  }] } });
2101
2235
 
2102
- class HandleComponent {
2103
- constructor() {
2104
- this.injector = inject(Injector);
2105
- this.handleService = inject(HandleService);
2106
- this.element = inject(ElementRef).nativeElement;
2107
- }
2108
- ngOnInit() {
2109
- this.model = new HandleModel({
2110
- position: this.position,
2111
- type: this.type,
2112
- id: this.id,
2113
- parentReference: this.element.parentElement,
2114
- template: this.template
2115
- }, this.handleService.node());
2116
- this.handleService.createHandle(this.model);
2117
- queueMicrotask(() => this.model.updateParent());
2118
- }
2119
- ngOnDestroy() {
2120
- this.handleService.destroyHandle(this.model);
2121
- }
2122
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2123
- 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: "" }); }
2124
- }
2125
- __decorate([
2126
- InjectionContext
2127
- ], HandleComponent.prototype, "ngOnInit", null);
2128
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, decorators: [{
2129
- type: Component,
2130
- args: [{ selector: 'handle', template: "" }]
2131
- }], propDecorators: { position: [{
2132
- type: Input,
2133
- args: [{ required: true }]
2134
- }], type: [{
2135
- type: Input,
2136
- args: [{ required: true }]
2137
- }], id: [{
2138
- type: Input
2139
- }], template: [{
2140
- type: Input
2141
- }], ngOnInit: [] } });
2142
-
2143
2236
  class SelectableDirective {
2144
2237
  constructor() {
2145
2238
  this.flowSettingsService = inject(FlowSettingsService);