@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 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 baseDx = 4;
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: x + labelDelta.dx,
219
+ x: baseX + labelDelta.dx,
211
220
  y: y + labelDelta.dy,
212
- style: makeAnnotationLabelStyle(11, 500, void 0, isDark),
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 valuePart = lastColon >= 0 ? ariaLabel.slice(lastColon + 1).trim() : "";
595
- if (!valuePart) continue;
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) return formatNumber3(num);
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
- if (spec.type === "line" || spec.type === "area") {
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 d3Format } from "d3-format";
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 = d3Format(column.format);
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 d3Format(column.format)(value);
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 d3Format2 } from "d3-format";
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 d3Format2(format)(value);
4102
+ return d3Format4(format)(value);
4057
4103
  } catch {
4058
4104
  return formatNumber5(value);
4059
4105
  }