@opendata-ai/openchart-engine 7.0.4 → 7.1.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 CHANGED
@@ -1259,8 +1259,9 @@ function computeEndpointLabels(spec, marks, theme, chartArea, strategy) {
1259
1259
  };
1260
1260
  if (showMarker) {
1261
1261
  entry.marker = {
1262
- x: p.dataX,
1262
+ x: p.dataX + markerRadius,
1263
1263
  y: p.dataY,
1264
+ dataX: p.dataX,
1264
1265
  fill: markerFill,
1265
1266
  stroke: config?.markerStyle?.stroke ?? p.color,
1266
1267
  strokeWidth: markerStrokeWidth,
@@ -2211,7 +2212,7 @@ import {
2211
2212
  } from "@opendata-ai/openchart-core";
2212
2213
  var LABEL_FONT_SIZE2 = 10;
2213
2214
  var LABEL_FONT_WEIGHT2 = 600;
2214
- var LABEL_OFFSET_Y = 6;
2215
+ var LABEL_OFFSET_Y = 8;
2215
2216
  function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, labelPrefix, valueField, labelColor) {
2216
2217
  const targetMarks = filterByDensity(marks, density);
2217
2218
  const formatter = buildD3Formatter2(labelFormat);
@@ -2257,7 +2258,7 @@ function computeColumnLabels(marks, _chartArea, density = "auto", labelFormat, l
2257
2258
  fill: labelColor ?? getRepresentativeColor2(mark.fill),
2258
2259
  lineHeight: 1.2,
2259
2260
  textAnchor: "middle",
2260
- dominantBaseline: isNegative ? "hanging" : "auto"
2261
+ dominantBaseline: "hanging"
2261
2262
  }
2262
2263
  });
2263
2264
  }
@@ -3476,6 +3477,10 @@ var STACKED_GRADIENT_STOPS = [
3476
3477
  { offset: 0, opacity: 0.65 },
3477
3478
  { offset: 1, opacity: 0.35 }
3478
3479
  ];
