@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.
@@ -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 = 800 / (dist * dist);
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 - 100) * 0.05;
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, isHovered ? size + 2 : size, 0, Math.PI * 2);
40412
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
40370
40413
  ctx.fillStyle = color;
40371
40414
  ctx.fill();
40372
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
40373
- ctx.lineWidth = isHovered ? 2 : 1;
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 + size + 12);
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
- onClick: (e) => {
40452
- const canvas = canvasRef.current;
40453
- if (!canvas) return;
40454
- const rect = canvas.getBoundingClientRect();
40455
- const x = (e.clientX - rect.left - offset.x) / zoom;
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 = 800 / (dist * dist);
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 - 100) * 0.05;
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, isHovered ? size + 2 : size, 0, Math.PI * 2);
40365
+ ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
40323
40366
  ctx.fillStyle = color;
40324
40367
  ctx.fill();
40325
- ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
40326
- ctx.lineWidth = isHovered ? 2 : 1;
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 + size + 12);
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
- onClick: (e) => {
40405
- const canvas = canvasRef.current;
40406
- if (!canvas) return;
40407
- const rect = canvas.getBoundingClientRect();
40408
- const x = (e.clientX - rect.left - offset.x) / zoom;
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 */