bezel-charts 1.4.0 → 1.6.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 +48 -7
- package/dist/bezel-charts.iife.js +18 -11
- package/dist/bezel-charts.js +367 -22
- package/dist/bezel-charts.min.js +18 -11
- package/dist/charts/area.js +2 -2
- package/dist/charts/bars.js +2 -2
- package/dist/charts/boxplot.js +8 -0
- package/dist/charts/donut.js +2 -2
- package/dist/charts/gauge.js +2 -2
- package/dist/charts/heatmap.js +2 -2
- package/dist/charts/histogram.js +3 -0
- package/dist/charts/line.js +2 -2
- package/dist/charts/progress.js +2 -2
- package/dist/charts/ring.js +2 -2
- package/dist/charts/scatter.js +2 -2
- package/dist/charts/{shared-APAAEWNX.js → shared-56ZZ4O3F.js} +2 -2
- package/dist/charts/{shared-XBZ5QGIC.js → shared-CYFOZG36.js} +2 -2
- package/dist/charts/shared-YIDF72XF.js +2 -0
- package/dist/charts/spark.js +2 -2
- package/package.json +7 -3
- package/src/charts/boxplot.js +134 -0
- package/src/charts/histogram.js +182 -0
- package/src/element.js +1 -1
- package/src/entries/boxplot.js +10 -0
- package/src/entries/histogram.js +10 -0
- package/src/index.js +3 -1
- package/src/render.js +1 -1
- package/dist/charts/shared-4QPUIMAU.js +0 -2
package/dist/bezel-charts.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Bezel Charts v1.
|
|
1
|
+
/*! Bezel Charts v1.6.0 | MIT | github.com/ShazMoghaddam/bezel-charts */
|
|
2
2
|
|
|
3
3
|
// src/core/util.js
|
|
4
4
|
var SANS = "ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif";
|
|
@@ -268,7 +268,7 @@ var THEMES = {
|
|
|
268
268
|
};
|
|
269
269
|
|
|
270
270
|
// src/render.js
|
|
271
|
-
var NAMES = ["spark", "line", "area", "bars", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
|
|
271
|
+
var NAMES = ["spark", "line", "area", "bars", "histogram", "boxplot", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
|
|
272
272
|
var TYPES = {};
|
|
273
273
|
function register(type, fn) {
|
|
274
274
|
TYPES[type] = fn;
|
|
@@ -346,7 +346,12 @@ var ATTRIBUTES = [
|
|
|
346
346
|
"angle-labels",
|
|
347
347
|
"dash",
|
|
348
348
|
"markers",
|
|
349
|
-
"patterns"
|
|
349
|
+
"patterns",
|
|
350
|
+
"bins",
|
|
351
|
+
"bin-width",
|
|
352
|
+
"mark",
|
|
353
|
+
"percent",
|
|
354
|
+
"whiskers"
|
|
350
355
|
];
|
|
351
356
|
var HOST_CSS = `:host{display:block;position:relative;max-width:100%}:host([hidden]){display:none}svg{display:block;max-width:100%;height:auto;overflow:visible}.tip{position:absolute;left:0;top:0;pointer-events:none;background:#0b0b10;color:#f2f1f7;font:600 12px/1.5 ${MONO};padding:7px 10px;border-radius:10px;white-space:pre;opacity:0;transform:translate(-50%,calc(-100% - 12px));transition:opacity .12s;z-index:3;border:1px solid #2a2935;box-shadow:0 8px 24px rgba(0,0,0,.35)}.tip.on{opacity:1}.c{outline:none}.c:focus-visible{outline:2px solid #fff;outline-offset:3px;box-shadow:0 0 0 7px #000}.k{stroke:currentColor;stroke-width:2;stroke-opacity:1}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media (forced-colors:active){.c:focus-visible{outline-color:Highlight}}`;
|
|
352
357
|
var STEPS = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 };
|
|
@@ -635,7 +640,7 @@ function axisWidth(ticks) {
|
|
|
635
640
|
return tw(t, 10, true);
|
|
636
641
|
})) + 10;
|
|
637
642
|
}
|
|
638
|
-
function seriesTable(o,
|
|
643
|
+
function seriesTable(o, total2) {
|
|
639
644
|
var S = series(o.values), names = list(o.series), labels = list(o.labels), rows = [];
|
|
640
645
|
var n = Math.max.apply(null, [0].concat(S.map(function(s) {
|
|
641
646
|
return s.length;
|
|
@@ -646,13 +651,13 @@ function seriesTable(o, total) {
|
|
|
646
651
|
row.push(i < s.length ? fmt(s[i], o) : "");
|
|
647
652
|
t += i < s.length ? s[i] : 0;
|
|
648
653
|
});
|
|
649
|
-
if (
|
|
654
|
+
if (total2) row.push(fmt(t, o));
|
|
650
655
|
rows.push(row);
|
|
651
656
|
}
|
|
652
657
|
var head = S.length > 1 || names.length ? S.map(function(_, k) {
|
|
653
658
|
return names[k] || "Series " + (k + 1);
|
|
654
659
|
}) : ["Value"];
|
|
655
|
-
return { head: ["Label"].concat(head,
|
|
660
|
+
return { head: ["Label"].concat(head, total2 ? ["Total"] : []), rows };
|
|
656
661
|
}
|
|
657
662
|
|
|
658
663
|
// src/core/xy.js
|
|
@@ -1164,8 +1169,348 @@ bars.table = function(o) {
|
|
|
1164
1169
|
register("bars", bars);
|
|
1165
1170
|
define("bars");
|
|
1166
1171
|
|
|
1172
|
+
// src/charts/histogram.js
|
|
1173
|
+
function histogram(o, th) {
|
|
1174
|
+
var b = binned(o), names = list(o.series), pal = palette(o, th), h = num(o.height, 190);
|
|
1175
|
+
var S = b ? b.S : [], k = S.length, multi = k > 1 || names.length > 0, pct = bool(o.percent);
|
|
1176
|
+
var nameOf = function(j) {
|
|
1177
|
+
return names[j] || "Series " + (j + 1);
|
|
1178
|
+
};
|
|
1179
|
+
var eo = Object.assign({}, o, { decimals: has(o.decimals) ? o.decimals : b ? b.dp : 0 });
|
|
1180
|
+
var range = function(i2) {
|
|
1181
|
+
return fmt(b.edge(i2), eo) + " to " + fmt(b.edge(i2 + 1), eo);
|
|
1182
|
+
};
|
|
1183
|
+
var share = function(j, i2) {
|
|
1184
|
+
return Math.round(b.counts[j][i2] / S[j].length * 100);
|
|
1185
|
+
};
|
|
1186
|
+
var height = function(j, i2) {
|
|
1187
|
+
return pct ? b.counts[j][i2] / S[j].length * 100 : b.counts[j][i2];
|
|
1188
|
+
};
|
|
1189
|
+
var marks = list(o.mark).filter(function(m) {
|
|
1190
|
+
return m === "mean" || m === "median";
|
|
1191
|
+
});
|
|
1192
|
+
var patOf = k > 1 ? styleOf(o.patterns, PATTERNS, "solid") : null, defs = "", fills = [];
|
|
1193
|
+
for (var q = 0; patOf && q < k; q++) {
|
|
1194
|
+
if (patOf(q) === "solid") continue;
|
|
1195
|
+
var pid = newId("g");
|
|
1196
|
+
defs += pattern(pid, patOf(q), pal[q % pal.length], o.background || th.tile);
|
|
1197
|
+
fills[q] = "url(#" + pid + ")";
|
|
1198
|
+
}
|
|
1199
|
+
var key = patOf && function(j, x, y) {
|
|
1200
|
+
return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + (fills[j] || pal[j % pal.length]) + '"/>';
|
|
1201
|
+
};
|
|
1202
|
+
var peak = 0, n = 0;
|
|
1203
|
+
if (b) {
|
|
1204
|
+
for (var i = 0; i < b.count; i++) if (total(b, i) > total(b, peak)) peak = i;
|
|
1205
|
+
S.forEach(function(s) {
|
|
1206
|
+
n += s.length;
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
1210
|
+
if (!b) return emptyState(th, x0, y0, w, h2);
|
|
1211
|
+
var s = defs ? "<defs>" + defs + "</defs>" : "", top = y0;
|
|
1212
|
+
if (multi) {
|
|
1213
|
+
var lg = legend(S.map(function(_, j) {
|
|
1214
|
+
return nameOf(j);
|
|
1215
|
+
}), pal, th, x0, top, w, key, key && 15);
|
|
1216
|
+
s += lg.svg;
|
|
1217
|
+
top += lg.height;
|
|
1218
|
+
}
|
|
1219
|
+
var most = 0;
|
|
1220
|
+
S.forEach(function(_, j) {
|
|
1221
|
+
for (var i2 = 0; i2 < b.count; i2++) most = Math.max(most, height(j, i2));
|
|
1222
|
+
});
|
|
1223
|
+
var sc = niceScale(0, most || 1, 4);
|
|
1224
|
+
var tl = sc.ticks.map(function(t) {
|
|
1225
|
+
return fmt(t, { lang: o.lang }, true) + (pct ? "%" : "");
|
|
1226
|
+
});
|
|
1227
|
+
var axisW = Math.max.apply(null, tl.map(function(t) {
|
|
1228
|
+
return tw(t, 10, true);
|
|
1229
|
+
})) + 10;
|
|
1230
|
+
var px = x0 + axisW, pw = w - axisW, ph = h2 - (top - y0) - 20, cw = pw / b.count;
|
|
1231
|
+
var X = function(i2) {
|
|
1232
|
+
return px + i2 * cw;
|
|
1233
|
+
};
|
|
1234
|
+
var Y = function(v) {
|
|
1235
|
+
return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
|
|
1236
|
+
};
|
|
1237
|
+
sc.ticks.forEach(function(t, i2) {
|
|
1238
|
+
var y = Y(t);
|
|
1239
|
+
s += '<line x1="' + r2(px) + '" x2="' + r2(px + pw) + '" y1="' + r2(y) + '" y2="' + r2(y) + '" stroke="' + th.grid + '"' + (t === 0 ? "" : ' stroke-dasharray="2 4"') + "/>" + text(px - 8, y + 3.5, tl[i2], small(th, 10, 'text-anchor="end"'));
|
|
1240
|
+
});
|
|
1241
|
+
var el = [];
|
|
1242
|
+
for (var e = 0; e <= b.count; e++) el.push(fmt(b.edge(e), eo, true));
|
|
1243
|
+
var every = Math.max(1, Math.ceil((Math.max.apply(null, el.map(function(l) {
|
|
1244
|
+
return tw(l, 10, true);
|
|
1245
|
+
})) * 1.5 + 8) / cw));
|
|
1246
|
+
for (e = 0; e <= b.count; e += every) {
|
|
1247
|
+
s += text(X(e), top + ph + 15, el[e], small(th, 10, 'text-anchor="' + (e === 0 ? "start" : e === b.count ? "end" : "middle") + '"'));
|
|
1248
|
+
}
|
|
1249
|
+
S.forEach(function(_, j) {
|
|
1250
|
+
for (var i2 = 0; i2 < b.count; i2++) {
|
|
1251
|
+
var v = height(j, i2);
|
|
1252
|
+
if (!v) continue;
|
|
1253
|
+
var y = Y(v);
|
|
1254
|
+
s += '<rect x="' + r2(X(i2) + 0.5) + '" y="' + r2(y) + '" width="' + r2(Math.max(1, cw - 1)) + '" height="' + r2(top + ph - y) + '" fill="' + (fills[j] || pal[j % pal.length]) + '"' + (multi ? ' fill-opacity=".55"' : "") + anim(o, "bzc-g", Math.min(0.6, i2 * 0.03)) + "/>";
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
var lo = b.edge(0), span = b.edge(b.count) - lo, lines = [];
|
|
1258
|
+
S.forEach(function(ser, j) {
|
|
1259
|
+
marks.forEach(function(m) {
|
|
1260
|
+
var v = m === "mean" ? mean(ser) : median(ser);
|
|
1261
|
+
lines.push({ x: px + (v - lo) / span * pw, c: multi ? pal[j % pal.length] : th.fg, t: (multi ? nameOf(j) + " " : "") + m + " " + fmt(v, o) });
|
|
1262
|
+
});
|
|
1263
|
+
});
|
|
1264
|
+
if (lines.length) {
|
|
1265
|
+
var xs = lines.map(function(l) {
|
|
1266
|
+
return l.x;
|
|
1267
|
+
}), left = (Math.min.apply(null, xs) + Math.max.apply(null, xs)) / 2 > px + pw / 2;
|
|
1268
|
+
var lx = left ? Math.min.apply(null, xs) - 5 : Math.max.apply(null, xs) + 5;
|
|
1269
|
+
lines.forEach(function(l, r) {
|
|
1270
|
+
s += '<line x1="' + r2(l.x) + '" x2="' + r2(l.x) + '" y1="' + r2(top) + '" y2="' + r2(top + ph) + '" stroke="' + l.c + '" stroke-width="1.5" stroke-dasharray="4 3"/>' + text(lx, top + 9 + r * 12, l.t, small(th, 10, 'text-anchor="' + (left ? "end" : "start") + '"', multi ? l.c : th.fg));
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
for (i = 0; i < b.count; i++) {
|
|
1274
|
+
var tip = range(i);
|
|
1275
|
+
S.forEach(function(_, j) {
|
|
1276
|
+
tip += "\n" + (multi ? nameOf(j) + ": " : "") + b.counts[j][i] + " (" + share(j, i) + "%)";
|
|
1277
|
+
});
|
|
1278
|
+
s += hit(X(i), top, cw, ph, tip);
|
|
1279
|
+
}
|
|
1280
|
+
return s;
|
|
1281
|
+
}, {
|
|
1282
|
+
summary: b ? (multi ? k + " series, " : "") + n + " values in " + b.count + " ranges from " + fmt(b.edge(0), eo) + " to " + fmt(b.edge(b.count), eo) + ", most in " + range(peak) : "no data"
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
histogram.table = function(o) {
|
|
1286
|
+
var b = binned(o), names = list(o.series);
|
|
1287
|
+
if (!b) return { head: ["Range", "Count"], rows: [] };
|
|
1288
|
+
var multi = b.S.length > 1 || names.length > 0, eo = Object.assign({}, o, { decimals: has(o.decimals) ? o.decimals : b.dp }), rows = [];
|
|
1289
|
+
for (var i = 0; i < b.count; i++) {
|
|
1290
|
+
var row = [fmt(b.edge(i), eo) + " to " + fmt(b.edge(i + 1), eo)];
|
|
1291
|
+
if (multi) b.S.forEach(function(_, j) {
|
|
1292
|
+
row.push(String(b.counts[j][i]));
|
|
1293
|
+
});
|
|
1294
|
+
else row.push(String(b.counts[0][i]), Math.round(b.counts[0][i] / b.S[0].length * 100) + "%");
|
|
1295
|
+
rows.push(row);
|
|
1296
|
+
}
|
|
1297
|
+
return { head: ["Range"].concat(multi ? b.S.map(function(_, j) {
|
|
1298
|
+
return names[j] || "Series " + (j + 1);
|
|
1299
|
+
}) : ["Count", "Share"]), rows };
|
|
1300
|
+
};
|
|
1301
|
+
function niceWidth(raw) {
|
|
1302
|
+
var e = Math.floor(Math.log10(raw)), p = Number("1e" + e), f = raw / p;
|
|
1303
|
+
var m = f < 1.4142 ? 1 : f < 2.2361 ? 2 : f < 3.5355 ? 2.5 : f < 7.0711 ? 5 : 10;
|
|
1304
|
+
return { w: m * p, dp: Math.max(0, m === 10 ? -e - 1 : -e) + (m === 2.5 ? 1 : 0) };
|
|
1305
|
+
}
|
|
1306
|
+
function binned(o) {
|
|
1307
|
+
var S = series(o.values), all = [].concat.apply([], S);
|
|
1308
|
+
if (!all.length) return null;
|
|
1309
|
+
var lo = Math.min.apply(null, all), hi = Math.max.apply(null, all), w = num(o.binWidth, 0), dp, nice;
|
|
1310
|
+
if (w > 0 && (hi - lo) / w <= 200) {
|
|
1311
|
+
var str = String(o.binWidth), dot = str.indexOf(".");
|
|
1312
|
+
dp = dot < 0 ? 0 : Math.min(6, str.length - dot - 1);
|
|
1313
|
+
} else {
|
|
1314
|
+
var k = num(o.bins, 0) > 0 ? Math.round(num(o.bins, 0)) : Math.ceil(Math.log2(all.length) + 1);
|
|
1315
|
+
k = Math.min(Math.max(k, 1), 50);
|
|
1316
|
+
nice = niceWidth(hi > lo ? (hi - lo) / k : Math.abs(lo) || 1);
|
|
1317
|
+
w = nice.w;
|
|
1318
|
+
dp = nice.dp;
|
|
1319
|
+
}
|
|
1320
|
+
var s0 = Math.floor(lo / w);
|
|
1321
|
+
var edge = function(i) {
|
|
1322
|
+
return Number(((s0 + i) * w).toFixed(dp + 3));
|
|
1323
|
+
};
|
|
1324
|
+
while (edge(0) > lo) s0--;
|
|
1325
|
+
var count = Math.max(1, Math.ceil((hi - edge(0)) / w));
|
|
1326
|
+
while (count > 1 && edge(count - 1) >= hi) count--;
|
|
1327
|
+
while (edge(count) < hi) count++;
|
|
1328
|
+
var counts = S.map(function(ser) {
|
|
1329
|
+
var c = [];
|
|
1330
|
+
for (var i = 0; i < count; i++) c.push(0);
|
|
1331
|
+
ser.forEach(function(x) {
|
|
1332
|
+
var i2 = Math.min(count - 1, Math.max(0, Math.floor((x - edge(0)) / w)));
|
|
1333
|
+
while (i2 > 0 && x < edge(i2)) i2--;
|
|
1334
|
+
while (i2 < count - 1 && x >= edge(i2 + 1)) i2++;
|
|
1335
|
+
c[i2]++;
|
|
1336
|
+
});
|
|
1337
|
+
return c;
|
|
1338
|
+
});
|
|
1339
|
+
return { S, edge, count, counts, dp };
|
|
1340
|
+
}
|
|
1341
|
+
function total(b, i) {
|
|
1342
|
+
var t = 0;
|
|
1343
|
+
b.counts.forEach(function(c) {
|
|
1344
|
+
t += c[i];
|
|
1345
|
+
});
|
|
1346
|
+
return t;
|
|
1347
|
+
}
|
|
1348
|
+
function mean(v) {
|
|
1349
|
+
var t = 0;
|
|
1350
|
+
v.forEach(function(x) {
|
|
1351
|
+
t += x;
|
|
1352
|
+
});
|
|
1353
|
+
return t / v.length;
|
|
1354
|
+
}
|
|
1355
|
+
function median(v) {
|
|
1356
|
+
var s = v.slice().sort(function(a, b) {
|
|
1357
|
+
return a - b;
|
|
1358
|
+
}), m = Math.floor(s.length / 2);
|
|
1359
|
+
return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// src/entries/histogram.js
|
|
1363
|
+
register("histogram", histogram);
|
|
1364
|
+
define("histogram");
|
|
1365
|
+
|
|
1366
|
+
// src/charts/boxplot.js
|
|
1367
|
+
function boxplot(o, th) {
|
|
1368
|
+
var S = series(o.values), names = list(o.series), pal = palette(o, th), k = S.length;
|
|
1369
|
+
var horiz = bool(o.horizontal), many = list(o.color).length > 1, showMean = list(o.mark).indexOf("mean") >= 0;
|
|
1370
|
+
var stats = S.map(function(v) {
|
|
1371
|
+
return summary(v, o.whiskers === "range");
|
|
1372
|
+
});
|
|
1373
|
+
var nameOf = function(i) {
|
|
1374
|
+
return names[i] || "Group " + (i + 1);
|
|
1375
|
+
};
|
|
1376
|
+
var h = num(o.height, horiz ? Math.max(120, k * 36 + 24) : 200);
|
|
1377
|
+
var f = function(v) {
|
|
1378
|
+
return fmt(v, o);
|
|
1379
|
+
};
|
|
1380
|
+
var meds = stats.map(function(s) {
|
|
1381
|
+
return s.median;
|
|
1382
|
+
});
|
|
1383
|
+
var about = k === 1 ? "median " + f(stats[0].median) + ", middle half " + f(stats[0].q1) + " to " + f(stats[0].q3) : k + " groups, medians from " + f(Math.min.apply(null, meds)) + " to " + f(Math.max.apply(null, meds));
|
|
1384
|
+
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
1385
|
+
if (!k) return emptyState(th, x0, y0, w, h2);
|
|
1386
|
+
var lo = Math.min.apply(null, stats.map(function(s2) {
|
|
1387
|
+
return s2.min;
|
|
1388
|
+
})), hi = Math.max.apply(null, stats.map(function(s2) {
|
|
1389
|
+
return s2.max;
|
|
1390
|
+
}));
|
|
1391
|
+
if (has(o.goal)) {
|
|
1392
|
+
lo = Math.min(lo, +o.goal);
|
|
1393
|
+
hi = Math.max(hi, +o.goal);
|
|
1394
|
+
}
|
|
1395
|
+
var sc = niceScale(lo, hi, 4), s = "";
|
|
1396
|
+
var tl = sc.ticks.map(function(t) {
|
|
1397
|
+
return fmt(t, o, true);
|
|
1398
|
+
});
|
|
1399
|
+
var nameW = horiz ? Math.min(w * 0.4, Math.max.apply(null, S.map(function(_, i) {
|
|
1400
|
+
return tw(nameOf(i), 11);
|
|
1401
|
+
})) + 12) : 0;
|
|
1402
|
+
var axisW = horiz ? 0 : Math.max.apply(null, tl.map(function(t) {
|
|
1403
|
+
return tw(t, 10, true);
|
|
1404
|
+
})) + 10;
|
|
1405
|
+
var px = x0 + (horiz ? nameW : axisW), pw = w - (horiz ? nameW : axisW), ph = h2 - 20;
|
|
1406
|
+
var V = horiz ? function(v) {
|
|
1407
|
+
return px + (v - sc.lo) / (sc.hi - sc.lo) * pw;
|
|
1408
|
+
} : function(v) {
|
|
1409
|
+
return y0 + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
|
|
1410
|
+
};
|
|
1411
|
+
var band = (horiz ? ph : pw) / k, half = Math.min(28, band * 0.28);
|
|
1412
|
+
var C = function(i) {
|
|
1413
|
+
return (horiz ? y0 : px) + band * (i + 0.5);
|
|
1414
|
+
};
|
|
1415
|
+
var P = function(c, v) {
|
|
1416
|
+
return horiz ? [v, c] : [c, v];
|
|
1417
|
+
};
|
|
1418
|
+
var seg = function(c1, v1, c2, v2, attrs) {
|
|
1419
|
+
var a = P(c1, v1), b = P(c2, v2);
|
|
1420
|
+
return '<line x1="' + r2(a[0]) + '" y1="' + r2(a[1]) + '" x2="' + r2(b[0]) + '" y2="' + r2(b[1]) + '"' + attrs + "/>";
|
|
1421
|
+
};
|
|
1422
|
+
sc.ticks.forEach(function(t, i) {
|
|
1423
|
+
var v = V(t);
|
|
1424
|
+
s += seg(horiz ? y0 : px, v, horiz ? y0 + ph : px + pw, v, ' stroke="' + th.grid + '"' + (t === 0 ? "" : ' stroke-dasharray="2 4"')) + (horiz ? text(v, y0 + ph + 15, tl[i], small(th, 10, 'text-anchor="middle"')) : text(px - 8, v + 3.5, tl[i], small(th, 10, 'text-anchor="end"')));
|
|
1425
|
+
});
|
|
1426
|
+
stats.forEach(function(st, i) {
|
|
1427
|
+
var c = C(i), col = many ? pal[i % pal.length] : pal[0], line2 = ' stroke="' + col + '" stroke-width="1.5"';
|
|
1428
|
+
if (k > 1 || names.length) {
|
|
1429
|
+
var label = fit(nameOf(i), horiz ? nameW - 10 : band - 6, horiz ? 11 : 10, !horiz);
|
|
1430
|
+
s += horiz ? text(x0, c + 4, label, small(th, 11, "", th.fg)) : text(c, y0 + ph + 15, label, small(th, 10, 'text-anchor="middle"'));
|
|
1431
|
+
}
|
|
1432
|
+
if (!st.n) return;
|
|
1433
|
+
var a1 = P(c - half, V(st.q1)), a2 = P(c + half, V(st.q3));
|
|
1434
|
+
s += "<g" + anim(o, "bzc-a", Math.min(0.6, i * 0.06)) + ">" + seg(c, V(st.low), c, V(st.q1), line2) + seg(c, V(st.q3), c, V(st.high), line2) + seg(c - half / 2, V(st.low), c + half / 2, V(st.low), line2) + seg(c - half / 2, V(st.high), c + half / 2, V(st.high), line2) + '<rect x="' + r2(Math.min(a1[0], a2[0])) + '" y="' + r2(Math.min(a1[1], a2[1])) + '" width="' + r2(Math.abs(a2[0] - a1[0])) + '" height="' + r2(Math.max(1, Math.abs(a2[1] - a1[1]))) + '" rx="3" fill="' + col + '" fill-opacity=".22"' + line2 + "/>" + seg(c - half, V(st.median), c + half, V(st.median), ' stroke="' + col + '" stroke-width="2.5"');
|
|
1435
|
+
st.outliers.forEach(function(v) {
|
|
1436
|
+
var p = P(c, V(v));
|
|
1437
|
+
s += '<circle cx="' + r2(p[0]) + '" cy="' + r2(p[1]) + '" r="2.5" fill="none"' + line2 + "/>";
|
|
1438
|
+
});
|
|
1439
|
+
if (showMean) {
|
|
1440
|
+
var m = P(c, V(st.mean));
|
|
1441
|
+
s += marker("diamond", m[0], m[1], 4, ' fill="' + (o.background || th.tile) + '"' + line2);
|
|
1442
|
+
}
|
|
1443
|
+
s += "</g>";
|
|
1444
|
+
});
|
|
1445
|
+
if (has(o.goal)) {
|
|
1446
|
+
var g = V(+o.goal);
|
|
1447
|
+
s += horiz ? seg(g, y0, g, y0 + ph, ' stroke="' + th.fg + '" stroke-opacity=".55" stroke-dasharray="4 4"') : goalLine(o, th, px, px + pw, g);
|
|
1448
|
+
}
|
|
1449
|
+
stats.forEach(function(st, i) {
|
|
1450
|
+
if (!st.n) return;
|
|
1451
|
+
var tip = nameOf(i) + "\nMedian " + f(st.median) + "\nMiddle half " + f(st.q1) + " to " + f(st.q3) + "\n" + (st.outliers.length ? "Range without outliers " : "Range ") + f(st.low) + " to " + f(st.high) + (st.outliers.length ? "\n" + st.outliers.length + (st.outliers.length === 1 ? " outlier" : " outliers") : "") + (showMean ? "\nMean " + f(st.mean) : "") + "\nn = " + st.n;
|
|
1452
|
+
s += horiz ? hit(px, C(i) - band / 2, pw, band, tip) : hit(C(i) - band / 2, y0, band, ph, tip);
|
|
1453
|
+
});
|
|
1454
|
+
return s;
|
|
1455
|
+
}, { summary: k ? about : "no data" });
|
|
1456
|
+
}
|
|
1457
|
+
boxplot.table = function(o) {
|
|
1458
|
+
var S = series(o.values), names = list(o.series), showMean = list(o.mark).indexOf("mean") >= 0;
|
|
1459
|
+
var f = function(v) {
|
|
1460
|
+
return fmt(v, o);
|
|
1461
|
+
};
|
|
1462
|
+
return {
|
|
1463
|
+
head: ["Group", "Count", "Lowest", "Lower quartile", "Median", "Upper quartile", "Highest", "Outliers"].concat(showMean ? ["Mean"] : []),
|
|
1464
|
+
rows: S.map(function(v, i) {
|
|
1465
|
+
var st = summary(v, o.whiskers === "range");
|
|
1466
|
+
return [names[i] || "Group " + (i + 1), String(st.n), f(st.min), f(st.q1), f(st.median), f(st.q3), f(st.max), String(st.outliers.length)].concat(showMean ? [f(st.mean)] : []);
|
|
1467
|
+
})
|
|
1468
|
+
};
|
|
1469
|
+
};
|
|
1470
|
+
function fit(name, width, size, mono) {
|
|
1471
|
+
var t = String(name);
|
|
1472
|
+
while (t.length > 1 && tw(t, size, mono) > width) t = trunc(t, t.length - 1);
|
|
1473
|
+
return t;
|
|
1474
|
+
}
|
|
1475
|
+
function quantile(s, p) {
|
|
1476
|
+
var h = (s.length - 1) * p, i = Math.floor(h);
|
|
1477
|
+
return i + 1 < s.length ? s[i] + (h - i) * (s[i + 1] - s[i]) : s[i];
|
|
1478
|
+
}
|
|
1479
|
+
function summary(v, range) {
|
|
1480
|
+
var s = v.slice().sort(function(a, b) {
|
|
1481
|
+
return a - b;
|
|
1482
|
+
}), n = s.length;
|
|
1483
|
+
if (!n) return { n: 0, outliers: [] };
|
|
1484
|
+
var q1 = quantile(s, 0.25), q3 = quantile(s, 0.75), iqr = q3 - q1, t = 0;
|
|
1485
|
+
var lowFence = range ? -Infinity : q1 - 1.5 * iqr, highFence = range ? Infinity : q3 + 1.5 * iqr;
|
|
1486
|
+
var inside = s.filter(function(x) {
|
|
1487
|
+
return x >= lowFence && x <= highFence;
|
|
1488
|
+
});
|
|
1489
|
+
s.forEach(function(x) {
|
|
1490
|
+
t += x;
|
|
1491
|
+
});
|
|
1492
|
+
return {
|
|
1493
|
+
n,
|
|
1494
|
+
min: s[0],
|
|
1495
|
+
max: s[n - 1],
|
|
1496
|
+
q1,
|
|
1497
|
+
median: quantile(s, 0.5),
|
|
1498
|
+
q3,
|
|
1499
|
+
mean: t / n,
|
|
1500
|
+
low: inside[0],
|
|
1501
|
+
high: inside[inside.length - 1],
|
|
1502
|
+
outliers: s.filter(function(x) {
|
|
1503
|
+
return x < lowFence || x > highFence;
|
|
1504
|
+
})
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// src/entries/boxplot.js
|
|
1509
|
+
register("boxplot", boxplot);
|
|
1510
|
+
define("boxplot");
|
|
1511
|
+
|
|
1167
1512
|
// src/charts/scatter.js
|
|
1168
|
-
function
|
|
1513
|
+
function fit2(xs, ys) {
|
|
1169
1514
|
var n = Math.min(xs.length, ys.length);
|
|
1170
1515
|
if (n < 2) return null;
|
|
1171
1516
|
var mx = 0, my = 0, i;
|
|
@@ -1270,7 +1615,7 @@ function scatter(o, th) {
|
|
|
1270
1615
|
if (has(o.goal)) s += goalLine(o, th, px, px + pw, PY(+o.goal));
|
|
1271
1616
|
if (trend) {
|
|
1272
1617
|
groups.forEach(function(gr, j) {
|
|
1273
|
-
var f =
|
|
1618
|
+
var f = fit2(gr.xs, gr.ys);
|
|
1274
1619
|
if (!f) return;
|
|
1275
1620
|
var xa = Math.min.apply(null, gr.xs), xb = Math.max.apply(null, gr.xs);
|
|
1276
1621
|
var ya = f.slope * xa + f.intercept, yb = f.slope * xb + f.intercept;
|
|
@@ -1395,43 +1740,43 @@ function donut(o, th) {
|
|
|
1395
1740
|
})));
|
|
1396
1741
|
labels.push("Other");
|
|
1397
1742
|
}
|
|
1398
|
-
var
|
|
1743
|
+
var total2 = sum(v), h = num(o.height, 150);
|
|
1399
1744
|
var st = statOf(v, o, "");
|
|
1400
1745
|
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
1401
|
-
if (!
|
|
1746
|
+
if (!total2) return emptyState(th, x0, y0, w, h2);
|
|
1402
1747
|
var s = "", size = Math.min(h2, w * 0.5), cx = x0 + size / 2, cy = y0 + h2 / 2;
|
|
1403
1748
|
var sw = size * 0.16, r = (size - sw) / 2, start = 0, gap = v.filter(Boolean).length > 1 ? 0.9 : 0;
|
|
1404
1749
|
v.forEach(function(n, i) {
|
|
1405
|
-
var len = n /
|
|
1750
|
+
var len = n / total2 * 100;
|
|
1406
1751
|
if (len <= 0) return;
|
|
1407
1752
|
var seg = Math.max(0.01, len - gap);
|
|
1408
1753
|
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r) + '" fill="none" stroke="' + pal[i % pal.length] + '" stroke-width="' + r2(sw) + '" pathLength="100" stroke-dasharray="' + r2(seg) + " " + r2(100 - seg) + '" stroke-dashoffset="' + r2(-start) + '" transform="rotate(-90 ' + r2(cx) + " " + r2(cy) + ')"' + anim(o, "bzc-a", i * 0.08) + "/>";
|
|
1409
1754
|
start += len;
|
|
1410
1755
|
});
|
|
1411
|
-
s += text(cx, cy + 2, fmt(
|
|
1756
|
+
s += text(cx, cy + 2, fmt(total2, o, true), 'text-anchor="middle" fill="' + th.fg + '" font-family="' + SANS + '" font-size="' + r2(size * 0.17) + '" font-weight="800"');
|
|
1412
1757
|
s += text(cx, cy + 2 + 14, (has(o.centerLabel) ? o.centerLabel : "Total").toUpperCase(), small(th, 9, 'text-anchor="middle" letter-spacing="1"'));
|
|
1413
1758
|
var lx = x0 + size + 22, rowH = Math.min(24, h2 / v.length), ly = cy - rowH * v.length / 2;
|
|
1414
1759
|
v.forEach(function(n, i) {
|
|
1415
|
-
var y = ly + i * rowH + rowH / 2, pct = Math.round(n /
|
|
1760
|
+
var y = ly + i * rowH + rowH / 2, pct = Math.round(n / total2 * 100);
|
|
1416
1761
|
var tip = (labels[i] || "#" + (i + 1)) + ": " + fmt(n, o) + " (" + pct + "%)";
|
|
1417
1762
|
s += '<circle cx="' + r2(lx + 4) + '" cy="' + r2(y) + '" r="4" fill="' + pal[i % pal.length] + '"/>' + text(lx + 14, y + 4, trunc(labels[i] || "#" + (i + 1), Math.floor((x0 + w - lx - 50) / 6.7)), 'fill="' + th.fg + '" font-family="' + SANS + '" font-size="12"') + text(x0 + w, y + 4, pct + "%", 'text-anchor="end" fill="' + th.muted + '" font-family="' + MONO + '" font-size="11" font-weight="600"') + hit(lx, y - rowH / 2, x0 + w - lx, rowH, tip);
|
|
1418
1763
|
});
|
|
1419
1764
|
start = 0;
|
|
1420
1765
|
v.forEach(function(n, i) {
|
|
1421
|
-
var len = n /
|
|
1766
|
+
var len = n / total2 * 100;
|
|
1422
1767
|
if (len <= 0) return;
|
|
1423
1768
|
s += '<circle cx="' + r2(cx) + '" cy="' + r2(cy) + '" r="' + r2(r) + '" fill="none" stroke="#000" stroke-opacity="0" stroke-width="' + r2(sw) + '" pathLength="100" stroke-dasharray="' + r2(len) + " " + r2(100 - len) + '" stroke-dashoffset="' + r2(-start) + '" transform="rotate(-90 ' + r2(cx) + " " + r2(cy) + ')" class="bzc-hit" data-skip="" data-tip="' + esc((labels[i] || "#" + (i + 1)) + ": " + fmt(n, o) + " (" + Math.round(len) + "%)") + '"/>';
|
|
1424
1769
|
start += len;
|
|
1425
1770
|
});
|
|
1426
1771
|
return s;
|
|
1427
|
-
}, { stat: st, summary: "total " + fmt(
|
|
1772
|
+
}, { stat: st, summary: "total " + fmt(total2, o) + " across " + v.length + " parts" });
|
|
1428
1773
|
}
|
|
1429
1774
|
donut.table = function(o) {
|
|
1430
1775
|
var v = nums(o.values).map(function(n) {
|
|
1431
1776
|
return Math.max(0, n);
|
|
1432
|
-
}), labels = list(o.labels),
|
|
1777
|
+
}), labels = list(o.labels), total2 = sum(v);
|
|
1433
1778
|
return { head: ["Label", "Value", "Share"], rows: v.map(function(n, i) {
|
|
1434
|
-
return [labels[i] || "#" + (i + 1), fmt(n, o), (
|
|
1779
|
+
return [labels[i] || "#" + (i + 1), fmt(n, o), (total2 ? Math.round(n / total2 * 100) : 0) + "%"];
|
|
1435
1780
|
}) };
|
|
1436
1781
|
};
|
|
1437
1782
|
|
|
@@ -1500,8 +1845,8 @@ function progress(o, th) {
|
|
|
1500
1845
|
var v = nums(o.values).map(function(n) {
|
|
1501
1846
|
return Math.max(0, n);
|
|
1502
1847
|
}), labels = list(o.labels), pal = palette(o, th);
|
|
1503
|
-
var single = v.length === 1,
|
|
1504
|
-
var max = has(o.max) ? num(o.max, 100) : single ? 100 :
|
|
1848
|
+
var single = v.length === 1, total2 = sum(v);
|
|
1849
|
+
var max = has(o.max) ? num(o.max, 100) : single ? 100 : total2;
|
|
1505
1850
|
if (!(max > 0)) max = 1;
|
|
1506
1851
|
var auto = !has(o.prefix) && !has(o.suffix) && max === 100 && single;
|
|
1507
1852
|
var show = function(n) {
|
|
@@ -1526,7 +1871,7 @@ function progress(o, th) {
|
|
|
1526
1871
|
return tile(o, th, h, function(x0, y0, w, h2) {
|
|
1527
1872
|
if (!v.length) return emptyState(th, x0, y0, w, h2);
|
|
1528
1873
|
var s = "", by = y0 + 22, cid = newId("c");
|
|
1529
|
-
var head = single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(
|
|
1874
|
+
var head = single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total2, o) + " of " + fmt(max, o);
|
|
1530
1875
|
if (single && labels[0]) s += text(x0, y0 + 11, labels[0], 'fill="' + th.fg + '" font-family="' + SANS + '" font-size="12"');
|
|
1531
1876
|
s += text(x0 + w, y0 + 11, head, 'text-anchor="end" fill="' + th.fg + '" font-family="' + MONO + '" font-size="11" font-weight="600"');
|
|
1532
1877
|
s += '<rect x="' + x0 + '" y="' + by + '" width="' + w + '" height="14" rx="7" fill="' + th.grid + '" fill-opacity=".7"/>';
|
|
@@ -1554,7 +1899,7 @@ function progress(o, th) {
|
|
|
1554
1899
|
s += '<circle cx="' + r2(lx + 4) + '" cy="' + r2(ly) + '" r="4" fill="' + pal[i % pal.length] + '"/><text x="' + r2(lx + 14) + '" y="' + r2(ly + 4) + '" fill="' + th.fg + '" font-family="' + SANS + '" font-size="12">' + esc(it.l) + '<tspan dx="6" fill="' + th.muted + '" font-family="' + MONO + '" font-size="11">' + esc(fmt(v[i], o)) + "</tspan></text>";
|
|
1555
1900
|
});
|
|
1556
1901
|
return s;
|
|
1557
|
-
}, { stat: st, summary: single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(
|
|
1902
|
+
}, { stat: st, summary: single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total2, o) + " of " + fmt(max, o) });
|
|
1558
1903
|
}
|
|
1559
1904
|
progress.table = function(o) {
|
|
1560
1905
|
var v = nums(o.values).map(function(n) {
|
|
@@ -1647,7 +1992,7 @@ register("heatmap", heatmap);
|
|
|
1647
1992
|
define("heatmap");
|
|
1648
1993
|
|
|
1649
1994
|
// src/index.js
|
|
1650
|
-
var version = "1.
|
|
1995
|
+
var version = "1.6.0";
|
|
1651
1996
|
var types = NAMES.slice();
|
|
1652
1997
|
export {
|
|
1653
1998
|
BezelChart,
|