ngx-vflow 0.12.0 → 0.13.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/custom-node-base/custom-node-base.component.mjs +7 -3
- package/esm2022/lib/vflow/components/node/node.component.mjs +3 -3
- package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +13 -4
- package/esm2022/lib/vflow/directives/flow-size-controller.directive.mjs +17 -16
- package/esm2022/lib/vflow/directives/root-pointer.directive.mjs +1 -1
- package/esm2022/lib/vflow/directives/selectable.directive.mjs +5 -2
- package/esm2022/lib/vflow/interfaces/component-node-event.interface.mjs +1 -1
- package/esm2022/lib/vflow/interfaces/node.interface.mjs +3 -3
- package/esm2022/lib/vflow/models/node.model.mjs +3 -3
- package/esm2022/lib/vflow/public-components/custom-dynamic-node/custom-dynamic-node.component.mjs +19 -0
- package/esm2022/lib/vflow/public-components/custom-node/custom-node.component.mjs +19 -0
- package/esm2022/lib/vflow/public-components/resizable/resizable.component.mjs +124 -180
- package/esm2022/lib/vflow/services/component-event-bus.service.mjs +1 -1
- package/esm2022/lib/vflow/services/draggable.service.mjs +40 -19
- package/esm2022/lib/vflow/services/keyboard.service.mjs +45 -0
- package/esm2022/lib/vflow/services/node-changes.service.mjs +6 -7
- package/esm2022/lib/vflow/services/selection.service.mjs +12 -5
- package/esm2022/lib/vflow/types/keyboard-action.type.mjs +2 -0
- package/esm2022/lib/vflow/utils/get-os.mjs +24 -0
- package/esm2022/public-api.mjs +4 -3
- package/fesm2022/ngx-vflow.mjs +279 -231
- package/fesm2022/ngx-vflow.mjs.map +1 -1
- package/lib/vflow/components/vflow/vflow.component.d.ts +4 -1
- package/lib/vflow/directives/flow-size-controller.directive.d.ts +3 -2
- package/lib/vflow/directives/root-pointer.directive.d.ts +10 -16
- package/lib/vflow/directives/space-point-context.directive.d.ts +1 -15
- package/lib/vflow/interfaces/component-node-event.interface.d.ts +2 -2
- package/lib/vflow/interfaces/node.interface.d.ts +2 -2
- package/lib/vflow/models/edge.model.d.ts +17 -1
- package/lib/vflow/public-components/{custom-dynamic-node.component.d.ts → custom-dynamic-node/custom-dynamic-node.component.d.ts} +2 -2
- package/lib/vflow/public-components/{custom-node.component.d.ts → custom-node/custom-node.component.d.ts} +2 -2
- package/lib/vflow/public-components/resizable/resizable.component.d.ts +6 -11
- package/lib/vflow/services/draggable.service.d.ts +2 -0
- package/lib/vflow/services/keyboard.service.d.ts +11 -0
- package/lib/vflow/services/selection.service.d.ts +1 -0
- package/lib/vflow/types/keyboard-action.type.d.ts +2 -0
- package/lib/vflow/utils/get-os.d.ts +1 -0
- package/package.json +1 -1
- package/public-api.d.ts +3 -2
- package/esm2022/lib/vflow/public-components/custom-dynamic-node.component.mjs +0 -19
- package/esm2022/lib/vflow/public-components/custom-node.component.mjs +0 -19
package/fesm2022/ngx-vflow.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i1 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, EventEmitter, Output, DestroyRef, Input, runInInjectionContext, Injector, Component, ChangeDetectionStrategy, HostListener, ViewChild, NgZone,
|
|
4
|
+
import { signal, computed, Injectable, inject, ElementRef, Directive, effect, untracked, TemplateRef, EventEmitter, Output, DestroyRef, Input, runInInjectionContext, Injector, Component, ChangeDetectionStrategy, HostListener, ViewChild, NgZone, ContentChild, NgModule } from '@angular/core';
|
|
5
5
|
import { select } from 'd3-selection';
|
|
6
6
|
import { zoomIdentity, zoom } from 'd3-zoom';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { switchMap, merge, fromEvent, tap, Subject, observeOn, animationFrameScheduler, skip, map, pairwise, filter, distinctUntilChanged, asyncScheduler, zip, share, Observable, startWith } from 'rxjs';
|
|
8
|
+
import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
9
9
|
import { drag } from 'd3-drag';
|
|
10
10
|
import { path } from 'd3-path';
|
|
11
11
|
import { __decorate } from 'tslib';
|
|
@@ -249,9 +249,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
249
249
|
}]
|
|
250
250
|
}] });
|
|
251
251
|
|
|
252
|
+
function getOS() {
|
|
253
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
254
|
+
const macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos)/i;
|
|
255
|
+
const windowsPlatforms = /(win32|win64|windows|wince)/i;
|
|
256
|
+
const iosPlatforms = /(iphone|ipad|ipod)/i;
|
|
257
|
+
let os = null;
|
|
258
|
+
if (macosPlatforms.test(userAgent)) {
|
|
259
|
+
os = "macos";
|
|
260
|
+
}
|
|
261
|
+
else if (iosPlatforms.test(userAgent)) {
|
|
262
|
+
os = "ios";
|
|
263
|
+
}
|
|
264
|
+
else if (windowsPlatforms.test(userAgent)) {
|
|
265
|
+
os = "windows";
|
|
266
|
+
}
|
|
267
|
+
else if (/android/.test(userAgent)) {
|
|
268
|
+
os = "android";
|
|
269
|
+
}
|
|
270
|
+
else if (!os && /linux/.test(userAgent)) {
|
|
271
|
+
os = "linux";
|
|
272
|
+
}
|
|
273
|
+
return os;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
class KeyboardService {
|
|
277
|
+
constructor() {
|
|
278
|
+
this.actions = signal({
|
|
279
|
+
multiSelection: [
|
|
280
|
+
getOS() === 'macos' ? 'MetaLeft' : 'ControlLeft',
|
|
281
|
+
getOS() === 'macos' ? 'MetaRight' : 'ControlRight',
|
|
282
|
+
]
|
|
283
|
+
});
|
|
284
|
+
this.actionsActive = {
|
|
285
|
+
multiSelection: false
|
|
286
|
+
};
|
|
287
|
+
toObservable(this.actions).pipe(switchMap(() => merge(fromEvent(document, 'keydown').pipe(tap(event => {
|
|
288
|
+
for (const action in this.actions()) {
|
|
289
|
+
const keyCodes = this.actions()[action] ?? [];
|
|
290
|
+
if (keyCodes.includes(event.code)) {
|
|
291
|
+
this.actionsActive[action] = true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
})), fromEvent(document, 'keyup').pipe(tap(event => {
|
|
295
|
+
for (const action in this.actions()) {
|
|
296
|
+
const keyCodes = this.actions()[action] ?? [];
|
|
297
|
+
if (keyCodes.includes(event.code)) {
|
|
298
|
+
this.actionsActive[action] = false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
})))), takeUntilDestroyed()).subscribe();
|
|
302
|
+
}
|
|
303
|
+
setShortcuts(newActions) {
|
|
304
|
+
this.actions.update((actions) => ({ ...actions, ...newActions }));
|
|
305
|
+
}
|
|
306
|
+
isActiveAction(action) {
|
|
307
|
+
return this.actionsActive[action];
|
|
308
|
+
}
|
|
309
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: KeyboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
310
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: KeyboardService }); }
|
|
311
|
+
}
|
|
312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: KeyboardService, decorators: [{
|
|
313
|
+
type: Injectable
|
|
314
|
+
}], ctorParameters: function () { return []; } });
|
|
315
|
+
|
|
252
316
|
class SelectionService {
|
|
253
317
|
constructor() {
|
|
254
318
|
this.flowEntitiesService = inject(FlowEntitiesService);
|
|
319
|
+
this.keyboardService = inject(KeyboardService);
|
|
255
320
|
this.viewport$ = new Subject();
|
|
256
321
|
this.resetSelection = this.viewport$.pipe(tap(({ start, end, target }) => {
|
|
257
322
|
if (start && end && target) {
|
|
@@ -273,10 +338,15 @@ class SelectionService {
|
|
|
273
338
|
this.viewport$.next(viewport);
|
|
274
339
|
}
|
|
275
340
|
select(entity) {
|
|
276
|
-
//
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
341
|
+
// ? May be not a responsibility of this method
|
|
342
|
+
// if entity already selected - do nothing
|
|
343
|
+
if (entity?.selected()) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (!this.keyboardService.isActiveAction('multiSelection')) {
|
|
347
|
+
// undo select for previously selected nodes
|
|
348
|
+
this.flowEntitiesService.entities().forEach(n => n.selected.set(false));
|
|
349
|
+
}
|
|
280
350
|
if (entity) {
|
|
281
351
|
// select passed entity
|
|
282
352
|
entity.selected.set(true);
|
|
@@ -377,6 +447,9 @@ const evTarget = (anyEvent) => {
|
|
|
377
447
|
const round = (num) => Math.round(num * 100) / 100;
|
|
378
448
|
|
|
379
449
|
class DraggableService {
|
|
450
|
+
constructor() {
|
|
451
|
+
this.entitiesService = inject(FlowEntitiesService);
|
|
452
|
+
}
|
|
380
453
|
/**
|
|
381
454
|
* Enable draggable behavior for element.
|
|
382
455
|
*
|
|
@@ -412,27 +485,24 @@ class DraggableService {
|
|
|
412
485
|
* @returns
|
|
413
486
|
*/
|
|
414
487
|
getDragBehavior(model) {
|
|
415
|
-
let
|
|
416
|
-
let
|
|
488
|
+
let dragNodes = [];
|
|
489
|
+
let initialPositions = [];
|
|
417
490
|
return drag()
|
|
418
491
|
.on('start', (event) => {
|
|
419
|
-
|
|
420
|
-
|
|
492
|
+
dragNodes = this.getDragNodes(model);
|
|
493
|
+
initialPositions = dragNodes.map(node => ({
|
|
494
|
+
x: node.point().x - event.x,
|
|
495
|
+
y: node.point().y - event.y
|
|
496
|
+
}));
|
|
421
497
|
})
|
|
422
498
|
.on('drag', (event) => {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
point.x = Math.min(parent.size().width - model.size().width, point.x);
|
|
431
|
-
point.x = Math.max(0, point.x);
|
|
432
|
-
point.y = Math.min(parent.size().height - model.size().height, point.y);
|
|
433
|
-
point.y = Math.max(0, point.y);
|
|
434
|
-
}
|
|
435
|
-
model.setPoint(point, true);
|
|
499
|
+
dragNodes.forEach((model, index) => {
|
|
500
|
+
let point = {
|
|
501
|
+
x: round(event.x + initialPositions[index].x),
|
|
502
|
+
y: round(event.y + initialPositions[index].y)
|
|
503
|
+
};
|
|
504
|
+
moveNode(model, point);
|
|
505
|
+
});
|
|
436
506
|
});
|
|
437
507
|
}
|
|
438
508
|
/**
|
|
@@ -445,12 +515,32 @@ class DraggableService {
|
|
|
445
515
|
event.sourceEvent.stopPropagation();
|
|
446
516
|
});
|
|
447
517
|
}
|
|
518
|
+
getDragNodes(model) {
|
|
519
|
+
return model.selected()
|
|
520
|
+
? this.entitiesService
|
|
521
|
+
.nodes()
|
|
522
|
+
// selected draggable nodes (with current node)
|
|
523
|
+
.filter(node => node.selected() && node.draggable())
|
|
524
|
+
// we only can move current node if it's not selected
|
|
525
|
+
: [model];
|
|
526
|
+
}
|
|
448
527
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
449
528
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService }); }
|
|
450
529
|
}
|
|
451
530
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DraggableService, decorators: [{
|
|
452
531
|
type: Injectable
|
|
453
532
|
}] });
|
|
533
|
+
function moveNode(model, point) {
|
|
534
|
+
const parent = model.parent();
|
|
535
|
+
// keep node in bounds of parent
|
|
536
|
+
if (parent) {
|
|
537
|
+
point.x = Math.min(parent.size().width - model.size().width, point.x);
|
|
538
|
+
point.x = Math.max(0, point.x);
|
|
539
|
+
point.y = Math.min(parent.size().height - model.size().height, point.y);
|
|
540
|
+
point.y = Math.max(0, point.y);
|
|
541
|
+
}
|
|
542
|
+
model.setPoint(point, true);
|
|
543
|
+
}
|
|
454
544
|
|
|
455
545
|
class EdgeTemplateDirective {
|
|
456
546
|
constructor() {
|
|
@@ -734,7 +824,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
734
824
|
|
|
735
825
|
class CustomNodeBaseComponent {
|
|
736
826
|
constructor() {
|
|
737
|
-
this.eventBus = inject(ComponentEventBusService);
|
|
827
|
+
this.eventBus = inject(ComponentEventBusService, { optional: true });
|
|
738
828
|
this.destroyRef = inject(DestroyRef);
|
|
739
829
|
/**
|
|
740
830
|
* Signal with selected state of node
|
|
@@ -746,7 +836,11 @@ class CustomNodeBaseComponent {
|
|
|
746
836
|
this.selected.set(value);
|
|
747
837
|
}
|
|
748
838
|
ngOnInit() {
|
|
749
|
-
|
|
839
|
+
if (this.eventBus) {
|
|
840
|
+
this.trackEvents()
|
|
841
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
842
|
+
.subscribe();
|
|
843
|
+
}
|
|
750
844
|
}
|
|
751
845
|
trackEvents() {
|
|
752
846
|
const props = Object.getOwnPropertyNames(this);
|
|
@@ -1243,12 +1337,11 @@ class NodesChangeService {
|
|
|
1243
1337
|
// Check for nodes list change and watch for specific node from this list change its position
|
|
1244
1338
|
switchMap((nodes) => merge(...nodes.map(node => node.point$.pipe(
|
|
1245
1339
|
// skip initial position from signal
|
|
1246
|
-
skip(1), map(() => node))))),
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
]));
|
|
1340
|
+
skip(1), map(() => node))))), map((changedNode) => {
|
|
1341
|
+
return this.entitiesService.nodes()
|
|
1342
|
+
.filter(node => node === changedNode || node.selected())
|
|
1343
|
+
.map(node => ({ type: 'position', id: node.node.id, point: node.point() }));
|
|
1344
|
+
}));
|
|
1252
1345
|
this.nodeSizeChange$ = toObservable(this.entitiesService.nodes)
|
|
1253
1346
|
.pipe(switchMap((nodes) => merge(...nodes.map(node => node.size$.pipe(skip(1), map(() => node))))), map(changedNode => [
|
|
1254
1347
|
{ type: 'size', id: changedNode.node.id, size: changedNode.size() }
|
|
@@ -1874,6 +1967,7 @@ class ResizableComponent {
|
|
|
1874
1967
|
this.nodeAccessor = inject(NodeAccessorService);
|
|
1875
1968
|
this.rootPointer = inject(RootPointerDirective);
|
|
1876
1969
|
this.viewportService = inject(ViewportService);
|
|
1970
|
+
this.spacePointContext = inject(SpacePointContextDirective);
|
|
1877
1971
|
this.hostRef = inject(ElementRef);
|
|
1878
1972
|
this.resizerColor = '#2e414c';
|
|
1879
1973
|
this.gap = 1.5;
|
|
@@ -1885,7 +1979,7 @@ class ResizableComponent {
|
|
|
1885
1979
|
this.minHeight = 0;
|
|
1886
1980
|
// TODO: allow reszie beside the flow
|
|
1887
1981
|
this.resizeOnGlobalMouseMove = this.rootPointer.pointerMovement$
|
|
1888
|
-
.pipe(filter(() => this.resizeSide !== null), tap((event) => this.resize(event)), takeUntilDestroyed())
|
|
1982
|
+
.pipe(filter(() => this.resizeSide !== null), filter((event) => event.movementX !== 0 || event.movementY !== 0), tap((event) => this.resize(event)), takeUntilDestroyed())
|
|
1889
1983
|
.subscribe();
|
|
1890
1984
|
this.endResizeOnGlobalMouseUp = this.rootPointer.documentPointerEnd$
|
|
1891
1985
|
.pipe(tap(() => this.endResize()), takeUntilDestroyed())
|
|
@@ -1914,193 +2008,55 @@ class ResizableComponent {
|
|
|
1914
2008
|
this.resizeSide = side;
|
|
1915
2009
|
this.model.resizing.set(true);
|
|
1916
2010
|
}
|
|
1917
|
-
resize(
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
if (x === this.getMinX() || x === this.getMaxX()) {
|
|
1927
|
-
return;
|
|
1928
|
-
}
|
|
1929
|
-
this.model.setPoint({ x, y: this.model.point().y }, false);
|
|
1930
|
-
this.model.size.update(({ height, width }) => {
|
|
1931
|
-
width -= offsetX;
|
|
1932
|
-
width = Math.max(width, this.minWidth);
|
|
1933
|
-
width = Math.min(width, this.getMaxWidth());
|
|
1934
|
-
return { height, width: width };
|
|
1935
|
-
});
|
|
1936
|
-
return;
|
|
1937
|
-
case 'right':
|
|
1938
|
-
this.model.size.update(({ height, width }) => {
|
|
1939
|
-
width += offsetX;
|
|
1940
|
-
width = Math.max(width, this.minWidth);
|
|
1941
|
-
width = Math.min(width, this.getMaxWidth());
|
|
1942
|
-
const bounds = getNodesBounds(this.model.children());
|
|
1943
|
-
width = Math.max(width, bounds.x + bounds.width);
|
|
1944
|
-
return { height, width };
|
|
1945
|
-
});
|
|
1946
|
-
return;
|
|
1947
|
-
case 'top':
|
|
1948
|
-
let y = this.model.point().y + offsetY;
|
|
1949
|
-
y = Math.max(y, this.getMinY());
|
|
1950
|
-
y = Math.min(y, this.getMaxY());
|
|
1951
|
-
if (y === this.getMinY() || y === this.getMaxY()) {
|
|
1952
|
-
return;
|
|
1953
|
-
}
|
|
1954
|
-
this.model.setPoint({ x: this.model.point().x, y }, false);
|
|
1955
|
-
this.model.size.update(({ height, width }) => {
|
|
1956
|
-
height -= offsetY;
|
|
1957
|
-
height = Math.max(height, this.minHeight);
|
|
1958
|
-
height = Math.min(height, this.getMaxHeight());
|
|
1959
|
-
return { width, height };
|
|
1960
|
-
});
|
|
1961
|
-
return;
|
|
1962
|
-
case 'bottom':
|
|
1963
|
-
this.model.size.update(({ height, width }) => {
|
|
1964
|
-
height += offsetY;
|
|
1965
|
-
height = Math.max(height, this.minHeight);
|
|
1966
|
-
height = Math.min(height, this.getMaxHeight());
|
|
1967
|
-
const bounds = getNodesBounds(this.model.children());
|
|
1968
|
-
height = Math.max(height, bounds.y + bounds.height);
|
|
1969
|
-
return { width, height };
|
|
1970
|
-
});
|
|
1971
|
-
return;
|
|
1972
|
-
case 'top-left': {
|
|
1973
|
-
let x = this.model.point().x + offsetX;
|
|
1974
|
-
x = Math.max(x, this.getMinX());
|
|
1975
|
-
x = Math.min(x, this.getMaxX());
|
|
1976
|
-
let y = this.model.point().y + offsetY;
|
|
1977
|
-
y = Math.max(y, this.getMinY());
|
|
1978
|
-
y = Math.min(y, this.getMaxY());
|
|
1979
|
-
if (x === this.getMinX() || y === this.getMinY() ||
|
|
1980
|
-
x === this.getMaxX() || y === this.getMaxY()) {
|
|
1981
|
-
return;
|
|
1982
|
-
}
|
|
1983
|
-
this.model.setPoint({ x, y }, false);
|
|
1984
|
-
this.model.size.update(({ height, width }) => {
|
|
1985
|
-
width -= offsetX;
|
|
1986
|
-
width = Math.max(width, this.minWidth);
|
|
1987
|
-
width = Math.min(width, this.getMaxWidth());
|
|
1988
|
-
height -= offsetY;
|
|
1989
|
-
height = Math.max(height, this.minHeight);
|
|
1990
|
-
height = Math.min(height, this.getMaxHeight());
|
|
1991
|
-
return { height, width };
|
|
1992
|
-
});
|
|
1993
|
-
return;
|
|
1994
|
-
}
|
|
1995
|
-
case 'top-right': {
|
|
1996
|
-
let y = this.model.point().y + offsetY;
|
|
1997
|
-
y = Math.max(y, this.getMinY());
|
|
1998
|
-
y = Math.min(y, this.getMaxY());
|
|
1999
|
-
if (y === this.getMinX() || y === this.getMaxY()) {
|
|
2000
|
-
return;
|
|
2001
|
-
}
|
|
2002
|
-
this.model.setPoint({ x: this.model.point().x, y }, false);
|
|
2003
|
-
this.model.size.update(({ height, width }) => {
|
|
2004
|
-
const bounds = getNodesBounds(this.model.children());
|
|
2005
|
-
width += offsetX;
|
|
2006
|
-
width = Math.max(width, this.minWidth);
|
|
2007
|
-
width = Math.min(width, this.getMaxWidth());
|
|
2008
|
-
width = Math.max(width, bounds.x + bounds.width);
|
|
2009
|
-
height -= offsetY;
|
|
2010
|
-
height = Math.max(height, this.minHeight);
|
|
2011
|
-
height = Math.min(height, this.getMaxHeight());
|
|
2012
|
-
return { height, width };
|
|
2013
|
-
});
|
|
2014
|
-
return;
|
|
2015
|
-
}
|
|
2016
|
-
case 'bottom-left': {
|
|
2017
|
-
let x = this.model.point().x + offsetX;
|
|
2018
|
-
x = Math.max(x, this.getMinX());
|
|
2019
|
-
x = Math.min(x, this.getMaxX());
|
|
2020
|
-
if (x === this.getMinX() || x === this.getMaxX()) {
|
|
2021
|
-
return;
|
|
2022
|
-
}
|
|
2023
|
-
this.model.setPoint({ x, y: this.model.point().y }, false);
|
|
2024
|
-
this.model.size.update(({ height, width }) => {
|
|
2025
|
-
width -= offsetX;
|
|
2026
|
-
width = Math.max(width, this.minWidth);
|
|
2027
|
-
width = Math.min(width, this.getMaxWidth());
|
|
2028
|
-
height += offsetY;
|
|
2029
|
-
height = Math.max(height, this.minHeight);
|
|
2030
|
-
height = Math.min(height, this.getMaxHeight());
|
|
2031
|
-
const bounds = getNodesBounds(this.model.children());
|
|
2032
|
-
height = Math.max(height, bounds.y + bounds.height);
|
|
2033
|
-
return { height, width };
|
|
2034
|
-
});
|
|
2035
|
-
return;
|
|
2036
|
-
}
|
|
2037
|
-
case 'bottom-right': {
|
|
2038
|
-
this.model.size.update(({ height, width }) => {
|
|
2039
|
-
const bounds = getNodesBounds(this.model.children());
|
|
2040
|
-
width += offsetX;
|
|
2041
|
-
width = Math.max(width, this.minWidth);
|
|
2042
|
-
width = Math.min(width, this.getMaxWidth());
|
|
2043
|
-
width = Math.max(width, bounds.x + bounds.width);
|
|
2044
|
-
height += offsetY;
|
|
2045
|
-
height = Math.max(height, this.minHeight);
|
|
2046
|
-
height = Math.min(height, this.getMaxHeight());
|
|
2047
|
-
height = Math.max(height, bounds.y + bounds.height);
|
|
2048
|
-
return { height, width };
|
|
2049
|
-
});
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2011
|
+
resize(event) {
|
|
2012
|
+
if (!this.resizeSide)
|
|
2013
|
+
return;
|
|
2014
|
+
if (this.isResizeConstrained(event))
|
|
2015
|
+
return;
|
|
2016
|
+
const offset = calcOffset(event.movementX, event.movementY, this.zoom());
|
|
2017
|
+
const { x, y, width, height } = constrainRect(applyResize(this.resizeSide, this.model, offset), this.model, this.resizeSide, this.minWidth, this.minHeight);
|
|
2018
|
+
this.model.setPoint({ x, y }, false);
|
|
2019
|
+
this.model.size.set({ width, height });
|
|
2052
2020
|
}
|
|
2053
2021
|
endResize() {
|
|
2054
2022
|
this.resizeSide = null;
|
|
2055
2023
|
this.model.resizing.set(false);
|
|
2056
2024
|
}
|
|
2057
|
-
|
|
2058
|
-
const
|
|
2059
|
-
if (
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
if (parent) {
|
|
2067
|
-
return parent.size().height - this.model.point().y;
|
|
2068
|
-
}
|
|
2069
|
-
return Infinity;
|
|
2070
|
-
}
|
|
2071
|
-
getMinX() {
|
|
2072
|
-
const parent = this.model.parent();
|
|
2073
|
-
if (parent) {
|
|
2074
|
-
return 0;
|
|
2025
|
+
isResizeConstrained({ x, y, movementX, movementY }) {
|
|
2026
|
+
const flowPoint = this.spacePointContext.documentPointToFlowPoint({ x, y });
|
|
2027
|
+
if (this.resizeSide?.includes('right')) {
|
|
2028
|
+
if (movementX > 0 && flowPoint.x < (this.model.point().x + this.model.size().width)) {
|
|
2029
|
+
return true;
|
|
2030
|
+
}
|
|
2031
|
+
if (movementX < 0 && flowPoint.x > this.model.point().x + this.model.size().width) {
|
|
2032
|
+
return true;
|
|
2033
|
+
}
|
|
2075
2034
|
}
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2035
|
+
if (this.resizeSide?.includes('left')) {
|
|
2036
|
+
if (movementX < 0 && flowPoint.x > this.model.point().x) {
|
|
2037
|
+
return true;
|
|
2038
|
+
}
|
|
2039
|
+
if (movementX > 0 && flowPoint.x < this.model.point().x) {
|
|
2040
|
+
return true;
|
|
2041
|
+
}
|
|
2082
2042
|
}
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
const bounds = getNodesBounds(children);
|
|
2091
|
-
return x + (bounds.x + bounds.width) >= x + width ? x : (width - this.minWidth) + x;
|
|
2043
|
+
if (this.resizeSide?.includes('bottom')) {
|
|
2044
|
+
if (movementY > 0 && flowPoint.y < (this.model.point().y + this.model.size().height)) {
|
|
2045
|
+
return true;
|
|
2046
|
+
}
|
|
2047
|
+
if (movementY < 0 && flowPoint.y > this.model.point().y + this.model.size().height) {
|
|
2048
|
+
return true;
|
|
2049
|
+
}
|
|
2092
2050
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
const bounds = getNodesBounds(children);
|
|
2101
|
-
return y + (bounds.y + bounds.height) >= y + height ? y : (height - this.minHeight) + y;
|
|
2051
|
+
if (this.resizeSide?.includes('top')) {
|
|
2052
|
+
if (movementY < 0 && flowPoint.y > this.model.point().y) {
|
|
2053
|
+
return true;
|
|
2054
|
+
}
|
|
2055
|
+
if (movementY > 0 && flowPoint.y < this.model.point().y) {
|
|
2056
|
+
return true;
|
|
2057
|
+
}
|
|
2102
2058
|
}
|
|
2103
|
-
return
|
|
2059
|
+
return false;
|
|
2104
2060
|
}
|
|
2105
2061
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ResizableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2106
2062
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ResizableComponent, selector: "[resizable]", inputs: { resizable: "resizable", resizerColor: "resizerColor", gap: "gap" }, viewQueries: [{ propertyName: "resizer", first: true, predicate: ["resizer"], descendants: true, static: true }], ngImport: i0, template: "<ng-template #resizer>\n <svg:g>\n <!-- top line -->\n <svg:line\n class=\"top\"\n [attr.x1]=\"lineGap\"\n [attr.y1]=\"-gap\"\n [attr.x2]=\"model.size().width - lineGap\"\n [attr.y2]=\"-gap\"\n [attr.stroke]=\"resizerColor\"\n stroke-width=\"2\"\n (pointerStart)=\"startResize('top', $event)\"\n />\n <!-- Left line -->\n <svg:line\n class=\"left\"\n [attr.x1]=\"-gap\"\n [attr.y1]=\"lineGap\"\n [attr.x2]=\"-gap\"\n [attr.y2]=\"model.size().height - lineGap\"\n [attr.stroke]=\"resizerColor\"\n stroke-width=\"2\"\n (pointerStart)=\"startResize('left', $event)\"\n />\n <!-- Bottom line -->\n <svg:line\n class=\"bottom\"\n [attr.x1]=\"lineGap\"\n [attr.y1]=\"model.size().height + gap\"\n [attr.x2]=\"model.size().width - lineGap\"\n [attr.y2]=\"model.size().height + gap\"\n [attr.stroke]=\"resizerColor\"\n stroke-width=\"2\"\n (pointerStart)=\"startResize('bottom', $event)\"\n />\n <!-- Right line -->\n <svg:line\n class=\"right\"\n [attr.x1]=\"model.size().width + gap\"\n [attr.y1]=\"lineGap\"\n [attr.x2]=\"model.size().width + gap\"\n [attr.y2]=\"model.size().height - lineGap\"\n [attr.stroke]=\"resizerColor\"\n stroke-width=\"2\"\n (pointerStart)=\"startResize('right', $event)\"\n />\n\n <!-- Top Left -->\n <svg:rect\n class=\"top-left\"\n [attr.x]=\"-(handleSize / 2) - gap\"\n [attr.y]=\"-(handleSize / 2) - gap\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor\"\n (pointerStart)=\"startResize('top-left', $event)\"\n />\n\n <!-- Top right -->\n <svg:rect\n class=\"top-right\"\n [attr.x]=\"model.size().width - (handleSize / 2) + gap\"\n [attr.y]=\"-(handleSize / 2) - gap\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor\"\n (pointerStart)=\"startResize('top-right', $event)\"\n />\n\n <!-- Bottom left -->\n <svg:rect\n class=\"bottom-left\"\n [attr.x]=\"-(handleSize / 2) - gap\"\n [attr.y]=\"model.size().height - (handleSize / 2) + gap\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor\"\n (pointerStart)=\"startResize('bottom-left', $event)\"\n />\n\n <!-- Bottom right -->\n <svg:rect\n class=\"bottom-right\"\n [attr.x]=\"model.size().width - (handleSize / 2) + gap\"\n [attr.y]=\"model.size().height - (handleSize / 2) + gap\"\n [attr.width]=\"handleSize\"\n [attr.height]=\"handleSize\"\n [attr.fill]=\"resizerColor\"\n (pointerStart)=\"startResize('bottom-right', $event)\"\n />\n </svg:g>\n</ng-template>\n\n<ng-content />\n", styles: [".top{cursor:n-resize}.left{cursor:w-resize}.right{cursor:e-resize}.bottom{cursor:s-resize}.top-left{cursor:nw-resize}.top-right{cursor:ne-resize}.bottom-left{cursor:sw-resize}.bottom-right{cursor:se-resize}\n"], dependencies: [{ kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }] }); }
|
|
@@ -2121,6 +2077,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2121
2077
|
type: ViewChild,
|
|
2122
2078
|
args: ['resizer', { static: true }]
|
|
2123
2079
|
}], ngAfterViewInit: [] } });
|
|
2080
|
+
function calcOffset(movementX, movementY, zoom) {
|
|
2081
|
+
return {
|
|
2082
|
+
offsetX: round(movementX / zoom),
|
|
2083
|
+
offsetY: round(movementY / zoom)
|
|
2084
|
+
};
|
|
2085
|
+
}
|
|
2086
|
+
function applyResize(side, model, offset) {
|
|
2087
|
+
const { offsetX, offsetY } = offset;
|
|
2088
|
+
const { x, y } = model.point();
|
|
2089
|
+
const { width, height } = model.size();
|
|
2090
|
+
// Handle each case of resizing (top, bottom, left, right, corners)
|
|
2091
|
+
switch (side) {
|
|
2092
|
+
case 'left':
|
|
2093
|
+
return { x: x + offsetX, y, width: width - offsetX, height };
|
|
2094
|
+
case 'right':
|
|
2095
|
+
return { x, y, width: width + offsetX, height };
|
|
2096
|
+
case 'top':
|
|
2097
|
+
return { x, y: y + offsetY, width, height: height - offsetY };
|
|
2098
|
+
case 'bottom':
|
|
2099
|
+
return { x, y, width, height: height + offsetY };
|
|
2100
|
+
case 'top-left':
|
|
2101
|
+
return { x: x + offsetX, y: y + offsetY, width: width - offsetX, height: height - offsetY };
|
|
2102
|
+
case 'top-right':
|
|
2103
|
+
return { x, y: y + offsetY, width: width + offsetX, height: height - offsetY };
|
|
2104
|
+
case 'bottom-left':
|
|
2105
|
+
return { x: x + offsetX, y, width: width - offsetX, height: height + offsetY };
|
|
2106
|
+
case 'bottom-right':
|
|
2107
|
+
return { x, y, width: width + offsetX, height: height + offsetY };
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
function constrainRect(rect, model, side, minWidth, minHeight) {
|
|
2111
|
+
let { x, y, width, height } = rect;
|
|
2112
|
+
// 1. Prevent negative dimensions
|
|
2113
|
+
width = Math.max(width, 0);
|
|
2114
|
+
height = Math.max(height, 0);
|
|
2115
|
+
// 2. Apply minimum size constraints
|
|
2116
|
+
width = Math.max(minWidth, width);
|
|
2117
|
+
height = Math.max(minHeight, height);
|
|
2118
|
+
// Apply left/top constraints based on minimum size
|
|
2119
|
+
x = Math.min(x, model.point().x + model.size().width - minWidth);
|
|
2120
|
+
y = Math.min(y, model.point().y + model.size().height - minHeight);
|
|
2121
|
+
const parent = model.parent();
|
|
2122
|
+
// 3. Apply maximum size constraints based on parent size (if exists)
|
|
2123
|
+
if (parent) {
|
|
2124
|
+
x = Math.max(x, 0); // Left boundary of the parent
|
|
2125
|
+
y = Math.max(y, 0); // Top boundary of the parent
|
|
2126
|
+
if (x === 0) {
|
|
2127
|
+
width = model.point().x + model.size().width;
|
|
2128
|
+
}
|
|
2129
|
+
if (y === 0) {
|
|
2130
|
+
height = model.point().y + model.size().height;
|
|
2131
|
+
}
|
|
2132
|
+
width = Math.min(width, parent.size().width - model.point().x);
|
|
2133
|
+
height = Math.min(height, parent.size().height - model.point().y);
|
|
2134
|
+
}
|
|
2135
|
+
const bounds = getNodesBounds(model.children());
|
|
2136
|
+
// 4. Apply child node constraints (if children exist)
|
|
2137
|
+
if (bounds) {
|
|
2138
|
+
if (side.includes('left')) {
|
|
2139
|
+
x = Math.min(x, (model.point().x + model.size().width) - (bounds.x + bounds.width));
|
|
2140
|
+
width = Math.max(width, bounds.x + bounds.width);
|
|
2141
|
+
}
|
|
2142
|
+
if (side.includes('right')) {
|
|
2143
|
+
width = Math.max(width, bounds.x + bounds.width);
|
|
2144
|
+
}
|
|
2145
|
+
if (side.includes('bottom')) {
|
|
2146
|
+
height = Math.max(height, bounds.y + bounds.height);
|
|
2147
|
+
}
|
|
2148
|
+
if (side.includes('top')) {
|
|
2149
|
+
y = Math.min(y, (model.point().y + model.size().height) - (bounds.y + bounds.height));
|
|
2150
|
+
height = Math.max(height, bounds.y + bounds.height);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
return {
|
|
2154
|
+
x,
|
|
2155
|
+
y,
|
|
2156
|
+
width,
|
|
2157
|
+
height
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2124
2160
|
|
|
2125
2161
|
class HandleSizeControllerDirective {
|
|
2126
2162
|
constructor() {
|
|
@@ -2232,7 +2268,7 @@ class NodeComponent {
|
|
|
2232
2268
|
}
|
|
2233
2269
|
}
|
|
2234
2270
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2235
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeTemplate: "nodeTemplate", groupNodeTemplate: "groupNodeTemplate" }, providers: [HandleService, NodeAccessorService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], ngImport: i0, template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (
|
|
2271
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: NodeComponent, selector: "g[node]", inputs: { nodeModel: "nodeModel", nodeTemplate: "nodeTemplate", groupNodeTemplate: "groupNodeTemplate" }, providers: [HandleService, NodeAccessorService], viewQueries: [{ propertyName: "nodeContentRef", first: true, predicate: ["nodeContent"], descendants: true }, { propertyName: "htmlWrapperRef", first: true, predicate: ["htmlWrapper"], descendants: true }], ngImport: i0, template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_selected]=\"nodeModel.selected()\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n [style.max-width]=\"styleWidth()\"\n [style.max-height]=\"styleHeight()\"\n >\n <div [outerHTML]=\"nodeModel.text()\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </div>\n</svg:foreignObject>\n\n<!-- Template node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Component node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.isComponentType\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Default group node -->\n<svg:rect\n *ngIf=\"nodeModel.node.type === 'default-group'\"\n [resizable]=\"nodeModel.resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel.color()\"\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [class.default-group-node_selected]=\"nodeModel.selected()\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n [style.stroke]=\"nodeModel.color()\"\n [style.fill]=\"nodeModel.color()\"\n (pointerStart)=\"pullNode(); selectNode()\"\n/>\n\n<!-- Template group node -->\n<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (pointerStart)=\"pullNode()\"\n>\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected, width: nodeModel.width, height: nodeModel.height } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n</svg:g>\n\n<!-- Resizer -->\n<ng-container *ngIf=\"nodeModel.resizerTemplate() as template\">\n <ng-container *ngIf=\"nodeModel.resizable()\">\n <ng-template [ngTemplateOutlet]=\"template\" />\n </ng-container>\n</ng-container>\n\n<!-- Handles -->\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(handle); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\"\n />\n</ng-container>\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-node{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"], dependencies: [{ kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: HandleComponent, selector: "handle", inputs: ["position", "type", "id", "template"] }, { kind: "component", type: ResizableComponent, selector: "[resizable]", inputs: ["resizable", "resizerColor", "gap"] }, { kind: "directive", type: HandleSizeControllerDirective, selector: "[handleSizeController]", inputs: ["handleSizeController"] }, { kind: "directive", type: PointerDirective, selector: "[pointerStart], [pointerEnd], [pointerOver], [pointerOut]", outputs: ["pointerOver", "pointerOut", "pointerStart", "pointerEnd"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2236
2272
|
}
|
|
2237
2273
|
__decorate([
|
|
2238
2274
|
InjectionContext
|
|
@@ -2242,7 +2278,7 @@ __decorate([
|
|
|
2242
2278
|
], NodeComponent.prototype, "ngAfterViewInit", null);
|
|
2243
2279
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NodeComponent, decorators: [{
|
|
2244
2280
|
type: Component,
|
|
2245
|
-
args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService, NodeAccessorService], template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (
|
|
2281
|
+
args: [{ selector: 'g[node]', changeDetection: ChangeDetectionStrategy.OnPush, providers: [HandleService, NodeAccessorService], template: "<!-- Default node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'default'\"\n class=\"selectable\"\n #nodeContent\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode(); selectNode()\"\n>\n <div\n #htmlWrapper\n class=\"default-node\"\n [class.default-node_selected]=\"nodeModel.selected()\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n [style.max-width]=\"styleWidth()\"\n [style.max-height]=\"styleHeight()\"\n >\n <div [outerHTML]=\"nodeModel.text()\"></div>\n\n <handle type=\"source\" [position]=\"nodeModel.sourcePosition()\" />\n <handle type=\"target\" [position]=\"nodeModel.targetPosition()\" />\n </div>\n</svg:foreignObject>\n\n<!-- Template node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.node.type === 'html-template' && nodeTemplate\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Component node -->\n<svg:foreignObject\n *ngIf=\"nodeModel.isComponentType\"\n class=\"selectable\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n (pointerStart)=\"pullNode()\"\n>\n <div\n #htmlWrapper\n class=\"wrapper\"\n [style.width]=\"styleWidth()\"\n [style.height]=\"styleHeight()\"\n >\n <ng-container\n [ngComponentOutlet]=\"$any(nodeModel.node.type)\"\n [ngComponentOutletInputs]=\"nodeModel.componentTypeInputs()\"\n [ngComponentOutletInjector]=\"injector\"\n />\n </div>\n</svg:foreignObject>\n\n<!-- Default group node -->\n<svg:rect\n *ngIf=\"nodeModel.node.type === 'default-group'\"\n [resizable]=\"nodeModel.resizable()\"\n [gap]=\"3\"\n [resizerColor]=\"nodeModel.color()\"\n class=\"default-group-node\"\n rx=\"5\"\n ry=\"5\"\n [class.default-group-node_selected]=\"nodeModel.selected()\"\n [attr.width]=\"nodeModel.size().width\"\n [attr.height]=\"nodeModel.size().height\"\n [style.stroke]=\"nodeModel.color()\"\n [style.fill]=\"nodeModel.color()\"\n (pointerStart)=\"pullNode(); selectNode()\"\n/>\n\n<!-- Template group node -->\n<svg:g\n *ngIf=\"nodeModel.node.type === 'template-group' && groupNodeTemplate\"\n class=\"selectable\"\n (pointerStart)=\"pullNode()\"\n>\n <ng-container\n [ngTemplateOutlet]=\"groupNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: { node: nodeModel.node, selected: nodeModel.selected, width: nodeModel.width, height: nodeModel.height } }\"\n [ngTemplateOutletInjector]=\"injector\"\n />\n</svg:g>\n\n<!-- Resizer -->\n<ng-container *ngIf=\"nodeModel.resizerTemplate() as template\">\n <ng-container *ngIf=\"nodeModel.resizable()\">\n <ng-template [ngTemplateOutlet]=\"template\" />\n </ng-container>\n</ng-container>\n\n<!-- Handles -->\n<ng-container *ngFor=\"let handle of nodeModel.handles()\">\n <svg:circle\n *ngIf=\"!handle.template\"\n class=\"default-handle\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n [attr.stroke-width]=\"handle.strokeWidth\"\n r=\"5\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n />\n\n <svg:g\n *ngIf=\"handle.template\"\n [handleSizeController]=\"handle\"\n (pointerStart)=\"startConnection($event, handle)\"\n (pointerEnd)=\"endConnection(handle)\"\n >\n <ng-container *ngTemplateOutlet=\"handle.template; context: handle.templateContext\" />\n </svg:g>\n\n <svg:circle\n *ngIf=\"showMagnet()\"\n class=\"magnet\"\n [attr.r]=\"nodeModel.magnetRadius\"\n [attr.cx]=\"handle.offset().x\"\n [attr.cy]=\"handle.offset().y\"\n (pointerEnd)=\"endConnection(handle); resetValidateConnection(handle)\"\n (pointerOver)=\"validateConnection(handle)\"\n (pointerOut)=\"resetValidateConnection(handle)\"\n />\n</ng-container>\n", styles: [".magnet{opacity:0}.wrapper{display:table-cell}.default-node{border:1.5px solid #1b262c;border-radius:5px;display:flex;align-items:center;justify-content:center;color:#000;background-color:#fff}.default-node_selected{border-width:2px}.default-group-node{stroke-width:1.5px;fill-opacity:.05}.default-group-node_selected{stroke-width:2px}.default-handle{stroke:#fff;fill:#1b262c}\n"] }]
|
|
2246
2282
|
}], propDecorators: { nodeModel: [{
|
|
2247
2283
|
type: Input
|
|
2248
2284
|
}], nodeTemplate: [{
|
|
@@ -2583,12 +2619,13 @@ class FlowSizeControllerDirective {
|
|
|
2583
2619
|
constructor() {
|
|
2584
2620
|
this.host = inject(ElementRef);
|
|
2585
2621
|
this.flowSettingsService = inject(FlowSettingsService);
|
|
2586
|
-
this.flowWidth =
|
|
2587
|
-
this.flowHeight = 0;
|
|
2588
|
-
effect(() => {
|
|
2622
|
+
this.flowWidth = computed(() => {
|
|
2589
2623
|
const view = this.flowSettingsService.view();
|
|
2590
|
-
|
|
2591
|
-
|
|
2624
|
+
return view === 'auto' ? '100%' : view[0];
|
|
2625
|
+
});
|
|
2626
|
+
this.flowHeight = computed(() => {
|
|
2627
|
+
const view = this.flowSettingsService.view();
|
|
2628
|
+
return view === 'auto' ? '100%' : view[1];
|
|
2592
2629
|
});
|
|
2593
2630
|
resizable([this.host.nativeElement], inject(NgZone)).pipe(tap(([entry]) => {
|
|
2594
2631
|
this.flowSettingsService.computedFlowWidth.set(entry.contentRect.width);
|
|
@@ -2596,18 +2633,18 @@ class FlowSizeControllerDirective {
|
|
|
2596
2633
|
}), takeUntilDestroyed()).subscribe();
|
|
2597
2634
|
}
|
|
2598
2635
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2599
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]", host: { properties: { "attr.width": "
|
|
2636
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]", host: { properties: { "attr.width": "flowWidth()", "attr.height": "flowHeight()" } }, ngImport: i0 }); }
|
|
2600
2637
|
}
|
|
2601
2638
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: FlowSizeControllerDirective, decorators: [{
|
|
2602
2639
|
type: Directive,
|
|
2603
|
-
args: [{
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2640
|
+
args: [{
|
|
2641
|
+
selector: 'svg[flowSizeController]',
|
|
2642
|
+
host: {
|
|
2643
|
+
'[attr.width]': 'flowWidth()',
|
|
2644
|
+
'[attr.height]': 'flowHeight()'
|
|
2645
|
+
}
|
|
2646
|
+
}]
|
|
2647
|
+
}], ctorParameters: function () { return []; } });
|
|
2611
2648
|
|
|
2612
2649
|
const connectionControllerHostDirective = {
|
|
2613
2650
|
directive: ConnectionControllerDirective,
|
|
@@ -2657,6 +2694,7 @@ class VflowComponent {
|
|
|
2657
2694
|
this.nodeRenderingService = inject(NodeRenderingService);
|
|
2658
2695
|
this.flowSettingsService = inject(FlowSettingsService);
|
|
2659
2696
|
this.componentEventBusService = inject(ComponentEventBusService);
|
|
2697
|
+
this.keyboardService = inject(KeyboardService);
|
|
2660
2698
|
this.injector = inject(Injector);
|
|
2661
2699
|
/**
|
|
2662
2700
|
* Background for flow
|
|
@@ -2748,6 +2786,9 @@ class VflowComponent {
|
|
|
2748
2786
|
set entitiesSelectable(value) {
|
|
2749
2787
|
this.flowSettingsService.entitiesSelectable.set(value);
|
|
2750
2788
|
}
|
|
2789
|
+
set keyboardShortcuts(value) {
|
|
2790
|
+
this.keyboardService.setShortcuts(value);
|
|
2791
|
+
}
|
|
2751
2792
|
/**
|
|
2752
2793
|
* Settings for connection (it renders when user tries to create edge between nodes)
|
|
2753
2794
|
*
|
|
@@ -2846,7 +2887,7 @@ class VflowComponent {
|
|
|
2846
2887
|
}
|
|
2847
2888
|
}
|
|
2848
2889
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2849
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "16.2.12", type: VflowComponent, selector: "vflow", inputs: { view: "view", minZoom: "minZoom", maxZoom: "maxZoom", handlePositions: "handlePositions", background: "background", optimization: "optimization", entitiesSelectable: "entitiesSelectable", connection: ["connection", "connection", (settings) => new ConnectionModel(settings)], nodes: "nodes", edges: "edges" }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
|
|
2890
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "16.1.0", version: "16.2.12", type: VflowComponent, selector: "vflow", inputs: { view: "view", minZoom: "minZoom", maxZoom: "maxZoom", handlePositions: "handlePositions", background: "background", optimization: "optimization", entitiesSelectable: "entitiesSelectable", keyboardShortcuts: "keyboardShortcuts", connection: ["connection", "connection", (settings) => new ConnectionModel(settings)], nodes: "nodes", edges: "edges" }, outputs: { onComponentNodeEvent: "onComponentNodeEvent" }, providers: [
|
|
2850
2891
|
DraggableService,
|
|
2851
2892
|
ViewportService,
|
|
2852
2893
|
FlowStatusService,
|
|
@@ -2856,7 +2897,8 @@ class VflowComponent {
|
|
|
2856
2897
|
NodeRenderingService,
|
|
2857
2898
|
SelectionService,
|
|
2858
2899
|
FlowSettingsService,
|
|
2859
|
-
ComponentEventBusService
|
|
2900
|
+
ComponentEventBusService,
|
|
2901
|
+
KeyboardService
|
|
2860
2902
|
], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateDirective, descendants: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true }, { propertyName: "spacePointContext", first: true, predicate: SpacePointContextDirective, descendants: true }], hostDirectives: [{ directive: ConnectionControllerDirective, outputs: ["onConnect", "onConnect"] }, { directive: ChangesControllerDirective, outputs: ["onNodesChange", "onNodesChange", "onNodesChange.position", "onNodesChange.position", "onNodesChange.position.single", "onNodesChange.position.single", "onNodesChange.position.many", "onNodesChange.position.many", "onNodesChange.size", "onNodesChange.size", "onNodesChange.size.single", "onNodesChange.size.single", "onNodesChange.size.many", "onNodesChange.size.many", "onNodesChange.add", "onNodesChange.add", "onNodesChange.add.single", "onNodesChange.add.single", "onNodesChange.add.many", "onNodesChange.add.many", "onNodesChange.remove", "onNodesChange.remove", "onNodesChange.remove.single", "onNodesChange.remove.single", "onNodesChange.remove.many", "onNodesChange.remove.many", "onNodesChange.select", "onNodesChange.select", "onNodesChange.select.single", "onNodesChange.select.single", "onNodesChange.select.many", "onNodesChange.select.many", "onEdgesChange", "onEdgesChange", "onEdgesChange.detached", "onEdgesChange.detached", "onEdgesChange.detached.single", "onEdgesChange.detached.single", "onEdgesChange.detached.many", "onEdgesChange.detached.many", "onEdgesChange.add", "onEdgesChange.add", "onEdgesChange.add.single", "onEdgesChange.add.single", "onEdgesChange.add.many", "onEdgesChange.add.many", "onEdgesChange.remove", "onEdgesChange.remove", "onEdgesChange.remove.single", "onEdgesChange.remove.single", "onEdgesChange.remove.many", "onEdgesChange.remove.many", "onEdgesChange.select", "onEdgesChange.select", "onEdgesChange.select.single", "onEdgesChange.select.single", "onEdgesChange.select.many", "onEdgesChange.select.many"] }], ngImport: i0, template: "<svg:svg\n rootSvgRef\n rootSvgContext\n rootPointer\n flowSizeController\n class=\"root-svg\"\n #flow\n>\n <defs [markers]=\"markers()\" flowDefs />\n\n <g [background]=\"background\"/>\n\n <svg:g\n mapContext\n spacePointContext\n >\n <!-- Connection -->\n <svg:g\n connection\n [model]=\"connection\"\n [template]=\"connectionTemplateDirective?.templateRef\"\n />\n\n <!-- Edges -->\n <svg:g\n *ngFor=\"let model of edgeModels(); trackBy: trackEdges\"\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective?.templateRef\"\n />\n\n <!-- Nodes -->\n <svg:g\n *ngFor=\"let model of nodeModels(); trackBy: trackNodes\"\n node\n [nodeModel]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective?.templateRef\"\n [attr.transform]=\"model.pointTransform()\"\n />\n </svg:g>\n\n</svg:svg>\n", styles: [":host{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["nodeModel", "nodeTemplate", "groupNodeTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "component", type: BackgroundComponent, selector: "g[background]", inputs: ["background"] }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]" }, { kind: "directive", type: RootSvgReferenceDirective, selector: "svg[rootSvgRef]" }, { kind: "directive", type: RootSvgContextDirective, selector: "svg[rootSvgContext]" }, { kind: "directive", type: RootPointerDirective, selector: "svg[rootPointer]" }, { kind: "directive", type: FlowSizeControllerDirective, selector: "svg[flowSizeController]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2861
2903
|
}
|
|
2862
2904
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: VflowComponent, decorators: [{
|
|
@@ -2871,7 +2913,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2871
2913
|
NodeRenderingService,
|
|
2872
2914
|
SelectionService,
|
|
2873
2915
|
FlowSettingsService,
|
|
2874
|
-
ComponentEventBusService
|
|
2916
|
+
ComponentEventBusService,
|
|
2917
|
+
KeyboardService
|
|
2875
2918
|
], hostDirectives: [
|
|
2876
2919
|
connectionControllerHostDirective,
|
|
2877
2920
|
changesControllerHostDirective
|
|
@@ -2890,6 +2933,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2890
2933
|
type: Input
|
|
2891
2934
|
}], entitiesSelectable: [{
|
|
2892
2935
|
type: Input
|
|
2936
|
+
}], keyboardShortcuts: [{
|
|
2937
|
+
type: Input
|
|
2893
2938
|
}], connection: [{
|
|
2894
2939
|
type: Input,
|
|
2895
2940
|
args: [{ transform: (settings) => new ConnectionModel(settings) }]
|
|
@@ -2946,7 +2991,7 @@ class SelectableDirective {
|
|
|
2946
2991
|
return null;
|
|
2947
2992
|
}
|
|
2948
2993
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SelectableDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2949
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SelectableDirective, selector: "[selectable]", host: { listeners: { "mousedown": "onMousedown()" } }, ngImport: i0 }); }
|
|
2994
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: SelectableDirective, selector: "[selectable]", host: { listeners: { "mousedown": "onMousedown()", "touchstart": "onMousedown()" } }, ngImport: i0 }); }
|
|
2950
2995
|
}
|
|
2951
2996
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SelectableDirective, decorators: [{
|
|
2952
2997
|
type: Directive,
|
|
@@ -2954,6 +2999,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2954
2999
|
}], propDecorators: { onMousedown: [{
|
|
2955
3000
|
type: HostListener,
|
|
2956
3001
|
args: ['mousedown']
|
|
3002
|
+
}, {
|
|
3003
|
+
type: HostListener,
|
|
3004
|
+
args: ['touchstart']
|
|
2957
3005
|
}] } });
|
|
2958
3006
|
|
|
2959
3007
|
const components = [
|