@aiready/components 0.1.0 → 0.1.3
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.
- package/dist/charts/ForceDirectedGraph.d.ts +17 -2
- package/dist/charts/ForceDirectedGraph.js +518 -196
- package/dist/charts/ForceDirectedGraph.js.map +1 -1
- package/dist/hooks/useForceSimulation.d.ts +4 -1
- package/dist/hooks/useForceSimulation.js +34 -5
- package/dist/hooks/useForceSimulation.js.map +1 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +651 -194
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/smoke.test.ts +5 -0
- package/src/charts/ForceDirectedGraph.tsx +441 -58
- package/src/charts/GraphControls.tsx +218 -0
- package/src/hooks/useForceSimulation.ts +49 -3
- package/src/index.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React2 from 'react';
|
|
2
|
-
import { useState, useEffect,
|
|
2
|
+
import { forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback } from 'react';
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
-
import * as
|
|
7
|
+
import * as d33 from 'd3';
|
|
8
8
|
|
|
9
9
|
// src/components/button.tsx
|
|
10
10
|
function cn(...inputs) {
|
|
@@ -595,7 +595,7 @@ function useD3(renderFn, dependencies = []) {
|
|
|
595
595
|
const ref = useRef(null);
|
|
596
596
|
useEffect(() => {
|
|
597
597
|
if (ref.current) {
|
|
598
|
-
const selection =
|
|
598
|
+
const selection = d33.select(ref.current);
|
|
599
599
|
renderFn(selection);
|
|
600
600
|
}
|
|
601
601
|
}, dependencies);
|
|
@@ -605,7 +605,7 @@ function useD3WithResize(renderFn, dependencies = []) {
|
|
|
605
605
|
const ref = useRef(null);
|
|
606
606
|
useEffect(() => {
|
|
607
607
|
if (!ref.current) return;
|
|
608
|
-
const selection =
|
|
608
|
+
const selection = d33.select(ref.current);
|
|
609
609
|
const render = () => renderFn(selection);
|
|
610
610
|
render();
|
|
611
611
|
const resizeObserver = new ResizeObserver(() => {
|
|
@@ -629,7 +629,8 @@ function useForceSimulation(initialNodes, initialLinks, options) {
|
|
|
629
629
|
width,
|
|
630
630
|
height,
|
|
631
631
|
alphaDecay = 0.0228,
|
|
632
|
-
velocityDecay = 0.4
|
|
632
|
+
velocityDecay = 0.4,
|
|
633
|
+
onTick
|
|
633
634
|
} = options;
|
|
634
635
|
const [nodes, setNodes] = useState(initialNodes);
|
|
635
636
|
const [links, setLinks] = useState(initialLinks);
|
|
@@ -639,15 +640,22 @@ function useForceSimulation(initialNodes, initialLinks, options) {
|
|
|
639
640
|
useEffect(() => {
|
|
640
641
|
const nodesCopy = initialNodes.map((node) => ({ ...node }));
|
|
641
642
|
const linksCopy = initialLinks.map((link) => ({ ...link }));
|
|
642
|
-
const simulation =
|
|
643
|
+
const simulation = d33.forceSimulation(nodesCopy).force(
|
|
643
644
|
"link",
|
|
644
|
-
|
|
645
|
-
).force("charge",
|
|
645
|
+
d33.forceLink(linksCopy).id((d) => d.id).distance((d) => d && d.distance != null ? d.distance : linkDistance).strength(linkStrength)
|
|
646
|
+
).force("charge", d33.forceManyBody().strength(chargeStrength)).force("center", d33.forceCenter(width / 2, height / 2).strength(centerStrength)).force(
|
|
646
647
|
"collision",
|
|
647
|
-
|
|
648
|
+
d33.forceCollide().radius((d) => {
|
|
649
|
+
const nodeSize = d && d.size ? d.size : 10;
|
|
650
|
+
return nodeSize + collisionRadius;
|
|
651
|
+
}).strength(collisionStrength)
|
|
648
652
|
).alphaDecay(alphaDecay).velocityDecay(velocityDecay);
|
|
649
653
|
simulationRef.current = simulation;
|
|
650
654
|
simulation.on("tick", () => {
|
|
655
|
+
try {
|
|
656
|
+
if (typeof onTick === "function") onTick(nodesCopy, linksCopy, simulation);
|
|
657
|
+
} catch (e) {
|
|
658
|
+
}
|
|
651
659
|
setNodes([...nodesCopy]);
|
|
652
660
|
setLinks([...linksCopy]);
|
|
653
661
|
setAlpha(simulation.alpha());
|
|
@@ -671,7 +679,8 @@ function useForceSimulation(initialNodes, initialLinks, options) {
|
|
|
671
679
|
width,
|
|
672
680
|
height,
|
|
673
681
|
alphaDecay,
|
|
674
|
-
velocityDecay
|
|
682
|
+
velocityDecay,
|
|
683
|
+
onTick
|
|
675
684
|
]);
|
|
676
685
|
const restart = () => {
|
|
677
686
|
if (simulationRef.current) {
|
|
@@ -685,13 +694,33 @@ function useForceSimulation(initialNodes, initialLinks, options) {
|
|
|
685
694
|
setIsRunning(false);
|
|
686
695
|
}
|
|
687
696
|
};
|
|
697
|
+
const originalForcesRef = useRef({ charge: chargeStrength, link: linkStrength, collision: collisionStrength });
|
|
698
|
+
const forcesEnabledRef = useRef(true);
|
|
699
|
+
const setForcesEnabled = (enabled) => {
|
|
700
|
+
const sim = simulationRef.current;
|
|
701
|
+
if (!sim) return;
|
|
702
|
+
if (forcesEnabledRef.current === enabled) return;
|
|
703
|
+
forcesEnabledRef.current = enabled;
|
|
704
|
+
try {
|
|
705
|
+
const charge = sim.force("charge");
|
|
706
|
+
if (charge && typeof charge.strength === "function") {
|
|
707
|
+
charge.strength(enabled ? originalForcesRef.current.charge : 0);
|
|
708
|
+
}
|
|
709
|
+
const link = sim.force("link");
|
|
710
|
+
if (link && typeof link.strength === "function") {
|
|
711
|
+
link.strength(enabled ? originalForcesRef.current.link : 0);
|
|
712
|
+
}
|
|
713
|
+
} catch (e) {
|
|
714
|
+
}
|
|
715
|
+
};
|
|
688
716
|
return {
|
|
689
717
|
nodes,
|
|
690
718
|
links,
|
|
691
719
|
restart,
|
|
692
720
|
stop,
|
|
693
721
|
isRunning,
|
|
694
|
-
alpha
|
|
722
|
+
alpha,
|
|
723
|
+
setForcesEnabled
|
|
695
724
|
};
|
|
696
725
|
}
|
|
697
726
|
function useDrag(simulation) {
|
|
@@ -717,211 +746,639 @@ function useDrag(simulation) {
|
|
|
717
746
|
onDragEnd: dragEnded
|
|
718
747
|
};
|
|
719
748
|
}
|
|
720
|
-
var ForceDirectedGraph = (
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
height,
|
|
725
|
-
simulationOptions,
|
|
726
|
-
enableZoom = true,
|
|
727
|
-
enableDrag = true,
|
|
728
|
-
onNodeClick,
|
|
729
|
-
onNodeHover,
|
|
730
|
-
onLinkClick,
|
|
731
|
-
selectedNodeId,
|
|
732
|
-
hoveredNodeId,
|
|
733
|
-
defaultNodeColor = "#69b3a2",
|
|
734
|
-
defaultNodeSize = 10,
|
|
735
|
-
defaultLinkColor = "#999",
|
|
736
|
-
defaultLinkWidth = 1,
|
|
737
|
-
showNodeLabels = true,
|
|
738
|
-
showLinkLabels = false,
|
|
739
|
-
className
|
|
740
|
-
}) => {
|
|
741
|
-
const svgRef = useRef(null);
|
|
742
|
-
const gRef = useRef(null);
|
|
743
|
-
const [transform, setTransform] = useState({ k: 1, x: 0, y: 0 });
|
|
744
|
-
const { nodes, links, restart } = useForceSimulation(initialNodes, initialLinks, {
|
|
749
|
+
var ForceDirectedGraph = forwardRef(
|
|
750
|
+
({
|
|
751
|
+
nodes: initialNodes,
|
|
752
|
+
links: initialLinks,
|
|
745
753
|
width,
|
|
746
754
|
height,
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
755
|
+
simulationOptions,
|
|
756
|
+
enableZoom = true,
|
|
757
|
+
enableDrag = true,
|
|
758
|
+
onNodeClick,
|
|
759
|
+
onNodeHover,
|
|
760
|
+
onLinkClick,
|
|
761
|
+
selectedNodeId,
|
|
762
|
+
hoveredNodeId,
|
|
763
|
+
defaultNodeColor = "#69b3a2",
|
|
764
|
+
defaultNodeSize = 10,
|
|
765
|
+
defaultLinkColor = "#999",
|
|
766
|
+
defaultLinkWidth = 1,
|
|
767
|
+
showNodeLabels = true,
|
|
768
|
+
showLinkLabels = false,
|
|
769
|
+
className,
|
|
770
|
+
manualLayout = false,
|
|
771
|
+
onManualLayoutChange,
|
|
772
|
+
packageBounds
|
|
773
|
+
}, ref) => {
|
|
774
|
+
const svgRef = useRef(null);
|
|
775
|
+
const gRef = useRef(null);
|
|
776
|
+
const [transform, setTransform] = useState({ k: 1, x: 0, y: 0 });
|
|
777
|
+
const dragNodeRef = useRef(null);
|
|
778
|
+
const dragActiveRef = useRef(false);
|
|
779
|
+
const [pinnedNodes, setPinnedNodes] = useState(/* @__PURE__ */ new Set());
|
|
780
|
+
const internalDragEnabledRef = useRef(enableDrag);
|
|
781
|
+
useEffect(() => {
|
|
782
|
+
internalDragEnabledRef.current = enableDrag;
|
|
783
|
+
}, [enableDrag]);
|
|
784
|
+
const onTick = (nodesCopy, _linksCopy, _sim) => {
|
|
785
|
+
const bounds = packageBounds && Object.keys(packageBounds).length ? packageBounds : void 0;
|
|
786
|
+
let effectiveBounds = bounds;
|
|
787
|
+
if (!effectiveBounds) {
|
|
788
|
+
try {
|
|
789
|
+
const counts = {};
|
|
790
|
+
(initialNodes || []).forEach((n) => {
|
|
791
|
+
if (n && n.kind === "file") {
|
|
792
|
+
const g = n.packageGroup || "root";
|
|
793
|
+
counts[g] = (counts[g] || 0) + 1;
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
const children = Object.keys(counts).map((k) => ({ name: k, value: counts[k] }));
|
|
797
|
+
if (children.length > 0) {
|
|
798
|
+
const root = d33.hierarchy({ children }).sum((d) => d.value);
|
|
799
|
+
const pack2 = d33.pack().size([width, height]).padding(30);
|
|
800
|
+
const packed = pack2(root);
|
|
801
|
+
const map = {};
|
|
802
|
+
if (packed.children) {
|
|
803
|
+
packed.children.forEach((c) => {
|
|
804
|
+
map[`pkg:${c.data.name}`] = { x: c.x, y: c.y, r: c.r * 0.95 };
|
|
805
|
+
});
|
|
806
|
+
effectiveBounds = map;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
} catch (e) {
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
if (!effectiveBounds) return;
|
|
813
|
+
try {
|
|
814
|
+
Object.values(nodesCopy).forEach((n) => {
|
|
815
|
+
if (!n) return;
|
|
816
|
+
if (n.kind === "package") return;
|
|
817
|
+
const pkg = n.packageGroup;
|
|
818
|
+
if (!pkg) return;
|
|
819
|
+
const bound = effectiveBounds[`pkg:${pkg}`];
|
|
820
|
+
if (!bound) return;
|
|
821
|
+
const margin = (n.size || 10) + 12;
|
|
822
|
+
const dx = (n.x || 0) - bound.x;
|
|
823
|
+
const dy = (n.y || 0) - bound.y;
|
|
824
|
+
const dist = Math.sqrt(dx * dx + dy * dy) || 1e-4;
|
|
825
|
+
const maxDist = Math.max(1, bound.r - margin);
|
|
826
|
+
if (dist > maxDist) {
|
|
827
|
+
const desiredX = bound.x + dx * (maxDist / dist);
|
|
828
|
+
const desiredY = bound.y + dy * (maxDist / dist);
|
|
829
|
+
const softness = 0.08;
|
|
830
|
+
n.vx = (n.vx || 0) + (desiredX - n.x) * softness;
|
|
831
|
+
n.vy = (n.vy || 0) + (desiredY - n.y) * softness;
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
} catch (e) {
|
|
835
|
+
}
|
|
760
836
|
};
|
|
761
|
-
|
|
762
|
-
const handleDragStart = useCallback(
|
|
763
|
-
(event, node) => {
|
|
764
|
-
if (!enableDrag) return;
|
|
765
|
-
event.stopPropagation();
|
|
766
|
-
node.fx = node.x;
|
|
767
|
-
node.fy = node.y;
|
|
768
|
-
restart();
|
|
769
|
-
},
|
|
770
|
-
[enableDrag, restart]
|
|
771
|
-
);
|
|
772
|
-
const handleDrag = useCallback(
|
|
773
|
-
(event, node) => {
|
|
774
|
-
if (!enableDrag) return;
|
|
775
|
-
const svg = svgRef.current;
|
|
776
|
-
if (!svg) return;
|
|
777
|
-
const rect = svg.getBoundingClientRect();
|
|
778
|
-
const x = (event.clientX - rect.left - transform.x) / transform.k;
|
|
779
|
-
const y = (event.clientY - rect.top - transform.y) / transform.k;
|
|
780
|
-
node.fx = x;
|
|
781
|
-
node.fy = y;
|
|
782
|
-
},
|
|
783
|
-
[enableDrag, transform]
|
|
784
|
-
);
|
|
785
|
-
const handleDragEnd = useCallback(
|
|
786
|
-
(event, node) => {
|
|
787
|
-
if (!enableDrag) return;
|
|
788
|
-
event.stopPropagation();
|
|
789
|
-
node.fx = null;
|
|
790
|
-
node.fy = null;
|
|
791
|
-
},
|
|
792
|
-
[enableDrag]
|
|
793
|
-
);
|
|
794
|
-
const handleNodeClick = useCallback(
|
|
795
|
-
(node) => {
|
|
796
|
-
onNodeClick?.(node);
|
|
797
|
-
},
|
|
798
|
-
[onNodeClick]
|
|
799
|
-
);
|
|
800
|
-
const handleNodeMouseEnter = useCallback(
|
|
801
|
-
(node) => {
|
|
802
|
-
onNodeHover?.(node);
|
|
803
|
-
},
|
|
804
|
-
[onNodeHover]
|
|
805
|
-
);
|
|
806
|
-
const handleNodeMouseLeave = useCallback(() => {
|
|
807
|
-
onNodeHover?.(null);
|
|
808
|
-
}, [onNodeHover]);
|
|
809
|
-
const handleLinkClick = useCallback(
|
|
810
|
-
(link) => {
|
|
811
|
-
onLinkClick?.(link);
|
|
812
|
-
},
|
|
813
|
-
[onLinkClick]
|
|
814
|
-
);
|
|
815
|
-
return /* @__PURE__ */ jsxs(
|
|
816
|
-
"svg",
|
|
817
|
-
{
|
|
818
|
-
ref: svgRef,
|
|
837
|
+
const { nodes, links, restart, stop, setForcesEnabled } = useForceSimulation(initialNodes, initialLinks, {
|
|
819
838
|
width,
|
|
820
839
|
height,
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
840
|
+
chargeStrength: manualLayout ? 0 : void 0,
|
|
841
|
+
onTick,
|
|
842
|
+
...simulationOptions
|
|
843
|
+
});
|
|
844
|
+
useEffect(() => {
|
|
845
|
+
if (!packageBounds) return;
|
|
846
|
+
try {
|
|
847
|
+
restart();
|
|
848
|
+
} catch (e) {
|
|
849
|
+
}
|
|
850
|
+
}, [packageBounds, restart]);
|
|
851
|
+
useEffect(() => {
|
|
852
|
+
try {
|
|
853
|
+
if (manualLayout || pinnedNodes.size > 0) setForcesEnabled(false);
|
|
854
|
+
else setForcesEnabled(true);
|
|
855
|
+
} catch (e) {
|
|
856
|
+
}
|
|
857
|
+
}, [manualLayout, pinnedNodes, setForcesEnabled]);
|
|
858
|
+
useImperativeHandle(
|
|
859
|
+
ref,
|
|
860
|
+
() => ({
|
|
861
|
+
pinAll: () => {
|
|
862
|
+
const newPinned = /* @__PURE__ */ new Set();
|
|
863
|
+
nodes.forEach((node) => {
|
|
864
|
+
node.fx = node.x;
|
|
865
|
+
node.fy = node.y;
|
|
866
|
+
newPinned.add(node.id);
|
|
867
|
+
});
|
|
868
|
+
setPinnedNodes(newPinned);
|
|
869
|
+
restart();
|
|
870
|
+
},
|
|
871
|
+
unpinAll: () => {
|
|
872
|
+
nodes.forEach((node) => {
|
|
873
|
+
node.fx = null;
|
|
874
|
+
node.fy = null;
|
|
875
|
+
});
|
|
876
|
+
setPinnedNodes(/* @__PURE__ */ new Set());
|
|
877
|
+
restart();
|
|
878
|
+
},
|
|
879
|
+
resetLayout: () => {
|
|
880
|
+
nodes.forEach((node) => {
|
|
881
|
+
node.fx = null;
|
|
882
|
+
node.fy = null;
|
|
883
|
+
});
|
|
884
|
+
setPinnedNodes(/* @__PURE__ */ new Set());
|
|
885
|
+
restart();
|
|
886
|
+
},
|
|
887
|
+
fitView: () => {
|
|
888
|
+
if (!svgRef.current || !nodes.length) return;
|
|
889
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
890
|
+
nodes.forEach((node) => {
|
|
891
|
+
if (node.x !== void 0 && node.y !== void 0) {
|
|
892
|
+
const size = node.size || 10;
|
|
893
|
+
minX = Math.min(minX, node.x - size);
|
|
894
|
+
maxX = Math.max(maxX, node.x + size);
|
|
895
|
+
minY = Math.min(minY, node.y - size);
|
|
896
|
+
maxY = Math.max(maxY, node.y + size);
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
if (!isFinite(minX)) return;
|
|
900
|
+
const padding = 40;
|
|
901
|
+
const nodeWidth = maxX - minX;
|
|
902
|
+
const nodeHeight = maxY - minY;
|
|
903
|
+
const scale = Math.min(
|
|
904
|
+
(width - padding * 2) / nodeWidth,
|
|
905
|
+
(height - padding * 2) / nodeHeight,
|
|
906
|
+
10
|
|
907
|
+
);
|
|
908
|
+
const centerX = (minX + maxX) / 2;
|
|
909
|
+
const centerY = (minY + maxY) / 2;
|
|
910
|
+
const x = width / 2 - centerX * scale;
|
|
911
|
+
const y = height / 2 - centerY * scale;
|
|
912
|
+
if (gRef.current && svgRef.current) {
|
|
913
|
+
const svg = d33.select(svgRef.current);
|
|
914
|
+
const newTransform = d33.zoomIdentity.translate(x, y).scale(scale);
|
|
915
|
+
svg.transition().duration(300).call(d33.zoom().transform, newTransform);
|
|
916
|
+
setTransform(newTransform);
|
|
834
917
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
918
|
+
},
|
|
919
|
+
getPinnedNodes: () => Array.from(pinnedNodes),
|
|
920
|
+
setDragMode: (enabled) => {
|
|
921
|
+
internalDragEnabledRef.current = enabled;
|
|
922
|
+
}
|
|
923
|
+
}),
|
|
924
|
+
[nodes, pinnedNodes, restart, width, height]
|
|
925
|
+
);
|
|
926
|
+
useEffect(() => {
|
|
927
|
+
try {
|
|
928
|
+
if (typeof onManualLayoutChange === "function") onManualLayoutChange(manualLayout);
|
|
929
|
+
} catch (e) {
|
|
930
|
+
}
|
|
931
|
+
}, [manualLayout, onManualLayoutChange]);
|
|
932
|
+
useEffect(() => {
|
|
933
|
+
if (!enableZoom || !svgRef.current || !gRef.current) return;
|
|
934
|
+
const svg = d33.select(svgRef.current);
|
|
935
|
+
const g = d33.select(gRef.current);
|
|
936
|
+
const zoom2 = d33.zoom().scaleExtent([0.1, 10]).on("zoom", (event) => {
|
|
937
|
+
g.attr("transform", event.transform);
|
|
938
|
+
setTransform(event.transform);
|
|
939
|
+
});
|
|
940
|
+
svg.call(zoom2);
|
|
941
|
+
return () => {
|
|
942
|
+
svg.on(".zoom", null);
|
|
943
|
+
};
|
|
944
|
+
}, [enableZoom]);
|
|
945
|
+
const handleDragStart = useCallback(
|
|
946
|
+
(event, node) => {
|
|
947
|
+
if (!enableDrag) return;
|
|
948
|
+
event.preventDefault();
|
|
949
|
+
event.stopPropagation();
|
|
950
|
+
dragActiveRef.current = true;
|
|
951
|
+
dragNodeRef.current = node;
|
|
952
|
+
node.fx = node.x;
|
|
953
|
+
node.fy = node.y;
|
|
954
|
+
setPinnedNodes((prev) => /* @__PURE__ */ new Set([...prev, node.id]));
|
|
955
|
+
try {
|
|
956
|
+
stop();
|
|
957
|
+
} catch (e) {
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
[enableDrag, restart]
|
|
961
|
+
);
|
|
962
|
+
useEffect(() => {
|
|
963
|
+
if (!enableDrag) return;
|
|
964
|
+
const handleWindowMove = (event) => {
|
|
965
|
+
if (!dragActiveRef.current || !dragNodeRef.current) return;
|
|
966
|
+
const svg = svgRef.current;
|
|
967
|
+
if (!svg) return;
|
|
968
|
+
const rect = svg.getBoundingClientRect();
|
|
969
|
+
const x = (event.clientX - rect.left - transform.x) / transform.k;
|
|
970
|
+
const y = (event.clientY - rect.top - transform.y) / transform.k;
|
|
971
|
+
dragNodeRef.current.fx = x;
|
|
972
|
+
dragNodeRef.current.fy = y;
|
|
973
|
+
};
|
|
974
|
+
const handleWindowUp = () => {
|
|
975
|
+
if (!dragActiveRef.current) return;
|
|
976
|
+
try {
|
|
977
|
+
setForcesEnabled(true);
|
|
978
|
+
restart();
|
|
979
|
+
} catch (e) {
|
|
980
|
+
}
|
|
981
|
+
dragNodeRef.current = null;
|
|
982
|
+
dragActiveRef.current = false;
|
|
983
|
+
};
|
|
984
|
+
const handleWindowLeave = (event) => {
|
|
985
|
+
if (event.relatedTarget === null) handleWindowUp();
|
|
986
|
+
};
|
|
987
|
+
window.addEventListener("mousemove", handleWindowMove);
|
|
988
|
+
window.addEventListener("mouseup", handleWindowUp);
|
|
989
|
+
window.addEventListener("mouseout", handleWindowLeave);
|
|
990
|
+
window.addEventListener("blur", handleWindowUp);
|
|
991
|
+
return () => {
|
|
992
|
+
window.removeEventListener("mousemove", handleWindowMove);
|
|
993
|
+
window.removeEventListener("mouseup", handleWindowUp);
|
|
994
|
+
window.removeEventListener("mouseout", handleWindowLeave);
|
|
995
|
+
window.removeEventListener("blur", handleWindowUp);
|
|
996
|
+
};
|
|
997
|
+
}, [enableDrag, transform]);
|
|
998
|
+
useEffect(() => {
|
|
999
|
+
if (!gRef.current || !enableDrag) return;
|
|
1000
|
+
const g = d33.select(gRef.current);
|
|
1001
|
+
const dragBehavior = d33.drag().on("start", function(event) {
|
|
1002
|
+
try {
|
|
1003
|
+
const target = event.sourceEvent && event.sourceEvent.target || event.target;
|
|
1004
|
+
const grp = target.closest?.("g.node");
|
|
1005
|
+
const id = grp?.getAttribute("data-id");
|
|
1006
|
+
if (!id) return;
|
|
1007
|
+
const node = nodes.find((n) => n.id === id);
|
|
1008
|
+
if (!node) return;
|
|
1009
|
+
if (!internalDragEnabledRef.current) return;
|
|
1010
|
+
if (!event.active) restart();
|
|
1011
|
+
dragActiveRef.current = true;
|
|
1012
|
+
dragNodeRef.current = node;
|
|
1013
|
+
node.fx = node.x;
|
|
1014
|
+
node.fy = node.y;
|
|
1015
|
+
setPinnedNodes((prev) => /* @__PURE__ */ new Set([...prev, node.id]));
|
|
1016
|
+
} catch (e) {
|
|
1017
|
+
}
|
|
1018
|
+
}).on("drag", function(event) {
|
|
1019
|
+
if (!dragActiveRef.current || !dragNodeRef.current) return;
|
|
1020
|
+
const svg = svgRef.current;
|
|
1021
|
+
if (!svg) return;
|
|
1022
|
+
const rect = svg.getBoundingClientRect();
|
|
1023
|
+
const x = (event.sourceEvent.clientX - rect.left - transform.x) / transform.k;
|
|
1024
|
+
const y = (event.sourceEvent.clientY - rect.top - transform.y) / transform.k;
|
|
1025
|
+
dragNodeRef.current.fx = x;
|
|
1026
|
+
dragNodeRef.current.fy = y;
|
|
1027
|
+
}).on("end", function() {
|
|
1028
|
+
try {
|
|
1029
|
+
setForcesEnabled(true);
|
|
1030
|
+
restart();
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
}
|
|
1033
|
+
dragNodeRef.current = null;
|
|
1034
|
+
dragActiveRef.current = false;
|
|
1035
|
+
});
|
|
1036
|
+
try {
|
|
1037
|
+
g.selectAll("g.node").call(dragBehavior);
|
|
1038
|
+
} catch (e) {
|
|
1039
|
+
}
|
|
1040
|
+
return () => {
|
|
1041
|
+
try {
|
|
1042
|
+
g.selectAll("g.node").on(".drag", null);
|
|
1043
|
+
} catch (e) {
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
}, [gRef, enableDrag, nodes, transform, restart]);
|
|
1047
|
+
const handleNodeClick = useCallback(
|
|
1048
|
+
(node) => {
|
|
1049
|
+
onNodeClick?.(node);
|
|
1050
|
+
},
|
|
1051
|
+
[onNodeClick]
|
|
1052
|
+
);
|
|
1053
|
+
const handleNodeDoubleClick = useCallback(
|
|
1054
|
+
(event, node) => {
|
|
1055
|
+
event.stopPropagation();
|
|
1056
|
+
if (!enableDrag) return;
|
|
1057
|
+
if (node.fx === null || node.fx === void 0) {
|
|
1058
|
+
node.fx = node.x;
|
|
1059
|
+
node.fy = node.y;
|
|
1060
|
+
setPinnedNodes((prev) => /* @__PURE__ */ new Set([...prev, node.id]));
|
|
1061
|
+
} else {
|
|
1062
|
+
node.fx = null;
|
|
1063
|
+
node.fy = null;
|
|
1064
|
+
setPinnedNodes((prev) => {
|
|
1065
|
+
const next = new Set(prev);
|
|
1066
|
+
next.delete(node.id);
|
|
1067
|
+
return next;
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
restart();
|
|
1071
|
+
},
|
|
1072
|
+
[enableDrag, restart]
|
|
1073
|
+
);
|
|
1074
|
+
const handleCanvasDoubleClick = useCallback(() => {
|
|
1075
|
+
nodes.forEach((node) => {
|
|
1076
|
+
node.fx = null;
|
|
1077
|
+
node.fy = null;
|
|
1078
|
+
});
|
|
1079
|
+
setPinnedNodes(/* @__PURE__ */ new Set());
|
|
1080
|
+
restart();
|
|
1081
|
+
}, [nodes, restart]);
|
|
1082
|
+
const handleNodeMouseEnter = useCallback(
|
|
1083
|
+
(node) => {
|
|
1084
|
+
onNodeHover?.(node);
|
|
1085
|
+
},
|
|
1086
|
+
[onNodeHover]
|
|
1087
|
+
);
|
|
1088
|
+
const handleNodeMouseLeave = useCallback(() => {
|
|
1089
|
+
onNodeHover?.(null);
|
|
1090
|
+
}, [onNodeHover]);
|
|
1091
|
+
const handleLinkClick = useCallback(
|
|
1092
|
+
(link) => {
|
|
1093
|
+
onLinkClick?.(link);
|
|
1094
|
+
},
|
|
1095
|
+
[onLinkClick]
|
|
1096
|
+
);
|
|
1097
|
+
return /* @__PURE__ */ jsxs(
|
|
1098
|
+
"svg",
|
|
1099
|
+
{
|
|
1100
|
+
ref: svgRef,
|
|
1101
|
+
width,
|
|
1102
|
+
height,
|
|
1103
|
+
className: cn("bg-white dark:bg-gray-900", className),
|
|
1104
|
+
onDoubleClick: handleCanvasDoubleClick,
|
|
1105
|
+
children: [
|
|
1106
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
|
|
1107
|
+
"marker",
|
|
1108
|
+
{
|
|
1109
|
+
id: "arrow",
|
|
1110
|
+
viewBox: "0 0 10 10",
|
|
1111
|
+
refX: "20",
|
|
1112
|
+
refY: "5",
|
|
1113
|
+
markerWidth: "6",
|
|
1114
|
+
markerHeight: "6",
|
|
1115
|
+
orient: "auto",
|
|
1116
|
+
children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: defaultLinkColor })
|
|
1117
|
+
}
|
|
1118
|
+
) }),
|
|
1119
|
+
/* @__PURE__ */ jsxs("g", { ref: gRef, children: [
|
|
1120
|
+
links.map((link, i) => {
|
|
1121
|
+
const source = link.source;
|
|
1122
|
+
const target = link.target;
|
|
1123
|
+
if (source.x == null || source.y == null || target.x == null || target.y == null) return null;
|
|
1124
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
1125
|
+
/* @__PURE__ */ jsx(
|
|
1126
|
+
"line",
|
|
1127
|
+
{
|
|
1128
|
+
x1: source.x,
|
|
1129
|
+
y1: source.y,
|
|
1130
|
+
x2: target.x,
|
|
1131
|
+
y2: target.y,
|
|
1132
|
+
stroke: link.color || defaultLinkColor,
|
|
1133
|
+
strokeWidth: link.width || defaultLinkWidth,
|
|
1134
|
+
opacity: 0.6,
|
|
1135
|
+
className: "cursor-pointer transition-opacity hover:opacity-100",
|
|
1136
|
+
onClick: () => handleLinkClick(link)
|
|
1137
|
+
}
|
|
1138
|
+
),
|
|
1139
|
+
showLinkLabels && link.label && /* @__PURE__ */ jsx(
|
|
1140
|
+
"text",
|
|
1141
|
+
{
|
|
1142
|
+
x: (source.x + target.x) / 2,
|
|
1143
|
+
y: (source.y + target.y) / 2,
|
|
1144
|
+
fill: "#666",
|
|
1145
|
+
fontSize: "10",
|
|
1146
|
+
textAnchor: "middle",
|
|
1147
|
+
dominantBaseline: "middle",
|
|
1148
|
+
pointerEvents: "none",
|
|
1149
|
+
children: link.label
|
|
1150
|
+
}
|
|
1151
|
+
)
|
|
1152
|
+
] }, `link-${i}`);
|
|
1153
|
+
}),
|
|
1154
|
+
nodes.map((node) => {
|
|
1155
|
+
if (node.x == null || node.y == null) return null;
|
|
1156
|
+
const isSelected = selectedNodeId === node.id;
|
|
1157
|
+
const isHovered = hoveredNodeId === node.id;
|
|
1158
|
+
const nodeSize = node.size || defaultNodeSize;
|
|
1159
|
+
const nodeColor = node.color || defaultNodeColor;
|
|
1160
|
+
return /* @__PURE__ */ jsxs(
|
|
1161
|
+
"g",
|
|
1162
|
+
{
|
|
1163
|
+
transform: `translate(${node.x},${node.y})`,
|
|
1164
|
+
className: "cursor-pointer node",
|
|
1165
|
+
"data-id": node.id,
|
|
1166
|
+
onClick: () => handleNodeClick(node),
|
|
1167
|
+
onDoubleClick: (event) => handleNodeDoubleClick(event, node),
|
|
1168
|
+
onMouseEnter: () => handleNodeMouseEnter(node),
|
|
1169
|
+
onMouseLeave: handleNodeMouseLeave,
|
|
1170
|
+
onMouseDown: (e) => handleDragStart(e, node),
|
|
1171
|
+
children: [
|
|
1172
|
+
/* @__PURE__ */ jsx(
|
|
1173
|
+
"circle",
|
|
1174
|
+
{
|
|
1175
|
+
r: nodeSize,
|
|
1176
|
+
fill: nodeColor,
|
|
1177
|
+
stroke: isSelected ? "#000" : isHovered ? "#666" : "none",
|
|
1178
|
+
strokeWidth: pinnedNodes.has(node.id) ? 3 : isSelected ? 2.5 : isHovered ? 2 : 1.5,
|
|
1179
|
+
opacity: isHovered || isSelected ? 1 : 0.9,
|
|
1180
|
+
className: "transition-all"
|
|
1181
|
+
}
|
|
1182
|
+
),
|
|
1183
|
+
pinnedNodes.has(node.id) && /* @__PURE__ */ jsx(
|
|
1184
|
+
"circle",
|
|
1185
|
+
{
|
|
1186
|
+
r: nodeSize + 4,
|
|
1187
|
+
fill: "none",
|
|
1188
|
+
stroke: "#ff6b6b",
|
|
1189
|
+
strokeWidth: 1,
|
|
1190
|
+
opacity: 0.5,
|
|
1191
|
+
className: "pointer-events-none"
|
|
1192
|
+
}
|
|
1193
|
+
),
|
|
1194
|
+
showNodeLabels && node.label && /* @__PURE__ */ jsx(
|
|
1195
|
+
"text",
|
|
1196
|
+
{
|
|
1197
|
+
y: nodeSize + 15,
|
|
1198
|
+
fill: "#333",
|
|
1199
|
+
fontSize: "12",
|
|
1200
|
+
textAnchor: "middle",
|
|
1201
|
+
dominantBaseline: "middle",
|
|
1202
|
+
pointerEvents: "none",
|
|
1203
|
+
className: "select-none",
|
|
1204
|
+
children: node.label
|
|
1205
|
+
}
|
|
1206
|
+
)
|
|
1207
|
+
]
|
|
1208
|
+
},
|
|
1209
|
+
node.id
|
|
1210
|
+
);
|
|
1211
|
+
}),
|
|
1212
|
+
packageBounds && Object.keys(packageBounds).length > 0 && /* @__PURE__ */ jsx("g", { className: "package-boundaries", pointerEvents: "none", children: Object.entries(packageBounds).map(([pid, b]) => /* @__PURE__ */ jsxs("g", { children: [
|
|
842
1213
|
/* @__PURE__ */ jsx(
|
|
843
|
-
"
|
|
1214
|
+
"circle",
|
|
844
1215
|
{
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
stroke:
|
|
850
|
-
strokeWidth:
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
onClick: () => handleLinkClick(link)
|
|
1216
|
+
cx: b.x,
|
|
1217
|
+
cy: b.y,
|
|
1218
|
+
r: b.r,
|
|
1219
|
+
fill: "rgba(148,163,184,0.06)",
|
|
1220
|
+
stroke: "#475569",
|
|
1221
|
+
strokeWidth: 2,
|
|
1222
|
+
strokeDasharray: "6 6",
|
|
1223
|
+
opacity: 0.9
|
|
854
1224
|
}
|
|
855
1225
|
),
|
|
856
|
-
|
|
1226
|
+
/* @__PURE__ */ jsx(
|
|
857
1227
|
"text",
|
|
858
1228
|
{
|
|
859
|
-
x:
|
|
860
|
-
y: (
|
|
861
|
-
fill: "#
|
|
862
|
-
fontSize:
|
|
1229
|
+
x: b.x,
|
|
1230
|
+
y: Math.max(12, b.y - b.r + 14),
|
|
1231
|
+
fill: "#475569",
|
|
1232
|
+
fontSize: 11,
|
|
863
1233
|
textAnchor: "middle",
|
|
864
|
-
dominantBaseline: "middle",
|
|
865
1234
|
pointerEvents: "none",
|
|
866
|
-
children:
|
|
1235
|
+
children: pid.replace(/^pkg:/, "")
|
|
867
1236
|
}
|
|
868
1237
|
)
|
|
869
|
-
] },
|
|
870
|
-
})
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1238
|
+
] }, pid)) })
|
|
1239
|
+
] })
|
|
1240
|
+
]
|
|
1241
|
+
}
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
);
|
|
1245
|
+
ForceDirectedGraph.displayName = "ForceDirectedGraph";
|
|
1246
|
+
var GraphControls = ({
|
|
1247
|
+
dragEnabled = true,
|
|
1248
|
+
onDragToggle,
|
|
1249
|
+
manualLayout = false,
|
|
1250
|
+
onManualLayoutToggle,
|
|
1251
|
+
onPinAll,
|
|
1252
|
+
onUnpinAll,
|
|
1253
|
+
onReset,
|
|
1254
|
+
onFitView,
|
|
1255
|
+
pinnedCount = 0,
|
|
1256
|
+
totalNodes = 0,
|
|
1257
|
+
visible = true,
|
|
1258
|
+
position = "top-left",
|
|
1259
|
+
className
|
|
1260
|
+
}) => {
|
|
1261
|
+
if (!visible) return null;
|
|
1262
|
+
const positionClasses = {
|
|
1263
|
+
"top-left": "top-4 left-4",
|
|
1264
|
+
"top-right": "top-4 right-4",
|
|
1265
|
+
"bottom-left": "bottom-4 left-4",
|
|
1266
|
+
"bottom-right": "bottom-4 right-4"
|
|
1267
|
+
};
|
|
1268
|
+
const ControlButton = ({ onClick, active = false, icon, label, disabled = false }) => /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
1269
|
+
/* @__PURE__ */ jsx(
|
|
1270
|
+
"button",
|
|
1271
|
+
{
|
|
1272
|
+
onClick,
|
|
1273
|
+
disabled,
|
|
1274
|
+
className: cn(
|
|
1275
|
+
"p-2 rounded-lg transition-all duration-200",
|
|
1276
|
+
active ? "bg-blue-500 text-white shadow-md hover:bg-blue-600" : "bg-gray-100 text-gray-700 hover:bg-gray-200",
|
|
1277
|
+
disabled && "opacity-50 cursor-not-allowed hover:bg-gray-100",
|
|
1278
|
+
"dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 dark:active:bg-blue-600"
|
|
1279
|
+
),
|
|
1280
|
+
title: label,
|
|
1281
|
+
children: /* @__PURE__ */ jsx("span", { className: "text-lg", children: icon })
|
|
1282
|
+
}
|
|
1283
|
+
),
|
|
1284
|
+
/* @__PURE__ */ jsx("div", { className: "absolute left-full ml-2 px-2 py-1 bg-gray-900 text-white text-xs rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-50", children: label })
|
|
1285
|
+
] });
|
|
1286
|
+
return /* @__PURE__ */ jsxs(
|
|
1287
|
+
"div",
|
|
1288
|
+
{
|
|
1289
|
+
className: cn(
|
|
1290
|
+
"fixed z-40 bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 border border-gray-200 dark:border-gray-700",
|
|
1291
|
+
positionClasses[position],
|
|
1292
|
+
className
|
|
1293
|
+
),
|
|
1294
|
+
children: [
|
|
1295
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1296
|
+
/* @__PURE__ */ jsx(
|
|
1297
|
+
ControlButton,
|
|
1298
|
+
{
|
|
1299
|
+
onClick: () => onDragToggle?.(!dragEnabled),
|
|
1300
|
+
active: dragEnabled,
|
|
1301
|
+
icon: "\u270B",
|
|
1302
|
+
label: dragEnabled ? "Drag enabled" : "Drag disabled"
|
|
1303
|
+
}
|
|
1304
|
+
),
|
|
1305
|
+
/* @__PURE__ */ jsx(
|
|
1306
|
+
ControlButton,
|
|
1307
|
+
{
|
|
1308
|
+
onClick: () => onManualLayoutToggle?.(!manualLayout),
|
|
1309
|
+
active: manualLayout,
|
|
1310
|
+
icon: "\u{1F527}",
|
|
1311
|
+
label: manualLayout ? "Manual layout: ON (drag freely)" : "Manual layout: OFF (forces active)"
|
|
1312
|
+
}
|
|
1313
|
+
),
|
|
1314
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-px bg-gray-300 dark:bg-gray-600 mx-auto my-1" }),
|
|
1315
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
1316
|
+
/* @__PURE__ */ jsx(
|
|
1317
|
+
ControlButton,
|
|
1318
|
+
{
|
|
1319
|
+
onClick: () => onPinAll?.(),
|
|
1320
|
+
disabled: totalNodes === 0,
|
|
1321
|
+
icon: "\u{1F4CC}",
|
|
1322
|
+
label: `Pin all nodes (${totalNodes})`
|
|
1323
|
+
}
|
|
1324
|
+
),
|
|
1325
|
+
/* @__PURE__ */ jsx(
|
|
1326
|
+
ControlButton,
|
|
879
1327
|
{
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
})
|
|
1328
|
+
onClick: () => onUnpinAll?.(),
|
|
1329
|
+
disabled: pinnedCount === 0,
|
|
1330
|
+
icon: "\u{1F4CD}",
|
|
1331
|
+
label: `Unpin all (${pinnedCount} pinned)`
|
|
1332
|
+
}
|
|
1333
|
+
)
|
|
1334
|
+
] }),
|
|
1335
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-px bg-gray-300 dark:bg-gray-600 mx-auto my-1" }),
|
|
1336
|
+
/* @__PURE__ */ jsx(
|
|
1337
|
+
ControlButton,
|
|
1338
|
+
{
|
|
1339
|
+
onClick: () => onFitView?.(),
|
|
1340
|
+
disabled: totalNodes === 0,
|
|
1341
|
+
icon: "\u{1F3AF}",
|
|
1342
|
+
label: "Fit all nodes in view"
|
|
1343
|
+
}
|
|
1344
|
+
),
|
|
1345
|
+
/* @__PURE__ */ jsx(
|
|
1346
|
+
ControlButton,
|
|
1347
|
+
{
|
|
1348
|
+
onClick: () => onReset?.(),
|
|
1349
|
+
disabled: totalNodes === 0,
|
|
1350
|
+
icon: "\u21BA",
|
|
1351
|
+
label: "Reset to auto-layout"
|
|
1352
|
+
}
|
|
1353
|
+
)
|
|
1354
|
+
] }),
|
|
1355
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 pt-3 border-t border-gray-200 dark:border-gray-700 text-xs text-gray-600 dark:text-gray-400", children: [
|
|
1356
|
+
/* @__PURE__ */ jsxs("div", { className: "whitespace-nowrap", children: [
|
|
1357
|
+
/* @__PURE__ */ jsx("strong", { children: "Nodes:" }),
|
|
1358
|
+
" ",
|
|
1359
|
+
totalNodes
|
|
1360
|
+
] }),
|
|
1361
|
+
pinnedCount > 0 && /* @__PURE__ */ jsxs("div", { className: "whitespace-nowrap", children: [
|
|
1362
|
+
/* @__PURE__ */ jsx("strong", { children: "Pinned:" }),
|
|
1363
|
+
" ",
|
|
1364
|
+
pinnedCount
|
|
1365
|
+
] }),
|
|
1366
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 text-gray-500 dark:text-gray-500 leading-snug", children: [
|
|
1367
|
+
/* @__PURE__ */ jsx("strong", { children: "Tips:" }),
|
|
1368
|
+
/* @__PURE__ */ jsxs("ul", { className: "mt-1 ml-1 space-y-0.5", children: [
|
|
1369
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Drag nodes to reposition" }),
|
|
1370
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Double-click to pin/unpin" }),
|
|
1371
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Double-click canvas to unpin all" }),
|
|
1372
|
+
/* @__PURE__ */ jsx("li", { children: "\u2022 Scroll to zoom" })
|
|
1373
|
+
] })
|
|
1374
|
+
] })
|
|
918
1375
|
] })
|
|
919
1376
|
]
|
|
920
1377
|
}
|
|
921
1378
|
);
|
|
922
1379
|
};
|
|
923
|
-
|
|
1380
|
+
GraphControls.displayName = "GraphControls";
|
|
924
1381
|
|
|
925
|
-
export { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Container, ForceDirectedGraph, Grid, Input, Label, RadioGroup, Select, Separator, Stack, Switch, Textarea, badgeVariants, buttonVariants, chartColors, cn, domainColors, formatCompactNumber, formatDate, formatDateTime, formatDecimal, formatDuration, formatFileSize, formatMetric, formatNumber, formatPercentage, formatRange, formatRelativeTime, getDomainColor, getSeverityColor, hexToRgba, severityColors, useD3, useD3WithResize, useDebounce, useDrag, useForceSimulation };
|
|
1382
|
+
export { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Container, ForceDirectedGraph, GraphControls, Grid, Input, Label, RadioGroup, Select, Separator, Stack, Switch, Textarea, badgeVariants, buttonVariants, chartColors, cn, domainColors, formatCompactNumber, formatDate, formatDateTime, formatDecimal, formatDuration, formatFileSize, formatMetric, formatNumber, formatPercentage, formatRange, formatRelativeTime, getDomainColor, getSeverityColor, hexToRgba, severityColors, useD3, useD3WithResize, useDebounce, useDrag, useForceSimulation };
|
|
926
1383
|
//# sourceMappingURL=index.js.map
|
|
927
1384
|
//# sourceMappingURL=index.js.map
|