3480
+ var STACKED_GRADIENT_STOPS_LIGHT = [
3481
+ { offset: 0, opacity: 0.65 },
3482
+ { offset: 1, opacity: 0 }
3483
+ ];
3479
3484
  function buildGradientFill(colorStr, stops) {
3480
3485
  return {
3481
3486
  gradient: "linear",
@@ -3568,7 +3573,7 @@ function computeSingleArea(spec, scales, _chartArea) {
3568
3573
  }
3569
3574
  return marks;
3570
3575
  }
3571
- function computeStackedArea(spec, scales, chartArea) {
3576
+ function computeStackedArea(spec, scales, chartArea, darkMode) {
3572
3577
  const encoding = spec.encoding;
3573
3578
  const xChannel = encoding.x;
3574
3579
  const yChannel = encoding.y;
@@ -3646,7 +3651,8 @@ function computeStackedArea(spec, scales, chartArea) {
3646
3651
  fillOpacity = isGradientDef3(markFill) ? 1 : spec.markDef.opacity ?? 0.7;
3647
3652
  } else {
3648
3653
  const colorStr = getRepresentativeColor4(color2);
3649
- fillValue = buildGradientFill(colorStr, STACKED_GRADIENT_STOPS);
3654
+ const stackedStops = darkMode ? STACKED_GRADIENT_STOPS : STACKED_GRADIENT_STOPS_LIGHT;
3655
+ fillValue = buildGradientFill(colorStr, stackedStops);
3650
3656
  fillOpacity = 1;
3651
3657
  }
3652
3658
  marks.push({
@@ -3674,11 +3680,11 @@ function computeStackedArea(spec, scales, chartArea) {
3674
3680
  }
3675
3681
  return marks;
3676
3682
  }
3677
- function computeAreaMarks(spec, scales, chartArea) {
3683
+ function computeAreaMarks(spec, scales, chartArea, darkMode) {
3678
3684
  const encoding = spec.encoding;
3679
3685
  const yChannel = encoding.y;
3680
3686
  if (yChannel && isStacked(yChannel.stack)) {
3681
- return computeStackedArea(spec, scales, chartArea);
3687
+ return computeStackedArea(spec, scales, chartArea, darkMode);
3682
3688
  }
3683
3689
  return computeSingleArea(spec, scales, chartArea);
3684
3690
  }
@@ -3937,8 +3943,8 @@ var lineRenderer = (spec, scales, chartArea, strategy, _theme) => {
3937
3943
  }
3938
3944
  return marks;
3939
3945
  };
3940
- var areaRenderer = (spec, scales, chartArea, strategy, _theme) => {
3941
- const areas = computeAreaMarks(spec, scales, chartArea);
3946
+ var areaRenderer = (spec, scales, chartArea, strategy, theme) => {
3947
+ const areas = computeAreaMarks(spec, scales, chartArea, theme.isDark);
3942
3948
  const encoding = spec.encoding;
3943
3949
  const hasColor = !!(encoding.color && "field" in encoding.color);
3944
3950
  const lines = hasColor ? linesFromAreas(areas) : computeLineMarks(spec, scales, chartArea, strategy);
@@ -9656,7 +9662,7 @@ var DEFAULT_COLLISION_PADDING = 5;
9656
9662
 
9657
9663
  // src/layout/axes/thinning.ts
9658
9664
  import { estimateTextWidth as estimateTextWidth11 } from "@opendata-ai/openchart-core";
9659
- var MIN_TICK_GAP_FACTOR = 1;
9665
+ var MIN_TICK_GAP_FACTOR = 0.5;
9660
9666
  var MIN_TICK_COUNT = 2;
9661
9667
  function measureLabel(text, fontSize, fontWeight, measureText) {
9662
9668
  return measureText ? measureText(text, fontSize, fontWeight).width : estimateTextWidth11(text, fontSize, fontWeight);
@@ -10158,6 +10164,7 @@ import {
10158
10164
  MAX_LEFT_LABEL_FRACTION_MEDIUM,
10159
10165
  MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX,
10160
10166
  NARROW_VIEWPORT_MAX,
10167
+ TICK_LABEL_OFFSET as TICK_LABEL_OFFSET2,
10161
10168
  TOP_PAD_EXTRA_NARROW
10162
10169
  } from "@opendata-ai/openchart-core";
10163
10170
 
@@ -10315,6 +10322,9 @@ function chromeToInput(chrome) {
10315
10322
  brand: chrome.brand
10316
10323
  };
10317
10324
  }
10325
+ function bottomMargin(bottomHeight, padding, xAxisHeight) {
10326
+ return padding + bottomHeight + xAxisHeight;
10327
+ }
10318
10328
  function scalePadding(basePadding, width, height) {
10319
10329
  const minDim = Math.min(width, height);
10320
10330
  if (minDim >= 500) return basePadding;
@@ -10435,7 +10445,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10435
10445
  const margins = {
10436
10446
  top: topPad + chrome.topHeight + tentativeMetricsHeight,
10437
10447
  right: hPad + (isRadial ? hPad : axisMargin),
10438
- bottom: padding + chrome.bottomHeight + xAxisHeight,
10448
+ bottom: bottomMargin(chrome.bottomHeight, padding, xAxisHeight),
10439
10449
  left: hPad + (isRadial ? hPad : axisMargin)
10440
10450
  };
10441
10451
  const labelDensity = spec.labels.density;
@@ -10572,7 +10582,41 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10572
10582
  }
10573
10583
  const yAxis = encoding.y?.axis;
10574
10584
  if (yAxis && (yAxis.title || yAxis.label) && !isRadial) {
10575
- const axisTitleOffset = getAxisTitleOffset2(width);
10585
+ const yFieldForTitle = encoding.y?.field;
10586
+ const yAxisFormatForTitle = yAxis?.format;
10587
+ let estTickLabelWidth = 0;
10588
+ if (yFieldForTitle && (encoding.y?.type === "quantitative" || encoding.y?.type === "temporal")) {
10589
+ let maxAbsValForTitle = 0;
10590
+ for (const row of spec.data) {
10591
+ const v = Number(row[yFieldForTitle]);
10592
+ if (Number.isFinite(v) && Math.abs(v) > maxAbsValForTitle) maxAbsValForTitle = Math.abs(v);
10593
+ }
10594
+ let sampleLabelForTitle;
10595
+ if (yAxisFormatForTitle) {
10596
+ try {
10597
+ const fmt = format(yAxisFormatForTitle);
10598
+ sampleLabelForTitle = fmt(maxAbsValForTitle);
10599
+ } catch {
10600
+ sampleLabelForTitle = String(maxAbsValForTitle);
10601
+ }
10602
+ } else {
10603
+ if (maxAbsValForTitle >= 1e9) sampleLabelForTitle = "1.5B";
10604
+ else if (maxAbsValForTitle >= 1e6) sampleLabelForTitle = "1.5M";
10605
+ else if (maxAbsValForTitle >= 1e3) sampleLabelForTitle = "1.5K";
10606
+ else if (maxAbsValForTitle >= 100) sampleLabelForTitle = "100";
10607
+ else if (maxAbsValForTitle >= 10) sampleLabelForTitle = "10";
10608
+ else sampleLabelForTitle = "0.0";
10609
+ }
10610
+ const negPrefixForTitle = spec.data.some((r) => Number(r[yFieldForTitle]) < 0) ? "-" : "";
10611
+ estTickLabelWidth = estimateTextWidth16(
10612
+ negPrefixForTitle + sampleLabelForTitle,
10613
+ theme.fonts.sizes.axisTick,
10614
+ theme.fonts.weights.normal
10615
+ );
10616
+ }
10617
+ const AXIS_TITLE_GAP = 8;
10618
+ const dynamicTitleOffset = TICK_LABEL_OFFSET2 + estTickLabelWidth + AXIS_TITLE_GAP;
10619
+ const axisTitleOffset = Math.max(dynamicTitleOffset, getAxisTitleOffset2(width));
10576
10620
  const halfGlyph = Math.ceil(theme.fonts.sizes.body / 2);
10577
10621
  const rotatedLabelMargin = axisTitleOffset + halfGlyph + (width < BREAKPOINT_COMPACT_MAX2 ? 0 : AXIS_TITLE_TRAILING_PAD2);
10578
10622
  margins.left = Math.max(margins.left, hPad + rotatedLabelMargin);
@@ -10611,7 +10655,7 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10611
10655
  const fallbackTopAxisGap = isRadial && fallbackChrome.topHeight === 0 ? 0 : axisMargin + inlineTickOverhang;
10612
10656
  const newTop = topPad + fallbackChrome.topHeight + tentativeMetricsHeight;
10613
10657
  const topDelta = margins.top - newTop;
10614
- const newBottom = padding + fallbackChrome.bottomHeight + xAxisHeight;
10658
+ const newBottom = bottomMargin(fallbackChrome.bottomHeight, padding, xAxisHeight);
10615
10659
  const bottomDelta = margins.bottom - newBottom;
10616
10660
  if (topDelta > 0 || bottomDelta > 0) {
10617
10661
  const gap = legendGap(width);