@opendata-ai/openchart-vanilla 6.23.1 → 6.24.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
@@ -3313,6 +3313,28 @@ function setupTableAnimationCleanup(wrapper) {
3313
3313
  };
3314
3314
  }
3315
3315
 
3316
+ // src/measure-text.ts
3317
+ function createMeasureText() {
3318
+ let canvas = null;
3319
+ let ctx = null;
3320
+ return (text, fontSize, fontWeight) => {
3321
+ if (!canvas) {
3322
+ canvas = document.createElement("canvas");
3323
+ ctx = canvas.getContext("2d");
3324
+ }
3325
+ if (!ctx) {
3326
+ return { width: text.length * fontSize * 0.6, height: fontSize * 1.2 };
3327
+ }
3328
+ const weight = fontWeight ?? 400;
3329
+ ctx.font = `${weight} ${fontSize}px Inter, sans-serif`;
3330
+ const metrics = ctx.measureText(text);
3331
+ return {
3332
+ width: metrics.width,
3333
+ height: fontSize * 1.2
3334
+ };
3335
+ };
3336
+ }
3337
+
3316
3338
  // src/svg-renderer.ts
3317
3339
  import { clampStaggerDelay } from "@opendata-ai/openchart-engine";
3318
3340
 
@@ -3418,12 +3440,11 @@ function setAttrs(el, attrs) {
3418
3440
  }
3419
3441
  }
3420
3442
  function applyTextStyle(el, style) {
3421
- setAttrs(el, {
3422
- "font-family": style.fontFamily,
3423
- "font-size": style.fontSize,
3424
- "font-weight": style.fontWeight
3425
- });
3426
- el.style.setProperty("fill", style.fill);
3443
+ const inline = el.style;
3444
+ inline.setProperty("fill", style.fill);
3445
+ inline.setProperty("font-size", `${style.fontSize}px`);
3446
+ inline.setProperty("font-weight", String(style.fontWeight));
3447
+ inline.setProperty("font-family", style.fontFamily);
3427
3448
  if (style.textAnchor) {
3428
3449
  el.setAttribute("text-anchor", style.textAnchor);
3429
3450
  }
@@ -3663,7 +3684,31 @@ function renderAxis(parent, axis, orientation, layout) {
3663
3684
  "dominant-baseline": "central"
3664
3685
  });
3665
3686
  applyTextStyle(label, axis.tickLabelStyle);
3666
- label.textContent = tick.label;
3687
+ const availableWidth = area.x - 6;
3688
+ const fontSize = axis.tickLabelStyle.fontSize;
3689
+ const fontWeight = axis.tickLabelStyle.fontWeight;
3690
+ const fullWidth = estimateTextWidth2(tick.label, fontSize, fontWeight);
3691
+ if (fullWidth > availableWidth && availableWidth > 20) {
3692
+ const ellipsis = "\u2026";
3693
+ const ellipsisWidth = estimateTextWidth2(ellipsis, fontSize, fontWeight);
3694
+ let lo = 0;
3695
+ let hi = tick.label.length;
3696
+ while (lo < hi) {
3697
+ const mid = lo + hi + 1 >>> 1;
3698
+ const candidate = tick.label.slice(0, mid);
3699
+ if (estimateTextWidth2(candidate, fontSize, fontWeight) + ellipsisWidth <= availableWidth) {
3700
+ lo = mid;
3701
+ } else {
3702
+ hi = mid - 1;
3703
+ }
3704
+ }
3705
+ label.textContent = lo > 0 ? tick.label.slice(0, lo).trimEnd() + ellipsis : ellipsis;
3706
+ const titleEl = createSVGElement("title");
3707
+ titleEl.textContent = tick.label;
3708
+ label.appendChild(titleEl);
3709
+ } else {
3710
+ label.textContent = tick.label;
3711
+ }
3667
3712
  g.appendChild(label);
3668
3713
  }
3669
3714
  }
@@ -4542,26 +4587,6 @@ function resolveDarkMode2(mode) {
4542
4587
  }
4543
4588
  return false;
4544
4589
  }
