ngx-vflow 1.14.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/vflow/components/node/node.component.mjs +5 -4
- package/esm2022/lib/vflow/interfaces/flow-entity.interface.mjs +1 -1
- package/esm2022/lib/vflow/interfaces/node.interface.mjs +13 -5
- package/esm2022/lib/vflow/interfaces/optimization.interface.mjs +2 -1
- package/esm2022/lib/vflow/interfaces/template-context.interface.mjs +1 -1
- package/esm2022/lib/vflow/models/edge.model.mjs +4 -9
- package/esm2022/lib/vflow/models/node.model.mjs +51 -7
- package/esm2022/lib/vflow/services/edge-rendering.service.mjs +1 -1
- package/esm2022/lib/vflow/services/flow-rendering.service.mjs +9 -1
- package/esm2022/lib/vflow/utils/is-callable.mjs +10 -0
- package/esm2022/lib/vflow/utils/is-vflow-component.mjs +9 -0
- package/esm2022/public-api.mjs +2 -1
- package/esm2022/testing/provide-custom-node-mocks.mjs +3 -2
- package/fesm2022/ngx-vflow-testing.mjs +2 -1
- package/fesm2022/ngx-vflow-testing.mjs.map +1 -1
- package/fesm2022/ngx-vflow.mjs +231 -167
- package/fesm2022/ngx-vflow.mjs.map +1 -1
- package/lib/vflow/interfaces/flow-entity.interface.d.ts +2 -1
- package/lib/vflow/interfaces/node.interface.d.ts +2 -2
- package/lib/vflow/interfaces/optimization.interface.d.ts +4 -0
- package/lib/vflow/interfaces/template-context.interface.d.ts +4 -0
- package/lib/vflow/models/edge.model.d.ts +2 -0
- package/lib/vflow/models/node.model.d.ts +4 -0
- package/lib/vflow/public-components/custom-template-edge/custom-template-edge.component.d.ts +1 -0
- package/lib/vflow/services/edge-rendering.service.d.ts +1 -1
- package/lib/vflow/services/flow-rendering.service.d.ts +4 -0
- package/lib/vflow/utils/is-callable.d.ts +1 -0
- package/lib/vflow/utils/is-vflow-component.d.ts +4 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
package/fesm2022/ngx-vflow.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { signal, computed, Injectable, inject, ElementRef, Directive, NgZone, effect, untracked, TemplateRef, DestroyRef, EventEmitter, OutputEmitterRef, input, assertInInjectionContext, Injector, runInInjectionContext, viewChild, Component, ChangeDetectionStrategy, output, HostListener, Renderer2, contentChild, Input, forwardRef } from '@angular/core';
|
|
3
3
|
import { select } from 'd3-selection';
|
|
4
4
|
import { zoomIdentity, zoom } from 'd3-zoom';
|
|
5
|
-
import { Subject, switchMap, merge, fromEvent, tap, Observable,
|
|
6
|
-
import { toObservable, takeUntilDestroyed,
|
|
5
|
+
import { Subject, switchMap, merge, fromEvent, tap, Observable, observeOn, asyncScheduler, filter, debounceTime, map, catchError, of, shareReplay, skip, pairwise, distinctUntilChanged, zip, animationFrameScheduler, share, startWith } from 'rxjs';
|
|
6
|
+
import { toObservable, takeUntilDestroyed, toSignal, outputFromObservable } from '@angular/core/rxjs-interop';
|
|
7
7
|
import { drag } from 'd3-drag';
|
|
8
8
|
import { __decorate } from 'tslib';
|
|
9
|
-
import { NgTemplateOutlet, NgComponentOutlet, KeyValuePipe } from '@angular/common';
|
|
9
|
+
import { NgTemplateOutlet, NgComponentOutlet, AsyncPipe, KeyValuePipe } from '@angular/common';
|
|
10
10
|
|
|
11
11
|
const getOverlappingArea = (rectA, rectB) => {
|
|
12
12
|
const xOverlap = Math.max(0, Math.min(rectA.x + rectA.width, rectB.x + rectB.width) - Math.max(rectA.x, rectB.x));
|
|
@@ -218,6 +218,7 @@ const DEFAULT_OPTIMIZATION = {
|
|
|
218
218
|
detachedGroupsLayer: false,
|
|
219
219
|
virtualization: false,
|
|
220
220
|
virtualizationZoomThreshold: 0.5,
|
|
221
|
+
lazyLoadTrigger: 'immediate',
|
|
221
222
|
};
|
|
222
223
|
|
|
223
224
|
class FlowSettingsService {
|
|
@@ -826,6 +827,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
826
827
|
type: Injectable
|
|
827
828
|
}] });
|
|
828
829
|
|
|
830
|
+
function isCallable(fn) {
|
|
831
|
+
try {
|
|
832
|
+
new Proxy(fn, { apply: () => undefined })();
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
catch (err) {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
829
840
|
class ComponentEventBusService {
|
|
830
841
|
constructor() {
|
|
831
842
|
this._event$ = new Subject();
|
|
@@ -906,7 +917,7 @@ function outputRefToObservable(ref) {
|
|
|
906
917
|
});
|
|
907
918
|
}
|
|
908
919
|
|
|
909
|
-
class
|
|
920
|
+
class CustomDynamicNodeComponent extends CustomNodeBaseComponent {
|
|
910
921
|
constructor() {
|
|
911
922
|
super(...arguments);
|
|
912
923
|
/**
|
|
@@ -915,19 +926,20 @@ class CustomNodeComponent extends CustomNodeBaseComponent {
|
|
|
915
926
|
this.node = input.required();
|
|
916
927
|
}
|
|
917
928
|
ngOnInit() {
|
|
918
|
-
|
|
919
|
-
|
|
929
|
+
const data = this.node().data;
|
|
930
|
+
if (data) {
|
|
931
|
+
this.data = data;
|
|
920
932
|
}
|
|
921
933
|
super.ngOnInit();
|
|
922
934
|
}
|
|
923
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
924
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type:
|
|
935
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CustomDynamicNodeComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
936
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type: CustomDynamicNodeComponent, inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
|
|
925
937
|
}
|
|
926
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
938
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CustomDynamicNodeComponent, decorators: [{
|
|
927
939
|
type: Directive
|
|
928
940
|
}] });
|
|
929
941
|
|
|
930
|
-
class
|
|
942
|
+
class CustomNodeComponent extends CustomNodeBaseComponent {
|
|
931
943
|
constructor() {
|
|
932
944
|
super(...arguments);
|
|
933
945
|
/**
|
|
@@ -936,19 +948,25 @@ class CustomDynamicNodeComponent extends CustomNodeBaseComponent {
|
|
|
936
948
|
this.node = input.required();
|
|
937
949
|
}
|
|
938
950
|
ngOnInit() {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
this.data = data;
|
|
951
|
+
if (this.node().data) {
|
|
952
|
+
this.data.set(this.node().data);
|
|
942
953
|
}
|
|
943
954
|
super.ngOnInit();
|
|
944
955
|
}
|
|
945
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
946
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type:
|
|
956
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CustomNodeComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
957
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "17.3.12", type: CustomNodeComponent, inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
|
|
947
958
|
}
|
|
948
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
959
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CustomNodeComponent, decorators: [{
|
|
949
960
|
type: Directive
|
|
950
961
|
}] });
|
|
951
962
|
|
|
963
|
+
function isCustomNodeComponent(type) {
|
|
964
|
+
return Object.prototype.isPrototypeOf.call(CustomNodeComponent, type);
|
|
965
|
+
}
|
|
966
|
+
function isCustomDynamicNodeComponent(type) {
|
|
967
|
+
return Object.prototype.isPrototypeOf.call(CustomDynamicNodeComponent, type);
|
|
968
|
+
}
|
|
969
|
+
|
|
952
970
|
function isStaticNode(node) {
|
|
953
971
|
return typeof node.point !== 'function';
|
|
954
972
|
}
|
|
@@ -956,10 +974,18 @@ function isDynamicNode(node) {
|
|
|
956
974
|
return typeof node.point === 'function';
|
|
957
975
|
}
|
|
958
976
|
function isComponentStaticNode(node) {
|
|
959
|
-
|
|
977
|
+
if (isCustomNodeComponent(node.type)) {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
// Check if the type is a function with dynamic import
|
|
981
|
+
return isCallable(node.type) && !isCallable(node.point);
|
|
960
982
|
}
|
|
961
983
|
function isComponentDynamicNode(node) {
|
|
962
|
-
|
|
984
|
+
if (isCustomDynamicNodeComponent(node.type)) {
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
// Check if the type is a function with dynamic import
|
|
988
|
+
return isCallable(node.type) && isCallable(node.point);
|
|
963
989
|
}
|
|
964
990
|
function isTemplateStaticNode(node) {
|
|
965
991
|
return node.type === 'html-template';
|
|
@@ -1018,6 +1044,138 @@ function toSignalProperties(obj) {
|
|
|
1018
1044
|
return newObj;
|
|
1019
1045
|
}
|
|
1020
1046
|
|
|
1047
|
+
function isGroupNode(node) {
|
|
1048
|
+
return node.rawNode.type === 'default-group' || node.rawNode.type === 'template-group';
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// MIT License
|
|
1052
|
+
// Copyright (c) 2023 Chau Tran
|
|
1053
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1054
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
1055
|
+
// in the Software without restriction, including without limitation the rights
|
|
1056
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1057
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
1058
|
+
// furnished to do so, subject to the following conditions:
|
|
1059
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
1060
|
+
// copies or substantial portions of the Software.
|
|
1061
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1062
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1063
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1064
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1065
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1066
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1067
|
+
// SOFTWARE.
|
|
1068
|
+
/* eslint-disable @typescript-eslint/ban-types */
|
|
1069
|
+
function assertInjector(fn, injector, runner) {
|
|
1070
|
+
!injector && assertInInjectionContext(fn);
|
|
1071
|
+
const assertedInjector = injector ?? inject(Injector);
|
|
1072
|
+
if (!runner)
|
|
1073
|
+
return assertedInjector;
|
|
1074
|
+
return runInInjectionContext(assertedInjector, runner);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// MIT License
|
|
1078
|
+
// Copyright (c) 2023 Chau Tran
|
|
1079
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1080
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
1081
|
+
// in the Software without restriction, including without limitation the rights
|
|
1082
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1083
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
1084
|
+
// furnished to do so, subject to the following conditions:
|
|
1085
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
1086
|
+
// copies or substantial portions of the Software.
|
|
1087
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1088
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1089
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1090
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1091
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1092
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1093
|
+
// SOFTWARE.
|
|
1094
|
+
/**
|
|
1095
|
+
* Function `toLazySignal()` is a proxy function that will call the original
|
|
1096
|
+
* `toSignal()` function when the returned signal is read for the first time.
|
|
1097
|
+
*/
|
|
1098
|
+
function toLazySignal(source, options) {
|
|
1099
|
+
const injector = assertInjector(toLazySignal, options?.injector);
|
|
1100
|
+
let s;
|
|
1101
|
+
return computed(() => {
|
|
1102
|
+
if (!s) {
|
|
1103
|
+
s = untracked(() => toSignal(source, { ...options, injector }));
|
|
1104
|
+
}
|
|
1105
|
+
return s();
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
class NodeRenderingService {
|
|
1110
|
+
constructor() {
|
|
1111
|
+
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
1112
|
+
this.flowSettingsService = inject(FlowSettingsService);
|
|
1113
|
+
this.viewportService = inject(ViewportService);
|
|
1114
|
+
this.nodes = computed(() => {
|
|
1115
|
+
if (!this.flowSettingsService.optimization().virtualization) {
|
|
1116
|
+
return [...this.flowEntitiesService.nodes()].sort((aNode, bNode) => aNode.renderOrder() - bNode.renderOrder());
|
|
1117
|
+
}
|
|
1118
|
+
return this.viewportNodesAfterInteraction().sort((aNode, bNode) => aNode.renderOrder() - bNode.renderOrder());
|
|
1119
|
+
});
|
|
1120
|
+
this.groups = computed(() => {
|
|
1121
|
+
return this.nodes().filter((n) => isGroupNode(n));
|
|
1122
|
+
});
|
|
1123
|
+
this.nonGroups = computed(() => {
|
|
1124
|
+
return this.nodes().filter((n) => !isGroupNode(n));
|
|
1125
|
+
});
|
|
1126
|
+
this.viewportNodes = computed(() => {
|
|
1127
|
+
const nodes = this.flowEntitiesService.nodes();
|
|
1128
|
+
const viewport = this.viewportService.readableViewport();
|
|
1129
|
+
const flowWidth = this.flowSettingsService.computedFlowWidth();
|
|
1130
|
+
const flowHeight = this.flowSettingsService.computedFlowHeight();
|
|
1131
|
+
return nodes.filter((n) => {
|
|
1132
|
+
const { x, y } = n.globalPoint();
|
|
1133
|
+
const width = n.width();
|
|
1134
|
+
const height = n.height();
|
|
1135
|
+
return isRectInViewport({ x, y, width, height }, viewport, flowWidth, flowHeight);
|
|
1136
|
+
});
|
|
1137
|
+
});
|
|
1138
|
+
this.viewportNodesAfterInteraction = toLazySignal(merge(
|
|
1139
|
+
// TODO: maybe there is a better way wait when viewport is ready?
|
|
1140
|
+
// (to correctly calculate viewport nodes on first render)
|
|
1141
|
+
toObservable(this.flowEntitiesService.nodes).pipe(observeOn(asyncScheduler), filter((nodes) => !!nodes.length)), this.viewportService.viewportChangeEnd$.pipe(debounceTime(300))).pipe(map(() => {
|
|
1142
|
+
const viewport = this.viewportService.readableViewport();
|
|
1143
|
+
const zoomThreshold = this.flowSettingsService.optimization().virtualizationZoomThreshold;
|
|
1144
|
+
return viewport.zoom < zoomThreshold ? [] : this.viewportNodes();
|
|
1145
|
+
})), {
|
|
1146
|
+
initialValue: [],
|
|
1147
|
+
});
|
|
1148
|
+
this.maxOrder = computed(() => {
|
|
1149
|
+
return Math.max(...this.flowEntitiesService.nodes().map((n) => n.renderOrder()));
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
pullNode(node) {
|
|
1153
|
+
// pull node
|
|
1154
|
+
node.renderOrder.set(this.maxOrder() + 1);
|
|
1155
|
+
// pull children
|
|
1156
|
+
node.children().forEach((n) => this.pullNode(n));
|
|
1157
|
+
}
|
|
1158
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeRenderingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1159
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeRenderingService }); }
|
|
1160
|
+
}
|
|
1161
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeRenderingService, decorators: [{
|
|
1162
|
+
type: Injectable
|
|
1163
|
+
}] });
|
|
1164
|
+
|
|
1165
|
+
// MIT License
|
|
1166
|
+
/**
|
|
1167
|
+
* @todo Use `linkedSignal` after Angular update
|
|
1168
|
+
*/
|
|
1169
|
+
function extendedComputed(computedCallback, options) {
|
|
1170
|
+
if (!options) {
|
|
1171
|
+
options = { equal: Object.is };
|
|
1172
|
+
}
|
|
1173
|
+
let currentValue = undefined;
|
|
1174
|
+
return computed(() => {
|
|
1175
|
+
return (currentValue = computedCallback(currentValue));
|
|
1176
|
+
}, options);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1021
1179
|
class NodeModel {
|
|
1022
1180
|
static { this.defaultWidth = 100; }
|
|
1023
1181
|
static { this.defaultHeight = 50; }
|
|
@@ -1025,6 +1183,8 @@ class NodeModel {
|
|
|
1025
1183
|
constructor(rawNode) {
|
|
1026
1184
|
this.rawNode = rawNode;
|
|
1027
1185
|
this.entitiesService = inject(FlowEntitiesService);
|
|
1186
|
+
this.settingsService = inject(FlowSettingsService);
|
|
1187
|
+
this.nodeRenderingService = inject(NodeRenderingService);
|
|
1028
1188
|
this.isVisible = signal(false);
|
|
1029
1189
|
this.point = signal({ x: 0, y: 0 });
|
|
1030
1190
|
this.width = signal(NodeModel.defaultWidth);
|
|
@@ -1063,6 +1223,39 @@ class NodeModel {
|
|
|
1063
1223
|
this.magnetRadius = 20;
|
|
1064
1224
|
// TODO: not sure if we need to statically store it
|
|
1065
1225
|
this.isComponentType = isComponentStaticNode(this.rawNode) || isComponentDynamicNode(this.rawNode);
|
|
1226
|
+
this.shouldLoad = extendedComputed((previousShouldLoad) => {
|
|
1227
|
+
if (previousShouldLoad) {
|
|
1228
|
+
return true;
|
|
1229
|
+
}
|
|
1230
|
+
if (this.settingsService.optimization().lazyLoadTrigger === 'immediate') {
|
|
1231
|
+
return true;
|
|
1232
|
+
}
|
|
1233
|
+
else if (this.settingsService.optimization().lazyLoadTrigger === 'viewport') {
|
|
1234
|
+
// Immediately load component if it's a plain class
|
|
1235
|
+
if (isCustomNodeComponent(this.rawNode.type)) {
|
|
1236
|
+
return true;
|
|
1237
|
+
}
|
|
1238
|
+
// Immediately load component if it's a plain class
|
|
1239
|
+
if (isCustomDynamicNodeComponent(this.rawNode.type)) {
|
|
1240
|
+
return true;
|
|
1241
|
+
}
|
|
1242
|
+
// For cases
|
|
1243
|
+
// - if it's a factory with dynamic import
|
|
1244
|
+
// - if it's a template (html, svg, group)
|
|
1245
|
+
// check if it's in the viewport
|
|
1246
|
+
if (isCallable(this.rawNode.type) ||
|
|
1247
|
+
this.rawNode.type === 'html-template' ||
|
|
1248
|
+
this.rawNode.type === 'svg-template' ||
|
|
1249
|
+
this.rawNode.type === 'template-group') {
|
|
1250
|
+
return this.nodeRenderingService.viewportNodes().includes(this);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
// For each other case, we want to load the component immediately
|
|
1254
|
+
return true;
|
|
1255
|
+
});
|
|
1256
|
+
this.componentInstance$ = toObservable(this.shouldLoad).pipe(filter(Boolean),
|
|
1257
|
+
// @ts-expect-error we assume it's a function with dynamic import
|
|
1258
|
+
switchMap(() => this.rawNode.type()), catchError(() => of(this.rawNode.type)), shareReplay(1));
|
|
1066
1259
|
// Default node specific thing
|
|
1067
1260
|
this.text = signal('');
|
|
1068
1261
|
// Component node specific thing
|
|
@@ -1112,7 +1305,8 @@ class NodeModel {
|
|
|
1112
1305
|
this.context = {
|
|
1113
1306
|
$implicit: {
|
|
1114
1307
|
node: rawNode,
|
|
1115
|
-
selected: this.selected,
|
|
1308
|
+
selected: this.selected.asReadonly(),
|
|
1309
|
+
shouldLoad: this.shouldLoad,
|
|
1116
1310
|
},
|
|
1117
1311
|
};
|
|
1118
1312
|
}
|
|
@@ -1120,9 +1314,10 @@ class NodeModel {
|
|
|
1120
1314
|
this.context = {
|
|
1121
1315
|
$implicit: {
|
|
1122
1316
|
node: rawNode,
|
|
1123
|
-
selected: this.selected,
|
|
1124
|
-
width: this.width,
|
|
1125
|
-
height: this.height,
|
|
1317
|
+
selected: this.selected.asReadonly(),
|
|
1318
|
+
width: this.width.asReadonly(),
|
|
1319
|
+
height: this.height.asReadonly(),
|
|
1320
|
+
shouldLoad: this.shouldLoad,
|
|
1126
1321
|
},
|
|
1127
1322
|
};
|
|
1128
1323
|
}
|
|
@@ -1131,8 +1326,9 @@ class NodeModel {
|
|
|
1131
1326
|
$implicit: {
|
|
1132
1327
|
node: rawNode,
|
|
1133
1328
|
selected: this.selected.asReadonly(),
|
|
1134
|
-
width: this.width,
|
|
1135
|
-
height: this.height,
|
|
1329
|
+
width: this.width.asReadonly(),
|
|
1330
|
+
height: this.height.asReadonly(),
|
|
1331
|
+
shouldLoad: this.shouldLoad,
|
|
1136
1332
|
},
|
|
1137
1333
|
};
|
|
1138
1334
|
}
|
|
@@ -1470,20 +1666,6 @@ function smoothStepPath({ sourcePoint, targetPoint, sourcePosition, targetPositi
|
|
|
1470
1666
|
};
|
|
1471
1667
|
}
|
|
1472
1668
|
|
|
1473
|
-
// MIT License
|
|
1474
|
-
/**
|
|
1475
|
-
* @todo Use `linkedSignal` after Angular update
|
|
1476
|
-
*/
|
|
1477
|
-
function extendedComputed(computedCallback, options) {
|
|
1478
|
-
if (!options) {
|
|
1479
|
-
options = { equal: Object.is };
|
|
1480
|
-
}
|
|
1481
|
-
let currentValue = undefined;
|
|
1482
|
-
return computed(() => {
|
|
1483
|
-
return (currentValue = computedCallback(currentValue));
|
|
1484
|
-
}, options);
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
1669
|
class EdgeModel {
|
|
1488
1670
|
constructor(edge) {
|
|
1489
1671
|
this.edge = edge;
|
|
@@ -1492,6 +1674,7 @@ class EdgeModel {
|
|
|
1492
1674
|
this.target = signal(undefined);
|
|
1493
1675
|
this.selected = signal(false);
|
|
1494
1676
|
this.selected$ = toObservable(this.selected);
|
|
1677
|
+
this.shouldLoad = computed(() => (this.source()?.shouldLoad() ?? false) && (this.target()?.shouldLoad() ?? false));
|
|
1495
1678
|
this.renderOrder = signal(0);
|
|
1496
1679
|
this.detached = computed(() => {
|
|
1497
1680
|
const source = this.source();
|
|
@@ -1521,14 +1704,7 @@ class EdgeModel {
|
|
|
1521
1704
|
const target = this.targetHandle();
|
|
1522
1705
|
// TODO: don't like this
|
|
1523
1706
|
if (!source || !target) {
|
|
1524
|
-
return {
|
|
1525
|
-
path: '',
|
|
1526
|
-
labelPoints: {
|
|
1527
|
-
start: { x: 0, y: 0 },
|
|
1528
|
-
center: { x: 0, y: 0 },
|
|
1529
|
-
end: { x: 0, y: 0 },
|
|
1530
|
-
},
|
|
1531
|
-
};
|
|
1707
|
+
return { path: '' };
|
|
1532
1708
|
}
|
|
1533
1709
|
const params = this.getPathFactoryParams(source, target);
|
|
1534
1710
|
switch (this.curve) {
|
|
@@ -1660,6 +1836,7 @@ class EdgeModel {
|
|
|
1660
1836
|
markerStart: this.markerStartUrl,
|
|
1661
1837
|
markerEnd: this.markerEndUrl,
|
|
1662
1838
|
selected: this.selected.asReadonly(),
|
|
1839
|
+
shouldLoad: this.shouldLoad,
|
|
1663
1840
|
},
|
|
1664
1841
|
};
|
|
1665
1842
|
this.edgeLabels = {};
|
|
@@ -1947,124 +2124,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1947
2124
|
}]
|
|
1948
2125
|
}] });
|
|
1949
2126
|
|
|
1950
|
-
function isGroupNode(node) {
|
|
1951
|
-
return node.rawNode.type === 'default-group' || node.rawNode.type === 'template-group';
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
// MIT License
|
|
1955
|
-
// Copyright (c) 2023 Chau Tran
|
|
1956
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1957
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1958
|
-
// in the Software without restriction, including without limitation the rights
|
|
1959
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1960
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1961
|
-
// furnished to do so, subject to the following conditions:
|
|
1962
|
-
// The above copyright notice and this permission notice shall be included in all
|
|
1963
|
-
// copies or substantial portions of the Software.
|
|
1964
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1965
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1966
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1967
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1968
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1969
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1970
|
-
// SOFTWARE.
|
|
1971
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
1972
|
-
function assertInjector(fn, injector, runner) {
|
|
1973
|
-
!injector && assertInInjectionContext(fn);
|
|
1974
|
-
const assertedInjector = injector ?? inject(Injector);
|
|
1975
|
-
if (!runner)
|
|
1976
|
-
return assertedInjector;
|
|
1977
|
-
return runInInjectionContext(assertedInjector, runner);
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
|
-
// MIT License
|
|
1981
|
-
// Copyright (c) 2023 Chau Tran
|
|
1982
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1983
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1984
|
-
// in the Software without restriction, including without limitation the rights
|
|
1985
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1986
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1987
|
-
// furnished to do so, subject to the following conditions:
|
|
1988
|
-
// The above copyright notice and this permission notice shall be included in all
|
|
1989
|
-
// copies or substantial portions of the Software.
|
|
1990
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1991
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1992
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1993
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1994
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1995
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1996
|
-
// SOFTWARE.
|
|
1997
|
-
/**
|
|
1998
|
-
* Function `toLazySignal()` is a proxy function that will call the original
|
|
1999
|
-
* `toSignal()` function when the returned signal is read for the first time.
|
|
2000
|
-
*/
|
|
2001
|
-
function toLazySignal(source, options) {
|
|
2002
|
-
const injector = assertInjector(toLazySignal, options?.injector);
|
|
2003
|
-
let s;
|
|
2004
|
-
return computed(() => {
|
|
2005
|
-
if (!s) {
|
|
2006
|
-
s = untracked(() => toSignal(source, { ...options, injector }));
|
|
2007
|
-
}
|
|
2008
|
-
return s();
|
|
2009
|
-
});
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
class NodeRenderingService {
|
|
2013
|
-
constructor() {
|
|
2014
|
-
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
2015
|
-
this.flowSettingsService = inject(FlowSettingsService);
|
|
2016
|
-
this.viewportService = inject(ViewportService);
|
|
2017
|
-
this.nodes = computed(() => {
|
|
2018
|
-
if (!this.flowSettingsService.optimization().virtualization) {
|
|
2019
|
-
return [...this.flowEntitiesService.nodes()].sort((aNode, bNode) => aNode.renderOrder() - bNode.renderOrder());
|
|
2020
|
-
}
|
|
2021
|
-
return this.viewportNodesAfterInteraction().sort((aNode, bNode) => aNode.renderOrder() - bNode.renderOrder());
|
|
2022
|
-
});
|
|
2023
|
-
this.groups = computed(() => {
|
|
2024
|
-
return this.nodes().filter((n) => isGroupNode(n));
|
|
2025
|
-
});
|
|
2026
|
-
this.nonGroups = computed(() => {
|
|
2027
|
-
return this.nodes().filter((n) => !isGroupNode(n));
|
|
2028
|
-
});
|
|
2029
|
-
this.viewportNodes = computed(() => {
|
|
2030
|
-
const nodes = this.flowEntitiesService.nodes();
|
|
2031
|
-
const viewport = this.viewportService.readableViewport();
|
|
2032
|
-
const flowWidth = this.flowSettingsService.computedFlowWidth();
|
|
2033
|
-
const flowHeight = this.flowSettingsService.computedFlowHeight();
|
|
2034
|
-
return nodes.filter((n) => {
|
|
2035
|
-
const { x, y } = n.globalPoint();
|
|
2036
|
-
const width = n.width();
|
|
2037
|
-
const height = n.height();
|
|
2038
|
-
return isRectInViewport({ x, y, width, height }, viewport, flowWidth, flowHeight);
|
|
2039
|
-
});
|
|
2040
|
-
});
|
|
2041
|
-
this.viewportNodesAfterInteraction = toLazySignal(merge(
|
|
2042
|
-
// TODO: maybe there is a better way wait when viewport is ready?
|
|
2043
|
-
// (to correctly calculate viewport nodes on first render)
|
|
2044
|
-
toObservable(this.flowEntitiesService.nodes).pipe(observeOn(asyncScheduler), filter((nodes) => !!nodes.length)), this.viewportService.viewportChangeEnd$.pipe(debounceTime(300))).pipe(map(() => {
|
|
2045
|
-
const viewport = this.viewportService.readableViewport();
|
|
2046
|
-
const zoomThreshold = this.flowSettingsService.optimization().virtualizationZoomThreshold;
|
|
2047
|
-
return viewport.zoom < zoomThreshold ? [] : this.viewportNodes();
|
|
2048
|
-
})), {
|
|
2049
|
-
initialValue: [],
|
|
2050
|
-
});
|
|
2051
|
-
this.maxOrder = computed(() => {
|
|
2052
|
-
return Math.max(...this.flowEntitiesService.nodes().map((n) => n.renderOrder()));
|
|
2053
|
-
});
|
|
2054
|
-
}
|
|
2055
|
-
pullNode(node) {
|
|
2056
|
-
// pull node
|
|
2057
|
-
node.renderOrder.set(this.maxOrder() + 1);
|
|
2058
|
-
// pull children
|
|
2059
|
-
node.children().forEach((n) => this.pullNode(n));
|
|
2060
|
-
}
|
|
2061
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeRenderingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2062
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeRenderingService }); }
|
|
2063
|
-
}
|
|
2064
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeRenderingService, decorators: [{
|
|
2065
|
-
type: Injectable
|
|
2066
|
-
}] });
|
|
2067
|
-
|
|
2068
2127
|
class RootPointerDirective {
|
|
2069
2128
|
constructor() {
|
|
2070
2129
|
this.host = inject(ElementRef).nativeElement;
|
|
@@ -3206,7 +3265,7 @@ class NodeComponent {
|
|
|
3206
3265
|
}
|
|
3207
3266
|
}
|
|
3208
3267
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3209
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NodeComponent, isStandalone: true, selector: "g[node]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, nodeSvgTemplate: { classPropertyName: "nodeSvgTemplate", publicName: "nodeSvgTemplate", isSignal: true, isRequired: false, transformFunction: null }, groupNodeTemplate: { classPropertyName: "groupNodeTemplate", publicName: "groupNodeTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "vflow-node" }, providers: [HandleService, NodeAccessorService], ngImport: i0, template: "<!-- Default node -->\n@if (model().rawNode.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- HTML Template node -->\n@if (model().rawNode.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- SVG Template node -->\n@if (model().rawNode.type === 'svg-template' && nodeSvgTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (click)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeSvgTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n <svg:foreignObject\n
|
|
3268
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NodeComponent, isStandalone: true, selector: "g[node]", inputs: { model: { classPropertyName: "model", publicName: "model", isSignal: true, isRequired: true, transformFunction: null }, nodeTemplate: { classPropertyName: "nodeTemplate", publicName: "nodeTemplate", isSignal: true, isRequired: false, transformFunction: null }, nodeSvgTemplate: { classPropertyName: "nodeSvgTemplate", publicName: "nodeSvgTemplate", isSignal: true, isRequired: false, transformFunction: null }, groupNodeTemplate: { classPropertyName: "groupNodeTemplate", publicName: "groupNodeTemplate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "vflow-node" }, providers: [HandleService, NodeAccessorService], ngImport: i0, template: "<!-- Default node -->\n@if (model().rawNode.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- HTML Template node -->\n@if (model().rawNode.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- SVG Template node -->\n@if (model().rawNode.type === 'svg-template' && nodeSvgTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (click)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeSvgTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n @if (model().componentInstance$ | async; as component) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(component)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n }\n}\n\n<!-- Default group node -->\n@if (model().rawNode.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (click)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().rawNode.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (click)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (handle.template === undefined) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template === null) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@for (toolbar of toolbars(); track toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"], dependencies: [{ kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }, { kind: "component", type: DefaultNodeComponent, selector: "default-node", inputs: ["selected"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template", "offsetX", "offsetY"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: NodeHandlesControllerDirective, selector: "[nodeHandlesController]" }, { kind: "directive", type: NodeResizeControllerDirective, selector: "[nodeResizeController]" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3210
3269
|
}
|
|
3211
3270
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NodeComponent, decorators: [{
|
|
3212
3271
|
type: Component,
|
|
@@ -3222,7 +3281,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
3222
3281
|
HandleSizeControllerDirective,
|
|
3223
3282
|
NodeHandlesControllerDirective,
|
|
3224
3283
|
NodeResizeControllerDirective,
|
|
3225
|
-
|
|
3284
|
+
AsyncPipe,
|
|
3285
|
+
], template: "<!-- Default node -->\n@if (model().rawNode.type === 'default') {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode(); selectNode()\">\n <default-node\n nodeHandlesController\n [selected]=\"model().selected()\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\"\n [style.max-width]=\"model().styleWidth()\"\n [style.max-height]=\"model().styleHeight()\">\n <div [outerHTML]=\"model().text()\"></div>\n\n <handle type=\"source\" position=\"right\" />\n <handle type=\"target\" position=\"left\" />\n </default-node>\n </svg:foreignObject>\n}\n\n<!-- HTML Template node -->\n@if (model().rawNode.type === 'html-template' && nodeTemplate()) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n}\n\n<!-- SVG Template node -->\n@if (model().rawNode.type === 'svg-template' && nodeSvgTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (click)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"nodeSvgTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Component node -->\n@if (model().isComponentType) {\n @if (model().componentInstance$ | async; as component) {\n <svg:foreignObject\n class=\"selectable\"\n [attr.width]=\"model().foWidth()\"\n [attr.height]=\"model().foHeight()\"\n (click)=\"pullNode()\">\n <div\n nodeHandlesController\n nodeResizeController\n class=\"wrapper\"\n [style.width]=\"model().styleWidth()\"\n [style.height]=\"model().styleHeight()\">\n <ng-container\n [ngComponentOutlet]=\"$any(component)\"\n [ngComponentOutletInputs]=\"model().componentTypeInputs\"\n [ngComponentOutletInjector]=\"injector\" />\n </div>\n </svg:foreignObject>\n }\n}\n\n<!-- Default group node -->\n@if (model().rawNode.type === 'default-group') {\n <svg:rect\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [resizable]=\"model().resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"model().color()\"\n [class.default-group-node_selected]=\"model().selected()\"\n [attr.width]=\"model().size().width\"\n [attr.height]=\"model().size().height\"\n [style.stroke]=\"model().color()\"\n [style.fill]=\"model().color()\"\n (click)=\"pullNode(); selectNode()\" />\n}\n\n<!-- Template group node -->\n@if (model().rawNode.type === 'template-group' && groupNodeTemplate()) {\n <svg:g class=\"selectable\" nodeHandlesController (click)=\"pullNode()\">\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate() ?? null\"\n [ngTemplateOutletContext]=\"model().context\"\n [ngTemplateOutletInjector]=\"injector\" />\n </svg:g>\n}\n\n<!-- Resizer -->\n@if (model().resizerTemplate(); as template) {\n @if (model().resizable()) {\n <ng-template [ngTemplateOutlet]=\"template\" />\n }\n}\n\n<!-- Handles -->\n@for (handle of model().handles(); track handle) {\n @if (handle.template === undefined) {\n <svg:circle\n class=\"default-handle\"\n r=\"5\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template === null) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\" />\n }\n\n @if (handle.template) {\n <svg:g\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection()\">\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n }\n\n @if (showMagnet()) {\n <svg:circle\n class=\"magnet\"\n [attr.r]=\"model().magnetRadius\"\n [attr.cx]=\"handle.hostOffset().x\"\n [attr.cy]=\"handle.hostOffset().y\"\n (pointerEnd)=\"endConnection(); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\" />\n }\n}\n\n<!-- Toolbar -->\n@for (toolbar of toolbars(); track toolbar) {\n <svg:foreignObject\n [attr.width]=\"toolbar.size().width\"\n [attr.height]=\"toolbar.size().height\"\n [attr.transform]=\"toolbar.transform()\">\n <ng-container [ngTemplateOutlet]=\"toolbar.template()\" />\n </svg:foreignObject>\n}\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
|
|
3226
3286
|
}] });
|
|
3227
3287
|
|
|
3228
3288
|
class ConnectionComponent {
|
|
@@ -3770,6 +3830,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
3770
3830
|
|
|
3771
3831
|
class FlowRenderingService {
|
|
3772
3832
|
constructor() {
|
|
3833
|
+
this.nodeRenderingService = inject(NodeRenderingService);
|
|
3834
|
+
this.edgeRenderingService = inject(EdgeRenderingService);
|
|
3835
|
+
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
3836
|
+
this.settingsService = inject(FlowSettingsService);
|
|
3773
3837
|
this.flowInitialized = signal(false);
|
|
3774
3838
|
inject(NgZone).runOutsideAngular(async () => {
|
|
3775
3839
|
await skipFrames(2);
|
|
@@ -4509,5 +4573,5 @@ const Vflow = [
|
|
|
4509
4573
|
* Generated bundle index. Do not edit.
|
|
4510
4574
|
*/
|
|
4511
4575
|
|
|
4512
|
-
export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, CustomDynamicNodeComponent, CustomNodeComponent, CustomTemplateEdgeComponent, DEFAULT_OPTIMIZATION, DragHandleDirective, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, GroupNodeTemplateDirective, HandleComponent, HandleTemplateDirective, MiniMapComponent, NodeHtmlTemplateDirective, NodeSvgTemplateDirective, NodeToolbarComponent, NodeToolbarWrapperDirective, ResizableComponent, SelectableDirective, Vflow, VflowComponent, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isSvgTemplateDynamicNode, isSvgTemplateStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, ComponentEventBusService as ɵComponentEventBusService, ConnectionModel as ɵConnectionModel, FlowEntitiesService as ɵFlowEntitiesService, FlowSettingsService as ɵFlowSettingsService, HandleModel as ɵHandleModel, HandleService as ɵHandleService, NodeAccessorService as ɵNodeAccessorService, NodeModel as ɵNodeModel, RootPointerDirective as ɵRootPointerDirective, SelectionService as ɵSelectionService, SpacePointContextDirective as ɵSpacePointContextDirective, ViewportService as ɵViewportService };
|
|
4576
|
+
export { ChangesControllerDirective, ConnectionControllerDirective, ConnectionTemplateDirective, CustomDynamicNodeComponent, CustomNodeComponent, CustomTemplateEdgeComponent, DEFAULT_OPTIMIZATION, DragHandleDirective, EdgeLabelHtmlTemplateDirective, EdgeTemplateDirective, GroupNodeTemplateDirective, HandleComponent, HandleTemplateDirective, MiniMapComponent, NodeHtmlTemplateDirective, NodeSvgTemplateDirective, NodeToolbarComponent, NodeToolbarWrapperDirective, ResizableComponent, SelectableDirective, Vflow, VflowComponent, isComponentDynamicNode, isComponentStaticNode, isDefaultDynamicGroupNode, isDefaultDynamicNode, isDefaultStaticGroupNode, isDefaultStaticNode, isDynamicNode, isStaticNode, isSvgTemplateDynamicNode, isSvgTemplateStaticNode, isTemplateDynamicGroupNode, isTemplateDynamicNode, isTemplateStaticGroupNode, isTemplateStaticNode, ComponentEventBusService as ɵComponentEventBusService, ConnectionModel as ɵConnectionModel, FlowEntitiesService as ɵFlowEntitiesService, FlowSettingsService as ɵFlowSettingsService, HandleModel as ɵHandleModel, HandleService as ɵHandleService, NodeAccessorService as ɵNodeAccessorService, NodeModel as ɵNodeModel, NodeRenderingService as ɵNodeRenderingService, RootPointerDirective as ɵRootPointerDirective, SelectionService as ɵSelectionService, SpacePointContextDirective as ɵSpacePointContextDirective, ViewportService as ɵViewportService };
|
|
4513
4577
|
//# sourceMappingURL=ngx-vflow.mjs.map
|