bezel-charts 1.5.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.
@@ -1,4 +1,4 @@
1
- /*! Bezel Charts v1.5.0 | MIT | github.com/ShazMoghaddam/bezel-charts */
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", "histogram", "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;
@@ -350,7 +350,8 @@ var ATTRIBUTES = [
350
350
  "bins",
351
351
  "bin-width",
352
352
  "mark",
353
- "percent"
353
+ "percent",
354
+ "whiskers"
354
355
  ];
355
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}}`;
356
357
  var STEPS = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 };
@@ -1362,8 +1363,154 @@ function median(v) {
1362
1363
  register("histogram", histogram);
1363
1364
  define("histogram");
1364
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
+
1365
1512
  // src/charts/scatter.js
1366
- function fit(xs, ys) {
1513
+ function fit2(xs, ys) {
1367
1514
  var n = Math.min(xs.length, ys.length);
1368
1515
  if (n < 2) return null;
1369
1516
  var mx = 0, my = 0, i;
@@ -1468,7 +1615,7 @@ function scatter(o, th) {
1468
1615
  if (has(o.goal)) s += goalLine(o, th, px, px + pw, PY(+o.goal));
1469
1616
  if (trend) {
1470
1617
  groups.forEach(function(gr, j) {
1471
- var f = fit(gr.xs, gr.ys);
1618
+ var f = fit2(gr.xs, gr.ys);
1472
1619
  if (!f) return;
1473
1620
  var xa = Math.min.apply(null, gr.xs), xb = Math.max.apply(null, gr.xs);
1474
1621
  var ya = f.slope * xa + f.intercept, yb = f.slope * xb + f.intercept;
@@ -1845,7 +1992,7 @@ register("heatmap", heatmap);
1845
1992
  define("heatmap");
1846
1993
 
1847
1994
  // src/index.js
1848
- var version = "1.5.0";
1995
+ var version = "1.6.0";
1849
1996
  var types = NAMES.slice();
1850
1997
  export {
1851
1998
  BezelChart,