bezel-charts 1.3.0 → 1.5.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.3.0 | MIT | github.com/ShazMoghaddam/bezel-charts */
1
+ /*! Bezel Charts v1.5.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", "bars", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
271
+ var NAMES = ["spark", "line", "area", "bars", "histogram", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
272
272
  var TYPES = {};
273
273
  function register(type, fn) {
274
274
  TYPES[type] = fn;
@@ -346,7 +346,11 @@ 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"
350
354
  ];
351
355
  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
356
  var STEPS = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 };
@@ -635,7 +639,7 @@ function axisWidth(ticks) {
635
639
  return tw(t, 10, true);
636
640
  })) + 10;
637
641
  }
638
- function seriesTable(o, total) {
642
+ function seriesTable(o, total2) {
639
643
  var S = series(o.values), names = list(o.series), labels = list(o.labels), rows = [];
640
644
  var n = Math.max.apply(null, [0].concat(S.map(function(s) {
641
645
  return s.length;
@@ -646,13 +650,87 @@ function seriesTable(o, total) {
646
650
  row.push(i < s.length ? fmt(s[i], o) : "");
647
651
  t += i < s.length ? s[i] : 0;
648
652
  });
649
- if (total) row.push(fmt(t, o));
653
+ if (total2) row.push(fmt(t, o));
650
654
  rows.push(row);
651
655
  }
652
656
  var head = S.length > 1 || names.length ? S.map(function(_, k) {
653
657
  return names[k] || "Series " + (k + 1);
654
658
  }) : ["Value"];
655
- return { head: ["Label"].concat(head, total ? ["Total"] : []), rows };
659
+ return { head: ["Label"].concat(head, total2 ? ["Total"] : []), rows };
660
+ }
661
+
662
+ // src/core/xy.js
663
+ function xyFrame(o, th, c, x0, y0, w, h) {
664
+ var s = "", top = y0, n = c.n, labels = c.labels;
665
+ if (c.names) {
666
+ var lg = legend(c.names, c.pal, th, x0, top, w, c.key, c.kw);
667
+ s += lg.svg;
668
+ top += lg.height;
669
+ }
670
+ var mn = c.lo, mx = c.hi;
671
+ if (has(o.goal)) {
672
+ mn = Math.min(mn, +o.goal);
673
+ mx = Math.max(mx, +o.goal);
674
+ }
675
+ if (c.zero) {
676
+ mn = Math.min(0, mn);
677
+ mx = Math.max(0, mx);
678
+ }
679
+ var sc = niceScale(mn, mx, 4);
680
+ var tl = sc.ticks.map(c.tick);
681
+ var axisW = Math.max.apply(null, tl.map(function(t) {
682
+ return tw(t, 10, true);
683
+ })) + 10;
684
+ var bottom = labels.length ? 20 : 4;
685
+ var px = x0 + axisW, pw = w - axisW, ph = h - (top - y0) - bottom;
686
+ var X = function(i2) {
687
+ return n === 1 ? px + pw / 2 : px + i2 * pw / (n - 1);
688
+ };
689
+ var Y = function(v) {
690
+ return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
691
+ };
692
+ sc.ticks.forEach(function(t, i2) {
693
+ var y = Y(t);
694
+ 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"'));
695
+ });
696
+ if (labels.length) {
697
+ var every = Math.max(1, Math.ceil(n * (Math.max.apply(null, labels.map(function(l) {
698
+ return tw(l, 10, true);
699
+ })) + 10) / pw));
700
+ for (var i = 0; i < n; i += every) {
701
+ if (labels[i] == null) continue;
702
+ var anchor = n > 1 && i === 0 ? "start" : n > 1 && i === n - 1 ? "end" : "middle";
703
+ s += text(X(i), top + ph + 15, labels[i], small(th, 10, 'text-anchor="' + anchor + '"'));
704
+ }
705
+ }
706
+ c.notes.forEach(function(nt) {
707
+ if (nt.i >= n) return;
708
+ var x = X(nt.i);
709
+ s += '<line x1="' + r2(x) + '" x2="' + r2(x) + '" y1="' + r2(top + 12) + '" y2="' + r2(top + ph) + '" stroke="' + th.fg + '" stroke-opacity=".4" stroke-dasharray="3 3"/>' + text(clamp(x, px + 30, px + pw - 30), top + 8, nt.text, small(th, 10, 'text-anchor="middle"', th.fg));
710
+ });
711
+ return { svg: s, X, Y, px, pw, top, ph };
712
+ }
713
+ function xyFinish(o, th, f, n, tip) {
714
+ var s = has(o.goal) ? goalLine(o, th, f.px, f.px + f.pw, f.Y(+o.goal)) : "";
715
+ var step = n > 1 ? f.pw / (n - 1) : f.pw;
716
+ for (var j = 0; j < n; j++) s += hit(f.X(j) - step / 2, f.top, step, f.ph, tip(j));
717
+ return s;
718
+ }
719
+ function lineKey(pal, dashOf, markOf, bg) {
720
+ return function(k, x, y) {
721
+ var c = pal[k % pal.length], dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
722
+ return '<line x1="' + r2(x) + '" x2="' + r2(x + 18) + '" y1="' + r2(y + 6) + '" y2="' + r2(y + 6) + '" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round"' + (dk ? ' stroke-dasharray="' + dk + '"' : "") + "/>" + (mk !== "none" ? marker(mk, x + 9, y + 6, 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"') : "");
723
+ };
724
+ }
725
+ function seriesLine(o, d, c, k, dk) {
726
+ return dk ? '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="' + dk + '"' + anim(o, "bzc-a", k * 0.15) + "/>" : '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" pathLength="1" style="stroke-dasharray:1' + (o.animate ? ";animation-delay:" + k * 0.15 + "s" : "") + '"' + (o.animate ? ' class="bzc-d"' : "") + "/>";
727
+ }
728
+ function pointMarks(o, mk, ys, X, Y, c, bg, k) {
729
+ var s = "", every = Math.ceil(ys.length / 40);
730
+ ys.forEach(function(y, i) {
731
+ if (i % every === 0 || i === ys.length - 1) s += marker(mk, X(i), Y(y), 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"' + anim(o, "bzc-a", 0.6 + k * 0.15));
732
+ });
733
+ return s;
656
734
  }
657
735
 
658
736
  // src/charts/line.js
@@ -665,68 +743,35 @@ function line(o, th) {
665
743
  var st = statOf(S[0] || [], o, "");
666
744
  var dashOf = styleOf(o.dash, Object.keys(DASHES), "solid"), markOf = styleOf(o.markers, SHAPES, "none");
667
745
  var bg = o.background || th.tile;
668
- var key = (dashOf || markOf) && function(k, x, y) {
669
- var c = pal[k % pal.length], dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
670
- return '<line x1="' + r2(x) + '" x2="' + r2(x + 18) + '" y1="' + r2(y + 6) + '" y2="' + r2(y + 6) + '" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round"' + (dk ? ' stroke-dasharray="' + dk + '"' : "") + "/>" + (mk !== "none" ? marker(mk, x + 9, y + 6, 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"') : "");
746
+ var key = (dashOf || markOf) && lineKey(pal, dashOf, markOf, bg);
747
+ var nameOf = function(k) {
748
+ return names[k] || "Series " + (k + 1);
671
749
  };
672
750
  return tile(o, th, h, function(x0, y0, w, h2) {
673
751
  if (!n) return emptyState(th, x0, y0, w, h2);
674
- var s = "", top = y0;
675
- if (multi) {
676
- var lg = legend(S.map(function(_, k) {
677
- return names[k] || "Series " + (k + 1);
678
- }), pal, th, x0, top, w, key, key && 24);
679
- s += lg.svg;
680
- top += lg.height;
681
- }
682
752
  var all = [].concat.apply([], S);
683
- var mn = Math.min.apply(null, all), mx = Math.max.apply(null, all);
684
- if (has(o.goal)) {
685
- mn = Math.min(mn, +o.goal);
686
- mx = Math.max(mx, +o.goal);
687
- }
688
- if (bool(o.zero)) {
689
- mn = Math.min(0, mn);
690
- mx = Math.max(0, mx);
691
- }
692
- var sc = niceScale(mn, mx, 4);
693
- var tl = sc.ticks.map(function(t) {
694
- return fmt(t, o, true);
695
- });
696
- var axisW = Math.max.apply(null, tl.map(function(t) {
697
- return tw(t, 10, true);
698
- })) + 10;
699
- var bottom = labels.length ? 20 : 4;
700
- var px = x0 + axisW, pw = w - axisW, ph = h2 - (top - y0) - bottom;
701
- var X = function(i2) {
702
- return n === 1 ? px + pw / 2 : px + i2 * pw / (n - 1);
703
- };
704
- var Y = function(v) {
705
- return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
706
- };
707
- sc.ticks.forEach(function(t, i2) {
708
- var y = Y(t);
709
- 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"'));
710
- });
711
- if (labels.length) {
712
- var every = Math.max(1, Math.ceil(n * (Math.max.apply(null, labels.map(function(l) {
713
- return tw(l, 10, true);
714
- })) + 10) / pw));
715
- for (var i = 0; i < n; i += every) {
716
- if (labels[i] == null) continue;
717
- var anchor = n > 1 && i === 0 ? "start" : n > 1 && i === n - 1 ? "end" : "middle";
718
- s += text(X(i), top + ph + 15, labels[i], small(th, 10, 'text-anchor="' + anchor + '"'));
753
+ var f = xyFrame(o, th, {
754
+ n,
755
+ names: multi ? S.map(function(_, k) {
756
+ return nameOf(k);
757
+ }) : null,
758
+ pal,
759
+ key,
760
+ kw: key && 24,
761
+ lo: Math.min.apply(null, all),
762
+ hi: Math.max.apply(null, all),
763
+ zero: bool(o.zero),
764
+ labels,
765
+ notes,
766
+ tick: function(t) {
767
+ return fmt(t, o, true);
719
768
  }
720
- }
721
- notes.forEach(function(nt) {
722
- if (nt.i >= n) return;
723
- var x = X(nt.i);
724
- s += '<line x1="' + r2(x) + '" x2="' + r2(x) + '" y1="' + r2(top + 12) + '" y2="' + r2(top + ph) + '" stroke="' + th.fg + '" stroke-opacity=".4" stroke-dasharray="3 3"/>' + text(clamp(x, px + 30, px + pw - 30), top + 8, nt.text, small(th, 10, 'text-anchor="middle"', th.fg));
725
- });
769
+ }, x0, y0, w, h2);
770
+ var s = f.svg, X = f.X, Y = f.Y, px = f.px, pw = f.pw, top = f.top, ph = f.ph;
726
771
  S.forEach(function(v, k) {
727
772
  var c = pal[k % pal.length];
728
- var P = v.map(function(y, i2) {
729
- return [X(i2), Y(y)];
773
+ var P = v.map(function(y, i) {
774
+ return [X(i), Y(y)];
730
775
  });
731
776
  if (P.length === 1) P = [[px, P[0][1]], [px + pw, P[0][1]]];
732
777
  var d = pathOf(P, bool(o.straight));
@@ -734,31 +779,25 @@ function line(o, th) {
734
779
  var g = newId("g"), last = P[P.length - 1];
735
780
  s += '<defs><linearGradient id="' + g + '" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="' + c + '" stop-opacity=".26"/><stop offset="1" stop-color="' + c + '" stop-opacity="0"/></linearGradient></defs><path d="' + d + "L" + r2(last[0]) + "," + r2(top + ph) + "L" + r2(P[0][0]) + "," + r2(top + ph) + 'Z" fill="url(#' + g + ')"' + anim(o, "bzc-a") + "/>";
736
781
  }
737
- var dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
738
- s += dk ? '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="' + dk + '"' + anim(o, "bzc-a", k * 0.15) + "/>" : '<path d="' + d + '" fill="none" stroke="' + c + '" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" pathLength="1" style="stroke-dasharray:1' + (o.animate ? ";animation-delay:" + k * 0.15 + "s" : "") + '"' + (o.animate ? ' class="bzc-d"' : "") + "/>";
782
+ var mk = markOf ? markOf(k) : "none";
783
+ s += seriesLine(o, d, c, k, dashOf ? DASHES[dashOf(k)] : "");
739
784
  if (mk !== "none") {
740
- var every2 = Math.ceil(v.length / 40);
741
- v.forEach(function(y, i2) {
742
- if (i2 % every2 === 0 || i2 === v.length - 1) s += marker(mk, X(i2), Y(y), 3.5, ' fill="' + c + '" stroke="' + bg + '" stroke-width="1.5"' + anim(o, "bzc-a", 0.6 + k * 0.15));
743
- });
785
+ s += pointMarks(o, mk, v, X, Y, c, bg, k);
744
786
  } else {
745
787
  var e = P[P.length - 1];
746
788
  s += '<circle cx="' + r2(e[0]) + '" cy="' + r2(e[1]) + '" r="4" fill="' + c + '" stroke="' + bg + '" stroke-width="2"/>';
747
789
  }
748
- if (bool(o.showValues)) v.forEach(function(y, i2) {
749
- s += text(X(i2), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
790
+ if (bool(o.showValues)) v.forEach(function(y, i) {
791
+ s += text(X(i), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
750
792
  });
751
793
  });
752
- if (has(o.goal)) s += goalLine(o, th, px, px + pw, Y(+o.goal));
753
- var step = n > 1 ? pw / (n - 1) : pw;
754
- for (var j = 0; j < n; j++) {
794
+ return s + xyFinish(o, th, f, n, function(j) {
755
795
  var tip = labels[j] || "#" + (j + 1);
756
796
  S.forEach(function(v, k) {
757
- if (j < v.length) tip += "\n" + (multi ? (names[k] || "Series " + (k + 1)) + ": " : "") + fmt(v[j], o);
797
+ if (j < v.length) tip += "\n" + (multi ? nameOf(k) + ": " : "") + fmt(v[j], o);
758
798
  });
759
- s += hit(X(j) - step / 2, top, step, ph, tip);
760
- }
761
- return s;
799
+ return tip;
800
+ });
762
801
  }, { stat: st, summary: S.length + " series over " + n + " points" });
763
802
  }
764
803
  line.table = function(o) {
@@ -769,6 +808,140 @@ line.table = function(o) {
769
808
  register("line", line);
770
809
  define("line");
771
810
 
811
+ // src/charts/area.js
812
+ function area(o, th) {
813
+ var S = series(o.values), names = list(o.series), labels = list(o.labels), pal = palette(o, th);
814
+ var h = num(o.height, 170), k = S.length, n = Math.max.apply(null, [0].concat(S.map(function(s) {
815
+ return s.length;
816
+ })));
817
+ var mode = k > 1 ? o.stacked === "percent" ? "percent" : bool(o.stacked) ? "stack" : "" : "";
818
+ var notes = parseNotes(o, labels), multi = k > 1 || names.length > 0;
819
+ var nameOf = function(j2) {
820
+ return names[j2] || "Series " + (j2 + 1);
821
+ };
822
+ var val = function(j2, i2) {
823
+ return i2 < S[j2].length ? S[j2][i2] : 0;
824
+ };
825
+ var lows = S.map(function() {
826
+ return [];
827
+ }), highs = S.map(function() {
828
+ return [];
829
+ }), totals = [], pos = [];
830
+ for (var i = 0; i < n; i++) {
831
+ var up = 0, down = 0, t = 0, p = 0;
832
+ for (var j = 0; j < k; j++) {
833
+ t += val(j, i);
834
+ p += Math.max(0, val(j, i));
835
+ }
836
+ totals.push(t);
837
+ pos.push(p);
838
+ for (j = 0; j < k; j++) {
839
+ var v = mode === "percent" ? p ? Math.max(0, val(j, i)) / p * 100 : 0 : val(j, i);
840
+ if (!mode) {
841
+ lows[j].push(0);
842
+ highs[j].push(v);
843
+ } else if (v >= 0) {
844
+ lows[j].push(up);
845
+ up += v;
846
+ highs[j].push(up);
847
+ } else {
848
+ lows[j].push(down);
849
+ down += v;
850
+ highs[j].push(down);
851
+ }
852
+ }
853
+ }
854
+ var edges = [].concat.apply([], lows.concat(highs));
855
+ var dashOf = styleOf(o.dash, Object.keys(DASHES), "solid"), markOf = styleOf(o.markers, SHAPES, "none");
856
+ var patOf = k > 1 ? styleOf(o.patterns, PATTERNS, "solid") : null, bg = o.background || th.tile, defs = "", fills = [];
857
+ for (var q = 0; patOf && q < k; q++) {
858
+ if (patOf(q) === "solid") continue;
859
+ var pid = newId("g");
860
+ defs += pattern(pid, patOf(q), pal[q % pal.length], bg);
861
+ fills[q] = "url(#" + pid + ")";
862
+ }
863
+ var key = patOf ? function(j2, x, y) {
864
+ return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + (fills[j2] || pal[j2 % pal.length]) + '"/>';
865
+ } : (dashOf || markOf) && lineKey(pal, dashOf, markOf, bg);
866
+ var share = function(j2, i2) {
867
+ return pos[i2] ? Math.round(Math.max(0, val(j2, i2)) / pos[i2] * 100) : 0;
868
+ };
869
+ return tile(o, th, h, function(x0, y0, w, h2) {
870
+ if (!n) return emptyState(th, x0, y0, w, h2);
871
+ var f = xyFrame(o, th, {
872
+ n,
873
+ names: multi ? S.map(function(_, j2) {
874
+ return nameOf(j2);
875
+ }) : null,
876
+ pal,
877
+ key,
878
+ kw: key && (patOf ? 15 : 24),
879
+ lo: Math.min.apply(null, edges),
880
+ hi: Math.max.apply(null, edges),
881
+ zero: true,
882
+ labels,
883
+ notes,
884
+ tick: mode === "percent" ? function(t2) {
885
+ return fmt(t2, { lang: o.lang }, true) + "%";
886
+ } : function(t2) {
887
+ return fmt(t2, o, true);
888
+ }
889
+ }, x0, y0, w, h2);
890
+ var s = (defs ? "<defs>" + defs + "</defs>" : "") + f.svg, X = f.X, Y = f.Y;
891
+ var pts = function(ys) {
892
+ var P = ys.map(function(y, i2) {
893
+ return [X(i2), Y(y)];
894
+ });
895
+ return P.length === 1 ? [[f.px, P[0][1]], [f.px + f.pw, P[0][1]]] : P;
896
+ };
897
+ var straight = bool(o.straight), alpha = mode ? ".8" : ".22";
898
+ var shown = function(j2) {
899
+ return mode ? highs[j2] : S[j2];
900
+ };
901
+ S.forEach(function(_, j2) {
902
+ var top = pts(shown(j2)), base = pts(mode ? lows[j2] : shown(j2).map(function() {
903
+ return 0;
904
+ })).reverse();
905
+ s += '<path d="' + pathOf(top, straight) + pathOf(base, straight).replace(/^M/, "L") + 'Z" fill="' + (fills[j2] || pal[j2 % pal.length]) + '" fill-opacity="' + alpha + '"' + (o.animate ? ' class="bzc-a" style="animation-delay:' + r2(j2 * 0.1) + 's"' : "") + "/>";
906
+ });
907
+ S.forEach(function(_, j2) {
908
+ var c = pal[j2 % pal.length], ys = shown(j2), P = pts(ys), mk = markOf ? markOf(j2) : "none";
909
+ s += seriesLine(o, pathOf(P, straight), c, j2, dashOf ? DASHES[dashOf(j2)] : "");
910
+ if (mk !== "none") s += pointMarks(o, mk, ys, X, Y, c, bg, j2);
911
+ else if (!mode) s += '<circle cx="' + r2(P[P.length - 1][0]) + '" cy="' + r2(P[P.length - 1][1]) + '" r="4" fill="' + c + '" stroke="' + bg + '" stroke-width="2"/>';
912
+ if (bool(o.showValues) && !mode) ys.forEach(function(y, i2) {
913
+ s += text(X(i2), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
914
+ });
915
+ });
916
+ if (bool(o.showValues) && mode === "stack") {
917
+ totals.forEach(function(t2, i2) {
918
+ var top = Math.max.apply(null, highs.map(function(hs) {
919
+ return hs[i2];
920
+ }).concat([0]));
921
+ s += text(X(i2), Y(top) - 8, fmt(t2, o, true), small(th, 9, 'text-anchor="middle"'));
922
+ });
923
+ }
924
+ return s + xyFinish(o, th, f, n, function(i2) {
925
+ var tip = labels[i2] || "#" + (i2 + 1);
926
+ S.forEach(function(ser, j2) {
927
+ if (i2 < ser.length) tip += "\n" + (multi ? nameOf(j2) + ": " : "") + fmt(ser[i2], o) + (mode === "percent" ? " (" + share(j2, i2) + "%)" : "");
928
+ });
929
+ return tip + (mode ? "\nTotal: " + fmt(totals[i2], o) : "");
930
+ });
931
+ }, {
932
+ stat: statOf(mode ? totals : S[0] || [], o, ""),
933
+ summary: k + " series " + (mode === "percent" ? "as shares of each total" : mode ? "stacked" : "filled") + " over " + n + " points"
934
+ });
935
+ }
936
+ area.table = function(o) {
937
+ var S = series(o.values);
938
+ return seriesTable(o, S.length > 1 && (o.stacked === "percent" || bool(o.stacked)));
939
+ };
940
+
941
+ // src/entries/area.js
942
+ register("area", area);
943
+ define("area");
944
+
772
945
  // src/charts/bars.js
773
946
  var ANGLE = 40;
774
947
  var ANGLE_MAX_CHARS = 24;
@@ -995,6 +1168,200 @@ bars.table = function(o) {
995
1168
  register("bars", bars);
996
1169
  define("bars");
997
1170
 
1171
+ // src/charts/histogram.js
1172
+ function histogram(o, th) {
1173
+ var b = binned(o), names = list(o.series), pal = palette(o, th), h = num(o.height, 190);
1174
+ var S = b ? b.S : [], k = S.length, multi = k > 1 || names.length > 0, pct = bool(o.percent);
1175
+ var nameOf = function(j) {
1176
+ return names[j] || "Series " + (j + 1);
1177
+ };
1178
+ var eo = Object.assign({}, o, { decimals: has(o.decimals) ? o.decimals : b ? b.dp : 0 });
1179
+ var range = function(i2) {
1180
+ return fmt(b.edge(i2), eo) + " to " + fmt(b.edge(i2 + 1), eo);
1181
+ };
1182
+ var share = function(j, i2) {
1183
+ return Math.round(b.counts[j][i2] / S[j].length * 100);
1184
+ };
1185
+ var height = function(j, i2) {
1186
+ return pct ? b.counts[j][i2] / S[j].length * 100 : b.counts[j][i2];
1187
+ };
1188
+ var marks = list(o.mark).filter(function(m) {
1189
+ return m === "mean" || m === "median";
1190
+ });
1191
+ var patOf = k > 1 ? styleOf(o.patterns, PATTERNS, "solid") : null, defs = "", fills = [];
1192
+ for (var q = 0; patOf && q < k; q++) {
1193
+ if (patOf(q) === "solid") continue;
1194
+ var pid = newId("g");
1195
+ defs += pattern(pid, patOf(q), pal[q % pal.length], o.background || th.tile);
1196
+ fills[q] = "url(#" + pid + ")";
1197
+ }
1198
+ var key = patOf && function(j, x, y) {
1199
+ return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + (fills[j] || pal[j % pal.length]) + '"/>';
1200
+ };
1201
+ var peak = 0, n = 0;
1202
+ if (b) {
1203
+ for (var i = 0; i < b.count; i++) if (total(b, i) > total(b, peak)) peak = i;
1204
+ S.forEach(function(s) {
1205
+ n += s.length;
1206
+ });
1207
+ }
1208
+ return tile(o, th, h, function(x0, y0, w, h2) {
1209
+ if (!b) return emptyState(th, x0, y0, w, h2);
1210
+ var s = defs ? "<defs>" + defs + "</defs>" : "", top = y0;
1211
+ if (multi) {
1212
+ var lg = legend(S.map(function(_, j) {
1213
+ return nameOf(j);
1214
+ }), pal, th, x0, top, w, key, key && 15);
1215
+ s += lg.svg;
1216
+ top += lg.height;
1217
+ }
1218
+ var most = 0;
1219
+ S.forEach(function(_, j) {
1220
+ for (var i2 = 0; i2 < b.count; i2++) most = Math.max(most, height(j, i2));
1221
+ });
1222
+ var sc = niceScale(0, most || 1, 4);
1223
+ var tl = sc.ticks.map(function(t) {
1224
+ return fmt(t, { lang: o.lang }, true) + (pct ? "%" : "");
1225
+ });
1226
+ var axisW = Math.max.apply(null, tl.map(function(t) {
1227
+ return tw(t, 10, true);
1228
+ })) + 10;
1229
+ var px = x0 + axisW, pw = w - axisW, ph = h2 - (top - y0) - 20, cw = pw / b.count;
1230
+ var X = function(i2) {
1231
+ return px + i2 * cw;
1232
+ };
1233
+ var Y = function(v) {
1234
+ return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
1235
+ };
1236
+ sc.ticks.forEach(function(t, i2) {
1237
+ var y = Y(t);
1238
+ 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"'));
1239
+ });
1240
+ var el = [];
1241
+ for (var e = 0; e <= b.count; e++) el.push(fmt(b.edge(e), eo, true));
1242
+ var every = Math.max(1, Math.ceil((Math.max.apply(null, el.map(function(l) {
1243
+ return tw(l, 10, true);
1244
+ })) * 1.5 + 8) / cw));
1245
+ for (e = 0; e <= b.count; e += every) {
1246
+ s += text(X(e), top + ph + 15, el[e], small(th, 10, 'text-anchor="' + (e === 0 ? "start" : e === b.count ? "end" : "middle") + '"'));
1247
+ }
1248
+ S.forEach(function(_, j) {
1249
+ for (var i2 = 0; i2 < b.count; i2++) {
1250
+ var v = height(j, i2);
1251
+ if (!v) continue;
1252
+ var y = Y(v);
1253
+ 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)) + "/>";
1254
+ }
1255
+ });
1256
+ var lo = b.edge(0), span = b.edge(b.count) - lo, lines = [];
1257
+ S.forEach(function(ser, j) {
1258
+ marks.forEach(function(m) {
1259
+ var v = m === "mean" ? mean(ser) : median(ser);
1260
+ lines.push({ x: px + (v - lo) / span * pw, c: multi ? pal[j % pal.length] : th.fg, t: (multi ? nameOf(j) + " " : "") + m + " " + fmt(v, o) });
1261
+ });
1262
+ });
1263
+ if (lines.length) {
1264
+ var xs = lines.map(function(l) {
1265
+ return l.x;
1266
+ }), left = (Math.min.apply(null, xs) + Math.max.apply(null, xs)) / 2 > px + pw / 2;
1267
+ var lx = left ? Math.min.apply(null, xs) - 5 : Math.max.apply(null, xs) + 5;
1268
+ lines.forEach(function(l, r) {
1269
+ 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));
1270
+ });
1271
+ }
1272
+ for (i = 0; i < b.count; i++) {
1273
+ var tip = range(i);
1274
+ S.forEach(function(_, j) {
1275
+ tip += "\n" + (multi ? nameOf(j) + ": " : "") + b.counts[j][i] + " (" + share(j, i) + "%)";
1276
+ });
1277
+ s += hit(X(i), top, cw, ph, tip);
1278
+ }
1279
+ return s;
1280
+ }, {
1281
+ 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"
1282
+ });
1283
+ }
1284
+ histogram.table = function(o) {
1285
+ var b = binned(o), names = list(o.series);
1286
+ if (!b) return { head: ["Range", "Count"], rows: [] };
1287
+ var multi = b.S.length > 1 || names.length > 0, eo = Object.assign({}, o, { decimals: has(o.decimals) ? o.decimals : b.dp }), rows = [];
1288
+ for (var i = 0; i < b.count; i++) {
1289
+ var row = [fmt(b.edge(i), eo) + " to " + fmt(b.edge(i + 1), eo)];
1290
+ if (multi) b.S.forEach(function(_, j) {
1291
+ row.push(String(b.counts[j][i]));
1292
+ });
1293
+ else row.push(String(b.counts[0][i]), Math.round(b.counts[0][i] / b.S[0].length * 100) + "%");
1294
+ rows.push(row);
1295
+ }
1296
+ return { head: ["Range"].concat(multi ? b.S.map(function(_, j) {
1297
+ return names[j] || "Series " + (j + 1);
1298
+ }) : ["Count", "Share"]), rows };
1299
+ };
1300
+ function niceWidth(raw) {
1301
+ var e = Math.floor(Math.log10(raw)), p = Number("1e" + e), f = raw / p;
1302
+ var m = f < 1.4142 ? 1 : f < 2.2361 ? 2 : f < 3.5355 ? 2.5 : f < 7.0711 ? 5 : 10;
1303
+ return { w: m * p, dp: Math.max(0, m === 10 ? -e - 1 : -e) + (m === 2.5 ? 1 : 0) };
1304
+ }
1305
+ function binned(o) {
1306
+ var S = series(o.values), all = [].concat.apply([], S);
1307
+ if (!all.length) return null;
1308
+ var lo = Math.min.apply(null, all), hi = Math.max.apply(null, all), w = num(o.binWidth, 0), dp, nice;
1309
+ if (w > 0 && (hi - lo) / w <= 200) {
1310
+ var str = String(o.binWidth), dot = str.indexOf(".");
1311
+ dp = dot < 0 ? 0 : Math.min(6, str.length - dot - 1);
1312
+ } else {
1313
+ var k = num(o.bins, 0) > 0 ? Math.round(num(o.bins, 0)) : Math.ceil(Math.log2(all.length) + 1);
1314
+ k = Math.min(Math.max(k, 1), 50);
1315
+ nice = niceWidth(hi > lo ? (hi - lo) / k : Math.abs(lo) || 1);
1316
+ w = nice.w;
1317
+ dp = nice.dp;
1318
+ }
1319
+ var s0 = Math.floor(lo / w);
1320
+ var edge = function(i) {
1321
+ return Number(((s0 + i) * w).toFixed(dp + 3));
1322
+ };
1323
+ while (edge(0) > lo) s0--;
1324
+ var count = Math.max(1, Math.ceil((hi - edge(0)) / w));
1325
+ while (count > 1 && edge(count - 1) >= hi) count--;
1326
+ while (edge(count) < hi) count++;
1327
+ var counts = S.map(function(ser) {
1328
+ var c = [];
1329
+ for (var i = 0; i < count; i++) c.push(0);
1330
+ ser.forEach(function(x) {
1331
+ var i2 = Math.min(count - 1, Math.max(0, Math.floor((x - edge(0)) / w)));
1332
+ while (i2 > 0 && x < edge(i2)) i2--;
1333
+ while (i2 < count - 1 && x >= edge(i2 + 1)) i2++;
1334
+ c[i2]++;
1335
+ });
1336
+ return c;
1337
+ });
1338
+ return { S, edge, count, counts, dp };
1339
+ }
1340
+ function total(b, i) {
1341
+ var t = 0;
1342
+ b.counts.forEach(function(c) {
1343
+ t += c[i];
1344
+ });
1345
+ return t;
1346
+ }
1347
+ function mean(v) {
1348
+ var t = 0;
1349
+ v.forEach(function(x) {
1350
+ t += x;
1351
+ });
1352
+ return t / v.length;
1353
+ }
1354
+ function median(v) {
1355
+ var s = v.slice().sort(function(a, b) {
1356
+ return a - b;
1357
+ }), m = Math.floor(s.length / 2);
1358
+ return s.length % 2 ? s[m] : (s[m - 1] + s[m]) / 2;
1359
+ }
1360
+
1361
+ // src/entries/histogram.js
1362
+ register("histogram", histogram);
1363
+ define("histogram");
1364
+
998
1365
  // src/charts/scatter.js
999
1366
  function fit(xs, ys) {
1000
1367
  var n = Math.min(xs.length, ys.length);
@@ -1226,43 +1593,43 @@ function donut(o, th) {
1226
1593
  })));
1227
1594
  labels.push("Other");
1228
1595
  }
1229
- var total = sum(v), h = num(o.height, 150);
1596
+ var total2 = sum(v), h = num(o.height, 150);
1230
1597
  var st = statOf(v, o, "");
1231
1598
  return tile(o, th, h, function(x0, y0, w, h2) {
1232
- if (!total) return emptyState(th, x0, y0, w, h2);
1599
+ if (!total2) return emptyState(th, x0, y0, w, h2);
1233
1600
  var s = "", size = Math.min(h2, w * 0.5), cx = x0 + size / 2, cy = y0 + h2 / 2;
1234
1601
  var sw = size * 0.16, r = (size - sw) / 2, start = 0, gap = v.filter(Boolean).length > 1 ? 0.9 : 0;
1235
1602
  v.forEach(function(n, i) {
1236
- var len = n / total * 100;
1603
+ var len = n / total2 * 100;
1237
1604
  if (len <= 0) return;
1238
1605
  var seg = Math.max(0.01, len - gap);
1239
1606
  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) + "/>";
1240
1607
  start += len;
1241
1608
  });
1242
- s += text(cx, cy + 2, fmt(total, o, true), 'text-anchor="middle" fill="' + th.fg + '" font-family="' + SANS + '" font-size="' + r2(size * 0.17) + '" font-weight="800"');
1609
+ 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"');
1243
1610
  s += text(cx, cy + 2 + 14, (has(o.centerLabel) ? o.centerLabel : "Total").toUpperCase(), small(th, 9, 'text-anchor="middle" letter-spacing="1"'));
1244
1611
  var lx = x0 + size + 22, rowH = Math.min(24, h2 / v.length), ly = cy - rowH * v.length / 2;
1245
1612
  v.forEach(function(n, i) {
1246
- var y = ly + i * rowH + rowH / 2, pct = Math.round(n / total * 100);
1613
+ var y = ly + i * rowH + rowH / 2, pct = Math.round(n / total2 * 100);
1247
1614
  var tip = (labels[i] || "#" + (i + 1)) + ": " + fmt(n, o) + " (" + pct + "%)";
1248
1615
  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);
1249
1616
  });
1250
1617
  start = 0;
1251
1618
  v.forEach(function(n, i) {
1252
- var len = n / total * 100;
1619
+ var len = n / total2 * 100;
1253
1620
  if (len <= 0) return;
1254
1621
  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) + "%)") + '"/>';
1255
1622
  start += len;
1256
1623
  });
1257
1624
  return s;
1258
- }, { stat: st, summary: "total " + fmt(total, o) + " across " + v.length + " parts" });
1625
+ }, { stat: st, summary: "total " + fmt(total2, o) + " across " + v.length + " parts" });
1259
1626
  }
1260
1627
  donut.table = function(o) {
1261
1628
  var v = nums(o.values).map(function(n) {
1262
1629
  return Math.max(0, n);
1263
- }), labels = list(o.labels), total = sum(v);
1630
+ }), labels = list(o.labels), total2 = sum(v);
1264
1631
  return { head: ["Label", "Value", "Share"], rows: v.map(function(n, i) {
1265
- return [labels[i] || "#" + (i + 1), fmt(n, o), (total ? Math.round(n / total * 100) : 0) + "%"];
1632
+ return [labels[i] || "#" + (i + 1), fmt(n, o), (total2 ? Math.round(n / total2 * 100) : 0) + "%"];
1266
1633
  }) };
1267
1634
  };
1268
1635
 
@@ -1331,8 +1698,8 @@ function progress(o, th) {
1331
1698
  var v = nums(o.values).map(function(n) {
1332
1699
  return Math.max(0, n);
1333
1700
  }), labels = list(o.labels), pal = palette(o, th);
1334
- var single = v.length === 1, total = sum(v);
1335
- var max = has(o.max) ? num(o.max, 100) : single ? 100 : total;
1701
+ var single = v.length === 1, total2 = sum(v);
1702
+ var max = has(o.max) ? num(o.max, 100) : single ? 100 : total2;
1336
1703
  if (!(max > 0)) max = 1;
1337
1704
  var auto = !has(o.prefix) && !has(o.suffix) && max === 100 && single;
1338
1705
  var show = function(n) {
@@ -1357,7 +1724,7 @@ function progress(o, th) {
1357
1724
  return tile(o, th, h, function(x0, y0, w, h2) {
1358
1725
  if (!v.length) return emptyState(th, x0, y0, w, h2);
1359
1726
  var s = "", by = y0 + 22, cid = newId("c");
1360
- var head = single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total, o) + " of " + fmt(max, o);
1727
+ var head = single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total2, o) + " of " + fmt(max, o);
1361
1728
  if (single && labels[0]) s += text(x0, y0 + 11, labels[0], 'fill="' + th.fg + '" font-family="' + SANS + '" font-size="12"');
1362
1729
  s += text(x0 + w, y0 + 11, head, 'text-anchor="end" fill="' + th.fg + '" font-family="' + MONO + '" font-size="11" font-weight="600"');
1363
1730
  s += '<rect x="' + x0 + '" y="' + by + '" width="' + w + '" height="14" rx="7" fill="' + th.grid + '" fill-opacity=".7"/>';
@@ -1385,7 +1752,7 @@ function progress(o, th) {
1385
1752
  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>";
1386
1753
  });
1387
1754
  return s;
1388
- }, { stat: st, summary: single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total, o) + " of " + fmt(max, o) });
1755
+ }, { stat: st, summary: single ? show(v[0]) + (auto ? "" : " of " + fmt(max, o)) : fmt(total2, o) + " of " + fmt(max, o) });
1389
1756
  }
1390
1757
  progress.table = function(o) {
1391
1758
  var v = nums(o.values).map(function(n) {
@@ -1478,7 +1845,7 @@ register("heatmap", heatmap);
1478
1845
  define("heatmap");
1479
1846
 
1480
1847
  // src/index.js
1481
- var version = "1.3.0";
1848
+ var version = "1.5.0";
1482
1849
  var types = NAMES.slice();
1483
1850
  export {
1484
1851
  BezelChart,