4545
- function createMeasureText() {
4546
- let canvas = null;
4547
- let ctx = null;
4548
- return (text, fontSize, fontWeight) => {
4549
- if (!canvas) {
4550
- canvas = document.createElement("canvas");
4551
- ctx = canvas.getContext("2d");
4552
- }
4553
- if (!ctx) {
4554
- return { width: text.length * fontSize * 0.6, height: fontSize * 1.2 };
4555
- }
4556
- const weight = fontWeight ?? 400;
4557
- ctx.font = `${weight} ${fontSize}px Inter, sans-serif`;
4558
- const metrics = ctx.measureText(text);
4559
- return {
4560
- width: metrics.width,
4561
- height: fontSize * 1.2
4562
- };
4563
- };
4564
- }
4565
4590
  function wireTooltipEvents(svg, tooltipDescriptors, tooltipManager) {
4566
4591
  const markElements = svg.querySelectorAll("[data-mark-id]");
4567
4592
  const cleanups = [];
@@ -6547,7 +6572,7 @@ function stampAnimationAttrs2(el, mark, fallbackIndex, animation) {
6547
6572
  el.setAttribute("data-animation-index", String(idx));
6548
6573
  el.style.setProperty("--oc-mark-index", String(idx));
6549
6574
  }
6550
- function renderChromeElement2(parent, element, className, chromeKey) {
6575
+ function renderChromeElement2(parent, element, className, chromeKey, measureText) {
6551
6576
  const text = createSVGElement2("text");
6552
6577
  setAttrs2(text, { x: element.x, y: element.y });
6553
6578
  applyTextStyle2(text, element.style);
@@ -6557,7 +6582,8 @@ function renderChromeElement2(parent, element, className, chromeKey) {
6557
6582
  element.text,
6558
6583
  element.style.fontSize,
6559
6584
  element.style.fontWeight,
6560
- element.maxWidth
6585
+ element.maxWidth,
6586
+ measureText
6561
6587
  );
6562
6588
  if (lines.length === 1) {
6563
6589
  text.textContent = element.text;
@@ -6575,12 +6601,12 @@ function renderChromeElement2(parent, element, className, chromeKey) {
6575
6601
  function renderChrome2(parent, layout) {
6576
6602
  const g = createSVGElement2("g");
6577
6603
  g.setAttribute("class", "oc-chrome");
6578
- const { chrome } = layout;
6604
+ const { chrome, measureText } = layout;
6579
6605
  if (chrome.title) {
6580
- renderChromeElement2(g, chrome.title, "oc-title", "title");
6606
+ renderChromeElement2(g, chrome.title, "oc-title", "title", measureText);
6581
6607
  }
6582
6608
  if (chrome.subtitle) {
6583
- renderChromeElement2(g, chrome.subtitle, "oc-subtitle", "subtitle");
6609
+ renderChromeElement2(g, chrome.subtitle, "oc-subtitle", "subtitle", measureText);
6584
6610
  }
6585
6611
  const bottomOffset = layout.area.y + layout.area.height;
6586
6612
  if (chrome.source) {
@@ -6588,7 +6614,8 @@ function renderChrome2(parent, layout) {
6588
6614
  g,
6589
6615
  { ...chrome.source, y: bottomOffset + chrome.source.y },
6590
6616
  "oc-source",
6591
- "source"
6617
+ "source",
6618
+ measureText
6592
6619
  );
6593
6620
  }
6594
6621
  if (chrome.byline) {
@@ -6596,7 +6623,8 @@ function renderChrome2(parent, layout) {
6596
6623
  g,
6597
6624
  { ...chrome.byline, y: bottomOffset + chrome.byline.y },
6598
6625
  "oc-byline",
6599
- "byline"
6626
+ "byline",
6627
+ measureText
6600
6628
  );
6601
6629
  }
6602
6630
  if (chrome.footer) {
@@ -6604,7 +6632,8 @@ function renderChrome2(parent, layout) {
6604
6632
  g,
6605
6633
  { ...chrome.footer, y: bottomOffset + chrome.footer.y },
6606
6634
  "oc-footer",
6607
- "footer"
6635
+ "footer",
6636
+ measureText
6608
6637
  );
6609
6638
  }
6610
6639
  parent.appendChild(g);
@@ -6824,7 +6853,7 @@ function renderNodes(parent, nodes, animation) {
6824
6853
  }
6825
6854
  parent.appendChild(g);
6826
6855
  }
6827
- function renderLabels(parent, nodes) {
6856
+ function renderLabels(parent, nodes, measureText) {
6828
6857
  const g = createSVGElement2("g");
6829
6858
  g.setAttribute("class", "oc-sankey-labels");
6830
6859
  for (const node of nodes) {
@@ -6836,7 +6865,7 @@ function renderLabels(parent, nodes) {
6836
6865
  if (label.maxWidth !== void 0 && label.maxWidth > 0) {
6837
6866
  const fontSize = label.style.fontSize ?? 12;
6838
6867
  const fontWeight = label.style.fontWeight ?? 400;
6839
- const lines = wrapText2(label.text, fontSize, fontWeight, label.maxWidth);
6868
+ const lines = wrapText2(label.text, fontSize, fontWeight, label.maxWidth, measureText);
6840
6869
  if (lines.length > 1) {
6841
6870
  const lineHeight = fontSize * (label.style.lineHeight ?? 1.3);
6842
6871
  const totalHeight = (lines.length - 1) * lineHeight;
@@ -6897,7 +6926,7 @@ function renderSankeySVG(layout, animation) {
6897
6926
  svg.appendChild(defs);
6898
6927
  renderLinks(svg, layout.links, nodePositions, animation);
6899
6928
  renderNodes(svg, layout.nodes, animation);
6900
- renderLabels(svg, layout.nodes);
6929
+ renderLabels(svg, layout.nodes, layout.measureText);
6901
6930
  renderLegend2(svg, layout.legend);
6902
6931
  renderChrome2(svg, layout);
6903
6932
  if (layout.watermark) {
@@ -6929,6 +6958,7 @@ function createSankey(container, spec, options) {
6929
6958
  let isFirstRender = true;
6930
6959
  let animationCleanup = null;
6931
6960
  let pendingResize = false;
6961
+ const measureText = createMeasureText();
6932
6962
  function getContainerDimensions() {
6933
6963
  const rect = container.getBoundingClientRect();
6934
6964
  return {
@@ -6944,7 +6974,8 @@ function createSankey(container, spec, options) {
6944
6974
  height,
6945
6975
  theme: options?.theme,
6946
6976
  darkMode,
6947
- watermark: options?.watermark
6977
+ watermark: options?.watermark,
6978
+ measureText
6948
6979
  };
6949
6980
  return compileSankey(currentSpec, compileOpts);
6950
6981
  }