resolvcharts 0.1.0 → 0.2.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/README.md +92 -26
- package/dist/index.cjs +216 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +111 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +111 -22
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +216 -35
- package/dist/index.mjs.map +1 -1
- package/dist/resolvcharts.global.min.js +8 -8
- package/dist/resolvcharts.global.min.js.map +1 -1
- package/package.json +7 -2
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
//#region src/charts/bar.ts
|
|
2
|
-
const MAX_BAR_W$1 = 40;
|
|
3
|
-
const RADIUS$1 = 3;
|
|
4
1
|
function barGeometry(r, d, catIdx, seriesIdx, value) {
|
|
5
2
|
const cats = d.categories.length;
|
|
6
3
|
if (cats === 0) return null;
|
|
7
4
|
const slot = r.plot.w / cats;
|
|
8
|
-
const groupW = Math.min(slot * .8,
|
|
5
|
+
const groupW = Math.min(slot * .8, 40 * d.series.length);
|
|
9
6
|
const barW = groupW / d.series.length;
|
|
10
7
|
const x = r.plot.x + slot * catIdx + (slot - groupW) / 2 + barW * seriesIdx;
|
|
11
8
|
const base = r.yScale(0);
|
|
@@ -45,7 +42,7 @@ function stackedGeometry(r, d, catIdx, v0, v1) {
|
|
|
45
42
|
const cats = d.categories.length;
|
|
46
43
|
if (cats === 0) return null;
|
|
47
44
|
const slot = r.plot.w / cats;
|
|
48
|
-
const w = Math.min(slot * .8,
|
|
45
|
+
const w = Math.min(slot * .8, 40);
|
|
49
46
|
const x = r.plot.x + slot * catIdx + (slot - w) / 2;
|
|
50
47
|
const yTop = r.yScale(Math.max(v0, v1));
|
|
51
48
|
const yBot = r.yScale(Math.min(v0, v1));
|
|
@@ -68,7 +65,7 @@ function renderBar(r) {
|
|
|
68
65
|
const slot = r.plot.w / Math.max(1, cats);
|
|
69
66
|
const animate = r.t < 1;
|
|
70
67
|
if (r.opts.stacked) {
|
|
71
|
-
const w = Math.min(slot * .8,
|
|
68
|
+
const w = Math.min(slot * .8, 40);
|
|
72
69
|
for (let ci = 0; ci < cats; ci++) {
|
|
73
70
|
const x = r.plot.x + slot * ci + (slot - w) / 2;
|
|
74
71
|
let pos = 0;
|
|
@@ -91,7 +88,7 @@ function renderBar(r) {
|
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
} else {
|
|
94
|
-
const groupW = Math.min(slot * .8,
|
|
91
|
+
const groupW = Math.min(slot * .8, 40 * d.series.length);
|
|
95
92
|
const barW = groupW / d.series.length;
|
|
96
93
|
const base = r.yScale(0);
|
|
97
94
|
for (let si = 0; si < d.series.length; si++) {
|
|
@@ -112,7 +109,7 @@ function renderBar(r) {
|
|
|
112
109
|
y,
|
|
113
110
|
w: barW,
|
|
114
111
|
h
|
|
115
|
-
},
|
|
112
|
+
}, 3, v >= 0);
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
}
|
|
@@ -375,6 +372,7 @@ function decimateSeriesPx(xs, ys, prev, t, yFallback, xScale, yScale, pixelWidth
|
|
|
375
372
|
* ys must be pixel-space y coordinates of equal length.
|
|
376
373
|
*
|
|
377
374
|
* Returns interleaved [x, y] pairs ready for path construction.
|
|
375
|
+
* @category Utilities
|
|
378
376
|
*/
|
|
379
377
|
function decimateM4(xs, ys, pixelWidth) {
|
|
380
378
|
const n = Math.min(xs.length, ys.length);
|
|
@@ -436,6 +434,7 @@ function decimateM4(xs, ys, pixelWidth) {
|
|
|
436
434
|
/**
|
|
437
435
|
* Binary search for the index whose value is nearest to `target`.
|
|
438
436
|
* `xs` must be sorted ascending. Returns -1 for empty input.
|
|
437
|
+
* @category Utilities
|
|
439
438
|
*/
|
|
440
439
|
function nearestIndex(xs, target) {
|
|
441
440
|
const n = xs.length;
|
|
@@ -845,8 +844,151 @@ function hitCandle(r, px) {
|
|
|
845
844
|
};
|
|
846
845
|
}
|
|
847
846
|
//#endregion
|
|
847
|
+
//#region src/charts/combo.ts
|
|
848
|
+
/** pooled scratch for overlay [x, y] pairs — consumed within the call */
|
|
849
|
+
let ovScratch = /* @__PURE__ */ new Float64Array(0);
|
|
850
|
+
function overlayPath(r, pts, n) {
|
|
851
|
+
const path = new Path2D();
|
|
852
|
+
if (n === 0) return path;
|
|
853
|
+
const curve = r.opts.curve ?? "linear";
|
|
854
|
+
if (curve === "step") {
|
|
855
|
+
path.moveTo(pts[0], pts[1]);
|
|
856
|
+
for (let i = 1; i < n; i++) {
|
|
857
|
+
const x = pts[2 * i];
|
|
858
|
+
path.lineTo(x, pts[2 * i - 1]);
|
|
859
|
+
path.lineTo(x, pts[2 * i + 1]);
|
|
860
|
+
}
|
|
861
|
+
} else if (curve === "smooth") smoothPath(path, pts, n);
|
|
862
|
+
else {
|
|
863
|
+
path.moveTo(pts[0], pts[1]);
|
|
864
|
+
for (let i = 1; i < n; i++) path.lineTo(pts[2 * i], pts[2 * i + 1]);
|
|
865
|
+
}
|
|
866
|
+
return path;
|
|
867
|
+
}
|
|
868
|
+
function renderCombo(r) {
|
|
869
|
+
if (r.data.kind !== "category") return;
|
|
870
|
+
const d = r.data;
|
|
871
|
+
const ctx = r.ctx;
|
|
872
|
+
ctx.save();
|
|
873
|
+
ctx.beginPath();
|
|
874
|
+
ctx.rect(r.plot.x, r.plot.y, r.plot.w, r.plot.h + 1);
|
|
875
|
+
ctx.clip();
|
|
876
|
+
const cats = d.categories.length;
|
|
877
|
+
const slot = r.plot.w / Math.max(1, cats);
|
|
878
|
+
const animate = r.t < 1;
|
|
879
|
+
const valAt = (s, ci) => {
|
|
880
|
+
const cur = s.values[ci] ?? 0;
|
|
881
|
+
const prev = s.prevValues?.[ci] ?? 0;
|
|
882
|
+
return animate ? prev + (cur - prev) * r.t : cur;
|
|
883
|
+
};
|
|
884
|
+
let nCols = 0;
|
|
885
|
+
for (const s of d.series) if (s.kind === "column") nCols++;
|
|
886
|
+
if (r.opts.stacked) {
|
|
887
|
+
const w = Math.min(slot * .8, 40);
|
|
888
|
+
for (let ci = 0; ci < cats; ci++) {
|
|
889
|
+
const x = r.plot.x + slot * ci + (slot - w) / 2;
|
|
890
|
+
let pos = 0;
|
|
891
|
+
let neg = 0;
|
|
892
|
+
for (const s of d.series) {
|
|
893
|
+
if (s.kind !== "column") continue;
|
|
894
|
+
const v = valAt(s, ci);
|
|
895
|
+
const v0 = v >= 0 ? pos : neg + v;
|
|
896
|
+
const v1 = v >= 0 ? pos + v : neg;
|
|
897
|
+
if (v >= 0) pos = v1;
|
|
898
|
+
else neg = v0;
|
|
899
|
+
const yTop = r.yScale(Math.max(v0, v1));
|
|
900
|
+
const h = Math.abs(r.yScale(Math.min(v0, v1)) - yTop);
|
|
901
|
+
if (h <= 0) continue;
|
|
902
|
+
ctx.fillStyle = s.color;
|
|
903
|
+
ctx.fillRect(x, yTop, w, h);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
} else {
|
|
907
|
+
const groupW = Math.min(slot * .8, 40 * nCols);
|
|
908
|
+
const barW = groupW / Math.max(1, nCols);
|
|
909
|
+
const base = r.yScale(0);
|
|
910
|
+
let k = 0;
|
|
911
|
+
for (const s of d.series) {
|
|
912
|
+
if (s.kind !== "column") continue;
|
|
913
|
+
ctx.fillStyle = s.color;
|
|
914
|
+
for (let ci = 0; ci < cats; ci++) {
|
|
915
|
+
const v = valAt(s, ci);
|
|
916
|
+
const x = r.plot.x + slot * ci + (slot - groupW) / 2 + barW * k;
|
|
917
|
+
const yv = r.yScale(v);
|
|
918
|
+
const y = Math.min(base, yv);
|
|
919
|
+
const h = Math.abs(base - yv);
|
|
920
|
+
if (h <= 0) continue;
|
|
921
|
+
topRounded(ctx, {
|
|
922
|
+
x,
|
|
923
|
+
y,
|
|
924
|
+
w: barW,
|
|
925
|
+
h
|
|
926
|
+
}, 3, v >= 0);
|
|
927
|
+
}
|
|
928
|
+
k++;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
const base = r.yScale(Math.max(0, r.yScale.domain[0]));
|
|
932
|
+
if (ovScratch.length < cats * 2) ovScratch = new Float64Array(cats * 2);
|
|
933
|
+
const pts = ovScratch;
|
|
934
|
+
for (const s of d.series) {
|
|
935
|
+
if (s.kind === "column") continue;
|
|
936
|
+
for (let ci = 0; ci < cats; ci++) {
|
|
937
|
+
pts[2 * ci] = r.plot.x + slot * (ci + .5);
|
|
938
|
+
pts[2 * ci + 1] = r.yScale(valAt(s, ci));
|
|
939
|
+
}
|
|
940
|
+
const path = overlayPath(r, pts, cats);
|
|
941
|
+
if (s.kind === "area" && cats > 1) {
|
|
942
|
+
const area = new Path2D(path);
|
|
943
|
+
area.lineTo(pts[2 * (cats - 1)], base);
|
|
944
|
+
area.lineTo(pts[0], base);
|
|
945
|
+
area.closePath();
|
|
946
|
+
const g = ctx.createLinearGradient(0, r.plot.y, 0, r.plot.y + r.plot.h);
|
|
947
|
+
g.addColorStop(0, withAlpha(s.color, .16));
|
|
948
|
+
g.addColorStop(1, withAlpha(s.color, 0));
|
|
949
|
+
ctx.fillStyle = g;
|
|
950
|
+
ctx.fill(area);
|
|
951
|
+
}
|
|
952
|
+
ctx.strokeStyle = s.color;
|
|
953
|
+
ctx.lineWidth = 2;
|
|
954
|
+
ctx.lineJoin = "round";
|
|
955
|
+
ctx.lineCap = "round";
|
|
956
|
+
ctx.stroke(path);
|
|
957
|
+
}
|
|
958
|
+
ctx.restore();
|
|
959
|
+
}
|
|
960
|
+
function hitCombo(r, px) {
|
|
961
|
+
if (r.data.kind !== "category") return null;
|
|
962
|
+
const d = r.data;
|
|
963
|
+
const cats = d.categories.length;
|
|
964
|
+
if (cats === 0 || px < r.plot.x || px > r.plot.x + r.plot.w) return null;
|
|
965
|
+
const cat = Math.min(cats - 1, Math.max(0, Math.floor((px - r.plot.x) / r.plot.w * cats)));
|
|
966
|
+
const slot = r.plot.w / cats;
|
|
967
|
+
let py = r.plot.y + r.plot.h;
|
|
968
|
+
const values = d.series.map((s) => {
|
|
969
|
+
const y = s.values[cat] ?? 0;
|
|
970
|
+
const sy = r.yScale(y);
|
|
971
|
+
if (sy < py) py = sy;
|
|
972
|
+
return {
|
|
973
|
+
label: s.label,
|
|
974
|
+
color: s.color,
|
|
975
|
+
y
|
|
976
|
+
};
|
|
977
|
+
});
|
|
978
|
+
return {
|
|
979
|
+
kind: "point",
|
|
980
|
+
index: cat,
|
|
981
|
+
x: cat,
|
|
982
|
+
px: r.plot.x + slot * (cat + .5),
|
|
983
|
+
py,
|
|
984
|
+
values,
|
|
985
|
+
title: d.categories[cat] ?? ""
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
//#endregion
|
|
848
989
|
//#region src/core/format.ts
|
|
849
|
-
/**
|
|
990
|
+
/** @category Formatting
|
|
991
|
+
* Compact number formatting: integers verbatim, else 4 significant digits. */
|
|
850
992
|
function formatNumber(v) {
|
|
851
993
|
if (!Number.isFinite(v)) return "";
|
|
852
994
|
if (Number.isInteger(v) && Math.abs(v) < 0x38d7ea4c68000) return String(v);
|
|
@@ -857,7 +999,8 @@ const MAGNITUDES = [
|
|
|
857
999
|
[1e6, "M"],
|
|
858
1000
|
[1e3, "k"]
|
|
859
1001
|
];
|
|
860
|
-
/**
|
|
1002
|
+
/** @category Formatting
|
|
1003
|
+
* SI-suffixed formatting: `1.2k`, `3.4M`, `5B`. */
|
|
861
1004
|
function formatSI(v) {
|
|
862
1005
|
if (!Number.isFinite(v)) return "";
|
|
863
1006
|
const abs = Math.abs(v);
|
|
@@ -1109,13 +1252,13 @@ function makeCanvas(cssW, cssH) {
|
|
|
1109
1252
|
c.height = Math.max(1, Math.round(cssH));
|
|
1110
1253
|
return c;
|
|
1111
1254
|
}
|
|
1112
|
-
function setup2d(canvas,
|
|
1113
|
-
const ctx = canvas.getContext("2d",
|
|
1255
|
+
function setup2d(canvas, settings) {
|
|
1256
|
+
const ctx = canvas.getContext("2d", settings);
|
|
1114
1257
|
if (!ctx) throw new Error("resolvcharts: could not get 2d context");
|
|
1115
1258
|
return ctx;
|
|
1116
1259
|
}
|
|
1117
1260
|
var CanvasBackend = class {
|
|
1118
|
-
constructor(container, cssW, cssH) {
|
|
1261
|
+
constructor(container, cssW, cssH, settings = {}) {
|
|
1119
1262
|
this.kind = "canvas2d";
|
|
1120
1263
|
this.plot = {
|
|
1121
1264
|
x: 0,
|
|
@@ -1126,14 +1269,18 @@ var CanvasBackend = class {
|
|
|
1126
1269
|
this.gridCache = null;
|
|
1127
1270
|
this.gridCtx = null;
|
|
1128
1271
|
this.container = container;
|
|
1272
|
+
this.settings = {
|
|
1273
|
+
alpha: true,
|
|
1274
|
+
...settings
|
|
1275
|
+
};
|
|
1129
1276
|
this.dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
1130
1277
|
this.width = cssW;
|
|
1131
1278
|
this.height = cssH;
|
|
1132
1279
|
this.mainCanvas = makeCanvas(cssW, cssH);
|
|
1133
1280
|
this.overlayCanvas = makeCanvas(cssW, cssH);
|
|
1134
1281
|
this.overlayCanvas.style.pointerEvents = "none";
|
|
1135
|
-
this.main = setup2d(this.mainCanvas,
|
|
1136
|
-
this.overlay = setup2d(this.overlayCanvas,
|
|
1282
|
+
this.main = setup2d(this.mainCanvas, this.settings);
|
|
1283
|
+
this.overlay = setup2d(this.overlayCanvas, this.settings);
|
|
1137
1284
|
container.append(this.mainCanvas, this.overlayCanvas);
|
|
1138
1285
|
this.applySize();
|
|
1139
1286
|
}
|
|
@@ -1157,7 +1304,7 @@ var CanvasBackend = class {
|
|
|
1157
1304
|
this.gridCache = document.createElement("canvas");
|
|
1158
1305
|
this.gridCache.width = Math.max(1, this.width * this.dpr);
|
|
1159
1306
|
this.gridCache.height = Math.max(1, this.height * this.dpr);
|
|
1160
|
-
this.gridCtx = setup2d(this.gridCache,
|
|
1307
|
+
this.gridCtx = setup2d(this.gridCache, this.settings);
|
|
1161
1308
|
this.gridCtx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
|
|
1162
1309
|
}
|
|
1163
1310
|
const ctx = this.gridCtx;
|
|
@@ -1192,7 +1339,8 @@ var CanvasBackend = class {
|
|
|
1192
1339
|
};
|
|
1193
1340
|
//#endregion
|
|
1194
1341
|
//#region src/core/scales.ts
|
|
1195
|
-
/**
|
|
1342
|
+
/** @category Utilities
|
|
1343
|
+
* Linear scale from domain `[d0, d1]` to range `[r0, r1]`, with `invert`. */
|
|
1196
1344
|
function linearScale(d0, d1, r0, r1) {
|
|
1197
1345
|
const dd = d1 - d0 || 1;
|
|
1198
1346
|
const dr = r1 - r0;
|
|
@@ -1202,7 +1350,8 @@ function linearScale(d0, d1, r0, r1) {
|
|
|
1202
1350
|
fn.range = [r0, r1];
|
|
1203
1351
|
return fn;
|
|
1204
1352
|
}
|
|
1205
|
-
/**
|
|
1353
|
+
/** @category Utilities
|
|
1354
|
+
* `[min, max]` of `values`; degenerate inputs get a sane fallback domain. */
|
|
1206
1355
|
function extent(values) {
|
|
1207
1356
|
let min = Infinity;
|
|
1208
1357
|
let max = -Infinity;
|
|
@@ -1215,7 +1364,8 @@ function extent(values) {
|
|
|
1215
1364
|
if (min === max) return min === 0 ? [0, 1] : [min - Math.abs(min) * .1, max + Math.abs(max) * .1];
|
|
1216
1365
|
return [min, max];
|
|
1217
1366
|
}
|
|
1218
|
-
/**
|
|
1367
|
+
/** @category Utilities
|
|
1368
|
+
* Pad a domain by 5% on each end; `includeZero` anchors the axis at 0. */
|
|
1219
1369
|
function padDomain(min, max, includeZero = false) {
|
|
1220
1370
|
let lo = includeZero ? Math.min(0, min) : min;
|
|
1221
1371
|
let hi = includeZero ? Math.max(0, max) : max;
|
|
@@ -1269,6 +1419,9 @@ const easeOutCubic = (t) => 1 - (1 - t) ** 3;
|
|
|
1269
1419
|
//#endregion
|
|
1270
1420
|
//#region src/core/theme.ts
|
|
1271
1421
|
const FONT = "Inter, system-ui, -apple-system, \"Segoe UI\", sans-serif";
|
|
1422
|
+
/** Built-in light theme.
|
|
1423
|
+
* @category Theming
|
|
1424
|
+
*/
|
|
1272
1425
|
const lightTheme = {
|
|
1273
1426
|
bg: "#ffffff",
|
|
1274
1427
|
text: "#1f1f1f",
|
|
@@ -1287,6 +1440,9 @@ const lightTheme = {
|
|
|
1287
1440
|
tooltipShadow: "0 4px 14px rgba(0,0,0,0.08)",
|
|
1288
1441
|
fontFamily: FONT
|
|
1289
1442
|
};
|
|
1443
|
+
/** Built-in dark theme.
|
|
1444
|
+
* @category Theming
|
|
1445
|
+
*/
|
|
1290
1446
|
const darkTheme = {
|
|
1291
1447
|
bg: "#0a0a0a",
|
|
1292
1448
|
text: "#ededed",
|
|
@@ -1317,6 +1473,9 @@ const categoricalDark = [
|
|
|
1317
1473
|
"#a97ef0",
|
|
1318
1474
|
"#f5b84d"
|
|
1319
1475
|
];
|
|
1476
|
+
/** Merge a theme option (`"light" | "dark" | partial tokens`) into full {@link ThemeTokens}.
|
|
1477
|
+
* @category Theming
|
|
1478
|
+
*/
|
|
1320
1479
|
function resolveTheme(opt, accent) {
|
|
1321
1480
|
const base = opt === "dark" ? {
|
|
1322
1481
|
...darkTheme,
|
|
@@ -1357,6 +1516,9 @@ function niceStep(range, target) {
|
|
|
1357
1516
|
}
|
|
1358
1517
|
return step * pow;
|
|
1359
1518
|
}
|
|
1519
|
+
/** "Nice" round tick values covering `[min, max]`, ~`target` count.
|
|
1520
|
+
* @category Utilities
|
|
1521
|
+
*/
|
|
1360
1522
|
function computeTicks(min, max, target = 5) {
|
|
1361
1523
|
if (!(min < max)) return [min];
|
|
1362
1524
|
const step = niceStep(max - min, target);
|
|
@@ -1479,6 +1641,12 @@ const MODULES = {
|
|
|
1479
1641
|
axes: true,
|
|
1480
1642
|
defaultTooltip: true
|
|
1481
1643
|
},
|
|
1644
|
+
combo: {
|
|
1645
|
+
render: renderCombo,
|
|
1646
|
+
hit: (r, px) => hitCombo(r, px),
|
|
1647
|
+
axes: true,
|
|
1648
|
+
defaultTooltip: true
|
|
1649
|
+
},
|
|
1482
1650
|
sparkline: {
|
|
1483
1651
|
render: renderSparkline,
|
|
1484
1652
|
hit: (r, px) => hitXY(r, px),
|
|
@@ -1527,6 +1695,9 @@ const GPU_KIND = {
|
|
|
1527
1695
|
};
|
|
1528
1696
|
/** points retained beyond the window's left edge so they slide off-screen instead of vanishing mid-plot */
|
|
1529
1697
|
const EDGE_KEEP = 3;
|
|
1698
|
+
/** Chart instance. Create via {@link createChart}.
|
|
1699
|
+
* @category Charts
|
|
1700
|
+
*/
|
|
1530
1701
|
var Chart = class {
|
|
1531
1702
|
constructor(el, options) {
|
|
1532
1703
|
this.tooltip = null;
|
|
@@ -1607,7 +1778,7 @@ var Chart = class {
|
|
|
1607
1778
|
if (options.height) el.style.height = `${options.height}px`;
|
|
1608
1779
|
const w = el.clientWidth || 320;
|
|
1609
1780
|
const h = el.clientHeight || (options.type === "sparkline" ? 32 : 240);
|
|
1610
|
-
this.backend = new CanvasBackend(el, w, h);
|
|
1781
|
+
this.backend = new CanvasBackend(el, w, h, options.canvas);
|
|
1611
1782
|
this.reduced = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1612
1783
|
if (options.tooltip ?? this.mod.defaultTooltip) this.tooltip = new Tooltip(el, this.theme);
|
|
1613
1784
|
this.data = this.normalize(options.data, null);
|
|
@@ -1718,7 +1889,7 @@ var Chart = class {
|
|
|
1718
1889
|
total: slices.reduce((a, s) => a + Math.max(0, s.value), 0)
|
|
1719
1890
|
};
|
|
1720
1891
|
}
|
|
1721
|
-
if (t === "bar") {
|
|
1892
|
+
if (t === "bar" || t === "combo") {
|
|
1722
1893
|
const d = data;
|
|
1723
1894
|
const prevSeries = prev?.kind === "category" ? prev.series : [];
|
|
1724
1895
|
return {
|
|
@@ -1727,6 +1898,7 @@ var Chart = class {
|
|
|
1727
1898
|
series: d.series.map((s, i) => ({
|
|
1728
1899
|
label: s.label ?? `Series ${i + 1}`,
|
|
1729
1900
|
color: seriesColor(this.theme, i, s.color),
|
|
1901
|
+
kind: s.kind ?? "column",
|
|
1730
1902
|
values: Float64Array.from(s.values),
|
|
1731
1903
|
prevValues: prevSeries[i]?.values ?? null
|
|
1732
1904
|
}))
|
|
@@ -1835,18 +2007,26 @@ var Chart = class {
|
|
|
1835
2007
|
if (d.kind === "category") {
|
|
1836
2008
|
let min = Infinity;
|
|
1837
2009
|
let max = -Infinity;
|
|
1838
|
-
if (this.options.stacked)
|
|
1839
|
-
let
|
|
1840
|
-
|
|
2010
|
+
if (this.options.stacked) {
|
|
2011
|
+
for (let ci = 0; ci < d.categories.length; ci++) {
|
|
2012
|
+
let pos = 0;
|
|
2013
|
+
let neg = 0;
|
|
2014
|
+
for (const s of d.series) {
|
|
2015
|
+
if (s.kind !== "column") continue;
|
|
2016
|
+
const v = s.values[ci] ?? 0;
|
|
2017
|
+
if (v >= 0) pos += v;
|
|
2018
|
+
else neg += v;
|
|
2019
|
+
}
|
|
2020
|
+
min = Math.min(min, neg);
|
|
2021
|
+
max = Math.max(max, pos);
|
|
2022
|
+
}
|
|
1841
2023
|
for (const s of d.series) {
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2024
|
+
if (s.kind === "column") continue;
|
|
2025
|
+
const e = extent(s.values);
|
|
2026
|
+
min = Math.min(min, e[0]);
|
|
2027
|
+
max = Math.max(max, e[1]);
|
|
1845
2028
|
}
|
|
1846
|
-
|
|
1847
|
-
max = Math.max(max, pos);
|
|
1848
|
-
}
|
|
1849
|
-
else for (const s of d.series) {
|
|
2029
|
+
} else for (const s of d.series) {
|
|
1850
2030
|
const e = extent(s.values);
|
|
1851
2031
|
min = Math.min(min, e[0]);
|
|
1852
2032
|
max = Math.max(max, e[1]);
|
|
@@ -1980,7 +2160,7 @@ var Chart = class {
|
|
|
1980
2160
|
ctx.fillText(fmtX(t), px, plot.y + plot.h + 6);
|
|
1981
2161
|
}
|
|
1982
2162
|
}
|
|
1983
|
-
if (this.options.type === "bar") {
|
|
2163
|
+
if (this.options.type === "bar" || this.options.type === "combo") {
|
|
1984
2164
|
const base = Math.round(yScale(0)) + .5;
|
|
1985
2165
|
if (base >= plot.y && base <= plot.y + plot.h + 1) {
|
|
1986
2166
|
ctx.strokeStyle = this.theme.axisBaseline;
|
|
@@ -2041,9 +2221,9 @@ var Chart = class {
|
|
|
2041
2221
|
o.lineTo(x, plot.y + plot.h);
|
|
2042
2222
|
o.stroke();
|
|
2043
2223
|
const d = this.data;
|
|
2044
|
-
if (d.kind === "xy"
|
|
2224
|
+
if (info.kind === "point" && (d.kind === "xy" || d.kind === "category")) for (const v of info.values) {
|
|
2045
2225
|
const s = d.series.find((s2) => s2.label === v.label && s2.color === v.color);
|
|
2046
|
-
const yv = s ? s.y[info.index] : v.y;
|
|
2226
|
+
const yv = s ? "y" in s ? s.y[info.index] : s.values[info.index] : v.y;
|
|
2047
2227
|
o.fillStyle = v.color;
|
|
2048
2228
|
o.strokeStyle = this.theme.bg;
|
|
2049
2229
|
o.lineWidth = 2;
|
|
@@ -2105,7 +2285,7 @@ var Chart = class {
|
|
|
2105
2285
|
color: v.color,
|
|
2106
2286
|
label: v.label,
|
|
2107
2287
|
value: fmtY(v.y)
|
|
2108
|
-
})), fmtX(info.x));
|
|
2288
|
+
})), info.title ?? fmtX(info.x));
|
|
2109
2289
|
} else if (info.kind === "bar") this.tooltip.show(info.px, info.py, [{
|
|
2110
2290
|
color: info.color,
|
|
2111
2291
|
label: info.label,
|
|
@@ -2282,6 +2462,7 @@ function resortXY(d) {
|
|
|
2282
2462
|
}
|
|
2283
2463
|
/**
|
|
2284
2464
|
* Create a chart inside `el`.
|
|
2465
|
+
* @category Charts
|
|
2285
2466
|
*
|
|
2286
2467
|
* @example
|
|
2287
2468
|
* ```ts
|