ngx-vflow 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/esm2022/lib/vflow/components/background/background.component.mjs +4 -3
  2. package/esm2022/lib/vflow/components/node/node.component.mjs +3 -1
  3. package/esm2022/lib/vflow/components/preview-flow/draw-node.mjs +100 -0
  4. package/esm2022/lib/vflow/components/preview-flow/preview-flow.component.mjs +62 -0
  5. package/esm2022/lib/vflow/components/vflow/vflow.component.mjs +26 -12
  6. package/esm2022/lib/vflow/directives/map-context.directive.mjs +28 -18
  7. package/esm2022/lib/vflow/directives/space-point-context.directive.mjs +3 -3
  8. package/esm2022/lib/vflow/interfaces/node-preview.interface.mjs +2 -0
  9. package/esm2022/lib/vflow/interfaces/node.interface.mjs +1 -1
  10. package/esm2022/lib/vflow/interfaces/optimization.interface.mjs +6 -2
  11. package/esm2022/lib/vflow/models/edge.model.mjs +42 -15
  12. package/esm2022/lib/vflow/models/handle.model.mjs +3 -1
  13. package/esm2022/lib/vflow/models/node.model.mjs +6 -1
  14. package/esm2022/lib/vflow/services/edge-rendering.service.mjs +14 -2
  15. package/esm2022/lib/vflow/services/flow-settings.service.mjs +3 -1
  16. package/esm2022/lib/vflow/services/node-rendering.service.mjs +35 -2
  17. package/esm2022/lib/vflow/services/preview-flow-render-strategy.service.mjs +21 -0
  18. package/esm2022/lib/vflow/services/viewport.service.mjs +8 -1
  19. package/esm2022/lib/vflow/utils/assert-injector.mjs +27 -0
  20. package/esm2022/lib/vflow/utils/nodes.mjs +3 -3
  21. package/esm2022/lib/vflow/utils/signals/extended-computed.mjs +15 -0
  22. package/esm2022/lib/vflow/utils/signals/to-lazy-signal.mjs +35 -0
  23. package/esm2022/lib/vflow/utils/viewport.mjs +37 -1
  24. package/esm2022/public-api.mjs +2 -1
  25. package/esm2022/testing/component-mocks/vflow-mock.component.mjs +7 -7
  26. package/fesm2022/ngx-vflow-testing.mjs +6 -6
  27. package/fesm2022/ngx-vflow-testing.mjs.map +1 -1
  28. package/fesm2022/ngx-vflow.mjs +438 -48
  29. package/fesm2022/ngx-vflow.mjs.map +1 -1
  30. package/lib/vflow/components/preview-flow/draw-node.d.ts +2 -0
  31. package/lib/vflow/components/preview-flow/preview-flow.component.d.ts +15 -0
  32. package/lib/vflow/components/vflow/vflow.component.d.ts +5 -2
  33. package/lib/vflow/directives/map-context.directive.d.ts +3 -2
  34. package/lib/vflow/interfaces/node-preview.interface.d.ts +3 -0
  35. package/lib/vflow/interfaces/node.interface.d.ts +3 -0
  36. package/lib/vflow/interfaces/optimization.interface.d.ts +17 -1
  37. package/lib/vflow/models/node.model.d.ts +3 -0
  38. package/lib/vflow/services/edge-rendering.service.d.ts +2 -0
  39. package/lib/vflow/services/flow-settings.service.d.ts +2 -0
  40. package/lib/vflow/services/node-rendering.service.d.ts +4 -0
  41. package/lib/vflow/services/preview-flow-render-strategy.service.d.ts +12 -0
  42. package/lib/vflow/services/viewport.service.d.ts +3 -0
  43. package/lib/vflow/utils/assert-injector.d.ts +44 -0
  44. package/lib/vflow/utils/signals/extended-computed.d.ts +5 -0
  45. package/lib/vflow/utils/signals/to-lazy-signal.d.ts +20 -0
  46. package/lib/vflow/utils/viewport.d.ts +19 -0
  47. package/package.json +1 -1
  48. package/public-api.d.ts +1 -0
  49. package/testing/component-mocks/vflow-mock.component.d.ts +3 -3
