@almadar/ui 5.58.0 → 5.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +153 -23
- package/dist/avl/index.js +153 -23
- package/dist/components/core/molecules/GraphCanvas.d.ts +6 -0
- package/dist/components/index.cjs +153 -23
- package/dist/components/index.js +153 -23
- package/dist/providers/index.cjs +153 -23
- package/dist/providers/index.js +153 -23
- package/dist/runtime/index.cjs +153 -23
- package/dist/runtime/index.js +153 -23
- package/package.json +1 -1
package/dist/avl/index.cjs
CHANGED
|
@@ -40169,6 +40169,12 @@ function getGroupColor(group, groups) {
|
|
|
40169
40169
|
const idx = groups.indexOf(group);
|
|
40170
40170
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
40171
40171
|
}
|
|
40172
|
+
function resolveColor2(color, el) {
|
|
40173
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
40174
|
+
if (!m) return color;
|
|
40175
|
+
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
40176
|
+
return resolved || (m[2]?.trim() ?? "#888888");
|
|
40177
|
+
}
|
|
40172
40178
|
var GROUP_COLORS2, GraphCanvas;
|
|
40173
40179
|
var init_GraphCanvas = __esm({
|
|
40174
40180
|
"components/core/molecules/GraphCanvas.tsx"() {
|
|
@@ -40199,6 +40205,9 @@ var init_GraphCanvas = __esm({
|
|
|
40199
40205
|
actions,
|
|
40200
40206
|
onNodeClick,
|
|
40201
40207
|
nodeClickEvent,
|
|
40208
|
+
selectedNodeId,
|
|
40209
|
+
repulsion = 800,
|
|
40210
|
+
linkDistance = 100,
|
|
40202
40211
|
layout = "force",
|
|
40203
40212
|
entity,
|
|
40204
40213
|
isLoading = false,
|
|
@@ -40214,6 +40223,37 @@ var init_GraphCanvas = __esm({
|
|
|
40214
40223
|
const [hoveredNode, setHoveredNode] = React90.useState(null);
|
|
40215
40224
|
const nodesRef = React90.useRef([]);
|
|
40216
40225
|
const [, forceUpdate] = React90.useState(0);
|
|
40226
|
+
const interactionRef = React90.useRef({
|
|
40227
|
+
mode: "none",
|
|
40228
|
+
dragNodeId: null,
|
|
40229
|
+
startMouse: { x: 0, y: 0 },
|
|
40230
|
+
startOffset: { x: 0, y: 0 },
|
|
40231
|
+
downPos: { x: 0, y: 0 }
|
|
40232
|
+
});
|
|
40233
|
+
const toCoords = React90.useCallback(
|
|
40234
|
+
(e) => {
|
|
40235
|
+
const canvas = canvasRef.current;
|
|
40236
|
+
if (!canvas) return null;
|
|
40237
|
+
const rect = canvas.getBoundingClientRect();
|
|
40238
|
+
const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
40239
|
+
const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
40240
|
+
const screenX = (e.clientX - rect.left) * scaleX;
|
|
40241
|
+
const screenY = (e.clientY - rect.top) * scaleY;
|
|
40242
|
+
return {
|
|
40243
|
+
screenX,
|
|
40244
|
+
screenY,
|
|
40245
|
+
graphX: (screenX - offset.x) / zoom,
|
|
40246
|
+
graphY: (screenY - offset.y) / zoom
|
|
40247
|
+
};
|
|
40248
|
+
},
|
|
40249
|
+
[offset, zoom]
|
|
40250
|
+
);
|
|
40251
|
+
const nodeAt = React90.useCallback((graphX, graphY) => {
|
|
40252
|
+
return nodesRef.current.find((n) => {
|
|
40253
|
+
const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
|
|
40254
|
+
return dist < (n.size || 8) + 4;
|
|
40255
|
+
});
|
|
40256
|
+
}, []);
|
|
40217
40257
|
const handleAction = React90.useCallback(
|
|
40218
40258
|
(action) => {
|
|
40219
40259
|
if (action.event) {
|
|
@@ -40279,7 +40319,7 @@ var init_GraphCanvas = __esm({
|
|
|
40279
40319
|
const dx = nodes[j].x - nodes[i].x;
|
|
40280
40320
|
const dy = nodes[j].y - nodes[i].y;
|
|
40281
40321
|
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
40282
|
-
const force =
|
|
40322
|
+
const force = repulsion / (dist * dist);
|
|
40283
40323
|
const fx = dx / dist * force;
|
|
40284
40324
|
const fy = dy / dist * force;
|
|
40285
40325
|
nodes[i].fx -= fx;
|
|
@@ -40295,7 +40335,7 @@ var init_GraphCanvas = __esm({
|
|
|
40295
40335
|
const dx = target.x - source.x;
|
|
40296
40336
|
const dy = target.y - source.y;
|
|
40297
40337
|
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
40298
|
-
const force = (dist -
|
|
40338
|
+
const force = (dist - linkDistance) * 0.05;
|
|
40299
40339
|
const fx = dx / dist * force;
|
|
40300
40340
|
const fy = dy / dist * force;
|
|
40301
40341
|
source.fx += fx;
|
|
@@ -40329,7 +40369,7 @@ var init_GraphCanvas = __esm({
|
|
|
40329
40369
|
return () => {
|
|
40330
40370
|
cancelAnimationFrame(animRef.current);
|
|
40331
40371
|
};
|
|
40332
|
-
}, [propNodes, propEdges, layout]);
|
|
40372
|
+
}, [propNodes, propEdges, layout, repulsion, linkDistance]);
|
|
40333
40373
|
React90.useEffect(() => {
|
|
40334
40374
|
const canvas = canvasRef.current;
|
|
40335
40375
|
if (!canvas) return;
|
|
@@ -40338,6 +40378,7 @@ var init_GraphCanvas = __esm({
|
|
|
40338
40378
|
const w = canvas.width;
|
|
40339
40379
|
const h = canvas.height;
|
|
40340
40380
|
const nodes = nodesRef.current;
|
|
40381
|
+
const accentColor = resolveColor2("var(--color-accent)", canvas);
|
|
40341
40382
|
ctx.clearRect(0, 0, w, h);
|
|
40342
40383
|
ctx.save();
|
|
40343
40384
|
ctx.translate(offset.x, offset.y);
|
|
@@ -40363,20 +40404,27 @@ var init_GraphCanvas = __esm({
|
|
|
40363
40404
|
}
|
|
40364
40405
|
for (const node of nodes) {
|
|
40365
40406
|
const size = node.size || 8;
|
|
40366
|
-
const color = node.color || getGroupColor(node.group, groups);
|
|
40407
|
+
const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
|
|
40367
40408
|
const isHovered = hoveredNode === node.id;
|
|
40409
|
+
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
40410
|
+
const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
|
|
40368
40411
|
ctx.beginPath();
|
|
40369
|
-
ctx.arc(node.x, node.y,
|
|
40412
|
+
ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
|
|
40370
40413
|
ctx.fillStyle = color;
|
|
40371
40414
|
ctx.fill();
|
|
40372
|
-
|
|
40373
|
-
|
|
40415
|
+
if (isSelected) {
|
|
40416
|
+
ctx.strokeStyle = accentColor;
|
|
40417
|
+
ctx.lineWidth = 3;
|
|
40418
|
+
} else {
|
|
40419
|
+
ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
|
|
40420
|
+
ctx.lineWidth = isHovered ? 2 : 1;
|
|
40421
|
+
}
|
|
40374
40422
|
ctx.stroke();
|
|
40375
40423
|
if (showLabels && node.label) {
|
|
40376
40424
|
ctx.fillStyle = "#666666";
|
|
40377
|
-
ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
|
|
40425
|
+
ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
|
|
40378
40426
|
ctx.textAlign = "center";
|
|
40379
|
-
ctx.fillText(node.label, node.x, node.y +
|
|
40427
|
+
ctx.fillText(node.label, node.x, node.y + radius + 12);
|
|
40380
40428
|
}
|
|
40381
40429
|
}
|
|
40382
40430
|
ctx.restore();
|
|
@@ -40387,6 +40435,97 @@ var init_GraphCanvas = __esm({
|
|
|
40387
40435
|
setZoom(1);
|
|
40388
40436
|
setOffset({ x: 0, y: 0 });
|
|
40389
40437
|
}, []);
|
|
40438
|
+
const handleWheel = React90.useCallback(
|
|
40439
|
+
(e) => {
|
|
40440
|
+
if (!interactive) return;
|
|
40441
|
+
e.preventDefault();
|
|
40442
|
+
const coords = toCoords(e);
|
|
40443
|
+
if (!coords) return;
|
|
40444
|
+
const oldZoom = zoom;
|
|
40445
|
+
const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
|
|
40446
|
+
const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
|
|
40447
|
+
if (newZoom === oldZoom) return;
|
|
40448
|
+
setOffset((o) => ({
|
|
40449
|
+
x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
|
|
40450
|
+
y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
|
|
40451
|
+
}));
|
|
40452
|
+
setZoom(newZoom);
|
|
40453
|
+
},
|
|
40454
|
+
[interactive, toCoords, zoom]
|
|
40455
|
+
);
|
|
40456
|
+
const handleMouseDown = React90.useCallback(
|
|
40457
|
+
(e) => {
|
|
40458
|
+
const coords = toCoords(e);
|
|
40459
|
+
if (!coords) return;
|
|
40460
|
+
const node = nodeAt(coords.graphX, coords.graphY);
|
|
40461
|
+
const state = interactionRef.current;
|
|
40462
|
+
state.downPos = { x: e.clientX, y: e.clientY };
|
|
40463
|
+
state.startMouse = { x: e.clientX, y: e.clientY };
|
|
40464
|
+
state.startOffset = { ...offset };
|
|
40465
|
+
if (draggable && node) {
|
|
40466
|
+
state.mode = "dragging";
|
|
40467
|
+
state.dragNodeId = node.id;
|
|
40468
|
+
} else if (interactive) {
|
|
40469
|
+
state.mode = "panning";
|
|
40470
|
+
state.dragNodeId = null;
|
|
40471
|
+
} else {
|
|
40472
|
+
state.mode = "none";
|
|
40473
|
+
state.dragNodeId = null;
|
|
40474
|
+
}
|
|
40475
|
+
},
|
|
40476
|
+
[toCoords, nodeAt, draggable, interactive, offset]
|
|
40477
|
+
);
|
|
40478
|
+
const handleMouseMove = React90.useCallback(
|
|
40479
|
+
(e) => {
|
|
40480
|
+
const state = interactionRef.current;
|
|
40481
|
+
if (state.mode === "panning") {
|
|
40482
|
+
const dx = e.clientX - state.startMouse.x;
|
|
40483
|
+
const dy = e.clientY - state.startMouse.y;
|
|
40484
|
+
setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
|
|
40485
|
+
return;
|
|
40486
|
+
}
|
|
40487
|
+
if (state.mode === "dragging" && state.dragNodeId) {
|
|
40488
|
+
const coords2 = toCoords(e);
|
|
40489
|
+
if (!coords2) return;
|
|
40490
|
+
const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
|
|
40491
|
+
if (node2) {
|
|
40492
|
+
node2.x = coords2.graphX;
|
|
40493
|
+
node2.y = coords2.graphY;
|
|
40494
|
+
node2.vx = 0;
|
|
40495
|
+
node2.vy = 0;
|
|
40496
|
+
forceUpdate((n) => n + 1);
|
|
40497
|
+
}
|
|
40498
|
+
return;
|
|
40499
|
+
}
|
|
40500
|
+
const coords = toCoords(e);
|
|
40501
|
+
if (!coords) return;
|
|
40502
|
+
const node = nodeAt(coords.graphX, coords.graphY);
|
|
40503
|
+
setHoveredNode(node?.id ?? null);
|
|
40504
|
+
},
|
|
40505
|
+
[toCoords, nodeAt]
|
|
40506
|
+
);
|
|
40507
|
+
const handleMouseUp = React90.useCallback(
|
|
40508
|
+
(e) => {
|
|
40509
|
+
const state = interactionRef.current;
|
|
40510
|
+
const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
|
|
40511
|
+
state.mode = "none";
|
|
40512
|
+
state.dragNodeId = null;
|
|
40513
|
+
if (moved < 4) {
|
|
40514
|
+
const coords = toCoords(e);
|
|
40515
|
+
if (!coords) return;
|
|
40516
|
+
const node = nodeAt(coords.graphX, coords.graphY);
|
|
40517
|
+
if (node) {
|
|
40518
|
+
handleNodeClick(node);
|
|
40519
|
+
}
|
|
40520
|
+
}
|
|
40521
|
+
},
|
|
40522
|
+
[toCoords, nodeAt, handleNodeClick]
|
|
40523
|
+
);
|
|
40524
|
+
const handleMouseLeave = React90.useCallback(() => {
|
|
40525
|
+
interactionRef.current.mode = "none";
|
|
40526
|
+
interactionRef.current.dragNodeId = null;
|
|
40527
|
+
setHoveredNode(null);
|
|
40528
|
+
}, []);
|
|
40390
40529
|
if (isLoading) {
|
|
40391
40530
|
return /* @__PURE__ */ jsxRuntime.jsx(LoadingState, { message: t("common.loading"), className });
|
|
40392
40531
|
}
|
|
@@ -40448,20 +40587,11 @@ var init_GraphCanvas = __esm({
|
|
|
40448
40587
|
height,
|
|
40449
40588
|
className: "w-full cursor-grab active:cursor-grabbing",
|
|
40450
40589
|
style: { height },
|
|
40451
|
-
|
|
40452
|
-
|
|
40453
|
-
|
|
40454
|
-
|
|
40455
|
-
|
|
40456
|
-
const y = (e.clientY - rect.top - offset.y) / zoom;
|
|
40457
|
-
const clickedNode = nodesRef.current.find((n) => {
|
|
40458
|
-
const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
|
|
40459
|
-
return dist < (n.size || 8) + 4;
|
|
40460
|
-
});
|
|
40461
|
-
if (clickedNode) {
|
|
40462
|
-
handleNodeClick(clickedNode);
|
|
40463
|
-
}
|
|
40464
|
-
}
|
|
40590
|
+
onWheel: handleWheel,
|
|
40591
|
+
onMouseDown: handleMouseDown,
|
|
40592
|
+
onMouseMove: handleMouseMove,
|
|
40593
|
+
onMouseUp: handleMouseUp,
|
|
40594
|
+
onMouseLeave: handleMouseLeave
|
|
40465
40595
|
}
|
|
40466
40596
|
) }),
|
|
40467
40597
|
groups.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
package/dist/avl/index.js
CHANGED
|
@@ -40122,6 +40122,12 @@ function getGroupColor(group, groups) {
|
|
|
40122
40122
|
const idx = groups.indexOf(group);
|
|
40123
40123
|
return GROUP_COLORS2[idx % GROUP_COLORS2.length];
|
|
40124
40124
|
}
|
|
40125
|
+
function resolveColor2(color, el) {
|
|
40126
|
+
const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
|
|
40127
|
+
if (!m) return color;
|
|
40128
|
+
const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
|
|
40129
|
+
return resolved || (m[2]?.trim() ?? "#888888");
|
|
40130
|
+
}
|
|
40125
40131
|
var GROUP_COLORS2, GraphCanvas;
|
|
40126
40132
|
var init_GraphCanvas = __esm({
|
|
40127
40133
|
"components/core/molecules/GraphCanvas.tsx"() {
|
|
@@ -40152,6 +40158,9 @@ var init_GraphCanvas = __esm({
|
|
|
40152
40158
|
actions,
|
|
40153
40159
|
onNodeClick,
|
|
40154
40160
|
nodeClickEvent,
|
|
40161
|
+
selectedNodeId,
|
|
40162
|
+
repulsion = 800,
|
|
40163
|
+
linkDistance = 100,
|
|
40155
40164
|
layout = "force",
|
|
40156
40165
|
entity,
|
|
40157
40166
|
isLoading = false,
|
|
@@ -40167,6 +40176,37 @@ var init_GraphCanvas = __esm({
|
|
|
40167
40176
|
const [hoveredNode, setHoveredNode] = useState(null);
|
|
40168
40177
|
const nodesRef = useRef([]);
|
|
40169
40178
|
const [, forceUpdate] = useState(0);
|
|
40179
|
+
const interactionRef = useRef({
|
|
40180
|
+
mode: "none",
|
|
40181
|
+
dragNodeId: null,
|
|
40182
|
+
startMouse: { x: 0, y: 0 },
|
|
40183
|
+
startOffset: { x: 0, y: 0 },
|
|
40184
|
+
downPos: { x: 0, y: 0 }
|
|
40185
|
+
});
|
|
40186
|
+
const toCoords = useCallback(
|
|
40187
|
+
(e) => {
|
|
40188
|
+
const canvas = canvasRef.current;
|
|
40189
|
+
if (!canvas) return null;
|
|
40190
|
+
const rect = canvas.getBoundingClientRect();
|
|
40191
|
+
const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
|
|
40192
|
+
const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
|
|
40193
|
+
const screenX = (e.clientX - rect.left) * scaleX;
|
|
40194
|
+
const screenY = (e.clientY - rect.top) * scaleY;
|
|
40195
|
+
return {
|
|
40196
|
+
screenX,
|
|
40197
|
+
screenY,
|
|
40198
|
+
graphX: (screenX - offset.x) / zoom,
|
|
40199
|
+
graphY: (screenY - offset.y) / zoom
|
|
40200
|
+
};
|
|
40201
|
+
},
|
|
40202
|
+
[offset, zoom]
|
|
40203
|
+
);
|
|
40204
|
+
const nodeAt = useCallback((graphX, graphY) => {
|
|
40205
|
+
return nodesRef.current.find((n) => {
|
|
40206
|
+
const dist = Math.sqrt((n.x - graphX) ** 2 + (n.y - graphY) ** 2);
|
|
40207
|
+
return dist < (n.size || 8) + 4;
|
|
40208
|
+
});
|
|
40209
|
+
}, []);
|
|
40170
40210
|
const handleAction = useCallback(
|
|
40171
40211
|
(action) => {
|
|
40172
40212
|
if (action.event) {
|
|
@@ -40232,7 +40272,7 @@ var init_GraphCanvas = __esm({
|
|
|
40232
40272
|
const dx = nodes[j].x - nodes[i].x;
|
|
40233
40273
|
const dy = nodes[j].y - nodes[i].y;
|
|
40234
40274
|
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
40235
|
-
const force =
|
|
40275
|
+
const force = repulsion / (dist * dist);
|
|
40236
40276
|
const fx = dx / dist * force;
|
|
40237
40277
|
const fy = dy / dist * force;
|
|
40238
40278
|
nodes[i].fx -= fx;
|
|
@@ -40248,7 +40288,7 @@ var init_GraphCanvas = __esm({
|
|
|
40248
40288
|
const dx = target.x - source.x;
|
|
40249
40289
|
const dy = target.y - source.y;
|
|
40250
40290
|
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
40251
|
-
const force = (dist -
|
|
40291
|
+
const force = (dist - linkDistance) * 0.05;
|
|
40252
40292
|
const fx = dx / dist * force;
|
|
40253
40293
|
const fy = dy / dist * force;
|
|
40254
40294
|
source.fx += fx;
|
|
@@ -40282,7 +40322,7 @@ var init_GraphCanvas = __esm({
|
|
|
40282
40322
|
return () => {
|
|
40283
40323
|
cancelAnimationFrame(animRef.current);
|
|
40284
40324
|
};
|
|
40285
|
-
}, [propNodes, propEdges, layout]);
|
|
40325
|
+
}, [propNodes, propEdges, layout, repulsion, linkDistance]);
|
|
40286
40326
|
useEffect(() => {
|
|
40287
40327
|
const canvas = canvasRef.current;
|
|
40288
40328
|
if (!canvas) return;
|
|
@@ -40291,6 +40331,7 @@ var init_GraphCanvas = __esm({
|
|
|
40291
40331
|
const w = canvas.width;
|
|
40292
40332
|
const h = canvas.height;
|
|
40293
40333
|
const nodes = nodesRef.current;
|
|
40334
|
+
const accentColor = resolveColor2("var(--color-accent)", canvas);
|
|
40294
40335
|
ctx.clearRect(0, 0, w, h);
|
|
40295
40336
|
ctx.save();
|
|
40296
40337
|
ctx.translate(offset.x, offset.y);
|
|
@@ -40316,20 +40357,27 @@ var init_GraphCanvas = __esm({
|
|
|
40316
40357
|
}
|
|
40317
40358
|
for (const node of nodes) {
|
|
40318
40359
|
const size = node.size || 8;
|
|
40319
|
-
const color = node.color || getGroupColor(node.group, groups);
|
|
40360
|
+
const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
|
|
40320
40361
|
const isHovered = hoveredNode === node.id;
|
|
40362
|
+
const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
|
|
40363
|
+
const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
|
|
40321
40364
|
ctx.beginPath();
|
|
40322
|
-
ctx.arc(node.x, node.y,
|
|
40365
|
+
ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
|
|
40323
40366
|
ctx.fillStyle = color;
|
|
40324
40367
|
ctx.fill();
|
|
40325
|
-
|
|
40326
|
-
|
|
40368
|
+
if (isSelected) {
|
|
40369
|
+
ctx.strokeStyle = accentColor;
|
|
40370
|
+
ctx.lineWidth = 3;
|
|
40371
|
+
} else {
|
|
40372
|
+
ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
|
|
40373
|
+
ctx.lineWidth = isHovered ? 2 : 1;
|
|
40374
|
+
}
|
|
40327
40375
|
ctx.stroke();
|
|
40328
40376
|
if (showLabels && node.label) {
|
|
40329
40377
|
ctx.fillStyle = "#666666";
|
|
40330
|
-
ctx.font = `${isHovered ? "bold " : ""}10px system-ui`;
|
|
40378
|
+
ctx.font = `${isSelected || isHovered ? "bold " : ""}10px system-ui`;
|
|
40331
40379
|
ctx.textAlign = "center";
|
|
40332
|
-
ctx.fillText(node.label, node.x, node.y +
|
|
40380
|
+
ctx.fillText(node.label, node.x, node.y + radius + 12);
|
|
40333
40381
|
}
|
|
40334
40382
|
}
|
|
40335
40383
|
ctx.restore();
|
|
@@ -40340,6 +40388,97 @@ var init_GraphCanvas = __esm({
|
|
|
40340
40388
|
setZoom(1);
|
|
40341
40389
|
setOffset({ x: 0, y: 0 });
|
|
40342
40390
|
}, []);
|
|
40391
|
+
const handleWheel = useCallback(
|
|
40392
|
+
(e) => {
|
|
40393
|
+
if (!interactive) return;
|
|
40394
|
+
e.preventDefault();
|
|
40395
|
+
const coords = toCoords(e);
|
|
40396
|
+
if (!coords) return;
|
|
40397
|
+
const oldZoom = zoom;
|
|
40398
|
+
const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
|
|
40399
|
+
const newZoom = Math.max(0.3, Math.min(3, oldZoom * factor));
|
|
40400
|
+
if (newZoom === oldZoom) return;
|
|
40401
|
+
setOffset((o) => ({
|
|
40402
|
+
x: coords.screenX - (coords.screenX - o.x) * (newZoom / oldZoom),
|
|
40403
|
+
y: coords.screenY - (coords.screenY - o.y) * (newZoom / oldZoom)
|
|
40404
|
+
}));
|
|
40405
|
+
setZoom(newZoom);
|
|
40406
|
+
},
|
|
40407
|
+
[interactive, toCoords, zoom]
|
|
40408
|
+
);
|
|
40409
|
+
const handleMouseDown = useCallback(
|
|
40410
|
+
(e) => {
|
|
40411
|
+
const coords = toCoords(e);
|
|
40412
|
+
if (!coords) return;
|
|
40413
|
+
const node = nodeAt(coords.graphX, coords.graphY);
|
|
40414
|
+
const state = interactionRef.current;
|
|
40415
|
+
state.downPos = { x: e.clientX, y: e.clientY };
|
|
40416
|
+
state.startMouse = { x: e.clientX, y: e.clientY };
|
|
40417
|
+
state.startOffset = { ...offset };
|
|
40418
|
+
if (draggable && node) {
|
|
40419
|
+
state.mode = "dragging";
|
|
40420
|
+
state.dragNodeId = node.id;
|
|
40421
|
+
} else if (interactive) {
|
|
40422
|
+
state.mode = "panning";
|
|
40423
|
+
state.dragNodeId = null;
|
|
40424
|
+
} else {
|
|
40425
|
+
state.mode = "none";
|
|
40426
|
+
state.dragNodeId = null;
|
|
40427
|
+
}
|
|
40428
|
+
},
|
|
40429
|
+
[toCoords, nodeAt, draggable, interactive, offset]
|
|
40430
|
+
);
|
|
40431
|
+
const handleMouseMove = useCallback(
|
|
40432
|
+
(e) => {
|
|
40433
|
+
const state = interactionRef.current;
|
|
40434
|
+
if (state.mode === "panning") {
|
|
40435
|
+
const dx = e.clientX - state.startMouse.x;
|
|
40436
|
+
const dy = e.clientY - state.startMouse.y;
|
|
40437
|
+
setOffset({ x: state.startOffset.x + dx, y: state.startOffset.y + dy });
|
|
40438
|
+
return;
|
|
40439
|
+
}
|
|
40440
|
+
if (state.mode === "dragging" && state.dragNodeId) {
|
|
40441
|
+
const coords2 = toCoords(e);
|
|
40442
|
+
if (!coords2) return;
|
|
40443
|
+
const node2 = nodesRef.current.find((n) => n.id === state.dragNodeId);
|
|
40444
|
+
if (node2) {
|
|
40445
|
+
node2.x = coords2.graphX;
|
|
40446
|
+
node2.y = coords2.graphY;
|
|
40447
|
+
node2.vx = 0;
|
|
40448
|
+
node2.vy = 0;
|
|
40449
|
+
forceUpdate((n) => n + 1);
|
|
40450
|
+
}
|
|
40451
|
+
return;
|
|
40452
|
+
}
|
|
40453
|
+
const coords = toCoords(e);
|
|
40454
|
+
if (!coords) return;
|
|
40455
|
+
const node = nodeAt(coords.graphX, coords.graphY);
|
|
40456
|
+
setHoveredNode(node?.id ?? null);
|
|
40457
|
+
},
|
|
40458
|
+
[toCoords, nodeAt]
|
|
40459
|
+
);
|
|
40460
|
+
const handleMouseUp = useCallback(
|
|
40461
|
+
(e) => {
|
|
40462
|
+
const state = interactionRef.current;
|
|
40463
|
+
const moved = Math.abs(e.clientX - state.downPos.x) + Math.abs(e.clientY - state.downPos.y);
|
|
40464
|
+
state.mode = "none";
|
|
40465
|
+
state.dragNodeId = null;
|
|
40466
|
+
if (moved < 4) {
|
|
40467
|
+
const coords = toCoords(e);
|
|
40468
|
+
if (!coords) return;
|
|
40469
|
+
const node = nodeAt(coords.graphX, coords.graphY);
|
|
40470
|
+
if (node) {
|
|
40471
|
+
handleNodeClick(node);
|
|
40472
|
+
}
|
|
40473
|
+
}
|
|
40474
|
+
},
|
|
40475
|
+
[toCoords, nodeAt, handleNodeClick]
|
|
40476
|
+
);
|
|
40477
|
+
const handleMouseLeave = useCallback(() => {
|
|
40478
|
+
interactionRef.current.mode = "none";
|
|
40479
|
+
interactionRef.current.dragNodeId = null;
|
|
40480
|
+
setHoveredNode(null);
|
|
40481
|
+
}, []);
|
|
40343
40482
|
if (isLoading) {
|
|
40344
40483
|
return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
|
|
40345
40484
|
}
|
|
@@ -40401,20 +40540,11 @@ var init_GraphCanvas = __esm({
|
|
|
40401
40540
|
height,
|
|
40402
40541
|
className: "w-full cursor-grab active:cursor-grabbing",
|
|
40403
40542
|
style: { height },
|
|
40404
|
-
|
|
40405
|
-
|
|
40406
|
-
|
|
40407
|
-
|
|
40408
|
-
|
|
40409
|
-
const y = (e.clientY - rect.top - offset.y) / zoom;
|
|
40410
|
-
const clickedNode = nodesRef.current.find((n) => {
|
|
40411
|
-
const dist = Math.sqrt((n.x - x) ** 2 + (n.y - y) ** 2);
|
|
40412
|
-
return dist < (n.size || 8) + 4;
|
|
40413
|
-
});
|
|
40414
|
-
if (clickedNode) {
|
|
40415
|
-
handleNodeClick(clickedNode);
|
|
40416
|
-
}
|
|
40417
|
-
}
|
|
40543
|
+
onWheel: handleWheel,
|
|
40544
|
+
onMouseDown: handleMouseDown,
|
|
40545
|
+
onMouseMove: handleMouseMove,
|
|
40546
|
+
onMouseUp: handleMouseUp,
|
|
40547
|
+
onMouseLeave: handleMouseLeave
|
|
40418
40548
|
}
|
|
40419
40549
|
) }),
|
|
40420
40550
|
groups.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
@@ -59,6 +59,12 @@ export interface GraphCanvasProps {
|
|
|
59
59
|
nodeClickEvent?: EventEmit<{
|
|
60
60
|
id: string;
|
|
61
61
|
}>;
|
|
62
|
+
/** Currently selected node id (rendered emphasized) */
|
|
63
|
+
selectedNodeId?: string;
|
|
64
|
+
/** Force-sim repulsion strength between nodes (larger ⇒ more spread out) */
|
|
65
|
+
repulsion?: number;
|
|
66
|
+
/** Force-sim target edge length (larger ⇒ more spread out) */
|
|
67
|
+
linkDistance?: number;
|
|
62
68
|
/** Layout algorithm */
|
|
63
69
|
layout?: "force" | "circular" | "grid";
|
|
64
70
|
/** Entity name for schema-driven auto-fetch */
|