@bicharts/chart-host 0.6.90 → 0.6.92

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.
package/README.md CHANGED
@@ -269,9 +269,11 @@ cue, a hierarchy level) is never flattened by a fix meant for another chart.
269
269
 
270
270
  **How far the rest dims, per chart.** `options.dimOpacity` (a share of full opacity, `0.01`-`1`;
271
271
  `DIM_OPACITY_MIN` is the floor) sets the level for one host without writing CSS, and it is live
272
- through `setOptions`. The host writes it as `--lch-dim-opacity` on its own container and removes
273
- only what it wrote, so with the option absent nothing is written and the CSS above decides: the
274
- stylesheet's `0.25`, or whatever your page sets on the variable.
272
+ through `setOptions`. It takes a number or a numeric string; a value above 1 is clamped to 1 (no
273
+ dim), never read as a percentage, and anything else is ignored. The host writes it as
274
+ `--lch-dim-opacity` on its own container and, when the option goes away or the host is destroyed,
275
+ puts back the value your page had set inline there, so with the option absent nothing is written
276
+ and the CSS above decides: the stylesheet's `0.25`, or whatever your page sets on the variable.
275
277
 
276
278
  ```js
277
279
  createChartHost(el, { code, data, options: { dimOpacity: 0.15 } });
@@ -410,15 +412,27 @@ const zoomHost: ZoomMemoryHost = {
410
412
  persistZoom: json => localStorage.setItem("chart-zoom", json), // "" forgets
411
413
  log: () => {},
412
414
  };
413
- const zoomMemory = createZoomMemoryHook(zoomHost); // once per container, reused by every host on it
415
+ const zoomMemory = createZoomMemoryHook(zoomHost); // once per chart, reused by every host built for it
414
416
  const host = createChartHost(el, { code, data, d3, zoomMemory });
415
417
  ```
416
418
 
417
419
  The view survives every redraw in the session whatever the preference says, and a close and reopen
418
420
  only while `rememberAcrossSessions()` is true. It is keyed by the chart's code and the viewport size
419
421
  (to the nearest 32 px), so a regenerated chart or a real resize opens at full extent instead of
420
- somewhere arbitrary. A chart with several zoomable panels is left alone. In React, pass the hook as
421
- the `zoomMemory` prop of `<BicChart>`.
422
+ somewhere arbitrary. A chart with several zoomable panels is left alone. The `ZoomMemoryHost` is the
423
+ memory: keep one per chart for as long as the chart is on the page, and hand its hook to every host
424
+ built for that chart, on the same element or, after a remount, a new one (a destroyed host releases
425
+ the hook's watcher, so the next one can take it). In React, pass the hook as the `zoomMemory` prop of
426
+ `<BicChart>`, built once (`zoomHost` as above, kept outside the render):
427
+
428
+ ```tsx
429
+ const [zoomMemory] = useState(() => createZoomMemoryHook(zoomHost)); // once, however often this renders
430
+ return <BicChart code={code} data={data} d3={d3} zoomMemory={zoomMemory} />;
431
+ ```
432
+
433
+ A hook that arrives after mount, or goes away, rebuilds the chart's host; swapping one hook for another
434
+ on a mounted `<BicChart>` rebuilds nothing, and applies at the next rebuild (a new `code`, say) or at
435
+ once if you remount it with a `key`, so a hook built inline in the props costs no rebuild.
422
436
 
423
437
  **A scrolled chart** keeps its place with no setup. A chart taller than its frame scrolls, and every
424
438
  redraw empties the container, which used to send the reader back to the top. The host now parks the
package/dist/card.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  /*! @bicharts/chart-host — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; Natural Earth and US Census/TIGER (public domain). Full text: NOTICE in this package. */
2
2
  import {
3
3
  computeSelectionCard
4
- } from "./chunk-Z6OZDX4Z.mjs";
5
- import "./chunk-KQN5C7SZ.mjs";
4
+ } from "./chunk-CJ4IG6G5.mjs";
5
+ import "./chunk-NQFJHNMC.mjs";
6
6
  import "./chunk-GHODWOAL.mjs";
7
7
 
8
8
  // src/selectionCardChrome.ts
