@bicharts/chart-host 0.5.89 → 0.5.91

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.
@@ -53,7 +53,7 @@ var require_papaparse_min = __commonJS({
53
53
  q3(this._config.error) ? this._config.error(e2) : a4 && this._config.error && n.postMessage({ workerId: v3.WORKER_ID, error: e2, finished: false });
54
54
  };
55
55
  }
56
- function f2(e) {
56
+ function f4(e) {
57
57
  var r2;
58
58
  (e = e || {}).chunkSize || (e.chunkSize = v3.RemoteChunkSize), u4.call(this, e), this._nextChunk = s2 ? function() {
59
59
  this._readChunk(), this._chunkLoaded();
@@ -137,7 +137,7 @@ var require_papaparse_min = __commonJS({
137
137
  }, this);
138
138
  }
139
139
  function i2(m2) {
140
- var n2, s4, a5, t, o2 = Math.pow(2, 53), h3 = -o2, u5 = /^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/, d2 = /^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/, i4 = this, r2 = 0, f4 = 0, l3 = false, e = false, c4 = [], p4 = { data: [], errors: [], meta: {} };
140
+ var n2, s4, a5, t, o2 = Math.pow(2, 53), h3 = -o2, u5 = /^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/, d2 = /^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/, i4 = this, r2 = 0, f5 = 0, l3 = false, e = false, c4 = [], p4 = { data: [], errors: [], meta: {} };
141
141
  function y5(e2) {
142
142
  return "greedy" === m2.skipEmptyLines ? "" === e2.join("").trim() : 1 === e2.length && 0 === e2[0].length;
143
143
  }
@@ -164,10 +164,10 @@ var require_papaparse_min = __commonJS({
164
164
  })(t5) ? parseFloat(t5) : d2.test(t5) ? new Date(t5) : "" === t5 ? null : t5) : t5)(n4 = m2.header ? r5 >= c4.length ? "__parsed_extra" : c4[r5] : n4, s5 = m2.transform ? m2.transform(s5, n4) : s5);
165
165
  "__parsed_extra" === n4 ? (i6[n4] = i6[n4] || [], i6[n4].push(s5)) : i6[n4] = s5;
166
166
  }
167
- return m2.header && (r5 > c4.length ? k2("FieldMismatch", "TooManyFields", "Too many fields: expected " + c4.length + " fields but parsed " + r5, f4 + t4) : r5 < c4.length && k2("FieldMismatch", "TooFewFields", "Too few fields: expected " + c4.length + " fields but parsed " + r5, f4 + t4)), i6;
167
+ return m2.header && (r5 > c4.length ? k2("FieldMismatch", "TooManyFields", "Too many fields: expected " + c4.length + " fields but parsed " + r5, f5 + t4) : r5 < c4.length && k2("FieldMismatch", "TooFewFields", "Too few fields: expected " + c4.length + " fields but parsed " + r5, f5 + t4)), i6;
168
168
  }
169
169
  var r4;
170
- p4 && (m2.header || m2.dynamicTyping || m2.transform) && (r4 = 1, !p4.data.length || Array.isArray(p4.data[0]) ? (p4.data = p4.data.map(i5), r4 = p4.data.length) : p4.data = i5(p4.data, 0), m2.header && p4.meta && (p4.meta.fields = c4), f4 += r4);
170
+ p4 && (m2.header || m2.dynamicTyping || m2.transform) && (r4 = 1, !p4.data.length || Array.isArray(p4.data[0]) ? (p4.data = p4.data.map(i5), r4 = p4.data.length) : p4.data = i5(p4.data, 0), m2.header && p4.meta && (p4.meta.fields = c4), f5 += r4);
171
171
  }
172
172
  function _3() {
173
173
  return m2.header && 0 === c4.length;
@@ -183,8 +183,8 @@ var require_papaparse_min = __commonJS({
183
183
  var s5, a6, o4, h4;
184
184
  n4 = n4 || [",", " ", "|", ";", v3.RECORD_SEP, v3.UNIT_SEP];
185
185
  for (var u6 = 0; u6 < n4.length; u6++) {
186
- for (var d3, f5 = n4[u6], l4 = 0, c5 = 0, p5 = 0, g5 = (o4 = void 0, new E4({ comments: r5, delimiter: f5, newline: t4, preview: 10 }).parse(e4)), _4 = 0; _4 < g5.data.length; _4++) i6 && y5(g5.data[_4]) ? p5++ : (d3 = g5.data[_4].length, c5 += d3, void 0 === o4 ? o4 = d3 : 0 < d3 && (l4 += Math.abs(d3 - o4), o4 = d3));
187
- 0 < g5.data.length && (c5 /= g5.data.length - p5), (void 0 === a6 || l4 <= a6) && (void 0 === h4 || h4 < c5) && 1.99 < c5 && (a6 = l4, s5 = f5, h4 = c5);
186
+ for (var d3, f6 = n4[u6], l4 = 0, c5 = 0, p5 = 0, g5 = (o4 = void 0, new E4({ comments: r5, delimiter: f6, newline: t4, preview: 10 }).parse(e4)), _4 = 0; _4 < g5.data.length; _4++) i6 && y5(g5.data[_4]) ? p5++ : (d3 = g5.data[_4].length, c5 += d3, void 0 === o4 ? o4 = d3 : 0 < d3 && (l4 += Math.abs(d3 - o4), o4 = d3));
187
+ 0 < g5.data.length && (c5 /= g5.data.length - p5), (void 0 === a6 || l4 <= a6) && (void 0 === h4 || h4 < c5) && 1.99 < c5 && (a6 = l4, s5 = f6, h4 = c5);
188
188
  }
189
189
  return { successful: !!(m2.delimiter = s5), bestDelimiter: s5 };
190
190
  })(e2, m2.newline, m2.skipEmptyLines, m2.comments, m2.delimitersToGuess)).successful ? m2.delimiter = r4.bestDelimiter : (a5 = true, m2.delimiter = v3.DefaultDelimiter), p4.meta.delimiter = m2.delimiter), b2(m2));
