@opendata-ai/openchart-engine 7.0.4 → 7.1.1
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 +57 -13
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/compile-snapshot.test.ts.snap +42 -38
- package/src/__tests__/compile-chart.test.ts +4 -1
- package/src/__tests__/dimensions.test.ts +6 -1
- package/src/charts/column/labels.ts +7 -4
- package/src/charts/line/__tests__/compute.test.ts +20 -1
- package/src/charts/line/area.ts +12 -2
- package/src/charts/line/index.ts +2 -2
- package/src/endpoint-labels/__tests__/compute.test.ts +5 -1
- package/src/endpoint-labels/compute.ts +4 -1
- package/src/layout/axes/thinning.ts +4 -2
- package/src/layout/dimensions.ts +58 -4
package/dist/index.js
CHANGED
|
@@ -1259,8 +1259,9 @@ function computeEndpointLabels(spec, marks, theme, chartArea, strategy) {
|
|
|
1259
1259
|
};
|
|
1260
1260
|
if (showMarker) {
|
|
1261
1261
|
entry.marker = {
|
|
1262
|
-
x: p.dataX,
|
|
1262
|
+
x: p.dataX + markerRadius,
|
|
1263
1263
|
y: p.dataY,
|
|
1264
|
+
dataX: p.dataX,
|
|
1264
1265
|
fill: markerFill,
|
|
1265
1266
|
stroke: config?.markerStyle?.stroke ?? p.color,
|
|
1266
1267
|
strokeWidth: markerStrokeWidth,
|
|
@@ -2211,7 +2212,7 @@ import {
|
|
|
2211
2212
|
} from "@opendata-ai/openchart-core";
|
|
2212
2213
|
var LABEL_FONT_SIZE2 = 10;
|
|
2213
2214
|
var LABEL_FONT_WEIGHT2 = 600;
|
|
2214
|
-
var LABEL_OFFSET_Y =
|
|
2215
|
+
var LABEL_OFFSET_Y = 8;
|
|
2215
2216
|
function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix, valueField, labelColor) {
|
|
2216
2217
|
const targetMarks = filterByDensity(marks, density);
|
|
2217
2218
|
const formatter = buildD3Formatter2(labelFormat);
|
|
@@ -2257,7 +2258,7 @@ function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, l
|
|
|
2257
2258
|
fill: labelColor ?? getRepresentativeColor2(mark.fill),
|
|
2258
2259
|
lineHeight: 1.2,
|
|
2259
2260
|
textAnchor: "middle",
|
|
2260
|
-
dominantBaseline:
|
|
2261
|
+
dominantBaseline: "hanging"
|
|
2261
2262
|
}
|
|
2262
2263
|
});
|
|
2263
2264
|
}
|
|
@@ -3476,6 +3477,10 @@ var STACKED_GRADIENT_STOPS = [
|
|
|
3476
3477
|
{ offset: 0, opacity: 0.65 },
|
|
3477
3478
|
{ offset: 1, opacity: 0.35 }
|
|
3478
3479
|
];
|
|
3480
|
+
var STACKED_GRADIENT_STOPS_LIGHT = [
|
|
3481
|
+
{ offset: 0, opacity: 0.65 },
|
|
3482
|
+
{ offset: 1, opacity: 0 }
|
|
3483
|
+
];
|
|
3479
3484
|
function buildGradientFill(colorStr, stops) {
|
|
3480
3485
|
return {
|
|
3481
3486
|
gradient: "linear",
|
|
@@ -3568,7 +3573,7 @@ function computeSingleArea(spec, scales, _chartArea) {
|
|
|
3568
3573
|
}
|
|
3569
3574
|
return marks;
|
|
3570
3575
|
}
|
|
3571
|
-
function computeStackedArea(spec, scales, chartArea) {
|
|
3576
|
+
function computeStackedArea(spec, scales, chartArea, darkMode) {
|
|
3572
3577
|
const encoding = spec.encoding;
|
|
3573
3578
|
const xChannel = encoding.x;
|
|
3574
3579
|
const yChannel = encoding.y;
|
|
@@ -3646,7 +3651,8 @@ function computeStackedArea(spec, scales, chartArea) {
|
|
|
3646
3651
|
fillOpacity = isGradientDef3(markFill) ? 1 : spec.markDef.opacity ?? 0.7;
|
|
3647
3652
|
} else {
|
|
3648
3653
|
const colorStr = getRepresentativeColor4(color2);
|
|
3649
|
-
|
|
3654
|
+
const stackedStops = darkMode ? STACKED_GRADIENT_STOPS : STACKED_GRADIENT_STOPS_LIGHT;
|
|
3655
|
+
fillValue = buildGradientFill(colorStr, stackedStops);
|
|
3650
3656
|
fillOpacity = 1;
|
|
3651
3657
|
}
|
|
3652
3658
|
marks.push({
|
|
@@ -3674,11 +3680,11 @@ function computeStackedArea(spec, scales, chartArea) {
|
|
|
3674
3680
|
}
|
|
3675
3681
|
return marks;
|
|
3676
3682
|
}
|
|
3677
|
-
function computeAreaMarks(spec, scales, chartArea) {
|
|
3683
|
+
function computeAreaMarks(spec, scales, chartArea, darkMode) {
|
|
3678
3684
|
const encoding = spec.encoding;
|
|
3679
3685
|
const yChannel = encoding.y;
|
|
3680
3686
|
if (yChannel && isStacked(yChannel.stack)) {
|
|
3681
|
-
return computeStackedArea(spec, scales, chartArea);
|
|
3687
|
+
return computeStackedArea(spec, scales, chartArea, darkMode);
|
|
3682
3688
|
}
|
|
3683
3689
|
return computeSingleArea(spec, scales, chartArea);
|
|
3684
3690
|
}
|
|
@@ -3937,8 +3943,8 @@ var lineRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
|
3937
3943
|
}
|
|
3938
3944
|
return marks;
|
|
3939
3945
|
};
|
|
3940
|
-
var areaRenderer = (spec, scales, chartArea, strategy,
|
|
3941
|
-
const areas = computeAreaMarks(spec, scales, chartArea);
|
|
3946
|
+
var areaRenderer = (spec, scales, chartArea, strategy, theme) => {
|
|
3947
|
+
const areas = computeAreaMarks(spec, scales, chartArea, theme.isDark);
|
|
3942
3948
|
const encoding = spec.encoding;
|
|
3943
3949
|
const hasColor = !!(encoding.color && "field" in encoding.color);
|
|
3944
3950
|
const lines = hasColor ? linesFromAreas(areas) : computeLineMarks(spec, scales, chartArea, strategy);
|
|
@@ -9656,7 +9662,7 @@ var DEFAULT_COLLISION_PADDING = 5;
|
|
|
9656
9662
|
|
|
9657
9663
|
// src/layout/axes/thinning.ts
|
|
9658
9664
|
import { estimateTextWidth as estimateTextWidth11 } from "@opendata-ai/openchart-core";
|
|
9659
|
-
var MIN_TICK_GAP_FACTOR =
|
|
9665
|
+
var MIN_TICK_GAP_FACTOR = 0.5;
|
|
9660
9666
|
var MIN_TICK_COUNT = 2;
|
|
9661
9667
|
function measureLabel(text, fontSize, fontWeight, measureText) {
|
|
9662
9668
|
return measureText ? measureText(text, fontSize, fontWeight).width : estimateTextWidth11(text, fontSize, fontWeight);
|
|
@@ -10158,6 +10164,7 @@ import {
|
|
|
10158
10164
|
MAX_LEFT_LABEL_FRACTION_MEDIUM,
|
|
10159
10165
|
MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX,
|
|
10160
10166
|
NARROW_VIEWPORT_MAX,
|
|
10167
|
+
TICK_LABEL_OFFSET as TICK_LABEL_OFFSET2,
|
|
10161
10168
|
TOP_PAD_EXTRA_NARROW
|
|
10162
10169
|
} from "@opendata-ai/openchart-core";
|
|
10163
10170
|
|
|
@@ -10315,6 +10322,9 @@ function chromeToInput(chrome) {
|
|
|
10315
10322
|
brand: chrome.brand
|
|
10316
10323
|
};
|
|
10317
10324
|
}
|
|
10325
|
+
function bottomMargin(bottomHeight, padding, xAxisHeight) {
|
|
10326
|
+
return padding + bottomHeight + xAxisHeight;
|
|
10327
|
+
}
|
|
10318
10328
|
function scalePadding(basePadding, width, height) {
|
|
10319
10329
|
const minDim = Math.min(width, height);
|
|
10320
10330
|
if (minDim >= 500) return basePadding;
|
|
@@ -10435,7 +10445,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
|
|
|
10435
10445
|
const margins = {
|
|
10436
10446
|
top: topPad + chrome.topHeight + tentativeMetricsHeight,
|
|
10437
10447
|
right: hPad + (isRadial ? hPad : axisMargin),
|
|
10438
|
-
bottom:
|
|
10448
|
+
bottom: bottomMargin(chrome.bottomHeight, padding, xAxisHeight),
|
|
10439
10449
|
left: hPad + (isRadial ? hPad : axisMargin)
|
|
10440
10450
|
};
|
|
10441
10451
|
const labelDensity = spec.labels.density;
|
|
@@ -10572,7 +10582,41 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
|
|
|
10572
10582
|
}
|
|
10573
10583
|
const yAxis = encoding.y?.axis;
|
|
10574
10584
|
if (yAxis && (yAxis.title || yAxis.label) && !isRadial) {
|
|
10575
|
-
const
|
|
10585
|
+
const yFieldForTitle = encoding.y?.field;
|
|
10586
|
+
const yAxisFormatForTitle = yAxis?.format;
|
|
10587
|
+
let estTickLabelWidth = 0;
|
|
10588
|
+
if (yFieldForTitle && (encoding.y?.type === "quantitative" || encoding.y?.type === "temporal")) {
|
|
10589
|
+
let maxAbsValForTitle = 0;
|
|
10590
|
+
for (const row of spec.data) {
|
|
10591
|
+
const v = Number(row[yFieldForTitle]);
|
|
10592
|
+
if (Number.isFinite(v) && Math.abs(v) > maxAbsValForTitle) maxAbsValForTitle = Math.abs(v);
|
|
10593
|
+
}
|
|
10594
|
+
let sampleLabelForTitle;
|
|
10595
|
+
if (yAxisFormatForTitle) {
|
|
10596
|
+
try {
|
|
10597
|
+
const fmt = format(yAxisFormatForTitle);
|
|
10598
|
+
sampleLabelForTitle = fmt(maxAbsValForTitle);
|
|
10599
|
+
} catch {
|
|
10600
|
+
sampleLabelForTitle = String(maxAbsValForTitle);
|
|
10601
|
+
}
|
|
10602
|
+
} else {
|
|
10603
|
+
if (maxAbsValForTitle >= 1e9) sampleLabelForTitle = "1.5B";
|
|
10604
|
+
else if (maxAbsValForTitle >= 1e6) sampleLabelForTitle = "1.5M";
|
|
10605
|
+
else if (maxAbsValForTitle >= 1e3) sampleLabelForTitle = "1.5K";
|
|
10606
|
+
else if (maxAbsValForTitle >= 100) sampleLabelForTitle = "100";
|
|
10607
|
+
else if (maxAbsValForTitle >= 10) sampleLabelForTitle = "10";
|
|
10608
|
+
else sampleLabelForTitle = "0.0";
|
|
10609
|
+
}
|
|
10610
|
+
const negPrefixForTitle = spec.data.some((r) => Number(r[yFieldForTitle]) < 0) ? "-" : "";
|
|
10611
|
+
estTickLabelWidth = estimateTextWidth16(
|
|
10612
|
+
negPrefixForTitle + sampleLabelForTitle,
|
|
10613
|
+
theme.fonts.sizes.axisTick,
|
|
10614
|
+
theme.fonts.weights.normal
|
|
10615
|
+
);
|
|
10616
|
+
}
|
|
10617
|
+
const AXIS_TITLE_GAP = 8;
|
|
10618
|
+
const dynamicTitleOffset = TICK_LABEL_OFFSET2 + estTickLabelWidth + AXIS_TITLE_GAP;
|
|
10619
|
+
const axisTitleOffset = Math.max(dynamicTitleOffset, getAxisTitleOffset2(width));
|
|
10576
10620
|
const halfGlyph = Math.ceil(theme.fonts.sizes.body / 2);
|
|
10577
10621
|
const rotatedLabelMargin = axisTitleOffset + halfGlyph + (width < BREAKPOINT_COMPACT_MAX2 ? 0 : AXIS_TITLE_TRAILING_PAD2);
|
|
10578
10622
|
margins.left = Math.max(margins.left, hPad + rotatedLabelMargin);
|
|
@@ -10611,7 +10655,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
|
|
|
10611
10655
|
const fallbackTopAxisGap = isRadial && fallbackChrome.topHeight === 0 ? 0 : axisMargin + inlineTickOverhang;
|
|
10612
10656
|
const newTop = topPad + fallbackChrome.topHeight + tentativeMetricsHeight;
|
|
10613
10657
|
const topDelta = margins.top - newTop;
|
|
10614
|
-
const newBottom =
|
|
10658
|
+
const newBottom = bottomMargin(fallbackChrome.bottomHeight, padding, xAxisHeight);
|
|
10615
10659
|
const bottomDelta = margins.bottom - newBottom;
|
|
10616
10660
|
if (topDelta > 0 || bottomDelta > 0) {
|
|
10617
10661
|
const gap = legendGap(width);
|