@opendata-ai/openchart-engine 6.24.0 → 6.24.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 +44 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/legend.test.ts +11 -0
- package/src/annotations/compute.ts +5 -4
- package/src/annotations/resolve-refline.ts +4 -2
- package/src/charts/bar/labels.ts +26 -9
- package/src/charts/line/__tests__/compute.test.ts +28 -0
- package/src/charts/line/area.ts +12 -2
- package/src/compiler/normalize.ts +2 -0
- package/src/layout/dimensions.ts +7 -2
- package/src/legend/compute.ts +31 -17
package/dist/index.js
CHANGED
|
@@ -467,7 +467,9 @@ function resolveRefLineAnnotation(annotation, scales, chartArea, isDark) {
|
|
|
467
467
|
return null;
|
|
468
468
|
}
|
|
469
469
|
let strokeDasharray;
|
|
470
|
-
if (annotation.
|
|
470
|
+
if (annotation.strokeDash && annotation.strokeDash.length > 0) {
|
|
471
|
+
strokeDasharray = annotation.strokeDash.join(" ");
|
|
472
|
+
} else if (annotation.style === "dashed" || annotation.style === void 0) {
|
|
471
473
|
strokeDasharray = DEFAULT_REFLINE_DASH;
|
|
472
474
|
} else if (annotation.style === "dotted") {
|
|
473
475
|
strokeDasharray = "2 2";
|
|
@@ -549,11 +551,12 @@ function resolveRefLineAnnotation(annotation, scales, chartArea, isDark) {
|
|
|
549
551
|
|
|
550
552
|
// src/annotations/compute.ts
|
|
551
553
|
function computeAnnotations(spec, scales, chartArea, strategy, isDark = false, obstacles = [], svgDimensions) {
|
|
552
|
-
|
|
553
|
-
return [];
|
|
554
|
-
}
|
|
554
|
+
const isCompact = strategy.annotationPosition === "tooltip-only";
|
|
555
555
|
const annotations = [];
|
|
556
556
|
for (const annotation of spec.annotations) {
|
|
557
|
+
if (isCompact && annotation.responsive !== false) {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
557
560
|
let resolved = null;
|
|
558
561
|
switch (annotation.type) {
|
|
559
562
|
case "text":
|
|
@@ -979,6 +982,7 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
979
982
|
import {
|
|
980
983
|
buildD3Formatter,
|
|
981
984
|
estimateTextWidth as estimateTextWidth2,
|
|
985
|
+
findAccessibleColor,
|
|
982
986
|
getRepresentativeColor,
|
|
983
987
|
resolveCollisions
|
|
984
988
|
} from "@opendata-ai/openchart-core";
|
|
@@ -1047,21 +1051,35 @@ function computeBarLabels(marks, _chartArea, density = "auto", labelFormat, labe
|
|
|
1047
1051
|
const textHeight = LABEL_FONT_SIZE * 1.2;
|
|
1048
1052
|
const isStacked = mark.cornerRadius === 0;
|
|
1049
1053
|
const isInside = mark.width >= MIN_WIDTH_FOR_INSIDE_LABEL;
|
|
1054
|
+
const isNegative = Number.isFinite(rawNum) ? rawNum < 0 : false;
|
|
1055
|
+
const bgColor = getRepresentativeColor(mark.fill);
|
|
1050
1056
|
let anchorX;
|
|
1051
1057
|
let fill;
|
|
1052
1058
|
let textAnchor;
|
|
1053
1059
|
if (isStacked && isInside) {
|
|
1054
1060
|
anchorX = mark.x + mark.width / 2;
|
|
1055
|
-
fill = "#ffffff";
|
|
1061
|
+
fill = findAccessibleColor("#ffffff", bgColor, 4.5);
|
|
1056
1062
|
textAnchor = "middle";
|
|
1057
1063
|
} else if (isInside) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1064
|
+
if (isNegative) {
|
|
1065
|
+
anchorX = mark.x + LABEL_PADDING;
|
|
1066
|
+
fill = findAccessibleColor("#ffffff", bgColor, 4.5);
|
|
1067
|
+
textAnchor = "start";
|
|
1068
|
+
} else {
|
|
1069
|
+
anchorX = mark.x + mark.width - LABEL_PADDING;
|
|
1070
|
+
fill = findAccessibleColor("#ffffff", bgColor, 4.5);
|
|
1071
|
+
textAnchor = "end";
|
|
1072
|
+
}
|
|
1061
1073
|
} else {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1074
|
+
if (isNegative) {
|
|
1075
|
+
anchorX = mark.x - LABEL_PADDING;
|
|
1076
|
+
fill = getRepresentativeColor(mark.fill);
|
|
1077
|
+
textAnchor = "end";
|
|
1078
|
+
} else {
|
|
1079
|
+
anchorX = mark.x + mark.width + LABEL_PADDING;
|
|
1080
|
+
fill = getRepresentativeColor(mark.fill);
|
|
1081
|
+
textAnchor = "start";
|
|
1082
|
+
}
|
|
1065
1083
|
}
|
|
1066
1084
|
const anchorY = mark.y + mark.height / 2;
|
|
1067
1085
|
const fits = !(isStacked && textWidth > mark.width - 2 * LABEL_PADDING);
|
|
@@ -2662,14 +2680,16 @@ function computeSingleArea(spec, scales, _chartArea) {
|
|
|
2662
2680
|
const color2 = getColor(scales, seriesKey);
|
|
2663
2681
|
const sortedRows = xChannel.type === "nominal" || xChannel.type === "ordinal" ? rows : sortByField(rows, xChannel.field);
|
|
2664
2682
|
const validPoints = [];
|
|
2683
|
+
const y2Channel = encoding.y2;
|
|
2665
2684
|
for (const row of sortedRows) {
|
|
2666
2685
|
const xVal = scaleValue(scales.x.scale, scales.x.type, row[xChannel.field]);
|
|
2667
2686
|
const yVal = scaleValue(scales.y.scale, scales.y.type, row[yChannel.field]);
|
|
2668
2687
|
if (xVal === null || yVal === null) continue;
|
|
2688
|
+
const yBottomVal = y2Channel && row[y2Channel.field] != null ? scaleValue(scales.y.scale, scales.y.type, row[y2Channel.field]) : null;
|
|
2669
2689
|
validPoints.push({
|
|
2670
2690
|
x: xVal,
|
|
2671
2691
|
yTop: yVal,
|
|
2672
|
-
yBottom: baselineY,
|
|
2692
|
+
yBottom: yBottomVal ?? baselineY,
|
|
2673
2693
|
row
|
|
2674
2694
|
});
|
|
2675
2695
|
}
|
|
@@ -2683,6 +2703,7 @@ function computeSingleArea(spec, scales, _chartArea) {
|
|
|
2683
2703
|
const bottomPoints = validPoints.map((p) => ({ x: p.x, y: p.yBottom }));
|
|
2684
2704
|
const ariaLabel = seriesKey === "__default__" ? `Area with ${validPoints.length} data points` : `${seriesKey}: area with ${validPoints.length} data points`;
|
|
2685
2705
|
const aria = { label: ariaLabel };
|
|
2706
|
+
const fillOpacity = y2Channel ? 0.25 : DEFAULT_FILL_OPACITY;
|
|
2686
2707
|
marks.push({
|
|
2687
2708
|
type: "area",
|
|
2688
2709
|
topPoints,
|
|
@@ -2690,7 +2711,7 @@ function computeSingleArea(spec, scales, _chartArea) {
|
|
|
2690
2711
|
path: pathStr,
|
|
2691
2712
|
topPath: topPathStr,
|
|
2692
2713
|
fill: color2,
|
|
2693
|
-
fillOpacity
|
|
2714
|
+
fillOpacity,
|
|
2694
2715
|
stroke: getRepresentativeColor4(color2),
|
|
2695
2716
|
strokeWidth: 2,
|
|
2696
2717
|
seriesKey: seriesKey === "__default__" ? void 0 : seriesKey,
|
|
@@ -6607,8 +6628,10 @@ function normalizeAnnotations(annotations) {
|
|
|
6607
6628
|
fill: ann.fill ?? "#000000"
|
|
6608
6629
|
};
|
|
6609
6630
|
case "refline":
|
|
6631
|
+
case "rule":
|
|
6610
6632
|
return {
|
|
6611
6633
|
...ann,
|
|
6634
|
+
type: "refline",
|
|
6612
6635
|
style: ann.style ?? "dashed",
|
|
6613
6636
|
strokeWidth: ann.strokeWidth ?? 1,
|
|
6614
6637
|
stroke: ann.stroke ?? "#666666",
|
|
@@ -8342,7 +8365,8 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
|
|
|
8342
8365
|
left: padding + (isRadial ? padding : axisMargin)
|
|
8343
8366
|
};
|
|
8344
8367
|
const labelDensity = spec.labels.density;
|
|
8345
|
-
|
|
8368
|
+
const labelsHiddenByStrategy = strategy?.labelMode === "none";
|
|
8369
|
+
if ((spec.markType === "line" || spec.markType === "area") && labelDensity !== "none" && !labelsHiddenByStrategy) {
|
|
8346
8370
|
const colorEnc = encoding.color;
|
|
8347
8371
|
const colorField = colorEnc && "field" in colorEnc ? colorEnc.field : void 0;
|
|
8348
8372
|
if (colorField) {
|
|
@@ -8979,6 +9003,7 @@ function extractColorEntries(spec, theme) {
|
|
|
8979
9003
|
...explicitDomain.filter((v) => dataValues.includes(v)),
|
|
8980
9004
|
...dataValues.filter((v) => !explicitDomain.includes(v))
|
|
8981
9005
|
] : dataValues;
|
|
9006
|
+
const excludeSet = new Set(spec.legend?.exclude ?? []);
|
|
8982
9007
|
return uniqueValues.map((value2, i) => {
|
|
8983
9008
|
let colorIndex = i;
|
|
8984
9009
|
if (explicitDomain && explicitRange) {
|
|
@@ -8991,7 +9016,7 @@ function extractColorEntries(spec, theme) {
|
|
|
8991
9016
|
shape,
|
|
8992
9017
|
active: true
|
|
8993
9018
|
};
|
|
8994
|
-
});
|
|
9019
|
+
}).filter((entry) => !excludeSet.has(entry.label));
|
|
8995
9020
|
}
|
|
8996
9021
|
function truncateEntries(entries, maxCount) {
|
|
8997
9022
|
if (maxCount >= entries.length || maxCount <= 0) return entries;
|
|
@@ -9029,7 +9054,10 @@ function computeLegend(spec, strategy, theme, chartArea, watermark = true) {
|
|
|
9029
9054
|
const hasLabels = spec.labels.density !== "none";
|
|
9030
9055
|
const labelsWillRender = strategy.labelMode !== "none";
|
|
9031
9056
|
const hasColorEncoding = spec.encoding.color != null;
|
|
9032
|
-
const
|
|
9057
|
+
const userConfiguredLegend = spec.legend != null && Object.keys(spec.legend).some(
|
|
9058
|
+
(k) => k !== "show" || spec.legend[k] !== false
|
|
9059
|
+
);
|
|
9060
|
+
const legendNotForced = !userConfiguredLegend;
|
|
9033
9061
|
if (isLineOrArea && hasLabels && labelsWillRender && hasColorEncoding && legendNotForced) {
|
|
9034
9062
|
const isArea = spec.markType === "area";
|
|
9035
9063
|
const quantChannel = spec.encoding.y?.type === "quantitative" ? spec.encoding.y : spec.encoding.x;
|