@bicharts/chart-host 0.5.45 → 0.5.47

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.
@@ -433,20 +433,20 @@ function A3(n) {
433
433
  if (r && i && c3(r) !== i && (r = null), n.city !== void 0 && n.city !== null) {
434
434
  let a2 = u(String(n.city)), s = O(a2), f = s ? F(s, n.mapKind) : null;
435
435
  if (f && f.length) {
436
- let d = n.state === void 0 || n.state === null ? "" : String(n.state).trim(), R = d ? u(d) : "", g = d ? Y(d) : null, S2 = [], m2 = -1;
436
+ let d = n.state === void 0 || n.state === null ? "" : String(n.state).trim(), R = d ? u(d) : "", g = d ? Y(d) : null, S = [], m2 = -1;
437
437
  for (let c of f) {
438
438
  let l = c.row, A = 0;
439
439
  if (d && l.a1 !== "") if (g && l.a1 === g || u(l.a1) === R) A++;
440
440
  else continue;
441
441
  if (i) if (T(l.cc) === i) A++;
442
442
  else continue;
443
- A > m2 ? (m2 = A, S2 = [c]) : A === m2 && S2.push(c);
443
+ A > m2 ? (m2 = A, S = [c]) : A === m2 && S.push(c);
444
444
  }
445
- S2.some((c) => c.primary) && (S2 = S2.filter((c) => c.primary));
445
+ S.some((c) => c.primary) && (S = S.filter((c) => c.primary));
446
446
  let p2 = /* @__PURE__ */ new Map();
447
- for (let c of S2) p2.set(c.row.lon.toFixed(2) + "," + c.row.lat.toFixed(2), c.row);
447
+ for (let c of S) p2.set(c.row.lon.toFixed(2) + "," + c.row.lat.toFixed(2), c.row);
448
448
  if (p2.size === 1) {
449
- let c = S2[0].row;
449
+ let c = S[0].row;
450
450
  return { lat: c.lat, lon: c.lon, precision: "city" };
451
451
  }
452
452
  if (p2.size > 1) return { ambiguous: true, matches: p2.size };
@@ -469,26 +469,26 @@ function A3(n) {
469
469
  var G = { latlon: 0, city: 1, zip3: 2, state: 3, country: 4 };
470
470
  var x = 8;
471
471
  function y3(n, e) {
472
- let t = new Array(n.length), o = new Array(n.length), i = new Array(n.length), r = /* @__PURE__ */ new Set(), a2 = [], s = /* @__PURE__ */ new Set(), f = [], d = /* @__PURE__ */ new Set(), R = [], g = { latlon: 0, city: 0, zip3: 0, state: 0, country: 0 }, S2 = 0, m2 = 0, p2 = null;
472
+ let t = new Array(n.length), o = new Array(n.length), i = new Array(n.length), r = /* @__PURE__ */ new Set(), a2 = [], s = /* @__PURE__ */ new Set(), f = [], d = /* @__PURE__ */ new Set(), R = [], g = { latlon: 0, city: 0, zip3: 0, state: 0, country: 0 }, S = 0, m2 = 0, p2 = null;
473
473
  for (let l = 0; l < n.length; ++l) {
474
- let A = n[l], C2 = A3({ ...A, mapKind: e });
475
- if (S3(C2)) {
474
+ let A = n[l], C = A3({ ...A, mapKind: e });
475
+ if (S3(C)) {
476
476
  t[l] = null, o[l] = null, i[l] = null, m2++;
477
- let M = K(A);
478
- M && !d.has(M.toLowerCase()) && (d.add(M.toLowerCase()), R.length < x && R.push(M));
479
- } else if (C2) {
480
- if (t[l] = C2.lat, o[l] = C2.lon, i[l] = C2.precision, S2++, g[C2.precision]++, (p2 === null || G[C2.precision] > G[p2]) && (p2 = C2.precision), G[C2.precision] > G.city) {
481
- let M = K(A);
482
- M && !s.has(M.toLowerCase()) && (s.add(M.toLowerCase()), f.length < x && f.push(M));
477
+ let M2 = K(A);
478
+ M2 && !d.has(M2.toLowerCase()) && (d.add(M2.toLowerCase()), R.length < x && R.push(M2));
479
+ } else if (C) {
480
+ if (t[l] = C.lat, o[l] = C.lon, i[l] = C.precision, S++, g[C.precision]++, (p2 === null || G[C.precision] > G[p2]) && (p2 = C.precision), G[C.precision] > G.city) {
481
+ let M2 = K(A);
482
+ M2 && !s.has(M2.toLowerCase()) && (s.add(M2.toLowerCase()), f.length < x && f.push(M2));
483
483
  }
484
484
  } else {
485
485
  t[l] = null, o[l] = null, i[l] = null;
486
- let M = K(A);
487
- M && !r.has(M.toLowerCase()) && (r.add(M.toLowerCase()), a2.push(M));
486
+ let M2 = K(A);
487
+ M2 && !r.has(M2.toLowerCase()) && (r.add(M2.toLowerCase()), a2.push(M2));
488
488
  }
489
489
  }
490
490
  let c = !n3() && n.some((l) => l.city !== null && l.city !== void 0 && String(l.city).trim() !== "");
491
- return { lat: t, lon: o, precisions: i, precision: p2, precisionCounts: g, coarseExamples: f, unmatched: a2, ambiguousRows: m2, ambiguousExamples: R, matchedRows: S2, totalRows: n.length, ...c ? { cityTableMissing: true } : {} };
491
+ return { lat: t, lon: o, precisions: i, precision: p2, precisionCounts: g, coarseExamples: f, unmatched: a2, ambiguousRows: m2, ambiguousExamples: R, matchedRows: S, totalRows: n.length, ...c ? { cityTableMissing: true } : {} };
492
492
  }
493
493
  function K(n) {
494
494
  let e = [n.city, n.state, n.zip, n.country].filter((t) => t != null && String(t).trim() !== "").map((t) => String(t).trim()).join(", ");
@@ -569,13 +569,13 @@ function wn(n) {
569
569
  }
570
570
  function Kn(n, s) {
571
571
  let i = new Array(n.length), r = /* @__PURE__ */ new Set(), A = [], y2 = 0;
572
- for (let S2 = 0; S2 < n.length; ++S2) {
573
- let O3 = hn(n[S2], s);
574
- if (i[S2] = O3, O3 !== null) y2++;
572
+ for (let S = 0; S < n.length; ++S) {
573
+ let O2 = hn(n[S], s);
574
+ if (i[S] = O2, O2 !== null) y2++;
575
575
  else {
576
- let C2 = n[S2];
577
- if (C2 != null) {
578
- let d = String(C2).trim();
576
+ let C = n[S];
577
+ if (C != null) {
578
+ let d = String(C).trim();
579
579
  if (d.length > 0) {
580
580
  let g = d.toLowerCase();
581
581
  r.has(g) || (r.add(g), A.push(d));
@@ -588,142 +588,211 @@ function Kn(n, s) {
588
588
 
589
589
  // ../shape-core/dist/index.mjs
590
590
  /*! @bicharts/shape-core — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; US Census/TIGER (public domain). Full text: NOTICE in this package. */
591
- var w = 2;
592
- var j2 = 400;
593
- function N2(r) {
594
- return !!E(r);
595
- }
596
- function P2(r) {
597
- let n = /* @__PURE__ */ new Set();
598
- for (let e of r) {
599
- if (e == null) continue;
600
- let s = u(String(e));
601
- if (!V(s) && !n.has(s) && (n.add(s), n.size >= j2)) break;
591
+ var W2 = ["rate", "ratio", "pct", "percent", "percentage", "rating", "share", "score", "index", "nps", "csat", "margin", "yield", "coverage", "utilization", "z[_-]?score", "average", "avg", "mean", "median", "stddev", "variance", "gpa", "percentile", "quantile", "efficiency", "accuracy", "probability", "multiplier", "coefficient", "correlation", "occupancy", "age", "bmi", "temperature", "density", "pressure", "humidity", "ph", "velocity", "speed", "elevation", "altitude", "latency", "throughput", "bandwidth", "iops"];
592
+ var X2 = ["pct", "percent", "percentage", "rate", "ratio", "rating", "share", "score", "index", "nps", "csat", "margin", "yield", "coverage", "utilization", "gpa", "percentile", "quantile", "bmi", "density", "pressure", "humidity", "velocity", "efficiency", "occupancy", "latency", "throughput", "bandwidth", "iops"];
593
+ var Y2 = ["latitude", "longitude"];
594
+ var J2 = ["latitude", "longitude"];
595
+ var Q2 = /^\s*(sum|count|count\s+distinct|distinct\s+count)\s+of\s+/i;
596
+ var Ut = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Za-z])(?=[0-9])|(?<=[0-9])(?=[A-Za-z])/g;
597
+ var Zt = new RegExp("\\b(" + W2.join("|") + ")\\b|(" + X2.join("|") + ")$", "i");
598
+ var qt = new RegExp("\\b(" + Y2.join("|") + ")\\b|(" + J2.join("|") + ")$", "i");
599
+ function M(e) {
600
+ return e ? String(e).replace(Q2, "").trim() : "";
601
+ }
602
+ function tt(e) {
603
+ return !!e && Q2.test(String(e));
604
+ }
605
+ function jt(e) {
606
+ let t = M(e);
607
+ return t.length === 0 ? "" : t.replace(Ut, " ");
608
+ }
609
+ function et(e, t) {
610
+ return t ? e.test(jt(t)) || e.test(M(t)) : false;
611
+ }
612
+ function L2(e) {
613
+ return et(qt, e);
614
+ }
615
+ function nt(e) {
616
+ return et(Zt, e) || L2(e);
617
+ }
618
+ var it = /int|double|decimal|single|float|number|currency|money/i;
619
+ function Wt(e) {
620
+ let t = String(e?.valueNature ?? "").trim().toLowerCase();
621
+ return t === "categorical" ? "Categorical" : t === "ordinal" ? "Ordinal" : t === "continuous" || e?.isMeasure === true || it.test(String(e?.dataType ?? "")) ? "Continuous" : "Categorical";
622
+ }
623
+ function Xt(e) {
624
+ return e?.isMeasure === true || it.test(String(e?.dataType ?? ""));
625
+ }
626
+ function z2(e) {
627
+ let t = e ?? {}, i = Wt(t);
628
+ if (i !== "Continuous") return { nature: i, additivity: "unknown", basis: "role", suppressed: false };
629
+ if (L2(t.name)) return { nature: i, additivity: "positional", basis: "name", suppressed: true };
630
+ let r = String(t.additivity ?? "").trim().toLowerCase();
631
+ if (!(r === "additive" && tt(t.name))) {
632
+ if (r === "additive") return { nature: i, additivity: "additive", basis: "measured", suppressed: false };
633
+ if (r === "part_of_whole") return { nature: i, additivity: "part_of_whole", basis: "measured", suppressed: false };
634
+ if (r === "intensive_rate") return { nature: i, additivity: "intensive_rate", basis: "measured", suppressed: false };
602
635
  }
603
- return Array.from(n);
604
- }
605
- function C(r) {
606
- let n = P2(r);
607
- if (n.length === 0 || E2(r) < 95) return false;
608
- let e = n.filter((l) => N2(l) && !Y(l)).length, s = n.filter((l) => !N2(l) && !!Y(l)).length;
609
- return e >= 1 && e >= s;
610
- }
611
- function q2(r, n) {
612
- if (!Y(r)) return false;
613
- let e = E(r);
614
- return e ? n === "world" ? true : s3.has(e) : false;
615
- }
616
- function E2(r) {
617
- let n = P2(r);
618
- if (n.length === 0) return 0;
619
- let e = 0;
620
- for (let s of n) N2(s) && e++;
621
- return Math.round(e / n.length * 1e3) / 10;
622
- }
623
- function S(r) {
624
- let n = P2(r);
625
- if (n.length === 0) return 0;
626
- let e = 0;
627
- for (let s of n) Y(s) && e++;
628
- return Math.round(e / n.length * 1e3) / 10;
629
- }
630
- function D2(r) {
631
- let n = /* @__PURE__ */ new Set();
632
- for (let e of r) {
633
- if (e == null) continue;
634
- let s = String(e).trim();
635
- if (!V(u(s)) && !n.has(s) && (n.add(s), n.size >= j2)) break;
636
+ return t.discourageAggregationAcrossGroups === true ? { nature: i, additivity: "intensive_rate", basis: "host-flag", suppressed: false } : nt(t.name) ? { nature: i, additivity: "intensive_rate", basis: "name", suppressed: false } : { nature: i, additivity: "additive", basis: "default", suppressed: false };
637
+ }
638
+ function G2(e) {
639
+ let t = z2(e), i = e ?? {};
640
+ if (t.nature === "Categorical") return ["first", "distinctcount", "count"];
641
+ if (t.nature === "Ordinal") return Xt(i) ? ["median", "average", "min", "max", "first", "distinctcount", "count"] : ["first", "distinctcount", "count"];
642
+ switch (t.additivity) {
643
+ case "positional":
644
+ return ["min", "max", "count", "distinctcount", "first"];
645
+ case "intensive_rate":
646
+ return ["average", "median", "min", "max", "count", "distinctcount", "first"];
647
+ default:
648
+ return ["sum", "average", "median", "min", "max", "count", "distinctcount", "first"];
636
649
  }
637
- return Array.from(n);
638
- }
639
- function O2(r) {
640
- let n = D2(r);
641
- if (n.length === 0) return 0;
642
- let e = 0;
643
- for (let s of n) Z(s).length > 0 && e++;
644
- return Math.round(e / n.length * 1e3) / 10;
645
- }
646
- function Z2(r, n) {
647
- return p(n, en) ? true : D2(r).some((e) => /^\d{5}(-\d{4})?$/.test(e.replace(/\.0+$/, "")));
648
- }
649
- function Gt(r, n) {
650
- let e = P2(r);
651
- if (e.length === 0) return 0;
652
- let s = 0;
653
- for (let l of e) p3(l, n) && s++;
654
- return Math.round(s / e.length * 1e3) / 10;
655
- }
656
- function $2(r, n) {
657
- let e = 0;
658
- for (let s of P2(r)) n(s) && e++;
659
- return e;
660
- }
661
- function _t(r, n) {
662
- let e = 0;
663
- for (let s of D2(r)) n(s) && e++;
664
- return e;
665
650
  }
666
- function wt(r, n, e, s) {
667
- let l = [], c = [], o = {}, v2 = {}, k2 = new Set(r), u2 = (t) => n.map((i) => i[t]);
668
- for (let t of ["city", "lat", "lon", "country"]) {
669
- let i = e?.[t];
670
- i && k2.has(i) && (o[t] = i);
651
+ function Yt(e) {
652
+ return G2(e)[0];
653
+ }
654
+ function Qt(e, t) {
655
+ if (t !== "sum" && t !== "count") return false;
656
+ if (t === "count") return true;
657
+ let i = z2(e);
658
+ return i.additivity === "additive" || i.additivity === "part_of_whole" || i.additivity === "unknown";
659
+ }
660
+ var F2 = 2;
661
+ var st = 400;
662
+ function D2(e) {
663
+ return !!E(e);
664
+ }
665
+ function v2(e) {
666
+ let t = /* @__PURE__ */ new Set();
667
+ for (let i of e) {
668
+ if (i == null) continue;
669
+ let r = u(String(i));
670
+ if (!V(r) && !t.has(r) && (t.add(r), t.size >= st)) break;
671
+ }
672
+ return Array.from(t);
673
+ }
674
+ function h2(e) {
675
+ let t = v2(e);
676
+ if (t.length === 0 || K2(e) < 95) return false;
677
+ let i = t.filter((u2) => D2(u2) && !Y(u2)).length, r = t.filter((u2) => !D2(u2) && !!Y(u2)).length;
678
+ return i >= 1 && i >= r;
679
+ }
680
+ function ot(e, t) {
681
+ if (!Y(e)) return false;
682
+ let i = E(e);
683
+ return i ? t === "world" ? true : s3.has(i) : false;
684
+ }
685
+ function K2(e) {
686
+ let t = v2(e);
687
+ if (t.length === 0) return 0;
688
+ let i = 0;
689
+ for (let r of t) D2(r) && i++;
690
+ return Math.round(i / t.length * 1e3) / 10;
691
+ }
692
+ function P2(e) {
693
+ let t = v2(e);
694
+ if (t.length === 0) return 0;
695
+ let i = 0;
696
+ for (let r of t) Y(r) && i++;
697
+ return Math.round(i / t.length * 1e3) / 10;
698
+ }
699
+ function H2(e) {
700
+ let t = /* @__PURE__ */ new Set();
701
+ for (let i of e) {
702
+ if (i == null) continue;
703
+ let r = String(i).trim();
704
+ if (!V(u(r)) && !t.has(r) && (t.add(r), t.size >= st)) break;
705
+ }
706
+ return Array.from(t);
707
+ }
708
+ function B2(e) {
709
+ let t = H2(e);
710
+ if (t.length === 0) return 0;
711
+ let i = 0;
712
+ for (let r of t) Z(r).length > 0 && i++;
713
+ return Math.round(i / t.length * 1e3) / 10;
714
+ }
715
+ function rt(e, t) {
716
+ return p(t, en) ? true : H2(e).some((i) => /^\d{5}(-\d{4})?$/.test(i.replace(/\.0+$/, "")));
717
+ }
718
+ function te(e, t) {
719
+ let i = v2(e);
720
+ if (i.length === 0) return 0;
721
+ let r = 0;
722
+ for (let u2 of i) p3(u2, t) && r++;
723
+ return Math.round(r / i.length * 1e3) / 10;
724
+ }
725
+ function $2(e, t) {
726
+ let i = 0;
727
+ for (let r of v2(e)) t(r) && i++;
728
+ return i;
729
+ }
730
+ function ee(e, t) {
731
+ let i = 0;
732
+ for (let r of H2(e)) t(r) && i++;
733
+ return i;
734
+ }
735
+ function ne(e, t, i, r) {
736
+ let u2 = [], c = [], o = {}, I2 = {}, R = new Set(e), l = (n) => t.map((s) => s[n]);
737
+ for (let n of ["city", "lat", "lon", "country"]) {
738
+ let s = i?.[n];
739
+ s && R.has(s) && (o[n] = s);
671
740
  }
672
- let y2 = e?.zip;
673
- if (y2 && k2.has(y2)) {
674
- let t = u2(y2), i = O2(t);
675
- i < 95 ? c.push(`zip=${y2} (only ${i}% of values read as ZIPs)`) : Z2(t, y2) ? o.zip = y2 : c.push(`zip=${y2} (all values are 3-4 digits with nothing to say they are postal)`);
741
+ let p2 = i?.zip;
742
+ if (p2 && R.has(p2)) {
743
+ let n = l(p2), s = B2(n);
744
+ s < 95 ? c.push(`zip=${p2} (only ${s}% of values read as ZIPs)`) : rt(n, p2) ? o.zip = p2 : c.push(`zip=${p2} (all values are 3-4 digits with nothing to say they are postal)`);
676
745
  }
677
- let m2 = e?.state;
678
- if (m2 && k2.has(m2)) {
679
- let t = u2(m2), i = P2(t);
680
- i.length > 0 && i.every((a2) => q2(a2, s)) ? c.push(`state=${m2} (every value is "CA", which is both Canada and California)`) : C(t) ? (c.push(`state=${m2} (every value is a country, not a state)`), o.country || (o.country = m2)) : S(t) < 95 ? c.push(`state=${m2} (only ${S(t)}% of values are states/provinces)`) : o.state = m2;
746
+ let f = i?.state;
747
+ if (f && R.has(f)) {
748
+ let n = l(f), s = v2(n);
749
+ s.length > 0 && s.every((a2) => ot(a2, r)) ? c.push(`state=${f} (every value is "CA", which is both Canada and California)`) : h2(n) ? (c.push(`state=${f} (every value is a country, not a state)`), o.country || (o.country = f)) : P2(n) < 95 ? c.push(`state=${f} (only ${P2(n)}% of values are states/provinces)`) : o.state = f;
681
750
  }
682
- let d = new Set(Object.values(o).filter(Boolean)), x2 = r.filter((t) => !d.has(t) && !t.startsWith("__"));
751
+ let m2 = new Set(Object.values(o).filter(Boolean)), x2 = e.filter((n) => !m2.has(n) && !n.startsWith("__"));
683
752
  if (!o.state) {
684
- let t = null;
685
- for (let i of x2) {
686
- let a2 = u2(i);
687
- if (C(a2)) continue;
688
- let f = S(a2);
689
- if (f < 95) continue;
690
- let z2 = $2(a2, (b2) => !!Y(b2)), W2 = $2(a2, (b2) => !!Y(b2) && !q2(b2, s));
691
- z2 < w && W2 < 1 || (!t || f > t.pct) && (t = { name: i, pct: f });
753
+ let n = null;
754
+ for (let s of x2) {
755
+ let a2 = l(s);
756
+ if (h2(a2)) continue;
757
+ let d = P2(a2);
758
+ if (d < 95) continue;
759
+ let E2 = $2(a2, (C) => !!Y(C)), ut = $2(a2, (C) => !!Y(C) && !ot(C, r));
760
+ E2 < F2 && ut < 1 || (!n || d > n.pct) && (n = { name: s, pct: d });
692
761
  }
693
- t && (o.state = t.name, d.add(t.name), l.push(`state=${t.name} (${t.pct}% states/provinces)`));
762
+ n && (o.state = n.name, m2.add(n.name), u2.push(`state=${n.name} (${n.pct}% states/provinces)`));
694
763
  }
695
- if (!o.zip) for (let t of x2) {
696
- if (d.has(t)) continue;
697
- let i = u2(t), a2 = O2(i);
698
- if (!(a2 < 95) && Z2(i, t) && !(_t(i, (f) => Z(f).length > 0) < w)) {
699
- o.zip = t, d.add(t), l.push(`zip=${t} (${a2}% ZIP codes)`);
764
+ if (!o.zip) for (let n of x2) {
765
+ if (m2.has(n)) continue;
766
+ let s = l(n), a2 = B2(s);
767
+ if (!(a2 < 95) && rt(s, n) && !(ee(s, (d) => Z(d).length > 0) < F2)) {
768
+ o.zip = n, m2.add(n), u2.push(`zip=${n} (${a2}% ZIP codes)`);
700
769
  break;
701
770
  }
702
771
  }
703
- let h2 = o.country;
704
- if (!h2 || !C(u2(h2))) {
705
- let t = null;
706
- for (let i of x2) {
707
- if (d.has(i) || !C(u2(i))) continue;
708
- let a2 = E2(u2(i));
709
- (!t || a2 > t.pct) && (t = { name: i, pct: a2 });
772
+ let A = o.country;
773
+ if (!A || !h2(l(A))) {
774
+ let n = null;
775
+ for (let s of x2) {
776
+ if (m2.has(s) || !h2(l(s))) continue;
777
+ let a2 = K2(l(s));
778
+ (!n || a2 > n.pct) && (n = { name: s, pct: a2 });
710
779
  }
711
- t && (h2 && (c.push(`country=${h2} (only ${E2(u2(h2))}% of values are country identifiers; using ${t.name} instead)`), v2.country = h2), o.country = t.name, d.add(t.name), l.push(`country=${t.name}`));
780
+ n && (A && (c.push(`country=${A} (only ${K2(l(A))}% of values are country identifiers; using ${n.name} instead)`), I2.country = A), o.country = n.name, m2.add(n.name), u2.push(`country=${n.name}`));
712
781
  }
713
782
  if (!o.city) {
714
- let t = null;
715
- for (let i of x2) {
716
- if (d.has(i)) continue;
717
- let a2 = u2(i), f = Gt(a2, s);
718
- f < m3 || $2(a2, (z2) => p3(z2, s)) < w || (!t || f > t.pct) && (t = { name: i, pct: f });
783
+ let n = null;
784
+ for (let s of x2) {
785
+ if (m2.has(s)) continue;
786
+ let a2 = l(s), d = te(a2, r);
787
+ d < m3 || $2(a2, (E2) => p3(E2, r)) < F2 || (!n || d > n.pct) && (n = { name: s, pct: d });
719
788
  }
720
- t && (o.city = t.name, l.push(`city=${t.name} (${t.pct}% known cities)`));
789
+ n && (o.city = n.name, u2.push(`city=${n.name} (${n.pct}% known cities)`));
721
790
  }
722
- let B2 = o.country ? u2(o.country) : [], I2 = !!o.country && C(B2), U2 = I2 ? new Set(B2.map((t) => E(t == null ? null : String(t))).filter(Boolean)).size : 0, F2 = I2 && U2 >= 2, L2 = !!(o.lat && o.lon) && n.some((t) => {
723
- let i = +String(t[o.lat] ?? "").trim(), a2 = +String(t[o.lon] ?? "").trim();
724
- return isFinite(i) && isFinite(a2) && i >= -90 && i <= 90 && a2 >= -180 && a2 <= 180 && String(t[o.lat] ?? "").trim() !== "" && String(t[o.lon] ?? "").trim() !== "";
791
+ let V2 = o.country ? l(o.country) : [], N2 = !!o.country && h2(V2), at = N2 ? new Set(V2.map((n) => E(n == null ? null : String(n))).filter(Boolean)).size : 0, U2 = N2 && at >= 2, O2 = !!(o.lat && o.lon) && t.some((n) => {
792
+ let s = +String(n[o.lat] ?? "").trim(), a2 = +String(n[o.lon] ?? "").trim();
793
+ return isFinite(s) && isFinite(a2) && s >= -90 && s <= 90 && a2 >= -180 && a2 <= 180 && String(n[o.lat] ?? "").trim() !== "" && String(n[o.lon] ?? "").trim() !== "";
725
794
  });
726
- return o.lat && o.lon && !L2 && c.push(`lat/lon=${o.lat}/${o.lon} (no row holds a usable coordinate pair)`), o.country && !F2 && !(o.city || o.state || o.zip || L2) && c.push(I2 ? `country=${o.country} (every row resolves to the same country - nothing to place apart)` : `country=${o.country} (values are not country identifiers)`), { bind: !!(o.city || o.state || o.zip || F2 || L2) ? o : null, backfilled: l, refused: c, ...Object.keys(v2).length ? { labelFallback: v2 } : {} };
795
+ return o.lat && o.lon && !O2 && c.push(`lat/lon=${o.lat}/${o.lon} (no row holds a usable coordinate pair)`), o.country && !U2 && !(o.city || o.state || o.zip || O2) && c.push(N2 ? `country=${o.country} (every row resolves to the same country - nothing to place apart)` : `country=${o.country} (values are not country identifiers)`), { bind: !!(o.city || o.state || o.zip || U2 || O2) ? o : null, backfilled: u2, refused: c, ...Object.keys(I2).length ? { labelFallback: I2 } : {} };
727
796
  }
728
797
 
729
798
  // src/payload.ts
@@ -738,7 +807,7 @@ function resolvePointChannel(cols, rowObjs, point) {
738
807
  let labelCol;
739
808
  if (bind) {
740
809
  const dims = cols.filter((c) => !c.isMeasure).map((c) => c.name);
741
- const res = wt(dims, rowObjs, bind, point?.mapKind);
810
+ const res = ne(dims, rowObjs, bind, point?.mapKind);
742
811
  bind = res.bind;
743
812
  rolesBackfilled = res.backfilled;
744
813
  rolesRefused = res.refused;
@@ -806,11 +875,11 @@ function buildRenderPayload(cols, rowObjs, geo, point, destination) {
806
875
  const row = rowObjs[r];
807
876
  const a2 = new Array(cols.length + extra);
808
877
  for (let c = 0; c < cols.length; c++) {
809
- const v2 = row[colNames[c]];
810
- if (v2 instanceof Date) {
811
- a2[c] = v2.toISOString();
878
+ const v3 = row[colNames[c]];
879
+ if (v3 instanceof Date) {
880
+ a2[c] = v3.toISOString();
812
881
  } else {
813
- a2[c] = v2 ?? null;
882
+ a2[c] = v3 ?? null;
814
883
  }
815
884
  }
816
885
  let k2 = cols.length;
@@ -862,8 +931,8 @@ function buildRenderPayload(cols, rowObjs, geo, point, destination) {
862
931
  function createMarkResolver(env) {
863
932
  const { root, doc, log } = env;
864
933
  const isElement = (t) => {
865
- const E4 = doc?.defaultView?.Element ?? globalThis.Element;
866
- if (E4 && t instanceof E4) return true;
934
+ const E2 = doc?.defaultView?.Element ?? globalThis.Element;
935
+ if (E2 && t instanceof E2) return true;
867
936
  return !!t && typeof t.closest === "function" && typeof t.getAttribute === "function";
868
937
  };
869
938
  const viewStyle = (n) => {
@@ -883,14 +952,14 @@ function createMarkResolver(env) {
883
952
  return s === "transparent" || s === "none" || so <= 0.01;
884
953
  };
885
954
  const distToPathCenterline = (el, x2, y2) => {
886
- const L2 = el.getTotalLength();
887
- if (!(L2 > 0)) return Number.MAX_VALUE;
955
+ const L4 = el.getTotalLength();
956
+ if (!(L4 > 0)) return Number.MAX_VALUE;
888
957
  const m2 = el.getScreenCTM();
889
958
  if (!m2) return Number.MAX_VALUE;
890
959
  let best = Number.MAX_VALUE;
891
- const N3 = 64;
892
- for (let i = 0; i <= N3; i++) {
893
- const pt = el.getPointAtLength(L2 * i / N3);
960
+ const N2 = 64;
961
+ for (let i = 0; i <= N2; i++) {
962
+ const pt = el.getPointAtLength(L4 * i / N2);
894
963
  const dx = m2.a * pt.x + m2.c * pt.y + m2.e - x2;
895
964
  const dy = m2.b * pt.x + m2.d * pt.y + m2.f - y2;
896
965
  const d = dx * dx + dy * dy;
@@ -1194,7 +1263,7 @@ function createChartHost(container, config) {
1194
1263
  let data = config.data;
1195
1264
  {
1196
1265
  const stamped = config.data?.meta?.hostContract;
1197
- const major = (v2) => (v2 || "").split(".")[0];
1266
+ const major = (v3) => (v3 || "").split(".")[0];
1198
1267
  if (stamped && major(stamped) !== major(HOST_CONTRACT_VERSION)) {
1199
1268
  try {
1200
1269
  (win?.console ?? console)?.warn(
@@ -1230,9 +1299,9 @@ function createChartHost(container, config) {
1230
1299
  const ensureAffordanceStyles = () => {
1231
1300
  try {
1232
1301
  if (!doc || doc.getElementById("bic-chart-host-affordances")) return;
1233
- const st = doc.createElement("style");
1234
- st.id = "bic-chart-host-affordances";
1235
- st.textContent = `
1302
+ const st2 = doc.createElement("style");
1303
+ st2.id = "bic-chart-host-affordances";
1304
+ st2.textContent = `
1236
1305
  /* TYPOGRAPHY FIREWALL. Generated charts set font-size but almost never line-height or
1237
1306
  letter-spacing, so they INHERIT whatever the host page cascades in. A page with
1238
1307
  \`:root { font: 18px/145% }\` (the stock Vite template does exactly this) hands every
@@ -1270,7 +1339,7 @@ function createChartHost(container, config) {
1270
1339
  font-weight: 700 !important;
1271
1340
  text-decoration: underline !important;
1272
1341
  }`;
1273
- (doc.head || doc.documentElement)?.appendChild(st);
1342
+ (doc.head || doc.documentElement)?.appendChild(st2);
1274
1343
  } catch {
1275
1344
  }
1276
1345
  };
@@ -1503,6 +1572,10 @@ export {
1503
1572
  registerGeoAsset,
1504
1573
  clearGeoCache,
1505
1574
  L3,
1575
+ z2 as z,
1576
+ G2 as G,
1577
+ Yt,
1578
+ Qt,
1506
1579
  buildRenderPayload,
1507
1580
  createMarkResolver,
1508
1581
  ensureCrossfilterHitTargets,
package/dist/index.mjs CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  DIM_OPACITY_DEFAULT,
21
21
  DIM_OPACITY_VAR,
22
22
  FLIP_MODE_DEFAULT,
23
+ G,
23
24
  GEO_POINT_PRECISIONS,
24
25
  HOST_CONTAINER_CLASS,
25
26
  HOST_CONTRACT_VERSION,
@@ -27,9 +28,11 @@ import {
27
28
  LEGEND_MARK_CLASS,
28
29
  MARK_CLASS,
29
30
  MARK_SELECTED_CLASS,
31
+ Qt,
30
32
  ROW_IDX_ATTR,
31
33
  SELECTION_ACTIVE_CLASS,
32
34
  XFILTER_REFRESH_EVENT,
35
+ Yt,
33
36
  blankRenderFlag,
34
37
  buildRenderPayload,
35
38
  censusMarks,
@@ -49,8 +52,9 @@ import {
49
52
  registerGeoAsset,
50
53
  requiredD3Plugins,
51
54
  resolveOptions,
52
- stripEsmExports
53
- } from "./chunk-DZ5FNFID.mjs";
55
+ stripEsmExports,
56
+ z
57
+ } from "./chunk-JPDIL3HN.mjs";
54
58
  import "./chunk-A2GMXZP7.mjs";
55
59
 
56
60
  // src/trivial.ts
@@ -625,9 +629,6 @@ function normaliseAggregation(raw) {
625
629
  if (/^(distinctcount|dcount|countdistinct|uniques?)$/.test(s)) return "distinctcount";
626
630
  return "sum";
627
631
  }
628
- function isAdditive(agg) {
629
- return agg === "sum" || agg === "count";
630
- }
631
632
  function aggLabel(agg) {
632
633
  switch (agg) {
633
634
  case "average":
@@ -654,6 +655,20 @@ function isMeasureColumn(col) {
654
655
  if (col.isMeasure === true) return true;
655
656
  return NUMERIC_DATATYPE.test(String(col.dataType ?? ""));
656
657
  }
658
+ function isRealColumn(col) {
659
+ return !!col && !isSynthetic(col.name);
660
+ }
661
+ function isSuppressed(col) {
662
+ return z(col).suppressed;
663
+ }
664
+ function resolveAgg(col, requested, auto) {
665
+ const fallback = () => {
666
+ const d = Yt(col);
667
+ return d === "first" ? "count" : d;
668
+ };
669
+ if (auto) return fallback();
670
+ return G(col).indexOf(requested) >= 0 ? requested : fallback();
671
+ }
657
672
  function toNumber(v) {
658
673
  if (v == null || v === "") return null;
659
674
  const n = typeof v === "number" ? v : Number(v);
@@ -709,9 +724,11 @@ function computeSelectionCard(payload, selectedRowIdxs, opts = {}) {
709
724
  if (pos != null && pos >= 0 && pos < rows.length) positions.push(pos);
710
725
  }
711
726
  if (!positions.length) return null;
727
+ const auto = String(opts.aggregation ?? "").trim().length === 0;
712
728
  const agg = normaliseAggregation(opts.aggregation);
713
729
  const maxMeasures = opts.maxMeasures ?? 4;
714
730
  const maxHeaderValues = opts.maxHeaderValues ?? 3;
731
+ const maxDimensions = opts.maxDimensions ?? 2;
715
732
  const dimIdx = columns.findIndex((c) => c && !isSynthetic(c.name) && !isMeasureColumn(c));
716
733
  let header;
717
734
  if (dimIdx >= 0) {
@@ -731,10 +748,20 @@ function computeSelectionCard(payload, selectedRowIdxs, opts = {}) {
731
748
  header = `${positions.length} mark${positions.length === 1 ? "" : "s"}`;
732
749
  }
733
750
  const measureCols = [];
734
- for (let c = 0; c < columns.length; c++) if (isMeasureColumn(columns[c])) measureCols.push(c);
751
+ const suppressedColumns = [];
752
+ for (let c = 0; c < columns.length; c++) {
753
+ if (!isMeasureColumn(columns[c])) continue;
754
+ if (isSuppressed(columns[c])) {
755
+ suppressedColumns.push(String(columns[c]?.name ?? ""));
756
+ continue;
757
+ }
758
+ measureCols.push(c);
759
+ }
735
760
  const shown = measureCols.slice(0, maxMeasures);
736
761
  const lines = [];
737
762
  for (const c of shown) {
763
+ const col = columns[c];
764
+ const colAgg = resolveAgg(col, agg, auto);
738
765
  const selVals = [];
739
766
  const selRaw = [];
740
767
  for (const p of positions) {
@@ -742,9 +769,9 @@ function computeSelectionCard(payload, selectedRowIdxs, opts = {}) {
742
769
  selRaw.push(rows[p]?.[c]);
743
770
  if (n != null) selVals.push(n);
744
771
  }
745
- const value = reduce(selVals, agg, selRaw);
772
+ const value = reduce(selVals, colAgg, selRaw);
746
773
  let sharePct = null;
747
- if (isAdditive(agg) && value != null) {
774
+ if (Qt(col, colAgg) && value != null) {
748
775
  const allVals = [];
749
776
  const allRaw = [];
750
777
  for (let r = 0; r < rows.length; r++) {
@@ -752,21 +779,49 @@ function computeSelectionCard(payload, selectedRowIdxs, opts = {}) {
752
779
  allRaw.push(rows[r]?.[c]);
753
780
  if (n != null) allVals.push(n);
754
781
  }
755
- const total = reduce(allVals, agg, allRaw);
782
+ const total = reduce(allVals, colAgg, allRaw);
756
783
  if (total != null && total > 0) sharePct = value / total * 100;
757
784
  }
758
785
  lines.push({
759
- column: String(columns[c]?.name ?? ""),
760
- label: `${aggLabel(agg)} of ${columns[c]?.name ?? ""}`,
786
+ column: String(col?.name ?? ""),
787
+ label: `${aggLabel(colAgg)} of ${col?.name ?? ""}`,
761
788
  value,
762
789
  valueText: value == null ? "\u2014" : fmtNumber(value, opts.cultureCode),
763
790
  sharePct,
764
- shareText: sharePct == null ? "" : `${sharePct < 0.1 ? "<0.1" : sharePct.toFixed(1)}% of total`
791
+ shareText: sharePct == null ? "" : `${sharePct < 0.1 ? "<0.1" : sharePct.toFixed(1)}% of total`,
792
+ aggregation: colAgg
793
+ });
794
+ }
795
+ const dimensionLines = [];
796
+ for (let c = 0; c < columns.length && dimensionLines.length < maxDimensions; c++) {
797
+ if (c === dimIdx) continue;
798
+ if (!isRealColumn(columns[c]) || isMeasureColumn(columns[c])) continue;
799
+ const name = String(columns[c]?.name ?? "");
800
+ const distinct = /* @__PURE__ */ new Set();
801
+ let firstText = "";
802
+ for (const p of positions) {
803
+ const raw = rows[p]?.[c];
804
+ const s = raw == null || raw === "" ? "(blank)" : String(raw);
805
+ if (!distinct.size) firstText = s;
806
+ distinct.add(s);
807
+ }
808
+ if (!distinct.size) continue;
809
+ const single = distinct.size === 1;
810
+ dimensionLines.push({
811
+ column: name,
812
+ label: single ? name : `Distinct ${name}`,
813
+ value: single ? null : distinct.size,
814
+ valueText: single ? firstText : fmtNumber(distinct.size, opts.cultureCode),
815
+ sharePct: null,
816
+ shareText: "",
817
+ aggregation: single ? "first" : "distinctcount"
765
818
  });
766
819
  }
767
820
  return {
768
821
  header,
769
822
  lines,
823
+ dimensionLines,
824
+ suppressedColumns,
770
825
  rowsText: `${positions.length} of ${rows.length} rows`,
771
826
  selectedRows: positions.length,
772
827
  totalRows: rows.length,
package/dist/react.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createChartHost,
5
5
  geoFromCache,
6
6
  loadGeo
7
- } from "./chunk-DZ5FNFID.mjs";
7
+ } from "./chunk-JPDIL3HN.mjs";
8
8
  import "./chunk-A2GMXZP7.mjs";
9
9
 
10
10
  // src/react.tsx
@@ -1,7 +1,16 @@
1
1
  import type { RenderPayload } from "./payload";
2
2
  export interface SelectionCardOptions {
3
3
  /** The chart's own aggregation, so the card AGREES with the picture it sits on. A card
4
- * reading "Sum" beside a chart drawn from averages is two answers to one question. */
4
+ * reading "Sum" beside a chart drawn from averages is two answers to one question.
5
+ *
6
+ * BLANK MEANS AUTO, and Auto is now per COLUMN. The Excel settings panel has offered
7
+ * "Auto — sum amounts, average rates" since the control shipped; `normaliseAggregation("")`
8
+ * answered `sum` for every column alike, so the label described a rule the code did not
9
+ * implement. A blank now resolves through `defaultAggregation` per column: amounts sum,
10
+ * rates average, dimensions report their value. A NAMED aggregation is still honoured
11
+ * wherever it is honest, and substituted per column where it is not — the label carries
12
+ * the substitution, so "Average of ChurnRate" beside "Sum of Revenue" reads as the answer
13
+ * it is rather than a silent override. */
5
14
  aggregation?: string;
6
15
  cultureCode?: string;
7
16
  /** Cards are capped at roughly a third of the tile, so the measure list is capped too.
@@ -10,29 +19,55 @@ export interface SelectionCardOptions {
10
19
  /** Past this many distinct dimension values the header becomes "N marks" — naming twelve
11
20
  * cities in a header is not a header. */
12
21
  maxHeaderValues?: number;
22
+ /** How many DIMENSION lines to add below the measures. Capped separately and small on
23
+ * purpose: dimensions must never crowd out the amounts, which is the failure the measure
24
+ * cap already had (on the sample table the two coordinate columns took two of the four
25
+ * measure slots and pushed the real measures under "+2 more"). Default 2. */
26
+ maxDimensions?: number;
13
27
  }
14
28
  export interface SelectionCardLine {
15
29
  column: string;
16
- /** "Sum of Revenue" — the aggregation is named because it is not always sum. */
30
+ /** "Sum of Revenue" — the aggregation is named because it is not always sum. For a
31
+ * dimension resolving to ONE value it is just the column name, because "First of Segment:
32
+ * East" says nothing "Segment: East" does not. */
17
33
  label: string;
34
+ /** Null for a dimension line, whose answer is text. `valueText` is what a host renders. */
18
35
  value: number | null;
19
36
  valueText: string;
20
- /** Share of the same aggregate over ALL rows. Null whenever a share would be a lie — see
21
- * isAdditive: the average of a subset is not a percentage of the average of the whole. */
37
+ /** Share of the same aggregate over ALL rows. Null whenever a share would be a lie — it
38
+ * needs BOTH an additive aggregation and an additive COLUMN. The average of a subset is
39
+ * not a percentage of the average of the whole; neither is a total of latitudes. */
22
40
  sharePct: number | null;
23
41
  shareText: string;
42
+ /** Which aggregation this line actually used, after any per-column substitution. */
43
+ aggregation: string;
24
44
  }
25
45
  export interface SelectionCardModel {
26
46
  header: string;
47
+ /** MEASURE lines. Kept measures-only so `maxMeasures` and `hiddenMeasures` keep meaning
48
+ * exactly what they meant; dimensions have their own list and their own cap. */
27
49
  lines: SelectionCardLine[];
50
+ /** DIMENSION lines — the "First, Count, Distinct" half. Before 2026-09-01 the FIRST dimension
51
+ * supplied the header and every other one was discarded, so on the sample table `Segment`
52
+ * appeared nowhere at all. The header column is still excluded here: it is already on
53
+ * screen. */
54
+ dimensionLines: SelectionCardLine[];
28
55
  /** "6 of 24 rows" — the one line that is true regardless of what the columns contain. */
29
56
  rowsText: string;
30
57
  selectedRows: number;
31
58
  totalRows: number;
32
59
  /** Measures the cap left out, so a host can say "+2 more" instead of implying there were none. */
33
60
  hiddenMeasures: number;
61
+ /** Columns the CLASSIFIER withheld rather than the cap — coordinates today. Not for display:
62
+ * a reader does not need telling that a latitude was not totalled. It is here so a host can
63
+ * log it, because a column vanishing from a card with no trace is the kind of thing that
64
+ * costs an afternoon later. */
65
+ suppressedColumns: string[];
34
66
  aggregation: string;
35
67
  }
68
+ /** The set this file reduces over. `first` is shape-core's eighth kind and is handled on the
69
+ * dimension path only, which is why it is absent here — a `reduce` over numbers has nothing to
70
+ * do with it. */
36
71
  type Agg = "sum" | "average" | "min" | "max" | "count" | "median" | "distinctcount";
37
72
  /** Normalise the many spellings the hosts use into the set this file reduces over. Unknown
38
73
  * spellings fall back to sum rather than throwing: a card is a courtesy, and refusing to draw
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.5.45",
3
+ "version": "0.5.47",
4
4
  "description": "Run a BIC-generated D3 chart in any web host: compiles the generated render() function, applies the shared option defaults, resolves mark clicks (through tooltip overlays), owns the selection affordance, and translates row indices between cross-filtered charts. The same contract the BIC Power BI visual implements, minus Power BI. React bindings at @bicharts/chart-host/react.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",