polly-graph 0.1.15 → 0.1.16

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.cjs CHANGED
@@ -4761,7 +4761,18 @@ function getShortenedSourcePoint(link, style) {
4761
4761
  const dx = targetX - sourceX;
4762
4762
  const dy = targetY - sourceY;
4763
4763
  const distance = Math.sqrt(dx * dx + dy * dy) || 1;
4764
- const sourceRadius = source.style?.radius ?? 12;
4764
+ let sourceRadius = source.style?.radius ?? 12;
4765
+ if (typeof document !== "undefined") {
4766
+ const circles = Array.from(document.querySelectorAll("circle"));
4767
+ for (const circle of circles) {
4768
+ const boundData = circle.__data__;
4769
+ if (boundData && boundData.id === source.id) {
4770
+ const currentRadius = parseFloat(circle.getAttribute("r") || "12");
4771
+ sourceRadius = currentRadius;
4772
+ break;
4773
+ }
4774
+ }
4775
+ }
4765
4776
  const sourceStrokeWidth = source.style?.strokeWidth ?? 1.5;
4766
4777
  const linkStrokeCompensation = style.strokeWidth / 2;
4767
4778
  const nodeStrokeOffset = sourceStrokeWidth / 2;
@@ -4782,7 +4793,18 @@ function getShortenedTargetPoint(link, style) {
4782
4793
  const dx = targetX - sourceX;
4783
4794
  const dy = targetY - sourceY;
4784
4795
  const distance = Math.sqrt(dx * dx + dy * dy) || 1;
4785
- const targetRadius = target.style?.radius ?? 12;
4796
+ let targetRadius = target.style?.radius ?? 12;
4797
+ if (typeof document !== "undefined") {
4798
+ const circles = Array.from(document.querySelectorAll("circle"));
4799
+ for (const circle of circles) {
4800
+ const boundData = circle.__data__;
4801
+ if (boundData && boundData.id === target.id) {
4802
+ const currentRadius = parseFloat(circle.getAttribute("r") || "12");
4803
+ targetRadius = currentRadius;
4804
+ break;
4805
+ }
4806
+ }
4807
+ }
4786
4808
  const targetStrokeWidth = target.style?.strokeWidth ?? 1.5;
4787
4809
  const arrowLength = style.arrow.enabled ? style.arrow.size * 2 : 0;
4788
4810
  const linkStrokeCompensation = style.strokeWidth / 2;
@@ -5407,48 +5429,38 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5407
5429
  const svgElement = firstNode.ownerSVGElement;
5408
5430
  if (!svgElement) return;
5409
5431
  const root2 = select_default2(svgElement);
5432
+ const elevatedElements = /* @__PURE__ */ new Set();
5410
5433
  function clearAllHoverLayers() {
5411
- const hoverNodesLayer = root2.select('[data-layer="hover-nodes"]').node();
5412
- const nodesLayer = root2.select('[data-layer="nodes"]').node();
5413
- if (hoverNodesLayer && nodesLayer) {
5414
- while (hoverNodesLayer.firstChild) {
5415
- nodesLayer.appendChild(hoverNodesLayer.firstChild);
5416
- }
5417
- }
5418
- const hoverNodeLabelsLayer = root2.select('[data-layer="hover-node-labels"]').node();
5419
- const nodeLabelsLayer = root2.select('[data-layer="node-labels"]').node();
5420
- if (hoverNodeLabelsLayer && nodeLabelsLayer) {
5421
- while (hoverNodeLabelsLayer.firstChild) {
5422
- nodeLabelsLayer.appendChild(hoverNodeLabelsLayer.firstChild);
5423
- }
5424
- }
5425
- const hoverLinksLayer = root2.select('[data-layer="hover-links"]').node();
5426
- const linksLayer = root2.select('[data-layer="links"]').node();
5427
- if (hoverLinksLayer && linksLayer) {
5428
- while (hoverLinksLayer.firstChild) {
5429
- const linkElement = hoverLinksLayer.firstChild;
5430
- linksLayer.appendChild(linkElement);
5431
- const event = new MouseEvent("mouseleave", {
5432
- bubbles: true,
5433
- cancelable: false,
5434
- view: window
5435
- });
5436
- linkElement.dispatchEvent(event);
5437
- }
5438
- }
5439
- const hoverLinkLabelsLayer = root2.select('[data-layer="hover-link-labels"]').node();
5440
- const linkLabelsLayer = root2.select('[data-layer="link-labels"]').node();
5441
- if (hoverLinkLabelsLayer && linkLabelsLayer) {
5442
- while (hoverLinkLabelsLayer.firstChild) {
5443
- const labelElement = hoverLinkLabelsLayer.firstChild;
5444
- const labelData = labelElement.__data__;
5445
- if (labelData && labelData.style.label.visibility === "hover" && !labelElement.classList.contains("label-selection-pinned")) {
5446
- labelElement.style.opacity = "0";
5447
- labelElement.style.pointerEvents = "none";
5434
+ elevatedElements.forEach((element) => {
5435
+ element.classList.remove("pg-hover-elevated");
5436
+ element.style.removeProperty("filter");
5437
+ if (element.tagName === "circle") {
5438
+ element.removeAttribute("data-hover-elevated");
5439
+ }
5440
+ if (element.classList.contains("link-label")) {
5441
+ const labelData = element.__data__;
5442
+ if (labelData && labelData.style.label.visibility === "hover" && !element.classList.contains("label-selection-pinned")) {
5443
+ element.style.opacity = "0";
5444
+ element.style.pointerEvents = "none";
5448
5445
  }
5449
- linkLabelsLayer.appendChild(labelElement);
5450
5446
  }
5451
- }
5447
+ });
5448
+ elevatedElements.clear();
5449
+ root2.selectAll("line[data-hover-elevated]").each(function() {
5450
+ const linkElement = this;
5451
+ linkElement.removeAttribute("data-hover-elevated");
5452
+ const event = new MouseEvent("mouseleave", {
5453
+ bubbles: true,
5454
+ cancelable: false,
5455
+ view: window
5456
+ });
5457
+ linkElement.dispatchEvent(event);
5458
+ });
5459
+ }
5460
+ function elevateElement(element) {
5461
+ element.classList.add("pg-hover-elevated");
5462
+ element.style.filter = "drop-shadow(0 2px 4px rgba(0,0,0,0.15))";
5463
+ elevatedElements.add(element);
5452
5464
  }
5453
5465
  nodeSelection.on("mouseenter.links", function(_event, hoveredNode) {
5454
5466
  const hoveredNodeElement = this;
@@ -5459,16 +5471,11 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5459
5471
  return;
5460
5472
  }
5461
5473
  clearAllHoverLayers();
5462
- const hoverNodesLayer = root2.select('[data-layer="hover-nodes"]').node();
5463
- if (hoverNodesLayer) {
5464
- hoverNodesLayer.appendChild(hoveredNodeElement);
5465
- }
5474
+ elevateElement(hoveredNodeElement);
5475
+ hoveredNodeElement.setAttribute("data-hover-elevated", "true");
5466
5476
  root2.selectAll("text").filter((d) => d.id === hoveredNode.id).each(function() {
5467
5477
  const labelElement = this;
5468
- const hoverNodeLabelsLayer = root2.select('[data-layer="hover-node-labels"]').node();
5469
- if (hoverNodeLabelsLayer) {
5470
- hoverNodeLabelsLayer.appendChild(labelElement);
5471
- }
5478
+ elevateElement(labelElement);
5472
5479
  });
5473
5480
  const connectedLinks = root2.selectAll("line:not(.link-hit-area)").filter((renderableLink) => {
5474
5481
  const source = renderableLink.link.source;
@@ -5477,10 +5484,8 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5477
5484
  });
5478
5485
  connectedLinks.each(function(_renderableLink) {
5479
5486
  const linkElement = this;
5480
- const hoverLinksLayer = root2.select('[data-layer="hover-links"]').node();
5481
- if (hoverLinksLayer) {
5482
- hoverLinksLayer.appendChild(linkElement);
5483
- }
5487
+ elevateElement(linkElement);
5488
+ linkElement.setAttribute("data-hover-elevated", "true");
5484
5489
  const event = new MouseEvent("mouseenter", {
5485
5490
  bubbles: true,
5486
5491
  cancelable: false,
@@ -5494,13 +5499,10 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5494
5499
  return source.id === hoveredNode.id || target.id === hoveredNode.id;
5495
5500
  }).each(function(item) {
5496
5501
  const labelElement = this;
5497
- const hoverLinkLabelsLayer = root2.select('[data-layer="hover-link-labels"]').node();
5498
- if (hoverLinkLabelsLayer) {
5499
- hoverLinkLabelsLayer.appendChild(labelElement);
5500
- if (item.style.label.visibility === "hover") {
5501
- labelElement.style.opacity = "1";
5502
- labelElement.style.pointerEvents = "auto";
5503
- }
5502
+ elevateElement(labelElement);
5503
+ if (item.style.label.visibility === "hover") {
5504
+ labelElement.style.opacity = "1";
5505
+ labelElement.style.pointerEvents = "auto";
5504
5506
  }
5505
5507
  });
5506
5508
  }).on("mouseleave.links", function(_event, _hoveredNode) {
@@ -5882,11 +5884,15 @@ function updateHitAreaDimensions(rectElement, item, root2) {
5882
5884
  const source = item.link.source;
5883
5885
  const target = item.link.target;
5884
5886
  if (!source.x || !source.y || !target.x || !target.y) return;
5887
+ const dx = target.x - source.x;
5888
+ const dy = target.y - source.y;
5889
+ const linkLength = Math.sqrt(dx * dx + dy * dy);
5890
+ const angle = Math.atan2(dy, dx);
5885
5891
  const midX = (source.x + target.x) / 2;
5886
5892
  const midY = (source.y + target.y) / 2;
5887
- const linkPadding = Math.max(item.style.strokeWidth || 2, item.style.arrow.size) * 2;
5888
- let width = linkPadding;
5889
- let height = linkPadding;
5893
+ const thickness = Math.max((item.style.strokeWidth || 2) * 4, item.style.arrow.size * 2, 20);
5894
+ let length = linkLength + item.style.arrow.size * 2;
5895
+ let width = thickness;
5890
5896
  if (item.link.label) {
5891
5897
  const labelElement = root2.select('[data-layer="link-labels"]').selectAll(".link-label").filter((labelItem) => labelItem.link === item.link).node();
5892
5898
  if (labelElement) {
@@ -5897,23 +5903,27 @@ function updateHitAreaDimensions(rectElement, item, root2) {
5897
5903
  const bbox = textElement.getBBox();
5898
5904
  const labelWidth = bbox.width + item.style.label.paddingX * 2;
5899
5905
  const labelHeight = bbox.height + item.style.label.paddingY * 2;
5900
- width = Math.max(width, labelWidth + 10);
5901
- height = Math.max(height, labelHeight + 10);
5906
+ width = Math.max(width, labelHeight + 10);
5907
+ length = Math.max(length, labelWidth + 20);
5902
5908
  }
5903
5909
  } catch {
5904
5910
  const text = item.link.label ?? "";
5905
5911
  const fontSize = item.style.label.fontSize;
5906
- const estimatedWidth = text.length * fontSize * 0.6 + item.style.label.paddingX * 2 + 10;
5912
+ const estimatedWidth = text.length * fontSize * 0.6 + item.style.label.paddingX * 2 + 20;
5907
5913
  const estimatedHeight = fontSize + item.style.label.paddingY * 2 + 10;
5908
- width = Math.max(width, estimatedWidth);
5909
- height = Math.max(height, estimatedHeight);
5914
+ width = Math.max(width, estimatedHeight);
5915
+ length = Math.max(length, estimatedWidth);
5910
5916
  }
5911
5917
  }
5912
5918
  }
5913
- rectElement.setAttribute("x", String(midX - width / 2));
5914
- rectElement.setAttribute("y", String(midY - height / 2));
5915
- rectElement.setAttribute("width", String(width));
5916
- rectElement.setAttribute("height", String(height));
5919
+ const rectX = midX - length / 2;
5920
+ const rectY = midY - width / 2;
5921
+ rectElement.setAttribute("width", String(length));
5922
+ rectElement.setAttribute("height", String(width));
5923
+ rectElement.setAttribute("x", String(rectX));
5924
+ rectElement.setAttribute("y", String(rectY));
5925
+ const degrees2 = angle * 180 / Math.PI;
5926
+ rectElement.setAttribute("transform", `rotate(${degrees2}, ${midX}, ${midY})`);
5917
5927
  }
5918
5928
 
5919
5929
  // src/utils/get-link-target-point.ts
@@ -6152,7 +6162,10 @@ var SelectionManager = class {
6152
6162
  if (style.stroke !== void 0) nodeElement.style.stroke = style.stroke;
6153
6163
  if (style.strokeWidth !== void 0) nodeElement.style.strokeWidth = String(style.strokeWidth);
6154
6164
  if (style.opacity !== void 0) nodeElement.style.opacity = String(style.opacity);
6155
- if (style.radius !== void 0) nodeElement.setAttribute("r", String(style.radius));
6165
+ if (style.radius !== void 0) {
6166
+ nodeElement.setAttribute("r", String(style.radius));
6167
+ this.updateConnectedLinkPositions(nodeData);
6168
+ }
6156
6169
  }
6157
6170
  this.root.selectAll(".link-label").filter((item) => {
6158
6171
  if (item.style.label.visibility !== "hover") return false;
@@ -6209,6 +6222,7 @@ var SelectionManager = class {
6209
6222
  element.style.opacity = "";
6210
6223
  const originalRadius = data.style?.radius ?? 8;
6211
6224
  element.setAttribute("r", String(originalRadius));
6225
+ this.updateConnectedLinkPositions(data);
6212
6226
  delete element.dataset.selected;
6213
6227
  this.root.selectAll(".link-label.label-selection-pinned").classed("label-selection-pinned", false).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
6214
6228
  this.state.selectedNode = null;
@@ -6221,7 +6235,7 @@ var SelectionManager = class {
6221
6235
  if (!this.state.selectedLink) return;
6222
6236
  const { element, data, originalMarker } = this.state.selectedLink;
6223
6237
  this.layers.links.appendChild(element);
6224
- this.root.selectAll(".link-label").filter((item) => item.link === data).each((d, i, nodes) => {
6238
+ this.root.selectAll(".link-label").filter((item) => item.link === data).each((_, i, nodes) => {
6225
6239
  const element2 = nodes[i];
6226
6240
  if (element2) {
6227
6241
  this.layers.linkLabels.appendChild(element2);
@@ -6291,13 +6305,13 @@ var SelectionManager = class {
6291
6305
  const target = d.link.target;
6292
6306
  return source.id === nodeData.id || target.id === nodeData.id;
6293
6307
  });
6294
- connectedLinks.each((d, i, nodes) => {
6308
+ connectedLinks.each((_, i, nodes) => {
6295
6309
  const element = nodes[i];
6296
6310
  if (element) {
6297
6311
  this.layers.selectionLayer.links.appendChild(element);
6298
6312
  }
6299
6313
  });
6300
- this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((d, i, nodes) => {
6314
+ this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((_, i, nodes) => {
6301
6315
  const element = nodes[i];
6302
6316
  if (element) {
6303
6317
  this.layers.selectionLayer.nodeLabels.appendChild(element);
@@ -6307,7 +6321,7 @@ var SelectionManager = class {
6307
6321
  const source = item.link.source;
6308
6322
  const target = item.link.target;
6309
6323
  return source.id === nodeData.id || target.id === nodeData.id;
6310
- }).each((d, i, nodes) => {
6324
+ }).each((_, i, nodes) => {
6311
6325
  const element = nodes[i];
6312
6326
  if (element) {
6313
6327
  this.layers.selectionLayer.linkLabels.appendChild(element);
@@ -6319,7 +6333,7 @@ var SelectionManager = class {
6319
6333
  */
6320
6334
  bringLinkToFront(linkElement, renderableLink) {
6321
6335
  this.layers.selectionLayer.links.appendChild(linkElement);
6322
- this.root.selectAll(".link-label").filter((item) => item.link === renderableLink.link).each((d, i, nodes) => {
6336
+ this.root.selectAll(".link-label").filter((item) => item.link === renderableLink.link).each((_, i, nodes) => {
6323
6337
  const element = nodes[i];
6324
6338
  if (element) {
6325
6339
  this.layers.selectionLayer.linkLabels.appendChild(element);
@@ -6330,7 +6344,7 @@ var SelectionManager = class {
6330
6344
  * Restore elements back to their original layers
6331
6345
  */
6332
6346
  restoreSelectedElements(nodeData) {
6333
- this.root.selectAll("circle").filter((d) => d.id === nodeData.id).each((d, i, nodes) => {
6347
+ this.root.selectAll("circle").filter((d) => d.id === nodeData.id).each((_, i, nodes) => {
6334
6348
  const element = nodes[i];
6335
6349
  if (element) {
6336
6350
  this.layers.nodes.appendChild(element);
@@ -6340,13 +6354,13 @@ var SelectionManager = class {
6340
6354
  const source = d.link.source;
6341
6355
  const target = d.link.target;
6342
6356
  return source.id === nodeData.id || target.id === nodeData.id;
6343
- }).each((d, i, nodes) => {
6357
+ }).each((_, i, nodes) => {
6344
6358
  const element = nodes[i];
6345
6359
  if (element) {
6346
6360
  this.layers.links.appendChild(element);
6347
6361
  }
6348
6362
  });
6349
- this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((d, i, nodes) => {
6363
+ this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((_, i, nodes) => {
6350
6364
  const element = nodes[i];
6351
6365
  if (element) {
6352
6366
  this.layers.nodeLabels.appendChild(element);
@@ -6356,22 +6370,13 @@ var SelectionManager = class {
6356
6370
  const source = item.link.source;
6357
6371
  const target = item.link.target;
6358
6372
  return source.id === nodeData.id || target.id === nodeData.id;
6359
- }).each((d, i, nodes) => {
6373
+ }).each((_, i, nodes) => {
6360
6374
  const element = nodes[i];
6361
6375
  if (element) {
6362
6376
  this.layers.linkLabels.appendChild(element);
6363
6377
  }
6364
6378
  });
6365
6379
  }
6366
- /**
6367
- * Utility method to bring any SVG element to front using appendChild
6368
- * Based on the reference implementation pattern
6369
- */
6370
- bringElementToFront(element) {
6371
- if (element.parentNode) {
6372
- element.parentNode.appendChild(element);
6373
- }
6374
- }
6375
6380
  /**
6376
6381
  * Clear hover state to prevent conflicts with selection
6377
6382
  * Similar to the clearAllHoverLayers function in create-node-hover.ts
@@ -6419,6 +6424,25 @@ var SelectionManager = class {
6419
6424
  }
6420
6425
  }
6421
6426
  }
6427
+ /**
6428
+ * Immediately update positions of links connected to the specified node
6429
+ * This ensures arrowheads reposition correctly when node radius changes
6430
+ */
6431
+ updateConnectedLinkPositions(nodeData) {
6432
+ this.root.selectAll("line:not(.link-hit-area)").filter((renderableLink) => {
6433
+ const source = renderableLink.link.source;
6434
+ const target = renderableLink.link.target;
6435
+ return source.id === nodeData.id || target.id === nodeData.id;
6436
+ }).each(function(item) {
6437
+ const linkElement = this;
6438
+ const sourcePoint = getShortenedSourcePoint(item.link, item.style);
6439
+ const targetPoint = getShortenedTargetPoint(item.link, item.style);
6440
+ linkElement.setAttribute("x1", String(sourcePoint.x));
6441
+ linkElement.setAttribute("y1", String(sourcePoint.y));
6442
+ linkElement.setAttribute("x2", String(targetPoint.x));
6443
+ linkElement.setAttribute("y2", String(targetPoint.y));
6444
+ });
6445
+ }
6422
6446
  /**
6423
6447
  * Clean up resources
6424
6448
  */
@@ -6585,6 +6609,13 @@ var InteractionManager = class {
6585
6609
  const nodeElement = event.currentTarget;
6586
6610
  this.manager.selectionManager?.selectNode(nodeElement, node);
6587
6611
  });
6612
+ selections.linkSelection.on("click.select", (event, renderableLinkData) => {
6613
+ event.stopPropagation();
6614
+ const linkElement = event.currentTarget;
6615
+ if (this.manager.selectionManager) {
6616
+ this.manager.selectionManager.selectLink(linkElement, renderableLinkData, event);
6617
+ }
6618
+ });
6588
6619
  selections.linkLabelSelection.on("click.select", (event, renderableLinkLabel) => {
6589
6620
  event.stopPropagation();
6590
6621
  const correspondingLink = selections.linkSelection.filter((d) => d.link === renderableLinkLabel.link).node();
@@ -6630,12 +6661,17 @@ var InteractionManager = class {
6630
6661
  const target = item.link.target;
6631
6662
  if (!source.x || !source.y || !target.x || !target.y) return;
6632
6663
  const rectElement = this;
6664
+ const dx = target.x - source.x;
6665
+ const dy = target.y - source.y;
6666
+ const angle = Math.atan2(dy, dx);
6633
6667
  const midX = (source.x + target.x) / 2;
6634
6668
  const midY = (source.y + target.y) / 2;
6635
6669
  const width = parseFloat(rectElement.getAttribute("width") || "20");
6636
6670
  const height = parseFloat(rectElement.getAttribute("height") || "20");
6637
6671
  rectElement.setAttribute("x", String(midX - width / 2));
6638
6672
  rectElement.setAttribute("y", String(midY - height / 2));
6673
+ const degrees2 = angle * 180 / Math.PI;
6674
+ rectElement.setAttribute("transform", `rotate(${degrees2}, ${midX}, ${midY})`);
6639
6675
  });
6640
6676
  });
6641
6677
  }
package/dist/index.css CHANGED
@@ -320,5 +320,19 @@
320
320
  cursor: pointer;
321
321
  transition: stroke-opacity 0.2s ease;
322
322
  }
323
+ .pg-hover-elevated {
324
+ paint-order: fill stroke markers;
325
+ transition: filter 0.15s ease-in-out;
326
+ }
327
+ .pg-hover-elevated.pg-layer-nodes circle,
328
+ .pg-hover-elevated[data-hover-elevated] {
329
+ paint-order: stroke fill markers;
330
+ }
331
+ svg .pg-hover-elevated {
332
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15));
333
+ }
334
+ [data-hover-elevated=true] {
335
+ opacity: 1 !important;
336
+ }
323
337
 
324
338
  /* src/styles/main.css */
package/dist/index.d.cts CHANGED
@@ -616,16 +616,16 @@ declare class SelectionManager {
616
616
  * Restore elements back to their original layers
617
617
  */
618
618
  private restoreSelectedElements;
619
- /**
620
- * Utility method to bring any SVG element to front using appendChild
621
- * Based on the reference implementation pattern
622
- */
623
- private bringElementToFront;
624
619
  /**
625
620
  * Clear hover state to prevent conflicts with selection
626
621
  * Similar to the clearAllHoverLayers function in create-node-hover.ts
627
622
  */
628
623
  private clearHoverState;
624
+ /**
625
+ * Immediately update positions of links connected to the specified node
626
+ * This ensures arrowheads reposition correctly when node radius changes
627
+ */
628
+ private updateConnectedLinkPositions;
629
629
  /**
630
630
  * Clean up resources
631
631
  */
package/dist/index.d.ts CHANGED
@@ -616,16 +616,16 @@ declare class SelectionManager {
616
616
  * Restore elements back to their original layers
617
617
  */
618
618
  private restoreSelectedElements;
619
- /**
620
- * Utility method to bring any SVG element to front using appendChild
621
- * Based on the reference implementation pattern
622
- */
623
- private bringElementToFront;
624
619
  /**
625
620
  * Clear hover state to prevent conflicts with selection
626
621
  * Similar to the clearAllHoverLayers function in create-node-hover.ts
627
622
  */
628
623
  private clearHoverState;
624
+ /**
625
+ * Immediately update positions of links connected to the specified node
626
+ * This ensures arrowheads reposition correctly when node radius changes
627
+ */
628
+ private updateConnectedLinkPositions;
629
629
  /**
630
630
  * Clean up resources
631
631
  */
package/dist/index.js CHANGED
@@ -4729,7 +4729,18 @@ function getShortenedSourcePoint(link, style) {
4729
4729
  const dx = targetX - sourceX;
4730
4730
  const dy = targetY - sourceY;
4731
4731
  const distance = Math.sqrt(dx * dx + dy * dy) || 1;
4732
- const sourceRadius = source.style?.radius ?? 12;
4732
+ let sourceRadius = source.style?.radius ?? 12;
4733
+ if (typeof document !== "undefined") {
4734
+ const circles = Array.from(document.querySelectorAll("circle"));
4735
+ for (const circle of circles) {
4736
+ const boundData = circle.__data__;
4737
+ if (boundData && boundData.id === source.id) {
4738
+ const currentRadius = parseFloat(circle.getAttribute("r") || "12");
4739
+ sourceRadius = currentRadius;
4740
+ break;
4741
+ }
4742
+ }
4743
+ }
4733
4744
  const sourceStrokeWidth = source.style?.strokeWidth ?? 1.5;
4734
4745
  const linkStrokeCompensation = style.strokeWidth / 2;
4735
4746
  const nodeStrokeOffset = sourceStrokeWidth / 2;
@@ -4750,7 +4761,18 @@ function getShortenedTargetPoint(link, style) {
4750
4761
  const dx = targetX - sourceX;
4751
4762
  const dy = targetY - sourceY;
4752
4763
  const distance = Math.sqrt(dx * dx + dy * dy) || 1;
4753
- const targetRadius = target.style?.radius ?? 12;
4764
+ let targetRadius = target.style?.radius ?? 12;
4765
+ if (typeof document !== "undefined") {
4766
+ const circles = Array.from(document.querySelectorAll("circle"));
4767
+ for (const circle of circles) {
4768
+ const boundData = circle.__data__;
4769
+ if (boundData && boundData.id === target.id) {
4770
+ const currentRadius = parseFloat(circle.getAttribute("r") || "12");
4771
+ targetRadius = currentRadius;
4772
+ break;
4773
+ }
4774
+ }
4775
+ }
4754
4776
  const targetStrokeWidth = target.style?.strokeWidth ?? 1.5;
4755
4777
  const arrowLength = style.arrow.enabled ? style.arrow.size * 2 : 0;
4756
4778
  const linkStrokeCompensation = style.strokeWidth / 2;
@@ -5375,48 +5397,38 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5375
5397
  const svgElement = firstNode.ownerSVGElement;
5376
5398
  if (!svgElement) return;
5377
5399
  const root2 = select_default2(svgElement);
5400
+ const elevatedElements = /* @__PURE__ */ new Set();
5378
5401
  function clearAllHoverLayers() {
5379
- const hoverNodesLayer = root2.select('[data-layer="hover-nodes"]').node();
5380
- const nodesLayer = root2.select('[data-layer="nodes"]').node();
5381
- if (hoverNodesLayer && nodesLayer) {
5382
- while (hoverNodesLayer.firstChild) {
5383
- nodesLayer.appendChild(hoverNodesLayer.firstChild);
5384
- }
5385
- }
5386
- const hoverNodeLabelsLayer = root2.select('[data-layer="hover-node-labels"]').node();
5387
- const nodeLabelsLayer = root2.select('[data-layer="node-labels"]').node();
5388
- if (hoverNodeLabelsLayer && nodeLabelsLayer) {
5389
- while (hoverNodeLabelsLayer.firstChild) {
5390
- nodeLabelsLayer.appendChild(hoverNodeLabelsLayer.firstChild);
5391
- }
5392
- }
5393
- const hoverLinksLayer = root2.select('[data-layer="hover-links"]').node();
5394
- const linksLayer = root2.select('[data-layer="links"]').node();
5395
- if (hoverLinksLayer && linksLayer) {
5396
- while (hoverLinksLayer.firstChild) {
5397
- const linkElement = hoverLinksLayer.firstChild;
5398
- linksLayer.appendChild(linkElement);
5399
- const event = new MouseEvent("mouseleave", {
5400
- bubbles: true,
5401
- cancelable: false,
5402
- view: window
5403
- });
5404
- linkElement.dispatchEvent(event);
5405
- }
5406
- }
5407
- const hoverLinkLabelsLayer = root2.select('[data-layer="hover-link-labels"]').node();
5408
- const linkLabelsLayer = root2.select('[data-layer="link-labels"]').node();
5409
- if (hoverLinkLabelsLayer && linkLabelsLayer) {
5410
- while (hoverLinkLabelsLayer.firstChild) {
5411
- const labelElement = hoverLinkLabelsLayer.firstChild;
5412
- const labelData = labelElement.__data__;
5413
- if (labelData && labelData.style.label.visibility === "hover" && !labelElement.classList.contains("label-selection-pinned")) {
5414
- labelElement.style.opacity = "0";
5415
- labelElement.style.pointerEvents = "none";
5402
+ elevatedElements.forEach((element) => {
5403
+ element.classList.remove("pg-hover-elevated");
5404
+ element.style.removeProperty("filter");
5405
+ if (element.tagName === "circle") {
5406
+ element.removeAttribute("data-hover-elevated");
5407
+ }
5408
+ if (element.classList.contains("link-label")) {
5409
+ const labelData = element.__data__;
5410
+ if (labelData && labelData.style.label.visibility === "hover" && !element.classList.contains("label-selection-pinned")) {
5411
+ element.style.opacity = "0";
5412
+ element.style.pointerEvents = "none";
5416
5413
  }
5417
- linkLabelsLayer.appendChild(labelElement);
5418
5414
  }
5419
- }
5415
+ });
5416
+ elevatedElements.clear();
5417
+ root2.selectAll("line[data-hover-elevated]").each(function() {
5418
+ const linkElement = this;
5419
+ linkElement.removeAttribute("data-hover-elevated");
5420
+ const event = new MouseEvent("mouseleave", {
5421
+ bubbles: true,
5422
+ cancelable: false,
5423
+ view: window
5424
+ });
5425
+ linkElement.dispatchEvent(event);
5426
+ });
5427
+ }
5428
+ function elevateElement(element) {
5429
+ element.classList.add("pg-hover-elevated");
5430
+ element.style.filter = "drop-shadow(0 2px 4px rgba(0,0,0,0.15))";
5431
+ elevatedElements.add(element);
5420
5432
  }
5421
5433
  nodeSelection.on("mouseenter.links", function(_event, hoveredNode) {
5422
5434
  const hoveredNodeElement = this;
@@ -5427,16 +5439,11 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5427
5439
  return;
5428
5440
  }
5429
5441
  clearAllHoverLayers();
5430
- const hoverNodesLayer = root2.select('[data-layer="hover-nodes"]').node();
5431
- if (hoverNodesLayer) {
5432
- hoverNodesLayer.appendChild(hoveredNodeElement);
5433
- }
5442
+ elevateElement(hoveredNodeElement);
5443
+ hoveredNodeElement.setAttribute("data-hover-elevated", "true");
5434
5444
  root2.selectAll("text").filter((d) => d.id === hoveredNode.id).each(function() {
5435
5445
  const labelElement = this;
5436
- const hoverNodeLabelsLayer = root2.select('[data-layer="hover-node-labels"]').node();
5437
- if (hoverNodeLabelsLayer) {
5438
- hoverNodeLabelsLayer.appendChild(labelElement);
5439
- }
5446
+ elevateElement(labelElement);
5440
5447
  });
5441
5448
  const connectedLinks = root2.selectAll("line:not(.link-hit-area)").filter((renderableLink) => {
5442
5449
  const source = renderableLink.link.source;
@@ -5445,10 +5452,8 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5445
5452
  });
5446
5453
  connectedLinks.each(function(_renderableLink) {
5447
5454
  const linkElement = this;
5448
- const hoverLinksLayer = root2.select('[data-layer="hover-links"]').node();
5449
- if (hoverLinksLayer) {
5450
- hoverLinksLayer.appendChild(linkElement);
5451
- }
5455
+ elevateElement(linkElement);
5456
+ linkElement.setAttribute("data-hover-elevated", "true");
5452
5457
  const event = new MouseEvent("mouseenter", {
5453
5458
  bubbles: true,
5454
5459
  cancelable: false,
@@ -5462,13 +5467,10 @@ function createNodeHover(nodeSelection, hoverStyle, options) {
5462
5467
  return source.id === hoveredNode.id || target.id === hoveredNode.id;
5463
5468
  }).each(function(item) {
5464
5469
  const labelElement = this;
5465
- const hoverLinkLabelsLayer = root2.select('[data-layer="hover-link-labels"]').node();
5466
- if (hoverLinkLabelsLayer) {
5467
- hoverLinkLabelsLayer.appendChild(labelElement);
5468
- if (item.style.label.visibility === "hover") {
5469
- labelElement.style.opacity = "1";
5470
- labelElement.style.pointerEvents = "auto";
5471
- }
5470
+ elevateElement(labelElement);
5471
+ if (item.style.label.visibility === "hover") {
5472
+ labelElement.style.opacity = "1";
5473
+ labelElement.style.pointerEvents = "auto";
5472
5474
  }
5473
5475
  });
5474
5476
  }).on("mouseleave.links", function(_event, _hoveredNode) {
@@ -5850,11 +5852,15 @@ function updateHitAreaDimensions(rectElement, item, root2) {
5850
5852
  const source = item.link.source;
5851
5853
  const target = item.link.target;
5852
5854
  if (!source.x || !source.y || !target.x || !target.y) return;
5855
+ const dx = target.x - source.x;
5856
+ const dy = target.y - source.y;
5857
+ const linkLength = Math.sqrt(dx * dx + dy * dy);
5858
+ const angle = Math.atan2(dy, dx);
5853
5859
  const midX = (source.x + target.x) / 2;
5854
5860
  const midY = (source.y + target.y) / 2;
5855
- const linkPadding = Math.max(item.style.strokeWidth || 2, item.style.arrow.size) * 2;
5856
- let width = linkPadding;
5857
- let height = linkPadding;
5861
+ const thickness = Math.max((item.style.strokeWidth || 2) * 4, item.style.arrow.size * 2, 20);
5862
+ let length = linkLength + item.style.arrow.size * 2;
5863
+ let width = thickness;
5858
5864
  if (item.link.label) {
5859
5865
  const labelElement = root2.select('[data-layer="link-labels"]').selectAll(".link-label").filter((labelItem) => labelItem.link === item.link).node();
5860
5866
  if (labelElement) {
@@ -5865,23 +5871,27 @@ function updateHitAreaDimensions(rectElement, item, root2) {
5865
5871
  const bbox = textElement.getBBox();
5866
5872
  const labelWidth = bbox.width + item.style.label.paddingX * 2;
5867
5873
  const labelHeight = bbox.height + item.style.label.paddingY * 2;
5868
- width = Math.max(width, labelWidth + 10);
5869
- height = Math.max(height, labelHeight + 10);
5874
+ width = Math.max(width, labelHeight + 10);
5875
+ length = Math.max(length, labelWidth + 20);
5870
5876
  }
5871
5877
  } catch {
5872
5878
  const text = item.link.label ?? "";
5873
5879
  const fontSize = item.style.label.fontSize;
5874
- const estimatedWidth = text.length * fontSize * 0.6 + item.style.label.paddingX * 2 + 10;
5880
+ const estimatedWidth = text.length * fontSize * 0.6 + item.style.label.paddingX * 2 + 20;
5875
5881
  const estimatedHeight = fontSize + item.style.label.paddingY * 2 + 10;
5876
- width = Math.max(width, estimatedWidth);
5877
- height = Math.max(height, estimatedHeight);
5882
+ width = Math.max(width, estimatedHeight);
5883
+ length = Math.max(length, estimatedWidth);
5878
5884
  }
5879
5885
  }
5880
5886
  }
5881
- rectElement.setAttribute("x", String(midX - width / 2));
5882
- rectElement.setAttribute("y", String(midY - height / 2));
5883
- rectElement.setAttribute("width", String(width));
5884
- rectElement.setAttribute("height", String(height));
5887
+ const rectX = midX - length / 2;
5888
+ const rectY = midY - width / 2;
5889
+ rectElement.setAttribute("width", String(length));
5890
+ rectElement.setAttribute("height", String(width));
5891
+ rectElement.setAttribute("x", String(rectX));
5892
+ rectElement.setAttribute("y", String(rectY));
5893
+ const degrees2 = angle * 180 / Math.PI;
5894
+ rectElement.setAttribute("transform", `rotate(${degrees2}, ${midX}, ${midY})`);
5885
5895
  }
5886
5896
 
5887
5897
  // src/utils/get-link-target-point.ts
@@ -6120,7 +6130,10 @@ var SelectionManager = class {
6120
6130
  if (style.stroke !== void 0) nodeElement.style.stroke = style.stroke;
6121
6131
  if (style.strokeWidth !== void 0) nodeElement.style.strokeWidth = String(style.strokeWidth);
6122
6132
  if (style.opacity !== void 0) nodeElement.style.opacity = String(style.opacity);
6123
- if (style.radius !== void 0) nodeElement.setAttribute("r", String(style.radius));
6133
+ if (style.radius !== void 0) {
6134
+ nodeElement.setAttribute("r", String(style.radius));
6135
+ this.updateConnectedLinkPositions(nodeData);
6136
+ }
6124
6137
  }
6125
6138
  this.root.selectAll(".link-label").filter((item) => {
6126
6139
  if (item.style.label.visibility !== "hover") return false;
@@ -6177,6 +6190,7 @@ var SelectionManager = class {
6177
6190
  element.style.opacity = "";
6178
6191
  const originalRadius = data.style?.radius ?? 8;
6179
6192
  element.setAttribute("r", String(originalRadius));
6193
+ this.updateConnectedLinkPositions(data);
6180
6194
  delete element.dataset.selected;
6181
6195
  this.root.selectAll(".link-label.label-selection-pinned").classed("label-selection-pinned", false).interrupt().transition().duration(200).style("opacity", 0).style("pointer-events", "none");
6182
6196
  this.state.selectedNode = null;
@@ -6189,7 +6203,7 @@ var SelectionManager = class {
6189
6203
  if (!this.state.selectedLink) return;
6190
6204
  const { element, data, originalMarker } = this.state.selectedLink;
6191
6205
  this.layers.links.appendChild(element);
6192
- this.root.selectAll(".link-label").filter((item) => item.link === data).each((d, i, nodes) => {
6206
+ this.root.selectAll(".link-label").filter((item) => item.link === data).each((_, i, nodes) => {
6193
6207
  const element2 = nodes[i];
6194
6208
  if (element2) {
6195
6209
  this.layers.linkLabels.appendChild(element2);
@@ -6259,13 +6273,13 @@ var SelectionManager = class {
6259
6273
  const target = d.link.target;
6260
6274
  return source.id === nodeData.id || target.id === nodeData.id;
6261
6275
  });
6262
- connectedLinks.each((d, i, nodes) => {
6276
+ connectedLinks.each((_, i, nodes) => {
6263
6277
  const element = nodes[i];
6264
6278
  if (element) {
6265
6279
  this.layers.selectionLayer.links.appendChild(element);
6266
6280
  }
6267
6281
  });
6268
- this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((d, i, nodes) => {
6282
+ this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((_, i, nodes) => {
6269
6283
  const element = nodes[i];
6270
6284
  if (element) {
6271
6285
  this.layers.selectionLayer.nodeLabels.appendChild(element);
@@ -6275,7 +6289,7 @@ var SelectionManager = class {
6275
6289
  const source = item.link.source;
6276
6290
  const target = item.link.target;
6277
6291
  return source.id === nodeData.id || target.id === nodeData.id;
6278
- }).each((d, i, nodes) => {
6292
+ }).each((_, i, nodes) => {
6279
6293
  const element = nodes[i];
6280
6294
  if (element) {
6281
6295
  this.layers.selectionLayer.linkLabels.appendChild(element);
@@ -6287,7 +6301,7 @@ var SelectionManager = class {
6287
6301
  */
6288
6302
  bringLinkToFront(linkElement, renderableLink) {
6289
6303
  this.layers.selectionLayer.links.appendChild(linkElement);
6290
- this.root.selectAll(".link-label").filter((item) => item.link === renderableLink.link).each((d, i, nodes) => {
6304
+ this.root.selectAll(".link-label").filter((item) => item.link === renderableLink.link).each((_, i, nodes) => {
6291
6305
  const element = nodes[i];
6292
6306
  if (element) {
6293
6307
  this.layers.selectionLayer.linkLabels.appendChild(element);
@@ -6298,7 +6312,7 @@ var SelectionManager = class {
6298
6312
  * Restore elements back to their original layers
6299
6313
  */
6300
6314
  restoreSelectedElements(nodeData) {
6301
- this.root.selectAll("circle").filter((d) => d.id === nodeData.id).each((d, i, nodes) => {
6315
+ this.root.selectAll("circle").filter((d) => d.id === nodeData.id).each((_, i, nodes) => {
6302
6316
  const element = nodes[i];
6303
6317
  if (element) {
6304
6318
  this.layers.nodes.appendChild(element);
@@ -6308,13 +6322,13 @@ var SelectionManager = class {
6308
6322
  const source = d.link.source;
6309
6323
  const target = d.link.target;
6310
6324
  return source.id === nodeData.id || target.id === nodeData.id;
6311
- }).each((d, i, nodes) => {
6325
+ }).each((_, i, nodes) => {
6312
6326
  const element = nodes[i];
6313
6327
  if (element) {
6314
6328
  this.layers.links.appendChild(element);
6315
6329
  }
6316
6330
  });
6317
- this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((d, i, nodes) => {
6331
+ this.root.selectAll("text").filter((d) => d.id === nodeData.id).each((_, i, nodes) => {
6318
6332
  const element = nodes[i];
6319
6333
  if (element) {
6320
6334
  this.layers.nodeLabels.appendChild(element);
@@ -6324,22 +6338,13 @@ var SelectionManager = class {
6324
6338
  const source = item.link.source;
6325
6339
  const target = item.link.target;
6326
6340
  return source.id === nodeData.id || target.id === nodeData.id;
6327
- }).each((d, i, nodes) => {
6341
+ }).each((_, i, nodes) => {
6328
6342
  const element = nodes[i];
6329
6343
  if (element) {
6330
6344
  this.layers.linkLabels.appendChild(element);
6331
6345
  }
6332
6346
  });
6333
6347
  }
6334
- /**
6335
- * Utility method to bring any SVG element to front using appendChild
6336
- * Based on the reference implementation pattern
6337
- */
6338
- bringElementToFront(element) {
6339
- if (element.parentNode) {
6340
- element.parentNode.appendChild(element);
6341
- }
6342
- }
6343
6348
  /**
6344
6349
  * Clear hover state to prevent conflicts with selection
6345
6350
  * Similar to the clearAllHoverLayers function in create-node-hover.ts
@@ -6387,6 +6392,25 @@ var SelectionManager = class {
6387
6392
  }
6388
6393
  }
6389
6394
  }
6395
+ /**
6396
+ * Immediately update positions of links connected to the specified node
6397
+ * This ensures arrowheads reposition correctly when node radius changes
6398
+ */
6399
+ updateConnectedLinkPositions(nodeData) {
6400
+ this.root.selectAll("line:not(.link-hit-area)").filter((renderableLink) => {
6401
+ const source = renderableLink.link.source;
6402
+ const target = renderableLink.link.target;
6403
+ return source.id === nodeData.id || target.id === nodeData.id;
6404
+ }).each(function(item) {
6405
+ const linkElement = this;
6406
+ const sourcePoint = getShortenedSourcePoint(item.link, item.style);
6407
+ const targetPoint = getShortenedTargetPoint(item.link, item.style);
6408
+ linkElement.setAttribute("x1", String(sourcePoint.x));
6409
+ linkElement.setAttribute("y1", String(sourcePoint.y));
6410
+ linkElement.setAttribute("x2", String(targetPoint.x));
6411
+ linkElement.setAttribute("y2", String(targetPoint.y));
6412
+ });
6413
+ }
6390
6414
  /**
6391
6415
  * Clean up resources
6392
6416
  */
@@ -6553,6 +6577,13 @@ var InteractionManager = class {
6553
6577
  const nodeElement = event.currentTarget;
6554
6578
  this.manager.selectionManager?.selectNode(nodeElement, node);
6555
6579
  });
6580
+ selections.linkSelection.on("click.select", (event, renderableLinkData) => {
6581
+ event.stopPropagation();
6582
+ const linkElement = event.currentTarget;
6583
+ if (this.manager.selectionManager) {
6584
+ this.manager.selectionManager.selectLink(linkElement, renderableLinkData, event);
6585
+ }
6586
+ });
6556
6587
  selections.linkLabelSelection.on("click.select", (event, renderableLinkLabel) => {
6557
6588
  event.stopPropagation();
6558
6589
  const correspondingLink = selections.linkSelection.filter((d) => d.link === renderableLinkLabel.link).node();
@@ -6598,12 +6629,17 @@ var InteractionManager = class {
6598
6629
  const target = item.link.target;
6599
6630
  if (!source.x || !source.y || !target.x || !target.y) return;
6600
6631
  const rectElement = this;
6632
+ const dx = target.x - source.x;
6633
+ const dy = target.y - source.y;
6634
+ const angle = Math.atan2(dy, dx);
6601
6635
  const midX = (source.x + target.x) / 2;
6602
6636
  const midY = (source.y + target.y) / 2;
6603
6637
  const width = parseFloat(rectElement.getAttribute("width") || "20");
6604
6638
  const height = parseFloat(rectElement.getAttribute("height") || "20");
6605
6639
  rectElement.setAttribute("x", String(midX - width / 2));
6606
6640
  rectElement.setAttribute("y", String(midY - height / 2));
6641
+ const degrees2 = angle * 180 / Math.PI;
6642
+ rectElement.setAttribute("transform", `rotate(${degrees2}, ${midX}, ${midY})`);
6607
6643
  });
6608
6644
  });
6609
6645
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polly-graph",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Reusable D3-based graph visualization SDK with configurable nodes, links, labels, interactions, and layout behaviors.",
5
5
  "license": "MIT",
6
6
  "author": "Badal",