@graphein/react 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,8 +34,14 @@ export function Dashboard() {
34
34
  ```
35
35
 
36
36
  `<Chart spec={…} />` renders into a fill-by-default container; pass a new `spec`
37
- to update in place, and it tears down on unmount. For headless control over your
38
- own element, use the `useChart(spec)` hook (returns a ref to attach).
37
+ to update in place, and it tears down on unmount. `<Chart>` and `<Dashboard>`
38
+ forward ordinary `<div>` props and refs. For headless control over your own
39
+ element, use the `useChart(spec)` hook (returns a ref to attach).
40
+
41
+ Selection state is safe under React 18 concurrency: `useSelection` subscribes via
42
+ `useSyncExternalStore`, changing only the shared `store` does not drop the next
43
+ spec update, and forwarded refs point at the host `<div>` while `onReady` exposes
44
+ the Graphein chart/dashboard instance.
39
45
 
40
46
  ## Documentation
41
47
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { RefObject, CSSProperties, ReactElement } from 'react';
1
+ import * as react from 'react';
2
+ import { RefObject, HTMLAttributes } from 'react';
2
3
  import { ChartInstance, SelectionStore, SelectionChangeListener, ChartSpec, DashboardInstance, DashboardSpec, SelectionValue } from 'graphein';
3
4
  export { ChartInstance, ChartSpec, DashboardInstance, DashboardSpec, SelectionChangeListener, SelectionStore, SelectionValue, createSelectionStore } from 'graphein';
4
5
 
@@ -36,11 +37,9 @@ interface UseChartOptions {
36
37
  */
37
38
  declare function useChart<T extends HTMLElement = HTMLDivElement>(spec: ChartSpec, options?: UseChartOptions): RefObject<T | null>;
38
39
 
39
- interface ChartProps extends UseChartOptions {
40
+ interface ChartProps extends UseChartOptions, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
40
41
  /** The Graphein chart spec to render. */
41
42
  spec: ChartSpec;
42
- className?: string;
43
- style?: CSSProperties;
44
43
  }
45
44
  /**
46
45
  * Declarative React wrapper around the Graphein core runtime.
@@ -55,7 +54,7 @@ interface ChartProps extends UseChartOptions {
55
54
  * </div>
56
55
  * ```
57
56
  */
58
- declare function Chart({ spec, className, style, ...options }: ChartProps): ReactElement;
57
+ declare const Chart: react.ForwardRefExoticComponent<ChartProps & react.RefAttributes<HTMLDivElement>>;
59
58
 
60
59
  interface UseDashboardOptions {
61
60
  /** Called with the live instance after each mount and update. */
@@ -88,11 +87,9 @@ interface UseDashboardOptions {
88
87
  */
89
88
  declare function useDashboard<T extends HTMLElement = HTMLDivElement>(spec: DashboardSpec, options?: UseDashboardOptions): RefObject<T | null>;
90
89
 
91
- interface DashboardProps extends UseDashboardOptions {
90
+ interface DashboardProps extends UseDashboardOptions, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
92
91
  /** The Graphein dashboard spec to render. */
93
92
  spec: DashboardSpec;
94
- className?: string;
95
- style?: CSSProperties;
96
93
  }
97
94
  /**
98
95
  * Declarative React wrapper around the Graphein dashboard runtime.
@@ -105,7 +102,7 @@ interface DashboardProps extends UseDashboardOptions {
105
102
  * <Dashboard spec={{ type: 'dashboard', data, views, interactions: 'auto' }} />
106
103
  * ```
107
104
  */
108
- declare function Dashboard({ spec, className, style, ...options }: DashboardProps): ReactElement;
105
+ declare const Dashboard: react.ForwardRefExoticComponent<DashboardProps & react.RefAttributes<HTMLDivElement>>;
109
106
 
110
107
  /** Anything that exposes a selection bus: a store, or a chart/dashboard instance. */
111
108
  type SelectionSource = SelectionStore | {
@@ -140,6 +137,6 @@ declare function useSelection(source: SelectionSource, name: string): [Selection
140
137
  * as React state. Core spec/instance types are re-exported for convenience.
141
138
  */
142
139
 
143
- declare const VERSION = "0.0.0";
140
+ declare const VERSION = "0.20.0";
144
141
 
145
142
  export { Chart, type ChartProps, Dashboard, type DashboardProps, type SelectionSource, type UseChartOptions, type UseDashboardOptions, VERSION, useChart, useDashboard, useSelection };
package/dist/index.js CHANGED
@@ -1,5 +1,9 @@
1
1
  // src/Chart.ts
2
- import { createElement } from "react";
2
+ import {
3
+ createElement,
4
+ forwardRef,
5
+ useCallback
6
+ } from "react";
3
7
 
4
8
  // src/useChart.ts
5
9
  import { useEffect, useRef } from "react";
@@ -19,35 +23,35 @@ function useChart(spec, options = {}) {
19
23
  storeRef.current = options.store;
20
24
  const inspectRef = useRef(options.inspect);
21
25
  inspectRef.current = options.inspect;
22
- const skipNextUpdate = useRef(true);
26
+ const instanceSpecRef = useRef(null);
23
27
  useEffect(() => {
24
28
  const el = ref.current;
25
29
  if (!el) return;
26
- const instance = render(el, specRef.current, {
30
+ const mountedSpec = specRef.current;
31
+ const instance = render(el, mountedSpec, {
27
32
  store: storeRef.current,
28
33
  inspect: inspectRef.current
29
34
  });
30
35
  instanceRef.current = instance;
36
+ instanceSpecRef.current = mountedSpec;
31
37
  const offSelection = instance.on(
32
38
  "selectionchange",
33
39
  (name, value) => onSelectionChangeRef.current?.(name, value)
34
40
  );
35
- skipNextUpdate.current = true;
36
41
  onReadyRef.current?.(instance);
37
42
  return () => {
38
43
  offSelection();
39
44
  instance.destroy();
40
45
  instanceRef.current = null;
46
+ instanceSpecRef.current = null;
41
47
  };
42
48
  }, [options.store]);
43
49
  useEffect(() => {
44
- if (skipNextUpdate.current) {
45
- skipNextUpdate.current = false;
46
- return;
47
- }
48
50
  const instance = instanceRef.current;
49
51
  if (!instance) return;
52
+ if (instanceSpecRef.current === spec) return;
50
53
  instance.update(spec);
54
+ instanceSpecRef.current = spec;
51
55
  onReadyRef.current?.(instance);
52
56
  }, [spec]);
53
57
  return ref;
@@ -55,17 +59,34 @@ function useChart(spec, options = {}) {
55
59
 
56
60
  // src/Chart.ts
57
61
  var FILL = { width: "100%", height: "100%" };
58
- function Chart({ spec, className, style, ...options }) {
59
- const ref = useChart(spec, options);
62
+ var Chart = forwardRef(function Chart2({
63
+ spec,
64
+ onReady,
65
+ store,
66
+ onSelectionChange,
67
+ inspect,
68
+ style,
69
+ ...divProps
70
+ }, forwardedRef) {
71
+ const ref = useChart(spec, { onReady, store, onSelectionChange, inspect });
72
+ const attachRef = useCallback((node) => {
73
+ ref.current = node;
74
+ if (typeof forwardedRef === "function") forwardedRef(node);
75
+ else if (forwardedRef) forwardedRef.current = node;
76
+ }, [forwardedRef, ref]);
60
77
  return createElement("div", {
61
- ref,
62
- className,
78
+ ...divProps,
79
+ ref: attachRef,
63
80
  style: style ? { ...FILL, ...style } : FILL
64
81
  });
65
- }
82
+ });
66
83
 
67
84
  // src/Dashboard.ts
68
- import { createElement as createElement2 } from "react";
85
+ import {
86
+ createElement as createElement2,
87
+ forwardRef as forwardRef2,
88
+ useCallback as useCallback2
89
+ } from "react";
69
90
 
70
91
  // src/useDashboard.ts
71
92
  import { useEffect as useEffect2, useRef as useRef2 } from "react";
@@ -85,35 +106,35 @@ function useDashboard(spec, options = {}) {
85
106
  storeRef.current = options.store;
86
107
  const inspectRef = useRef2(options.inspect);
87
108
  inspectRef.current = options.inspect;
88
- const skipNextUpdate = useRef2(true);
109
+ const instanceSpecRef = useRef2(null);
89
110
  useEffect2(() => {
90
111
  const el = ref.current;
91
112
  if (!el) return;
92
- const instance = renderDashboard(el, specRef.current, {
113
+ const mountedSpec = specRef.current;
114
+ const instance = renderDashboard(el, mountedSpec, {
93
115
  store: storeRef.current,
94
116
  inspect: inspectRef.current
95
117
  });
96
118
  instanceRef.current = instance;
119
+ instanceSpecRef.current = mountedSpec;
97
120
  const offSelection = instance.on(
98
121
  "selectionchange",
99
122
  (name, value) => onSelectionChangeRef.current?.(name, value)
100
123
  );
101
- skipNextUpdate.current = true;
102
124
  onReadyRef.current?.(instance);
103
125
  return () => {
104
126
  offSelection();
105
127
  instance.destroy();
106
128
  instanceRef.current = null;
129
+ instanceSpecRef.current = null;
107
130
  };
108
131
  }, [options.store]);
109
132
  useEffect2(() => {
110
- if (skipNextUpdate.current) {
111
- skipNextUpdate.current = false;
112
- return;
113
- }
114
133
  const instance = instanceRef.current;
115
134
  if (!instance) return;
135
+ if (instanceSpecRef.current === spec) return;
116
136
  instance.update(spec);
137
+ instanceSpecRef.current = spec;
117
138
  onReadyRef.current?.(instance);
118
139
  }, [spec]);
119
140
  return ref;
@@ -121,36 +142,46 @@ function useDashboard(spec, options = {}) {
121
142
 
122
143
  // src/Dashboard.ts
123
144
  var FILL2 = { width: "100%" };
124
- function Dashboard({ spec, className, style, ...options }) {
125
- const ref = useDashboard(spec, options);
145
+ var Dashboard = forwardRef2(function Dashboard2({
146
+ spec,
147
+ onReady,
148
+ store,
149
+ onSelectionChange,
150
+ inspect,
151
+ style,
152
+ ...divProps
153
+ }, forwardedRef) {
154
+ const ref = useDashboard(spec, { onReady, store, onSelectionChange, inspect });
155
+ const attachRef = useCallback2((node) => {
156
+ ref.current = node;
157
+ if (typeof forwardedRef === "function") forwardedRef(node);
158
+ else if (forwardedRef) forwardedRef.current = node;
159
+ }, [forwardedRef, ref]);
126
160
  return createElement2("div", {
127
- ref,
128
- className,
161
+ ...divProps,
162
+ ref: attachRef,
129
163
  style: style ? { ...FILL2, ...style } : FILL2
130
164
  });
131
- }
165
+ });
132
166
 
133
167
  // src/useSelection.ts
134
- import { useCallback, useEffect as useEffect3, useState } from "react";
168
+ import { useCallback as useCallback3, useSyncExternalStore } from "react";
135
169
  function resolveStore(source) {
136
170
  if (!source) return null;
137
171
  return "store" in source ? source.store : source;
138
172
  }
139
173
  function useSelection(source, name) {
140
174
  const store = resolveStore(source);
141
- const [value, setValue] = useState(() => store?.get(name) ?? null);
142
- useEffect3(() => {
143
- if (!store) {
144
- setValue(null);
145
- return;
146
- }
147
- setValue(store.get(name) ?? null);
148
- const unsubscribe = store.subscribe((changed, next) => {
149
- if (changed === name) setValue(next);
175
+ const subscribe = useCallback3((notify) => {
176
+ if (!store) return () => {
177
+ };
178
+ return store.subscribe((changed) => {
179
+ if (changed === name) notify();
150
180
  });
151
- return unsubscribe;
152
181
  }, [store, name]);
153
- const set = useCallback(
182
+ const getSnapshot = useCallback3(() => store?.get(name) ?? null, [store, name]);
183
+ const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
184
+ const set = useCallback3(
154
185
  (next) => {
155
186
  store?.set(name, next ?? null);
156
187
  },
@@ -161,7 +192,7 @@ function useSelection(source, name) {
161
192
 
162
193
  // src/index.ts
163
194
  import { createSelectionStore } from "graphein";
164
- var VERSION = "0.0.0";
195
+ var VERSION = "0.20.0";
165
196
  export {
166
197
  Chart,
167
198
  Dashboard,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Chart.ts","../src/useChart.ts","../src/Dashboard.ts","../src/useDashboard.ts","../src/useSelection.ts","../src/index.ts"],"sourcesContent":["import { createElement, type CSSProperties, type ReactElement } from 'react';\nimport type { ChartSpec } from 'graphein';\nimport { useChart, type UseChartOptions } from './useChart';\n\nexport interface ChartProps extends UseChartOptions {\n /** The Graphein chart spec to render. */\n spec: ChartSpec;\n className?: string;\n style?: CSSProperties;\n}\n\nconst FILL: CSSProperties = { width: '100%', height: '100%' };\n\n/**\n * Declarative React wrapper around the Graphein core runtime.\n *\n * Renders a container `<div>` that fills its parent (override via `style`) and\n * draws `spec` into it. Pass a new `spec` to update; unmounting tears the chart\n * down. Give the parent an explicit size for best results.\n *\n * ```tsx\n * <div style={{ height: 360 }}>\n * <Chart spec={{ type: 'line', data, encoding: { x, y } }} />\n * </div>\n * ```\n */\nexport function Chart({ spec, className, style, ...options }: ChartProps): ReactElement {\n const ref = useChart<HTMLDivElement>(spec, options);\n return createElement('div', {\n ref,\n className,\n style: style ? { ...FILL, ...style } : FILL,\n });\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport {\n render,\n type ChartInstance,\n type ChartSpec,\n type SelectionChangeListener,\n type SelectionStore,\n} from 'graphein';\n\nexport interface UseChartOptions {\n /** Called with the live instance after each mount and update. */\n onReady?: (instance: ChartInstance) => void;\n /**\n * A shared selection bus. Pass the same store to several charts (or a\n * dashboard) to link them — cross-highlight / cross-filter. Should be stable\n * across renders (e.g. `useMemo(() => createSelectionStore(), [])`); changing\n * its identity remounts the chart.\n */\n store?: SelectionStore;\n /** Called whenever any selection this chart is bound to changes. */\n onSelectionChange?: SelectionChangeListener;\n /**\n * Publish the chart's config to a `data-graphein-spec` DOM attribute and\n * re-render when an external tool edits it. Defaults to `true`; set `false` to\n * opt out. Read once at mount.\n */\n inspect?: boolean;\n}\n\n/**\n * Mount an Graphein chart into a DOM node and keep it in sync with `spec`.\n *\n * Returns a ref to attach to the container element. The chart is created on\n * mount, re-rendered via `instance.update()` whenever `spec` changes identity,\n * and torn down on unmount. The effect lifecycle is StrictMode-safe (double\n * mount → destroy → mount produces no leaked instances or duplicate renders).\n *\n * ```tsx\n * const ref = useChart(spec);\n * return <div ref={ref} style={{ width: '100%', height: 360 }} />;\n * ```\n */\nexport function useChart<T extends HTMLElement = HTMLDivElement>(\n spec: ChartSpec,\n options: UseChartOptions = {},\n): RefObject<T | null> {\n const ref = useRef<T | null>(null);\n const instanceRef = useRef<ChartInstance | null>(null);\n // Hold the latest spec / callback in refs so the mount effect (which runs\n // once) always reads current values without re-subscribing.\n const specRef = useRef(spec);\n specRef.current = spec;\n const onReadyRef = useRef(options.onReady);\n onReadyRef.current = options.onReady;\n const onSelectionChangeRef = useRef(options.onSelectionChange);\n onSelectionChangeRef.current = options.onSelectionChange;\n const storeRef = useRef(options.store);\n storeRef.current = options.store;\n const inspectRef = useRef(options.inspect);\n inspectRef.current = options.inspect;\n const skipNextUpdate = useRef(true);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const instance = render(el, specRef.current, {\n store: storeRef.current,\n inspect: inspectRef.current,\n });\n instanceRef.current = instance;\n const offSelection = instance.on('selectionchange', (name, value) =>\n onSelectionChangeRef.current?.(name, value),\n );\n // The update effect runs once right after this on mount; skip that pass so\n // we don't redundantly re-render the freshly created chart.\n skipNextUpdate.current = true;\n onReadyRef.current?.(instance);\n return () => {\n offSelection();\n instance.destroy();\n instanceRef.current = null;\n };\n }, [options.store]);\n\n useEffect(() => {\n if (skipNextUpdate.current) {\n skipNextUpdate.current = false;\n return;\n }\n const instance = instanceRef.current;\n if (!instance) return;\n instance.update(spec);\n onReadyRef.current?.(instance);\n }, [spec]);\n\n return ref;\n}\n","import { createElement, type CSSProperties, type ReactElement } from 'react';\nimport type { DashboardSpec } from 'graphein';\nimport { useDashboard, type UseDashboardOptions } from './useDashboard';\n\nexport interface DashboardProps extends UseDashboardOptions {\n /** The Graphein dashboard spec to render. */\n spec: DashboardSpec;\n className?: string;\n style?: CSSProperties;\n}\n\nconst FILL: CSSProperties = { width: '100%' };\n\n/**\n * Declarative React wrapper around the Graphein dashboard runtime.\n *\n * Renders a container `<div>` (full width by default; height comes from the\n * dashboard's grid rows) and draws `spec` into it. Pass a new `spec` to update;\n * unmounting tears it down. Auto-wires cross-interaction unless `spec` opts out.\n *\n * ```tsx\n * <Dashboard spec={{ type: 'dashboard', data, views, interactions: 'auto' }} />\n * ```\n */\nexport function Dashboard({ spec, className, style, ...options }: DashboardProps): ReactElement {\n const ref = useDashboard<HTMLDivElement>(spec, options);\n return createElement('div', {\n ref,\n className,\n style: style ? { ...FILL, ...style } : FILL,\n });\n}\n","import { useEffect, useRef, type RefObject } from 'react';\nimport {\n renderDashboard,\n type DashboardInstance,\n type DashboardSpec,\n type SelectionChangeListener,\n type SelectionStore,\n} from 'graphein';\n\nexport interface UseDashboardOptions {\n /** Called with the live instance after each mount and update. */\n onReady?: (instance: DashboardInstance) => void;\n /**\n * A shared selection bus. Omit to let the dashboard own one internally; pass\n * one (stable across renders) to drive it from React via {@link useSelection}\n * or to link it with standalone charts. Changing its identity remounts.\n */\n store?: SelectionStore;\n /** Called whenever any selection in the dashboard changes. */\n onSelectionChange?: SelectionChangeListener;\n /**\n * Publish each view's config (and the dashboard's) to `data-graphein-spec` DOM\n * attributes and re-render on external edits. Defaults to `true`; set `false` to\n * opt out for the whole dashboard. Read once at mount.\n */\n inspect?: boolean;\n}\n\n/**\n * Mount an Graphein dashboard into a DOM node and keep it in sync with `spec`.\n *\n * Mirrors {@link useChart}: created on mount, updated in place when `spec`\n * changes identity, torn down on unmount, StrictMode-safe.\n *\n * ```tsx\n * const ref = useDashboard(dashboardSpec);\n * return <div ref={ref} style={{ width: '100%' }} />;\n * ```\n */\nexport function useDashboard<T extends HTMLElement = HTMLDivElement>(\n spec: DashboardSpec,\n options: UseDashboardOptions = {},\n): RefObject<T | null> {\n const ref = useRef<T | null>(null);\n const instanceRef = useRef<DashboardInstance | null>(null);\n const specRef = useRef(spec);\n specRef.current = spec;\n const onReadyRef = useRef(options.onReady);\n onReadyRef.current = options.onReady;\n const onSelectionChangeRef = useRef(options.onSelectionChange);\n onSelectionChangeRef.current = options.onSelectionChange;\n const storeRef = useRef(options.store);\n storeRef.current = options.store;\n const inspectRef = useRef(options.inspect);\n inspectRef.current = options.inspect;\n const skipNextUpdate = useRef(true);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const instance = renderDashboard(el, specRef.current, {\n store: storeRef.current,\n inspect: inspectRef.current,\n });\n instanceRef.current = instance;\n const offSelection = instance.on('selectionchange', (name, value) =>\n onSelectionChangeRef.current?.(name, value),\n );\n skipNextUpdate.current = true;\n onReadyRef.current?.(instance);\n return () => {\n offSelection();\n instance.destroy();\n instanceRef.current = null;\n };\n }, [options.store]);\n\n useEffect(() => {\n if (skipNextUpdate.current) {\n skipNextUpdate.current = false;\n return;\n }\n const instance = instanceRef.current;\n if (!instance) return;\n instance.update(spec);\n onReadyRef.current?.(instance);\n }, [spec]);\n\n return ref;\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport type { SelectionStore, SelectionValue } from 'graphein';\n\n/** Anything that exposes a selection bus: a store, or a chart/dashboard instance. */\nexport type SelectionSource =\n | SelectionStore\n | { store: SelectionStore }\n | null\n | undefined;\n\nfunction resolveStore(source: SelectionSource): SelectionStore | null {\n if (!source) return null;\n return 'store' in source ? source.store : source;\n}\n\n/**\n * Read and write a named selection as React state.\n *\n * Pass a shared {@link SelectionStore} (or a chart/dashboard instance, which\n * exposes one as `.store`) plus the param name. Returns the current value and a\n * setter that publishes to the bus — driving cross-highlight / cross-filter\n * across every visual bound to that store.\n *\n * ```tsx\n * const store = useMemo(() => createSelectionStore(), []);\n * const [region, setRegion] = useSelection(store, 'region');\n * // …\n * <Dashboard spec={spec} store={store} />\n * <button onClick={() => setRegion({ kind: 'set', field: 'region', values: ['West'] })}>\n * West\n * </button>\n * ```\n */\nexport function useSelection(\n source: SelectionSource,\n name: string,\n): [SelectionValue | null, (value: SelectionValue | null) => void] {\n const store = resolveStore(source);\n const [value, setValue] = useState<SelectionValue | null>(() => store?.get(name) ?? null);\n\n useEffect(() => {\n if (!store) {\n setValue(null);\n return;\n }\n setValue(store.get(name) ?? null);\n const unsubscribe = store.subscribe((changed, next) => {\n if (changed === name) setValue(next);\n });\n return unsubscribe;\n }, [store, name]);\n\n const set = useCallback(\n (next: SelectionValue | null) => {\n store?.set(name, next ?? null);\n },\n [store, name],\n );\n\n return [value, set];\n}\n","/**\n * @graphein/react — a thin React wrapper around the `graphein` runtime.\n *\n * `<Chart spec={…} />` for a single chart or `<Dashboard spec={…} />` for a\n * cross-interacting page; the `useChart` / `useDashboard` hooks give headless\n * control over your own container, and `useSelection` reads/writes a selection\n * as React state. Core spec/instance types are re-exported for convenience.\n */\nexport { Chart, type ChartProps } from './Chart';\nexport { useChart, type UseChartOptions } from './useChart';\nexport { Dashboard, type DashboardProps } from './Dashboard';\nexport { useDashboard, type UseDashboardOptions } from './useDashboard';\nexport { useSelection, type SelectionSource } from './useSelection';\nexport { createSelectionStore } from 'graphein';\nexport type {\n ChartSpec,\n ChartInstance,\n DashboardSpec,\n DashboardInstance,\n SelectionStore,\n SelectionValue,\n SelectionChangeListener,\n} from 'graphein';\n\nexport const VERSION = '0.0.0';\n"],"mappings":";AAAA,SAAS,qBAA4D;;;ACArE,SAAS,WAAW,cAA8B;AAClD;AAAA,EACE;AAAA,OAKK;AAmCA,SAAS,SACd,MACA,UAA2B,CAAC,GACP;AACrB,QAAM,MAAM,OAAiB,IAAI;AACjC,QAAM,cAAc,OAA6B,IAAI;AAGrD,QAAM,UAAU,OAAO,IAAI;AAC3B,UAAQ,UAAU;AAClB,QAAM,aAAa,OAAO,QAAQ,OAAO;AACzC,aAAW,UAAU,QAAQ;AAC7B,QAAM,uBAAuB,OAAO,QAAQ,iBAAiB;AAC7D,uBAAqB,UAAU,QAAQ;AACvC,QAAM,WAAW,OAAO,QAAQ,KAAK;AACrC,WAAS,UAAU,QAAQ;AAC3B,QAAM,aAAa,OAAO,QAAQ,OAAO;AACzC,aAAW,UAAU,QAAQ;AAC7B,QAAM,iBAAiB,OAAO,IAAI;AAElC,YAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,WAAW,OAAO,IAAI,QAAQ,SAAS;AAAA,MAC3C,OAAO,SAAS;AAAA,MAChB,SAAS,WAAW;AAAA,IACtB,CAAC;AACD,gBAAY,UAAU;AACtB,UAAM,eAAe,SAAS;AAAA,MAAG;AAAA,MAAmB,CAAC,MAAM,UACzD,qBAAqB,UAAU,MAAM,KAAK;AAAA,IAC5C;AAGA,mBAAe,UAAU;AACzB,eAAW,UAAU,QAAQ;AAC7B,WAAO,MAAM;AACX,mBAAa;AACb,eAAS,QAAQ;AACjB,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,QAAQ,KAAK,CAAC;AAElB,YAAU,MAAM;AACd,QAAI,eAAe,SAAS;AAC1B,qBAAe,UAAU;AACzB;AAAA,IACF;AACA,UAAM,WAAW,YAAY;AAC7B,QAAI,CAAC,SAAU;AACf,aAAS,OAAO,IAAI;AACpB,eAAW,UAAU,QAAQ;AAAA,EAC/B,GAAG,CAAC,IAAI,CAAC;AAET,SAAO;AACT;;;ADrFA,IAAM,OAAsB,EAAE,OAAO,QAAQ,QAAQ,OAAO;AAerD,SAAS,MAAM,EAAE,MAAM,WAAW,OAAO,GAAG,QAAQ,GAA6B;AACtF,QAAM,MAAM,SAAyB,MAAM,OAAO;AAClD,SAAO,cAAc,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,QAAQ,EAAE,GAAG,MAAM,GAAG,MAAM,IAAI;AAAA,EACzC,CAAC;AACH;;;AEjCA,SAAS,iBAAAA,sBAA4D;;;ACArE,SAAS,aAAAC,YAAW,UAAAC,eAA8B;AAClD;AAAA,EACE;AAAA,OAKK;AAgCA,SAAS,aACd,MACA,UAA+B,CAAC,GACX;AACrB,QAAM,MAAMA,QAAiB,IAAI;AACjC,QAAM,cAAcA,QAAiC,IAAI;AACzD,QAAM,UAAUA,QAAO,IAAI;AAC3B,UAAQ,UAAU;AAClB,QAAM,aAAaA,QAAO,QAAQ,OAAO;AACzC,aAAW,UAAU,QAAQ;AAC7B,QAAM,uBAAuBA,QAAO,QAAQ,iBAAiB;AAC7D,uBAAqB,UAAU,QAAQ;AACvC,QAAM,WAAWA,QAAO,QAAQ,KAAK;AACrC,WAAS,UAAU,QAAQ;AAC3B,QAAM,aAAaA,QAAO,QAAQ,OAAO;AACzC,aAAW,UAAU,QAAQ;AAC7B,QAAM,iBAAiBA,QAAO,IAAI;AAElC,EAAAD,WAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,WAAW,gBAAgB,IAAI,QAAQ,SAAS;AAAA,MACpD,OAAO,SAAS;AAAA,MAChB,SAAS,WAAW;AAAA,IACtB,CAAC;AACD,gBAAY,UAAU;AACtB,UAAM,eAAe,SAAS;AAAA,MAAG;AAAA,MAAmB,CAAC,MAAM,UACzD,qBAAqB,UAAU,MAAM,KAAK;AAAA,IAC5C;AACA,mBAAe,UAAU;AACzB,eAAW,UAAU,QAAQ;AAC7B,WAAO,MAAM;AACX,mBAAa;AACb,eAAS,QAAQ;AACjB,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,QAAQ,KAAK,CAAC;AAElB,EAAAA,WAAU,MAAM;AACd,QAAI,eAAe,SAAS;AAC1B,qBAAe,UAAU;AACzB;AAAA,IACF;AACA,UAAM,WAAW,YAAY;AAC7B,QAAI,CAAC,SAAU;AACf,aAAS,OAAO,IAAI;AACpB,eAAW,UAAU,QAAQ;AAAA,EAC/B,GAAG,CAAC,IAAI,CAAC;AAET,SAAO;AACT;;;AD9EA,IAAME,QAAsB,EAAE,OAAO,OAAO;AAarC,SAAS,UAAU,EAAE,MAAM,WAAW,OAAO,GAAG,QAAQ,GAAiC;AAC9F,QAAM,MAAM,aAA6B,MAAM,OAAO;AACtD,SAAOC,eAAc,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,OAAO,QAAQ,EAAE,GAAGD,OAAM,GAAG,MAAM,IAAIA;AAAA,EACzC,CAAC;AACH;;;AE/BA,SAAS,aAAa,aAAAE,YAAW,gBAAgB;AAUjD,SAAS,aAAa,QAAgD;AACpE,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,WAAW,SAAS,OAAO,QAAQ;AAC5C;AAoBO,SAAS,aACd,QACA,MACiE;AACjE,QAAM,QAAQ,aAAa,MAAM;AACjC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAgC,MAAM,OAAO,IAAI,IAAI,KAAK,IAAI;AAExF,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,OAAO;AACV,eAAS,IAAI;AACb;AAAA,IACF;AACA,aAAS,MAAM,IAAI,IAAI,KAAK,IAAI;AAChC,UAAM,cAAc,MAAM,UAAU,CAAC,SAAS,SAAS;AACrD,UAAI,YAAY,KAAM,UAAS,IAAI;AAAA,IACrC,CAAC;AACD,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,IAAI,CAAC;AAEhB,QAAM,MAAM;AAAA,IACV,CAAC,SAAgC;AAC/B,aAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC/B;AAAA,IACA,CAAC,OAAO,IAAI;AAAA,EACd;AAEA,SAAO,CAAC,OAAO,GAAG;AACpB;;;AC/CA,SAAS,4BAA4B;AAW9B,IAAM,UAAU;","names":["createElement","useEffect","useRef","FILL","createElement","useEffect"]}
1
+ {"version":3,"sources":["../src/Chart.ts","../src/useChart.ts","../src/Dashboard.ts","../src/useDashboard.ts","../src/useSelection.ts","../src/index.ts"],"sourcesContent":["import {\n createElement,\n forwardRef,\n useCallback,\n type CSSProperties,\n type HTMLAttributes,\n type MutableRefObject,\n type ReactElement,\n} from 'react';\nimport type { ChartSpec } from 'graphein';\nimport { useChart, type UseChartOptions } from './useChart';\n\nexport interface ChartProps extends UseChartOptions, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /** The Graphein chart spec to render. */\n spec: ChartSpec;\n}\n\nconst FILL: CSSProperties = { width: '100%', height: '100%' };\n\n/**\n * Declarative React wrapper around the Graphein core runtime.\n *\n * Renders a container `<div>` that fills its parent (override via `style`) and\n * draws `spec` into it. Pass a new `spec` to update; unmounting tears the chart\n * down. Give the parent an explicit size for best results.\n *\n * ```tsx\n * <div style={{ height: 360 }}>\n * <Chart spec={{ type: 'line', data, encoding: { x, y } }} />\n * </div>\n * ```\n */\nexport const Chart = forwardRef<HTMLDivElement, ChartProps>(function Chart({\n spec,\n onReady,\n store,\n onSelectionChange,\n inspect,\n style,\n ...divProps\n}, forwardedRef): ReactElement {\n const ref = useChart<HTMLDivElement>(spec, { onReady, store, onSelectionChange, inspect });\n const attachRef = useCallback((node: HTMLDivElement | null) => {\n ref.current = node;\n if (typeof forwardedRef === 'function') forwardedRef(node);\n else if (forwardedRef) (forwardedRef as MutableRefObject<HTMLDivElement | null>).current = node;\n }, [forwardedRef, ref]);\n return createElement('div', {\n ...divProps,\n ref: attachRef,\n style: style ? { ...FILL, ...style } : FILL,\n });\n});\n","import { useEffect, useRef, type RefObject } from 'react';\nimport {\n render,\n type ChartInstance,\n type ChartSpec,\n type SelectionChangeListener,\n type SelectionStore,\n} from 'graphein';\n\nexport interface UseChartOptions {\n /** Called with the live instance after each mount and update. */\n onReady?: (instance: ChartInstance) => void;\n /**\n * A shared selection bus. Pass the same store to several charts (or a\n * dashboard) to link them — cross-highlight / cross-filter. Should be stable\n * across renders (e.g. `useMemo(() => createSelectionStore(), [])`); changing\n * its identity remounts the chart.\n */\n store?: SelectionStore;\n /** Called whenever any selection this chart is bound to changes. */\n onSelectionChange?: SelectionChangeListener;\n /**\n * Publish the chart's config to a `data-graphein-spec` DOM attribute and\n * re-render when an external tool edits it. Defaults to `true`; set `false` to\n * opt out. Read once at mount.\n */\n inspect?: boolean;\n}\n\n/**\n * Mount an Graphein chart into a DOM node and keep it in sync with `spec`.\n *\n * Returns a ref to attach to the container element. The chart is created on\n * mount, re-rendered via `instance.update()` whenever `spec` changes identity,\n * and torn down on unmount. The effect lifecycle is StrictMode-safe (double\n * mount → destroy → mount produces no leaked instances or duplicate renders).\n *\n * ```tsx\n * const ref = useChart(spec);\n * return <div ref={ref} style={{ width: '100%', height: 360 }} />;\n * ```\n */\nexport function useChart<T extends HTMLElement = HTMLDivElement>(\n spec: ChartSpec,\n options: UseChartOptions = {},\n): RefObject<T | null> {\n const ref = useRef<T | null>(null);\n const instanceRef = useRef<ChartInstance | null>(null);\n // Hold the latest spec / callback in refs so the mount effect (which runs\n // once) always reads current values without re-subscribing.\n const specRef = useRef(spec);\n specRef.current = spec;\n const onReadyRef = useRef(options.onReady);\n onReadyRef.current = options.onReady;\n const onSelectionChangeRef = useRef(options.onSelectionChange);\n onSelectionChangeRef.current = options.onSelectionChange;\n const storeRef = useRef(options.store);\n storeRef.current = options.store;\n const inspectRef = useRef(options.inspect);\n inspectRef.current = options.inspect;\n const instanceSpecRef = useRef<ChartSpec | null>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const mountedSpec = specRef.current;\n const instance = render(el, mountedSpec, {\n store: storeRef.current,\n inspect: inspectRef.current,\n });\n instanceRef.current = instance;\n instanceSpecRef.current = mountedSpec;\n const offSelection = instance.on('selectionchange', (name, value) =>\n onSelectionChangeRef.current?.(name, value),\n );\n onReadyRef.current?.(instance);\n return () => {\n offSelection();\n instance.destroy();\n instanceRef.current = null;\n instanceSpecRef.current = null;\n };\n }, [options.store]);\n\n useEffect(() => {\n const instance = instanceRef.current;\n if (!instance) return;\n if (instanceSpecRef.current === spec) return;\n instance.update(spec);\n instanceSpecRef.current = spec;\n onReadyRef.current?.(instance);\n }, [spec]);\n\n return ref;\n}\n","import {\n createElement,\n forwardRef,\n useCallback,\n type CSSProperties,\n type HTMLAttributes,\n type MutableRefObject,\n type ReactElement,\n} from 'react';\nimport type { DashboardSpec } from 'graphein';\nimport { useDashboard, type UseDashboardOptions } from './useDashboard';\n\nexport interface DashboardProps extends UseDashboardOptions, Omit<HTMLAttributes<HTMLDivElement>, 'children'> {\n /** The Graphein dashboard spec to render. */\n spec: DashboardSpec;\n}\n\nconst FILL: CSSProperties = { width: '100%' };\n\n/**\n * Declarative React wrapper around the Graphein dashboard runtime.\n *\n * Renders a container `<div>` (full width by default; height comes from the\n * dashboard's grid rows) and draws `spec` into it. Pass a new `spec` to update;\n * unmounting tears it down. Auto-wires cross-interaction unless `spec` opts out.\n *\n * ```tsx\n * <Dashboard spec={{ type: 'dashboard', data, views, interactions: 'auto' }} />\n * ```\n */\nexport const Dashboard = forwardRef<HTMLDivElement, DashboardProps>(function Dashboard({\n spec,\n onReady,\n store,\n onSelectionChange,\n inspect,\n style,\n ...divProps\n}, forwardedRef): ReactElement {\n const ref = useDashboard<HTMLDivElement>(spec, { onReady, store, onSelectionChange, inspect });\n const attachRef = useCallback((node: HTMLDivElement | null) => {\n ref.current = node;\n if (typeof forwardedRef === 'function') forwardedRef(node);\n else if (forwardedRef) (forwardedRef as MutableRefObject<HTMLDivElement | null>).current = node;\n }, [forwardedRef, ref]);\n return createElement('div', {\n ...divProps,\n ref: attachRef,\n style: style ? { ...FILL, ...style } : FILL,\n });\n});\n","import { useEffect, useRef, type RefObject } from 'react';\nimport {\n renderDashboard,\n type DashboardInstance,\n type DashboardSpec,\n type SelectionChangeListener,\n type SelectionStore,\n} from 'graphein';\n\nexport interface UseDashboardOptions {\n /** Called with the live instance after each mount and update. */\n onReady?: (instance: DashboardInstance) => void;\n /**\n * A shared selection bus. Omit to let the dashboard own one internally; pass\n * one (stable across renders) to drive it from React via {@link useSelection}\n * or to link it with standalone charts. Changing its identity remounts.\n */\n store?: SelectionStore;\n /** Called whenever any selection in the dashboard changes. */\n onSelectionChange?: SelectionChangeListener;\n /**\n * Publish each view's config (and the dashboard's) to `data-graphein-spec` DOM\n * attributes and re-render on external edits. Defaults to `true`; set `false` to\n * opt out for the whole dashboard. Read once at mount.\n */\n inspect?: boolean;\n}\n\n/**\n * Mount an Graphein dashboard into a DOM node and keep it in sync with `spec`.\n *\n * Mirrors {@link useChart}: created on mount, updated in place when `spec`\n * changes identity, torn down on unmount, StrictMode-safe.\n *\n * ```tsx\n * const ref = useDashboard(dashboardSpec);\n * return <div ref={ref} style={{ width: '100%' }} />;\n * ```\n */\nexport function useDashboard<T extends HTMLElement = HTMLDivElement>(\n spec: DashboardSpec,\n options: UseDashboardOptions = {},\n): RefObject<T | null> {\n const ref = useRef<T | null>(null);\n const instanceRef = useRef<DashboardInstance | null>(null);\n const specRef = useRef(spec);\n specRef.current = spec;\n const onReadyRef = useRef(options.onReady);\n onReadyRef.current = options.onReady;\n const onSelectionChangeRef = useRef(options.onSelectionChange);\n onSelectionChangeRef.current = options.onSelectionChange;\n const storeRef = useRef(options.store);\n storeRef.current = options.store;\n const inspectRef = useRef(options.inspect);\n inspectRef.current = options.inspect;\n const instanceSpecRef = useRef<DashboardSpec | null>(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const mountedSpec = specRef.current;\n const instance = renderDashboard(el, mountedSpec, {\n store: storeRef.current,\n inspect: inspectRef.current,\n });\n instanceRef.current = instance;\n instanceSpecRef.current = mountedSpec;\n const offSelection = instance.on('selectionchange', (name, value) =>\n onSelectionChangeRef.current?.(name, value),\n );\n onReadyRef.current?.(instance);\n return () => {\n offSelection();\n instance.destroy();\n instanceRef.current = null;\n instanceSpecRef.current = null;\n };\n }, [options.store]);\n\n useEffect(() => {\n const instance = instanceRef.current;\n if (!instance) return;\n if (instanceSpecRef.current === spec) return;\n instance.update(spec);\n instanceSpecRef.current = spec;\n onReadyRef.current?.(instance);\n }, [spec]);\n\n return ref;\n}\n","import { useCallback, useSyncExternalStore } from 'react';\nimport type { SelectionStore, SelectionValue } from 'graphein';\n\n/** Anything that exposes a selection bus: a store, or a chart/dashboard instance. */\nexport type SelectionSource =\n | SelectionStore\n | { store: SelectionStore }\n | null\n | undefined;\n\nfunction resolveStore(source: SelectionSource): SelectionStore | null {\n if (!source) return null;\n return 'store' in source ? source.store : source;\n}\n\n/**\n * Read and write a named selection as React state.\n *\n * Pass a shared {@link SelectionStore} (or a chart/dashboard instance, which\n * exposes one as `.store`) plus the param name. Returns the current value and a\n * setter that publishes to the bus — driving cross-highlight / cross-filter\n * across every visual bound to that store.\n *\n * ```tsx\n * const store = useMemo(() => createSelectionStore(), []);\n * const [region, setRegion] = useSelection(store, 'region');\n * // …\n * <Dashboard spec={spec} store={store} />\n * <button onClick={() => setRegion({ kind: 'set', field: 'region', values: ['West'] })}>\n * West\n * </button>\n * ```\n */\nexport function useSelection(\n source: SelectionSource,\n name: string,\n): [SelectionValue | null, (value: SelectionValue | null) => void] {\n const store = resolveStore(source);\n\n const subscribe = useCallback((notify: () => void) => {\n if (!store) return () => {};\n return store.subscribe((changed) => {\n if (changed === name) notify();\n });\n }, [store, name]);\n\n const getSnapshot = useCallback(() => store?.get(name) ?? null, [store, name]);\n const value = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n const set = useCallback(\n (next: SelectionValue | null) => {\n store?.set(name, next ?? null);\n },\n [store, name],\n );\n\n return [value, set];\n}\n","/**\n * @graphein/react — a thin React wrapper around the `graphein` runtime.\n *\n * `<Chart spec={…} />` for a single chart or `<Dashboard spec={…} />` for a\n * cross-interacting page; the `useChart` / `useDashboard` hooks give headless\n * control over your own container, and `useSelection` reads/writes a selection\n * as React state. Core spec/instance types are re-exported for convenience.\n */\nexport { Chart, type ChartProps } from './Chart';\nexport { useChart, type UseChartOptions } from './useChart';\nexport { Dashboard, type DashboardProps } from './Dashboard';\nexport { useDashboard, type UseDashboardOptions } from './useDashboard';\nexport { useSelection, type SelectionSource } from './useSelection';\nexport { createSelectionStore } from 'graphein';\nexport type {\n ChartSpec,\n ChartInstance,\n DashboardSpec,\n DashboardInstance,\n SelectionStore,\n SelectionValue,\n SelectionChangeListener,\n} from 'graphein';\n\nexport const VERSION = '0.20.0';\n"],"mappings":"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","names":["Chart","createElement","forwardRef","useCallback","useEffect","useRef","FILL","forwardRef","Dashboard","useCallback","createElement","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphein/react",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "type": "module",
5
5
  "description": "Graphein React wrapper — declarative <Chart spec={...} /> for the Graphein visualization engine.",
6
6
  "license": "MIT",
@@ -46,17 +46,18 @@
46
46
  "access": "public"
47
47
  },
48
48
  "scripts": {
49
- "build": "tsup",
49
+ "sync:version": "node ../core/scripts/sync-version.mjs .",
50
+ "build": "npm run sync:version && tsup",
50
51
  "dev": "tsup --watch",
51
52
  "test": "vitest run",
52
53
  "typecheck": "tsc --noEmit",
53
- "prepack": "tsup"
54
+ "prepack": "npm run sync:version && tsup"
54
55
  },
55
56
  "peerDependencies": {
56
57
  "react": ">=18"
57
58
  },
58
59
  "dependencies": {
59
- "graphein": "^0.18.0"
60
+ "graphein": "^0.20.0"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@types/react": "^19.2.17",