@opendata-ai/openchart-engine 6.19.2 → 6.20.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
@@ -583,7 +583,7 @@ function computeAnnotations(spec, scales, chartArea, strategy, isDark = false, o
583
583
  }
584
584
 
585
585
  // src/charts/bar/compute.ts
586
- import { abbreviateNumber, formatNumber, isGradientDef } from "@opendata-ai/openchart-core";
586
+ import { isGradientDef } from "@opendata-ai/openchart-core";
587
587
 
588
588
  // src/transforms/predicates.ts
589
589
  function isFieldPredicate(pred) {
@@ -653,6 +653,13 @@ function isConditionalValueDef(def) {
653
653
  return def !== null && typeof def === "object" && "condition" in def;
654
654
  }
655
655
 
656
+ // src/charts/_shared/format-label-value.ts
657
+ import { abbreviateNumber, formatNumber } from "@opendata-ai/openchart-core";
658
+ function formatLabelValue(value2) {
659
+ if (Math.abs(value2) >= 1e3) return abbreviateNumber(value2);
660
+ return formatNumber(value2);
661
+ }
662
+
656
663
  // src/charts/utils.ts
657
664
  var DEFAULT_COLOR = "#1b7fa3";
658
665
  function scaleValue(scale, scaleType, value2) {
@@ -735,10 +742,6 @@ function orientGradientForHorizontalBar(grad) {
735
742
  return { ...lg, x1: 0, y1: 0, x2: 1, y2: 0 };
736
743
  }
737
744
  var MIN_BAR_WIDTH = 1;
738
- function formatBarValue(value2) {
739
- if (Math.abs(value2) >= 1e3) return abbreviateNumber(value2);
740
- return formatNumber(value2);
741
- }
742
745
  function computeBarMarks(spec, scales, _chartArea, _strategy) {
743
746
  const encoding = spec.encoding;
744
747
  const xChannel = encoding.x;
@@ -836,7 +839,7 @@ function computeStackedBars(data, valueField, categoryField, colorField, xScale,
836
839
  const xRight = xScale(cumulativeValue + value2);
837
840
  const barWidth = Math.max(Math.abs(xRight - xLeft), MIN_BAR_WIDTH);
838
841
  const aria = {
839
- label: `${category}, ${groupKey2}: ${formatBarValue(rawValue)}`
842
+ label: `${category}, ${groupKey2}: ${formatLabelValue(rawValue)}`
840
843
  };
841
844
  marks.push({
842
845
  type: "rect",
@@ -883,7 +886,7 @@ function computeGroupedBars(data, valueField, categoryField, colorField, xScale,
883
886
  const barWidth = Math.max(Math.abs(xScale(value2) - baseline), MIN_BAR_WIDTH);
884
887
  const subY = bandY + groupIndex * (subBandHeight + gap);
885
888
  const aria = {
886
- label: `${category}, ${groupKey2}: ${formatBarValue(value2)}`
889
+ label: `${category}, ${groupKey2}: ${formatLabelValue(value2)}`
887
890
  };
888
891
  marks.push({
889
892
  type: "rect",
@@ -914,7 +917,7 @@ function computeColoredBars(data, valueField, categoryField, colorField, xScale,
914
917
  const xPos = value2 >= 0 ? baseline : xScale(value2);
915
918
  const barWidth = Math.max(Math.abs(xScale(value2) - baseline), MIN_BAR_WIDTH);
916
919
  const aria = {
917
- label: `${category}, ${groupKey2}: ${formatBarValue(value2)}`
920
+ label: `${category}, ${groupKey2}: ${formatLabelValue(value2)}`
918
921
  };
919
922
  marks.push({
920
923
  type: "rect",
@@ -955,7 +958,7 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
955
958
  const xPos = value2 >= 0 ? baseline : xScale(value2);
956
959
  const barWidth = Math.max(Math.abs(xScale(value2) - baseline), MIN_BAR_WIDTH);
957
960
  const aria = {
958
- label: `${category}: ${formatBarValue(value2)}`
961
+ label: `${category}: ${formatLabelValue(value2)}`
959
962
  };
960
963
  marks.push({
961
964
  type: "rect",
@@ -975,17 +978,11 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
975
978
 
976
979
  // src/charts/bar/labels.ts
977
980
  import {
978
- abbreviateNumber as abbreviateNumber2,
979
981
  buildD3Formatter,
980
982
  estimateTextWidth as estimateTextWidth2,
981
- formatNumber as formatNumber2,
982
983
  getRepresentativeColor,
983
984
  resolveCollisions
984
985
  } from "@opendata-ai/openchart-core";
985
- function formatBarValue2(value2) {
986
- if (Math.abs(value2) >= 1e3) return abbreviateNumber2(value2);
987
- return formatNumber2(value2);
988
- }
989
986
  var SUFFIX_MULTIPLIERS = {
990
987
  K: 1e3,
991
988
  M: 1e6,
@@ -1023,7 +1020,7 @@ function computeBarLabels(marks, _chartArea, density = "auto", labelFormat, labe
1023
1020
  if (formatter && Number.isFinite(rawNum)) {
1024
1021
  valuePart = formatter(rawNum);
1025
1022
  } else if (Number.isFinite(rawNum)) {
1026
- valuePart = formatBarValue2(rawNum);
1023
+ valuePart = formatLabelValue(rawNum);
1027
1024
  } else {
1028
1025
  const ariaLabel = mark.aria.label;
1029
1026
  const lastColon = ariaLabel.lastIndexOf(":");
@@ -1135,12 +1132,8 @@ var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
1135
1132
  };
1136
1133
 
1137
1134
  // src/charts/column/compute.ts
1138
- import { abbreviateNumber as abbreviateNumber3, formatNumber as formatNumber3, isGradientDef as isGradientDef2 } from "@opendata-ai/openchart-core";
1135
+ import { isGradientDef as isGradientDef2 } from "@opendata-ai/openchart-core";
1139
1136
  var MIN_COLUMN_HEIGHT = 1;
1140
- function formatColumnValue(value2) {
1141
- if (Math.abs(value2) >= 1e3) return abbreviateNumber3(value2);
1142
- return formatNumber3(value2);
1143
- }
1144
1137
  function computeColumnMarks(spec, scales, _chartArea, _strategy) {
1145
1138
  const encoding = spec.encoding;
1146
1139
  const xChannel = encoding.x;
@@ -1241,7 +1234,7 @@ function computeSimpleColumns(data, categoryField, valueField, xScale, yScale, b
1241
1234
  const columnHeight = Math.max(Math.abs(baseline - yPos), MIN_COLUMN_HEIGHT);
1242
1235
  const y2 = value2 >= 0 ? yPos : baseline;
1243
1236
  const aria = {
1244
- label: `${category}: ${formatColumnValue(value2)}`
1237
+ label: `${category}: ${formatLabelValue(value2)}`
1245
1238
  };
1246
1239
  marks.push({
1247
1240
  type: "rect",
@@ -1272,7 +1265,7 @@ function computeColoredColumns(data, categoryField, valueField, colorField, xSca
1272
1265
  const columnHeight = Math.max(Math.abs(baseline - yPos), MIN_COLUMN_HEIGHT);
1273
1266
  const y2 = value2 >= 0 ? yPos : baseline;
1274
1267
  const aria = {
1275
- label: `${category}, ${groupKey2}: ${formatColumnValue(value2)}`
1268
+ label: `${category}, ${groupKey2}: ${formatLabelValue(value2)}`
1276
1269
  };
1277
1270
  marks.push({
1278
1271
  type: "rect",
@@ -1320,7 +1313,7 @@ function computeGroupedColumns(data, categoryField, valueField, colorField, xSca
1320
1313
  const y2 = value2 >= 0 ? yPos : baseline;
1321
1314
  const subX = bandX + groupIndex * (subBandWidth + gap);
1322
1315
  const aria = {
1323
- label: `${category}, ${groupKey2}: ${formatColumnValue(value2)}`
1316
+ label: `${category}, ${groupKey2}: ${formatLabelValue(value2)}`
1324
1317
  };
1325
1318
  marks.push({
1326
1319
  type: "rect",
@@ -1360,7 +1353,7 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
1360
1353
  const yBottom = yScale(cumulativeValue);
1361
1354
  const columnHeight = Math.max(Math.abs(yBottom - yTop), MIN_COLUMN_HEIGHT);
1362
1355
  const aria = {
1363
- label: `${category}, ${groupKey2}: ${formatColumnValue(rawValue)}`
1356
+ label: `${category}, ${groupKey2}: ${formatLabelValue(rawValue)}`
1364
1357
  };
1365
1358
  marks.push({
1366
1359
  type: "rect",
@@ -1383,17 +1376,11 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
1383
1376
 
1384
1377
  // src/charts/column/labels.ts
1385
1378
  import {
1386
- abbreviateNumber as abbreviateNumber4,
1387
1379
  buildD3Formatter as buildD3Formatter2,
1388
1380
  estimateTextWidth as estimateTextWidth3,
1389
- formatNumber as formatNumber4,
1390
1381
  getRepresentativeColor as getRepresentativeColor2,
1391
1382
  resolveCollisions as resolveCollisions2
1392
1383
  } from "@opendata-ai/openchart-core";
1393
- function formatColumnValue2(value2) {
1394
- if (Math.abs(value2) >= 1e3) return abbreviateNumber4(value2);
1395
- return formatNumber4(value2);
1396
- }
1397
1384
  var LABEL_FONT_SIZE2 = 10;
1398
1385
  var LABEL_FONT_WEIGHT2 = 600;
1399
1386
  var LABEL_OFFSET_Y = 6;
@@ -1408,7 +1395,7 @@ function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, l
1408
1395
  if (formatter && Number.isFinite(rawNum)) {
1409
1396
  valuePart = formatter(rawNum);
1410
1397
  } else if (Number.isFinite(rawNum)) {
1411
- valuePart = formatColumnValue2(rawNum);
1398
+ valuePart = formatLabelValue(rawNum);
1412
1399
  } else {
1413
1400
  const ariaLabel = mark.aria.label;
1414
1401
  const lastColon = ariaLabel.lastIndexOf(":");
@@ -1627,17 +1614,11 @@ function computeLollipopMarks(data, valueField, categoryField, xScale, yScale, b
1627
1614
 
1628
1615
  // src/charts/dot/labels.ts
1629
1616
  import {
1630
- abbreviateNumber as abbreviateNumber5,
1631
1617
  buildD3Formatter as buildD3Formatter3,
1632
1618
  estimateTextWidth as estimateTextWidth4,
1633
- formatNumber as formatNumber5,
1634
1619
  getRepresentativeColor as getRepresentativeColor3,
1635
1620
  resolveCollisions as resolveCollisions3
1636
1621
  } from "@opendata-ai/openchart-core";
1637
- function formatDotValue(value2) {
1638
- if (Math.abs(value2) >= 1e3) return abbreviateNumber5(value2);
1639
- return formatNumber5(value2);
1640
- }
1641
1622
  var LABEL_FONT_SIZE3 = 11;
1642
1623
  var LABEL_FONT_WEIGHT3 = 600;
1643
1624
  var LABEL_OFFSET_X = 10;
@@ -1652,7 +1633,7 @@ function computeDotLabels(marks, _chartArea, density = "auto", labelPrefix, labe
1652
1633
  if (formatter && Number.isFinite(rawNum)) {
1653
1634
  valuePart = formatter(rawNum);
1654
1635
  } else if (Number.isFinite(rawNum)) {
1655
- valuePart = formatDotValue(rawNum);
1636
+ valuePart = formatLabelValue(rawNum);
1656
1637
  } else {
1657
1638
  const ariaLabel = mark.aria.label;
1658
1639
  const lastColon = ariaLabel.lastIndexOf(":");
@@ -7769,12 +7750,12 @@ var DEFAULT_COLLISION_PADDING = 5;
7769
7750
 
7770
7751
  // src/layout/axes.ts
7771
7752
  import {
7772
- abbreviateNumber as abbreviateNumber6,
7753
+ abbreviateNumber as abbreviateNumber2,
7773
7754
  buildD3Formatter as buildD3Formatter4,
7774
7755
  buildTemporalFormatter,
7775
7756
  estimateTextWidth as estimateTextWidth7,
7776
7757
  formatDate,
7777
- formatNumber as formatNumber6
7758
+ formatNumber as formatNumber2
7778
7759
  } from "@opendata-ai/openchart-core";
7779
7760
  var TICK_COUNTS = {
7780
7761
  full: 12,
@@ -7802,9 +7783,19 @@ function effectiveDensity(baseDensity, axisLength, minimalThreshold, reducedThre
7802
7783
  function measureLabel(text, fontSize, fontWeight, measureText) {
7803
7784
  return measureText ? measureText(text, fontSize, fontWeight).width : estimateTextWidth7(text, fontSize, fontWeight);
7804
7785
  }
7805
- function ticksOverlap(ticks2, fontSize, fontWeight, measureText) {
7786
+ function ticksOverlap(ticks2, fontSize, fontWeight, measureText, orientation = "horizontal") {
7806
7787
  if (ticks2.length < 2) return false;
7807
7788
  const minGap = fontSize * MIN_TICK_GAP_FACTOR;
7789
+ if (orientation === "vertical") {
7790
+ const sorted = [...ticks2].sort((a, b) => a.position - b.position);
7791
+ const labelHeight = fontSize * 1.2;
7792
+ for (let i = 0; i < sorted.length - 1; i++) {
7793
+ const aBottom = sorted[i].position + labelHeight / 2;
7794
+ const bTop = sorted[i + 1].position - labelHeight / 2;
7795
+ if (aBottom + minGap > bTop) return true;
7796
+ }
7797
+ return false;
7798
+ }
7808
7799
  for (let i = 0; i < ticks2.length - 1; i++) {
7809
7800
  const aWidth = measureLabel(ticks2[i].label, fontSize, fontWeight, measureText);
7810
7801
  const bWidth = measureLabel(ticks2[i + 1].label, fontSize, fontWeight, measureText);
@@ -7814,8 +7805,8 @@ function ticksOverlap(ticks2, fontSize, fontWeight, measureText) {
7814
7805
  }
7815
7806
  return false;
7816
7807
  }
7817
- function thinTicksUntilFit(ticks2, fontSize, fontWeight, measureText) {
7818
- if (!ticksOverlap(ticks2, fontSize, fontWeight, measureText)) return ticks2;
7808
+ function thinTicksUntilFit(ticks2, fontSize, fontWeight, measureText, orientation = "horizontal") {
7809
+ if (!ticksOverlap(ticks2, fontSize, fontWeight, measureText, orientation)) return ticks2;
7819
7810
  let current = ticks2;
7820
7811
  while (current.length > MIN_TICK_COUNT) {
7821
7812
  const thinned = [current[0]];
@@ -7824,7 +7815,7 @@ function thinTicksUntilFit(ticks2, fontSize, fontWeight, measureText) {
7824
7815
  }
7825
7816
  if (current.length > 1) thinned.push(current[current.length - 1]);
7826
7817
  current = thinned;
7827
- if (!ticksOverlap(current, fontSize, fontWeight, measureText)) break;
7818
+ if (!ticksOverlap(current, fontSize, fontWeight, measureText, orientation)) break;
7828
7819
  }
7829
7820
  return current;
7830
7821
  }
@@ -7894,8 +7885,8 @@ function formatTickLabel(value2, resolvedScale) {
7894
7885
  const fmt = buildD3Formatter4(formatStr);
7895
7886
  if (fmt) return fmt(num);
7896
7887
  }
7897
- if (Math.abs(num) >= 1e3) return abbreviateNumber6(num);
7898
- return formatNumber6(num);
7888
+ if (Math.abs(num) >= 1e3) return abbreviateNumber2(num);
7889
+ return formatNumber2(num);
7899
7890
  }
7900
7891
  return String(value2);
7901
7892
  }
@@ -8011,7 +8002,7 @@ function computeAxes(scales, chartArea, strategy, theme, measureText) {
8011
8002
  allTicks = continuousTicks(scales.y, yDensity);
8012
8003
  }
8013
8004
  const shouldThin = scales.y.type !== "band" && !axisConfig?.tickCount && !axisConfig?.values;
8014
- const ticks2 = shouldThin ? thinTicksUntilFit(allTicks, fontSize, fontWeight, measureText) : allTicks;
8005
+ const ticks2 = shouldThin ? thinTicksUntilFit(allTicks, fontSize, fontWeight, measureText, "vertical") : allTicks;
8015
8006
  const gridlines = ticks2.map((t) => ({
8016
8007
  position: t.position,
8017
8008
  major: true
@@ -8709,10 +8700,46 @@ function computeScales(spec, chartArea, data) {
8709
8700
  }
8710
8701
 
8711
8702
  // src/legend/compute.ts
8712
- import { BRAND_RESERVE_WIDTH, estimateTextWidth as estimateTextWidth9 } from "@opendata-ai/openchart-core";
8703
+ import { BRAND_RESERVE_WIDTH, estimateTextWidth as estimateTextWidth10 } from "@opendata-ai/openchart-core";
8704
+
8705
+ // src/legend/wrap.ts
8706
+ import { estimateTextWidth as estimateTextWidth9 } from "@opendata-ai/openchart-core";
8713
8707
  var SWATCH_SIZE2 = 12;
8714
8708
  var SWATCH_GAP2 = 6;
8715
8709
  var ENTRY_GAP2 = 16;
8710
+ function measureLegendWrap(entries, maxWidth, labelStyle, maxRows) {
8711
+ if (entries.length === 0) {
8712
+ return { rowCount: 0, fittingCount: 0, rowWidths: [] };
8713
+ }
8714
+ let rowCount = 1;
8715
+ let rowWidth = 0;
8716
+ const rowWidths = [];
8717
+ let fittingCount = entries.length;
8718
+ let fittingCountLocked = false;
8719
+ for (let i = 0; i < entries.length; i++) {
8720
+ const labelWidth = estimateTextWidth9(
8721
+ entries[i].label,
8722
+ labelStyle.fontSize,
8723
+ labelStyle.fontWeight
8724
+ );
8725
+ const entryWidth = SWATCH_SIZE2 + SWATCH_GAP2 + labelWidth + ENTRY_GAP2;
8726
+ if (rowWidth + entryWidth > maxWidth && rowWidth > 0) {
8727
+ rowWidths.push(rowWidth);
8728
+ rowCount++;
8729
+ rowWidth = entryWidth;
8730
+ if (!fittingCountLocked && maxRows != null && rowCount > maxRows) {
8731
+ fittingCount = i;
8732
+ fittingCountLocked = true;
8733
+ }
8734
+ } else {
8735
+ rowWidth += entryWidth;
8736
+ }
8737
+ }
8738
+ rowWidths.push(rowWidth);
8739
+ return { rowCount, fittingCount, rowWidths };
8740
+ }
8741
+
8742
+ // src/legend/compute.ts
8716
8743
  var LEGEND_PADDING = 8;
8717
8744
  var LEGEND_RIGHT_WIDTH = 120;
8718
8745
  var RIGHT_LEGEND_MAX_HEIGHT_RATIO = 0.4;
@@ -8753,26 +8780,6 @@ function extractColorEntries(spec, theme) {
8753
8780
  };
8754
8781
  });
8755
8782
  }
8756
- function entriesThatFit(entries, maxWidth, maxRows, labelStyle) {
8757
- let row = 1;
8758
- let rowWidth = 0;
8759
- for (let i = 0; i < entries.length; i++) {
8760
- const labelWidth = estimateTextWidth9(
8761
- entries[i].label,
8762
- labelStyle.fontSize,
8763
- labelStyle.fontWeight
8764
- );
8765
- const entryWidth = SWATCH_SIZE2 + SWATCH_GAP2 + labelWidth + ENTRY_GAP2;
8766
- if (rowWidth + entryWidth > maxWidth && rowWidth > 0) {
8767
- row++;
8768
- rowWidth = entryWidth;
8769
- if (row > maxRows) return i;
8770
- } else {
8771
- rowWidth += entryWidth;
8772
- }
8773
- }
8774
- return entries.length;
8775
- }
8776
8783
  function truncateEntries(entries, maxCount) {
8777
8784
  if (maxCount >= entries.length || maxCount <= 0) return entries;
8778
8785
  const truncated = entries.slice(0, maxCount);
@@ -8840,7 +8847,7 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
8840
8847
  }
8841
8848
  if (resolvedPosition === "right" || resolvedPosition === "bottom-right") {
8842
8849
  const maxLabelWidth = Math.max(
8843
- ...entries.map((e) => estimateTextWidth9(e.label, labelStyle.fontSize, labelStyle.fontWeight))
8850
+ ...entries.map((e) => estimateTextWidth10(e.label, labelStyle.fontSize, labelStyle.fontWeight))
8844
8851
  );
8845
8852
  const legendWidth = Math.min(
8846
8853
  LEGEND_RIGHT_WIDTH,
@@ -8886,26 +8893,15 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
8886
8893
  }
8887
8894
  }
8888
8895
  const maxRows = spec.legend?.maxRows != null ? Math.max(1, spec.legend.maxRows) : spec.legend?.columns != null ? Math.ceil(entries.length / spec.legend.columns) : TOP_LEGEND_MAX_ROWS;
8889
- const maxFit = entriesThatFit(entries, availableWidth, maxRows, labelStyle);
8890
- if (maxFit < entries.length) {
8891
- entries = truncateEntries(entries, maxFit);
8896
+ const { fittingCount } = measureLegendWrap(entries, availableWidth, labelStyle, maxRows);
8897
+ if (fittingCount < entries.length) {
8898
+ entries = truncateEntries(entries, fittingCount);
8892
8899
  }
8893
8900
  const totalWidth = entries.reduce((sum2, entry) => {
8894
- const labelWidth = estimateTextWidth9(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
8901
+ const labelWidth = estimateTextWidth10(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
8895
8902
  return sum2 + SWATCH_SIZE2 + SWATCH_GAP2 + labelWidth + ENTRY_GAP2;
8896
8903
  }, 0);
8897
- let rowCount = 1;
8898
- let rowWidth = 0;
8899
- for (const entry of entries) {
8900
- const labelWidth = estimateTextWidth9(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
8901
- const entryWidth = SWATCH_SIZE2 + SWATCH_GAP2 + labelWidth + ENTRY_GAP2;
8902
- if (rowWidth + entryWidth > availableWidth && rowWidth > 0) {
8903
- rowCount++;
8904
- rowWidth = entryWidth;
8905
- } else {
8906
- rowWidth += entryWidth;
8907
- }
8908
- }
8904
+ const { rowCount } = measureLegendWrap(entries, availableWidth, labelStyle);
8909
8905
  const rowHeight = SWATCH_SIZE2 + 4;
8910
8906
  const legendHeight = rowCount * rowHeight + LEGEND_PADDING * 2;
8911
8907
  const offsetDx = spec.legend?.offset?.dx ?? 0;
@@ -8931,8 +8927,8 @@ import {
8931
8927
  adaptTheme as adaptTheme2,
8932
8928
  buildD3Formatter as buildD3Formatter5,
8933
8929
  computeChrome as computeChrome3,
8934
- estimateTextWidth as estimateTextWidth10,
8935
- formatNumber as formatNumber7,
8930
+ estimateTextWidth as estimateTextWidth11,
8931
+ formatNumber as formatNumber3,
8936
8932
  resolveTheme as resolveTheme2
8937
8933
  } from "@opendata-ai/openchart-core";
8938
8934
 
@@ -9408,9 +9404,6 @@ function generateLinkPath(link) {
9408
9404
  }
9409
9405
 
9410
9406
  // src/sankey/compile-sankey.ts
9411
- var SWATCH_SIZE3 = 12;
9412
- var SWATCH_GAP3 = 6;
9413
- var ENTRY_GAP3 = 16;
9414
9407
  var LABEL_GAP = 6;
9415
9408
  var LINK_OPACITY_LIGHT = 0.5;
9416
9409
  var LINK_OPACITY_DARK = 0.75;
@@ -9615,7 +9608,7 @@ function compileSankey(spec, options) {
9615
9608
  if (labelsLeft) continue;
9616
9609
  const labelX = (node.x1 ?? nodeWidth) + LABEL_GAP;
9617
9610
  const labelText = node.label ?? node.id;
9618
- const labelWidth = estimateTextWidth10(labelText, labelFontSize, labelFontWeight);
9611
+ const labelWidth = estimateTextWidth11(labelText, labelFontSize, labelFontWeight);
9619
9612
  const overflow = labelX + labelWidth - rightEdge;
9620
9613
  if (overflow > maxOverflow) maxOverflow = overflow;
9621
9614
  }
@@ -9780,22 +9773,11 @@ function buildSankeyLegend(nodeColorMap, colorField, data, sourceField, targetFi
9780
9773
  }
9781
9774
  let bounds = { x: 0, y: 0, width: 0, height: 0 };
9782
9775
  if (entries.length > 0) {
9783
- const ROW_HEIGHT = SWATCH_SIZE3 + 4;
9776
+ const ROW_HEIGHT = SWATCH_SIZE2 + 4;
9784
9777
  const availableWidth = area.width;
9785
- let rowCount = 1;
9786
- let rowX = 0;
9787
- for (const entry of entries) {
9788
- const labelWidth = estimateTextWidth10(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
9789
- const entryWidth = SWATCH_SIZE3 + SWATCH_GAP3 + labelWidth + ENTRY_GAP3;
9790
- if (rowX > 0 && rowX + entryWidth > availableWidth) {
9791
- rowCount++;
9792
- rowX = entryWidth;
9793
- } else {
9794
- rowX += entryWidth;
9795
- }
9796
- }
9797
- rowCount = Math.min(rowCount, 2);
9798
- const legendHeight = rowCount * ROW_HEIGHT;
9778
+ const { rowCount } = measureLegendWrap(entries, availableWidth, labelStyle);
9779
+ const cappedRowCount = Math.min(rowCount, 2);
9780
+ const legendHeight = cappedRowCount * ROW_HEIGHT;
9799
9781
  bounds = {
9800
9782
  x: area.x,
9801
9783
  y: area.y,
@@ -9808,9 +9790,9 @@ function buildSankeyLegend(nodeColorMap, colorField, data, sourceField, targetFi
9808
9790
  entries,
9809
9791
  bounds,
9810
9792
  labelStyle,
9811
- swatchSize: SWATCH_SIZE3,
9812
- swatchGap: SWATCH_GAP3,
9813
- entryGap: ENTRY_GAP3
9793
+ swatchSize: SWATCH_SIZE2,
9794
+ swatchGap: SWATCH_GAP2,
9795
+ entryGap: ENTRY_GAP2
9814
9796
  };
9815
9797
  }
9816
9798
  function formatFlowValue(value2, valueFormat) {
@@ -9818,7 +9800,7 @@ function formatFlowValue(value2, valueFormat) {
9818
9800
  const fmt = buildD3Formatter5(valueFormat);
9819
9801
  if (fmt) return fmt(value2);
9820
9802
  }
9821
- return formatNumber7(value2);
9803
+ return formatNumber3(value2);
9822
9804
  }
9823
9805
  function buildTooltipDescriptors(nodes, links, valueFormat) {
9824
9806
  const descriptors = /* @__PURE__ */ new Map();
@@ -9866,9 +9848,9 @@ function emptyLayout(area, chrome, theme, options, watermark) {
9866
9848
  fill: theme.colors.text,
9867
9849
  lineHeight: 1.3
9868
9850
  },
9869
- swatchSize: SWATCH_SIZE3,
9870
- swatchGap: SWATCH_GAP3,
9871
- entryGap: ENTRY_GAP3
9851
+ swatchSize: SWATCH_SIZE2,
9852
+ swatchGap: SWATCH_GAP2,
9853
+ entryGap: ENTRY_GAP2
9872
9854
  },
9873
9855
  tooltipDescriptors: /* @__PURE__ */ new Map(),
9874
9856
  a11y: {
@@ -9887,7 +9869,7 @@ function emptyLayout(area, chrome, theme, options, watermark) {
9887
9869
  }
9888
9870
 
9889
9871
  // src/tables/compile-table.ts
9890
- import { computeChrome as computeChrome4, estimateTextWidth as estimateTextWidth11 } from "@opendata-ai/openchart-core";
9872
+ import { computeChrome as computeChrome4, estimateTextWidth as estimateTextWidth12 } from "@opendata-ai/openchart-core";
9891
9873
 
9892
9874
  // src/tables/bar-column.ts
9893
9875
  var NEGATIVE_BAR_COLOR = "#c44e52";
@@ -10004,7 +9986,7 @@ function computeCategoryColors(data, column, theme, darkMode) {
10004
9986
  }
10005
9987
 
10006
9988
  // src/tables/format-cells.ts
10007
- import { buildD3Formatter as buildD3Formatter6, formatDate as formatDate2, formatNumber as formatNumber8 } from "@opendata-ai/openchart-core";
9989
+ import { buildD3Formatter as buildD3Formatter6, formatDate as formatDate2, formatNumber as formatNumber4 } from "@opendata-ai/openchart-core";
10008
9990
  function isNumericValue(value2) {
10009
9991
  if (typeof value2 === "number") return Number.isFinite(value2);
10010
9992
  return false;
@@ -10035,7 +10017,7 @@ function formatCell(value2, column) {
10035
10017
  if (isNumericValue(value2)) {
10036
10018
  return {
10037
10019
  value: value2,
10038
- formattedValue: formatNumber8(value2),
10020
+ formattedValue: formatNumber4(value2),
10039
10021
  style
10040
10022
  };
10041
10023
  }
@@ -10061,7 +10043,7 @@ function formatValueForSearch(value2, column) {
10061
10043
  }
10062
10044
  }
10063
10045
  if (isNumericValue(value2)) {
10064
- return formatNumber8(value2);
10046
+ return formatNumber4(value2);
10065
10047
  }
10066
10048
  return String(value2);
10067
10049
  }
@@ -10302,13 +10284,13 @@ function estimateColumnWidth(col, data, fontSize) {
10302
10284
  if (col.image) return (col.image.width ?? 24) + PADDING;
10303
10285
  if (col.flag) return 60;
10304
10286
  const label = col.label ?? col.key;
10305
- const headerWidth = estimateTextWidth11(label, fontSize, 600) + PADDING;
10287
+ const headerWidth = estimateTextWidth12(label, fontSize, 600) + PADDING;
10306
10288
  const sampleSize = Math.min(100, data.length);
10307
10289
  let maxDataWidth = 0;
10308
10290
  for (let i = 0; i < sampleSize; i++) {
10309
10291
  const val = data[i][col.key];
10310
10292
  const text = val == null ? "" : String(val);
10311
- const width = estimateTextWidth11(text, fontSize, 400) + PADDING;
10293
+ const width = estimateTextWidth12(text, fontSize, 400) + PADDING;
10312
10294
  if (width > maxDataWidth) maxDataWidth = width;
10313
10295
  }
10314
10296
  return Math.max(MIN_WIDTH, headerWidth, maxDataWidth);
@@ -10536,7 +10518,7 @@ function compileTableLayout(spec, options, theme) {
10536
10518
  import {
10537
10519
  buildTemporalFormatter as buildTemporalFormatter2,
10538
10520
  formatDate as formatDate3,
10539
- formatNumber as formatNumber9,
10521
+ formatNumber as formatNumber5,
10540
10522
  getRepresentativeColor as getRepresentativeColor10
10541
10523
  } from "@opendata-ai/openchart-core";
10542
10524
  function formatValue(value2, fieldType, format2) {
@@ -10551,10 +10533,10 @@ function formatValue(value2, fieldType, format2) {
10551
10533
  try {
10552
10534
  return format(format2)(value2);
10553
10535
  } catch {
10554
- return formatNumber9(value2);
10536
+ return formatNumber5(value2);
10555
10537
  }
10556
10538
  }
10557
- return formatNumber9(value2);
10539
+ return formatNumber5(value2);
10558
10540
  }
10559
10541
  return String(value2);
10560
10542
  }