Data transformation and configuration for chart rendering using ECharts and other visualization libraries.
The visualization module provides data model classes that transform query results into visualization-ready formats. These classes handle:
Enum for controlling the position of value labels on chart elements.
enum ValueLabelPositionOptions {
HIDDEN = 'hidden',
TOP = 'top',
BOTTOM = 'bottom',
LEFT = 'left',
RIGHT = 'right',
INSIDE = 'inside'
}Controls where value labels appear on chart elements (bars, points, etc.):
HIDDEN: No value labels displayedTOP: Labels above chart elementsBOTTOM: Labels below chart elementsLEFT: Labels to the left of chart elementsRIGHT: Labels to the right of chart elementsINSIDE: Labels inside chart elementsMain class for transforming data for cartesian charts (line, bar, area, scatter, mixed).
class CartesianChartDataModel {
/**
* Create a new CartesianChartDataModel
* @param args.resultsRunner - Results runner interface providing query results
* @param args.fieldConfig - Optional pivot chart layout configuration
* @param args.type - Optional cartesian chart kind (BAR, LINE, AREA, SCATTER, MIXED)
*/
constructor(args: {
resultsRunner: IResultsRunner;
fieldConfig?: PivotChartLayout;
type?: CartesianChartKind;
});
/**
* Get visualization configuration options including pivot layout and ECharts config
* @returns VizCartesianChartOptions with layout configuration and chart settings
*/
getChartOptions(): VizCartesianChartOptions;
/**
* Merge existing configuration with new chart kind
* @param chartKind - Cartesian chart kind to use
* @param existingConfig - Existing visualization configuration to merge
* @returns Merged visualization configuration
*/
mergeConfig(
chartKind: CartesianChartKind,
existingConfig: VizCartesianChartConfig | undefined
): VizCartesianChartConfig;
/**
* Get default layout configuration for the chart
* @returns Default pivot chart layout or undefined
*/
getDefaultLayout(): PivotChartLayout | undefined;
/**
* Validate field configuration and return any errors
* @param fieldConfig - Optional field configuration to validate
* @returns Configuration errors if any, or undefined
*/
getConfigErrors(fieldConfig?: PivotChartLayout): VizConfigErrors | undefined;
/**
* Get transformed and pivoted data for the chart
* @param query - SQL runner query with sort, filters, limit, and SQL
* @returns Promise resolving to pivot chart data
*/
async getTransformedData(query?: SqlRunnerQuery): Promise<PivotChartData | undefined>;
/**
* Get pivoted chart data with applied filters and sorting
* @param args.sortBy - Sort configuration
* @param args.filters - Filter rules to apply
* @param args.limit - Result limit
* @param args.sql - SQL query
* @returns Promise resolving to pivot chart data
*/
async getPivotedChartData(args: {
sortBy?: SqlRunnerQuery['sortBy'];
filters?: SqlRunnerQuery['filters'];
limit?: SqlRunnerQuery['limit'];
sql: SqlRunnerQuery['sql'];
}): Promise<PivotChartData | undefined>;
/**
* Get pivoted data in table format
* @returns Object with columns and rows, or undefined
*/
getPivotedTableData(): { columns: string[]; rows: RawResultRow[] } | undefined;
/**
* Get download URL for exporting chart data
* @returns Download URL string or undefined
*/
getDataDownloadUrl(): string | undefined;
/**
* Get Vega-Lite specification for the chart
* @param display - Optional cartesian chart display configuration
* @param colors - Optional organization chart colors
* @returns Vega-Lite specification object
*/
getSpec(
display?: CartesianChartDisplay,
colors?: Organization['chartColors']
): Record<string, any>;
/**
* Get tooltip formatter function for a given format
* @param format - Format configuration
* @returns Formatter function or undefined
*/
static getTooltipFormatter(
format: Format | undefined
): ((value: number) => string) | undefined;
/**
* Get value formatter function for a given format
* @param format - Format configuration
* @param isStack100 - Whether using 100% stacking
* @returns Formatter function or undefined
*/
static getValueFormatter(
format: Format | undefined,
isStack100?: boolean
): ((params: any) => string) | undefined;
/**
* Get default color for series by index
* @param index - Series index
* @param orgColors - Optional organization color palette
* @returns Color string
*/
static getDefaultColor(index: number, orgColors?: string[]): string;
}Example:
import { CartesianChartDataModel, ChartKind } from '@lightdash/common';
const dataModel = new CartesianChartDataModel({
resultsRunner: queryResults,
fieldConfig: pivotChartLayout,
type: ChartKind.VERTICAL_BAR
});
// Get ECharts options for rendering
const echartsOptions = dataModel.getChartOptions();
// Use with ECharts library
echarts.init(container).setOption(echartsOptions);Class for transforming data for pie and donut charts.
class PieChartDataModel {
/**
* Create a new PieChartDataModel
* @param args.resultsRunner - Results runner interface providing query results
* @param args.fieldConfig - Optional pivot chart layout configuration
*/
constructor(args: {
resultsRunner: IResultsRunner;
fieldConfig?: PivotChartLayout;
});
/**
* Get default layout configuration for pie charts
* @returns Default pivot chart layout or undefined
*/
getDefaultLayout(): PivotChartLayout | undefined;
/**
* Merge existing configuration with new chart kind
* @param chartKind - Must be ChartKind.PIE
* @param currentVizConfig - Current visualization configuration to merge
* @returns Merged pie chart configuration
*/
mergeConfig(
chartKind: ChartKind.PIE,
currentVizConfig?: VizPieChartConfig
): VizPieChartConfig;
/**
* Get available field options for pie chart configuration
* @returns Object with group field options, metric field options, and custom metric options
*/
getResultOptions(): {
groupFieldOptions: any[];
metricFieldOptions: any[];
customMetricFieldOptions: any[];
};
/**
* Validate configuration and return any errors
* @param config - Optional pivot chart layout to validate
* @returns Configuration errors if any, or undefined
*/
getConfigErrors(config?: PivotChartLayout): VizConfigErrors | undefined;
/**
* Get transformed data for the pie chart
* @param query - SQL runner query or undefined
* @returns Promise resolving to pivot chart data
*/
async getTransformedData(
query: SqlRunnerQuery | undefined
): Promise<PivotChartData | undefined>;
/**
* Get pivoted chart data with applied filters and sorting
* @param args.sortBy - Sort configuration
* @param args.filters - Filter rules to apply
* @param args.limit - Result limit
* @param args.sql - SQL query
* @returns Promise resolving to pivot chart data
*/
async getPivotedChartData(args: {
sortBy?: SqlRunnerQuery['sortBy'];
filters?: SqlRunnerQuery['filters'];
limit?: SqlRunnerQuery['limit'];
sql: SqlRunnerQuery['sql'];
}): Promise<PivotChartData | undefined>;
/**
* Get pivoted data in table format
* @returns Object with columns and rows, or undefined
*/
getPivotedTableData(): { columns: string[]; rows: RawResultRow[] } | undefined;
/**
* Get download URL for exporting chart data
* @returns Download URL string or undefined
*/
getDataDownloadUrl(): string | undefined;
/**
* Get Vega-Lite specification for the pie chart
* @param display - Optional pie chart display configuration
* @returns Vega-Lite specification object
*/
getSpec(display?: VizPieChartDisplay): Record<string, any>;
}Example:
import { PieChartDataModel } from '@lightdash/common';
const dataModel = new PieChartDataModel({
resultsRunner: queryResults,
fieldConfig: pivotChartLayout
});
const spec = dataModel.getSpec();Class for transforming data for table visualizations.
class TableDataModel {
/**
* Create a new TableDataModel
* @param args.resultsRunner - Results runner interface providing query results
* @param args.columnsConfig - Optional column configuration for the table
*/
constructor(args: {
resultsRunner: IResultsRunner;
columnsConfig?: VizTableConfig['columns'] | undefined;
});
/**
* Get visible column names only
* @returns Array of visible column names
*/
public getVisibleColumns(): string[];
/**
* Get all data rows
* @returns Array of raw result rows
*/
public getRows(): RawResultRow[];
/**
* Get total number of rows
* @returns Row count
*/
public getRowsCount(): number;
/**
* Get total number of columns
* @returns Column count
*/
public getColumnsCount(): number;
/**
* Get table header sort configuration from query
* @param columnNames - Array of column names
* @param query - SQL runner query with sort settings
* @returns Table header sort configuration
*/
static getTableHeaderSortConfig(
columnNames: string[],
query: SqlRunnerQuery
): VizTableHeaderSortConfig;
/**
* Get available result options including default column config
* @returns Object with default column configuration
*/
public getResultOptions(): {
defaultColumnConfig: VizTableConfig['columns'];
};
/**
* Get current column configuration
* @returns Current columns config or undefined
*/
public getConfig(): VizTableConfig['columns'] | undefined;
/**
* Get accessor function for a specific column
* @param column - Column name
* @returns Function to access column value from a row
*/
static getColumnsAccessorFn(column: string): (row: RawResultRow) => unknown;
/**
* Merge configuration for table chart
* @param chartKind - Must be ChartKind.TABLE
* @returns Merged table configuration
*/
mergeConfig(chartKind: ChartKind.TABLE): VizTableConfig;
/**
* Get pivoted chart data (returns raw data for tables)
* @returns Promise resolving to pivot chart data
*/
async getPivotedChartData(): Promise<PivotChartData>;
/**
* Get download URL for exporting table data
* @returns Download URL string or undefined
*/
getDataDownloadUrl(): string | undefined;
/**
* Get pivoted table data
* @returns Object with columns and rows, or undefined
*/
getPivotedTableData(): { columns: string[]; rows: RawResultRow[] } | undefined;
/**
* Get Vega-Lite specification for the table
* @param _display - Optional table display configuration (unused)
* @returns Object with spec and table data
*/
getSpec(_display?: VizTableDisplay): {
spec: Record<string, any>;
tableData: { columns: string[]; rows: RawResultRow[] } | undefined;
};
}Example:
import { TableDataModel } from '@lightdash/common';
const dataModel = new TableDataModel({
resultsRunner: queryResults,
columnsConfig: tableConfig.columns
});
const columns = dataModel.getVisibleColumns();
const rows = dataModel.getRows();
// Render table
columns.forEach(col => {
renderColumn(col);
});
rows.forEach(row => {
columns.forEach(col => {
renderCell(row[col]);
});
});interface VizColumn {
reference: string;
/** @deprecated Use DimensionType instead. This property is optional and may be undefined. */
type?: DimensionType;
}Configuration for chart visual display properties.
/**
* Display configuration for CartesianChartDataModel
* Used with the getSpec() method to customize chart appearance
*/
type CartesianChartDisplay = {
xAxis?: {
label?: string;
type?: VizIndexType;
};
yAxis?: {
label?: string;
position?: string;
format?: Format;
}[];
series?: {
[key: string]: {
label?: string;
format?: Format;
yAxisIndex?: number;
color?: string;
type?: CartesianSeriesType.LINE | CartesianSeriesType.BAR;
valueLabelPosition?: ValueLabelPositionOptions;
whichYAxis?: AxisSide;
};
};
legend?: {
position: 'top' | 'bottom' | 'left' | 'right';
align: 'start' | 'center' | 'end';
};
stack?: boolean | StackType;
};
interface VizPieChartDisplay {
isDonut?: boolean;
legend?: object; // ECharts legend configuration
labels?: object; // ECharts label configuration
}
interface VizTableDisplay {
showColumnCalculation?: boolean;
showRowCalculation?: boolean;
showTableNames?: boolean;
}enum VizAggregationOptions {
SUM = 'sum',
COUNT = 'count',
AVERAGE = 'avg',
MIN = 'min',
MAX = 'max',
ANY = 'any',
}
const vizAggregationOptions: VizAggregationOptions[];
const VIZ_DEFAULT_AGGREGATION: VizAggregationOptions;enum SortByDirection {
ASC = 'ASC',
DESC = 'DESC',
}
interface VizSortBy {
reference: string;
direction: SortByDirection;
}enum VizIndexType {
TIME = 'time',
CATEGORY = 'category',
}
function getColumnAxisType(dimensionType: DimensionType): VizIndexType;
function getTableCalculationAxisType(tableCalculationType: TableCalculationType): VizIndexType;enum AxisSide {
LEFT = 0,
RIGHT = 1,
}enum StackType {
NONE = 'none',
NORMAL = 'stack',
PERCENT = 'stack100',
}interface VizCartesianChartOptions {
indexLayoutOptions: VizIndexLayoutOptions[];
valuesLayoutOptions: {
preAggregated: VizValuesLayoutOptions[];
customAggregations: VizCustomMetricLayoutOptions[];
};
pivotLayoutOptions: VizPivotLayoutOptions[];
}
interface VizIndexLayoutOptions {
axisType: VizIndexType;
dimensionType: DimensionType;
aggregationOptions?: VizAggregationOptions[];
reference: string;
}
interface VizValuesLayoutOptions {
reference: string;
aggregation: VizAggregationOptions;
aggregationOptions?: VizAggregationOptions[];
}
interface VizCustomMetricLayoutOptions {
axisType: VizIndexType;
dimensionType: DimensionType;
aggregation: VizAggregationOptions;
aggregationOptions?: VizAggregationOptions[];
reference: string;
}
interface VizPivotLayoutOptions {
reference: string;
}/**
* Field options available for configuring pie charts
* Returned by PieChartDataModel.getResultOptions()
*/
interface VizPieChartOptions {
/** Dimension fields available for grouping pie slices */
groupFieldOptions: VizIndexLayoutOptions[];
/** Pre-aggregated metric fields available as slice values */
metricFieldOptions: VizValuesLayoutOptions[];
/** Custom metric fields (dimensions with aggregations) available as slice values */
customMetricFieldOptions: VizCustomMetricLayoutOptions[];
}interface PivotChartLayout {
/** X-axis field with reference and type */
x: {
reference: string;
type: VizIndexType;
} | undefined;
/** Y-axis fields with reference and aggregation */
y: {
reference: string;
aggregation: VizAggregationOptions;
}[];
/** Group by fields for series grouping */
groupBy: {
reference: string;
}[] | undefined;
/** Sort configuration */
sortBy?: VizSortBy[];
/** Stacking configuration */
stack?: boolean | StackType;
}Note: PivotChartLayout is currently used by both CartesianChartDataModel and PieChartDataModel, even though the x/y structure is cartesian-specific. This design may evolve in future versions to have separate layout types for different chart kinds.
interface PivotChartData {
/** Unique identifier for the query execution */
queryUuid: string | undefined;
/** URL to the file containing results (if stored externally) */
fileUrl: string | undefined;
/** Transformed data rows for chart rendering */
results: RawResultRow[];
/** Index column configuration for the pivot */
indexColumn: PivotConfiguration['indexColumn'] | undefined;
/** Values columns configuration for metrics */
valuesColumns: PivotValuesColumn[];
/** All column metadata including generated pivot columns */
columns: VizColumn[];
/** Total number of columns in the result set */
columnCount: number | undefined;
}
interface PivotValuesColumn {
/** Reference to the underlying metric field */
referenceField: string;
/** Display name for the pivot column */
pivotColumnName: string;
/** Aggregation function applied to the metric */
aggregation: VizAggregationOptions;
/** Pivot dimension values that generated this column */
pivotValues: {
referenceField: string;
value: unknown;
formatted?: string;
}[];
/** Column index in the result set */
columnIndex?: number;
}
function isPivotChartLayout(layout: unknown): layout is PivotChartLayout;Complete type definitions for visualization chart configurations used by the visualization data models.
/**
* Base configuration shared by all viz chart types
*/
interface VizBaseConfig {
metadata: {
version: number;
};
type: ChartKind;
}
/**
* Configuration for Cartesian charts (bar and line charts)
* Used with CartesianChartDataModel
*/
type VizCartesianChartConfig = VizBaseConfig & {
type: ChartKind.VERTICAL_BAR | ChartKind.LINE;
/** Field configuration defining x-axis, y-axis, and grouping */
fieldConfig: PivotChartLayout | undefined;
/** Display options for visual customization */
display: CartesianChartDisplay | undefined;
};
/**
* Configuration specific to bar charts
*/
type VizBarChartConfig = VizBaseConfig & {
type: ChartKind.VERTICAL_BAR;
fieldConfig: PivotChartLayout | undefined;
display: CartesianChartDisplay | undefined;
};
/**
* Configuration specific to line charts
*/
type VizLineChartConfig = VizBaseConfig & {
type: ChartKind.LINE;
fieldConfig: PivotChartLayout | undefined;
display: CartesianChartDisplay | undefined;
};
/**
* Configuration for pie and donut charts
* Used with PieChartDataModel
*/
type VizPieChartConfig = VizBaseConfig & {
type: ChartKind.PIE;
/** Field configuration defining grouping dimension and metric */
fieldConfig: PivotChartLayout | undefined;
/** Display options including donut mode */
display: VizPieChartDisplay | undefined;
};
/**
* Configuration for table visualizations
* Used with TableDataModel
*/
type VizTableConfig = VizBaseConfig & {
type: ChartKind.TABLE;
/** Column-specific configuration for visibility, labels, order, and styling */
columns: VizColumnsConfig;
/** Display options for table features */
display: VizTableDisplay | undefined;
};
/**
* Union type of all viz chart configurations
*/
type AllVizChartConfig =
| VizBarChartConfig
| VizLineChartConfig
| VizPieChartConfig
| VizTableConfig;/**
* Configuration for individual table columns
*/
interface VizColumnConfig {
/** Whether the column is visible */
visible: boolean;
/** Column field reference */
reference: string;
/** Display label for the column */
label: string;
/** Whether the column is frozen (sticky) during horizontal scrolling */
frozen: boolean;
/** Display order of the column */
order?: number;
/** Aggregation function for the column */
aggregation?: VizAggregationOptions;
/** Display style for column values */
displayStyle?: 'text' | 'bar';
/** Bar chart configuration for bar-style columns */
barConfig?: {
/** Minimum value for bar scaling (auto-calculated if not specified) */
min?: number;
/** Maximum value for bar scaling (auto-calculated if not specified) */
max?: number;
/** Bar color (default: '#5470c6') */
color?: string;
};
}
/**
* Map of column references to their configurations
*/
type VizColumnsConfig = { [columnReference: string]: VizColumnConfig };
/**
* Container for columns configuration used in table charts
*/
interface VizTableColumnsConfig {
columns: VizColumnsConfig;
}function isVizBarChartConfig(config: unknown): config is VizBarChartConfig;
function isVizLineChartConfig(config: unknown): config is VizLineChartConfig;
function isVizCartesianChartConfig(config: unknown): config is VizCartesianChartConfig;
function isVizPieChartConfig(config: unknown): config is VizPieChartConfig;
function isVizTableConfig(config: unknown): config is VizTableConfig;Helper functions for generating ECharts style configurations.
function getAxisLabelStyle(): {
color: string;
fontWeight: string;
fontSize: number;
};
function getAxisTitleStyle(): {
color: string;
fontWeight: string;
fontSize: number;
};
function getAxisLineStyle(): {
show: boolean;
lineStyle: {
color: string;
type: 'solid';
};
};
function getAxisTickStyle(show?: boolean): {
show: boolean;
lineStyle: {
color: string;
type: 'solid';
};
};
function getAxisPointerStyle(useLinePointer?: boolean): {
type: 'line' | 'shadow';
lineStyle?: {
color: string;
type: number[];
width: number;
};
label: {
show: boolean;
fontWeight: number;
fontSize: number;
color: string;
backgroundColor: string;
};
};function getLineChartGridStyle(): GridStyle;
function getBarChartGridStyle(): GridStyle;function calculateDynamicBorderRadius(
dataPointCount: number,
seriesCount: number,
isStacked: boolean,
isHorizontal: boolean = false
): number;
function getBarBorderRadius(
isHorizontal: boolean,
isStackEnd: boolean,
radius: number = 4
): number | number[];
function getBarStyle(): {
barCategoryGap: '25%';
};
/**
* Get bar total label styling for stack totals displayed above or beside stacked bars
*/
function getBarTotalLabelStyle(): {
color: string;
fontWeight: string;
fontSize: number;
};
/**
* Resolve the tuple index for x/y axis given encode configuration and dimension names
*/
function getIndexFromEncode(
enc: EChartsSeries['encode'] | SqlRunnerEChartsSeries['encode'],
dimNames: string[] | undefined,
which: 'x' | 'y'
): number | undefined;
/**
* Apply rounded corners to stacked bars (Explorer format with nested result values)
*/
function applyRoundedCornersToStackData(
series: EChartsSeries[],
rows: ResultRow[],
options?: {
radius?: number;
isHorizontal?: boolean;
legendSelected?: { [name: string]: boolean } | undefined;
}
): EChartsSeries[];
/**
* Apply rounded corners to stacked bars (SQL Runner format with flat result values)
*/
function applyRoundedCornersToSqlRunnerStackData(
series: SqlRunnerEChartsSeries[],
rows: RawResultRow[],
options?: {
radius?: number;
isHorizontal?: boolean;
legendSelected?: { [name: string]: boolean } | undefined;
}
): SqlRunnerEChartsSeries[];The vizThemeColors namespace provides theme color constants for ECharts styling. See the Theme Colors section under Style Helpers for detailed documentation of all available constants.
interface AxisStyle {
show?: boolean;
name?: string;
nameLocation?: 'start' | 'middle' | 'end';
nameGap?: number;
min?: number | string;
max?: number | string;
axisLine?: {
show?: boolean;
lineStyle?: LineStyle;
};
axisTick?: {
show?: boolean;
};
axisLabel?: {
show?: boolean;
formatter?: string | Function;
rotate?: number;
};
splitLine?: {
show?: boolean;
lineStyle?: LineStyle;
};
}
interface LineStyle {
color?: string;
width?: number;
type?: 'solid' | 'dashed' | 'dotted';
}interface GridStyle {
left?: number | string;
right?: number | string;
top?: number | string;
bottom?: number | string;
containLabel?: boolean;
backgroundColor?: string;
borderColor?: string;
borderWidth?: number;
}interface LegendStyle {
show?: boolean;
type?: 'plain' | 'scroll';
orient?: 'horizontal' | 'vertical';
left?: number | string;
right?: number | string;
top?: number | string;
bottom?: number | string;
align?: 'auto' | 'left' | 'right';
padding?: number | number[];
itemGap?: number;
itemWidth?: number;
itemHeight?: number;
textStyle?: TextStyle;
}
interface TextStyle {
color?: string;
fontStyle?: 'normal' | 'italic' | 'oblique';
fontWeight?: 'normal' | 'bold' | 'bolder' | 'lighter' | number;
fontSize?: number;
}Helper functions for formatting, styling, and transforming visualization data.
/**
* Get axis formatter configuration for cartesian charts
* Handles axis labels, pointers, formatting, and rotation
* @param config - Configuration object
* @returns Axis configuration object with formatter functions and styling
*/
function getCartesianAxisFormatterConfig(config: {
axisItem: ItemsMap[string] | undefined;
longestLabelWidth?: number;
rotate?: number;
defaultNameGap?: number;
show?: boolean;
parameters?: Record<string, unknown>;
}): {
axisLabel?: object;
axisLine?: object;
axisTick?: object;
axisPointer?: object;
nameGap?: number;
minInterval?: number;
};Example:
import { getCartesianAxisFormatterConfig } from '@lightdash/common';
const yAxisConfig = getCartesianAxisFormatterConfig({
axisItem: itemsMap['orders_total_revenue'],
longestLabelWidth: 100,
rotate: 0,
defaultNameGap: 15,
show: true,
parameters: { param1: 'value1' }
});
const chartOptions = {
yAxis: {
...yAxisConfig,
name: 'Revenue'
}
};/**
* Build tooltip formatter for Explorer cartesian charts
* @param config - Tooltip formatter configuration
* @returns Tooltip formatter function for ECharts (callback that receives tooltip params and returns HTML string)
*/
function buildCartesianTooltipFormatter(config: {
itemsMap: ItemsMap;
explore: Explore;
cartesianConfig: VizCartesianChartConfig;
format: 'value' | 'raw';
}): (params: any) => string;
/**
* Build tooltip formatter for SQL Runner cartesian charts
* @param config - SQL Runner tooltip configuration
* @returns Tooltip formatter function for ECharts (callback that receives tooltip params and returns HTML string)
*/
function buildSqlRunnerCartesianTooltipFormatter(config: {
itemsMap: ItemsMap;
series: SqlRunnerEChartsSeries[];
pivotValuesColumnsMap?: Record<string, PivotValuesColumn> | null;
parameters?: ParametersValuesMap;
}): (params: any) => string;
/**
* Create tooltip formatter for 100% stacking mode
* Displays both percentage and absolute values
* @param config - Stack 100 configuration
* @returns Tooltip formatter function (callback that receives tooltip params and returns HTML string)
*/
function createStack100TooltipFormatter(config: {
itemsMap: ItemsMap;
series: EChartsSeries[];
}): (params: any) => string;
/**
* Transform series data to percentage stacking
* Converts absolute values to percentages based on total at each data point
* @param data - Series data array (array of ECharts series data objects)
* @returns Transformed series data with percentage values
*/
function transformToPercentageStacking(data: any[]): any[];Example:
import {
buildCartesianTooltipFormatter,
buildSqlRunnerCartesianTooltipFormatter,
createStack100TooltipFormatter,
transformToPercentageStacking,
} from '@lightdash/common';
// Explorer tooltip
const explorerTooltip = buildCartesianTooltipFormatter({
itemsMap,
explore,
cartesianConfig,
format: 'value'
});
// SQL Runner tooltip
const sqlRunnerTooltip = buildSqlRunnerCartesianTooltipFormatter({
itemsMap,
series: chartSeries,
pivotValuesColumnsMap: null,
parameters: { param1: 'value1' }
});
// 100% stacking tooltip
const stack100Tooltip = createStack100TooltipFormatter({
itemsMap,
series: chartSeries
});
// Transform data for percentage stacking
const percentageData = transformToPercentageStacking(seriesData);/**
* Get formatted value for display in charts and tooltips
* Applies field-specific formatting including custom formats, dates, and numbers
* @param value - Raw value to format
* @param key - Field key/identifier
* @param itemsMap - Map of all items in the query
* @param convertToUTC - Whether to convert timestamps to UTC (default: true)
* @param pivotValuesColumnsMap - Optional pivot column mapping
* @param parameters - Optional parameters for template substitution
* @returns Formatted string value
*/
function getFormattedValue(
value: unknown,
key: string,
itemsMap: ItemsMap,
convertToUTC?: boolean,
pivotValuesColumnsMap?: Record<string, PivotValuesColumn> | null,
parameters?: ParametersValuesMap
): string;
/**
* Create a value formatter function for a specific field
* Returns a function that formats raw values according to field configuration
* @param yFieldId - Field identifier to format
* @param itemsMap - Map of all items in the query
* @param pivotValuesColumnsMap - Optional pivot column mapping
* @param parameters - Optional parameters for template substitution
* @returns Function that formats raw values to strings
*/
function valueFormatter(
yFieldId: string,
itemsMap: ItemsMap,
pivotValuesColumnsMap?: Record<string, PivotValuesColumn> | null,
parameters?: ParametersValuesMap
): (rawValue: unknown) => string;Example:
import { getFormattedValue, valueFormatter } from '@lightdash/common';
// Format a single value
const formatted = getFormattedValue(
1234.56,
'orders_total_revenue',
itemsMap,
true,
null,
{ param1: 'value1' }
);
// Returns: "$1,234.56" (if field has currency formatting)
// Create a formatter function for a field
const revenueFormatter = valueFormatter(
'orders_total_revenue',
itemsMap,
null,
{ param1: 'value1' }
);
// Use the formatter
const value1 = revenueFormatter(1234.56); // "$1,234.56"
const value2 = revenueFormatter(7890.12); // "$7,890.12"
// Use in ECharts series
const chartSeries = {
name: 'Revenue',
type: 'bar',
label: {
show: true,
formatter: (params: any) => revenueFormatter(params.value)
}
};import {
CartesianChartDataModel,
PieChartDataModel,
TableDataModel,
type MetricQueryResponse,
} from '@lightdash/common';
function renderVisualization(
response: MetricQueryResponse,
chartConfig: ChartConfig,
explore: Explore
) {
if (isCartesianChartConfig(chartConfig)) {
// Render cartesian chart
const dataModel = new CartesianChartDataModel({
resultsRunner: response,
fieldConfig: chartConfig.layout,
type: chartConfig.type
});
const echartsOptions = dataModel.getChartOptions();
// Initialize ECharts
const chart = echarts.init(document.getElementById('chart'));
chart.setOption(echartsOptions);
} else if (isPieChartConfig(chartConfig)) {
// Render pie chart
const dataModel = new PieChartDataModel({
resultsRunner: response,
fieldConfig: chartConfig.layout
});
const echartsOptions = dataModel.getSpec();
const chart = echarts.init(document.getElementById('chart'));
chart.setOption(echartsOptions);
} else if (isTableChartConfig(chartConfig)) {
// Render table
const dataModel = new TableDataModel({
resultsRunner: response,
columnsConfig: chartConfig.columns
});
const columns = dataModel.getVisibleColumns();
const rows = dataModel.getRows();
renderTable(columns, rows);
}
}
function renderTable(
columns: string[],
rows: RawResultRow[]
) {
// Render table header
const headerRow = document.createElement('tr');
columns
.filter(col => col.visible)
.sort((a, b) => a.order - b.order)
.forEach(col => {
const th = document.createElement('th');
th.textContent = col.label;
if (col.frozen) {
th.classList.add('frozen');
}
headerRow.appendChild(th);
});
// Render data rows
rows.forEach(row => {
const tr = document.createElement('tr');
columns
.filter(col => col.visible)
.sort((a, b) => a.order - b.order)
.forEach(col => {
const td = document.createElement('td');
td.textContent = row[col.id].formatted;
tr.appendChild(td);
});
});
// Render totals row
if (Object.keys(totals).length > 0) {
const totalRow = document.createElement('tr');
totalRow.classList.add('total-row');
columns
.filter(col => col.visible)
.sort((a, b) => a.order - b.order)
.forEach(col => {
const td = document.createElement('td');
if (totals[col.id] !== undefined) {
td.textContent = String(totals[col.id]);
}
totalRow.appendChild(td);
});
}
}interface IResultsRunner {
/** Get pivoted visualization data for charts */
getPivotedVisualizationData(
query: SqlRunnerQuery,
context?: string
): Promise<PivotChartData>;
/** Get all column names from the result set */
getColumnNames(): string[];
/** Get all rows from the result set */
getRows(): RawResultRow[];
/** Get dimension field options for pivot query configuration */
getPivotQueryDimensions(): VizIndexLayoutOptions[];
/** Get metric field options for pivot query configuration */
getPivotQueryMetrics(): VizValuesLayoutOptions[];
/** Get custom metric field options for pivot query configuration */
getPivotQueryCustomMetrics(): VizCustomMetricLayoutOptions[];
}Used by visualization components to fetch and transform data for chart rendering.
ECharts styling utilities for consistent chart appearance across the platform.
/**
* Get axis label styling (for values like "Jan", "Feb", "Mar")
*/
function getAxisLabelStyle(): AxisLabelStyle;
/**
* Get axis title styling (for titles like "Month", "Amount")
*/
function getAxisTitleStyle(): AxisTitleStyle;
/**
* Get axis line styling (the main axis line)
*/
function getAxisLineStyle(): AxisLineStyle;
/**
* Get tick line styling (small marks on axis)
* @param show - Whether to show tick lines
*/
function getAxisTickStyle(show?: boolean): AxisTickStyle;
/**
* Get axis pointer styling (for highlighting when hovering)
* @param useLinePointer - Use line pointer for line charts, shadow for bar charts
*/
function getAxisPointerStyle(useLinePointer?: boolean): AxisPointerStyle;Example:
import {
getAxisLabelStyle,
getAxisTitleStyle,
getAxisLineStyle,
getAxisTickStyle,
getAxisPointerStyle,
} from '@lightdash/common';
const chartOptions = {
xAxis: {
axisLabel: getAxisLabelStyle(),
axisLine: getAxisLineStyle(),
axisTick: getAxisTickStyle(false),
axisPointer: getAxisPointerStyle(true),
name: 'Month',
nameTextStyle: getAxisTitleStyle(),
},
};/**
* Get line chart grid styling configuration
*/
function getLineChartGridStyle(): GridStyle;
/**
* Get bar chart grid styling configuration
*/
function getBarChartGridStyle(): GridStyle;Example:
import { getLineChartGridStyle, getBarChartGridStyle } from '@lightdash/common';
const lineChartOptions = {
grid: getLineChartGridStyle(),
};
const barChartOptions = {
grid: getBarChartGridStyle(),
};/**
* Get legend styling configuration
*/
function getLegendStyle(iconType: 'line' | 'square' = 'square'): LegendStyle;Example:
import { getLegendStyle } from '@lightdash/common';
const chartOptions = {
legend: {
...getLegendStyle(),
data: seriesNames,
},
};/**
* Get tooltip styling configuration
*/
function getTooltipStyle(): TooltipStyle;Example:
import { getTooltipStyle } from '@lightdash/common';
const chartOptions = {
tooltip: {
...getTooltipStyle(),
trigger: 'axis',
},
};/**
* Get bar total label styling
* Styles for total value labels displayed above or beside stacked bars
*/
function getBarTotalLabelStyle(): BarTotalLabelStyle;
/**
* Resolve the tuple index for x/y axis given encode configuration and dimension names
* Helper function for determining which data dimension index corresponds to an axis
* @param enc - The ECharts series encode object
* @param dimNames - Array of dimension names
* @param which - The axis to resolve ('x' or 'y')
* @returns The numeric index of the axis, or undefined if not found
*/
function getIndexFromEncode(
enc: EChartsSeries['encode'] | SqlRunnerEChartsSeries['encode'],
dimNames: string[] | undefined,
which: 'x' | 'y'
): number | undefined;
/**
* Apply rounded corners to stacked bar chart data (Explorer format)
* Dynamically calculates which bars should have rounded corners based on stacking
* @param series - ECharts series array
* @param rows - Result rows from query
* @param options - Optional configuration for corner rounding
*/
function applyRoundedCornersToStackData(
series: EChartsSeries[],
rows: ResultRow[],
options?: RoundedCornersOptions
): EChartsSeries[];
/**
* Apply rounded corners to stacked bar chart data (SQL Runner format)
* Dynamically calculates which bars should have rounded corners based on stacking
* @param series - SQL Runner ECharts series array
* @param rows - Raw result rows from SQL query
* @param options - Optional configuration for corner rounding
*/
function applyRoundedCornersToSqlRunnerStackData(
series: SqlRunnerEChartsSeries[],
rows: RawResultRow[],
options?: RoundedCornersOptions
): SqlRunnerEChartsSeries[];/**
* Get pie slice styling for series-level configuration
* @param isDonut - Whether the pie chart should be rendered as a donut chart
*/
function getPieSliceStyle(isDonut: boolean): object;
/**
* Get external label styling for pie charts
*/
function getPieExternalLabelStyle(): object;
/**
* Get internal label styling for pie charts (labels inside slices)
*/
function getPieInternalLabelStyle(): object;
/**
* Get label line (connector) styling for pie chart external labels
*/
function getPieLabelLineStyle(): object;
/**
* Calculate border radius for a pie slice based on its percentage
* Uses smooth scaling to prevent over-rounding on small slices
* @param percent - Slice percentage (0-100)
*/
function calculateBorderRadiusForSlice(percent: number): number;/**
* Get reference line styling
* @param color - Line color
*/
function getReferenceLineStyle(color?: string): ReferenceLineStyle;/**
* Get value label styling for data points
* @param position - Label position relative to data point
* @param type - Series type
*/
function getValueLabelStyle(
position: 'left' | 'right' | 'top' | 'bottom' | 'inside' | undefined,
type: Series['type']
): ValueLabelStyle;Theme color constants for ECharts styling. Centralizes color usage across all chart style utilities using Mantine's default color palette. Exported as a namespace from @lightdash/common.
namespace vizThemeColors {
// Gray scale constants (0 = lightest, 9 = darkest)
const GRAY_0: string; // 'var(--mantine-color-ldGray-0)'
const GRAY_1: string; // 'var(--mantine-color-ldGray-1)'
const GRAY_2: string; // 'var(--mantine-color-ldGray-2)'
const GRAY_3: string; // 'var(--mantine-color-ldGray-3)'
const GRAY_4: string; // 'var(--mantine-color-ldGray-4)'
const GRAY_5: string; // 'var(--mantine-color-ldGray-5)'
const GRAY_6: string; // 'var(--mantine-color-ldGray-6)'
const GRAY_7: string; // 'var(--mantine-color-ldGray-7)'
const GRAY_8: string; // 'var(--mantine-color-ldGray-8)'
const GRAY_9: string; // 'var(--mantine-color-ldGray-9)'
// Base colors
const WHITE: string; // 'var(--mantine-color-white)'
// Semantic colors for theming
const FOREGROUND: string; // 'var(--mantine-color-foreground-0)'
const BACKGROUND: string; // 'var(--mantine-color-background-0)'
const TOOLTIP_BACKGROUND: string; // 'var(--mantine-color-background-1)'
// Specialized colors
const AXIS_TITLE_COLOR: string; // 'var(--mantine-color-gray-6)'
}All color values reference CSS variables from the Mantine design system, ensuring consistent theming across light and dark modes.
Import:
import { vizThemeColors } from '@lightdash/common';Example:
import { vizThemeColors } from '@lightdash/common';
// Use in ECharts configuration
const customStyle = {
backgroundColor: vizThemeColors.BACKGROUND,
textStyle: {
color: vizThemeColors.GRAY_7,
},
axisLine: {
lineStyle: {
color: vizThemeColors.GRAY_4,
},
},
tooltip: {
backgroundColor: vizThemeColors.TOOLTIP_BACKGROUND,
textStyle: {
color: vizThemeColors.FOREGROUND,
},
},
};
// Use in axis configuration
const chartOptions = {
xAxis: {
nameTextStyle: {
color: vizThemeColors.AXIS_TITLE_COLOR,
},
axisLine: {
lineStyle: {
color: vizThemeColors.GRAY_3,
},
},
splitLine: {
lineStyle: {
color: vizThemeColors.GRAY_2,
},
},
},
};/**
* Build tooltip formatter for cartesian charts
* @param config - Chart configuration and data
* @returns Tooltip formatter function
*/
function buildCartesianTooltipFormatter(config: TooltipFormatterConfig): (params: any) => string;
/**
* Build tooltip formatter for SQL Runner cartesian charts
* @returns Tooltip formatter function
*/
function buildSqlRunnerCartesianTooltipFormatter(config: SqlRunnerTooltipConfig): (params: any) => string;
/**
* Transform series data to percentage stacking
* @returns Transformed series data
*/
function transformToPercentageStacking(data: any[]): any[];
/**
* Create 100% stacking tooltip formatter
* @returns Tooltip formatter function
*/
function createStack100TooltipFormatter(config: Stack100Config): (params: any) => string;
/**
* Check if a series option is a line series
*/
function isLineSeriesOption(series: SeriesOption): series is LineSeriesOption;
interface TooltipParam {
seriesName: string;
value: number | number[];
color: string;
marker: string;
axisValue?: string;
}Example:
import {
buildCartesianTooltipFormatter,
transformToPercentageStacking,
} from '@lightdash/common';
const tooltipFormatter = buildCartesianTooltipFormatter({
itemsMap,
explore,
cartesianConfig,
format: 'value',
});
const chartOptions = {
tooltip: {
formatter: tooltipFormatter,
},
};Low-level HTML string formatting utilities for building custom tooltips with styled elements.
/**
* Format a tooltip value with pill styling
* @param value - The value to format (usually a formatted number or metric value)
* @returns HTML string with pill-styled value
*/
function formatTooltipValue(value: string): string;
/**
* Format tooltip header text
* @param header - Header text (usually dimension value or timestamp)
* @returns HTML string with styled header
*/
function formatTooltipHeader(header: string): string;
/**
* Get tooltip divider HTML element
* @returns HTML string for a visual divider between tooltip sections
*/
function getTooltipDivider(): string;
/**
* Format a color indicator (square) for tooltip rows
* @param color - CSS color value (hex, rgb, etc.)
* @returns HTML string with colored square indicator
*/
function formatColorIndicator(color: string): string;
/**
* Format a simple text label for tooltip
* @param text - Label text
* @returns HTML string with styled label
*/
function formatTooltipLabel(text: string): string;
/**
* Format a complete tooltip row with color indicator, label, and value
* Uses stacked vertical layout with color indicator and label on top, value below
* @param colorIndicator - HTML string from formatColorIndicator()
* @param label - Label text
* @param value - Value HTML string (usually from formatTooltipValue())
* @returns HTML string for complete tooltip row
*/
function formatTooltipRow(
colorIndicator: string,
label: string,
value: string
): string;
/**
* Format a cartesian chart tooltip row with inline layout
* Places color indicator, series name, and value pill in a horizontal row
* @param colorIndicator - HTML string from formatColorIndicator()
* @param seriesName - Name of the data series
* @param valuePill - Value HTML string (usually from formatTooltipValue())
* @returns HTML string for complete tooltip row
*/
function formatCartesianTooltipRow(
colorIndicator: string,
seriesName: string,
valuePill: string
): string;Example:
import {
formatTooltipHeader,
formatColorIndicator,
formatTooltipValue,
formatCartesianTooltipRow,
getTooltipDivider,
} from '@lightdash/common';
// Build custom tooltip HTML
const tooltipHTML = [
formatTooltipHeader('January 2024'),
getTooltipDivider(),
formatCartesianTooltipRow(
formatColorIndicator('#5470c6'),
'Revenue',
formatTooltipValue('$125,432')
),
formatCartesianTooltipRow(
formatColorIndicator('#91cc75'),
'Expenses',
formatTooltipValue('$89,210')
),
].join('');/**
* Creates a value formatter function for a specific field
* @param yFieldId - Field identifier to format
* @param itemsMap - Map of all items in the query
* @param pivotValuesColumnsMap - Optional pivot column mapping
* @param parameters - Optional parameters for template substitution
* @returns Function that formats raw values to strings
*/
function valueFormatter(
yFieldId: string,
itemsMap: ItemsMap,
pivotValuesColumnsMap?: Record<string, PivotValuesColumn> | null,
parameters?: ParametersValuesMap
): (rawValue: unknown) => string;
/**
* Formats a single value from query results
* @param value - Raw value to format
* @param key - Field key/identifier
* @param itemsMap - Map of all items in the query
* @param convertToUTC - Whether to convert timestamps to UTC (default: true)
* @param pivotValuesColumnsMap - Optional pivot column mapping
* @param parameters - Optional parameters for template substitution
* @returns Formatted string value
*/
function getFormattedValue(
value: unknown,
key: string,
itemsMap: ItemsMap,
convertToUTC: boolean = true,
pivotValuesColumnsMap?: Record<string, PivotValuesColumn> | null,
parameters?: ParametersValuesMap
): string;Example:
import { valueFormatter, getFormattedValue } from '@lightdash/common';
// Create a formatter for a specific field
const formatter = valueFormatter('orders_total_revenue', itemsMap);
const formatted = formatter(1234.56);
// Or format a single value directly
const formatted2 = getFormattedValue(
1234.56,
'orders_total_revenue',
itemsMap,
true,
null,
{ param1: 'value1' }
);/**
* Get axis formatter configuration
* @param config - Configuration object with axis item, rotation, spacing, and parameters
*/
function getCartesianAxisFormatterConfig(config: {
axisItem: ItemsMap[string] | undefined;
longestLabelWidth?: number;
rotate?: number;
defaultNameGap?: number;
show?: boolean;
parameters?: Record<string, unknown>;
}): {
axisLabel?: object;
axisLine?: object;
axisTick?: object;
axisPointer?: object;
nameGap?: number;
minInterval?: number;
};Example:
import { getCartesianAxisFormatterConfig } from '@lightdash/common';
const yAxisConfig = getCartesianAxisFormatterConfig(
metricField,
{ type: 'number', compact: 'short' }
);
const chartOptions = {
yAxis: {
axisLabel: {
formatter: yAxisConfig.formatter,
},
},
};