API Reference
Complete type definitions and manifest schema for custom visuals
Complete TypeScript types and manifest schema for building custom visuals and filters.
Visual Props
SingleInputVisualProps
Props passed to single-input custom visual components.
type SingleInputVisualProps = {
/** Query results from the card's data source */
data: Record<string, string | number | boolean>[];
/** Component settings defined in manifest */
settings?: Record<string, string | number | boolean>;
/** Card metadata including title, type, and formatting */
cardMetadata?: CardMetadata;
/** Dashboard theme configuration */
theme?: CustomCardTheme;
/** Active filter definitions on the dashboard */
filters?: DashboardFilter[];
/** Current filter values (selections) */
filterValues?: ActiveFilterValue[];
/**
* Pre-rendered inline filter components.
* Render these wherever you want in your layout.
* Always an array (empty if no inline filters configured).
*/
inlineFilters?: ReactNode[];
};MultiInputVisualProps
Props passed to multi-input custom visual components.
type MultiInputVisualProps = {
/** Array of datasets, one per slot (indexed by tab position) */
data: Record<string, string | number | boolean>[][];
/** Global settings from the config card */
settings?: Record<string, string | number | boolean>;
/** Per-slot settings (aligned with data array) */
slotSettings?: Array<Record<string, string | number | boolean> | undefined>;
/** Dashboard theme configuration */
theme?: CustomCardTheme;
/** Active filter definitions on the dashboard */
filters?: DashboardFilter[];
/** Current filter values (selections) */
filterValues?: ActiveFilterValue[];
/**
* Pre-rendered inline filter components.
* Render these wherever you want in your layout.
*/
inlineFilters?: ReactNode[];
/** Tab metadata (titles, types, IDs) */
tabMetadata?: TabMetadata;
/** Rich metadata for each slot */
cardMetadata?: CardMetadata[];
};Filter Props
CustomFilterProps
Props passed to custom filter components for text and select filters.
type CustomFilterProps = {
/** Available filter options from the query */
options: TSelectedRecord[];
/** Loading state for initial data fetch */
isLoading: boolean;
/** Fetching state for refetches */
isFetching: boolean;
/** Error state */
isError: boolean;
/** Currently selected values */
selectedValues: TSelectedRecord[];
/** Called when selection changes */
onChange: (records: TSelectedRecord[]) => void;
/** Called to clear all selections */
onClear: () => void;
/** Called to toggle select all (if supported) */
onSelectAll?: (checked: boolean) => void;
/** Current search query (for searchable filters) */
searchQuery?: string;
/** Called when search query changes */
onSearchChange?: (query: string) => void;
/** True when search is in progress */
isSearching?: boolean;
/** Dashboard theme configuration */
theme?: CustomCardTheme;
/** Plugin settings from manifest */
settings?: Record<string, string | number | boolean>;
/** True if filter is single-select mode */
isSingleSelect?: boolean;
/** True if all options are selected */
allSelected?: boolean;
};CustomDateFilterProps
Props passed to custom filter components for date filters.
type CustomDateFilterProps = {
/** Current date range selection */
dateRange: DateRange | undefined;
/** Initial date range (for reset) */
initialDateRange: DateRange | undefined;
/** Loading state for initial data fetch */
isLoading: boolean;
/** Fetching state for refetches */
isFetching: boolean;
/** Error state */
isError: boolean;
/** Called when date range changes */
onDateChange: (range: DateRange | undefined) => void;
/** Called when relative date changes (e.g., "Last 7 days") */
onRelativeDateChange?: (
range: DateRange | undefined,
meta: RelativeDateFilter
) => void;
/** Called to clear the date selection */
onClear: () => void;
/** Called to reset to initial values */
onReset?: () => void;
/** Dashboard theme configuration */
theme?: CustomCardTheme;
/** Plugin settings from manifest */
settings?: Record<string, string | number | boolean>;
};TSelectedRecord
A selected record in a filter (value from filter options).
type TSelectedRecord = {
value: string | number | boolean;
label?: string;
};DateRange
Date range type for date filters.
type DateRange = {
from: Date | undefined;
to?: Date | undefined;
};RelativeDateFilter
Relative date filter configuration (e.g., "Last 7 days").
type RelativeDateFilter = {
mode: 'last' | 'this' | 'previous' | 'between';
unit: 'day' | 'week' | 'month' | 'quarter' | 'year';
n?: number;
complete?: boolean;
toDate?: boolean;
from?: number;
to?: number;
};Metadata Types
CardMetadata
Rich metadata about a card's configuration.
type CardMetadata = {
/** Card type (kpi, bar, table, custom, etc.) */
cardType: string;
/** Card title */
title: string;
/** Card description */
description?: string;
/** KPI-specific configuration */
kpiConfig?: {
/** Comparison period metadata */
comparisonMetadata?: ComparisonMetadataMap;
/** KPI display options */
options?: {
/** True if lower values are better (e.g., costs) */
lowerIsBetter?: boolean;
/** Show sparkline trendline */
showTrendline?: boolean;
/** Show comparison value */
showComparison?: boolean;
};
/** Legacy number formatting */
formatNumber?: LegacyFormatNumber;
};
/** Number formatting configuration */
formatConfig?: CustomVisualFormatConfig;
};TabMetadata
Lightweight metadata about tabs in a multi-input visual.
type TabMetadata = {
/** Tab titles (labels) */
titles: string[];
/** Card types per tab */
cardTypes: string[];
/** Card IDs per tab */
cardIds: string[];
};CustomVisualFormatConfig
Number formatting configuration for custom visuals.
type CustomVisualFormatConfig = {
/** KPI number formatting */
kpi?: {
/** Primary value format */
primary?: FormatOptions;
/** Comparison value format */
comparison?: FormatOptions;
/** Conditional color ranges */
colorRanges?: ColorRange[];
};
/** Chart axis formatting */
axes?: {
xAxis?: FormatOptions;
yAxis?: FormatOptions;
secondaryYAxis?: FormatOptions;
};
/** Data label formatting */
dataLabels?: FormatOptions;
/** Table column formatting */
tables?: {
/** Per-column format settings */
columns?: Array<{
id?: string;
label?: string;
position?: number;
numberFormat?: ColumnNumberFormat | FormatOptions;
}>;
/** Column format lookup by ID */
columnMap?: Record<string, { numberFormat?: ColumnNumberFormat | FormatOptions }>;
/** Comparison column format */
comparison?: FormatOptions;
/** Default format for numeric columns */
defaultNumberFormat?: FormatOptions;
};
/** Legacy format fields (for backward compatibility) */
legacy?: {
formatNumber?: LegacyFormatNumber;
numberAxisFormat?: {
decimalPlaces?: number;
suffix?: string;
currency?: string;
locale?: string;
};
};
};FormatOptions
Number formatting options.
type FormatOptions = {
/** Format type */
type?: 'auto' | 'number' | 'currency' | 'percent' | 'scientific' | 'date';
/** Decimal places to display */
decimalPlaces?: number;
/** Currency code (e.g., 'USD', 'EUR') */
currency?: string;
/** Locale for formatting (e.g., 'en-US', 'de-DE') */
locale?: string;
/** Text prepended to value */
prefix?: string;
/** Text appended to value */
suffix?: string;
/** Enable automatic suffix (K, M, B) */
useSuffix?: boolean;
/** Show negative numbers in parentheses */
negativeInParentheses?: boolean;
/** Multiply value before display (e.g., 100 for percentages) */
multiplyBy?: number;
/** Date format string (e.g., 'MM/DD/YYYY') */
dateFormat?: string;
};ColorRange
Conditional color range for values.
type ColorRange = {
/** Start of range (inclusive) */
start: number;
/** End of range (inclusive) */
end: number;
/** Color to apply (hex or CSS color) */
color: string;
};ColumnNumberFormat
Table column number format options.
type ColumnNumberFormat = {
/** Number style */
style: 'decimal' | 'currency' | 'percent';
/** Currency code */
currency: string;
/** Locale for formatting */
locale: string;
/** Minimum decimal places */
minimumFractionDigits: number;
/** Maximum decimal places */
maximumFractionDigits: number;
/** Show data bar visualization */
showDataBar: boolean;
/** Data bar color */
dataBarColor: string;
/** Data bar minimum value (for scaling) */
dataBarMinValue?: number;
/** Data bar maximum value (for scaling) */
dataBarMaxValue?: number;
};LegacyFormatNumber
Legacy number format (for backward compatibility).
type LegacyFormatNumber = {
decimalPlaces?: number;
currency?: string;
locale?: string;
suffix?: string;
enabled?: boolean | string;
colorRanges?: ColorRange[];
[key: string]: unknown; // Allows additional legacy fields
};ComparisonMetadataEntry
Metadata for a single comparison type.
type ComparisonMetadataEntry = {
type: 'previous_period' | 'same_period_last_year' | 'start_vs_end' | 'target';
displayName?: string;
displayLabel?: string;
currentPeriod?: { start: string; end: string };
comparisonPeriod?: { start: string; end: string };
};ComparisonMetadataMap
Map of comparison metadata entries keyed by column name.
type ComparisonMetadataMap = Record<string, ComparisonMetadataEntry>;Theme Types
CustomCardTheme
Theme configuration passed to custom visuals.
type CustomCardTheme = {
/** Color palette from dashboard theme */
colors?: string[];
/** Color mode */
mode?: 'light' | 'dark' | 'system';
};Filter Types
FilterOperation
Filter operation types.
type FilterOperation =
| '='
| '!='
| '>'
| '<'
| '>='
| '<='
| 'in'
| 'not in'
| 'like'
| 'not like'
| 'between'
| 'not between';DashboardFilter
Simplified filter definition (metadata about a dashboard filter).
type DashboardFilter = {
id: string;
title: string; // Display name
column: string; // Column being filtered
table: string; // Table containing the column
dataType: string; // 'text', 'number', 'date', etc.
operation: FilterOperation;
};ActiveFilterValue
Active filter value (current selection).
type ActiveFilterValue = {
filterId: string; // References DashboardFilter.id
name: string; // Display name
operation: FilterOperation;
valueType: 'string' | 'number' | 'date' | 'boolean';
values: (string | number | boolean)[];
relativeDateMeta?: RelativeDateFilter;
};Manifest Schema
The components.config.ts file defines your plugin's components.
ComponentsConfig
Top-level configuration structure.
type ComponentsConfig = {
/** Visual components (charts, KPIs, tables) */
visuals: VisualConfig[];
/** Filter components (optional) */
filters?: FilterConfig[];
};VisualConfig
Configuration for a visual component.
type VisualConfig = {
/** Display name in the chart selector */
name: string;
/** React component export name */
component: string;
/** Component type (always 'chart' for visuals) */
componentType: 'chart';
/** Unique identifier for this visual */
chartType?: string;
/** Icon name from lucide-react */
icon?: string;
/** 'single' or 'multiple' data inputs */
visualType?: 'single' | 'multiple';
/** Minimum required inputs (multi-input only) */
minInputs?: number;
/** Maximum allowed inputs (multi-input only) */
maxInputs?: number;
/** Slot definitions (multi-input only) */
slots?: SlotDefinition[];
/** Global settings schema */
settings?: Record<string, SettingConfig>;
/** Per-slot settings schema (multi-input only) */
slotSettings?: Record<string, SettingConfig>;
/** Documentation for the visual */
docs?: VisualDocumentation;
};FilterConfig
Configuration for a filter component.
type FilterConfig = {
/** Display name (must be unique) */
name: string;
/** React component export name */
component: string;
/** Unique filter type identifier */
filterType?: string;
/** Icon name from lucide-react */
icon?: string;
/** Supported data types (e.g., ['text', 'varchar']) */
supportedDataTypes?: string[];
/** Settings schema */
settings?: Record<string, SettingConfig>;
/** Documentation for the filter */
docs?: FilterDocumentation;
};SlotDefinition
Slot definition for multi-input visuals.
type SlotDefinition = {
/**
* Position identifier:
* - number: exact position (0, 1, 2)
* - "N+": position N and all subsequent
* - "N-M": positions N through M
*/
position: number | string;
/** Tab label shown in editor */
label: string;
/** Tooltip description */
description?: string;
/** Expected card type(s) for this slot */
expectedType?: string | string[];
/** True if slot must have data */
required?: boolean;
};Position format examples:
| Position | Meaning |
|---|---|
0 | Exactly position 0 (first tab) |
1 | Exactly position 1 (second tab) |
"0+" | All positions starting from 0 |
"1+" | Position 1 and all subsequent |
"0-2" | Positions 0, 1, and 2 only |
SettingConfig
Configuration for a single setting.
type SettingConfig = {
/** Display title in the settings panel */
title: string;
/** Default value */
defaultValue: string;
/** UI control type */
ui: 'input' | 'select';
/** Options for select UI */
options?: { label: string; value: string }[];
/** Setting documentation */
docs?: SettingDocumentation;
};VisualDocumentation
Documentation for a visual component.
type VisualDocumentation = {
/** What this visual does */
description: string;
/** Markdown describing expected data format */
dataSchema: string;
/** When to use this visual */
useCases?: string[];
/** Preview image (base64 or URL) */
thumbnail?: string;
};FilterDocumentation
Documentation for a filter component.
type FilterDocumentation = {
/** What this filter does */
description: string;
/** When to use this filter */
useCases?: string[];
};SettingDocumentation
Documentation for a setting.
type SettingDocumentation = {
/** What this setting controls */
description?: string;
};Helper Functions
Utility functions available in the plugin quickstart template.
parseKPIData
Parse KPI data using the segment convention.
type Data = Record<string, string | number | boolean>[];
function parseKPIData(data: Data): {
currentValue: string | number | boolean | null;
comparisonValue: string | number | boolean | null;
trendlineData: Data;
valueKey: string | undefined;
};Usage:
const { currentValue, comparisonValue, trendlineData } = parseKPIData(data);
// currentValue: value from row where segment === 'current'
// comparisonValue: value from row where segment === 'comparison'
// trendlineData: rows where segment === 'trendline'getPercentChange
Calculate percent change between two values.
function getPercentChange(
current: number | undefined,
comparison: number | undefined,
isLowerBetter?: boolean
): {
value: number | null;
isPositive: boolean;
isBetter: boolean;
isNeutral: boolean;
};Usage:
const change = getPercentChange(125000, 98000);
// { value: 27.55, isPositive: true, isBetter: true, isNeutral: false }
const costChange = getPercentChange(80000, 100000, true); // lower is better
// { value: -20, isPositive: false, isBetter: true, isNeutral: false }formatKPIValue
Format a number using KPI format configuration.
function formatKPIValue(
value: number | undefined,
formatConfig?: FormatOptions | LegacyFormatNumber
): string;Usage:
formatKPIValue(125000, { currency: 'USD', decimalPlaces: 0 });
// "$125,000"
formatKPIValue(0.2755, { type: 'percent', decimalPlaces: 1 });
// "27.6%"
formatKPIValue(1500000, { suffix: 'M', multiplyBy: 0.000001, decimalPlaces: 1 });
// "1.5M"getComparisonLabel
Get display label for a comparison type.
function getComparisonLabel(metadata?: {
type?: string;
displayLabel?: string;
}): string;Usage:
getComparisonLabel({ type: 'previous_period' });
// "vs Previous Period"
getComparisonLabel({ type: 'same_period_last_year' });
// "vs Same Period Last Year"
getComparisonLabel({ displayLabel: 'vs Budget' });
// "vs Budget"Comparison types:
| Type | Default Label |
|---|---|
previous_period | vs Previous Period |
same_period_last_year | vs Same Period Last Year |
start_vs_end | Change Over Period |
target | vs Target |
formatDateRange
Format a date range for display.
function formatDateRange(
start: string,
end: string,
displayTimezone?: string
): string;Usage:
formatDateRange('2024-01-01', '2024-01-31');
// "Jan 1, 2024 - Jan 31, 2024"formatDateShort
Format a date range with year de-duplication.
function formatDateShort(
start: string,
end: string,
displayTimezone?: string
): string;Usage:
formatDateShort('2024-01-01', '2024-01-31');
// "Jan 1 - Jan 31, 2024"
formatDateShort('2023-12-01', '2024-01-31');
// "Dec 1, 2023 - Jan 31, 2024"Related Topics
- Single-Input Visuals - Building standard visuals
- Multi-Input Visuals - Combining multiple data sources
- Custom Filters - Building filter components