@opendata-ai/openchart-engine 7.2.1 → 7.2.3

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
@@ -766,16 +766,18 @@ function resolveRangeAnnotation(annotation, scales, chartArea, isDark) {
766
766
  let y2 = chartArea.y;
767
767
  let width = chartArea.width;
768
768
  let height = chartArea.height;
769
+ const extend2 = annotation.extendToEdges !== false;
770
+ const resolveEdge = (value2, scale, edge) => extend2 ? resolvePositionEdge(value2, scale, edge) : resolvePosition(value2, scale);
769
771
  if (annotation.x1 !== void 0 && annotation.x2 !== void 0) {
770
- const x1px = resolvePositionEdge(annotation.x1, scales.x, "start");
771
- const x2px = resolvePositionEdge(annotation.x2, scales.x, "end");
772
+ const x1px = resolveEdge(annotation.x1, scales.x, "start");
773
+ const x2px = resolveEdge(annotation.x2, scales.x, "end");
772
774
  if (x1px === null || x2px === null) return null;
773
775
  x2 = Math.min(x1px, x2px);
774
776
  width = Math.abs(x2px - x1px);
775
777
  }
776
778
  if (annotation.y1 !== void 0 && annotation.y2 !== void 0) {
777
- const y1px = resolvePositionEdge(annotation.y1, scales.y, "end");
778
- const y2px = resolvePositionEdge(annotation.y2, scales.y, "start");
779
+ const y1px = resolveEdge(annotation.y1, scales.y, "end");
780
+ const y2px = resolveEdge(annotation.y2, scales.y, "start");
779
781
  if (y1px === null || y2px === null) return null;
780
782
  y2 = Math.min(y1px, y2px);
781
783
  height = Math.abs(y2px - y1px);
@@ -788,7 +790,12 @@ function resolveRangeAnnotation(annotation, scales, chartArea, isDark) {
788
790
  const baseDx = centered ? 0 : anchor === "right" ? -4 : 4;
789
791
  const baseDy = 14;
790
792
  const labelDelta = applyOffset({ dx: baseDx, dy: baseDy }, annotation.labelOffset);
791
- const style = makeAnnotationLabelStyle(11, 500, void 0, isDark);
793
+ const style = makeAnnotationLabelStyle(
794
+ annotation.fontSize ?? 11,
795
+ annotation.fontWeight ?? 500,
796
+ void 0,
797
+ isDark
798
+ );
792
799
  if (centered) {
793
800
  style.textAnchor = "middle";
794
801
  } else if (anchor === "right") {
@@ -893,7 +900,12 @@ function resolveRefLineAnnotation(annotation, scales, chartArea, isDark) {
893
900
  }
894
901
  const labelDelta = applyOffset({ dx: baseDx, dy: baseDy }, annotation.labelOffset);
895
902
  const defaultStroke2 = isDark ? DARK_REFLINE_STROKE : LIGHT_REFLINE_STROKE;
896
- const style = makeAnnotationLabelStyle(11, 400, annotation.stroke ?? defaultStroke2, isDark);
903
+ const style = makeAnnotationLabelStyle(
904
+ annotation.fontSize ?? 11,
905
+ annotation.fontWeight ?? 400,
906
+ annotation.stroke ?? defaultStroke2,
907
+ isDark
908
+ );
897
909
  style.textAnchor = textAnchor;
898
910
  label = {
899
911
  text: annotation.label,
@@ -8973,9 +8985,9 @@ function compileLayerIndependent(leaves, layerSpec, options, compileChart2) {
8973
8985
  const hasRightAxisTitle = !!yAxisConfig?.title;
8974
8986
  const tickExtent = TICK_LABEL_OFFSET + rightAxisWidth;
8975
8987
  const bodyFontSize = theme.fonts?.sizes?.body ?? 13;
8976
- const axisTitleOffset = getAxisTitleOffset(options.width);
8988
+ const axisTitleOffset2 = getAxisTitleOffset(options.width);
8977
8989
  const halfGlyph = Math.ceil(bodyFontSize / 2);
8978
- const titleExtent = hasRightAxisTitle ? axisTitleOffset + halfGlyph + (options.width < BREAKPOINT_COMPACT_MAX ? 0 : AXIS_TITLE_TRAILING_PAD) : 0;
8990
+ const titleExtent = hasRightAxisTitle ? axisTitleOffset2 + halfGlyph + (options.width < BREAKPOINT_COMPACT_MAX ? 0 : AXIS_TITLE_TRAILING_PAD) : 0;
8979
8991
  const rightReserve = Math.max(tickExtent, titleExtent);
8980
8992
  const optionsWithReserve = {
8981
8993
  ...options,
@@ -10187,12 +10199,11 @@ function computeAxes(scales, chartArea, strategy, theme, measureText, dataContex
10187
10199
 
10188
10200
  // src/layout/dimensions.ts
10189
10201
  import {
10190
- AXIS_TITLE_GAP,
10191
10202
  AXIS_TITLE_TRAILING_PAD as AXIS_TITLE_TRAILING_PAD2,
10203
+ axisTitleOffset,
10192
10204
  BREAKPOINT_COMPACT_MAX as BREAKPOINT_COMPACT_MAX2,
10193
10205
  computeChrome as computeChrome3,
10194
10206
  estimateTextWidth as estimateTextWidth15,
10195
- getAxisTitleOffset as getAxisTitleOffset2,
10196
10207
  HPAD_COMPACT_FRACTION,
10197
10208
  HPAD_COMPACT_MIN,
10198
10209
  LABEL_GAP_COMPACT,
@@ -10202,7 +10213,6 @@ import {
10202
10213
  MAX_LEFT_LABEL_FRACTION_MEDIUM,
10203
10214
  MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX,
10204
10215
  NARROW_VIEWPORT_MAX,
10205
- TICK_LABEL_OFFSET as TICK_LABEL_OFFSET2,
10206
10216
  TOP_PAD_EXTRA_NARROW
10207
10217
  } from "@opendata-ai/openchart-core";
10208
10218
 
@@ -10299,19 +10309,23 @@ function measureLegendWrap(entries, maxWidth, labelStyle, maxRows, entryGap = EN
10299
10309
 
10300
10310
  // src/layout/metrics.ts
10301
10311
  import { estimateTextWidth as estimateTextWidth14 } from "@opendata-ai/openchart-core";
10302
- var LABEL_FONT_SIZE7 = 10;
10303
- var VALUE_FONT_SIZE2 = 22;
10312
+ var DEFAULT_LABEL_FONT_SIZE = 10;
10313
+ var DEFAULT_VALUE_FONT_SIZE = 22;
10304
10314
  var LABEL_LINE_HEIGHT_RATIO = 1.4;
10305
10315
  var VALUE_LINE_HEIGHT_RATIO = 1.15;
10306
10316
  var INTER_ROW_GAP = 4;
10307
10317
  var TOP_GAP = 16;
10308
10318
  var BOTTOM_GAP = 20;
10319
+ var DEFAULT_METRIC_FONT_SIZES = {
10320
+ label: DEFAULT_LABEL_FONT_SIZE,
10321
+ value: DEFAULT_VALUE_FONT_SIZE
10322
+ };
10309
10323
  var MIN_BAR_WIDTH2 = 480;
10310
10324
  var MIN_CHART_HEIGHT = 150;
10311
10325
  var CELL_INNER_PAD = 8;
10312
- function metricBarHeight() {
10313
- const labelLine = LABEL_FONT_SIZE7 * LABEL_LINE_HEIGHT_RATIO;
10314
- const valueLine = VALUE_FONT_SIZE2 * VALUE_LINE_HEIGHT_RATIO;
10326
+ function metricBarHeight(fonts = DEFAULT_METRIC_FONT_SIZES) {
10327
+ const labelLine = fonts.label * LABEL_LINE_HEIGHT_RATIO;
10328
+ const valueLine = fonts.value * VALUE_LINE_HEIGHT_RATIO;
10315
10329
  return TOP_GAP + labelLine + INTER_ROW_GAP + valueLine + BOTTOM_GAP;
10316
10330
  }
10317
10331
  function valueRunText(metric) {
@@ -10320,19 +10334,19 @@ function valueRunText(metric) {
10320
10334
  if (metric.secondary) parts.push(metric.secondary);
10321
10335
  return parts.join(" ");
10322
10336
  }
10323
- function computeMetricBar(metrics, metricsTopY, metricsArea, remainingChartHeight, measureText) {
10337
+ function computeMetricBar(metrics, metricsTopY, metricsArea, remainingChartHeight, measureText, fonts = DEFAULT_METRIC_FONT_SIZES) {
10324
10338
  if (!metrics || metrics.length === 0) return void 0;
10325
10339
  if (metricsArea.width < MIN_BAR_WIDTH2) return void 0;
10326
10340
  if (remainingChartHeight < MIN_CHART_HEIGHT) return void 0;
10327
10341
  const cellWidth = metricsArea.width / metrics.length;
10328
10342
  for (const metric of metrics) {
10329
10343
  const text = valueRunText(metric);
10330
- const measured = measureText ? measureText(text, VALUE_FONT_SIZE2, 510).width : estimateTextWidth14(text, VALUE_FONT_SIZE2, 510);
10344
+ const measured = measureText ? measureText(text, fonts.value, 510).width : estimateTextWidth14(text, fonts.value, 510);
10331
10345
  if (measured > cellWidth - CELL_INNER_PAD) return void 0;
10332
10346
  }
10333
- const labelLine = LABEL_FONT_SIZE7 * LABEL_LINE_HEIGHT_RATIO;
10334
- const labelY = metricsTopY + TOP_GAP + LABEL_FONT_SIZE7;
10335
- const valueY = metricsTopY + TOP_GAP + labelLine + INTER_ROW_GAP + VALUE_FONT_SIZE2;
10347
+ const labelLine = fonts.label * LABEL_LINE_HEIGHT_RATIO;
10348
+ const labelY = metricsTopY + TOP_GAP + fonts.label;
10349
+ const valueY = metricsTopY + TOP_GAP + labelLine + INTER_ROW_GAP + fonts.value;
10336
10350
  const cells = metrics.map((metric, i) => ({
10337
10351
  x: metricsArea.x + i * cellWidth,
10338
10352
  cellWidth,
@@ -10343,12 +10357,15 @@ function computeMetricBar(metrics, metricsTopY, metricsArea, remainingChartHeigh
10343
10357
  }));
10344
10358
  return {
10345
10359
  y: metricsTopY,
10346
- height: metricBarHeight(),
10360
+ height: metricBarHeight(fonts),
10347
10361
  cells
10348
10362
  };
10349
10363
  }
10350
10364
 
10351
10365
  // src/layout/dimensions.ts
10366
+ function metricFonts(theme) {
10367
+ return { label: theme.fonts.sizes.metricLabel, value: theme.fonts.sizes.metricValue };
10368
+ }
10352
10369
  function chromeToInput(chrome) {
10353
10370
  return {
10354
10371
  eyebrow: chrome.eyebrow,
@@ -10480,7 +10497,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10480
10497
  const topAxisGap = isRadial && chrome.topHeight === 0 ? 0 : axisMargin + inlineTickOverhang;
10481
10498
  const topPad = width < NARROW_VIEWPORT_MAX ? padding + TOP_PAD_EXTRA_NARROW : padding;
10482
10499
  const wantsMetrics = !!spec.metrics && spec.metrics.length > 0 && chromeMode !== "hidden";
10483
- const tentativeMetricsHeight = wantsMetrics ? metricBarHeight() : 0;
10500
+ const tentativeMetricsHeight = wantsMetrics ? metricBarHeight(metricFonts(theme)) : 0;
10484
10501
  const margins = {
10485
10502
  top: topPad + chrome.topHeight + tentativeMetricsHeight,
10486
10503
  right: hPad + (isRadial ? hPad : axisMargin),
@@ -10653,10 +10670,10 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10653
10670
  theme.fonts.weights.normal
10654
10671
  );
10655
10672
  }
10656
- const dynamicTitleOffset = TICK_LABEL_OFFSET2 + estTickLabelWidth + AXIS_TITLE_GAP;
10657
- const axisTitleOffset = Math.max(dynamicTitleOffset, getAxisTitleOffset2(width));
10658
- const halfGlyph = Math.ceil(theme.fonts.sizes.body / 2);
10659
- const rotatedLabelMargin = axisTitleOffset + halfGlyph + (width < BREAKPOINT_COMPACT_MAX2 ? 0 : AXIS_TITLE_TRAILING_PAD2);
10673
+ const titleFontSize = theme.fonts.sizes.body;
10674
+ const offset = axisTitleOffset(estTickLabelWidth, titleFontSize, width);
10675
+ const halfGlyph = Math.ceil(titleFontSize / 2);
10676
+ const rotatedLabelMargin = offset + halfGlyph + (width < BREAKPOINT_COMPACT_MAX2 ? 0 : AXIS_TITLE_TRAILING_PAD2);
10660
10677
  margins.left = Math.max(margins.left, hPad + rotatedLabelMargin);
10661
10678
  }
10662
10679
  if (options.rightAxisReserve && options.rightAxisReserve > 0) {
@@ -10714,7 +10731,8 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10714
10731
  fallbackMetricsTopY,
10715
10732
  fallbackMetricsArea,
10716
10733
  chartArea.height,
10717
- options.measureText
10734
+ options.measureText,
10735
+ theme
10718
10736
  ) : void 0;
10719
10737
  if (wantsMetrics && !fallbackMetrics) {
10720
10738
  margins.top = Math.max(0, margins.top - tentativeMetricsHeight);
@@ -10737,7 +10755,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10737
10755
  }
10738
10756
  const metricsTopY = topPad + chrome.topHeight;
10739
10757
  const metricsArea = { x: hPad, width: Math.max(0, width - hPad * 2) };
10740
- const resolvedMetrics = wantsMetrics ? resolveMetrics(spec, metricsTopY, metricsArea, chartArea.height, options.measureText) : void 0;
10758
+ const resolvedMetrics = wantsMetrics ? resolveMetrics(spec, metricsTopY, metricsArea, chartArea.height, options.measureText, theme) : void 0;
10741
10759
  if (wantsMetrics && !resolvedMetrics) {
10742
10760
  margins.top = Math.max(0, margins.top - tentativeMetricsHeight);
10743
10761
  chartArea = {
@@ -10756,13 +10774,14 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10756
10774
  xAxisHeight
10757
10775
  };
10758
10776
  }
10759
- function resolveMetrics(spec, metricsTopY, metricsArea, remainingChartHeight, measureText) {
10777
+ function resolveMetrics(spec, metricsTopY, metricsArea, remainingChartHeight, measureText, theme) {
10760
10778
  return computeMetricBar(
10761
10779
  spec.metrics,
10762
10780
  metricsTopY,
10763
10781
  metricsArea,
10764
10782
  remainingChartHeight,
10765
- measureText
10783
+ measureText,
10784
+ metricFonts(theme)
10766
10785
  );
10767
10786
  }
10768
10787
 
@@ -14265,17 +14284,18 @@ function compileChart(spec, options) {
14265
14284
  const chartArea = dims.chartArea;
14266
14285
  const legendArea = { ...chartArea };
14267
14286
  if ("entries" in legendLayout && legendLayout.entries.length > 0) {
14287
+ const legendInnerWidth = options.width - theme.spacing.padding - chartArea.x;
14268
14288
  const gap = legendGap(options.width);
14269
14289
  switch (legendLayout.position) {
14270
14290
  case "top":
14271
- legendArea.x = theme.spacing.padding;
14272
- legendArea.width = options.width - theme.spacing.padding * 2;
14291
+ legendArea.x = chartArea.x;
14292
+ legendArea.width = legendInnerWidth;
14273
14293
  legendArea.y -= legendLayout.bounds.height + gap;
14274
14294
  legendArea.height += legendLayout.bounds.height + gap;
14275
14295
  break;
14276
14296
  case "bottom":
14277
- legendArea.x = theme.spacing.padding;
14278
- legendArea.width = options.width - theme.spacing.padding * 2;
14297
+ legendArea.x = chartArea.x;
14298
+ legendArea.width = legendInnerWidth;
14279
14299
  legendArea.height += legendLayout.bounds.height + gap + dims.xAxisHeight;
14280
14300
  break;
14281
14301
  case "right":