ngx-vflow 2.1.0 → 2.2.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.
@@ -3314,9 +3314,10 @@ function constrainRect(rect, model, side, minWidth, minHeight, maxWidth, maxHeig
3314
3314
  }
3315
3315
 
3316
3316
  class HandleModel {
3317
- constructor(rawHandle, parentNode) {
3317
+ constructor(rawHandle, parentNode, batchingService) {
3318
3318
  this.rawHandle = rawHandle;
3319
3319
  this.parentNode = parentNode;
3320
+ this.batchingService = batchingService;
3320
3321
  this.strokeWidth = 2;
3321
3322
  /**
3322
3323
  * Pre-computed size for default handle, changed dynamically
@@ -3339,10 +3340,15 @@ class HandleModel {
3339
3340
  initialValue: { width: 0, height: 0 },
3340
3341
  });
3341
3342
  // TODO: for some reason toLazySignal breaks unit tests, so we use toSignal here
3342
- this.hostPosition = toSignal(this.updateHostSizeAndPosition$.pipe(map(() => ({
3343
- x: this.hostReference instanceof HTMLElement ? this.hostReference.offsetLeft : 0, // for now just 0 for group nodes
3344
- y: this.hostReference instanceof HTMLElement ? this.hostReference.offsetTop : 0, // for now just 0 for group nodes
3345
- }))), {
3343
+ this.hostPosition = toSignal(this.updateHostSizeAndPosition$.pipe(map(() => {
3344
+ const offsets = this.hostReference instanceof HTMLElement
3345
+ ? this.batchingService.getElementOffsets(this.hostReference)
3346
+ : undefined;
3347
+ return {
3348
+ x: offsets ? offsets.offsetLeft : 0, // for now just 0 for group nodes
3349
+ y: offsets ? offsets.offsetTop : 0, // for now just 0 for group nodes
3350
+ };
3351
+ })), {
3346
3352
  initialValue: { x: 0, y: 0 },
3347
3353
  });
3348
3354
  this.hostOffset = computed(() => {
@@ -3390,16 +3396,29 @@ class HandleModel {
3390
3396
  node: this.parentNode.rawNode,
3391
3397
  },
3392
3398
  };
3399
+ if (this.hostReference instanceof HTMLElement) {
3400
+ this.batchingService.addElementCache(this.hostReference);
3401
+ }
3402
+ }
3403
+ onDestroy() {
3404
+ if (this.hostReference instanceof HTMLElement) {
3405
+ this.batchingService.removeElementCache(this.hostReference);
3406
+ }
3393
3407
  }
3394
3408
  updateHost() {
3409
+ this.batchingService.markCacheAsDirty();
3395
3410
  this.updateHostSizeAndPosition$.next();
3396
3411
  }
3397
3412
  getHostSize() {
3413
+ //TODO only get the hist ref width once ?
3398
3414
  if (this.hostReference instanceof HTMLElement) {
3399
- return {
3400
- width: this.hostReference.offsetWidth,
3401
- height: this.hostReference.offsetHeight,
3402
- };
3415
+ const offsets = this.batchingService.getElementOffsets(this.hostReference);
3416
+ if (offsets) {
3417
+ return {
3418
+ width: offsets.offsetWidth,
3419
+ height: offsets.offsetHeight,
3420
+ };
3421
+ }
3403
3422
  }
3404
3423
  else if (this.hostReference instanceof SVGGraphicsElement) {
3405
3424
  return this.hostReference.getBBox();
@@ -3408,12 +3427,102 @@ class HandleModel {
3408
3427
  }
3409
3428
  }
3410
3429
 
3430
+ class OffsetBatchingCacheService {
3431
+ constructor() {
3432
+ this.elementOffsetCache = new Map();
3433
+ this.cacheIsDirty = true;
3434
+ this.minMsBetweenDirty = 16; //1000 ms/second to get 60fps = ~16ms
3435
+ this.lastDirty = undefined;
3436
+ }
3437
+ addElementCache(element) {
3438
+ this.elementOffsetCache.set(element, undefined);
3439
+ this.markCacheAsDirty();
3440
+ }
3441
+ removeElementCache(element) {
3442
+ this.elementOffsetCache.delete(element);
3443
+ }
3444
+ getElementOffsets(requestedElement) {
3445
+ let requestedCache = undefined;
3446
+ const cachedOffset = this.elementOffsetCache.get(requestedElement);
3447
+ if (cachedOffset === undefined) {
3448
+ this.addElementCache(requestedElement);
3449
+ }
3450
+ else {
3451
+ requestedCache = cachedOffset;
3452
+ }
3453
+ //When something request to get the offset of a given element, compute the cache of all the elements of interest until we get the next dirty request.
3454
+ if (this.cacheIsDirty) {
3455
+ for (const { [0]: element } of this.elementOffsetCache) {
3456
+ const offsetWidth = element.offsetWidth;
3457
+ const offsetHeight = element.offsetHeight;
3458
+ const offsetLeft = element.offsetLeft;
3459
+ const offsetTop = element.offsetTop;
3460
+ const cacheEntry = { offsetWidth, offsetHeight, offsetLeft, offsetTop };
3461
+ this.elementOffsetCache.set(element, cacheEntry);
3462
+ if (element === requestedElement) {
3463
+ requestedCache = cacheEntry;
3464
+ }
3465
+ }
3466
+ this.cacheIsDirty = false;
3467
+ }
3468
+ return requestedCache;
3469
+ }
3470
+ markCacheAsDirty() {
3471
+ const now = new Date();
3472
+ if (this.lastDirty === undefined) {
3473
+ this.cacheIsDirty = true;
3474
+ this.lastDirty = now;
3475
+ return;
3476
+ }
3477
+ //force the cache ttl to at minimum 16ms before considering it dirty
3478
+ const msSinceLastDirty = now.getTime() - this.lastDirty?.getTime();
3479
+ if (msSinceLastDirty > this.minMsBetweenDirty) {
3480
+ this.cacheIsDirty = true;
3481
+ this.lastDirty = now;
3482
+ }
3483
+ }
3484
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OffsetBatchingCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3485
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OffsetBatchingCacheService }); }
3486
+ }
3487
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: OffsetBatchingCacheService, decorators: [{
3488
+ type: Injectable
3489
+ }] });
3490
+
3491
+ class RequestAnimationFrameBatchingService {
3492
+ constructor() {
3493
+ this.callbacks = [];
3494
+ this.requestAnimationFrameStarted = false;
3495
+ }
3496
+ batchAnimationFrame(callback) {
3497
+ this.callbacks.push(callback);
3498
+ if (!this.requestAnimationFrameStarted) {
3499
+ this.requestAnimationFrameStarted = true;
3500
+ requestAnimationFrame(() => {
3501
+ this.callbacks.map((x) => {
3502
+ if (x) {
3503
+ x();
3504
+ }
3505
+ });
3506
+ this.callbacks = [];
3507
+ this.requestAnimationFrameStarted = false;
3508
+ });
3509
+ }
3510
+ }
3511
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RequestAnimationFrameBatchingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3512
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RequestAnimationFrameBatchingService }); }
3513
+ }
3514
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: RequestAnimationFrameBatchingService, decorators: [{
3515
+ type: Injectable
3516
+ }] });
3517
+
3411
3518
  class HandleComponent {
3412
3519
  constructor() {
3413
3520
  this.injector = inject(Injector);
3414
3521
  this.handleService = inject(HandleService);
3415
3522
  this.element = inject(ElementRef).nativeElement;
3416
3523
  this.destroyRef = inject(DestroyRef);
3524
+ this.requestAnimationFrameBatchingService = inject(RequestAnimationFrameBatchingService);
3525
+ this.offsetBatchingCacheService = inject(OffsetBatchingCacheService);
3417
3526
  /**
3418
3527
  * At what side of node this component should be placed
3419
3528
  */
@@ -3442,10 +3551,15 @@ class HandleComponent {
3442
3551
  template: this.template(),
3443
3552
  userOffsetX: this.offsetX(),
3444
3553
  userOffsetY: this.offsetY(),
3445
- }, node);
3554
+ }, node, this.offsetBatchingCacheService);
3446
3555
  this.handleService.createHandle(model);
