@opendata-ai/openchart-engine 7.2.0 → 7.2.2

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") {
@@ -3932,6 +3939,7 @@ function computeLineLabels(marks, strategy, density = "auto", labelOffsets, spec
3932
3939
  }
3933
3940
 
3934
3941
  // src/charts/line/index.ts
3942
+ var AREA_POINT_RADIUS = 3;
3935
3943
  var lineRenderer = (spec, scales, chartArea, strategy, _theme) => {
3936
3944
  const marks = computeLineMarks(spec, scales, chartArea, strategy);
3937
3945
  const lineMarks = marks.filter((m) => m.type === "line");
@@ -3957,7 +3965,8 @@ var areaRenderer = (spec, scales, chartArea, strategy, theme) => {
3957
3965
  const encoding = spec.encoding;
3958
3966
  const hasColor = !!(encoding.color && "field" in encoding.color);
3959
3967
  const lines = hasColor ? linesFromAreas(areas) : computeLineMarks(spec, scales, chartArea, strategy);
3960
- return [...areas, ...lines];
3968
+ const points = hasColor && spec.markDef.point ? pointsFromAreas(areas, spec.markDef.point) : [];
3969
+ return [...areas, ...lines, ...points];
3961
3970
  };
3962
3971
  function linesFromAreas(areas) {
3963
3972
  return areas.map((a) => ({
@@ -3972,6 +3981,34 @@ function linesFromAreas(areas) {
3972
3981
  aria: { label: `${a.seriesKey ?? "Series"}: line with ${a.topPoints.length} data points` }
3973
3982
  }));
3974
3983
  }
3984
+ function pointsFromAreas(areas, pointMode) {
3985
+ const isTransparent = pointMode === "transparent";
3986
+ const isEndpoints = pointMode === "endpoints";
3987
+ const points = [];
3988
+ for (const a of areas) {
3989
+ const stroke = getRepresentativeColor6(a.fill);
3990
+ const lastIdx = a.topPoints.length - 1;
3991
+ for (let i = 0; i < a.topPoints.length; i++) {
3992
+ const pt = a.topPoints[i];
3993
+ const isEndpoint = i === 0 || i === lastIdx;
3994
+ const visible = !isTransparent && (!isEndpoints || isEndpoint);
3995
+ const hollow = isEndpoints && visible;
3996
+ points.push({
3997
+ type: "point",
3998
+ cx: pt.x,
3999
+ cy: pt.y,
4000
+ r: visible ? AREA_POINT_RADIUS : 0,
4001
+ fill: hollow ? "transparent" : stroke,
4002
+ stroke: hollow ? stroke : visible ? "#ffffff" : "transparent",
4003
+ strokeWidth: visible ? 1.5 : 0,
4004
+ fillOpacity: isTransparent ? 0 : 1,
4005
+ data: a.data[i] ?? {},
4006
+ aria: { decorative: true }
4007
+ });
4008
+ }
4009
+ }
4010
+ return points;
4011
+ }
3975
4012
 
3976
4013
  // src/charts/pie/compute.ts
3977
4014
  import { isConditionalDef, isGradientDef as isGradientDef4 } from "@opendata-ai/openchart-core";
@@ -8943,9 +8980,9 @@ function compileLayerIndependent(leaves, layerSpec, options, compileChart2) {
8943
8980
  const hasRightAxisTitle = !!yAxisConfig?.title;
8944
8981
  const tickExtent = TICK_LABEL_OFFSET + rightAxisWidth;
8945
8982
  const bodyFontSize = theme.fonts?.sizes?.body ?? 13;
8946
- const axisTitleOffset = getAxisTitleOffset(options.width);
8983
+ const axisTitleOffset2 = getAxisTitleOffset(options.width);
8947
8984
  const halfGlyph = Math.ceil(bodyFontSize / 2);
8948
- const titleExtent = hasRightAxisTitle ? axisTitleOffset + halfGlyph + (options.width < BREAKPOINT_COMPACT_MAX ? 0 : AXIS_TITLE_TRAILING_PAD) : 0;
8985
+ const titleExtent = hasRightAxisTitle ? axisTitleOffset2 + halfGlyph + (options.width < BREAKPOINT_COMPACT_MAX ? 0 : AXIS_TITLE_TRAILING_PAD) : 0;
8949
8986
  const rightReserve = Math.max(tickExtent, titleExtent);
8950
8987
  const optionsWithReserve = {
8951
8988
  ...options,
@@ -10157,12 +10194,11 @@ function computeAxes(scales, chartArea, strategy, theme, measureText, dataContex
10157
10194
 
10158
10195
  // src/layout/dimensions.ts
10159
10196
  import {
10160
- AXIS_TITLE_GAP,
10161
10197
  AXIS_TITLE_TRAILING_PAD as AXIS_TITLE_TRAILING_PAD2,
10198
+ axisTitleOffset,
10162
10199
  BREAKPOINT_COMPACT_MAX as BREAKPOINT_COMPACT_MAX2,
10163
10200
  computeChrome as computeChrome3,
10164
10201
  estimateTextWidth as estimateTextWidth15,
10165
- getAxisTitleOffset as getAxisTitleOffset2,
10166
10202
  HPAD_COMPACT_FRACTION,
10167
10203
  HPAD_COMPACT_MIN,
10168
10204
  LABEL_GAP_COMPACT,
@@ -10172,7 +10208,6 @@ import {
10172
10208
  MAX_LEFT_LABEL_FRACTION_MEDIUM,
10173
10209
  MAX_LEFT_LABEL_FRACTION_MEDIUM_MAX,
10174
10210
  NARROW_VIEWPORT_MAX,
10175
- TICK_LABEL_OFFSET as TICK_LABEL_OFFSET2,
10176
10211
  TOP_PAD_EXTRA_NARROW
10177
10212
  } from "@opendata-ai/openchart-core";
10178
10213
 
@@ -10623,10 +10658,10 @@ function computeDimensions(spec, options, legendLayout, theme, strategy, waterma
10623
10658
  theme.fonts.weights.normal
10624
10659
  );
10625
10660
  }
10626
- const dynamicTitleOffset = TICK_LABEL_OFFSET2 + estTickLabelWidth + AXIS_TITLE_GAP;
10627
- const axisTitleOffset = Math.max(dynamicTitleOffset, getAxisTitleOffset2(width));
10628
- const halfGlyph = Math.ceil(theme.fonts.sizes.body / 2);
10629
- const rotatedLabelMargin = axisTitleOffset + halfGlyph + (width < BREAKPOINT_COMPACT_MAX2 ? 0 : AXIS_TITLE_TRAILING_PAD2);
10661
+ const titleFontSize = theme.fonts.sizes.body;
10662
+ const offset = axisTitleOffset(estTickLabelWidth, titleFontSize, width);
10663
+ const halfGlyph = Math.ceil(titleFontSize / 2);
10664
+ const rotatedLabelMargin = offset + halfGlyph + (width < BREAKPOINT_COMPACT_MAX2 ? 0 : AXIS_TITLE_TRAILING_PAD2);
10630
10665
  margins.left = Math.max(margins.left, hPad + rotatedLabelMargin);
10631
10666
  }
10632
10667
  if (options.rightAxisReserve && options.rightAxisReserve > 0) {
@@ -10978,7 +11013,7 @@ function buildBandScale(channel, data, rangeStart, rangeEnd) {
10978
11013
  }
10979
11014
  function buildPointScale(channel, data, rangeStart, rangeEnd) {
10980
11015
  const values = channel.scale?.domain ? channel.scale.domain : applyCategoricalSort(uniqueStrings(fieldValues(data, channel.field)), channel.sort);
10981
- const padding = channel.scale?.padding ?? 0.5;
11016
+ const padding = channel.scale?.padding ?? channel.scale?.paddingOuter ?? 0.5;
10982
11017
  const scale = point4().domain(values).range([rangeStart, rangeEnd]).padding(padding);
10983
11018
  if (channel.scale?.reverse) {
10984
11019
  const [r0, r1] = scale.range();
@@ -14235,17 +14270,18 @@ function compileChart(spec, options) {
14235
14270
  const chartArea = dims.chartArea;
14236
14271
  const legendArea = { ...chartArea };
14237
14272
  if ("entries" in legendLayout && legendLayout.entries.length > 0) {
14273
+ const legendInnerWidth = options.width - theme.spacing.padding - chartArea.x;
14238
14274
  const gap = legendGap(options.width);
14239
14275
  switch (legendLayout.position) {
14240
14276
  case "top":
14241
- legendArea.x = theme.spacing.padding;
14242
- legendArea.width = options.width - theme.spacing.padding * 2;
14277
+ legendArea.x = chartArea.x;
14278
+ legendArea.width = legendInnerWidth;
14243
14279
  legendArea.y -= legendLayout.bounds.height + gap;
14244
14280
  legendArea.height += legendLayout.bounds.height + gap;
14245
14281
  break;
14246
14282
  case "bottom":
14247
- legendArea.x = theme.spacing.padding;
14248
- legendArea.width = options.width - theme.spacing.padding * 2;
14283
+ legendArea.x = chartArea.x;
14284
+ legendArea.width = legendInnerWidth;
14249
14285
  legendArea.height += legendLayout.bounds.height + gap + dims.xAxisHeight;
14250
14286
  break;
14251
14287
  case "right":