@memberjunction/ng-graph-view 6.1.4 → 6.2.0-edge.1
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.
|
@@ -53,10 +53,10 @@ export declare class GraphViewComponent implements OnInit, OnChanges, OnDestroy
|
|
|
53
53
|
ngOnInit(): void;
|
|
54
54
|
ngOnChanges(changes: SimpleChanges): void;
|
|
55
55
|
ngOnDestroy(): void;
|
|
56
|
-
private
|
|
56
|
+
private initializePositions;
|
|
57
57
|
SimulatePhysics(): void;
|
|
58
|
-
private
|
|
59
|
-
private
|
|
58
|
+
private syncCoordinates;
|
|
59
|
+
private applyCircularLayout;
|
|
60
60
|
ZoomIn(): void;
|
|
61
61
|
ZoomOut(): void;
|
|
62
62
|
FitToView(): void;
|
|
@@ -75,10 +75,10 @@ export declare class GraphViewComponent implements OnInit, OnChanges, OnDestroy
|
|
|
75
75
|
OnCanvasMouseUp(_event: MouseEvent): void;
|
|
76
76
|
OnCanvasWheel(event: WheelEvent): void;
|
|
77
77
|
OnNodeMouseDown(node: GraphNode, event: MouseEvent): void;
|
|
78
|
-
private
|
|
79
|
-
private
|
|
80
|
-
private
|
|
81
|
-
private
|
|
78
|
+
private emitViewport;
|
|
79
|
+
private getCanvasRelativeCoords;
|
|
80
|
+
private getContainerWidth;
|
|
81
|
+
private getContainerHeight;
|
|
82
82
|
ApplyFilters(): void;
|
|
83
83
|
GetNodeX(id: string): number;
|
|
84
84
|
GetNodeY(id: string): number;
|
|
@@ -551,13 +551,13 @@ export class GraphViewComponent {
|
|
|
551
551
|
draggedNode = null;
|
|
552
552
|
simulation = null;
|
|
553
553
|
ngOnInit() {
|
|
554
|
-
this.
|
|
554
|
+
this.initializePositions();
|
|
555
555
|
this.ApplyFilters();
|
|
556
556
|
this.SimulatePhysics();
|
|
557
557
|
}
|
|
558
558
|
ngOnChanges(changes) {
|
|
559
559
|
if (changes['Nodes'] || changes['Edges']) {
|
|
560
|
-
this.
|
|
560
|
+
this.initializePositions();
|
|
561
561
|
this.ApplyFilters();
|
|
562
562
|
this.SimulatePhysics();
|
|
563
563
|
}
|
|
@@ -575,9 +575,9 @@ export class GraphViewComponent {
|
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
// ── Positioning & D3 Physics Engine ──────────────────────────────────
|
|
578
|
-
|
|
579
|
-
const cx = this.
|
|
580
|
-
const cy = this.
|
|
578
|
+
initializePositions() {
|
|
579
|
+
const cx = this.getContainerWidth() / 2 || 400;
|
|
580
|
+
const cy = this.getContainerHeight() / 2 || 250;
|
|
581
581
|
const count = this.Nodes.length || 1;
|
|
582
582
|
this.Nodes.forEach((node, idx) => {
|
|
583
583
|
if (node.x === undefined || node.y === undefined) {
|
|
@@ -602,7 +602,7 @@ export class GraphViewComponent {
|
|
|
602
602
|
}
|
|
603
603
|
SimulatePhysics() {
|
|
604
604
|
if (this.LayoutMode === 'circular') {
|
|
605
|
-
this.
|
|
605
|
+
this.applyCircularLayout();
|
|
606
606
|
if (this.AutoFitOnLoad) {
|
|
607
607
|
this.AutoFitToView();
|
|
608
608
|
}
|
|
@@ -613,8 +613,8 @@ export class GraphViewComponent {
|
|
|
613
613
|
this.simulation.stop();
|
|
614
614
|
this.simulation = null;
|
|
615
615
|
}
|
|
616
|
-
const cx = this.
|
|
617
|
-
const cy = this.
|
|
616
|
+
const cx = this.getContainerWidth() / 2 || 400;
|
|
617
|
+
const cy = this.getContainerHeight() / 2 || 250;
|
|
618
618
|
// Resolve endpoints to node objects so a case-skewed UUID or dangling
|
|
619
619
|
// edge cannot throw "node not found" out of ngOnInit. Raw-string
|
|
620
620
|
// .id(d => d.ID) compared with ===, which UUIDsEqual elsewhere
|
|
@@ -647,19 +647,19 @@ export class GraphViewComponent {
|
|
|
647
647
|
for (let i = 0; i < 90; ++i) {
|
|
648
648
|
this.simulation.tick();
|
|
649
649
|
}
|
|
650
|
-
this.
|
|
650
|
+
this.syncCoordinates();
|
|
651
651
|
// Auto-center & fit into view
|
|
652
652
|
if (this.AutoFitOnLoad) {
|
|
653
653
|
this.AutoFitToView();
|
|
654
654
|
}
|
|
655
655
|
// Attach live tick handler for interactive drag & drop
|
|
656
656
|
this.simulation.on('tick', () => {
|
|
657
|
-
this.
|
|
657
|
+
this.syncCoordinates();
|
|
658
658
|
this.cdr.markForCheck();
|
|
659
659
|
});
|
|
660
660
|
this.cdr.markForCheck();
|
|
661
661
|
}
|
|
662
|
-
|
|
662
|
+
syncCoordinates() {
|
|
663
663
|
for (const n of this.Nodes) {
|
|
664
664
|
n.X = n.x;
|
|
665
665
|
n.Y = n.y;
|
|
@@ -667,9 +667,9 @@ export class GraphViewComponent {
|
|
|
667
667
|
n.VY = n.vy;
|
|
668
668
|
}
|
|
669
669
|
}
|
|
670
|
-
|
|
671
|
-
const cx = this.
|
|
672
|
-
const cy = this.
|
|
670
|
+
applyCircularLayout() {
|
|
671
|
+
const cx = this.getContainerWidth() / 2 || 400;
|
|
672
|
+
const cy = this.getContainerHeight() / 2 || 250;
|
|
673
673
|
const r = Math.min(cx, cy) * 0.7 || 160;
|
|
674
674
|
const count = this.Nodes.length || 1;
|
|
675
675
|
this.Nodes.forEach((node, idx) => {
|
|
@@ -683,11 +683,11 @@ export class GraphViewComponent {
|
|
|
683
683
|
// ── Public Programmatic Methods ───────────────────────────────────
|
|
684
684
|
ZoomIn() {
|
|
685
685
|
this.Scale = Math.min(this.Scale * 1.2, 3.0);
|
|
686
|
-
this.
|
|
686
|
+
this.emitViewport();
|
|
687
687
|
}
|
|
688
688
|
ZoomOut() {
|
|
689
689
|
this.Scale = Math.max(this.Scale / 1.2, 0.3);
|
|
690
|
-
this.
|
|
690
|
+
this.emitViewport();
|
|
691
691
|
}
|
|
692
692
|
FitToView() {
|
|
693
693
|
this.AutoFitToView(40);
|
|
@@ -695,8 +695,8 @@ export class GraphViewComponent {
|
|
|
695
695
|
AutoFitToView(padding = 40) {
|
|
696
696
|
if (!this.Nodes || this.Nodes.length === 0)
|
|
697
697
|
return;
|
|
698
|
-
const width = this.
|
|
699
|
-
const height = this.
|
|
698
|
+
const width = this.getContainerWidth() || 800;
|
|
699
|
+
const height = this.getContainerHeight() || 420;
|
|
700
700
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
701
701
|
for (const n of this.Nodes) {
|
|
702
702
|
const nx = n.x ?? n.X ?? 0;
|
|
@@ -721,11 +721,11 @@ export class GraphViewComponent {
|
|
|
721
721
|
this.Scale = targetScale;
|
|
722
722
|
this.PanX = (width - (minX + maxX) * this.Scale) / 2;
|
|
723
723
|
this.PanY = (height - (minY + maxY) * this.Scale) / 2;
|
|
724
|
-
this.
|
|
724
|
+
this.emitViewport();
|
|
725
725
|
this.cdr.markForCheck();
|
|
726
726
|
}
|
|
727
727
|
Rearrange() {
|
|
728
|
-
this.
|
|
728
|
+
this.initializePositions();
|
|
729
729
|
this.SimulatePhysics();
|
|
730
730
|
}
|
|
731
731
|
SetLayoutMode(mode) {
|
|
@@ -817,11 +817,11 @@ export class GraphViewComponent {
|
|
|
817
817
|
if (this.IsPanning) {
|
|
818
818
|
this.PanX = event.clientX - this.panStartX;
|
|
819
819
|
this.PanY = event.clientY - this.panStartY;
|
|
820
|
-
this.
|
|
820
|
+
this.emitViewport();
|
|
821
821
|
this.cdr.markForCheck();
|
|
822
822
|
}
|
|
823
823
|
else if (this.draggedNode) {
|
|
824
|
-
const coords = this.
|
|
824
|
+
const coords = this.getCanvasRelativeCoords(event);
|
|
825
825
|
this.draggedNode.fx = coords.x;
|
|
826
826
|
this.draggedNode.fy = coords.y;
|
|
827
827
|
this.draggedNode.x = coords.x;
|
|
@@ -846,7 +846,7 @@ export class GraphViewComponent {
|
|
|
846
846
|
event.preventDefault();
|
|
847
847
|
const delta = event.deltaY > 0 ? 0.9 : 1.1;
|
|
848
848
|
this.Scale = Math.min(Math.max(this.Scale * delta, 0.3), 3.0);
|
|
849
|
-
this.
|
|
849
|
+
this.emitViewport();
|
|
850
850
|
this.cdr.markForCheck();
|
|
851
851
|
}
|
|
852
852
|
OnNodeMouseDown(node, event) {
|
|
@@ -858,11 +858,11 @@ export class GraphViewComponent {
|
|
|
858
858
|
this.simulation.alphaTarget(0.3).restart();
|
|
859
859
|
}
|
|
860
860
|
}
|
|
861
|
-
|
|
861
|
+
emitViewport() {
|
|
862
862
|
this.ViewportTransform.emit(new ViewportTransformEventArgs({ Scale: this.Scale, PanX: this.PanX, PanY: this.PanY }));
|
|
863
863
|
this.cdr.markForCheck();
|
|
864
864
|
}
|
|
865
|
-
|
|
865
|
+
getCanvasRelativeCoords(event) {
|
|
866
866
|
const canvas = this.CanvasRef?.nativeElement;
|
|
867
867
|
if (!canvas) {
|
|
868
868
|
return {
|
|
@@ -878,10 +878,10 @@ export class GraphViewComponent {
|
|
|
878
878
|
y: (mouseY - this.PanY) / this.Scale
|
|
879
879
|
};
|
|
880
880
|
}
|
|
881
|
-
|
|
881
|
+
getContainerWidth() {
|
|
882
882
|
return this.CanvasRef?.nativeElement?.clientWidth || this.WrapperRef?.nativeElement?.clientWidth || 800;
|
|
883
883
|
}
|
|
884
|
-
|
|
884
|
+
getContainerHeight() {
|
|
885
885
|
return this.CanvasRef?.nativeElement?.clientHeight || this.WrapperRef?.nativeElement?.clientHeight || 420;
|
|
886
886
|
}
|
|
887
887
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-view.component.js","sourceRoot":"","sources":["../../../src/lib/components/graph-view.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,KAAK,EACL,MAAM,EACN,YAAY,EAKZ,uBAAuB,EACvB,iBAAiB,EAEjB,SAAS,EACT,MAAM,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAUzB,OAAO,EACH,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,0BAA0B,EAC7B,MAAM,wBAAwB,CAAC;;;;;;;;;IAgVR,0BAAsC;IACtC,+BAAiC;IAC7B,wBAAwK;IACxK,iCAI0C;IAFtC,gUAAyB;IACzB,2NAAiB,0BAAmB,KAAC;IAE7C,AALI,iBAI0C,EACxC;;;IAHE,eAAyB;IAAzB,kDAAyB;;;;IA/CrC,AADJ,8BAA8B,cACkF;IACxG,uBAAgF;IAChF,+BACJ;IAAA,iBAAO;IAEP,0BAAsC;IAGtC,kCAKmC;IAD/B,sLAAS,qBAAc,OAAO,CAAC,KAAC;IAEhC,wBAAgC;IAAC,uBACrC;IAAA,iBAAS;IACT,kCAK4B;IADxB,sLAAS,qBAAc,UAAU,CAAC,KAAC;IAEnC,wBAAwC;IAAC,2BAC7C;IAAA,iBAAS;IAET,2BAAsC;IAGtC,mCAAmF;IAAnC,uLAAS,eAAQ,KAAC;IAC9D,yBAAiD;IACrD,iBAAS;IACT,mCAAqF;IAArC,uLAAS,gBAAS,KAAC;IAC/D,yBAAkD;IACtD,iBAAS;IACT,mCAAuF;IAAvC,uLAAS,kBAAW,KAAC;IACjE,yBAAkC;IACtC,iBAAS;IACT,mCAAkG;IAAlD,uLAAS,kBAAW,KAAC;IACjE,yBAAyC;IAC7C,iBAAS;IAET,0FAAkB;IAWtB,iBAAM;;;IAzCE,eAAuC;IAAvC,uDAAuC;IAQvC,eAA0C;IAA1C,0DAA0C;IAsB9C,gBAUC;IAVD,6CAUC;;;IAQG,+BAAkC;IAC9B,0BAAsE;IACtE,YACJ;IAAA,iBAAM;;;IAF+B,cAA8B;IAA9B,0CAA8B;IAC/D,cACJ;IADI,6CACJ;;;IALR,8BAA6B;IACzB,oGAKC;IACL,iBAAM;;;IANF,cAKC;IALD,sCAKC;;;IA2Be,gCAA2L;IAAA,8BAAc;IAAA,iBAAO;;;;IAYxN,kCAI6C;IAAzC,sMAAS,4CAA8B,KAAC;IACxC,wBAAsD;IAAC,kCAC3D;IAAA,iBAAS;;;;IArCb,AADJ,AADJ,8BAAgC,cACW,eACI;IAAA,8BAAc;IAAA,iBAAO;IAC5D,kCAAkF;IAA3B,sLAAS,uBAAgB,KAAC;IAC7E,wBAAiC;IAEzC,AADI,iBAAS,EACP;IAGF,AADJ,+BAAqC,cACsD;IACnF,oBAA2C;IAC/C,iBAAM;IAEF,AADJ,+BAAwC,eACyF;IACzH,aACJ;IAAA,iBAAM;IAEF,AADJ,gCAA4E,gBACR;IAC5D,aACJ;IAAA,iBAAO;IACP,sGAAiC;IAK7C,AADI,AADI,iBAAM,EACJ,EACJ;IAEN,gCAAoF;IAChF,0CAAwB;IAAA,+BAAQ;IAAA,aAAuC;IAC3E,AAD2E,iBAAS,EAC9E;IAEN,gCAAwC;IACpC,wGAAkC;IASlC,mCAI6C;IAAzC,uLAAS,0CAA4B,CAAC,CAAC,KAAC;IACxC,yBAAwC;IAAC,yCAC7C;IAER,AADI,AADI,iBAAS,EACP,EACJ;;;IAxCyC,eAA+C;IAA/C,sEAA+C;IAC/E,cAAmC;IAAnC,sDAAmC;IAIlC,eACJ;IADI,0DACJ;IAGQ,eACJ;IADI,6FACJ;IACA,cAEC;IAFD,mEAEC;IAMuB,eAAuC;IAAvC,qEAAuC;IAIvE,eAQC;IARD,oEAQC;;;;;IA2BG,AADJ,yBAAG,eAQyC;IAApC,4MAAS,mCAAyB,KAAC;IACvC,iBAAO;IACP,gCAG2E;IACvE,YACJ;IACJ,AADI,iBAAO,EACP;;;;IAbI,cAAgD;IAAhD,mEAAgD;;IAShD,cAAkE;;IAElE,cACJ;IADI,8CACJ;;;;IAeI,6BAAyF;;;;IAEzF,6BAA8G;;;;IAoB9G,gCAAmE;IAC/D,YACJ;IAAA,iBAAO;;;;;IADH,cACJ;IADI,4EACJ;;;;;IAjCR,6BAKwC;IAApC,AADA,AADA,mNAAa,wCAA6B,KAAC,8LAClC,oCAAyB,KAAC,8LACvB,iCAAsB,KAAC;IAKjC,AAFF,oGAA4B,8EAEI;IAchC,AATA,6BAMS,aAG4E;IAGrF,gCAAgE;IAC5D,YACJ;IAAA,iBAAO;IACP,kGAAqB;IAKzB,iBAAI;;;;;IA3BA,cAIC;IAJD,6FAIC;IAKG,eAA4B;;IAOxB,cAAkC;;IAGd,cAAmC;;IAC3D,cACJ;IADI,yEACJ;IACA,cAIC;IAJD,4CAIC;;AAle7B,MAAM,CAAC,MAAM,wBAAwB,GAA0B;IAC3D,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,EAAE;IACxF,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,EAAE;IACxG,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,EAAE;IAClG,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,2BAA2B,EAAE;IAC7G,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE;IACrF,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE;IACvF,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,0BAA0B,EAAE;CACnG,CAAC;AAEF,MAAM,wBAAwB,GAAG;IAC7B,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IACrD,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;CACxD,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmQtB,CAAC;AAEF;;;;;GAKG;AAkNH,MAAM,OAAO,kBAAkB;IACnB,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAEO,UAAU,CAA8B;IACtC,SAAS,CAA6B;IAEvF,qEAAqE;IACrD,KAAK,GAAgB,EAAE,CAAC;IACxB,KAAK,GAAgB,EAAE,CAAC;IACxB,UAAU,GAA0B,wBAAwB,CAAC;IAC7D,UAAU,GAAoB,OAAO,CAAC;IACtC,cAAc,GAAG,CAAC,CAAC;IACnB,cAAc,CAAU;IACxB,WAAW,CAAU;IACrB,iBAAiB,GAAG,KAAK,CAAC;IAC1B,WAAW,GAAG,IAAI,CAAC;IACnB,UAAU,GAAG,IAAI,CAAC;IAClB,aAAa,GAAG,IAAI,CAAC;IACrB,UAAU,GAAG,IAAI,CAAC;IAClB,aAAa,GAAG,IAAI,CAAC;IACrB,OAAO,GAAuB;QAC1C,SAAS,EAAE,CAAC,GAAG;QACf,YAAY,EAAE,GAAG;QACjB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,GAAG;KACrB,CAAC;IAEF,sEAAsE;IACrD,gBAAgB,GAAG,IAAI,YAAY,EAA6B,CAAC;IACjE,YAAY,GAAG,IAAI,YAAY,EAAyB,CAAC;IAEzD,gBAAgB,GAAG,IAAI,YAAY,EAA6B,CAAC;IACjE,YAAY,GAAG,IAAI,YAAY,EAAyB,CAAC;IAEzD,eAAe,GAAG,IAAI,YAAY,EAA4B,CAAC;IAC/D,WAAW,GAAG,IAAI,YAAY,EAAwB,CAAC;IAEvD,kBAAkB,GAAG,IAAI,YAAY,EAA+B,CAAC;IACrE,aAAa,GAAG,IAAI,YAAY,EAA0B,CAAC;IAE3D,kBAAkB,GAAG,IAAI,YAAY,EAA+B,CAAC;IACrE,aAAa,GAAG,IAAI,YAAY,EAA0B,CAAC;IAE3D,iBAAiB,GAAG,IAAI,YAAY,EAA8B,CAAC;IAEpF,qEAAqE;IAC9D,aAAa,GAAgB,EAAE,CAAC;IAChC,YAAY,GAAqB,IAAI,CAAC;IACtC,YAAY,GAAqB,IAAI,CAAC;IACtC,WAAW,GAAG,EAAE,CAAC;IAEjB,KAAK,GAAG,GAAG,CAAC;IACZ,IAAI,GAAG,GAAG,CAAC;IACX,IAAI,GAAG,GAAG,CAAC;IACX,SAAS,GAAG,KAAK,CAAC;IACjB,SAAS,GAAG,CAAC,CAAC;IACd,SAAS,GAAG,CAAC,CAAC;IAEd,WAAW,GAAqB,IAAI,CAAC;IACrC,UAAU,GAAuE,IAAI,CAAC;IAEvF,QAAQ;QACX,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3B,CAAC;IAEM,WAAW,CAAC,OAAsB;QACrC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,eAAe,EAAE,CAAC;QAC3B,CAAC;QACD,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;YAC1E,IAAI,KAAK,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAClC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC9B,CAAC;QACL,CAAC;IACL,CAAC;IAEM,WAAW;QACd,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;IACL,CAAC;IAED,wEAAwE;IAChE,mBAAmB;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAErC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7B,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC/C,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;oBAC/C,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACJ,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;oBAC1C,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;oBACnC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAClC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAClC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBACpB,CAAC;gBACD,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;YAChB,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEM,eAAe;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACjC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YACxB,OAAO;QACX,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAEhD,sEAAsE;QACtE,iEAAiE;QACjE,+DAA+D;QAC/D,yBAAyB;QACzB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,GAAG,EAAE,CAAC;gBACN,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC5B,CAAC;QACL,CAAC;QACD,MAAM,KAAK,GAAwC,EAAE,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1D,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YACnC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,eAAe,CAAY,IAAI,CAAC,KAAK,CAAC;aACtD,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAA+C,KAAK,CAAC;aAC3E,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC;aAC/C,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,aAAa,EAAa;aACzC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC;aACxC,WAAW,CAAC,GAAG,CAAC,CAAC;aACrB,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC,YAAY,CAAY,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;aACnG,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAY,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAExD,oGAAoG;QACpG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,8BAA8B;QAC9B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,EAAE,CAAC;QACzB,CAAC;QAED,uDAAuD;QACvD,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,eAAe;QACnB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACV,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACV,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACZ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QAChB,CAAC;IACL,CAAC;IAEO,mBAAmB;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAChD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAErC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7B,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YAChB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,qEAAqE;IAC9D,MAAM;QACT,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEM,OAAO;QACV,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEM,SAAS;QACZ,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAEM,aAAa,CAAC,OAAO,GAAG,EAAE;QAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEnD,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,CAAC;QAEhD,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC;QACzE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,yBAAyB;YAC1D,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QAErF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;QAEzC,MAAM,MAAM,GAAG,CAAC,KAAK,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAC7C,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAE5E,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEtD,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,SAAS;QACZ,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3B,CAAC;IAEM,aAAa,CAAC,IAAqB;QACtC,MAAM,WAAW,GAAG,IAAI,2BAA2B,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAEM,UAAU,CAAC,EAAU;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,IAAI,IAAI;YAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAEM,cAAc;QACjB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,WAAW,CAAC,KAAa;QAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEM,UAAU,CAAC,MAAc,EAAE,KAAa;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,MAAM,WAAW,GAAG,IAAI,wBAAwB,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;QAC/G,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACvC,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,oEAAoE;QACpE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACnG,CAAC;IAED,qEAAqE;IAC9D,WAAW,CAAC,IAAe,EAAE,KAAiB;QACjD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3E,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,WAAW,CAAC,IAAe,EAAE,KAAiB;QACjD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;QAC1E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;QAE1E,MAAM,WAAW,GAAG,IAAI,yBAAyB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAClE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,gBAAgB,CAAC,IAAe;QACnC,kEAAkE;QAClE,mEAAmE;QACnE,8BAA8B;QAC9B,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACxJ,MAAM,WAAW,GAAG,IAAI,2BAA2B,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;QAC7E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,mEAAmE;QACnE,2EAA2E;QAC3E,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,qEAAqE;IAC9D,iBAAiB,CAAC,KAAiB;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,aAAa,IAAK,KAAK,CAAC,MAAsB,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACnG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;YAC3C,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QAC/C,CAAC;IACL,CAAC;IAEM,iBAAiB,CAAC,KAAiB;QACtC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3C,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3C,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;aAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;YACnD,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAEM,eAAe,CAAC,MAAkB;QACrC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACL,CAAC;IACL,CAAC;IAEM,aAAa,CAAC,KAAiB;QAClC,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,eAAe,CAAC,IAAe,EAAE,KAAiB;QACrD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QAC/C,CAAC;IACL,CAAC;IAEO,YAAY;QAChB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,0BAA0B,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrH,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,uBAAuB,CAAC,KAAiB;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC;QAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO;gBACH,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;gBAC3C,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;aAC9C,CAAC;QACN,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC;QACxC,OAAO;YACH,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;YACpC,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;SACvC,CAAC;IACN,CAAC;IAEO,iBAAiB;QACrB,OAAO,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,IAAI,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,IAAI,GAAG,CAAC;IAC5G,CAAC;IAEO,kBAAkB;QACtB,OAAO,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,YAAY,IAAI,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,YAAY,IAAI,GAAG,CAAC;IAC9G,CAAC;IAED,qEAAqE;IAC9D,YAAY;QACf,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAChD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACvC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3G,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,QAAQ,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IAEM,QAAQ,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IAEM,iBAAiB,CAAC,QAAiB;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;QAC3E,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QAExB,MAAM,OAAO,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;QACrF,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAE5B,uEAAuE;QACvE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnG,MAAM,aAAa,GAAG,wBAAwB,CAAC,IAAI,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACvF,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAChG,OAAO;YACH,QAAQ,EAAE,QAAQ,IAAI,QAAQ;YAC9B,KAAK,EAAE,cAAc;YACrB,KAAK,EAAE,aAAa;YACpB,SAAS,EAAE,wBAAwB;SACtC,CAAC;IACN,CAAC;IAED,IAAW,gBAAgB;QACvB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,MAAM,GAA0B,EAAE,CAAC;QACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAEM,gBAAgB,CAAC,QAAiB;QACrC,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IAEM,YAAY,CAAC,IAAe;QAC/B,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;IACrE,CAAC;IAEM,WAAW,CAAC,IAAe;QAC9B,OAAO,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,IAAI,wBAAwB,CAAC;IACzG,CAAC;IAEM,gBAAgB,CAAC,EAAU;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACnG,CAAC;IAEM,iBAAiB,CAAC,IAAe;QACpC,OAAO,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;YAC7C,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC;YAChD,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED;;;OAGG;IACI,cAAc,CAAC,IAAe;QACjC,OAAO,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAEM,aAAa,CAAC,IAAY,EAAE,MAAc;QAC7C,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,CAAC;IAEM,WAAW,CAAC,IAAe;QAC9B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAClE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,OAAO,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,SAAS,KAAK,UAAU,IAAI,SAAS,KAAK,UAAU,CAAC;IACzG,CAAC;IAED;;;;OAIG;IACK,mBAAmB,CAAC,EAAU;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACX,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,KAAK,GACP,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;YAC/D,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,CAAC;4GAhiBQ,kBAAkB;6DAAlB,kBAAkB;;;;;;;YA3MvB,iCAAuC;YAEnC,qFAAmB;YA0DnB,oFAAiD;YAYjD,qFAAqC;;YAsDrC,iCAQoC;YAAhC,AADA,AADA,AADA,AADA,0GAAa,6BAAyB,IAAC,6FAC1B,6BAAyB,IAAC,yFAC5B,2BAAuB,IAAC,+FACrB,2BAAuB,IAAC,qFAC7B,yBAAqB,IAAC;YAE/B,yBAAkF;YAE9E,2FAkBC;YAGD,0FAqCC;YAGb,AADI,AADI,iBAAI,EACF,EACJ;;YArMF,eAuDC;YAvDD,0CAuDC;YAGD,cASC;YATD,4EASC;YAGD,cAmDC;YAnDD,gEAmDC;YAMG,cAA8B;YAA9B,2CAA8B;YAO3B,eAA8E;;YAE7E,cAkBC;YAlBD,wBAkBC;YAGD,eAqCC;YArCD,gCAqCC;4BAvMP,YAAY,EAAE,WAAW;;iFA8M1B,kBAAkB;cAjN9B,SAAS;2BACI,eAAe,cACb,IAAI,WACP,CAAC,YAAY,EAAE,WAAW,CAAC,mBACnB,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAyMT;;kBAMA,SAAS;mBAAC,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;;kBACrC,SAAS;mBAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;;kBAGvC,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBASL,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kFA5CE,kBAAkB","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnInit,\n OnChanges,\n OnDestroy,\n SimpleChanges,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n ElementRef,\n ViewChild,\n inject\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';\nimport * as d3 from 'd3';\nimport type {\n GraphNode,\n GraphEdge,\n GraphLayoutMode,\n GraphNodeCategory,\n GraphCategoryConfig,\n GraphViewportTransform,\n GraphPhysicsConfig\n} from '../models/graph-models';\nimport {\n BeforeNodeSelectEventArgs,\n NodeSelectedEventArgs,\n BeforeEdgeSelectEventArgs,\n EdgeSelectedEventArgs,\n BeforeHopExpandEventArgs,\n HopExpandedEventArgs,\n BeforeLayoutChangeEventArgs,\n LayoutChangedEventArgs,\n BeforeNodeNavigateEventArgs,\n NodeNavigatedEventArgs,\n ViewportTransformEventArgs\n} from '../events/graph-events';\n\nexport const DEFAULT_GRAPH_CATEGORIES: GraphCategoryConfig[] = [\n { Category: 'person', Label: 'Person', Color: '#10b981', IconClass: 'fa-solid fa-user' },\n { Category: 'organization', Label: 'Organization', Color: '#38bdf8', IconClass: 'fa-solid fa-building' },\n { Category: 'committee', Label: 'Committee', Color: '#8b5cf6', IconClass: 'fa-solid fa-landmark' },\n { Category: 'account', Label: 'Holding / Account', Color: '#f59e0b', IconClass: 'fa-solid fa-building-flag' },\n { Category: 'asset', Label: 'Asset', Color: '#ec4899', IconClass: 'fa-solid fa-gem' },\n { Category: 'group', Label: 'Group', Color: '#06b6d4', IconClass: 'fa-solid fa-users' },\n { Category: 'custom', Label: 'Custom', Color: '#64748b', IconClass: 'fa-solid fa-circle-nodes' }\n];\n\nconst DYNAMIC_CATEGORY_PALETTE = [\n '#10b981', '#38bdf8', '#8b5cf6', '#f59e0b', '#ec4899',\n '#06b6d4', '#84cc16', '#f43f5e', '#a855f7', '#14b8a6'\n];\n\nconst GRAPH_VIEW_CSS = `\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: var(--mj-bg-surface-sunken, #0b1220);\n border-radius: var(--mj-radius-lg, 12px);\n border: 1px solid var(--mj-border-default, #223254);\n user-select: none;\n}\n\n.mj-graph-wrapper {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-graph-svg-canvas {\n width: 100%;\n height: 100%;\n cursor: grab;\n}\n\n.mj-graph-svg-canvas.is-panning {\n cursor: grabbing;\n}\n\n/* Floating Toolbar */\n.mj-graph-toolbar {\n position: absolute;\n top: 14px;\n left: 14px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n background: rgba(20, 31, 54, 0.88);\n backdrop-filter: blur(10px);\n border: 1px solid var(--mj-border-default, #223254);\n padding: 6px 12px;\n border-radius: var(--mj-radius-lg, 10px);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n}\n\n.mj-graph-tool-btn {\n background: var(--mj-bg-surface-elevated, #1a2744);\n border: 1px solid var(--mj-border-default, #223254);\n color: var(--mj-text-primary, #f8fafc);\n border-radius: 6px;\n padding: 5px 10px;\n font-size: 11.5px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s ease;\n}\n\n.mj-graph-tool-btn:hover {\n border-color: var(--mj-brand-primary, #38bdf8);\n background: var(--mj-border-hover, #334876);\n}\n\n.mj-graph-tool-btn.active {\n background: var(--mj-brand-primary, #38bdf8);\n color: #090e1a;\n border-color: var(--mj-brand-primary, #38bdf8);\n}\n\n.mj-graph-separator {\n width: 1px;\n height: 18px;\n background: var(--mj-border-default, #223254);\n}\n\n.mj-graph-search-input {\n background: var(--mj-bg-surface-sunken, #090e1a);\n border: 1px solid var(--mj-border-default, #223254);\n border-radius: 6px;\n color: var(--mj-text-primary, #f8fafc);\n padding: 4px 8px 4px 24px;\n font-size: 11.5px;\n width: 150px;\n}\n\n.mj-graph-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary, #38bdf8);\n width: 190px;\n}\n\n/* Floating Legend */\n.mj-graph-legend {\n position: absolute;\n bottom: 14px;\n left: 14px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 12px;\n background: rgba(20, 31, 54, 0.88);\n backdrop-filter: blur(10px);\n border: 1px solid var(--mj-border-default, #223254);\n padding: 6px 14px;\n border-radius: var(--mj-radius-lg, 10px);\n font-size: 11.5px;\n color: var(--mj-text-secondary, #94a3b8);\n}\n\n.mj-graph-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-graph-legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n}\n\n/* Inspector Drawer */\n.mj-graph-inspector {\n position: absolute;\n top: 14px;\n right: 14px;\n width: 300px;\n max-height: calc(100% - 28px);\n background: rgba(20, 31, 54, 0.95);\n backdrop-filter: blur(14px);\n border: 1px solid var(--mj-border-default, #223254);\n border-radius: var(--mj-radius-lg, 12px);\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n z-index: 25;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);\n overflow-y: auto;\n scrollbar-width: thin;\n animation: mjGraphSlide 0.2s ease;\n}\n\n.mj-graph-inspector-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.mj-graph-inspector-title {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted, #64748b);\n}\n\n.mj-graph-inspector-close {\n background: none;\n border: none;\n color: var(--mj-text-muted, #64748b);\n cursor: pointer;\n font-size: 14px;\n}\n\n.mj-graph-inspector-card {\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--mj-bg-surface, #111a2e);\n border: 1px solid var(--mj-border-default, #223254);\n border-radius: 8px;\n padding: 10px 12px;\n}\n\n.mj-graph-inspector-avatar {\n width: 40px;\n height: 40px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n color: #fff;\n flex-shrink: 0;\n}\n\n.mj-graph-inspector-actions {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n}\n\n/* SVG Rendering Elements */\n.mj-edge-path {\n stroke: var(--mj-border-hover, #2d416b);\n stroke-width: 2;\n fill: none;\n transition: stroke 0.2s ease;\n}\n\n.mj-edge-path.is-highlighted {\n stroke: var(--mj-brand-primary, #38bdf8);\n stroke-width: 3;\n}\n\n.mj-edge-text {\n font-size: 10px;\n font-weight: 500;\n fill: var(--mj-text-muted, #64748b);\n text-anchor: middle;\n pointer-events: none;\n paint-order: stroke;\n stroke: var(--mj-bg-surface-sunken, #0b1220);\n stroke-width: 3px;\n stroke-linecap: butt;\n stroke-linejoin: miter;\n}\n\n.mj-node-group {\n cursor: pointer;\n transition: transform 0.1s ease;\n}\n\n.mj-node-group:hover .mj-node-circle {\n filter: drop-shadow(0 0 10px var(--mj-brand-primary, #38bdf8));\n}\n\n.mj-node-circle {\n transition: all 0.2s ease;\n}\n\n.mj-node-label {\n font-size: 11px;\n font-weight: 700;\n fill: var(--mj-text-primary, #f8fafc);\n text-anchor: middle;\n pointer-events: none;\n}\n\n.mj-node-sublabel {\n font-size: 9.5px;\n font-weight: 500;\n fill: var(--mj-text-secondary, #94a3b8);\n text-anchor: middle;\n pointer-events: none;\n}\n\n@keyframes mjGraphSlide {\n from { opacity: 0; transform: translateX(20px); }\n to { opacity: 1; transform: translateX(0); }\n}\n`;\n\n/**\n * `<mj-graph-view>` — Interactive network and entity relationship graph visualization.\n *\n * Supports force-directed dynamic layout, multi-category entity badges, hop-expansion,\n * zoom/pan controls, inspector drawer, search filters, and cancelable Before/After event pairs.\n */\n@Component({\n selector: 'mj-graph-view',\n standalone: true,\n imports: [CommonModule, FormsModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"mj-graph-wrapper\" #wrapper>\n <!-- 1. Floating Action Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-graph-toolbar\">\n <span style=\"font-size: 12px; font-weight: 700; color: #fff; display: flex; align-items: center; gap: 6px;\">\n <i class=\"fa-solid fa-circle-nodes\" style=\"color: var(--mj-brand-primary);\"></i>\n Network Graph\n </span>\n\n <div class=\"mj-graph-separator\"></div>\n\n <!-- Layout Mode Selector -->\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn\"\n [class.active]=\"LayoutMode === 'force'\"\n (click)=\"SetLayoutMode('force')\"\n title=\"Force-Directed Physics\">\n <i class=\"fa-solid fa-atom\"></i> Force\n </button>\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn\"\n [class.active]=\"LayoutMode === 'circular'\"\n (click)=\"SetLayoutMode('circular')\"\n title=\"Circular Layout\">\n <i class=\"fa-solid fa-circle-notch\"></i> Circular\n </button>\n\n <div class=\"mj-graph-separator\"></div>\n\n <!-- Zoom Controls -->\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"ZoomIn()\" title=\"Zoom In\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n </button>\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"ZoomOut()\" title=\"Zoom Out\">\n <i class=\"fa-solid fa-magnifying-glass-minus\"></i>\n </button>\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"FitToView()\" title=\"Fit View\">\n <i class=\"fa-solid fa-expand\"></i>\n </button>\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"Rearrange()\" title=\"Re-simulate Physics\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n\n @if (Searchable) {\n <div class=\"mj-graph-separator\"></div>\n <div style=\"position: relative;\">\n <i class=\"fa-solid fa-magnifying-glass\" style=\"position: absolute; left: 8px; top: 50%; transform: translateY(-50%); font-size: 10px; color: var(--mj-text-muted);\"></i>\n <input\n class=\"mj-graph-search-input\"\n [(ngModel)]=\"SearchQuery\"\n (ngModelChange)=\"SearchNodes($event)\"\n placeholder=\"Search graph nodes...\" />\n </div>\n }\n </div>\n }\n\n <!-- 2. Floating Dynamic Category Legend -->\n @if (ShowLegend && ActiveCategories.length > 0) {\n <div class=\"mj-graph-legend\">\n @for (cat of ActiveCategories; track cat.Category) {\n <div class=\"mj-graph-legend-item\">\n <div class=\"mj-graph-legend-dot\" [style.background]=\"cat.Color\"></div>\n {{ cat.Label }}\n </div>\n }\n </div>\n }\n\n <!-- 3. Selected Node Inspector Drawer -->\n @if (ShowInspector && SelectedNode) {\n <div class=\"mj-graph-inspector\">\n <div class=\"mj-graph-inspector-header\">\n <span class=\"mj-graph-inspector-title\">Node Inspector</span>\n <button class=\"mj-graph-inspector-close\" type=\"button\" (click)=\"ClearSelection()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"mj-graph-inspector-card\">\n <div class=\"mj-graph-inspector-avatar\" [style.background]=\"GetNodeColor(SelectedNode)\">\n <i [class]=\"GetNodeIcon(SelectedNode)\"></i>\n </div>\n <div style=\"overflow: hidden; flex: 1;\">\n <div style=\"font-size: 13px; font-weight: 700; color: #fff; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;\">\n {{ SelectedNode.Label }}\n </div>\n <div style=\"display: flex; align-items: center; gap: 6px; margin-top: 3px;\">\n <span style=\"font-size: 11px; color: var(--mj-text-secondary);\">\n {{ SelectedNode.Sublabel || SelectedNode.Category }}\n </span>\n @if (IsFocalNode(SelectedNode)) {\n <span style=\"font-size: 10px; padding: 1px 6px; border-radius: 9999px; background: rgba(56, 189, 248, 0.15); color: #38bdf8; font-weight: 600; border: 1px solid rgba(56, 189, 248, 0.3);\">Current Record</span>\n }\n </div>\n </div>\n </div>\n\n <div style=\"font-size: 11.5px; color: var(--mj-text-secondary); line-height: 1.45;\">\n Direct Connected Edges: <strong>{{ GetNodeEdgeCount(SelectedNode.ID) }}</strong>\n </div>\n\n <div class=\"mj-graph-inspector-actions\">\n @if (!IsFocalNode(SelectedNode)) {\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn active\"\n style=\"justify-content: center; width: 100%;\"\n (click)=\"NavigateToEntity(SelectedNode)\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Entity Form\n </button>\n }\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn\"\n style=\"justify-content: center; width: 100%;\"\n (click)=\"ExpandHops(SelectedNode.ID, 1)\">\n <i class=\"fa-solid fa-circle-nodes\"></i> Expand 1-Hop Neighbors\n </button>\n </div>\n </div>\n }\n\n <!-- 4. Interactive SVG Graph Canvas -->\n <svg\n #svgCanvas\n class=\"mj-graph-svg-canvas\"\n [class.is-panning]=\"IsPanning\"\n (mousedown)=\"OnCanvasMouseDown($event)\"\n (mousemove)=\"OnCanvasMouseMove($event)\"\n (mouseup)=\"OnCanvasMouseUp($event)\"\n (mouseleave)=\"OnCanvasMouseUp($event)\"\n (wheel)=\"OnCanvasWheel($event)\">\n\n <g [attr.transform]=\"'translate(' + PanX + ',' + PanY + ') scale(' + Scale + ')'\">\n <!-- Edges Layer -->\n @for (edge of Edges; track edge.ID) {\n <g>\n <line\n class=\"mj-edge-path\"\n [class.is-highlighted]=\"IsEdgeHighlighted(edge)\"\n [attr.x1]=\"GetNodeX(edge.SourceID)\"\n [attr.y1]=\"GetNodeY(edge.SourceID)\"\n [attr.x2]=\"GetNodeX(edge.TargetID)\"\n [attr.y2]=\"GetNodeY(edge.TargetID)\"\n (click)=\"OnEdgeClick(edge, $event)\">\n </line>\n <text\n class=\"mj-edge-text\"\n [attr.x]=\"(GetNodeX(edge.SourceID) + GetNodeX(edge.TargetID)) / 2\"\n [attr.y]=\"(GetNodeY(edge.SourceID) + GetNodeY(edge.TargetID)) / 2 - 4\">\n {{ edge.Label }}\n </text>\n </g>\n }\n\n <!-- Nodes Layer -->\n @for (node of FilteredNodes; track node.ID) {\n <g\n class=\"mj-node-group\"\n [attr.transform]=\"'translate(' + (node.X || 0) + ',' + (node.Y || 0) + ')'\"\n (mousedown)=\"OnNodeMouseDown(node, $event)\"\n (click)=\"OnNodeClick(node, $event)\"\n (dblclick)=\"NavigateToEntity(node)\">\n\n <!-- Outer Selection / Focal Halo -->\n @if (IsNodeSelected(node)) {\n <circle r=\"36\" fill=\"rgba(56, 189, 248, 0.2)\" stroke=\"#38bdf8\" stroke-width=\"2\"></circle>\n } @else if (IsFocalNode(node)) {\n <circle r=\"34\" fill=\"none\" stroke=\"rgba(56, 189, 248, 0.45)\" stroke-width=\"2\" stroke-dasharray=\"4 3\"></circle>\n }\n\n <!-- Node Body Circle -->\n <circle\n class=\"mj-node-circle\"\n [attr.r]=\"node.Radius || 26\"\n fill=\"#141f36\"\n [attr.stroke]=\"GetNodeColor(node)\"\n stroke-width=\"3\">\n </circle>\n\n <!-- Node Icon Center -->\n <circle [attr.r]=\"(node.Radius || 26) - 8\" [attr.fill]=\"GetNodeColor(node)\"></circle>\n\n <!-- Labels -->\n <text class=\"mj-node-label\" [attr.y]=\"(node.Radius || 26) + 16\">\n {{ TruncateLabel(node.Label, 18) }}\n </text>\n @if (node.Sublabel) {\n <text class=\"mj-node-sublabel\" [attr.y]=\"(node.Radius || 26) + 28\">\n {{ TruncateLabel(node.Sublabel, 20) }}\n </text>\n }\n </g>\n }\n </g>\n </svg>\n </div>\n `,\n styles: [GRAPH_VIEW_CSS]\n})\nexport class GraphViewComponent implements OnInit, OnChanges, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n @ViewChild('wrapper', { static: true }) public WrapperRef!: ElementRef<HTMLDivElement>;\n @ViewChild('svgCanvas', { static: true }) public CanvasRef!: ElementRef<SVGSVGElement>;\n\n // ── Inputs ────────────────────────────────────────────────────────\n @Input() public Nodes: GraphNode[] = [];\n @Input() public Edges: GraphEdge[] = [];\n @Input() public Categories: GraphCategoryConfig[] = DEFAULT_GRAPH_CATEGORIES;\n @Input() public LayoutMode: GraphLayoutMode = 'force';\n @Input() public MaxHopDistance = 2;\n @Input() public SelectedNodeId?: string;\n @Input() public FocalNodeId?: string;\n @Input() public AutoOpenInspector = false;\n @Input() public ShowToolbar = true;\n @Input() public ShowLegend = true;\n @Input() public ShowInspector = true;\n @Input() public Searchable = true;\n @Input() public AutoFitOnLoad = true;\n @Input() public Physics: GraphPhysicsConfig = {\n Repulsion: -500,\n LinkDistance: 130,\n Gravity: 0.05,\n Damping: 0.88,\n MaxIterations: 200\n };\n\n // ── Cancelable Before/After Outputs ────────────────────────────────\n @Output() public BeforeNodeSelect = new EventEmitter<BeforeNodeSelectEventArgs>();\n @Output() public NodeSelected = new EventEmitter<NodeSelectedEventArgs>();\n\n @Output() public BeforeEdgeSelect = new EventEmitter<BeforeEdgeSelectEventArgs>();\n @Output() public EdgeSelected = new EventEmitter<EdgeSelectedEventArgs>();\n\n @Output() public BeforeHopExpand = new EventEmitter<BeforeHopExpandEventArgs>();\n @Output() public HopExpanded = new EventEmitter<HopExpandedEventArgs>();\n\n @Output() public BeforeLayoutChange = new EventEmitter<BeforeLayoutChangeEventArgs>();\n @Output() public LayoutChanged = new EventEmitter<LayoutChangedEventArgs>();\n\n @Output() public BeforeNodeNavigate = new EventEmitter<BeforeNodeNavigateEventArgs>();\n @Output() public NodeNavigated = new EventEmitter<NodeNavigatedEventArgs>();\n\n @Output() public ViewportTransform = new EventEmitter<ViewportTransformEventArgs>();\n\n // ── Internal State ────────────────────────────────────────────────\n public FilteredNodes: GraphNode[] = [];\n public SelectedNode: GraphNode | null = null;\n public SelectedEdge: GraphEdge | null = null;\n public SearchQuery = '';\n\n public Scale = 1.0;\n public PanX = 200;\n public PanY = 150;\n public IsPanning = false;\n private panStartX = 0;\n private panStartY = 0;\n\n private draggedNode: GraphNode | null = null;\n private simulation: d3.Simulation<GraphNode, d3.SimulationLinkDatum<GraphNode>> | null = null;\n\n public ngOnInit(): void {\n this.InitializePositions();\n this.ApplyFilters();\n this.SimulatePhysics();\n }\n\n public ngOnChanges(changes: SimpleChanges): void {\n if (changes['Nodes'] || changes['Edges']) {\n this.InitializePositions();\n this.ApplyFilters();\n this.SimulatePhysics();\n }\n if (changes['SelectedNodeId'] && this.SelectedNodeId) {\n const found = this.Nodes.find(n => UUIDsEqual(n.ID, this.SelectedNodeId));\n if (found && this.AutoOpenInspector) {\n this.SelectedNode = found;\n }\n }\n }\n\n public ngOnDestroy(): void {\n if (this.simulation) {\n this.simulation.stop();\n this.simulation = null;\n }\n }\n\n // ── Positioning & D3 Physics Engine ──────────────────────────────────\n private InitializePositions(): void {\n const cx = this.GetContainerWidth() / 2 || 400;\n const cy = this.GetContainerHeight() / 2 || 250;\n const count = this.Nodes.length || 1;\n\n this.Nodes.forEach((node, idx) => {\n if (node.x === undefined || node.y === undefined) {\n if (node.X !== undefined && node.Y !== undefined) {\n node.x = node.X;\n node.y = node.Y;\n } else {\n const angle = (idx / count) * 2 * Math.PI;\n const r = 100 + Math.random() * 60;\n node.x = cx + r * Math.cos(angle);\n node.y = cy + r * Math.sin(angle);\n node.X = node.x;\n node.Y = node.y;\n }\n node.vx = 0;\n node.vy = 0;\n node.VX = 0;\n node.VY = 0;\n }\n });\n }\n\n public SimulatePhysics(): void {\n if (this.LayoutMode === 'circular') {\n this.ApplyCircularLayout();\n if (this.AutoFitOnLoad) {\n this.AutoFitToView();\n }\n this.cdr.markForCheck();\n return;\n }\n\n if (this.simulation) {\n this.simulation.stop();\n this.simulation = null;\n }\n\n const cx = this.GetContainerWidth() / 2 || 400;\n const cy = this.GetContainerHeight() / 2 || 250;\n\n // Resolve endpoints to node objects so a case-skewed UUID or dangling\n // edge cannot throw \"node not found\" out of ngOnInit. Raw-string\n // .id(d => d.ID) compared with ===, which UUIDsEqual elsewhere\n // deliberately does not.\n const nodeById = new Map<string, GraphNode>();\n for (const node of this.Nodes) {\n const key = NormalizeUUID(node.ID);\n if (key) {\n nodeById.set(key, node);\n }\n }\n const links: d3.SimulationLinkDatum<GraphNode>[] = [];\n for (const edge of this.Edges) {\n const source = nodeById.get(NormalizeUUID(edge.SourceID));\n const target = nodeById.get(NormalizeUUID(edge.TargetID));\n if (source && target) {\n links.push({ source, target });\n }\n }\n\n this.simulation = d3.forceSimulation<GraphNode>(this.Nodes)\n .force('link', d3.forceLink<GraphNode, d3.SimulationLinkDatum<GraphNode>>(links)\n .distance(this.Physics.LinkDistance || 120))\n .force('charge', d3.forceManyBody<GraphNode>()\n .strength(this.Physics.Repulsion || -450)\n .distanceMax(700))\n .force('collide', d3.forceCollide<GraphNode>((d: GraphNode) => (d.Radius || 26) + 16).iterations(2))\n .force('center', d3.forceCenter<GraphNode>(cx, cy));\n\n // Headless Pre-Warming: run 80 ticks synchronously in memory so the SVG renders resting immediately\n this.simulation.stop();\n for (let i = 0; i < 90; ++i) {\n this.simulation.tick();\n }\n\n this.SyncCoordinates();\n\n // Auto-center & fit into view\n if (this.AutoFitOnLoad) {\n this.AutoFitToView();\n }\n\n // Attach live tick handler for interactive drag & drop\n this.simulation.on('tick', () => {\n this.SyncCoordinates();\n this.cdr.markForCheck();\n });\n\n this.cdr.markForCheck();\n }\n\n private SyncCoordinates(): void {\n for (const n of this.Nodes) {\n n.X = n.x;\n n.Y = n.y;\n n.VX = n.vx;\n n.VY = n.vy;\n }\n }\n\n private ApplyCircularLayout(): void {\n const cx = this.GetContainerWidth() / 2 || 400;\n const cy = this.GetContainerHeight() / 2 || 250;\n const r = Math.min(cx, cy) * 0.7 || 160;\n const count = this.Nodes.length || 1;\n\n this.Nodes.forEach((node, idx) => {\n const angle = (idx / count) * 2 * Math.PI;\n node.x = cx + r * Math.cos(angle);\n node.y = cy + r * Math.sin(angle);\n node.X = node.x;\n node.Y = node.y;\n });\n }\n\n // ── Public Programmatic Methods ───────────────────────────────────\n public ZoomIn(): void {\n this.Scale = Math.min(this.Scale * 1.2, 3.0);\n this.EmitViewport();\n }\n\n public ZoomOut(): void {\n this.Scale = Math.max(this.Scale / 1.2, 0.3);\n this.EmitViewport();\n }\n\n public FitToView(): void {\n this.AutoFitToView(40);\n }\n\n public AutoFitToView(padding = 40): void {\n if (!this.Nodes || this.Nodes.length === 0) return;\n\n const width = this.GetContainerWidth() || 800;\n const height = this.GetContainerHeight() || 420;\n\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;\n for (const n of this.Nodes) {\n const nx = n.x ?? n.X ?? 0;\n const ny = n.y ?? n.Y ?? 0;\n const r = (n.Radius || 26) + 20; // include badge & labels\n if (nx - r < minX) minX = nx - r;\n if (nx + r > maxX) maxX = nx + r;\n if (ny - r < minY) minY = ny - r;\n if (ny + r > maxY) maxY = ny + r;\n }\n\n if (!isFinite(minX) || !isFinite(maxX) || !isFinite(minY) || !isFinite(maxY)) return;\n\n const bboxW = Math.max(maxX - minX, 100);\n const bboxH = Math.max(maxY - minY, 100);\n\n const scaleX = (width - padding * 2) / bboxW;\n const scaleY = (height - padding * 2) / bboxH;\n const targetScale = Math.min(Math.max(Math.min(scaleX, scaleY), 0.4), 1.25);\n\n this.Scale = targetScale;\n this.PanX = (width - (minX + maxX) * this.Scale) / 2;\n this.PanY = (height - (minY + maxY) * this.Scale) / 2;\n\n this.EmitViewport();\n this.cdr.markForCheck();\n }\n\n public Rearrange(): void {\n this.InitializePositions();\n this.SimulatePhysics();\n }\n\n public SetLayoutMode(mode: GraphLayoutMode): void {\n const beforeEvent = new BeforeLayoutChangeEventArgs(mode, this.LayoutMode);\n this.BeforeLayoutChange.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n const prev = this.LayoutMode;\n this.LayoutMode = mode;\n this.SimulatePhysics();\n this.LayoutChanged.emit(new LayoutChangedEventArgs(mode, prev));\n }\n\n public SelectNode(id: string): void {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, id));\n if (node) this.OnNodeClick(node, new MouseEvent('click'));\n }\n\n public ClearSelection(): void {\n this.SelectedNode = null;\n this.SelectedEdge = null;\n this.cdr.markForCheck();\n }\n\n public SearchNodes(query: string): void {\n this.SearchQuery = query;\n this.ApplyFilters();\n }\n\n public ExpandHops(nodeId: string, depth: number): void {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, nodeId));\n if (!node) return;\n\n const beforeEvent = new BeforeHopExpandEventArgs(node, (node.HopDistance || 0) + depth, node.HopDistance || 0);\n this.BeforeHopExpand.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n // Fire after event for host listener to fetch incremental relations\n this.HopExpanded.emit(new HopExpandedEventArgs(node, [], [], (node.HopDistance || 0) + depth));\n }\n\n // ── Event Handlers ────────────────────────────────────────────────\n public OnNodeClick(node: GraphNode, event: MouseEvent): void {\n event.stopPropagation();\n const beforeEvent = new BeforeNodeSelectEventArgs(node, this.SelectedNode);\n this.BeforeNodeSelect.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n const prev = this.SelectedNode;\n this.SelectedNode = node;\n this.SelectedEdge = null;\n this.NodeSelected.emit(new NodeSelectedEventArgs(node, prev));\n this.cdr.markForCheck();\n }\n\n public OnEdgeClick(edge: GraphEdge, event: MouseEvent): void {\n event.stopPropagation();\n const src = this.Nodes.find(n => UUIDsEqual(n.ID, edge.SourceID)) || null;\n const tgt = this.Nodes.find(n => UUIDsEqual(n.ID, edge.TargetID)) || null;\n\n const beforeEvent = new BeforeEdgeSelectEventArgs(edge, src, tgt);\n this.BeforeEdgeSelect.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n this.SelectedEdge = edge;\n this.EdgeSelected.emit(new EdgeSelectedEventArgs(edge, src, tgt));\n this.cdr.markForCheck();\n }\n\n public NavigateToEntity(node: GraphNode): void {\n // Focal-node dblclick must not navigate away from the record this\n // graph is illustrating — the inspector already hides the navigate\n // button for the same reason.\n if (this.IsFocalNode(node)) {\n return;\n }\n const rawId = this.stripCategoryPrefix(String(node.Data?.['ID'] || node.ID));\n const entityName = String(node.Data?.['EntityName'] || (node.Category === 'person' ? 'MJ_BizApps_Common: People' : 'MJ_BizApps_Common: Organizations'));\n const beforeEvent = new BeforeNodeNavigateEventArgs(node, entityName, rawId);\n this.BeforeNodeNavigate.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n // Hosts navigate. This widget is L1 and must not import Explorer's\n // NavigationService — emit the intent and let the surface open the record.\n this.NodeNavigated.emit(new NodeNavigatedEventArgs(node, entityName, rawId));\n }\n\n // ── Mouse Drag & Pan Handlers ─────────────────────────────────────\n public OnCanvasMouseDown(event: MouseEvent): void {\n if (event.target === this.CanvasRef.nativeElement || (event.target as HTMLElement).tagName === 'svg') {\n this.IsPanning = true;\n this.panStartX = event.clientX - this.PanX;\n this.panStartY = event.clientY - this.PanY;\n }\n }\n\n public OnCanvasMouseMove(event: MouseEvent): void {\n if (this.IsPanning) {\n this.PanX = event.clientX - this.panStartX;\n this.PanY = event.clientY - this.panStartY;\n this.EmitViewport();\n this.cdr.markForCheck();\n } else if (this.draggedNode) {\n const coords = this.GetCanvasRelativeCoords(event);\n this.draggedNode.fx = coords.x;\n this.draggedNode.fy = coords.y;\n this.draggedNode.x = coords.x;\n this.draggedNode.y = coords.y;\n this.draggedNode.X = coords.x;\n this.draggedNode.Y = coords.y;\n this.cdr.markForCheck();\n }\n }\n\n public OnCanvasMouseUp(_event: MouseEvent): void {\n this.IsPanning = false;\n if (this.draggedNode) {\n this.draggedNode.fx = null;\n this.draggedNode.fy = null;\n this.draggedNode = null;\n if (this.simulation) {\n this.simulation.alphaTarget(0);\n }\n }\n }\n\n public OnCanvasWheel(event: WheelEvent): void {\n event.preventDefault();\n const delta = event.deltaY > 0 ? 0.9 : 1.1;\n this.Scale = Math.min(Math.max(this.Scale * delta, 0.3), 3.0);\n this.EmitViewport();\n this.cdr.markForCheck();\n }\n\n public OnNodeMouseDown(node: GraphNode, event: MouseEvent): void {\n event.stopPropagation();\n this.draggedNode = node;\n node.fx = node.x ?? node.X;\n node.fy = node.y ?? node.Y;\n if (this.simulation) {\n this.simulation.alphaTarget(0.3).restart();\n }\n }\n\n private EmitViewport(): void {\n this.ViewportTransform.emit(new ViewportTransformEventArgs({ Scale: this.Scale, PanX: this.PanX, PanY: this.PanY }));\n this.cdr.markForCheck();\n }\n\n private GetCanvasRelativeCoords(event: MouseEvent): { x: number; y: number } {\n const canvas = this.CanvasRef?.nativeElement;\n if (!canvas) {\n return {\n x: (event.clientX - this.PanX) / this.Scale,\n y: (event.clientY - this.PanY) / this.Scale\n };\n }\n const rect = canvas.getBoundingClientRect();\n const mouseX = event.clientX - rect.left;\n const mouseY = event.clientY - rect.top;\n return {\n x: (mouseX - this.PanX) / this.Scale,\n y: (mouseY - this.PanY) / this.Scale\n };\n }\n\n private GetContainerWidth(): number {\n return this.CanvasRef?.nativeElement?.clientWidth || this.WrapperRef?.nativeElement?.clientWidth || 800;\n }\n\n private GetContainerHeight(): number {\n return this.CanvasRef?.nativeElement?.clientHeight || this.WrapperRef?.nativeElement?.clientHeight || 420;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────\n public ApplyFilters(): void {\n const q = this.SearchQuery.trim().toLowerCase();\n this.FilteredNodes = this.Nodes.filter(n => {\n return !q || n.Label.toLowerCase().includes(q) || (n.Sublabel && n.Sublabel.toLowerCase().includes(q));\n });\n this.cdr.markForCheck();\n }\n\n public GetNodeX(id: string): number {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, id));\n return node ? (node.X ?? node.x ?? 0) : 0;\n }\n\n public GetNodeY(id: string): number {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, id));\n return node ? (node.Y ?? node.y ?? 0) : 0;\n }\n\n public GetCategoryConfig(category?: string): GraphCategoryConfig {\n const key = String(category || '').toLowerCase();\n const found = this.Categories?.find(c => c.Category.toLowerCase() === key);\n if (found) return found;\n\n const builtin = DEFAULT_GRAPH_CATEGORIES.find(c => c.Category.toLowerCase() === key);\n if (builtin) return builtin;\n\n // Deterministic palette hash for arbitrary consumer-defined categories\n const hash = Math.abs(key.split('').reduce((acc, ch) => ((acc << 5) - acc) + ch.charCodeAt(0), 0));\n const fallbackColor = DYNAMIC_CATEGORY_PALETTE[hash % DYNAMIC_CATEGORY_PALETTE.length];\n const formattedLabel = category ? category.charAt(0).toUpperCase() + category.slice(1) : 'Item';\n return {\n Category: category || 'custom',\n Label: formattedLabel,\n Color: fallbackColor,\n IconClass: 'fa-solid fa-circle-dot'\n };\n }\n\n public get ActiveCategories(): GraphCategoryConfig[] {\n const present = new Set<string>();\n for (const n of this.Nodes) {\n if (n.Category) present.add(n.Category.toLowerCase());\n }\n const result: GraphCategoryConfig[] = [];\n for (const catKey of present) {\n result.push(this.GetCategoryConfig(catKey));\n }\n return result;\n }\n\n public GetCategoryLabel(category?: string): string {\n return this.GetCategoryConfig(category).Label;\n }\n\n public GetNodeColor(node: GraphNode): string {\n return node.Color ?? this.GetCategoryConfig(node.Category).Color;\n }\n\n public GetNodeIcon(node: GraphNode): string {\n return node.IconClass ?? this.GetCategoryConfig(node.Category).IconClass ?? 'fa-solid fa-circle-dot';\n }\n\n public GetNodeEdgeCount(id: string): number {\n return this.Edges.filter(e => UUIDsEqual(e.SourceID, id) || UUIDsEqual(e.TargetID, id)).length;\n }\n\n public IsEdgeHighlighted(edge: GraphEdge): boolean {\n return UUIDsEqual(this.SelectedEdge?.ID, edge.ID) ||\n UUIDsEqual(this.SelectedNode?.ID, edge.SourceID) ||\n UUIDsEqual(this.SelectedNode?.ID, edge.TargetID);\n }\n\n /**\n * Whether this node is the selected one. Exists as a method because the template cannot call\n * an imported function directly, and the comparison must go through UUIDsEqual rather than ===.\n */\n public IsNodeSelected(node: GraphNode): boolean {\n return UUIDsEqual(this.SelectedNode?.ID, node.ID);\n }\n\n public TruncateLabel(text: string, maxLen: number): string {\n if (!text) return '';\n return text.length > maxLen ? text.substring(0, maxLen - 1) + '…' : text;\n }\n\n public IsFocalNode(node: GraphNode): boolean {\n if (!node || !this.FocalNodeId) return false;\n const focalClean = this.stripCategoryPrefix(this.FocalNodeId).toLowerCase();\n const nodeClean = this.stripCategoryPrefix(node.ID).toLowerCase();\n const dataClean = node.Data?.['ID'] ? String(node.Data['ID']).toLowerCase() : '';\n return UUIDsEqual(node.ID, this.FocalNodeId) || nodeClean === focalClean || dataClean === focalClean;\n }\n\n /**\n * Hosts prefix node IDs with `{category}:`. Categories are dynamic\n * (`organization`, `asset`, `group`, plus caller keys), so strip any\n * known-category prefix rather than a hardcoded subset.\n */\n private stripCategoryPrefix(id: string): string {\n const idx = id.indexOf(':');\n if (idx <= 0) {\n return id;\n }\n const prefix = id.slice(0, idx).toLowerCase();\n const known =\n this.Categories?.some(c => c.Category.toLowerCase() === prefix) ||\n DEFAULT_GRAPH_CATEGORIES.some(c => c.Category.toLowerCase() === prefix);\n return known ? id.slice(idx + 1) : id;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-view.component.js","sourceRoot":"","sources":["../../../src/lib/components/graph-view.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,KAAK,EACL,MAAM,EACN,YAAY,EAKZ,uBAAuB,EACvB,iBAAiB,EAEjB,SAAS,EACT,MAAM,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAUzB,OAAO,EACH,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,0BAA0B,EAC7B,MAAM,wBAAwB,CAAC;;;;;;;;;IAgVR,0BAAsC;IACtC,+BAAiC;IAC7B,wBAAwK;IACxK,iCAI0C;IAFtC,gUAAyB;IACzB,2NAAiB,0BAAmB,KAAC;IAE7C,AALI,iBAI0C,EACxC;;;IAHE,eAAyB;IAAzB,kDAAyB;;;;IA/CrC,AADJ,8BAA8B,cACkF;IACxG,uBAAgF;IAChF,+BACJ;IAAA,iBAAO;IAEP,0BAAsC;IAGtC,kCAKmC;IAD/B,sLAAS,qBAAc,OAAO,CAAC,KAAC;IAEhC,wBAAgC;IAAC,uBACrC;IAAA,iBAAS;IACT,kCAK4B;IADxB,sLAAS,qBAAc,UAAU,CAAC,KAAC;IAEnC,wBAAwC;IAAC,2BAC7C;IAAA,iBAAS;IAET,2BAAsC;IAGtC,mCAAmF;IAAnC,uLAAS,eAAQ,KAAC;IAC9D,yBAAiD;IACrD,iBAAS;IACT,mCAAqF;IAArC,uLAAS,gBAAS,KAAC;IAC/D,yBAAkD;IACtD,iBAAS;IACT,mCAAuF;IAAvC,uLAAS,kBAAW,KAAC;IACjE,yBAAkC;IACtC,iBAAS;IACT,mCAAkG;IAAlD,uLAAS,kBAAW,KAAC;IACjE,yBAAyC;IAC7C,iBAAS;IAET,0FAAkB;IAWtB,iBAAM;;;IAzCE,eAAuC;IAAvC,uDAAuC;IAQvC,eAA0C;IAA1C,0DAA0C;IAsB9C,gBAUC;IAVD,6CAUC;;;IAQG,+BAAkC;IAC9B,0BAAsE;IACtE,YACJ;IAAA,iBAAM;;;IAF+B,cAA8B;IAA9B,0CAA8B;IAC/D,cACJ;IADI,6CACJ;;;IALR,8BAA6B;IACzB,oGAKC;IACL,iBAAM;;;IANF,cAKC;IALD,sCAKC;;;IA2Be,gCAA2L;IAAA,8BAAc;IAAA,iBAAO;;;;IAYxN,kCAI6C;IAAzC,sMAAS,4CAA8B,KAAC;IACxC,wBAAsD;IAAC,kCAC3D;IAAA,iBAAS;;;;IArCb,AADJ,AADJ,8BAAgC,cACW,eACI;IAAA,8BAAc;IAAA,iBAAO;IAC5D,kCAAkF;IAA3B,sLAAS,uBAAgB,KAAC;IAC7E,wBAAiC;IAEzC,AADI,iBAAS,EACP;IAGF,AADJ,+BAAqC,cACsD;IACnF,oBAA2C;IAC/C,iBAAM;IAEF,AADJ,+BAAwC,eACyF;IACzH,aACJ;IAAA,iBAAM;IAEF,AADJ,gCAA4E,gBACR;IAC5D,aACJ;IAAA,iBAAO;IACP,sGAAiC;IAK7C,AADI,AADI,iBAAM,EACJ,EACJ;IAEN,gCAAoF;IAChF,0CAAwB;IAAA,+BAAQ;IAAA,aAAuC;IAC3E,AAD2E,iBAAS,EAC9E;IAEN,gCAAwC;IACpC,wGAAkC;IASlC,mCAI6C;IAAzC,uLAAS,0CAA4B,CAAC,CAAC,KAAC;IACxC,yBAAwC;IAAC,yCAC7C;IAER,AADI,AADI,iBAAS,EACP,EACJ;;;IAxCyC,eAA+C;IAA/C,sEAA+C;IAC/E,cAAmC;IAAnC,sDAAmC;IAIlC,eACJ;IADI,0DACJ;IAGQ,eACJ;IADI,6FACJ;IACA,cAEC;IAFD,mEAEC;IAMuB,eAAuC;IAAvC,qEAAuC;IAIvE,eAQC;IARD,oEAQC;;;;;IA2BG,AADJ,yBAAG,eAQyC;IAApC,4MAAS,mCAAyB,KAAC;IACvC,iBAAO;IACP,gCAG2E;IACvE,YACJ;IACJ,AADI,iBAAO,EACP;;;;IAbI,cAAgD;IAAhD,mEAAgD;;IAShD,cAAkE;;IAElE,cACJ;IADI,8CACJ;;;;IAeI,6BAAyF;;;;IAEzF,6BAA8G;;;;IAoB9G,gCAAmE;IAC/D,YACJ;IAAA,iBAAO;;;;;IADH,cACJ;IADI,4EACJ;;;;;IAjCR,6BAKwC;IAApC,AADA,AADA,mNAAa,wCAA6B,KAAC,8LAClC,oCAAyB,KAAC,8LACvB,iCAAsB,KAAC;IAKjC,AAFF,oGAA4B,8EAEI;IAchC,AATA,6BAMS,aAG4E;IAGrF,gCAAgE;IAC5D,YACJ;IAAA,iBAAO;IACP,kGAAqB;IAKzB,iBAAI;;;;;IA3BA,cAIC;IAJD,6FAIC;IAKG,eAA4B;;IAOxB,cAAkC;;IAGd,cAAmC;;IAC3D,cACJ;IADI,yEACJ;IACA,cAIC;IAJD,4CAIC;;AAle7B,MAAM,CAAC,MAAM,wBAAwB,GAA0B;IAC3D,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,EAAE;IACxF,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,EAAE;IACxG,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,sBAAsB,EAAE;IAClG,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,2BAA2B,EAAE;IAC7G,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAiB,EAAE;IACrF,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE;IACvF,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,0BAA0B,EAAE;CACnG,CAAC;AAEF,MAAM,wBAAwB,GAAG;IAC7B,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IACrD,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;CACxD,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmQtB,CAAC;AAEF;;;;;GAKG;AAkNH,MAAM,OAAO,kBAAkB;IACnB,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAEO,UAAU,CAA8B;IACtC,SAAS,CAA6B;IAEvF,qEAAqE;IACrD,KAAK,GAAgB,EAAE,CAAC;IACxB,KAAK,GAAgB,EAAE,CAAC;IACxB,UAAU,GAA0B,wBAAwB,CAAC;IAC7D,UAAU,GAAoB,OAAO,CAAC;IACtC,cAAc,GAAG,CAAC,CAAC;IACnB,cAAc,CAAU;IACxB,WAAW,CAAU;IACrB,iBAAiB,GAAG,KAAK,CAAC;IAC1B,WAAW,GAAG,IAAI,CAAC;IACnB,UAAU,GAAG,IAAI,CAAC;IAClB,aAAa,GAAG,IAAI,CAAC;IACrB,UAAU,GAAG,IAAI,CAAC;IAClB,aAAa,GAAG,IAAI,CAAC;IACrB,OAAO,GAAuB;QAC1C,SAAS,EAAE,CAAC,GAAG;QACf,YAAY,EAAE,GAAG;QACjB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,GAAG;KACrB,CAAC;IAEF,sEAAsE;IACrD,gBAAgB,GAAG,IAAI,YAAY,EAA6B,CAAC;IACjE,YAAY,GAAG,IAAI,YAAY,EAAyB,CAAC;IAEzD,gBAAgB,GAAG,IAAI,YAAY,EAA6B,CAAC;IACjE,YAAY,GAAG,IAAI,YAAY,EAAyB,CAAC;IAEzD,eAAe,GAAG,IAAI,YAAY,EAA4B,CAAC;IAC/D,WAAW,GAAG,IAAI,YAAY,EAAwB,CAAC;IAEvD,kBAAkB,GAAG,IAAI,YAAY,EAA+B,CAAC;IACrE,aAAa,GAAG,IAAI,YAAY,EAA0B,CAAC;IAE3D,kBAAkB,GAAG,IAAI,YAAY,EAA+B,CAAC;IACrE,aAAa,GAAG,IAAI,YAAY,EAA0B,CAAC;IAE3D,iBAAiB,GAAG,IAAI,YAAY,EAA8B,CAAC;IAEpF,qEAAqE;IAC9D,aAAa,GAAgB,EAAE,CAAC;IAChC,YAAY,GAAqB,IAAI,CAAC;IACtC,YAAY,GAAqB,IAAI,CAAC;IACtC,WAAW,GAAG,EAAE,CAAC;IAEjB,KAAK,GAAG,GAAG,CAAC;IACZ,IAAI,GAAG,GAAG,CAAC;IACX,IAAI,GAAG,GAAG,CAAC;IACX,SAAS,GAAG,KAAK,CAAC;IACjB,SAAS,GAAG,CAAC,CAAC;IACd,SAAS,GAAG,CAAC,CAAC;IAEd,WAAW,GAAqB,IAAI,CAAC;IACrC,UAAU,GAAuE,IAAI,CAAC;IAEvF,QAAQ;QACX,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3B,CAAC;IAEM,WAAW,CAAC,OAAsB;QACrC,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,eAAe,EAAE,CAAC;QAC3B,CAAC;QACD,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;YAC1E,IAAI,KAAK,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAClC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC9B,CAAC;QACL,CAAC;IACL,CAAC;IAEM,WAAW;QACd,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;IACL,CAAC;IAED,wEAAwE;IAChE,mBAAmB;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAErC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7B,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC/C,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;oBAC/C,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACJ,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;oBAC1C,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;oBACnC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAClC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAClC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;oBAChB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;gBACpB,CAAC;gBACD,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;gBACZ,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;YAChB,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEM,eAAe;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YACjC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YACxB,OAAO;QACX,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAEhD,sEAAsE;QACtE,iEAAiE;QACjE,+DAA+D;QAC/D,yBAAyB;QACzB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnC,IAAI,GAAG,EAAE,CAAC;gBACN,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAC5B,CAAC;QACL,CAAC;QACD,MAAM,KAAK,GAAwC,EAAE,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC1D,IAAI,MAAM,IAAI,MAAM,EAAE,CAAC;gBACnB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;YACnC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,eAAe,CAAY,IAAI,CAAC,KAAK,CAAC;aACtD,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAA+C,KAAK,CAAC;aAC3E,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC;aAC/C,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,aAAa,EAAa;aACzC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC;aACxC,WAAW,CAAC,GAAG,CAAC,CAAC;aACrB,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC,YAAY,CAAY,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;aACnG,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAY,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAExD,oGAAoG;QACpG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,8BAA8B;QAC9B,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,EAAE,CAAC;QACzB,CAAC;QAED,uDAAuD;QACvD,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,eAAe;QACnB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACV,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACV,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACZ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QAChB,CAAC;IACL,CAAC;IAEO,mBAAmB;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;QAChD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAErC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7B,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAClC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YAChB,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,qEAAqE;IAC9D,MAAM;QACT,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEM,OAAO;QACV,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEM,SAAS;QACZ,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAEM,aAAa,CAAC,OAAO,GAAG,EAAE;QAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEnD,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,CAAC;QAEhD,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC;QACzE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,yBAAyB;YAC1D,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QAErF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;QAEzC,MAAM,MAAM,GAAG,CAAC,KAAK,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAC7C,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;QAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;QAE5E,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAEtD,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,SAAS;QACZ,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,eAAe,EAAE,CAAC;IAC3B,CAAC;IAEM,aAAa,CAAC,IAAqB;QACtC,MAAM,WAAW,GAAG,IAAI,2BAA2B,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAEM,UAAU,CAAC,EAAU;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,IAAI,IAAI;YAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAEM,cAAc;QACjB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,WAAW,CAAC,KAAa;QAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,YAAY,EAAE,CAAC;IACxB,CAAC;IAEM,UAAU,CAAC,MAAc,EAAE,KAAa;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,MAAM,WAAW,GAAG,IAAI,wBAAwB,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;QAC/G,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACvC,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,oEAAoE;QACpE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IACnG,CAAC;IAED,qEAAqE;IAC9D,WAAW,CAAC,IAAe,EAAE,KAAiB;QACjD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3E,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC;QAC/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,WAAW,CAAC,IAAe,EAAE,KAAiB;QACjD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;QAC1E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;QAE1E,MAAM,WAAW,GAAG,IAAI,yBAAyB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAClE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,gBAAgB,CAAC,IAAe;QACnC,kEAAkE;QAClE,mEAAmE;QACnE,8BAA8B;QAC9B,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7E,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACxJ,MAAM,WAAW,GAAG,IAAI,2BAA2B,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC;QAC7E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,WAAW,CAAC,MAAM;YAAE,OAAO;QAE/B,mEAAmE;QACnE,2EAA2E;QAC3E,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,qEAAqE;IAC9D,iBAAiB,CAAC,KAAiB;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,aAAa,IAAK,KAAK,CAAC,MAAsB,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACnG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;YAC3C,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QAC/C,CAAC;IACL,CAAC;IAEM,iBAAiB,CAAC,KAAiB;QACtC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3C,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3C,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;aAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC;YACnD,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAEM,eAAe,CAAC,MAAkB;QACrC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBAClB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YACnC,CAAC;QACL,CAAC;IACL,CAAC;IAEM,aAAa,CAAC,KAAiB;QAClC,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,eAAe,CAAC,IAAe,EAAE,KAAiB;QACrD,KAAK,CAAC,eAAe,EAAE,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC;QAC3B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QAC/C,CAAC;IACL,CAAC;IAEO,YAAY;QAChB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,0BAA0B,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACrH,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,uBAAuB,CAAC,KAAiB;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC;QAC7C,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO;gBACH,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;gBAC3C,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;aAC9C,CAAC;QACN,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC;QACxC,OAAO;YACH,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;YACpC,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;SACvC,CAAC;IACN,CAAC;IAEO,iBAAiB;QACrB,OAAO,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,IAAI,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,WAAW,IAAI,GAAG,CAAC;IAC5G,CAAC;IAEO,kBAAkB;QACtB,OAAO,IAAI,CAAC,SAAS,EAAE,aAAa,EAAE,YAAY,IAAI,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,YAAY,IAAI,GAAG,CAAC;IAC9G,CAAC;IAED,qEAAqE;IAC9D,YAAY;QACf,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAChD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;YACvC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3G,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,QAAQ,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IAEM,QAAQ,CAAC,EAAU;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IAEM,iBAAiB,CAAC,QAAiB;QACtC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;QAC3E,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QAExB,MAAM,OAAO,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC;QACrF,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAE5B,uEAAuE;QACvE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnG,MAAM,aAAa,GAAG,wBAAwB,CAAC,IAAI,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACvF,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAChG,OAAO;YACH,QAAQ,EAAE,QAAQ,IAAI,QAAQ;YAC9B,KAAK,EAAE,cAAc;YACrB,KAAK,EAAE,aAAa;YACpB,SAAS,EAAE,wBAAwB;SACtC,CAAC;IACN,CAAC;IAED,IAAW,gBAAgB;QACvB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,QAAQ;gBAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,MAAM,GAA0B,EAAE,CAAC;QACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAEM,gBAAgB,CAAC,QAAiB;QACrC,OAAO,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IAEM,YAAY,CAAC,IAAe;QAC/B,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC;IACrE,CAAC;IAEM,WAAW,CAAC,IAAe;QAC9B,OAAO,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,IAAI,wBAAwB,CAAC;IACzG,CAAC;IAEM,gBAAgB,CAAC,EAAU;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACnG,CAAC;IAEM,iBAAiB,CAAC,IAAe;QACpC,OAAO,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;YAC7C,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC;YAChD,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED;;;OAGG;IACI,cAAc,CAAC,IAAe;QACjC,OAAO,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IAEM,aAAa,CAAC,IAAY,EAAE,MAAc;QAC7C,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,CAAC;IAEM,WAAW,CAAC,IAAe;QAC9B,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAClE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,OAAO,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,SAAS,KAAK,UAAU,IAAI,SAAS,KAAK,UAAU,CAAC;IACzG,CAAC;IAED;;;;OAIG;IACK,mBAAmB,CAAC,EAAU;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;YACX,OAAO,EAAE,CAAC;QACd,CAAC;QACD,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,KAAK,GACP,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;YAC/D,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,CAAC;4GAhiBQ,kBAAkB;6DAAlB,kBAAkB;;;;;;;YA3MvB,iCAAuC;YAEnC,qFAAmB;YA0DnB,oFAAiD;YAYjD,qFAAqC;;YAsDrC,iCAQoC;YAAhC,AADA,AADA,AADA,AADA,0GAAa,6BAAyB,IAAC,6FAC1B,6BAAyB,IAAC,yFAC5B,2BAAuB,IAAC,+FACrB,2BAAuB,IAAC,qFAC7B,yBAAqB,IAAC;YAE/B,yBAAkF;YAE9E,2FAkBC;YAGD,0FAqCC;YAGb,AADI,AADI,iBAAI,EACF,EACJ;;YArMF,eAuDC;YAvDD,0CAuDC;YAGD,cASC;YATD,4EASC;YAGD,cAmDC;YAnDD,gEAmDC;YAMG,cAA8B;YAA9B,2CAA8B;YAO3B,eAA8E;;YAE7E,cAkBC;YAlBD,wBAkBC;YAGD,eAqCC;YArCD,gCAqCC;4BAvMP,YAAY,EAAE,WAAW;;iFA8M1B,kBAAkB;cAjN9B,SAAS;2BACI,eAAe,cACb,IAAI,WACP,CAAC,YAAY,EAAE,WAAW,CAAC,mBACnB,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAyMT;;kBAMA,SAAS;mBAAC,SAAS,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;;kBACrC,SAAS;mBAAC,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;;kBAGvC,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBASL,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBAEN,MAAM;;kFA5CE,kBAAkB","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnInit,\n OnChanges,\n OnDestroy,\n SimpleChanges,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n ElementRef,\n ViewChild,\n inject\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';\nimport * as d3 from 'd3';\nimport type {\n GraphNode,\n GraphEdge,\n GraphLayoutMode,\n GraphNodeCategory,\n GraphCategoryConfig,\n GraphViewportTransform,\n GraphPhysicsConfig\n} from '../models/graph-models';\nimport {\n BeforeNodeSelectEventArgs,\n NodeSelectedEventArgs,\n BeforeEdgeSelectEventArgs,\n EdgeSelectedEventArgs,\n BeforeHopExpandEventArgs,\n HopExpandedEventArgs,\n BeforeLayoutChangeEventArgs,\n LayoutChangedEventArgs,\n BeforeNodeNavigateEventArgs,\n NodeNavigatedEventArgs,\n ViewportTransformEventArgs\n} from '../events/graph-events';\n\nexport const DEFAULT_GRAPH_CATEGORIES: GraphCategoryConfig[] = [\n { Category: 'person', Label: 'Person', Color: '#10b981', IconClass: 'fa-solid fa-user' },\n { Category: 'organization', Label: 'Organization', Color: '#38bdf8', IconClass: 'fa-solid fa-building' },\n { Category: 'committee', Label: 'Committee', Color: '#8b5cf6', IconClass: 'fa-solid fa-landmark' },\n { Category: 'account', Label: 'Holding / Account', Color: '#f59e0b', IconClass: 'fa-solid fa-building-flag' },\n { Category: 'asset', Label: 'Asset', Color: '#ec4899', IconClass: 'fa-solid fa-gem' },\n { Category: 'group', Label: 'Group', Color: '#06b6d4', IconClass: 'fa-solid fa-users' },\n { Category: 'custom', Label: 'Custom', Color: '#64748b', IconClass: 'fa-solid fa-circle-nodes' }\n];\n\nconst DYNAMIC_CATEGORY_PALETTE = [\n '#10b981', '#38bdf8', '#8b5cf6', '#f59e0b', '#ec4899',\n '#06b6d4', '#84cc16', '#f43f5e', '#a855f7', '#14b8a6'\n];\n\nconst GRAPH_VIEW_CSS = `\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n background: var(--mj-bg-surface-sunken, #0b1220);\n border-radius: var(--mj-radius-lg, 12px);\n border: 1px solid var(--mj-border-default, #223254);\n user-select: none;\n}\n\n.mj-graph-wrapper {\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-graph-svg-canvas {\n width: 100%;\n height: 100%;\n cursor: grab;\n}\n\n.mj-graph-svg-canvas.is-panning {\n cursor: grabbing;\n}\n\n/* Floating Toolbar */\n.mj-graph-toolbar {\n position: absolute;\n top: 14px;\n left: 14px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 8px;\n background: rgba(20, 31, 54, 0.88);\n backdrop-filter: blur(10px);\n border: 1px solid var(--mj-border-default, #223254);\n padding: 6px 12px;\n border-radius: var(--mj-radius-lg, 10px);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n}\n\n.mj-graph-tool-btn {\n background: var(--mj-bg-surface-elevated, #1a2744);\n border: 1px solid var(--mj-border-default, #223254);\n color: var(--mj-text-primary, #f8fafc);\n border-radius: 6px;\n padding: 5px 10px;\n font-size: 11.5px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s ease;\n}\n\n.mj-graph-tool-btn:hover {\n border-color: var(--mj-brand-primary, #38bdf8);\n background: var(--mj-border-hover, #334876);\n}\n\n.mj-graph-tool-btn.active {\n background: var(--mj-brand-primary, #38bdf8);\n color: #090e1a;\n border-color: var(--mj-brand-primary, #38bdf8);\n}\n\n.mj-graph-separator {\n width: 1px;\n height: 18px;\n background: var(--mj-border-default, #223254);\n}\n\n.mj-graph-search-input {\n background: var(--mj-bg-surface-sunken, #090e1a);\n border: 1px solid var(--mj-border-default, #223254);\n border-radius: 6px;\n color: var(--mj-text-primary, #f8fafc);\n padding: 4px 8px 4px 24px;\n font-size: 11.5px;\n width: 150px;\n}\n\n.mj-graph-search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary, #38bdf8);\n width: 190px;\n}\n\n/* Floating Legend */\n.mj-graph-legend {\n position: absolute;\n bottom: 14px;\n left: 14px;\n z-index: 20;\n display: flex;\n align-items: center;\n gap: 12px;\n background: rgba(20, 31, 54, 0.88);\n backdrop-filter: blur(10px);\n border: 1px solid var(--mj-border-default, #223254);\n padding: 6px 14px;\n border-radius: var(--mj-radius-lg, 10px);\n font-size: 11.5px;\n color: var(--mj-text-secondary, #94a3b8);\n}\n\n.mj-graph-legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.mj-graph-legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n}\n\n/* Inspector Drawer */\n.mj-graph-inspector {\n position: absolute;\n top: 14px;\n right: 14px;\n width: 300px;\n max-height: calc(100% - 28px);\n background: rgba(20, 31, 54, 0.95);\n backdrop-filter: blur(14px);\n border: 1px solid var(--mj-border-default, #223254);\n border-radius: var(--mj-radius-lg, 12px);\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n z-index: 25;\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);\n overflow-y: auto;\n scrollbar-width: thin;\n animation: mjGraphSlide 0.2s ease;\n}\n\n.mj-graph-inspector-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.mj-graph-inspector-title {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted, #64748b);\n}\n\n.mj-graph-inspector-close {\n background: none;\n border: none;\n color: var(--mj-text-muted, #64748b);\n cursor: pointer;\n font-size: 14px;\n}\n\n.mj-graph-inspector-card {\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--mj-bg-surface, #111a2e);\n border: 1px solid var(--mj-border-default, #223254);\n border-radius: 8px;\n padding: 10px 12px;\n}\n\n.mj-graph-inspector-avatar {\n width: 40px;\n height: 40px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n color: #fff;\n flex-shrink: 0;\n}\n\n.mj-graph-inspector-actions {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 4px;\n}\n\n/* SVG Rendering Elements */\n.mj-edge-path {\n stroke: var(--mj-border-hover, #2d416b);\n stroke-width: 2;\n fill: none;\n transition: stroke 0.2s ease;\n}\n\n.mj-edge-path.is-highlighted {\n stroke: var(--mj-brand-primary, #38bdf8);\n stroke-width: 3;\n}\n\n.mj-edge-text {\n font-size: 10px;\n font-weight: 500;\n fill: var(--mj-text-muted, #64748b);\n text-anchor: middle;\n pointer-events: none;\n paint-order: stroke;\n stroke: var(--mj-bg-surface-sunken, #0b1220);\n stroke-width: 3px;\n stroke-linecap: butt;\n stroke-linejoin: miter;\n}\n\n.mj-node-group {\n cursor: pointer;\n transition: transform 0.1s ease;\n}\n\n.mj-node-group:hover .mj-node-circle {\n filter: drop-shadow(0 0 10px var(--mj-brand-primary, #38bdf8));\n}\n\n.mj-node-circle {\n transition: all 0.2s ease;\n}\n\n.mj-node-label {\n font-size: 11px;\n font-weight: 700;\n fill: var(--mj-text-primary, #f8fafc);\n text-anchor: middle;\n pointer-events: none;\n}\n\n.mj-node-sublabel {\n font-size: 9.5px;\n font-weight: 500;\n fill: var(--mj-text-secondary, #94a3b8);\n text-anchor: middle;\n pointer-events: none;\n}\n\n@keyframes mjGraphSlide {\n from { opacity: 0; transform: translateX(20px); }\n to { opacity: 1; transform: translateX(0); }\n}\n`;\n\n/**\n * `<mj-graph-view>` — Interactive network and entity relationship graph visualization.\n *\n * Supports force-directed dynamic layout, multi-category entity badges, hop-expansion,\n * zoom/pan controls, inspector drawer, search filters, and cancelable Before/After event pairs.\n */\n@Component({\n selector: 'mj-graph-view',\n standalone: true,\n imports: [CommonModule, FormsModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"mj-graph-wrapper\" #wrapper>\n <!-- 1. Floating Action Toolbar -->\n @if (ShowToolbar) {\n <div class=\"mj-graph-toolbar\">\n <span style=\"font-size: 12px; font-weight: 700; color: #fff; display: flex; align-items: center; gap: 6px;\">\n <i class=\"fa-solid fa-circle-nodes\" style=\"color: var(--mj-brand-primary);\"></i>\n Network Graph\n </span>\n\n <div class=\"mj-graph-separator\"></div>\n\n <!-- Layout Mode Selector -->\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn\"\n [class.active]=\"LayoutMode === 'force'\"\n (click)=\"SetLayoutMode('force')\"\n title=\"Force-Directed Physics\">\n <i class=\"fa-solid fa-atom\"></i> Force\n </button>\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn\"\n [class.active]=\"LayoutMode === 'circular'\"\n (click)=\"SetLayoutMode('circular')\"\n title=\"Circular Layout\">\n <i class=\"fa-solid fa-circle-notch\"></i> Circular\n </button>\n\n <div class=\"mj-graph-separator\"></div>\n\n <!-- Zoom Controls -->\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"ZoomIn()\" title=\"Zoom In\">\n <i class=\"fa-solid fa-magnifying-glass-plus\"></i>\n </button>\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"ZoomOut()\" title=\"Zoom Out\">\n <i class=\"fa-solid fa-magnifying-glass-minus\"></i>\n </button>\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"FitToView()\" title=\"Fit View\">\n <i class=\"fa-solid fa-expand\"></i>\n </button>\n <button type=\"button\" class=\"mj-graph-tool-btn\" (click)=\"Rearrange()\" title=\"Re-simulate Physics\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n\n @if (Searchable) {\n <div class=\"mj-graph-separator\"></div>\n <div style=\"position: relative;\">\n <i class=\"fa-solid fa-magnifying-glass\" style=\"position: absolute; left: 8px; top: 50%; transform: translateY(-50%); font-size: 10px; color: var(--mj-text-muted);\"></i>\n <input\n class=\"mj-graph-search-input\"\n [(ngModel)]=\"SearchQuery\"\n (ngModelChange)=\"SearchNodes($event)\"\n placeholder=\"Search graph nodes...\" />\n </div>\n }\n </div>\n }\n\n <!-- 2. Floating Dynamic Category Legend -->\n @if (ShowLegend && ActiveCategories.length > 0) {\n <div class=\"mj-graph-legend\">\n @for (cat of ActiveCategories; track cat.Category) {\n <div class=\"mj-graph-legend-item\">\n <div class=\"mj-graph-legend-dot\" [style.background]=\"cat.Color\"></div>\n {{ cat.Label }}\n </div>\n }\n </div>\n }\n\n <!-- 3. Selected Node Inspector Drawer -->\n @if (ShowInspector && SelectedNode) {\n <div class=\"mj-graph-inspector\">\n <div class=\"mj-graph-inspector-header\">\n <span class=\"mj-graph-inspector-title\">Node Inspector</span>\n <button class=\"mj-graph-inspector-close\" type=\"button\" (click)=\"ClearSelection()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"mj-graph-inspector-card\">\n <div class=\"mj-graph-inspector-avatar\" [style.background]=\"GetNodeColor(SelectedNode)\">\n <i [class]=\"GetNodeIcon(SelectedNode)\"></i>\n </div>\n <div style=\"overflow: hidden; flex: 1;\">\n <div style=\"font-size: 13px; font-weight: 700; color: #fff; text-overflow: ellipsis; overflow: hidden; white-space: nowrap;\">\n {{ SelectedNode.Label }}\n </div>\n <div style=\"display: flex; align-items: center; gap: 6px; margin-top: 3px;\">\n <span style=\"font-size: 11px; color: var(--mj-text-secondary);\">\n {{ SelectedNode.Sublabel || SelectedNode.Category }}\n </span>\n @if (IsFocalNode(SelectedNode)) {\n <span style=\"font-size: 10px; padding: 1px 6px; border-radius: 9999px; background: rgba(56, 189, 248, 0.15); color: #38bdf8; font-weight: 600; border: 1px solid rgba(56, 189, 248, 0.3);\">Current Record</span>\n }\n </div>\n </div>\n </div>\n\n <div style=\"font-size: 11.5px; color: var(--mj-text-secondary); line-height: 1.45;\">\n Direct Connected Edges: <strong>{{ GetNodeEdgeCount(SelectedNode.ID) }}</strong>\n </div>\n\n <div class=\"mj-graph-inspector-actions\">\n @if (!IsFocalNode(SelectedNode)) {\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn active\"\n style=\"justify-content: center; width: 100%;\"\n (click)=\"NavigateToEntity(SelectedNode)\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open Entity Form\n </button>\n }\n <button\n type=\"button\"\n class=\"mj-graph-tool-btn\"\n style=\"justify-content: center; width: 100%;\"\n (click)=\"ExpandHops(SelectedNode.ID, 1)\">\n <i class=\"fa-solid fa-circle-nodes\"></i> Expand 1-Hop Neighbors\n </button>\n </div>\n </div>\n }\n\n <!-- 4. Interactive SVG Graph Canvas -->\n <svg\n #svgCanvas\n class=\"mj-graph-svg-canvas\"\n [class.is-panning]=\"IsPanning\"\n (mousedown)=\"OnCanvasMouseDown($event)\"\n (mousemove)=\"OnCanvasMouseMove($event)\"\n (mouseup)=\"OnCanvasMouseUp($event)\"\n (mouseleave)=\"OnCanvasMouseUp($event)\"\n (wheel)=\"OnCanvasWheel($event)\">\n\n <g [attr.transform]=\"'translate(' + PanX + ',' + PanY + ') scale(' + Scale + ')'\">\n <!-- Edges Layer -->\n @for (edge of Edges; track edge.ID) {\n <g>\n <line\n class=\"mj-edge-path\"\n [class.is-highlighted]=\"IsEdgeHighlighted(edge)\"\n [attr.x1]=\"GetNodeX(edge.SourceID)\"\n [attr.y1]=\"GetNodeY(edge.SourceID)\"\n [attr.x2]=\"GetNodeX(edge.TargetID)\"\n [attr.y2]=\"GetNodeY(edge.TargetID)\"\n (click)=\"OnEdgeClick(edge, $event)\">\n </line>\n <text\n class=\"mj-edge-text\"\n [attr.x]=\"(GetNodeX(edge.SourceID) + GetNodeX(edge.TargetID)) / 2\"\n [attr.y]=\"(GetNodeY(edge.SourceID) + GetNodeY(edge.TargetID)) / 2 - 4\">\n {{ edge.Label }}\n </text>\n </g>\n }\n\n <!-- Nodes Layer -->\n @for (node of FilteredNodes; track node.ID) {\n <g\n class=\"mj-node-group\"\n [attr.transform]=\"'translate(' + (node.X || 0) + ',' + (node.Y || 0) + ')'\"\n (mousedown)=\"OnNodeMouseDown(node, $event)\"\n (click)=\"OnNodeClick(node, $event)\"\n (dblclick)=\"NavigateToEntity(node)\">\n\n <!-- Outer Selection / Focal Halo -->\n @if (IsNodeSelected(node)) {\n <circle r=\"36\" fill=\"rgba(56, 189, 248, 0.2)\" stroke=\"#38bdf8\" stroke-width=\"2\"></circle>\n } @else if (IsFocalNode(node)) {\n <circle r=\"34\" fill=\"none\" stroke=\"rgba(56, 189, 248, 0.45)\" stroke-width=\"2\" stroke-dasharray=\"4 3\"></circle>\n }\n\n <!-- Node Body Circle -->\n <circle\n class=\"mj-node-circle\"\n [attr.r]=\"node.Radius || 26\"\n fill=\"#141f36\"\n [attr.stroke]=\"GetNodeColor(node)\"\n stroke-width=\"3\">\n </circle>\n\n <!-- Node Icon Center -->\n <circle [attr.r]=\"(node.Radius || 26) - 8\" [attr.fill]=\"GetNodeColor(node)\"></circle>\n\n <!-- Labels -->\n <text class=\"mj-node-label\" [attr.y]=\"(node.Radius || 26) + 16\">\n {{ TruncateLabel(node.Label, 18) }}\n </text>\n @if (node.Sublabel) {\n <text class=\"mj-node-sublabel\" [attr.y]=\"(node.Radius || 26) + 28\">\n {{ TruncateLabel(node.Sublabel, 20) }}\n </text>\n }\n </g>\n }\n </g>\n </svg>\n </div>\n `,\n styles: [GRAPH_VIEW_CSS]\n})\nexport class GraphViewComponent implements OnInit, OnChanges, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n @ViewChild('wrapper', { static: true }) public WrapperRef!: ElementRef<HTMLDivElement>;\n @ViewChild('svgCanvas', { static: true }) public CanvasRef!: ElementRef<SVGSVGElement>;\n\n // ── Inputs ────────────────────────────────────────────────────────\n @Input() public Nodes: GraphNode[] = [];\n @Input() public Edges: GraphEdge[] = [];\n @Input() public Categories: GraphCategoryConfig[] = DEFAULT_GRAPH_CATEGORIES;\n @Input() public LayoutMode: GraphLayoutMode = 'force';\n @Input() public MaxHopDistance = 2;\n @Input() public SelectedNodeId?: string;\n @Input() public FocalNodeId?: string;\n @Input() public AutoOpenInspector = false;\n @Input() public ShowToolbar = true;\n @Input() public ShowLegend = true;\n @Input() public ShowInspector = true;\n @Input() public Searchable = true;\n @Input() public AutoFitOnLoad = true;\n @Input() public Physics: GraphPhysicsConfig = {\n Repulsion: -500,\n LinkDistance: 130,\n Gravity: 0.05,\n Damping: 0.88,\n MaxIterations: 200\n };\n\n // ── Cancelable Before/After Outputs ────────────────────────────────\n @Output() public BeforeNodeSelect = new EventEmitter<BeforeNodeSelectEventArgs>();\n @Output() public NodeSelected = new EventEmitter<NodeSelectedEventArgs>();\n\n @Output() public BeforeEdgeSelect = new EventEmitter<BeforeEdgeSelectEventArgs>();\n @Output() public EdgeSelected = new EventEmitter<EdgeSelectedEventArgs>();\n\n @Output() public BeforeHopExpand = new EventEmitter<BeforeHopExpandEventArgs>();\n @Output() public HopExpanded = new EventEmitter<HopExpandedEventArgs>();\n\n @Output() public BeforeLayoutChange = new EventEmitter<BeforeLayoutChangeEventArgs>();\n @Output() public LayoutChanged = new EventEmitter<LayoutChangedEventArgs>();\n\n @Output() public BeforeNodeNavigate = new EventEmitter<BeforeNodeNavigateEventArgs>();\n @Output() public NodeNavigated = new EventEmitter<NodeNavigatedEventArgs>();\n\n @Output() public ViewportTransform = new EventEmitter<ViewportTransformEventArgs>();\n\n // ── Internal State ────────────────────────────────────────────────\n public FilteredNodes: GraphNode[] = [];\n public SelectedNode: GraphNode | null = null;\n public SelectedEdge: GraphEdge | null = null;\n public SearchQuery = '';\n\n public Scale = 1.0;\n public PanX = 200;\n public PanY = 150;\n public IsPanning = false;\n private panStartX = 0;\n private panStartY = 0;\n\n private draggedNode: GraphNode | null = null;\n private simulation: d3.Simulation<GraphNode, d3.SimulationLinkDatum<GraphNode>> | null = null;\n\n public ngOnInit(): void {\n this.initializePositions();\n this.ApplyFilters();\n this.SimulatePhysics();\n }\n\n public ngOnChanges(changes: SimpleChanges): void {\n if (changes['Nodes'] || changes['Edges']) {\n this.initializePositions();\n this.ApplyFilters();\n this.SimulatePhysics();\n }\n if (changes['SelectedNodeId'] && this.SelectedNodeId) {\n const found = this.Nodes.find(n => UUIDsEqual(n.ID, this.SelectedNodeId));\n if (found && this.AutoOpenInspector) {\n this.SelectedNode = found;\n }\n }\n }\n\n public ngOnDestroy(): void {\n if (this.simulation) {\n this.simulation.stop();\n this.simulation = null;\n }\n }\n\n // ── Positioning & D3 Physics Engine ──────────────────────────────────\n private initializePositions(): void {\n const cx = this.getContainerWidth() / 2 || 400;\n const cy = this.getContainerHeight() / 2 || 250;\n const count = this.Nodes.length || 1;\n\n this.Nodes.forEach((node, idx) => {\n if (node.x === undefined || node.y === undefined) {\n if (node.X !== undefined && node.Y !== undefined) {\n node.x = node.X;\n node.y = node.Y;\n } else {\n const angle = (idx / count) * 2 * Math.PI;\n const r = 100 + Math.random() * 60;\n node.x = cx + r * Math.cos(angle);\n node.y = cy + r * Math.sin(angle);\n node.X = node.x;\n node.Y = node.y;\n }\n node.vx = 0;\n node.vy = 0;\n node.VX = 0;\n node.VY = 0;\n }\n });\n }\n\n public SimulatePhysics(): void {\n if (this.LayoutMode === 'circular') {\n this.applyCircularLayout();\n if (this.AutoFitOnLoad) {\n this.AutoFitToView();\n }\n this.cdr.markForCheck();\n return;\n }\n\n if (this.simulation) {\n this.simulation.stop();\n this.simulation = null;\n }\n\n const cx = this.getContainerWidth() / 2 || 400;\n const cy = this.getContainerHeight() / 2 || 250;\n\n // Resolve endpoints to node objects so a case-skewed UUID or dangling\n // edge cannot throw \"node not found\" out of ngOnInit. Raw-string\n // .id(d => d.ID) compared with ===, which UUIDsEqual elsewhere\n // deliberately does not.\n const nodeById = new Map<string, GraphNode>();\n for (const node of this.Nodes) {\n const key = NormalizeUUID(node.ID);\n if (key) {\n nodeById.set(key, node);\n }\n }\n const links: d3.SimulationLinkDatum<GraphNode>[] = [];\n for (const edge of this.Edges) {\n const source = nodeById.get(NormalizeUUID(edge.SourceID));\n const target = nodeById.get(NormalizeUUID(edge.TargetID));\n if (source && target) {\n links.push({ source, target });\n }\n }\n\n this.simulation = d3.forceSimulation<GraphNode>(this.Nodes)\n .force('link', d3.forceLink<GraphNode, d3.SimulationLinkDatum<GraphNode>>(links)\n .distance(this.Physics.LinkDistance || 120))\n .force('charge', d3.forceManyBody<GraphNode>()\n .strength(this.Physics.Repulsion || -450)\n .distanceMax(700))\n .force('collide', d3.forceCollide<GraphNode>((d: GraphNode) => (d.Radius || 26) + 16).iterations(2))\n .force('center', d3.forceCenter<GraphNode>(cx, cy));\n\n // Headless Pre-Warming: run 80 ticks synchronously in memory so the SVG renders resting immediately\n this.simulation.stop();\n for (let i = 0; i < 90; ++i) {\n this.simulation.tick();\n }\n\n this.syncCoordinates();\n\n // Auto-center & fit into view\n if (this.AutoFitOnLoad) {\n this.AutoFitToView();\n }\n\n // Attach live tick handler for interactive drag & drop\n this.simulation.on('tick', () => {\n this.syncCoordinates();\n this.cdr.markForCheck();\n });\n\n this.cdr.markForCheck();\n }\n\n private syncCoordinates(): void {\n for (const n of this.Nodes) {\n n.X = n.x;\n n.Y = n.y;\n n.VX = n.vx;\n n.VY = n.vy;\n }\n }\n\n private applyCircularLayout(): void {\n const cx = this.getContainerWidth() / 2 || 400;\n const cy = this.getContainerHeight() / 2 || 250;\n const r = Math.min(cx, cy) * 0.7 || 160;\n const count = this.Nodes.length || 1;\n\n this.Nodes.forEach((node, idx) => {\n const angle = (idx / count) * 2 * Math.PI;\n node.x = cx + r * Math.cos(angle);\n node.y = cy + r * Math.sin(angle);\n node.X = node.x;\n node.Y = node.y;\n });\n }\n\n // ── Public Programmatic Methods ───────────────────────────────────\n public ZoomIn(): void {\n this.Scale = Math.min(this.Scale * 1.2, 3.0);\n this.emitViewport();\n }\n\n public ZoomOut(): void {\n this.Scale = Math.max(this.Scale / 1.2, 0.3);\n this.emitViewport();\n }\n\n public FitToView(): void {\n this.AutoFitToView(40);\n }\n\n public AutoFitToView(padding = 40): void {\n if (!this.Nodes || this.Nodes.length === 0) return;\n\n const width = this.getContainerWidth() || 800;\n const height = this.getContainerHeight() || 420;\n\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;\n for (const n of this.Nodes) {\n const nx = n.x ?? n.X ?? 0;\n const ny = n.y ?? n.Y ?? 0;\n const r = (n.Radius || 26) + 20; // include badge & labels\n if (nx - r < minX) minX = nx - r;\n if (nx + r > maxX) maxX = nx + r;\n if (ny - r < minY) minY = ny - r;\n if (ny + r > maxY) maxY = ny + r;\n }\n\n if (!isFinite(minX) || !isFinite(maxX) || !isFinite(minY) || !isFinite(maxY)) return;\n\n const bboxW = Math.max(maxX - minX, 100);\n const bboxH = Math.max(maxY - minY, 100);\n\n const scaleX = (width - padding * 2) / bboxW;\n const scaleY = (height - padding * 2) / bboxH;\n const targetScale = Math.min(Math.max(Math.min(scaleX, scaleY), 0.4), 1.25);\n\n this.Scale = targetScale;\n this.PanX = (width - (minX + maxX) * this.Scale) / 2;\n this.PanY = (height - (minY + maxY) * this.Scale) / 2;\n\n this.emitViewport();\n this.cdr.markForCheck();\n }\n\n public Rearrange(): void {\n this.initializePositions();\n this.SimulatePhysics();\n }\n\n public SetLayoutMode(mode: GraphLayoutMode): void {\n const beforeEvent = new BeforeLayoutChangeEventArgs(mode, this.LayoutMode);\n this.BeforeLayoutChange.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n const prev = this.LayoutMode;\n this.LayoutMode = mode;\n this.SimulatePhysics();\n this.LayoutChanged.emit(new LayoutChangedEventArgs(mode, prev));\n }\n\n public SelectNode(id: string): void {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, id));\n if (node) this.OnNodeClick(node, new MouseEvent('click'));\n }\n\n public ClearSelection(): void {\n this.SelectedNode = null;\n this.SelectedEdge = null;\n this.cdr.markForCheck();\n }\n\n public SearchNodes(query: string): void {\n this.SearchQuery = query;\n this.ApplyFilters();\n }\n\n public ExpandHops(nodeId: string, depth: number): void {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, nodeId));\n if (!node) return;\n\n const beforeEvent = new BeforeHopExpandEventArgs(node, (node.HopDistance || 0) + depth, node.HopDistance || 0);\n this.BeforeHopExpand.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n // Fire after event for host listener to fetch incremental relations\n this.HopExpanded.emit(new HopExpandedEventArgs(node, [], [], (node.HopDistance || 0) + depth));\n }\n\n // ── Event Handlers ────────────────────────────────────────────────\n public OnNodeClick(node: GraphNode, event: MouseEvent): void {\n event.stopPropagation();\n const beforeEvent = new BeforeNodeSelectEventArgs(node, this.SelectedNode);\n this.BeforeNodeSelect.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n const prev = this.SelectedNode;\n this.SelectedNode = node;\n this.SelectedEdge = null;\n this.NodeSelected.emit(new NodeSelectedEventArgs(node, prev));\n this.cdr.markForCheck();\n }\n\n public OnEdgeClick(edge: GraphEdge, event: MouseEvent): void {\n event.stopPropagation();\n const src = this.Nodes.find(n => UUIDsEqual(n.ID, edge.SourceID)) || null;\n const tgt = this.Nodes.find(n => UUIDsEqual(n.ID, edge.TargetID)) || null;\n\n const beforeEvent = new BeforeEdgeSelectEventArgs(edge, src, tgt);\n this.BeforeEdgeSelect.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n this.SelectedEdge = edge;\n this.EdgeSelected.emit(new EdgeSelectedEventArgs(edge, src, tgt));\n this.cdr.markForCheck();\n }\n\n public NavigateToEntity(node: GraphNode): void {\n // Focal-node dblclick must not navigate away from the record this\n // graph is illustrating — the inspector already hides the navigate\n // button for the same reason.\n if (this.IsFocalNode(node)) {\n return;\n }\n const rawId = this.stripCategoryPrefix(String(node.Data?.['ID'] || node.ID));\n const entityName = String(node.Data?.['EntityName'] || (node.Category === 'person' ? 'MJ_BizApps_Common: People' : 'MJ_BizApps_Common: Organizations'));\n const beforeEvent = new BeforeNodeNavigateEventArgs(node, entityName, rawId);\n this.BeforeNodeNavigate.emit(beforeEvent);\n if (beforeEvent.Cancel) return;\n\n // Hosts navigate. This widget is L1 and must not import Explorer's\n // NavigationService — emit the intent and let the surface open the record.\n this.NodeNavigated.emit(new NodeNavigatedEventArgs(node, entityName, rawId));\n }\n\n // ── Mouse Drag & Pan Handlers ─────────────────────────────────────\n public OnCanvasMouseDown(event: MouseEvent): void {\n if (event.target === this.CanvasRef.nativeElement || (event.target as HTMLElement).tagName === 'svg') {\n this.IsPanning = true;\n this.panStartX = event.clientX - this.PanX;\n this.panStartY = event.clientY - this.PanY;\n }\n }\n\n public OnCanvasMouseMove(event: MouseEvent): void {\n if (this.IsPanning) {\n this.PanX = event.clientX - this.panStartX;\n this.PanY = event.clientY - this.panStartY;\n this.emitViewport();\n this.cdr.markForCheck();\n } else if (this.draggedNode) {\n const coords = this.getCanvasRelativeCoords(event);\n this.draggedNode.fx = coords.x;\n this.draggedNode.fy = coords.y;\n this.draggedNode.x = coords.x;\n this.draggedNode.y = coords.y;\n this.draggedNode.X = coords.x;\n this.draggedNode.Y = coords.y;\n this.cdr.markForCheck();\n }\n }\n\n public OnCanvasMouseUp(_event: MouseEvent): void {\n this.IsPanning = false;\n if (this.draggedNode) {\n this.draggedNode.fx = null;\n this.draggedNode.fy = null;\n this.draggedNode = null;\n if (this.simulation) {\n this.simulation.alphaTarget(0);\n }\n }\n }\n\n public OnCanvasWheel(event: WheelEvent): void {\n event.preventDefault();\n const delta = event.deltaY > 0 ? 0.9 : 1.1;\n this.Scale = Math.min(Math.max(this.Scale * delta, 0.3), 3.0);\n this.emitViewport();\n this.cdr.markForCheck();\n }\n\n public OnNodeMouseDown(node: GraphNode, event: MouseEvent): void {\n event.stopPropagation();\n this.draggedNode = node;\n node.fx = node.x ?? node.X;\n node.fy = node.y ?? node.Y;\n if (this.simulation) {\n this.simulation.alphaTarget(0.3).restart();\n }\n }\n\n private emitViewport(): void {\n this.ViewportTransform.emit(new ViewportTransformEventArgs({ Scale: this.Scale, PanX: this.PanX, PanY: this.PanY }));\n this.cdr.markForCheck();\n }\n\n private getCanvasRelativeCoords(event: MouseEvent): { x: number; y: number } {\n const canvas = this.CanvasRef?.nativeElement;\n if (!canvas) {\n return {\n x: (event.clientX - this.PanX) / this.Scale,\n y: (event.clientY - this.PanY) / this.Scale\n };\n }\n const rect = canvas.getBoundingClientRect();\n const mouseX = event.clientX - rect.left;\n const mouseY = event.clientY - rect.top;\n return {\n x: (mouseX - this.PanX) / this.Scale,\n y: (mouseY - this.PanY) / this.Scale\n };\n }\n\n private getContainerWidth(): number {\n return this.CanvasRef?.nativeElement?.clientWidth || this.WrapperRef?.nativeElement?.clientWidth || 800;\n }\n\n private getContainerHeight(): number {\n return this.CanvasRef?.nativeElement?.clientHeight || this.WrapperRef?.nativeElement?.clientHeight || 420;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────\n public ApplyFilters(): void {\n const q = this.SearchQuery.trim().toLowerCase();\n this.FilteredNodes = this.Nodes.filter(n => {\n return !q || n.Label.toLowerCase().includes(q) || (n.Sublabel && n.Sublabel.toLowerCase().includes(q));\n });\n this.cdr.markForCheck();\n }\n\n public GetNodeX(id: string): number {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, id));\n return node ? (node.X ?? node.x ?? 0) : 0;\n }\n\n public GetNodeY(id: string): number {\n const node = this.Nodes.find(n => UUIDsEqual(n.ID, id));\n return node ? (node.Y ?? node.y ?? 0) : 0;\n }\n\n public GetCategoryConfig(category?: string): GraphCategoryConfig {\n const key = String(category || '').toLowerCase();\n const found = this.Categories?.find(c => c.Category.toLowerCase() === key);\n if (found) return found;\n\n const builtin = DEFAULT_GRAPH_CATEGORIES.find(c => c.Category.toLowerCase() === key);\n if (builtin) return builtin;\n\n // Deterministic palette hash for arbitrary consumer-defined categories\n const hash = Math.abs(key.split('').reduce((acc, ch) => ((acc << 5) - acc) + ch.charCodeAt(0), 0));\n const fallbackColor = DYNAMIC_CATEGORY_PALETTE[hash % DYNAMIC_CATEGORY_PALETTE.length];\n const formattedLabel = category ? category.charAt(0).toUpperCase() + category.slice(1) : 'Item';\n return {\n Category: category || 'custom',\n Label: formattedLabel,\n Color: fallbackColor,\n IconClass: 'fa-solid fa-circle-dot'\n };\n }\n\n public get ActiveCategories(): GraphCategoryConfig[] {\n const present = new Set<string>();\n for (const n of this.Nodes) {\n if (n.Category) present.add(n.Category.toLowerCase());\n }\n const result: GraphCategoryConfig[] = [];\n for (const catKey of present) {\n result.push(this.GetCategoryConfig(catKey));\n }\n return result;\n }\n\n public GetCategoryLabel(category?: string): string {\n return this.GetCategoryConfig(category).Label;\n }\n\n public GetNodeColor(node: GraphNode): string {\n return node.Color ?? this.GetCategoryConfig(node.Category).Color;\n }\n\n public GetNodeIcon(node: GraphNode): string {\n return node.IconClass ?? this.GetCategoryConfig(node.Category).IconClass ?? 'fa-solid fa-circle-dot';\n }\n\n public GetNodeEdgeCount(id: string): number {\n return this.Edges.filter(e => UUIDsEqual(e.SourceID, id) || UUIDsEqual(e.TargetID, id)).length;\n }\n\n public IsEdgeHighlighted(edge: GraphEdge): boolean {\n return UUIDsEqual(this.SelectedEdge?.ID, edge.ID) ||\n UUIDsEqual(this.SelectedNode?.ID, edge.SourceID) ||\n UUIDsEqual(this.SelectedNode?.ID, edge.TargetID);\n }\n\n /**\n * Whether this node is the selected one. Exists as a method because the template cannot call\n * an imported function directly, and the comparison must go through UUIDsEqual rather than ===.\n */\n public IsNodeSelected(node: GraphNode): boolean {\n return UUIDsEqual(this.SelectedNode?.ID, node.ID);\n }\n\n public TruncateLabel(text: string, maxLen: number): string {\n if (!text) return '';\n return text.length > maxLen ? text.substring(0, maxLen - 1) + '…' : text;\n }\n\n public IsFocalNode(node: GraphNode): boolean {\n if (!node || !this.FocalNodeId) return false;\n const focalClean = this.stripCategoryPrefix(this.FocalNodeId).toLowerCase();\n const nodeClean = this.stripCategoryPrefix(node.ID).toLowerCase();\n const dataClean = node.Data?.['ID'] ? String(node.Data['ID']).toLowerCase() : '';\n return UUIDsEqual(node.ID, this.FocalNodeId) || nodeClean === focalClean || dataClean === focalClean;\n }\n\n /**\n * Hosts prefix node IDs with `{category}:`. Categories are dynamic\n * (`organization`, `asset`, `group`, plus caller keys), so strip any\n * known-category prefix rather than a hardcoded subset.\n */\n private stripCategoryPrefix(id: string): string {\n const idx = id.indexOf(':');\n if (idx <= 0) {\n return id;\n }\n const prefix = id.slice(0, idx).toLowerCase();\n const known =\n this.Categories?.some(c => c.Category.toLowerCase() === prefix) ||\n DEFAULT_GRAPH_CATEGORIES.some(c => c.Category.toLowerCase() === prefix);\n return known ? id.slice(idx + 1) : id;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-graph-view",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.2.0-edge.1",
|
|
4
4
|
"description": "MemberJunction: Generic interactive network and relationship graph viewer with force-directed physics, Before/After cancelable events, and MJ design tokens",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"@angular/forms": "^21.2.22"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@memberjunction/ng-ui-components": "6.1
|
|
32
|
-
"@memberjunction/global": "6.1
|
|
31
|
+
"@memberjunction/ng-ui-components": "6.2.0-edge.1",
|
|
32
|
+
"@memberjunction/global": "6.2.0-edge.1",
|
|
33
33
|
"@types/d3": "^7.4.3",
|
|
34
34
|
"d3": "^7.9.0",
|
|
35
35
|
"tslib": "^2.8.1",
|