UNPKG

@swimlane/ngx-graph

Version:
1,057 lines (1,039 loc) 58.9 kB
import * as _angular_core from '@angular/core'; import { EventEmitter, ElementRef, NgZone, OnInit, OnChanges, OnDestroy, AfterViewInit, ChangeDetectorRef, TemplateRef, SimpleChanges } from '@angular/core'; import * as i1 from '@angular/common'; import { Observable, Subscription, Subject } from 'rxjs'; import * as d3_scale from 'd3-scale'; import { Layout as Layout$1, ID3StyleLayoutAdaptor, Group, InputNode, Link } from 'webcola'; interface NodePosition { x: number; y: number; } interface NodeDimension { width: number; height: number; } interface Node { id: string; position?: NodePosition; dimension?: NodeDimension; transform?: string; label?: string; data?: any; meta?: any; layoutOptions?: any; parentId?: string; hidden?: boolean; } interface ClusterNode extends Node { childNodeIds?: string[]; } interface CompoundNode extends Node { childNodeIds?: string[]; } interface Edge { id?: string; source: string; target: string; label?: string; data?: any; points?: any; /** Raw layout polyline from before the latest tick; morphing resamples this in redrawLines. */ previousPoints?: Array<{ x: number; y: number; }>; line?: string; textTransform?: string; textAngle?: number; oldLine?: any; oldTextPath?: string; textPath?: string; midPoint?: NodePosition; } interface Graph { edges: Edge[]; nodes: Node[]; compoundNodes?: CompoundNode[]; clusters?: ClusterNode[]; edgeLabels?: any; } /** * Layout engine contract. Optional hooks support custom drag behavior and parsing `Node.transform` for * layout transition bookkeeping (same translate semantics as the graph default: node-group origin in layout space). */ interface Layout { settings?: any; run(graph: Graph): Graph | Observable<Graph>; updateEdge(graph: Graph, edge: Edge): Graph | Observable<Graph>; onDragStart?(draggingNode: Node, $event: MouseEvent): void; onDrag?(draggingNode: Node, $event: MouseEvent): void; onDragEnd?(draggingNode: Node, $event: MouseEvent): void; parseTranslate?(transformStr: string | undefined): { tx: number; ty: number; }; } declare class LayoutService { getLayout(name: string): Layout; static ɵfac: _angular_core.ɵɵFactoryDeclaration<LayoutService, never>; static ɵprov: _angular_core.ɵɵInjectableDeclaration<LayoutService>; } declare enum PanningAxis { Both = "both", Horizontal = "horizontal", Vertical = "vertical" } declare enum MiniMapPosition { UpperLeft = "UpperLeft", UpperRight = "UpperRight", LowerLeft = "LowerLeft", LowerRight = "LowerRight" } interface MiniMapMargin { top: number; right: number; bottom: number; left: number; } declare const DefaultMiniMapMargin: { top: number; right: number; bottom: number; left: number; }; declare class ColorHelper { scale: any; colorDomain: any[]; domain: any; customColors: any; constructor(scheme: any, domain: any, customColors?: any); generateColorScheme(scheme: any, domain: any): d3_scale.ScaleOrdinal<string, unknown, never>; getColor(value: any): any; } interface ViewDimensions { width: number; height: number; } /** * Visibility Observer */ declare class VisibilityObserver { private element; private zone; visible: EventEmitter<any>; timeout: any; isVisible: boolean; constructor(element: ElementRef, zone: NgZone); destroy(): void; onVisibilityChange(): void; runCheck(): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration<VisibilityObserver, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration<VisibilityObserver, "visibility-observer", never, {}, { "visible": "visible"; }, never, never, true, never>; } /** Named easings mapped to d3-ease factories (same as layout morph animations). */ type GraphTransitionEasingName = 'linear' | 'cubicIn' | 'cubicOut' | 'cubicInOut' | 'quadInOut' | 'elasticOut'; type GraphLayoutTransitionMode = 'none' | 'instant' | 'tween'; /** * Where **previous** `translate(tx,ty)` values come from before the graph model is replaced (layout morph). * Applies to **nodes, clusters, and compound nodes** equally (`g.node-group[id]` under `.graph.chart`). * Does **not** affect cluster/compound **size** morph: prior width/height for that tween always come from the * graph model at capture time, not from the DOM. */ type LayoutMorphPreviousSource = /** Read `transform` on the graph model only (backward compatible). */ 'model-transform' /** Read `g.node-group[id]` under `.graph.chart` (excludes minimap). */ | 'dom-svg' /** DOM first; if translate is near zero, use model `position` / `transform` (see `degenerateEpsilon`). */ | 'dom-with-model-fallback'; /** * Options for capturing prior node-group translates when `mode: 'tween'`. * With `scope: 'full'`, the same unified rAF tween drives **nodes, clusters, and compounds** (translate lerp plus, * for clusters/compounds only, optional width/height lerp from model snapshots). */ interface LayoutMorphCapture { /** * Default `model-transform` (backward compatible). * DOM modes read the same `g.node-group` elements used for regular nodes; clusters and compounds use the same * `class="node-group"` + `id` wiring in the graph template. */ previousSource?: LayoutMorphPreviousSource; /** Used with `dom-with-model-fallback`. Default `1e-3`. */ degenerateEpsilon?: number; /** * Order when resolving a node id on the **model** graph for fallback math. * Default `['compound', 'cluster', 'node']`. */ modelResolutionOrder?: Array<'compound' | 'cluster' | 'node'>; /** * After `tick()`, for `scope: 'full'` only, recompute `layoutAnimationTargets` from layout `position` (and * `dimension` via `centerNodesOnPositionChange`) for every id already in the target map — **including clusters and * compounds**. Runs before transforms are reset to “previous” for the tween, so endpoints match the new layout. * Default false. */ syncTargetsFromPositionAfterTick?: boolean; /** * When true: new **node** ids also get `previousLayoutTransforms` snapped to the current target so they do not tween * from a missing or bogus origin; plus degenerate-stable snap for clusters/compounds (see `degenerateEpsilon`). * Role changes (same id moving between `nodes` / `clusters` / `compoundNodes`) and **new** cluster/compound ids snap * even when this flag is false. Default false. */ snapAddedNodeIds?: boolean; } declare const DEFAULT_LAYOUT_MORPH_CAPTURE: LayoutMorphCapture; declare function mergeLayoutMorphCapture(partial: LayoutMorphCapture | null | undefined): LayoutMorphCapture; /** * Layout transition after graph model / layout output changes. * - `none` / `instant`: keep prior snapshot for continuity, then snap to final layout in one frame (no rAF morph). * - `tween`: interpolate node-group translates and edge paths over `durationMs` with `easing`. Full scope tweens * **nodes, clusters, and compounds** together; cluster/compound rects can also tween width/height from prior model * dimensions to the new layout’s dimensions (`scope: 'additive'` skips positional and size tweens on groups). */ interface GraphLayoutTransition { mode: GraphLayoutTransitionMode; /** When `tween`, only new ids interpolate in additive mode; stable nodes/edges snap. */ scope: 'full' | 'additive'; durationMs: number; easing: GraphTransitionEasingName | ((t: number) => number); /** When `mode: 'tween'`, how **prior translates** are captured; see {@link LayoutMorphPreviousSource}. */ morphCapture?: LayoutMorphCapture; } declare const DEFAULT_GRAPH_LAYOUT_TRANSITION: GraphLayoutTransition; /** Programmatic viewport pan (panTo, center, minimap, zoomToFit autoCenter): optional eased translation only; zoom scale unchanged. */ interface ViewportTranslationTransition { enabled: boolean; durationMs: number; easing: GraphTransitionEasingName | ((t: number) => number); } declare const DEFAULT_VIEWPORT_TRANSLATION_TRANSITION: ViewportTranslationTransition; /** Optional flair during layout tween only (default off). */ interface LayoutTransitionEffect { kind: 'none' | 'perspectiveFlip' | 'rotate'; /** Max rotation in degrees (perspective flip uses rotateX). */ peakDegrees?: number; /** For `rotate`: pivot in graph coordinates. */ rotatePivot?: 'graphCenter' | 'viewportCenter' | { nodeId: string; }; } declare const DEFAULT_LAYOUT_TRANSITION_EFFECT: LayoutTransitionEffect; declare function resolveGraphTransitionEasing(easing: GraphLayoutTransition['easing'] | ViewportTranslationTransition['easing']): (t: number) => number; /** * Resolves final layout transition from the graph `transitionAfterChanges` input merged with defaults. * When unset or empty, returns {@link DEFAULT_GRAPH_LAYOUT_TRANSITION} (`mode: 'instant'`). */ declare function mergeGraphLayoutTransition(explicit: Partial<GraphLayoutTransition> | null | undefined): GraphLayoutTransition; declare function mergeViewportTransition(explicit: Partial<ViewportTranslationTransition> | null | undefined): ViewportTranslationTransition; declare function mergeLayoutEffect(explicit: Partial<LayoutTransitionEffect> | null | undefined): LayoutTransitionEffect; /** * Matrix */ interface Matrix { a: number; b: number; c: number; d: number; e: number; f: number; } interface NgxGraphZoomOptions { autoCenter?: boolean; force?: boolean; } declare enum NgxGraphStates { Init = "init", Subscribe = "subscribe", Transform = "transform", Output = "output" } interface NgxGraphStateChangeEvent { state: NgxGraphStates; } /** * Root graph component (`ngx-graph`). * * **Layout transitions:** JS-driven morphing (`transitionAfterChanges` with `mode: 'tween'`) is **opt-in**; if the input is * omitted, merged defaults use `mode: 'instant'` (no rAF tween). The host may carry `layout-js-driven` (see * {@link useLayoutTransitions}): when present, styles set `transition: none` on `.node-group` so imperative * `transform` updates do not fight CSS. **`smooth-layout`** is set only while tweening is active. * * **Template outlets:** Context objects are stable per graph id (see `outletContextGraphNode` / `outletContextLink` / …): `transitionAfterChangesActive` and `$implicit` are updated in place so consumer templates are not recreated on viewport-only CD. */ declare class GraphComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit { private el; zone: NgZone; cd: ChangeDetectorRef; private layoutService; private readonly injector; readonly nodes: _angular_core.InputSignal<Node[]>; readonly clusters: _angular_core.InputSignal<ClusterNode[]>; readonly compoundNodes: _angular_core.InputSignal<CompoundNode[]>; readonly links: _angular_core.InputSignal<Edge[]>; readonly activeEntries: _angular_core.ModelSignal<any[]>; readonly curve: _angular_core.ModelSignal<any>; readonly enableDrag: _angular_core.ModelSignal<boolean>; readonly nodeHeight: _angular_core.InputSignal<number>; readonly nodeMaxHeight: _angular_core.InputSignal<number>; readonly nodeMinHeight: _angular_core.InputSignal<number>; readonly nodeWidth: _angular_core.InputSignal<number>; readonly nodeMinWidth: _angular_core.InputSignal<number>; readonly nodeMaxWidth: _angular_core.InputSignal<number>; readonly enablePan: _angular_core.ModelSignal<boolean>; readonly panningAxis: _angular_core.InputSignal<PanningAxis>; readonly enableZoom: _angular_core.ModelSignal<boolean>; readonly zoomSpeed: _angular_core.InputSignal<number>; readonly minZoomLevel: _angular_core.InputSignal<number>; readonly maxZoomLevel: _angular_core.InputSignal<number>; readonly autoZoom: _angular_core.InputSignal<boolean>; readonly panOnZoom: _angular_core.InputSignal<boolean>; readonly animate: _angular_core.InputSignal<boolean>; readonly autoCenter: _angular_core.InputSignal<boolean>; readonly update$: _angular_core.InputSignal<Observable<any>>; readonly center$: _angular_core.InputSignal<Observable<any>>; readonly zoomToFit$: _angular_core.InputSignal<Observable<NgxGraphZoomOptions>>; readonly panToNode$: _angular_core.InputSignal<Observable<any>>; readonly layout: _angular_core.ModelSignal<string | Layout>; readonly layoutSettings: _angular_core.InputSignal<any>; readonly enableTrackpadSupport: _angular_core.InputSignal<boolean>; readonly showMiniMap: _angular_core.InputSignal<boolean>; readonly miniMapMaxWidth: _angular_core.InputSignal<number>; readonly miniMapMaxHeight: _angular_core.InputSignal<number>; readonly miniMapPosition: _angular_core.InputSignal<MiniMapPosition>; readonly miniMapMargin: _angular_core.InputSignal<MiniMapMargin>; readonly view: _angular_core.InputSignal<[number, number]>; readonly scheme: _angular_core.InputSignal<any>; readonly customColors: _angular_core.InputSignal<any>; readonly deferDisplayUntilPosition: _angular_core.InputSignal<boolean>; readonly centerNodesOnPositionChange: _angular_core.InputSignal<boolean>; readonly enablePreUpdateTransform: _angular_core.InputSignal<boolean>; /** * Layout transition after nodes/links change. Merged with defaults via {@link mergeGraphLayoutTransition}; when omitted or * empty, defaults apply (`mode: 'instant'`). Use `{ mode: 'tween', scope: 'full' }` for full-graph interpolation, or * `{ mode: 'tween', scope: 'additive', durationMs: 0 }` for additive-only tweening. * * **`mode: 'tween'` is opt-in** (no tween unless you pass a partial that resolves to tween after merge). */ readonly transitionAfterChanges: _angular_core.InputSignal<Partial<GraphLayoutTransition>>; /** * Number of samples along each edge polyline when building `line` / morph segments (layout tick, drag * {@link redrawEdge}, unified morph). Clamped to `[2, 512]`; default `48` when unset or non-finite. */ readonly edgePathSampleCount: _angular_core.InputSignal<number>; /** * When `true` (default), the host always has the `layout-js-driven` class so CSS does not animate `.node-group` * `transform` (matches historical behavior). When `false`, `layout-js-driven` is applied only while JS layout morphing * is active ({@link layoutJsMorphEnabled}), allowing host CSS transitions on node groups when not using `mode: 'tween'`. */ readonly useLayoutTransitions: _angular_core.InputSignal<boolean>; /** Optional eased translation for programmatic pan only (`panTo`, `center`, minimap, `zoomToFit` autoCenter). Zoom scale is never animated. */ readonly transitionDuringTransform: _angular_core.InputSignal<Partial<ViewportTranslationTransition>>; /** Optional perspective / rotate flair during layout morph only (`mode: 'tween'`). */ readonly layoutTransitionEffect: _angular_core.InputSignal<Partial<LayoutTransitionEffect>>; /** Template alias `zoomLevel` — imperatively sets zoom; see {@link zoomTo}. */ readonly zoomLevelInput: _angular_core.InputSignal<number>; /** Template alias `panOffsetX` — imperatively pans; see {@link panTo}. */ readonly panOffsetXInput: _angular_core.InputSignal<number>; /** Template alias `panOffsetY` — imperatively pans; see {@link panTo}. */ readonly panOffsetYInput: _angular_core.InputSignal<number>; readonly select: _angular_core.OutputEmitterRef<void>; readonly activate: _angular_core.OutputEmitterRef<any>; readonly deactivate: _angular_core.OutputEmitterRef<any>; readonly zoomChange: _angular_core.OutputEmitterRef<number>; readonly clickHandler: _angular_core.OutputEmitterRef<MouseEvent>; readonly stateChange: _angular_core.OutputEmitterRef<NgxGraphStateChangeEvent>; readonly drawComplete: _angular_core.OutputEmitterRef<void>; readonly linkTemplate: _angular_core.Signal<TemplateRef<any>>; readonly nodeTemplate: _angular_core.Signal<TemplateRef<any>>; readonly clusterTemplate: _angular_core.Signal<TemplateRef<any>>; readonly defsTemplate: _angular_core.Signal<TemplateRef<any>>; readonly miniMapNodeTemplate: _angular_core.Signal<TemplateRef<any>>; readonly nodeElements: _angular_core.Signal<readonly ElementRef<any>[]>; readonly clusterElements: _angular_core.Signal<readonly ElementRef<any>[]>; readonly linkElements: _angular_core.Signal<readonly ElementRef<any>[]>; chartWidth: any; private isMouseMoveCalled; graphSubscription: Subscription; colors: ColorHelper; dims: ViewDimensions; seriesDomain: any; transform: string; isPanning: boolean; isDragging: boolean; draggingNode: Node; initialized: boolean; graph: Graph; graphDims: any; _oldLinks: Edge[]; oldNodes: Set<string>; oldClusters: Set<string>; oldCompoundNodes: Set<string>; /** Incremented at the start of each {@link tick}; completion callbacks only emit when this matches. */ private drawCompleteTickId; private _graphDestroyed; transformationMatrix: Matrix; _touchLastX: any; _touchLastY: any; minimapScaleCoefficient: number; minimapTransform: string; minimapOffsetX: number; minimapOffsetY: number; isMinimapPanning: boolean; minimapClipPathId: string; width: number; height: number; resizeSubscription: any; visibilityObserver: VisibilityObserver; private waitForGraphDims; private destroy$; /** Latest requestAnimationFrame id per edge for imperative path morphing (cancel on layout / drag). */ private readonly edgePathRafIds; /** * Stable {@link NgTemplateOutlet} context objects keyed by graph id so consumer templates (tooltips, nested * directives) are not destroyed/recreated every CD when only the viewport or `$implicit` reference changes. */ private readonly graphMainNodeOutletCtx; private readonly graphMinimapNodeOutletCtx; private readonly graphClusterOutletCtx; private readonly graphCompoundOutletCtx; private readonly graphLinkOutletCtx; /** Single rAF when layout morph unifies node transforms + edge paths. */ private layoutUnifiedRafId; /** rAF for programmatic viewport pan easing (translation only). */ private viewportPanAnimRafId; /** CSS `transform` on `.ngx-graph-outer` during optional layout flair (perspective / rotate). */ layoutOuterTransform: string | null; /** `transform-origin` for {@link layoutOuterTransform} when using rotate pivot modes. */ layoutEffectTransformOrigin: string; /** Parsed `translate(tx,ty)` from the graph before a new layout is applied. */ private previousLayoutTransforms; /** Target `translate(tx,ty)` after tick applyTransforms (before reset to previous for animation). */ private layoutAnimationTargets; /** * Prior layout `dimension` for cluster and compound ids (single map; compounds overwrite clusters on id clash). * Always taken from the **graph model** at {@link capturePreviousLayoutTransforms} time — not from the DOM, even * when {@link LayoutMorphCapture.previousSource} uses DOM for translates. Cleared with {@link previousLayoutTransforms}. */ private previousLayoutClusterCompoundDimensions; /** * Target dimensions after `applyTransforms` for the current morph tick (clusters then compounds). Cleared with * {@link layoutAnimationTargets}. Only set for non-`additive` scope. During the tween, translate is lerped between * fixed endpoints while dimensions interpolate separately; with {@link centerNodesOnPositionChange} and large size * changes, the visual center can drift slightly mid-tween though start and end states match layout. */ private layoutAnimationClusterCompoundDimensions; /** Node ids present after the previous completed `tick` (for additive smooth transitions). */ private priorTickGraphNodeIds; /** * Which graph collection owned each id after the previous `tick` (`nodes`, `clusters`, or `compoundNodes`). * Used with {@link priorTickGraphNodeIds} so layout morph does not treat an id as “stable” when it moved between * collections (e.g. same id reused for a node then a compound). Duplicate ids across lists are resolved like * {@link collectPreviousTranslatesFromModelTransforms}: later collections overwrite earlier ones. */ private priorTickGraphKindById; /** Edge keys present after the previous completed `tick` (aligned with {@link linkKeyForLookup}). */ private priorTickEdgeKeys; /** Snapshot of {@link priorTickEdgeKeys} at the start of the current `tick` (for classifying new edges). */ private edgeKeysAtLayoutTickStart; /** Skip layout morph on the next `tick()` when an update follows viewport-only zoom (unchanged host inputs). */ private suppressLayoutMorphThisTick; /** True for the current {@link tick} when node/edge layout morph is suppressed (viewport-only zoom). */ private morphSuppressedThisTick; /** Until this timestamp, `createGraph` may set {@link suppressLayoutMorphThisTick} when inputs are unchanged. */ private viewportZoomMorphSuppressUntilMs; private static readonly WHEEL_ZOOM_MORPH_SUPPRESS_MS; /** Sorted host-input id/edge signature; used for viewport-only zoom morph suppress. */ private lastInputTopologySignature; /** Container size from the last full `update()` (not viewport-only fast path). */ private lastFullUpdateWidth; private lastFullUpdateHeight; constructor(el: ElementRef, zone: NgZone, cd: ChangeDetectorRef, layoutService: LayoutService); /** * Updates pan, zoom, and/or node-drag interaction flags in one call. Only keys present are applied. * * @example graph.setViewportInteractions({ pan: false, zoom: false, drag: false }) */ setViewportInteractions(options: { pan?: boolean; zoom?: boolean; drag?: boolean; }): void; /** Starts canvas pan on mouse down only when {@link enablePan} is true. */ onPanningSurfaceMouseDown(): void; /** Coloring domain key; default coalesces `label`, then `id`, then `''` so {@link ColorHelper} never receives null/undefined. */ readonly groupResultsBy: _angular_core.InputSignal<(node: any) => string>; /** Merged layout transition config from {@link transitionAfterChanges} and defaults. */ get effectiveLayoutTransition(): GraphLayoutTransition; /** `true` when rAF morph should run after layout (`mode: 'tween'`). */ get layoutMorphActive(): boolean; /** Whether layout morphing is active (`transitionAfterChanges` resolved to `mode: 'tween'`). Exposed on template outlets as `transitionAfterChangesActive`. */ get layoutJsMorphEnabled(): boolean; /** Host `layout-js-driven` class: always on when {@link useLayoutTransitions} is `true`; otherwise only when {@link layoutJsMorphEnabled}. */ get layoutJsDrivenHostClass(): boolean; get effectiveViewportTransition(): ViewportTranslationTransition; get effectiveLayoutEffect(): LayoutTransitionEffect; /** * Get the current zoom level */ get zoomLevel(): number; /** * Get the current `x` position of the graph */ get panOffsetX(): number; /** * Get the current `y` position of the graph */ get panOffsetY(): number; /** * Angular lifecycle event * * * @memberOf GraphComponent */ ngOnInit(): void; ngOnChanges(changes: SimpleChanges): void; /** * @param layoutInputChanged - When true, clears `initialized` so `@if (initialized && graph)` does not * flash empty on unrelated input updates (nodes/links only). Only layout identity changes should reset. */ setLayout(layout: string | Layout, layoutInputChanged?: boolean): void; setLayoutSettings(settings: any): void; /** * Angular lifecycle event * * * @memberOf GraphComponent */ ngOnDestroy(): void; /** * Angular lifecycle event * * * @memberOf GraphComponent */ ngAfterViewInit(): void; /** * Base class update implementation for the dag graph * * @param options.forceRelayout When true, always run the full layout path (used by {@link zoomTo} with `layout: true`). * @memberOf GraphComponent */ update(options?: { forceRelayout?: boolean; }): void; /** * Creates the dagre graph engine * * @memberOf GraphComponent */ createGraph(): void; /** Host-input topology (not post-layout internal graph shape). */ private buildInputTopology; private isViewportMorphSuppressActive; private hasContainerSizeChangedSinceLastFullUpdate; /** Host-input topology unchanged while the post-zoom morph-suppress window is active. */ private shouldSuppressLayoutMorphForViewport; private setViewportMorphSuppress; /** * While async layout (e.g. ELK) runs, keep showing the previous snapshot's positions and edge routes * so inputs without x/y do not flash to (0,0). Seeds `capturePreviousLayoutTransforms` with real geometry. */ private applyVisualContinuityBeforeLayout; private setDisplayTransformsFromPositions; private edgeEndpointNodeId; /** Merged ELK `properties` from the active layout (defaults + instance settings). */ private elkMergedProperties; /** * Inter-layer gap for provisional seeding. Graph-level merged props often carry `20` from ElkLayout defaults while * `createNodeTree` applies {@link LAYERED_NODE_NODE_BETWEEN_LAYERS_PX} per-node — match the latter for seed math. */ private getElkLayerSpacingGapPx; private elkDirectionOrDown; /** * Place the new node center where layered ELK will approximately put it: half parent + inter-layer gap + half child, * along the layout axis. Aligns with `buildFallbackEdgePoints` (center-to-center) better than a flat pixel delta. */ private seedProvisionalPositionFromParentEdge; /** * For a new cluster/compound before layout, approximate group center from child node positions already merged * onto `next.nodes` (avoids a (0,0) flash at the top-left). */ private seedProvisionalGroupPositionFromChildren; /** Bootstrap: hide nodes still at default origin until first ELK `tick()` supplies real positions. */ private markDefaultOriginNodesHiddenUntilLayout; /** * Draws the graph using dagre layouts * * * @memberOf GraphComponent */ draw(): void; /** Default: first `translate(a, b)` in the node-group transform string. */ private parseTranslateDefault; /** Prefers `Layout.parseTranslate` on the resolved layout object when present. */ resolveTranslateFromTransform(transformStr: string | undefined): { tx: number; ty: number; }; /** Same key shape as dagre/graphlib `_edgeLabels` keys and as used in tick edge maps. */ private linkKeyForLookup; /** Matches layout engines that merge `defaultSettings` with `settings` (e.g. DagreNodesOnly multigraph). */ private isLayoutMultigraph; /** * graphlib `edgeArgsToId`: v + \\x01 + w + \\x01 + name (name defaults to \\x00). * Aligns with {@link linkKeyForLookup}; falls back to legacy regex when the id is not graphlib-shaped. */ private graphlibEdgeLabelIdToLookupKey; /** * Snapshot current node-group translates (and cluster/compound dimensions) before replacing `graph` for layout morph. * {@link LayoutMorphCapture.previousSource} controls **translates only** (`previousLayoutTransforms`): DOM modes read * `g.node-group[id]` for nodes, clusters, and compounds. Prior cluster/compound size always use the model * (`previousLayoutClusterCompoundDimensions`). Requires `_oldLinks` so the first paint does not run an empty morph. */ capturePreviousLayoutTransforms(): void; /** Resample count for edge polylines (layout, morph, drag). */ private effectiveEdgePathSampleCount; private collectPreviousTranslatesFromModelTransforms; /** Prior `dimension` for clusters then compounds (model graph; used with layout morph size tween). */ private collectPreviousClusterCompoundDimensionsFromModel; private getMainChartGroupElement; /** Main-chart `g.node-group` only (not minimap duplicates). */ private findMainChartNodeGroup; private translateFromLayoutPositionForNode; private findMorphNodeById; private modelTranslateForMorphFallback; private isDegenerateTranslate; /** `translate` of `nodeEl`'s origin in `chartG` user space (pan/zoom excluded from node-local chain). */ private translateNodeGroupInChartUserSpace; private collectPreviousTranslatesFromDom; /** When {@link LayoutMorphCapture.syncTargetsFromPositionAfterTick} is on, recompute targets from `position`. */ private syncLayoutAnimationTargetsFromPositions; /** * Aligns `previousLayoutTransforms` with layout targets before `resetToPrevious` when: * - The same `id` moved between `nodes` / `clusters` / `compoundNodes` (always; does not require * {@link LayoutMorphCapture.snapAddedNodeIds}), * - A cluster or compound id is new since the last tick (always, so new groups do not tween from a missing origin), * - With {@link LayoutMorphCapture.snapAddedNodeIds}: any new id (including plain nodes), plus degenerate-stable snap * for clusters/compounds (see {@link isDegenerateTranslate} and `morphCapture.degenerateEpsilon`). */ private snapMorphPreviousForAddedNodes; applyAdditiveSmoothTransitionFilters(targets: Map<string, { tx: number; ty: number; }>, priorTickGraphIds: Set<string>, priorTickGraphKindById: Map<string, 'node' | 'cluster' | 'compound'>): void; private refreshPriorTickGraphIds; /** * Builds one edge entry for {@link tick}. `lookupKey` / `legacyKey` must match {@link linkKeyForLookup} / * `_oldLinks` (graphlib uses string ids; ELK uses array `edgeLabels` and real keys from the edge). */ private pushTickEdgeLink; /** Drop outlet contexts for ids no longer in the graph so templates do not retain stale references. */ private pruneTemplateOutletContextCaches; /** Stable context for `#nodeTemplate` on the main chart (see {@link graphMainNodeOutletCtx}). */ outletContextGraphNode(node: Node): { $implicit: Node; transitionAfterChangesActive: boolean; }; /** Stable context for `#nodeTemplate` / `#miniMapNodeTemplate` on the minimap. */ outletContextMinimapNode(node: Node): { $implicit: Node; transitionAfterChangesActive: boolean; }; /** Stable context for `#clusterTemplate`. */ outletContextCluster(node: Node): { $implicit: Node; transitionAfterChangesActive: boolean; }; /** Stable context for `#nodeTemplate` on compound nodes. */ outletContextCompoundNode(node: Node): { $implicit: Node; transitionAfterChangesActive: boolean; }; /** Stable context for `#linkTemplate`. */ outletContextLink(link: Edge): { $implicit: Edge; transitionAfterChangesActive: boolean; }; tick(): void; private applyTransforms; /** `translate` for `<g class="node-group">` from `position` (center when `centerNodesOnPositionChange`). */ private updateNodeGroupTransform; /** Call after `applyNodeDimensions` when node box size changes but `position` (center) must stay fixed. */ private syncNodeTransformsFromLayoutPositions; /** Optional CSS transform on the chart host during layout morph (`layoutTransitionEffect`). */ private applyLayoutOuterEffect; private cancelViewportPanAnimation; /** Applies one programmatic pan step with optional viewport easing (translation only). */ private animateViewportPanDelta; /** * Default node template: circle inscribed in the layout box so ELK/Dagre edge ports (box edges) meet the glyph. */ defaultNodeCircleRadius(node: Node): number; getMinimapTransform(): string; updateGraphDims(): void; /** True when the graph has at least one node, compound node, or cluster (for bounds / minimap). */ private hasGraphNodeLikeContent; updateMinimap(): void; /** * Resolves a layout node, compound node, or cluster by the `<g>` element `id`. */ private findLayoutNodeByElementId; /** * Measures one node-group SVG element and writes `dimension` on the model node. */ private applyNodeDimensionFromSvgGroup; /** * Measures the node element and applies the dimensions * * @memberOf GraphComponent */ applyNodeDimensions(): void; /** * Runs after Angular commits the template so D3 binds to the live link `<path>` elements * (OnPush + rAF alone can run too early). Retries once after `requestAnimationFrame` if link * groups are not ready yet — avoids two immediate `afterNextRender` passes that cancel unified RAF. */ private scheduleRedrawLinesAfterView; /** * d3 `select('#…')` for a host subtree element by HTML `id`. Raw ids may contain `--`, leading digits, etc., which are * invalid in unescaped CSS id selectors. */ private selectTextPathInHostById; /** * Imperative paint from `edge.line` / `edge.textPath` only — does not cancel unified layout morph or per-edge rAF. */ private repaintLinkPathsDomFromModel; /** * Binds D3 to link `<path>` elements, then emits {@link stateChange} (Output) and {@link drawComplete} * when link groups match edge count (or after bounded retries). * * Observable layouts (Cola, D3 force) can emit faster than `afterNextRender`; callbacks may run with a * superseded `tickId`. Those passes must not call {@link redrawLines} with morph enabled — it would * {@link cancelLayoutUnifiedAnimation} and interrupt full-graph layout morphs. Instead, repaint paths * from the current model when no rAF tween owns the paths; the latest `tickId` still runs full {@link redrawLines}. * {@link finalizeTickOutput} alone enforces `drawCompleteTickId`. */ private tryRedrawLinesAfterView; private finalizeTickOutput; private cancelEdgePathAnimation; private cancelAllEdgePathAnimations; private cancelLayoutUnifiedAnimation; /** * One rAF driver: same eased t for node transforms (lerp) and edge path d (interpolatePinnedEdgeRoute). * In `additive` mode, only edges morph; node transforms stay at layout output from `tick()` (no positional tween). * Full scope lerps translates for nodes, clusters, and compounds; clusters/compounds also lerp `dimension` when both * prior and target dimension maps are populated (see {@link layoutAnimationClusterCompoundDimensions}). */ private runUnifiedLayoutAnimation; /** * Imperative path morph: D3 transitions do not reliably repaint `d` under Zone; rAF does. */ private runEdgePathMorphAnimation; /** * Redraws the lines when dragged or viewport updated * * @memberOf GraphComponent */ redrawLines(_animate?: boolean): void; private clonePoints; /** Resample a polyline to `count` points along cumulative arc length. */ private resamplePolyline; /** * Interpolate between two layout snapshots. The first and last points always follow * the raw layout endpoints (ports on source/target nodes); interior points blend the * arc-length–resampled routes so straight, orthogonal, and curved polylines all morph smoothly. */ private interpolatePinnedEdgeRoute; private lineAndTextPathFromPoints; /** * Layout-space node center from a prior `translate(tx,ty)` (inverse of {@link updateNodeGroupTransform}). */ private layoutCenterFromPreviousTransform; /** Same curve template as {@link buildFallbackEdgePoints} — smooth polyline between two layout centers. */ private polylineBetweenLayoutCenters; /** * Prior-tick polyline for a brand-new edge key so full-scope morph starts from anchors aligned with * `previousLayoutTransforms` (and current position for endpoints without a prior transform). */ private syntheticPreviousEdgePointsFromPriorTransforms; private layoutCenterForSyntheticEdgeEndpoint; /** * When layout does not provide edge routes, build a smooth polyline between node centers (Bezier-friendly). */ private buildFallbackEdgePoints; /** * Calculate the text directions / flipping * * @memberOf GraphComponent */ calcDominantBaseline(link: any, displayPoints?: Array<{ x: number; y: number; }>): void; /** * Generate the new line path * * @memberOf GraphComponent */ generateLine(points: any): any; /** * Resamples the route polyline to {@link edgePathSampleCount} (via {@link effectiveEdgePathSampleCount}) and builds * the stroke with {@link generateLine} so drag-time paths match layout ticks and morphs (same d3 `curve` + density). */ private lineAndDisplayFromRoutePoints; /** * Zoom was invoked from event * * @memberOf GraphComponent */ onZoom($event: WheelEvent, direction: string): void; /** * Pan by x/y * * @param x * @param y */ pan(x: number, y: number, ignoreZoomLevel?: boolean): void; /** * Pan to a fixed x/y * */ panTo(x: number, y: number): void; /** * Zoom by a factor * */ zoom(factor: number): void; /** * Zoom to a fixed level. Pass `{ layout: false }` for viewport-only sync (e.g. `[zoomLevel]` binding). */ zoomTo(level: number, options?: { layout?: boolean; }): void; /** * Drag was invoked from an event * * @memberOf GraphComponent */ onDrag(event: MouseEvent): void; redrawEdge(edge: Edge): void; /** * Update the entire view for the new pan position * * * @memberOf GraphComponent */ updateTransform(): void; /** * Node was clicked * * * @memberOf GraphComponent */ onClick(event: any): void; /** * Node was focused * * * @memberOf GraphComponent */ onActivate(event: any): void; /** * Node was defocused * * @memberOf GraphComponent */ onDeactivate(event: any): void; /** * Get the domain series for the nodes * * @memberOf GraphComponent */ getSeriesDomain(): any[]; /** * Tracking for the link * * * @memberOf GraphComponent */ trackLinkBy(index: number, link: Edge): any; /** * Tracking for the node * * * @memberOf GraphComponent */ trackNodeBy(index: number, node: Node): any; /** * Sets the colors the nodes * * * @memberOf GraphComponent */ setColors(): void; /** * On mouse move event, used for panning and dragging. * * @memberOf GraphComponent */ onMouseMove($event: MouseEvent): void; onMouseDown(event: MouseEvent): void; graphClick(event: MouseEvent): void; /** * On touch start event to enable panning. * * @memberOf GraphComponent */ onTouchStart(event: any): void; /** * On touch move event, used for panning. * */ onTouchMove($event: any): void; /** * On touch end event to disable panning. * * @memberOf GraphComponent */ onTouchEnd(): void; /** * On mouse up event to disable panning/dragging. * * @memberOf GraphComponent */ onMouseUp(event: MouseEvent): void; /** * On node mouse down to kick off dragging * * @memberOf GraphComponent */ onNodeMouseDown(event: MouseEvent, node: any): void; /** * On minimap drag mouse down to kick off minimap panning * * @memberOf GraphComponent */ onMinimapDragMouseDown(): void; /** * On minimap pan event. Pans the graph to the clicked position. * Uses screen→local conversion so it works for any `miniMapPosition` (the old formula assumed UpperRight). * * @memberOf GraphComponent */ onMinimapPanTo(event: MouseEvent): void; /** * Map a click on the minimap background to graph (world) coordinates. `minimapTransform` is applied on the host * `<g class="minimap">`; `getScreenCTM()` on the target rect includes that transform so all corners behave the same. */ private minimapClientEventToGraphCoords; /** * Center the graph in the viewport */ center(): void; /** * Zooms to fit the entire graph */ zoomToFit(zoomOptions?: NgxGraphZoomOptions): void; /** * Pans to the node * @param nodeId */ panToNodeId(nodeId: string): void; getCompoundNodeChildren(ids: Array<string>): Node[]; private panWithConstraints; private updateMidpointOnEdge; private _calcMidPointElk; basicUpdate(): void; getContainerDims(): any; /** * Checks if the graph has dimensions */ hasGraphDims(): boolean; /** * Checks if all nodes have dimension */ hasNodeDims(): boolean; /** * Checks if all compound nodes have dimension */ hasCompoundNodeDims(): boolean; /** * Checks if all clusters have dimension */ hasClusterDims(): boolean; /** * Checks if the graph and all nodes have dimension. */ hasDims(): boolean; protected unbindEvents(): void; private bindWindowResizeEvent; static ɵfac: _angular_core.ɵɵFactoryDeclaration<GraphComponent, never>; static ɵcmp: _angular_core.ɵɵComponentDeclaration<GraphComponent, "ngx-graph", never, { "nodes": { "alias": "nodes"; "required": false; "isSignal": true; }; "clusters": { "alias": "clusters"; "required": false; "isSignal": true; }; "compoundNodes": { "alias": "compoundNodes"; "required": false; "isSignal": true; }; "links": { "alias": "links"; "required": false; "isSignal": true; }; "activeEntries": { "alias": "activeEntries"; "required": false; "isSignal": true; }; "curve": { "alias": "curve"; "required": false; "isSignal": true; }; "enableDrag": { "alias": "enableDrag"; "required": false; "isSignal": true; }; "nodeHeight": { "alias": "nodeHeight"; "required": false; "isSignal": true; }; "nodeMaxHeight": { "alias": "nodeMaxHeight"; "required": false; "isSignal": true; }; "nodeMinHeight": { "alias": "nodeMinHeight"; "required": false; "isSignal": true; }; "nodeWidth": { "alias": "nodeWidth"; "required": false; "isSignal": true; }; "nodeMinWidth": { "alias": "nodeMinWidth"; "required": false; "isSignal": true; }; "nodeMaxWidth": { "alias": "nodeMaxWidth"; "required": false; "isSignal": true; }; "enablePan": { "alias": "enablePan"; "required": false; "isSignal": true; }; "panningAxis": { "alias": "panningAxis"; "required": false; "isSignal": true; }; "enableZoom": { "alias": "enableZoom"; "required": false; "isSignal": true; }; "zoomSpeed": { "alias": "zoomSpeed"; "required": false; "isSignal": true; }; "minZoomLevel": { "alias": "minZoomLevel"; "required": false; "isSignal": true; }; "maxZoomLevel": { "alias": "maxZoomLevel"; "required": false; "isSignal": true; }; "autoZoom": { "alias": "autoZoom"; "required": false; "isSignal": true; }; "panOnZoom": { "alias": "panOnZoom"; "required": false; "isSignal": true; }; "animate": { "alias": "animate"; "required": false; "isSignal": true; }; "autoCenter": { "alias": "autoCenter"; "required": false; "isSignal": true; }; "update$": { "alias": "update$"; "required": false; "isSignal": true; }; "center$": { "alias": "center$"; "required": false; "isSignal": true; }; "zoomToFit$": { "alias": "zoomToFit$"; "required": false; "isSignal": true; }; "panToNode$": { "alias": "panToNode$"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; "layoutSettings": { "alias": "layoutSettings"; "required": false; "isSignal": true; }; "enableTrackpadSupport": { "alias": "enableTrackpadSupport"; "required": false; "isSignal": true; }; "showMiniMap": { "alias": "showMiniMap"; "required": false; "isSignal": true; }; "miniMapMaxWidth": { "alias": "miniMapMaxWidth"; "required": false; "isSignal": true; }; "miniMapMaxHeight": { "alias": "miniMapMaxHeight"; "required": false; "isSignal": true; }; "miniMapPosition": { "alias": "miniMapPosition"; "required": false; "isSignal": true; }; "miniMapMargin": { "alias": "miniMapMargin"; "required": false; "isSignal": true; }; "view": { "alias": "view"; "required": false; "isSignal": true; }; "scheme": { "alias": "scheme"; "required": false; "isSignal": true; }; "customColors": { "alias": "customColors"; "required": false; "isSignal": true; }; "deferDisplayUntilPosition": { "alias": "deferDisplayUntilPosition"; "required": false; "isSignal": true; }; "centerNodesOnPositionChange": { "alias": "centerNodesOnPositionChange"; "required": false; "isSignal": true; }; "enablePreUpdateTransform": { "alias": "enablePreUpdateTransform"; "required": false; "isSignal": true; }; "transitionAfterChanges": { "alias": "transitionAfterChanges"; "required": false; "isSignal": true; }; "edgePathSampleCount": { "alias": "edgePathSampleCount"; "required": false; "isSignal": true; }; "useLayoutTransitions": { "alias": "useLayoutTransitions"; "required": false; "isSignal": true; }; "transitionDuringTransform": { "alias": "transitionDuringTransform"; "required": false; "isSignal": true; }; "layoutTransitionEffect": { "alias": "layoutTransitionEffect"; "required": false; "isSignal": true; }; "zoomLevelInput": { "alias": "zoomLevel"; "required": false; "isSignal": true; }; "panOffsetXInput": { "alias": "panOffsetX"; "required": false; "isSignal": true; }; "panOffsetYInput": { "alias": "panOffsetY"; "required": false; "isSignal": true; }; "groupResultsBy": { "alias": "groupResultsBy"; "required": false; "isSignal": true; }; }, { "activeEntries": "activeEntriesChange"; "curve": "curveChange"; "enableDrag": "enableDragChange"; "enablePan": "enablePanChange"; "enableZoom": "enableZoomChange"; "layout": "layoutChange"; "select": "select"; "activate": "activate"; "deactivate": "deactivate"; "zoomChange": "zoomChange"; "clickHandler": "clickHandler"; "stateChange": "stateChange"; "drawComplete": "drawComplete"; }, ["linkTemplate", "nodeTemplate", "clusterTemplate", "defsTemplate", "miniMapNodeTemplate"], ["*"], true, never>; } /** * Mousewheel directive * https://github.com/SodhanaLibrary/angular2-examples/blob/master/app/mouseWheelDirective/mousewheel.directive.ts * * @export */ declare class MouseWheelDirective { readonly mouseWheelUp: _angular_core.OutputEmitterRef<WheelEvent>; readonly mouseWheelDown: _angular_core.OutputEmitterRef<WheelEvent>; onMouseWheelChrome(event: any): void; onMouseWheelFirefox(event: any): void; onWheel(event: any): void; onMouseWheelIE(event: any): void; mouseWheelFunc(event: any): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration<MouseWheelDirective, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MouseWheelDirective, "[mouseWheel]", never, {}, { "mouseWheelUp": "mouseWheelUp"; "mouseWheelDown": "mouseWheelDown"; }, never, never, true, never>; } /** * @deprecated `GraphComponent`, `MouseWheelDirective`, and `VisibilityObserver` are now standalone. * Import them directly into your component's `imports` array instead of importing this module. */ declare class GraphModule { static ɵfac: _angular_core.ɵɵFactoryDeclaration<GraphModule, never>; static ɵmod: _angular_core.ɵɵNgModuleDeclaration<GraphModule, never, [typeof i1.CommonModule, typeof GraphComponent, typeof MouseWheelDirective, typeof VisibilityObserver], [typeof GraphComponent, typeof MouseWheelDirective]>; static ɵinj: _angular_core.ɵɵInjectorDeclaration<GraphModule>; } /** * @deprecated `GraphComponent` is now standalone. Import it directly into your component's * `imports` array instead of importing `NgxGraphModule`. */ declare class NgxGraphModule { static ɵfac: _angular_core.ɵɵFactoryDeclaration<NgxGraphModule, never>; static ɵmod: _angular_core.ɵɵNgModuleDeclaration<NgxGraphModule, never, [typeof i1.CommonModule, typeof GraphModule], [typeof GraphModule]>; static ɵinj: _angular_core.ɵɵInjectorDeclaration<NgxGraphModule>; } interface ColaForceDirectedSettings { force?: Layout$1 & ID3StyleLayoutAdaptor; forceModifierFn?: (force: Layout$1 & ID3StyleLayoutAdaptor) => Layout$1 & ID3StyleLayoutAdaptor; onTickListener?: (internalGraph: ColaGraph) => void; viewDimensions?: ViewDimensions; } interface ColaGraph { groups: Group[]; nodes: InputNode[]; links: Array<Link<number>>; } declare function toNode(nodes: InputNode[], nodeRef: InputNode | number): InputNode; declare class ColaForceDirectedLayout implements Layout { defaultSettings: ColaForceDirectedSettings; settings: ColaForceDirectedSettings; inputGraph: Graph; outputGraph: Graph; internalGraph: ColaGraph & { groupLinks?: Edge[]; }; outputGraph$: Subject<Graph>; draggingStart: { x: number; y: number; }; run(graph: Graph): Observable<Graph>; updateEdge(graph: Graph, edge: Edge): Observable<Graph>; internalGraphToOutputGraph(internalGraph: any): Graph; onDragStart(draggingNode: Node, $event: MouseEvent): void; onDrag(draggingNode: Node, $event: MouseEvent): void; onDragEnd(draggingNode: Node, $event: MouseEvent): void; } interface D3ForceDirectedSettings {