bezel-charts 1.3.0 → 1.4.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.4.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", "scatter", "ring", "donut", "gauge", "progress", "heatmap"];
272
272
  var TYPES = {};
273
273
  function register(type, fn) {
274
274
  TYPES[type] = fn;
@@ -655,6 +655,80 @@ function seriesTable(o, total) {
655
655
  return { head: ["Label"].concat(head, total ? ["Total"] : []), rows };
656
656
  }
657
657
 
658
+ // src/core/xy.js
659
+ function xyFrame(o, th, c, x0, y0, w, h) {
660
+ var s = "", top = y0, n = c.n, labels = c.labels;
661
+ if (c.names) {
662
+ var lg = legend(c.names, c.pal, th, x0, top, w, c.key, c.kw);
663
+ s += lg.svg;
664
+ top += lg.height;
665
+ }
666
+ var mn = c.lo, mx = c.hi;
667
+ if (has(o.goal)) {
668
+ mn = Math.min(mn, +o.goal);
669
+ mx = Math.max(mx, +o.goal);
670
+ }
671
+ if (c.zero) {
672
+ mn = Math.min(0, mn);
673
+ mx = Math.max(0, mx);
674
+ }
675
+ var sc = niceScale(mn, mx, 4);
676
+ var tl = sc.ticks.map(c.tick);
677
+ var axisW = Math.max.apply(null, tl.map(function(t) {
678
+ return tw(t, 10, true);
679
+ })) + 10;
680
+ var bottom = labels.length ? 20 : 4;
681
+ var px = x0 + axisW, pw = w - axisW, ph = h - (top - y0) - bottom;
682
+ var X = function(i2) {
683
+ return n === 1 ? px + pw / 2 : px + i2 * pw / (n - 1);
684
+ };
685
+ var Y = function(v) {
686
+ return top + ph - (v - sc.lo) / (sc.hi - sc.lo) * ph;
687
+ };
688
+ sc.ticks.forEach(function(t, i2) {
689
+ var y = Y(t);
690
+ 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"'));
691
+ });
692
+ if (labels.length) {
693
+ var every = Math.max(1, Math.ceil(n * (Math.max.apply(null, labels.map(function(l) {
694
+ return tw(l, 10, true);
695
+ })) + 10) / pw));
696
+ for (var i = 0; i < n; i += every) {
697
+ if (labels[i] == null) continue;
698
+ var anchor = n > 1 && i === 0 ? "start" : n > 1 && i === n - 1 ? "end" : "middle";
699
+ s += text(X(i), top + ph + 15, labels[i], small(th, 10, 'text-anchor="' + anchor + '"'));
700
+ }
701
+ }
702
+ c.notes.forEach(function(nt) {
703
+ if (nt.i >= n) return;
704
+ var x = X(nt.i);
705
+ 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));
706
+ });
707
+ return { svg: s, X, Y, px, pw, top, ph };
708
+ }
709
+ function xyFinish(o, th, f, n, tip) {
710
+ var s = has(o.goal) ? goalLine(o, th, f.px, f.px + f.pw, f.Y(+o.goal)) : "";
711
+ var step = n > 1 ? f.pw / (n - 1) : f.pw;
712
+ for (var j = 0; j < n; j++) s += hit(f.X(j) - step / 2, f.top, step, f.ph, tip(j));
713
+ return s;
714
+ }
715
+ function lineKey(pal, dashOf, markOf, bg) {
716
+ return function(k, x, y) {
717
+ var c = pal[k % pal.length], dk = dashOf ? DASHES[dashOf(k)] : "", mk = markOf ? markOf(k) : "none";
718
+ 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"') : "");
719
+ };
720
+ }
721
+ function seriesLine(o, d, c, k, dk) {
722
+ 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"' : "") + "/>";
723
+ }
724
+ function pointMarks(o, mk, ys, X, Y, c, bg, k) {
725
+ var s = "", every = Math.ceil(ys.length / 40);
726
+ ys.forEach(function(y, i) {
727
+ 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));
728
+ });
729
+ return s;
730
+ }
731
+
658
732
  // src/charts/line.js