@@ -34,10 +34,10 @@ import {
34
34
  import {
35
35
  Fn,
36
36
  J3,
37
+ Jn,
37
38
  X,
38
- Xn,
39
39
  Yn
40
- } from "./chunk-KQN5C7SZ.mjs";
40
+ } from "./chunk-NQFJHNMC.mjs";
41
41
  import {
42
42
  NA_ISO3,
43
43
  NA_LAT_MAX,
@@ -255,7 +255,7 @@ function linearise(c) {
255
255
  const v = c / 255;
256
256
  return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
257
257
  }
258
- var Xn2 = 0.95047;
258
+ var Xn = 0.95047;
259
259
  var Yn2 = 1;
260
260
  var Zn = 1.08883;
261
261
  var EPS = 216 / 24389;
@@ -268,7 +268,7 @@ function rgbToLab(r, g, b) {
268
268
  const X2 = 0.4124564 * R + 0.3575761 * G + 0.1804375 * B;
269
269
  const Y = 0.2126729 * R + 0.7151522 * G + 0.072175 * B;
270
270
  const Z = 0.0193339 * R + 0.119192 * G + 0.9503041 * B;
271
- const fx = f(X2 / Xn2), fy = f(Y / Yn2), fz = f(Z / Zn);
271
+ const fx = f(X2 / Xn), fy = f(Y / Yn2), fz = f(Z / Zn);
272
272
  return { L: 116 * fy - 16, a: 500 * (fx - fy), b: 200 * (fy - fz) };
273
273
  }
274
274
  function hexToLab(hex2) {
@@ -323,7 +323,7 @@ function labToHex(lab) {
323
323
  return t3 > EPS ? t3 : (116 * t - 16) / KAPPA;
324
324
  };
325
325
  const fy = (lab.L + 16) / 116, fx = fy + lab.a / 500, fz = fy - lab.b / 200;
326
- const X2 = Xn2 * finv(fx);
326
+ const X2 = Xn * finv(fx);
327
327
  const Y = Yn2 * (lab.L > KAPPA * EPS ? fy * fy * fy : lab.L / KAPPA);
328
328
  const Z = Zn * finv(fz);
329
329
  const lin = [
@@ -614,10 +614,14 @@ function resolveOptions(p) {
614
614
  cultureCode: p.cultureCode,
615
615
  allowTooltips: p.allowTooltips,
616
616
  noDataText: p.noDataText,
617
- // A finite number is clamped to DIM_OPACITY_MIN-1; absent, blank or junk stays undefined so the stylesheet - and a
618
- // page's own CSS override - still decides. Never defaulted here: that would override every page's CSS.
617
+ // A finite number, or a string that is numeric once trimmed, is clamped to DIM_OPACITY_MIN-1; anything else (absent,
618
+ // blank, a boolean, an array, an object) stays undefined so the stylesheet - and a page's own CSS override - still
619
+ // decides. Not numberOr: Number() reads true as 1 and false, [] and whitespace as 0, which would dim to the floor.
620
+ // Above 1 is clamped to 1, which dims nothing; it is never reinterpreted as a percentage, so the option keeps one
621
+ // unit (a share of full opacity) for every value. Never defaulted here: that would override every page's CSS.
619
622
  dimOpacity: (() => {
620
- const n = numberOr(p.dimOpacity, NaN);
623
+ const v = p.dimOpacity;
624
+ const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
621
625
  return Number.isFinite(n) ? clamp(n, DIM_OPACITY_MIN, 1) : void 0;
622
626
  })(),
623
627
  // A recognised glyph or none: an unknown string must never reach the painter as a live mode, and "none" is
@@ -847,7 +851,7 @@ function resolvePointChannel(cols, rowObjs, point) {
847
851
  let labelCol;
848
852
  if (bind) {
849
853
  const dims = cols.filter((c) => !c.isMeasure).map((c) => c.name);
850
- const res = Xn(dims, rowObjs, bind, point?.mapKind, cols.map((c) => c.name));
854
+ const res = Jn(dims, rowObjs, bind, point?.mapKind, cols.map((c) => c.name));
851
855
  bind = res.bind;
852
856
  rolesBackfilled = res.backfilled;
853
857
  rolesRefused = res.refused;
@@ -2031,6 +2035,7 @@ function compileRenderFn(code, win, doc, d3) {
2031
2035
  }
2032
2036
  var ASYNC_RENDER_TIMEOUT_MS = 15e3;
2033
2037
  var isThenable = (v) => !!v && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
2038
+ var dimVarPage = /* @__PURE__ */ new WeakMap();
2034
2039
  function createChartHost(container, config) {
2035
2040
  const doc = container.ownerDocument;
2036
2041
  const win = doc?.defaultView ?? globalThis;
@@ -2098,6 +2103,19 @@ function createChartHost(container, config) {
2098
2103
  });
2099
2104
  };
2100
2105
  let dimVarSet = false;
2106
+ const releaseDimVar = () => {
2107
+ if (!dimVarSet) return;
2108
+ dimVarSet = false;
2109
+ const page = dimVarPage.get(container);
2110
+ if (!page || --page.holders > 0) return;
2111
+ dimVarPage.delete(container);
2112
+ try {
2113
+ if (container.style?.getPropertyValue?.(DIM_OPACITY_VAR) !== page.wrote) return;
2114
+ if (page.was) container.style?.setProperty(DIM_OPACITY_VAR, page.was.value, page.was.priority);
2115
+ else container.style?.removeProperty(DIM_OPACITY_VAR);
2116
+ } catch {
2117
+ }
2118
+ };
2101
2119
  let current = null;
2102
2120
  let activeTickEl = null;
2103
2121
  let selectionIsPeriod = false;
@@ -2169,6 +2187,7 @@ function createChartHost(container, config) {
2169
2187
  }
2170
2188
  };
2171
2189
  const paintSelection = () => {
2190
+ if (destroyed) return;
2172
2191
  try {
2173
2192
  const sel = current;
2174
2193
  const active = !!sel && sel.length > 0;
@@ -2176,12 +2195,20 @@ function createChartHost(container, config) {
2176
2195
  container.classList?.[dim ? "add" : "remove"](SELECTION_ACTIVE_CLASS);
2177
2196
  const want = resolved.dimOpacity;
2178
2197
  if (typeof want === "number") {
2179
- container.style?.setProperty(DIM_OPACITY_VAR, String(want));
2180
- dimVarSet = true;
2181
- } else if (dimVarSet) {
2182
- container.style?.removeProperty(DIM_OPACITY_VAR);
2183
- dimVarSet = false;
2184
- }
2198
+ const level = String(want);
2199
+ let page = dimVarPage.get(container);
2200
+ if (!dimVarSet) {
2201
+ if (!page) {
2202
+ const value = container.style?.getPropertyValue?.(DIM_OPACITY_VAR) ?? "";
2203
+ page = { was: value ? { value, priority: container.style?.getPropertyPriority?.(DIM_OPACITY_VAR) ?? "" } : null, holders: 0, wrote: level };
2204
+ dimVarPage.set(container, page);
2205
+ }
2206
+ page.holders++;
2207
+ dimVarSet = true;
2208
+ }
2209
+ if (page) page.wrote = level;
2210
+ container.style?.setProperty(DIM_OPACITY_VAR, level);
2211
+ } else releaseDimVar();
2185
2212
  const lit = container.querySelectorAll(`.${ACTIVE_TICK_CLASS}`);
2186
2213
  for (const t of Array.from(lit)) t.classList?.remove(ACTIVE_TICK_CLASS);
2187
2214
  const tickStillDrawn = !!activeTickEl && (typeof container.contains === "function" ? container.contains(activeTickEl) : activeTickEl.isConnected !== false);
@@ -2418,6 +2445,7 @@ function createChartHost(container, config) {
2418
2445
  }
2419
2446
  };
2420
2447
  const notify = (rowIdxs, source, tick, fromChart = false) => {
2448
+ if (destroyed) return;
2421
2449
  current = rowIdxs;
2422
2450
  selectionFromChart = rowIdxs.length > 0 && fromChart;
2423
2451
  activeTickEl = rowIdxs.length ? tick ?? null : null;
@@ -2523,6 +2551,14 @@ function createChartHost(container, config) {
2523
2551
  }
2524
2552
  }
2525
2553
  };
2554
+ const releaseZoom = () => {
2555
+ if (zoomMemory) {
2556
+ try {
2557
+ zoomMemory.release?.(container);
2558
+ } catch {
2559
+ }
2560
+ }
2561
+ };
2526
2562
  const keepScroll = config.restoreScroll !== false;
2527
2563
  let chartSource;
2528
2564
  const scrollKey = () => {
@@ -2761,12 +2797,18 @@ function createChartHost(container, config) {
2761
2797
  );
2762
2798
  }));
2763
2799
  },
2800
+ // A DESTROYED HOST OWNS NOTHING ON THE CONTAINER any more. A host rebuilt on the same element has taken over
2801
+ // the chart's slots (its clear, its animation stop, its initial mark) and the clicked-marks record, so every
2802
+ // method below that reaches the container returns first: a stale reference to the old host (a closure, a late
2803
+ // callback) must not clear, stop or annotate the chart that is on screen now.
2764
2804
  setOptions(partial) {
2805
+ if (destroyed) return;
2765
2806
  raw = { ...raw, ...partial };
2766
2807
  resolved = resolveForHost(raw);
2767
2808
  host.render();
2768
2809
  },
2769
2810
  setData(next) {
2811
+ if (destroyed) return;
2770
2812
  data = next;
2771
2813
  host.render();
2772
2814
  },
@@ -2774,8 +2816,9 @@ function createChartHost(container, config) {
2774
2816
  return resolved;
2775
2817
  },
2776
2818
  setAnnotations(list) {
2819
+ if (destroyed) return;
2777
2820
  annotationLayer.set(list);
2778
- if (!destroyed) try {
2821
+ try {
2779
2822
  drawAnnotations();
2780
2823
  } catch {
2781
2824
  }
@@ -2794,6 +2837,7 @@ function createChartHost(container, config) {
2794
2837
  return () => subs.delete(cb);
2795
2838
  },
2796
2839
  clear() {
2840
+ if (destroyed) return;
2797
2841
  const s = container[CONTAINER_SLOT_XF_CLEAR];
2798
2842
  if (typeof s === "function") {
2799
2843
  try {
@@ -2806,6 +2850,7 @@ function createChartHost(container, config) {
2806
2850
  notify([], "host");
2807
2851
  },
2808
2852
  highlight(rowIdxs) {
2853
+ if (destroyed) return;
2809
2854
  const rows = Array.isArray(rowIdxs) ? rowIdxs.slice() : [];
2810
2855
  if (lasso && lasso.rows().length && !sameRowSet(rows, lasso.rows())) lasso.clear({ publish: false, source: "host" });
2811
2856
  notify(rows, "host");
@@ -2814,9 +2859,11 @@ function createChartHost(container, config) {
2814
2859
  return current;
2815
2860
  }
2816
2861
  },
2817
- animation: { stop: stopAnim },
2862
+ animation: { stop() {
2863
+ if (!destroyed) stopAnim();
2864
+ } },
2818
2865
  initialMark() {
2819
- return container[CONTAINER_SLOT_INITIAL_XF_MARK] ?? null;
2866
+ return destroyed ? null : container[CONTAINER_SLOT_INITIAL_XF_MARK] ?? null;
2820
2867
  },
2821
2868
  destroy() {
2822
2869
  if (destroyed) return;
@@ -2828,17 +2875,12 @@ function createChartHost(container, config) {
2828
2875
  clearPress(container);
2829
2876
  disposeLasso();
2830
2877
  subs.clear();
2831
- if (dimVarSet) {
2832
- try {
2833
- container.style?.removeProperty(DIM_OPACITY_VAR);
2834
- } catch {
2835
- }
2836
- dimVarSet = false;
2837
- }
2878
+ releaseDimVar();
2838
2879
  annotationLayer.destroy();
2839
2880
  if (hasRendered && keepScroll) rememberScroll(container, scrollKey());
2840
2881
  unpinScrolledAxis(container);
2841
2882
  flushZoom();
2883
+ releaseZoom();
2842
2884
  followGlyphs(false);
2843
2885
  container.innerHTML = "";
2844
2886
  if (glyphPositionWas !== null) {
@@ -3,10 +3,10 @@ import {
3
3
  Ct,
4
4
  Z,
5
5
  g,
6
- ii,
7
- ni,
6
+ ri,
7
+ si,
8
8
  xt
9
- } from "./chunk-KQN5C7SZ.mjs";
9
+ } from "./chunk-NQFJHNMC.mjs";
10
10
 
11
11
  // src/sourceDateFormat.ts
12
12
  function formatSourceDate(raw, opts = {}) {
@@ -439,7 +439,7 @@ function isSuppressed(col) {
439
439
  }
440
440
  function resolveAgg(col, requested, auto) {
441
441
  const fallback = () => {
442
- const d = ni(col);
442
+ const d = ri(col);
443
443
  return d === "first" ? "count" : d;
444
444
  };
445
445
  if (auto) return fallback();
@@ -546,7 +546,7 @@ function computeSelectionCard(payload, selectedRowIdxs, opts = {}) {
546
546
  }
547
547
  const value = reduce(selVals, colAgg, selRaw);
548
548
  let sharePct = null;
549
- if (ii(col, colAgg) && value != null) {
549
+ if (si(col, colAgg) && value != null) {
550
550
  const allVals = [];
551
551
  const allRaw = [];
552
552
  for (let r = 0; r < rows.length; r++) {
@@ -998,115 +998,115 @@ function Fn(n, s2) {
998
998
  return { iso: o2, unmatched: I2, matchedRows: N4, totalRows: n.length };
999
999
  }
1000
1000
 
1001
- // ../shape-core/dist/chunk-AQ2WZ4TT.mjs
1001
+ // ../shape-core/dist/chunk-TBH5LFYH.mjs
1002
1002
  var import_papaparse = __toESM(require_papaparse_min(), 1);
1003
1003
  /*! @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. */
1004
1004
  var st = { "north-america": "AG AI AW BB BL BM BQ BS BZ CA CR CU CW DM DO GD GL GP GT HN HT JM KN KY LC MF MQ MS MX NI PA PM PR SV SX TC TT US VC VG VI", "south-america": "AR BO BR CL CO EC FK GF GY PE PY SR UY VE", europe: "AD AL AT AX BA BE BG BY CH CY CZ DE DK EE ES FI FO FR GB GG GI GR HR HU IE IM IS IT JE LI LT LU LV MC MD ME MK MT NL NO PL PT RO RS RU SE SI SJ SK SM UA VA", africa: "AO BF BI BJ BW CD CF CG CI CM CV DJ DZ EG EH ER ET GA GH GM GN GQ GW KE KM LR LS LY MA MG ML MR MU MW MZ NA NE NG RE RW SC SD SH SL SN SO SS ST SZ TD TG TN TZ UG YT ZA ZM ZW", asia: "AE AF AM AZ BD BH BN BT CC CN CX GE HK ID IL IN IO IQ IR JO JP KG KH KP KR KW KZ LA LB LK MM MN MO MV MY NP OM PH PK PS QA SA SG SY TH TJ TL TM TR TW UZ VN YE", oceania: "AS AU CK FJ FM GU KI MH MP NC NF NR NU NZ PF PG PN PW SB TK TO TV UM VU WF WS", antarctica: "AQ BV GS HM TF" };
1005
1005
  var se = (() => {
1006
- let t = /* @__PURE__ */ new Map();
1007
- for (let e of Object.keys(st)) for (let n of st[e].split(/\s+/)) n && t.set(n, e);
1008
- return t;
1006
+ let n = /* @__PURE__ */ new Map();
1007
+ for (let e of Object.keys(st)) for (let t of st[e].split(/\s+/)) t && n.set(t, e);
1008
+ return n;
1009
1009
  })();
1010
- function Vt(t) {
1011
- for (let [e, n] of t) {
1010
+ function Kt(n) {
1011
+ for (let [e, t] of n) {
1012
1012
  let s2 = se.get(e);
1013
- s2 && !se.has(n) && se.set(n, s2);
1013
+ s2 && !se.has(t) && se.set(t, s2);
1014
1014
  }
1015
1015
  }
1016
- Vt(L);
1017
- var nn = ["rate", "ratio", "pct", "percent", "percentage", "rating", "share", "score", "index", "nps", "csat", "margin", "yield", "coverage", "utilization", "utilisation", "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", "ppm", "ppb", "dpmo", "bps", "capita", "temperatuur", "temperatur", "temperatura", "teplota", "lampotila", "homerseklet", "sicaklik", "pression", "presion", "pressao", "pressione", "druck", "tryck", "tlak", "paine", "nyomas", "presiune", "humidite", "humedad", "umidade", "umidita", "feuchtigkeit", "vochtigheid", "kosteus", "vlhkost", "vitesse", "velocidad", "velocidade", "velocita", "geschwindigkeit", "snelheid", "hastighet", "hastighed", "rychlost", "nopeus", "sebesseg", "brzina", "predkosc", "densite", "densidad", "densidade", "densita", "dichte", "dichtheid", "hustota", "tiheys", "gustoca", "moyenne", "mediane", "promedio", "mediana", "gemiddelde", "durchschnitt", "mittelwert", "genomsnitt", "gennemsnit", "gjennomsnitt", "keskiarvo", "atlag", "ortalama", "prosjek", "srednia", "prumer", "priemer", "medie", "taux", "tasa", "taxa", "tasso", "verhaltnis", "verhouding", "aandeel", "anteil", "andel", "osuus", "podil", "udzial", "udio", "pourcentage", "porcentaje", "percentual", "prozent", "procent", "prosentti", "szazalek", "yuzde", "participacion", "participacao", "pondere", "omjer", "arany", "oran", "rata", "leeftijd", "edad", "idade", "wiek", "eletkor", "varsta", "puntuacion", "pontuacao", "punteggio", "betyg", "ocena", "indice", "indeks", "wskaznik", "puan", "ocupacion", "ocupacao", "occupazione", "auslastung", "bezetting", "belaggning", "belaegning", "belegg", "kayttoaste", "oblozenie", "obsazenost", "obsadenost", "kihasznaltsag", "doluluk", "ocupare", "popunjenost", "efficacite", "eficiencia", "efficienza", "effizienz", "effectiviteit", "verimlilik"];
1018
- var rn = ["pct", "percent", "percentage", "rate", "ratio", "rating", "share", "score", "index", "nps", "csat", "margin", "yield", "coverage", "utilization", "utilisation", "gpa", "percentile", "quantile", "bmi", "density", "pressure", "humidity", "velocity", "efficiency", "occupancy", "latency", "throughput", "bandwidth", "iops", "ppm", "ppb", "dpmo", "bps"];
1019
- var sn = ["latitude", "longitude"];
1016
+ Kt(L);
1017
+ var rn = ["rate", "ratio", "pct", "percent", "percentage", "rating", "share", "score", "index", "nps", "csat", "margin", "yield", "coverage", "utilization", "utilisation", "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", "ppm", "ppb", "dpmo", "bps", "capita", "temperatuur", "temperatur", "temperatura", "teplota", "lampotila", "homerseklet", "sicaklik", "pression", "presion", "pressao", "pressione", "druck", "tryck", "tlak", "paine", "nyomas", "presiune", "humidite", "humedad", "umidade", "umidita", "feuchtigkeit", "vochtigheid", "kosteus", "vlhkost", "vitesse", "velocidad", "velocidade", "velocita", "geschwindigkeit", "snelheid", "hastighet", "hastighed", "rychlost", "nopeus", "sebesseg", "brzina", "predkosc", "densite", "densidad", "densidade", "densita", "dichte", "dichtheid", "hustota", "tiheys", "gustoca", "moyenne", "mediane", "promedio", "mediana", "gemiddelde", "durchschnitt", "mittelwert", "genomsnitt", "gennemsnit", "gjennomsnitt", "keskiarvo", "atlag", "ortalama", "prosjek", "srednia", "prumer", "priemer", "medie", "taux", "tasa", "taxa", "tasso", "verhaltnis", "verhouding", "aandeel", "anteil", "andel", "osuus", "podil", "udzial", "udio", "pourcentage", "porcentaje", "percentual", "prozent", "procent", "prosentti", "szazalek", "yuzde", "participacion", "participacao", "pondere", "omjer", "arany", "oran", "rata", "leeftijd", "edad", "idade", "wiek", "eletkor", "varsta", "puntuacion", "pontuacao", "punteggio", "betyg", "ocena", "indice", "indeks", "wskaznik", "puan", "ocupacion", "ocupacao", "occupazione", "auslastung", "bezetting", "belaggning", "belaegning", "belegg", "kayttoaste", "oblozenie", "obsazenost", "obsadenost", "kihasznaltsag", "doluluk", "ocupare", "popunjenost", "efficacite", "eficiencia", "efficienza", "effizienz", "effectiviteit", "verimlilik"];
1018
+ var sn = ["pct", "percent", "percentage", "rate", "ratio", "rating", "share", "score", "index", "nps", "csat", "margin", "yield", "coverage", "utilization", "utilisation", "gpa", "percentile", "quantile", "bmi", "density", "pressure", "humidity", "velocity", "efficiency", "occupancy", "latency", "throughput", "bandwidth", "iops", "ppm", "ppb", "dpmo", "bps"];
1020
1019
  var on = ["latitude", "longitude"];
1021
- function Z2(t) {
1022
- return c(t);
1020
+ var an = ["latitude", "longitude"];
1021
+ function Z2(n) {
1022
+ return c(n);
1023
1023
  }
1024
1024
  var dt = ["somme de", "suma de", "soma de", "summe von", "som van", "somma di", "summa av", "sum av", "sum af", "soucet z", "suma z", "totaal van", "total de", "toplam", "osszeg", "nombre de", "recuento de", "contagem de", "anzahl von", "aantal van", "conteggio di", "antal av", "lukumaara", "pocet z", "sum pa", "\u0441\u0443\u043C\u043C\u0430", "\u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E", "\u0441\u0443\u043C\u0430", "\u043A\u0456\u043B\u044C\u043A\u0456\u0441\u0442\u044C", "\u03B1\u03B8\u03C1\u03BF\u03B9\u03C3\u03BC\u03B1", "\u03C0\u03BB\u03B7\u03B8\u03BF\u03C2"];
1025
- var ln = ["osszege", "sucet", "\u7684\u603B\u548C", "\u7684\u8BA1\u6570", "\u306E\u5408\u8A08", "\uC758 \uD569\uACC4"];
1026
- var un = ["atlaga", "priemer", "\u7684\u5E73\u5747\u503C", "\u306E\u5E73\u5747", "\uC758 \uD3C9\uADE0"];
1025
+ var un = ["osszege", "sucet", "\u7684\u603B\u548C", "\u7684\u8BA1\u6570", "\u306E\u5408\u8A08", "\uC758 \uD569\uACC4"];
1026
+ var cn = ["atlaga", "priemer", "\u7684\u5E73\u5747\u503C", "\u306E\u5E73\u5747", "\uC758 \uD3C9\uADE0"];
1027
1027
  var ye = Object.freeze({ "nombre de": "count", "recuento de": "count", "contagem de": "count", "anzahl von": "count", "aantal van": "count", "conteggio di": "count", "antal av": "count", lukumaara: "count", "pocet z": "count", \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E: "count", \u043A\u0456\u043B\u044C\u043A\u0456\u0441\u0442\u044C: "count", \u03C0\u03BB\u03B7\u03B8\u03BF\u03C2: "count", \u7684\u8BA1\u6570: "count", "minimum de": "min", "minimo de": "min", "minimo di": "min", "minimum von": "min", "maximum de": "max", "maximo de": "max", "massimo di": "max", "maximum von": "max" });
1028
- function Ne(t) {
1029
- return [...t].sort((e, n) => n.length - e.length).map((e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+")).join("|");
1028
+ function Ne(n) {
1029
+ return [...n].sort((e, t) => t.length - e.length).map((e) => e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+")).join("|");
1030
1030
  }
1031
1031
  var ft = new RegExp("^\\s*(?:(?:sum|count|count\\s+distinct|distinct\\s+count)\\s+of\\s+|(?:" + Ne(dt) + ")\\s+)", "i");
1032
- function ct(t) {
1033
- for (let e of t) {
1034
- let n = e.codePointAt(0) ?? 0;
1035
- if (n >= 11904 && n <= 40959 || n >= 44032 && n <= 55215) return true;
1032
+ function ct(n) {
1033
+ for (let e of n) {
1034
+ let t = e.codePointAt(0) ?? 0;
1035
+ if (t >= 11904 && t <= 40959 || t >= 44032 && t <= 55215) return true;
1036
1036
  }
1037
1037
  return false;
1038
1038
  }
1039
- var cn = "[\u2013\u2014-]";
1040
- function gt(t) {
1041
- let e = t.filter((r2) => !ct(r2)), n = t.filter(ct), s2 = [];
1042
- return e.length && s2.push("(?:\\s+|\\s*" + cn + "\\s*)(" + Ne(e) + ")"), n.length && s2.push("\\s*(" + Ne(n) + ")"), new RegExp("(?:" + s2.join("|") + ")\\s*$", "i");
1043
- }
1044
- var Ee = gt(ln);
1045
- var mn2 = gt(un);
1046
- function be(t, e) {
1047
- let n = e.exec(t);
1048
- if (!n) return null;
1049
- let s2 = t.slice(0, n.index);
1050
- return s2.trim() === "" ? null : { base: s2, label: (n[1] ?? n[2] ?? "").toLowerCase() };
1051
- }
1052
- var dn2 = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Za-z])(?=[0-9])|(?<=[0-9])(?=[A-Za-z])/g;
1053
- var fn2 = new RegExp("\\b(" + nn.join("|") + ")\\b|(" + rn.join("|") + ")$", "i");
1054
- var gn = new RegExp("\\b(" + sn.join("|") + ")\\b|(" + on.join("|") + ")$", "i");
1055
- function Se(t) {
1056
- if (!t) return "";
1057
- let e = String(t), n = Z2(e), s2 = ft.exec(n);
1039
+ var mn2 = "[\u2013\u2014-]";
1040
+ function gt(n) {
1041
+ let e = n.filter((r2) => !ct(r2)), t = n.filter(ct), s2 = [];
1042
+ return e.length && s2.push("(?:\\s+|\\s*" + mn2 + "\\s*)(" + Ne(e) + ")"), t.length && s2.push("\\s*(" + Ne(t) + ")"), new RegExp("(?:" + s2.join("|") + ")\\s*$", "i");
1043
+ }
1044
+ var Ee = gt(un);
1045
+ var dn2 = gt(cn);
1046
+ function be(n, e) {
1047
+ let t = e.exec(n);
1048
+ if (!t) return null;
1049
+ let s2 = n.slice(0, t.index);
1050
+ return s2.trim() === "" ? null : { base: s2, label: (t[1] ?? t[2] ?? "").toLowerCase() };
1051
+ }
1052
+ var fn2 = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Za-z])(?=[0-9])|(?<=[0-9])(?=[A-Za-z])/g;
1053
+ var gn = new RegExp("\\b(" + rn.join("|") + ")\\b|(" + sn.join("|") + ")$", "i");
1054
+ var pn2 = new RegExp("\\b(" + on.join("|") + ")\\b|(" + an.join("|") + ")$", "i");
1055
+ function Se(n) {
1056
+ if (!n) return "";
1057
+ let e = String(n), t = Z2(e), s2 = ft.exec(t);
1058
1058
  if (s2) return e.slice(s2[0].length).trim();
1059
- let r2 = be(n, Ee);
1059
+ let r2 = be(t, Ee);
1060
1060
  return (r2 ? e.slice(0, r2.base.length) : e).trim();
1061
1061
  }
1062
- function pn2(t) {
1063
- if (!t) return false;
1064
- let e = Z2(String(t));
1062
+ function hn(n) {
1063
+ if (!n) return false;
1064
+ let e = Z2(String(n));
1065
1065
  return ft.test(e) || be(e, Ee) !== null;
1066
1066
  }
1067
- var hn = ["count (distinct)", "count distinct", "distinct count", "standard deviation", "average", "variance", "median", "product", "stddev", "std dev", "count", "sum", "avg", "min", "minimum", "max", "maximum", "var"];
1068
- var mt = new RegExp("^\\s*(" + [...hn].sort((t, e) => e.length - t.length).map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+")).join("|") + ")\\s+of\\s+", "i");
1067
+ var yn2 = ["count (distinct)", "count distinct", "distinct count", "standard deviation", "average", "variance", "median", "product", "stddev", "std dev", "count", "sum", "avg", "min", "minimum", "max", "maximum", "var"];
1068
+ var mt = new RegExp("^\\s*(" + [...yn2].sort((n, e) => e.length - n.length).map((n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+")).join("|") + ")\\s+of\\s+", "i");
1069
1069
  var yt = { Sum: ["Sum of "], Avg: ["Average of "], Average: ["Average of "], Count: ["Count of "], CountNonNull: ["Count of "], Min: ["Min of ", "First ", "Earliest "], Max: ["Max of ", "Last ", "Latest "], Median: ["Median of "], StandardDeviation: ["Standard deviation of "], Variance: ["Variance of "] };
1070
- var yn2 = new RegExp("^(" + Object.keys(yt).join("|") + ")\\((.+)\\)$");
1071
- function bn(t) {
1072
- let e = Se(t);
1073
- return e.length === 0 ? "" : e.replace(dn2, " ");
1070
+ var bn = new RegExp("^(" + Object.keys(yt).join("|") + ")\\((.+)\\)$");
1071
+ function Sn2(n) {
1072
+ let e = Se(n);
1073
+ return e.length === 0 ? "" : e.replace(fn2, " ");
1074
1074
  }
1075
- function St(t, e) {
1075
+ function St(n, e) {
1076
1076
  if (!e) return false;
1077
- let n = Se(e);
1078
- return t.test(Z2(bn(e))) || t.test(Z2(n)) || t.test(Z2(f(n).join(" ")));
1077
+ let t = Se(e);
1078
+ return n.test(Z2(Sn2(e))) || n.test(Z2(t)) || n.test(Z2(f(t).join(" ")));
1079
1079
  }
1080
- function At(t) {
1081
- return St(gn, t);
1080
+ function At(n) {
1081
+ return St(pn2, n);
1082
1082
  }
1083
- function Sn2(t) {
1084
- return St(fn2, t) || At(t);
1083
+ function An2(n) {
1084
+ return St(gn, n) || At(n);
1085
1085
  }
1086
1086
  var _t = /int|double|decimal|single|float|number|currency|money/i;
1087
- function An2(t) {
1088
- let e = String(t?.valueNature ?? "").trim().toLowerCase();
1089
- return e === "categorical" ? "Categorical" : e === "ordinal" ? "Ordinal" : e === "continuous" || t?.isMeasure === true || _t.test(String(t?.dataType ?? "")) ? "Continuous" : "Categorical";
1087
+ function _n(n) {
1088
+ let e = String(n?.valueNature ?? "").trim().toLowerCase();
1089
+ return e === "categorical" ? "Categorical" : e === "ordinal" ? "Ordinal" : e === "continuous" || n?.isMeasure === true || _t.test(String(n?.dataType ?? "")) ? "Continuous" : "Categorical";
1090
1090
  }
1091
- function _n(t) {
1092
- return t?.isMeasure === true || _t.test(String(t?.dataType ?? ""));
1091
+ function xn(n) {
1092
+ return n?.isMeasure === true || _t.test(String(n?.dataType ?? ""));
1093
1093
  }
1094
- function xt(t) {
1095
- let e = t ?? {}, n = An2(e);
1096
- if (n !== "Continuous") return { nature: n, additivity: "unknown", basis: "role", suppressed: false };
1097
- if (At(e.name)) return { nature: n, additivity: "positional", basis: "name", suppressed: true };
1094
+ function xt(n) {
1095
+ let e = n ?? {}, t = _n(e);
1096
+ if (t !== "Continuous") return { nature: t, additivity: "unknown", basis: "role", suppressed: false };
1097
+ if (At(e.name)) return { nature: t, additivity: "positional", basis: "name", suppressed: true };
1098
1098
  let s2 = String(e.additivity ?? "").trim().toLowerCase();
1099
- if (!(s2 === "additive" && pn2(e.name))) {
1100
- if (s2 === "additive") return { nature: n, additivity: "additive", basis: "measured", suppressed: false };
1101
- if (s2 === "part_of_whole") return { nature: n, additivity: "part_of_whole", basis: "measured", suppressed: false };
1102
- if (s2 === "intensive_rate") return { nature: n, additivity: "intensive_rate", basis: "measured", suppressed: false };
1099
+ if (!(s2 === "additive" && hn(e.name))) {
1100
+ if (s2 === "additive") return { nature: t, additivity: "additive", basis: "measured", suppressed: false };
1101
+ if (s2 === "part_of_whole") return { nature: t, additivity: "part_of_whole", basis: "measured", suppressed: false };
1102
+ if (s2 === "intensive_rate") return { nature: t, additivity: "intensive_rate", basis: "measured", suppressed: false };
1103
1103
  }
1104
- return e.discourageAggregationAcrossGroups === true ? { nature: n, additivity: "intensive_rate", basis: "host-flag", suppressed: false } : Sn2(e.name) ? { nature: n, additivity: "intensive_rate", basis: "name", suppressed: false } : { nature: n, additivity: "additive", basis: "default", suppressed: false };
1104
+ return e.discourageAggregationAcrossGroups === true ? { nature: t, additivity: "intensive_rate", basis: "host-flag", suppressed: false } : An2(e.name) ? { nature: t, additivity: "intensive_rate", basis: "name", suppressed: false } : { nature: t, additivity: "additive", basis: "default", suppressed: false };
1105
1105
  }
1106
- function Ct(t) {
1107
- let e = xt(t), n = t ?? {};
1106
+ function Ct(n) {
1107
+ let e = xt(n), t = n ?? {};
1108
1108
  if (e.nature === "Categorical") return ["first", "distinctcount", "count"];
1109
- if (e.nature === "Ordinal") return _n(n) ? ["median", "average", "min", "max", "first", "distinctcount", "count"] : ["first", "distinctcount", "count"];
1109
+ if (e.nature === "Ordinal") return xn(t) ? ["median", "average", "min", "max", "first", "distinctcount", "count"] : ["first", "distinctcount", "count"];
1110
1110
  switch (e.additivity) {
1111
1111
  case "positional":
1112
1112
  return ["min", "max", "count", "distinctcount", "first"];
@@ -1116,16 +1116,16 @@ function Ct(t) {
1116
1116
  return ["sum", "average", "median", "min", "max", "count", "distinctcount", "first"];
1117
1117
  }
1118
1118
  }
1119
- function ni(t) {
1120
- return Ct(t)[0];
1119
+ function ri(n) {
1120
+ return Ct(n)[0];
1121
1121
  }
1122
- function ii(t, e) {
1122
+ function si(n, e) {
1123
1123
  if (e !== "sum" && e !== "count") return false;
1124
1124
  if (e === "count") return true;
1125
- let n = xt(t);
1126
- return n.additivity === "additive" || n.additivity === "part_of_whole" || n.additivity === "unknown";
1125
+ let t = xt(n);
1126
+ return t.additivity === "additive" || t.additivity === "part_of_whole" || t.additivity === "unknown";
1127
1127
  }
1128
- var Ln = new RegExp([String.raw`\bisMeasure\b`, String.raw`\bdataType\b`, String.raw`\bcolumns\s*\[\s*-?\d`, String.raw`\.iloc\s*\[[^\]]*,`, String.raw`\.iat\s*\[`, String.raw`\bselect_dtypes\b`, String.raw`\b_get_numeric_data\b`, String.raw`\bis_numeric_dtype\b`, String.raw`\bfor\s+\w+\s+in\s+(?:list\s*\(\s*)?df\.columns\b`, String.raw`\bdf\.columns\s*\.\s*(?:tolist|to_list|difference|drop)\b`, String.raw`\bdf\.dtypes\b`, String.raw`\bObject\s*\.\s*(?:keys|values|entries)\s*\(\s*(?:rows|data|df)?\s*\[\s*0\s*\]`, String.raw`\b(?:row|rw|r)\s*\[\s*\d+\s*\]`].join("|"));
1128
+ var Pn = new RegExp([String.raw`\bisMeasure\b`, String.raw`\bdataType\b`, String.raw`\bcolumns\s*\[\s*-?\d`, String.raw`\.iloc\s*\[[^\]]*,`, String.raw`\.iat\s*\[`, String.raw`\bselect_dtypes\b`, String.raw`\b_get_numeric_data\b`, String.raw`\bis_numeric_dtype\b`, String.raw`\bfor\s+\w+\s+in\s+(?:list\s*\(\s*)?df\.columns\b`, String.raw`\bdf\.columns\s*\.\s*(?:tolist|to_list|difference|drop)\b`, String.raw`\bdf\.dtypes\b`, String.raw`\bObject\s*\.\s*(?:keys|values|entries)\s*\(\s*(?:rows|data|df)?\s*\[\s*0\s*\]`, String.raw`\b(?:row|rw|r)\s*\[\s*\d+\s*\]`].join("|"));
1129
1129
 
1130
1130
  // ../shape-core/dist/index.mjs
1131
1131
  /*! @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. */
@@ -1187,7 +1187,7 @@ function re2(e) {
1187
1187
  function de(e, t) {
1188
1188
  return y(t, fn) ? true : oe2(e).some((n) => /^\d{5}(-\d{4})?$/.test(n.replace(/\.0+$/, "")));
1189
1189
  }
1190
- function Yn2(e, t) {
1190
+ function Qn(e, t) {
1191
1191
  let n = F3(e);
1192
1192
  if (n.length === 0) return 0;
1193
1193
  let o2 = 0;
@@ -1199,12 +1199,12 @@ function ee(e, t) {
1199
1199
  for (let o2 of F3(e)) t(o2) && n++;
1200
1200
  return n;
1201
1201
  }
1202
- function Qn(e, t) {
1202
+ function Xn(e, t) {
1203
1203
  let n = 0;
1204
1204
  for (let o2 of oe2(e)) t(o2) && n++;
1205
1205
  return n;
1206
1206
  }
1207
- function Xn(e, t, n, o2, a3) {
1207
+ function Jn(e, t, n, o2, a3) {
1208
1208
  let u4 = [], l2 = [], i = {}, h4 = {}, C = new Set(e), y2 = (s2) => t.map((d2) => d2[s2]);
1209
1209
  for (let s2 of ["city", "country"]) {
1210
1210
  let d2 = n?.[s2];
@@ -1241,7 +1241,7 @@ function Xn(e, t, n, o2, a3) {
1241
1241
  if (!i.zip) for (let s2 of b4) {
1242
1242
  if (p4.has(s2)) continue;
1243
1243
  let d2 = y2(s2), S2 = re2(d2);
1244
- if (!(S2 < 95) && de(d2, s2) && !(Qn(d2, (O4) => r3(O4).length > 0) < Z3)) {
1244
+ if (!(S2 < 95) && de(d2, s2) && !(Xn(d2, (O4) => r3(O4).length > 0) < Z3)) {
1245
1245
  i.zip = s2, p4.add(s2), u4.push(`zip=${s2} (${S2}% ZIP codes)`);
1246
1246
  break;
1247
1247
  }
@@ -1260,7 +1260,7 @@ function Xn(e, t, n, o2, a3) {
1260
1260
  let s2 = null;
1261
1261
  for (let d2 of b4) {
1262
1262
  if (p4.has(d2)) continue;
1263
- let S2 = y2(d2), O4 = Yn2(S2, o2);
1263
+ let S2 = y2(d2), O4 = Qn(S2, o2);
1264
1264
  O4 < D3 || ee(S2, (K3) => j3(K3, o2)) < Z3 || (!s2 || O4 > s2.pct) && (s2 = { name: d2, pct: O4 });
1265
1265
  }
1266
1266
  s2 && (i.city = s2.name, u4.push(`city=${s2.name} (${s2.pct}% known cities)`));
@@ -1273,11 +1273,11 @@ function Xn(e, t, n, o2, a3) {
1273
1273
  }
1274
1274
  var r = (e, ...t) => ({ part: e, langs: Object.freeze(t) });
1275
1275
  var ie = Object.freeze({ jaar: r("year", "nl"), kwartaal: r("quarter", "nl"), maand: r("month", "nl"), dag: r("day", "nl", "sv", "da", "nb"), jahr: r("year", "de"), quartal: r("quarter", "de"), monat: r("month", "de"), tag: r("day", "de"), ann\u00E9e: r("year", "fr"), mois: r("month", "fr"), jour: r("day", "fr"), trimestre: r("quarter", "fr", "es", "pt", "it"), a\u00F1o: r("year", "es"), mes: r("month", "es"), d\u00EDa: r("day", "es"), ano: r("year", "pt"), m\u00EAs: r("month", "pt"), dia: r("day", "pt"), anno: r("year", "it"), mese: r("month", "it"), giorno: r("day", "it"), \u0433\u043E\u0434: r("year", "ru"), \u043C\u0435\u0441\u044F\u0446: r("month", "ru"), \u0440\u0456\u043A: r("year", "uk"), \u043C\u0456\u0441\u044F\u0446\u044C: r("month", "uk"), \u043A\u0432\u0430\u0440\u0442\u0430\u043B: r("quarter", "ru", "uk"), \u0434\u0435\u043D\u044C: r("day", "ru", "uk"), rok: r("year", "pl", "cs", "sk"), kwarta\u0142: r("quarter", "pl"), miesi\u0105c: r("month", "pl"), dzie\u0144: r("day", "pl"), \u010Dtvrtlet\u00ED: r("quarter", "cs"), m\u011Bs\u00EDc: r("month", "cs"), den: r("day", "cs"), \u0161tvr\u0165rok: r("quarter", "sk"), mesiac: r("month", "sk"), de\u0148: r("day", "sk"), \u00E5r: r("year", "sv", "da", "nb"), kvartal: r("quarter", "sv", "da", "nb"), m\u00E5nad: r("month", "sv"), m\u00E5ned: r("month", "da", "nb"), vuosi: r("year", "fi"), vuosinelj\u00E4nnes: r("quarter", "fi"), kuukausi: r("month", "fi"), p\u00E4iv\u00E4: r("day", "fi"), \u00E9v: r("year", "hu"), negyed\u00E9v: r("quarter", "hu"), h\u00F3nap: r("month", "hu"), nap: r("day", "hu"), y\u0131l: r("year", "tr"), \u00E7eyrek: r("quarter", "tr"), ay: r("month", "tr"), g\u00FCn: r("day", "tr"), \u03AD\u03C4\u03BF\u03C2: r("year", "el"), \u03C4\u03C1\u03AF\u03BC\u03B7\u03BD\u03BF: r("quarter", "el"), \u03BC\u03AE\u03BD\u03B1\u03C2: r("month", "el"), \u03B7\u03BC\u03AD\u03C1\u03B1: r("day", "el"), an: r("year", "ro"), trimestru: r("quarter", "ro"), lun\u0103: r("month", "ro"), zi: r("day", "ro"), godina: r("year", "hr"), tromjese\u010Dje: r("quarter", "hr"), mjesec: r("month", "hr"), dan: r("day", "hr"), \u5E74: r("year", "ja", "zh"), \u56DB\u534A\u671F: r("quarter", "ja"), \u5B63\u5EA6: r("quarter", "zh"), \u6708: r("month", "ja", "zh"), \u65E5: r("day", "ja", "zh"), \uC5F0\uB3C4: r("year", "ko"), \uBD84\uAE30: r("quarter", "ko"), \uC6D4: r("month", "ko"), \uC77C: r("day", "ko"), tahun: r("year", "id"), kuartal: r("quarter", "id"), triwulan: r("quarter", "id"), bulan: r("month", "id"), hari: r("day", "id"), n\u0103m: r("year", "vi"), qu\u00FD: r("quarter", "vi"), th\u00E1ng: r("month", "vi"), ng\u00E0y: r("day", "vi"), \u0E1B\u0E35: r("year", "th"), \u0E44\u0E15\u0E23\u0E21\u0E32\u0E2A: r("quarter", "th"), \u0E40\u0E14\u0E37\u0E2D\u0E19: r("month", "th"), \u0E27\u0E31\u0E19: r("day", "th"), \u0627\u0644\u0633\u0646\u0629: r("year", "ar"), \u0633\u0646\u0629: r("year", "ar"), \u0627\u0644\u0639\u0627\u0645: r("year", "ar"), \u0639\u0627\u0645: r("year", "ar"), \u0627\u0644\u0631\u0628\u0639: r("quarter", "ar"), \u0631\u0628\u0639: r("quarter", "ar"), "\u0631\u0628\u0639 \u0627\u0644\u0633\u0646\u0629": r("quarter", "ar"), \u0627\u0644\u0634\u0647\u0631: r("month", "ar"), \u0634\u0647\u0631: r("month", "ar"), \u0627\u0644\u064A\u0648\u0645: r("day", "ar"), \u064A\u0648\u0645: r("day", "ar"), \u05E9\u05E0\u05D4: r("year", "he"), \u05E8\u05D1\u05E2\u05D5\u05DF: r("quarter", "he"), \u05D7\u05D5\u05D3\u05E9: r("month", "he"), \u05D9\u05D5\u05DD: r("day", "he"), \u0935\u0930\u094D\u0937: r("year", "hi"), \u0938\u093E\u0932: r("year", "hi"), \u0924\u093F\u092E\u093E\u0939\u0940: r("quarter", "hi"), \u092E\u0939\u0940\u0928\u093E: r("month", "hi"), \u092E\u093E\u0939: r("month", "hi"), \u0926\u093F\u0928: r("day", "hi"), \u0633\u0627\u0644: r("year", "fa"), \u0645\u0627\u0647: r("month", "fa"), \u0631\u0648\u0632: r("day", "fa") });
1276
- var Jn2 = Object.freeze(Array.from(new Set(Object.values(ie).flatMap((e) => e.langs))));
1276
+ var Zn = Object.freeze(Array.from(new Set(Object.values(ie).flatMap((e) => e.langs))));
1277
1277
  var fe = Object.freeze(["fa", "ar", "he", "th"]);
1278
- var Lr = Object.freeze({ integer: "number", decimal: "number", double: "number", datetime: "date", date: "date", boolean: "boolean", string: "text" });
1279
- var Gr = Object.freeze({ RENDER_START: "render-start", RENDER_OK: "render-ok", RENDER_ERROR: "render-error", VIEW: "view", VIEW_VERDICT: "view-verdict", REDRAW: "redraw", INTERACT: "interact" });
1280
- var Vr2 = Object.freeze(["open", "generate", "version", "rebind", "data", "sample", "fix", "resize", "other"]);
1278
+ var Ar = Object.freeze({ integer: "number", decimal: "number", double: "number", datetime: "date", date: "date", boolean: "boolean", string: "text" });
1279
+ var Vr2 = Object.freeze({ RENDER_START: "render-start", RENDER_OK: "render-ok", RENDER_ERROR: "render-error", VIEW: "view", VIEW_VERDICT: "view-verdict", REDRAW: "redraw", INTERACT: "interact" });
1280
+ var Ur = Object.freeze(["open", "generate", "version", "rebind", "data", "sample", "fix", "resize", "other"]);
1281
1281
  var Ae = 2 ** 31 - 1;
1282
1282
 
1283
1283
  export {
@@ -1293,7 +1293,7 @@ export {
1293
1293
  Fn,
1294
1294
  xt,
1295
1295
  Ct,
1296
- ni,
1297
- ii,
1298
- Xn
1296
+ ri,
1297
+ si,
1298
+ Jn
1299
1299
  };
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  formatSourceDateFor,
6
6
  isExcelDateFormat,
7
7
  normaliseAggregation
8
- } from "./chunk-Z6OZDX4Z.mjs";
8
+ } from "./chunk-CJ4IG6G5.mjs";
9
9
  import {
10
10
  captureSvgSnapshot,
11
11
  chartSvgOf,
@@ -97,7 +97,7 @@ import {
97
97
  toRGBA,
98
98
  toSourceRows,
99
99
  valuePlacementFlag
100
- } from "./chunk-FHHT7DBM.mjs";
100
+ } from "./chunk-4ICQEVZX.mjs";
101
101
  import {
102
102
  LASSO_AXES_SLOT,
103
103
  LASSO_COUNT_CLASS,
@@ -187,7 +187,7 @@ import {
187
187
  _3,
188
188
  l,
189
189
  o
190
- } from "./chunk-KQN5C7SZ.mjs";
190
+ } from "./chunk-NQFJHNMC.mjs";
191
191
  import "./chunk-A2GMXZP7.mjs";
192
192
  import {
193
193
  ACTIVE_TICK_CLASS,
package/dist/react.mjs CHANGED
@@ -22,12 +22,12 @@ import {
22
22
  loadGeo,
23
23
  syncMemberSelection,
24
24
  toSourceRows
25
- } from "./chunk-FHHT7DBM.mjs";
25
+ } from "./chunk-4ICQEVZX.mjs";
26
26
  import "./chunk-QHMLMF4L.mjs";
27
27
  import "./chunk-PDVEIW42.mjs";
28
28
  import "./chunk-VEVBXK44.mjs";
29
29
  import "./chunk-NNLBFIWU.mjs";
30
- import "./chunk-KQN5C7SZ.mjs";
30
+ import "./chunk-NQFJHNMC.mjs";
31
31
  import "./chunk-A2GMXZP7.mjs";
32
32
  import "./chunk-45VILLJT.mjs";
33
33
  import "./chunk-GHODWOAL.mjs";
@@ -113,6 +113,11 @@ function BicChart(props) {
113
113
  };
114
114
  const userSelectedRef = useRef(false);
115
115
  const optKey = useMemo(() => JSON.stringify(options ?? {}), [options]);
116
+ const zoomOn = zoomMemory != null;
117
+ const zoomRef = useRef(zoomMemory ?? null);
118
+ useLayoutEffect(() => {
119
+ zoomRef.current = zoomMemory ?? null;
120
+ });
116
121
  const builtWithRef = useRef(null);
117
122
  const onSelectRef = useRef(onSelect);
118
123
  onSelectRef.current = onSelect;
@@ -155,7 +160,7 @@ function BicChart(props) {
155
160
  d3,
156
161
  geoKind,
157
162
  viewState,
158
- zoomMemory,
163
+ zoomMemory: zoomRef.current,
159
164
  labelContrast,
160
165
  layoutPass,
161
166
  restoreScroll,
@@ -191,7 +196,7 @@ function BicChart(props) {
191
196
  host.destroy();
192
197
  hostRef.current = null;
193
198
  };
194
- }, [code, renderFn, d3, geoKind, labelContrast, layoutPass, restoreScroll, controls]);
199
+ }, [code, renderFn, d3, geoKind, labelContrast, layoutPass, restoreScroll, zoomOn, controls]);
195
200
  useLayoutEffect(() => {
196
201
  const host = hostRef.current;
197
202
  if (!host || !selects) return;
@@ -217,7 +222,7 @@ function BicChart(props) {
217
222
  b.detach();
218
223
  if (bindingRef.current === b) bindingRef.current = null;
219
224
  };
220
- }, [selects, code, renderFn, d3, geoKind, labelContrast, layoutPass, restoreScroll]);
225
+ }, [selects, code, renderFn, d3, geoKind, labelContrast, layoutPass, restoreScroll, zoomOn]);
221
226
  useEffect(() => {
222
227
  if (!hostRef.current || !options) return;
223
228
  if (builtWithRef.current?.optKey === optKey) return;
@@ -211,7 +211,7 @@ export interface RenderOptions {
211
211
  * holds a list of chart types, so a chart saved before the capability existed has none.
212
212
  * enabled - the reader's setting; on unless it is exactly false. Both must hold for a surface to be drawn.
213
213
  * rowCap - the most rows in view that still get a lasso; above it the whole lasso is off and a caption says
214
- * so. Absent is LASSO_ROW_CAP (provisional).
214
+ * so. Absent is LASSO_ROW_CAP.
215
215
  * actions - what the host runs on release, in order, by name. Absent is ["select"]; a name the host does
216
216
  * not know is skipped, so a list can be sent ahead of the build that understands it.
217
217
  */
@@ -80,9 +80,10 @@ export interface ChartHostConfig {
80
80
  /**
81
81
  * REMEMBER THE READER'S ZOOM AND PAN on a chart that calls d3.zoom(), across every re-render (and across a
82
82
  * close and reopen, if the host's store says so). Build it with createZoomMemoryHook() from
83
- * "@bicharts/chart-host/zoom-memory", ONCE per container, and hand the same hook to every host created on
84
- * that container. Absent (the default), d3.zoom is never wrapped and nothing is listened for. It applies to a
85
- * chart compiled from `code` only: a precompiled `renderFn` closed over its own d3.
83
+ * "@bicharts/chart-host/zoom-memory", ONCE per chart, and hand the same hook to every host built for that chart
84
+ * (on the same element or, after a remount, a new one): a host that is destroyed releases the hook's watcher, so
85
+ * the hook can go to the next. Absent (the default), d3.zoom is never wrapped and nothing is listened for. It
86
+ * applies to a chart compiled from `code` only: a precompiled `renderFn` closed over its own d3.
86
87
  */
87
88
  zoomMemory?: ZoomMemoryHook | null;
88
89
  /** D3 v7 instance handed to the compiled code. Default: (globalThis as any).d3. */
@@ -2,10 +2,11 @@ import { type RenderOptions } from "./contract";
2
2
  /**
3
3
  * THE MOST ROWS A CHART MAY HAVE IN VIEW AND STILL OFFER A LASSO.
4
4
  *
5
- * PROVISIONAL. Unmeasured: 5,000 is a stand-in until the time Power BI Desktop takes to apply a selection
6
- * of that many ids is measured on a release-test page (1,000, 5,000 and 13,000 ids), and that number
7
- * replaces this one. Do not read it as a finding. It is the worst case that is allowed for, not the
8
- * typical one: a reader can lasso everything.
5
+ * Measured on a real Power BI Desktop page with a card and a table reacting to the selection: applying
6
+ * 1,000 ids took 0.3 s, 5,000 ids 0.7 s and 13,000 ids 1.2 s from the click until the page showed the
7
+ * result, while the select call itself returned in under 8 ms. 5,000 is the largest of those sizes that
8
+ * stays responsive at a one-second bar; richer pages react more slowly, so it is not raised. It is the
9
+ * worst case that is allowed for, not the typical one: a reader can lasso everything.
9
10
  *
10
11
  * Above it the WHOLE lasso is off: no surface is installed, and a one-line caption says why. It is one
11
12
  * number for every host; a host may pass its own through RenderOptions.lasso.rowCap.
@@ -35,8 +35,11 @@ export interface BicChartProps {
35
35
  /**
36
36
  * REMEMBER THE READER'S ZOOM AND PAN on a chart that calls d3.zoom(), across re-renders and, if
37
37
  * the hook's store keeps it, across a reload. Build it with createZoomMemoryHook() from
38
- * "@bicharts/chart-host/zoom-memory", once per chart (module scope or useMemo) with somewhere to
39
- * keep the view. Absent, nothing is remembered. See createChartHost's `zoomMemory`.
38
+ * "@bicharts/chart-host/zoom-memory" over one ZoomMemoryHost per chart (module scope, useRef or
39
+ * useMemo) with somewhere to keep the view. Absent (null or undefined), nothing is remembered. Host config:
40
+ * a hook that arrives after mount, or goes away, rebuilds the host; a change from one hook to another does
41
+ * not, and applies at the next rebuild (a new `code`, say), or at once if the component is remounted with a
42
+ * `key`. So a hook built inline in the props is harmless. See createChartHost's `zoomMemory`.
40
43
  */
41
44
  zoomMemory?: ChartHostConfig["zoomMemory"];
42
45
  /**
@@ -1,20 +1,23 @@
1
1
  import { type ZoomState } from "./zoomMemory";
2
2
  /**
3
3
  * WHAT A HOST SUPPLIES: where the remembered view lives, and whether it may outlive the session.
4
- * One per chart container, kept for as long as the container is, so it outlives a host that is destroyed
5
- * and re-created on the same element (a redraw on every resize, say).
4
+ * One per chart, kept for as long as the chart is on the page, so it outlives a host that is destroyed
5
+ * and re-created (on the same element for a redraw on every resize, say, or on a new one after a remount).
6
6
  */
7
7
  export interface ZoomMemoryHost {
8
8
  /** The in-session tier, always on: survives every re-render, never written anywhere. */
9
9
  liveZoomState: ZoomState | null;
10
10
  /** The key the watcher stamps on what it records (the latest render's). */
11
11
  zoomWatchKey: string;
12
- /** Whether the watcher's listeners are on zoomContainer (they attach once). */
12
+ /** Whether the watcher's listeners are on zoomContainer (they attach once per element, and come off when the
13
+ * host that drew into it is destroyed). */
13
14
  zoomWatchAttached: boolean;
14
15
  /** The pending settle timer, 0 when none. */
15
16
  zoomSettleTimer: number;
16
17
  /** The element the chart draws into; it outlives each render, so listeners attach to it once. Null until
17
- * known: createZoomMemoryHook's restore adopts the render's container then. */
18
+ * known: createZoomMemoryHook's restore adopts the render's container then, moves to a new one when a
19
+ * render arrives on a different element (a remount), and gives it back when the host is destroyed. An element
20
+ * set here beforehand is the page's choice: the hook watches it and neither replaces nor gives it back. */
18
21
  zoomContainer: Element | null;
19
22
  /** Is the persisted tier on NOW? Read on every restore and every write, so a change applies to the next. */
20
23
  rememberAcrossSessions(): boolean;
@@ -68,7 +71,13 @@ export interface ZoomMemoryHook {
68
71
  flush(): void;
69
72
  /** forgetZoom: drop the session view and a pending gesture. */
70
73
  forget(): void;
74
+ /** Let go of the watcher on `container`, the element a destroyed host drew into (after flush, which records a
75
+ * gesture still settling), so the next host handed this hook attaches afresh and a hook that is replaced leaves
76
+ * nothing on the element. Without `container`, wherever the watcher is. A watcher that has since moved to another
77
+ * element is left alone. Optional: a hook written by hand without it works as before. */
78
+ release?(container?: Element): void;
71
79
  }
72
- /** Bind the zoom memory to one host object. Build it once per container and hand the same hook to every
73
- * host created on that container, so the session tier outlives each one. */
80
+ /** Bind the zoom memory to one host object. Build it once per chart and hand the same hook to every
81
+ * host created for it, so the session tier outlives each one - on the same element, or on a new one
82
+ * after a remount, where the watcher moves with it. A host that is destroyed releases the watcher. */
74
83
  export declare function createZoomMemoryHook(host: ZoomMemoryHost): ZoomMemoryHook;
@@ -1,7 +1,7 @@
1
1
  /*! @bicharts/chart-host — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; Natural Earth and US Census/TIGER (public domain). Full text: NOTICE in this package. */
2
2
  import {
3
3
  B
4
- } from "./chunk-KQN5C7SZ.mjs";
4
+ } from "./chunk-NQFJHNMC.mjs";
5
5
  import "./chunk-GHODWOAL.mjs";
6
6
 
7
7
  // src/zoomMemory/zoomMemory.ts
@@ -192,15 +192,34 @@ function attachZoomWatcher(host, key) {
192
192
  captureCurrentZoom(host);
193
193
  }, SETTLE_MS);
194
194
  };
195
- for (const evt of ["wheel", "pointerup", "pointercancel", "click", "dblclick", "keyup"]) {
195
+ for (const evt of WATCHED_EVENTS) {
196
196
  try {
197
197
  ctr.addEventListener(evt, settle, true);
198
198
  } catch {
199
199
  }
200
200
  }
201
+ attachedWatchers.set(host, { ctr, settle });
201
202
  host.zoomWatchAttached = true;
202
203
  host.log("zoom-watcher-attached", {});
203
204
  }
205
+ var WATCHED_EVENTS = ["wheel", "pointerup", "pointercancel", "click", "dblclick", "keyup"];
206
+ var attachedWatchers = /* @__PURE__ */ new WeakMap();
207
+ var adoptedContainers = /* @__PURE__ */ new WeakMap();
208
+ function detachZoomWatcher(host) {
209
+ const w = attachedWatchers.get(host);
210
+ if (w) {
211
+ for (const evt of WATCHED_EVENTS) {
212
+ try {
213
+ w.ctr.removeEventListener(evt, w.settle, true);
214
+ } catch {
215
+ }
216
+ }
217
+ attachedWatchers.delete(host);
218
+ }
219
+ if (host.zoomSettleTimer) clearTimeout(host.zoomSettleTimer);
220
+ host.zoomSettleTimer = 0;
221
+ host.zoomWatchAttached = false;
222
+ }
204
223
  function captureCurrentZoom(host) {
205
224
  try {
206
225
  const key = host.zoomWatchKey;
@@ -242,17 +261,32 @@ function forgetZoom(host) {
242
261
  host.liveZoomState = null;
243
262
  }
244
263
  function createZoomMemoryHook(host) {
245
- return {
264
+ const hook = {
246
265
  keyFor: (code, width, height) => zoomStateKey(B(String(code ?? "")), width, height),
247
266
  capture: (d3) => captureZoomBehaviors(d3, host),
248
267
  restore: (container, capture, key, d3) => {
249
- if (!host.zoomContainer) host.zoomContainer = container;
268
+ const mine = adoptedContainers.get(host);
269
+ if (host.zoomContainer && host.zoomContainer === mine && mine !== container) {
270
+ detachZoomWatcher(host);
271
+ host.zoomContainer = null;
272
+ }
273
+ if (!host.zoomContainer) {
274
+ host.zoomContainer = container;
275
+ adoptedContainers.set(host, container);
276
+ }
250
277
  restoreZoomForRender(d3, host, capture, key);
251
278
  attachZoomWatcher(host, key);
252
279
  },
253
280
  flush: () => flushPendingZoom(host),
254
- forget: () => forgetZoom(host)
281
+ forget: () => forgetZoom(host),
282
+ release: (container) => {
283
+ if (container && attachedWatchers.get(host)?.ctr !== container) return;
284
+ detachZoomWatcher(host);
285
+ if (host.zoomContainer && host.zoomContainer === adoptedContainers.get(host)) host.zoomContainer = null;
286
+ adoptedContainers.delete(host);
287
+ }
255
288
  };
289
+ return hook;
256
290
  }
257
291
  export {
258
292
  attachZoomWatcher,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.6.90",
3
+ "version": "0.6.92",
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",