@opendata-ai/openchart-engine 6.25.2 → 6.25.4

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
@@ -4,7 +4,7 @@ import {
4
4
  adaptTheme as adaptTheme3,
5
5
  BREAKPOINT_COMPACT_MAX as BREAKPOINT_COMPACT_MAX2,
6
6
  computeLabelBounds,
7
- estimateTextWidth as estimateTextWidth13,
7
+ estimateTextWidth as estimateTextWidth14,
8
8
  generateAltText,
9
9
  generateDataTable,
10
10
  getAxisTitleOffset as getAxisTitleOffset2,
@@ -6651,6 +6651,16 @@ function normalizeAnnotations(annotations) {
6651
6651
  }
6652
6652
  });
6653
6653
  }
6654
+ function normalizeLabels(labels) {
6655
+ if (labels === false) return { density: "none", format: "", prefix: "" };
6656
+ if (labels === true || labels === void 0) return { density: "auto", format: "", prefix: "" };
6657
+ return {
6658
+ density: labels.density ?? "auto",
6659
+ format: labels.format ?? "",
6660
+ prefix: labels.prefix ?? "",
6661
+ offsets: labels.offsets
6662
+ };
6663
+ }
6654
6664
  function normalizeChartSpec(spec, warnings) {
6655
6665
  const encoding = inferEncodingTypes(spec.encoding, spec.data, warnings);
6656
6666
  const markType = resolveMarkType(spec.mark);
@@ -6662,12 +6672,7 @@ function normalizeChartSpec(spec, warnings) {
6662
6672
  encoding,
6663
6673
  chrome: normalizeChrome(spec.chrome),
6664
6674
  annotations: normalizeAnnotations(spec.annotations),
6665
- labels: {
6666
- density: spec.labels?.density ?? "auto",
6667
- format: spec.labels?.format ?? "",
6668
- prefix: spec.labels?.prefix ?? "",
6669
- offsets: spec.labels?.offsets
6670
- },
6675
+ labels: normalizeLabels(spec.labels),
6671
6676
  legend: spec.legend,
6672
6677
  responsive: spec.responsive ?? true,
6673
6678
  theme: spec.theme ?? {},
@@ -7921,6 +7926,7 @@ import {
7921
7926
  abbreviateNumber as abbreviateNumber2,
7922
7927
  buildD3Formatter as buildD3Formatter4,
7923
7928
  buildTemporalFormatter,
7929
+ estimateTextWidth as estimateTextWidth8,
7924
7930
  formatDate,
7925
7931
  formatNumber as formatNumber2
7926
7932
  } from "@opendata-ai/openchart-core";
@@ -8015,16 +8021,41 @@ function scaleSupportsTickCount(resolvedScale) {
8015
8021
  const scale = resolvedScale.scale;
8016
8022
  return "ticks" in scale && typeof scale.ticks === "function";
8017
8023
  }
8018
- function categoricalTicks(resolvedScale, density) {
8024
+ function categoricalTicks(resolvedScale, density, orientation = "horizontal", bandwidth, labelAngle, fontSize, fontWeight, measureText) {
8019
8025
  const scale = resolvedScale.scale;
8020
8026
  const domain = scale.domain();
8021
8027
  const explicitTickCount = resolvedScale.channel.axis?.tickCount;
8022
- const maxTicks = explicitTickCount ?? TICK_COUNTS[density];
8023
8028
  let selectedValues = domain;
8024
- const shouldThinBand = resolvedScale.type === "band" && (explicitTickCount || density !== "full");
8025
- if ((resolvedScale.type !== "band" || shouldThinBand) && domain.length > maxTicks) {
8026
- const step = Math.ceil(domain.length / maxTicks);
8027
- selectedValues = domain.filter((_, i) => i % step === 0);
8029
+ if (resolvedScale.type === "band" && orientation === "horizontal") {
8030
+ if (bandwidth !== void 0 && bandwidth > 0 && fontSize !== void 0) {
8031
+ const maxLabelWidth = domain.reduce((max4, v) => {
8032
+ const w = measureText ? measureText(v, fontSize, fontWeight ?? 400).width : estimateTextWidth8(v, fontSize, fontWeight ?? 400);
8033
+ return Math.max(max4, w);
8034
+ }, 0);
8035
+ const angleRad = labelAngle !== void 0 ? Math.abs(labelAngle) * Math.PI / 180 : 0;
8036
+ const footprint = angleRad > 0 ? maxLabelWidth * Math.abs(Math.cos(angleRad)) : maxLabelWidth;
8037
+ const minGap = fontSize * 0.5;
8038
+ if (footprint + minGap > bandwidth) {
8039
+ const maxFitting = Math.max(1, Math.floor(bandwidth / (footprint + minGap)));
8040
+ const cap = explicitTickCount ?? Math.min(domain.length, Math.max(maxFitting, TICK_COUNTS[density]));
8041
+ if (domain.length > cap) {
8042
+ const step = Math.ceil(domain.length / cap);
8043
+ selectedValues = domain.filter((_, i) => i % step === 0);
8044
+ }
8045
+ }
8046
+ } else {
8047
+ const maxTicks = explicitTickCount ?? TICK_COUNTS[density];
8048
+ if ((explicitTickCount || density !== "full") && domain.length > maxTicks) {
8049
+ const step = Math.ceil(domain.length / maxTicks);
8050
+ selectedValues = domain.filter((_, i) => i % step === 0);
8051
+ }
8052
+ }
8053
+ } else if (resolvedScale.type !== "band") {
8054
+ const maxTicks = explicitTickCount ?? TICK_COUNTS[density];
8055
+ if (domain.length > maxTicks) {
8056
+ const step = Math.ceil(domain.length / maxTicks);
8057
+ selectedValues = domain.filter((_, i) => i % step === 0);
8058
+ }
8028
8059
  }
8029
8060
  const ticks2 = selectedValues.map((value2) => {
8030
8061
  const bandScale = resolvedScale.type === "band" ? scale : null;
@@ -8145,7 +8176,17 @@ function computeAxes(scales, chartArea, strategy, theme, measureText) {
8145
8176
  if (axisConfig?.values) {
8146
8177
  allTicks = resolveExplicitTicks(axisConfig.values, scales.x);
8147
8178
  } else if (!isContinuousX) {
8148
- allTicks = categoricalTicks(scales.x, xDensity);
8179
+ const xBandwidth = scales.x.type === "band" ? scales.x.scale.bandwidth() : void 0;
8180
+ allTicks = categoricalTicks(
8181
+ scales.x,
8182
+ xDensity,
8183
+ "horizontal",
8184
+ xBandwidth,
8185
+ axisConfig?.labelAngle,
8186
+ fontSize,
8187
+ fontWeight,
8188
+ measureText
8189
+ );
8149
8190
  } else {
8150
8191
  allTicks = continuousTicks(scales.x, xDensity, xTargetCount);
8151
8192
  }
@@ -8213,7 +8254,7 @@ function computeAxes(scales, chartArea, strategy, theme, measureText) {
8213
8254
  if (axisConfig?.values) {
8214
8255
  allTicks = resolveExplicitTicks(axisConfig.values, scales.y);
8215
8256
  } else if (!isContinuousY) {
8216
- allTicks = categoricalTicks(scales.y, yDensity);
8257
+ allTicks = categoricalTicks(scales.y, yDensity, "vertical");
8217
8258
  } else {
8218
8259
  allTicks = continuousTicks(scales.y, yDensity, yTargetCount);
8219
8260
  }
@@ -8270,7 +8311,7 @@ import {
8270
8311
  AXIS_TITLE_TRAILING_PAD,
8271
8312
  BREAKPOINT_COMPACT_MAX,
8272
8313
  computeChrome as computeChrome2,
8273
- estimateTextWidth as estimateTextWidth9,
8314
+ estimateTextWidth as estimateTextWidth10,
8274
8315
  getAxisTitleOffset,
8275
8316
  HPAD_COMPACT_FRACTION,
8276
8317
  HPAD_COMPACT_MIN,
@@ -8285,7 +8326,7 @@ import {
8285
8326
  } from "@opendata-ai/openchart-core";
8286
8327
 
8287
8328
  // src/legend/wrap.ts
8288
- import { COMPACT_WIDTH, estimateTextWidth as estimateTextWidth8 } from "@opendata-ai/openchart-core";
8329
+ import { COMPACT_WIDTH, estimateTextWidth as estimateTextWidth9 } from "@opendata-ai/openchart-core";
8289
8330
  var SWATCH_SIZE2 = 12;
8290
8331
  var SWATCH_GAP2 = 6;
8291
8332
  var ENTRY_GAP2 = 16;
@@ -8304,7 +8345,7 @@ function measureLegendWrap(entries, maxWidth, labelStyle, maxRows, entryGap = EN
8304
8345
  let fittingCount = entries.length;
8305
8346
  let fittingCountLocked = false;
8306
8347
  for (let i = 0; i < entries.length; i++) {
8307
- const labelWidth = estimateTextWidth8(
8348
+ const labelWidth = estimateTextWidth9(
8308
8349
  entries[i].label,
8309
8350
  labelStyle.fontSize,
8310
8351
  labelStyle.fontWeight
@@ -8376,7 +8417,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
8376
8417
  if (xField) {
8377
8418
  for (const row of spec.data) {
8378
8419
  const label = String(row[xField] ?? "");
8379
- const w = estimateTextWidth9(label, theme.fonts.sizes.axisTick, theme.fonts.weights.normal);
8420
+ const w = estimateTextWidth10(label, theme.fonts.sizes.axisTick, theme.fonts.weights.normal);
8380
8421
  if (w > maxLabelWidth) maxLabelWidth = w;
8381
8422
  }
8382
8423
  }
@@ -8406,7 +8447,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
8406
8447
  const label = String(row[colorField] ?? "");
8407
8448
  if (!seen.has(label)) {
8408
8449
  seen.add(label);
8409
- const w = estimateTextWidth9(label, 11, 600);
8450
+ const w = estimateTextWidth10(label, 11, 600);
8410
8451
  if (w > maxLabelWidth) maxLabelWidth = w;
8411
8452
  }
8412
8453
  }
@@ -8426,7 +8467,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
8426
8467
  const maxXStr = String(maxX);
8427
8468
  for (const ann of spec.annotations) {
8428
8469
  if (ann.type === "text" && String(ann.x) === maxXStr) {
8429
- const textWidth = estimateTextWidth9(ann.text, ann.fontSize ?? 11, ann.fontWeight ?? 600);
8470
+ const textWidth = estimateTextWidth10(ann.text, ann.fontSize ?? 11, ann.fontWeight ?? 600);
8430
8471
  const dx = ann.offset?.dx ?? 0;
8431
8472
  const anchor = ann.anchor ?? "auto";
8432
8473
  const baseRightExtent = anchor === "left" ? textWidth : (
@@ -8450,7 +8491,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
8450
8491
  let maxLabelWidth = 0;
8451
8492
  for (const row of spec.data) {
8452
8493
  const label = String(row[yField] ?? "");
8453
- const w = estimateTextWidth9(label, theme.fonts.sizes.axisTick, theme.fonts.weights.normal);
8494
+ const w = estimateTextWidth10(label, theme.fonts.sizes.axisTick, theme.fonts.weights.normal);
8454
8495
  if (w > maxLabelWidth) maxLabelWidth = w;
8455
8496
  }
8456
8497
  if (maxLabelWidth > 0) {
@@ -8486,7 +8527,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
8486
8527
  }
8487
8528
  const negPrefix = spec.data.some((r) => Number(r[yField]) < 0) ? "-" : "";
8488
8529
  const labelEst = negPrefix + sampleLabel;
8489
- const labelWidth = estimateTextWidth9(
8530
+ const labelWidth = estimateTextWidth10(
8490
8531
  labelEst,
8491
8532
  theme.fonts.sizes.axisTick,
8492
8533
  theme.fonts.weights.normal
@@ -9007,7 +9048,7 @@ function computeScales(spec, chartArea, data) {
9007
9048
  }
9008
9049
 
9009
9050
  // src/legend/compute.ts
9010
- import { BRAND_RESERVE_WIDTH as BRAND_RESERVE_WIDTH2, COMPACT_WIDTH as COMPACT_WIDTH2, estimateTextWidth as estimateTextWidth10 } from "@opendata-ai/openchart-core";
9051
+ import { BRAND_RESERVE_WIDTH as BRAND_RESERVE_WIDTH2, COMPACT_WIDTH as COMPACT_WIDTH2, estimateTextWidth as estimateTextWidth11 } from "@opendata-ai/openchart-core";
9011
9052
  var LEGEND_PADDING = 8;
9012
9053
  var LEGEND_RIGHT_WIDTH = 120;
9013
9054
  var RIGHT_LEGEND_MAX_HEIGHT_RATIO = 0.4;
@@ -9123,7 +9164,7 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
9123
9164
  }
9124
9165
  if (resolvedPosition === "right" || resolvedPosition === "bottom-right") {
9125
9166
  const maxLabelWidth = Math.max(
9126
- ...entries.map((e) => estimateTextWidth10(e.label, labelStyle.fontSize, labelStyle.fontWeight))
9167
+ ...entries.map((e) => estimateTextWidth11(e.label, labelStyle.fontSize, labelStyle.fontWeight))
9127
9168
  );
9128
9169
  const legendWidth = Math.min(
9129
9170
  LEGEND_RIGHT_WIDTH,
@@ -9183,7 +9224,7 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
9183
9224
  entries = truncateEntries(entries, fittingCount);
9184
9225
  }
9185
9226
  const totalWidth = entries.reduce((sum2, entry) => {
9186
- const labelWidth = estimateTextWidth10(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
9227
+ const labelWidth = estimateTextWidth11(entry.label, labelStyle.fontSize, labelStyle.fontWeight);
9187
9228
  return sum2 + SWATCH_SIZE2 + SWATCH_GAP2 + labelWidth + effectiveEntryGap;
9188
9229
  }, 0);
9189
9230
  const { rowCount } = measureLegendWrap(
@@ -9218,7 +9259,7 @@ import {
9218
9259
  adaptTheme as adaptTheme2,
9219
9260
  buildD3Formatter as buildD3Formatter5,
9220
9261
  computeChrome as computeChrome3,
9221
- estimateTextWidth as estimateTextWidth11,
9262
+ estimateTextWidth as estimateTextWidth12,
9222
9263
  formatNumber as formatNumber3,
9223
9264
  resolveTheme as resolveTheme2
9224
9265
  } from "@opendata-ai/openchart-core";
@@ -9899,7 +9940,7 @@ function compileSankey(spec, options) {
9899
9940
  if (labelsLeft) continue;
9900
9941
  const labelX = (node.x1 ?? nodeWidth) + LABEL_GAP;
9901
9942
  const labelText = node.label ?? node.id;
9902
- const labelWidth = estimateTextWidth11(labelText, labelFontSize, labelFontWeight);
9943
+ const labelWidth = estimateTextWidth12(labelText, labelFontSize, labelFontWeight);
9903
9944
  const overflow = labelX + labelWidth - rightEdge;
9904
9945
  if (overflow > maxOverflow) maxOverflow = overflow;
9905
9946
  }
@@ -10161,7 +10202,7 @@ function emptyLayout(area, chrome, theme, options, watermark) {
10161
10202
  }
10162
10203
 
10163
10204
  // src/tables/compile-table.ts
10164
- import { computeChrome as computeChrome4, estimateTextWidth as estimateTextWidth12 } from "@opendata-ai/openchart-core";
10205
+ import { computeChrome as computeChrome4, estimateTextWidth as estimateTextWidth13 } from "@opendata-ai/openchart-core";
10165
10206
 
10166
10207
  // src/tables/bar-column.ts
10167
10208
  var NEGATIVE_BAR_COLOR = "#c44e52";
@@ -10576,13 +10617,13 @@ function estimateColumnWidth(col, data, fontSize) {
10576
10617
  if (col.image) return (col.image.width ?? 24) + PADDING;
10577
10618
  if (col.flag) return 60;
10578
10619
  const label = col.label ?? col.key;
10579
- const headerWidth = estimateTextWidth12(label, fontSize, 600) + PADDING;
10620
+ const headerWidth = estimateTextWidth13(label, fontSize, 600) + PADDING;
10580
10621
  const sampleSize = Math.min(100, data.length);
10581
10622
  let maxDataWidth = 0;
10582
10623
  for (let i = 0; i < sampleSize; i++) {
10583
10624
  const val = data[i][col.key];
10584
10625
  const text = val == null ? "" : String(val);
10585
- const width = estimateTextWidth12(text, fontSize, 400) + PADDING;
10626
+ const width = estimateTextWidth13(text, fontSize, 400) + PADDING;
10586
10627
  if (width > maxDataWidth) maxDataWidth = width;
10587
10628
  }
10588
10629
  return Math.max(MIN_WIDTH, headerWidth, maxDataWidth);
@@ -11374,14 +11415,21 @@ function compileChart(spec, options) {
11374
11415
  }
11375
11416
  };
11376
11417
  }
11377
- if (bp.labels) {
11378
- chartSpec = {
11379
- ...chartSpec,
11380
- labels: {
11381
- ...chartSpec.labels,
11382
- ...bp.labels
11383
- }
11384
- };
11418
+ if (bp.labels !== void 0) {
11419
+ if (typeof bp.labels === "boolean") {
11420
+ chartSpec = {
11421
+ ...chartSpec,
11422
+ labels: bp.labels ? { density: "auto", format: "", prefix: "" } : { density: "none", format: "", prefix: "" }
11423
+ };
11424
+ } else {
11425
+ chartSpec = {
11426
+ ...chartSpec,
11427
+ labels: {
11428
+ ...chartSpec.labels,
11429
+ ...bp.labels
11430
+ }
11431
+ };
11432
+ }
11385
11433
  }
11386
11434
  if (bp.legend) {
11387
11435
  chartSpec = {
@@ -11578,7 +11626,7 @@ function estimateYAxisLabelWidth(data, encoding, baseFontSize) {
11578
11626
  let maxWidth = 0;
11579
11627
  for (const row of data) {
11580
11628
  const label = String(row[yField] ?? "");
11581
- const w = estimateTextWidth13(label, baseFontSize, 400);
11629
+ const w = estimateTextWidth14(label, baseFontSize, 400);
11582
11630
  if (w > maxWidth) maxWidth = w;
11583
11631
  }
11584
11632
  return maxWidth > 0 ? maxWidth + 10 : 40;
@@ -11607,7 +11655,7 @@ function estimateYAxisLabelWidth(data, encoding, baseFontSize) {
11607
11655
  }
11608
11656
  const hasNeg = data.some((r) => Number(r[yField]) < 0);
11609
11657
  const labelEst = (hasNeg ? "-" : "") + sampleLabel;
11610
- return estimateTextWidth13(labelEst, baseFontSize, 400) + 10;
11658
+ return estimateTextWidth14(labelEst, baseFontSize, 400) + 10;
11611
11659
  }
11612
11660
  function compileLayerIndependent(leaves, layerSpec, options) {
11613
11661
  if (leaves.length > 2) {