@@ -217,7 +217,7 @@ var require_papaparse_min = __commonJS({
217
217
  var z3 = 0, M = false;
218
218
  this.parse = function(i4, t, r2) {
219
219
  if ("string" != typeof i4) throw new Error("Input must be a string");
220
- var n2 = i4.length, e = S.length, s4 = O2.length, a5 = x2.length, o2 = q3(I2), h3 = [], u5 = [], d2 = [], f4 = z3 = 0;
220
+ var n2 = i4.length, e = S.length, s4 = O2.length, a5 = x2.length, o2 = q3(I2), h3 = [], u5 = [], d2 = [], f5 = z3 = 0;
221
221
  if (!i4) return w2();
222
222
  if (T3 || false !== T3 && -1 === i4.indexOf(F4)) {
223
223
  for (var l3 = i4.split(O2), c4 = 0; c4 < l3.length; c4++) {
@@ -263,7 +263,7 @@ var require_papaparse_min = __commonJS({
263
263
  }
264
264
  return E5();
265
265
  function k2(e2) {
266
- h3.push(e2), f4 = z3;
266
+ h3.push(e2), f5 = z3;
267
267
  }
268
268
  function v4(e2) {
269
269
  var t2 = 0;
@@ -290,7 +290,7 @@ var require_papaparse_min = __commonJS({
290
290
  }
291
291
  n4 && console.warn("Duplicate headers found and renamed."), L2 = true;
292
292
  }
293
- return { data: h3, errors: u5, meta: { delimiter: S, linebreak: O2, aborted: M, truncated: !!e2, cursor: f4 + (t || 0), renamedHeaders: D3 } };
293
+ return { data: h3, errors: u5, meta: { delimiter: S, linebreak: O2, aborted: M, truncated: !!e2, cursor: f5 + (t || 0), renamedHeaders: D3 } };
294
294
  }
295
295
  function R() {
296
296
  I2(w2()), h3 = [], u5 = [];
@@ -339,7 +339,7 @@ var require_papaparse_min = __commonJS({
339
339
  return v3.parse = function(e, t) {
340
340
  var i4 = (t = t || {}).dynamicTyping || false;
341
341
  q3(i4) && (t.dynamicTypingFunction = i4, i4 = {});
342
- if (t.dynamicTyping = i4, t.transform = !!q3(t.transform) && t.transform, !t.worker || !v3.WORKERS_SUPPORTED) return i4 = null, v3.NODE_STREAM_INPUT, "string" == typeof e ? (e = P4(e), i4 = new (t.download ? f2 : c2)(t)) : true === e.readable && q3(e.read) && q3(e.on) ? i4 = new p2(t) : (n.File && e instanceof File || e instanceof Object) && (i4 = new l2(t)), i4.stream(e);
342
+ if (t.dynamicTyping = i4, t.transform = !!q3(t.transform) && t.transform, !t.worker || !v3.WORKERS_SUPPORTED) return i4 = null, v3.NODE_STREAM_INPUT, "string" == typeof e ? (e = P4(e), i4 = new (t.download ? f4 : c2)(t)) : true === e.readable && q3(e.read) && q3(e.on) ? i4 = new p2(t) : (n.File && e instanceof File || e instanceof Object) && (i4 = new l2(t)), i4.stream(e);
343
343
  (i4 = (() => {
344
344
  var e2;
345
345
  return !!v3.WORKERS_SUPPORTED && (e2 = (() => {
@@ -374,11 +374,11 @@ var require_papaparse_min = __commonJS({
374
374
  for (var o4 = 0; o4 < t2.length; o4++) {
375
375
  var h4 = (n4 ? e2 : t2[o4]).length, u6 = false, d2 = n4 ? 0 === Object.keys(t2[o4]).length : 0 === t2[o4].length;
376
376
  if (i5 && !n4 && (u6 = "greedy" === i5 ? "" === t2[o4].join("").trim() : 1 === t2[o4].length && 0 === t2[o4][0].length), "greedy" === i5 && n4) {
377
- for (var f4 = [], l3 = 0; l3 < h4; l3++) {
377
+ for (var f5 = [], l3 = 0; l3 < h4; l3++) {
378
378
  var c4 = s5 ? e2[l3] : l3;
379
- f4.push(t2[o4][c4]);
379
+ f5.push(t2[o4][c4]);
380
380
  }
381
- u6 = "" === f4.join("").trim();
381
+ u6 = "" === f5.join("").trim();
382
382
  }
383
383
  if (!u6) {
384
384
  for (var p4 = 0; p4 < h4; p4++) {
@@ -398,7 +398,7 @@ var require_papaparse_min = __commonJS({
398
398
  return false;
399
399
  })(r4, v3.BAD_DELIMITERS) || -1 < r4.indexOf(m2) || -1 < i5.indexOf(a5) || " " === r4.charAt(0) || " " === r4.charAt(r4.length - 1)) ? a5 + r4 + a5 : r4);
400
400
  }
401
- }, v3.RECORD_SEP = String.fromCharCode(30), v3.UNIT_SEP = String.fromCharCode(31), v3.BYTE_ORDER_MARK = "\uFEFF", v3.BAD_DELIMITERS = ["\r", "\n", '"', v3.BYTE_ORDER_MARK], v3.WORKERS_SUPPORTED = !s2 && !!n.Worker, v3.NODE_STREAM_INPUT = 1, v3.LocalChunkSize = 10485760, v3.RemoteChunkSize = 5242880, v3.DefaultDelimiter = ",", v3.Parser = E4, v3.ParserHandle = i2, v3.NetworkStreamer = f2, v3.FileStreamer = l2, v3.StringStreamer = c2, v3.ReadableStreamStreamer = p2, n.jQuery && ((d = n.jQuery).fn.parse = function(o2) {
401
+ }, v3.RECORD_SEP = String.fromCharCode(30), v3.UNIT_SEP = String.fromCharCode(31), v3.BYTE_ORDER_MARK = "\uFEFF", v3.BAD_DELIMITERS = ["\r", "\n", '"', v3.BYTE_ORDER_MARK], v3.WORKERS_SUPPORTED = !s2 && !!n.Worker, v3.NODE_STREAM_INPUT = 1, v3.LocalChunkSize = 10485760, v3.RemoteChunkSize = 5242880, v3.DefaultDelimiter = ",", v3.Parser = E4, v3.ParserHandle = i2, v3.NetworkStreamer = f4, v3.FileStreamer = l2, v3.StringStreamer = c2, v3.ReadableStreamStreamer = p2, n.jQuery && ((d = n.jQuery).fn.parse = function(o2) {
402
402
  var i4 = o2.config || {}, h3 = [];
403
403
  return this.each(function(e2) {
404
404
  if (!("INPUT" === d(this).prop("tagName").toUpperCase() && "file" === d(this).attr("type").toLowerCase() && n.FileReader) || !this.files || 0 === this.files.length) return true;
@@ -429,7 +429,7 @@ var require_papaparse_min = __commonJS({
429
429
  e = e.data;
430
430
  void 0 === v3.WORKER_ID && e && (v3.WORKER_ID = e.workerId);
431
431
  "string" == typeof e.input ? n.postMessage({ workerId: v3.WORKER_ID, results: v3.parse(e.input, e.config), finished: true }) : (n.File && e.input instanceof File || e.input instanceof Object) && (e = v3.parse(e.input, e.config)) && n.postMessage({ workerId: v3.WORKER_ID, results: e, finished: true });
432
- }), (f2.prototype = Object.create(u4.prototype)).constructor = f2, (l2.prototype = Object.create(u4.prototype)).constructor = l2, (c2.prototype = Object.create(c2.prototype)).constructor = c2, (p2.prototype = Object.create(u4.prototype)).constructor = p2, v3;
432
+ }), (f4.prototype = Object.create(u4.prototype)).constructor = f4, (l2.prototype = Object.create(u4.prototype)).constructor = l2, (c2.prototype = Object.create(c2.prototype)).constructor = c2, (p2.prototype = Object.create(u4.prototype)).constructor = p2, v3;
433
433
  });
434
434
  }
435
435
  });
@@ -630,7 +630,7 @@ function worldBasemap(world) {
630
630
  if (!_worldBase) {
631
631
  _worldBase = {
632
632
  type: "FeatureCollection",
633
- features: world.features.filter((f2) => f2?.geometry && f2.geometry.type !== "Point")
633
+ features: world.features.filter((f4) => f4?.geometry && f4.geometry.type !== "Point")
634
634
  };
635
635
  }
636
636
  return _worldBase;
@@ -639,7 +639,7 @@ function northAmerica(world) {
639
639
  if (!_na) {
640
640
  _na = {
641
641
  type: "FeatureCollection",
642
- features: world.features.filter((f2) => NA_ISO3.has(f2?.id) && f2?.geometry && f2.geometry.type !== "Point").map((f2) => clipFeatureToLat(f2, NA_LAT_MAX)).filter((f2) => f2 !== null)
642
+ features: world.features.filter((f4) => NA_ISO3.has(f4?.id) && f4?.geometry && f4.geometry.type !== "Point").map((f4) => clipFeatureToLat(f4, NA_LAT_MAX)).filter((f4) => f4 !== null)
643
643
  };
644
644
  }
645
645
  return _na;
@@ -900,10 +900,10 @@ function A3(n) {
900
900
  }
901
901
  let i2 = E(n.country), r = Y(n.state);
902
902
  if (r && i2 && c3(r) !== i2 && (r = null), n.city !== void 0 && n.city !== null) {
903
- let a4 = u(String(n.city)), s2 = O(a4), f2 = s2 ? F(s2, n.mapKind) : null;
904
- if (f2 && f2.length) {
903
+ let a4 = u(String(n.city)), s2 = O(a4), f4 = s2 ? F(s2, n.mapKind) : null;
904
+ if (f4 && f4.length) {
905
905
  let d = n.state === void 0 || n.state === null ? "" : String(n.state).trim(), R = d ? u(d) : "", g3 = d ? Y(d) : null, S = [], m = -1;
906
- for (let c2 of f2) {
906
+ for (let c2 of f4) {
907
907
  let l2 = c2.row, A = 0;
908
908
  if (d && l2.a1 !== "") if (g3 && l2.a1 === g3 || u(l2.a1) === R) A++;
909
909
  else continue;
@@ -938,7 +938,7 @@ function A3(n) {
938
938
  var G = { latlon: 0, city: 1, zip3: 2, state: 3, country: 4 };
939
939
  var x = 8;
940
940
  function y3(n, e) {
941
- let t = new Array(n.length), o = new Array(n.length), i2 = new Array(n.length), r = /* @__PURE__ */ new Set(), a4 = [], s2 = /* @__PURE__ */ new Set(), f2 = [], d = /* @__PURE__ */ new Set(), R = [], g3 = { latlon: 0, city: 0, zip3: 0, state: 0, country: 0 }, S = 0, m = 0, p2 = null;
941
+ let t = new Array(n.length), o = new Array(n.length), i2 = new Array(n.length), r = /* @__PURE__ */ new Set(), a4 = [], s2 = /* @__PURE__ */ new Set(), f4 = [], d = /* @__PURE__ */ new Set(), R = [], g3 = { latlon: 0, city: 0, zip3: 0, state: 0, country: 0 }, S = 0, m = 0, p2 = null;
942
942
  for (let l2 = 0; l2 < n.length; ++l2) {
943
943
  let A = n[l2], C2 = A3({ ...A, mapKind: e });
944
944
  if (S3(C2)) {
@@ -948,7 +948,7 @@ function y3(n, e) {
948
948
  } else if (C2) {
949
949
  if (t[l2] = C2.lat, o[l2] = C2.lon, i2[l2] = C2.precision, S++, g3[C2.precision]++, (p2 === null || G[C2.precision] > G[p2]) && (p2 = C2.precision), G[C2.precision] > G.city) {
950
950
  let M = K(A);
951
- M && !s2.has(M.toLowerCase()) && (s2.add(M.toLowerCase()), f2.length < x && f2.push(M));
951
+ M && !s2.has(M.toLowerCase()) && (s2.add(M.toLowerCase()), f4.length < x && f4.push(M));
952
952
  }
953
953
  } else {
954
954
  t[l2] = null, o[l2] = null, i2[l2] = null;
@@ -957,7 +957,7 @@ function y3(n, e) {
957
957
  }
958
958
  }
959
959
  let c2 = !n3() && n.some((l2) => l2.city !== null && l2.city !== void 0 && String(l2.city).trim() !== "");
960
- return { lat: t, lon: o, precisions: i2, precision: p2, precisionCounts: g3, coarseExamples: f2, unmatched: a4, ambiguousRows: m, ambiguousExamples: R, matchedRows: S, totalRows: n.length, ...c2 ? { cityTableMissing: true } : {} };
960
+ return { lat: t, lon: o, precisions: i2, precision: p2, precisionCounts: g3, coarseExamples: f4, unmatched: a4, ambiguousRows: m, ambiguousExamples: R, matchedRows: S, totalRows: n.length, ...c2 ? { cityTableMissing: true } : {} };
961
961
  }
962
962
  function K(n) {
963
963
  let e = [n.city, n.state, n.zip, n.country].filter((t) => t != null && String(t).trim() !== "").map((t) => String(t).trim()).join(", ");
@@ -1271,17 +1271,17 @@ function ie(r, n, e, s2, c2) {
1271
1271
  for (let i2 of x2) {
1272
1272
  let a4 = l2(i2);
1273
1273
  if (P2(a4)) continue;
1274
- let f2 = v2(a4);
1275
- if (f2 < 95) continue;
1274
+ let f4 = v2(a4);
1275
+ if (f4 < 95) continue;
1276
1276
  let N2 = z2(a4, (S) => !!Y(S)), Y2 = z2(a4, (S) => !!Y(S) && !q2(S, s2));
1277
- N2 < E2 && Y2 < 1 || (!t || f2 > t.pct) && (t = { name: i2, pct: f2 });
1277
+ N2 < E2 && Y2 < 1 || (!t || f4 > t.pct) && (t = { name: i2, pct: f4 });
1278
1278
  }
1279
1279
  t && (o.state = t.name, d.add(t.name), A.push(`state=${t.name} (${t.pct}% states/provinces)`));
1280
1280
  }
1281
1281
  if (!o.zip) for (let t of x2) {
1282
1282
  if (d.has(t)) continue;
1283
1283
  let i2 = l2(t), a4 = D2(i2);
1284
- if (!(a4 < 95) && Z2(i2, t) && !(oe(i2, (f2) => Z(f2).length > 0) < E2)) {
1284
+ if (!(a4 < 95) && Z2(i2, t) && !(oe(i2, (f4) => Z(f4).length > 0) < E2)) {
1285
1285
  o.zip = t, d.add(t), A.push(`zip=${t} (${a4}% ZIP codes)`);
1286
1286
  break;
1287
1287
  }
@@ -1300,8 +1300,8 @@ function ie(r, n, e, s2, c2) {
1300
1300
  let t = null;
1301
1301
  for (let i2 of x2) {
1302
1302
  if (d.has(i2)) continue;
1303
- let a4 = l2(i2), f2 = ne(a4, s2);
1304
- f2 < m3 || z2(a4, (N2) => p3(N2, s2)) < E2 || (!t || f2 > t.pct) && (t = { name: i2, pct: f2 });
1303
+ let a4 = l2(i2), f4 = ne(a4, s2);
1304
+ f4 < m3 || z2(a4, (N2) => p3(N2, s2)) < E2 || (!t || f4 > t.pct) && (t = { name: i2, pct: f4 });
1305
1305
  }
1306
1306
  t && (o.city = t.name, A.push(`city=${t.name} (${t.pct}% known cities)`));
1307
1307
  }
@@ -1462,8 +1462,8 @@ function createMarkResolver(env) {
1462
1462
  };
1463
1463
  const isInvisibleStrokePath = (el) => {
1464
1464
  if (el.tagName.toLowerCase() !== "path") return false;
1465
- const f2 = el.getAttribute("fill");
1466
- if (f2 !== null && f2 !== "" && f2 !== "none" && f2 !== "transparent") return false;
1465
+ const f4 = el.getAttribute("fill");
1466
+ if (f4 !== null && f4 !== "" && f4 !== "none" && f4 !== "transparent") return false;
1467
1467
  const s2 = (el.getAttribute("stroke") || "").trim().toLowerCase();
1468
1468
  const so = parseFloat(el.getAttribute("stroke-opacity") || "1");
1469
1469
  return s2 === "transparent" || s2 === "none" || so <= 0.01;
@@ -1734,8 +1734,8 @@ function attrOrComputed(el, name, cs) {
1734
1734
  return c2 === null || c2 === void 0 ? "" : String(c2).trim();
1735
1735
  }
1736
1736
  function isUnfilled(fill) {
1737
- const f2 = fill.toLowerCase();
1738
- return f2 === "none" || f2 === "transparent" || f2 === "rgba(0, 0, 0, 0)" || f2 === "rgba(0,0,0,0)";
1737
+ const f4 = fill.toLowerCase();
1738
+ return f4 === "none" || f4 === "transparent" || f4 === "rgba(0, 0, 0, 0)" || f4 === "rgba(0,0,0,0)";
1739
1739
  }
1740
1740
  function widthPx(raw) {
1741
1741
  const m = /^(-?\d+(?:\.\d+)?)\s*(px)?$/i.exec(raw);
@@ -1789,6 +1789,195 @@ function hitBandFlag(c2) {
1789
1789
  return c2.uncovered > 0 ? "hitband:d3:thin" : "hitband:d3:ok";
1790
1790
  }
1791
1791
 
1792
+ // src/deltaE.ts
1793
+ function linearise(c2) {
1794
+ const v3 = c2 / 255;
1795
+ return v3 <= 0.04045 ? v3 / 12.92 : Math.pow((v3 + 0.055) / 1.055, 2.4);
1796
+ }
1797
+ var Xn = 0.95047;
1798
+ var Yn = 1;
1799
+ var Zn = 1.08883;
1800
+ var EPS = 216 / 24389;
1801
+ var KAPPA = 24389 / 27;
1802
+ function f2(t) {
1803
+ return t > EPS ? Math.cbrt(t) : (KAPPA * t + 16) / 116;
1804
+ }
1805
+ function rgbToLab(r, g3, b2) {
1806
+ const R = linearise(r), G3 = linearise(g3), B2 = linearise(b2);
1807
+ const X2 = 0.4124564 * R + 0.3575761 * G3 + 0.1804375 * B2;
1808
+ const Y2 = 0.2126729 * R + 0.7151522 * G3 + 0.072175 * B2;
1809
+ const Z3 = 0.0193339 * R + 0.119192 * G3 + 0.9503041 * B2;
1810
+ const fx = f2(X2 / Xn), fy = f2(Y2 / Yn), fz = f2(Z3 / Zn);
1811
+ return { L: 116 * fy - 16, a: 500 * (fx - fy), b: 200 * (fy - fz) };
1812
+ }
1813
+ function hexToLab(hex2) {
1814
+ const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex2.trim());
1815
+ if (!m) return null;
1816
+ const h2 = m[1].length === 3 ? m[1][0] + m[1][0] + m[1][1] + m[1][1] + m[1][2] + m[1][2] : m[1];
1817
+ return rgbToLab(parseInt(h2.slice(0, 2), 16), parseInt(h2.slice(2, 4), 16), parseInt(h2.slice(4, 6), 16));
1818
+ }
1819
+ var RAD = Math.PI / 180;
1820
+ var DEG = 180 / Math.PI;
1821
+ var NEUTRAL_CHROMA = 1e-4;
1822
+ function deltaE2000(c1, c2) {
1823
+ const C1 = Math.hypot(c1.a, c1.b), C2 = Math.hypot(c2.a, c2.b);
1824
+ const Cbar = (C1 + C2) / 2;
1825
+ const C7 = Math.pow(Cbar, 7);
1826
+ const G3 = 0.5 * (1 - Math.sqrt(C7 / (C7 + Math.pow(25, 7))));
1827
+ const a1p = (1 + G3) * c1.a, a2p = (1 + G3) * c2.a;
1828
+ const C1p = Math.hypot(a1p, c1.b), C2p = Math.hypot(a2p, c2.b);
1829
+ const neutral = C1p < NEUTRAL_CHROMA || C2p < NEUTRAL_CHROMA;
1830
+ const h1p = C1p < NEUTRAL_CHROMA ? 0 : (Math.atan2(c1.b, a1p) * DEG + 360) % 360;
1831
+ const h2p = C2p < NEUTRAL_CHROMA ? 0 : (Math.atan2(c2.b, a2p) * DEG + 360) % 360;
1832
+ const dLp = c2.L - c1.L;
1833
+ const dCp = C2p - C1p;
1834
+ let dhp;
1835
+ if (neutral) dhp = 0;
1836
+ else if (Math.abs(h2p - h1p) <= 180) dhp = h2p - h1p;
1837
+ else if (h2p - h1p > 180) dhp = h2p - h1p - 360;
1838
+ else dhp = h2p - h1p + 360;
1839
+ const dHp = 2 * Math.sqrt(C1p * C2p) * Math.sin(dhp / 2 * RAD);
1840
+ const Lbarp = (c1.L + c2.L) / 2;
1841
+ const Cbarp = (C1p + C2p) / 2;
1842
+ let hbarp;
1843
+ if (neutral) hbarp = h1p + h2p;
1844
+ else if (Math.abs(h1p - h2p) <= 180) hbarp = (h1p + h2p) / 2;
1845
+ else if (h1p + h2p < 360) hbarp = (h1p + h2p + 360) / 2;
1846
+ else hbarp = (h1p + h2p - 360) / 2;
1847
+ const T3 = 1 - 0.17 * Math.cos((hbarp - 30) * RAD) + 0.24 * Math.cos(2 * hbarp * RAD) + 0.32 * Math.cos((3 * hbarp + 6) * RAD) - 0.2 * Math.cos((4 * hbarp - 63) * RAD);
1848
+ const dTheta = 30 * Math.exp(-Math.pow((hbarp - 275) / 25, 2));
1849
+ const Cbarp7 = Math.pow(Cbarp, 7);
1850
+ const RC = 2 * Math.sqrt(Cbarp7 / (Cbarp7 + Math.pow(25, 7)));
1851
+ const RT = -RC * Math.sin(2 * dTheta * RAD);
1852
+ const Lm50 = Math.pow(Lbarp - 50, 2);
1853
+ const SL = 1 + 0.015 * Lm50 / Math.sqrt(20 + Lm50);
1854
+ const SC = 1 + 0.045 * Cbarp;
1855
+ const SH = 1 + 0.015 * Cbarp * T3;
1856
+ const tL = dLp / SL, tC = dCp / SC, tH = dHp / SH;
1857
+ return Math.sqrt(tL * tL + tC * tC + tH * tH + RT * tC * tH);
1858
+ }
1859
+
1860
+ // src/colourSpread.ts
1861
+ var SAME_SHADE_DELTA_E = 5;
1862
+ var MIN_RAMP_FILLS = 4;
1863
+ var RAMP_SLACK_MAX = 0.15;
1864
+ var MIN_MARKS = 24;
1865
+ var ELEMENT_CAP3 = 5e3;
1866
+ var MAX_CELLS = 48;
1867
+ var CELL_STEP = 32;
1868
+ var EMPTY3 = { marks: 0, distinctFills: 0, crowdShare: 0, spanDeltaE: 0 };
1869
+ function styleOf2(el, doc) {
1870
+ try {
1871
+ const view = doc?.defaultView;
1872
+ return view?.getComputedStyle ? view.getComputedStyle(el) : null;
1873
+ } catch {
1874
+ return null;
1875
+ }
1876
+ }
1877
+ function fillOf(el, cs) {
1878
+ const a4 = el.getAttribute?.("fill");
1879
+ if (a4 !== null && a4 !== void 0 && String(a4).trim() !== "") return String(a4).trim();
1880
+ const c2 = cs?.fill;
1881
+ return c2 === null || c2 === void 0 ? "" : String(c2).trim();
1882
+ }
1883
+ function toRgb(raw) {
1884
+ const s2 = raw.trim().toLowerCase();
1885
+ if (!s2 || s2 === "none" || s2 === "transparent") return null;
1886
+ const hex2 = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(s2);
1887
+ if (hex2) {
1888
+ const h2 = hex2[1];
1889
+ const full = h2.length === 3 ? h2[0] + h2[0] + h2[1] + h2[1] + h2[2] + h2[2] : h2;
1890
+ return [
1891
+ parseInt(full.slice(0, 2), 16),
1892
+ parseInt(full.slice(2, 4), 16),
1893
+ parseInt(full.slice(4, 6), 16)
1894
+ ];
1895
+ }
1896
+ const m = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)\s*(?:[,/]\s*([\d.]+)\s*)?\)$/.exec(s2);
1897
+ if (!m) return null;
1898
+ if (m[4] !== void 0 && parseFloat(m[4]) === 0) return null;
1899
+ return [Math.round(parseFloat(m[1])), Math.round(parseFloat(m[2])), Math.round(parseFloat(m[3]))];
1900
+ }
1901
+ function cellKey(rgb) {
1902
+ return [
1903
+ Math.round(rgb[0] / CELL_STEP),
1904
+ Math.round(rgb[1] / CELL_STEP),
1905
+ Math.round(rgb[2] / CELL_STEP)
1906
+ ].join(",");
1907
+ }
1908
+ function hex(rgb) {
1909
+ return "#" + rgb.map((v3) => Math.max(0, Math.min(255, v3)).toString(16).padStart(2, "0")).join("");
1910
+ }
1911
+ function censusColourSpread(container, doc) {
1912
+ if (!container || typeof container.querySelectorAll !== "function") return EMPTY3;
1913
+ try {
1914
+ const els = Array.from(
1915
+ container.querySelectorAll(`.${MARK_CLASS}, [${ROW_IDX_ATTR}]`)
1916
+ );
1917
+ if (els.length === 0 || els.length > ELEMENT_CAP3) return EMPTY3;
1918
+ const ownerDoc = doc ?? container.ownerDocument;
1919
+ const distinct = /* @__PURE__ */ new Set();
1920
+ const cells = /* @__PURE__ */ new Map();
1921
+ let marks = 0;
1922
+ for (const el of els) {
1923
+ const rgb = toRgb(fillOf(el, styleOf2(el, ownerDoc)));
1924
+ if (!rgb) continue;
1925
+ marks++;
1926
+ distinct.add(rgb.join(","));
1927
+ const k2 = cellKey(rgb);
1928
+ const cur = cells.get(k2);
1929
+ if (cur) cur.n++;
1930
+ else cells.set(k2, { rgb, n: 1 });
1931
+ }
1932
+ if (marks < MIN_MARKS || distinct.size < MIN_RAMP_FILLS) return EMPTY3;
1933
+ const top = [...cells.values()].sort((a4, b2) => b2.n - a4.n).slice(0, MAX_CELLS);
1934
+ const cs = top.map((c2) => hexToLab(hex(c2.rgb))).filter((l2) => l2 !== null);
1935
+ if (cs.length < MIN_RAMP_FILLS) return EMPTY3;
1936
+ const d = cs.map(() => new Array(cs.length).fill(0));
1937
+ let span = 0, lo = 0, hi = 1;
1938
+ for (let i2 = 0; i2 < cs.length; i2++) {
1939
+ for (let j3 = i2 + 1; j3 < cs.length; j3++) {
1940
+ const v3 = deltaE2000(cs[i2], cs[j3]);
1941
+ d[i2][j3] = v3;
1942
+ d[j3][i2] = v3;
1943
+ if (v3 > span) {
1944
+ span = v3;
1945
+ lo = i2;
1946
+ hi = j3;
1947
+ }
1948
+ }
1949
+ }
1950
+ if (span <= 0) return EMPTY3;
1951
+ let slack = 0, interior = 0;
1952
+ for (let i2 = 0; i2 < cs.length; i2++) {
1953
+ if (i2 === lo || i2 === hi) continue;
1954
+ slack += (d[lo][i2] + d[i2][hi]) / span - 1;
1955
+ interior++;
1956
+ }
1957
+ if (interior < 2 || slack / interior > RAMP_SLACK_MAX) return EMPTY3;
1958
+ let crowd = 0;
1959
+ for (let i2 = 0; i2 < cs.length; i2++) {
1960
+ let n = 0;
1961
+ for (let j3 = 0; j3 < cs.length; j3++) {
1962
+ if (i2 === j3 || d[i2][j3] <= SAME_SHADE_DELTA_E) n += top[j3].n;
1963
+ }
1964
+ if (n > crowd) crowd = n;
1965
+ }
1966
+ return {
1967
+ marks,
1968
+ distinctFills: distinct.size,
1969
+ crowdShare: Math.round(crowd / marks * 100),
1970
+ spanDeltaE: Math.round(span)
1971
+ };
1972
+ } catch {
1973
+ return EMPTY3;
1974
+ }
1975
+ }
1976
+ function colourSpreadFlag(c2) {
1977
+ if (c2.marks === 0) return "";
1978
+ return "ramp:d3:crowd" + Math.min(80, Math.floor(c2.crowdShare / 20) * 20);
1979
+ }
1980
+
1792
1981
  // src/fit.ts
1793
1982
  var SCROLL_SLACK_PX = 8;
1794
1983
  function needsScroll(contentPx, viewportPx, slackPx = SCROLL_SLACK_PX) {
@@ -2403,15 +2592,15 @@ function clamp01(v3) {
2403
2592
  if (typeof v3 !== "number" || !isFinite(v3)) return 1;
2404
2593
  return Math.max(0, Math.min(1, v3));
2405
2594
  }
2406
- function hexToRGB(hex) {
2407
- const p2 = toRGBA(hex);
2595
+ function hexToRGB(hex2) {
2596
+ const p2 = toRGBA(hex2);
2408
2597
  return [p2[0], p2[1], p2[2]];
2409
2598
  }
2410
2599
 
2411
2600
  // src/labelContrastDom.ts
2412
2601
  var LABEL_CONTRAST_DONE_ATTR = "data-lch-contrast";
2413
2602
  var LABEL_CONTRAST_CAP = 2e3;
2414
- var EMPTY3 = { rects: 0, scanned: 0, fixed: 0, pillsBoosted: 0, offFill: 0, pageMajority: 0 };
2603
+ var EMPTY4 = { rects: 0, scanned: 0, fixed: 0, pillsBoosted: 0, offFill: 0, pageMajority: 0 };
2415
2604
  function backedSamples(el, pts) {
2416
2605
  try {
2417
2606
  const ge2 = el;
@@ -2434,7 +2623,7 @@ function backedSamples(el, pts) {
2434
2623
  }
2435
2624
  }
2436
2625
  function applyLabelContrast(container, opts = {}) {
2437
- const report = { ...EMPTY3 };
2626
+ const report = { ...EMPTY4 };
2438
2627
  if (!container) {
2439
2628
  report.skipped = "no-container";
2440
2629
  return report;
@@ -2897,6 +3086,12 @@ function createChartHost(container, config) {
2897
3086
  } catch {
2898
3087
  }
2899
3088
  }
3089
+ if (config.onColourSpreadCensus) {
3090
+ try {
3091
+ config.onColourSpreadCensus(censusColourSpread(container, doc));
3092
+ } catch {
3093
+ }
3094
+ }
2900
3095
  if (fitOpts !== false) {
2901
3096
  try {
2902
3097
  const r = fitRenderedChart(container, fitOpts === true ? {} : fitOpts);
@@ -3016,6 +3211,10 @@ export {
3016
3211
  MIN_HIT_BAND_PX,
3017
3212
  censusHitBands,
3018
3213
  hitBandFlag,
3214
+ SAME_SHADE_DELTA_E,
3215
+ MIN_RAMP_FILLS,
3216
+ censusColourSpread,
3217
+ colourSpreadFlag,
3019
3218
  SCROLL_SLACK_PX,
3020
3219
  needsScroll,
3021
3220
  contentExtentOf,
package/dist/index.mjs CHANGED
@@ -44,6 +44,7 @@ import {
44
44
  MAX_FRAME_GROW_FACTOR,
45
45
  MIN_CONTRAST,
46
46
  MIN_HIT_BAND_PX,
47
+ MIN_RAMP_FILLS,
47
48
  Me,
48
49
  PAGE_MATCH_TOLERANCE,
49
50
  PHANTOM_FRACTION,
@@ -51,6 +52,7 @@ import {
51
52
  PILL_MIN_COVERAGE,
52
53
  PILL_OPAQUE_ALPHA,
53
54
  ROW_IDX_ATTR,
55
+ SAME_SHADE_DELTA_E,
54
56
  SCROLL_SLACK_PX,
55
57
  SEASONAL_MARKERS_DEFAULT,
56
58
  SELECTION_ACTIVE_CLASS,
@@ -64,10 +66,12 @@ import {
64
66
  blankRenderFlag,
65
67
  buildRenderPayload,
66
68
  cellSuppressesNormalize,
69
+ censusColourSpread,
67
70
  censusHitBands,
68
71
  censusMarks,
69
72
  chartOwnsTimeline,
70
73
  clearGeoCache,
74
+ colourSpreadFlag,
71
75
  compileRenderFn,
72
76
  compositeOver,
73
77
  contentExtentOf,
@@ -112,7 +116,7 @@ import {
112
116
  u,
113
117
  unpinScrolledAxis,
114
118
  vt
115
- } from "./chunk-NWMMNXH2.mjs";
119
+ } from "./chunk-YIRWZUKN.mjs";
116
120
  import "./chunk-A2GMXZP7.mjs";
117
121
  import "./chunk-GHODWOAL.mjs";
118
122
 
@@ -2083,6 +2087,7 @@ export {
2083
2087
  MIN_CONTRAST,
2084
2088
  MIN_DELTA_E,
2085
2089
  MIN_HIT_BAND_PX,
2090
+ MIN_RAMP_FILLS,
2086
2091
  PAGE_MATCH_TOLERANCE,
2087
2092
  PHANTOM_FRACTION,
2088
2093
  PILL_MIN_ALPHA,
@@ -2090,6 +2095,7 @@ export {
2090
2095
  PILL_OPAQUE_ALPHA,
2091
2096
  QUALIFY_REFUSAL_UNSPECIFIED,
2092
2097
  ROW_IDX_ATTR,
2098
+ SAME_SHADE_DELTA_E,
2093
2099
  SCROLL_SLACK_PX,
2094
2100
  SEASONAL_MARKERS_DEFAULT,
2095
2101
  SELECTION_ACTIVE_CLASS,
@@ -2110,11 +2116,13 @@ export {
2110
2116
  canConfirmLaunch,
2111
2117
  captureSvgSnapshot,
2112
2118
  cellSuppressesNormalize,
2119
+ censusColourSpread,
2113
2120
  censusHitBands,
2114
2121
  censusMarks,
2115
2122
  chartOwnsTimeline,
2116
2123
  chooserFitsViewport,
2117
2124
  clearGeoCache,
2125
+ colourSpreadFlag,
2118
2126
  compileRenderFn,
2119
2127
  compileTrivialSource,
2120
2128
  compositeOver,
package/dist/react.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createChartHost,
5
5
  geoFromCache,
6
6
  loadGeo
7
- } from "./chunk-NWMMNXH2.mjs";
7
+ } from "./chunk-YIRWZUKN.mjs";
8
8
  import "./chunk-A2GMXZP7.mjs";
9
9
  import "./chunk-GHODWOAL.mjs";
10
10
 
@@ -0,0 +1,75 @@
1
+ /**
2
+ * CIE2000 distance at which two fills stop being separable at a glance, side by side on a chart.
3
+ *
4
+ * Not a tuned parameter — a perceptual constant. 1.0 is the textbook just-noticeable difference
5
+ * under laboratory conditions with the two patches touching; 5 is the conventional "clearly
6
+ * different to an ordinary observer in ordinary conditions", which is what a reader scanning a
7
+ * choropleth is. Erring high is the safe direction: it makes the census UNDERstate crowding.
8
+ */
9
+ export declare const SAME_SHADE_DELTA_E = 5;
10
+ /**
11
+ * Fewer distinct fills than this and there is nothing to characterise.
12
+ *
13
+ * Four is the floor for the collinearity test below, not a judgement: the two extremes anchor the
14
+ * line, so a fourth fill is the second interior point and the first one that can disagree.
15
+ */
16
+ export declare const MIN_RAMP_FILLS = 4;
17
+ /**
18
+ * How far off a straight line a set of fills may sit and still be a RAMP.
19
+ *
20
+ * THIS IS WHAT SEPARATES A VALUE-ENCODED COLOUR CHANNEL FROM A CATEGORICAL ONE, and counting
21
+ * fills cannot do it. The obvious "at least six distinct colours" rule fails at exactly the
22
+ * wrong moment: a five-bin quantile scale — the recommended REMEDY for the washout this census
23
+ * exists to find — has five fills, so a count floor would have excluded the charts that did the
24
+ * right thing and biased the prevalence it was built to measure.
25
+ *
26
+ * A sequential ramp is, by construction, a path between two colours: every fill lies close to the
27
+ * segment joining its extremes, so `d(lo,x) + d(x,hi)` barely exceeds `d(lo,hi)`. A categorical
28
+ * scheme is chosen for mutual separation and its members sit well off that line. Measured in
29
+ * CIE2000 over the standard schemes, the two populations do not overlap:
30
+ *
31
+ * ```text
32
+ * interpolateBlues (5, 8 and 40 stops) mean slack -0.05 SEQUENTIAL
33
+ * interpolateReds (8) mean slack +0.06 SEQUENTIAL
34
+ * viridis (8) mean slack +0.02 SEQUENTIAL
35
+ * schemeTableau10 (5 and 8) mean slack +0.24 categorical
36
+ * RdBu diverging (7) mean slack +0.35 two arms, not one line
37
+ * ```
38
+ *
39
+ * 0.15 sits in the four-fold gap. A DIVERGING ramp landing outside is correct rather than a miss:
40
+ * it is genuinely two paths from a neutral middle, and measuring it as one would report the arm
41
+ * that happened to be longer. It is excluded and said so, not mismeasured.
42
+ */
43
+ export declare const RAMP_SLACK_MAX = 0.15;
44
+ export interface ColourSpreadCensus {
45
+ /** Marks carrying a resolvable, non-transparent fill. */
46
+ marks: number;
47
+ /** Distinct fills among them, before quantisation. */
48
+ distinctFills: number;
49
+ /** Share (0-100) of marks whose fill is within SAME_SHADE_DELTA_E of the most crowded fill. */
50
+ crowdShare: number;
51
+ /** Largest CIE2000 distance between any two sampled fills — the ramp's perceptual reach. */
52
+ spanDeltaE: number;
53
+ }
54
+ /**
55
+ * How much of this chart is one shade?
56
+ *
57
+ * Never throws. Telemetry must not be the reason a delivered render fails, and an environment
58
+ * that cannot report a fill reports nothing rather than a number that describes the environment.
59
+ *
60
+ * KNOWN LIMIT, stated rather than hidden: a DIVERGING ramp is measured the same way as any
61
+ * other, so a chart whose two arms are each well spread but which crowds around its neutral
62
+ * midpoint reports honestly, while one that crowds at BOTH ends reports the larger arm only. That
63
+ * understates rather than overstates, which is the direction an audit should err.
64
+ */
65
+ export declare function censusColourSpread(container: any, doc?: any): ColourSpreadCensus;
66
+ /**
67
+ * The always-on behaviour tag, or "" when the census had nothing to say.
68
+ *
69
+ * A BUCKETED MEASUREMENT, NOT A VERDICT. `ramp:d3:crowd60` says three in five marks are one
70
+ * shade; it does not say that is wrong. Twenty-point buckets are coarse enough to read as a
71
+ * distribution off a few hundred renders and fine enough to separate "a bit clustered" from the
72
+ * failure this exists for, and the whole family is one `LIKE 'ramp:d3:crowd%'` away — which is
73
+ * the denominator any rate needs. When the distribution is in, a cut can be argued from it.
74
+ */
75
+ export declare function colourSpreadFlag(c: ColourSpreadCensus): string;
@@ -0,0 +1,24 @@
1
+ /** CIE L*a*b*, D65. */
2
+ export interface Lab {
3
+ L: number;
4
+ a: number;
5
+ b: number;
6
+ }
7
+ /** sRGB (0-255 per channel) to CIE L*a*b*. */
8
+ export declare function rgbToLab(r: number, g: number, b: number): Lab;
9
+ /** `#rgb` / `#rrggbb` to Lab. Returns null on anything else rather than guessing. */
10
+ export declare function hexToLab(hex: string): Lab | null;
11
+ /**
12
+ * CIEDE2000, with the standard parametric weights (kL = kC = kH = 1).
13
+ *
14
+ * Transcribed from the published formulation rather than from another implementation - which is
15
+ * why it does not match colorsea, and why deltaE.test.ts proves that is colorsea's doing rather
16
+ * than a slip here. The two fiddly parts, which is where transcriptions usually go
17
+ * wrong: the mean hue h-bar must account for the 360-degree wrap (and is undefined, so taken as
18
+ * the sum, when either chroma is zero), and the rotation term applies only in the blue region -
19
+ * without it, two blues read as further apart than they look, which is exactly the region a
20
+ * sequential ramp spends most of its length in.
21
+ */
22
+ export declare function deltaE2000(c1: Lab, c2: Lab): number;
23
+ /** CIEDE2000 between two `#rrggbb` colours. NaN when either cannot be parsed. */
24
+ export declare function deltaEHex(a: string, b: string): number;
@@ -2,6 +2,7 @@ import { type RenderOptions, type ViewStateProvider } from "./contract";
2
2
  import { type ResolveOptionsInput } from "./defaults";
3
3
  import { type MarkCensus } from "./blankRender";
4
4
  import { type HitBandCensus } from "./hitBands";
5
+ import { type ColourSpreadCensus } from "./colourSpread";
5
6
  import { type FitRenderedChartOptions, type FitRenderedChartResult } from "./fitDom";
6
7
  import { type LabelContrastOptions, type LabelContrastReport } from "./labelContrastDom";
7
8
  export type RenderFn = (container: HTMLElement, data: any, options: RenderOptions) => void;
@@ -92,6 +93,21 @@ export interface ChartHostConfig {
92
93
  * number only the browser knows, and this reads the COMPUTED width off the rendered element.
93
94
  */
94
95
  onHitBandCensus?: (census: HitBandCensus) => void;
96
+ /**
97
+ * HOW MUCH OF THIS CHART IS ONE SHADE (2026-09-09)? Runs after every render, counts only.
98
+ *
99
+ * The sibling question to the hit-band census, on the colour channel: a right-skewed measure
100
+ * on a linear ramp is accurate and unreadable, because the outliers eat the ramp and the
101
+ * dense bulk lands in one indistinguishable tint. It measures the OUTCOME, never the scale,
102
+ * so a well-spread scale of any kind reports well and a curved or hand-rolled ramp needs no
103
+ * special case; it declines outright on a categorical palette or a diverging ramp, which are
104
+ * a different question wearing the same numbers.
105
+ *
106
+ * The other half a server-side check cannot do. A code rule reads `scaleLinear()` and a
107
+ * domain built from `d3.max`; whether THAT washes out depends on the data, and the shape's
108
+ * own skew is measured on the raw column while the ramp encodes an aggregate.
109
+ */
110
+ onColourSpreadCensus?: (census: ColourSpreadCensus) => void;
95
111
  /** This chart declares time keyframes: frame one is allowed to be empty, so no blank verdict
96
112
  * is issued for it. */
97
113
  animated?: boolean;
@@ -21,6 +21,7 @@ export { applyLabelContrast, LABEL_CONTRAST_DONE_ATTR, LABEL_CONTRAST_CAP, type
21
21
  export { decideLabelColor, toRGBA, compositeOver, relativeLuminance, contrastRatio, isPillBackdropAlpha, pillBacksGlyph, backingHoldsGlyph, cellSuppressesNormalize, glyphSampleGrid, DARK_TEXT, LIGHT_TEXT, MIN_CONTRAST, WHITE_TEXT_BG_LUM, PILL_MIN_ALPHA, PILL_OPAQUE_ALPHA, PILL_MIN_COVERAGE, PAGE_MATCH_TOLERANCE, BACKING_MAJORITY, GLYPH_SAMPLE_N, type LabelDecision, } from "./labelContrast";
22
22
  export { censusMarks, isBlankRender, blankRenderFlag, type MarkCensus, type BlankVerdictInput } from "./blankRender";
23
23
  export { censusHitBands, hitBandFlag, MIN_HIT_BAND_PX, type HitBandCensus } from "./hitBands";
24
+ export { censusColourSpread, colourSpreadFlag, SAME_SHADE_DELTA_E, MIN_RAMP_FILLS, type ColourSpreadCensus } from "./colourSpread";
24
25
  export { SCROLL_SLACK_PX, MAX_FRAME_GROW_FACTOR, PHANTOM_FRACTION, FIT_CONTENT_SELECTOR, needsScroll, contentExtentOf, scrollFitFor, planFrameGrow, isPhantomBox, type MeasuredBox, type ContentExtent, type ScrollFit, type FrameGrowPlan, } from "./fit";
25
26
  export { fitRenderedChart, fitReadingFor, measureContainerBoxes, svgInkReach, ctmScaleOf, type FitReading, type InkReach, type FitRenderedChartOptions, type FitRenderedChartResult, } from "./fitDom";
26
27
  export { AXIS_PIN_MIN_LABELS, AXIS_PIN_BAND_PAD_PX, AXIS_PIN_MAX_BAND_FRACTION, AXIS_PIN_TRACK_REACH_PX, isHorizontalLabelRow, labelBand, planAxisPin, axisPinPlacement, type LabelRowBox, type AxisBand, type AxisPinCandidate, type AxisPinPlan, type AxisPinEdge, } from "./fit";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.5.89",
3
+ "version": "0.5.91",
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",