mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 10:12:34 +00:00
Data inspector: input/resolved rows below the chart, with a resizable divider
A collapsible Data panel under the Live Preview and Chart Builder charts shows the rows the chart actually uses, switching between Input (parsed source) and Resolved (post-transform) views read from the live Vega view. Collapsed by default; the open-state and a draggable height divider persist. Rows come through a new RenderHandle.inspectData() accessor, so no component touches the view: core/result-data picks the most-upstream source and most-downstream result from the compiled dataflow, read lazily. The divider reuses the window-splitter pattern (horizontal variant). Consolidations: a shared DataTable primitive replaces the inspector's and the builder's duplicate read-only tables; useResizeDrag merges the col/row drag-gesture twins. Docs: spec 04/06 and arch 05/10 updated; the now-shipped exploration memo removed.
This commit is contained in:
@@ -12,6 +12,7 @@ import type { VisualizationSpec } from 'vega-embed';
|
||||
import type { Config } from 'vega-lite';
|
||||
import { collectFontFamilies } from '@core/custom-theme';
|
||||
import { embedFontsInSvg } from '@core/chart-export';
|
||||
import { pickResultDataset, pickSourceDataset } from '@core/result-data';
|
||||
import type { FontAsset } from '@core/font-asset';
|
||||
|
||||
/** Options for `RenderHandle.toImageURL` (spec §08 → Per-chart export). */
|
||||
@@ -43,6 +44,15 @@ interface ImageExportOptions {
|
||||
embedFonts?: ReadonlyArray<FontAsset>;
|
||||
}
|
||||
|
||||
/** The two ends of the chart's data pipeline, for the data inspector (spec §04). */
|
||||
export interface InspectedData {
|
||||
/** Parsed source rows, before the spec's transforms run — the input. */
|
||||
input: ReadonlyArray<Record<string, unknown>>;
|
||||
/** Post-transform rows the chart draws — the output (equals `input` when the
|
||||
* spec has no transforms). */
|
||||
resolved: ReadonlyArray<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
export interface RenderHandle {
|
||||
/** Finalize the underlying Vega view and clear the node. */
|
||||
destroy(): void;
|
||||
@@ -67,6 +77,21 @@ export interface RenderHandle {
|
||||
* (fixed ones have no such handler), which is exactly right for Width/Height.
|
||||
*/
|
||||
resize(): void;
|
||||
/**
|
||||
* The chart's input and resolved (post-transform) rows — for the data inspector
|
||||
* (spec §04). Reads the live view's compiled dataflow once: lists its datasets,
|
||||
* picks the most-upstream
|
||||
* source and most-downstream result (`@core/result-data`), and returns both
|
||||
* tables' rows. This is the one place besides export that reaches into the view,
|
||||
* so the embedding boundary holds (arch 05 §1–§2) — callers get rows, never the
|
||||
* `view`.
|
||||
*
|
||||
* Returns `null` when there is nothing to inspect (the view was finalized, or
|
||||
* the spec produced no inspectable table). Either side can be `[]` when its
|
||||
* table is empty — a real signal (e.g. a filter removed every row on the
|
||||
* resolved side), kept distinct from "no chart" so the inspector can say which.
|
||||
*/
|
||||
inspectData(): InspectedData | null;
|
||||
}
|
||||
|
||||
export interface RenderOptions {
|
||||
@@ -235,8 +260,13 @@ export async function renderSpec(
|
||||
tooltip: { disableDefaultStyle: true },
|
||||
});
|
||||
|
||||
// Reads of a finalized view throw; `inspectData` checks this to no-op safely
|
||||
// after destroy() (the inspector may read on a render that resolved late).
|
||||
let finalized = false;
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
finalized = true;
|
||||
result.view.finalize();
|
||||
node.replaceChildren();
|
||||
},
|
||||
@@ -265,5 +295,24 @@ export async function renderSpec(
|
||||
// no-op after destroy().
|
||||
if (typeof window !== 'undefined') window.dispatchEvent(new Event('resize'));
|
||||
},
|
||||
inspectData() {
|
||||
if (finalized) return null;
|
||||
// Enumerate the dataflow's datasets once, then pick the input + result
|
||||
// tables. getState with a truthy `data` filter is Vega's documented way to
|
||||
// list datasets (vega/editor's Data Viewer does the same) — we read only the
|
||||
// keys. Rows come from view.data(name), which hands back the live array (no copy).
|
||||
const state = result.view.getState({
|
||||
data: () => true,
|
||||
signals: () => false,
|
||||
recurse: true,
|
||||
}) as { data?: Record<string, unknown> };
|
||||
const names = Object.keys(state.data ?? {});
|
||||
const sourceName = pickSourceDataset(names);
|
||||
const resultName = pickResultDataset(names);
|
||||
if (sourceName === null && resultName === null) return null;
|
||||
const rows = (name: string | null): ReadonlyArray<Record<string, unknown>> =>
|
||||
name === null ? [] : ((result.view.data(name) ?? []) as Record<string, unknown>[]);
|
||||
return { input: rows(sourceName), resolved: rows(resultName) };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user