or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api

features

charts

charts.mdconditional-formatting.mdvisualizations.md
authorization.mdchangesets.mdcharts-as-code.mdcompiler.mddashboards.mddbt.mdee-features.mdformatting.mdparameters.mdpivot.mdprojects-spaces.mdsql-runner.mdtemplating.mdwarehouse.md
index.md
tile.json

visualizations.mddocs/api/features/charts/

Visualization Data Models

Data transformation and configuration for chart rendering using ECharts and other visualization libraries.

Overview

The visualization module provides data model classes that transform query results into visualization-ready formats. These classes handle:

  • Data structure transformation for different chart types
  • Axis configuration and formatting
  • Series generation and styling
  • Color palette application
  • Tooltip formatting
  • Legend configuration

Value Label Position Options

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 displayed
  • TOP: Labels above chart elements
  • BOTTOM: Labels below chart elements
  • LEFT: Labels to the left of chart elements
  • RIGHT: Labels to the right of chart elements
  • INSIDE: Labels inside chart elements

CartesianChartDataModel

Main 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);

PieChartDataModel

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();

TableDataModel

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]);
  });
});

Visualization Types

VizColumn

interface VizColumn {
  reference: string;
  /** @deprecated Use DimensionType instead. This property is optional and may be undefined. */
  type?: DimensionType;
}

Chart Display Options

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;
}

Aggregation Options

enum VizAggregationOptions {
  SUM = 'sum',
  COUNT = 'count',
  AVERAGE = 'avg',
  MIN = 'min',
  MAX = 'max',
  ANY = 'any',
}

const vizAggregationOptions: VizAggregationOptions[];
const VIZ_DEFAULT_AGGREGATION: VizAggregationOptions;

Sort Configuration

enum SortByDirection {
  ASC = 'ASC',
  DESC = 'DESC',
}

interface VizSortBy {
  reference: string;
  direction: SortByDirection;
}

Index Types

enum VizIndexType {
  TIME = 'time',
  CATEGORY = 'category',
}

function getColumnAxisType(dimensionType: DimensionType): VizIndexType;
function getTableCalculationAxisType(tableCalculationType: TableCalculationType): VizIndexType;

Axis Configuration

enum AxisSide {
  LEFT = 0,
  RIGHT = 1,
}

Stack Configuration

enum StackType {
  NONE = 'none',
  NORMAL = 'stack',
  PERCENT = 'stack100',
}

Chart-Specific Types

Cartesian Chart

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;
}

Pie Chart Options

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

Pivot Charts

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;

Viz Chart Configuration Types

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;

Table Column Configuration

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

Type Guards

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;

Style Helper Functions

Helper functions for generating ECharts style configurations.

Axis Styling Helpers

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;
  };
};

Grid Styling Helpers

function getLineChartGridStyle(): GridStyle;
function getBarChartGridStyle(): GridStyle;

Bar Chart Styling Helpers

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[];

Styling Configuration

Theme Colors

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.

Axis Styling

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';
}

Grid Styling

interface GridStyle {
  left?: number | string;
  right?: number | string;
  top?: number | string;
  bottom?: number | string;
  containLabel?: boolean;
  backgroundColor?: string;
  borderColor?: string;
  borderWidth?: number;
}

Legend Styling

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;
}

Visualization Helper Functions

Helper functions for formatting, styling, and transforming visualization data.

Axis Formatter Configuration

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

Tooltip Formatter Functions

/**
 * 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);

Value Formatter Functions

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

Complete Visualization Example

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);
      });
  }
}

Results Runner Interface

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.

Style Helpers

ECharts styling utilities for consistent chart appearance across the platform.

Axis Styles

/**
 * 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(),
  },
};

Grid Styles

/**
 * 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(),
};

Legend Styles

/**
 * Get legend styling configuration
 */
function getLegendStyle(iconType: 'line' | 'square' = 'square'): LegendStyle;

Example:

import { getLegendStyle } from '@lightdash/common';

const chartOptions = {
  legend: {
    ...getLegendStyle(),
    data: seriesNames,
  },
};

Tooltip Styles

/**
 * Get tooltip styling configuration
 */
function getTooltipStyle(): TooltipStyle;

Example:

import { getTooltipStyle } from '@lightdash/common';

const chartOptions = {
  tooltip: {
    ...getTooltipStyle(),
    trigger: 'axis',
  },
};

Bar Chart Styles

/**
 * 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[];

Pie Chart Styles

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

Reference Line Styles

/**
 * Get reference line styling
 * @param color - Line color
 */
function getReferenceLineStyle(color?: string): ReferenceLineStyle;

Value Label Styles

/**
 * 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 Colors

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,
      },
    },
  },
};

Formatter Utilities

Tooltip Formatter

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

Tooltip HTML Formatting Helpers

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('');

Value Formatting Helpers

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

Axis Formatter Config

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