@tanstack/charts
Version:
A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.
1,073 lines • 55.5 kB
TypeScript
export type ChartValue = number | string | Date;
export type ChartKey = string | number;
export interface ChartCurve {
line: (points: readonly (readonly [number, number])[]) => string;
area: (top: readonly (readonly [number, number])[], bottom: readonly (readonly [number, number])[]) => string;
}
export interface ChartScaleResolveContext {
id: string;
values: readonly unknown[];
range: readonly [number, number];
options: ChartAxisOptions<any> | undefined;
tickCount: number;
includeZero: boolean;
}
export type ChartContinuousValue = number | Date;
export type ChartContinuousDomain<TValue extends ChartContinuousValue = ChartContinuousValue> = (Extract<TValue, number> extends never ? never : readonly [number, number]) | (Extract<TValue, Date> extends never ? never : readonly [Date, Date]);
export interface ChartScale {
id: string;
resolve: (context: ChartScaleResolveContext) => ResolvedScale;
}
export interface ConfiguredScaleLike<TValue extends ChartValue> {
(value: TValue): number | undefined;
bandwidth?: () => number;
copy: () => ConfiguredScaleLike<TValue>;
domain: () => readonly TValue[];
invert?: (position: number) => TValue;
range: (values: Iterable<number>) => ConfiguredScaleLike<TValue>;
ticks?: (count: number) => readonly TValue[];
tickFormat?: (count: number) => (value: TValue) => string;
}
export interface InferableScaleLike<TValue extends ChartValue> extends ConfiguredScaleLike<TValue> {
domain: {
(): readonly TValue[];
(values: Iterable<TValue>): InferableScaleLike<TValue>;
};
}
export type ChartScaleFactory<TValue extends ChartValue> = Function & {
readonly copy?: never;
readonly __chartValue?: TValue;
};
export type ChartScaleInput<TValue extends ChartValue> = TValue extends ChartValue ? ConfiguredScaleLike<TValue> | ChartScaleFactory<TValue> : never;
export interface ChartNumericScaleOptions {
scale: ChartScaleInput<number>;
nice?: boolean | number;
}
export type ChartNumericScale = ((value: number) => number) | ChartNumericScaleOptions;
export type ChartScaleResolver = (context: ChartScaleResolveContext) => ResolvedScale;
export interface ChannelAccessorContext<TDatum> {
index: number;
data: readonly TDatum[];
}
export type ChannelAccessor<TDatum, TValue> = (datum: TDatum, context: ChannelAccessorContext<TDatum>) => TValue;
export type ChannelField<TDatum, TValue> = {
[TKey in Extract<keyof TDatum, string>]-?: TDatum[TKey] extends TValue ? TKey : never;
}[Extract<keyof TDatum, string>];
export type Channel<TDatum, TValue> = ChannelField<TDatum, TValue> | ChannelAccessor<TDatum, TValue>;
export type WidenChartValue<TValue> = TValue extends string ? string : TValue extends number ? number : TValue extends Date ? Date : never;
export type ChannelOutput<TDatum, TChannel, TFallback extends ChartValue> = TChannel extends ChannelAccessor<TDatum, infer TValue> ? WidenChartValue<NonNullable<TValue>> : TChannel extends keyof TDatum ? WidenChartValue<NonNullable<TDatum[TChannel]>> : WidenChartValue<TFallback>;
export type OptionChannelOutput<TDatum, TOptions, TKey extends PropertyKey, TFallback extends ChartValue> = TOptions extends unknown ? TKey extends keyof TOptions ? ChannelOutput<TDatum, TOptions[TKey], TFallback> : WidenChartValue<TFallback> : never;
export type VisualChannel<TDatum, TValue> = TValue | ChannelAccessor<TDatum, TValue>;
export interface ChartMarkStateContext<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
datum: TDatum;
index: number;
data: readonly TDatum[];
point: ChartPoint<TDatum, TXValue, TYValue>;
focus: ChartFocusState<TDatum, TXValue, TYValue>;
pointer: ChartTooltipPosition | null;
matches: (match: ChartFocusMatch) => boolean;
}
export type ChartMarkStateValue<TDatum, TValue> = TValue | ((context: ChartMarkStateContext<TDatum>) => TValue);
export interface ChartMarkStateStyle<TDatum = unknown> {
fill?: ChartMarkStateValue<TDatum, string>;
fillOpacity?: ChartMarkStateValue<TDatum, number>;
stroke?: ChartMarkStateValue<TDatum, string>;
strokeOpacity?: ChartMarkStateValue<TDatum, number>;
strokeWidth?: ChartMarkStateValue<TDatum, number>;
opacity?: ChartMarkStateValue<TDatum, number>;
strokeDasharray?: ChartMarkStateValue<TDatum, string>;
r?: ChartMarkStateValue<TDatum, number>;
radius?: ChartMarkStateValue<TDatum, number>;
inset?: ChartMarkStateValue<TDatum, number>;
fontSize?: ChartMarkStateValue<TDatum, number>;
fontWeight?: ChartMarkStateValue<TDatum, number>;
dx?: ChartMarkStateValue<TDatum, number>;
dy?: ChartMarkStateValue<TDatum, number>;
rotate?: ChartMarkStateValue<TDatum, number>;
}
export type ChartDotStateStyle<TDatum = unknown> = Pick<ChartMarkStateStyle<TDatum>, 'fill' | 'fillOpacity' | 'stroke' | 'strokeOpacity' | 'strokeWidth' | 'opacity' | 'r'>;
export type ChartBarStateStyle<TDatum = unknown> = Pick<ChartMarkStateStyle<TDatum>, 'fill' | 'fillOpacity' | 'stroke' | 'strokeWidth' | 'opacity' | 'radius' | 'inset'>;
export type ChartRectStateStyle<TDatum = unknown> = ChartBarStateStyle<TDatum>;
export type ChartLineStateStyle<TDatum = unknown> = Pick<ChartMarkStateStyle<TDatum>, 'stroke' | 'strokeOpacity' | 'strokeWidth' | 'strokeDasharray' | 'opacity'>;
export type ChartAreaStateStyle<TDatum = unknown> = Pick<ChartMarkStateStyle<TDatum>, 'fill' | 'fillOpacity' | 'stroke' | 'strokeOpacity' | 'strokeWidth' | 'opacity'>;
export type ChartTextStateStyle<TDatum = unknown> = Pick<ChartMarkStateStyle<TDatum>, 'fill' | 'fillOpacity' | 'stroke' | 'strokeWidth' | 'opacity' | 'fontSize' | 'fontWeight' | 'dx' | 'dy' | 'rotate'>;
export interface ChartMarkStateSelector {
focus: ChartFocusMatch | 'unmatched';
source?: ChartFocusSource | readonly ChartFocusSource[];
pinned?: boolean;
}
export interface ChartMarkState<TDatum = unknown, TStyle extends ChartMarkStateStyle<TDatum> = ChartMarkStateStyle<TDatum>> {
when: ChartMarkStateSelector | ((context: ChartMarkStateContext<TDatum>) => boolean);
style: TStyle;
transition?: ChartMarkStateTransition;
}
export type ChartMarkStateTransition = ChartMotionTransition & {
respectReducedMotion?: boolean;
};
export interface ChartSize {
width: number;
height: number;
}
export interface ChartBounds extends ChartSize {
x: number;
y: number;
}
export interface ChartMargin {
top: number;
right: number;
bottom: number;
left: number;
}
export interface ChartTextMeasureOptions {
fontSize: number;
fontWeight?: number;
fontFamily: string;
fontStyle: string;
fontStretch: string;
letterSpacing: number;
direction: 'ltr' | 'rtl' | 'inherit';
locale?: string;
fontScale: number;
anchor: 'start' | 'middle' | 'end';
baseline: 'auto' | 'middle' | 'hanging';
}
export interface ChartTextTypography {
fontFamily?: string;
fontStyle?: string;
fontStretch?: string;
letterSpacing?: number;
direction?: 'ltr' | 'rtl' | 'inherit';
locale?: string;
/** Host text scale, such as the React Native accessibility font scale. */
fontScale?: number;
}
export interface ChartTextMetrics {
/** Left edge of the painted glyph box relative to the anchored label origin. */
x: number;
/** Top edge of the painted glyph box relative to the baseline origin. */
y: number;
width: number;
height: number;
}
export type ChartTextMeasurer = (text: string, options: ChartTextMeasureOptions) => ChartTextMetrics;
export interface ChartLayoutOptions {
measureText?: ChartTextMeasurer;
/** Host typography used for measurement and deterministic layout. */
typography?: ChartTextTypography;
/** Host defaults applied before the authored definition theme. */
defaultTheme?: Partial<ChartTheme>;
}
export interface ChartRuntimeOptions {
/** Platform theme passed to responsive builders and final scene resolution. */
defaultTheme?: Partial<ChartTheme>;
}
export interface ChartAxisTickOptions<TValue extends ChartValue = any> {
/** Preferred semantic candidate count. The scale may choose a nearby count. */
count?: number;
/** Preferred pixels between semantic candidates. */
spacing?: number;
/** Exact semantic candidates. */
values?: readonly TValue[];
/** Length of the visible tick stub in pixels. */
size?: number;
/** Gap between the tick stub and label in pixels. */
padding?: number;
format?: (value: TValue) => string;
motion?: ChartMotionDefinition;
}
export interface ChartAxisTickLabelThinOptions<TValue extends ChartValue = any> {
minGap?: number;
priority?: 'ends';
/** Values whose labels must remain visible even when they collide. */
keep?: readonly TValue[];
}
export interface ChartAxisTickLabelContext<TValue extends ChartValue = ChartValue> {
/** Semantic tick value. */
value: TValue;
/** Candidate index before collision-aware thinning. */
index: number;
/** Resolved scale position at the center of the tick. */
position: number;
/** Resolved band width, or zero for a continuous scale. */
bandwidth: number;
}
export type ChartAxisTickLabelValue<TValue extends ChartValue, TOutput> = TOutput | ((context: ChartAxisTickLabelContext<TValue>) => TOutput | undefined);
export interface ChartAxisTickLabelOptions<TValue extends ChartValue = any> {
rotate?: number;
thin?: boolean | ChartAxisTickLabelThinOptions<TValue>;
fontSize?: ChartAxisTickLabelValue<TValue, number>;
fontWeight?: ChartAxisTickLabelValue<TValue, number>;
opacity?: ChartAxisTickLabelValue<TValue, number>;
anchor?: ChartAxisTickLabelValue<TValue, 'start' | 'middle' | 'end'>;
dx?: ChartAxisTickLabelValue<TValue, number>;
dy?: ChartAxisTickLabelValue<TValue, number>;
motion?: ChartMotionDefinition;
}
export interface ChartAxisLabelOptions {
text: string;
offset?: number | 'auto';
motion?: ChartMotionDefinition;
}
export interface ChartAxisPresentationOptions<TValue extends ChartValue = any> {
line?: boolean;
ticks?: false | ChartAxisTickOptions<TValue>;
tickLabels?: false | ChartAxisTickLabelOptions<TValue>;
label?: string | ChartAxisLabelOptions;
motion?: ChartMotionDefinition;
}
interface ChartAxisViewportBase {
/** Transient output-space displacement applied to chart content, in scene pixels. */
translate?: number;
}
export type ChartAxisViewportOptions<TValue extends ChartContinuousValue = ChartContinuousValue> = ChartAxisViewportBase & {
/** Committed semantic window used by stationary guides. */
domain: ChartContinuousDomain<TValue>;
};
type ChartAxisViewportFor<TValue extends ChartValue> = IsAny<TValue> extends true ? ChartAxisViewportOptions : [Extract<TValue, ChartContinuousValue>] extends [never] ? never : ChartAxisViewportOptions<Extract<TValue, ChartContinuousValue>>;
export interface ChartAxisOptions<TValue extends ChartValue = any> {
/**
* A D3 scale factory infers its domain from materialized mark channels.
* A scale instance retains its configured domain.
*/
scale: ChartScale | ChartScaleInput<TValue>;
/** Applies D3 nicening after an inferred or configured domain is resolved. */
nice?: boolean | number;
reverse?: boolean;
/** A semantic window over the scale's complete configured or inferred domain. */
viewport?: ChartAxisViewportFor<TValue>;
/** Grid lines use semantic tick candidates before label thinning. */
grid?: boolean;
/** Axis presentation. False keeps the scale but omits the visible axis. */
axis?: false | ChartAxisPresentationOptions<TValue>;
}
export interface ChartColorOptions {
/**
* A D3 color-scale factory infers its domain from color channels.
* A scale instance retains its configured domain.
*/
scale?: ConfiguredColorScaleLike<any, any> | ChartColorScaleFactory<any, any>;
resolver?: ChartColorScale;
domain?: readonly ChartKey[];
range?: readonly string[];
nice?: boolean | number;
legend?: ChartColorLegend;
}
export type ResolvedColorScaleKind = 'categorical' | 'continuous' | 'quantile' | 'quantize' | 'threshold';
export interface ConfiguredColorScaleLike<TValue extends ChartKey, TOutput> {
(value: TValue): TOutput | undefined;
copy: () => ConfiguredColorScaleLike<TValue, TOutput>;
domain?: () => readonly TValue[];
range?: () => readonly TOutput[];
}
export interface InferableColorScaleLike<TValue extends ChartKey, TOutput> extends ConfiguredColorScaleLike<TValue, TOutput> {
domain: {
(): readonly TValue[];
(values: Iterable<TValue>): InferableColorScaleLike<TValue, TOutput>;
};
range: {
(): readonly TOutput[];
(values: Iterable<TOutput>): InferableColorScaleLike<TValue, TOutput>;
};
ticks?: (count: number) => readonly TValue[];
nice?: (count?: number) => InferableColorScaleLike<TValue, TOutput>;
thresholds?: () => readonly number[];
quantiles?: (count?: number) => readonly number[];
invertExtent?: (value: TOutput) => readonly [TValue | undefined, TValue | undefined];
}
export type ChartColorScaleFactory<TValue extends ChartKey, TOutput> = Function & {
readonly copy?: never;
readonly __chartValue?: TValue;
readonly __chartOutput?: TOutput;
};
export interface ChartColorScaleContext {
values: readonly unknown[];
domain?: readonly ChartKey[];
range?: readonly string[];
theme: ChartTheme;
}
export interface ChartColorScale {
id: string;
resolve: (context: ChartColorScaleContext) => ResolvedColorScale;
}
export interface ChartColorLegendContext {
colors: ResolvedColorScale;
chart: ChartBounds;
bounds: ChartBounds;
theme: ChartTheme;
width: number;
height: number;
}
export type ChartLegendPlacement = 'top' | 'bottom';
export interface ChartHostControlExtensionToken {
readonly id: string;
readonly create: Function;
readonly __chartExtensionType?: 'host-control';
}
export interface ChartHostControl {
readonly key: string;
readonly extension: ChartHostControlExtensionToken;
readonly fallbackNodeKey?: string;
}
export interface ChartControlContext {
chart: ChartBounds;
scales: Readonly<Record<string, ResolvedScale>>;
colors: ResolvedColorScale;
theme: ChartTheme;
width: number;
height: number;
}
export interface ChartControlScene {
nodes?: readonly SceneNode[];
controls?: readonly ChartHostControl[];
}
/** Resolves renderer-neutral interaction output after scales and bounds exist. */
export interface ChartControl<TXValue extends ChartValue = any, TYValue extends ChartValue = any> {
readonly id: string;
resolve: (context: ChartControlContext) => ChartControlScene;
readonly __xValue?: TXValue;
readonly __yValue?: TYValue;
}
export interface ChartColorLegend {
placement?: ChartLegendPlacement;
height: (itemCount: number, context: ChartColorLegendContext) => number;
render: (context: ChartColorLegendContext) => SceneNode;
/** Keeps hidden series in scale inference while removing their scene output. */
seriesVisible?: (value: ChartKey) => boolean;
filterMark?: (scene: MarkScene, context: {
seriesFromColor?: boolean;
}) => MarkScene;
control?: (context: ChartColorLegendContext) => ChartHostControl;
}
export interface ChartTheme {
foreground: string;
muted: string;
grid: string;
background: string;
palette: readonly string[];
}
export interface ChartGradientStop {
offset: number;
color: string;
opacity?: number;
}
export interface ChartLinearGradient {
id: string;
x1?: number;
y1?: number;
x2?: number;
y2?: number;
stops: readonly ChartGradientStop[];
}
export type ChartMarkScaleX<TMark> = TMark extends ChartMark<any, any, any, infer TValue, any> ? TValue : never;
export type ChartMarkScaleY<TMark> = TMark extends ChartMark<any, any, any, any, infer TValue> ? TValue : never;
export type ChartMarkPointX<TMark> = TMark extends ChartMark<infer TDatum, infer TXValue, any, any, any> ? [TDatum] extends [never] ? never : TXValue : never;
export type ChartMarkPointY<TMark> = TMark extends ChartMark<infer TDatum, any, infer TYValue, any, any> ? [TDatum] extends [never] ? never : TYValue : never;
/** @deprecated Prefer ChartMarkPointX when distinguishing point and scale values. */
export type ChartMarkX<TMark> = ChartMarkPointX<TMark>;
/** @deprecated Prefer ChartMarkPointY when distinguishing point and scale values. */
export type ChartMarkY<TMark> = ChartMarkPointY<TMark>;
type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
export type ChartAxisValue<TValue> = IsAny<TValue> extends true ? any : [TValue] extends [never] ? any : [ChartValue] extends [TValue] ? any : WidenChartValue<TValue>;
type AnyChartMarks = readonly ChartMark<unknown, any, any>[];
type IsUnion<TValue, TWhole = TValue> = TValue extends TWhole ? [TWhole] extends [TValue] ? false : true : never;
type ChartXOptionsForMarks<TMarks extends AnyChartMarks> = IsUnion<TMarks> extends false ? ChartAxisOptions<ChartAxisValue<ChartMarkScaleX<TMarks[number]>>> : TMarks extends AnyChartMarks ? ChartAxisOptions<ChartAxisValue<ChartMarkScaleX<TMarks[number]>>> : never;
type ChartYOptionsForMarks<TMarks extends AnyChartMarks> = IsUnion<TMarks> extends false ? ChartAxisOptions<ChartAxisValue<ChartMarkScaleY<TMarks[number]>>> : TMarks extends AnyChartMarks ? ChartAxisOptions<ChartAxisValue<ChartMarkScaleY<TMarks[number]>>> : never;
interface ChartSpecBase {
/** Omit all Cartesian axes and grids. */
guides?: boolean;
color?: ChartColorOptions;
gradients?: readonly ChartLinearGradient[];
clip?: boolean;
margin?: number | Partial<ChartMargin>;
theme?: Partial<ChartTheme>;
}
export type ChartMotionPhase = 'enter' | 'update' | 'exit';
export type ChartMotionRole = 'area' | 'arc' | 'arrow' | 'axis' | 'axis-label' | 'band' | 'bar' | 'dot' | 'facet' | 'frame' | 'geo' | 'grid' | 'hexagon' | 'line' | 'link' | 'mark' | 'rect' | 'rule' | 'text' | 'tick' | 'tick-label' | 'vector';
export interface ChartMotionContext<TDatum = unknown> {
phase: ChartMotionPhase;
role: ChartMotionRole;
key: string;
markId?: string;
seriesKey: string;
seriesIndex: number;
datumIndex: number;
datumCount: number;
datum: TDatum | undefined;
point: ChartPoint<TDatum> | undefined;
axis?: 'x' | 'y';
}
export interface ChartMotionTweenTransition {
type: 'tween';
duration?: number;
easing?: ChartAnimationOptions['easing'];
}
export interface ChartMotionSpringTransition {
type: 'spring';
stiffness?: number;
damping?: number;
mass?: number;
restSpeed?: number;
restDelta?: number;
}
export type ChartMotionTransition = ChartMotionTweenTransition | ChartMotionSpringTransition;
export interface ChartRollingPathMotion {
update: 'rolling';
x: 'shift';
y?: 'fixed' | 'reproject';
fallback?: 'snap' | 'morph';
}
export type ChartMotionPath = 'morph' | ChartRollingPathMotion;
export interface ChartMotionTiming<TDatum = unknown> {
delay?: number | ((context: ChartMotionContext<TDatum>) => number | undefined);
transition?: ChartMotionTransition;
/** How line and area paths move between compatible keyed updates. */
path?: ChartMotionPath;
}
export type ChartMotionDefinition<TDatum = unknown> = false | ChartMotionTiming<TDatum> | ((context: ChartMotionContext<TDatum>) => false | ChartMotionTiming<TDatum> | undefined);
export interface ChartMarkMotionOptions<TDatum = unknown> {
motion?: ChartMotionDefinition<TDatum>;
}
interface StoredChartSpec extends ChartSpecBase {
marks: readonly ChartMark<unknown, any, any>[];
x?: ChartAxisOptions<any> | null;
y?: ChartAxisOptions<any> | null;
}
type ChartXSpec<TMarks extends AnyChartMarks> = IsAny<ChartMarkScaleX<TMarks[number]>> extends true ? {
x: ChartXOptionsForMarks<TMarks> | null;
} : [ChartMarkScaleX<TMarks[number]>] extends [never] ? {
x?: null;
} : {
x: ChartXOptionsForMarks<TMarks>;
};
type ChartYSpec<TMarks extends AnyChartMarks> = IsAny<ChartMarkScaleY<TMarks[number]>> extends true ? {
y: ChartYOptionsForMarks<TMarks> | null;
} : [ChartMarkScaleY<TMarks[number]>] extends [never] ? {
y?: null;
} : {
y: ChartYOptionsForMarks<TMarks>;
};
type ChartSpecForMarks<TMarks extends AnyChartMarks> = {
marks: TMarks;
} & ChartSpecBase & ChartXSpec<TMarks> & ChartYSpec<TMarks>;
export type ChartSpec<TMarks extends AnyChartMarks | undefined = undefined> = [
TMarks
] extends [AnyChartMarks] ? ChartSpecForMarks<Extract<TMarks, AnyChartMarks>> : StoredChartSpec;
export type ChartSelectionSource = 'pointer' | 'keyboard';
export interface ChartSelectionController<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
readonly type: 'keyed';
change: (point: ChartPoint<TDatum, TXValue, TYValue> | null, source: ChartSelectionSource) => void;
}
export interface ChartDefinitionOptions<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue, TTooltipHost extends string = string> {
maxFocusDistance?: number;
focus?: ChartFocusMode<NoInfer<TDatum>, NoInfer<TXValue>, NoInfer<TYValue>>;
/** Shows the built-in primary-point focus ring. Defaults to true. */
focusRing?: boolean;
/** Optional app-owned cursor shared by one or more chart definitions. */
cursor?: ChartCursorBinding<NoInfer<TDatum>, NoInfer<TXValue>, NoInfer<TYValue>>;
spatialIndex?: ChartSpatialIndexFactory<TDatum, TXValue, TYValue>;
svgAnimation?: boolean | ChartAnimationOptions;
/** Renderer-neutral motion defaults. An optional motion implementation consumes them. */
motion?: ChartMotionDefinition<NoInfer<TDatum>>;
/** Enables chart-owned pointer focus and selection. Defaults to true. */
pointer?: boolean;
keyboard?: boolean;
selection?: ChartSelectionController<NoInfer<TDatum>, NoInfer<TXValue>, NoInfer<TYValue>>;
controls?: readonly ChartControl<NoInfer<TXValue>, NoInfer<TYValue>>[];
tooltip?: false | ChartTooltipInput<NoInfer<TDatum>, NoInfer<TXValue>, NoInfer<TYValue>, TTooltipHost>;
}
interface StoredChartDefinitionOptions<TTooltipHost extends string = string> {
maxFocusDistance?: number;
focus?: ChartFocusMode<any, any, any>;
focusRing?: boolean;
cursor?: ChartCursorBinding<any, any, any>;
spatialIndex?: ChartSpatialIndexFactory<any, any, any>;
svgAnimation?: boolean | ChartAnimationOptions;
motion?: ChartMotionDefinition<any>;
pointer?: boolean;
keyboard?: boolean;
selection?: ChartSelectionController<any, any, any>;
controls?: readonly ChartControl<any, any>[];
tooltip?: false | ChartTooltipInput<any, any, any, TTooltipHost>;
}
export interface StaticChartDefinition<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue, TTooltipHost extends string = string> extends StoredChartSpec, StoredChartDefinitionOptions<TTooltipHost> {
marks: readonly ChartMark<unknown, any, any>[];
readonly __datum?: TDatum;
readonly __xValue?: TXValue;
readonly __yValue?: TYValue;
}
export interface ChartBuildContext {
width: number;
height: number;
/** Platform default tokens before a returned chart spec applies its theme. */
defaultTheme: ChartTheme;
}
export type CheckedChartSpec<TSpec extends StoredChartSpec> = TSpec & ChartSpec<TSpec['marks']>;
export interface ResponsiveChartConfig<TSpec extends StoredChartSpec = StoredChartSpec, TTooltipHost extends string = string> extends ChartDefinitionOptions<ChartSpecDatum<TSpec>, ChartSpecXValue<TSpec>, ChartSpecYValue<TSpec>, TTooltipHost> {
chart: (context: ChartBuildContext) => CheckedChartSpec<TSpec>;
}
export interface ResponsiveChartDefinition<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue, TTooltipHost extends string = string> extends StoredChartDefinitionOptions<TTooltipHost> {
chart: (context: ChartBuildContext) => StoredChartSpec;
readonly __datum?: TDatum;
readonly __xValue?: TXValue;
readonly __yValue?: TYValue;
}
export type ChartDefinition<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue, TTooltipHost extends string = string> = StaticChartDefinition<TDatum, TXValue, TYValue, TTooltipHost> | ResponsiveChartDefinition<TDatum, TXValue, TYValue, TTooltipHost>;
export type ChartDefinitionForTooltipHost<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue, TTooltipHost extends string = string> = (Omit<StaticChartDefinition<TDatum, TXValue, TYValue, TTooltipHost>, 'tooltip'> & {
tooltip?: false | ChartTooltipInput<TDatum, TXValue, TYValue, TTooltipHost>;
}) | (Omit<ResponsiveChartDefinition<TDatum, TXValue, TYValue, TTooltipHost>, 'tooltip'> & {
tooltip?: false | ChartTooltipInput<TDatum, TXValue, TYValue, TTooltipHost>;
});
export type DomChartDefinition<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = ChartDefinitionForTooltipHost<TDatum, TXValue, TYValue, 'dom'>;
export type ChartMarkDatum<TMark> = TMark extends ChartMark<infer TDatum, any, any> ? TDatum : never;
export type ChartSpecDatum<TSpec extends StoredChartSpec> = '__datum' extends keyof TSpec ? TSpec extends {
readonly __datum?: infer TDatum;
} ? TDatum : never : ChartMarkDatum<TSpec['marks'][number]>;
export type ChartSpecXValue<TSpec extends StoredChartSpec> = '__xValue' extends keyof TSpec ? TSpec extends {
readonly __xValue?: infer TXValue extends ChartValue;
} ? TXValue : never : ChartMarkPointX<TSpec['marks'][number]>;
export type ChartSpecYValue<TSpec extends StoredChartSpec> = '__yValue' extends keyof TSpec ? TSpec extends {
readonly __yValue?: infer TYValue extends ChartValue;
} ? TYValue : never : ChartMarkPointY<TSpec['marks'][number]>;
export interface MaterializedChannel {
scale?: string;
values: readonly unknown[];
includeZero?: boolean;
}
export interface MarkInitializeContext {
markIndex: number;
}
export interface ResolvedScaleViewport {
/** Complete domain resolved before applying the semantic viewport. */
contentDomain: readonly ChartValue[];
/** Committed semantic window mapped into the plot range. */
domain: ChartContinuousDomain;
/** Transient scene-pixel displacement of presented chart content. */
translate: number;
/** Maps a semantic value to its presented coordinate. */
map: (value: unknown) => number;
}
export interface ResolvedScale {
id: string;
type: string;
domain: readonly ChartValue[];
map: (value: unknown) => number;
invert?: (position: number) => ChartValue;
ticks: readonly ChartTick[];
bandwidth: number;
viewport?: ResolvedScaleViewport;
}
export interface ResolvedColorScale {
type: string;
kind?: ResolvedColorScaleKind;
domain: readonly ChartKey[];
range: readonly string[];
/** Exact interior legend boundaries for a custom stepped scale. */
thresholds?: readonly number[];
map: (value: ChartKey | null | undefined) => string;
}
export interface MarkRenderContext {
markIndex: number;
surface: ChartBounds;
chart: ChartBounds;
scales: Readonly<Record<string, ResolvedScale>>;
theme: ChartTheme;
color: (value: ChartKey | null | undefined) => string;
colors: ResolvedColorScale;
layout: ChartLayoutOptions;
}
/**
* Final positional scale and plot geometry available to a mark-local layout.
*
* A layout may run more than once while automatic margins converge. It must be
* synchronous, pure, and deterministic. Positional scale domains come only
* from the channels returned by `initialize`; layout-resolved channels may
* contribute to non-positional scales such as color.
*/
export interface MarkResolvedLayoutContext {
markIndex: number;
chart: ChartBounds;
scales: Readonly<Record<string, ResolvedScale>>;
theme: ChartTheme;
layout: ChartLayoutOptions;
}
export interface ChartMark<TDatum = unknown, TXPointValue extends ChartValue = ChartValue, TYPointValue extends ChartValue = ChartValue, TXScaleValue extends ChartValue = TXPointValue, TYScaleValue extends ChartValue = TYPointValue> {
initialize: (context: MarkInitializeContext) => InitializedMark<TDatum, TXPointValue, TYPointValue>;
motion?: ChartMotionDefinition<any>;
readonly __xValue?: TXPointValue;
readonly __yValue?: TYPointValue;
readonly __xScaleValue?: TXScaleValue;
readonly __yScaleValue?: TYScaleValue;
}
interface InitializedMarkBase<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
id: string;
channels: Readonly<Record<string, MaterializedChannel>>;
/** Scene-local motion policy resolved while this mark is initialized. */
motion?: ChartMotionDefinition<any>;
/** Overrides channel-inferred ownership of each continuous viewport axis. */
viewport?: Readonly<Partial<Record<'x' | 'y', 'content' | 'fixed'>>>;
/** This mark contributes only dynamic focus-guide presentation. */
focusGuideOnly?: boolean;
/** The mark uses a discrete color channel as inferred series identity. */
seriesFromColor?: boolean;
focus?: ChartFocusFilter;
states?: {
data: readonly unknown[];
definitions: readonly ChartMarkState<any>[];
};
/** Optional final mark-scene pass after chart-level domain-dependent filters. */
postDomain?: (scene: MarkScene<any, any, any>) => MarkScene<any, any, any>;
layoutLabels?: (context: MarkRenderContext) => readonly SceneLabel[];
}
export interface ResolvedMarkLayout<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
/**
* Final channels used by non-positional scale inference. When omitted, the
* initialized channels are retained. These channels never re-domain x or y.
*/
channels?: Readonly<Record<string, MaterializedChannel>>;
states?: {
data: readonly unknown[];
definitions: readonly ChartMarkState<any>[];
};
postDomain?: (scene: MarkScene<any, any, any>) => MarkScene<any, any, any>;
layoutLabels?: (context: MarkRenderContext) => readonly SceneLabel[];
render: (context: MarkRenderContext) => MarkScene<TDatum, TXValue, TYValue>;
}
export interface InitializedMark<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> extends InitializedMarkBase<TDatum, TXValue, TYValue> {
render: (context: MarkRenderContext) => MarkScene<TDatum, TXValue, TYValue>;
resolveLayout?: (context: MarkResolvedLayoutContext) => ResolvedMarkLayout<TDatum, TXValue, TYValue>;
}
export interface ResolvedLayoutMarkInitialization<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> extends InitializedMarkBase<TDatum, TXValue, TYValue> {
render?: never;
resolveLayout: (context: MarkResolvedLayoutContext) => ResolvedMarkLayout<TDatum, TXValue, TYValue>;
}
export type MarkInitialization<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = InitializedMark<TDatum, TXValue, TYValue> | ResolvedLayoutMarkInitialization<TDatum, TXValue, TYValue>;
export interface MarkScene<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
nodes: readonly SceneNode[];
points?: readonly ChartPoint<TDatum, TXValue, TYValue>[];
/** Semantic anchors used only when this mark is wrapped in `whenFocused`. */
focusAnchors?: readonly ChartFocusAnchor[];
/** Dynamic focus presentation emitted by data-less guide marks. */
focusGuides?: readonly MarkFocusGuide[];
}
/** Guide emitted by a mark before the scene compiler resolves placement. */
export type MarkFocusGuide = Omit<SceneFocusGuide, 'placement'> & {
/** Overrides mark-order placement, primarily for composed nested scenes. */
placement?: SceneFocusGuide['placement'];
};
/** Semantic identity for focus-filtered geometry without pointer hit testing. */
export interface ChartFocusAnchor {
key: string;
markId: string;
group: ChartKey | null;
datum: unknown;
datumIndex: number;
xValue?: ChartValue;
yValue?: ChartValue;
}
export type ChartFocusAffinity = 'x' | 'y' | 'xy' | 'geometry';
export interface ChartPoint<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
key: string;
markId: string;
group: ChartKey | null;
groupLabel: string;
datum: TDatum;
datumIndex: number;
xValue: TXValue;
yValue: TYValue;
x1Value?: ChartValue;
x2Value?: ChartValue;
y1Value?: ChartValue;
y2Value?: ChartValue;
xInterval?: 'range' | 'difference';
yInterval?: 'range' | 'difference';
x: number;
y: number;
color: string;
}
/** Semantic focus data attached to the scene primitive that paints it. */
export type SceneInteraction = {
point: ChartPoint;
points?: never;
/** Natural pointer fallback after exact geometry containment. */
affinity?: ChartFocusAffinity;
} | {
point?: never;
points: readonly ChartPoint[];
/** Natural pointer fallback after exact geometry containment. */
affinity?: ChartFocusAffinity;
};
export interface ChartTick {
value: ChartValue;
label: string;
position: number;
}
export interface SceneStyle {
fill?: string;
fillOpacity?: number;
stroke?: string;
strokeOpacity?: number;
strokeWidth?: number;
opacity?: number;
lineCap?: 'butt' | 'round' | 'square';
lineJoin?: 'arcs' | 'bevel' | 'miter' | 'miter-clip' | 'round';
strokeDasharray?: string;
}
export interface SceneFocusGuideLabel {
format?: (value: ChartValue) => string;
offset: number;
fontSize: number;
fontWeight?: number;
style: SceneStyle;
}
export interface SceneFocusGuideAxis {
style: SceneStyle;
label?: SceneFocusGuideLabel;
/** Categorical band geometry that replaces the axis rule when present. */
band?: SceneFocusGuideBand;
}
export interface SceneFocusGuideBand {
/** Full categorical scale bandwidth before applying `inset`. */
bandwidth: number;
/** Inset from both categorical band edges. Negative values create an outset. */
inset: number;
radius?: number;
style: SceneStyle;
}
export interface SceneFocusGuideMarker {
radius: number;
style: SceneStyle;
}
export interface SceneFocusGuideResolveContext {
scene: ChartScene;
guide: SceneFocusGuide;
focus: ChartFocusState | null;
pointer?: ChartTooltipPosition | null;
cursor?: ChartCursorPresentation | null;
}
export type SceneFocusGuideResolver = (context: SceneFocusGuideResolveContext) => SceneNode | undefined;
/** Renderer-neutral description of presentation derived from chart focus. */
export interface SceneFocusGuide {
key: string;
markId: string;
chart: ChartBounds;
surface: ChartBounds;
placement: 'under' | 'over';
x?: SceneFocusGuideAxis;
y?: SceneFocusGuideAxis;
marker?: SceneFocusGuideMarker;
/** Projects semantic cursor values into this guide's local x coordinate. */
projectX?: (value: ChartValue) => number | undefined;
/** Projects semantic cursor values into this guide's local y coordinate. */
projectY?: (value: ChartValue) => number | undefined;
motion?: ChartMotionDefinition<never>;
measureText?: ChartTextMeasurer;
/** Facet-owned point-key prefix. Omitted for a top-level guide. */
scope?: string;
/** Resolves this guide's dynamic presentation without retaining its implementation in every renderer bundle. */
resolve: SceneFocusGuideResolver;
}
export interface ChartFocusPresentation {
under: readonly SceneNode[];
over: readonly SceneNode[];
}
interface SceneNodeBase {
key: string;
className?: string;
style?: SceneStyle;
ariaHidden?: boolean;
/** Point ownership for decorative geometry; does not make the node interactive. */
pointOwner?: ChartPoint;
}
interface InteractiveSceneNodeBase extends SceneNodeBase {
/** Interaction semantics for this primitive's rendered geometry. */
interaction?: SceneInteraction;
}
export interface SceneGroup extends SceneNodeBase {
kind: 'group';
children: readonly SceneNode[];
translateX?: number;
translateY?: number;
clip?: ChartBounds;
/** Point slot owned by this subtree inside an enclosing focus candidate tree. */
focusCandidateIndex?: number;
focus?: {
match: ChartFocusMatch;
/** Semantic focus anchors; these are not scene hit-test points. */
anchors?: readonly ChartFocusAnchor[];
/**
* Interaction points contributed by the focused mark. Decorative marks can
* leave this empty while supplying semantic `anchors`.
*/
points: readonly ChartPoint[];
placement: 'under' | 'over';
/** Keeps candidate geometry out of paint until focus resolves it. */
retarget?: boolean;
/** Renderer-neutral source geometry for a retargeting focus layer. */
candidates?: readonly SceneNode[];
/** Focus points represented by the currently selected children. */
activePoints?: readonly ChartPoint[];
};
states?: {
data: readonly unknown[];
definitions: readonly ChartMarkState<any>[];
points: readonly ChartPoint[];
};
}
export interface SceneRule extends InteractiveSceneNodeBase {
kind: 'rule';
x1: number;
y1: number;
x2: number;
y2: number;
}
export interface ScenePolyline extends InteractiveSceneNodeBase {
kind: 'polyline';
points: readonly (readonly [number, number])[];
path?: string;
}
/** One closed area boundary. The first ring in a polygon is its exterior. */
export type ScenePolygonRing = readonly (readonly [number, number])[];
/** One polygon expressed as an exterior ring followed by zero or more holes. */
export type ScenePolygon = readonly ScenePolygonRing[];
export interface SceneArea extends InteractiveSceneNodeBase {
kind: 'area';
points: readonly (readonly [number, number])[];
/** Structured disconnected polygons. When present, this is the rendered geometry. */
polygons?: readonly ScenePolygon[];
path?: string;
}
export interface SceneDot extends InteractiveSceneNodeBase {
kind: 'dot';
x: number;
y: number;
radius: number;
}
export interface SceneRect extends InteractiveSceneNodeBase {
kind: 'rect';
x: number;
y: number;
width: number;
height: number;
radius?: number;
/** Applied inset retained for absolute inline-state overrides. */
inset?: number;
/** Axes affected by `inset`; bars use only their categorical axis. */
insetAxis?: 'x' | 'y' | 'xy';
/** Categorical size ceiling retained while resolving inline-state insets. */
maxThickness?: number;
}
export interface SceneLabel extends SceneNodeBase {
kind: 'label';
x: number;
y: number;
text: string;
anchor?: 'start' | 'middle' | 'end';
baseline?: 'auto' | 'middle' | 'hanging';
rotate?: number;
fontSize?: number;
fontWeight?: number;
}
export type SceneNode = SceneGroup | SceneRule | ScenePolyline | SceneArea | SceneDot | SceneRect | SceneLabel;
export interface ChartScene<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> extends ChartSize {
margin: ChartMargin;
chart: ChartBounds;
nodes: readonly SceneNode[];
points: readonly ChartPoint<TDatum, TXValue, TYValue>[];
scales: Readonly<Record<string, ResolvedScale>>;
colors: ResolvedColorScale;
gradients: readonly ChartLinearGradient[];
theme: ChartTheme;
controls?: readonly ChartHostControl[];
focusGuides?: readonly SceneFocusGuide[];
}
export interface RenderChartOptions {
ariaLabel: string;
ariaDescription?: string;
className?: string;
tabIndex?: number;
idPrefix?: string;
}
export type RenderChartSvgOptions = RenderChartOptions;
export type ChartSvgRenderer<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = (scene: ChartScene<TDatum, TXValue, TYValue>, options: RenderChartSvgOptions) => string;
export interface ChartAnimationOptions {
duration?: number;
easing?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | ((progress: number) => number);
respectReducedMotion?: boolean;
resize?: boolean;
}
export interface ChartTooltipOptions<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
className?: string;
/** Overrides tooltip motion from the active motion renderer; `false` keeps it immediate. */
motion?: false | ChartMotionTransition;
portal?: ChartTooltipPortalInput;
items?: readonly ChartTooltipItem<TDatum, TXValue, TYValue>[];
sort?: ChartTooltipSort<TDatum, TXValue, TYValue>;
anchor?: ChartTooltipAnchor<TDatum, TXValue, TYValue>;
placement?: 'auto' | ChartTooltipPlacement | readonly ChartTooltipPlacement[];
offset?: number;
content?: (points: readonly ChartPoint<TDatum, TXValue, TYValue>[], context: ChartTooltipContentContext) => ChartTooltipContent;
format?: (point: ChartPoint<TDatum, TXValue, TYValue>, context: ChartTooltipContentContext) => string;
formatGroup?: (points: readonly ChartPoint<TDatum, TXValue, TYValue>[], context: ChartTooltipContentContext) => string;
sticky?: boolean;
visibility?: 'focus' | 'pinned';
}
export type ChartExtensionInput<TExtension, TOptions> = TExtension | ({
use: TExtension;
} & TOptions);
export interface ChartTooltipExtensionToken<THost extends string = string> {
readonly id: string;
readonly create: Function;
readonly __chartExtensionType: 'tooltip';
readonly __chartTooltipHost: THost;
}
export type ChartTooltipInput<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue, THost extends string = string> = ChartExtensionInput<ChartTooltipExtensionToken<THost>, ChartTooltipOptions<TDatum, TXValue, TYValue>>;
export type ChartTooltipPortalOptions = Record<never, never>;
export interface ChartTooltipPortalExtensionToken {
readonly id: string;
readonly create: Function;
readonly __chartExtensionType?: 'tooltip-portal';
}
export type ChartTooltipPortalInput = ChartExtensionInput<ChartTooltipPortalExtensionToken, ChartTooltipPortalOptions>;
export type ChartTooltipPlacement = 'top' | 'top-right' | 'right' | 'bottom-right' | 'bottom' | 'bottom-left' | 'left' | 'top-left';
export interface ChartTooltipPosition {
x: number;
y: number;
}
export type ChartFocusSource = 'pointer' | 'keyboard' | 'programmatic' | 'restored';
export interface ChartFocusState<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
primary: ChartPoint<TDatum, TXValue, TYValue>;
group: readonly ChartPoint<TDatum, TXValue, TYValue>[];
source: ChartFocusSource;
pinned: boolean;
}
/** One or both Cartesian cursor coordinates. */
export type ChartCursorCoordinates<TValue> = {
readonly x: TValue;
readonly y?: TValue;
} | {
readonly x?: TValue;
readonly y: TValue;
};
/** Local point identity used to disambiguate equal semantic cursor values. */
export interface ChartCursorPointIdentity {
readonly key: string;
readonly markId: string;
readonly datumIndex: number;
}
/** One or both semantic axis values carried by a cursor. */
export type ChartCursorValues<TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = {
readonly x: TXValue;
readonly y?: TYValue;
} | {
readonly x?: TXValue;
readonly y: TYValue;
};
interface ChartCursorStateBase<TXValue extends ChartValue, TYValue extends ChartValue> {
/** The interaction that most recently changed this cursor. */
readonly source: ChartFocusSource;
/** A pinned cursor survives pointer leave, cancellation, and blur. */
readonly pinned: boolean;
/** Preferred series when a semantic focus cursor resolves multiple points. */
readonly group?: ChartKey | null;
/**
* Optional host-local tie-breaker for equal semantic values. Consumers ignore
* it when the same point identity does not exist in their scene.
*/
readonly origin?: ChartCursorPointIdentity;
/** Coordinates in the scene that last emitted this state. */
readonly scene?: ChartCursorCoordinates<number>;
/** Plot-relative coordinates where left/top are zero and right/bottom are one. */
readonly normalized?: ChartCursorCoordinates<number>;
/** Semantic values resolved by focus or a free cursor's scale/axis policy. */
readonly value?: ChartCursorValues<TXValue, TYValue>;
}
/**
* App-owned cursor state. `anchor` identifies the authoritative coordinate
* space; the other coordinate fields are derived diagnostics from the host
* that most recently emitted the state.
*/
export type ChartCursorState<TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = ChartCursorStateBase<TXValue, TYValue> & ({
readonly anchor: 'scene';
readonly scene: ChartCursorCoordinates<number>;
} | {
readonly anchor: 'normalized';
readonly normalized: ChartCursorCoordinates<number>;
} | {
readonly anchor: 'value';
readonly value: ChartCursorValues<TXValue, TYValue>;
});
export type ChartCursorStateUpdater<TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = ChartCursorState<TXValue, TYValue> | null | ((previous: ChartCursorState<TXValue, TYValue> | null) => ChartCursorState<TXValue, TYValue> | null);
/** Framework-neutral observable state shared by one or more chart hosts. */
export interface ChartCursorController<TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
getState: () => ChartCursorState<TXValue, TYValue> | null;
subscribe: (listener: () => void) => () => void;
setState: (next: ChartCursorStateUpdater<TXValue, TYValue>) => void;
}
export interface ChartCursorExtensionToken {
readonly id: string;
readonly create: Function;
readonly __chartExtensionType?: 'cursor';
}
export interface ChartCursorAxisContext<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
axis: 'x' | 'y';
scene: ChartScene<TDatum, TXValue, TYValue>;
/** Position in chart-scene coordinates. */
position: number;
/** Plot-relative position where left/top are zero and right/bottom are one. */
normalized: number;
}
export interface ChartCursorAxisOptions<TDatum, TValue extends ChartValue, TXValue extends ChartValue, TYValue extends ChartValue> {
/**
* Overrides resolved-scale inversion for a free scene coordinate. Use this
* for explicit snapping or another semantic mapping policy.
*/
valueAt?: (context: ChartCursorAxisContext<TDatum, TXValue, TYValue>) => TValue | undefined;
}
export interface ChartFocusCursorBinding<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
use: ChartCursorExtensionToken;
controller: ChartCursorController<TXValue, TYValue>;
mode: 'focus';
/** Semantic axes shared between hosts. Defaults to `xy`. */
match?: 'x' | 'y' | 'xy';
/** Click, tap, Enter, or Space may pin and dismiss this cursor. */
pin?: boolean;
}
export interface ChartFreeCursorBinding<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
use: ChartCursorExtensionToken;
controller: ChartCursorController<TXValue, TYValue>;
mode: 'free';
/** Click or tap may pin and dismiss this cursor. */
pin?: boolean;
x?: ChartCursorAxisOptions<TDatum, TXValue, TXValue, TYValue>;
y?: ChartCursorAxisOptions<TDatum, TYValue, TXValue, TYValue>;
}
export type ChartCursorBinding<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> = ChartFocusCursorBinding<TDatum, TXValue, TYValue> | ChartFreeCursorBinding<TDatum, TXValue, TYValue>;
export interface ChartCursorAxisPresentation<TValue extends ChartValue = ChartValue> {
position: number;
normalized: number;
value?: TValue;
}
/** Host-local projection of app-owned cursor state into the current scene. */
export interface ChartCursorPresentation<TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = ChartValue> {
state: ChartCursorState<TXValue, TYValue>;
/** Axes enabled by the consuming binding after focus-match policy. */
axes: 'x' | 'y' | 'xy';
x?: ChartCursorAxisPresentation<TXValue>;
y?: ChartCursorAxisPresentation<TYValue>;
}
export type ChartFocusMatch = 'primary' | 'group' | 'key' | 'x' | 'y' | 'series';
export interface ChartFocusFilter {
match?: ChartFocusMatch;
/**
* Keeps only the current focus selection in the rendered scene and gives it
* stable structural keys so ordinary renderer motion can retarget it.
*/
retarget?: boolean;
}
export type ChartTooltipXAnchor = 'point' | 'pointer' | 'value' | 'group-center' | 'plot-left' | 'plot-center' | 'plot-right';
export type ChartTooltipYAnchor = 'point' | 'pointer' | 'value' | 'group-center' | 'plot-top' | 'plot-center' | 'plot-bottom';
export interface ChartTooltipAxisAnchor {
x: ChartTooltipXAnchor;
y: ChartTooltipYAnchor;
}
export interface ChartTooltipAnchorContext<TDatum = unknown, TXValue extends ChartValue = ChartValue, TYValue extends ChartValue = Char