@bicharts/chart-host 0.6.58 → 0.6.60
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/dist/{chunk-ZEZXNAFL.mjs → chunk-XIAL6EEC.mjs} +131 -0
- package/dist/page.mjs +9 -3
- package/dist/react.mjs +95 -4
- package/dist/types/filterScope.d.ts +41 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/page.d.ts +1 -0
- package/dist/types/react.d.ts +29 -2
- package/package.json +1 -1
|
@@ -1,6 +1,26 @@
|
|
|
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
|
|
|
3
3
|
// src/filterScope.ts
|
|
4
|
+
function viewToken(view) {
|
|
5
|
+
const bytes = new TextEncoder().encode(JSON.stringify(view));
|
|
6
|
+
let bin = "";
|
|
7
|
+
bytes.forEach((b) => {
|
|
8
|
+
bin += String.fromCharCode(b);
|
|
9
|
+
});
|
|
10
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
11
|
+
}
|
|
12
|
+
function viewFromToken(token) {
|
|
13
|
+
if (!token) return null;
|
|
14
|
+
try {
|
|
15
|
+
const b64 = token.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((token.length + 3) % 4);
|
|
16
|
+
const bin = atob(b64);
|
|
17
|
+
const bytes = Uint8Array.from(bin, (ch) => ch.charCodeAt(0));
|
|
18
|
+
const v = JSON.parse(new TextDecoder().decode(bytes));
|
|
19
|
+
return v && v.v === 1 ? v : null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
4
24
|
function keyString(values) {
|
|
5
25
|
return JSON.stringify(values.map((v) => v instanceof Date ? `__date:${v.getTime()}` : v === void 0 ? null : v));
|
|
6
26
|
}
|
|
@@ -28,6 +48,8 @@ function createFilter(columns, opts = {}) {
|
|
|
28
48
|
let reason = null;
|
|
29
49
|
let version = 0;
|
|
30
50
|
const subs = /* @__PURE__ */ new Set();
|
|
51
|
+
let hovered = null;
|
|
52
|
+
const hoverSubs = /* @__PURE__ */ new Set();
|
|
31
53
|
const kept = /* @__PURE__ */ new WeakMap();
|
|
32
54
|
const commit = (nextKeys, nextRows, why) => {
|
|
33
55
|
const nextSet = new Set(nextKeys.map(keyString));
|
|
@@ -65,6 +87,23 @@ function createFilter(columns, opts = {}) {
|
|
|
65
87
|
let warnedKeep = false;
|
|
66
88
|
const filter = {
|
|
67
89
|
id: opts.id ?? `f${++nextId}`,
|
|
90
|
+
key: opts.key ?? cols.join("+"),
|
|
91
|
+
hoverEnabled: !!opts.hover,
|
|
92
|
+
get hovered() {
|
|
93
|
+
return hovered;
|
|
94
|
+
},
|
|
95
|
+
setHover(k) {
|
|
96
|
+
const next = k && k.length ? k.slice(0, cols.length) : null;
|
|
97
|
+
if ((next ? keyString(next) : null) === (hovered ? keyString(hovered) : null)) return;
|
|
98
|
+
hovered = next;
|
|
99
|
+
for (const cb of Array.from(hoverSubs)) cb(hovered);
|
|
100
|
+
},
|
|
101
|
+
onHover(cb) {
|
|
102
|
+
hoverSubs.add(cb);
|
|
103
|
+
return () => {
|
|
104
|
+
hoverSubs.delete(cb);
|
|
105
|
+
};
|
|
106
|
+
},
|
|
68
107
|
columns: cols,
|
|
69
108
|
label: opts.label ?? cols.join(" / "),
|
|
70
109
|
get active() {
|
|
@@ -148,6 +187,8 @@ function createFilter(columns, opts = {}) {
|
|
|
148
187
|
function createFilterScope() {
|
|
149
188
|
const list = [];
|
|
150
189
|
const offs = /* @__PURE__ */ new Map();
|
|
190
|
+
const ctl = /* @__PURE__ */ new Map();
|
|
191
|
+
const ctlOffs = /* @__PURE__ */ new Map();
|
|
151
192
|
const subs = /* @__PURE__ */ new Set();
|
|
152
193
|
const notify = () => {
|
|
153
194
|
for (const cb of Array.from(subs)) cb(scope);
|
|
@@ -188,10 +229,60 @@ function createFilterScope() {
|
|
|
188
229
|
return () => {
|
|
189
230
|
subs.delete(cb);
|
|
190
231
|
};
|
|
232
|
+
},
|
|
233
|
+
get controls() {
|
|
234
|
+
return ctl;
|
|
235
|
+
},
|
|
236
|
+
addControls(key, c) {
|
|
237
|
+
let k = key, n = 1;
|
|
238
|
+
while (ctl.has(k) && ctl.get(k) !== c) k = `${key}-${++n}`;
|
|
239
|
+
if (!ctl.has(k)) {
|
|
240
|
+
ctl.set(k, c);
|
|
241
|
+
ctlOffs.set(k, c.onChange(() => notify()));
|
|
242
|
+
notify();
|
|
243
|
+
}
|
|
244
|
+
return () => {
|
|
245
|
+
if (ctl.get(k) !== c) return;
|
|
246
|
+
ctlOffs.get(k)?.();
|
|
247
|
+
ctlOffs.delete(k);
|
|
248
|
+
ctl.delete(k);
|
|
249
|
+
notify();
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
save() {
|
|
253
|
+
const view = { v: 1 };
|
|
254
|
+
for (const f of list) if (f.active) (view.filters ??= {})[f.key] = f.keys.map((k) => k.slice());
|
|
255
|
+
for (const [k, c] of ctl) {
|
|
256
|
+
const values = c.values;
|
|
257
|
+
if (Object.keys(values).length) (view.controls ??= {})[k] = { ...values };
|
|
258
|
+
}
|
|
259
|
+
return view;
|
|
260
|
+
},
|
|
261
|
+
restore(view) {
|
|
262
|
+
if (!view || view.v !== 1) return;
|
|
263
|
+
for (const f of list) {
|
|
264
|
+
const keys = view.filters?.[f.key];
|
|
265
|
+
if (keys && keys.length) f.set(keys, "page");
|
|
266
|
+
else f.clear("page");
|
|
267
|
+
}
|
|
268
|
+
for (const [k, c] of ctl) {
|
|
269
|
+
const values = view.controls?.[k];
|
|
270
|
+
if (values) c.set(values);
|
|
271
|
+
}
|
|
191
272
|
}
|
|
192
273
|
};
|
|
193
274
|
return scope;
|
|
194
275
|
}
|
|
276
|
+
var LINKED_HOVER_CLASS = "bic-linked-hover";
|
|
277
|
+
var hoverStyled = false;
|
|
278
|
+
function ensureHoverStyle(doc) {
|
|
279
|
+
if (hoverStyled || !doc?.head) return;
|
|
280
|
+
hoverStyled = true;
|
|
281
|
+
const s = doc.createElement("style");
|
|
282
|
+
s.setAttribute("data-bic", "linked-hover");
|
|
283
|
+
s.textContent = `.${LINKED_HOVER_CLASS}{filter:drop-shadow(0 0 2px var(--bic-linked-hover,rgba(0,0,0,.65))) drop-shadow(0 0 1px var(--bic-linked-hover,rgba(0,0,0,.65)))}`;
|
|
284
|
+
doc.head.appendChild(s);
|
|
285
|
+
}
|
|
195
286
|
function payloadRowReader(getPayload) {
|
|
196
287
|
return {
|
|
197
288
|
rowAt(i) {
|
|
@@ -258,6 +349,41 @@ function bindFilter(host, filter, opts) {
|
|
|
258
349
|
if (o && o.keys !== ownerKeys()) owners.delete(filter);
|
|
259
350
|
paint();
|
|
260
351
|
});
|
|
352
|
+
const el = opts.container ?? null;
|
|
353
|
+
let offHoverDom = () => {
|
|
354
|
+
};
|
|
355
|
+
let offHover = () => {
|
|
356
|
+
};
|
|
357
|
+
if (filter.hoverEnabled && el) {
|
|
358
|
+
ensureHoverStyle(el.ownerDocument);
|
|
359
|
+
const markRow = (t) => {
|
|
360
|
+
const m = t?.closest?.(".d3-mark[data-row-idx]");
|
|
361
|
+
const v = m ? parseInt((m.getAttribute("data-row-idx") ?? "").split(",")[0], 10) : NaN;
|
|
362
|
+
return Number.isFinite(v) ? v : null;
|
|
363
|
+
};
|
|
364
|
+
const over = (e) => {
|
|
365
|
+
const i = markRow(e.target);
|
|
366
|
+
const r = i === null ? null : opts.rowAt(i);
|
|
367
|
+
if (r) filter.setHover(keyOfRow(r, filter.columns));
|
|
368
|
+
};
|
|
369
|
+
const leave = () => filter.setHover(null);
|
|
370
|
+
el.addEventListener("mouseover", over);
|
|
371
|
+
el.addEventListener("mouseleave", leave);
|
|
372
|
+
offHoverDom = () => {
|
|
373
|
+
el.removeEventListener("mouseover", over);
|
|
374
|
+
el.removeEventListener("mouseleave", leave);
|
|
375
|
+
};
|
|
376
|
+
const paintHover = (k) => {
|
|
377
|
+
const want = k ? keyString(k) : null;
|
|
378
|
+
el.querySelectorAll(".d3-mark[data-row-idx]").forEach((m) => {
|
|
379
|
+
const idx = parseInt((m.getAttribute("data-row-idx") ?? "").split(",")[0], 10);
|
|
380
|
+
const r = Number.isFinite(idx) ? opts.rowAt(idx) : null;
|
|
381
|
+
const on = !!want && !!r && keyString(keyOfRow(r, filter.columns)) === want;
|
|
382
|
+
m.classList.toggle(LINKED_HOVER_CLASS, on);
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
offHover = filter.onHover(paintHover);
|
|
386
|
+
}
|
|
261
387
|
return {
|
|
262
388
|
reconcile() {
|
|
263
389
|
if (filter.active && opts.rowCount() > 0 && isMine()) {
|
|
@@ -279,6 +405,8 @@ function bindFilter(host, filter, opts) {
|
|
|
279
405
|
detach() {
|
|
280
406
|
offHost();
|
|
281
407
|
offFilter();
|
|
408
|
+
offHoverDom();
|
|
409
|
+
offHover();
|
|
282
410
|
const o = owners.get(filter);
|
|
283
411
|
if (o && o.token === token) owners.set(filter, { ...o, orphan: true });
|
|
284
412
|
}
|
|
@@ -453,8 +581,11 @@ function notesFor(notes, key, opts) {
|
|
|
453
581
|
}
|
|
454
582
|
|
|
455
583
|
export {
|
|
584
|
+
viewToken,
|
|
585
|
+
viewFromToken,
|
|
456
586
|
createFilter,
|
|
457
587
|
createFilterScope,
|
|
588
|
+
LINKED_HOVER_CLASS,
|
|
458
589
|
payloadRowReader,
|
|
459
590
|
bindFilter,
|
|
460
591
|
fromVegaInteraction,
|
package/dist/page.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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
|
+
LINKED_HOVER_CLASS,
|
|
3
4
|
attachControls,
|
|
4
5
|
badgeKey,
|
|
5
6
|
bindFilter,
|
|
@@ -10,10 +11,13 @@ import {
|
|
|
10
11
|
noteBadges,
|
|
11
12
|
notesFor,
|
|
12
13
|
payloadRowReader,
|
|
13
|
-
readControls
|
|
14
|
-
|
|
14
|
+
readControls,
|
|
15
|
+
viewFromToken,
|
|
16
|
+
viewToken
|
|
17
|
+
} from "./chunk-XIAL6EEC.mjs";
|
|
15
18
|
import "./chunk-GHODWOAL.mjs";
|
|
16
19
|
export {
|
|
20
|
+
LINKED_HOVER_CLASS,
|
|
17
21
|
attachControls,
|
|
18
22
|
badgeKey,
|
|
19
23
|
bindFilter,
|
|
@@ -24,5 +28,7 @@ export {
|
|
|
24
28
|
noteBadges,
|
|
25
29
|
notesFor,
|
|
26
30
|
payloadRowReader,
|
|
27
|
-
readControls
|
|
31
|
+
readControls,
|
|
32
|
+
viewFromToken,
|
|
33
|
+
viewToken
|
|
28
34
|
};
|
package/dist/react.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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
|
+
LINKED_HOVER_CLASS,
|
|
3
4
|
attachControls,
|
|
4
5
|
badgeKey,
|
|
5
6
|
bindFilter,
|
|
@@ -9,8 +10,10 @@ import {
|
|
|
9
10
|
fromVegaInteraction,
|
|
10
11
|
noteBadges,
|
|
11
12
|
notesFor,
|
|
12
|
-
payloadRowReader
|
|
13
|
-
|
|
13
|
+
payloadRowReader,
|
|
14
|
+
viewFromToken,
|
|
15
|
+
viewToken
|
|
16
|
+
} from "./chunk-XIAL6EEC.mjs";
|
|
14
17
|
import {
|
|
15
18
|
assembleD3,
|
|
16
19
|
createChartGroup,
|
|
@@ -179,6 +182,7 @@ function BicChart(props) {
|
|
|
179
182
|
if (!host || !selects) return;
|
|
180
183
|
const payloadRows = payloadRowReader(() => dataRef.current);
|
|
181
184
|
const b = bindFilter(host, selects, {
|
|
185
|
+
container: ref.current,
|
|
182
186
|
rowAt(i) {
|
|
183
187
|
const g = groupRef.current, map = rowMapRef.current;
|
|
184
188
|
if (g && map) {
|
|
@@ -261,13 +265,94 @@ function useBicFilter(columns, opts) {
|
|
|
261
265
|
useSyncExternalStore((cb) => f.onChange(() => cb()), () => f.version, () => f.version);
|
|
262
266
|
return f;
|
|
263
267
|
}
|
|
264
|
-
function useBicControls(initial) {
|
|
268
|
+
function useBicControls(initial, opts) {
|
|
269
|
+
const scope = useContext(PageCtx);
|
|
265
270
|
const ref = useRef(null);
|
|
266
271
|
if (!ref.current) ref.current = createControls(initial);
|
|
267
272
|
const c = ref.current;
|
|
273
|
+
useEffect(() => scope.addControls(opts?.id ?? "controls", c), [scope, c, opts?.id]);
|
|
268
274
|
useSyncExternalStore((cb) => c.onChange(() => cb()), () => c.version, () => c.version);
|
|
269
275
|
return c;
|
|
270
276
|
}
|
|
277
|
+
function useBicView() {
|
|
278
|
+
const scope = useContext(PageCtx);
|
|
279
|
+
return useMemo(() => ({
|
|
280
|
+
save: () => scope.save(),
|
|
281
|
+
restore: (v) => scope.restore(v),
|
|
282
|
+
token: () => viewToken(scope.save()),
|
|
283
|
+
restoreToken: (t) => scope.restore(viewFromToken(t))
|
|
284
|
+
}), [scope]);
|
|
285
|
+
}
|
|
286
|
+
function useBicUrlState(param = "view") {
|
|
287
|
+
const scope = useContext(PageCtx);
|
|
288
|
+
useEffect(() => {
|
|
289
|
+
if (typeof window === "undefined") return;
|
|
290
|
+
const url = new URL(window.location.href);
|
|
291
|
+
scope.restore(viewFromToken(url.searchParams.get(param)));
|
|
292
|
+
return scope.onChange(() => {
|
|
293
|
+
const u = new URL(window.location.href);
|
|
294
|
+
const view = scope.save();
|
|
295
|
+
const empty = !view.filters && !view.controls;
|
|
296
|
+
if (empty) u.searchParams.delete(param);
|
|
297
|
+
else u.searchParams.set(param, viewToken(view));
|
|
298
|
+
if (u.href !== window.location.href) window.history.replaceState(window.history.state, "", u.href);
|
|
299
|
+
});
|
|
300
|
+
}, [scope, param]);
|
|
301
|
+
}
|
|
302
|
+
function BicDevtools({ max = 20 }) {
|
|
303
|
+
const scope = useContext(PageCtx);
|
|
304
|
+
const [, tick] = useState(0);
|
|
305
|
+
const log = useRef([]);
|
|
306
|
+
useEffect(() => scope.onChange(() => {
|
|
307
|
+
const at = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
308
|
+
const changed = scope.filters.map((f) => `${f.label}: ${f.active ? f.text : "(none)"}${f.reason ? ` [${f.reason}]` : ""}`);
|
|
309
|
+
log.current = [`${at} ${changed.join(" | ")}`, ...log.current].slice(0, max);
|
|
310
|
+
tick((t) => t + 1);
|
|
311
|
+
}), [scope, max]);
|
|
312
|
+
const box = {
|
|
313
|
+
position: "fixed",
|
|
314
|
+
right: 8,
|
|
315
|
+
bottom: 8,
|
|
316
|
+
zIndex: 2147483e3,
|
|
317
|
+
maxWidth: 420,
|
|
318
|
+
maxHeight: "50vh",
|
|
319
|
+
overflow: "auto",
|
|
320
|
+
font: "12px/1.4 ui-monospace, monospace",
|
|
321
|
+
background: "Canvas",
|
|
322
|
+
color: "CanvasText",
|
|
323
|
+
border: "1px solid GrayText",
|
|
324
|
+
borderRadius: 6,
|
|
325
|
+
padding: 6,
|
|
326
|
+
opacity: 0.95
|
|
327
|
+
};
|
|
328
|
+
return /* @__PURE__ */ jsxs("details", { className: "bic-devtools", style: box, children: [
|
|
329
|
+
/* @__PURE__ */ jsxs("summary", { style: { cursor: "pointer" }, children: [
|
|
330
|
+
"chart-host: ",
|
|
331
|
+
scope.active.length,
|
|
332
|
+
" filter(s) active"
|
|
333
|
+
] }),
|
|
334
|
+
scope.filters.map((f) => /* @__PURE__ */ jsxs("div", { children: [
|
|
335
|
+
/* @__PURE__ */ jsx("b", { children: f.label }),
|
|
336
|
+
" [",
|
|
337
|
+
f.key,
|
|
338
|
+
"] ",
|
|
339
|
+
f.active ? JSON.stringify(f.keys) : "(none)",
|
|
340
|
+
" ",
|
|
341
|
+
f.text && `"${f.text}"`,
|
|
342
|
+
f.reason ? ` - last: ${f.reason}` : "",
|
|
343
|
+
" v",
|
|
344
|
+
f.version
|
|
345
|
+
] }, f.id)),
|
|
346
|
+
[...scope.controls].map(([k, c]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
347
|
+
/* @__PURE__ */ jsx("b", { children: "controls" }),
|
|
348
|
+
" [",
|
|
349
|
+
k,
|
|
350
|
+
"] ",
|
|
351
|
+
c.summary || JSON.stringify(c.values)
|
|
352
|
+
] }, k)),
|
|
353
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 4, opacity: 0.8 }, children: log.current.map((l, i) => /* @__PURE__ */ jsx("div", { children: l }, i)) })
|
|
354
|
+
] });
|
|
355
|
+
}
|
|
271
356
|
function useBicFilters() {
|
|
272
357
|
const scope = useContext(PageCtx);
|
|
273
358
|
const [, tick] = useState(0);
|
|
@@ -372,8 +457,10 @@ function useMeasuredSize(ref) {
|
|
|
372
457
|
export {
|
|
373
458
|
BicChart,
|
|
374
459
|
BicChartGroup,
|
|
460
|
+
BicDevtools,
|
|
375
461
|
BicFilterChips,
|
|
376
462
|
BicPage,
|
|
463
|
+
LINKED_HOVER_CLASS,
|
|
377
464
|
assembleD3,
|
|
378
465
|
badgeKey,
|
|
379
466
|
createControls,
|
|
@@ -386,5 +473,9 @@ export {
|
|
|
386
473
|
useBicFilter,
|
|
387
474
|
useBicFilters,
|
|
388
475
|
useBicSelection,
|
|
389
|
-
|
|
476
|
+
useBicUrlState,
|
|
477
|
+
useBicView,
|
|
478
|
+
useMeasuredSize,
|
|
479
|
+
viewFromToken,
|
|
480
|
+
viewToken
|
|
390
481
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChartHost } from "./host";
|
|
2
|
+
import type { Controls } from "./controls";
|
|
2
3
|
/** Why a filter changed. */
|
|
3
4
|
export type FilterChangeReason = "click" | "clear" | "page" | "data";
|
|
4
5
|
/** One selected key: a value per key column, in the filter's column order. */
|
|
@@ -14,9 +15,26 @@ export interface FilterOptions {
|
|
|
14
15
|
display?: string | readonly string[];
|
|
15
16
|
/** A stable id for this filter within its scope. Generated when absent. */
|
|
16
17
|
id?: string;
|
|
18
|
+
/** Its name in a saved view (scope.save / a link). Defaults to the key columns joined with "+". */
|
|
19
|
+
key?: string;
|
|
20
|
+
/**
|
|
21
|
+
* LINKED HOVER: hovering a mark on a chart bound to this filter lights the marks with the same key on every other
|
|
22
|
+
* chart bound to it (a glow, never a filter). Off by default.
|
|
23
|
+
*/
|
|
24
|
+
hover?: boolean;
|
|
17
25
|
}
|
|
18
26
|
export interface Filter {
|
|
19
27
|
readonly id: string;
|
|
28
|
+
/** Its name in a saved view. */
|
|
29
|
+
readonly key: string;
|
|
30
|
+
/** Whether linked hover is on (FilterOptions.hover). */
|
|
31
|
+
readonly hoverEnabled: boolean;
|
|
32
|
+
/** The key being hovered on a bound chart now, or null. */
|
|
33
|
+
readonly hovered: FilterKey | null;
|
|
34
|
+
/** Set or clear the hovered key (bound charts do this; a page may too). */
|
|
35
|
+
setHover(key: FilterKey | null): void;
|
|
36
|
+
/** Subscribe to hover changes. Returns the unsubscribe. */
|
|
37
|
+
onHover(cb: (key: FilterKey | null) => void): () => void;
|
|
20
38
|
/** The key column(s). */
|
|
21
39
|
readonly columns: readonly string[];
|
|
22
40
|
readonly label: string;
|
|
@@ -74,9 +92,27 @@ export interface FilterScope {
|
|
|
74
92
|
add(filter: Filter): () => void;
|
|
75
93
|
/** Clear every filter. */
|
|
76
94
|
clearAll(reason?: FilterChangeReason): void;
|
|
77
|
-
/** Subscribe to any change of any filter, and to
|
|
95
|
+
/** Subscribe to any change of any filter or controls, and to either being added or removed. */
|
|
78
96
|
onChange(cb: (scope: FilterScope) => void): () => void;
|
|
97
|
+
/** A chart's controls, to be saved and restored with the page's view. Returns the remove. */
|
|
98
|
+
addControls(key: string, controls: Controls): () => void;
|
|
99
|
+
/** The registered controls, by key. */
|
|
100
|
+
readonly controls: ReadonlyMap<string, Controls>;
|
|
101
|
+
/** The page's view now: every filter's keys and every registered controls' values - JSON-safe. */
|
|
102
|
+
save(): PageView;
|
|
103
|
+
/** Put a saved view back: each filter set (or cleared when the view doesn't name it), each controls set. */
|
|
104
|
+
restore(view: PageView | null | undefined): void;
|
|
105
|
+
}
|
|
106
|
+
/** A saved page view: what save() returns and restore() takes. */
|
|
107
|
+
export interface PageView {
|
|
108
|
+
v: 1;
|
|
109
|
+
filters?: Record<string, unknown[][]>;
|
|
110
|
+
controls?: Record<string, Record<string, unknown>>;
|
|
79
111
|
}
|
|
112
|
+
/** A view as a URL-safe token (base64url of its JSON), for a link or a bookmark. */
|
|
113
|
+
export declare function viewToken(view: PageView): string;
|
|
114
|
+
/** A token from viewToken back to a view; null when it isn't one. */
|
|
115
|
+
export declare function viewFromToken(token: string | null | undefined): PageView | null;
|
|
80
116
|
/** Make a filter keyed by `columns` (one name, or several for a compound key such as a route). */
|
|
81
117
|
export declare function createFilter(columns: string | readonly string[], opts?: FilterOptions): Filter;
|
|
82
118
|
/** Make a page's filter scope. */
|
|
@@ -98,7 +134,11 @@ export interface BindFilterOptions {
|
|
|
98
134
|
rowAt(i: number): Record<string, unknown> | null;
|
|
99
135
|
/** How many rows the chart is drawing now. */
|
|
100
136
|
rowCount(): number;
|
|
137
|
+
/** The chart's container, for linked hover (FilterOptions.hover). Without it the chart takes no part in hover. */
|
|
138
|
+
container?: HTMLElement | null;
|
|
101
139
|
}
|
|
140
|
+
/** The class a mark wears while its key is hovered on another chart bound to the same filter. */
|
|
141
|
+
export declare const LINKED_HOVER_CLASS = "bic-linked-hover";
|
|
102
142
|
/** The source row behind each payload row, from a payload of { columns, rows: unknown[][] }. */
|
|
103
143
|
export declare function payloadRowReader(getPayload: () => {
|
|
104
144
|
columns: readonly {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export { ANNOTATION_CLASS, ANNOTATION_LAYER_CLASS, type MarkAnnotation, type Ann
|
|
|
12
12
|
export type { MarkNoteLike, NoteBadgeOptions } from "./markNotes";
|
|
13
13
|
export type { Controls, ControlInfo, ControlsAttachment } from "./controls";
|
|
14
14
|
export { createChartGroup, toSourceRows, syncMemberSelection, type ChartGroup, type ChartGroupMember, type ChartGroupSelection, type ChartGroupChange, type ChartGroupMemberOptions, type ChartGroupSourceOptions, type ChartGroupPayload } from "./group";
|
|
15
|
-
export type { Filter, FilterScope, FilterOptions, FilterKey, FilterChangeReason, FilterBinding, BindFilterOptions, VegaInteractionEvent } from "./filterScope";
|
|
15
|
+
export type { Filter, FilterScope, FilterOptions, FilterKey, FilterChangeReason, FilterBinding, BindFilterOptions, VegaInteractionEvent, PageView } from "./filterScope";
|
|
16
16
|
export { createMarkResolver, isInsideControl, rowIdxsFromMark, nextSelection, collectD3GlyphCenters, clearSelectionGlyphs, paintSelectionGlyphs, SELECTION_GLYPH_CLASS, type MarkResolver, type MarkResolverEnv, type GlyphCenter, type SelectionGlyphStyle, type SelectionModifiers, } from "./selection";
|
|
17
17
|
export { planTrivialChart, compileTrivialSource, type TrivialPlan, type TrivialShapeKind } from "./trivial";
|
|
18
18
|
export { captureSvgSnapshot, svgToDataUrl, svgNaturalSize, rasterizeSvgToPngDataUrl, tableHostOf, chartSvgOf, htmlToSvgDataUrl, htmlNaturalSize, type SnapshotOptions } from "./snapshot";
|
package/dist/types/page.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { viewToken, viewFromToken, LINKED_HOVER_CLASS, type PageView } from "./filterScope";
|
|
1
2
|
export { createFilter, createFilterScope, bindFilter, payloadRowReader, fromVegaInteraction, type Filter, type FilterScope, type FilterOptions, type FilterKey, type FilterChangeReason, type FilterBinding, type BindFilterOptions, type VegaInteractionEvent } from "./filterScope";
|
|
2
3
|
export { createControls, attachControls, readControls, type Controls, type ControlInfo, type ControlsAttachment } from "./controls";
|
|
3
4
|
export { noteBadges, badgeKey, notesFor, type MarkNoteLike, type NoteBadgeOptions } from "./markNotes";
|
package/dist/types/react.d.ts
CHANGED
|
@@ -2,11 +2,12 @@ import type { ReactNode, RefObject } from "react";
|
|
|
2
2
|
import { type ChartHostConfig } from "./host";
|
|
3
3
|
import type { ResolveOptionsInput } from "./defaults";
|
|
4
4
|
import type { GeoPointBinding } from "./payload";
|
|
5
|
-
import { type Filter, type FilterOptions } from "./filterScope";
|
|
5
|
+
import { type PageView, type Filter, type FilterOptions } from "./filterScope";
|
|
6
6
|
export { assembleD3 } from "./host";
|
|
7
7
|
import { type Controls } from "./controls";
|
|
8
8
|
export { createControls, type Controls, type ControlInfo } from "./controls";
|
|
9
9
|
export { noteBadges, badgeKey, notesFor, type MarkNoteLike, type NoteBadgeOptions } from "./markNotes";
|
|
10
|
+
export { viewToken, viewFromToken, LINKED_HOVER_CLASS, type PageView } from "./filterScope";
|
|
10
11
|
export { createFilter, createFilterScope, fromVegaInteraction, type Filter, type FilterOptions, type FilterScope, type FilterChangeReason } from "./filterScope";
|
|
11
12
|
export interface BicChartProps {
|
|
12
13
|
/** Generated render() source (an ES module's `code` export, or the raw string). */
|
|
@@ -150,7 +151,33 @@ export declare function useBicFilter(columns: string | readonly string[], opts?:
|
|
|
150
151
|
* and `summary`, `set(saved)` to apply a saved scenario, `reset()` for the defaults. Re-renders the
|
|
151
152
|
* component on every change. `initial` seeds the values the chart first draws with.
|
|
152
153
|
*/
|
|
153
|
-
export declare function useBicControls(initial?: Readonly<Record<string, unknown
|
|
154
|
+
export declare function useBicControls(initial?: Readonly<Record<string, unknown>>, opts?: {
|
|
155
|
+
id?: string;
|
|
156
|
+
}): Controls;
|
|
157
|
+
/**
|
|
158
|
+
* THE PAGE'S VIEW, SAVED AND PUT BACK: every page filter's keys and every chart's controls (useBicControls). `save()`
|
|
159
|
+
* returns a JSON-safe view to store (a saved view, a user preference); `restore(view)` puts one back; `token()` /
|
|
160
|
+
* `restoreToken(t)` do the same as a URL-safe string for a link.
|
|
161
|
+
*/
|
|
162
|
+
export declare function useBicView(): {
|
|
163
|
+
save: () => PageView;
|
|
164
|
+
restore: (v: PageView | null | undefined) => void;
|
|
165
|
+
token: () => string;
|
|
166
|
+
restoreToken: (t: string | null | undefined) => void;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* THE VIEW IN THE ADDRESS BAR (opt-in): restores the page's filters and controls from `?<param>=` on mount, then keeps
|
|
170
|
+
* the parameter current as they change (history.replaceState - no navigation, no new history entries). A copied link
|
|
171
|
+
* opens the page as it was. Call once, in the page component, after its useBicFilter / useBicControls calls.
|
|
172
|
+
*/
|
|
173
|
+
export declare function useBicUrlState(param?: string): void;
|
|
174
|
+
/**
|
|
175
|
+
* A DEVELOPER'S VIEW OF THE PAGE'S STATE: every filter (its keys, its text, the last change and why), every chart's
|
|
176
|
+
* controls, and the last changes in order. Collapsed in a corner; render it only in development.
|
|
177
|
+
*/
|
|
178
|
+
export declare function BicDevtools({ max }: {
|
|
179
|
+
max?: number;
|
|
180
|
+
}): import("react").JSX.Element;
|
|
154
181
|
/** Every filter on the page (or the enclosing <BicPage>), and its clear-all - for a page's own chip bar or count. */
|
|
155
182
|
export declare function useBicFilters(): {
|
|
156
183
|
filters: readonly Filter[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-host",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.60",
|
|
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",
|