@onealmonddotcom/ngx-graph 12.0.0-alpha.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.
@@ -0,0 +1,2641 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Output, Directive, Injectable, HostListener, Input, ViewChildren, ContentChild, ChangeDetectionStrategy, ViewEncapsulation, Component, NgModule } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { __decorate } from 'tslib';
6
+ import { trigger, transition, style, animate } from '@angular/animations';
7
+ import { select } from 'd3-selection';
8
+ import * as shape from 'd3-shape';
9
+ import * as ease from 'd3-ease';
10
+ import 'd3-transition';
11
+ import { Subject, Subscription, Observable, of, fromEvent } from 'rxjs';
12
+ import { takeUntil, debounceTime } from 'rxjs/operators';
13
+ import { identity, transform, translate, scale, toSVG, smoothMatrix } from 'transformation-matrix';
14
+ import { scaleOrdinal } from 'd3-scale';
15
+ import * as dagre from 'dagre';
16
+ import * as d3Force from 'd3-force';
17
+ import { forceLink, forceSimulation, forceManyBody, forceCollide } from 'd3-force';
18
+ import { d3adaptor } from 'webcola';
19
+ import * as d3Dispatch from 'd3-dispatch';
20
+ import * as d3Timer from 'd3-timer';
21
+
22
+ const cache = {};
23
+ /**
24
+ * Generates a short id.
25
+ *
26
+ */
27
+ function id() {
28
+ let newId = ('0000' + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4);
29
+ newId = `a${newId}`;
30
+ // ensure not already used
31
+ if (!cache[newId]) {
32
+ cache[newId] = true;
33
+ return newId;
34
+ }
35
+ return id();
36
+ }
37
+
38
+ var PanningAxis;
39
+ (function (PanningAxis) {
40
+ PanningAxis["Both"] = "both";
41
+ PanningAxis["Horizontal"] = "horizontal";
42
+ PanningAxis["Vertical"] = "vertical";
43
+ })(PanningAxis || (PanningAxis = {}));
44
+
45
+ var MiniMapPosition;
46
+ (function (MiniMapPosition) {
47
+ MiniMapPosition["UpperLeft"] = "UpperLeft";
48
+ MiniMapPosition["UpperRight"] = "UpperRight";
49
+ })(MiniMapPosition || (MiniMapPosition = {}));
50
+
51
+ /**
52
+ * Throttle a function
53
+ *
54
+ * @export
55
+ * @param {*} func
56
+ * @param {number} wait
57
+ * @param {*} [options]
58
+ * @returns
59
+ */
60
+ function throttle(context, func, wait, options) {
61
+ options = options || {};
62
+ let args;
63
+ let result;
64
+ let timeout = null;
65
+ let previous = 0;
66
+ function later() {
67
+ previous = options.leading === false ? 0 : +new Date();
68
+ timeout = null;
69
+ result = func.apply(context, args);
70
+ }
71
+ return function (..._arguments) {
72
+ const now = +new Date();
73
+ if (!previous && options.leading === false) {
74
+ previous = now;
75
+ }
76
+ const remaining = wait - (now - previous);
77
+ args = _arguments;
78
+ if (remaining <= 0) {
79
+ clearTimeout(timeout);
80
+ timeout = null;
81
+ previous = now;
82
+ result = func.apply(context, args);
83
+ }
84
+ else if (!timeout && options.trailing !== false) {
85
+ timeout = setTimeout(later, remaining);
86
+ }
87
+ return result;
88
+ };
89
+ }
90
+ /**
91
+ * Throttle decorator
92
+ *
93
+ * class MyClass {
94
+ * throttleable(10)
95
+ * myFn() { ... }
96
+ * }
97
+ *
98
+ * @export
99
+ * @param {number} duration
100
+ * @param {*} [options]
101
+ * @returns
102
+ */
103
+ function throttleable(duration, options) {
104
+ return function innerDecorator(target, key, descriptor) {
105
+ return {
106
+ configurable: true,
107
+ enumerable: descriptor.enumerable,
108
+ get: function getter() {
109
+ Object.defineProperty(this, key, {
110
+ configurable: true,
111
+ enumerable: descriptor.enumerable,
112
+ value: throttle(this, descriptor.value, duration, options)
113
+ });
114
+ return this[key];
115
+ }
116
+ };
117
+ };
118
+ }
119
+
120
+ const colorSets = [
121
+ {
122
+ name: 'vivid',
123
+ selectable: true,
124
+ group: 'Ordinal',
125
+ domain: [
126
+ '#647c8a',
127
+ '#3f51b5',
128
+ '#2196f3',
129
+ '#00b862',
130
+ '#afdf0a',
131
+ '#a7b61a',
132
+ '#f3e562',
133
+ '#ff9800',
134
+ '#ff5722',
135
+ '#ff4514'
136
+ ]
137
+ },
138
+ {
139
+ name: 'natural',
140
+ selectable: true,
141
+ group: 'Ordinal',
142
+ domain: [
143
+ '#bf9d76',
144
+ '#e99450',
145
+ '#d89f59',
146
+ '#f2dfa7',
147
+ '#a5d7c6',
148
+ '#7794b1',
149
+ '#afafaf',
150
+ '#707160',
151
+ '#ba9383',
152
+ '#d9d5c3'
153
+ ]
154
+ },
155
+ {
156
+ name: 'cool',
157
+ selectable: true,
158
+ group: 'Ordinal',
159
+ domain: [
160
+ '#a8385d',
161
+ '#7aa3e5',
162
+ '#a27ea8',
163
+ '#aae3f5',
164
+ '#adcded',
165
+ '#a95963',
166
+ '#8796c0',
167
+ '#7ed3ed',
168
+ '#50abcc',
169
+ '#ad6886'
170
+ ]
171
+ },
172
+ {
173
+ name: 'fire',
174
+ selectable: true,
175
+ group: 'Ordinal',
176
+ domain: ['#ff3d00', '#bf360c', '#ff8f00', '#ff6f00', '#ff5722', '#e65100', '#ffca28', '#ffab00']
177
+ },
178
+ {
179
+ name: 'solar',
180
+ selectable: true,
181
+ group: 'Continuous',
182
+ domain: [
183
+ '#fff8e1',
184
+ '#ffecb3',
185
+ '#ffe082',
186
+ '#ffd54f',
187
+ '#ffca28',
188
+ '#ffc107',
189
+ '#ffb300',
190
+ '#ffa000',
191
+ '#ff8f00',
192
+ '#ff6f00'
193
+ ]
194
+ },
195
+ {
196
+ name: 'air',
197
+ selectable: true,
198
+ group: 'Continuous',
199
+ domain: [
200
+ '#e1f5fe',
201
+ '#b3e5fc',
202
+ '#81d4fa',
203
+ '#4fc3f7',
204
+ '#29b6f6',
205
+ '#03a9f4',
206
+ '#039be5',
207
+ '#0288d1',
208
+ '#0277bd',
209
+ '#01579b'
210
+ ]
211
+ },
212
+ {
213
+ name: 'aqua',
214
+ selectable: true,
215
+ group: 'Continuous',
216
+ domain: [
217
+ '#e0f7fa',
218
+ '#b2ebf2',
219
+ '#80deea',
220
+ '#4dd0e1',
221
+ '#26c6da',
222
+ '#00bcd4',
223
+ '#00acc1',
224
+ '#0097a7',
225
+ '#00838f',
226
+ '#006064'
227
+ ]
228
+ },
229
+ {
230
+ name: 'flame',
231
+ selectable: false,
232
+ group: 'Ordinal',
233
+ domain: [
234
+ '#A10A28',
235
+ '#D3342D',
236
+ '#EF6D49',
237
+ '#FAAD67',
238
+ '#FDDE90',
239
+ '#DBED91',
240
+ '#A9D770',
241
+ '#6CBA67',
242
+ '#2C9653',
243
+ '#146738'
244
+ ]
245
+ },
246
+ {
247
+ name: 'ocean',
248
+ selectable: false,
249
+ group: 'Ordinal',
250
+ domain: [
251
+ '#1D68FB',
252
+ '#33C0FC',
253
+ '#4AFFFE',
254
+ '#AFFFFF',
255
+ '#FFFC63',
256
+ '#FDBD2D',
257
+ '#FC8A25',
258
+ '#FA4F1E',
259
+ '#FA141B',
260
+ '#BA38D1'
261
+ ]
262
+ },
263
+ {
264
+ name: 'forest',
265
+ selectable: false,
266
+ group: 'Ordinal',
267
+ domain: [
268
+ '#55C22D',
269
+ '#C1F33D',
270
+ '#3CC099',
271
+ '#AFFFFF',
272
+ '#8CFC9D',
273
+ '#76CFFA',
274
+ '#BA60FB',
275
+ '#EE6490',
276
+ '#C42A1C',
277
+ '#FC9F32'
278
+ ]
279
+ },
280
+ {
281
+ name: 'horizon',
282
+ selectable: false,
283
+ group: 'Ordinal',
284
+ domain: [
285
+ '#2597FB',
286
+ '#65EBFD',
287
+ '#99FDD0',
288
+ '#FCEE4B',
289
+ '#FEFCFA',
290
+ '#FDD6E3',
291
+ '#FCB1A8',
292
+ '#EF6F7B',
293
+ '#CB96E8',
294
+ '#EFDEE0'
295
+ ]
296
+ },
297
+ {
298
+ name: 'neons',
299
+ selectable: false,
300
+ group: 'Ordinal',
301
+ domain: [
302
+ '#FF3333',
303
+ '#FF33FF',
304
+ '#CC33FF',
305
+ '#0000FF',
306
+ '#33CCFF',
307
+ '#33FFFF',
308
+ '#33FF66',
309
+ '#CCFF33',
310
+ '#FFCC00',
311
+ '#FF6600'
312
+ ]
313
+ },
314
+ {
315
+ name: 'picnic',
316
+ selectable: false,
317
+ group: 'Ordinal',
318
+ domain: [
319
+ '#FAC51D',
320
+ '#66BD6D',
321
+ '#FAA026',
322
+ '#29BB9C',
323
+ '#E96B56',
324
+ '#55ACD2',
325
+ '#B7332F',
326
+ '#2C83C9',
327
+ '#9166B8',
328
+ '#92E7E8'
329
+ ]
330
+ },
331
+ {
332
+ name: 'night',
333
+ selectable: false,
334
+ group: 'Ordinal',
335
+ domain: [
336
+ '#2B1B5A',
337
+ '#501356',
338
+ '#183356',
339
+ '#28203F',
340
+ '#391B3C',
341
+ '#1E2B3C',
342
+ '#120634',
343
+ '#2D0432',
344
+ '#051932',
345
+ '#453080',
346
+ '#75267D',
347
+ '#2C507D',
348
+ '#4B3880',
349
+ '#752F7D',
350
+ '#35547D'
351
+ ]
352
+ },
353
+ {
354
+ name: 'nightLights',
355
+ selectable: false,
356
+ group: 'Ordinal',
357
+ domain: [
358
+ '#4e31a5',
359
+ '#9c25a7',
360
+ '#3065ab',
361
+ '#57468b',
362
+ '#904497',
363
+ '#46648b',
364
+ '#32118d',
365
+ '#a00fb3',
366
+ '#1052a2',
367
+ '#6e51bd',
368
+ '#b63cc3',
369
+ '#6c97cb',
370
+ '#8671c1',
371
+ '#b455be',
372
+ '#7496c3'
373
+ ]
374
+ }
375
+ ];
376
+
377
+ class ColorHelper {
378
+ scale;
379
+ colorDomain;
380
+ domain;
381
+ customColors;
382
+ constructor(scheme, domain, customColors) {
383
+ if (typeof scheme === 'string') {
384
+ scheme = colorSets.find(cs => {
385
+ return cs.name === scheme;
386
+ });
387
+ }
388
+ this.colorDomain = scheme.domain;
389
+ this.domain = domain;
390
+ this.customColors = customColors;
391
+ this.scale = this.generateColorScheme(scheme, this.domain);
392
+ }
393
+ generateColorScheme(scheme, domain) {
394
+ if (typeof scheme === 'string') {
395
+ scheme = colorSets.find(cs => {
396
+ return cs.name === scheme;
397
+ });
398
+ }
399
+ return scaleOrdinal().range(scheme.domain).domain(domain);
400
+ }
401
+ getColor(value) {
402
+ if (value === undefined || value === null) {
403
+ throw new Error('Value can not be null');
404
+ }
405
+ if (typeof this.customColors === 'function') {
406
+ return this.customColors(value);
407
+ }
408
+ const formattedValue = value.toString();
409
+ let found; // todo type customColors
410
+ if (this.customColors && this.customColors.length > 0) {
411
+ found = this.customColors.find(mapping => {
412
+ return mapping.name.toLowerCase() === formattedValue.toLowerCase();
413
+ });
414
+ }
415
+ if (found) {
416
+ return found.value;
417
+ }
418
+ else {
419
+ return this.scale(value);
420
+ }
421
+ }
422
+ }
423
+
424
+ function calculateViewDimensions({ width, height }) {
425
+ let chartWidth = width;
426
+ let chartHeight = height;
427
+ chartWidth = Math.max(0, chartWidth);
428
+ chartHeight = Math.max(0, chartHeight);
429
+ return {
430
+ width: Math.floor(chartWidth),
431
+ height: Math.floor(chartHeight)
432
+ };
433
+ }
434
+
435
+ /**
436
+ * Visibility Observer
437
+ */
438
+ class VisibilityObserver {
439
+ element;
440
+ zone;
441
+ visible = new EventEmitter();
442
+ timeout;
443
+ isVisible = false;
444
+ constructor(element, zone) {
445
+ this.element = element;
446
+ this.zone = zone;
447
+ this.runCheck();
448
+ }
449
+ destroy() {
450
+ clearTimeout(this.timeout);
451
+ }
452
+ onVisibilityChange() {
453
+ // trigger zone recalc for columns
454
+ this.zone.run(() => {
455
+ this.isVisible = true;
456
+ this.visible.emit(true);
457
+ });
458
+ }
459
+ runCheck() {
460
+ const check = () => {
461
+ if (!this.element) {
462
+ return;
463
+ }
464
+ // https://davidwalsh.name/offsetheight-visibility
465
+ const { offsetHeight, offsetWidth } = this.element.nativeElement;
466
+ if (offsetHeight && offsetWidth) {
467
+ clearTimeout(this.timeout);
468
+ this.onVisibilityChange();
469
+ }
470
+ else {
471
+ clearTimeout(this.timeout);
472
+ this.zone.runOutsideAngular(() => {
473
+ this.timeout = setTimeout(() => check(), 100);
474
+ });
475
+ }
476
+ };
477
+ this.zone.runOutsideAngular(() => {
478
+ this.timeout = setTimeout(() => check());
479
+ });
480
+ }
481
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: VisibilityObserver, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
482
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: VisibilityObserver, isStandalone: false, selector: "visibility-observer", outputs: { visible: "visible" }, ngImport: i0 });
483
+ }
484
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: VisibilityObserver, decorators: [{
485
+ type: Directive,
486
+ args: [{
487
+ // tslint:disable-next-line:directive-selector
488
+ selector: 'visibility-observer',
489
+ standalone: false
490
+ }]
491
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { visible: [{
492
+ type: Output
493
+ }] } });
494
+
495
+ var Orientation;
496
+ (function (Orientation) {
497
+ Orientation["LEFT_TO_RIGHT"] = "LR";
498
+ Orientation["RIGHT_TO_LEFT"] = "RL";
499
+ Orientation["TOP_TO_BOTTOM"] = "TB";
500
+ Orientation["BOTTOM_TO_TOM"] = "BT";
501
+ })(Orientation || (Orientation = {}));
502
+ var Alignment;
503
+ (function (Alignment) {
504
+ Alignment["CENTER"] = "C";
505
+ Alignment["UP_LEFT"] = "UL";
506
+ Alignment["UP_RIGHT"] = "UR";
507
+ Alignment["DOWN_LEFT"] = "DL";
508
+ Alignment["DOWN_RIGHT"] = "DR";
509
+ })(Alignment || (Alignment = {}));
510
+ class DagreLayout {
511
+ defaultSettings = {
512
+ orientation: Orientation.LEFT_TO_RIGHT,
513
+ marginX: 20,
514
+ marginY: 20,
515
+ edgePadding: 100,
516
+ rankPadding: 100,
517
+ nodePadding: 50,
518
+ multigraph: true,
519
+ compound: true
520
+ };
521
+ settings = {};
522
+ dagreGraph;
523
+ dagreNodes;
524
+ dagreEdges;
525
+ run(graph) {
526
+ this.createDagreGraph(graph);
527
+ dagre.layout(this.dagreGraph);
528
+ graph.edgeLabels = this.dagreGraph._edgeLabels;
529
+ for (const dagreNodeId in this.dagreGraph._nodes) {
530
+ const dagreNode = this.dagreGraph._nodes[dagreNodeId];
531
+ const node = graph.nodes.find(n => n.id === dagreNode.id);
532
+ node.position = {
533
+ x: dagreNode.x,
534
+ y: dagreNode.y
535
+ };
536
+ node.dimension = {
537
+ width: dagreNode.width,
538
+ height: dagreNode.height
539
+ };
540
+ }
541
+ return graph;
542
+ }
543
+ updateEdge(graph, edge) {
544
+ const sourceNode = graph.nodes.find(n => n.id === edge.source);
545
+ const targetNode = graph.nodes.find(n => n.id === edge.target);
546
+ // determine new arrow position
547
+ const dir = sourceNode.position.y <= targetNode.position.y ? -1 : 1;
548
+ const startingPoint = {
549
+ x: sourceNode.position.x,
550
+ y: sourceNode.position.y - dir * (sourceNode.dimension.height / 2)
551
+ };
552
+ const endingPoint = {
553
+ x: targetNode.position.x,
554
+ y: targetNode.position.y + dir * (targetNode.dimension.height / 2)
555
+ };
556
+ // generate new points
557
+ edge.points = [startingPoint, endingPoint];
558
+ return graph;
559
+ }
560
+ createDagreGraph(graph) {
561
+ const settings = Object.assign({}, this.defaultSettings, this.settings);
562
+ this.dagreGraph = new dagre.graphlib.Graph({ compound: settings.compound, multigraph: settings.multigraph });
563
+ this.dagreGraph.setGraph({
564
+ rankdir: settings.orientation,
565
+ marginx: settings.marginX,
566
+ marginy: settings.marginY,
567
+ edgesep: settings.edgePadding,
568
+ ranksep: settings.rankPadding,
569
+ nodesep: settings.nodePadding,
570
+ align: settings.align,
571
+ acyclicer: settings.acyclicer,
572
+ ranker: settings.ranker,
573
+ multigraph: settings.multigraph,
574
+ compound: settings.compound
575
+ });
576
+ // Default to assigning a new object as a label for each new edge.
577
+ this.dagreGraph.setDefaultEdgeLabel(() => {
578
+ return {
579
+ /* empty */
580
+ };
581
+ });
582
+ this.dagreNodes = graph.nodes.map(n => {
583
+ const node = Object.assign({}, n);
584
+ node.width = n.dimension.width;
585
+ node.height = n.dimension.height;
586
+ node.x = n.position.x;
587
+ node.y = n.position.y;
588
+ return node;
589
+ });
590
+ this.dagreEdges = graph.edges.map(l => {
591
+ const newLink = Object.assign({}, l);
592
+ if (!newLink.id) {
593
+ newLink.id = id();
594
+ }
595
+ return newLink;
596
+ });
597
+ for (const node of this.dagreNodes) {
598
+ if (!node.width) {
599
+ node.width = 20;
600
+ }
601
+ if (!node.height) {
602
+ node.height = 30;
603
+ }
604
+ // update dagre
605
+ this.dagreGraph.setNode(node.id, node);
606
+ }
607
+ // update dagre
608
+ for (const edge of this.dagreEdges) {
609
+ if (settings.multigraph) {
610
+ this.dagreGraph.setEdge(edge.source, edge.target, edge, edge.id);
611
+ }
612
+ else {
613
+ this.dagreGraph.setEdge(edge.source, edge.target);
614
+ }
615
+ }
616
+ return this.dagreGraph;
617
+ }
618
+ }
619
+
620
+ class DagreClusterLayout {
621
+ defaultSettings = {
622
+ orientation: Orientation.LEFT_TO_RIGHT,
623
+ marginX: 20,
624
+ marginY: 20,
625
+ edgePadding: 100,
626
+ rankPadding: 100,
627
+ nodePadding: 50,
628
+ multigraph: true,
629
+ compound: true
630
+ };
631
+ settings = {};
632
+ dagreGraph;
633
+ dagreNodes;
634
+ dagreClusters;
635
+ dagreEdges;
636
+ run(graph) {
637
+ this.createDagreGraph(graph);
638
+ dagre.layout(this.dagreGraph);
639
+ graph.edgeLabels = this.dagreGraph._edgeLabels;
640
+ const dagreToOutput = node => {
641
+ const dagreNode = this.dagreGraph._nodes[node.id];
642
+ return {
643
+ ...node,
644
+ position: {
645
+ x: dagreNode.x,
646
+ y: dagreNode.y
647
+ },
648
+ dimension: {
649
+ width: dagreNode.width,
650
+ height: dagreNode.height
651
+ }
652
+ };
653
+ };
654
+ graph.clusters = (graph.clusters || []).map(dagreToOutput);
655
+ graph.nodes = graph.nodes.map(dagreToOutput);
656
+ return graph;
657
+ }
658
+ updateEdge(graph, edge) {
659
+ const sourceNode = graph.nodes.find(n => n.id === edge.source);
660
+ const targetNode = graph.nodes.find(n => n.id === edge.target);
661
+ // determine new arrow position
662
+ const dir = sourceNode.position.y <= targetNode.position.y ? -1 : 1;
663
+ const startingPoint = {
664
+ x: sourceNode.position.x,
665
+ y: sourceNode.position.y - dir * (sourceNode.dimension.height / 2)
666
+ };
667
+ const endingPoint = {
668
+ x: targetNode.position.x,
669
+ y: targetNode.position.y + dir * (targetNode.dimension.height / 2)
670
+ };
671
+ // generate new points
672
+ edge.points = [startingPoint, endingPoint];
673
+ return graph;
674
+ }
675
+ createDagreGraph(graph) {
676
+ const settings = Object.assign({}, this.defaultSettings, this.settings);
677
+ this.dagreGraph = new dagre.graphlib.Graph({ compound: settings.compound, multigraph: settings.multigraph });
678
+ this.dagreGraph.setGraph({
679
+ rankdir: settings.orientation,
680
+ marginx: settings.marginX,
681
+ marginy: settings.marginY,
682
+ edgesep: settings.edgePadding,
683
+ ranksep: settings.rankPadding,
684
+ nodesep: settings.nodePadding,
685
+ align: settings.align,
686
+ acyclicer: settings.acyclicer,
687
+ ranker: settings.ranker,
688
+ multigraph: settings.multigraph,
689
+ compound: settings.compound
690
+ });
691
+ // Default to assigning a new object as a label for each new edge.
692
+ this.dagreGraph.setDefaultEdgeLabel(() => {
693
+ return {
694
+ /* empty */
695
+ };
696
+ });
697
+ this.dagreNodes = graph.nodes.map((n) => {
698
+ const node = Object.assign({}, n);
699
+ node.width = n.dimension.width;
700
+ node.height = n.dimension.height;
701
+ node.x = n.position.x;
702
+ node.y = n.position.y;
703
+ return node;
704
+ });
705
+ this.dagreClusters = graph.clusters || [];
706
+ this.dagreEdges = graph.edges.map(l => {
707
+ const newLink = Object.assign({}, l);
708
+ if (!newLink.id) {
709
+ newLink.id = id();
710
+ }
711
+ return newLink;
712
+ });
713
+ for (const node of this.dagreNodes) {
714
+ this.dagreGraph.setNode(node.id, node);
715
+ }
716
+ for (const cluster of this.dagreClusters) {
717
+ this.dagreGraph.setNode(cluster.id, cluster);
718
+ cluster.childNodeIds.forEach(childNodeId => {
719
+ this.dagreGraph.setParent(childNodeId, cluster.id);
720
+ });
721
+ }
722
+ // update dagre
723
+ for (const edge of this.dagreEdges) {
724
+ if (settings.multigraph) {
725
+ this.dagreGraph.setEdge(edge.source, edge.target, edge, edge.id);
726
+ }
727
+ else {
728
+ this.dagreGraph.setEdge(edge.source, edge.target);
729
+ }
730
+ }
731
+ return this.dagreGraph;
732
+ }
733
+ }
734
+
735
+ const DEFAULT_EDGE_NAME = '\x00';
736
+ const GRAPH_NODE = '\x00';
737
+ const EDGE_KEY_DELIM = '\x01';
738
+ class DagreNodesOnlyLayout {
739
+ defaultSettings = {
740
+ orientation: Orientation.LEFT_TO_RIGHT,
741
+ marginX: 20,
742
+ marginY: 20,
743
+ edgePadding: 100,
744
+ rankPadding: 100,
745
+ nodePadding: 50,
746
+ curveDistance: 20,
747
+ multigraph: true,
748
+ compound: true
749
+ };
750
+ settings = {};
751
+ dagreGraph;
752
+ dagreNodes;
753
+ dagreEdges;
754
+ run(graph) {
755
+ this.createDagreGraph(graph);
756
+ dagre.layout(this.dagreGraph);
757
+ graph.edgeLabels = this.dagreGraph._edgeLabels;
758
+ for (const dagreNodeId in this.dagreGraph._nodes) {
759
+ const dagreNode = this.dagreGraph._nodes[dagreNodeId];
760
+ const node = graph.nodes.find(n => n.id === dagreNode.id);
761
+ node.position = {
762
+ x: dagreNode.x,
763
+ y: dagreNode.y
764
+ };
765
+ node.dimension = {
766
+ width: dagreNode.width,
767
+ height: dagreNode.height
768
+ };
769
+ }
770
+ for (const edge of graph.edges) {
771
+ this.updateEdge(graph, edge);
772
+ }
773
+ return graph;
774
+ }
775
+ updateEdge(graph, edge) {
776
+ const sourceNode = graph.nodes.find(n => n.id === edge.source);
777
+ const targetNode = graph.nodes.find(n => n.id === edge.target);
778
+ const rankAxis = this.settings.orientation === 'BT' || this.settings.orientation === 'TB' ? 'y' : 'x';
779
+ const orderAxis = rankAxis === 'y' ? 'x' : 'y';
780
+ const rankDimension = rankAxis === 'y' ? 'height' : 'width';
781
+ // determine new arrow position
782
+ const dir = sourceNode.position[rankAxis] <= targetNode.position[rankAxis] ? -1 : 1;
783
+ const startingPoint = {
784
+ [orderAxis]: sourceNode.position[orderAxis],
785
+ [rankAxis]: sourceNode.position[rankAxis] - dir * (sourceNode.dimension[rankDimension] / 2)
786
+ };
787
+ const endingPoint = {
788
+ [orderAxis]: targetNode.position[orderAxis],
789
+ [rankAxis]: targetNode.position[rankAxis] + dir * (targetNode.dimension[rankDimension] / 2)
790
+ };
791
+ const curveDistance = this.settings.curveDistance || this.defaultSettings.curveDistance;
792
+ // generate new points
793
+ edge.points = [
794
+ startingPoint,
795
+ {
796
+ [orderAxis]: startingPoint[orderAxis],
797
+ [rankAxis]: startingPoint[rankAxis] - dir * curveDistance
798
+ },
799
+ {
800
+ [orderAxis]: endingPoint[orderAxis],
801
+ [rankAxis]: endingPoint[rankAxis] + dir * curveDistance
802
+ },
803
+ endingPoint
804
+ ];
805
+ const edgeLabelId = `${edge.source}${EDGE_KEY_DELIM}${edge.target}${EDGE_KEY_DELIM}${DEFAULT_EDGE_NAME}`;
806
+ const matchingEdgeLabel = graph.edgeLabels[edgeLabelId];
807
+ if (matchingEdgeLabel) {
808
+ matchingEdgeLabel.points = edge.points;
809
+ }
810
+ return graph;
811
+ }
812
+ createDagreGraph(graph) {
813
+ const settings = Object.assign({}, this.defaultSettings, this.settings);
814
+ this.dagreGraph = new dagre.graphlib.Graph({ compound: settings.compound, multigraph: settings.multigraph });
815
+ this.dagreGraph.setGraph({
816
+ rankdir: settings.orientation,
817
+ marginx: settings.marginX,
818
+ marginy: settings.marginY,
819
+ edgesep: settings.edgePadding,
820
+ ranksep: settings.rankPadding,
821
+ nodesep: settings.nodePadding,
822
+ align: settings.align,
823
+ acyclicer: settings.acyclicer,
824
+ ranker: settings.ranker,
825
+ multigraph: settings.multigraph,
826
+ compound: settings.compound
827
+ });
828
+ // Default to assigning a new object as a label for each new edge.
829
+ this.dagreGraph.setDefaultEdgeLabel(() => {
830
+ return {
831
+ /* empty */
832
+ };
833
+ });
834
+ this.dagreNodes = graph.nodes.map(n => {
835
+ const node = Object.assign({}, n);
836
+ node.width = n.dimension.width;
837
+ node.height = n.dimension.height;
838
+ node.x = n.position.x;
839
+ node.y = n.position.y;
840
+ return node;
841
+ });
842
+ this.dagreEdges = graph.edges.map(l => {
843
+ const newLink = Object.assign({}, l);
844
+ if (!newLink.id) {
845
+ newLink.id = id();
846
+ }
847
+ return newLink;
848
+ });
849
+ for (const node of this.dagreNodes) {
850
+ if (!node.width) {
851
+ node.width = 20;
852
+ }
853
+ if (!node.height) {
854
+ node.height = 30;
855
+ }
856
+ // update dagre
857
+ this.dagreGraph.setNode(node.id, node);
858
+ }
859
+ // update dagre
860
+ for (const edge of this.dagreEdges) {
861
+ if (settings.multigraph) {
862
+ this.dagreGraph.setEdge(edge.source, edge.target, edge, edge.id);
863
+ }
864
+ else {
865
+ this.dagreGraph.setEdge(edge.source, edge.target);
866
+ }
867
+ }
868
+ return this.dagreGraph;
869
+ }
870
+ }
871
+
872
+ function toD3Node(maybeNode) {
873
+ if (typeof maybeNode === 'string') {
874
+ return {
875
+ id: maybeNode,
876
+ x: 0,
877
+ y: 0
878
+ };
879
+ }
880
+ return maybeNode;
881
+ }
882
+ class D3ForceDirectedLayout {
883
+ defaultSettings = {
884
+ force: forceSimulation().force('charge', forceManyBody().strength(-150)).force('collide', forceCollide(5)),
885
+ forceLink: forceLink()
886
+ .id(node => node.id)
887
+ .distance(() => 100)
888
+ };
889
+ settings = {};
890
+ inputGraph;
891
+ outputGraph;
892
+ d3Graph;
893
+ outputGraph$ = new Subject();
894
+ draggingStart;
895
+ run(graph) {
896
+ this.inputGraph = graph;
897
+ this.d3Graph = {
898
+ nodes: [...this.inputGraph.nodes.map(n => ({ ...n }))],
899
+ edges: [...this.inputGraph.edges.map(e => ({ ...e }))]
900
+ };
901
+ this.outputGraph = {
902
+ nodes: [],
903
+ edges: [],
904
+ edgeLabels: []
905
+ };
906
+ this.outputGraph$.next(this.outputGraph);
907
+ this.settings = Object.assign({}, this.defaultSettings, this.settings);
908
+ if (this.settings.force) {
909
+ this.settings.force
910
+ .nodes(this.d3Graph.nodes)
911
+ .force('link', this.settings.forceLink.links(this.d3Graph.edges))
912
+ .alpha(0.5)
913
+ .restart()
914
+ .on('tick', () => {
915
+ this.outputGraph$.next(this.d3GraphToOutputGraph(this.d3Graph));
916
+ });
917
+ }
918
+ return this.outputGraph$.asObservable();
919
+ }
920
+ updateEdge(graph, edge) {
921
+ const settings = Object.assign({}, this.defaultSettings, this.settings);
922
+ if (settings.force) {
923
+ settings.force
924
+ .nodes(this.d3Graph.nodes)
925
+ .force('link', settings.forceLink.links(this.d3Graph.edges))
926
+ .alpha(0.5)
927
+ .restart()
928
+ .on('tick', () => {
929
+ this.outputGraph$.next(this.d3GraphToOutputGraph(this.d3Graph));
930
+ });
931
+ }
932
+ return this.outputGraph$.asObservable();
933
+ }
934
+ d3GraphToOutputGraph(d3Graph) {
935
+ this.outputGraph.nodes = this.d3Graph.nodes.map((node) => ({
936
+ ...node,
937
+ id: node.id || id(),
938
+ position: {
939
+ x: node.x,
940
+ y: node.y
941
+ },
942
+ dimension: {
943
+ width: (node.dimension && node.dimension.width) || 20,
944
+ height: (node.dimension && node.dimension.height) || 20
945
+ },
946
+ transform: `translate(${node.x - ((node.dimension && node.dimension.width) || 20) / 2 || 0}, ${node.y - ((node.dimension && node.dimension.height) || 20) / 2 || 0})`
947
+ }));
948
+ this.outputGraph.edges = this.d3Graph.edges.map(edge => ({
949
+ ...edge,
950
+ source: toD3Node(edge.source).id,
951
+ target: toD3Node(edge.target).id,
952
+ points: [
953
+ {
954
+ x: toD3Node(edge.source).x,
955
+ y: toD3Node(edge.source).y
956
+ },
957
+ {
958
+ x: toD3Node(edge.target).x,
959
+ y: toD3Node(edge.target).y
960
+ }
961
+ ]
962
+ }));
963
+ this.outputGraph.edgeLabels = this.outputGraph.edges;
964
+ return this.outputGraph;
965
+ }
966
+ onDragStart(draggingNode, $event) {
967
+ this.settings.force.alphaTarget(0.3).restart();
968
+ const node = this.d3Graph.nodes.find(d3Node => d3Node.id === draggingNode.id);
969
+ if (!node) {
970
+ return;
971
+ }
972
+ this.draggingStart = { x: $event.x - node.x, y: $event.y - node.y };
973
+ node.fx = $event.x - this.draggingStart.x;
974
+ node.fy = $event.y - this.draggingStart.y;
975
+ }
976
+ onDrag(draggingNode, $event) {
977
+ if (!draggingNode) {
978
+ return;
979
+ }
980
+ const node = this.d3Graph.nodes.find(d3Node => d3Node.id === draggingNode.id);
981
+ if (!node) {
982
+ return;
983
+ }
984
+ node.fx = $event.x - this.draggingStart.x;
985
+ node.fy = $event.y - this.draggingStart.y;
986
+ }
987
+ onDragEnd(draggingNode, $event) {
988
+ if (!draggingNode) {
989
+ return;
990
+ }
991
+ const node = this.d3Graph.nodes.find(d3Node => d3Node.id === draggingNode.id);
992
+ if (!node) {
993
+ return;
994
+ }
995
+ this.settings.force.alphaTarget(0);
996
+ node.fx = undefined;
997
+ node.fy = undefined;
998
+ }
999
+ }
1000
+
1001
+ function toNode(nodes, nodeRef) {
1002
+ if (typeof nodeRef === 'number') {
1003
+ return nodes[nodeRef];
1004
+ }
1005
+ return nodeRef;
1006
+ }
1007
+ class ColaForceDirectedLayout {
1008
+ defaultSettings = {
1009
+ force: d3adaptor({
1010
+ ...d3Dispatch,
1011
+ ...d3Force,
1012
+ ...d3Timer
1013
+ })
1014
+ .linkDistance(150)
1015
+ .avoidOverlaps(true),
1016
+ viewDimensions: {
1017
+ width: 600,
1018
+ height: 600
1019
+ }
1020
+ };
1021
+ settings = {};
1022
+ inputGraph;
1023
+ outputGraph;
1024
+ internalGraph;
1025
+ outputGraph$ = new Subject();
1026
+ draggingStart;
1027
+ run(graph) {
1028
+ this.inputGraph = graph;
1029
+ if (!this.inputGraph.clusters) {
1030
+ this.inputGraph.clusters = [];
1031
+ }
1032
+ this.internalGraph = {
1033
+ nodes: [
1034
+ ...this.inputGraph.nodes.map(n => ({
1035
+ ...n,
1036
+ width: n.dimension ? n.dimension.width : 20,
1037
+ height: n.dimension ? n.dimension.height : 20
1038
+ }))
1039
+ ],
1040
+ groups: [
1041
+ ...this.inputGraph.clusters.map((cluster) => ({
1042
+ padding: 5,
1043
+ groups: cluster.childNodeIds
1044
+ .map(nodeId => this.inputGraph.clusters.findIndex(node => node.id === nodeId))
1045
+ .filter(x => x >= 0),
1046
+ leaves: cluster.childNodeIds
1047
+ .map(nodeId => this.inputGraph.nodes.findIndex(node => node.id === nodeId))
1048
+ .filter(x => x >= 0)
1049
+ }))
1050
+ ],
1051
+ links: [
1052
+ ...this.inputGraph.edges
1053
+ .map(e => {
1054
+ const sourceNodeIndex = this.inputGraph.nodes.findIndex(node => e.source === node.id);
1055
+ const targetNodeIndex = this.inputGraph.nodes.findIndex(node => e.target === node.id);
1056
+ if (sourceNodeIndex === -1 || targetNodeIndex === -1) {
1057
+ return undefined;
1058
+ }
1059
+ return {
1060
+ ...e,
1061
+ source: sourceNodeIndex,
1062
+ target: targetNodeIndex
1063
+ };
1064
+ })
1065
+ .filter(x => !!x)
1066
+ ],
1067
+ groupLinks: [
1068
+ ...this.inputGraph.edges
1069
+ .map(e => {
1070
+ const sourceNodeIndex = this.inputGraph.nodes.findIndex(node => e.source === node.id);
1071
+ const targetNodeIndex = this.inputGraph.nodes.findIndex(node => e.target === node.id);
1072
+ if (sourceNodeIndex >= 0 && targetNodeIndex >= 0) {
1073
+ return undefined;
1074
+ }
1075
+ return e;
1076
+ })
1077
+ .filter(x => !!x)
1078
+ ]
1079
+ };
1080
+ this.outputGraph = {
1081
+ nodes: [],
1082
+ clusters: [],
1083
+ edges: [],
1084
+ edgeLabels: []
1085
+ };
1086
+ this.outputGraph$.next(this.outputGraph);
1087
+ this.settings = Object.assign({}, this.defaultSettings, this.settings);
1088
+ if (this.settings.force) {
1089
+ this.settings.force = this.settings.force
1090
+ .nodes(this.internalGraph.nodes)
1091
+ .groups(this.internalGraph.groups)
1092
+ .links(this.internalGraph.links)
1093
+ .alpha(0.5)
1094
+ .on('tick', () => {
1095
+ if (this.settings.onTickListener) {
1096
+ this.settings.onTickListener(this.internalGraph);
1097
+ }
1098
+ this.outputGraph$.next(this.internalGraphToOutputGraph(this.internalGraph));
1099
+ });
1100
+ if (this.settings.viewDimensions) {
1101
+ this.settings.force = this.settings.force.size([
1102
+ this.settings.viewDimensions.width,
1103
+ this.settings.viewDimensions.height
1104
+ ]);
1105
+ }
1106
+ if (this.settings.forceModifierFn) {
1107
+ this.settings.force = this.settings.forceModifierFn(this.settings.force);
1108
+ }
1109
+ this.settings.force.start();
1110
+ }
1111
+ return this.outputGraph$.asObservable();
1112
+ }
1113
+ updateEdge(graph, edge) {
1114
+ const settings = Object.assign({}, this.defaultSettings, this.settings);
1115
+ if (settings.force) {
1116
+ settings.force.start();
1117
+ }
1118
+ return this.outputGraph$.asObservable();
1119
+ }
1120
+ internalGraphToOutputGraph(internalGraph) {
1121
+ this.outputGraph.nodes = internalGraph.nodes.map(node => ({
1122
+ ...node,
1123
+ id: node.id || id(),
1124
+ position: {
1125
+ x: node.x,
1126
+ y: node.y
1127
+ },
1128
+ dimension: {
1129
+ width: (node.dimension && node.dimension.width) || 20,
1130
+ height: (node.dimension && node.dimension.height) || 20
1131
+ },
1132
+ transform: `translate(${node.x - ((node.dimension && node.dimension.width) || 20) / 2 || 0}, ${node.y - ((node.dimension && node.dimension.height) || 20) / 2 || 0})`
1133
+ }));
1134
+ this.outputGraph.edges = internalGraph.links
1135
+ .map(edge => {
1136
+ const source = toNode(internalGraph.nodes, edge.source);
1137
+ const target = toNode(internalGraph.nodes, edge.target);
1138
+ return {
1139
+ ...edge,
1140
+ source: source.id,
1141
+ target: target.id,
1142
+ points: [
1143
+ source.bounds.rayIntersection(target.bounds.cx(), target.bounds.cy()),
1144
+ target.bounds.rayIntersection(source.bounds.cx(), source.bounds.cy())
1145
+ ]
1146
+ };
1147
+ })
1148
+ .concat(internalGraph.groupLinks.map(groupLink => {
1149
+ const sourceNode = internalGraph.nodes.find(foundNode => foundNode.id === groupLink.source);
1150
+ const targetNode = internalGraph.nodes.find(foundNode => foundNode.id === groupLink.target);
1151
+ const source = sourceNode || internalGraph.groups.find(foundGroup => foundGroup.id === groupLink.source);
1152
+ const target = targetNode || internalGraph.groups.find(foundGroup => foundGroup.id === groupLink.target);
1153
+ return {
1154
+ ...groupLink,
1155
+ source: source.id,
1156
+ target: target.id,
1157
+ points: [
1158
+ source.bounds.rayIntersection(target.bounds.cx(), target.bounds.cy()),
1159
+ target.bounds.rayIntersection(source.bounds.cx(), source.bounds.cy())
1160
+ ]
1161
+ };
1162
+ }));
1163
+ this.outputGraph.clusters = internalGraph.groups.map((group, index) => {
1164
+ const inputGroup = this.inputGraph.clusters[index];
1165
+ return {
1166
+ ...inputGroup,
1167
+ dimension: {
1168
+ width: group.bounds ? group.bounds.width() : 20,
1169
+ height: group.bounds ? group.bounds.height() : 20
1170
+ },
1171
+ position: {
1172
+ x: group.bounds ? group.bounds.x + group.bounds.width() / 2 : 0,
1173
+ y: group.bounds ? group.bounds.y + group.bounds.height() / 2 : 0
1174
+ }
1175
+ };
1176
+ });
1177
+ this.outputGraph.edgeLabels = this.outputGraph.edges;
1178
+ return this.outputGraph;
1179
+ }
1180
+ onDragStart(draggingNode, $event) {
1181
+ const nodeIndex = this.outputGraph.nodes.findIndex(foundNode => foundNode.id === draggingNode.id);
1182
+ const node = this.internalGraph.nodes[nodeIndex];
1183
+ if (!node) {
1184
+ return;
1185
+ }
1186
+ this.draggingStart = { x: node.x - $event.x, y: node.y - $event.y };
1187
+ node.fixed = 1;
1188
+ this.settings.force.start();
1189
+ }
1190
+ onDrag(draggingNode, $event) {
1191
+ if (!draggingNode) {
1192
+ return;
1193
+ }
1194
+ const nodeIndex = this.outputGraph.nodes.findIndex(foundNode => foundNode.id === draggingNode.id);
1195
+ const node = this.internalGraph.nodes[nodeIndex];
1196
+ if (!node) {
1197
+ return;
1198
+ }
1199
+ node.x = this.draggingStart.x + $event.x;
1200
+ node.y = this.draggingStart.y + $event.y;
1201
+ }
1202
+ onDragEnd(draggingNode, $event) {
1203
+ if (!draggingNode) {
1204
+ return;
1205
+ }
1206
+ const nodeIndex = this.outputGraph.nodes.findIndex(foundNode => foundNode.id === draggingNode.id);
1207
+ const node = this.internalGraph.nodes[nodeIndex];
1208
+ if (!node) {
1209
+ return;
1210
+ }
1211
+ node.fixed = 0;
1212
+ }
1213
+ }
1214
+
1215
+ const layouts = {
1216
+ dagre: DagreLayout,
1217
+ dagreCluster: DagreClusterLayout,
1218
+ dagreNodesOnly: DagreNodesOnlyLayout,
1219
+ d3ForceDirected: D3ForceDirectedLayout,
1220
+ colaForceDirected: ColaForceDirectedLayout
1221
+ };
1222
+ class LayoutService {
1223
+ getLayout(name) {
1224
+ if (layouts[name]) {
1225
+ return new layouts[name]();
1226
+ }
1227
+ else {
1228
+ throw new Error(`Unknown layout type '${name}'`);
1229
+ }
1230
+ }
1231
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1232
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LayoutService });
1233
+ }
1234
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: LayoutService, decorators: [{
1235
+ type: Injectable
1236
+ }] });
1237
+
1238
+ /**
1239
+ * Mousewheel directive
1240
+ * https://github.com/SodhanaLibrary/angular2-examples/blob/master/app/mouseWheelDirective/mousewheel.directive.ts
1241
+ *
1242
+ * @export
1243
+ */
1244
+ // tslint:disable-next-line: directive-selector
1245
+ class MouseWheelDirective {
1246
+ mouseWheelUp = new EventEmitter();
1247
+ mouseWheelDown = new EventEmitter();
1248
+ onMouseWheelChrome(event) {
1249
+ this.mouseWheelFunc(event);
1250
+ }
1251
+ onMouseWheelFirefox(event) {
1252
+ this.mouseWheelFunc(event);
1253
+ }
1254
+ onWheel(event) {
1255
+ this.mouseWheelFunc(event);
1256
+ }
1257
+ onMouseWheelIE(event) {
1258
+ this.mouseWheelFunc(event);
1259
+ }
1260
+ mouseWheelFunc(event) {
1261
+ if (window.event) {
1262
+ event = window.event;
1263
+ }
1264
+ const delta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail || event.deltaY || event.deltaX));
1265
+ // Firefox don't have native support for wheel event, as a result delta values are reverse
1266
+ const isWheelMouseUp = event.wheelDelta ? delta > 0 : delta < 0;
1267
+ const isWheelMouseDown = event.wheelDelta ? delta < 0 : delta > 0;
1268
+ if (isWheelMouseUp) {
1269
+ this.mouseWheelUp.emit(event);
1270
+ }
1271
+ else if (isWheelMouseDown) {
1272
+ this.mouseWheelDown.emit(event);
1273
+ }
1274
+ // for IE
1275
+ event.returnValue = false;
1276
+ // for Chrome and Firefox
1277
+ if (event.preventDefault) {
1278
+ event.preventDefault();
1279
+ }
1280
+ }
1281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: MouseWheelDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1282
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: MouseWheelDirective, isStandalone: false, selector: "[mouseWheel]", outputs: { mouseWheelUp: "mouseWheelUp", mouseWheelDown: "mouseWheelDown" }, host: { listeners: { "mousewheel": "onMouseWheelChrome($event)", "DOMMouseScroll": "onMouseWheelFirefox($event)", "wheel": "onWheel($event)", "onmousewheel": "onMouseWheelIE($event)" } }, ngImport: i0 });
1283
+ }
1284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: MouseWheelDirective, decorators: [{
1285
+ type: Directive,
1286
+ args: [{
1287
+ selector: '[mouseWheel]',
1288
+ standalone: false
1289
+ }]
1290
+ }], propDecorators: { mouseWheelUp: [{
1291
+ type: Output
1292
+ }], mouseWheelDown: [{
1293
+ type: Output
1294
+ }], onMouseWheelChrome: [{
1295
+ type: HostListener,
1296
+ args: ['mousewheel', ['$event']]
1297
+ }], onMouseWheelFirefox: [{
1298
+ type: HostListener,
1299
+ args: ['DOMMouseScroll', ['$event']]
1300
+ }], onWheel: [{
1301
+ type: HostListener,
1302
+ args: ['wheel', ['$event']]
1303
+ }], onMouseWheelIE: [{
1304
+ type: HostListener,
1305
+ args: ['onmousewheel', ['$event']]
1306
+ }] } });
1307
+
1308
+ var NgxGraphStates;
1309
+ (function (NgxGraphStates) {
1310
+ NgxGraphStates["Init"] = "init";
1311
+ NgxGraphStates["Subscribe"] = "subscribe";
1312
+ NgxGraphStates["Transform"] = "transform";
1313
+ /* eslint-disable @typescript-eslint/no-shadow */
1314
+ NgxGraphStates["Output"] = "output";
1315
+ })(NgxGraphStates || (NgxGraphStates = {}));
1316
+ class GraphComponent {
1317
+ el;
1318
+ zone;
1319
+ cd;
1320
+ layoutService;
1321
+ nodes = [];
1322
+ clusters = [];
1323
+ compoundNodes = [];
1324
+ links = [];
1325
+ activeEntries = [];
1326
+ curve;
1327
+ draggingEnabled = true;
1328
+ nodeHeight;
1329
+ nodeMaxHeight;
1330
+ nodeMinHeight;
1331
+ nodeWidth;
1332
+ nodeMinWidth;
1333
+ nodeMaxWidth;
1334
+ panningEnabled = true;
1335
+ panningAxis = PanningAxis.Both;
1336
+ enableZoom = true;
1337
+ zoomSpeed = 0.1;
1338
+ minZoomLevel = 0.1;
1339
+ maxZoomLevel = 4.0;
1340
+ autoZoom = false;
1341
+ panOnZoom = true;
1342
+ animate = false;
1343
+ autoCenter = false;
1344
+ update$;
1345
+ center$;
1346
+ zoomToFit$;
1347
+ panToNode$;
1348
+ layout;
1349
+ layoutSettings;
1350
+ enableTrackpadSupport = false;
1351
+ showMiniMap = false;
1352
+ miniMapMaxWidth = 100;
1353
+ miniMapMaxHeight;
1354
+ miniMapPosition = MiniMapPosition.UpperRight;
1355
+ view;
1356
+ scheme = 'cool';
1357
+ customColors;
1358
+ deferDisplayUntilPosition = false;
1359
+ centerNodesOnPositionChange = true;
1360
+ enablePreUpdateTransform = true;
1361
+ select = new EventEmitter();
1362
+ activate = new EventEmitter();
1363
+ deactivate = new EventEmitter();
1364
+ zoomChange = new EventEmitter();
1365
+ clickHandler = new EventEmitter();
1366
+ stateChange = new EventEmitter();
1367
+ linkTemplate;
1368
+ nodeTemplate;
1369
+ clusterTemplate;
1370
+ defsTemplate;
1371
+ miniMapNodeTemplate;
1372
+ nodeElements;
1373
+ linkElements;
1374
+ chartWidth;
1375
+ isMouseMoveCalled = false;
1376
+ graphSubscription = new Subscription();
1377
+ colors;
1378
+ dims;
1379
+ seriesDomain;
1380
+ transform;
1381
+ isPanning = false;
1382
+ isDragging = false;
1383
+ draggingNode;
1384
+ initialized = false;
1385
+ graph;
1386
+ graphDims = { width: 0, height: 0 };
1387
+ _oldLinks = [];
1388
+ oldNodes = new Set();
1389
+ oldClusters = new Set();
1390
+ oldCompoundNodes = new Set();
1391
+ transformationMatrix = identity();
1392
+ _touchLastX = null;
1393
+ _touchLastY = null;
1394
+ minimapScaleCoefficient = 3;
1395
+ minimapTransform;
1396
+ minimapOffsetX = 0;
1397
+ minimapOffsetY = 0;
1398
+ isMinimapPanning = false;
1399
+ minimapClipPathId;
1400
+ width;
1401
+ height;
1402
+ resizeSubscription;
1403
+ visibilityObserver;
1404
+ destroy$ = new Subject();
1405
+ constructor(el, zone, cd, layoutService) {
1406
+ this.el = el;
1407
+ this.zone = zone;
1408
+ this.cd = cd;
1409
+ this.layoutService = layoutService;
1410
+ }
1411
+ groupResultsBy = node => node.label;
1412
+ /**
1413
+ * Get the current zoom level
1414
+ */
1415
+ get zoomLevel() {
1416
+ return this.transformationMatrix.a;
1417
+ }
1418
+ /**
1419
+ * Set the current zoom level
1420
+ */
1421
+ set zoomLevel(level) {
1422
+ this.zoomTo(Number(level));
1423
+ }
1424
+ /**
1425
+ * Get the current `x` position of the graph
1426
+ */
1427
+ get panOffsetX() {
1428
+ return this.transformationMatrix.e;
1429
+ }
1430
+ /**
1431
+ * Set the current `x` position of the graph
1432
+ */
1433
+ set panOffsetX(x) {
1434
+ this.panTo(Number(x), null);
1435
+ }
1436
+ /**
1437
+ * Get the current `y` position of the graph
1438
+ */
1439
+ get panOffsetY() {
1440
+ return this.transformationMatrix.f;
1441
+ }
1442
+ /**
1443
+ * Set the current `y` position of the graph
1444
+ */
1445
+ set panOffsetY(y) {
1446
+ this.panTo(null, Number(y));
1447
+ }
1448
+ /**
1449
+ * Angular lifecycle event
1450
+ *
1451
+ *
1452
+ * @memberOf GraphComponent
1453
+ */
1454
+ ngOnInit() {
1455
+ if (this.update$) {
1456
+ this.update$.pipe(takeUntil(this.destroy$)).subscribe(() => {
1457
+ this.update();
1458
+ });
1459
+ }
1460
+ if (this.center$) {
1461
+ this.center$.pipe(takeUntil(this.destroy$)).subscribe(() => {
1462
+ this.center();
1463
+ });
1464
+ }
1465
+ if (this.zoomToFit$) {
1466
+ this.zoomToFit$.pipe(takeUntil(this.destroy$)).subscribe(options => {
1467
+ this.zoomToFit(options ? options : {});
1468
+ });
1469
+ }
1470
+ if (this.panToNode$) {
1471
+ this.panToNode$.pipe(takeUntil(this.destroy$)).subscribe((nodeId) => {
1472
+ this.panToNodeId(nodeId);
1473
+ });
1474
+ }
1475
+ this.minimapClipPathId = `minimapClip${id()}`;
1476
+ this.stateChange.emit({ state: NgxGraphStates.Subscribe });
1477
+ }
1478
+ ngOnChanges(changes) {
1479
+ this.basicUpdate();
1480
+ const { layoutSettings } = changes;
1481
+ this.setLayout(this.layout);
1482
+ if (layoutSettings) {
1483
+ this.setLayoutSettings(this.layoutSettings);
1484
+ }
1485
+ if (this.layout && this.nodes.length && this.links.length) {
1486
+ this.update();
1487
+ }
1488
+ }
1489
+ setLayout(layout) {
1490
+ this.initialized = false;
1491
+ if (!layout) {
1492
+ layout = 'dagre';
1493
+ }
1494
+ if (typeof layout === 'string') {
1495
+ this.layout = this.layoutService.getLayout(layout);
1496
+ this.setLayoutSettings(this.layoutSettings);
1497
+ }
1498
+ }
1499
+ setLayoutSettings(settings) {
1500
+ if (this.layout && typeof this.layout !== 'string') {
1501
+ this.layout.settings = settings;
1502
+ }
1503
+ }
1504
+ /**
1505
+ * Angular lifecycle event
1506
+ *
1507
+ *
1508
+ * @memberOf GraphComponent
1509
+ */
1510
+ ngOnDestroy() {
1511
+ this.unbindEvents();
1512
+ if (this.visibilityObserver) {
1513
+ this.visibilityObserver.visible.unsubscribe();
1514
+ this.visibilityObserver.destroy();
1515
+ }
1516
+ this.destroy$.next();
1517
+ this.destroy$.complete();
1518
+ }
1519
+ /**
1520
+ * Angular lifecycle event
1521
+ *
1522
+ *
1523
+ * @memberOf GraphComponent
1524
+ */
1525
+ ngAfterViewInit() {
1526
+ this.bindWindowResizeEvent();
1527
+ // listen for visibility of the element for hidden by default scenario
1528
+ this.visibilityObserver = new VisibilityObserver(this.el, this.zone);
1529
+ this.visibilityObserver.visible.subscribe(this.update.bind(this));
1530
+ setTimeout(() => this.update());
1531
+ }
1532
+ /**
1533
+ * Base class update implementation for the dag graph
1534
+ *
1535
+ * @memberOf GraphComponent
1536
+ */
1537
+ update() {
1538
+ this.basicUpdate();
1539
+ if (!this.curve) {
1540
+ this.curve = shape.curveBundle.beta(1);
1541
+ }
1542
+ this.zone.run(() => {
1543
+ this.dims = calculateViewDimensions({
1544
+ width: this.width,
1545
+ height: this.height
1546
+ });
1547
+ this.seriesDomain = this.getSeriesDomain();
1548
+ this.setColors();
1549
+ this.createGraph();
1550
+ this.updateTransform();
1551
+ if (!this.initialized) {
1552
+ this.stateChange.emit({ state: NgxGraphStates.Init });
1553
+ }
1554
+ this.initialized = true;
1555
+ });
1556
+ }
1557
+ /**
1558
+ * Creates the dagre graph engine
1559
+ *
1560
+ * @memberOf GraphComponent
1561
+ */
1562
+ createGraph() {
1563
+ this.graphSubscription.unsubscribe();
1564
+ this.graphSubscription = new Subscription();
1565
+ const initializeNode = (n) => {
1566
+ if (!n.meta) {
1567
+ n.meta = {};
1568
+ }
1569
+ if (!n.id) {
1570
+ n.id = id();
1571
+ }
1572
+ if (!n.dimension) {
1573
+ n.dimension = {
1574
+ width: this.nodeWidth ? this.nodeWidth : 30,
1575
+ height: this.nodeHeight ? this.nodeHeight : 30
1576
+ };
1577
+ n.meta.forceDimensions = false;
1578
+ }
1579
+ else {
1580
+ n.meta.forceDimensions = n.meta.forceDimensions === undefined ? true : n.meta.forceDimensions;
1581
+ }
1582
+ if (!n.position) {
1583
+ n.position = {
1584
+ x: 0,
1585
+ y: 0
1586
+ };
1587
+ if (this.deferDisplayUntilPosition) {
1588
+ n.hidden = true;
1589
+ }
1590
+ }
1591
+ n.data = n.data ? n.data : {};
1592
+ return n;
1593
+ };
1594
+ const initializeEdge = (e) => {
1595
+ if (!e.id) {
1596
+ e.id = id();
1597
+ }
1598
+ return e;
1599
+ };
1600
+ this.graph = {
1601
+ nodes: this.nodes.map(n => initializeNode(n)),
1602
+ clusters: this.clusters.map(n => initializeNode(n)),
1603
+ compoundNodes: this.compoundNodes.map(n => initializeNode(n)),
1604
+ edges: this.links.map(e => initializeEdge(e))
1605
+ };
1606
+ requestAnimationFrame(() => this.draw());
1607
+ }
1608
+ /**
1609
+ * Draws the graph using dagre layouts
1610
+ *
1611
+ *
1612
+ * @memberOf GraphComponent
1613
+ */
1614
+ draw() {
1615
+ // Recalculate the layout
1616
+ const result = this.layout.run(this.graph);
1617
+ const result$ = result instanceof Observable ? result : of(result);
1618
+ this.graphSubscription.add(result$.subscribe(graph => {
1619
+ this.graph = graph;
1620
+ this.tick();
1621
+ }));
1622
+ }
1623
+ tick() {
1624
+ // Transposes view options to the node
1625
+ const oldNodes = new Set();
1626
+ const oldClusters = new Set();
1627
+ const oldCompoundNodes = new Set();
1628
+ this.graph.nodes.forEach(n => {
1629
+ n.transform = `translate(${n.position.x - (this.centerNodesOnPositionChange ? n.dimension.width / 2 : 0) || 0}, ${n.position.y - (this.centerNodesOnPositionChange ? n.dimension.height / 2 : 0) || 0})`;
1630
+ if (!n.data) {
1631
+ n.data = {};
1632
+ }
1633
+ n.data.color = this.colors.getColor(this.groupResultsBy(n));
1634
+ if (this.deferDisplayUntilPosition) {
1635
+ n.hidden = false;
1636
+ }
1637
+ oldNodes.add(n.id);
1638
+ });
1639
+ (this.graph.clusters || []).forEach(n => {
1640
+ n.transform = `translate(${n.position.x - (this.centerNodesOnPositionChange ? n.dimension.width / 2 : 0) || 0}, ${n.position.y - (this.centerNodesOnPositionChange ? n.dimension.height / 2 : 0) || 0})`;
1641
+ if (!n.data) {
1642
+ n.data = {};
1643
+ }
1644
+ n.data.color = this.colors.getColor(this.groupResultsBy(n));
1645
+ if (this.deferDisplayUntilPosition) {
1646
+ n.hidden = false;
1647
+ }
1648
+ oldClusters.add(n.id);
1649
+ });
1650
+ (this.graph.compoundNodes || []).forEach(n => {
1651
+ n.transform = `translate(${n.position.x - (this.centerNodesOnPositionChange ? n.dimension.width / 2 : 0) || 0}, ${n.position.y - (this.centerNodesOnPositionChange ? n.dimension.height / 2 : 0) || 0})`;
1652
+ if (!n.data) {
1653
+ n.data = {};
1654
+ }
1655
+ n.data.color = this.colors.getColor(this.groupResultsBy(n));
1656
+ if (this.deferDisplayUntilPosition) {
1657
+ n.hidden = false;
1658
+ }
1659
+ oldCompoundNodes.add(n.id);
1660
+ });
1661
+ // Prevent animations on new nodes
1662
+ setTimeout(() => {
1663
+ this.oldNodes = oldNodes;
1664
+ this.oldClusters = oldClusters;
1665
+ this.oldCompoundNodes = oldCompoundNodes;
1666
+ }, 500);
1667
+ // Update the labels to the new positions
1668
+ const newLinks = [];
1669
+ for (const edgeLabelId in this.graph.edgeLabels) {
1670
+ const edgeLabel = this.graph.edgeLabels[edgeLabelId];
1671
+ const normKey = edgeLabelId.replace(/[^\w-]*/g, '');
1672
+ const isMultigraph = this.layout && typeof this.layout !== 'string' && this.layout.settings && this.layout.settings.multigraph;
1673
+ let oldLink = isMultigraph
1674
+ ? this._oldLinks.find(ol => `${ol.source}${ol.target}${ol.id}` === normKey)
1675
+ : this._oldLinks.find(ol => `${ol.source}${ol.target}` === normKey);
1676
+ const linkFromGraph = isMultigraph
1677
+ ? this.graph.edges.find(nl => `${nl.source}${nl.target}${nl.id}` === normKey)
1678
+ : this.graph.edges.find(nl => `${nl.source}${nl.target}` === normKey);
1679
+ if (!oldLink) {
1680
+ oldLink = linkFromGraph || edgeLabel;
1681
+ }
1682
+ else if (oldLink.data &&
1683
+ linkFromGraph &&
1684
+ linkFromGraph.data &&
1685
+ JSON.stringify(oldLink.data) !== JSON.stringify(linkFromGraph.data)) {
1686
+ // Compare old link to new link and replace if not equal
1687
+ oldLink.data = linkFromGraph.data;
1688
+ }
1689
+ oldLink.oldLine = oldLink.line;
1690
+ const points = edgeLabel.points;
1691
+ const line = this.generateLine(points);
1692
+ const newLink = Object.assign({}, oldLink);
1693
+ newLink.line = line;
1694
+ newLink.points = points;
1695
+ this.updateMidpointOnEdge(newLink, points);
1696
+ const textPos = points[Math.floor(points.length / 2)];
1697
+ if (textPos) {
1698
+ newLink.textTransform = `translate(${textPos.x || 0},${textPos.y || 0})`;
1699
+ }
1700
+ newLink.textAngle = 0;
1701
+ if (!newLink.oldLine) {
1702
+ newLink.oldLine = newLink.line;
1703
+ }
1704
+ this.calcDominantBaseline(newLink);
1705
+ newLinks.push(newLink);
1706
+ }
1707
+ this.graph.edges = newLinks;
1708
+ // Map the old links for animations
1709
+ if (this.graph.edges) {
1710
+ this._oldLinks = this.graph.edges.map(l => {
1711
+ const newL = Object.assign({}, l);
1712
+ newL.oldLine = l.line;
1713
+ return newL;
1714
+ });
1715
+ }
1716
+ this.applyNodeDimensions();
1717
+ this.redrawLines();
1718
+ this.updateMinimap();
1719
+ requestAnimationFrame(() => {
1720
+ this.applyNodeDimensions();
1721
+ this.redrawLines();
1722
+ this.updateMinimap();
1723
+ if (this.autoZoom) {
1724
+ this.zoomToFit({ autoCenter: this.autoCenter ? this.autoCenter : false });
1725
+ }
1726
+ else if (this.autoCenter) {
1727
+ // Auto-center when rendering
1728
+ this.center();
1729
+ }
1730
+ this.stateChange.emit({ state: NgxGraphStates.Output });
1731
+ });
1732
+ this.cd.markForCheck();
1733
+ }
1734
+ getMinimapTransform() {
1735
+ switch (this.miniMapPosition) {
1736
+ case MiniMapPosition.UpperLeft: {
1737
+ return '';
1738
+ }
1739
+ case MiniMapPosition.UpperRight: {
1740
+ return 'translate(' + (this.dims.width - this.graphDims.width / this.minimapScaleCoefficient) + ',' + 0 + ')';
1741
+ }
1742
+ default: {
1743
+ return '';
1744
+ }
1745
+ }
1746
+ }
1747
+ updateGraphDims() {
1748
+ let minX = +Infinity;
1749
+ let maxX = -Infinity;
1750
+ let minY = +Infinity;
1751
+ let maxY = -Infinity;
1752
+ for (let i = 0; i < this.graph.nodes.length; i++) {
1753
+ const node = this.graph.nodes[i];
1754
+ minX = node.position.x < minX ? node.position.x : minX;
1755
+ minY = node.position.y < minY ? node.position.y : minY;
1756
+ maxX = node.position.x + node.dimension.width > maxX ? node.position.x + node.dimension.width : maxX;
1757
+ maxY = node.position.y + node.dimension.height > maxY ? node.position.y + node.dimension.height : maxY;
1758
+ }
1759
+ minX -= 100;
1760
+ minY -= 100;
1761
+ maxX += 100;
1762
+ maxY += 100;
1763
+ this.graphDims.width = maxX - minX;
1764
+ this.graphDims.height = maxY - minY;
1765
+ this.minimapOffsetX = minX;
1766
+ this.minimapOffsetY = minY;
1767
+ }
1768
+ updateMinimap() {
1769
+ // Calculate the height/width total, but only if we have any nodes
1770
+ if (this.graph.nodes && this.graph.nodes.length) {
1771
+ this.updateGraphDims();
1772
+ if (this.miniMapMaxWidth) {
1773
+ this.minimapScaleCoefficient = this.graphDims.width / this.miniMapMaxWidth;
1774
+ }
1775
+ if (this.miniMapMaxHeight) {
1776
+ this.minimapScaleCoefficient = Math.max(this.minimapScaleCoefficient, this.graphDims.height / this.miniMapMaxHeight);
1777
+ }
1778
+ this.minimapTransform = this.getMinimapTransform();
1779
+ }
1780
+ }
1781
+ /**
1782
+ * Measures the node element and applies the dimensions
1783
+ *
1784
+ * @memberOf GraphComponent
1785
+ */
1786
+ applyNodeDimensions() {
1787
+ if (this.nodeElements && this.nodeElements.length) {
1788
+ this.nodeElements.forEach(elem => {
1789
+ const nativeElement = elem.nativeElement;
1790
+ const node = this.graph.nodes.find(n => n.id === nativeElement.id);
1791
+ if (!node) {
1792
+ return;
1793
+ }
1794
+ // calculate the height
1795
+ let dims;
1796
+ try {
1797
+ dims = nativeElement.getBBox();
1798
+ if (!dims.width || !dims.height) {
1799
+ return;
1800
+ }
1801
+ }
1802
+ catch (ex) {
1803
+ // Skip drawing if element is not displayed - Firefox would throw an error here
1804
+ return;
1805
+ }
1806
+ if (this.nodeHeight) {
1807
+ node.dimension.height =
1808
+ node.dimension.height && node.meta.forceDimensions ? node.dimension.height : this.nodeHeight;
1809
+ }
1810
+ else {
1811
+ node.dimension.height =
1812
+ node.dimension.height && node.meta.forceDimensions ? node.dimension.height : dims.height;
1813
+ }
1814
+ if (this.nodeMaxHeight) {
1815
+ node.dimension.height = Math.max(node.dimension.height, this.nodeMaxHeight);
1816
+ }
1817
+ if (this.nodeMinHeight) {
1818
+ node.dimension.height = Math.min(node.dimension.height, this.nodeMinHeight);
1819
+ }
1820
+ if (this.nodeWidth) {
1821
+ node.dimension.width =
1822
+ node.dimension.width && node.meta.forceDimensions ? node.dimension.width : this.nodeWidth;
1823
+ }
1824
+ else {
1825
+ // calculate the width
1826
+ if (nativeElement.getElementsByTagName('text').length) {
1827
+ let maxTextDims;
1828
+ try {
1829
+ for (const textElem of nativeElement.getElementsByTagName('text')) {
1830
+ const currentBBox = textElem.getBBox();
1831
+ if (!maxTextDims) {
1832
+ maxTextDims = currentBBox;
1833
+ }
1834
+ else {
1835
+ if (currentBBox.width > maxTextDims.width) {
1836
+ maxTextDims.width = currentBBox.width;
1837
+ }
1838
+ if (currentBBox.height > maxTextDims.height) {
1839
+ maxTextDims.height = currentBBox.height;
1840
+ }
1841
+ }
1842
+ }
1843
+ }
1844
+ catch (ex) {
1845
+ // Skip drawing if element is not displayed - Firefox would throw an error here
1846
+ return;
1847
+ }
1848
+ node.dimension.width =
1849
+ node.dimension.width && node.meta.forceDimensions ? node.dimension.width : maxTextDims.width + 20;
1850
+ }
1851
+ else {
1852
+ node.dimension.width =
1853
+ node.dimension.width && node.meta.forceDimensions ? node.dimension.width : dims.width;
1854
+ }
1855
+ }
1856
+ if (this.nodeMaxWidth) {
1857
+ node.dimension.width = Math.max(node.dimension.width, this.nodeMaxWidth);
1858
+ }
1859
+ if (this.nodeMinWidth) {
1860
+ node.dimension.width = Math.min(node.dimension.width, this.nodeMinWidth);
1861
+ }
1862
+ });
1863
+ }
1864
+ }
1865
+ /**
1866
+ * Redraws the lines when dragged or viewport updated
1867
+ *
1868
+ * @memberOf GraphComponent
1869
+ */
1870
+ redrawLines(_animate = this.animate) {
1871
+ this.linkElements?.forEach(linkEl => {
1872
+ const edge = this.graph.edges.find(lin => lin.id === linkEl.nativeElement.id);
1873
+ if (edge) {
1874
+ const linkSelection = select(linkEl.nativeElement).select('.line');
1875
+ linkSelection
1876
+ .attr('d', edge.oldLine)
1877
+ .transition()
1878
+ .ease(ease.easeSinInOut)
1879
+ .duration(_animate ? 500 : 0)
1880
+ .attr('d', edge.line);
1881
+ const textPathSelection = select(this.el.nativeElement).select(`#${edge.id}`);
1882
+ textPathSelection
1883
+ .attr('d', edge.oldTextPath)
1884
+ .transition()
1885
+ .ease(ease.easeSinInOut)
1886
+ .duration(_animate ? 500 : 0)
1887
+ .attr('d', edge.textPath);
1888
+ this.updateMidpointOnEdge(edge, edge.points);
1889
+ }
1890
+ });
1891
+ }
1892
+ /**
1893
+ * Calculate the text directions / flipping
1894
+ *
1895
+ * @memberOf GraphComponent
1896
+ */
1897
+ calcDominantBaseline(link) {
1898
+ const firstPoint = link.points[0];
1899
+ const lastPoint = link.points[link.points.length - 1];
1900
+ link.oldTextPath = link.textPath;
1901
+ if (lastPoint.x < firstPoint.x) {
1902
+ link.dominantBaseline = 'text-before-edge';
1903
+ // reverse text path for when its flipped upside down
1904
+ link.textPath = this.generateLine([...link.points].reverse());
1905
+ }
1906
+ else {
1907
+ link.dominantBaseline = 'text-after-edge';
1908
+ link.textPath = link.line;
1909
+ }
1910
+ }
1911
+ /**
1912
+ * Generate the new line path
1913
+ *
1914
+ * @memberOf GraphComponent
1915
+ */
1916
+ generateLine(points) {
1917
+ const lineFunction = shape
1918
+ .line()
1919
+ .x(d => d.x)
1920
+ .y(d => d.y)
1921
+ .curve(this.curve);
1922
+ return lineFunction(points);
1923
+ }
1924
+ /**
1925
+ * Zoom was invoked from event
1926
+ *
1927
+ * @memberOf GraphComponent
1928
+ */
1929
+ onZoom($event, direction) {
1930
+ if (this.enableTrackpadSupport && !$event.ctrlKey) {
1931
+ this.pan($event.deltaX * -1, $event.deltaY * -1);
1932
+ return;
1933
+ }
1934
+ const zoomFactor = 1 + (direction === 'in' ? this.zoomSpeed : -this.zoomSpeed);
1935
+ // Check that zooming wouldn't put us out of bounds
1936
+ const newZoomLevel = this.zoomLevel * zoomFactor;
1937
+ if (newZoomLevel <= this.minZoomLevel || newZoomLevel >= this.maxZoomLevel) {
1938
+ return;
1939
+ }
1940
+ // Check if zooming is enabled or not
1941
+ if (!this.enableZoom) {
1942
+ return;
1943
+ }
1944
+ if (this.panOnZoom === true && $event) {
1945
+ // Absolute mouse X/Y on the screen
1946
+ const mouseX = $event.clientX;
1947
+ const mouseY = $event.clientY;
1948
+ // Transform the mouse X/Y into a SVG X/Y
1949
+ const svg = this.el.nativeElement.querySelector('svg');
1950
+ const svgGroup = svg.querySelector('g.chart');
1951
+ const point = svg.createSVGPoint();
1952
+ point.x = mouseX;
1953
+ point.y = mouseY;
1954
+ const svgPoint = point.matrixTransform(svgGroup.getScreenCTM().inverse());
1955
+ // Panzoom
1956
+ this.pan(svgPoint.x, svgPoint.y, true);
1957
+ this.zoom(zoomFactor);
1958
+ this.pan(-svgPoint.x, -svgPoint.y, true);
1959
+ }
1960
+ else {
1961
+ this.zoom(zoomFactor);
1962
+ }
1963
+ }
1964
+ /**
1965
+ * Pan by x/y
1966
+ *
1967
+ * @param x
1968
+ * @param y
1969
+ */
1970
+ pan(x, y, ignoreZoomLevel = false) {
1971
+ const zoomLevel = ignoreZoomLevel ? 1 : this.zoomLevel;
1972
+ this.transformationMatrix = transform(this.transformationMatrix, translate(x / zoomLevel, y / zoomLevel));
1973
+ this.updateTransform();
1974
+ }
1975
+ /**
1976
+ * Pan to a fixed x/y
1977
+ *
1978
+ */
1979
+ panTo(x, y) {
1980
+ if (x === null || x === undefined || isNaN(x) || y === null || y === undefined || isNaN(y)) {
1981
+ return;
1982
+ }
1983
+ const panX = -this.panOffsetX - x * this.zoomLevel + this.dims.width / 2;
1984
+ const panY = -this.panOffsetY - y * this.zoomLevel + this.dims.height / 2;
1985
+ this.transformationMatrix = transform(this.transformationMatrix, translate(panX / this.zoomLevel, panY / this.zoomLevel));
1986
+ this.updateTransform();
1987
+ }
1988
+ /**
1989
+ * Zoom by a factor
1990
+ *
1991
+ */
1992
+ zoom(factor) {
1993
+ this.transformationMatrix = transform(this.transformationMatrix, scale(factor, factor));
1994
+ this.zoomChange.emit(this.zoomLevel);
1995
+ this.updateTransform();
1996
+ }
1997
+ /**
1998
+ * Zoom to a fixed level
1999
+ *
2000
+ */
2001
+ zoomTo(level) {
2002
+ this.transformationMatrix.a = isNaN(level) ? this.transformationMatrix.a : Number(level);
2003
+ this.transformationMatrix.d = isNaN(level) ? this.transformationMatrix.d : Number(level);
2004
+ this.zoomChange.emit(this.zoomLevel);
2005
+ if (this.enablePreUpdateTransform) {
2006
+ this.updateTransform();
2007
+ }
2008
+ this.update();
2009
+ }
2010
+ /**
2011
+ * Drag was invoked from an event
2012
+ *
2013
+ * @memberOf GraphComponent
2014
+ */
2015
+ onDrag(event) {
2016
+ if (!this.draggingEnabled) {
2017
+ return;
2018
+ }
2019
+ const node = this.draggingNode;
2020
+ if (this.layout && typeof this.layout !== 'string' && this.layout.onDrag) {
2021
+ this.layout.onDrag(node, event);
2022
+ }
2023
+ node.position.x += event.movementX / this.zoomLevel;
2024
+ node.position.y += event.movementY / this.zoomLevel;
2025
+ // move the node
2026
+ const x = node.position.x - (this.centerNodesOnPositionChange ? node.dimension.width / 2 : 0);
2027
+ const y = node.position.y - (this.centerNodesOnPositionChange ? node.dimension.height / 2 : 0);
2028
+ node.transform = `translate(${x}, ${y})`;
2029
+ for (const link of this.graph.edges) {
2030
+ if (link.target === node.id ||
2031
+ link.source === node.id ||
2032
+ link.target.id === node.id ||
2033
+ link.source.id === node.id) {
2034
+ if (this.layout && typeof this.layout !== 'string') {
2035
+ const result = this.layout.updateEdge(this.graph, link);
2036
+ const result$ = result instanceof Observable ? result : of(result);
2037
+ this.graphSubscription.add(result$.subscribe(graph => {
2038
+ this.graph = graph;
2039
+ this.redrawEdge(link);
2040
+ }));
2041
+ }
2042
+ }
2043
+ }
2044
+ this.redrawLines(false);
2045
+ this.updateMinimap();
2046
+ }
2047
+ redrawEdge(edge) {
2048
+ const line = this.generateLine(edge.points);
2049
+ this.calcDominantBaseline(edge);
2050
+ edge.oldLine = edge.line;
2051
+ edge.line = line;
2052
+ }
2053
+ /**
2054
+ * Update the entire view for the new pan position
2055
+ *
2056
+ *
2057
+ * @memberOf GraphComponent
2058
+ */
2059
+ updateTransform() {
2060
+ this.transform = toSVG(smoothMatrix(this.transformationMatrix, 100));
2061
+ this.stateChange.emit({ state: NgxGraphStates.Transform });
2062
+ }
2063
+ /**
2064
+ * Node was clicked
2065
+ *
2066
+ *
2067
+ * @memberOf GraphComponent
2068
+ */
2069
+ onClick(event) {
2070
+ this.select.emit(event);
2071
+ }
2072
+ /**
2073
+ * Node was focused
2074
+ *
2075
+ *
2076
+ * @memberOf GraphComponent
2077
+ */
2078
+ onActivate(event) {
2079
+ if (this.activeEntries.indexOf(event) > -1) {
2080
+ return;
2081
+ }
2082
+ this.activeEntries = [event, ...this.activeEntries];
2083
+ this.activate.emit({ value: event, entries: this.activeEntries });
2084
+ }
2085
+ /**
2086
+ * Node was defocused
2087
+ *
2088
+ * @memberOf GraphComponent
2089
+ */
2090
+ onDeactivate(event) {
2091
+ const idx = this.activeEntries.indexOf(event);
2092
+ this.activeEntries.splice(idx, 1);
2093
+ this.activeEntries = [...this.activeEntries];
2094
+ this.deactivate.emit({ value: event, entries: this.activeEntries });
2095
+ }
2096
+ /**
2097
+ * Get the domain series for the nodes
2098
+ *
2099
+ * @memberOf GraphComponent
2100
+ */
2101
+ getSeriesDomain() {
2102
+ return this.nodes
2103
+ .map(d => this.groupResultsBy(d))
2104
+ .reduce((nodes, node) => (nodes.indexOf(node) !== -1 ? nodes : nodes.concat([node])), [])
2105
+ .sort();
2106
+ }
2107
+ /**
2108
+ * Tracking for the link
2109
+ *
2110
+ *
2111
+ * @memberOf GraphComponent
2112
+ */
2113
+ trackLinkBy(index, link) {
2114
+ return link.id;
2115
+ }
2116
+ /**
2117
+ * Tracking for the node
2118
+ *
2119
+ *
2120
+ * @memberOf GraphComponent
2121
+ */
2122
+ trackNodeBy(index, node) {
2123
+ return node.id;
2124
+ }
2125
+ /**
2126
+ * Sets the colors the nodes
2127
+ *
2128
+ *
2129
+ * @memberOf GraphComponent
2130
+ */
2131
+ setColors() {
2132
+ this.colors = new ColorHelper(this.scheme, this.seriesDomain, this.customColors);
2133
+ }
2134
+ /**
2135
+ * On mouse move event, used for panning and dragging.
2136
+ *
2137
+ * @memberOf GraphComponent
2138
+ */
2139
+ onMouseMove($event) {
2140
+ this.isMouseMoveCalled = true;
2141
+ if ((this.isPanning || this.isMinimapPanning) && this.panningEnabled) {
2142
+ this.panWithConstraints(this.panningAxis, $event);
2143
+ }
2144
+ else if (this.isDragging && this.draggingEnabled) {
2145
+ this.onDrag($event);
2146
+ }
2147
+ }
2148
+ onMouseDown(event) {
2149
+ this.isMouseMoveCalled = false;
2150
+ }
2151
+ graphClick(event) {
2152
+ if (!this.isMouseMoveCalled)
2153
+ this.clickHandler.emit(event);
2154
+ }
2155
+ /**
2156
+ * On touch start event to enable panning.
2157
+ *
2158
+ * @memberOf GraphComponent
2159
+ */
2160
+ onTouchStart(event) {
2161
+ this._touchLastX = event.changedTouches[0].clientX;
2162
+ this._touchLastY = event.changedTouches[0].clientY;
2163
+ this.isPanning = true;
2164
+ }
2165
+ /**
2166
+ * On touch move event, used for panning.
2167
+ *
2168
+ */
2169
+ onTouchMove($event) {
2170
+ if (this.isPanning && this.panningEnabled) {
2171
+ const clientX = $event.changedTouches[0].clientX;
2172
+ const clientY = $event.changedTouches[0].clientY;
2173
+ const movementX = clientX - this._touchLastX;
2174
+ const movementY = clientY - this._touchLastY;
2175
+ this._touchLastX = clientX;
2176
+ this._touchLastY = clientY;
2177
+ this.pan(movementX, movementY);
2178
+ }
2179
+ }
2180
+ /**
2181
+ * On touch end event to disable panning.
2182
+ *
2183
+ * @memberOf GraphComponent
2184
+ */
2185
+ onTouchEnd() {
2186
+ this.isPanning = false;
2187
+ }
2188
+ /**
2189
+ * On mouse up event to disable panning/dragging.
2190
+ *
2191
+ * @memberOf GraphComponent
2192
+ */
2193
+ onMouseUp(event) {
2194
+ this.isDragging = false;
2195
+ this.isPanning = false;
2196
+ this.isMinimapPanning = false;
2197
+ if (this.layout && typeof this.layout !== 'string' && this.layout.onDragEnd) {
2198
+ this.layout.onDragEnd(this.draggingNode, event);
2199
+ }
2200
+ }
2201
+ /**
2202
+ * On node mouse down to kick off dragging
2203
+ *
2204
+ * @memberOf GraphComponent
2205
+ */
2206
+ onNodeMouseDown(event, node) {
2207
+ if (!this.draggingEnabled) {
2208
+ return;
2209
+ }
2210
+ this.isDragging = true;
2211
+ this.draggingNode = node;
2212
+ if (this.layout && typeof this.layout !== 'string' && this.layout.onDragStart) {
2213
+ this.layout.onDragStart(node, event);
2214
+ }
2215
+ }
2216
+ /**
2217
+ * On minimap drag mouse down to kick off minimap panning
2218
+ *
2219
+ * @memberOf GraphComponent
2220
+ */
2221
+ onMinimapDragMouseDown() {
2222
+ this.isMinimapPanning = true;
2223
+ }
2224
+ /**
2225
+ * On minimap pan event. Pans the graph to the clicked position
2226
+ *
2227
+ * @memberOf GraphComponent
2228
+ */
2229
+ onMinimapPanTo(event) {
2230
+ const x = event.offsetX - (this.dims.width - (this.graphDims.width + this.minimapOffsetX) / this.minimapScaleCoefficient);
2231
+ const y = event.offsetY + this.minimapOffsetY / this.minimapScaleCoefficient;
2232
+ this.panTo(x * this.minimapScaleCoefficient, y * this.minimapScaleCoefficient);
2233
+ this.isMinimapPanning = true;
2234
+ }
2235
+ /**
2236
+ * Center the graph in the viewport
2237
+ */
2238
+ center() {
2239
+ this.panTo(this.graphDims.width / 2, this.graphDims.height / 2);
2240
+ }
2241
+ /**
2242
+ * Zooms to fit the entire graph
2243
+ */
2244
+ zoomToFit(zoomOptions) {
2245
+ this.dims = calculateViewDimensions({
2246
+ width: this.width,
2247
+ height: this.height
2248
+ });
2249
+ this.updateGraphDims();
2250
+ const heightZoom = this.dims.height / this.graphDims.height;
2251
+ const widthZoom = this.dims.width / this.graphDims.width;
2252
+ let zoomLevel = Math.min(heightZoom, widthZoom, 1);
2253
+ if (zoomLevel < this.minZoomLevel) {
2254
+ zoomLevel = this.minZoomLevel;
2255
+ }
2256
+ if (zoomLevel > this.maxZoomLevel) {
2257
+ zoomLevel = this.maxZoomLevel;
2258
+ }
2259
+ if (zoomOptions?.force === true || zoomLevel !== this.zoomLevel) {
2260
+ this.zoomLevel = zoomLevel;
2261
+ if (zoomOptions?.autoCenter !== true) {
2262
+ this.updateTransform();
2263
+ }
2264
+ if (zoomOptions?.autoCenter === true) {
2265
+ this.center();
2266
+ }
2267
+ this.zoomChange.emit(this.zoomLevel);
2268
+ }
2269
+ }
2270
+ /**
2271
+ * Pans to the node
2272
+ * @param nodeId
2273
+ */
2274
+ panToNodeId(nodeId) {
2275
+ const node = this.graph.nodes.find(n => n.id === nodeId);
2276
+ if (!node) {
2277
+ return;
2278
+ }
2279
+ this.panTo(node.position.x, node.position.y);
2280
+ }
2281
+ getCompoundNodeChildren(ids) {
2282
+ return this.nodes.filter(node => ids.includes(node.id));
2283
+ }
2284
+ panWithConstraints(key, event) {
2285
+ let x = event.movementX;
2286
+ let y = event.movementY;
2287
+ if (this.isMinimapPanning) {
2288
+ x = -this.minimapScaleCoefficient * x * this.zoomLevel;
2289
+ y = -this.minimapScaleCoefficient * y * this.zoomLevel;
2290
+ }
2291
+ switch (key) {
2292
+ case PanningAxis.Horizontal:
2293
+ this.pan(x, 0);
2294
+ break;
2295
+ case PanningAxis.Vertical:
2296
+ this.pan(0, y);
2297
+ break;
2298
+ default:
2299
+ this.pan(x, y);
2300
+ break;
2301
+ }
2302
+ }
2303
+ updateMidpointOnEdge(edge, points) {
2304
+ if (!edge || !points) {
2305
+ return;
2306
+ }
2307
+ if (points.length % 2 === 1) {
2308
+ edge.midPoint = points[Math.floor(points.length / 2)];
2309
+ }
2310
+ else {
2311
+ // Checking if the current layout is Elk
2312
+ if (this.layout?.settings?.properties?.['elk.direction']) {
2313
+ this._calcMidPointElk(edge, points);
2314
+ }
2315
+ else {
2316
+ const _first = points[points.length / 2];
2317
+ const _second = points[points.length / 2 - 1];
2318
+ edge.midPoint = {
2319
+ x: (_first.x + _second.x) / 2,
2320
+ y: (_first.y + _second.y) / 2
2321
+ };
2322
+ }
2323
+ }
2324
+ }
2325
+ _calcMidPointElk(edge, points) {
2326
+ let _firstX = null;
2327
+ let _secondX = null;
2328
+ let _firstY = null;
2329
+ let _secondY = null;
2330
+ const orientation = this.layout.settings?.properties['elk.direction'];
2331
+ const hasBend = orientation === 'RIGHT' ? points.some(p => p.y !== points[0].y) : points.some(p => p.x !== points[0].x);
2332
+ if (hasBend) {
2333
+ // getting the last two points
2334
+ _firstX = points[points.length - 1];
2335
+ _secondX = points[points.length - 2];
2336
+ _firstY = points[points.length - 1];
2337
+ _secondY = points[points.length - 2];
2338
+ }
2339
+ else {
2340
+ if (orientation === 'RIGHT') {
2341
+ _firstX = points[0];
2342
+ _secondX = points[points.length - 1];
2343
+ _firstY = points[points.length / 2];
2344
+ _secondY = points[points.length / 2 - 1];
2345
+ }
2346
+ else {
2347
+ _firstX = points[points.length / 2];
2348
+ _secondX = points[points.length / 2 - 1];
2349
+ _firstY = points[0];
2350
+ _secondY = points[points.length - 1];
2351
+ }
2352
+ }
2353
+ edge.midPoint = {
2354
+ x: (_firstX.x + _secondX.x) / 2,
2355
+ y: (_firstY.y + _secondY.y) / 2
2356
+ };
2357
+ }
2358
+ basicUpdate() {
2359
+ if (this.view) {
2360
+ this.width = this.view[0];
2361
+ this.height = this.view[1];
2362
+ }
2363
+ else {
2364
+ const dims = this.getContainerDims();
2365
+ if (dims) {
2366
+ this.width = dims.width;
2367
+ this.height = dims.height;
2368
+ }
2369
+ }
2370
+ // default values if width or height are 0 or undefined
2371
+ if (!this.width) {
2372
+ this.width = 600;
2373
+ }
2374
+ if (!this.height) {
2375
+ this.height = 400;
2376
+ }
2377
+ this.width = Math.floor(this.width);
2378
+ this.height = Math.floor(this.height);
2379
+ if (this.cd) {
2380
+ this.cd.markForCheck();
2381
+ }
2382
+ }
2383
+ getContainerDims() {
2384
+ let width;
2385
+ let height;
2386
+ const hostElem = this.el.nativeElement;
2387
+ if (hostElem.parentNode !== null) {
2388
+ // Get the container dimensions
2389
+ const dims = hostElem.parentNode.getBoundingClientRect();
2390
+ width = dims.width;
2391
+ height = dims.height;
2392
+ }
2393
+ if (width && height) {
2394
+ return { width, height };
2395
+ }
2396
+ return null;
2397
+ }
2398
+ /**
2399
+ * Checks if the graph has dimensions
2400
+ */
2401
+ hasGraphDims() {
2402
+ return this.graphDims.width > 0 && this.graphDims.height > 0;
2403
+ }
2404
+ /**
2405
+ * Checks if all nodes have dimension
2406
+ */
2407
+ hasNodeDims() {
2408
+ return this.graph.nodes?.every(node => node.dimension.width > 0 && node.dimension.height > 0);
2409
+ }
2410
+ /**
2411
+ * Checks if all compound nodes have dimension
2412
+ */
2413
+ hasCompoundNodeDims() {
2414
+ return this.graph.compoundNodes?.every(node => node.dimension.width > 0 && node.dimension.height > 0);
2415
+ }
2416
+ /**
2417
+ * Checks if all clusters have dimension
2418
+ */
2419
+ hasClusterDims() {
2420
+ return this.graph.clusters?.every(node => node.dimension.width > 0 && node.dimension.height > 0);
2421
+ }
2422
+ /**
2423
+ * Checks if the graph and all nodes have dimension.
2424
+ */
2425
+ hasDims() {
2426
+ return (this.hasGraphDims() &&
2427
+ this.hasNodeDims() &&
2428
+ ((this.compoundNodes?.length ? this.hasCompoundNodeDims() : true) ||
2429
+ (this.clusters?.length ? this.hasClusterDims() : true)));
2430
+ }
2431
+ unbindEvents() {
2432
+ if (this.resizeSubscription) {
2433
+ this.resizeSubscription.unsubscribe();
2434
+ }
2435
+ }
2436
+ bindWindowResizeEvent() {
2437
+ const source = fromEvent(window, 'resize');
2438
+ const subscription = source.pipe(debounceTime(200)).subscribe(e => {
2439
+ this.update();
2440
+ if (this.cd) {
2441
+ this.cd.markForCheck();
2442
+ }
2443
+ });
2444
+ this.resizeSubscription = subscription;
2445
+ }
2446
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GraphComponent, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: LayoutService }], target: i0.ɵɵFactoryTarget.Component });
2447
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.1", type: GraphComponent, isStandalone: false, selector: "ngx-graph", inputs: { nodes: "nodes", clusters: "clusters", compoundNodes: "compoundNodes", links: "links", activeEntries: "activeEntries", curve: "curve", draggingEnabled: "draggingEnabled", nodeHeight: "nodeHeight", nodeMaxHeight: "nodeMaxHeight", nodeMinHeight: "nodeMinHeight", nodeWidth: "nodeWidth", nodeMinWidth: "nodeMinWidth", nodeMaxWidth: "nodeMaxWidth", panningEnabled: "panningEnabled", panningAxis: "panningAxis", enableZoom: "enableZoom", zoomSpeed: "zoomSpeed", minZoomLevel: "minZoomLevel", maxZoomLevel: "maxZoomLevel", autoZoom: "autoZoom", panOnZoom: "panOnZoom", animate: "animate", autoCenter: "autoCenter", update$: "update$", center$: "center$", zoomToFit$: "zoomToFit$", panToNode$: "panToNode$", layout: "layout", layoutSettings: "layoutSettings", enableTrackpadSupport: "enableTrackpadSupport", showMiniMap: "showMiniMap", miniMapMaxWidth: "miniMapMaxWidth", miniMapMaxHeight: "miniMapMaxHeight", miniMapPosition: "miniMapPosition", view: "view", scheme: "scheme", customColors: "customColors", deferDisplayUntilPosition: "deferDisplayUntilPosition", centerNodesOnPositionChange: "centerNodesOnPositionChange", enablePreUpdateTransform: "enablePreUpdateTransform", groupResultsBy: "groupResultsBy", zoomLevel: "zoomLevel", panOffsetX: "panOffsetX", panOffsetY: "panOffsetY" }, outputs: { select: "select", activate: "activate", deactivate: "deactivate", zoomChange: "zoomChange", clickHandler: "clickHandler", stateChange: "stateChange" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mousedown": "onMouseDown($event)", "document:click": "graphClick($event)", "document:touchmove": "onTouchMove($event)", "document:mouseup": "onMouseUp($event)" } }, queries: [{ propertyName: "linkTemplate", first: true, predicate: ["linkTemplate"], descendants: true }, { propertyName: "nodeTemplate", first: true, predicate: ["nodeTemplate"], descendants: true }, { propertyName: "clusterTemplate", first: true, predicate: ["clusterTemplate"], descendants: true }, { propertyName: "defsTemplate", first: true, predicate: ["defsTemplate"], descendants: true }, { propertyName: "miniMapNodeTemplate", first: true, predicate: ["miniMapNodeTemplate"], descendants: true }], viewQueries: [{ propertyName: "nodeElements", predicate: ["nodeElement"], descendants: true }, { propertyName: "linkElements", predicate: ["linkElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"ngx-graph-outer\"\n [style.width.px]=\"width\"\n [@animationState]=\"'active'\"\n [@.disabled]=\"!animate\"\n (mouseWheelUp)=\"onZoom($event, 'in')\"\n (mouseWheelDown)=\"onZoom($event, 'out')\"\n mouseWheel\n>\n <svg:svg class=\"ngx-graph\" [attr.width]=\"width\" [attr.height]=\"height\">\n @if (initialized && graph) {\n <svg:g\n [attr.transform]=\"transform\"\n (touchstart)=\"onTouchStart($event)\"\n (touchend)=\"onTouchEnd()\"\n class=\"graph chart\"\n >\n <defs>\n @if (defsTemplate) {\n <ng-container [ngTemplateOutlet]=\"defsTemplate\"></ng-container>\n } @for (link of graph.edges; track link) {\n <svg:path class=\"text-path\" [attr.d]=\"link.textPath\" [attr.id]=\"link.id\"></svg:path>\n }\n </defs>\n <svg:rect\n class=\"panning-rect\"\n [attr.width]=\"dims.width * 100\"\n [attr.height]=\"dims.height * 100\"\n [attr.transform]=\"'translate(' + (-dims.width || 0) * 50 + ',' + (-dims.height || 0) * 50 + ')'\"\n (mousedown)=\"isPanning = true\"\n />\n <ng-content></ng-content>\n <svg:g class=\"clusters\">\n @for (node of graph.clusters; track trackNodeBy($index, node)) {\n <svg:g\n #clusterElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldClusters.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n (click)=\"onClick(node)\"\n >\n @if (clusterTemplate && !node.hidden) {\n <ng-container\n [ngTemplateOutlet]=\"clusterTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!clusterTemplate) {\n <svg:g class=\"node cluster\">\n <svg:rect\n [attr.width]=\"node.dimension.width\"\n [attr.height]=\"node.dimension.height\"\n [attr.fill]=\"node.data?.color\"\n />\n <svg:text alignment-baseline=\"central\" [attr.x]=\"10\" [attr.y]=\"node.dimension.height / 2\">\n {{ node.label }}\n </svg:text>\n </svg:g>\n }\n </svg:g>\n }\n </svg:g>\n <svg:g class=\"compound-nodes\">\n @for (node of graph.compoundNodes; track trackNodeBy($index, node)) {\n <svg:g\n #nodeElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldCompoundNodes.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n (click)=\"onClick(node)\"\n (mousedown)=\"onNodeMouseDown($event, node)\"\n >\n @if (nodeTemplate && !node.hidden) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!nodeTemplate) {\n <svg:g class=\"node compound-node\">\n <svg:rect\n [attr.width]=\"node.dimension.width\"\n [attr.height]=\"node.dimension.height\"\n [attr.fill]=\"node.data?.color\"\n />\n <svg:text alignment-baseline=\"central\" [attr.x]=\"10\" [attr.y]=\"node.dimension.height / 2\">\n {{ node.label }}\n </svg:text>\n </svg:g>\n }\n </svg:g>\n }\n </svg:g>\n <svg:g class=\"links\">\n @for (link of graph.edges; track trackLinkBy($index, link)) {\n <svg:g #linkElement class=\"link-group\" [id]=\"link.id\">\n @if (linkTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"linkTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: link }\"\n ></ng-container>\n } @if (!linkTemplate) {\n <svg:path class=\"edge\" [attr.d]=\"link.line\" />\n }\n </svg:g>\n }\n </svg:g>\n <svg:g class=\"nodes\" #nodeGroup>\n @for (node of graph.nodes; track trackNodeBy($index, node)) {\n <svg:g\n #nodeElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldNodes.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n (click)=\"onClick(node)\"\n (mousedown)=\"onNodeMouseDown($event, node)\"\n >\n @if (nodeTemplate && !node.hidden) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!nodeTemplate) {\n <svg:circle\n r=\"10\"\n [attr.cx]=\"node.dimension.width / 2\"\n [attr.cy]=\"node.dimension.height / 2\"\n [attr.fill]=\"node.data?.color\"\n />\n }\n </svg:g>\n }\n </svg:g>\n </svg:g>\n }\n\n <svg:clipPath [attr.id]=\"minimapClipPathId\">\n <svg:rect\n [attr.width]=\"graphDims.width / minimapScaleCoefficient\"\n [attr.height]=\"graphDims.height / minimapScaleCoefficient\"\n ></svg:rect>\n </svg:clipPath>\n\n @if (showMiniMap) {\n <svg:g class=\"minimap\" [attr.transform]=\"minimapTransform\" [attr.clip-path]=\"'url(#' + minimapClipPathId + ')'\">\n <svg:rect\n class=\"minimap-background\"\n [attr.width]=\"graphDims.width / minimapScaleCoefficient\"\n [attr.height]=\"graphDims.height / minimapScaleCoefficient\"\n (mousedown)=\"onMinimapPanTo($event)\"\n ></svg:rect>\n <svg:g\n [style.transform]=\"\n 'translate(' +\n -minimapOffsetX / minimapScaleCoefficient +\n 'px,' +\n -minimapOffsetY / minimapScaleCoefficient +\n 'px)'\n \"\n >\n <svg:g class=\"minimap-nodes\" [style.transform]=\"'scale(' + 1 / minimapScaleCoefficient + ')'\">\n @for (node of graph.nodes; track trackNodeBy($index, node)) {\n <svg:g\n #nodeElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldNodes.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n >\n @if (miniMapNodeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"miniMapNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!miniMapNodeTemplate && nodeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!nodeTemplate && !miniMapNodeTemplate) {\n <svg:circle\n r=\"10\"\n [attr.cx]=\"node.dimension.width / 2 / minimapScaleCoefficient\"\n [attr.cy]=\"node.dimension.height / 2 / minimapScaleCoefficient\"\n [attr.fill]=\"node.data?.color\"\n />\n }\n </svg:g>\n }\n </svg:g>\n <svg:rect\n [attr.transform]=\"\n 'translate(' +\n panOffsetX / zoomLevel / -minimapScaleCoefficient +\n ',' +\n panOffsetY / zoomLevel / -minimapScaleCoefficient +\n ')'\n \"\n class=\"minimap-drag\"\n [class.panning]=\"isMinimapPanning\"\n [attr.width]=\"width / minimapScaleCoefficient / zoomLevel\"\n [attr.height]=\"height / minimapScaleCoefficient / zoomLevel\"\n (mousedown)=\"onMinimapDragMouseDown()\"\n ></svg:rect>\n </svg:g>\n </svg:g>\n }\n </svg:svg>\n</div>\n", styles: [".minimap .minimap-background{fill:#0000001a}.minimap .minimap-drag{fill:#0003;stroke:#fff;stroke-width:1px;stroke-dasharray:2px;stroke-dashoffset:2px;cursor:pointer}.minimap .minimap-drag.panning{fill:#0000004d}.minimap .minimap-nodes{opacity:.5;pointer-events:none}.graph{-webkit-user-select:none;user-select:none}.graph .edge{stroke:#666;fill:none}.graph .edge .edge-label{stroke:none;font-size:12px;fill:#251e1e}.graph .panning-rect{fill:#0000;cursor:move}.graph .node-group.old-node{transition:transform .5s ease-in-out}.graph .node-group .node:focus{outline:none}.graph .compound-node rect{opacity:.5}.graph .cluster rect{opacity:.2}\n"], dependencies: [{ kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: MouseWheelDirective, selector: "[mouseWheel]", outputs: ["mouseWheelUp", "mouseWheelDown"] }], animations: [
2448
+ trigger('animationState', [
2449
+ transition(':enter', [style({ opacity: 0 }), animate('500ms 100ms', style({ opacity: 1 }))])
2450
+ ])
2451
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2452
+ }
2453
+ __decorate([
2454
+ throttleable(500)
2455
+ ], GraphComponent.prototype, "updateMinimap", null);
2456
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GraphComponent, decorators: [{
2457
+ type: Component,
2458
+ args: [{ selector: 'ngx-graph', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, animations: [
2459
+ trigger('animationState', [
2460
+ transition(':enter', [style({ opacity: 0 }), animate('500ms 100ms', style({ opacity: 1 }))])
2461
+ ])
2462
+ ], standalone: false, template: "<div\n class=\"ngx-graph-outer\"\n [style.width.px]=\"width\"\n [@animationState]=\"'active'\"\n [@.disabled]=\"!animate\"\n (mouseWheelUp)=\"onZoom($event, 'in')\"\n (mouseWheelDown)=\"onZoom($event, 'out')\"\n mouseWheel\n>\n <svg:svg class=\"ngx-graph\" [attr.width]=\"width\" [attr.height]=\"height\">\n @if (initialized && graph) {\n <svg:g\n [attr.transform]=\"transform\"\n (touchstart)=\"onTouchStart($event)\"\n (touchend)=\"onTouchEnd()\"\n class=\"graph chart\"\n >\n <defs>\n @if (defsTemplate) {\n <ng-container [ngTemplateOutlet]=\"defsTemplate\"></ng-container>\n } @for (link of graph.edges; track link) {\n <svg:path class=\"text-path\" [attr.d]=\"link.textPath\" [attr.id]=\"link.id\"></svg:path>\n }\n </defs>\n <svg:rect\n class=\"panning-rect\"\n [attr.width]=\"dims.width * 100\"\n [attr.height]=\"dims.height * 100\"\n [attr.transform]=\"'translate(' + (-dims.width || 0) * 50 + ',' + (-dims.height || 0) * 50 + ')'\"\n (mousedown)=\"isPanning = true\"\n />\n <ng-content></ng-content>\n <svg:g class=\"clusters\">\n @for (node of graph.clusters; track trackNodeBy($index, node)) {\n <svg:g\n #clusterElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldClusters.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n (click)=\"onClick(node)\"\n >\n @if (clusterTemplate && !node.hidden) {\n <ng-container\n [ngTemplateOutlet]=\"clusterTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!clusterTemplate) {\n <svg:g class=\"node cluster\">\n <svg:rect\n [attr.width]=\"node.dimension.width\"\n [attr.height]=\"node.dimension.height\"\n [attr.fill]=\"node.data?.color\"\n />\n <svg:text alignment-baseline=\"central\" [attr.x]=\"10\" [attr.y]=\"node.dimension.height / 2\">\n {{ node.label }}\n </svg:text>\n </svg:g>\n }\n </svg:g>\n }\n </svg:g>\n <svg:g class=\"compound-nodes\">\n @for (node of graph.compoundNodes; track trackNodeBy($index, node)) {\n <svg:g\n #nodeElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldCompoundNodes.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n (click)=\"onClick(node)\"\n (mousedown)=\"onNodeMouseDown($event, node)\"\n >\n @if (nodeTemplate && !node.hidden) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!nodeTemplate) {\n <svg:g class=\"node compound-node\">\n <svg:rect\n [attr.width]=\"node.dimension.width\"\n [attr.height]=\"node.dimension.height\"\n [attr.fill]=\"node.data?.color\"\n />\n <svg:text alignment-baseline=\"central\" [attr.x]=\"10\" [attr.y]=\"node.dimension.height / 2\">\n {{ node.label }}\n </svg:text>\n </svg:g>\n }\n </svg:g>\n }\n </svg:g>\n <svg:g class=\"links\">\n @for (link of graph.edges; track trackLinkBy($index, link)) {\n <svg:g #linkElement class=\"link-group\" [id]=\"link.id\">\n @if (linkTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"linkTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: link }\"\n ></ng-container>\n } @if (!linkTemplate) {\n <svg:path class=\"edge\" [attr.d]=\"link.line\" />\n }\n </svg:g>\n }\n </svg:g>\n <svg:g class=\"nodes\" #nodeGroup>\n @for (node of graph.nodes; track trackNodeBy($index, node)) {\n <svg:g\n #nodeElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldNodes.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n (click)=\"onClick(node)\"\n (mousedown)=\"onNodeMouseDown($event, node)\"\n >\n @if (nodeTemplate && !node.hidden) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!nodeTemplate) {\n <svg:circle\n r=\"10\"\n [attr.cx]=\"node.dimension.width / 2\"\n [attr.cy]=\"node.dimension.height / 2\"\n [attr.fill]=\"node.data?.color\"\n />\n }\n </svg:g>\n }\n </svg:g>\n </svg:g>\n }\n\n <svg:clipPath [attr.id]=\"minimapClipPathId\">\n <svg:rect\n [attr.width]=\"graphDims.width / minimapScaleCoefficient\"\n [attr.height]=\"graphDims.height / minimapScaleCoefficient\"\n ></svg:rect>\n </svg:clipPath>\n\n @if (showMiniMap) {\n <svg:g class=\"minimap\" [attr.transform]=\"minimapTransform\" [attr.clip-path]=\"'url(#' + minimapClipPathId + ')'\">\n <svg:rect\n class=\"minimap-background\"\n [attr.width]=\"graphDims.width / minimapScaleCoefficient\"\n [attr.height]=\"graphDims.height / minimapScaleCoefficient\"\n (mousedown)=\"onMinimapPanTo($event)\"\n ></svg:rect>\n <svg:g\n [style.transform]=\"\n 'translate(' +\n -minimapOffsetX / minimapScaleCoefficient +\n 'px,' +\n -minimapOffsetY / minimapScaleCoefficient +\n 'px)'\n \"\n >\n <svg:g class=\"minimap-nodes\" [style.transform]=\"'scale(' + 1 / minimapScaleCoefficient + ')'\">\n @for (node of graph.nodes; track trackNodeBy($index, node)) {\n <svg:g\n #nodeElement\n class=\"node-group\"\n [class.old-node]=\"animate && oldNodes.has(node.id)\"\n [id]=\"node.id\"\n [attr.transform]=\"node.transform\"\n >\n @if (miniMapNodeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"miniMapNodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!miniMapNodeTemplate && nodeTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"nodeTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: node }\"\n ></ng-container>\n } @if (!nodeTemplate && !miniMapNodeTemplate) {\n <svg:circle\n r=\"10\"\n [attr.cx]=\"node.dimension.width / 2 / minimapScaleCoefficient\"\n [attr.cy]=\"node.dimension.height / 2 / minimapScaleCoefficient\"\n [attr.fill]=\"node.data?.color\"\n />\n }\n </svg:g>\n }\n </svg:g>\n <svg:rect\n [attr.transform]=\"\n 'translate(' +\n panOffsetX / zoomLevel / -minimapScaleCoefficient +\n ',' +\n panOffsetY / zoomLevel / -minimapScaleCoefficient +\n ')'\n \"\n class=\"minimap-drag\"\n [class.panning]=\"isMinimapPanning\"\n [attr.width]=\"width / minimapScaleCoefficient / zoomLevel\"\n [attr.height]=\"height / minimapScaleCoefficient / zoomLevel\"\n (mousedown)=\"onMinimapDragMouseDown()\"\n ></svg:rect>\n </svg:g>\n </svg:g>\n }\n </svg:svg>\n</div>\n", styles: [".minimap .minimap-background{fill:#0000001a}.minimap .minimap-drag{fill:#0003;stroke:#fff;stroke-width:1px;stroke-dasharray:2px;stroke-dashoffset:2px;cursor:pointer}.minimap .minimap-drag.panning{fill:#0000004d}.minimap .minimap-nodes{opacity:.5;pointer-events:none}.graph{-webkit-user-select:none;user-select:none}.graph .edge{stroke:#666;fill:none}.graph .edge .edge-label{stroke:none;font-size:12px;fill:#251e1e}.graph .panning-rect{fill:#0000;cursor:move}.graph .node-group.old-node{transition:transform .5s ease-in-out}.graph .node-group .node:focus{outline:none}.graph .compound-node rect{opacity:.5}.graph .cluster rect{opacity:.2}\n"] }]
2463
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: LayoutService }], propDecorators: { nodes: [{
2464
+ type: Input
2465
+ }], clusters: [{
2466
+ type: Input
2467
+ }], compoundNodes: [{
2468
+ type: Input
2469
+ }], links: [{
2470
+ type: Input
2471
+ }], activeEntries: [{
2472
+ type: Input
2473
+ }], curve: [{
2474
+ type: Input
2475
+ }], draggingEnabled: [{
2476
+ type: Input
2477
+ }], nodeHeight: [{
2478
+ type: Input
2479
+ }], nodeMaxHeight: [{
2480
+ type: Input
2481
+ }], nodeMinHeight: [{
2482
+ type: Input
2483
+ }], nodeWidth: [{
2484
+ type: Input
2485
+ }], nodeMinWidth: [{
2486
+ type: Input
2487
+ }], nodeMaxWidth: [{
2488
+ type: Input
2489
+ }], panningEnabled: [{
2490
+ type: Input
2491
+ }], panningAxis: [{
2492
+ type: Input
2493
+ }], enableZoom: [{
2494
+ type: Input
2495
+ }], zoomSpeed: [{
2496
+ type: Input
2497
+ }], minZoomLevel: [{
2498
+ type: Input
2499
+ }], maxZoomLevel: [{
2500
+ type: Input
2501
+ }], autoZoom: [{
2502
+ type: Input
2503
+ }], panOnZoom: [{
2504
+ type: Input
2505
+ }], animate: [{
2506
+ type: Input
2507
+ }], autoCenter: [{
2508
+ type: Input
2509
+ }], update$: [{
2510
+ type: Input
2511
+ }], center$: [{
2512
+ type: Input
2513
+ }], zoomToFit$: [{
2514
+ type: Input
2515
+ }], panToNode$: [{
2516
+ type: Input
2517
+ }], layout: [{
2518
+ type: Input
2519
+ }], layoutSettings: [{
2520
+ type: Input
2521
+ }], enableTrackpadSupport: [{
2522
+ type: Input
2523
+ }], showMiniMap: [{
2524
+ type: Input
2525
+ }], miniMapMaxWidth: [{
2526
+ type: Input
2527
+ }], miniMapMaxHeight: [{
2528
+ type: Input
2529
+ }], miniMapPosition: [{
2530
+ type: Input
2531
+ }], view: [{
2532
+ type: Input
2533
+ }], scheme: [{
2534
+ type: Input
2535
+ }], customColors: [{
2536
+ type: Input
2537
+ }], deferDisplayUntilPosition: [{
2538
+ type: Input
2539
+ }], centerNodesOnPositionChange: [{
2540
+ type: Input
2541
+ }], enablePreUpdateTransform: [{
2542
+ type: Input
2543
+ }], select: [{
2544
+ type: Output
2545
+ }], activate: [{
2546
+ type: Output
2547
+ }], deactivate: [{
2548
+ type: Output
2549
+ }], zoomChange: [{
2550
+ type: Output
2551
+ }], clickHandler: [{
2552
+ type: Output
2553
+ }], stateChange: [{
2554
+ type: Output
2555
+ }], linkTemplate: [{
2556
+ type: ContentChild,
2557
+ args: ['linkTemplate']
2558
+ }], nodeTemplate: [{
2559
+ type: ContentChild,
2560
+ args: ['nodeTemplate']
2561
+ }], clusterTemplate: [{
2562
+ type: ContentChild,
2563
+ args: ['clusterTemplate']
2564
+ }], defsTemplate: [{
2565
+ type: ContentChild,
2566
+ args: ['defsTemplate']
2567
+ }], miniMapNodeTemplate: [{
2568
+ type: ContentChild,
2569
+ args: ['miniMapNodeTemplate']
2570
+ }], nodeElements: [{
2571
+ type: ViewChildren,
2572
+ args: ['nodeElement']
2573
+ }], linkElements: [{
2574
+ type: ViewChildren,
2575
+ args: ['linkElement']
2576
+ }], groupResultsBy: [{
2577
+ type: Input
2578
+ }], zoomLevel: [{
2579
+ type: Input,
2580
+ args: ['zoomLevel']
2581
+ }], panOffsetX: [{
2582
+ type: Input,
2583
+ args: ['panOffsetX']
2584
+ }], panOffsetY: [{
2585
+ type: Input,
2586
+ args: ['panOffsetY']
2587
+ }], updateMinimap: [], onMouseMove: [{
2588
+ type: HostListener,
2589
+ args: ['document:mousemove', ['$event']]
2590
+ }], onMouseDown: [{
2591
+ type: HostListener,
2592
+ args: ['document:mousedown', ['$event']]
2593
+ }], graphClick: [{
2594
+ type: HostListener,
2595
+ args: ['document:click', ['$event']]
2596
+ }], onTouchMove: [{
2597
+ type: HostListener,
2598
+ args: ['document:touchmove', ['$event']]
2599
+ }], onMouseUp: [{
2600
+ type: HostListener,
2601
+ args: ['document:mouseup', ['$event']]
2602
+ }] } });
2603
+
2604
+ class GraphModule {
2605
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GraphModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2606
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.1", ngImport: i0, type: GraphModule, declarations: [GraphComponent, MouseWheelDirective, VisibilityObserver], imports: [CommonModule], exports: [GraphComponent, MouseWheelDirective] });
2607
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GraphModule, providers: [LayoutService], imports: [CommonModule] });
2608
+ }
2609
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: GraphModule, decorators: [{
2610
+ type: NgModule,
2611
+ args: [{
2612
+ imports: [CommonModule],
2613
+ declarations: [GraphComponent, MouseWheelDirective, VisibilityObserver],
2614
+ exports: [GraphComponent, MouseWheelDirective],
2615
+ providers: [LayoutService]
2616
+ }]
2617
+ }] });
2618
+
2619
+ class NgxGraphModule {
2620
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxGraphModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2621
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.1", ngImport: i0, type: NgxGraphModule, imports: [CommonModule], exports: [GraphModule] });
2622
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxGraphModule, imports: [CommonModule, GraphModule] });
2623
+ }
2624
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: NgxGraphModule, decorators: [{
2625
+ type: NgModule,
2626
+ args: [{
2627
+ imports: [CommonModule],
2628
+ exports: [GraphModule]
2629
+ }]
2630
+ }] });
2631
+
2632
+ /*
2633
+ * Public API Surface of ngx-graph
2634
+ */
2635
+
2636
+ /**
2637
+ * Generated bundle index. Do not edit.
2638
+ */
2639
+
2640
+ export { Alignment, ColaForceDirectedLayout, D3ForceDirectedLayout, DagreClusterLayout, DagreLayout, DagreNodesOnlyLayout, GraphComponent, GraphModule, LayoutService, MiniMapPosition, MouseWheelDirective, NgxGraphModule, NgxGraphStates, Orientation, PanningAxis, toD3Node, toNode };
2641
+ //# sourceMappingURL=onealmonddotcom-ngx-graph.mjs.map