659
733
  function line(o, th) {
660
734
  var S = series(o.values), names = list(o.series), labels = list(o.labels), pal = palette(o, th);
@@ -665,68 +739,35 @@ function line(o, th) {
665
739
  var st = statOf(S[0] || [], o, "");
666
740
  var dashOf = styleOf(o.dash, Object.keys(DASHES), "solid"), markOf = styleOf(o.markers, SHAPES, "none");
667
741
  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"') : "");
742
+ var key = (dashOf || markOf) && lineKey(pal, dashOf, markOf, bg);
743
+ var nameOf = function(k) {
744
+ return names[k] || "Series " + (k + 1);
671
745
  };
672
746
  return tile(o, th, h, function(x0, y0, w, h2) {
673
747
  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
748
  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 + '"'));
749
+ var f = xyFrame(o, th, {
750
+ n,
751
+ names: multi ? S.map(function(_, k) {
752
+ return nameOf(k);
753
+ }) : null,
754
+ pal,
755
+ key,
756
+ kw: key && 24,
757
+ lo: Math.min.apply(null, all),
758
+ hi: Math.max.apply(null, all),
759
+ zero: bool(o.zero),
760
+ labels,
761
+ notes,
762
+ tick: function(t) {
763
+ return fmt(t, o, true);
719
764
  }
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
- });
765
+ }, x0, y0, w, h2);
766
+ var s = f.svg, X = f.X, Y = f.Y, px = f.px, pw = f.pw, top = f.top, ph = f.ph;
726
767
  S.forEach(function(v, k) {
727
768
  var c = pal[k % pal.length];
728
- var P = v.map(function(y, i2) {
729
- return [X(i2), Y(y)];
769
+ var P = v.map(function(y, i) {
770
+ return [X(i), Y(y)];
730
771
  });
731
772
  if (P.length === 1) P = [[px, P[0][1]], [px + pw, P[0][1]]];
732
773
  var d = pathOf(P, bool(o.straight));
@@ -734,31 +775,25 @@ function line(o, th) {
734
775
  var g = newId("g"), last = P[P.length - 1];
735
776
  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
777
  }
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"' : "") + "/>";
778
+ var mk = markOf ? markOf(k) : "none";
779
+ s += seriesLine(o, d, c, k, dashOf ? DASHES[dashOf(k)] : "");
739
780
  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
- });
781
+ s += pointMarks(o, mk, v, X, Y, c, bg, k);
744
782
  } else {
745
783
  var e = P[P.length - 1];
746
784
  s += '<circle cx="' + r2(e[0]) + '" cy="' + r2(e[1]) + '" r="4" fill="' + c + '" stroke="' + bg + '" stroke-width="2"/>';
747
785
  }
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"'));
786
+ if (bool(o.showValues)) v.forEach(function(y, i) {
787
+ s += text(X(i), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
750
788
  });
751
789
  });
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++) {
790
+ return s + xyFinish(o, th, f, n, function(j) {
755
791
  var tip = labels[j] || "#" + (j + 1);
756
792
  S.forEach(function(v, k) {
757
- if (j < v.length) tip += "\n" + (multi ? (names[k] || "Series " + (k + 1)) + ": " : "") + fmt(v[j], o);
793
+ if (j < v.length) tip += "\n" + (multi ? nameOf(k) + ": " : "") + fmt(v[j], o);
758
794
  });
759
- s += hit(X(j) - step / 2, top, step, ph, tip);
760
- }
761
- return s;
795
+ return tip;
796
+ });
762
797
  }, { stat: st, summary: S.length + " series over " + n + " points" });
763
798
  }
