@bicharts/chart-host 0.6.86 → 0.6.88
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 +34 -0
- package/dist/adapters.mjs +3 -3
- package/dist/card.mjs +2 -2
- package/dist/{chunk-DTOGTKAJ.mjs → chunk-56IZWXW2.mjs} +1 -1
- package/dist/{chunk-MXL5SK7X.mjs → chunk-HXH6NIRV.mjs} +77 -14
- package/dist/{chunk-IURROJT7.mjs → chunk-IUXNCX7D.mjs} +1 -1
- package/dist/{chunk-QIRLWJBI.mjs → chunk-IZXVV74H.mjs} +2 -2
- package/dist/{chunk-2ZUUIZWL.mjs → chunk-KQN5C7SZ.mjs} +7 -0
- package/dist/{chunk-BQFG5HWZ.mjs → chunk-QHIPRAZK.mjs} +1 -1
- package/dist/{chunk-ZGQURBRJ.mjs → chunk-UBB5LNH4.mjs} +1 -1
- package/dist/{chunk-7HKVV5T5.mjs → chunk-XNS34AMM.mjs} +1 -1
- package/dist/{chunk-MR6TL7QZ.mjs → chunk-YDHXOIAI.mjs} +2 -0
- package/dist/{chunk-GG4U66E4.mjs → chunk-Z6OZDX4Z.mjs} +1 -1
- package/dist/index.mjs +15 -13
- package/dist/legend-fit.mjs +3 -3
- package/dist/page.mjs +4 -4
- package/dist/react.mjs +10 -8
- package/dist/testing.mjs +1 -1
- package/dist/types/contract.d.ts +4 -0
- package/dist/types/host.d.ts +9 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/react.d.ts +7 -0
- package/dist/types/zoomMemory/index.d.ts +2 -0
- package/dist/types/zoomMemory/zoomMemory.d.ts +59 -0
- package/dist/types/zoomMemory/zoomMemoryDom.d.ts +74 -0
- package/dist/zoom-memory.mjs +276 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -267,6 +267,16 @@ would show its selected lines at 38%, which the eye reads as dimmed. Such a char
|
|
|
267
267
|
painted at full opacity. Undeclared charts are unchanged, so alpha used as an encoding (a depth
|
|
268
268
|
cue, a hierarchy level) is never flattened by a fix meant for another chart.
|
|
269
269
|
|
|
270
|
+
**How far the rest dims, per chart.** `options.dimOpacity` (a share of full opacity, `0.01`-`1`;
|
|
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.
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
createChartHost(el, { code, data, options: { dimOpacity: 0.15 } });
|
|
278
|
+
```
|
|
279
|
+
|
|
270
280
|
**Which marks count as selected.** By default a mark is selected when *any* of the rows in its
|
|
271
281
|
`data-row-idx` list is selected, which is what lights a legend swatch together with its series. A
|
|
272
282
|
chart whose marks share rows declares the other rule by stamping
|
|
@@ -351,6 +361,30 @@ const report = await assertViewStateConformance(service, {
|
|
|
351
361
|
expect(report.skipped).toEqual([]); // nothing went unchecked
|
|
352
362
|
```
|
|
353
363
|
|
|
364
|
+
**A zoomed or panned view** is not in the bag: d3-zoom keeps it on the DOM node, and every redraw
|
|
365
|
+
(a resize, a cross-filter, a restyle) opens the chart at full extent again. `@bicharts/chart-host/zoom-memory`
|
|
366
|
+
remembers it. You supply where the view lives; the hook does the rest on every render:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
import { createZoomMemoryHook, type ZoomMemoryHost } from "@bicharts/chart-host/zoom-memory";
|
|
370
|
+
|
|
371
|
+
const zoomHost: ZoomMemoryHost = {
|
|
372
|
+
liveZoomState: null, zoomWatchKey: "", zoomWatchAttached: false, zoomSettleTimer: 0, zoomContainer: el,
|
|
373
|
+
rememberAcrossSessions: () => prefs.rememberZoom, // the persisted tier is on now?
|
|
374
|
+
readPersistedZoom: () => localStorage.getItem("chart-zoom"),
|
|
375
|
+
persistZoom: json => localStorage.setItem("chart-zoom", json), // "" forgets
|
|
376
|
+
log: () => {},
|
|
377
|
+
};
|
|
378
|
+
const zoomMemory = createZoomMemoryHook(zoomHost); // once per container, reused by every host on it
|
|
379
|
+
const host = createChartHost(el, { code, data, d3, zoomMemory });
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
The view survives every redraw in the session whatever the preference says, and a close and reopen
|
|
383
|
+
only while `rememberAcrossSessions()` is true. It is keyed by the chart's code and the viewport size
|
|
384
|
+
(to the nearest 32 px), so a regenerated chart or a real resize opens at full extent instead of
|
|
385
|
+
somewhere arbitrary. A chart with several zoomable panels is left alone. In React, pass the hook as
|
|
386
|
+
the `zoomMemory` prop of `<BicChart>`.
|
|
387
|
+
|
|
354
388
|
## Keeping a drawn chart readable inside its frame
|
|
355
389
|
|
|
356
390
|
A generated chart cannot know how wide its own legend text will come out, where its tick labels will
|
package/dist/adapters.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
censusHitBands,
|
|
4
4
|
ensureCrossfilterHitTargets,
|
|
5
5
|
hitBandFlag
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XNS34AMM.mjs";
|
|
7
7
|
import {
|
|
8
8
|
clickWasDrag,
|
|
9
9
|
collectD3GlyphCenters,
|
|
@@ -12,13 +12,13 @@ import {
|
|
|
12
12
|
markKeyOf,
|
|
13
13
|
rowIdxsFromMark,
|
|
14
14
|
selectionRuleOf
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-IUXNCX7D.mjs";
|
|
16
16
|
import {
|
|
17
17
|
AXIS_FILTER_CLASS,
|
|
18
18
|
LEGEND_MARK_CLASS,
|
|
19
19
|
MARK_CLASS,
|
|
20
20
|
ROW_IDX_ATTR
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-YDHXOIAI.mjs";
|
|
22
22
|
import "./chunk-GHODWOAL.mjs";
|
|
23
23
|
|
|
24
24
|
// src/adapters/types.ts
|
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-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-Z6OZDX4Z.mjs";
|
|
5
|
+
import "./chunk-KQN5C7SZ.mjs";
|
|
6
6
|
import "./chunk-GHODWOAL.mjs";
|
|
7
7
|
|
|
8
8
|
// src/selectionCardChrome.ts
|
|
@@ -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
|
LASSO_KNOB_KEY
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IZXVV74H.mjs";
|
|
5
5
|
|
|
6
6
|
// src/filterScope.ts
|
|
7
7
|
function viewToken(view) {
|
|
@@ -2,18 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
LASSO_OUTLINE_CLASS,
|
|
4
4
|
installLasso
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import {
|
|
7
|
-
Fn,
|
|
8
|
-
J3,
|
|
9
|
-
X,
|
|
10
|
-
Xn,
|
|
11
|
-
Yn
|
|
12
|
-
} from "./chunk-2ZUUIZWL.mjs";
|
|
5
|
+
} from "./chunk-IZXVV74H.mjs";
|
|
13
6
|
import {
|
|
14
7
|
censusHitBands,
|
|
15
8
|
ensureCrossfilterHitTargets
|
|
16
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XNS34AMM.mjs";
|
|
17
10
|
import {
|
|
18
11
|
clearPress,
|
|
19
12
|
clickWasDrag,
|
|
@@ -27,12 +20,19 @@ import {
|
|
|
27
20
|
selectionRuleOf,
|
|
28
21
|
trackPress,
|
|
29
22
|
validRowSet
|
|
30
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-IUXNCX7D.mjs";
|
|
31
24
|
import {
|
|
32
25
|
clipWindowOf,
|
|
33
26
|
fitRenderedChart,
|
|
34
27
|
unpinScrolledAxis
|
|
35
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-QHIPRAZK.mjs";
|
|
29
|
+
import {
|
|
30
|
+
Fn,
|
|
31
|
+
J3,
|
|
32
|
+
X,
|
|
33
|
+
Xn,
|
|
34
|
+
Yn
|
|
35
|
+
} from "./chunk-KQN5C7SZ.mjs";
|
|
36
36
|
import {
|
|
37
37
|
NA_ISO3,
|
|
38
38
|
NA_LAT_MAX,
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
CONTAINER_SLOT_XF_CLEAR,
|
|
61
61
|
CONTROL_CLASS,
|
|
62
62
|
DIM_OPACITY_DEFAULT,
|
|
63
|
+
DIM_OPACITY_MIN,
|
|
63
64
|
DIM_OPACITY_VAR,
|
|
64
65
|
FLIP_DIRECTION_DEFAULT,
|
|
65
66
|
FLIP_MODE_DEFAULT,
|
|
@@ -78,7 +79,7 @@ import {
|
|
|
78
79
|
XFILTER_REFRESH_EVENT,
|
|
79
80
|
chartOwnsTimeline,
|
|
80
81
|
periodTickSuppressesFeedback
|
|
81
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-YDHXOIAI.mjs";
|
|
82
83
|
|
|
83
84
|
// src/labelContrast.ts
|
|
84
85
|
function parseRGBA(c) {
|
|
@@ -608,6 +609,12 @@ function resolveOptions(p) {
|
|
|
608
609
|
cultureCode: p.cultureCode,
|
|
609
610
|
allowTooltips: p.allowTooltips,
|
|
610
611
|
noDataText: p.noDataText,
|
|
612
|
+
// A finite number is clamped to DIM_OPACITY_MIN-1; absent, blank or junk stays undefined so the stylesheet - and a
|
|
613
|
+
// page's own CSS override - still decides. Never defaulted here: that would override every page's CSS.
|
|
614
|
+
dimOpacity: (() => {
|
|
615
|
+
const n = numberOr(p.dimOpacity, NaN);
|
|
616
|
+
return Number.isFinite(n) ? clamp(n, DIM_OPACITY_MIN, 1) : void 0;
|
|
617
|
+
})(),
|
|
611
618
|
onSelect: p.onSelect,
|
|
612
619
|
// Control gestures (1.10.0). Passed straight through like onSelect: the host owns what a gesture MEANS,
|
|
613
620
|
// and a host that supplies nothing leaves every control behaving exactly as before.
|
|
@@ -2015,6 +2022,7 @@ function createChartHost(container, config) {
|
|
|
2015
2022
|
const doc = container.ownerDocument;
|
|
2016
2023
|
const win = doc?.defaultView ?? globalThis;
|
|
2017
2024
|
const d3 = config.d3 ?? globalThis.d3 ?? win?.d3;
|
|
2025
|
+
const chartD3 = writableD3(d3);
|
|
2018
2026
|
let data = config.data;
|
|
2019
2027
|
{
|
|
2020
2028
|
const stamped = config.data?.meta?.hostContract;
|
|
@@ -2076,6 +2084,7 @@ function createChartHost(container, config) {
|
|
|
2076
2084
|
p.catch(() => {
|
|
2077
2085
|
});
|
|
2078
2086
|
};
|
|
2087
|
+
let dimVarSet = false;
|
|
2079
2088
|
let current = null;
|
|
2080
2089
|
let activeTickEl = null;
|
|
2081
2090
|
let selectionIsPeriod = false;
|
|
@@ -2152,6 +2161,14 @@ function createChartHost(container, config) {
|
|
|
2152
2161
|
const active = !!sel && sel.length > 0;
|
|
2153
2162
|
const dim = active && !selectionIsPeriod;
|
|
2154
2163
|
container.classList?.[dim ? "add" : "remove"](SELECTION_ACTIVE_CLASS);
|
|
2164
|
+
const want = resolved.dimOpacity;
|
|
2165
|
+
if (typeof want === "number") {
|
|
2166
|
+
container.style?.setProperty(DIM_OPACITY_VAR, String(want));
|
|
2167
|
+
dimVarSet = true;
|
|
2168
|
+
} else if (dimVarSet) {
|
|
2169
|
+
container.style?.removeProperty(DIM_OPACITY_VAR);
|
|
2170
|
+
dimVarSet = false;
|
|
2171
|
+
}
|
|
2155
2172
|
const lit = container.querySelectorAll(`.${ACTIVE_TICK_CLASS}`);
|
|
2156
2173
|
for (const t of Array.from(lit)) t.classList?.remove(ACTIVE_TICK_CLASS);
|
|
2157
2174
|
const tickStillDrawn = !!activeTickEl && (typeof container.contains === "function" ? container.contains(activeTickEl) : activeTickEl.isConnected !== false);
|
|
@@ -2285,7 +2302,24 @@ function createChartHost(container, config) {
|
|
|
2285
2302
|
}
|
|
2286
2303
|
}
|
|
2287
2304
|
};
|
|
2305
|
+
const zoomMemory = config.zoomMemory ?? null;
|
|
2306
|
+
let zoomCap = null;
|
|
2307
|
+
let zoomKey = "";
|
|
2308
|
+
const flushZoom = () => {
|
|
2309
|
+
if (zoomMemory) {
|
|
2310
|
+
try {
|
|
2311
|
+
zoomMemory.flush();
|
|
2312
|
+
} catch {
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
};
|
|
2288
2316
|
const runPostRenderPasses = () => {
|
|
2317
|
+
if (zoomMemory && zoomCap) {
|
|
2318
|
+
try {
|
|
2319
|
+
zoomMemory.restore(container, zoomCap, zoomKey, chartD3);
|
|
2320
|
+
} catch {
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2289
2323
|
try {
|
|
2290
2324
|
const r = scopeChartIds(container, idScope);
|
|
2291
2325
|
if (r.collisions.length && !warnedIds) {
|
|
@@ -2404,10 +2438,11 @@ function createChartHost(container, config) {
|
|
|
2404
2438
|
const seq2 = ++renderSeq;
|
|
2405
2439
|
stopAnim();
|
|
2406
2440
|
disposeLasso();
|
|
2441
|
+
flushZoom();
|
|
2407
2442
|
container.innerHTML = "";
|
|
2408
2443
|
if (!renderFn) {
|
|
2409
2444
|
if (!config.code) throw new Error("createChartHost: provide code or renderFn");
|
|
2410
|
-
renderFn = compileRenderFn(config.code, win, doc,
|
|
2445
|
+
renderFn = compileRenderFn(config.code, win, doc, chartD3);
|
|
2411
2446
|
}
|
|
2412
2447
|
if (resolved.geo === void 0 && config.geoKind) {
|
|
2413
2448
|
const g = config.geoProvider?.(config.geoKind) ?? geoFromCache(config.geoKind);
|
|
@@ -2427,8 +2462,28 @@ function createChartHost(container, config) {
|
|
|
2427
2462
|
if (resolved.geoPointDest === void 0 && data.geoPointDest) resolved = { ...resolved, geoPointDest: data.geoPointDest };
|
|
2428
2463
|
if (resolved.dateCellsAreUtcDays === void 0 && data.dateCellsAreUtcDays) resolved = { ...resolved, dateCellsAreUtcDays: true };
|
|
2429
2464
|
let outcome;
|
|
2465
|
+
zoomCap = null;
|
|
2466
|
+
if (zoomMemory && config.code && !config.renderFn) {
|
|
2467
|
+
try {
|
|
2468
|
+
zoomKey = zoomMemory.keyFor(
|
|
2469
|
+
config.code,
|
|
2470
|
+
resolved.width ?? container.clientWidth,
|
|
2471
|
+
resolved.height ?? container.clientHeight
|
|
2472
|
+
);
|
|
2473
|
+
zoomCap = zoomMemory.capture(chartD3);
|
|
2474
|
+
} catch {
|
|
2475
|
+
zoomCap = null;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2430
2478
|
try {
|
|
2431
|
-
|
|
2479
|
+
try {
|
|
2480
|
+
outcome = renderFn(container, { columns: data.columns, rows: data.rows }, resolved);
|
|
2481
|
+
} finally {
|
|
2482
|
+
try {
|
|
2483
|
+
zoomCap?.release();
|
|
2484
|
+
} catch {
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2432
2487
|
} catch (err) {
|
|
2433
2488
|
const fatal = renderFailure(err);
|
|
2434
2489
|
if (fatal) {
|
|
@@ -2541,8 +2596,16 @@ function createChartHost(container, config) {
|
|
|
2541
2596
|
clearPress(container);
|
|
2542
2597
|
disposeLasso();
|
|
2543
2598
|
subs.clear();
|
|
2599
|
+
if (dimVarSet) {
|
|
2600
|
+
try {
|
|
2601
|
+
container.style?.removeProperty(DIM_OPACITY_VAR);
|
|
2602
|
+
} catch {
|
|
2603
|
+
}
|
|
2604
|
+
dimVarSet = false;
|
|
2605
|
+
}
|
|
2544
2606
|
annotationLayer.destroy();
|
|
2545
2607
|
unpinScrolledAxis(container);
|
|
2608
|
+
flushZoom();
|
|
2546
2609
|
container.innerHTML = "";
|
|
2547
2610
|
}
|
|
2548
2611
|
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
gestureWasDrag,
|
|
5
5
|
isInsideControl,
|
|
6
6
|
rowIdxsFromMark
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IUXNCX7D.mjs";
|
|
8
8
|
import {
|
|
9
9
|
AXIS_FILTER_CLASS,
|
|
10
10
|
CONTROL_CLASS,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
MARK_CLASS,
|
|
13
13
|
ROW_IDX_ATTR,
|
|
14
14
|
SNAPSHOT_SVG_ATTR
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-YDHXOIAI.mjs";
|
|
16
16
|
|
|
17
17
|
// src/lasso.ts
|
|
18
18
|
var LASSO_ROW_CAP = 5e3;
|
|
@@ -841,6 +841,12 @@ var N = /[^\p{L}\p{M}\p{N}\x00-\x7F]/gu;
|
|
|
841
841
|
function f(n) {
|
|
842
842
|
return n ? String(n).normalize("NFC").replace(F2, "$1 $2").replace(S, " ").replace(N, " ").toLowerCase().split(/\s+/).filter((e) => e.length > 0) : [];
|
|
843
843
|
}
|
|
844
|
+
function B2(n) {
|
|
845
|
+
if (!n) return 0;
|
|
846
|
+
let e = 0xcbf29ce484222325n, t = 0x100000001b3n, r2 = e;
|
|
847
|
+
for (let o2 = 0; o2 < n.length; o2++) r2 ^= BigInt(n.charCodeAt(o2)), r2 = r2 * t & 0xFFFFFFFFFFFFFFFFn;
|
|
848
|
+
return Number(r2 & 0x1FFFFFFFFFFFFFn);
|
|
849
|
+
}
|
|
844
850
|
function Z(n) {
|
|
845
851
|
let e = Date.parse(n);
|
|
846
852
|
if (isNaN(e)) return null;
|
|
@@ -1275,6 +1281,7 @@ var Vr2 = Object.freeze(["open", "generate", "version", "rebind", "data", "sampl
|
|
|
1275
1281
|
var Ae = 2 ** 31 - 1;
|
|
1276
1282
|
|
|
1277
1283
|
export {
|
|
1284
|
+
B2 as B,
|
|
1278
1285
|
Z,
|
|
1279
1286
|
g2 as g,
|
|
1280
1287
|
X2 as X,
|
|
@@ -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
|
SNAPSHOT_SVG_ATTR
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YDHXOIAI.mjs";
|
|
5
5
|
|
|
6
6
|
// src/fit.ts
|
|
7
7
|
var SCROLL_SLACK_PX = 8;
|
|
@@ -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
|
SNAPSHOT_SVG_ATTR
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YDHXOIAI.mjs";
|
|
5
5
|
|
|
6
6
|
// src/snapshot.ts
|
|
7
7
|
var DEFAULT_TIMEOUT_MS = 2e3;
|
|
@@ -34,6 +34,7 @@ var MARK_SELECTED_CLASS = "lch-mark-selected";
|
|
|
34
34
|
var ACTIVE_TICK_CLASS = "lch-tick-active";
|
|
35
35
|
var DIM_OPACITY_VAR = "--lch-dim-opacity";
|
|
36
36
|
var DIM_OPACITY_DEFAULT = 0.25;
|
|
37
|
+
var DIM_OPACITY_MIN = 0.01;
|
|
37
38
|
function chartOwnsTimeline(container) {
|
|
38
39
|
return !!container && (!!container[CONTAINER_SLOT_XF_CLEAR] || !!container[CONTAINER_SLOT_ANIM_STOP]);
|
|
39
40
|
}
|
|
@@ -97,6 +98,7 @@ export {
|
|
|
97
98
|
ACTIVE_TICK_CLASS,
|
|
98
99
|
DIM_OPACITY_VAR,
|
|
99
100
|
DIM_OPACITY_DEFAULT,
|
|
101
|
+
DIM_OPACITY_MIN,
|
|
100
102
|
chartOwnsTimeline,
|
|
101
103
|
periodTickSuppressesFeedback,
|
|
102
104
|
ANIM_PLAY_SPEED_DEFAULT,
|
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
formatSourceDateFor,
|
|
6
6
|
isExcelDateFormat,
|
|
7
7
|
normaliseAggregation
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-Z6OZDX4Z.mjs";
|
|
9
9
|
import {
|
|
10
10
|
captureSvgSnapshot,
|
|
11
11
|
chartSvgOf,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
svgNaturalSize,
|
|
16
16
|
svgToDataUrl,
|
|
17
17
|
tableHostOf
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-UBB5LNH4.mjs";
|
|
19
19
|
import {
|
|
20
20
|
ANNOTATION_CLASS,
|
|
21
21
|
ANNOTATION_LAYER_CLASS,
|
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
toRGBA,
|
|
98
98
|
toSourceRows,
|
|
99
99
|
valuePlacementFlag
|
|
100
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-HXH6NIRV.mjs";
|
|
101
101
|
import {
|
|
102
102
|
LASSO_AXES_SLOT,
|
|
103
103
|
LASSO_COUNT_CLASS,
|
|
@@ -122,18 +122,13 @@ import {
|
|
|
122
122
|
lassoVerdict,
|
|
123
123
|
pointInShape,
|
|
124
124
|
registerLassoAction
|
|
125
|
-
} from "./chunk-
|
|
126
|
-
import {
|
|
127
|
-
_3,
|
|
128
|
-
l,
|
|
129
|
-
o
|
|
130
|
-
} from "./chunk-2ZUUIZWL.mjs";
|
|
125
|
+
} from "./chunk-IZXVV74H.mjs";
|
|
131
126
|
import {
|
|
132
127
|
MIN_HIT_BAND_PX,
|
|
133
128
|
censusHitBands,
|
|
134
129
|
ensureCrossfilterHitTargets,
|
|
135
130
|
hitBandFlag
|
|
136
|
-
} from "./chunk-
|
|
131
|
+
} from "./chunk-XNS34AMM.mjs";
|
|
137
132
|
import {
|
|
138
133
|
CONTAINER_SLOT_PRESS_AT,
|
|
139
134
|
DRAG_NOT_CLICK_PX,
|
|
@@ -158,7 +153,7 @@ import {
|
|
|
158
153
|
selectionRuleOf,
|
|
159
154
|
trackPress,
|
|
160
155
|
validRowSet
|
|
161
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-IUXNCX7D.mjs";
|
|
162
157
|
import {
|
|
163
158
|
AXIS_PIN_BAND_PAD_PX,
|
|
164
159
|
AXIS_PIN_MAX_BAND_FRACTION,
|
|
@@ -184,7 +179,12 @@ import {
|
|
|
184
179
|
scrollFitFor,
|
|
185
180
|
svgInkReach,
|
|
186
181
|
unpinScrolledAxis
|
|
187
|
-
} from "./chunk-
|
|
182
|
+
} from "./chunk-QHIPRAZK.mjs";
|
|
183
|
+
import {
|
|
184
|
+
_3,
|
|
185
|
+
l,
|
|
186
|
+
o
|
|
187
|
+
} from "./chunk-KQN5C7SZ.mjs";
|
|
188
188
|
import "./chunk-A2GMXZP7.mjs";
|
|
189
189
|
import {
|
|
190
190
|
ACTIVE_TICK_CLASS,
|
|
@@ -209,6 +209,7 @@ import {
|
|
|
209
209
|
CONTAINER_SLOT_XF_CLEAR,
|
|
210
210
|
CONTROL_CLASS,
|
|
211
211
|
DIM_OPACITY_DEFAULT,
|
|
212
|
+
DIM_OPACITY_MIN,
|
|
212
213
|
DIM_OPACITY_VAR,
|
|
213
214
|
FLIP_DIRECTION_DEFAULT,
|
|
214
215
|
FLIP_INTERVAL_MAX_MS,
|
|
@@ -236,7 +237,7 @@ import {
|
|
|
236
237
|
chartOwnsTimeline,
|
|
237
238
|
periodTickSuppressesFeedback,
|
|
238
239
|
withoutViewOnlyKeys
|
|
239
|
-
} from "./chunk-
|
|
240
|
+
} from "./chunk-YDHXOIAI.mjs";
|
|
240
241
|
import "./chunk-GHODWOAL.mjs";
|
|
241
242
|
|
|
242
243
|
// src/optionsVocabulary.ts
|
|
@@ -2161,6 +2162,7 @@ export {
|
|
|
2161
2162
|
DEFAULT_FILTER_MATCH_STYLE,
|
|
2162
2163
|
DEFAULT_MODEL_SUFFIX,
|
|
2163
2164
|
DIM_OPACITY_DEFAULT,
|
|
2165
|
+
DIM_OPACITY_MIN,
|
|
2164
2166
|
DIM_OPACITY_VAR,
|
|
2165
2167
|
DRAG_NOT_CLICK_PX,
|
|
2166
2168
|
DUPLICATE_GENERATE_WINDOW_MS,
|
package/dist/legend-fit.mjs
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
chartSvgOf,
|
|
4
4
|
tableHostOf
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UBB5LNH4.mjs";
|
|
6
6
|
import {
|
|
7
7
|
FIT_CONTENT_SELECTOR,
|
|
8
8
|
ctmScaleOf,
|
|
9
9
|
isPhantomBox
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-QHIPRAZK.mjs";
|
|
11
|
+
import "./chunk-YDHXOIAI.mjs";
|
|
12
12
|
import "./chunk-GHODWOAL.mjs";
|
|
13
13
|
|
|
14
14
|
// src/legendFit/legendReconcile.ts
|
package/dist/page.mjs
CHANGED
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
readControls,
|
|
15
15
|
viewFromToken,
|
|
16
16
|
viewToken
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-56IZWXW2.mjs";
|
|
18
|
+
import "./chunk-IZXVV74H.mjs";
|
|
19
|
+
import "./chunk-IUXNCX7D.mjs";
|
|
20
|
+
import "./chunk-YDHXOIAI.mjs";
|
|
21
21
|
import "./chunk-GHODWOAL.mjs";
|
|
22
22
|
export {
|
|
23
23
|
LINKED_HOVER_CLASS,
|
package/dist/react.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
payloadRowReader,
|
|
14
14
|
viewFromToken,
|
|
15
15
|
viewToken
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-56IZWXW2.mjs";
|
|
17
17
|
import {
|
|
18
18
|
assembleD3,
|
|
19
19
|
createChartGroup,
|
|
@@ -22,14 +22,14 @@ import {
|
|
|
22
22
|
loadGeo,
|
|
23
23
|
syncMemberSelection,
|
|
24
24
|
toSourceRows
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
25
|
+
} from "./chunk-HXH6NIRV.mjs";
|
|
26
|
+
import "./chunk-IZXVV74H.mjs";
|
|
27
|
+
import "./chunk-XNS34AMM.mjs";
|
|
28
|
+
import "./chunk-IUXNCX7D.mjs";
|
|
29
|
+
import "./chunk-QHIPRAZK.mjs";
|
|
30
|
+
import "./chunk-KQN5C7SZ.mjs";
|
|
31
31
|
import "./chunk-A2GMXZP7.mjs";
|
|
32
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-YDHXOIAI.mjs";
|
|
33
33
|
import "./chunk-GHODWOAL.mjs";
|
|
34
34
|
|
|
35
35
|
// src/react.tsx
|
|
@@ -67,6 +67,7 @@ function BicChart(props) {
|
|
|
67
67
|
d3,
|
|
68
68
|
geoKind,
|
|
69
69
|
viewState,
|
|
70
|
+
zoomMemory,
|
|
70
71
|
labelContrast,
|
|
71
72
|
onLabelContrast,
|
|
72
73
|
layoutPass,
|
|
@@ -150,6 +151,7 @@ function BicChart(props) {
|
|
|
150
151
|
d3,
|
|
151
152
|
geoKind,
|
|
152
153
|
viewState,
|
|
154
|
+
zoomMemory,
|
|
153
155
|
labelContrast,
|
|
154
156
|
layoutPass,
|
|
155
157
|
onLabelContrast: (r) => onLabelContrastRef.current?.(r),
|
package/dist/testing.mjs
CHANGED
package/dist/types/contract.d.ts
CHANGED
|
@@ -72,6 +72,7 @@ export declare const MARK_SELECTED_CLASS = "lch-mark-selected";
|
|
|
72
72
|
export declare const ACTIVE_TICK_CLASS = "lch-tick-active";
|
|
73
73
|
export declare const DIM_OPACITY_VAR = "--lch-dim-opacity";
|
|
74
74
|
export declare const DIM_OPACITY_DEFAULT = 0.25;
|
|
75
|
+
export declare const DIM_OPACITY_MIN = 0.01;
|
|
75
76
|
export declare function chartOwnsTimeline(container: any): boolean;
|
|
76
77
|
export declare function periodTickSuppressesFeedback(isAxisTick: boolean, ownsTimeline: boolean): boolean;
|
|
77
78
|
export declare const ANIM_PLAY_SPEED_DEFAULT = 1000;
|
|
@@ -159,6 +160,9 @@ export interface RenderOptions {
|
|
|
159
160
|
cultureCode?: string;
|
|
160
161
|
allowTooltips?: boolean;
|
|
161
162
|
noDataText?: string;
|
|
163
|
+
/** How far a selection dims the marks outside it, as a share of full opacity (DIM_OPACITY_MIN-1). Absent: the
|
|
164
|
+
* stylesheet decides - DIM_OPACITY_DEFAULT, or whatever the page sets on --lch-dim-opacity. */
|
|
165
|
+
dimOpacity?: number;
|
|
162
166
|
animAutoPlay?: boolean;
|
|
163
167
|
animPlaySpeedMs?: number;
|
|
164
168
|
animLoopDelaySec?: number;
|
package/dist/types/host.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { type FitRenderedChartOptions, type FitRenderedChartResult } from "./fit
|
|
|
18
18
|
import { type LabelContrastOptions, type LabelContrastReport } from "./labelContrastDom";
|
|
19
19
|
import { type IdScopeReport } from "./idScope";
|
|
20
20
|
import { type MarkAnnotation, type AnnotationReport } from "./annotations";
|
|
21
|
+
import type { ZoomMemoryHook } from "./zoomMemory/zoomMemoryDom";
|
|
21
22
|
export type RenderFn = (container: HTMLElement, data: any, options: RenderOptions) => void;
|
|
22
23
|
/**
|
|
23
24
|
* THE SESSION PROVIDER — what every host got hard-coded before contract 1.6.0, now named.
|
|
@@ -73,6 +74,14 @@ export interface ChartHostConfig {
|
|
|
73
74
|
* session store parked on the container — the behaviour every host had before 1.6.0.
|
|
74
75
|
* A raw `options.setUiState` still wins over this, so an existing caller is untouched. */
|
|
75
76
|
viewState?: ViewStateProvider;
|
|
77
|
+
/**
|
|
78
|
+
* REMEMBER THE READER'S ZOOM AND PAN on a chart that calls d3.zoom(), across every re-render (and across a
|
|
79
|
+
* close and reopen, if the host's store says so). Build it with createZoomMemoryHook() from
|
|
80
|
+
* "@bicharts/chart-host/zoom-memory", ONCE per container, and hand the same hook to every host created on
|
|
81
|
+
* that container. Absent (the default), d3.zoom is never wrapped and nothing is listened for. It applies to a
|
|
82
|
+
* chart compiled from `code` only: a precompiled `renderFn` closed over its own d3.
|
|
83
|
+
*/
|
|
84
|
+
zoomMemory?: ZoomMemoryHook | null;
|
|
76
85
|
/** D3 v7 instance handed to the compiled code. Default: (globalThis as any).d3. */
|
|
77
86
|
d3?: any;
|
|
78
87
|
/** Attach geometry for this geoKind as options.geo (choropleths / basemaps). Resolved
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { HOST_CONTRACT_VERSION, GEO_POINT_PRECISIONS, MARK_CLASS, LEGEND_MARK_CLASS, AXIS_FILTER_CLASS, ROW_IDX_ATTR, LIFT_SELECTED_CLASS, CONTROL_CLASS, NO_ROW_MARKS_ATTR, SELECTION_RULE_ATTR, SELECTION_RULE_CLICKED_MARKS, MARK_KEY_ATTR, CONTAINER_SLOT_CLICKED_MARKS, SNAPSHOT_SVG_ATTR, XFILTER_REFRESH_EVENT, CONTAINER_SLOT_ANIM_STOP, CONTAINER_SLOT_XF_CLEAR, CONTAINER_SLOT_INITIAL_XF_MARK, CONTAINER_SLOT_UI_STATE, VIEW_ONLY_UI_STATE_KEYS, withoutViewOnlyKeys, HOST_CONTAINER_CLASS, SELECTION_ACTIVE_CLASS, MARK_SELECTED_CLASS, ACTIVE_TICK_CLASS, DIM_OPACITY_VAR, DIM_OPACITY_DEFAULT, chartOwnsTimeline, periodTickSuppressesFeedback, ANIM_PLAY_SPEED_DEFAULT, ANIM_PLAY_SPEED_MIN, ANIM_PLAY_SPEED_MAX, ANIM_LOOP_DELAY_DEFAULT, ANIM_LOOP_DELAY_MIN, ANIM_MAX_IDEAL_FRAMES_DEFAULT, ANIM_MAX_IDEAL_FRAMES_MIN, ANIM_MAX_IDEAL_FRAMES_MAX, ANIMATION_OPTION_KEYS, COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT, COLOR_SCALE_SELF_CLAMP_PCT_MIN, COLOR_SCALE_SELF_CLAMP_PCT_MAX, FLIP_MODE_DEFAULT, FLIP_INTERVAL_MIN_MS, FLIP_INTERVAL_MAX_MS, FLIP_DIRECTION_DEFAULT, APPROXIMATE_POSITIONS_DEFAULT, VALUE_AXIS_BASELINE_DEFAULT, SEASONAL_MARKERS_DEFAULT, MAX_MAP_POINTS_DEFAULT, type GeoPointPrecision, type GeoMapKind, type TimelineStyle, type FlipMode, type FlipDirection, type ApproximatePositions, type ValueAxisBaseline, type SeasonalMarkers, type ColorScaleScope, type RenderOptions, type ViewStateProvider, type ChartControlChange, type XfilterRefreshDetail, } from "./contract";
|
|
1
|
+
export { HOST_CONTRACT_VERSION, GEO_POINT_PRECISIONS, MARK_CLASS, LEGEND_MARK_CLASS, AXIS_FILTER_CLASS, ROW_IDX_ATTR, LIFT_SELECTED_CLASS, CONTROL_CLASS, NO_ROW_MARKS_ATTR, SELECTION_RULE_ATTR, SELECTION_RULE_CLICKED_MARKS, MARK_KEY_ATTR, CONTAINER_SLOT_CLICKED_MARKS, SNAPSHOT_SVG_ATTR, XFILTER_REFRESH_EVENT, CONTAINER_SLOT_ANIM_STOP, CONTAINER_SLOT_XF_CLEAR, CONTAINER_SLOT_INITIAL_XF_MARK, CONTAINER_SLOT_UI_STATE, VIEW_ONLY_UI_STATE_KEYS, withoutViewOnlyKeys, HOST_CONTAINER_CLASS, SELECTION_ACTIVE_CLASS, MARK_SELECTED_CLASS, ACTIVE_TICK_CLASS, DIM_OPACITY_VAR, DIM_OPACITY_DEFAULT, DIM_OPACITY_MIN, chartOwnsTimeline, periodTickSuppressesFeedback, ANIM_PLAY_SPEED_DEFAULT, ANIM_PLAY_SPEED_MIN, ANIM_PLAY_SPEED_MAX, ANIM_LOOP_DELAY_DEFAULT, ANIM_LOOP_DELAY_MIN, ANIM_MAX_IDEAL_FRAMES_DEFAULT, ANIM_MAX_IDEAL_FRAMES_MIN, ANIM_MAX_IDEAL_FRAMES_MAX, ANIMATION_OPTION_KEYS, COLOR_SCALE_SELF_CLAMP_PCT_DEFAULT, COLOR_SCALE_SELF_CLAMP_PCT_MIN, COLOR_SCALE_SELF_CLAMP_PCT_MAX, FLIP_MODE_DEFAULT, FLIP_INTERVAL_MIN_MS, FLIP_INTERVAL_MAX_MS, FLIP_DIRECTION_DEFAULT, APPROXIMATE_POSITIONS_DEFAULT, VALUE_AXIS_BASELINE_DEFAULT, SEASONAL_MARKERS_DEFAULT, MAX_MAP_POINTS_DEFAULT, type GeoPointPrecision, type GeoMapKind, type TimelineStyle, type FlipMode, type FlipDirection, type ApproximatePositions, type ValueAxisBaseline, type SeasonalMarkers, type ColorScaleScope, type RenderOptions, type ViewStateProvider, type ChartControlChange, type XfilterRefreshDetail, } from "./contract";
|
|
2
2
|
export { resolveOptions, type ResolveOptionsInput } from "./defaults";
|
|
3
3
|
export { OPTIONS_VOCABULARY, LIVE_OPTION_KEYS, MAX_MAP_POINTS_MIN, MAX_MAP_POINTS_MAX, LIMIT_COUNT_MAX, wireLimitCount, codeReadsOption, type OptionVocabularyEntry, type OptionTiming, type OptionWire, type OptionKey, } from "./optionsVocabulary";
|
|
4
4
|
export { loadGeo, geoFromCache, registerGeo, registerGeoAsset, geoAssetFor, clearGeoCache, type GeoAssetName, } from "./geoLazy";
|
package/dist/types/react.d.ts
CHANGED
|
@@ -32,6 +32,13 @@ export interface BicChartProps {
|
|
|
32
32
|
* default could pick between them, which is why this is a prop rather than a flag.
|
|
33
33
|
*/
|
|
34
34
|
viewState?: ChartHostConfig["viewState"];
|
|
35
|
+
/**
|
|
36
|
+
* REMEMBER THE READER'S ZOOM AND PAN on a chart that calls d3.zoom(), across re-renders and, if
|
|
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`.
|
|
40
|
+
*/
|
|
41
|
+
zoomMemory?: ChartHostConfig["zoomMemory"];
|
|
35
42
|
/**
|
|
36
43
|
* CAN THE LABELS ON THE MARKS BE READ? Default on: after every render a label sitting on a
|
|
37
44
|
* mark it cannot be read against is recoloured black or white. Pass `false` for a page that
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { zoomStateKey, viewportBucket, isUsableTransform, isIdentity, transformsDiffer, serializeZoomState, parseZoomState, isZoomStateUsable, readNodeTransform, findZoomedNodes, soleZoomedNode, type ZoomTransform, type ZoomState, } from "./zoomMemory";
|
|
2
|
+
export { captureZoomBehaviors, restoreZoomForRender, attachZoomWatcher, captureCurrentZoom, flushPendingZoom, forgetZoom, createZoomMemoryHook, type ZoomMemoryHost, type ZoomCapture, type ZoomMemoryHook, } from "./zoomMemoryDom";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** A d3-zoom transform, reduced to the three numbers that define it. */
|
|
2
|
+
export interface ZoomTransform {
|
|
3
|
+
k: number;
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
}
|
|
7
|
+
/** What gets persisted: the transform plus what it was captured against. */
|
|
8
|
+
export interface ZoomState extends ZoomTransform {
|
|
9
|
+
/** Identity of the chart + viewport this transform is meaningful for. */
|
|
10
|
+
key: string;
|
|
11
|
+
}
|
|
12
|
+
/** Identity is not identity across a resize (see viewportBucket) or a regeneration. */
|
|
13
|
+
export declare function zoomStateKey(codeHash: string | number, width: number, height: number): string;
|
|
14
|
+
/** Round a dimension so ordinary 1-2px reflow jitter does not discard a good transform,
|
|
15
|
+
* while a real resize (which changes the fitted frame the transform was chosen against)
|
|
16
|
+
* does. 32px is a bucket the reader cannot hit by accident and cannot miss on purpose. */
|
|
17
|
+
export declare function viewportBucket(px: number): number;
|
|
18
|
+
/** A transform we are willing to APPLY.
|
|
19
|
+
*
|
|
20
|
+
* A PERSISTED TRANSFORM IS UNTRUSTED: it round-trips through a saved file that a person can hand-edit,
|
|
21
|
+
* and it was captured against a geometry that may no longer exist. The floor here is only "these are
|
|
22
|
+
* three real numbers and the scale is not nonsense".
|
|
23
|
+
*
|
|
24
|
+
* NOTE ON WHO CLAMPS. `zoom.transform` does NOT constrain - d3-zoom applies `constrain()` in its
|
|
25
|
+
* wheel/drag/touch handlers and then assigns the result, but the programmatic `zoom.transform` path sets
|
|
26
|
+
* `__zoom` to exactly what it is given. So the host must clamp before applying, and it does it with the
|
|
27
|
+
* BEHAVIOR'S OWN accessors (scaleExtent(), extent(), translateExtent(), constrain()) rather than
|
|
28
|
+
* re-deriving the limits here - see constrainToBehavior in zoomMemoryDom.ts. That keeps one source of
|
|
29
|
+
* truth: the chart that built the zoom owns what a legal view is, and the host only asks it. */
|
|
30
|
+
export declare function isUsableTransform(t: unknown): t is ZoomTransform;
|
|
31
|
+
/** k=1 with no offset is the identity - the state a chart is already in after a fresh
|
|
32
|
+
* render. Storing or restoring it is pure noise, and persisting it would write to the
|
|
33
|
+
* saved file every time somebody zoomed out again. */
|
|
34
|
+
export declare function isIdentity(t: ZoomTransform): boolean;
|
|
35
|
+
/** Sub-pixel differences are re-render noise, not the reader's intent. Without this, restoring a
|
|
36
|
+
* transform would itself look like a change and write straight back - a persist loop. */
|
|
37
|
+
export declare function transformsDiffer(a: ZoomTransform | null, b: ZoomTransform | null): boolean;
|
|
38
|
+
export declare function serializeZoomState(t: ZoomTransform, key: string): string;
|
|
39
|
+
/** Never throws. A blob that will not parse is treated as absent, which is the same
|
|
40
|
+
* outcome as never having saved one. */
|
|
41
|
+
export declare function parseZoomState(raw: string | null | undefined): ZoomState | null;
|
|
42
|
+
/** A remembered transform is only meaningful for the chart + viewport that produced it.
|
|
43
|
+
* A regeneration changes the code hash; a real resize changes the viewport bucket. Either
|
|
44
|
+
* way the old transform describes a frame that no longer exists, and the honest thing is
|
|
45
|
+
* to open at identity rather than somewhere arbitrary. */
|
|
46
|
+
export declare function isZoomStateUsable(state: ZoomState | null, currentKey: string): boolean;
|
|
47
|
+
/** Read d3-zoom's stored transform off a node, defensively.
|
|
48
|
+
* `node.__zoom` is a d3 ZoomTransform instance; only its three numbers are wanted. */
|
|
49
|
+
export declare function readNodeTransform(node: any): ZoomTransform | null;
|
|
50
|
+
/** Every element under `root` (inclusive) that d3-zoom has attached to.
|
|
51
|
+
*
|
|
52
|
+
* Nearly always exactly one - the map's pan-catcher rect or the SVG itself. More than one
|
|
53
|
+
* means a composite chart with independently zoomable panels; those are deliberately NOT
|
|
54
|
+
* remembered, because pairing several transforms to several panels across a re-render
|
|
55
|
+
* needs an identity there is none of, and restoring them to the WRONG panels would be worse
|
|
56
|
+
* than not restoring at all. */
|
|
57
|
+
export declare function findZoomedNodes(root: Element | null): Element[];
|
|
58
|
+
/** The single zoomed node worth remembering, or null. See findZoomedNodes on why >1 opts out. */
|
|
59
|
+
export declare function soleZoomedNode(root: Element | null): Element | null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ZoomState } from "./zoomMemory";
|
|
2
|
+
/**
|
|
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).
|
|
6
|
+
*/
|
|
7
|
+
export interface ZoomMemoryHost {
|
|
8
|
+
/** The in-session tier, always on: survives every re-render, never written anywhere. */
|
|
9
|
+
liveZoomState: ZoomState | null;
|
|
10
|
+
/** The key the watcher stamps on what it records (the latest render's). */
|
|
11
|
+
zoomWatchKey: string;
|
|
12
|
+
/** Whether the watcher's listeners are on zoomContainer (they attach once). */
|
|
13
|
+
zoomWatchAttached: boolean;
|
|
14
|
+
/** The pending settle timer, 0 when none. */
|
|
15
|
+
zoomSettleTimer: number;
|
|
16
|
+
/** 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
|
+
zoomContainer: Element | null;
|
|
19
|
+
/** Is the persisted tier on NOW? Read on every restore and every write, so a change applies to the next. */
|
|
20
|
+
rememberAcrossSessions(): boolean;
|
|
21
|
+
/** The persisted blob, or null. Read only while rememberAcrossSessions() is true; `key` is the current
|
|
22
|
+
* render's, for a host that wants to say why a saved view did not apply. */
|
|
23
|
+
readPersistedZoom(key: string): string | null;
|
|
24
|
+
/** Write the persisted blob; "" forgets it. Called only while rememberAcrossSessions() is true. */
|
|
25
|
+
persistZoom(json: string): void;
|
|
26
|
+
/** Diagnostics: a tag and a small payload. Never transform data beyond the three numbers and the key. */
|
|
27
|
+
log(tag: string, data?: Record<string, unknown>): void;
|
|
28
|
+
}
|
|
29
|
+
/** What captureZoomBehaviors hands back: the behaviors the chart created, and the unwrap. */
|
|
30
|
+
export interface ZoomCapture {
|
|
31
|
+
behaviors: any[];
|
|
32
|
+
release: () => void;
|
|
33
|
+
}
|
|
34
|
+
/** Wrap `d3.zoom` so every behavior the generated code creates is recorded.
|
|
35
|
+
*
|
|
36
|
+
* This works BECAUSE d3's chainable setters mutate and return the same object:
|
|
37
|
+
* `d3.zoom().scaleExtent([1,8]).translateExtent(...)` hands back the very object captured, so the
|
|
38
|
+
* reference stays live no matter how the chart configures it. (Wrapping the returned object instead
|
|
39
|
+
* would NOT work - the first chained call would unwrap it.) */
|
|
40
|
+
export declare function captureZoomBehaviors(d3: any, host: ZoomMemoryHost): ZoomCapture;
|
|
41
|
+
/** Put the remembered view back. Called synchronously after render() returned and before the browser
|
|
42
|
+
* paints - which is what makes this a restore rather than a visible jump - and before any pass that
|
|
43
|
+
* measures the chart, so they measure the view the reader will see. */
|
|
44
|
+
export declare function restoreZoomForRender(d3: any, host: ZoomMemoryHost, capture: ZoomCapture, key: string): void;
|
|
45
|
+
/** Record the transform after interaction settles.
|
|
46
|
+
*
|
|
47
|
+
* Listeners are attached ONCE to the container (which outlives individual renders) and read the key
|
|
48
|
+
* from the host object, so a re-render does not stack another set. Capture phase, because the chart's
|
|
49
|
+
* own handlers may stop propagation. */
|
|
50
|
+
export declare function attachZoomWatcher(host: ZoomMemoryHost, key: string): void;
|
|
51
|
+
/** Read the live transform off the DOM and remember it. */
|
|
52
|
+
export declare function captureCurrentZoom(host: ZoomMemoryHost): void;
|
|
53
|
+
/** A gesture that has not settled yet is recorded NOW. Call it before the chart's DOM goes away (a
|
|
54
|
+
* redraw, a teardown): the pending timer would otherwise fire over an empty container and lose it. */
|
|
55
|
+
export declare function flushPendingZoom(host: ZoomMemoryHost): void;
|
|
56
|
+
/** Drop the session view and any pending gesture, and persist nothing - for a host that swaps in a
|
|
57
|
+
* different chart, where a timer armed on the old one must not write after the swap. */
|
|
58
|
+
export declare function forgetZoom(host: ZoomMemoryHost): void;
|
|
59
|
+
/** WHAT createChartHost TAKES as `zoomMemory`: the functions above bound to one ZoomMemoryHost. */
|
|
60
|
+
export interface ZoomMemoryHook {
|
|
61
|
+
/** The key a render's view is remembered under: the code's hash and the viewport's 32px bucket. */
|
|
62
|
+
keyFor(code: string, width: number, height: number): string;
|
|
63
|
+
/** Wrap d3.zoom on the d3 the chart is handed, for the duration of its run. */
|
|
64
|
+
capture(d3: any): ZoomCapture;
|
|
65
|
+
/** Put the remembered view back, then attach the watcher (once) under this render's key. */
|
|
66
|
+
restore(container: Element, capture: ZoomCapture, key: string, d3: any): void;
|
|
67
|
+
/** flushPendingZoom: record a gesture still settling. */
|
|
68
|
+
flush(): void;
|
|
69
|
+
/** forgetZoom: drop the session view and a pending gesture. */
|
|
70
|
+
forget(): void;
|
|
71
|
+
}
|
|
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. */
|
|
74
|
+
export declare function createZoomMemoryHook(host: ZoomMemoryHost): ZoomMemoryHook;
|
|
@@ -0,0 +1,276 @@
|
|
|
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
|
+
import {
|
|
3
|
+
B
|
|
4
|
+
} from "./chunk-KQN5C7SZ.mjs";
|
|
5
|
+
import "./chunk-GHODWOAL.mjs";
|
|
6
|
+
|
|
7
|
+
// src/zoomMemory/zoomMemory.ts
|
|
8
|
+
function zoomStateKey(codeHash, width, height) {
|
|
9
|
+
return `${codeHash}:${viewportBucket(width)}x${viewportBucket(height)}`;
|
|
10
|
+
}
|
|
11
|
+
function viewportBucket(px) {
|
|
12
|
+
if (!isFiniteNumber(px) || px <= 0) return 0;
|
|
13
|
+
return Math.round(px / 32);
|
|
14
|
+
}
|
|
15
|
+
function isFiniteNumber(v) {
|
|
16
|
+
return typeof v === "number" && isFinite(v);
|
|
17
|
+
}
|
|
18
|
+
function isUsableTransform(t) {
|
|
19
|
+
if (!t || typeof t !== "object") return false;
|
|
20
|
+
const c = t;
|
|
21
|
+
if (!isFiniteNumber(c.k) || !isFiniteNumber(c.x) || !isFiniteNumber(c.y)) return false;
|
|
22
|
+
if (c.k <= 0 || c.k > 1e6) return false;
|
|
23
|
+
if (Math.abs(c.x) > 1e7 || Math.abs(c.y) > 1e7) return false;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function isIdentity(t) {
|
|
27
|
+
return t.k === 1 && t.x === 0 && t.y === 0;
|
|
28
|
+
}
|
|
29
|
+
function transformsDiffer(a, b) {
|
|
30
|
+
if (!a || !b) return a !== b;
|
|
31
|
+
return Math.abs(a.k - b.k) > 1e-6 || Math.abs(a.x - b.x) > 0.5 || Math.abs(a.y - b.y) > 0.5;
|
|
32
|
+
}
|
|
33
|
+
function serializeZoomState(t, key) {
|
|
34
|
+
return JSON.stringify({
|
|
35
|
+
k: Math.round(t.k * 1e4) / 1e4,
|
|
36
|
+
x: Math.round(t.x * 100) / 100,
|
|
37
|
+
y: Math.round(t.y * 100) / 100,
|
|
38
|
+
key
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function parseZoomState(raw) {
|
|
42
|
+
if (!raw || typeof raw !== "string") return null;
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(raw);
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (!isUsableTransform(parsed)) return null;
|
|
50
|
+
const key = typeof parsed.key === "string" ? parsed.key : "";
|
|
51
|
+
if (!key) return null;
|
|
52
|
+
return { k: parsed.k, x: parsed.x, y: parsed.y, key };
|
|
53
|
+
}
|
|
54
|
+
function isZoomStateUsable(state, currentKey) {
|
|
55
|
+
return !!state && !!currentKey && state.key === currentKey && isUsableTransform(state);
|
|
56
|
+
}
|
|
57
|
+
function readNodeTransform(node) {
|
|
58
|
+
if (!node) return null;
|
|
59
|
+
const z = node.__zoom;
|
|
60
|
+
if (!z) return null;
|
|
61
|
+
const t = { k: z.k, x: z.x, y: z.y };
|
|
62
|
+
return isUsableTransform(t) ? t : null;
|
|
63
|
+
}
|
|
64
|
+
function findZoomedNodes(root) {
|
|
65
|
+
if (!root) return [];
|
|
66
|
+
const out = [];
|
|
67
|
+
if (root.__zoom) out.push(root);
|
|
68
|
+
let all;
|
|
69
|
+
try {
|
|
70
|
+
all = root.querySelectorAll("*");
|
|
71
|
+
} catch {
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
for (let i = 0; i < all.length; i++) {
|
|
75
|
+
if (all[i].__zoom) out.push(all[i]);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
function soleZoomedNode(root) {
|
|
80
|
+
const nodes = findZoomedNodes(root);
|
|
81
|
+
return nodes.length === 1 ? nodes[0] : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/zoomMemory/zoomMemoryDom.ts
|
|
85
|
+
var SETTLE_MS = 350;
|
|
86
|
+
function captureZoomBehaviors(d3, host) {
|
|
87
|
+
const noop = { behaviors: [], release: () => void 0 };
|
|
88
|
+
if (!d3 || typeof d3.zoom !== "function") return noop;
|
|
89
|
+
const behaviors = [];
|
|
90
|
+
const original = d3.zoom;
|
|
91
|
+
try {
|
|
92
|
+
const wrapped = function(...args) {
|
|
93
|
+
const b = original.apply(this, args);
|
|
94
|
+
try {
|
|
95
|
+
if (b) behaviors.push(b);
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
return b;
|
|
99
|
+
};
|
|
100
|
+
try {
|
|
101
|
+
for (const k of Object.keys(original)) wrapped[k] = original[k];
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
d3.zoom = wrapped;
|
|
105
|
+
} catch (e) {
|
|
106
|
+
host.log("zoom-capture-wrap-failed", { message: e instanceof Error ? e.message : String(e) });
|
|
107
|
+
return noop;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
behaviors,
|
|
111
|
+
release: () => {
|
|
112
|
+
try {
|
|
113
|
+
d3.zoom = original;
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function constrainToBehavior(d3, behavior, node, t) {
|
|
120
|
+
let out = d3.zoomIdentity.translate(t.x, t.y).scale(t.k);
|
|
121
|
+
try {
|
|
122
|
+
const scaleExtent = typeof behavior.scaleExtent === "function" ? behavior.scaleExtent() : null;
|
|
123
|
+
if (scaleExtent && scaleExtent.length === 2) {
|
|
124
|
+
const k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k));
|
|
125
|
+
out = d3.zoomIdentity.translate(t.x, t.y).scale(k);
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const constrain = typeof behavior.constrain === "function" ? behavior.constrain() : null;
|
|
131
|
+
const extentFn = typeof behavior.extent === "function" ? behavior.extent() : null;
|
|
132
|
+
const translateExtent = typeof behavior.translateExtent === "function" ? behavior.translateExtent() : null;
|
|
133
|
+
if (typeof constrain === "function" && translateExtent) {
|
|
134
|
+
const extent = typeof extentFn === "function" ? extentFn.apply(node, [node && node.__data__]) : extentFn;
|
|
135
|
+
if (extent) out = constrain(out, extent, translateExtent);
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
function rememberedFor(host, key) {
|
|
142
|
+
if (isZoomStateUsable(host.liveZoomState, key)) return host.liveZoomState;
|
|
143
|
+
if (!rememberAcrossSessions(host)) return null;
|
|
144
|
+
let raw = null;
|
|
145
|
+
try {
|
|
146
|
+
raw = host.readPersistedZoom(key);
|
|
147
|
+
} catch {
|
|
148
|
+
raw = null;
|
|
149
|
+
}
|
|
150
|
+
const persisted = parseZoomState(raw);
|
|
151
|
+
return isZoomStateUsable(persisted, key) ? persisted : null;
|
|
152
|
+
}
|
|
153
|
+
function rememberAcrossSessions(host) {
|
|
154
|
+
try {
|
|
155
|
+
return !!host.rememberAcrossSessions();
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function restoreZoomForRender(d3, host, capture, key) {
|
|
161
|
+
try {
|
|
162
|
+
const state = rememberedFor(host, key);
|
|
163
|
+
if (!state) return;
|
|
164
|
+
const node = soleZoomedNode(host.zoomContainer);
|
|
165
|
+
if (!node) return;
|
|
166
|
+
const current = readNodeTransform(node);
|
|
167
|
+
if (current && !transformsDiffer(current, state)) return;
|
|
168
|
+
const behavior = capture.behaviors.length === 1 ? capture.behaviors[0] : null;
|
|
169
|
+
if (!behavior || typeof behavior.transform !== "function") {
|
|
170
|
+
host.log("zoom-restore-no-behavior", { behaviors: capture.behaviors.length });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!d3 || !d3.zoomIdentity || typeof d3.select !== "function") return;
|
|
174
|
+
const t = constrainToBehavior(d3, behavior, node, state);
|
|
175
|
+
d3.select(node).call(behavior.transform, t);
|
|
176
|
+
host.log("zoom-restored", { k: round(t.k), x: round(t.x), y: round(t.y), key });
|
|
177
|
+
} catch (e) {
|
|
178
|
+
host.log("zoom-restore-ex", { message: e instanceof Error ? e.message : String(e) });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function round(n) {
|
|
182
|
+
return Math.round(n * 100) / 100;
|
|
183
|
+
}
|
|
184
|
+
function attachZoomWatcher(host, key) {
|
|
185
|
+
host.zoomWatchKey = key;
|
|
186
|
+
const ctr = host.zoomContainer;
|
|
187
|
+
if (host.zoomWatchAttached || !ctr) return;
|
|
188
|
+
const settle = () => {
|
|
189
|
+
if (host.zoomSettleTimer) clearTimeout(host.zoomSettleTimer);
|
|
190
|
+
host.zoomSettleTimer = setTimeout(() => {
|
|
191
|
+
host.zoomSettleTimer = 0;
|
|
192
|
+
captureCurrentZoom(host);
|
|
193
|
+
}, SETTLE_MS);
|
|
194
|
+
};
|
|
195
|
+
for (const evt of ["wheel", "pointerup", "pointercancel", "click", "dblclick", "keyup"]) {
|
|
196
|
+
try {
|
|
197
|
+
ctr.addEventListener(evt, settle, true);
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
host.zoomWatchAttached = true;
|
|
202
|
+
host.log("zoom-watcher-attached", {});
|
|
203
|
+
}
|
|
204
|
+
function captureCurrentZoom(host) {
|
|
205
|
+
try {
|
|
206
|
+
const key = host.zoomWatchKey;
|
|
207
|
+
if (!key) return;
|
|
208
|
+
const node = soleZoomedNode(host.zoomContainer);
|
|
209
|
+
if (!node) return;
|
|
210
|
+
const t = readNodeTransform(node);
|
|
211
|
+
if (!t) return;
|
|
212
|
+
if (isIdentity(t)) {
|
|
213
|
+
if (host.liveZoomState) {
|
|
214
|
+
host.liveZoomState = null;
|
|
215
|
+
persist(host, "");
|
|
216
|
+
host.log("zoom-forgotten", { key });
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const previous = host.liveZoomState;
|
|
221
|
+
if (previous && previous.key === key && !transformsDiffer(previous, t)) return;
|
|
222
|
+
host.liveZoomState = { k: t.k, x: t.x, y: t.y, key };
|
|
223
|
+
persist(host, serializeZoomState(t, key));
|
|
224
|
+
host.log("zoom-remembered", { k: round(t.k), x: round(t.x), y: round(t.y), key });
|
|
225
|
+
} catch (e) {
|
|
226
|
+
host.log("zoom-capture-ex", { message: e instanceof Error ? e.message : String(e) });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function persist(host, json) {
|
|
230
|
+
if (!rememberAcrossSessions(host)) return;
|
|
231
|
+
host.persistZoom(json);
|
|
232
|
+
}
|
|
233
|
+
function flushPendingZoom(host) {
|
|
234
|
+
if (!host.zoomSettleTimer) return;
|
|
235
|
+
clearTimeout(host.zoomSettleTimer);
|
|
236
|
+
host.zoomSettleTimer = 0;
|
|
237
|
+
captureCurrentZoom(host);
|
|
238
|
+
}
|
|
239
|
+
function forgetZoom(host) {
|
|
240
|
+
if (host.zoomSettleTimer) clearTimeout(host.zoomSettleTimer);
|
|
241
|
+
host.zoomSettleTimer = 0;
|
|
242
|
+
host.liveZoomState = null;
|
|
243
|
+
}
|
|
244
|
+
function createZoomMemoryHook(host) {
|
|
245
|
+
return {
|
|
246
|
+
keyFor: (code, width, height) => zoomStateKey(B(String(code ?? "")), width, height),
|
|
247
|
+
capture: (d3) => captureZoomBehaviors(d3, host),
|
|
248
|
+
restore: (container, capture, key, d3) => {
|
|
249
|
+
if (!host.zoomContainer) host.zoomContainer = container;
|
|
250
|
+
restoreZoomForRender(d3, host, capture, key);
|
|
251
|
+
attachZoomWatcher(host, key);
|
|
252
|
+
},
|
|
253
|
+
flush: () => flushPendingZoom(host),
|
|
254
|
+
forget: () => forgetZoom(host)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
export {
|
|
258
|
+
attachZoomWatcher,
|
|
259
|
+
captureCurrentZoom,
|
|
260
|
+
captureZoomBehaviors,
|
|
261
|
+
createZoomMemoryHook,
|
|
262
|
+
findZoomedNodes,
|
|
263
|
+
flushPendingZoom,
|
|
264
|
+
forgetZoom,
|
|
265
|
+
isIdentity,
|
|
266
|
+
isUsableTransform,
|
|
267
|
+
isZoomStateUsable,
|
|
268
|
+
parseZoomState,
|
|
269
|
+
readNodeTransform,
|
|
270
|
+
restoreZoomForRender,
|
|
271
|
+
serializeZoomState,
|
|
272
|
+
soleZoomedNode,
|
|
273
|
+
transformsDiffer,
|
|
274
|
+
viewportBucket,
|
|
275
|
+
zoomStateKey
|
|
276
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-host",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.88",
|
|
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",
|
|
@@ -64,6 +64,11 @@
|
|
|
64
64
|
"import": "./dist/legend-fit.mjs",
|
|
65
65
|
"default": "./dist/legend-fit.mjs"
|
|
66
66
|
},
|
|
67
|
+
"./zoom-memory": {
|
|
68
|
+
"types": "./dist/types/zoomMemory/index.d.ts",
|
|
69
|
+
"import": "./dist/zoom-memory.mjs",
|
|
70
|
+
"default": "./dist/zoom-memory.mjs"
|
|
71
|
+
},
|
|
67
72
|
"./dax": {
|
|
68
73
|
"types": "./dist/types/dax.d.ts",
|
|
69
74
|
"import": "./dist/dax.mjs",
|