@aiready/components 0.1.7 → 0.1.8
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 +15 -6
- package/dist/charts/ForceDirectedGraph.js +112 -452
- package/dist/charts/ForceDirectedGraph.js.map +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +98 -171
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/charts/ForceDirectedGraph.tsx +148 -234
- package/src/index.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React2 from 'react';
|
|
2
|
-
import React2__default, { forwardRef, useRef, useState, useEffect,
|
|
2
|
+
import React2__default, { forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle } from 'react';
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -1015,7 +1015,6 @@ var ForceDirectedGraph = forwardRef(
|
|
|
1015
1015
|
links: initialLinks,
|
|
1016
1016
|
width,
|
|
1017
1017
|
height,
|
|
1018
|
-
simulationOptions,
|
|
1019
1018
|
enableZoom = true,
|
|
1020
1019
|
enableDrag = true,
|
|
1021
1020
|
onNodeClick,
|
|
@@ -1032,7 +1031,9 @@ var ForceDirectedGraph = forwardRef(
|
|
|
1032
1031
|
className,
|
|
1033
1032
|
manualLayout = false,
|
|
1034
1033
|
onManualLayoutChange,
|
|
1035
|
-
packageBounds
|
|
1034
|
+
packageBounds,
|
|
1035
|
+
layout: externalLayout,
|
|
1036
|
+
onLayoutChange
|
|
1036
1037
|
}, ref) => {
|
|
1037
1038
|
const svgRef = useRef(null);
|
|
1038
1039
|
const gRef = useRef(null);
|
|
@@ -1042,180 +1043,102 @@ var ForceDirectedGraph = forwardRef(
|
|
|
1042
1043
|
const dragActiveRef = useRef(false);
|
|
1043
1044
|
const [pinnedNodes, setPinnedNodes] = useState(/* @__PURE__ */ new Set());
|
|
1044
1045
|
const internalDragEnabledRef = useRef(enableDrag);
|
|
1046
|
+
const [layout, setLayout] = useState(externalLayout || "force");
|
|
1047
|
+
useEffect(() => {
|
|
1048
|
+
if (externalLayout && externalLayout !== layout) {
|
|
1049
|
+
setLayout(externalLayout);
|
|
1050
|
+
}
|
|
1051
|
+
}, [externalLayout]);
|
|
1052
|
+
const handleLayoutChange = useCallback((newLayout) => {
|
|
1053
|
+
setLayout(newLayout);
|
|
1054
|
+
onLayoutChange?.(newLayout);
|
|
1055
|
+
}, [onLayoutChange]);
|
|
1045
1056
|
useEffect(() => {
|
|
1046
1057
|
internalDragEnabledRef.current = enableDrag;
|
|
1047
1058
|
}, [enableDrag]);
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
if (!key) return;
|
|
1059
|
-
const center = boundsToUse[key];
|
|
1060
|
-
if (!center) return;
|
|
1061
|
-
const dx = center.x - (n.x ?? 0);
|
|
1062
|
-
const dy = center.y - (n.y ?? 0);
|
|
1063
|
-
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
1064
|
-
const pullStrength = Math.min(0.5, 0.15 * (dist / (center.r || 200)) + 0.06);
|
|
1065
|
-
if (!isNaN(pullStrength) && isFinite(pullStrength)) {
|
|
1066
|
-
n.vx = (n.vx ?? 0) + dx / (dist || 1) * pullStrength;
|
|
1067
|
-
n.vy = (n.vy ?? 0) + dy / (dist || 1) * pullStrength;
|
|
1068
|
-
}
|
|
1069
|
-
if (center.r && dist > center.r) {
|
|
1070
|
-
const excess = (dist - center.r) / (dist || 1);
|
|
1071
|
-
n.vx = (n.vx ?? 0) - dx * 0.02 * excess;
|
|
1072
|
-
n.vy = (n.vy ?? 0) - dy * 0.02 * excess;
|
|
1073
|
-
}
|
|
1074
|
-
});
|
|
1075
|
-
}
|
|
1076
|
-
} catch (e) {
|
|
1059
|
+
const nodes = React2__default.useMemo(() => {
|
|
1060
|
+
if (!initialNodes || !initialNodes.length) return initialNodes;
|
|
1061
|
+
const cx = width / 2;
|
|
1062
|
+
const cy = height / 2;
|
|
1063
|
+
if (layout === "force") {
|
|
1064
|
+
return initialNodes.map((n) => ({
|
|
1065
|
+
...n,
|
|
1066
|
+
x: Math.random() * width,
|
|
1067
|
+
y: Math.random() * height
|
|
1068
|
+
}));
|
|
1077
1069
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
const
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1070
|
+
if (layout === "circular") {
|
|
1071
|
+
const radius = Math.min(width, height) * 0.35;
|
|
1072
|
+
return initialNodes.map((n, i) => ({
|
|
1073
|
+
...n,
|
|
1074
|
+
x: cx + Math.cos(2 * Math.PI * i / initialNodes.length) * radius,
|
|
1075
|
+
y: cy + Math.sin(2 * Math.PI * i / initialNodes.length) * radius
|
|
1076
|
+
}));
|
|
1077
|
+
}
|
|
1078
|
+
if (layout === "hierarchical") {
|
|
1079
|
+
const cols = Math.ceil(Math.sqrt(initialNodes.length));
|
|
1080
|
+
const spacingX = width / (cols + 1);
|
|
1081
|
+
const spacingY = height / (Math.ceil(initialNodes.length / cols) + 1);
|
|
1082
|
+
return initialNodes.map((n, i) => ({
|
|
1083
|
+
...n,
|
|
1084
|
+
x: spacingX * (i % cols + 1),
|
|
1085
|
+
y: spacingY * (Math.floor(i / cols) + 1)
|
|
1086
|
+
}));
|
|
1087
|
+
}
|
|
1088
|
+
return initialNodes;
|
|
1089
|
+
}, [initialNodes, width, height, layout]);
|
|
1090
|
+
const links = initialLinks;
|
|
1091
|
+
const restart = React2__default.useCallback(() => {
|
|
1092
|
+
}, []);
|
|
1093
|
+
const stop = React2__default.useCallback(() => {
|
|
1094
|
+
}, []);
|
|
1095
|
+
const setForcesEnabled = React2__default.useCallback((_enabled) => {
|
|
1096
|
+
}, []);
|
|
1097
|
+
useEffect(() => {
|
|
1098
|
+
if (!nodes || nodes.length === 0) return;
|
|
1099
|
+
const applyLayout = () => {
|
|
1100
|
+
const cx = width / 2;
|
|
1101
|
+
const cy = height / 2;
|
|
1102
|
+
if (layout === "circular") {
|
|
1103
|
+
const radius = Math.min(width, height) * 0.35;
|
|
1104
|
+
nodes.forEach((node, i) => {
|
|
1105
|
+
const angle = 2 * Math.PI * i / nodes.length;
|
|
1106
|
+
node.fx = cx + Math.cos(angle) * radius;
|
|
1107
|
+
node.fy = cy + Math.sin(angle) * radius;
|
|
1099
1108
|
});
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
const s = typeof l.source === "string" ? l.source : l.source && l.source.id;
|
|
1107
|
-
const t = typeof l.target === "string" ? l.target : l.target && l.target.id;
|
|
1108
|
-
return localNodes.some((ln) => ln.id === s) && localNodes.some((ln) => ln.id === t);
|
|
1109
|
-
}).map((l) => ({ source: typeof l.source === "string" ? l.source : l.source.id, target: typeof l.target === "string" ? l.target : l.target.id }));
|
|
1110
|
-
if (localNodes.length === 1) {
|
|
1111
|
-
localPositions2[localNodes[0].id] = { x: 0, y: 0 };
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
const sim = d33.forceSimulation(localNodes).force("link", d33.forceLink(localLinks).id((d) => d.id).distance(30).strength(0.8)).force("charge", d33.forceManyBody().strength(-15)).force("collide", d33.forceCollide((d) => (d.size || 10) + 6).iterations(2)).stop();
|
|
1115
|
-
const ticks = 300;
|
|
1116
|
-
for (let i = 0; i < ticks; i++) sim.tick();
|
|
1117
|
-
localNodes.forEach((ln) => {
|
|
1118
|
-
localPositions2[ln.id] = { x: ln.x ?? 0, y: ln.y ?? 0 };
|
|
1109
|
+
} else if (layout === "hierarchical") {
|
|
1110
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1111
|
+
nodes.forEach((n) => {
|
|
1112
|
+
const key = n.packageGroup || n.group || "root";
|
|
1113
|
+
if (!groups.has(key)) groups.set(key, []);
|
|
1114
|
+
groups.get(key).push(n);
|
|
1119
1115
|
});
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
}, [initialNodes, packageAreas, localPositions]);
|
|
1137
|
-
const { nodes, links, restart, stop, setForcesEnabled } = useForceSimulation(seededNodes || initialNodes, initialLinks, {
|
|
1138
|
-
width,
|
|
1139
|
-
height,
|
|
1140
|
-
chargeStrength: manualLayout ? 0 : void 0,
|
|
1141
|
-
onTick,
|
|
1142
|
-
...simulationOptions
|
|
1143
|
-
});
|
|
1144
|
-
const nodesById = React2__default.useMemo(() => {
|
|
1145
|
-
const m = {};
|
|
1146
|
-
(nodes || []).forEach((n) => {
|
|
1147
|
-
if (n && n.id) m[n.id] = n;
|
|
1148
|
-
});
|
|
1149
|
-
return m;
|
|
1150
|
-
}, [nodes]);
|
|
1151
|
-
const clusterBounds = React2__default.useMemo(() => {
|
|
1152
|
-
try {
|
|
1153
|
-
if (!links || !nodes) return null;
|
|
1154
|
-
const nodeIds = new Set(nodes.map((n) => n.id));
|
|
1155
|
-
const adj = /* @__PURE__ */ new Map();
|
|
1156
|
-
nodes.forEach((n) => adj.set(n.id, /* @__PURE__ */ new Set()));
|
|
1157
|
-
links.forEach((l) => {
|
|
1158
|
-
const type = l.type || "reference";
|
|
1159
|
-
if (type !== "dependency") return;
|
|
1160
|
-
const s = typeof l.source === "string" ? l.source : l.source && l.source.id || null;
|
|
1161
|
-
const t = typeof l.target === "string" ? l.target : l.target && l.target.id || null;
|
|
1162
|
-
if (!s || !t) return;
|
|
1163
|
-
if (!nodeIds.has(s) || !nodeIds.has(t)) return;
|
|
1164
|
-
adj.get(s)?.add(t);
|
|
1165
|
-
adj.get(t)?.add(s);
|
|
1166
|
-
});
|
|
1167
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1168
|
-
const comps = [];
|
|
1169
|
-
for (const nid of nodeIds) {
|
|
1170
|
-
if (visited.has(nid)) continue;
|
|
1171
|
-
const stack = [nid];
|
|
1172
|
-
const comp = [];
|
|
1173
|
-
visited.add(nid);
|
|
1174
|
-
while (stack.length) {
|
|
1175
|
-
const cur = stack.pop();
|
|
1176
|
-
comp.push(cur);
|
|
1177
|
-
const neigh = adj.get(cur);
|
|
1178
|
-
if (!neigh) continue;
|
|
1179
|
-
for (const nb of neigh) {
|
|
1180
|
-
if (!visited.has(nb)) {
|
|
1181
|
-
visited.add(nb);
|
|
1182
|
-
stack.push(nb);
|
|
1183
|
-
}
|
|
1116
|
+
const groupArray = Array.from(groups.entries());
|
|
1117
|
+
const cols = Math.ceil(Math.sqrt(groupArray.length));
|
|
1118
|
+
const groupSpacingX = width * 0.8 / cols;
|
|
1119
|
+
const groupSpacingY = height * 0.8 / Math.ceil(groupArray.length / cols);
|
|
1120
|
+
groupArray.forEach(([groupKey, groupNodes], gi) => {
|
|
1121
|
+
const col = gi % cols;
|
|
1122
|
+
const row = Math.floor(gi / cols);
|
|
1123
|
+
const groupX = (col + 0.5) * groupSpacingX;
|
|
1124
|
+
const groupY = (row + 0.5) * groupSpacingY;
|
|
1125
|
+
if (groupKey.startsWith("pkg:") || groupKey === groupKey) {
|
|
1126
|
+
groupNodes.forEach((n, ni) => {
|
|
1127
|
+
const angle = 2 * Math.PI * ni / groupNodes.length;
|
|
1128
|
+
const r = Math.min(80, 20 + groupNodes.length * 8);
|
|
1129
|
+
n.fx = groupX + Math.cos(angle) * r;
|
|
1130
|
+
n.fy = groupY + Math.sin(angle) * r;
|
|
1131
|
+
});
|
|
1184
1132
|
}
|
|
1185
|
-
}
|
|
1186
|
-
comps.push(comp);
|
|
1133
|
+
});
|
|
1187
1134
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
const circleRadius = base * Math.max(30, num * 20, Math.sqrt(num) * 12);
|
|
1196
|
-
const map = {};
|
|
1197
|
-
comps.forEach((c, i) => {
|
|
1198
|
-
const angle = 2 * Math.PI * i / num;
|
|
1199
|
-
const x = cx + Math.cos(angle) * circleRadius;
|
|
1200
|
-
const y = cy + Math.sin(angle) * circleRadius;
|
|
1201
|
-
const sizeBias = Math.sqrt(Math.max(1, c.length));
|
|
1202
|
-
const r = Math.max(200, 100 * sizeBias);
|
|
1203
|
-
map[`cluster:${i}`] = { x, y, r };
|
|
1204
|
-
});
|
|
1205
|
-
const nodeToCluster = {};
|
|
1206
|
-
comps.forEach((c, i) => c.forEach((nid) => nodeToCluster[nid] = `cluster:${i}`));
|
|
1207
|
-
return { bounds: map, nodeToCluster };
|
|
1208
|
-
} catch (e) {
|
|
1209
|
-
return null;
|
|
1210
|
-
}
|
|
1211
|
-
}, [nodes, links, width, height]);
|
|
1212
|
-
useEffect(() => {
|
|
1213
|
-
if (!packageBounds && !clusterBounds && (!packageAreas || Object.keys(packageAreas).length === 0)) return;
|
|
1214
|
-
try {
|
|
1215
|
-
restart();
|
|
1216
|
-
} catch (e) {
|
|
1217
|
-
}
|
|
1218
|
-
}, [packageBounds, clusterBounds, packageAreas, restart]);
|
|
1135
|
+
try {
|
|
1136
|
+
restart();
|
|
1137
|
+
} catch (e) {
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
applyLayout();
|
|
1141
|
+
}, [layout, nodes, width, height, restart]);
|
|
1219
1142
|
useEffect(() => {
|
|
1220
1143
|
try {
|
|
1221
1144
|
if (manualLayout || pinnedNodes.size > 0) setForcesEnabled(false);
|
|
@@ -1287,9 +1210,13 @@ var ForceDirectedGraph = forwardRef(
|
|
|
1287
1210
|
getPinnedNodes: () => Array.from(pinnedNodes),
|
|
1288
1211
|
setDragMode: (enabled) => {
|
|
1289
1212
|
internalDragEnabledRef.current = enabled;
|
|
1290
|
-
}
|
|
1213
|
+
},
|
|
1214
|
+
setLayout: (newLayout) => {
|
|
1215
|
+
handleLayoutChange(newLayout);
|
|
1216
|
+
},
|
|
1217
|
+
getLayout: () => layout
|
|
1291
1218
|
}),
|
|
1292
|
-
[nodes, pinnedNodes, restart, width, height]
|
|
1219
|
+
[nodes, pinnedNodes, restart, width, height, layout, handleLayoutChange]
|
|
1293
1220
|
);
|
|
1294
1221
|
useEffect(() => {
|
|
1295
1222
|
try {
|