@@ -0,0 +1,2 @@
1
+ import { NodeModel } from '../../models/node.model';
2
+ export declare function drawNode(ctx: CanvasRenderingContext2D, node: NodeModel): void;
@@ -0,0 +1,15 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class PreviewFlowComponent {
3
+ private viewportService;
4
+ private renderStrategy;
5
+ private nodeRenderingService;
6
+ private renderer2;
7
+ private element;
8
+ private ctx;
9
+ readonly width: import("@angular/core").InputSignal<number>;
10
+ readonly height: import("@angular/core").InputSignal<number>;
11
+ private readonly dpr;
12
+ constructor();
13
+ static ɵfac: i0.ɵɵFactoryDeclaration<PreviewFlowComponent, never>;
14
+ static ɵcmp: i0.ɵɵComponentDeclaration<PreviewFlowComponent, "canvas[previewFlow]", never, { "width": { "alias": "width"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
15
+ }
@@ -52,7 +52,7 @@ export declare class VflowComponent {
52
52
  * Background for flow
53
53
  */
54
54
  set background(value: Background | string);
55
- optimization: import("@angular/core").InputSignal<Optimization>;
55
+ set optimization(newOptimization: Optimization);
56
56
  /**
57
57
  * Global rule if you can or can't select entities
58
58
  */
@@ -132,6 +132,9 @@ export declare class VflowComponent {
132
132
  readonly edgesChange$: import("rxjs").Observable<EdgeChange[]>;
133
133
  protected markers: import("@angular/core").Signal<Map<number, import("ngx-vflow").Marker>>;
134
134
  protected minimap: import("@angular/core").WritableSignal<import("../../models/minimap.model").MinimapModel | null>;
135
+ protected flowOptimization: import("@angular/core").WritableSignal<Required<Optimization>>;
136
+ protected flowWidth: import("@angular/core").WritableSignal<number>;
137
+ protected flowHeight: import("@angular/core").WritableSignal<number>;
135
138
  /**
136
139
  * Change viewport to specified state
137
140
  *
@@ -196,6 +199,6 @@ export declare class VflowComponent {
196
199
  protected trackNodes(idx: number, { rawNode: node }: NodeModel): Node<unknown> | DynamicNode<unknown>;
197
200
  protected trackEdges(idx: number, { edge }: EdgeModel): Edge<unknown>;
198
201
  static ɵfac: i0.ɵɵFactoryDeclaration<VflowComponent, never>;
199
- static ɵcmp: i0.ɵɵComponentDeclaration<VflowComponent, "vflow", never, { "view": { "alias": "view"; "required": false; }; "minZoom": { "alias": "minZoom"; "required": false; }; "maxZoom": { "alias": "maxZoom"; "required": false; }; "background": { "alias": "background"; "required": false; }; "optimization": { "alias": "optimization"; "required": false; "isSignal": true; }; "entitiesSelectable": { "alias": "entitiesSelectable"; "required": false; }; "keyboardShortcuts": { "alias": "keyboardShortcuts"; "required": false; }; "connection": { "alias": "connection"; "required": false; }; "snapGrid": { "alias": "snapGrid"; "required": false; }; "elevateNodesOnSelect": { "alias": "elevateNodesOnSelect"; "required": false; }; "elevateEdgesOnSelect": { "alias": "elevateEdgesOnSelect"; "required": false; }; "nodes": { "alias": "nodes"; "required": true; }; "edges": { "alias": "edges"; "required": false; }; }, { "onComponentNodeEvent": "onComponentNodeEvent"; }, ["nodeTemplateDirective", "nodeSvgTemplateDirective", "groupNodeTemplateDirective", "edgeTemplateDirective", "edgeLabelHtmlDirective", "connectionTemplateDirective"], never, true, [{ directive: typeof i1.ChangesControllerDirective; inputs: {}; 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"; }; }]>;
202
+ static ɵcmp: i0.ɵɵComponentDeclaration<VflowComponent, "vflow", never, { "view": { "alias": "view"; "required": false; }; "minZoom": { "alias": "minZoom"; "required": false; }; "maxZoom": { "alias": "maxZoom"; "required": false; }; "background": { "alias": "background"; "required": false; }; "optimization": { "alias": "optimization"; "required": false; }; "entitiesSelectable": { "alias": "entitiesSelectable"; "required": false; }; "keyboardShortcuts": { "alias": "keyboardShortcuts"; "required": false; }; "connection": { "alias": "connection"; "required": false; }; "snapGrid": { "alias": "snapGrid"; "required": false; }; "elevateNodesOnSelect": { "alias": "elevateNodesOnSelect"; "required": false; }; "elevateEdgesOnSelect": { "alias": "elevateEdgesOnSelect"; "required": false; }; "nodes": { "alias": "nodes"; "required": true; }; "edges": { "alias": "edges"; "required": false; }; }, { "onComponentNodeEvent": "onComponentNodeEvent"; }, ["nodeTemplateDirective", "nodeSvgTemplateDirective", "groupNodeTemplateDirective", "edgeTemplateDirective", "edgeLabelHtmlDirective", "connectionTemplateDirective"], never, true, [{ directive: typeof i1.ChangesControllerDirective; inputs: {}; 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"; }; }]>;
200
203
  static ngAcceptInputType_connection: i2.ConnectionSettings;
201
204
  }
@@ -1,4 +1,4 @@
1
- import { OnInit } from '@angular/core';
1
+ import { NgZone, OnInit } from '@angular/core';
2
2
  import { ZoomBehavior } from 'd3-zoom';
3
3
  import { ViewportService } from '../services/viewport.service';
4
4
  import { SelectionService, ViewportForSelection } from '../services/selection.service';
@@ -10,8 +10,9 @@ export declare class MapContextDirective implements OnInit {
10
10
  protected selectionService: SelectionService;
11
11
  protected viewportService: ViewportService;
12
12
  protected flowSettingsService: FlowSettingsService;
13
+ protected zone: NgZone;
13
14
  protected rootSvgSelection: import("d3-selection").Selection<SVGSVGElement, unknown, null, undefined>;
14
- protected zoomableSelection: import("d3-selection").Selection<SVGGElement, unknown, null, undefined>;
15
+ protected transform: import("@angular/core").WritableSignal<string>;
15
16
  protected viewportForSelection: Partial<ViewportForSelection>;
16
17
  protected manualViewportChangeEffect: import("@angular/core").EffectRef;
17
18
  protected zoomBehavior: ZoomBehavior<SVGSVGElement, unknown>;
@@ -0,0 +1,3 @@
1
+ export interface NodePreview {
2
+ style: Pick<Partial<CSSStyleDeclaration>, 'backgroundColor' | 'borderColor' | 'borderWidth' | 'borderRadius'>;
3
+ }
@@ -2,6 +2,7 @@ import { Type, WritableSignal } from '@angular/core';
2
2
  import { Point } from './point.interface';
3
3
  import { CustomNodeComponent } from '../public-components/custom-node/custom-node.component';
4
4
  import { CustomDynamicNodeComponent } from '../public-components/custom-dynamic-node/custom-dynamic-node.component';
5
+ import { NodePreview } from './node-preview.interface';
5
6
  export type Node<T = any> = DefaultNode | HtmlTemplateNode<T> | SvgTemplateNode<T> | ComponentNode<T> | DefaultGroupNode | TemplateGroupNode<T>;
6
7
  export type DynamicNode<T = any> = DefaultDynamicNode | HtmlTemplateDynamicNode<T> | SvgTemplateDynamicNode<T> | ComponentDynamicNode<T> | DefaultDynamicGroupNode | TemplateDynamicGroupNode<T>;
7
8
  export interface SharedNode {
@@ -9,12 +10,14 @@ export interface SharedNode {
9
10
  point: Point;
10
11
  draggable?: boolean;
11
12
  parentId?: string | null;
13
+ preview?: NodePreview;
12
14
  }
13
15
  export interface SharedDynamicNode {
14
16
  id: string;
15
17
  point: WritableSignal<Point>;
16
18
  draggable?: WritableSignal<boolean>;
17
19
  parentId?: WritableSignal<string | null>;
20
+ preview?: WritableSignal<NodePreview>;
18
21
  }
19
22
  export interface DefaultNode extends SharedNode {
20
23
  type: 'default';
@@ -1,3 +1,4 @@
1
+ export declare const DEFAULT_OPTIMIZATION: Required<Optimization>;
1
2
  export interface Optimization {
2
3
  /**
3
4
  * If true, the layer with groups will be placed behind the edges layer.
@@ -5,5 +6,20 @@ export interface Optimization {
5
6
  *
6
7
  * @default false
7
8
  */
8
- detachedGroupsLayer: boolean;
9
+ detachedGroupsLayer?: boolean;
10
+ /**
11
+ * If true, enables viewport virtualization to improve performance by only rendering
12
+ * nodes and edges that are currently visible in the viewport. This optimization
13
+ * filters out entities that are outside the visible area, reducing the number of
14
+ * DOM elements and improving rendering performance for large flows.
15
+ *
16
+ * It uses canvas as a virtualization layer during viewport change.
17
+ * When the viewport change ends, the library hydrates the canvas with the actual nodes and edges.
18
+ */
19
+ virtualization?: boolean;
20
+ /**
21
+ * The zoom threshold below which the only virtualization layer is drawn.
22
+ * This should help to avoid performance issues when zooming out too much.
23
+ */
24
+ virtualizationZoomThreshold?: number;
9
25
  }
@@ -6,6 +6,7 @@ import { Point } from '../interfaces/point.interface';
6
6
  import { Contextable } from '../interfaces/contextable.interface';
7
7
  import { GroupNodeContext, NodeContext } from '../interfaces/template-context.interface';
8
8
  import { Observable } from 'rxjs';
9
+ import { NodePreview } from '../interfaces/node-preview.interface';
9
10
  export declare class NodeModel<T = unknown> implements FlowEntity, Contextable<NodeContext | GroupNodeContext | {
10
11
  $implicit: object;
11
12
  }> {
@@ -14,6 +15,7 @@ export declare class NodeModel<T = unknown> implements FlowEntity, Contextable<N
14
15
  private static defaultHeight;
15
16
  private static defaultColor;
16
17
  private entitiesService;
18
+ isVisible: import("@angular/core").WritableSignal<boolean>;
17
19
  point: import("@angular/core").WritableSignal<Point>;
18
20
  point$: Observable<Point>;
19
21
  width: import("@angular/core").WritableSignal<number>;
@@ -41,6 +43,7 @@ export declare class NodeModel<T = unknown> implements FlowEntity, Contextable<N
41
43
  renderOrder: import("@angular/core").WritableSignal<number>;
42
44
  selected: import("@angular/core").WritableSignal<boolean>;
43
45
  selected$: Observable<boolean>;
46
+ preview: import("@angular/core").WritableSignal<NodePreview>;
44
47
  globalPoint: import("@angular/core").Signal<{
45
48
  x: number;
46
49
  y: number;
@@ -2,7 +2,9 @@ import { EdgeModel } from '../models/edge.model';
2
2
  import * as i0 from "@angular/core";
3
3
  export declare class EdgeRenderingService {
4
4
  private flowEntitiesService;
5
+ private flowSettingsService;
5
6
  readonly edges: import("@angular/core").Signal<EdgeModel[]>;
7
+ private viewportEdges;
6
8
  private maxOrder;
7
9
  pull(edge: EdgeModel): void;
8
10
  static ɵfac: i0.ɵɵFactoryDeclaration<EdgeRenderingService, never>;
@@ -1,5 +1,6 @@
1
1
  import { WritableSignal } from '@angular/core';
2
2
  import { Background } from '../types/background.type';
3
+ import { Optimization } from '../interfaces/optimization.interface';
3
4
  import * as i0 from "@angular/core";
4
5
  export declare class FlowSettingsService {
5
6
  entitiesSelectable: WritableSignal<boolean>;
@@ -21,6 +22,7 @@ export declare class FlowSettingsService {
21
22
  maxZoom: WritableSignal<number>;
22
23
  background: WritableSignal<Background>;
23
24
  snapGrid: WritableSignal<[number, number]>;
25
+ optimization: WritableSignal<Required<Optimization>>;
24
26
  static ɵfac: i0.ɵɵFactoryDeclaration<FlowSettingsService, never>;
25
27
  static ɵprov: i0.ɵɵInjectableDeclaration<FlowSettingsService>;
26
28
  }
@@ -2,9 +2,13 @@ import { NodeModel } from '../models/node.model';
2
2
  import * as i0 from "@angular/core";
3
3
  export declare class NodeRenderingService {
4
4
  private flowEntitiesService;
5
+ private flowSettingsService;
6
+ private viewportService;
5
7
  readonly nodes: import("@angular/core").Signal<NodeModel<unknown>[]>;
6
8
  readonly groups: import("@angular/core").Signal<NodeModel<unknown>[]>;
7
9
  readonly nonGroups: import("@angular/core").Signal<NodeModel<unknown>[]>;
10
+ viewportNodes: import("@angular/core").Signal<NodeModel<unknown>[]>;
11
+ private viewportNodesAfterInteraction;
8
12
  private maxOrder;
9
13
  pullNode(node: NodeModel): void;
10
14
  static ɵfac: i0.ɵɵFactoryDeclaration<NodeRenderingService, never>;
@@ -0,0 +1,12 @@
1
+ import { NodeModel } from '../models/node.model';
2
+ import * as i0 from "@angular/core";
3
+ export declare abstract class PreviewFlowRenderStrategyService {
4
+ abstract shouldRenderNode(node: NodeModel): boolean;
5
+ static ɵfac: i0.ɵɵFactoryDeclaration<PreviewFlowRenderStrategyService, never>;
6
+ static ɵprov: i0.ɵɵInjectableDeclaration<PreviewFlowRenderStrategyService>;
7
+ }
8
+ export declare class ViewportPreviewFlowRenderStrategyService extends PreviewFlowRenderStrategyService {
9
+ shouldRenderNode(node: NodeModel): boolean;
10
+ static ɵfac: i0.ɵɵFactoryDeclaration<ViewportPreviewFlowRenderStrategyService, never>;
11
+ static ɵprov: i0.ɵɵInjectableDeclaration<ViewportPreviewFlowRenderStrategyService>;
12
+ }
@@ -1,6 +1,7 @@
1
1
  import { WritableSignal } from '@angular/core';
2
2
  import { ViewportState, WritableViewport } from '../interfaces/viewport.interface';
3
3
  import { FitViewOptions } from '../interfaces/fit-view-options.interface';
4
+ import { Subject } from 'rxjs';
4
5
  import * as i0 from "@angular/core";
5
6
  export declare class ViewportService {
6
7
  private entitiesService;
@@ -22,7 +23,9 @@ export declare class ViewportService {
22
23
  * - writableViewport signal
23
24
  */
24
25
  readonly readableViewport: WritableSignal<ViewportState>;
26
+ readonly viewportChangeEnd$: Subject<void>;
25
27
  fitView(options?: FitViewOptions): void;
28
+ triggerViewportChangeEvent(type: 'end'): void;
26
29
  private getBoundsNodes;
27
30
  static ɵfac: i0.ɵɵFactoryDeclaration<ViewportService, never>;
28
31
  static ɵprov: i0.ɵɵInjectableDeclaration<ViewportService>;
@@ -0,0 +1,44 @@
1
+ import { Injector } from '@angular/core';
2
+ /**
3
+ * `assertInjector` extends `assertInInjectionContext` with an optional `Injector`
4
+ * After assertion, `assertInjector` runs the `runner` function with the guaranteed `Injector`
5
+ * whether it is the default `Injector` within the current **Injection Context**
6
+ * or the custom `Injector` that was passed in.
7
+ *
8
+ * @template {() => any} Runner - Runner is a function that can return anything
9
+ * @param {Function} fn - the Function to pass in `assertInInjectionContext`
10
+ * @param {Injector | undefined | null} injector - the optional "custom" Injector
11
+ * @param {Runner} runner - the runner fn
12
+ * @returns {ReturnType<Runner>} result - returns the result of the Runner
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * function injectValue(injector?: Injector) {
17
+ * return assertInjector(injectValue, injector, () => 'value');
18
+ * }
19
+ *
20
+ * injectValue(); // string
21
+ * ```
22
+ */
23
+ export declare function assertInjector<Runner extends () => any>(fn: Function, injector: Injector | undefined | null, runner: Runner): ReturnType<Runner>;
24
+ /**
25
+ * `assertInjector` extends `assertInInjectionContext` with an optional `Injector`
26
+ * After assertion, `assertInjector` returns a guaranteed `Injector` whether it is the default `Injector`
27
+ * within the current **Injection Context** or the custom `Injector` that was passed in.
28
+ *
29
+ * @param {Function} fn - the Function to pass in `assertInInjectionContext`
30
+ * @param {Injector | undefined | null} injector - the optional "custom" Injector
31
+ * @returns Injector
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * function injectDestroy(injector?: Injector) {
36
+ * injector = assertInjector(injectDestroy, injector);
37
+ *
38
+ * return runInInjectionContext(injector, () => {
39
+ * // code
40
+ * })
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function assertInjector(fn: Function, injector: Injector | undefined | null): Injector;
@@ -0,0 +1,5 @@
1
+ import type { CreateComputedOptions } from '@angular/core';
2
+ /**
3
+ * @todo Use `linkedSignal` after Angular update
4
+ */
5
+ export declare function extendedComputed<TValue>(computedCallback: (currentValue: TValue) => TValue, options?: CreateComputedOptions<TValue>): import("@angular/core").Signal<TValue>;
@@ -0,0 +1,20 @@
1
+ import { type Signal } from '@angular/core';
2
+ import { type ToSignalOptions } from '@angular/core/rxjs-interop';
3
+ import type { Observable, Subscribable } from 'rxjs';
4
+ export declare function toLazySignal<T>(source: Observable<T> | Subscribable<T>): Signal<T | undefined>;
5
+ export declare function toLazySignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
6
+ initialValue?: undefined;
7
+ requireSync?: false;
8
+ }): Signal<T | undefined>;
9
+ export declare function toLazySignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
10
+ initialValue?: null;
11
+ requireSync?: false;
12
+ }): Signal<T | null>;
13
+ export declare function toLazySignal<T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
14
+ initialValue?: undefined;
15
+ requireSync: true;
16
+ }): Signal<T>;
17
+ export declare function toLazySignal<T, const U extends T>(source: Observable<T> | Subscribable<T>, options: ToSignalOptions & {
18
+ initialValue: U;
19
+ requireSync?: false;
20
+ }): Signal<T | U>;
@@ -2,3 +2,22 @@ import { Rect } from '../interfaces/rect';
2
2
  import { ViewportState } from '../interfaces/viewport.interface';
3
3
  export declare function getViewportForBounds(bounds: Rect, width: number, height: number, minZoom: number, maxZoom: number, padding: number): ViewportState;
4
4
  export declare function clamp(value: number, min?: number, max?: number): number;
5
+ /**
6
+ * Calculates the visible area bounds in world coordinates based on the current viewport state
7
+ *
8
+ * @param viewport Current viewport state (x, y, zoom)
9
+ * @param flowWidth Width of the flow container
10
+ * @param flowHeight Height of the flow container
11
+ * @returns Rect representing the visible area in world coordinates
12
+ */
13
+ export declare function getViewportBounds(viewport: ViewportState, flowWidth: number, flowHeight: number): Rect;
14
+ /**
15
+ * Checks if a rectangle intersects with the viewport's visible area
16
+ *
17
+ * @param rect Rectangle to check (in world coordinates)
18
+ * @param viewport Current viewport state
19
+ * @param flowWidth Width of the flow container
20
+ * @param flowHeight Height of the flow container
21
+ * @returns true if the rectangle intersects with the viewport, false otherwise
22
+ */
23
+ export declare function isRectInViewport(rect: Rect, viewport: ViewportState, flowWidth: number, flowHeight: number): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-vflow",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "license": "MIT",
5
5
  "homepage": "https://www.ngx-vflow.org/",
6
6
  "author": "Artem Mangilev",
package/public-api.d.ts CHANGED
@@ -13,6 +13,7 @@ export * from './lib/vflow/interfaces/fit-view-options.interface';
13
13
  export * from './lib/vflow/interfaces/optimization.interface';
14
14
  export * from './lib/vflow/interfaces/intersecting-nodes-options.interface';
15
15
  export * from './lib/vflow/interfaces/curve-factory.interface';
16
+ export * from './lib/vflow/interfaces/node-preview.interface';
16
17
  export * from './lib/vflow/types/node-change.type';
17
18
  export * from './lib/vflow/types/edge-change.type';
18
19
  export * from './lib/vflow/types/position.type';
@@ -1,5 +1,5 @@
1
1
  import { WritableSignal, OnInit } from '@angular/core';
2
- import { Node, DynamicNode, Edge, SpacePoint, Point, Background, Optimization, KeyboardShortcuts, ViewportState, NodeChange, EdgeChange, FitViewOptions, VflowComponent, IntersectingNodesOptions, ɵConnectionModel as ConnectionModel } from 'ngx-vflow';
2
+ import { Node, DynamicNode, Edge, SpacePoint, Point, Background, KeyboardShortcuts, ViewportState, NodeChange, EdgeChange, FitViewOptions, VflowComponent, IntersectingNodesOptions, ɵConnectionModel as ConnectionModel } from 'ngx-vflow';
3
3
  import { ConnectionTemplateMockDirective, EdgeLabelHtmlTemplateMockDirective, EdgeTemplateMockDirective, GroupNodeTemplateMockDirective, NodeHtmlTemplateMockDirective } from '../directive-mocks/template-mock.directive';
4
4
  import { AsInterface } from '../types';
5
5
  import * as i0 from "@angular/core";
@@ -14,7 +14,7 @@ export declare class VflowMockComponent implements AsInterface<VflowComponent>,
14
14
  readonly minZoom = 0.5;
15
15
  readonly maxZoom = 3;
16
16
  readonly background: Background | string;
17
- readonly optimization: import("@angular/core").InputSignal<Optimization>;
17
+ readonly optimization: Required<import("ngx-vflow").Optimization>;
18
18
  readonly entitiesSelectable = true;
19
19
  readonly keyboardShortcuts: KeyboardShortcuts;
20
20
  readonly connection: ConnectionModel;
@@ -53,6 +53,6 @@ export declare class VflowMockComponent implements AsInterface<VflowComponent>,
53
53
  getDetachedEdges(): Edge[];
54
54
  protected createSignal<T>(value: T): WritableSignal<T>;
55
55
  static ɵfac: i0.ɵɵFactoryDeclaration<VflowMockComponent, never>;
56
- static ɵcmp: i0.ɵɵComponentDeclaration<VflowMockComponent, "vflow", never, { "nodes": { "alias": "nodes"; "required": true; }; "edges": { "alias": "edges"; "required": false; }; "view": { "alias": "view"; "required": false; }; "minZoom": { "alias": "minZoom"; "required": false; }; "maxZoom": { "alias": "maxZoom"; "required": false; }; "background": { "alias": "background"; "required": false; }; "optimization": { "alias": "optimization"; "required": false; "isSignal": true; }; "entitiesSelectable": { "alias": "entitiesSelectable"; "required": false; }; "keyboardShortcuts": { "alias": "keyboardShortcuts"; "required": false; }; "connection": { "alias": "connection"; "required": false; }; "snapGrid": { "alias": "snapGrid"; "required": false; }; "elevateNodesOnSelect": { "alias": "elevateNodesOnSelect"; "required": false; }; "elevateEdgesOnSelect": { "alias": "elevateEdgesOnSelect"; "required": false; }; }, { "onComponentNodeEvent": "onComponentNodeEvent"; }, ["nodeTemplateDirective", "groupNodeTemplateDirective", "edgeTemplateDirective", "edgeLabelHtmlDirective", "connectionTemplateDirective"], ["*"], true, never>;
56
+ static ɵcmp: i0.ɵɵComponentDeclaration<VflowMockComponent, "vflow", never, { "nodes": { "alias": "nodes"; "required": true; }; "edges": { "alias": "edges"; "required": false; }; "view": { "alias": "view"; "required": false; }; "minZoom": { "alias": "minZoom"; "required": false; }; "maxZoom": { "alias": "maxZoom"; "required": false; }; "background": { "alias": "background"; "required": false; }; "optimization": { "alias": "optimization"; "required": false; }; "entitiesSelectable": { "alias": "entitiesSelectable"; "required": false; }; "keyboardShortcuts": { "alias": "keyboardShortcuts"; "required": false; }; "connection": { "alias": "connection"; "required": false; }; "snapGrid": { "alias": "snapGrid"; "required": false; }; "elevateNodesOnSelect": { "alias": "elevateNodesOnSelect"; "required": false; }; "elevateEdgesOnSelect": { "alias": "elevateEdgesOnSelect"; "required": false; }; }, { "onComponentNodeEvent": "onComponentNodeEvent"; }, ["nodeTemplateDirective", "groupNodeTemplateDirective", "edgeTemplateDirective", "edgeLabelHtmlDirective", "connectionTemplateDirective"], ["*"], true, never>;
57
57
  static ngAcceptInputType_connection: i1.ConnectionSettings;
58
58
  }