mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Inspector: live data updates under interactive selections
This commit is contained in:
@@ -25,7 +25,12 @@ vi.mock('../services/chart-renderer', () => {
|
||||
}
|
||||
return {
|
||||
renderSpec: vi.fn(() =>
|
||||
Promise.resolve({ destroy() {}, resize() {}, inspectData: () => null }),
|
||||
Promise.resolve({
|
||||
destroy() {},
|
||||
resize() {},
|
||||
inspectData: () => null,
|
||||
onDataChange: () => () => {},
|
||||
}),
|
||||
),
|
||||
ChartTooLargeError,
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ vi.mock('../services/chart-renderer', () => ({
|
||||
resize() {},
|
||||
toImageURL: () => Promise.resolve(''),
|
||||
inspectData: () => null,
|
||||
onDataChange: () => () => {},
|
||||
}),
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -73,7 +73,12 @@ interface DataInspectorPanelProps {
|
||||
* `renderEpoch`).
|
||||
*/
|
||||
getData: () => InspectedData | null;
|
||||
/** Bumps whenever a render settles, so the open table re-reads the new rows. */
|
||||
/**
|
||||
* Refresh trigger: bumps whenever the data to show may have changed, so the open
|
||||
* table re-reads. The live-preview pane bumps it on each settled render *and* on
|
||||
* an interactive selection that changes the inspected rows (live mode, M5); the
|
||||
* builder bumps it on render only.
|
||||
*/
|
||||
renderEpoch: number;
|
||||
/**
|
||||
* Explicit panel height (px) — the live-preview pane sets this from its
|
||||
|
||||
@@ -45,6 +45,7 @@ vi.mock('../services/chart-renderer', () => ({
|
||||
},
|
||||
resize() {},
|
||||
inspectData: () => null,
|
||||
onDataChange: () => () => {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -211,6 +211,13 @@ export function LivePreview() {
|
||||
// not `chartReady` — consecutive successful renders keep `chartReady` true, but
|
||||
// each one is new data the inspector must pick up.
|
||||
const [renderEpoch, setRenderEpoch] = useState(0);
|
||||
// Bumped (debounced, inside the handle) when an interactive selection changes
|
||||
// the inspected data without a re-render — the live data inspector (spec §04;
|
||||
// multi-view scope doc M5). Kept separate from `renderEpoch` so a brush pulse
|
||||
// re-reads the table without re-subscribing the listener; their sum is the
|
||||
// inspector's single refresh trigger (each event bumps exactly one, so the sum
|
||||
// is strictly monotonic — no collisions).
|
||||
const [liveEpoch, setLiveEpoch] = useState(0);
|
||||
|
||||
// Busy-indication timer ref: if a render exceeds ~1s we surface a non-blocking
|
||||
// overlay (arch §10.2 NN/g: >1s owes a busy indication; <1s shows nothing to
|
||||
@@ -397,6 +404,19 @@ export function LivePreview() {
|
||||
// on `renderEpoch`, so this need not depend on it (it always reads the latest handle).
|
||||
const getInspectData = useCallback(() => handleRef.current?.inspectData() ?? null, []);
|
||||
|
||||
// Live data inspection (spec §04): while the inspector is open, re-read the table
|
||||
// when an interactive selection changes the data it shows (a filtering brush). The
|
||||
// handle owns the Vega listeners + debounce; we just bump `liveEpoch` on each fire.
|
||||
// Re-subscribes whenever a render settles (`renderEpoch`) so it tracks the current
|
||||
// handle, and only while the inspector is open so a collapsed one costs nothing.
|
||||
// handleRef is a ref (read, not a dep); the cleanup unsubscribes.
|
||||
useEffect(() => {
|
||||
if (!inspectorOpen) return;
|
||||
const handle = handleRef.current;
|
||||
if (!handle) return;
|
||||
return handle.onDataChange(() => setLiveEpoch((e) => e + 1));
|
||||
}, [inspectorOpen, renderEpoch]);
|
||||
|
||||
// Re-fit the chart when its container resizes (e.g. a pane drag). Vega doesn't
|
||||
// observe the element, so we do: one observer on the stable host node for the
|
||||
// component's life. Only responsive fit modes depend on container size;
|
||||
@@ -478,7 +498,7 @@ export function LivePreview() {
|
||||
<DataInspector
|
||||
id="preview-data-inspector"
|
||||
getData={getInspectData}
|
||||
renderEpoch={renderEpoch}
|
||||
renderEpoch={renderEpoch + liveEpoch}
|
||||
heightPx={inspectorOpen ? inspectorHeight : undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,13 @@ import { Onboarding } from './Onboarding';
|
||||
// never touches vega-embed. A resolved no-op handle is enough — Onboarding only
|
||||
// finalizes it on unmount.
|
||||
vi.mock('../services/chart-renderer', () => ({
|
||||
renderSpec: () => Promise.resolve({ destroy() {}, resize() {}, inspectData: () => null }),
|
||||
renderSpec: () =>
|
||||
Promise.resolve({
|
||||
destroy() {},
|
||||
resize() {},
|
||||
inspectData: () => null,
|
||||
onDataChange: () => () => {},
|
||||
}),
|
||||
}));
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
@@ -21,6 +21,7 @@ const okHandle = () => ({
|
||||
resize() {},
|
||||
toImageURL: () => Promise.resolve(''),
|
||||
inspectData: () => null,
|
||||
onDataChange: () => () => {},
|
||||
});
|
||||
|
||||
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
@@ -22,6 +22,7 @@ vi.mock('../services/chart-renderer', () => ({
|
||||
resize() {},
|
||||
toImageURL: () => Promise.resolve(''),
|
||||
inspectData: () => null,
|
||||
onDataChange: () => () => {},
|
||||
}),
|
||||
),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user