@opendata-ai/openchart-engine 2.5.0 → 2.7.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 +57 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/annotations/compute.ts +24 -5
- package/src/charts/bar/__tests__/compute.test.ts +51 -0
- package/src/charts/bar/index.ts +1 -1
- package/src/charts/bar/labels.ts +17 -3
- package/src/charts/column/__tests__/compute.test.ts +76 -0
- package/src/charts/column/index.ts +1 -1
- package/src/charts/column/labels.ts +18 -4
- package/src/layout/axes.ts +10 -2
- package/src/layout/dimensions.ts +4 -2
package/dist/index.js
CHANGED
|
@@ -202,14 +202,23 @@ function resolveRangeAnnotation(annotation, scales, chartArea, isDark) {
|
|
|
202
202
|
const rect = { x, y, width, height };
|
|
203
203
|
let label;
|
|
204
204
|
if (annotation.label) {
|
|
205
|
-
const
|
|
205
|
+
const anchor = annotation.labelAnchor ?? "left";
|
|
206
|
+
const centered = anchor === "top" || anchor === "bottom" || anchor === "auto";
|
|
207
|
+
const baseDx = centered ? 0 : anchor === "right" ? -4 : 4;
|
|
206
208
|
const baseDy = 14;
|
|
207
209
|
const labelDelta = applyOffset({ dx: baseDx, dy: baseDy }, annotation.labelOffset);
|
|
210
|
+
const style = makeAnnotationLabelStyle(11, 500, void 0, isDark);
|
|
211
|
+
if (centered) {
|
|
212
|
+
style.textAnchor = "middle";
|
|
213
|
+
} else if (anchor === "right") {
|
|
214
|
+
style.textAnchor = "end";
|
|
215
|
+
}
|
|
216
|
+
const baseX = centered ? x + width / 2 : anchor === "right" ? x + width : x;
|
|
208
217
|
label = {
|
|
209
218
|
text: annotation.label,
|
|
210
|
-
x:
|
|
219
|
+
x: baseX + labelDelta.dx,
|
|
211
220
|
y: y + labelDelta.dy,
|
|
212
|
-
style
|
|
221
|
+
style,
|
|
213
222
|
visible: true
|
|
214
223
|
};
|
|
215
224
|
}
|
|
@@ -249,8 +258,9 @@ function resolveRefLineAnnotation(annotation, scales, chartArea, isDark) {
|
|
|
249
258
|
let label;
|
|
250
259
|
if (annotation.label) {
|
|
251
260
|
const isHorizontal = annotation.y !== void 0;
|
|
261
|
+
const anchor = annotation.labelAnchor ?? "top";
|
|
252
262
|
const baseDx = isHorizontal ? -4 : 4;
|
|
253
|
-
const baseDy = -4;
|
|
263
|
+
const baseDy = anchor === "bottom" ? 14 : -4;
|
|
254
264
|
const labelDelta = applyOffset({ dx: baseDx, dy: baseDy }, annotation.labelOffset);
|
|
255
265
|
const defaultStroke2 = isDark ? DARK_REFLINE_STROKE : LIGHT_REFLINE_STROKE;
|
|
256
266
|
const style = makeAnnotationLabelStyle(11, 400, annotation.stroke ?? defaultStroke2, isDark);
|
|
@@ -579,20 +589,30 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
579
589
|
}
|
|
580
590
|
|
|
581
591
|
// src/charts/bar/labels.ts
|
|
582
|
-
import {
|
|
592
|
+
import {
|
|
593
|
+
buildD3Formatter,
|
|
594
|
+
estimateTextWidth as estimateTextWidth2,
|
|
595
|
+
resolveCollisions
|
|
596
|
+
} from "@opendata-ai/openchart-core";
|
|
583
597
|
var LABEL_FONT_SIZE = 11;
|
|
584
598
|
var LABEL_FONT_WEIGHT = 600;
|
|
585
599
|
var LABEL_PADDING = 6;
|
|
586
600
|
var MIN_WIDTH_FOR_INSIDE_LABEL = 40;
|
|
587
|
-
function computeBarLabels(marks, _chartArea, density = "auto") {
|
|
601
|
+
function computeBarLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
588
602
|
if (density === "none") return [];
|
|
589
603
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
590
604
|
const candidates = [];
|
|
605
|
+
const formatter = buildD3Formatter(labelFormat);
|
|
591
606
|
for (const mark of targetMarks) {
|
|
592
607
|
const ariaLabel = mark.aria.label;
|
|
593
608
|
const lastColon = ariaLabel.lastIndexOf(":");
|
|
594
|
-
const
|
|
595
|
-
if (!
|
|
609
|
+
const rawValue = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
610
|
+
if (!rawValue) continue;
|
|
611
|
+
let valuePart = rawValue;
|
|
612
|
+
if (formatter) {
|
|
613
|
+
const num = Number(rawValue.replace(/[^0-9.-]/g, ""));
|
|
614
|
+
if (!Number.isNaN(num)) valuePart = formatter(num);
|
|
615
|
+
}
|
|
596
616
|
const textWidth = estimateTextWidth2(valuePart, LABEL_FONT_SIZE, LABEL_FONT_WEIGHT);
|
|
597
617
|
const textHeight = LABEL_FONT_SIZE * 1.2;
|
|
598
618
|
const isStacked = mark.cornerRadius === 0;
|
|
@@ -648,7 +668,7 @@ function computeBarLabels(marks, _chartArea, density = "auto") {
|
|
|
648
668
|
// src/charts/bar/index.ts
|
|
649
669
|
var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
650
670
|
const marks = computeBarMarks(spec, scales, chartArea, strategy);
|
|
651
|
-
const labels = computeBarLabels(marks, chartArea, spec.labels.density);
|
|
671
|
+
const labels = computeBarLabels(marks, chartArea, spec.labels.density, spec.labels.format);
|
|
652
672
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
653
673
|
marks[i].label = labels[i];
|
|
654
674
|
}
|
|
@@ -813,24 +833,34 @@ function computeStackedColumns(data, categoryField, valueField, colorField, xSca
|
|
|
813
833
|
}
|
|
814
834
|
|
|
815
835
|
// src/charts/column/labels.ts
|
|
816
|
-
import {
|
|
836
|
+
import {
|
|
837
|
+
buildD3Formatter as buildD3Formatter2,
|
|
838
|
+
estimateTextWidth as estimateTextWidth3,
|
|
839
|
+
resolveCollisions as resolveCollisions2
|
|
840
|
+
} from "@opendata-ai/openchart-core";
|
|
817
841
|
var LABEL_FONT_SIZE2 = 10;
|
|
818
842
|
var LABEL_FONT_WEIGHT2 = 600;
|
|
819
843
|
var LABEL_OFFSET_Y = 6;
|
|
820
|
-
function computeColumnLabels(marks, _chartArea, density = "auto") {
|
|
844
|
+
function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
821
845
|
if (density === "none") return [];
|
|
822
846
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
847
|
+
const formatter = buildD3Formatter2(labelFormat);
|
|
823
848
|
const candidates = [];
|
|
824
849
|
for (const mark of targetMarks) {
|
|
825
850
|
const ariaLabel = mark.aria.label;
|
|
826
851
|
const lastColon = ariaLabel.lastIndexOf(":");
|
|
827
|
-
const
|
|
828
|
-
if (!
|
|
852
|
+
const rawValue = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
853
|
+
if (!rawValue) continue;
|
|
854
|
+
let valuePart = rawValue;
|
|
855
|
+
if (formatter) {
|
|
856
|
+
const num = Number(rawValue.replace(/[^0-9.-]/g, ""));
|
|
857
|
+
if (!Number.isNaN(num)) valuePart = formatter(num);
|
|
858
|
+
}
|
|
829
859
|
const numericValue = parseFloat(valuePart);
|
|
830
860
|
const isNegative = Number.isFinite(numericValue) && numericValue < 0;
|
|
831
861
|
const textWidth = estimateTextWidth3(valuePart, LABEL_FONT_SIZE2, LABEL_FONT_WEIGHT2);
|
|
832
862
|
const textHeight = LABEL_FONT_SIZE2 * 1.2;
|
|
833
|
-
const anchorX = mark.x + mark.width / 2
|
|
863
|
+
const anchorX = mark.x + mark.width / 2;
|
|
834
864
|
const anchorY = isNegative ? mark.y + mark.height + LABEL_OFFSET_Y : mark.y - LABEL_OFFSET_Y - textHeight;
|
|
835
865
|
candidates.push({
|
|
836
866
|
text: valuePart,
|
|
@@ -866,7 +896,7 @@ function computeColumnLabels(marks, _chartArea, density = "auto") {
|
|
|
866
896
|
// src/charts/column/index.ts
|
|
867
897
|
var columnRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
868
898
|
const marks = computeColumnMarks(spec, scales, chartArea, strategy);
|
|
869
|
-
const labels = computeColumnLabels(marks, chartArea, spec.labels.density);
|
|
899
|
+
const labels = computeColumnLabels(marks, chartArea, spec.labels.density, spec.labels.format);
|
|
870
900
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
871
901
|
marks[i].label = labels[i];
|
|
872
902
|
}
|
|
@@ -2804,7 +2834,12 @@ function compileGraph(spec, options) {
|
|
|
2804
2834
|
var DEFAULT_COLLISION_PADDING = 5;
|
|
2805
2835
|
|
|
2806
2836
|
// src/layout/axes.ts
|
|
2807
|
-
import {
|
|
2837
|
+
import {
|
|
2838
|
+
abbreviateNumber as abbreviateNumber3,
|
|
2839
|
+
buildD3Formatter as buildD3Formatter3,
|
|
2840
|
+
formatDate,
|
|
2841
|
+
formatNumber as formatNumber3
|
|
2842
|
+
} from "@opendata-ai/openchart-core";
|
|
2808
2843
|
var TICK_COUNTS = {
|
|
2809
2844
|
full: 8,
|
|
2810
2845
|
reduced: 5,
|
|
@@ -2864,7 +2899,10 @@ function formatTickLabel(value, resolvedScale) {
|
|
|
2864
2899
|
}
|
|
2865
2900
|
if (resolvedScale.type === "linear" || resolvedScale.type === "log") {
|
|
2866
2901
|
const num = value;
|
|
2867
|
-
if (formatStr)
|
|
2902
|
+
if (formatStr) {
|
|
2903
|
+
const fmt = buildD3Formatter3(formatStr);
|
|
2904
|
+
if (fmt) return fmt(num);
|
|
2905
|
+
}
|
|
2868
2906
|
if (Math.abs(num) >= 1e3) return abbreviateNumber3(num);
|
|
2869
2907
|
return formatNumber3(num);
|
|
2870
2908
|
}
|
|
@@ -2986,7 +3024,8 @@ function computeDimensions(spec, options, legendLayout, theme) {
|
|
|
2986
3024
|
bottom: padding + chrome.bottomHeight + xAxisHeight,
|
|
2987
3025
|
left: padding + (isRadial ? padding : axisMargin)
|
|
2988
3026
|
};
|
|
2989
|
-
|
|
3027
|
+
const labelDensity = spec.labels.density;
|
|
3028
|
+
if ((spec.type === "line" || spec.type === "area") && labelDensity !== "none") {
|
|
2990
3029
|
const colorField = encoding.color?.field;
|
|
2991
3030
|
if (colorField) {
|
|
2992
3031
|
let maxLabelWidth = 0;
|