764
799
  line.table = function(o) {
@@ -769,6 +804,140 @@ line.table = function(o) {
769
804
  register("line", line);
770
805
  define("line");
771
806
 
807
+ // src/charts/area.js
808
+ function area(o, th) {
809
+ var S = series(o.values), names = list(o.series), labels = list(o.labels), pal = palette(o, th);
810
+ var h = num(o.height, 170), k = S.length, n = Math.max.apply(null, [0].concat(S.map(function(s) {
811
+ return s.length;
812
+ })));
813
+ var mode = k > 1 ? o.stacked === "percent" ? "percent" : bool(o.stacked) ? "stack" : "" : "";
814
+ var notes = parseNotes(o, labels), multi = k > 1 || names.length > 0;
815
+ var nameOf = function(j2) {
816
+ return names[j2] || "Series " + (j2 + 1);
817
+ };
818
+ var val = function(j2, i2) {
819
+ return i2 < S[j2].length ? S[j2][i2] : 0;
820
+ };
821
+ var lows = S.map(function() {
822
+ return [];
823
+ }), highs = S.map(function() {
824
+ return [];
825
+ }), totals = [], pos = [];
826
+ for (var i = 0; i < n; i++) {
827
+ var up = 0, down = 0, t = 0, p = 0;
828
+ for (var j = 0; j < k; j++) {
829
+ t += val(j, i);
830
+ p += Math.max(0, val(j, i));
831
+ }
832
+ totals.push(t);
833
+ pos.push(p);
834
+ for (j = 0; j < k; j++) {
835
+ var v = mode === "percent" ? p ? Math.max(0, val(j, i)) / p * 100 : 0 : val(j, i);
836
+ if (!mode) {
837
+ lows[j].push(0);
838
+ highs[j].push(v);
839
+ } else if (v >= 0) {
840
+ lows[j].push(up);
841
+ up += v;
842
+ highs[j].push(up);
843
+ } else {
844
+ lows[j].push(down);
845
+ down += v;
846
+ highs[j].push(down);
847
+ }
848
+ }
849
+ }
850
+ var edges = [].concat.apply([], lows.concat(highs));
851
+ var dashOf = styleOf(o.dash, Object.keys(DASHES), "solid"), markOf = styleOf(o.markers, SHAPES, "none");
852
+ var patOf = k > 1 ? styleOf(o.patterns, PATTERNS, "solid") : null, bg = o.background || th.tile, defs = "", fills = [];
853
+ for (var q = 0; patOf && q < k; q++) {
854
+ if (patOf(q) === "solid") continue;
855
+ var pid = newId("g");
856
+ defs += pattern(pid, patOf(q), pal[q % pal.length], bg);
857
+ fills[q] = "url(#" + pid + ")";
858
+ }
859
+ var key = patOf ? function(j2, x, y) {
860
+ return '<rect x="' + r2(x) + '" y="' + r2(y + 1) + '" width="10" height="10" rx="2" fill="' + (fills[j2] || pal[j2 % pal.length]) + '"/>';
861
+ } : (dashOf || markOf) && lineKey(pal, dashOf, markOf, bg);
862
+ var share = function(j2, i2) {
863
+ return pos[i2] ? Math.round(Math.max(0, val(j2, i2)) / pos[i2] * 100) : 0;
864
+ };
865
+ return tile(o, th, h, function(x0, y0, w, h2) {
866
+ if (!n) return emptyState(th, x0, y0, w, h2);
867
+ var f = xyFrame(o, th, {
868
+ n,
869
+ names: multi ? S.map(function(_, j2) {
870
+ return nameOf(j2);
871
+ }) : null,
872
+ pal,
873
+ key,
874
+ kw: key && (patOf ? 15 : 24),
875
+ lo: Math.min.apply(null, edges),
876
+ hi: Math.max.apply(null, edges),
877
+ zero: true,
878
+ labels,
879
+ notes,
880
+ tick: mode === "percent" ? function(t2) {
881
+ return fmt(t2, { lang: o.lang }, true) + "%";
882
+ } : function(t2) {
883
+ return fmt(t2, o, true);
884
+ }
885
+ }, x0, y0, w, h2);
886
+ var s = (defs ? "<defs>" + defs + "</defs>" : "") + f.svg, X = f.X, Y = f.Y;
887
+ var pts = function(ys) {
888
+ var P = ys.map(function(y, i2) {
889
+ return [X(i2), Y(y)];
890
+ });
891
+ return P.length === 1 ? [[f.px, P[0][1]], [f.px + f.pw, P[0][1]]] : P;
892
+ };
893
+ var straight = bool(o.straight), alpha = mode ? ".8" : ".22";
894
+ var shown = function(j2) {
895
+ return mode ? highs[j2] : S[j2];
896
+ };
897
+ S.forEach(function(_, j2) {
898
+ var top = pts(shown(j2)), base = pts(mode ? lows[j2] : shown(j2).map(function() {
899
+ return 0;
900
+ })).reverse();
901
+ 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"' : "") + "/>";
902
+ });
903
+ S.forEach(function(_, j2) {
904
+ var c = pal[j2 % pal.length], ys = shown(j2), P = pts(ys), mk = markOf ? markOf(j2) : "none";
905
+ s += seriesLine(o, pathOf(P, straight), c, j2, dashOf ? DASHES[dashOf(j2)] : "");
906
+ if (mk !== "none") s += pointMarks(o, mk, ys, X, Y, c, bg, j2);
907
+ 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"/>';
908
+ if (bool(o.showValues) && !mode) ys.forEach(function(y, i2) {
909
+ s += text(X(i2), Y(y) - 8, fmt(y, o, true), small(th, 9, 'text-anchor="middle"'));
910
+ });
911
+ });
912
+ if (bool(o.showValues) && mode === "stack") {
913
+ totals.forEach(function(t2, i2) {
914
+ var top = Math.max.apply(null, highs.map(function(hs) {
915
+ return hs[i2];
916
+ }).concat([0]));
917
+ s += text(X(i2), Y(top) - 8, fmt(t2, o, true), small(th, 9, 'text-anchor="middle"'));
918
+ });
919
+ }
920
+ return s + xyFinish(o, th, f, n, function(i2) {
921
+ var tip = labels[i2] || "#" + (i2 + 1);
922
+ S.forEach(function(ser, j2) {
923
+ if (i2 < ser.length) tip += "\n" + (multi ? nameOf(j2) + ": " : "") + fmt(ser[i2], o) + (mode === "percent" ? " (" + share(j2, i2) + "%)" : "");
924
+ });
925
+ return tip + (mode ? "\nTotal: " + fmt(totals[i2], o) : "");
926
+ });
927
+ }, {
928
+ stat: statOf(mode ? totals : S[0] || [], o, ""),
929
+ summary: k + " series " + (mode === "percent" ? "as shares of each total" : mode ? "stacked" : "filled") + " over " + n + " points"
930
+ });
931
+ }
932
+ area.table = function(o) {
933
+ var S = series(o.values);
934
+ return seriesTable(o, S.length > 1 && (o.stacked === "percent" || bool(o.stacked)));
935
+ };
936
+
937
+ // src/entries/area.js
938
+ register("area", area);
939
+ define("area");
940
+
772
941
  // src/charts/bars.js
773
942
  var ANGLE = 40;
774
943
  var ANGLE_MAX_CHARS = 24;
@@ -1478,7 +1647,7 @@ register("heatmap", heatmap);
1478
1647
  define("heatmap");
1479
1648
 
1480
1649
  // src/index.js
1481
- var version = "1.3.0";
1650
+ var version = "1.4.0";
1482
1651
  var types = NAMES.slice();
1483
1652
  export {
1484
1653
  BezelChart,