ngx-vflow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +24 -0
  2. package/esm2022/lib/vflow/components/connection/connection.component.mjs +100 -0
  3. package/esm2022/lib/vflow/components/defs/defs.component.mjs +19 -0
  4. package/esm2022/lib/vflow/components/edge/edge.component.mjs +42 -0
  5. package/esm2022/lib/vflow/components/edge-label/edge-label.component.mjs +58 -0
  6. package/esm2022/lib/vflow/components/node/node.component.mjs +146 -0
  7. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +183 -0
  8. package/esm2022/lib/vflow/directives/changes-controller.directive.mjs +34 -0
  9. package/esm2022/lib/vflow/directives/connection-controller.directive.mjs +36 -0
  10. package/esm2022/lib/vflow/directives/map-context.directive.mjs +62 -0
  11. package/esm2022/lib/vflow/directives/reference.directive.mjs +16 -0
  12. package/esm2022/lib/vflow/directives/root-svg-context.directive.mjs +26 -0
  13. package/esm2022/lib/vflow/directives/space-point-context.directive.mjs +29 -0
  14. package/esm2022/lib/vflow/directives/template.directive.mjs +58 -0
  15. package/esm2022/lib/vflow/interfaces/connection-settings.interface.mjs +2 -0
  16. package/esm2022/lib/vflow/interfaces/connection.interface.mjs +2 -0
  17. package/esm2022/lib/vflow/interfaces/edge-label.interface.mjs +2 -0
  18. package/esm2022/lib/vflow/interfaces/edge.interface.mjs +2 -0
  19. package/esm2022/lib/vflow/interfaces/handle-positions.interface.mjs +2 -0
  20. package/esm2022/lib/vflow/interfaces/marker.interface.mjs +2 -0
  21. package/esm2022/lib/vflow/interfaces/node.interface.mjs +2 -0
  22. package/esm2022/lib/vflow/interfaces/path-data.interface.mjs +2 -0
  23. package/esm2022/lib/vflow/interfaces/point.interface.mjs +2 -0
  24. package/esm2022/lib/vflow/interfaces/template-context.interface.mjs +2 -0
  25. package/esm2022/lib/vflow/interfaces/viewport.interface.mjs +2 -0
  26. package/esm2022/lib/vflow/math/edge-path/bezier-path.mjs +77 -0
  27. package/esm2022/lib/vflow/math/edge-path/straigh-path.mjs +18 -0
  28. package/esm2022/lib/vflow/math/point-on-line-by-ratio.mjs +12 -0
  29. package/esm2022/lib/vflow/models/connection.model.mjs +9 -0
  30. package/esm2022/lib/vflow/models/edge-label.model.mjs +8 -0
  31. package/esm2022/lib/vflow/models/edge.model.mjs +30 -0
  32. package/esm2022/lib/vflow/models/flow.model.mjs +16 -0
  33. package/esm2022/lib/vflow/models/node.model.mjs +90 -0
  34. package/esm2022/lib/vflow/services/draggable.service.mjs +66 -0
  35. package/esm2022/lib/vflow/services/edge-changes.service.mjs +38 -0
  36. package/esm2022/lib/vflow/services/flow-entities.service.mjs +49 -0
  37. package/esm2022/lib/vflow/services/flow-status.service.mjs +39 -0
  38. package/esm2022/lib/vflow/services/node-changes.service.mjs +32 -0
  39. package/esm2022/lib/vflow/services/viewport.service.mjs +32 -0
  40. package/esm2022/lib/vflow/types/edge-change.type.mjs +2 -0
  41. package/esm2022/lib/vflow/types/node-change.type.mjs +2 -0
  42. package/esm2022/lib/vflow/types/position.type.mjs +2 -0
  43. package/esm2022/lib/vflow/types/using-points.type.mjs +2 -0
  44. package/esm2022/lib/vflow/types/viewport-change-type.type.mjs +2 -0
  45. package/esm2022/lib/vflow/utils/add-nodes-to-edges.mjs +11 -0
  46. package/esm2022/lib/vflow/utils/hash.mjs +7 -0
  47. package/esm2022/lib/vflow/utils/is-defined.mjs +4 -0
  48. package/esm2022/lib/vflow/utils/reference-keeper.mjs +31 -0
  49. package/esm2022/lib/vflow/utils/round.mjs +2 -0
  50. package/esm2022/lib/vflow/vflow.module.mjs +68 -0
  51. package/esm2022/ngx-vflow.mjs +5 -0
  52. package/esm2022/public-api.mjs +23 -0
  53. package/fesm2022/ngx-vflow.mjs +1332 -0
  54. package/fesm2022/ngx-vflow.mjs.map +1 -0
  55. package/index.d.ts +5 -0
  56. package/lib/vflow/components/connection/connection.component.d.ts +20 -0
  57. package/lib/vflow/components/defs/defs.component.d.ts +8 -0
  58. package/lib/vflow/components/edge/edge.component.d.ts +16 -0
  59. package/lib/vflow/components/edge-label/edge-label.component.d.ts +31 -0
  60. package/lib/vflow/components/node/node.component.d.ts +53 -0
  61. package/lib/vflow/components/vflow/vflow.component.d.ts +73 -0
  62. package/lib/vflow/directives/changes-controller.directive.d.ts +16 -0
  63. package/lib/vflow/directives/connection-controller.directive.d.ts +11 -0
  64. package/lib/vflow/directives/map-context.directive.d.ts +19 -0
  65. package/lib/vflow/directives/reference.directive.d.ts +6 -0
  66. package/lib/vflow/directives/root-svg-context.directive.d.ts +7 -0
  67. package/lib/vflow/directives/space-point-context.directive.d.ts +14 -0
  68. package/lib/vflow/directives/template.directive.d.ts +28 -0
  69. package/lib/vflow/interfaces/connection-settings.interface.d.ts +10 -0
  70. package/lib/vflow/interfaces/connection.interface.d.ts +4 -0
  71. package/lib/vflow/interfaces/edge-label.interface.d.ts +6 -0
  72. package/lib/vflow/interfaces/edge.interface.d.ts +18 -0
  73. package/lib/vflow/interfaces/handle-positions.interface.d.ts +5 -0
  74. package/lib/vflow/interfaces/marker.interface.d.ts +9 -0
  75. package/lib/vflow/interfaces/node.interface.d.ts +16 -0
  76. package/lib/vflow/interfaces/path-data.interface.d.ts +8 -0
  77. package/lib/vflow/interfaces/point.interface.d.ts +4 -0
  78. package/lib/vflow/interfaces/template-context.interface.d.ts +10 -0
  79. package/lib/vflow/interfaces/viewport.interface.d.ts +9 -0
  80. package/lib/vflow/math/edge-path/bezier-path.d.ts +5 -0
  81. package/lib/vflow/math/edge-path/straigh-path.d.ts +4 -0
  82. package/lib/vflow/math/point-on-line-by-ratio.d.ts +7 -0
  83. package/lib/vflow/models/connection.model.d.ts +9 -0
  84. package/lib/vflow/models/edge-label.model.d.ts +9 -0
  85. package/lib/vflow/models/edge.model.d.ts +17 -0
  86. package/lib/vflow/models/flow.model.d.ts +14 -0
  87. package/lib/vflow/models/node.model.d.ts +70 -0
  88. package/lib/vflow/services/draggable.service.d.ts +32 -0
  89. package/lib/vflow/services/edge-changes.service.d.ts +22 -0
  90. package/lib/vflow/services/flow-entities.service.d.ts +15 -0
  91. package/lib/vflow/services/flow-status.service.d.ts +43 -0
  92. package/lib/vflow/services/node-changes.service.d.ts +26 -0
  93. package/lib/vflow/services/viewport.service.d.ts +24 -0
  94. package/lib/vflow/types/edge-change.type.d.ts +14 -0
  95. package/lib/vflow/types/node-change.type.d.ts +16 -0
  96. package/lib/vflow/types/position.type.d.ts +1 -0
  97. package/lib/vflow/types/using-points.type.d.ts +5 -0
  98. package/lib/vflow/types/viewport-change-type.type.d.ts +3 -0
  99. package/lib/vflow/utils/add-nodes-to-edges.d.ts +3 -0
  100. package/lib/vflow/utils/hash.d.ts +1 -0
  101. package/lib/vflow/utils/is-defined.d.ts +1 -0
  102. package/lib/vflow/utils/reference-keeper.d.ts +14 -0
  103. package/lib/vflow/utils/round.d.ts +1 -0
  104. package/lib/vflow/vflow.module.d.ts +18 -0
  105. package/package.json +25 -0
  106. package/public-api.d.ts +18 -0