3447
- requestAnimationFrame(() => model.updateHost());
3448
- this.destroyRef.onDestroy(() => this.handleService.destroyHandle(model));
3556
+ this.requestAnimationFrameBatchingService.batchAnimationFrame(() => {
3557
+ model.updateHost();
3558
+ });
3559
+ this.destroyRef.onDestroy(() => {
3560
+ this.handleService.destroyHandle(model);
3561
+ model.onDestroy();
3562
+ });
3449
3563
  }
3450
3564
  });
3451
3565
  }
@@ -3457,19 +3571,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
3457
3571
  args: [{ standalone: true, selector: 'handle', changeDetection: ChangeDetectionStrategy.OnPush, template: "" }]
3458
3572
  }] });
3459
3573
 
3574
+ class ResizeObserverService {
3575
+ constructor() {
3576
+ this.zone = inject(NgZone);
3577
+ this.thingsToObserve = new Map();
3578
+ this.resizeObserver = new ResizeObserver((entries) => {
3579
+ this.zone.run(() => {
3580
+ for (const entry of entries) {
3581
+ const callbacks = this.thingsToObserve.get(entry.target);
3582
+ if (callbacks !== undefined) {
3583
+ callbacks.forEach((c) => c(entry));
3584
+ }
3585
+ }
3586
+ });
3587
+ });
3588
+ }
3589
+ addObserver(element, callback) {
3590
+ const callbacks = this.thingsToObserve.get(element);
3591
+ if (callbacks === undefined) {
3592
+ this.thingsToObserve.set(element, [callback]);
3593
+ }
3594
+ else {
3595
+ callbacks.push(callback);
3596
+ }
3597
+ this.resizeObserver.observe(element);
3598
+ }
3599
+ removeObserver(element) {
3600
+ this.thingsToObserve.delete(element);
3601
+ if (this.resizeObserver) {
3602
+ this.resizeObserver.unobserve(element);
3603
+ }
3604
+ }
3605
+ ngOnDestroy() {
3606
+ this.resizeObserver.disconnect();
3607
+ }
3608
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ResizeObserverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3609
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ResizeObserverService }); }
3610
+ }
3611
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: ResizeObserverService, decorators: [{
3612
+ type: Injectable
3613
+ }], ctorParameters: () => [] });
3614
+
3460
3615
  class NodeHandlesControllerDirective {
3461
3616
  constructor() {
3462
3617
  this.nodeAccessor = inject(NodeAccessorService);
3463
- this.zone = inject(NgZone);
3464
3618
  this.destroyRef = inject(DestroyRef);
3465
3619
  this.hostElementRef = inject(ElementRef);
3620
+ this.resizeObserverService = inject(ResizeObserverService);
3621
+ this.requestAnimationFrameBatchingService = inject(RequestAnimationFrameBatchingService);
3466
3622
  }
3467
3623
  ngOnInit() {
3468
3624
  const model = this.nodeAccessor.model();
3625
+ let isTrackingHostElement = false;
3469
3626
  model.handles$
3470
- .pipe(switchMap((handles) => resizable([...handles.map((h) => h.hostReference), this.hostElementRef.nativeElement], this.zone).pipe(map(() => handles))), tap((handles) => {
3627
+ .pipe(pairwise(), tap(([previousHandles, currentHandles]) => {
3628
+ const handlesToRemove = previousHandles.filter((prev) => currentHandles.find((curr) => curr.hostReference === prev.hostReference) === undefined);
3629
+ handlesToRemove.forEach((h) => this.resizeObserverService.removeObserver(h.hostReference));
3630
+ const handlesToAdd = currentHandles.filter((curr) => previousHandles.find((prev) => curr.hostReference === prev.hostReference) === undefined);
3631
+ if (!isTrackingHostElement) {
3632
+ this.resizeObserverService.addObserver(this.hostElementRef.nativeElement, () => {
3633
+ currentHandles.forEach((h) => h.updateHost());
3634
+ });
3635
+ isTrackingHostElement = true;
3636
+ }
3637
+ handlesToAdd.forEach((h) => this.resizeObserverService.addObserver(h.hostReference, () => {
3638
+ currentHandles.forEach((h) => h.updateHost());
3639
+ }));
3640
+ //Here we need this to be in a requestAnimationFrame otherwise the handle can still be present in the dom which throws off the offset cache
3641
+ this.requestAnimationFrameBatchingService.batchAnimationFrame(() => {
3642
+ currentHandles.forEach((h) => h.updateHost());
3643
+ });
3471
3644
  // TODO (performance) inspect how to avoid calls of this when flow initially rendered
3472
- handles.forEach((h) => h.updateHost());
3473
3645
  }), takeUntilDestroyed(this.destroyRef))
3474
3646
  .subscribe();
3475
3647
  }
@@ -3490,19 +3662,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
3490
3662
  class NodeResizeControllerDirective {
3491
3663
  constructor() {
3492
3664
  this.nodeAccessor = inject(NodeAccessorService);
3493
- this.zone = inject(NgZone);
3494
- this.destroyRef = inject(DestroyRef);
3665
+ this.resizeObserverService = inject(ResizeObserverService);
3495
3666
  this.hostElementRef = inject(ElementRef);
3496
3667
  }
3497
3668
  ngOnInit() {
3498
3669
  const model = this.nodeAccessor.model();
3499
- const host = this.hostElementRef.nativeElement;
3500
- merge(resizable([host], this.zone))
3501
- .pipe(startWith(null), filter(() => !model.resizing()), tap(() => {
3502
- model.width.set(host.clientWidth);
3503
- model.height.set(host.clientHeight);
3504
- }), takeUntilDestroyed(this.destroyRef))
3505
- .subscribe();
3670
+ this.resizeObserverService.addObserver(this.hostElementRef.nativeElement, (resizeEntry) => {
3671
+ model.width.set(resizeEntry.target.clientWidth);
3672
+ model.height.set(resizeEntry.target.clientHeight);
3673
+ });
3674
+ }
3675
+ ngOnDestroy() {
3676
+ this.resizeObserverService.removeObserver(this.hostElementRef.nativeElement);
3506
3677
  }
3507
3678
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: NodeResizeControllerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3508
3679
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.17", type: NodeResizeControllerDirective, isStandalone: true, selector: "[nodeResizeController]", ngImport: i0 }); }
@@ -3756,6 +3927,8 @@ const defaultBg = '#fff';
3756
3927
  const defaultGap = 20;
3757
3928
  const defaultDotSize = 2;
3758
3929
  const defaultDotColor = 'rgb(177, 177, 183)';
3930
+ const defaultGridSize = 20;
3931
+ const defaultStrokeWidth = 2;
3759
3932
  const defaultImageScale = 0.1;
3760
3933
  const defaultRepeated = true;
3761
3934
  class BackgroundComponent {
@@ -3764,21 +3937,16 @@ class BackgroundComponent {
3764
3937
  this.rootSvg = inject(RootSvgReferenceDirective).element;
3765
3938
  this.settingsService = inject(FlowSettingsService);
3766
3939
  this.backgroundSignal = this.settingsService.background;
3767
- // DOTS PATTERN
3768
- this.scaledGap = computed(() => {
3769
- const background = this.backgroundSignal();
3770
- if (background.type === 'dots') {
3771
- const zoom = this.viewportService.readableViewport().zoom;
3772
- return zoom * (background.gap ?? defaultGap);
3773
- }
3774
- return 0;
3940
+ this.x = computed(() => {
3941
+ return this.viewportService.readableViewport().x % this.scaledGap();
3942
+ });
3943
+ this.y = computed(() => {
3944
+ return this.viewportService.readableViewport().y % this.scaledGap();
3775
3945
  });
3776
- this.x = computed(() => this.viewportService.readableViewport().x % this.scaledGap());
3777
- this.y = computed(() => this.viewportService.readableViewport().y % this.scaledGap());
3778
3946
  this.patternColor = computed(() => {
3779
- const bg = this.backgroundSignal();
3780
- if (bg.type === 'dots') {
3781
- return bg.color ?? defaultDotColor;
3947
+ const background = this.backgroundSignal();
3948
+ if (background.type === 'dots' || background.type === 'grid') {
3949
+ return background.color ?? defaultDotColor;
3782
3950
  }
3783
3951
  return defaultDotColor;
3784
3952
  });
@@ -3787,6 +3955,29 @@ class BackgroundComponent {
3787
3955
  if (background.type === 'dots') {
3788
3956
  return (this.viewportService.readableViewport().zoom * (background.size ?? defaultDotSize)) / 2;
3789
3957
  }
3958
+ if (background.type === 'grid') {
3959
+ return this.viewportService.readableViewport().zoom * (background.size ?? defaultGridSize);
3960
+ }
3961
+ return 0;
3962
+ });
3963
+ this.scaledGap = computed(() => {
3964
+ const background = this.backgroundSignal();
3965
+ const zoom = this.viewportService.readableViewport().zoom;
3966
+ if (background.type === 'dots') {
3967
+ return zoom * (background.gap ?? defaultGap);
3968
+ }
3969
+ if (background.type === 'grid') {
3970
+ return zoom * (background.size ?? defaultGridSize);
3971
+ }
3972
+ return 0;
3973
+ });
3974
+ // GRID PATTERN
3975
+ this.strokeWidth = computed(() => {
3976
+ const background = this.backgroundSignal();
3977
+ if (background.type === 'grid') {
3978
+ const zoom = this.viewportService.readableViewport().zoom;
3979
+ return zoom * ((background.strokeWidth ?? defaultStrokeWidth) / 2);
3980
+ }
3790
3981
  return 0;
3791
3982
  });
3792
3983
  // IMAGE PATTERN
@@ -3844,17 +4035,20 @@ class BackgroundComponent {
3844
4035
  if (background.type === 'dots') {
3845
4036
  this.rootSvg.style.backgroundColor = background.backgroundColor ?? defaultBg;
3846
4037
  }
4038
+ if (background.type === 'grid') {
4039
+ this.rootSvg.style.backgroundColor = background.backgroundColor ?? defaultBg;
4040
+ }
3847
4041
  if (background.type === 'solid') {
3848
4042
  this.rootSvg.style.backgroundColor = background.color;
3849
4043
  }
3850
4044
  });
3851
4045
  }
3852
4046
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BackgroundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3853
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: BackgroundComponent, isStandalone: true, selector: "g[background]", ngImport: i0, template: "@if (backgroundSignal().type === 'dots') {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\">\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n}\n\n@if (backgroundSignal().type === 'image') {\n @if (repeated()) {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\">\n <svg:image [attr.href]=\"bgImageSrc()\" [attr.width]=\"scaledImageWidth()\" [attr.height]=\"scaledImageHeight()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n }\n\n @if (!repeated()) {\n <svg:image\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\"\n [attr.href]=\"bgImageSrc()\" />\n }\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4047
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.17", type: BackgroundComponent, isStandalone: true, selector: "g[background]", ngImport: i0, template: "@if (backgroundSignal().type === 'dots') {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\">\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n}\n\n@if (backgroundSignal().type === 'image') {\n @if (repeated()) {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\">\n <svg:image [attr.href]=\"bgImageSrc()\" [attr.width]=\"scaledImageWidth()\" [attr.height]=\"scaledImageHeight()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n }\n\n @if (!repeated()) {\n <svg:image\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\"\n [attr.href]=\"bgImageSrc()\" />\n }\n}\n\n@if (backgroundSignal().type === 'grid') {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"patternSize()\"\n [attr.height]=\"patternSize()\">\n <svg:path\n fill=\"none\"\n [attr.d]=\"'M ' + patternSize() + ' 0 L 0 0 0 ' + patternSize()\"\n [attr.stroke]=\"patternColor()\"\n [attr.stroke-width]=\"strokeWidth()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n}\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3854
4048
  }
3855
4049
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: BackgroundComponent, decorators: [{
3856
4050
  type: Component,
3857
- args: [{ standalone: true, selector: 'g[background]', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (backgroundSignal().type === 'dots') {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\">\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n}\n\n@if (backgroundSignal().type === 'image') {\n @if (repeated()) {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\">\n <svg:image [attr.href]=\"bgImageSrc()\" [attr.width]=\"scaledImageWidth()\" [attr.height]=\"scaledImageHeight()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n }\n\n @if (!repeated()) {\n <svg:image\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\"\n [attr.href]=\"bgImageSrc()\" />\n }\n}\n" }]
4051
+ args: [{ standalone: true, selector: 'g[background]', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (backgroundSignal().type === 'dots') {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"scaledGap()\"\n [attr.height]=\"scaledGap()\">\n <svg:circle\n [attr.cx]=\"patternSize()\"\n [attr.cy]=\"patternSize()\"\n [attr.r]=\"patternSize()\"\n [attr.fill]=\"patternColor()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n}\n\n@if (backgroundSignal().type === 'image') {\n @if (repeated()) {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\">\n <svg:image [attr.href]=\"bgImageSrc()\" [attr.width]=\"scaledImageWidth()\" [attr.height]=\"scaledImageHeight()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n }\n\n @if (!repeated()) {\n <svg:image\n [attr.x]=\"imageX()\"\n [attr.y]=\"imageY()\"\n [attr.width]=\"scaledImageWidth()\"\n [attr.height]=\"scaledImageHeight()\"\n [attr.href]=\"bgImageSrc()\" />\n }\n}\n\n@if (backgroundSignal().type === 'grid') {\n <svg:pattern\n patternUnits=\"userSpaceOnUse\"\n [attr.id]=\"patternId\"\n [attr.x]=\"x()\"\n [attr.y]=\"y()\"\n [attr.width]=\"patternSize()\"\n [attr.height]=\"patternSize()\">\n <svg:path\n fill=\"none\"\n [attr.d]=\"'M ' + patternSize() + ' 0 L 0 0 0 ' + patternSize()\"\n [attr.stroke]=\"patternColor()\"\n [attr.stroke-width]=\"strokeWidth()\" />\n </svg:pattern>\n\n <svg:rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" [attr.fill]=\"patternUrl\" />\n}\n" }]
3858
4052
  }], ctorParameters: () => [] });
3859
4053
  function createImage(url) {
3860
4054
  const image = new Image();
@@ -4695,6 +4889,9 @@ class VflowComponent {
4695
4889
  OverlaysService,
4696
4890
  { provide: PreviewFlowRenderStrategyService, useClass: ViewportPreviewFlowRenderStrategyService },
4697
4891
  FlowRenderingService,
4892
+ ResizeObserverService,
4893
+ OffsetBatchingCacheService,
4894
+ RequestAnimationFrameBatchingService,
4698
4895
  ], queries: [{ propertyName: "nodeTemplateDirective", first: true, predicate: NodeHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "nodeSvgTemplateDirective", first: true, predicate: NodeSvgTemplateDirective, descendants: true, isSignal: true }, { propertyName: "groupNodeTemplateDirective", first: true, predicate: GroupNodeTemplateDirective, descendants: true, isSignal: true }, { propertyName: "edgeTemplateDirective", first: true, predicate: EdgeTemplateDirective, descendants: true, isSignal: true }, { propertyName: "edgeLabelHtmlDirective", first: true, predicate: EdgeLabelHtmlTemplateDirective, descendants: true, isSignal: true }, { propertyName: "connectionTemplateDirective", first: true, predicate: ConnectionTemplateDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "mapContext", first: true, predicate: MapContextDirective, descendants: true, isSignal: true }, { propertyName: "spacePointContext", first: true, predicate: SpacePointContextDirective, descendants: true, isSignal: true }], hostDirectives: [{ directive: ChangesControllerDirective, outputs: ["nodesChanges", "nodesChanges", "nodesChanges.position", "nodesChanges.position", "nodesChanges.size", "nodesChanges.size", "nodesChanges.add", "nodesChanges.add", "nodesChanges.remove", "nodesChanges.remove", "nodesChanges.select", "nodesChanges.select", "edgesChanges", "edgesChanges", "edgesChanges.detached", "edgesChanges.detached", "edgesChanges.add", "edgesChanges.add", "edgesChanges.remove", "edgesChanges.remove", "edgesChanges.select", "edgesChanges.select"] }], ngImport: i0, template: "<svg:svg #flow rootSvgRef rootSvgContext rootPointer flowSizeController class=\"root-svg\">\n <defs flowDefs [markers]=\"markers()\" />\n\n <g background />\n\n <svg:g mapContext spacePointContext autoPan>\n @if (alignmentHelper(); as alignmentHelper) {\n @if (alignmentHelper === true) {\n <svg:g alignmentHelper />\n } @else {\n <svg:g alignmentHelper [tolerance]=\"alignmentHelper.tolerance\" [lineColor]=\"alignmentHelper.lineColor\" />\n }\n }\n\n <!-- Connection -->\n <svg:g connection [model]=\"connection\" [template]=\"connectionTemplateDirective()?.templateRef\" />\n\n @if (flowOptimization().detachedGroupsLayer) {\n <!-- Groups -->\n @for (model of groups(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n <!-- Nodes -->\n @for (model of nonGroups(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [nodeSvgTemplate]=\"nodeSvgTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n\n @if (!flowOptimization().detachedGroupsLayer) {\n <!-- Edges -->\n @for (model of edgeModels(); track trackEdges($index, model)) {\n <svg:g\n edge\n [model]=\"model\"\n [edgeTemplate]=\"edgeTemplateDirective()?.templateRef\"\n [edgeLabelHtmlTemplate]=\"edgeLabelHtmlDirective()?.templateRef\" />\n }\n\n @for (model of nodeModels(); track trackNodes($index, model)) {\n <svg:g\n node\n [model]=\"model\"\n [nodeTemplate]=\"nodeTemplateDirective()?.templateRef\"\n [nodeSvgTemplate]=\"nodeSvgTemplateDirective()?.templateRef\"\n [groupNodeTemplate]=\"groupNodeTemplateDirective()?.templateRef\"\n [attr.transform]=\"model.pointTransform()\" />\n }\n }\n </svg:g>\n\n <!-- Minimap -->\n @if (minimap(); as minimap) {\n <ng-container [ngTemplateOutlet]=\"minimap.template()\" />\n }\n</svg:svg>\n\n@if (flowOptimization().virtualization) {\n <canvas previewFlow class=\"preview-flow\" [width]=\"flowWidth()\" [height]=\"flowHeight()\"></canvas>\n}\n", styles: [":host{display:grid;grid-template-columns:1fr;width:100%;height:100%;-webkit-user-select:none;user-select:none}:host ::ng-deep *{box-sizing:border-box}.root-svg{grid-row-start:1;grid-column-start:1}.preview-flow{pointer-events:none;grid-row-start:1;grid-column-start:1}\n"], dependencies: [{ 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]" }, { kind: "component", type: DefsComponent, selector: "defs[flowDefs]", inputs: ["markers"] }, { kind: "component", type: BackgroundComponent, selector: "g[background]" }, { kind: "directive", type: MapContextDirective, selector: "g[mapContext]" }, { kind: "directive", type: SpacePointContextDirective, selector: "g[spacePointContext]" }, { kind: "component", type: ConnectionComponent, selector: "g[connection]", inputs: ["model", "template"] }, { kind: "component", type: NodeComponent, selector: "g[node]", inputs: ["model", "nodeTemplate", "nodeSvgTemplate", "groupNodeTemplate"] }, { kind: "component", type: EdgeComponent, selector: "g[edge]", inputs: ["model", "edgeTemplate", "edgeLabelHtmlTemplate"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PreviewFlowComponent, selector: "canvas[previewFlow]", inputs: ["width", "height"] }, { kind: "component", type: AlignmentHelperComponent, selector: "g[alignmentHelper]", inputs: ["tolerance", "lineColor"] }, { kind: "directive", type: AutoPanDirective, selector: "[autoPan]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4699
4896
  }
4700
4897
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: VflowComponent, decorators: [{
@@ -4715,6 +4912,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
4715
4912
  OverlaysService,
4716
4913
  { provide: PreviewFlowRenderStrategyService, useClass: ViewportPreviewFlowRenderStrategyService },
4717
4914
  FlowRenderingService,
4915
+ ResizeObserverService,
4916
+ OffsetBatchingCacheService,
4917
+ RequestAnimationFrameBatchingService,
4718
4918
  ], hostDirectives: [changesControllerHostDirective], imports: [
4719
4919
  RootSvgReferenceDirective,
4720
4920
  RootSvgContextDirective,