@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 +89 -41
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/axes.test.ts +75 -0
- package/src/compile.ts +17 -8
- package/src/compiler/normalize.ts +17 -6
- package/src/layout/axes/ticks.ts +64 -9
- package/src/layout/axes.ts +13 -2
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
|
|
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
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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) =>
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
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 =
|
|
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
|
|
11658
|
+
return estimateTextWidth14(labelEst, baseFontSize, 400) + 10;
|
|
11611
11659
|
}
|
|
11612
11660
|
function compileLayerIndependent(leaves, layerSpec, options) {
|
|
11613
11661
|
if (leaves.length > 2) {
|