@bicharts/chart-host 0.6.5 → 0.6.6
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 +28 -0
- package/dist/{chunk-CZRYIPEL.mjs → chunk-ZPWRXCS5.mjs} +118 -1
- package/dist/index.mjs +9 -1
- package/dist/react.mjs +53 -41
- package/dist/types/group.d.ts +88 -0
- package/dist/types/host.d.ts +10 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/react.d.ts +15 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,6 +114,31 @@ host.setOptions({ colorScaleLow: "#eee" }); // live restyle — never a regene
|
|
|
114
114
|
host.destroy(); // call on unmount
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
+
### Several charts, one selection, without React
|
|
118
|
+
|
|
119
|
+
`createChartGroup` is the group `<BicChartGroup>` wraps: it owns the source table, derives each
|
|
120
|
+
member's payload, and translates every click to **source** row indices, so the row-index hazard
|
|
121
|
+
above is handled the same way in any framework (or none).
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
import { createChartGroup, createChartHost, assembleD3 } from "@bicharts/chart-host";
|
|
125
|
+
|
|
126
|
+
const group = createChartGroup(columns, rows, { point: { city: "City" } });
|
|
127
|
+
const d3all = assembleD3(d3, d3Sankey); // one extensible d3 for every chart
|
|
128
|
+
|
|
129
|
+
function mount(id, el, code, opts = {}) { // opts: { filteredBy, respondsWith }
|
|
130
|
+
const host = createChartHost(el, { code, d3: d3all, data: group.memberPayload(id, opts).payload,
|
|
131
|
+
options: { width: 800, height: 500 } });
|
|
132
|
+
host.render();
|
|
133
|
+
return group.attach(id, host, opts); // .detach() on teardown
|
|
134
|
+
}
|
|
135
|
+
mount("map", mapEl, mapCode, { respondsWith: "highlight" });
|
|
136
|
+
mount("table", tableEl, tableCode);
|
|
137
|
+
|
|
138
|
+
group.onChange(sel => console.log(sel.sourceId, sel.rows)); // SOURCE row indices
|
|
139
|
+
group.clear();
|
|
140
|
+
```
|
|
141
|
+
|
|
117
142
|
## Sizing is yours
|
|
118
143
|
|
|
119
144
|
`width`/`height` have **no defaults**. A chart draws once at whatever it is handed and does not
|
|
@@ -125,6 +150,9 @@ a background or headless tab, where frames are barely produced.
|
|
|
125
150
|
useLayoutEffect(() => setW(ref.current.getBoundingClientRect().width), []);
|
|
126
151
|
```
|
|
127
152
|
|
|
153
|
+
In React, `useMeasuredSize(ref)` from `@bicharts/chart-host/react` does exactly that and follows
|
|
154
|
+
later resizes: give the element its size in CSS and pass what it measures.
|
|
155
|
+
|
|
128
156
|
## Maps
|
|
129
157
|
|
|
130
158
|
Geometry is **not** bundled into the entry — the three assets are ~1.3 MB raw, and a bar chart
|
|
@@ -3331,6 +3331,9 @@ function noopViewStateProvider() {
|
|
|
3331
3331
|
} };
|
|
3332
3332
|
}
|
|
3333
3333
|
var parseRowIdxs = (s) => (s || "").split(",").map((x3) => parseInt(x3, 10)).filter((n) => Number.isFinite(n));
|
|
3334
|
+
function assembleD3(base, ...plugins) {
|
|
3335
|
+
return Object.assign({}, base, ...plugins);
|
|
3336
|
+
}
|
|
3334
3337
|
var D3_PLUGIN_PACKAGES = {
|
|
3335
3338
|
sankey: "d3-sankey",
|
|
3336
3339
|
sankeyLinkHorizontal: "d3-sankey",
|
|
@@ -3839,6 +3842,116 @@ function createChartHost(container, config) {
|
|
|
3839
3842
|
return host;
|
|
3840
3843
|
}
|
|
3841
3844
|
|
|
3845
|
+
// src/group.ts
|
|
3846
|
+
var EMPTY6 = Object.freeze({ sourceId: null, rows: Object.freeze([]) });
|
|
3847
|
+
function toSourceRows(rowMap, payloadRowIdxs) {
|
|
3848
|
+
if (!rowMap) return payloadRowIdxs.slice();
|
|
3849
|
+
return payloadRowIdxs.map((i) => rowMap[i]).filter((i) => i !== void 0);
|
|
3850
|
+
}
|
|
3851
|
+
function syncMemberSelection(host, selection, id, highlight, incoming) {
|
|
3852
|
+
const mine = selection.sourceId === id && selection.rows.length > 0;
|
|
3853
|
+
if (mine) return;
|
|
3854
|
+
if (highlight && incoming && incoming.length) {
|
|
3855
|
+
host.selection.highlight(incoming.slice());
|
|
3856
|
+
return;
|
|
3857
|
+
}
|
|
3858
|
+
if (host.selection.current && host.selection.current.length) host.selection.clear();
|
|
3859
|
+
}
|
|
3860
|
+
function createChartGroup(columns, rows, opts = {}) {
|
|
3861
|
+
let cols = columns;
|
|
3862
|
+
let table = rows;
|
|
3863
|
+
let geo = opts.geo ?? null;
|
|
3864
|
+
let point = opts.point ?? null;
|
|
3865
|
+
let selection = EMPTY6;
|
|
3866
|
+
const subs = /* @__PURE__ */ new Set();
|
|
3867
|
+
const members = /* @__PURE__ */ new Set();
|
|
3868
|
+
const notify = (change) => {
|
|
3869
|
+
for (const cb of Array.from(subs)) cb(selection, change);
|
|
3870
|
+
};
|
|
3871
|
+
const incomingFor = (id, filteredBy) => {
|
|
3872
|
+
const s = selection;
|
|
3873
|
+
const forId = s.rows.length && s.sourceId && s.sourceId !== id ? s.rows.slice() : null;
|
|
3874
|
+
if (filteredBy) return s.sourceId === filteredBy ? forId : null;
|
|
3875
|
+
return forId;
|
|
3876
|
+
};
|
|
3877
|
+
const payloadFor = (sourceRowIdxs) => {
|
|
3878
|
+
const idxs = sourceRowIdxs ? sourceRowIdxs.slice() : table.map((_2, i) => i);
|
|
3879
|
+
const subset = idxs.map((i) => table[i]);
|
|
3880
|
+
const payload = buildRenderPayload(cols, subset, geo ?? void 0, point ?? void 0);
|
|
3881
|
+
return { payload, rowMap: idxs };
|
|
3882
|
+
};
|
|
3883
|
+
const memberPayload = (id, o = {}) => payloadFor(o.respondsWith === "highlight" ? null : incomingFor(id, o.filteredBy));
|
|
3884
|
+
const group = {
|
|
3885
|
+
get selection() {
|
|
3886
|
+
return selection;
|
|
3887
|
+
},
|
|
3888
|
+
get columns() {
|
|
3889
|
+
return cols;
|
|
3890
|
+
},
|
|
3891
|
+
get rows() {
|
|
3892
|
+
return table;
|
|
3893
|
+
},
|
|
3894
|
+
incomingFor,
|
|
3895
|
+
payloadFor,
|
|
3896
|
+
memberPayload,
|
|
3897
|
+
publish(id, sourceRowIdxs) {
|
|
3898
|
+
selection = sourceRowIdxs.length ? { sourceId: id, rows: sourceRowIdxs.slice() } : { sourceId: null, rows: [] };
|
|
3899
|
+
notify(sourceRowIdxs.length ? "select" : "clear");
|
|
3900
|
+
},
|
|
3901
|
+
clear() {
|
|
3902
|
+
selection = { sourceId: null, rows: [] };
|
|
3903
|
+
notify("clear");
|
|
3904
|
+
},
|
|
3905
|
+
setSource(nextColumns, nextRows, o = {}) {
|
|
3906
|
+
cols = nextColumns;
|
|
3907
|
+
table = nextRows;
|
|
3908
|
+
geo = o.geo ?? null;
|
|
3909
|
+
point = o.point ?? null;
|
|
3910
|
+
notify("source");
|
|
3911
|
+
},
|
|
3912
|
+
onChange(cb) {
|
|
3913
|
+
subs.add(cb);
|
|
3914
|
+
return () => {
|
|
3915
|
+
subs.delete(cb);
|
|
3916
|
+
};
|
|
3917
|
+
},
|
|
3918
|
+
attach(id, host, o = {}) {
|
|
3919
|
+
const highlight = o.respondsWith === "highlight";
|
|
3920
|
+
let rowMap = memberPayload(id, o).rowMap;
|
|
3921
|
+
const offHost = host.selection.onChange((payloadIdxs, source) => {
|
|
3922
|
+
if (source === "host") return;
|
|
3923
|
+
const sourceIdxs = toSourceRows(rowMap, payloadIdxs);
|
|
3924
|
+
if (id) group.publish(id, sourceIdxs);
|
|
3925
|
+
o.onSelect?.(sourceIdxs);
|
|
3926
|
+
});
|
|
3927
|
+
const offGroup = group.onChange((sel) => {
|
|
3928
|
+
const incoming = incomingFor(id, o.filteredBy);
|
|
3929
|
+
const next = payloadFor(highlight ? null : incoming);
|
|
3930
|
+
rowMap = next.rowMap;
|
|
3931
|
+
host.setData(next.payload);
|
|
3932
|
+
syncMemberSelection(host, sel, id, highlight, incoming);
|
|
3933
|
+
});
|
|
3934
|
+
const member = {
|
|
3935
|
+
get rowMap() {
|
|
3936
|
+
return rowMap;
|
|
3937
|
+
},
|
|
3938
|
+
detach() {
|
|
3939
|
+
offHost();
|
|
3940
|
+
offGroup();
|
|
3941
|
+
members.delete(member);
|
|
3942
|
+
}
|
|
3943
|
+
};
|
|
3944
|
+
members.add(member);
|
|
3945
|
+
return member;
|
|
3946
|
+
},
|
|
3947
|
+
destroy() {
|
|
3948
|
+
for (const m of Array.from(members)) m.detach();
|
|
3949
|
+
subs.clear();
|
|
3950
|
+
}
|
|
3951
|
+
};
|
|
3952
|
+
return group;
|
|
3953
|
+
}
|
|
3954
|
+
|
|
3842
3955
|
export {
|
|
3843
3956
|
HOST_CONTRACT_VERSION,
|
|
3844
3957
|
GEO_POINT_PRECISIONS,
|
|
@@ -3970,9 +4083,13 @@ export {
|
|
|
3970
4083
|
invalidSentinelReason,
|
|
3971
4084
|
sessionViewStateProvider,
|
|
3972
4085
|
noopViewStateProvider,
|
|
4086
|
+
assembleD3,
|
|
3973
4087
|
requiredD3Plugins,
|
|
3974
4088
|
explainRenderFailure,
|
|
3975
4089
|
stripEsmExports,
|
|
3976
4090
|
compileRenderFn,
|
|
3977
|
-
createChartHost
|
|
4091
|
+
createChartHost,
|
|
4092
|
+
toSourceRows,
|
|
4093
|
+
syncMemberSelection,
|
|
4094
|
+
createChartGroup
|
|
3978
4095
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
We,
|
|
70
70
|
XFILTER_REFRESH_EVENT,
|
|
71
71
|
applyLabelContrast,
|
|
72
|
+
assembleD3,
|
|
72
73
|
autoRampCanvas,
|
|
73
74
|
autoRampMinStep,
|
|
74
75
|
axisPinPlacement,
|
|
@@ -87,6 +88,7 @@ import {
|
|
|
87
88
|
compositeOver,
|
|
88
89
|
contentExtentOf,
|
|
89
90
|
contrastRatio,
|
|
91
|
+
createChartGroup,
|
|
90
92
|
createChartHost,
|
|
91
93
|
createMarkResolver,
|
|
92
94
|
ctmScaleOf,
|
|
@@ -131,11 +133,13 @@ import {
|
|
|
131
133
|
stripEsmExports,
|
|
132
134
|
stripJsComments,
|
|
133
135
|
svgInkReach,
|
|
136
|
+
syncMemberSelection,
|
|
134
137
|
toRGBA,
|
|
138
|
+
toSourceRows,
|
|
135
139
|
unpinScrolledAxis,
|
|
136
140
|
valuePlacementFlag,
|
|
137
141
|
withoutViewOnlyKeys
|
|
138
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-ZPWRXCS5.mjs";
|
|
139
143
|
import "./chunk-A2GMXZP7.mjs";
|
|
140
144
|
import "./chunk-GHODWOAL.mjs";
|
|
141
145
|
|
|
@@ -2499,6 +2503,7 @@ export {
|
|
|
2499
2503
|
actionFor,
|
|
2500
2504
|
applyLabelContrast,
|
|
2501
2505
|
askApplyImprovements,
|
|
2506
|
+
assembleD3,
|
|
2502
2507
|
autoRampCanvas,
|
|
2503
2508
|
autoRampMinStep,
|
|
2504
2509
|
axisPinPlacement,
|
|
@@ -2531,6 +2536,7 @@ export {
|
|
|
2531
2536
|
confirmLaunch,
|
|
2532
2537
|
contentExtentOf,
|
|
2533
2538
|
contrastRatio,
|
|
2539
|
+
createChartGroup,
|
|
2534
2540
|
createChartHost,
|
|
2535
2541
|
createMarkResolver,
|
|
2536
2542
|
ctmScaleOf,
|
|
@@ -2631,7 +2637,9 @@ export {
|
|
|
2631
2637
|
svgInkReach,
|
|
2632
2638
|
svgNaturalSize,
|
|
2633
2639
|
svgToDataUrl,
|
|
2640
|
+
syncMemberSelection,
|
|
2634
2641
|
toRGBA,
|
|
2642
|
+
toSourceRows,
|
|
2635
2643
|
transportFailureShouldRecover,
|
|
2636
2644
|
unpinScrolledAxis,
|
|
2637
2645
|
valuePlacementFlag,
|
package/dist/react.mjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
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
|
+
assembleD3,
|
|
4
|
+
createChartGroup,
|
|
4
5
|
createChartHost,
|
|
5
6
|
geoFromCache,
|
|
6
|
-
loadGeo
|
|
7
|
-
|
|
7
|
+
loadGeo,
|
|
8
|
+
syncMemberSelection,
|
|
9
|
+
toSourceRows
|
|
10
|
+
} from "./chunk-ZPWRXCS5.mjs";
|
|
8
11
|
import "./chunk-A2GMXZP7.mjs";
|
|
9
12
|
import "./chunk-GHODWOAL.mjs";
|
|
10
13
|
|
|
@@ -17,32 +20,28 @@ function useBicSelection() {
|
|
|
17
20
|
return {
|
|
18
21
|
rows: g?.selection.rows ?? [],
|
|
19
22
|
sourceId: g?.selection.sourceId ?? null,
|
|
20
|
-
clear: () => g?.clear()
|
|
23
|
+
clear: () => g?.group.clear()
|
|
21
24
|
};
|
|
22
25
|
}
|
|
23
26
|
function BicChartGroup({ rows, columns, geo, point, children }) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
point
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
const groupRef = useRef(null);
|
|
28
|
+
if (!groupRef.current) groupRef.current = createChartGroup(columns, rows, { geo, point });
|
|
29
|
+
const group = groupRef.current;
|
|
30
|
+
const [sel, setSel] = useState(group.selection);
|
|
31
|
+
const source = useMemo(() => {
|
|
32
|
+
group.setSource(columns, rows, { geo, point });
|
|
33
|
+
return {};
|
|
34
|
+
}, [group, rows, columns, geo, point]);
|
|
35
|
+
useEffect(() => group.onChange((s, change) => {
|
|
36
|
+
if (change !== "source") setSel(s);
|
|
37
|
+
}), [group]);
|
|
38
|
+
const value = useMemo(
|
|
39
|
+
() => ({ group, selection: sel }),
|
|
40
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
|
+
[group, sel, source]
|
|
42
|
+
);
|
|
38
43
|
return /* @__PURE__ */ jsx(Ctx.Provider, { value, children });
|
|
39
44
|
}
|
|
40
|
-
function payloadFor(g, sourceIdxs) {
|
|
41
|
-
const idxs = sourceIdxs ?? g.rows.map((_, i) => i);
|
|
42
|
-
const subset = idxs.map((i) => g.rows[i]);
|
|
43
|
-
const p = buildRenderPayload(g.columns, subset, g.geo ?? void 0, g.point ?? void 0);
|
|
44
|
-
return { payload: p, rowMap: idxs };
|
|
45
|
-
}
|
|
46
45
|
function BicChart(props) {
|
|
47
46
|
const {
|
|
48
47
|
code,
|
|
@@ -64,15 +63,15 @@ function BicChart(props) {
|
|
|
64
63
|
const ref = useRef(null);
|
|
65
64
|
const hostRef = useRef(null);
|
|
66
65
|
const rowMapRef = useRef(null);
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const incoming =
|
|
66
|
+
const ctx = useContext(Ctx);
|
|
67
|
+
const group = ctx ? ctx.group : null;
|
|
68
|
+
const incoming = group ? group.incomingFor(id, filteredBy) : null;
|
|
70
69
|
const highlightMode = respondsWith === "highlight";
|
|
71
70
|
const filterSel = highlightMode ? null : incoming;
|
|
72
71
|
const built = useMemo(() => {
|
|
73
72
|
if (!group) return null;
|
|
74
|
-
return payloadFor(
|
|
75
|
-
}, [
|
|
73
|
+
return group.payloadFor(filterSel);
|
|
74
|
+
}, [ctx, filterSel && filterSel.join(",")]);
|
|
76
75
|
const data = built ? built.payload : props.data;
|
|
77
76
|
rowMapRef.current = built ? built.rowMap : null;
|
|
78
77
|
const onSelectRef = useRef(onSelect);
|
|
@@ -120,8 +119,7 @@ function BicChart(props) {
|
|
|
120
119
|
hostRef.current = host;
|
|
121
120
|
const off = host.selection.onChange((payloadIdxs, source) => {
|
|
122
121
|
if (source === "host") return;
|
|
123
|
-
const
|
|
124
|
-
const sourceIdxs = map ? payloadIdxs.map((i) => map[i]).filter((i) => i !== void 0) : payloadIdxs;
|
|
122
|
+
const sourceIdxs = toSourceRows(rowMapRef.current, payloadIdxs);
|
|
125
123
|
if (group && id) group.publish(id, sourceIdxs);
|
|
126
124
|
onSelectRef.current?.(sourceIdxs);
|
|
127
125
|
});
|
|
@@ -141,19 +139,33 @@ function BicChart(props) {
|
|
|
141
139
|
}, [data]);
|
|
142
140
|
useEffect(() => {
|
|
143
141
|
const host = hostRef.current;
|
|
144
|
-
if (!host || !
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (highlightMode && incoming && incoming.length) {
|
|
148
|
-
host.selection.highlight(incoming);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (host.selection.current && host.selection.current.length) host.selection.clear();
|
|
152
|
-
}, [group?.selection, id, group, highlightMode, incoming && incoming.join(",")]);
|
|
142
|
+
if (!host || !ctx) return;
|
|
143
|
+
syncMemberSelection(host, ctx.selection, id, highlightMode, incoming);
|
|
144
|
+
}, [ctx?.selection, id, ctx, highlightMode, incoming && incoming.join(",")]);
|
|
153
145
|
return /* @__PURE__ */ jsx("div", { ref, className, style });
|
|
154
146
|
}
|
|
147
|
+
function useMeasuredSize(ref) {
|
|
148
|
+
const [size, setSize] = useState({ width: 0, height: 0 });
|
|
149
|
+
useLayoutEffect(() => {
|
|
150
|
+
const el = ref.current;
|
|
151
|
+
if (!el) return;
|
|
152
|
+
const read = () => {
|
|
153
|
+
const r = el.getBoundingClientRect();
|
|
154
|
+
const width = Math.round(r.width), height = Math.round(r.height);
|
|
155
|
+
setSize((s) => s.width === width && s.height === height ? s : { width, height });
|
|
156
|
+
};
|
|
157
|
+
read();
|
|
158
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
159
|
+
const ro = new ResizeObserver(read);
|
|
160
|
+
ro.observe(el);
|
|
161
|
+
return () => ro.disconnect();
|
|
162
|
+
}, [ref]);
|
|
163
|
+
return size;
|
|
164
|
+
}
|
|
155
165
|
export {
|
|
156
166
|
BicChart,
|
|
157
167
|
BicChartGroup,
|
|
158
|
-
|
|
168
|
+
assembleD3,
|
|
169
|
+
useBicSelection,
|
|
170
|
+
useMeasuredSize
|
|
159
171
|
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ChartHost } from "./host";
|
|
2
|
+
import { type GeoPointBinding, type RenderPayload } from "./payload";
|
|
3
|
+
/** The group's one active selection, in SOURCE row indices. `sourceId` is the member that made it. */
|
|
4
|
+
export interface ChartGroupSelection {
|
|
5
|
+
readonly sourceId: string | null;
|
|
6
|
+
readonly rows: readonly number[];
|
|
7
|
+
}
|
|
8
|
+
/** The table every member's payload is derived from, and the geo bindings applied to each. */
|
|
9
|
+
export interface ChartGroupSourceOptions {
|
|
10
|
+
/** Choropleth join binding, applied when building each member's payload. */
|
|
11
|
+
geo?: {
|
|
12
|
+
column: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
} | null;
|
|
15
|
+
/** Point-map geocoding binding (city/state/zip/lat/lon column names). */
|
|
16
|
+
point?: GeoPointBinding | null;
|
|
17
|
+
}
|
|
18
|
+
/** How one member takes part in the group. */
|
|
19
|
+
export interface ChartGroupMemberOptions {
|
|
20
|
+
/** Respond only to this member's selection (one-way wiring). Absent: respond to any sibling. */
|
|
21
|
+
filteredBy?: string;
|
|
22
|
+
/** "filter" (default) re-derives the member's payload; "highlight" keeps it and paints. */
|
|
23
|
+
respondsWith?: "filter" | "highlight";
|
|
24
|
+
}
|
|
25
|
+
/** A member's payload and the map from each payload row to its source row. */
|
|
26
|
+
export interface ChartGroupPayload {
|
|
27
|
+
payload: RenderPayload;
|
|
28
|
+
/** `rowMap[k]` is the source row index of payload row `k`. */
|
|
29
|
+
rowMap: number[];
|
|
30
|
+
}
|
|
31
|
+
/** Why the group notified: a member's selection, a clear, or a new source table. */
|
|
32
|
+
export type ChartGroupChange = "select" | "clear" | "source";
|
|
33
|
+
/** A live chart wired into the group by `attach`. */
|
|
34
|
+
export interface ChartGroupMember {
|
|
35
|
+
/** The payload-row -> source-row map of the payload the member is drawing now. */
|
|
36
|
+
readonly rowMap: readonly number[];
|
|
37
|
+
/** Unwire the member: its clicks stop publishing and the group stops redrawing it. */
|
|
38
|
+
detach(): void;
|
|
39
|
+
}
|
|
40
|
+
export interface ChartGroup {
|
|
41
|
+
/** The active selection. A new object on every publish and clear; kept across a new source. */
|
|
42
|
+
readonly selection: ChartGroupSelection;
|
|
43
|
+
readonly columns: readonly any[];
|
|
44
|
+
readonly rows: readonly Record<string, any>[];
|
|
45
|
+
/**
|
|
46
|
+
* The source rows member `id` should be FILTERED to now, or null for all of them. Never the
|
|
47
|
+
* member's own selection. With `filteredBy`, only that partner's selection counts.
|
|
48
|
+
*/
|
|
49
|
+
incomingFor(id: string | undefined, filteredBy?: string): number[] | null;
|
|
50
|
+
/** A payload for a subset of source rows (null = every row), with its row map. */
|
|
51
|
+
payloadFor(sourceRowIdxs: readonly number[] | null): ChartGroupPayload;
|
|
52
|
+
/** The payload member `id` should draw now, under the given options. */
|
|
53
|
+
memberPayload(id: string | undefined, opts?: ChartGroupMemberOptions): ChartGroupPayload;
|
|
54
|
+
/** Publish member `id`'s selection in SOURCE rows. An empty list clears the selection. */
|
|
55
|
+
publish(id: string, sourceRowIdxs: readonly number[]): void;
|
|
56
|
+
/** Clear the group's selection. */
|
|
57
|
+
clear(): void;
|
|
58
|
+
/** Replace the source table (and bindings). The selection is kept, as source indices. */
|
|
59
|
+
setSource(columns: readonly any[], rows: readonly Record<string, any>[], opts?: ChartGroupSourceOptions): void;
|
|
60
|
+
/** Subscribe to every change. Returns the unsubscribe. */
|
|
61
|
+
onChange(cb: (selection: ChartGroupSelection, change: ChartGroupChange) => void): () => void;
|
|
62
|
+
/**
|
|
63
|
+
* Wire a live chart host into the group as member `id` (undefined: a member that responds but
|
|
64
|
+
* never publishes). Create the host with `memberPayload(id, opts).payload` as its data. From
|
|
65
|
+
* then on its clicks publish SOURCE rows (and reach `onSelect`), and every group change hands
|
|
66
|
+
* it a re-derived payload and paints or clears the selection it should show.
|
|
67
|
+
*/
|
|
68
|
+
attach(id: string | undefined, host: ChartHost, opts?: ChartGroupMemberOptions & {
|
|
69
|
+
onSelect?: (sourceRowIdxs: number[]) => void;
|
|
70
|
+
}): ChartGroupMember;
|
|
71
|
+
/** Detach every member and drop every subscriber. */
|
|
72
|
+
destroy(): void;
|
|
73
|
+
}
|
|
74
|
+
/** Payload row indices -> source row indices, dropping any the map does not cover. */
|
|
75
|
+
export declare function toSourceRows(rowMap: readonly number[] | null | undefined, payloadRowIdxs: readonly number[]): number[];
|
|
76
|
+
/**
|
|
77
|
+
* Make a member's painted selection agree with the group's. A member that made the selection
|
|
78
|
+
* keeps what its host already painted. A highlight member with an incoming selection paints it.
|
|
79
|
+
* Any other member that still shows a selection clears it - a stale highlight left on the origin
|
|
80
|
+
* after the selection moved elsewhere, or after a clear. Both paths notify with source "host",
|
|
81
|
+
* so nothing done here is republished.
|
|
82
|
+
*/
|
|
83
|
+
export declare function syncMemberSelection(host: ChartHost, selection: ChartGroupSelection, id: string | undefined, highlight: boolean, incoming: readonly number[] | null): void;
|
|
84
|
+
/**
|
|
85
|
+
* Create a cross-filter group over one source table: `rows` are objects keyed by column name,
|
|
86
|
+
* `columns` the column descriptors (`ingest()` returns both).
|
|
87
|
+
*/
|
|
88
|
+
export declare function createChartGroup(columns: readonly any[], rows: readonly Record<string, any>[], opts?: ChartGroupSourceOptions): ChartGroup;
|
package/dist/types/host.d.ts
CHANGED
|
@@ -235,6 +235,16 @@ export interface ChartHost {
|
|
|
235
235
|
initialMark(): Element | null;
|
|
236
236
|
destroy(): void;
|
|
237
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* ONE d3 OBJECT FROM d3 AND ITS PLUGINS - the object a chart is handed as `d3`.
|
|
240
|
+
*
|
|
241
|
+
* In a bundled app d3 and each plugin (d3-sankey, d3-hexbin, ...) are separate module
|
|
242
|
+
* namespaces, and a chart reaches all of them through one `d3`. A namespace is also FROZEN, and a
|
|
243
|
+
* generated chart installs its own helpers onto the d3 it is given, so the object must be a plain
|
|
244
|
+
* extensible copy: `assembleD3(d3, d3Sankey, d3Hexbin)`. Later arguments win on a name clash.
|
|
245
|
+
* `requiredD3Plugins(code)` names the plugin packages a given chart needs.
|
|
246
|
+
*/
|
|
247
|
+
export declare function assembleD3(base: object, ...plugins: object[]): Record<string, any>;
|
|
238
248
|
export declare function requiredD3Plugins(code: string): string[];
|
|
239
249
|
export declare function explainRenderFailure(err: unknown, d3: any): unknown;
|
|
240
250
|
export declare function stripEsmExports(code: string): string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export { loadGeo, geoFromCache, registerGeo, registerGeoAsset, geoAssetFor, clea
|
|
|
6
6
|
export declare function registerCityTable(packed: string): void;
|
|
7
7
|
export { buildRenderPayload, type RenderPayload, type GeoPointBinding } from "./payload";
|
|
8
8
|
export { codeReadsDatesInLocalTime, vegaSpecReadsDatesInLocalTime } from "./dateCells";
|
|
9
|
-
export { createChartHost, compileRenderFn, stripEsmExports, requiredD3Plugins, explainRenderFailure, sessionViewStateProvider, noopViewStateProvider, type ChartHost, type ChartHostConfig, type RenderFn } from "./host";
|
|
9
|
+
export { createChartHost, compileRenderFn, stripEsmExports, requiredD3Plugins, explainRenderFailure, sessionViewStateProvider, noopViewStateProvider, assembleD3, type ChartHost, type ChartHostConfig, type RenderFn } from "./host";
|
|
10
|
+
export { createChartGroup, toSourceRows, syncMemberSelection, type ChartGroup, type ChartGroupMember, type ChartGroupSelection, type ChartGroupChange, type ChartGroupMemberOptions, type ChartGroupSourceOptions, type ChartGroupPayload } from "./group";
|
|
10
11
|
export { createMarkResolver, isInsideControl, type MarkResolver, type MarkResolverEnv } from "./selection";
|
|
11
12
|
export { planTrivialChart, compileTrivialSource, type TrivialPlan, type TrivialShapeKind } from "./trivial";
|
|
12
13
|
export { captureSvgSnapshot, svgToDataUrl, svgNaturalSize, rasterizeSvgToPngDataUrl, type SnapshotOptions } from "./snapshot";
|
package/dist/types/react.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ReactNode, RefObject } from "react";
|
|
2
2
|
import { type ChartHostConfig } from "./host";
|
|
3
3
|
import type { ResolveOptionsInput } from "./defaults";
|
|
4
|
-
import {
|
|
4
|
+
import type { GeoPointBinding } from "./payload";
|
|
5
|
+
export { assembleD3 } from "./host";
|
|
5
6
|
export interface BicChartProps {
|
|
6
7
|
/** Generated render() source (an ES module's `code` export, or the raw string). */
|
|
7
8
|
code?: string;
|
|
@@ -89,3 +90,15 @@ export interface BicChartGroupProps {
|
|
|
89
90
|
*/
|
|
90
91
|
export declare function BicChartGroup({ rows, columns, geo, point, children }: BicChartGroupProps): import("react").JSX.Element;
|
|
91
92
|
export declare function BicChart(props: BicChartProps): import("react").JSX.Element;
|
|
93
|
+
/**
|
|
94
|
+
* THE SIZE A CHART SHOULD DRAW AT: the element's measured box, kept current as it resizes.
|
|
95
|
+
*
|
|
96
|
+
* A chart draws to `options.width` / `options.height`, and a page that hard-codes them draws the
|
|
97
|
+
* same size in a phone and on a wall. Give the element its size in CSS (a width of 100%, a height
|
|
98
|
+
* or an aspect ratio) and pass what this measures. `{ width: 0, height: 0 }` until the element has
|
|
99
|
+
* been measured - skip rendering until then rather than drawing at zero.
|
|
100
|
+
*/
|
|
101
|
+
export declare function useMeasuredSize(ref: RefObject<Element | null>): {
|
|
102
|
+
width: number;
|
|
103
|
+
height: number;
|
|
104
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-host",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
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",
|