@bicharts/chart-host 0.6.7 → 0.6.9

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.
@@ -3848,9 +3848,27 @@ function toSourceRows(rowMap, payloadRowIdxs) {
3848
3848
  if (!rowMap) return payloadRowIdxs.slice();
3849
3849
  return payloadRowIdxs.map((i) => rowMap[i]).filter((i) => i !== void 0);
3850
3850
  }
3851
- function syncMemberSelection(host, selection, id, highlight, incoming) {
3851
+ function toPayloadRows(rowMap, sourceRowIdxs) {
3852
+ const at3 = /* @__PURE__ */ new Map();
3853
+ rowMap.forEach((src, k3) => {
3854
+ if (!at3.has(src)) at3.set(src, k3);
3855
+ });
3856
+ return sourceRowIdxs.map((s) => at3.get(s)).filter((k3) => k3 !== void 0);
3857
+ }
3858
+ var sameRows = (a4, b4) => {
3859
+ if (a4.length !== b4.length) return false;
3860
+ const x2 = a4.slice().sort((p2, q3) => p2 - q3), y5 = b4.slice().sort((p2, q3) => p2 - q3);
3861
+ return x2.every((v4, i) => v4 === y5[i]);
3862
+ };
3863
+ function syncMemberSelection(host, selection, id, highlight, incoming, rowMap) {
3852
3864
  const mine = selection.sourceId === id && selection.rows.length > 0;
3853
- if (mine) return;
3865
+ if (mine) {
3866
+ if (rowMap) {
3867
+ const want = toPayloadRows(rowMap, selection.rows);
3868
+ if (!sameRows(want, host.selection.current ?? [])) host.selection.highlight(want);
3869
+ }
3870
+ return;
3871
+ }
3854
3872
  if (highlight && incoming && incoming.length) {
3855
3873
  host.selection.highlight(incoming.slice());
3856
3874
  return;
@@ -3918,18 +3936,24 @@ function createChartGroup(columns, rows, opts = {}) {
3918
3936
  attach(id, host, o = {}) {
3919
3937
  const highlight = o.respondsWith === "highlight";
3920
3938
  let rowMap = memberPayload(id, o).rowMap;
3939
+ const filterKey = (incoming) => highlight || !incoming ? "*" : incoming.join(",");
3940
+ let drawnUnder = filterKey(incomingFor(id, o.filteredBy));
3921
3941
  const offHost = host.selection.onChange((payloadIdxs, source) => {
3922
3942
  if (source === "host") return;
3923
3943
  const sourceIdxs = toSourceRows(rowMap, payloadIdxs);
3924
3944
  if (id) group.publish(id, sourceIdxs);
3925
3945
  o.onSelect?.(sourceIdxs);
3926
3946
  });
3927
- const offGroup = group.onChange((sel) => {
3947
+ const offGroup = group.onChange((sel, change) => {
3928
3948
  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);
3949
+ const key = filterKey(incoming);
3950
+ if (change === "source" || key !== drawnUnder) {
3951
+ const next = payloadFor(highlight ? null : incoming);
3952
+ rowMap = next.rowMap;
3953
+ drawnUnder = key;
3954
+ host.setData(next.payload);
3955
+ }
3956
+ syncMemberSelection(host, sel, id, highlight, incoming, rowMap);
3933
3957
  });
3934
3958
  const member = {
3935
3959
  get rowMap() {
package/dist/index.mjs CHANGED
@@ -139,7 +139,7 @@ import {
139
139
  unpinScrolledAxis,
140
140
  valuePlacementFlag,
141
141
  withoutViewOnlyKeys
142
- } from "./chunk-RKOFRMS4.mjs";
142
+ } from "./chunk-GJF6V6BZ.mjs";
143
143
  import "./chunk-A2GMXZP7.mjs";
144
144
  import "./chunk-GHODWOAL.mjs";
145
145
 
package/dist/react.mjs CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  loadGeo,
8
8
  syncMemberSelection,
9
9
  toSourceRows
10
- } from "./chunk-RKOFRMS4.mjs";
10
+ } from "./chunk-GJF6V6BZ.mjs";
11
11
  import "./chunk-A2GMXZP7.mjs";
12
12
  import "./chunk-GHODWOAL.mjs";
13
13
 
@@ -35,11 +35,7 @@ function BicChartGroup({ rows, columns, geo, point, children }) {
35
35
  useEffect(() => group.onChange((s, change) => {
36
36
  if (change !== "source") setSel(s);
37
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
+ const value = useMemo(() => ({ group, selection: sel, source }), [group, sel, source]);
43
39
  return /* @__PURE__ */ jsx(Ctx.Provider, { value, children });
44
40
  }
45
41
  function BicChart(props) {
@@ -71,9 +67,11 @@ function BicChart(props) {
71
67
  const built = useMemo(() => {
72
68
  if (!group) return null;
73
69
  return group.payloadFor(filterSel);
74
- }, [ctx, filterSel && filterSel.join(",")]);
70
+ }, [group, ctx?.source, filterSel && filterSel.join(",")]);
75
71
  const data = built ? built.payload : props.data;
76
72
  rowMapRef.current = built ? built.rowMap : null;
73
+ const optKey = useMemo(() => JSON.stringify(options ?? {}), [options]);
74
+ const builtWithRef = useRef(null);
77
75
  const onSelectRef = useRef(onSelect);
78
76
  onSelectRef.current = onSelect;
79
77
  const onLabelContrastRef = useRef(onLabelContrast);
@@ -124,23 +122,29 @@ function BicChart(props) {
124
122
  onSelectRef.current?.(sourceIdxs);
125
123
  });
126
124
  host.render();
125
+ builtWithRef.current = { data, optKey };
127
126
  return () => {
128
127
  off();
129
128
  host.destroy();
130
129
  hostRef.current = null;
131
130
  };
132
131
  }, [code, renderFn, d3, geoKind, labelContrast]);
133
- const optKey = useMemo(() => JSON.stringify(options ?? {}), [options]);
134
132
  useEffect(() => {
135
- if (hostRef.current && options) hostRef.current.setOptions(options);
133
+ if (!hostRef.current || !options) return;
134
+ if (builtWithRef.current?.optKey === optKey) return;
135
+ builtWithRef.current = { ...builtWithRef.current, optKey };
136
+ hostRef.current.setOptions(options);
136
137
  }, [optKey]);
137
138
  useEffect(() => {
138
- if (hostRef.current && data) hostRef.current.setData(data);
139
+ if (!hostRef.current || !data) return;
140
+ if (builtWithRef.current?.data === data) return;
141
+ builtWithRef.current = { ...builtWithRef.current, data };
142
+ hostRef.current.setData(data);
139
143
  }, [data]);
140
144
  useEffect(() => {
141
145
  const host = hostRef.current;
142
146
  if (!host || !ctx) return;
143
- syncMemberSelection(host, ctx.selection, id, highlightMode, incoming);
147
+ syncMemberSelection(host, ctx.selection, id, highlightMode, incoming, rowMapRef.current);
144
148
  }, [ctx?.selection, id, ctx, highlightMode, incoming && incoming.join(",")]);
145
149
  return /* @__PURE__ */ jsx("div", { ref, className, style });
146
150
  }
@@ -74,13 +74,20 @@ export interface ChartGroup {
74
74
  /** Payload row indices -> source row indices, dropping any the map does not cover. */
75
75
  export declare function toSourceRows(rowMap: readonly number[] | null | undefined, payloadRowIdxs: readonly number[]): number[];
76
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.
77
+ * Make a member's painted selection agree with the group's. A highlight member with an incoming
78
+ * selection paints it. Any other member that still shows a selection clears it - a stale
79
+ * highlight left on the origin after the selection moved elsewhere, or after a clear.
80
+ *
81
+ * THE MEMBER THAT MADE THE SELECTION keeps it, repainted in the payload it draws NOW when
82
+ * `rowMap` (that payload's row map) is given. Its host holds the selection as PAYLOAD rows, and
83
+ * a member that was filtered when it was clicked stops filtering the moment it becomes the
84
+ * origin - its payload grows back to the whole table and renumbers, so the payload rows its
85
+ * host holds would name different records. The group's selection is in source rows, so it is
86
+ * translated through the new map and repainted when it differs.
87
+ *
88
+ * Every path notifies with source "host", so nothing done here is republished.
82
89
  */
83
- export declare function syncMemberSelection(host: ChartHost, selection: ChartGroupSelection, id: string | undefined, highlight: boolean, incoming: readonly number[] | null): void;
90
+ export declare function syncMemberSelection(host: ChartHost, selection: ChartGroupSelection, id: string | undefined, highlight: boolean, incoming: readonly number[] | null, rowMap?: readonly number[] | null): void;
84
91
  /**
85
92
  * Create a cross-filter group over one source table: `rows` are objects keyed by column name,
86
93
  * `columns` the column descriptors (`ingest()` returns both).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
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",