@opendata-ai/openchart-vanilla 2.9.1 → 2.10.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/index.js CHANGED
@@ -640,10 +640,13 @@ var ZoomTransform = class _ZoomTransform {
640
640
  /**
641
641
  * Compute a transform that fits all nodes within the given canvas
642
642
  * dimensions with the specified padding.
643
+ *
644
+ * Returns the transform and the ideal content height (in screen pixels)
645
+ * so callers can shrink the canvas to eliminate dead space.
643
646
  */
644
647
  static fitBounds(nodes, canvasW, canvasH, padding = 40) {
645
648
  if (nodes.length === 0) {
646
- return _ZoomTransform.identity();
649
+ return { transform: _ZoomTransform.identity(), contentHeight: canvasH };
647
650
  }
648
651
  let minX = Infinity;
649
652
  let minY = Infinity;
@@ -659,7 +662,10 @@ var ZoomTransform = class _ZoomTransform {
659
662
  const graphW = maxX - minX;
660
663
  const graphH = maxY - minY;
661
664
  if (graphW === 0 && graphH === 0) {
662
- return new _ZoomTransform(canvasW / 2 - minX, canvasH / 2 - minY, 1);
665
+ return {
666
+ transform: new _ZoomTransform(canvasW / 2 - minX, canvasH / 2 - minY, 1),
667
+ contentHeight: padding * 2
668
+ };
663
669
  }
664
670
  const availW = canvasW - padding * 2;
665
671
  const availH = canvasH - padding * 2;
@@ -667,7 +673,11 @@ var ZoomTransform = class _ZoomTransform {
667
673
  const cx = (minX + maxX) / 2;
668
674
  const tx = canvasW / 2 - cx * k;
669
675
  const ty = padding - minY * k;
670
- return new _ZoomTransform(tx, ty, k);
676
+ const contentHeight = graphH * k + padding * 2;
677
+ return {
678
+ transform: new _ZoomTransform(tx, ty, k),
679
+ contentHeight
680
+ };
671
681
  }
672
682
  /** Identity transform (no pan, no zoom). */
673
683
  static identity() {
@@ -2474,6 +2484,7 @@ function createGraph(container, spec, options) {
2474
2484
  let needsRender = false;
2475
2485
  let isGesturing = false;
2476
2486
  let gestureTimeout = null;
2487
+ let selfResizing = false;
2477
2488
  function markGesture() {
2478
2489
  isGesturing = true;
2479
2490
  if (gestureTimeout !== null) clearTimeout(gestureTimeout);
@@ -2677,10 +2688,28 @@ function createGraph(container, spec, options) {
2677
2688
  scheduleRender();
2678
2689
  });
2679
2690
  simulation.onSettled(() => {
2680
- if (canvas && positionedNodes.length > 0 && interactionManager) {
2691
+ if (canvas && positionedNodes.length > 0 && interactionManager && renderer) {
2681
2692
  const { width: cw, height: ch } = getCanvasDimensions();
2682
- const fitTransform = ZoomTransform.fitBounds(positionedNodes, cw, ch);
2693
+ const { transform: fitTransform, contentHeight } = ZoomTransform.fitBounds(
2694
+ positionedNodes,
2695
+ cw,
2696
+ ch
2697
+ );
2683
2698
  interactionManager.setTransform(fitTransform);
2699
+ const chromeH = chromeEl?.getBoundingClientRect().height || 0;
2700
+ const totalContentHeight = Math.ceil(contentHeight) + chromeH;
2701
+ const containerH = container.getBoundingClientRect().height;
2702
+ if (totalContentHeight < containerH) {
2703
+ selfResizing = true;
2704
+ const targetCanvasH = Math.ceil(contentHeight);
2705
+ renderer.resize(cw, targetCanvasH);
2706
+ const refit = ZoomTransform.fitBounds(positionedNodes, cw, targetCanvasH);
2707
+ interactionManager.setTransform(refit.transform);
2708
+ container.style.height = "fit-content";
2709
+ setTimeout(() => {
2710
+ selfResizing = false;
2711
+ }, 100);
2712
+ }
2684
2713
  needsRender = true;
2685
2714
  scheduleRender();
2686
2715
  }
@@ -2858,7 +2887,7 @@ function createGraph(container, spec, options) {
2858
2887
  function zoomToFit() {
2859
2888
  if (destroyed || !interactionManager || positionedNodes.length === 0) return;
2860
2889
  const { width: cw, height: ch } = getCanvasDimensions();
2861
- const fitTransform = ZoomTransform.fitBounds(positionedNodes, cw, ch);
2890
+ const { transform: fitTransform } = ZoomTransform.fitBounds(positionedNodes, cw, ch);
2862
2891
  interactionManager.setTransform(fitTransform);
2863
2892
  needsRender = true;
2864
2893
  scheduleRender();
@@ -2887,7 +2916,8 @@ function createGraph(container, spec, options) {
2887
2916
  return [...selectedNodeIds];
2888
2917
  }
2889
2918
  function doResize() {
2890
- if (destroyed || !canvas || !renderer || !wrapper) return;
2919
+ if (destroyed || !canvas || !renderer || !wrapper || selfResizing) return;
2920
+ container.style.height = "";
2891
2921
  const { width, height } = getContainerDimensions();
2892
2922
  const chromeHeight = chromeEl?.getBoundingClientRect().height || 0;
2893
2923
  const canvasHeight = Math.max(height - chromeHeight, 200);
@@ -3680,13 +3710,19 @@ function renderLegend(parent, legend) {
3680
3710
  entryG.setAttribute("role", "listitem");
3681
3711
  entryG.setAttribute("data-legend-index", String(i));
3682
3712
  entryG.setAttribute("data-legend-label", entry.label);
3683
- entryG.setAttribute(
3684
- "aria-label",
3685
- `${entry.label}: ${entry.active !== false ? "visible" : "hidden"}`
3686
- );
3687
- entryG.setAttribute("style", "cursor: pointer");
3688
- if (entry.active === false) {
3689
- entryG.setAttribute("opacity", "0.3");
3713
+ if (entry.overflow) {
3714
+ entryG.setAttribute("data-legend-overflow", "true");
3715
+ entryG.setAttribute("aria-label", entry.label);
3716
+ entryG.setAttribute("opacity", "0.5");
3717
+ } else {
3718
+ entryG.setAttribute(
3719
+ "aria-label",
3720
+ `${entry.label}: ${entry.active !== false ? "visible" : "hidden"}`
3721
+ );
3722
+ entryG.setAttribute("style", "cursor: pointer");
3723
+ if (entry.active === false) {
3724
+ entryG.setAttribute("opacity", "0.3");
3725
+ }
3690
3726
  }
3691
3727
  if (entry.shape === "circle") {
3692
3728
  const circle = createSVGElement("circle");
@@ -4538,6 +4574,7 @@ function wireLegendInteraction(svg, _layout, onLegendToggle, onEdit) {
4538
4574
  const cleanups = [];
4539
4575
  const hiddenSeries = /* @__PURE__ */ new Set();
4540
4576
  for (const entry of legendEntries) {
4577
+ if (entry.getAttribute("data-legend-overflow") === "true") continue;
4541
4578
  const handleClick = () => {
4542
4579
  const label = entry.getAttribute("data-legend-label");
4543
4580
  if (!label) return;