/** * 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 {};