ngx-vflow 0.7.0 → 0.9.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.
- package/esm2022/lib/vflow/components/background/background.component.mjs +4 -4
- package/esm2022/lib/vflow/components/connection/connection.component.mjs +2 -2
- package/esm2022/lib/vflow/components/handle/handle.component.mjs +5 -5
- package/esm2022/lib/vflow/components/node/node.component.mjs +31 -82
- package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +32 -16
- package/esm2022/lib/vflow/directives/connection-controller.directive.mjs +91 -12
- package/esm2022/lib/vflow/directives/flow-size-controller.directive.mjs +36 -0
- package/esm2022/lib/vflow/directives/map-context.directive.mjs +16 -12
- package/esm2022/lib/vflow/directives/space-point-context.directive.mjs +11 -5
- package/esm2022/lib/vflow/interfaces/box.mjs +2 -0
- package/esm2022/lib/vflow/interfaces/connection-settings.interface.mjs +1 -1
- package/esm2022/lib/vflow/interfaces/connection.internal.interface.mjs +2 -0
- package/esm2022/lib/vflow/interfaces/fit-view-options.interface.mjs +2 -0
- package/esm2022/lib/vflow/interfaces/rect.mjs +2 -0
- package/esm2022/lib/vflow/interfaces/viewport.interface.mjs +1 -1
- package/esm2022/lib/vflow/models/connection.model.mjs +28 -6
- package/esm2022/lib/vflow/models/handle.model.mjs +1 -1
- package/esm2022/lib/vflow/services/flow-entities.service.mjs +2 -2
- package/esm2022/lib/vflow/services/flow-settings.service.mjs +12 -4
- package/esm2022/lib/vflow/services/flow-status.service.mjs +7 -7
- package/esm2022/lib/vflow/services/handle.service.mjs +1 -1
- package/esm2022/lib/vflow/services/node-changes.service.mjs +3 -3
- package/esm2022/lib/vflow/services/viewport.service.mjs +26 -3
- package/esm2022/lib/vflow/types/connection-mode.type.mjs +2 -0
- package/esm2022/lib/vflow/utils/adjust-direction.mjs +30 -0
- package/esm2022/lib/vflow/utils/nodes.mjs +36 -0
- package/esm2022/lib/vflow/utils/viewport.mjs +15 -0
- package/esm2022/lib/vflow/vflow.module.mjs +6 -3
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/ngx-vflow.mjs +573 -362
- package/fesm2022/ngx-vflow.mjs.map +1 -1
- package/lib/vflow/components/node/node.component.d.ts +4 -12
- package/lib/vflow/components/vflow/vflow.component.d.ts +10 -4
- package/lib/vflow/directives/connection-controller.directive.d.ts +6 -0
- package/lib/vflow/directives/flow-size-controller.directive.d.ts +10 -0
- package/lib/vflow/directives/map-context.directive.d.ts +9 -3
- package/lib/vflow/directives/space-point-context.directive.d.ts +1 -0
- package/lib/vflow/interfaces/box.d.ts +6 -0
- package/lib/vflow/interfaces/connection-settings.interface.d.ts +2 -0
- package/lib/vflow/interfaces/connection.internal.interface.d.ts +8 -0
- package/lib/vflow/interfaces/fit-view-options.interface.d.ts +15 -0
- package/lib/vflow/interfaces/rect.d.ts +6 -0
- package/lib/vflow/interfaces/viewport.interface.d.ts +1 -0
- package/lib/vflow/models/connection.model.d.ts +5 -2
- package/lib/vflow/models/edge.model.d.ts +17 -1
- package/lib/vflow/models/handle.model.d.ts +1 -1
- package/lib/vflow/services/flow-settings.service.d.ts +10 -2
- package/lib/vflow/services/flow-status.service.d.ts +7 -18
- package/lib/vflow/services/viewport.service.d.ts +5 -0
- package/lib/vflow/types/connection-mode.type.d.ts +1 -0
- package/lib/vflow/utils/adjust-direction.d.ts +11 -0
- package/lib/vflow/utils/nodes.d.ts +3 -0
- package/lib/vflow/utils/viewport.d.ts +4 -0
- package/lib/vflow/vflow.module.d.ts +4 -3
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
package/fesm2022/ngx-vflow.mjs
CHANGED
|
@@ -1,71 +1,81 @@
|
|
|
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,
|
|
4
|
+
import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, EventEmitter, Output, DestroyRef, Input, runInInjectionContext, Injector, Component, ChangeDetectionStrategy, HostListener, ViewChild, HostBinding, 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,
|
|
7
|
+
import { Subject, tap, merge, BehaviorSubject, observeOn, animationFrameScheduler, switchMap, skip, map, pairwise, filter, distinctUntilChanged, zip, asyncScheduler, 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';
|
|
11
11
|
import { __decorate } from 'tslib';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Public signal with viewport state. User can directly read from this signal. It's updated by:
|
|
25
|
-
* - user events on flow
|
|
26
|
-
* - writableViewport signal
|
|
27
|
-
*/
|
|
28
|
-
this.readableViewport = signal(ViewportService.getDefaultViewport());
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* The default value used by d3, just copy it here
|
|
32
|
-
*
|
|
33
|
-
* @returns default viewport value
|
|
34
|
-
*/
|
|
35
|
-
static getDefaultViewport() { return { zoom: 1, x: 0, y: 0 }; }
|
|
36
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
37
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService }); }
|
|
13
|
+
function getNodesBounds(nodes) {
|
|
14
|
+
if (nodes.length === 0) {
|
|
15
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
16
|
+
}
|
|
17
|
+
let box = { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity };
|
|
18
|
+
nodes.forEach(node => {
|
|
19
|
+
const nodeBox = nodeToBox(node);
|
|
20
|
+
box = getBoundsOfBoxes(box, nodeBox);
|
|
21
|
+
});
|
|
22
|
+
return boxToRect(box);
|
|
38
23
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
24
|
+
function nodeToBox(node) {
|
|
25
|
+
return {
|
|
26
|
+
x: node.point().x,
|
|
27
|
+
y: node.point().y,
|
|
28
|
+
x2: node.point().x + node.size().width,
|
|
29
|
+
y2: node.point().y + node.size().height,
|
|
30
|
+
};
|
|
45
31
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
function boxToRect({ x, y, x2, y2 }) {
|
|
33
|
+
return {
|
|
34
|
+
x,
|
|
35
|
+
y,
|
|
36
|
+
width: x2 - x,
|
|
37
|
+
height: y2 - y,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function getBoundsOfBoxes(box1, box2) {
|
|
41
|
+
return {
|
|
42
|
+
x: Math.min(box1.x, box2.x),
|
|
43
|
+
y: Math.min(box1.y, box2.y),
|
|
44
|
+
x2: Math.max(box1.x2, box2.x2),
|
|
45
|
+
y2: Math.max(box1.y2, box2.y2),
|
|
46
|
+
};
|
|
53
47
|
}
|
|
54
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgReferenceDirective, decorators: [{
|
|
55
|
-
type: Directive,
|
|
56
|
-
args: [{
|
|
57
|
-
selector: 'svg[rootSvgRef]'
|
|
58
|
-
}]
|
|
59
|
-
}] });
|
|
60
48
|
|
|
61
49
|
class ConnectionModel {
|
|
62
|
-
constructor(
|
|
63
|
-
this.
|
|
64
|
-
this.curve =
|
|
65
|
-
this.type =
|
|
66
|
-
this.
|
|
50
|
+
constructor(settings) {
|
|
51
|
+
this.settings = settings;
|
|
52
|
+
this.curve = settings.curve ?? 'bezier';
|
|
53
|
+
this.type = settings.type ?? 'default';
|
|
54
|
+
this.mode = settings.mode ?? 'strict';
|
|
55
|
+
const validatorsToRun = this.getValidators(settings);
|
|
56
|
+
this.validator = (connection) => validatorsToRun.every((v) => v(connection));
|
|
57
|
+
}
|
|
58
|
+
getValidators(settings) {
|
|
59
|
+
const validators = [];
|
|
60
|
+
validators.push(notSelfValidator);
|
|
61
|
+
if (this.mode === 'loose') {
|
|
62
|
+
validators.push(hasSourceAndTargetHandleValidator);
|
|
63
|
+
}
|
|
64
|
+
if (settings.validator) {
|
|
65
|
+
validators.push(settings.validator);
|
|
66
|
+
}
|
|
67
|
+
return validators;
|
|
67
68
|
}
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Internal validator that not allows self connections
|
|
72
|
+
*/
|
|
73
|
+
const notSelfValidator = (connection) => {
|
|
74
|
+
return connection.source !== connection.target;
|
|
75
|
+
};
|
|
76
|
+
const hasSourceAndTargetHandleValidator = (connection) => {
|
|
77
|
+
return connection.sourceHandle !== undefined && connection.targetHandle !== undefined;
|
|
78
|
+
};
|
|
69
79
|
|
|
70
80
|
function hashCode(str) {
|
|
71
81
|
return str.split('').reduce((a, b) => {
|
|
@@ -97,7 +107,7 @@ class FlowEntitiesService {
|
|
|
97
107
|
markersMap.set(hash, e.edge.markers.end);
|
|
98
108
|
}
|
|
99
109
|
});
|
|
100
|
-
const connectionMarker = this.connection().
|
|
110
|
+
const connectionMarker = this.connection().settings.marker;
|
|
101
111
|
if (connectionMarker) {
|
|
102
112
|
const hash = hashCode(JSON.stringify(connectionMarker));
|
|
103
113
|
markersMap.set(hash, connectionMarker);
|
|
@@ -126,6 +136,119 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
126
136
|
type: Injectable
|
|
127
137
|
}] });
|
|
128
138
|
|
|
139
|
+
function getViewportForBounds(bounds, width, height, minZoom, maxZoom, padding) {
|
|
140
|
+
const xZoom = width / (bounds.width * (1 + padding));
|
|
141
|
+
const yZoom = height / (bounds.height * (1 + padding));
|
|
142
|
+
const zoom = Math.min(xZoom, yZoom);
|
|
143
|
+
const clampedZoom = clamp(zoom, minZoom, maxZoom);
|
|
144
|
+
const boundsCenterX = bounds.x + bounds.width / 2;
|
|
145
|
+
const boundsCenterY = bounds.y + bounds.height / 2;
|
|
146
|
+
const x = width / 2 - boundsCenterX * clampedZoom;
|
|
147
|
+
const y = height / 2 - boundsCenterY * clampedZoom;
|
|
148
|
+
return { x, y, zoom: clampedZoom };
|
|
149
|
+
}
|
|
150
|
+
function clamp(value, min = 0, max = 1) {
|
|
151
|
+
return Math.min(Math.max(value, min), max);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class FlowSettingsService {
|
|
155
|
+
constructor() {
|
|
156
|
+
this.entitiesSelectable = signal(true);
|
|
157
|
+
/**
|
|
158
|
+
* Global setting with handle positions. Nodes derive this value
|
|
159
|
+
*
|
|
160
|
+
* @deprecated
|
|
161
|
+
*/
|
|
162
|
+
this.handlePositions = signal({ source: 'right', target: 'left' });
|
|
163
|
+
/**
|
|
164
|
+
* @see {VflowComponent.view}
|
|
165
|
+
*/
|
|
166
|
+
this.view = signal([400, 400]);
|
|
167
|
+
/**
|
|
168
|
+
* Set based on view property. May change if view is 'auto'
|
|
169
|
+
*/
|
|
170
|
+
this.computedFlowWidth = signal(0);
|
|
171
|
+
/**
|
|
172
|
+
* Set based on view property. May change if view is 'auto'
|
|
173
|
+
*/
|
|
174
|
+
this.computedFlowHeight = signal(0);
|
|
175
|
+
this.minZoom = signal(0.5);
|
|
176
|
+
this.maxZoom = signal(3);
|
|
177
|
+
}
|
|
178
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
179
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService }); }
|
|
180
|
+
}
|
|
181
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService, decorators: [{
|
|
182
|
+
type: Injectable
|
|
183
|
+
}] });
|
|
184
|
+
|
|
185
|
+
class ViewportService {
|
|
186
|
+
constructor() {
|
|
187
|
+
this.entitiesService = inject(FlowEntitiesService);
|
|
188
|
+
this.flowSettingsService = inject(FlowSettingsService);
|
|
189
|
+
/**
|
|
190
|
+
* Internal signal that accepts value from user by lib api
|
|
191
|
+
* When this signal changes, lib sets new view state and update readableViewport signal
|
|
192
|
+
*/
|
|
193
|
+
this.writableViewport = signal({
|
|
194
|
+
changeType: 'initial',
|
|
195
|
+
state: ViewportService.getDefaultViewport(),
|
|
196
|
+
duration: 0
|
|
197
|
+
});
|
|
198
|
+
/**
|
|
199
|
+
* Public signal with viewport state. User can directly read from this signal. It's updated by:
|
|
200
|
+
* - user events on flow
|
|
201
|
+
* - writableViewport signal
|
|
202
|
+
*/
|
|
203
|
+
this.readableViewport = signal(ViewportService.getDefaultViewport());
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The default value used by d3, just copy it here
|
|
207
|
+
*
|
|
208
|
+
* @returns default viewport value
|
|
209
|
+
*/
|
|
210
|
+
static getDefaultViewport() { return { zoom: 1, x: 0, y: 0 }; }
|
|
211
|
+
// TODO: add writableViewportWithConstraints (to apply min zoom/max zoom values)
|
|
212
|
+
fitView(options = { padding: .1, duration: 0, nodes: [] }) {
|
|
213
|
+
const nodes = this.getBoundsNodes(options.nodes ?? []);
|
|
214
|
+
const state = getViewportForBounds(getNodesBounds(nodes), this.flowSettingsService.computedFlowWidth(), this.flowSettingsService.computedFlowHeight(), this.flowSettingsService.minZoom(), this.flowSettingsService.maxZoom(), options.padding ?? .1);
|
|
215
|
+
const duration = options.duration ?? 0;
|
|
216
|
+
this.writableViewport.set({ changeType: 'absolute', state, duration });
|
|
217
|
+
}
|
|
218
|
+
getBoundsNodes(nodeIds) {
|
|
219
|
+
return !nodeIds?.length
|
|
220
|
+
// If nodes option not passed or the list is empty, then get fit the whole view
|
|
221
|
+
? this.entitiesService.nodes()
|
|
222
|
+
// Otherwise fit to specific nodes
|
|
223
|
+
: nodeIds
|
|
224
|
+
.map(nodeId => this.entitiesService.nodes().find(({ node }) => node.id === nodeId))
|
|
225
|
+
.filter((node) => !!node);
|
|
226
|
+
}
|
|
227
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
228
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService }); }
|
|
229
|
+
}
|
|
230
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ViewportService, decorators: [{
|
|
231
|
+
type: Injectable
|
|
232
|
+
}] });
|
|
233
|
+
|
|
234
|
+
function isDefined(data) {
|
|
235
|
+
return data !== undefined;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
class RootSvgReferenceDirective {
|
|
239
|
+
constructor() {
|
|
240
|
+
this.element = inject(ElementRef).nativeElement;
|
|
241
|
+
}
|
|
242
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgReferenceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
243
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]", ngImport: i0 }); }
|
|
244
|
+
}
|
|
245
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootSvgReferenceDirective, decorators: [{
|
|
246
|
+
type: Directive,
|
|
247
|
+
args: [{
|
|
248
|
+
selector: 'svg[rootSvgRef]'
|
|
249
|
+
}]
|
|
250
|
+
}] });
|
|
251
|
+
|
|
129
252
|
class SelectionService {
|
|
130
253
|
constructor() {
|
|
131
254
|
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
@@ -172,6 +295,7 @@ class MapContextDirective {
|
|
|
172
295
|
this.host = inject(ElementRef).nativeElement;
|
|
173
296
|
this.selectionService = inject(SelectionService);
|
|
174
297
|
this.viewportService = inject(ViewportService);
|
|
298
|
+
this.flowSettingsService = inject(FlowSettingsService);
|
|
175
299
|
this.rootSvgSelection = select(this.rootSvg);
|
|
176
300
|
this.zoomableSelection = select(this.host);
|
|
177
301
|
this.viewportForSelection = {};
|
|
@@ -184,19 +308,25 @@ class MapContextDirective {
|
|
|
184
308
|
}
|
|
185
309
|
// If only zoom provided
|
|
186
310
|
if (isDefined(state.zoom) && (!isDefined(state.x) && !isDefined(state.y))) {
|
|
187
|
-
this.rootSvgSelection
|
|
311
|
+
this.rootSvgSelection
|
|
312
|
+
.transition().duration(viewport.duration)
|
|
313
|
+
.call(this.zoomBehavior.scaleTo, state.zoom);
|
|
188
314
|
return;
|
|
189
315
|
}
|
|
190
316
|
// If only pan provided
|
|
191
317
|
if ((isDefined(state.x) && isDefined(state.y)) && !isDefined(state.zoom)) {
|
|
192
318
|
// remain same zoom value
|
|
193
319
|
const zoom = untracked(this.viewportService.readableViewport).zoom;
|
|
194
|
-
this.rootSvgSelection
|
|
320
|
+
this.rootSvgSelection
|
|
321
|
+
.transition().duration(viewport.duration)
|
|
322
|
+
.call(this.zoomBehavior.transform, zoomIdentity.translate(state.x, state.y).scale(zoom));
|
|
195
323
|
return;
|
|
196
324
|
}
|
|
197
325
|
// If whole viewort state provided
|
|
198
326
|
if (isDefined(state.x) && isDefined(state.y) && isDefined(state.zoom)) {
|
|
199
|
-
this.rootSvgSelection
|
|
327
|
+
this.rootSvgSelection
|
|
328
|
+
.transition().duration(viewport.duration)
|
|
329
|
+
.call(this.zoomBehavior.transform, zoomIdentity.translate(state.x, state.y).scale(state.zoom));
|
|
200
330
|
return;
|
|
201
331
|
}
|
|
202
332
|
}, { allowSignalWrites: true });
|
|
@@ -208,7 +338,7 @@ class MapContextDirective {
|
|
|
208
338
|
}
|
|
209
339
|
ngOnInit() {
|
|
210
340
|
this.zoomBehavior = zoom()
|
|
211
|
-
.scaleExtent([this.minZoom, this.maxZoom])
|
|
341
|
+
.scaleExtent([this.flowSettingsService.minZoom(), this.flowSettingsService.maxZoom()])
|
|
212
342
|
.on('start', (event) => this.onD3zoomStart(event))
|
|
213
343
|
.on('zoom', (event) => this.handleZoom(event))
|
|
214
344
|
.on('end', (event) => this.onD3zoomEnd(event));
|
|
@@ -230,16 +360,12 @@ class MapContextDirective {
|
|
|
230
360
|
this.selectionService.setViewport(this.viewportForSelection);
|
|
231
361
|
}
|
|
232
362
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MapContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
233
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: MapContextDirective, selector: "g[mapContext]",
|
|
363
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: MapContextDirective, selector: "g[mapContext]", ngImport: i0 }); }
|
|
234
364
|
}
|
|
235
365
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: MapContextDirective, decorators: [{
|
|
236
366
|
type: Directive,
|
|
237
367
|
args: [{ selector: 'g[mapContext]' }]
|
|
238
|
-
}]
|
|
239
|
-
type: Input
|
|
240
|
-
}], maxZoom: [{
|
|
241
|
-
type: Input
|
|
242
|
-
}] } });
|
|
368
|
+
}] });
|
|
243
369
|
const mapTransformToViewportState = (transform) => ({ zoom: transform.k, x: transform.x, y: transform.y });
|
|
244
370
|
const evTarget = (anyEvent) => {
|
|
245
371
|
if (anyEvent instanceof Event && anyEvent.target instanceof Element) {
|
|
@@ -385,14 +511,14 @@ class FlowStatusService {
|
|
|
385
511
|
setIdleStatus() {
|
|
386
512
|
this.status.set({ state: 'idle', payload: null });
|
|
387
513
|
}
|
|
388
|
-
setConnectionStartStatus(
|
|
389
|
-
this.status.set({ state: 'connection-start', payload: {
|
|
514
|
+
setConnectionStartStatus(source, sourceHandle) {
|
|
515
|
+
this.status.set({ state: 'connection-start', payload: { source, sourceHandle } });
|
|
390
516
|
}
|
|
391
|
-
setConnectionValidationStatus(valid,
|
|
392
|
-
this.status.set({ state: 'connection-validation', payload: {
|
|
517
|
+
setConnectionValidationStatus(valid, source, target, sourceHandle, targetHandle) {
|
|
518
|
+
this.status.set({ state: 'connection-validation', payload: { source, target, sourceHandle, targetHandle, valid } });
|
|
393
519
|
}
|
|
394
|
-
setConnectionEndStatus(
|
|
395
|
-
this.status.set({ state: 'connection-end', payload: {
|
|
520
|
+
setConnectionEndStatus(source, target, sourceHandle, targetHandle) {
|
|
521
|
+
this.status.set({ state: 'connection-end', payload: { source, target, sourceHandle, targetHandle } });
|
|
396
522
|
}
|
|
397
523
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
398
524
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowStatusService }); }
|
|
@@ -415,6 +541,36 @@ function batchStatusChanges(...changes) {
|
|
|
415
541
|
}
|
|
416
542
|
}
|
|
417
543
|
|
|
544
|
+
/**
|
|
545
|
+
* This function contains a hack-y behavior.
|
|
546
|
+
* If the handles are of the same type (source-source or target-target),
|
|
547
|
+
* it returns nodes where source === target and
|
|
548
|
+
* handles where sourceHandle === targetHandle
|
|
549
|
+
*
|
|
550
|
+
* This leads to that notSelfValidator returns false for these cases,
|
|
551
|
+
* exactly what we need for strict connection type
|
|
552
|
+
*/
|
|
553
|
+
function adjustDirection(connection) {
|
|
554
|
+
const result = {};
|
|
555
|
+
if (connection.sourceHandle.rawHandle.type === 'source') {
|
|
556
|
+
result.source = connection.source;
|
|
557
|
+
result.sourceHandle = connection.sourceHandle;
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
result.source = connection.target;
|
|
561
|
+
result.sourceHandle = connection.targetHandle;
|
|
562
|
+
}
|
|
563
|
+
if (connection.targetHandle.rawHandle.type === 'target') {
|
|
564
|
+
result.target = connection.target;
|
|
565
|
+
result.targetHandle = connection.targetHandle;
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
result.target = connection.source;
|
|
569
|
+
result.targetHandle = connection.sourceHandle;
|
|
570
|
+
}
|
|
571
|
+
return result;
|
|
572
|
+
}
|
|
573
|
+
|
|
418
574
|
class ConnectionControllerDirective {
|
|
419
575
|
constructor() {
|
|
420
576
|
/**
|
|
@@ -431,18 +587,96 @@ class ConnectionControllerDirective {
|
|
|
431
587
|
this.connectEffect = effect(() => {
|
|
432
588
|
const status = this.statusService.status();
|
|
433
589
|
if (status.state === 'connection-end') {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
590
|
+
let source = status.payload.source;
|
|
591
|
+
let target = status.payload.target;
|
|
592
|
+
let sourceHandle = status.payload.sourceHandle;
|
|
593
|
+
let targetHandle = status.payload.targetHandle;
|
|
594
|
+
if (this.isStrictMode()) {
|
|
595
|
+
const adjusted = adjustDirection({
|
|
596
|
+
source: status.payload.source,
|
|
597
|
+
sourceHandle: status.payload.sourceHandle,
|
|
598
|
+
target: status.payload.target,
|
|
599
|
+
targetHandle: status.payload.targetHandle
|
|
600
|
+
});
|
|
601
|
+
source = adjusted.source;
|
|
602
|
+
target = adjusted.target;
|
|
603
|
+
sourceHandle = adjusted.sourceHandle;
|
|
604
|
+
targetHandle = adjusted.targetHandle;
|
|
605
|
+
}
|
|
606
|
+
const sourceId = source.node.id;
|
|
607
|
+
const targetId = target.node.id;
|
|
608
|
+
const sourceHandleId = sourceHandle.rawHandle.id;
|
|
609
|
+
const targetHandleId = targetHandle.rawHandle.id;
|
|
610
|
+
const connectionModel = this.flowEntitiesService.connection();
|
|
611
|
+
const connection = {
|
|
612
|
+
source: sourceId, target: targetId,
|
|
613
|
+
sourceHandle: sourceHandleId, targetHandle: targetHandleId
|
|
614
|
+
};
|
|
615
|
+
if (connectionModel.validator(connection)) {
|
|
616
|
+
this.onConnect.emit(connection);
|
|
443
617
|
}
|
|
444
618
|
}
|
|
445
619
|
}, { allowSignalWrites: true });
|
|
620
|
+
this.isStrictMode = computed(() => this.flowEntitiesService.connection().mode === 'strict');
|
|
621
|
+
}
|
|
622
|
+
startConnection(handle) {
|
|
623
|
+
this.statusService.setConnectionStartStatus(handle.parentNode, handle);
|
|
624
|
+
}
|
|
625
|
+
validateConnection(handle) {
|
|
626
|
+
const status = this.statusService.status();
|
|
627
|
+
if (status.state === 'connection-start') {
|
|
628
|
+
let source = status.payload.source;
|
|
629
|
+
let target = handle.parentNode;
|
|
630
|
+
let sourceHandle = status.payload.sourceHandle;
|
|
631
|
+
let targetHandle = handle;
|
|
632
|
+
if (this.isStrictMode()) {
|
|
633
|
+
// swap direction (if needed) according to actual source and target of strict mode
|
|
634
|
+
const adjusted = adjustDirection({
|
|
635
|
+
source: status.payload.source,
|
|
636
|
+
sourceHandle: status.payload.sourceHandle,
|
|
637
|
+
target: handle.parentNode,
|
|
638
|
+
targetHandle: handle
|
|
639
|
+
});
|
|
640
|
+
source = adjusted.source;
|
|
641
|
+
target = adjusted.target;
|
|
642
|
+
sourceHandle = adjusted.sourceHandle;
|
|
643
|
+
targetHandle = adjusted.targetHandle;
|
|
644
|
+
}
|
|
645
|
+
const valid = this.flowEntitiesService.connection().validator({
|
|
646
|
+
source: source.node.id,
|
|
647
|
+
target: target.node.id,
|
|
648
|
+
sourceHandle: sourceHandle.rawHandle.id,
|
|
649
|
+
targetHandle: targetHandle.rawHandle.id
|
|
650
|
+
});
|
|
651
|
+
// TODO: check how react flow handles highlight of handle
|
|
652
|
+
// if direction changes
|
|
653
|
+
handle.state.set(valid ? 'valid' : 'invalid');
|
|
654
|
+
// status is about how we draw connection, so we don't need
|
|
655
|
+
// swapped diretion here
|
|
656
|
+
this.statusService.setConnectionValidationStatus(valid, status.payload.source, handle.parentNode, status.payload.sourceHandle, handle);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
resetValidateConnection(targetHandle) {
|
|
660
|
+
targetHandle.state.set('idle');
|
|
661
|
+
// drop back to start status
|
|
662
|
+
const status = this.statusService.status();
|
|
663
|
+
if (status.state === 'connection-validation') {
|
|
664
|
+
this.statusService.setConnectionStartStatus(status.payload.source, status.payload.sourceHandle);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
endConnection(handle) {
|
|
668
|
+
const status = this.statusService.status();
|
|
669
|
+
if (status.state === 'connection-validation') {
|
|
670
|
+
const source = status.payload.source;
|
|
671
|
+
const sourceHandle = status.payload.sourceHandle;
|
|
672
|
+
const target = status.payload.target;
|
|
673
|
+
const targetHandle = status.payload.targetHandle;
|
|
674
|
+
batchStatusChanges(
|
|
675
|
+
// call to create connection
|
|
676
|
+
() => this.statusService.setConnectionEndStatus(source, target, sourceHandle, targetHandle),
|
|
677
|
+
// when connection created, we need go back to idle status
|
|
678
|
+
() => this.statusService.setIdleStatus());
|
|
679
|
+
}
|
|
446
680
|
}
|
|
447
681
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ConnectionControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
448
682
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ConnectionControllerDirective, isStandalone: true, selector: "[connectionController]", outputs: { onConnect: "onConnect" }, ngImport: i0 }); }
|
|
@@ -457,29 +691,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
457
691
|
type: Output
|
|
458
692
|
}] } });
|
|
459
693
|
|
|
460
|
-
class FlowSettingsService {
|
|
461
|
-
constructor() {
|
|
462
|
-
this.entitiesSelectable = signal(true);
|
|
463
|
-
/**
|
|
464
|
-
* Global setting with handle positions. Nodes derive this value
|
|
465
|
-
*
|
|
466
|
-
* @deprecated
|
|
467
|
-
*/
|
|
468
|
-
this.handlePositions = signal({ source: 'right', target: 'left' });
|
|
469
|
-
/**
|
|
470
|
-
* @see {VflowComponent.view}
|
|
471
|
-
*/
|
|
472
|
-
this.view = signal([400, 400]);
|
|
473
|
-
this.flowWidth = computed(() => this.view() === 'auto' ? '100%' : this.view()[0]);
|
|
474
|
-
this.flowHeight = computed(() => this.view() === 'auto' ? '100%' : this.view()[1]);
|
|
475
|
-
}
|
|
476
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
477
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService }); }
|
|
478
|
-
}
|
|
479
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSettingsService, decorators: [{
|
|
480
|
-
type: Injectable
|
|
481
|
-
}] });
|
|
482
|
-
|
|
483
694
|
class ComponentEventBusService {
|
|
484
695
|
constructor() {
|
|
485
696
|
this._event$ = new Subject();
|
|
@@ -832,7 +1043,7 @@ class NodesChangeService {
|
|
|
832
1043
|
this.changes$ = merge(this.nodesPositionChange$, this.nodeAddChange$, this.nodeRemoveChange$, this.nodeSelectedChange$).pipe(
|
|
833
1044
|
// this fixes a bug when on fire node event change,
|
|
834
1045
|
// you can't get valid list of detached edges
|
|
835
|
-
observeOn(
|
|
1046
|
+
observeOn(animationFrameScheduler));
|
|
836
1047
|
}
|
|
837
1048
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
838
1049
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodesChangeService }); }
|
|
@@ -1040,34 +1251,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
1040
1251
|
type: Injectable
|
|
1041
1252
|
}] });
|
|
1042
1253
|
|
|
1043
|
-
class
|
|
1254
|
+
class RootPointerDirective {
|
|
1044
1255
|
constructor() {
|
|
1045
|
-
this.
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1256
|
+
this.host = inject(ElementRef).nativeElement;
|
|
1257
|
+
this.initialTouch$ = new Subject();
|
|
1258
|
+
// TODO: do not emit if mouse not down
|
|
1259
|
+
this.mouseMovement$ = fromEvent(this.host, 'mousemove').pipe(map(event => ({
|
|
1260
|
+
x: event.clientX,
|
|
1261
|
+
y: event.clientY,
|
|
1262
|
+
originalEvent: event
|
|
1263
|
+
})), observeOn(animationFrameScheduler), share());
|
|
1264
|
+
this.touchMovement$ = merge(this.initialTouch$, fromEvent(this.host, 'touchmove')).pipe(tap((event) => event.preventDefault()), map((originalEvent) => {
|
|
1265
|
+
const x = originalEvent.touches[0]?.clientX ?? 0;
|
|
1266
|
+
const y = originalEvent.touches[0]?.clientY ?? 0;
|
|
1267
|
+
const target = document.elementFromPoint(x, y);
|
|
1268
|
+
return { x, y, target, originalEvent };
|
|
1269
|
+
}), observeOn(animationFrameScheduler), share());
|
|
1270
|
+
this.touchEnd$ = fromEvent(this.host, 'touchend').pipe(map((originalEvent) => {
|
|
1271
|
+
const x = originalEvent.changedTouches[0]?.clientX ?? 0;
|
|
1272
|
+
const y = originalEvent.changedTouches[0]?.clientY ?? 0;
|
|
1273
|
+
const target = document.elementFromPoint(x, y);
|
|
1274
|
+
return { x, y, target, originalEvent };
|
|
1275
|
+
}), share());
|
|
1276
|
+
this.pointerMovement$ = merge(this.mouseMovement$, this.touchMovement$);
|
|
1052
1277
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1278
|
+
/**
|
|
1279
|
+
* We should know when user started a touch in order to not
|
|
1280
|
+
* show old touch position when connection creation is started
|
|
1281
|
+
*/
|
|
1282
|
+
setInitialTouch(event) {
|
|
1283
|
+
this.initialTouch$.next(event);
|
|
1058
1284
|
}
|
|
1059
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type:
|
|
1060
|
-
static { this.ɵ
|
|
1285
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1286
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: RootPointerDirective, selector: "svg[rootPointer]", ngImport: i0 }); }
|
|
1061
1287
|
}
|
|
1062
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type:
|
|
1063
|
-
type:
|
|
1288
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: RootPointerDirective, decorators: [{
|
|
1289
|
+
type: Directive,
|
|
1290
|
+
args: [{ selector: 'svg[rootPointer]' }]
|
|
1064
1291
|
}] });
|
|
1065
1292
|
|
|
1066
|
-
class
|
|
1067
|
-
constructor(
|
|
1068
|
-
this.
|
|
1069
|
-
this.
|
|
1070
|
-
this.
|
|
1293
|
+
class SpacePointContextDirective {
|
|
1294
|
+
constructor() {
|
|
1295
|
+
this.pointerMovementDirective = inject(RootPointerDirective);
|
|
1296
|
+
this.rootSvg = inject(RootSvgReferenceDirective).element;
|
|
1297
|
+
this.host = inject(ElementRef).nativeElement;
|
|
1298
|
+
/**
|
|
1299
|
+
* Signal with current mouse position in svg space
|
|
1300
|
+
*/
|
|
1301
|
+
this.svgCurrentSpacePoint = computed(() => {
|
|
1302
|
+
const movement = this.pointerMovement();
|
|
1303
|
+
if (!movement) {
|
|
1304
|
+
return { x: 0, y: 0 };
|
|
1305
|
+
}
|
|
1306
|
+
return this.documentPointToFlowPoint({
|
|
1307
|
+
x: movement.x,
|
|
1308
|
+
y: movement.y
|
|
1309
|
+
});
|
|
1310
|
+
});
|
|
1311
|
+
this.pointerMovement = toSignal(this.pointerMovementDirective.pointerMovement$);
|
|
1312
|
+
}
|
|
1313
|
+
documentPointToFlowPoint(documentPoint) {
|
|
1314
|
+
const point = this.rootSvg.createSVGPoint();
|
|
1315
|
+
point.x = documentPoint.x;
|
|
1316
|
+
point.y = documentPoint.y;
|
|
1317
|
+
return point.matrixTransform(this.host.getScreenCTM().inverse());
|
|
1318
|
+
}
|
|
1319
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1320
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SpacePointContextDirective, selector: "g[spacePointContext]", ngImport: i0 }); }
|
|
1321
|
+
}
|
|
1322
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SpacePointContextDirective, decorators: [{
|
|
1323
|
+
type: Directive,
|
|
1324
|
+
args: [{ selector: 'g[spacePointContext]' }]
|
|
1325
|
+
}] });
|
|
1326
|
+
|
|
1327
|
+
class HandleService {
|
|
1328
|
+
constructor() {
|
|
1329
|
+
this.node = signal(null);
|
|
1330
|
+
}
|
|
1331
|
+
createHandle(newHandle) {
|
|
1332
|
+
const node = this.node();
|
|
1333
|
+
if (node) {
|
|
1334
|
+
node.handles.update(handles => [...handles, newHandle]);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
destroyHandle(handleToDestoy) {
|
|
1338
|
+
const node = this.node();
|
|
1339
|
+
if (node) {
|
|
1340
|
+
node.handles.update(handles => handles.filter(handle => handle !== handleToDestoy));
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1344
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleService }); }
|
|
1345
|
+
}
|
|
1346
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleService, decorators: [{
|
|
1347
|
+
type: Injectable
|
|
1348
|
+
}] });
|
|
1349
|
+
|
|
1350
|
+
function resizable(elems) {
|
|
1351
|
+
return new Observable((subscriber) => {
|
|
1352
|
+
let ro = new ResizeObserver((entries) => {
|
|
1353
|
+
subscriber.next(entries);
|
|
1354
|
+
});
|
|
1355
|
+
elems.forEach(e => ro.observe(e));
|
|
1356
|
+
return () => ro.disconnect();
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
function InjectionContext(target, key, descriptor) {
|
|
1361
|
+
const originalMethod = descriptor.value;
|
|
1362
|
+
descriptor.value = function (...args) {
|
|
1363
|
+
if (implementsWithInjector(this)) {
|
|
1364
|
+
return runInInjectionContext(this.injector, () => originalMethod.apply(this, args));
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
throw new Error('Class that contains decorated method must extends WithInjectorDirective class');
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
// Return the modified descriptor
|
|
1371
|
+
return descriptor;
|
|
1372
|
+
}
|
|
1373
|
+
const implementsWithInjector = (instance) => {
|
|
1374
|
+
return 'injector' in instance && 'get' in instance.injector;
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
function Microtask(target, key, descriptor) {
|
|
1378
|
+
const originalMethod = descriptor.value;
|
|
1379
|
+
descriptor.value = function (...args) {
|
|
1380
|
+
queueMicrotask(() => {
|
|
1381
|
+
originalMethod?.apply(this, args);
|
|
1382
|
+
});
|
|
1383
|
+
};
|
|
1384
|
+
// Return the modified descriptor
|
|
1385
|
+
return descriptor;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
class HandleModel {
|
|
1389
|
+
constructor(rawHandle, parentNode) {
|
|
1390
|
+
this.rawHandle = rawHandle;
|
|
1391
|
+
this.parentNode = parentNode;
|
|
1392
|
+
this.strokeWidth = 2;
|
|
1071
1393
|
/**
|
|
1072
1394
|
* Pre-computed size for default handle, changed dynamically
|
|
1073
1395
|
* for custom handles
|
|
@@ -1138,43 +1460,46 @@ class HandleModel {
|
|
|
1138
1460
|
}
|
|
1139
1461
|
}
|
|
1140
1462
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
|
|
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;
|
|
1463
|
+
class HandleComponent {
|
|
1464
|
+
constructor() {
|
|
1465
|
+
this.injector = inject(Injector);
|
|
1466
|
+
this.handleService = inject(HandleService);
|
|
1467
|
+
this.element = inject(ElementRef).nativeElement;
|
|
1468
|
+
}
|
|
1469
|
+
ngOnInit() {
|
|
1470
|
+
this.model = new HandleModel({
|
|
1471
|
+
position: this.position,
|
|
1472
|
+
type: this.type,
|
|
1473
|
+
id: this.id,
|
|
1474
|
+
parentReference: this.element.parentElement,
|
|
1475
|
+
template: this.template
|
|
1476
|
+
}, this.handleService.node());
|
|
1477
|
+
this.handleService.createHandle(this.model);
|
|
1478
|
+
requestAnimationFrame(() => this.model.updateParent());
|
|
1479
|
+
}
|
|
1480
|
+
ngOnDestroy() {
|
|
1481
|
+
this.handleService.destroyHandle(this.model);
|
|
1482
|
+
}
|
|
1483
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1484
|
+
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
1485
|
}
|
|
1486
|
+
__decorate([
|
|
1487
|
+
InjectionContext
|
|
1488
|
+
], HandleComponent.prototype, "ngOnInit", null);
|
|
1489
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HandleComponent, decorators: [{
|
|
1490
|
+
type: Component,
|
|
1491
|
+
args: [{ selector: 'handle', changeDetection: ChangeDetectionStrategy.OnPush, template: "" }]
|
|
1492
|
+
}], propDecorators: { position: [{
|
|
1493
|
+
type: Input,
|
|
1494
|
+
args: [{ required: true }]
|
|
1495
|
+
}], type: [{
|
|
1496
|
+
type: Input,
|
|
1497
|
+
args: [{ required: true }]
|
|
1498
|
+
}], id: [{
|
|
1499
|
+
type: Input
|
|
1500
|
+
}], template: [{
|
|
1501
|
+
type: Input
|
|
1502
|
+
}], ngOnInit: [] } });
|
|
1178
1503
|
|
|
1179
1504
|
class HandleSizeControllerDirective {
|
|
1180
1505
|
constructor() {
|
|
@@ -1212,45 +1537,6 @@ function getChildStrokeWidth(element) {
|
|
|
1212
1537
|
return 0;
|
|
1213
1538
|
}
|
|
1214
1539
|
|
|
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
1540
|
class PointerDirective {
|
|
1255
1541
|
constructor() {
|
|
1256
1542
|
this.hostElement = inject(ElementRef).nativeElement;
|
|
@@ -1336,28 +1622,27 @@ class NodeComponent {
|
|
|
1336
1622
|
this.handleService = inject(HandleService);
|
|
1337
1623
|
this.draggableService = inject(DraggableService);
|
|
1338
1624
|
this.flowStatusService = inject(FlowStatusService);
|
|
1339
|
-
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
1340
1625
|
this.nodeRenderingService = inject(NodeRenderingService);
|
|
1341
1626
|
this.flowSettingsService = inject(FlowSettingsService);
|
|
1342
1627
|
this.selectionService = inject(SelectionService);
|
|
1343
1628
|
this.hostRef = inject(ElementRef);
|
|
1629
|
+
this.connectionController = inject(ConnectionControllerDirective);
|
|
1344
1630
|
this.showMagnet = computed(() => this.flowStatusService.status().state === 'connection-start' ||
|
|
1345
1631
|
this.flowStatusService.status().state === 'connection-validation');
|
|
1346
1632
|
this.styleWidth = computed(() => `${this.nodeModel.size().width}px`);
|
|
1347
1633
|
this.styleHeight = computed(() => `${this.nodeModel.size().height}px`);
|
|
1348
|
-
this.subscription = new Subscription();
|
|
1349
1634
|
}
|
|
1350
1635
|
ngOnInit() {
|
|
1351
1636
|
this.handleService.node.set(this.nodeModel);
|
|
1352
1637
|
this.draggableService.toggleDraggable(this.hostRef.nativeElement, this.nodeModel);
|
|
1353
|
-
|
|
1354
|
-
.pipe(switchMap((handles) => resizable(handles.map(h => h.parentReference))
|
|
1355
|
-
|
|
1638
|
+
this.nodeModel.handles$
|
|
1639
|
+
.pipe(switchMap((handles) => resizable(handles.map(h => h.parentReference)).pipe(map(() => handles))), tap((handles) => {
|
|
1640
|
+
// TODO (performance) inspect how to avoid calls of this when flow initially rendered
|
|
1641
|
+
handles.forEach(h => h.updateParent());
|
|
1642
|
+
}), takeUntilDestroyed())
|
|
1356
1643
|
.subscribe();
|
|
1357
|
-
this.subscription.add(sub);
|
|
1358
1644
|
}
|
|
1359
1645
|
ngAfterViewInit() {
|
|
1360
|
-
this.setInitialHandles();
|
|
1361
1646
|
if (this.nodeModel.node.type === 'default') {
|
|
1362
1647
|
this.nodeModel.size.set({
|
|
1363
1648
|
width: this.nodeModel.node.width ?? NodeModel.defaultTypeSize.width,
|
|
@@ -1365,69 +1650,30 @@ class NodeComponent {
|
|
|
1365
1650
|
});
|
|
1366
1651
|
}
|
|
1367
1652
|
if (this.nodeModel.node.type === 'html-template' || this.nodeModel.isComponentType) {
|
|
1368
|
-
|
|
1653
|
+
resizable([this.htmlWrapperRef.nativeElement])
|
|
1369
1654
|
.pipe(startWith(null), tap(() => {
|
|
1370
1655
|
const width = this.htmlWrapperRef.nativeElement.clientWidth;
|
|
1371
1656
|
const height = this.htmlWrapperRef.nativeElement.clientHeight;
|
|
1372
1657
|
this.nodeModel.size.set({ width, height });
|
|
1373
|
-
})).subscribe();
|
|
1374
|
-
this.subscription.add(sub);
|
|
1658
|
+
}), takeUntilDestroyed()).subscribe();
|
|
1375
1659
|
}
|
|
1376
1660
|
}
|
|
1377
1661
|
ngOnDestroy() {
|
|
1378
1662
|
this.draggableService.destroy(this.hostRef.nativeElement);
|
|
1379
|
-
this.subscription.unsubscribe();
|
|
1380
1663
|
}
|
|
1381
1664
|
startConnection(event, handle) {
|
|
1382
1665
|
// ignore drag by stopping propagation
|
|
1383
1666
|
event.stopPropagation();
|
|
1384
|
-
this.
|
|
1667
|
+
this.connectionController.startConnection(handle);
|
|
1385
1668
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
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
|
-
}
|
|
1669
|
+
validateConnection(handle) {
|
|
1670
|
+
this.connectionController.validateConnection(handle);
|
|
1399
1671
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
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
|
-
}
|
|
1672
|
+
resetValidateConnection(targetHandle) {
|
|
1673
|
+
this.connectionController.resetValidateConnection(targetHandle);
|
|
1420
1674
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
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
|
-
}
|
|
1675
|
+
endConnection(handle) {
|
|
1676
|
+
this.connectionController.endConnection(handle);
|
|
1431
1677
|
}
|
|
1432
1678
|
pullNode() {
|
|
1433
1679
|
this.nodeRenderingService.pullNode(this.nodeModel);
|
|
@@ -1437,32 +1683,20 @@ class NodeComponent {
|
|
|
1437
1683
|
this.selectionService.select(this.nodeModel);
|
|
1438
1684
|
}
|
|
1439
1685
|
}
|
|
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
1686
|
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 [
|
|
1687
|
+
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
1688
|
}
|
|
1457
1689
|
__decorate([
|
|
1458
|
-
|
|
1459
|
-
], NodeComponent.prototype, "
|
|
1690
|
+
InjectionContext
|
|
1691
|
+
], NodeComponent.prototype, "ngOnInit", null);
|
|
1460
1692
|
__decorate([
|
|
1693
|
+
Microtask // TODO (performance) check if we need microtask here
|
|
1694
|
+
,
|
|
1461
1695
|
InjectionContext
|
|
1462
|
-
], NodeComponent.prototype, "
|
|
1696
|
+
], NodeComponent.prototype, "ngAfterViewInit", null);
|
|
1463
1697
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
|
|
1464
1698
|
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 [
|
|
1699
|
+
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
1700
|
}], propDecorators: { nodeModel: [{
|
|
1467
1701
|
type: Input
|
|
1468
1702
|
}], nodeHtmlTemplate: [{
|
|
@@ -1473,7 +1707,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
1473
1707
|
}], htmlWrapperRef: [{
|
|
1474
1708
|
type: ViewChild,
|
|
1475
1709
|
args: ['htmlWrapper']
|
|
1476
|
-
}],
|
|
1710
|
+
}], ngOnInit: [], ngAfterViewInit: [] } });
|
|
1477
1711
|
|
|
1478
1712
|
class EdgeLabelComponent {
|
|
1479
1713
|
constructor() {
|
|
@@ -1578,34 +1812,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
1578
1812
|
type: Input
|
|
1579
1813
|
}] } });
|
|
1580
1814
|
|
|
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
1815
|
class ConnectionComponent {
|
|
1610
1816
|
constructor() {
|
|
1611
1817
|
this.flowStatusService = inject(FlowStatusService);
|
|
@@ -1643,7 +1849,7 @@ class ConnectionComponent {
|
|
|
1643
1849
|
return null;
|
|
1644
1850
|
});
|
|
1645
1851
|
this.markerUrl = computed(() => {
|
|
1646
|
-
const marker = this.model.
|
|
1852
|
+
const marker = this.model.settings.marker;
|
|
1647
1853
|
if (marker) {
|
|
1648
1854
|
return `url(#${hashCode(JSON.stringify(marker))})`;
|
|
1649
1855
|
}
|
|
@@ -1784,11 +1990,11 @@ class BackgroundComponent {
|
|
|
1784
1990
|
});
|
|
1785
1991
|
}
|
|
1786
1992
|
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"] }] }); }
|
|
1993
|
+
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
1994
|
}
|
|
1789
1995
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: BackgroundComponent, decorators: [{
|
|
1790
1996
|
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" }]
|
|
1997
|
+
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
1998
|
}], ctorParameters: function () { return []; }, propDecorators: { background: [{
|
|
1793
1999
|
type: Input,
|
|
1794
2000
|
args: [{ required: true, transform }]
|
|
@@ -1825,6 +2031,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
1825
2031
|
args: ['document:touchend']
|
|
1826
2032
|
}] } });
|
|
1827
2033
|
|
|
2034
|
+
class FlowSizeControllerDirective {
|
|
2035
|
+
constructor() {
|
|
2036
|
+
this.host = inject(ElementRef);
|
|
2037
|
+
this.flowSettingsService = inject(FlowSettingsService);
|
|
2038
|
+
this.flowWidth = 0;
|
|
2039
|
+
this.flowHeight = 0;
|
|
2040
|
+
effect(() => {
|
|
2041
|
+
const view = this.flowSettingsService.view();
|
|
2042
|
+
this.flowWidth = view === 'auto' ? '100%' : view[0];
|
|
2043
|
+
this.flowHeight = view === 'auto' ? '100%' : view[1];
|
|
2044
|
+
});
|
|
2045
|
+
resizable([this.host.nativeElement]).pipe(tap(([entry]) => {
|
|
2046
|
+
this.flowSettingsService.computedFlowWidth.set(entry.contentRect.width);
|
|
2047
|
+
this.flowSettingsService.computedFlowHeight.set(entry.contentRect.height);
|
|
2048
|
+
}), takeUntilDestroyed()).subscribe();
|
|
2049
|
+
}
|
|
2050
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2051
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]", host: { properties: { "attr.width": "this.flowWidth", "attr.height": "this.flowHeight" } }, ngImport: i0 }); }
|
|
2052
|
+
}
|
|
2053
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSizeControllerDirective, decorators: [{
|
|
2054
|
+
type: Directive,
|
|
2055
|
+
args: [{ selector: 'svg[flowSizeController]' }]
|
|
2056
|
+
}], ctorParameters: function () { return []; }, propDecorators: { flowWidth: [{
|
|
2057
|
+
type: HostBinding,
|
|
2058
|
+
args: ['attr.width']
|
|
2059
|
+
}], flowHeight: [{
|
|
2060
|
+
type: HostBinding,
|
|
2061
|
+
args: ['attr.height']
|
|
2062
|
+
}] } });
|
|
2063
|
+
|
|
1828
2064
|
const connectionControllerHostDirective = {
|
|
1829
2065
|
directive: ConnectionControllerDirective,
|
|
1830
2066
|
outputs: ['onConnect']
|
|
@@ -1871,14 +2107,6 @@ class VflowComponent {
|
|
|
1871
2107
|
this.flowSettingsService = inject(FlowSettingsService);
|
|
1872
2108
|
this.componentEventBusService = inject(ComponentEventBusService);
|
|
1873
2109
|
this.injector = inject(Injector);
|
|
1874
|
-
/**
|
|
1875
|
-
* Minimum zoom value
|
|
1876
|
-
*/
|
|
1877
|
-
this.minZoom = 0.5;
|
|
1878
|
-
/**
|
|
1879
|
-
* Maximum zoom value
|
|
1880
|
-
*/
|
|
1881
|
-
this.maxZoom = 3;
|
|
1882
2110
|
/**
|
|
1883
2111
|
* Background for flow
|
|
1884
2112
|
*/
|
|
@@ -1923,8 +2151,6 @@ class VflowComponent {
|
|
|
1923
2151
|
*/
|
|
1924
2152
|
this.edgesChange$ = this.edgesChangeService.changes$;
|
|
1925
2153
|
// #endregion
|
|
1926
|
-
this.flowWidth = this.flowSettingsService.flowWidth;
|
|
1927
|
-
this.flowHeight = this.flowSettingsService.flowHeight;
|
|
1928
2154
|
this.markers = this.flowEntitiesService.markers;
|
|
1929
2155
|
}
|
|
1930
2156
|
// #endregion
|
|
@@ -1939,6 +2165,18 @@ class VflowComponent {
|
|
|
1939
2165
|
set view(view) {
|
|
1940
2166
|
this.flowSettingsService.view.set(view);
|
|
1941
2167
|
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Minimum zoom value
|
|
2170
|
+
*/
|
|
2171
|
+
set minZoom(value) {
|
|
2172
|
+
this.flowSettingsService.minZoom.set(value);
|
|
2173
|
+
}
|
|
2174
|
+
/**
|
|
2175
|
+
* Maximum zoom value
|
|
2176
|
+
*/
|
|
2177
|
+
set maxZoom(value) {
|
|
2178
|
+
this.flowSettingsService.maxZoom.set(value);
|
|
2179
|
+
}
|
|
1942
2180
|
/**
|
|
1943
2181
|
* Object that controls flow direction.
|
|
1944
2182
|
*
|
|
@@ -1990,7 +2228,7 @@ class VflowComponent {
|
|
|
1990
2228
|
* @param viewport viewport state
|
|
1991
2229
|
*/
|
|
1992
2230
|
viewportTo(viewport) {
|
|
1993
|
-
this.viewportService.writableViewport.set({ changeType: 'absolute', state: viewport });
|
|
2231
|
+
this.viewportService.writableViewport.set({ changeType: 'absolute', state: viewport, duration: 0 });
|
|
1994
2232
|
}
|
|
1995
2233
|
/**
|
|
1996
2234
|
* Change zoom
|
|
@@ -1998,7 +2236,7 @@ class VflowComponent {
|
|
|
1998
2236
|
* @param zoom zoom value
|
|
1999
2237
|
*/
|
|
2000
2238
|
zoomTo(zoom) {
|
|
2001
|
-
this.viewportService.writableViewport.set({ changeType: 'absolute', state: { zoom } });
|
|
2239
|
+
this.viewportService.writableViewport.set({ changeType: 'absolute', state: { zoom }, duration: 0 });
|
|
2002
2240
|
}
|
|
2003
2241
|
/**
|
|
2004
2242
|
* Move to specified coordinate
|
|
@@ -2006,7 +2244,10 @@ class VflowComponent {
|
|
|
2006
2244
|
* @param point point where to move
|
|
2007
2245
|
*/
|
|
2008
2246
|
panTo(point) {
|
|
2009
|
-
this.viewportService.writableViewport.set({ changeType: 'absolute', state: point });
|
|
2247
|
+
this.viewportService.writableViewport.set({ changeType: 'absolute', state: point, duration: 0 });
|
|
2248
|
+
}
|
|
2249
|
+
fitView(options) {
|
|
2250
|
+
this.viewportService.fitView(options);
|
|
2010
2251
|
}
|
|
2011
2252
|
/**
|
|
2012
2253
|
* Get node by id
|
|
@@ -2022,6 +2263,12 @@ class VflowComponent {
|
|
|
2022
2263
|
getDetachedEdges() {
|
|
2023
2264
|
return this.flowEntitiesService.getDetachedEdges().map(e => e.edge);
|
|
2024
2265
|
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Convert point received from document to point on the flow
|
|
2268
|
+
*/
|
|
2269
|
+
documentPointToFlowPoint(point) {
|
|
2270
|
+
return this.spacePointContext.documentPointToFlowPoint(point);
|
|
2271
|
+
}
|
|
2025
2272
|
// #endregion
|
|
2026
2273
|
trackNodes(idx, { node }) {
|
|
2027
2274
|
return node;
|
|
@@ -2041,7 +2288,7 @@ class VflowComponent {
|
|
|
2041
2288
|
SelectionService,
|
|
2042
2289
|
FlowSettingsService,
|
|
2043
2290
|
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
|
|
2291
|
+
], 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 flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g [background]=\"background\"/>\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [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]" }, { kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }, { kind: "directive", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2045
2292
|
}
|
|
2046
2293
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
|
|
2047
2294
|
type: Component,
|
|
@@ -2059,7 +2306,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2059
2306
|
], hostDirectives: [
|
|
2060
2307
|
connectionControllerHostDirective,
|
|
2061
2308
|
changesControllerHostDirective
|
|
2062
|
-
], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n class=\"root-svg\"\n #flow\n
|
|
2309
|
+
], template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g [background]=\"background\"/>\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [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"] }]
|
|
2063
2310
|
}], propDecorators: { view: [{
|
|
2064
2311
|
type: Input
|
|
2065
2312
|
}], minZoom: [{
|
|
@@ -2097,49 +2344,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2097
2344
|
}], mapContext: [{
|
|
2098
2345
|
type: ViewChild,
|
|
2099
2346
|
args: [MapContextDirective]
|
|
2347
|
+
}], spacePointContext: [{
|
|
2348
|
+
type: ViewChild,
|
|
2349
|
+
args: [SpacePointContextDirective]
|
|
2100
2350
|
}] } });
|
|
2101
2351
|
|
|
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
2352
|
class SelectableDirective {
|
|
2144
2353
|
constructor() {
|
|
2145
2354
|
this.flowSettingsService = inject(FlowSettingsService);
|
|
@@ -2191,7 +2400,8 @@ const directives = [
|
|
|
2191
2400
|
HandleSizeControllerDirective,
|
|
2192
2401
|
SelectableDirective,
|
|
2193
2402
|
PointerDirective,
|
|
2194
|
-
RootPointerDirective
|
|
2403
|
+
RootPointerDirective,
|
|
2404
|
+
FlowSizeControllerDirective
|
|
2195
2405
|
];
|
|
2196
2406
|
const templateDirectives = [
|
|
2197
2407
|
NodeHtmlTemplateDirective,
|
|
@@ -2216,7 +2426,8 @@ class VflowModule {
|
|
|
2216
2426
|
HandleSizeControllerDirective,
|
|
2217
2427
|
SelectableDirective,
|
|
2218
2428
|
PointerDirective,
|
|
2219
|
-
RootPointerDirective,
|
|
2429
|
+
RootPointerDirective,
|
|
2430
|
+
FlowSizeControllerDirective, NodeHtmlTemplateDirective,
|
|
2220
2431
|
EdgeLabelHtmlTemplateDirective,
|
|
2221
2432
|
EdgeTemplateDirective,
|
|
2222
2433
|
ConnectionTemplateDirective,
|