@@ -0,0 +1,1332 @@
1
+ import * as i1 from '@angular/common';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { signal, Injectable, inject, ElementRef, Directive, effect, Input, TemplateRef, computed, EventEmitter, Output, untracked, Component, ChangeDetectionStrategy, ViewChild, HostListener, Injector, runInInjectionContext, ContentChild, NgModule } from '@angular/core';
5
+ import { select } from 'd3-selection';
6
+ import { zoomIdentity, zoom } from 'd3-zoom';
7
+ import { drag } from 'd3-drag';
8
+ import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
9
+ import { switchMap, merge, skip, map, pairwise, filter, observeOn, asyncScheduler, tap, fromEvent } from 'rxjs';
10
+ import { path } from 'd3-path';
11
+
12
+ class ViewportService {
13
+ constructor() {
14
+ /**
15
+ * Internal signal that accepts value from user by lib api
16
+ * When this signal changes, lib sets new view state and update readableViewport signal
17
+ */
18
+ this.writableViewport = signal({
19
+ changeType: 'initial',
20
+ state: ViewportService.getDefaultViewport()
21
+ });
22
+ /**
23
+ * Public signal with viewport state. User can directly read from this signal. It's updated by:
24
+ * - user events on flow
25
+ * - writableViewport signal
26
+ */
27
+ this.readableViewport = signal(ViewportService.getDefaultViewport());
28
+ }
29
+ /**
30
+ * The default value used by d3, just copy it here
31
+ *
32
+ * @returns default viewport value
33
+ */
34
+ static getDefaultViewport() { return { zoom: 1, x: 0, y: 0 }; }
35
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
36
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService }); }
37
+ }
38
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService, decorators: [{
39
+ type: Injectable
40
+ }] });
41
+
42
+ function isDefined(data) {
43
+ return data !== undefined;
44
+ }
45
+
46
+ class RootSvgReferenceDirective {
47
+ constructor() {
48
+ this.element = inject(ElementRef).nativeElement;
49
+ }
50
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgReferenceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
51
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]", ngImport: i0 }); }
52
+ }
53
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgReferenceDirective, decorators: [{
54
+ type: Directive,
55
+ args: [{
56
+ selector: 'svg[rootSvgRef]'
57
+ }]
58
+ }] });
59
+
60
+ class MapContextDirective {
61
+ constructor() {
62
+ this.rootSvg = inject(RootSvgReferenceDirective).element;
63
+ this.host = inject(ElementRef).nativeElement;
64
+ this.viewportService = inject(ViewportService);
65
+ this.rootSvgSelection = select(this.rootSvg);
66
+ this.zoomableSelection = select(this.host);
67
+ // under the hood this effect triggers handleZoom, so error throws without this flag
68
+ // TODO: hack with timer fixes wrong node scaling (handle positions not matched with content size)
69
+ this.manualViewportChangeEffect = effect(() => setTimeout(() => {
70
+ const viewport = this.viewportService.writableViewport();
71
+ const state = viewport.state;
72
+ if (viewport.changeType === 'initial') {
73
+ return;
74
+ }
75
+ // If only zoom provided
76
+ if (isDefined(state.zoom) && (!isDefined(state.x) && !isDefined(state.y))) {
77
+ this.rootSvgSelection.call(this.zoomBehavior.scaleTo, state.zoom);
78
+ return;
79
+ }
80
+ // If only pan provided
81
+ if ((isDefined(state.x) && isDefined(state.y)) && !isDefined(state.zoom)) {
82
+ this.rootSvgSelection.call(this.zoomBehavior.translateTo, state.x, state.y);
83
+ return;
84
+ }
85
+ // If whole viewort state provided
86
+ if (isDefined(state.x) && isDefined(state.y) && isDefined(state.zoom)) {
87
+ this.rootSvgSelection.call(this.zoomBehavior.transform, zoomIdentity.translate(state.x, state.y).scale(state.zoom));
88
+ return;
89
+ }
90
+ }), { allowSignalWrites: true });
91
+ this.handleZoom = ({ transform }) => {
92
+ // update public signal for user to read
93
+ this.viewportService.readableViewport.set({ zoom: transform.k, x: transform.x, y: transform.y });
94
+ this.zoomableSelection.attr('transform', transform.toString());
95
+ };
96
+ }
97
+ ngOnInit() {
98
+ this.zoomBehavior = zoom()
99
+ .scaleExtent([this.minZoom, this.maxZoom])
100
+ .on('zoom', this.handleZoom);
101
+ this.rootSvgSelection.call(this.zoomBehavior);
102
+ }
103
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MapContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
104
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: MapContextDirective, selector: "g[mapContext]", inputs: { minZoom: "minZoom", maxZoom: "maxZoom" }, ngImport: i0 }); }
105
+ }
106
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MapContextDirective, decorators: [{
107
+ type: Directive,
108
+ args: [{ selector: 'g[mapContext]' }]
109
+ }], propDecorators: { minZoom: [{
110
+ type: Input
111
+ }], maxZoom: [{
112
+ type: Input
113
+ }] } });
114
+
115
+ const round = (num) => Math.round(num * 100) / 100;
116
+
117
+ class DraggableService {
118
+ /**
119
+ * Enable or disable draggable behavior for element.
120
+ * model contains draggable flag which declares if draggable should be enabled or not
121
+ *
122
+ * @param element target element for toggling draggable
123
+ * @param model model with data for this element
124
+ */
125
+ toggleDraggable(element, model) {
126
+ const d3Element = select(element);
127
+ const behavior = model.draggable
128
+ ? this.getDragBehavior(model)
129
+ : this.getIgnoreDragBehavior();
130
+ d3Element.call(behavior);
131
+ }
132
+ /**
133
+ * TODO: not shure if this work, need to check
134
+ *
135
+ * @param element
136
+ */
137
+ destroy(element) {
138
+ select(element).on('.drag', null);
139
+ }
140
+ /**
141
+ * Node drag behavior. Updated node's coordinate according to dragging
142
+ *
143
+ * @param model
144
+ * @returns
145
+ */
146
+ getDragBehavior(model) {
147
+ let deltaX;
148
+ let deltaY;
149
+ return drag()
150
+ .on('start', (event) => {
151
+ deltaX = model.point().x - event.x;
152
+ deltaY = model.point().y - event.y;
153
+ })
154
+ .on('drag', (event) => {
155
+ model.point.set({
156
+ x: round(event.x + deltaX),
157
+ y: round(event.y + deltaY)
158
+ });
159
+ });
160
+ }
161
+ /**
162
+ * Specify ignoring drag behavior. It's responsible for not moving the map when user tries to drag node
163
+ * with disabled drag behavior
164
+ */
165
+ getIgnoreDragBehavior() {
166
+ return drag()
167
+ .on('drag', (event) => {
168
+ event.sourceEvent.stopPropagation();
169
+ });
170
+ }
171
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
172
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService }); }
173
+ }
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService, decorators: [{
175
+ type: Injectable
176
+ }] });
177
+
178
+ class EdgeTemplateDirective {
179
+ constructor() {
180
+ this.templateRef = inject(TemplateRef);
181
+ }
182
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
183
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: EdgeTemplateDirective, selector: "ng-template[edge]", ngImport: i0 }); }
184
+ }
185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeTemplateDirective, decorators: [{
186
+ type: Directive,
187
+ args: [{ selector: 'ng-template[edge]' }]
188
+ }] });
189
+ class ConnectionTemplateDirective {
190
+ constructor() {
191
+ this.templateRef = inject(TemplateRef);
192
+ }
193
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
194
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ConnectionTemplateDirective, selector: "ng-template[connection]", ngImport: i0 }); }
195
+ }
196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionTemplateDirective, decorators: [{
197
+ type: Directive,
198
+ args: [{ selector: 'ng-template[connection]' }]
199
+ }] });
200
+ class EdgeLabelHtmlTemplateDirective {
201
+ constructor() {
202
+ this.templateRef = inject(TemplateRef);
203
+ }
204
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeLabelHtmlTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
205
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: EdgeLabelHtmlTemplateDirective, selector: "ng-template[edgeLabelHtml]", ngImport: i0 }); }
206
+ }
207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeLabelHtmlTemplateDirective, decorators: [{
208
+ type: Directive,
209
+ args: [{ selector: 'ng-template[edgeLabelHtml]' }]
210
+ }] });
211
+ class NodeHtmlTemplateDirective {
212
+ constructor() {
213
+ this.templateRef = inject(TemplateRef);
214
+ }
215
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeHtmlTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
216
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: NodeHtmlTemplateDirective, selector: "ng-template[nodeHtml]", ngImport: i0 }); }
217
+ }
218
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeHtmlTemplateDirective, decorators: [{
219
+ type: Directive,
220
+ args: [{ selector: 'ng-template[nodeHtml]' }]
221
+ }] });
222
+ class HandleTemplateDirective {
223
+ constructor() {
224
+ this.templateRef = inject(TemplateRef);
225
+ }
226
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
227
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HandleTemplateDirective, selector: "ng-template[handle]", ngImport: i0 }); }
228
+ }
229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleTemplateDirective, decorators: [{
230
+ type: Directive,
231
+ args: [{ selector: 'ng-template[handle]' }]
232
+ }] });
233
+
234
+ function addNodesToEdges(nodes, edges) {
235
+ const nodesById = nodes.reduce((acc, n) => {
236
+ acc[n.node.id] = n;
237
+ return acc;
238
+ }, {});
239
+ edges.forEach(e => {
240
+ e.source = nodesById[e.edge.source];
241
+ e.target = nodesById[e.edge.target];
242
+ });
243
+ }
244
+
245
+ class FlowModel {
246
+ constructor() {
247
+ /**
248
+ * Global setting with handle positions. Nodes derive this value
249
+ */
250
+ this.handlePositions = signal({ source: 'right', target: 'left' });
251
+ /**
252
+ * @see {VflowComponent.view}
253
+ */
254
+ this.view = signal([400, 400]);
255
+ this.flowWidth = computed(() => this.view() === 'auto' ? '100%' : this.view()[0]);
256
+ this.flowHeight = computed(() => this.view() === 'auto' ? '100%' : this.view()[1]);
257
+ }
258
+ }
259
+
260
+ class FlowStatusService {
261
+ constructor() {
262
+ this.status = signal({ state: 'idle', payload: null });
263
+ }
264
+ setIdleStatus() {
265
+ this.status.set({ state: 'idle', payload: null });
266
+ }
267
+ setConnectionStartStatus(sourceNode) {
268
+ this.status.set({ state: 'connection-start', payload: { sourceNode } });
269
+ }
270
+ setConnectionValidationStatus(sourceNode, targetNode, valid) {
271
+ this.status.set({ state: 'connection-validation', payload: { sourceNode, targetNode, valid } });
272
+ }
273
+ setConnectionEndStatus(sourceNode, targetNode) {
274
+ this.status.set({ state: 'connection-end', payload: { sourceNode, targetNode } });
275
+ }
276
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
277
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService }); }
278
+ }
279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService, decorators: [{
280
+ type: Injectable
281
+ }] });
282
+ /**
283
+ * Batch status changes together to call them one after another
284
+ *
285
+ * @param changes list of set[FlowStatus.state]Status() calls
286
+ */
287
+ function batchStatusChanges(...changes) {
288
+ if (changes.length) {
289
+ const [firstChange, ...restChanges] = changes;
290
+ // first change is sync
291
+ firstChange();
292
+ // without timer, subscribed effects/comuted signals only get latest value
293
+ restChanges.forEach(change => setTimeout(() => change()));
294
+ }
295
+ }
296
+
297
+ class ConnectionModel {
298
+ constructor(connection) {
299
+ this.connection = connection;
300
+ this.curve = connection.curve ?? 'bezier';
301
+ this.type = connection.type ?? 'default';
302
+ this.validator = connection.validator ?? (() => true);
303
+ }
304
+ }
305
+
306
+ function hashCode(str) {
307
+ return str.split('').reduce((a, b) => {
308
+ a = ((a << 5) - a) + b.charCodeAt(0);
309
+ return a & a;
310
+ }, 0);
311
+ }
312
+
313
+ class FlowEntitiesService {
314
+ constructor() {
315
+ this.nodes = signal([], {
316
+ // empty arrays considered equal, other arrays may not be equal
317
+ equal: (a, b) => !a.length && !b.length ? true : a === b
318
+ });
319
+ this.edges = signal([], {
320
+ // empty arrays considered equal, other arrays may not be equal
321
+ equal: (a, b) => !a.length && !b.length ? true : a === b
322
+ });
323
+ this.connection = signal(new ConnectionModel({}));
324
+ this.markers = computed(() => {
325
+ const markersMap = new Map();
326
+ this.validEdges().forEach(e => {
327
+ if (e.edge.markers?.start) {
328
+ const hash = hashCode(JSON.stringify(e.edge.markers.start));
329
+ markersMap.set(hash, e.edge.markers.start);
330
+ }
331
+ if (e.edge.markers?.end) {
332
+ const hash = hashCode(JSON.stringify(e.edge.markers.end));
333
+ markersMap.set(hash, e.edge.markers.end);
334
+ }
335
+ });
336
+ const connectionMarker = this.connection().connection.marker;
337
+ if (connectionMarker) {
338
+ const hash = hashCode(JSON.stringify(connectionMarker));
339
+ markersMap.set(hash, connectionMarker);
340
+ }
341
+ return markersMap;
342
+ });
343
+ this.validEdges = computed(() => {
344
+ const nodes = this.nodes();
345
+ return this.edges().filter(e => nodes.includes(e.source) && nodes.includes(e.target));
346
+ });
347
+ }
348
+ getNode(id) {
349
+ return this.nodes().find(({ node }) => node.id === id);
350
+ }
351
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowEntitiesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
352
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowEntitiesService }); }
353
+ }
354
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowEntitiesService, decorators: [{
355
+ type: Injectable
356
+ }] });
357
+
358
+ class ConnectionControllerDirective {
359
+ constructor() {
360
+ this.onConnect = new EventEmitter();
361
+ this.statusService = inject(FlowStatusService);
362
+ this.flowEntitiesService = inject(FlowEntitiesService);
363
+ this.connectEffect = effect(() => {
364
+ const status = this.statusService.status();
365
+ if (status.state === 'connection-end') {
366
+ const sourceModel = status.payload.sourceNode;
367
+ const targetModel = status.payload.targetNode;
368
+ const source = sourceModel.node.id;
369
+ const target = targetModel.node.id;
370
+ const connection = this.flowEntitiesService.connection();
371
+ if (connection.validator({ source, target })) {
372
+ this.onConnect.emit({ source, target });
373
+ }
374
+ }
375
+ }, { allowSignalWrites: true });
376
+ }
377
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
378
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[connectionController]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
379
+ }
380
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionControllerDirective, decorators: [{
381
+ type: Directive,
382
+ args: [{
383
+ selector: '[connectionController]',
384
+ standalone: true
385
+ }]
386
+ }], propDecorators: { onConnect: [{
387
+ type: Output
388
+ }] } });
389
+
390
+ class NodeModel {
391
+ constructor(node) {
392
+ this.node = node;
393
+ this.point = signal({ x: 0, y: 0 });
394
+ this.point$ = toObservable(this.point);
395
+ this.size = signal({ width: 0, height: 0 });
396
+ this.pointTransform = computed(() => `translate(${this.point().x}, ${this.point().y})`);
397
+ this.sourceOffset = computed(() => {
398
+ const { width, height } = this.size();
399
+ switch (this.sourcePosition()) {
400
+ case 'left': return { x: 0, y: height / 2 };
401
+ case 'right': return { x: width, y: height / 2 };
402
+ case 'top': return { x: width / 2, y: 0 };
403
+ case 'bottom': return { x: width / 2, y: height };
404
+ }
405
+ });
406
+ this.targetOffset = computed(() => {
407
+ const { width, height } = this.size();
408
+ switch (this.targetPosition()) {
409
+ case 'left': return { x: 0, y: (height / 2) };
410
+ case 'right': return { x: width, y: height / 2 };
411
+ case 'top': return { x: width / 2, y: 0 };
412
+ case 'bottom': return { x: width / 2, y: height };
413
+ }
414
+ });
415
+ this.sourcePointAbsolute = computed(() => {
416
+ return {
417
+ x: this.point().x + this.sourceOffset().x + this.sourceHandleOffset().x,
418
+ y: this.point().y + this.sourceOffset().y + this.sourceHandleOffset().y
419
+ };
420
+ });
421
+ this.targetPointAbsolute = computed(() => {
422
+ return {
423
+ x: this.point().x + this.targetOffset().x + this.targetHandleOffset().x,
424
+ y: this.point().y + this.targetOffset().y + this.targetHandleOffset().y
425
+ };
426
+ });
427
+ // Now source and handle positions derived from parent flow
428
+ this.sourcePosition = computed(() => this.flow.handlePositions().source);
429
+ this.targetPosition = computed(() => this.flow.handlePositions().target);
430
+ this.sourceHandleSize = signal({ width: 0, height: 0 });
431
+ this.targetHandleSize = signal({ width: 0, height: 0 });
432
+ this.sourceHandleOffset = computed(() => {
433
+ switch (this.sourcePosition()) {
434
+ case 'left': return { x: -(this.sourceHandleSize().width / 2), y: 0 };
435
+ case 'right': return { x: this.sourceHandleSize().width / 2, y: 0 };
436
+ case 'top': return { x: 0, y: -(this.sourceHandleSize().height / 2) };
437
+ case 'bottom': return { x: 0, y: this.sourceHandleSize().height / 2 };
438
+ }
439
+ });
440
+ this.targetHandleOffset = computed(() => {
441
+ switch (this.targetPosition()) {
442
+ case 'left': return { x: -(this.targetHandleSize().width / 2), y: 0 };
443
+ case 'right': return { x: this.targetHandleSize().width / 2, y: 0 };
444
+ case 'top': return { x: 0, y: -(this.targetHandleSize().height / 2) };
445
+ case 'bottom': return { x: 0, y: this.targetHandleSize().height / 2 };
446
+ }
447
+ });
448
+ this.sourceOffsetAligned = computed(() => {
449
+ return {
450
+ x: this.sourceOffset().x - (this.sourceHandleSize().width / 2),
451
+ y: this.sourceOffset().y - (this.sourceHandleSize().height / 2),
452
+ };
453
+ });
454
+ this.targetOffsetAligned = computed(() => {
455
+ return {
456
+ x: this.targetOffset().x - (this.targetHandleSize().width / 2),
457
+ y: this.targetOffset().y - (this.targetHandleSize().height / 2),
458
+ };
459
+ });
460
+ this.draggable = true;
461
+ // disabled for configuration for now
462
+ this.magnetRadius = 20;
463
+ this.point.set(node.point);
464
+ if (isDefined(node.draggable))
465
+ this.draggable = node.draggable;
466
+ }
467
+ /**
468
+ * Bind parent flow model to node
469
+ *
470
+ * @param flow parent flow
471
+ */
472
+ bindFlow(flow) {
473
+ this.flow = flow;
474
+ }
475
+ }
476
+
477
+ class EdgeLabelModel {
478
+ constructor(edgeLabel) {
479
+ this.edgeLabel = edgeLabel;
480
+ this.size = signal({ width: 0, height: 0 });
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Get point on line
486
+ *
487
+ * https://math.stackexchange.com/questions/563566/how-do-i-find-the-middle1-2-1-3-1-4-etc-of-a-line
488
+ */
489
+ function getPointOnLineByRatio(start, end, ratio) {
490
+ return {
491
+ x: (1 - ratio) * start.x + ratio * end.x,
492
+ y: (1 - ratio) * start.y + ratio * end.y,
493
+ };
494
+ }
495
+
496
+ function straightPath(source, target, usingPoints = [false, false, false]) {
497
+ const [start, center, end] = usingPoints;
498
+ const nullPoint = { x: 0, y: 0 };
499
+ const path$1 = path();
500
+ path$1.moveTo(source.x, source.y);
501
+ path$1.lineTo(target.x, target.y);
502
+ return {
503
+ path: path$1.toString(),
504
+ points: {
505
+ start: start ? getPointOnLineByRatio(source, target, .15) : nullPoint,
506
+ center: center ? getPointOnLineByRatio(source, target, .50) : nullPoint,
507
+ end: end ? getPointOnLineByRatio(source, target, .85) : nullPoint,
508
+ }
509
+ };
510
+ }
511
+
512
+ function bezierPath(source, target, sourcePosition, targetPosition, usingPoints = [false, false, false]) {
513
+ const path$1 = path();
514
+ path$1.moveTo(source.x, source.y);
515
+ const distanceVector = { x: source.x - target.x, y: source.y - target.y };
516
+ const firstControl = calcControlPoint(source, sourcePosition, distanceVector);
517
+ const secondControl = calcControlPoint(target, targetPosition, distanceVector);
518
+ path$1.bezierCurveTo(firstControl.x, firstControl.y, secondControl.x, secondControl.y, target.x, target.y);
519
+ return getPathData(path$1, source, target, firstControl, secondControl, usingPoints);
520
+ }
521
+ /**
522
+ * Calculate control point based on provided point
523
+ *
524
+ * @param point relative this point control point is gonna be computed (the source or the target)
525
+ * @param pointPosition position of {point} on block
526
+ * @param distanceVector transmits the distance between the source and the target as x and y coordinates
527
+ */
528
+ function calcControlPoint(point, pointPosition, distanceVector) {
529
+ const factorPoint = { x: 0, y: 0 };
530
+ switch (pointPosition) {
531
+ case 'top':
532
+ factorPoint.y = 1;
533
+ break;
534
+ case 'bottom':
535
+ factorPoint.y = -1;
536
+ break;
537
+ case 'right':
538
+ factorPoint.x = 1;
539
+ break;
540
+ case 'left':
541
+ factorPoint.x = -1;
542
+ break;
543
+ }
544
+ // TODO: explain name
545
+ const fullDistanceVector = {
546
+ x: distanceVector.x * Math.abs(factorPoint.x),
547
+ y: distanceVector.y * Math.abs(factorPoint.y),
548
+ };
549
+ // TODO: probably need to make this configurable
550
+ const curvature = 0.25;
551
+ // thanks colleagues from react/svelte world
552
+ // https://github.com/xyflow/xyflow/blob/f0117939bae934447fa7f232081f937169ee23b5/packages/system/src/utils/edges/bezier-edge.ts#L56
553
+ const controlOffset = curvature * 25 * Math.sqrt(Math.abs(fullDistanceVector.x + fullDistanceVector.y));
554
+ return {
555
+ x: point.x + factorPoint.x * controlOffset,
556
+ y: point.y - factorPoint.y * controlOffset,
557
+ };
558
+ }
559
+ function getPathData(path, source, target, firstControl, secondControl, usingPoints) {
560
+ const [start, center, end] = usingPoints;
561
+ const nullPoint = { x: 0, y: 0 };
562
+ return {
563
+ path: path.toString(),
564
+ points: {
565
+ start: start
566
+ ? getPointOnBezier(source, target, firstControl, secondControl, 0.1)
567
+ : nullPoint,
568
+ center: center
569
+ ? getPointOnBezier(source, target, firstControl, secondControl, 0.5)
570
+ : nullPoint,
571
+ end: end
572
+ ? getPointOnBezier(source, target, firstControl, secondControl, 0.9)
573
+ : nullPoint,
574
+ },
575
+ };
576
+ }
577
+ /**
578
+ * Get point on bezier curve by ratio
579
+ */
580
+ function getPointOnBezier(sourcePoint, targetPoint, controlPoint1, controlPoint2, ratio) {
581
+ const fromSourceToFirstControl = getPointOnLineByRatio(sourcePoint, controlPoint1, ratio);
582
+ const fromFirstControlToSecond = getPointOnLineByRatio(controlPoint1, controlPoint2, ratio);
583
+ const fromSecondControlToTarget = getPointOnLineByRatio(controlPoint2, targetPoint, ratio);
584
+ return getPointOnLineByRatio(getPointOnLineByRatio(fromSourceToFirstControl, fromFirstControlToSecond, ratio), getPointOnLineByRatio(fromFirstControlToSecond, fromSecondControlToTarget, ratio), ratio);
585
+ }
586
+
587
+ class EdgeModel {
588
+ constructor(edge) {
589
+ this.edge = edge;
590
+ this.path = computed(() => {
591
+ const source = this.source.sourcePointAbsolute();
592
+ const target = this.target.targetPointAbsolute();
593
+ switch (this.curve) {
594
+ case 'straight':
595
+ return straightPath(source, target, this.usingPoints);
596
+ case 'bezier':
597
+ return bezierPath(source, target, this.source.sourcePosition(), this.target.targetPosition(), this.usingPoints);
598
+ }
599
+ });
600
+ this.edgeLabels = {};
601
+ this.type = edge.type ?? 'default';
602
+ this.curve = edge.curve ?? 'bezier';
603
+ if (edge.edgeLabels?.start)
604
+ this.edgeLabels.start = new EdgeLabelModel(edge.edgeLabels.start);
605
+ if (edge.edgeLabels?.center)
606
+ this.edgeLabels.center = new EdgeLabelModel(edge.edgeLabels.center);
607
+ if (edge.edgeLabels?.end)
608
+ this.edgeLabels.end = new EdgeLabelModel(edge.edgeLabels.end);
609
+ this.usingPoints = [!!this.edgeLabels.start, !!this.edgeLabels.center, !!this.edgeLabels.end];
610
+ }
611
+ }
612
+
613
+ class ReferenceKeeper {
614
+ /**
615
+ * Create new models for new node references and keep old models for old node references
616
+ */
617
+ static nodes(newNodes, oldNodeModels) {
618
+ const oldNodesMap = new Map();
619
+ oldNodeModels.forEach(model => oldNodesMap.set(model.node, model));
620
+ return newNodes.map(newNode => {
621
+ if (oldNodesMap.has(newNode))
622
+ return oldNodesMap.get(newNode);
623
+ else
624
+ return new NodeModel(newNode);
625
+ });
626
+ }
627
+ /**
628
+ * Create new models for new edge references and keep old models for old edge references
629
+ */
630
+ static edges(newEdges, oldEdgeModels) {
631
+ const oldEdgesMap = new Map();
632
+ oldEdgeModels.forEach(model => oldEdgesMap.set(model.edge, model));
633
+ return newEdges.map(newEdge => {
634
+ if (oldEdgesMap.has(newEdge))
635
+ return oldEdgesMap.get(newEdge);
636
+ else
637
+ return new EdgeModel(newEdge);
638
+ });
639
+ }
640
+ }
641
+
642
+ class NodesChangeService {
643
+ constructor() {
644
+ this.entitiesService = inject(FlowEntitiesService);
645
+ this.nodesPositionChange$ = toObservable(this.entitiesService.nodes)
646
+ .pipe(
647
+ // Check for nodes list change and watch for specific node from this list change its position
648
+ switchMap((nodes) => merge(...nodes.map(node => node.point$.pipe(
649
+ // skip initial position from signal
650
+ skip(1), map(() => node))))),
651
+ // For now it's a single node, later this list will also be filled
652
+ // with child node position changes
653
+ map(changedNode => [
654
+ { type: 'position', id: changedNode.node.id, point: changedNode.point() }
655
+ ]));
656
+ this.nodeAddChange$ = toObservable(this.entitiesService.nodes)
657
+ .pipe(pairwise(), map(([oldList, newList]) => newList.filter(node => !oldList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map(node => ({ type: 'add', id: node.node.id }))));
658
+ this.nodeRemoveChange$ = toObservable(this.entitiesService.nodes)
659
+ .pipe(pairwise(), map(([oldList, newList]) => oldList.filter(node => !newList.includes(node))), filter((nodes) => !!nodes.length), map((nodes) => nodes.map(node => ({ type: 'remove', id: node.node.id }))));
660
+ this.changes$ = merge(this.nodesPositionChange$, this.nodeAddChange$, this.nodeRemoveChange$);
661
+ }
662
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
663
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService }); }
664
+ }
665
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService, decorators: [{
666
+ type: Injectable
667
+ }] });
668
+
669
+ class EdgeChangesService {
670
+ constructor() {
671
+ this.entitiesService = inject(FlowEntitiesService);
672
+ this.edgeDetachedChange$ = toObservable(computed(() => {
673
+ const nodes = this.entitiesService.nodes();
674
+ const edges = untracked(this.entitiesService.edges);
675
+ return edges.filter(({ source, target }) => !nodes.includes(source) || !nodes.includes(target));
676
+ })).pipe(
677
+ // TODO check why there are 2 emits from single call inside computed
678
+ filter(edges => !!edges.length), map((edges) => edges.map(({ edge }) => ({ type: 'detached', id: edge.id }))));
679
+ this.edgeAddChange$ = toObservable(this.entitiesService.edges)
680
+ .pipe(pairwise(), map(([oldList, newList]) => {
681
+ return newList.filter(edge => !oldList.includes(edge));
682
+ }), filter(edges => !!edges.length), map((edges) => edges.map(({ edge }) => ({ type: 'add', id: edge.id }))));
683
+ this.edgeRemoveChange$ = toObservable(this.entitiesService.edges)
684
+ .pipe(pairwise(), map(([oldList, newList]) => {
685
+ return oldList.filter(edge => !newList.includes(edge));
686
+ }), filter(edges => !!edges.length), map((edges) => edges.map(({ edge }) => ({ type: 'remove', id: edge.id }))));
687
+ this.changes$ = merge(this.edgeDetachedChange$, this.edgeAddChange$, this.edgeRemoveChange$)
688
+ .pipe(
689
+ // this fixes the case when user gets 'deteched' changes
690
+ // and tries to delete these edges inside stream
691
+ // angular may ignore this change because [edges] input changed
692
+ // right after [nodes] input change
693
+ observeOn(asyncScheduler));
694
+ }
695
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeChangesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
696
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeChangesService }); }
697
+ }
698
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeChangesService, decorators: [{
699
+ type: Injectable
700
+ }] });
701
+
702
+ class ChangesControllerDirective {
703
+ constructor() {
704
+ this.nodesChangeService = inject(NodesChangeService);
705
+ this.edgesChangeService = inject(EdgeChangesService);
706
+ this.onNodesChange = new EventEmitter();
707
+ this.onEdgesChange = new EventEmitter();
708
+ this.nodesChangeProxySubscription = this.nodesChangeService.changes$
709
+ .pipe(tap((changes) => this.onNodesChange.emit(changes)), takeUntilDestroyed())
710
+ .subscribe();
711
+ this.edgesChangeProxySubscription = this.edgesChangeService.changes$
712
+ .pipe(tap((changes) => this.onEdgesChange.emit(changes)), takeUntilDestroyed())
713
+ .subscribe();
714
+ }
715
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ChangesControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
716
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ChangesControllerDirective, isStandalone: true, selector: "[changesController]", outputs: { onNodesChange: "onNodesChange", onEdgesChange: "onEdgesChange" }, ngImport: i0 }); }
717
+ }
718
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ChangesControllerDirective, decorators: [{
719
+ type: Directive,
720
+ args: [{
721
+ selector: '[changesController]',
722
+ standalone: true
723
+ }]
724
+ }], propDecorators: { onNodesChange: [{
725
+ type: Output
726
+ }], onEdgesChange: [{
727
+ type: Output
728
+ }] } });
729
+
730
+ class NodeComponent {
731
+ constructor() {
732
+ this.draggableService = inject(DraggableService);
733
+ this.flowStatusService = inject(FlowStatusService);
734
+ this.flowEntitiesService = inject(FlowEntitiesService);
735
+ this.hostRef = inject(ElementRef);
736
+ this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
737
+ this.flowStatusService.status().state === 'connection-validation');
738
+ this.defaultHandleStrokeWidth = 2;
739
+ this.sourceHanldeState = signal('idle');
740
+ this.targetHandleState = signal('idle');
741
+ this.sourceHanldeStateReadonly = this.sourceHanldeState.asReadonly();
742
+ this.targetHanldeStateReadonly = this.targetHandleState.asReadonly();
743
+ }
744
+ ngOnInit() {
745
+ this.draggableService.toggleDraggable(this.hostRef.nativeElement, this.nodeModel);
746
+ }
747
+ ngAfterViewInit() {
748
+ // TODO remove microtask
749
+ queueMicrotask(() => {
750
+ if (this.nodeModel.node.type === 'default') {
751
+ const { width, height } = this.nodeContentRef.nativeElement.getBBox();
752
+ this.nodeModel.size.set({ width, height });
753
+ }
754
+ if (this.nodeModel.node.type === 'html-template') {
755
+ const width = this.htmlWrapperRef.nativeElement.clientWidth;
756
+ const height = this.htmlWrapperRef.nativeElement.clientHeight;
757
+ this.nodeModel.size.set({ width, height });
758
+ }
759
+ this.setSourceHandleSize();
760
+ this.setTargetHandleSize();
761
+ });
762
+ }
763
+ ngOnDestroy() {
764
+ this.draggableService.destroy(this.hostRef.nativeElement);
765
+ }
766
+ startConnection(event) {
767
+ // ignore drag by stopping propagation
768
+ event.stopPropagation();
769
+ this.flowStatusService.setConnectionStartStatus(this.nodeModel);
770
+ }
771
+ endConnection() {
772
+ const status = this.flowStatusService.status();
773
+ if (status.state === 'connection-validation') {
774
+ const sourceNode = status.payload.sourceNode;
775
+ const targetNode = this.nodeModel;
776
+ batchStatusChanges(
777
+ // call to create connection
778
+ () => this.flowStatusService.setConnectionEndStatus(sourceNode, targetNode),
779
+ // when connection created, we need go back to idle status
780
+ () => this.flowStatusService.setIdleStatus());
781
+ }
782
+ }
783
+ /**
784
+ * TODO srp
785
+ */
786
+ validateTargetHandle() {
787
+ const status = this.flowStatusService.status();
788
+ if (status.state === 'connection-start') {
789
+ const sourceNode = status.payload.sourceNode;
790
+ const targetNode = this.nodeModel;
791
+ const source = sourceNode.node.id;
792
+ const target = targetNode.node.id;
793
+ const valid = this.flowEntitiesService.connection().validator({ source, target });
794
+ this.targetHandleState.set(valid ? 'valid' : 'invalid');
795
+ this.flowStatusService.setConnectionValidationStatus(sourceNode, targetNode, valid);
796
+ }
797
+ }
798
+ /**
799
+ * TODO srp
800
+ */
801
+ resetValidateTargetHandle() {
802
+ this.targetHandleState.set('idle');
803
+ // drop back to start status
804
+ const status = this.flowStatusService.status();
805
+ if (status.state === 'connection-validation') {
806
+ this.flowStatusService.setConnectionStartStatus(status.payload.sourceNode);
807
+ }
808
+ }
809
+ getHandleContext(type) {
810
+ if (type === 'source') {
811
+ return {
812
+ $implicit: {
813
+ point: this.nodeModel.sourceOffset,
814
+ alignedPoint: this.nodeModel.sourceOffsetAligned,
815
+ state: this.sourceHanldeStateReadonly
816
+ }
817
+ };
818
+ }
819
+ return {
820
+ $implicit: {
821
+ point: this.nodeModel.targetOffset,
822
+ alignedPoint: this.nodeModel.targetOffsetAligned,
823
+ state: this.targetHanldeStateReadonly
824
+ }
825
+ };
826
+ }
827
+ setSourceHandleSize() {
828
+ // if handle template provided, we don't know its stroke so it's 0
829
+ const strokeWidth = this.handleTemplate ? 0 : (2 * this.defaultHandleStrokeWidth);
830
+ const sourceBox = this.sourceHandleRef.nativeElement.getBBox({ stroke: true });
831
+ this.nodeModel.sourceHandleSize.set({
832
+ width: sourceBox.width + strokeWidth,
833
+ height: sourceBox.height + strokeWidth
834
+ });
835
+ }
836
+ setTargetHandleSize() {
837
+ const strokeWidth = this.handleTemplate ? 0 : (2 * this.defaultHandleStrokeWidth);
838
+ const targetBox = this.targetHandleRef.nativeElement.getBBox({ stroke: true });
839
+ this.nodeModel.targetHandleSize.set({
840
+ width: targetBox.width + strokeWidth,
841
+ height: targetBox.height + strokeWidth
842
+ });
843
+ }
844
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
845
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeHtmlTemplate: "nodeHtmlTemplate", handleTemplate: "handleTemplate" }, viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }, { propertyName: "sourceHandleRef", first: true, predicate: ["sourceHandle"], descendants: true }, { propertyName: "targetHandleRef", first: true, predicate: ["targetHandle"], descendants: true }], ngImport: i0, template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n>\n <div class=\"default-node\" [innerHTML]=\"nodeModel.node.text ?? ''\"></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n *ngTemplateOutlet=\"nodeHtmlTemplate; context: { $implicit: { node: nodeModel.node } }\"\n />\n </div>\n</svg:foreignObject>\n\n<ng-container *ngIf=\"handleTemplate\">\n <svg:g #sourceHandle (mousedown)=\"startConnection($event)\">\n <ng-container *ngTemplateOutlet=\"handleTemplate; context: getHandleContext('source')\" />\n </svg:g>\n\n <svg:g #targetHandle>\n <ng-container *ngTemplateOutlet=\"handleTemplate; context: getHandleContext('target')\" />\n </svg:g>\n</ng-container>\n\n<ng-container *ngIf=\"!handleTemplate\">\n <svg:circle\n #sourceHandle\n [attr.cx]=\"nodeModel.sourceOffset().x\"\n [attr.cy]=\"nodeModel.sourceOffset().y\"\n [attr.stroke-width]=\"defaultHandleStrokeWidth\"\n r=\"5\"\n stroke=\"white\"\n fill=\"black\"\n (mousedown)=\"startConnection($event)\"\n />\n\n <svg:circle\n #targetHandle\n [attr.cx]=\"nodeModel.targetOffset().x\"\n [attr.cy]=\"nodeModel.targetOffset().y\"\n [attr.stroke-width]=\"defaultHandleStrokeWidth\"\n r=\"5\"\n stroke=\"white\"\n fill=\"black\"\n (mouseup)=\"endConnection()\"\n />\n</ng-container>\n\n<svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"nodeModel.targetOffset().x\"\n [attr.cy]=\"nodeModel.targetOffset().y\"\n (mouseup)=\"endConnection(); resetValidateTargetHandle()\"\n (mouseover)=\"validateTargetHandle()\"\n (mouseout)=\"resetValidateTargetHandle()\"\n/>\n\n\n", styles: [".wrapper{width:max-content}.magnet{opacity:0}.default-node{max-width:100px;max-height:100px;width:100px;height:50px;border:2px solid black;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
846
+ }
847
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
848
+ type: Component,
849
+ args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n #nodeContent\n width=\"100\"\n height=\"50\"\n>\n <div class=\"default-node\" [innerHTML]=\"nodeModel.node.text ?? ''\"></div>\n</svg:foreignObject>\n\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeHtmlTemplate\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n>\n <div #htmlWrapper class=\"wrapper\">\n <ng-container\n *ngTemplateOutlet=\"nodeHtmlTemplate; context: { $implicit: { node: nodeModel.node } }\"\n />\n </div>\n</svg:foreignObject>\n\n<ng-container *ngIf=\"handleTemplate\">\n <svg:g #sourceHandle (mousedown)=\"startConnection($event)\">\n <ng-container *ngTemplateOutlet=\"handleTemplate; context: getHandleContext('source')\" />\n </svg:g>\n\n <svg:g #targetHandle>\n <ng-container *ngTemplateOutlet=\"handleTemplate; context: getHandleContext('target')\" />\n </svg:g>\n</ng-container>\n\n<ng-container *ngIf=\"!handleTemplate\">\n <svg:circle\n #sourceHandle\n [attr.cx]=\"nodeModel.sourceOffset().x\"\n [attr.cy]=\"nodeModel.sourceOffset().y\"\n [attr.stroke-width]=\"defaultHandleStrokeWidth\"\n r=\"5\"\n stroke=\"white\"\n fill=\"black\"\n (mousedown)=\"startConnection($event)\"\n />\n\n <svg:circle\n #targetHandle\n [attr.cx]=\"nodeModel.targetOffset().x\"\n [attr.cy]=\"nodeModel.targetOffset().y\"\n [attr.stroke-width]=\"defaultHandleStrokeWidth\"\n r=\"5\"\n stroke=\"white\"\n fill=\"black\"\n (mouseup)=\"endConnection()\"\n />\n</ng-container>\n\n<svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"nodeModel.targetOffset().x\"\n [attr.cy]=\"nodeModel.targetOffset().y\"\n (mouseup)=\"endConnection(); resetValidateTargetHandle()\"\n (mouseover)=\"validateTargetHandle()\"\n (mouseout)=\"resetValidateTargetHandle()\"\n/>\n\n\n", styles: [".wrapper{width:max-content}.magnet{opacity:0}.default-node{max-width:100px;max-height:100px;width:100px;height:50px;border:2px solid black;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}\n"] }]
850
+ }], propDecorators: { nodeModel: [{
851
+ type: Input
852
+ }], nodeHtmlTemplate: [{
853
+ type: Input
854
+ }], handleTemplate: [{
855
+ type: Input
856
+ }], nodeContentRef: [{
857
+ type: ViewChild,
858
+ args: ['nodeContent']
859
+ }], htmlWrapperRef: [{
860
+ type: ViewChild,
861
+ args: ['htmlWrapper']
862
+ }], sourceHandleRef: [{
863
+ type: ViewChild,
864
+ args: ['sourceHandle']
865
+ }], targetHandleRef: [{
866
+ type: ViewChild,
867
+ args: ['targetHandle']
868
+ }] } });
869
+
870
+ class EdgeLabelComponent {
871
+ constructor() {
872
+ /**
873
+ * Centered point of label
874
+ *
875
+ * TODO: maybe put it into EdgeLabelModel
876
+ */
877
+ this.edgeLabelPoint = computed(() => {
878
+ const point = this.pointSignal();
879
+ const { width, height } = this.model.size();
880
+ return {
881
+ x: point.x - (width / 2),
882
+ y: point.y - (height / 2)
883
+ };
884
+ });
885
+ this.pointSignal = signal({ x: 0, y: 0 });
886
+ }
887
+ set point(point) { this.pointSignal.set(point); }
888
+ ngAfterViewInit() {
889
+ queueMicrotask(() => {
890
+ // this is a fix for visual artifact in chrome that for some reason adresses only for edge label.
891
+ // the bug reproduces if edgeLabelWrapperRef size fully matched the size of parent foreignObject
892
+ const MAGIC_VALUE_TO_FIX_GLITCH_IN_CHROME = 2;
893
+ const width = this.edgeLabelWrapperRef.nativeElement.clientWidth + MAGIC_VALUE_TO_FIX_GLITCH_IN_CHROME;
894
+ const height = this.edgeLabelWrapperRef.nativeElement.clientHeight + MAGIC_VALUE_TO_FIX_GLITCH_IN_CHROME;
895
+ this.model.size.set({ width, height });
896
+ });
897
+ }
898
+ getLabelContext() {
899
+ return {
900
+ $implicit: {
901
+ edge: this.edgeModel.edge,
902
+ label: this.model.edgeLabel
903
+ }
904
+ };
905
+ }
906
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeLabelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
907
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: EdgeLabelComponent, selector: "g[edgeLabel]", inputs: { model: "model", edgeModel: "edgeModel", point: "point", htmlTemplate: "htmlTemplate" }, viewQueries: [{ propertyName: "edgeLabelWrapperRef", first: true, predicate: ["edgeLabelWrapper"], descendants: true }], ngImport: i0, template: "<svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\"\n *ngIf=\"model.edgeLabel.type === 'html-template' && htmlTemplate\"\n>\n <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container\n *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\"\n />\n </div>\n</svg:foreignObject>\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
908
+ }
909
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeLabelComponent, decorators: [{
910
+ type: Component,
911
+ args: [{ selector: 'g[edgeLabel]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg:foreignObject\n [attr.x]=\"edgeLabelPoint().x\"\n [attr.y]=\"edgeLabelPoint().y\"\n [attr.width]=\"model.size().width\"\n [attr.height]=\"model.size().height\"\n *ngIf=\"model.edgeLabel.type === 'html-template' && htmlTemplate\"\n>\n <div #edgeLabelWrapper class=\"edge-label-wrapper\">\n <ng-container\n *ngTemplateOutlet=\"htmlTemplate; context: getLabelContext()\"\n />\n </div>\n</svg:foreignObject>\n", styles: [".edge-label-wrapper{width:max-content;margin-top:1px;margin-left:1px}\n"] }]
912
+ }], propDecorators: { model: [{
913
+ type: Input
914
+ }], edgeModel: [{
915
+ type: Input
916
+ }], point: [{
917
+ type: Input
918
+ }], htmlTemplate: [{
919
+ type: Input
920
+ }], edgeLabelWrapperRef: [{
921
+ type: ViewChild,
922
+ args: ['edgeLabelWrapper']
923
+ }] } });
924
+
925
+ class EdgeComponent {
926
+ constructor() {
927
+ this.markerStartUrl = computed(() => {
928
+ const marker = this.model.edge.markers?.start;
929
+ return marker ? `url(#${hashCode(JSON.stringify(marker))})` : '';
930
+ });
931
+ this.markerEndUrl = computed(() => {
932
+ const marker = this.model.edge.markers?.end;
933
+ return marker ? `url(#${hashCode(JSON.stringify(marker))})` : '';
934
+ });
935
+ this.defaultColor = 'rgb(177, 177, 183)';
936
+ }
937
+ ngOnInit() {
938
+ this.edgeContext = {
939
+ $implicit: {
940
+ // TODO: check if edge could change
941
+ edge: this.model.edge,
942
+ path: computed(() => this.model.path().path),
943
+ markerStart: this.markerStartUrl,
944
+ markerEnd: this.markerEndUrl
945
+ }
946
+ };
947
+ }
948
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
949
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: EdgeComponent, selector: "g[edge]", inputs: { model: "model", edgeTemplate: "edgeTemplate", edgeLabelHtmlTemplate: "edgeLabelHtmlTemplate" }, ngImport: i0, template: "<svg:path\n *ngIf=\"model.type === 'default'\"\n [attr.d]=\"model.path().path\"\n [attr.marker-start]=\"markerStartUrl()\"\n [attr.marker-end]=\"markerEndUrl()\"\n [attr.stroke]=\"defaultColor\"\n stroke-width=\"2\"\n fill=\"none\"\n/>\n\n<ng-container *ngIf=\"model.type === 'template' && edgeTemplate\">\n <ng-container *ngTemplateOutlet=\"edgeTemplate; context: edgeContext\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"model.edgeLabels?.start as label\">\n <svg:g edgeLabel\n [model]=\"label\"\n [point]=\"model.path().points.start\"\n [edgeModel]=\"model\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate\"\n />\n</ng-container>\n\n<ng-container *ngIf=\"model.edgeLabels?.center as label\">\n <svg:g edgeLabel\n [model]=\"label\"\n [point]=\"model.path().points.center\"\n [edgeModel]=\"model\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate\"\n />\n</ng-container>\n\n<ng-container *ngIf=\"model.edgeLabels?.end as label\">\n <svg:g edgeLabel\n [model]=\"label\"\n [point]=\"model.path().points.end\"\n [edgeModel]=\"model\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate\"\n />\n</ng-container>\n", dependencies: [{ 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: EdgeLabelComponent, selector: "g[edgeLabel]", inputs: ["model", "edgeModel", "point", "htmlTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
950
+ }
951
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: EdgeComponent, decorators: [{
952
+ type: Component,
953
+ args: [{ selector: 'g[edge]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg:path\n *ngIf=\"model.type === 'default'\"\n [attr.d]=\"model.path().path\"\n [attr.marker-start]=\"markerStartUrl()\"\n [attr.marker-end]=\"markerEndUrl()\"\n [attr.stroke]=\"defaultColor\"\n stroke-width=\"2\"\n fill=\"none\"\n/>\n\n<ng-container *ngIf=\"model.type === 'template' && edgeTemplate\">\n <ng-container *ngTemplateOutlet=\"edgeTemplate; context: edgeContext\"></ng-container>\n</ng-container>\n\n<ng-container *ngIf=\"model.edgeLabels?.start as label\">\n <svg:g edgeLabel\n [model]=\"label\"\n [point]=\"model.path().points.start\"\n [edgeModel]=\"model\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate\"\n />\n</ng-container>\n\n<ng-container *ngIf=\"model.edgeLabels?.center as label\">\n <svg:g edgeLabel\n [model]=\"label\"\n [point]=\"model.path().points.center\"\n [edgeModel]=\"model\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate\"\n />\n</ng-container>\n\n<ng-container *ngIf=\"model.edgeLabels?.end as label\">\n <svg:g edgeLabel\n [model]=\"label\"\n [point]=\"model.path().points.end\"\n [edgeModel]=\"model\"\n [htmlTemplate]=\"edgeLabelHtmlTemplate\"\n />\n</ng-container>\n" }]
954
+ }], propDecorators: { model: [{
955
+ type: Input
956
+ }], edgeTemplate: [{
957
+ type: Input
958
+ }], edgeLabelHtmlTemplate: [{
959
+ type: Input
960
+ }] } });
961
+
962
+ class SpacePointContextDirective {
963
+ constructor() {
964
+ /**
965
+ * Signal with current mouse position in svg space
966
+ */
967
+ this.svgCurrentSpacePoint = computed(() => {
968
+ const movement = this.mouseMovement();
969
+ const point = this.rootSvg.createSVGPoint();
970
+ point.x = movement.x;
971
+ point.y = movement.y;
972
+ return point.matrixTransform(this.host.getScreenCTM().inverse());
973
+ });
974
+ this.rootSvg = inject(RootSvgReferenceDirective).element;
975
+ this.host = inject(ElementRef).nativeElement;
976
+ this.mouseMovement = toSignal(fromEvent(this.rootSvg, 'mousemove').pipe(map(event => ({ x: event.clientX, y: event.clientY }))), { initialValue: { x: 0, y: 0 } });
977
+ }
978
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
979
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SpacePointContextDirective, selector: "g[spacePointContext]", ngImport: i0 }); }
980
+ }
981
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, decorators: [{
982
+ type: Directive,
983
+ args: [{ selector: 'g[spacePointContext]' }]
984
+ }] });
985
+
986
+ class ConnectionComponent {
987
+ constructor() {
988
+ this.flowStatusService = inject(FlowStatusService);
989
+ this.spacePointContext = inject(SpacePointContextDirective);
990
+ this.path = computed(() => {
991
+ const status = this.flowStatusService.status();
992
+ if (status.state === 'connection-start') {
993
+ const sourceNode = status.payload.sourceNode;
994
+ const sourcePoint = sourceNode.sourcePointAbsolute();
995
+ const targetPoint = this.spacePointContext.svgCurrentSpacePoint();
996
+ switch (this.model.curve) {
997
+ case 'straight': return straightPath(sourcePoint, targetPoint).path;
998
+ case 'bezier': return bezierPath(sourcePoint, targetPoint, sourceNode.sourcePosition(), sourceNode.targetPosition()).path;
999
+ }
1000
+ }
1001
+ if (status.state === 'connection-validation') {
1002
+ const sourcePoint = status.payload.sourceNode.sourcePointAbsolute();
1003
+ // ignore magnet if validation failed
1004
+ const targetPoint = status.payload.valid
1005
+ ? status.payload.targetNode.targetPointAbsolute()
1006
+ : this.spacePointContext.svgCurrentSpacePoint();
1007
+ switch (this.model.curve) {
1008
+ case 'straight': return straightPath(sourcePoint, targetPoint).path;
1009
+ case 'bezier': return bezierPath(sourcePoint, targetPoint, status.payload.sourceNode.sourcePosition(), status.payload.sourceNode.targetPosition()).path;
1010
+ }
1011
+ }
1012
+ return null;
1013
+ });
1014
+ this.markerUrl = computed(() => {
1015
+ const marker = this.model.connection.marker;
1016
+ if (marker) {
1017
+ return `url(#${hashCode(JSON.stringify(marker))})`;
1018
+ }
1019
+ return '';
1020
+ });
1021
+ this.defaultColor = 'rgb(177, 177, 183)';
1022
+ }
1023
+ getContext() {
1024
+ return {
1025
+ $implicit: {
1026
+ path: this.path,
1027
+ marker: this.markerUrl
1028
+ }
1029
+ };
1030
+ }
1031
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1032
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ConnectionComponent, selector: "g[connection]", inputs: { model: "model", template: "template" }, ngImport: i0, template: `
1033
+ <ng-container *ngIf="model.type === 'default'">
1034
+ <svg:path
1035
+ *ngIf="path() as path"
1036
+ [attr.d]="path"
1037
+ [attr.marker-end]="markerUrl()"
1038
+ [attr.stroke]="defaultColor"
1039
+ fill="none"
1040
+ stroke-width="2"
1041
+ />
1042
+ </ng-container>
1043
+
1044
+ <ng-container *ngIf="model.type === 'template' && template">
1045
+ <ng-container *ngTemplateOutlet="template; context: getContext()" />
1046
+ </ng-container>
1047
+ `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1048
+ }
1049
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionComponent, decorators: [{
1050
+ type: Component,
1051
+ args: [{
1052
+ selector: 'g[connection]',
1053
+ template: `
1054
+ <ng-container *ngIf="model.type === 'default'">
1055
+ <svg:path
1056
+ *ngIf="path() as path"
1057
+ [attr.d]="path"
1058
+ [attr.marker-end]="markerUrl()"
1059
+ [attr.stroke]="defaultColor"
1060
+ fill="none"
1061
+ stroke-width="2"
1062
+ />
1063
+ </ng-container>
1064
+
1065
+ <ng-container *ngIf="model.type === 'template' && template">
1066
+ <ng-container *ngTemplateOutlet="template; context: getContext()" />
1067
+ </ng-container>
1068
+ `,
1069
+ changeDetection: ChangeDetectionStrategy.OnPush
1070
+ }]
1071
+ }], propDecorators: { model: [{
1072
+ type: Input,
1073
+ args: [{ required: true }]
1074
+ }], template: [{
1075
+ type: Input
1076
+ }] } });
1077
+
1078
+ class DefsComponent {
1079
+ constructor() {
1080
+ this.markers = new Map();
1081
+ this.defaultColor = 'rgb(177, 177, 183)';
1082
+ }
1083
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DefsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1084
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: DefsComponent, selector: "defs[flowDefs]", inputs: { markers: "markers" }, ngImport: i0, template: "<svg:marker\n *ngFor=\"let marker of markers | keyvalue\"\n [attr.id]=\"marker.key\"\n [attr.markerWidth]=\"marker.value.width ?? 16.5\"\n [attr.markerHeight]=\"marker.value.height ?? 16.5\"\n [attr.orient]=\"marker.value.orient ?? 'auto-start-reverse'\"\n viewBox=\"-10 -10 20 20\"\n [attr.markerUnits]=\"marker.value.markerUnits ?? 'userSpaceOnUse'\"\n refX=\"0\"\n refY=\"0\"\n>\n <polyline\n *ngIf=\"marker.value.type === 'arrow-closed' || !marker.value.type\"\n class=\"marker__arrow_closed\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n [style.fill]=\"marker.value.color ?? defaultColor\"\n points=\"-5,-4 1,0 -5,4 -5,-4\"\n />\n\n <polyline\n *ngIf=\"marker.value.type === 'arrow'\"\n class=\"marker__arrow_default\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n points=\"-5,-4 0,0 -5,4\"\n />\n</svg:marker>\n", styles: [".marker__arrow_default{stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;fill:none}.marker__arrow_closed{stroke-linecap:round;stroke-linejoin:round}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1085
+ }
1086
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DefsComponent, decorators: [{
1087
+ type: Component,
1088
+ args: [{ selector: 'defs[flowDefs]', changeDetection: ChangeDetectionStrategy.OnPush, template: "<svg:marker\n *ngFor=\"let marker of markers | keyvalue\"\n [attr.id]=\"marker.key\"\n [attr.markerWidth]=\"marker.value.width ?? 16.5\"\n [attr.markerHeight]=\"marker.value.height ?? 16.5\"\n [attr.orient]=\"marker.value.orient ?? 'auto-start-reverse'\"\n viewBox=\"-10 -10 20 20\"\n [attr.markerUnits]=\"marker.value.markerUnits ?? 'userSpaceOnUse'\"\n refX=\"0\"\n refY=\"0\"\n>\n <polyline\n *ngIf=\"marker.value.type === 'arrow-closed' || !marker.value.type\"\n class=\"marker__arrow_closed\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n [style.fill]=\"marker.value.color ?? defaultColor\"\n points=\"-5,-4 1,0 -5,4 -5,-4\"\n />\n\n <polyline\n *ngIf=\"marker.value.type === 'arrow'\"\n class=\"marker__arrow_default\"\n [style.stroke]=\"marker.value.color ?? defaultColor\"\n [style.stroke-width]=\"marker.value.strokeWidth ?? 2\"\n points=\"-5,-4 0,0 -5,4\"\n />\n</svg:marker>\n", styles: [".marker__arrow_default{stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;fill:none}.marker__arrow_closed{stroke-linecap:round;stroke-linejoin:round}\n"] }]
1089
+ }], propDecorators: { markers: [{
1090
+ type: Input,
1091
+ args: [{ required: true }]
1092
+ }] } });
1093
+
1094
+ // TODO: too general purpose nane
1095
+ class RootSvgContextDirective {
1096
+ constructor() {
1097
+ this.flowStatusService = inject(FlowStatusService);
1098
+ }
1099
+ // TODO: check for multiple instances on page
1100
+ resetConnection() {
1101
+ const status = this.flowStatusService.status();
1102
+ if (status.state === 'connection-start') {
1103
+ this.flowStatusService.setIdleStatus();
1104
+ }
1105
+ }
1106
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1107
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootSvgContextDirective, selector: "svg[rootSvgContext]", host: { listeners: { "document:mouseup": "resetConnection()" } }, ngImport: i0 }); }
1108
+ }
1109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgContextDirective, decorators: [{
1110
+ type: Directive,
1111
+ args: [{ selector: 'svg[rootSvgContext]' }]
1112
+ }], propDecorators: { resetConnection: [{
1113
+ type: HostListener,
1114
+ args: ['document:mouseup']
1115
+ }] } });
1116
+
1117
+ const connectionControllerHostDirective = {
1118
+ directive: ConnectionControllerDirective,
1119
+ outputs: ['onConnect']
1120
+ };
1121
+ const changesControllerHostDirective = {
1122
+ directive: ChangesControllerDirective,
1123
+ outputs: ['onNodesChange', 'onEdgesChange']
1124
+ };
1125
+ class VflowComponent {
1126
+ constructor() {
1127
+ // #region DI
1128
+ this.viewportService = inject(ViewportService);
1129
+ this.flowEntitiesService = inject(FlowEntitiesService);
1130
+ this.nodesChangeService = inject(NodesChangeService);
1131
+ this.edgesChangeService = inject(EdgeChangesService);
1132
+ this.injector = inject(Injector);
1133
+ this.minZoom = 0.5;
1134
+ this.maxZoom = 3;
1135
+ this.background = '#FFFFFF';
1136
+ // #endregion
1137
+ // #region SIGNAL_API
1138
+ this.viewport = this.viewportService.readableViewport.asReadonly();
1139
+ this.nodesChange = toSignal(this.nodesChangeService.changes$, { initialValue: [] });
1140
+ this.edgesChange = toSignal(this.edgesChangeService.changes$, { initialValue: [] });
1141
+ // #endregion
1142
+ // #region RX_API
1143
+ this.viewportChanges$ = toObservable(this.viewportService.readableViewport)
1144
+ .pipe(skip(1)); // skip default value that set by signal
1145
+ this.nodesChange$ = this.nodesChangeService.changes$;
1146
+ this.edgesChange$ = this.edgesChangeService.changes$;
1147
+ // #endregion
1148
+ // TODO: probably better to make it injectable
1149
+ this.flowModel = new FlowModel();
1150
+ this.markers = this.flowEntitiesService.markers;
1151
+ }
1152
+ // #endregion
1153
+ // #region SETTINGS
1154
+ /**
1155
+ * Size for flow view
1156
+ *
1157
+ * accepts
1158
+ * - absolute size in format [width, height] or
1159
+ * - 'auto' to compute size based on parent element size
1160
+ */
1161
+ set view(view) {
1162
+ this.flowModel.view.set(view);
1163
+ }
1164
+ set handlePositions(handlePositions) {
1165
+ this.flowModel.handlePositions.set(handlePositions);
1166
+ }
1167
+ set connection(connection) { this.flowEntitiesService.connection.set(connection); }
1168
+ get connection() { return this.flowEntitiesService.connection(); }
1169
+ // #endregion
1170
+ // #region MAIN_INPUTS
1171
+ set nodes(newNodes) {
1172
+ const newModels = runInInjectionContext(this.injector, () => ReferenceKeeper.nodes(newNodes, this.flowEntitiesService.nodes()));
1173
+ // TODO better to solve this by DI
1174
+ bindFlowToNodes(this.flowModel, newModels);
1175
+ // quick and dirty binding nodes to edges
1176
+ addNodesToEdges(newModels, this.flowEntitiesService.edges());
1177
+ this.flowEntitiesService.nodes.set(newModels);
1178
+ }
1179
+ get nodeModels() { return this.flowEntitiesService.nodes(); }
1180
+ set edges(newEdges) {
1181
+ const newModels = ReferenceKeeper.edges(newEdges, this.flowEntitiesService.edges());
1182
+ // quick and dirty binding nodes to edges
1183
+ addNodesToEdges(this.nodeModels, newModels);
1184
+ this.flowEntitiesService.edges.set(newModels);
1185
+ }
1186
+ get edgeModels() { return this.flowEntitiesService.validEdges(); }
1187
+ // #region METHODS_API
1188
+ viewportTo(viewport) {
1189
+ this.viewportService.writableViewport.set({ changeType: 'absolute', state: viewport });
1190
+ }
1191
+ zoomTo(zoom) {
1192
+ this.viewportService.writableViewport.set({ changeType: 'absolute', state: { zoom } });
1193
+ }
1194
+ panTo(point) {
1195
+ this.viewportService.writableViewport.set({ changeType: 'absolute', state: point });
1196
+ }
1197
+ getNode(id) {
1198
+ return this.flowEntitiesService.getNode(id)?.node;
1199
+ }
1200
+ // #endregion
1201
+ trackNodes(idx, { node }) {
1202
+ return node.id;
1203
+ }
1204
+ trackEdges(idx, { edge }) {
1205
+ return edge.id;
1206
+ }
1207
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1208
+ 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", connection: ["connection", "connection", (settings) => new ConnectionModel(settings)], nodes: "nodes", edges: "edges" }, providers: [
1209
+ DraggableService,
1210
+ ViewportService,
1211
+ FlowStatusService,
1212
+ FlowEntitiesService,
1213
+ NodesChangeService,
1214
+ EdgeChangesService
1215
+ ], 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 }, { propertyName: "handleTemplateDirective", first: true, predicate: HandleTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onEdgesChange", "onEdgesChange"] }], ngImport: i0, template: "<svg:svg\n rootSvgRef\n rootSvgContext\n class=\"root-svg\"\n #flow\n [style.backgroundColor]=\"background\"\n [attr.width]=\"flowModel.flowWidth()\"\n [attr.height]=\"flowModel.flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\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 [handleTemplate]=\"handleTemplateDirective?.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}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeHtmlTemplate", "handleTemplate"] }, { 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: "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]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1216
+ }
1217
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
1218
+ type: Component,
1219
+ args: [{ selector: 'vflow', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
1220
+ DraggableService,
1221
+ ViewportService,
1222
+ FlowStatusService,
1223
+ FlowEntitiesService,
1224
+ NodesChangeService,
1225
+ EdgeChangesService
1226
+ ], hostDirectives: [
1227
+ connectionControllerHostDirective,
1228
+ changesControllerHostDirective
1229
+ ], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n class=\"root-svg\"\n #flow\n [style.backgroundColor]=\"background\"\n [attr.width]=\"flowModel.flowWidth()\"\n [attr.height]=\"flowModel.flowHeight()\"\n>\n <defs [markers]=\"markers()\" flowDefs />\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 [handleTemplate]=\"handleTemplateDirective?.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}\n"] }]
1230
+ }], propDecorators: { view: [{
1231
+ type: Input
1232
+ }], minZoom: [{
1233
+ type: Input
1234
+ }], maxZoom: [{
1235
+ type: Input
1236
+ }], handlePositions: [{
1237
+ type: Input
1238
+ }], background: [{
1239
+ type: Input
1240
+ }], connection: [{
1241
+ type: Input,
1242
+ args: [{ transform: (settings) => new ConnectionModel(settings) }]
1243
+ }], nodes: [{
1244
+ type: Input,
1245
+ args: [{ required: true }]
1246
+ }], edges: [{
1247
+ type: Input
1248
+ }], nodeHtmlDirective: [{
1249
+ type: ContentChild,
1250
+ args: [NodeHtmlTemplateDirective]
1251
+ }], edgeTemplateDirective: [{
1252
+ type: ContentChild,
1253
+ args: [EdgeTemplateDirective]
1254
+ }], edgeLabelHtmlDirective: [{
1255
+ type: ContentChild,
1256
+ args: [EdgeLabelHtmlTemplateDirective]
1257
+ }], connectionTemplateDirective: [{
1258
+ type: ContentChild,
1259
+ args: [ConnectionTemplateDirective]
1260
+ }], handleTemplateDirective: [{
1261
+ type: ContentChild,
1262
+ args: [HandleTemplateDirective]
1263
+ }], mapContext: [{
1264
+ type: ViewChild,
1265
+ args: [MapContextDirective]
1266
+ }] } });
1267
+ function bindFlowToNodes(flow, nodes) {
1268
+ nodes.forEach(n => n.bindFlow(flow));
1269
+ }
1270
+
1271
+ const components = [
1272
+ VflowComponent,
1273
+ NodeComponent,
1274
+ EdgeComponent,
1275
+ EdgeLabelComponent,
1276
+ ConnectionComponent,
1277
+ DefsComponent
1278
+ ];
1279
+ const directives = [
1280
+ SpacePointContextDirective,
1281
+ MapContextDirective,
1282
+ RootSvgReferenceDirective,
1283
+ RootSvgContextDirective,
1284
+ ];
1285
+ const templateDirectives = [
1286
+ NodeHtmlTemplateDirective,
1287
+ EdgeLabelHtmlTemplateDirective,
1288
+ EdgeTemplateDirective,
1289
+ ConnectionTemplateDirective,
1290
+ HandleTemplateDirective
1291
+ ];
1292
+ class VflowModule {
1293
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1294
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, declarations: [VflowComponent,
1295
+ NodeComponent,
1296
+ EdgeComponent,
1297
+ EdgeLabelComponent,
1298
+ ConnectionComponent,
1299
+ DefsComponent, SpacePointContextDirective,
1300
+ MapContextDirective,
1301
+ RootSvgReferenceDirective,
1302
+ RootSvgContextDirective, NodeHtmlTemplateDirective,
1303
+ EdgeLabelHtmlTemplateDirective,
1304
+ EdgeTemplateDirective,
1305
+ ConnectionTemplateDirective,
1306
+ HandleTemplateDirective], imports: [CommonModule], exports: [VflowComponent, NodeHtmlTemplateDirective,
1307
+ EdgeLabelHtmlTemplateDirective,
1308
+ EdgeTemplateDirective,
1309
+ ConnectionTemplateDirective,
1310
+ HandleTemplateDirective] }); }
1311
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, imports: [CommonModule] }); }
1312
+ }
1313
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowModule, decorators: [{
1314
+ type: NgModule,
1315
+ args: [{
1316
+ imports: [CommonModule],
1317
+ exports: [
1318
+ VflowComponent,
1319
+ ...templateDirectives
1320
+ ],
1321
+ declarations: [...components, ...directives, ...templateDirectives],
1322
+ }]
1323
+ }] });
1324
+
1325
+ // Modules
1326
+
1327
+ /**
1328
+ * Generated bundle index. Do not edit.
1329
+ */
1330
+
1331
+ export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, HandleTemplateDirective, NodeHtmlTemplateDirective, VflowComponent, VflowModule };
1332
+ //# sourceMappingURL=ngx-vflow.mjs.map