/**
* Data app viz render context — the iframe-side counterpart to the host's
* `useAppSdkBridge` push. A data app viz is a data-agnostic renderer: the host
* owns the query, fetches the rows, and pushes them (plus a field mapping)
* into the iframe. This module is the SDK primitive generated vizs use to
* receive that context, so they never hand-roll a `window.addEventListener`.
*
* Delivery is a handshake: the receiver posts `viz-context-request` once its
* listener is mounted, and the host replies with the current context (and
* re-pushes on every change). `VizContextProvider` owns that handshake at the
* scaffold level — mounted above ``, its effect runs *after* the app's
* own effects (React fires child effects before parent effects), so the
* request is guaranteed to be sent after any listener the app registered. The
* host's reply therefore can't be missed. No timers, no races.
*/
import { type ReactNode } from 'react';
import type { ColumnType, DownloadResultsOptions, DownloadResultsResult, Transport, UnderlyingDataResult } from './types';
/** A single cell of a Lightdash result row: `{ value: { raw, formatted } }`. */
export type VizContextCell = {
value?: {
raw?: unknown;
formatted?: string;
};
};
/** A result row keyed by query field id. */
export type VizContextRow = Record;
/**
* A config option value. Its shape follows the option's declared type:
* `boolean` → boolean, `number` → number, `select`/`text`/`color` → string.
* Series colours are not an option — they arrive on `colorPalette`.
*/
export type VizContextOptionValue = boolean | number | string;
/**
* The host's complete backend-pivot layout metadata. This is a structural
* mirror because query-sdk is published without a dependency on
* `@lightdash/common`.
*/
export type VizContextPivotDetails = {
totalColumnCount: number | null;
indexColumn: {
reference: string;
type: 'time' | 'category';
} | {
reference: string;
type: 'time' | 'category';
}[] | undefined;
valuesColumns: {
referenceField: string;
pivotColumnName: string;
aggregation: string;
pivotValues: {
referenceField: string;
value: unknown;
formatted?: string;
}[];
columnIndex?: number;
}[];
groupByColumns: {
reference: string;
}[] | undefined;
sortBy: {
reference: string;
direction: 'ASC' | 'DESC';
nullsFirst?: boolean;
pivotValues?: {
reference: string;
value: string | number | boolean | null;
}[];
}[] | undefined;
originalColumns: Record;
passthroughDimensions?: {
reference: string;
}[];
};
/**
* Pushed by the host into the iframe. `fieldMapping` maps each field name the
* renderer declared to the query field id it resolves to; `rows` are the
* host-fetched result rows keyed by field id; `options` holds the current
* value of each config option the renderer declared; `colorPalette` is the
* Lightdash palette resolved for this chart, pushed whether or not the
* renderer declared one.
*/
export type DataAppVizContextMessage = {
type: 'lightdash:sdk:data-app-viz-context';
fieldMapping: Record;
rows: VizContextRow[];
/** Absent when the installed host predates config-option delivery. */
options?: Record;
/** Absent when the installed host predates palette delivery. */
colorPalette?: string[];
/** Null for unpivoted rows; absent when the installed host predates pivot metadata delivery. */
pivotDetails?: VizContextPivotDetails | null;
/** Absent when the installed host predates underlying-data delivery. */
underlyingData?: {
enabled?: boolean;
};
/** Absent when the installed host predates drill-down delivery. */
drillDown?: {
enabled?: boolean;
};
};
/** Posted by the iframe on mount so the host pushes the current context. */
export type VizContextRequestMessage = {
type: 'lightdash:sdk:viz-context-request';
};
/** Display string for a field's cell in a row, e.g. `"$1,234"`. Empty when unset. */
export declare const getFormatted: (row: VizContextRow | undefined, fieldId: string | undefined) => string;
/** Raw value for a field's cell in a row (number/string/etc.), or null when unset. */
export declare const getRaw: (row: VizContextRow | undefined, fieldId: string | undefined) => unknown;
/**
* Host-mediated access to the raw rows behind a clicked data point. `enabled`
* is false when the host predates the capability, the viewer lacks permission,
* or no transport is mounted — render no menu item in that case (never a
* disabled one). `row` is the untransformed source row from `rows`; `metric`
* is the declared field NAME bound to the clicked metric slot.
*/
export type VizUnderlyingData = {
enabled: boolean;
get: (opts: {
row: VizContextRow;
metric: string;
limit?: number;
}) => Promise;
download: (opts: {
row: VizContextRow;
metric: string;
} & DownloadResultsOptions) => Promise;
};
export type VizContext = {
/** field name → query field id, as bound in the host field mapping UI. */
fieldMapping: Record;
/** Host-fetched result rows, keyed by query field id. */
rows: VizContextRow[];
/** Config option name → current value (the user's choice, else the declared default). */
options: Record;
/**
* Ordered series colours resolved from the Lightdash palette the viewer
* picked. Colour multi-series charts with
* `colorPalette[i % colorPalette.length]`. Empty only when the host
* resolved no palette; keep a fallback array in your own code for that.
*/
colorPalette: string[];
/** Metadata that maps generated pivot column names back to their metric and series values. */
pivotDetails: VizContextPivotDetails | null;
/** False until the first context arrives — render a placeholder while false. */
ready: boolean;
/** Fetch/export the raw rows behind a clicked data point via the host. */
underlyingData: VizUnderlyingData;
/** Fire a drill-down on a clicked data point; the host opens its drill dialog. */
drillDown: VizDrillDown;
};
type VizContextValue = {
fieldMapping: Record;
rows: VizContextRow[];
options: Record;
colorPalette: string[];
pivotDetails: VizContextPivotDetails | null;
underlyingDataEnabled: boolean;
drillDownEnabled: boolean;
};
type VizContextState = VizContextValue | null;
/**
* Normalises an inbound host message into provider state. Optional capabilities
* are absent from hosts predating them and receive stable fallback values.
*/
export declare function toVizContextState(message: DataAppVizContextMessage): VizContextValue;
/**
* Builds the `underlyingData` surface from the host's availability flag and
* the mounted transport (null when no `LightdashProvider` is present, e.g.
* standalone `useVizContext` usage). Exported for tests.
*/
export declare function buildVizUnderlyingData(hostEnabled: boolean, transport: Transport | null): VizUnderlyingData;
/**
* Host-mediated drill-down for a clicked data point. `enabled` is false when
* the host predates the capability, the viewer lacks permission, results are
* pivoted, or no transport is mounted — render no menu item in that case
* (never a disabled one). `open` fires the intent; the HOST shows the drill
* dialog, nothing renders in the viz.
*/
export type VizDrillDown = {
enabled: boolean;
open: (opts: {
row: VizContextRow;
metric: string;
}) => Promise;
};
/** Builds the `drillDown` surface. Exported for tests. */
export declare function buildVizDrillDown(hostEnabled: boolean, transport: Transport | null): VizDrillDown;
declare const NO_PROVIDER: unique symbol;
/**
* Owns the single listener + handshake for a data app viz. Mount it in the
* scaffold, wrapping ``, so generated renderers receive the host's
* context through `useVizContext()` without hand-rolling a message listener.
*/
export declare function VizContextProvider({ children }: {
children: ReactNode;
}): import("react").FunctionComponentElement>;
/**
* Subscribe to the host's render context. Reads from `VizContextProvider` when
* one is mounted (the scaffold default); otherwise self-subscribes so the hook
* still works standalone. Re-renders whenever the host pushes (on load, on
* mapping change, on query change). Resolve a declared field to its bound cell
* with `fieldMapping[name]` then `getFormatted`/`getRaw`; read a declared
* config option with `options[name]`, and colour series from `colorPalette`.
*/
export declare function useVizContext(): VizContext;
export {};