@opendata-ai/openchart-engine 2.5.0 → 2.6.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 +61 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/annotations/compute.ts +24 -5
- package/src/charts/bar/index.ts +1 -1
- package/src/charts/bar/labels.ts +33 -2
- package/src/layout/axes.ts +16 -1
- 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);
|
|
@@ -580,19 +590,41 @@ function computeSimpleBars(data, valueField, categoryField, xScale, yScale, band
|
|
|
580
590
|
|
|
581
591
|
// src/charts/bar/labels.ts
|
|
582
592
|
import { estimateTextWidth as estimateTextWidth2, resolveCollisions } from "@opendata-ai/openchart-core";
|
|
593
|
+
import { format as d3Format } from "d3-format";
|
|
583
594
|
var LABEL_FONT_SIZE = 11;
|
|
584
595
|
var LABEL_FONT_WEIGHT = 600;
|
|
585
596
|
var LABEL_PADDING = 6;
|
|
586
597
|
var MIN_WIDTH_FOR_INSIDE_LABEL = 40;
|
|
587
|
-
function computeBarLabels(marks, _chartArea, density = "auto") {
|
|
598
|
+
function computeBarLabels(marks, _chartArea, density = "auto", labelFormat) {
|
|
588
599
|
if (density === "none") return [];
|
|
589
600
|
const targetMarks = density === "endpoints" && marks.length > 1 ? [marks[0], marks[marks.length - 1]] : marks;
|
|
590
601
|
const candidates = [];
|
|
602
|
+
let formatter = null;
|
|
603
|
+
if (labelFormat) {
|
|
604
|
+
try {
|
|
605
|
+
formatter = d3Format(labelFormat);
|
|
606
|
+
} catch {
|
|
607
|
+
const suffixMatch = labelFormat.match(/^(.+[a-z])([^a-z]+)$/i);
|
|
608
|
+
if (suffixMatch) {
|
|
609
|
+
try {
|
|
610
|
+
const d3Fmt = d3Format(suffixMatch[1]);
|
|
611
|
+
const suffix = suffixMatch[2];
|
|
612
|
+
formatter = (v) => d3Fmt(v) + suffix;
|
|
613
|
+
} catch {
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
591
618
|
for (const mark of targetMarks) {
|
|
592
619
|
const ariaLabel = mark.aria.label;
|
|
593
620
|
const lastColon = ariaLabel.lastIndexOf(":");
|
|
594
|
-
const
|
|
595
|
-
if (!
|
|
621
|
+
const rawValue = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
|
|
622
|
+
if (!rawValue) continue;
|
|
623
|
+
let valuePart = rawValue;
|
|
624
|
+
if (formatter) {
|
|
625
|
+
const num = Number(rawValue.replace(/[^0-9.-]/g, ""));
|
|
626
|
+
if (!Number.isNaN(num)) valuePart = formatter(num);
|
|
627
|
+
}
|
|
596
628
|
const textWidth = estimateTextWidth2(valuePart, LABEL_FONT_SIZE, LABEL_FONT_WEIGHT);
|
|
597
629
|
const textHeight = LABEL_FONT_SIZE * 1.2;
|
|
598
630
|
const isStacked = mark.cornerRadius === 0;
|
|
@@ -648,7 +680,7 @@ function computeBarLabels(marks, _chartArea, density = "auto") {
|
|
|
648
680
|
// src/charts/bar/index.ts
|
|
649
681
|
var barRenderer = (spec, scales, chartArea, strategy, _theme) => {
|
|
650
682
|
const marks = computeBarMarks(spec, scales, chartArea, strategy);
|
|
651
|
-
const labels = computeBarLabels(marks, chartArea, spec.labels.density);
|
|
683
|
+
const labels = computeBarLabels(marks, chartArea, spec.labels.density, spec.labels.format);
|
|
652
684
|
for (let i = 0; i < marks.length && i < labels.length; i++) {
|
|
653
685
|
marks[i].label = labels[i];
|
|
654
686
|
}
|
|
@@ -2805,6 +2837,7 @@ var DEFAULT_COLLISION_PADDING = 5;
|
|
|
2805
2837
|
|
|
2806
2838
|
// src/layout/axes.ts
|
|
2807
2839
|
import { abbreviateNumber as abbreviateNumber3, formatDate, formatNumber as formatNumber3 } from "@opendata-ai/openchart-core";
|
|
2840
|
+
import { format as d3Format2 } from "d3-format";
|
|
2808
2841
|
var TICK_COUNTS = {
|
|
2809
2842
|
full: 8,
|
|
2810
2843
|
reduced: 5,
|
|
@@ -2864,7 +2897,19 @@ function formatTickLabel(value, resolvedScale) {
|
|
|
2864
2897
|
}
|
|
2865
2898
|
if (resolvedScale.type === "linear" || resolvedScale.type === "log") {
|
|
2866
2899
|
const num = value;
|
|
2867
|
-
if (formatStr)
|
|
2900
|
+
if (formatStr) {
|
|
2901
|
+
try {
|
|
2902
|
+
return d3Format2(formatStr)(num);
|
|
2903
|
+
} catch {
|
|
2904
|
+
const suffixMatch = formatStr.match(/^(.+[a-z])([^a-z]+)$/i);
|
|
2905
|
+
if (suffixMatch) {
|
|
2906
|
+
try {
|
|
2907
|
+
return d3Format2(suffixMatch[1])(num) + suffixMatch[2];
|
|
2908
|
+
} catch {
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2868
2913
|
if (Math.abs(num) >= 1e3) return abbreviateNumber3(num);
|
|
2869
2914
|
return formatNumber3(num);
|
|
2870
2915
|
}
|
|
@@ -2986,7 +3031,8 @@ function computeDimensions(spec, options, legendLayout, theme) {
|
|
|
2986
3031
|
bottom: padding + chrome.bottomHeight + xAxisHeight,
|
|
2987
3032
|
left: padding + (isRadial ? padding : axisMargin)
|
|
2988
3033
|
};
|
|
2989
|
-
|
|
3034
|
+
const labelDensity = spec.labels.density;
|
|
3035
|
+
if ((spec.type === "line" || spec.type === "area") && labelDensity !== "none") {
|
|
2990
3036
|
const colorField = encoding.color?.field;
|
|
2991
3037
|
if (colorField) {
|
|
2992
3038
|
let maxLabelWidth = 0;
|
|
@@ -3517,7 +3563,7 @@ function computeCategoryColors(data, column, theme, darkMode) {
|
|
|
3517
3563
|
|
|
3518
3564
|
// src/tables/format-cells.ts
|
|
3519
3565
|
import { formatDate as formatDate2, formatNumber as formatNumber4 } from "@opendata-ai/openchart-core";
|
|
3520
|
-
import { format as
|
|
3566
|
+
import { format as d3Format3 } from "d3-format";
|
|
3521
3567
|
function isNumericValue(value) {
|
|
3522
3568
|
if (typeof value === "number") return Number.isFinite(value);
|
|
3523
3569
|
return false;
|
|
@@ -3537,7 +3583,7 @@ function formatCell(value, column) {
|
|
|
3537
3583
|
}
|
|
3538
3584
|
if (column.format && isNumericValue(value)) {
|
|
3539
3585
|
try {
|
|
3540
|
-
const formatter =
|
|
3586
|
+
const formatter = d3Format3(column.format);
|
|
3541
3587
|
return {
|
|
3542
3588
|
value,
|
|
3543
3589
|
formattedValue: formatter(value),
|
|
@@ -3570,7 +3616,7 @@ function formatValueForSearch(value, column) {
|
|
|
3570
3616
|
if (value == null) return "";
|
|
3571
3617
|
if (column.format && isNumericValue(value)) {
|
|
3572
3618
|
try {
|
|
3573
|
-
return
|
|
3619
|
+
return d3Format3(column.format)(value);
|
|
3574
3620
|
} catch {
|
|
3575
3621
|
}
|
|
3576
3622
|
}
|
|
@@ -4044,7 +4090,7 @@ function compileTableLayout(spec, options, theme) {
|
|
|
4044
4090
|
|
|
4045
4091
|
// src/tooltips/compute.ts
|
|
4046
4092
|
import { formatDate as formatDate3, formatNumber as formatNumber5 } from "@opendata-ai/openchart-core";
|
|
4047
|
-
import { format as
|
|
4093
|
+
import { format as d3Format4 } from "d3-format";
|
|
4048
4094
|
function formatValue(value, fieldType, format) {
|
|
4049
4095
|
if (value == null) return "";
|
|
4050
4096
|
if (fieldType === "temporal" || value instanceof Date) {
|
|
@@ -4053,7 +4099,7 @@ function formatValue(value, fieldType, format) {
|
|
|
4053
4099
|
if (typeof value === "number") {
|
|
4054
4100
|
if (format) {
|
|
4055
4101
|
try {
|
|
4056
|
-
return
|
|
4102
|
+
return d3Format4(format)(value);
|
|
4057
4103
|
} catch {
|
|
4058
4104
|
return formatNumber5(value);
|
|
4059
4105
|
}
|