@opendata-ai/openchart-engine 8.0.0-rc.17 → 8.0.0-rc.19

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
@@ -9087,7 +9087,7 @@ function resolvePosition2(value2, channelType, scale) {
9087
9087
  }
9088
9088
  }
9089
9089
  }
9090
- function computeScatterMarks(spec, scales, _chartArea, _strategy) {
9090
+ function computeScatterMarks(spec, scales, _chartArea, _strategy, theme) {
9091
9091
  const encoding = spec.encoding;
9092
9092
  const xChannel = encoding.x;
9093
9093
  const yChannel = encoding.y;
@@ -9105,6 +9105,10 @@ function computeScatterMarks(spec, scales, _chartArea, _strategy) {
9105
9105
  const sizeField = sizeEnc?.field;
9106
9106
  const resolvedSize = buildSizeScale(sizeEnc, spec.data, SIZE_SCALE_DEFAULTS.point);
9107
9107
  const sizeScale = resolvedSize?.scale;
9108
+ const constFill = spec.markDef.fill;
9109
+ const constSize = spec.markDef.size;
9110
+ const bg = theme?.colors?.background;
9111
+ const defaultStroke = bg && bg !== "transparent" ? bg : "#ffffff";
9108
9112
  const keyEnc = encoding.key && "field" in encoding.key ? encoding.key : void 0;
9109
9113
  const keyField = keyEnc?.field;
9110
9114
  const marks = [];
@@ -9119,10 +9123,12 @@ function computeScatterMarks(spec, scales, _chartArea, _strategy) {
9119
9123
  if (isSequentialColor && colorField) {
9120
9124
  const val = Number(row[colorField]);
9121
9125
  color2 = Number.isFinite(val) ? getSequentialColor(scales, val) : getColor(scales, "__default__");
9122
- } else {
9126
+ } else if (colorField) {
9123
9127
  color2 = getColor(scales, category ?? "__default__");
9128
+ } else {
9129
+ color2 = constFill ?? getColor(scales, "__default__");
9124
9130
  }
9125
- let radius = DEFAULT_POINT_RADIUS2;
9131
+ let radius = constSize != null ? constSize : DEFAULT_POINT_RADIUS2;
9126
9132
  if (sizeScale && sizeField) {
9127
9133
  const sizeVal = Number(row[sizeField]);
9128
9134
  if (Number.isFinite(sizeVal)) {
@@ -9143,9 +9149,9 @@ function computeScatterMarks(spec, scales, _chartArea, _strategy) {
9143
9149
  cy,
9144
9150
  r: radius,
9145
9151
  fill: color2,
9146
- stroke: "#ffffff",
9147
- strokeWidth: 1,
9148
- fillOpacity: 0.7,
9152
+ stroke: spec.markDef.stroke ?? defaultStroke,
9153
+ strokeWidth: spec.markDef.strokeWidth ?? 1,
9154
+ fillOpacity: spec.markDef.opacity ?? 0.7,
9149
9155
  data: row,
9150
9156
  aria
9151
9157
  });
@@ -9186,7 +9192,7 @@ function linearRegression(points) {
9186
9192
  const intercept = (sumY - slope * sumX) / n;
9187
9193
  return { slope, intercept };
9188
9194
  }
9189
- function computeTrendLine(marks) {
9195
+ function computeTrendLine(marks, stroke = TRENDLINE_COLOR, strokeWidth = TRENDLINE_STROKE_WIDTH) {
9190
9196
  if (marks.length < 2) return null;
9191
9197
  const points = marks.map((m) => ({ x: m.cx, y: m.cy }));
9192
9198
  const result = linearRegression(points);
@@ -9209,8 +9215,8 @@ function computeTrendLine(marks) {
9209
9215
  { x: minX, y: y12 },
9210
9216
  { x: maxX, y: y2 }
9211
9217
  ],
9212
- stroke: TRENDLINE_COLOR,
9213
- strokeWidth: TRENDLINE_STROKE_WIDTH,
9218
+ stroke,
9219
+ strokeWidth,
9214
9220
  strokeDasharray: TRENDLINE_DASH,
9215
9221
  data: [],
9216
9222
  aria
@@ -9218,13 +9224,20 @@ function computeTrendLine(marks) {
9218
9224
  }
9219
9225
 
9220
9226
  // src/charts/scatter/index.ts
9221
- var scatterRenderer = (spec, scales, chartArea, strategy, _theme) => {
9222
- const pointMarks = computeScatterMarks(spec, scales, chartArea, strategy);
9227
+ var scatterRenderer = (spec, scales, chartArea, strategy, theme) => {
9228
+ const pointMarks = computeScatterMarks(spec, scales, chartArea, strategy, theme);
9223
9229
  const marks = [...pointMarks];
9224
- if (spec.markDef?.trendline !== false) {
9225
- const trendLine = computeTrendLine(pointMarks);
9230
+ const trendline = spec.markDef?.trendline;
9231
+ if (trendline !== false) {
9232
+ const config = typeof trendline === "object" ? trendline : void 0;
9233
+ const stroke = config?.stroke ?? theme?.colors?.text;
9234
+ const trendLine = computeTrendLine(pointMarks, stroke, config?.strokeWidth);
9226
9235
  if (trendLine) {
9227
- marks.unshift(trendLine);
9236
+ if (config?.layer === "above") {
9237
+ marks.push(trendLine);
9238
+ } else {
9239
+ marks.unshift(trendLine);
9240
+ }
9228
9241
  }
9229
9242
  }
9230
9243
  return marks;
@@ -15430,11 +15443,9 @@ function compileLayerIndependent(leaves, layerSpec, options, compileChart2) {
15430
15443
  };
15431
15444
  const xField0 = leaf0.encoding?.x?.field;
15432
15445
  const xField1 = leaf1.encoding?.x?.field;
15433
- const unionXValues = /* @__PURE__ */ new Set();
15434
- if (xField0) for (const row of leaf0.data) unionXValues.add(row[xField0]);
15435
- if (xField1) for (const row of leaf1.data) unionXValues.add(row[xField1]);
15436
- let leaf0WithUnionX = ensureXDomainCoverage(leaf0, xField0, unionXValues);
15437
- let leaf1WithUnionX = ensureXDomainCoverage(leaf1, xField1, unionXValues);
15446
+ const union = withUnionXDomain(leaf0, leaf1);
15447
+ let leaf0WithUnionX = union.leaf0;
15448
+ let leaf1WithUnionX = union.leaf1;
15438
15449
  const aligned = alignYDomains(leaf0WithUnionX, leaf1WithUnionX);
15439
15450
  if (aligned) {
15440
15451
  leaf0WithUnionX = withYDomain(leaf0WithUnionX, aligned.domain0);
@@ -15581,20 +15592,75 @@ function compileLayerIndependent(leaves, layerSpec, options, compileChart2) {
15581
15592
  tooltipDescriptors: mergedTooltips
15582
15593
  };
15583
15594
  }
15584
- function ensureXDomainCoverage(leaf, xField, allXValues) {
15585
- if (!xField || allXValues.size === 0) return leaf;
15586
- const existingXValues = /* @__PURE__ */ new Set();
15587
- for (const row of leaf.data) existingXValues.add(row[xField]);
15588
- const missingRows = [];
15589
- for (const xVal of allXValues) {
15590
- if (!existingXValues.has(xVal)) {
15591
- missingRows.push({ [xField]: xVal });
15595
+ function withXDomain(leaf, domain) {
15596
+ if (!leaf.encoding?.x) return leaf;
15597
+ return {
15598
+ ...leaf,
15599
+ encoding: {
15600
+ ...leaf.encoding,
15601
+ x: {
15602
+ ...leaf.encoding.x,
15603
+ scale: {
15604
+ ...leaf.encoding.x.scale,
15605
+ domain
15606
+ }
15607
+ }
15592
15608
  }
15609
+ };
15610
+ }
15611
+ function withUnionXDomain(leaf0, leaf1) {
15612
+ const xEnc0 = leaf0.encoding?.x;
15613
+ const xEnc1 = leaf1.encoding?.x;
15614
+ const xField0 = xEnc0?.field;
15615
+ const xField1 = xEnc1?.field;
15616
+ if (!xField0 || !xField1) return { leaf0, leaf1 };
15617
+ const xType = xEnc0?.type ?? xEnc1?.type;
15618
+ const pinned0 = xEnc0?.scale?.domain !== void 0;
15619
+ const pinned1 = xEnc1?.scale?.domain !== void 0;
15620
+ if (pinned0 && pinned1) return { leaf0, leaf1 };
15621
+ const values0 = leaf0.data.map((row) => row[xField0]);
15622
+ const values1 = leaf1.data.map((row) => row[xField1]);
15623
+ const allValues = [...values0, ...values1].filter((v) => v != null);
15624
+ if (allValues.length === 0) return { leaf0, leaf1 };
15625
+ let domain;
15626
+ if (xType === "temporal") {
15627
+ let minMs = Number.POSITIVE_INFINITY;
15628
+ let maxMs = Number.NEGATIVE_INFINITY;
15629
+ for (const v of allValues) {
15630
+ const ms = (v instanceof Date ? v : new Date(String(v))).getTime();
15631
+ if (Number.isNaN(ms)) continue;
15632
+ if (ms < minMs) minMs = ms;
15633
+ if (ms > maxMs) maxMs = ms;
15634
+ }
15635
+ if (minMs <= maxMs) {
15636
+ domain = [new Date(minMs).toISOString(), new Date(maxMs).toISOString()];
15637
+ }
15638
+ } else if (xType === "quantitative") {
15639
+ let min4 = Number.POSITIVE_INFINITY;
15640
+ let max4 = Number.NEGATIVE_INFINITY;
15641
+ for (const v of allValues) {
15642
+ const n = typeof v === "number" ? v : Number(v);
15643
+ if (!Number.isFinite(n)) continue;
15644
+ if (n < min4) min4 = n;
15645
+ if (n > max4) max4 = n;
15646
+ }
15647
+ if (min4 <= max4) domain = [min4, max4];
15648
+ } else {
15649
+ const seen = /* @__PURE__ */ new Set();
15650
+ const categories = [];
15651
+ for (const v of allValues) {
15652
+ const s = String(v);
15653
+ if (!seen.has(s)) {
15654
+ seen.add(s);
15655
+ categories.push(s);
15656
+ }
15657
+ }
15658
+ if (categories.length > 0) domain = categories;
15593
15659
  }
15594
- if (missingRows.length === 0) return leaf;
15660
+ if (domain === void 0) return { leaf0, leaf1 };
15595
15661
  return {
15596
- ...leaf,
15597
- data: [...leaf.data, ...missingRows]
15662
+ leaf0: pinned0 ? leaf0 : withXDomain(leaf0, domain),
15663
+ leaf1: pinned1 ? leaf1 : withXDomain(leaf1, domain)
15598
15664
  };
15599
15665
  }
15600
15666
  function alignYDomains(leaf0, leaf1) {