@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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React2 from 'react';
2
- import React2__default, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback } from 'react';
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 onTick = (_nodesCopy, _linksCopy, _sim) => {
1049
- try {
1050
- const boundsToUse = clusterBounds?.bounds ?? packageBounds;
1051
- const nodeClusterMap = clusterBounds?.nodeToCluster ?? {};
1052
- if (boundsToUse) {
1053
- Object.values(nodesById).forEach((n) => {
1054
- if (!n) return;
1055
- const group = n.group ?? n.packageGroup;
1056
- const clusterKey = nodeClusterMap[n.id];
1057
- const key = clusterKey ?? (group ? `pkg:${group}` : void 0);
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
- const { packageAreas, localPositions } = React2__default.useMemo(() => {
1080
- try {
1081
- if (!initialNodes || !initialNodes.length) return { packageAreas: {}, localPositions: {} };
1082
- const groups = /* @__PURE__ */ new Map();
1083
- initialNodes.forEach((n) => {
1084
- const key = n.packageGroup || n.group || "root";
1085
- if (!groups.has(key)) groups.set(key, []);
1086
- groups.get(key).push(n);
1087
- });
1088
- const groupKeys = Array.from(groups.keys());
1089
- const children = groupKeys.map((k) => ({ name: k, value: Math.max(1, groups.get(k).length) }));
1090
- const root = d33.hierarchy({ children });
1091
- root.sum((d) => d.value);
1092
- const pack2 = d33.pack().size([width, height]).padding(Math.max(20, Math.min(width, height) * 0.03));
1093
- const packed = pack2(root);
1094
- const packageAreas2 = {};
1095
- if (packed.children) {
1096
- packed.children.forEach((c) => {
1097
- const name = c.data.name;
1098
- packageAreas2[name] = { x: c.x, y: c.y, r: Math.max(40, c.r) };
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
- const localPositions2 = {};
1102
- groups.forEach((nodesInGroup, _key) => {
1103
- if (!nodesInGroup || nodesInGroup.length === 0) return;
1104
- const localNodes = nodesInGroup.map((n) => ({ id: n.id, x: Math.random() * 10 - 5, y: Math.random() * 10 - 5, size: n.size || 10 }));
1105
- const localLinks = (initialLinks || []).filter((l) => {
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
- return { packageAreas: packageAreas2, localPositions: localPositions2 };
1122
- } catch (e) {
1123
- return { packageAreas: {}, localPositions: {} };
1124
- }
1125
- }, [initialNodes, initialLinks, width, height]);
1126
- const seededNodes = React2__default.useMemo(() => {
1127
- if (!initialNodes || !Object.keys(packageAreas || {}).length) return initialNodes;
1128
- return initialNodes.map((n) => {
1129
- const key = n.packageGroup || n.group || "root";
1130
- const area = packageAreas[key];
1131
- const lp = localPositions[n.id];
1132
- if (!area || !lp) return n;
1133
- const scale = Math.max(0.5, area.r * 0.6 / (Math.max(1, Math.sqrt(lp.x * lp.x + lp.y * lp.y)) || 1));
1134
- return { ...n, x: area.x + lp.x * scale, y: area.y + lp.y * scale };
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
- if (comps.length <= 1) return null;
1189
- const children = comps.map((c, i) => ({ name: String(i), value: Math.max(1, c.length) }));
1190
- d33.hierarchy({ children }).sum((d) => d.value).sort((a, b) => b.value - a.value);
1191
- const num = comps.length;
1192
- const cx = width / 2;
1193
- const cy = height / 2;
1194
- const base = Math.max(width, height);
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 {