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; channel: ChartPositionChannel; values: readonly unknown[]; range: readonly [number, number]; options: ChartPositionScaleOptions | undefined; tickCount: number; includeZero: boolean; } export type ChartContinuousValue = number | Date; export type ChartContinuousDomain = (Extract extends never ? never : readonly [number, number]) | (Extract extends never ? never : readonly [Date, Date]); export interface ChartScale { id: string; resolve: (context: ChartScaleResolveContext) => ResolvedScale; } export interface ConfiguredScaleLike { (value: TValue): number | undefined; bandwidth?: () => number; copy: () => ConfiguredScaleLike; domain: () => readonly TValue[]; invert?: (position: number) => TValue; range: (values: Iterable) => ConfiguredScaleLike; ticks?: (count: number) => readonly TValue[]; tickFormat?: (count: number) => (value: TValue) => string; } export interface InferableScaleLike extends ConfiguredScaleLike { domain: { (): readonly TValue[]; (values: Iterable): InferableScaleLike; }; } export type ChartScaleFactory = Function & { readonly copy?: never; readonly __chartValue?: TValue; }; export type ChartScaleInput = TValue extends ChartValue ? ConfiguredScaleLike | ChartScaleFactory : never; export interface ChartNumericScaleOptions { scale: ChartScaleInput; nice?: boolean | number; } export type ChartNumericScale = ((value: number) => number) | ChartNumericScaleOptions; export type ChartScaleResolver = (context: ChartScaleResolveContext) => ResolvedScale; export interface ChannelAccessorContext { index: number; data: readonly TDatum[]; } export type ChannelAccessor = (datum: TDatum, context: ChannelAccessorContext) => TValue; export type ChannelField = { [TKey in Extract]-?: TDatum[TKey] extends TValue ? TKey : never; }[Extract]; export type Channel = ChannelField | ChannelAccessor; export type WidenChartValue = TValue extends string ? string : TValue extends number ? number : TValue extends Date ? Date : never; export type ChannelOutput = TChannel extends ChannelAccessor ? WidenChartValue> : TChannel extends keyof TDatum ? WidenChartValue> : WidenChartValue; export type OptionChannelOutput = TOptions extends unknown ? TKey extends keyof TOptions ? ChannelOutput : WidenChartValue : never; export type VisualChannel = TValue | ChannelAccessor; export interface ChartMarkStateContext { datum: TDatum; index: number; data: readonly TDatum[]; point: ChartPoint; focus: ChartFocusState; pointer: ChartTooltipPosition | null; matches: (match: ChartFocusMatch) => boolean; } export type ChartMarkStateValue = TValue | ((context: ChartMarkStateContext) => TValue); export interface ChartMarkStateStyle { fill?: ChartMarkStateValue; fillOpacity?: ChartMarkStateValue; stroke?: ChartMarkStateValue; strokeOpacity?: ChartMarkStateValue; strokeWidth?: ChartMarkStateValue; opacity?: ChartMarkStateValue; strokeDasharray?: ChartMarkStateValue; r?: ChartMarkStateValue; radius?: ChartMarkStateValue; inset?: ChartMarkStateValue; fontSize?: ChartMarkStateValue; fontWeight?: ChartMarkStateValue; dx?: ChartMarkStateValue; dy?: ChartMarkStateValue; rotate?: ChartMarkStateValue; } export type ChartDotStateStyle = Pick, 'fill' | 'fillOpacity' | 'stroke' | 'strokeOpacity' | 'strokeWidth' | 'opacity' | 'r'>; export type ChartBarStateStyle = Pick, 'fill' | 'fillOpacity' | 'stroke' | 'strokeWidth' | 'opacity' | 'radius' | 'inset'>; export type ChartRectStateStyle = ChartBarStateStyle; export type ChartLineStateStyle = Pick, 'stroke' | 'strokeOpacity' | 'strokeWidth' | 'strokeDasharray' | 'opacity'>; export type ChartAreaStateStyle = Pick, 'fill' | 'fillOpacity' | 'stroke' | 'strokeOpacity' | 'strokeWidth' | 'opacity'>; export type ChartTextStateStyle = Pick, '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 = ChartMarkStateStyle> { when: ChartMarkStateSelector | ((context: ChartMarkStateContext) => 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; } export interface ChartRuntimeOptions { /** Platform theme passed to responsive builders and final scene resolution. */ defaultTheme?: Partial; } export interface ChartAxisTickOptions { /** 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 { minGap?: number; priority?: 'ends'; /** Values whose labels must remain visible even when they collide. */ keep?: readonly TValue[]; } export interface ChartAxisTickLabelContext { /** 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 = TOutput | ((context: ChartAxisTickLabelContext) => TOutput | undefined); export interface ChartAxisTickLabelOptions { rotate?: number; thin?: boolean | ChartAxisTickLabelThinOptions; fontSize?: ChartAxisTickLabelValue; fontWeight?: ChartAxisTickLabelValue; opacity?: ChartAxisTickLabelValue; anchor?: ChartAxisTickLabelValue; dx?: ChartAxisTickLabelValue; dy?: ChartAxisTickLabelValue; motion?: ChartMotionDefinition; } export interface ChartAxisLabelOptions { text: string; offset?: number | 'auto'; motion?: ChartMotionDefinition; } export interface ChartAxisPresentationOptions { line?: boolean; ticks?: false | ChartAxisTickOptions; tickLabels?: false | ChartAxisTickLabelOptions; label?: string | ChartAxisLabelOptions; motion?: ChartMotionDefinition; } interface ChartAxisViewportBase { /** Transient output-space displacement applied to chart content, in scene pixels. */ translate?: number; } export type ChartAxisViewportOptions = ChartAxisViewportBase & { /** Committed semantic window used by stationary guides. */ domain: ChartContinuousDomain; }; type ChartAxisViewportFor = IsAny extends true ? ChartAxisViewportOptions : [Extract] extends [never] ? never : ChartAxisViewportOptions>; export interface ChartAxisOptions { /** * A D3 scale factory infers its domain from materialized mark channels. * A scale instance retains its configured domain. */ scale: ChartScale | ChartScaleInput; /** 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; /** 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; } export type ChartPositionChannel = 'x' | 'y'; export type ChartAxisSide = 'top' | 'right' | 'bottom' | 'left'; export interface ChartPositionScaleOptions extends ChartAxisOptions { /** Required for named scales other than the reserved `x` and `y` defaults. */ channel?: ChartPositionChannel; /** Defaults to `bottom` for x scales and `left` for y scales. */ side?: ChartAxisSide; } export interface CartesianScaleBindings { /** Named x scale. Omit to use the reserved `x` scale. */ xScale?: string; /** Named y scale. Omit to use the reserved `y` scale. */ yScale?: string; } export interface ChartColorOptions { /** * A D3 color-scale factory infers its domain from color channels. * A scale instance retains its configured domain. */ scale?: ConfiguredColorScaleLike | ChartColorScaleFactory; resolver?: ChartColorScale; domain?: readonly ChartKey[]; range?: readonly string[]; nice?: boolean | number; legend?: ChartColorLegend; } export type ResolvedColorScaleKind = 'categorical' | 'continuous' | 'quantile' | 'quantize' | 'threshold'; export interface ConfiguredColorScaleLike { (value: TValue): TOutput | undefined; copy: () => ConfiguredColorScaleLike; domain?: () => readonly TValue[]; range?: () => readonly TOutput[]; } export interface InferableColorScaleLike extends ConfiguredColorScaleLike { domain: { (): readonly TValue[]; (values: Iterable): InferableColorScaleLike; }; range: { (): readonly TOutput[]; (values: Iterable): InferableColorScaleLike; }; ticks?: (count: number) => readonly TValue[]; nice?: (count?: number) => InferableColorScaleLike; thresholds?: () => readonly number[]; quantiles?: (count?: number) => readonly number[]; invertExtent?: (value: TOutput) => readonly [TValue | undefined, TValue | undefined]; } export type ChartColorScaleFactory = 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>; 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 { 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[]; } /** A scale-contributing mark that does not own interactive chart points. */ export type DecorativeChartMark> = TMark & { readonly __decorativeMark: TMark; }; interface DecorativeChartMarkBrand { readonly __decorativeMark: ChartMark; } export type ChartMarkScaleX = TMark extends { readonly __decorativeMark: infer TSource; } ? ChartMarkScaleX : TMark extends ChartMark ? 'x' extends TScaleId ? TValue : never : never; export type ChartMarkScaleY = TMark extends { readonly __decorativeMark: infer TSource; } ? ChartMarkScaleY : TMark extends ChartMark ? 'y' extends TScaleId ? TValue : never : never; export type ChartMarkPointX = TMark extends DecorativeChartMarkBrand ? never : TMark extends ChartMark ? [TDatum] extends [never] ? never : TXValue : never; export type ChartMarkPointY = TMark extends DecorativeChartMarkBrand ? never : TMark extends ChartMark ? [TDatum] extends [never] ? never : TYValue : never; /** @deprecated Prefer ChartMarkPointX when distinguishing point and scale values. */ export type ChartMarkX = ChartMarkPointX; /** @deprecated Prefer ChartMarkPointY when distinguishing point and scale values. */ export type ChartMarkY = ChartMarkPointY; type IsAny = 0 extends 1 & TValue ? true : false; export type ChartAxisValue = IsAny extends true ? any : [TValue] extends [never] ? any : [ChartValue] extends [TValue] ? any : WidenChartValue; type AnyChartMarks = readonly ChartMark[]; type IsUnion = TValue extends TWhole ? [TWhole] extends [TValue] ? false : true : never; type ChartXOptionsForMarks = IsUnion extends false ? ChartPositionScaleOptions>> : TMarks extends AnyChartMarks ? ChartPositionScaleOptions>> : never; type ChartYOptionsForMarks = IsUnion extends false ? ChartPositionScaleOptions>> : TMarks extends AnyChartMarks ? ChartPositionScaleOptions>> : never; interface ChartSpecBase { /** Omit all Cartesian axes and grids. */ guides?: boolean; color?: ChartColorOptions; gradients?: readonly ChartLinearGradient[]; clip?: boolean; margin?: number | Partial; theme?: Partial; } 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 { phase: ChartMotionPhase; role: ChartMotionRole; key: string; markId?: string; seriesKey: string; seriesIndex: number; datumIndex: number; datumCount: number; datum: TDatum | undefined; point: ChartPoint | undefined; axis?: 'x' | 'y'; /** Positional scale that owns an axis or grid element. */ scaleId?: string; } 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 { delay?: number | ((context: ChartMotionContext) => number | undefined); transition?: ChartMotionTransition; /** How line and area paths move between compatible keyed updates. */ path?: ChartMotionPath; } export type ChartMotionDefinition = false | ChartMotionTiming | ((context: ChartMotionContext) => false | ChartMotionTiming | undefined); /** Renderer capability a mark can select without importing a DOM contract. */ export interface ChartMarkRenderer { readonly kind: 'chart-layer-renderer'; readonly id: string; } export interface ChartMarkOptions { /** Paints this mark through a renderer that can compose with the chart host. */ renderer?: ChartMarkRenderer; } export interface ChartMarkMotionOptions extends ChartMarkOptions { motion?: ChartMotionDefinition; } type ChartXSpec = IsAny> extends true ? { x: ChartXOptionsForMarks | null; } : [ChartMarkScaleX] extends [never] ? { x?: null; } : { x: ChartXOptionsForMarks; }; type ChartYSpec = IsAny> extends true ? { y: ChartYOptionsForMarks | null; } : [ChartMarkScaleY] extends [never] ? { y?: null; } : { y: ChartYOptionsForMarks; }; export type ChartScales = Readonly> & { x: ChartXOptionsForMarks | null; y: ChartYOptionsForMarks | null; }; interface CanonicalChartScaleSpec { scales: ChartScales; /** @deprecated Move this value to `scales.x`. */ x?: ChartXOptionsForMarks | null; /** @deprecated Move this value to `scales.y`. */ y?: ChartYOptionsForMarks | null; } type LegacyChartScaleSpec = { scales?: undefined; } & ChartXSpec & ChartYSpec; type ChartSpecForMarks = { marks: TMarks; } & ChartSpecBase & (CanonicalChartScaleSpec | LegacyChartScaleSpec); interface StoredChartSpec extends ChartSpecBase { marks: AnyChartMarks; scales?: Readonly>; /** @deprecated Move this value to `scales.x`. */ x?: ChartXOptionsForMarks | null; /** @deprecated Move this value to `scales.y`. */ y?: ChartYOptionsForMarks | null; } export type ChartSpec = [ TMarks ] extends [AnyChartMarks] ? ChartSpecForMarks> : StoredChartSpec; export type ChartSelectionSource = 'pointer' | 'keyboard'; export interface ChartSelectionController { readonly type: 'keyed'; change: (point: ChartPoint | null, source: ChartSelectionSource) => void; } export interface ChartDefinitionOptions { maxFocusDistance?: number; focus?: ChartFocusMode, NoInfer, NoInfer>; /** 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, NoInfer>; spatialIndex?: ChartSpatialIndexFactory; svgAnimation?: boolean | ChartAnimationOptions; /** Renderer-neutral motion defaults. An optional motion implementation consumes them. */ motion?: ChartMotionDefinition>; /** Enables chart-owned pointer focus and selection. Defaults to true. */ pointer?: boolean; keyboard?: boolean; selection?: ChartSelectionController, NoInfer, NoInfer>; controls?: readonly ChartControl, NoInfer>[]; tooltip?: false | ChartTooltipInput, NoInfer, NoInfer, TTooltipHost>; } interface StoredChartDefinitionOptions { maxFocusDistance?: number; focus?: ChartFocusMode; focusRing?: boolean; cursor?: ChartCursorBinding; spatialIndex?: ChartSpatialIndexFactory; svgAnimation?: boolean | ChartAnimationOptions; motion?: ChartMotionDefinition; pointer?: boolean; keyboard?: boolean; selection?: ChartSelectionController; controls?: readonly ChartControl[]; tooltip?: false | ChartTooltipInput; } export interface StaticChartDefinition extends StoredChartSpec, StoredChartDefinitionOptions { marks: AnyChartMarks; 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 & ChartSpec; export interface ResponsiveChartConfig extends ChartDefinitionOptions, ChartSpecXValue, ChartSpecYValue, TTooltipHost> { chart: (context: ChartBuildContext) => CheckedChartSpec; } export interface ResponsiveChartDefinition extends StoredChartDefinitionOptions { chart: (context: ChartBuildContext) => StoredChartSpec; readonly __datum?: TDatum; readonly __xValue?: TXValue; readonly __yValue?: TYValue; } export type ChartDefinition = StaticChartDefinition | ResponsiveChartDefinition; export type ChartDefinitionForTooltipHost = (Omit, 'tooltip'> & { tooltip?: false | ChartTooltipInput; }) | (Omit, 'tooltip'> & { tooltip?: false | ChartTooltipInput; }); export type DomChartDefinition = ChartDefinitionForTooltipHost; export type ChartMarkDatum = TMark extends DecorativeChartMarkBrand ? never : TMark extends ChartMark ? TDatum : never; export type ChartSpecDatum = '__datum' extends keyof TSpec ? TSpec extends { readonly __datum?: infer TDatum; } ? TDatum : never : ChartMarkDatum; export type ChartSpecXValue = '__xValue' extends keyof TSpec ? TSpec extends { readonly __xValue?: infer TXValue extends ChartValue; } ? TXValue : never : ChartMarkPointX; export type ChartSpecYValue = '__yValue' extends keyof TSpec ? TSpec extends { readonly __yValue?: infer TYValue extends ChartValue; } ? TYValue : never : ChartMarkPointY; 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>; 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>; theme: ChartTheme; layout: ChartLayoutOptions; } export interface ChartMark { initialize: (context: MarkInitializeContext) => InitializedMark; motion?: ChartMotionDefinition; renderer?: ChartMarkRenderer; readonly __xValue?: TXPointValue; readonly __yValue?: TYPointValue; readonly __xScaleValue?: TXScaleValue; readonly __yScaleValue?: TYScaleValue; readonly __xScaleId?: TXScaleId; readonly __yScaleId?: TYScaleId; } export type OptionScaleId = TOptions extends unknown ? TKey extends keyof TOptions ? [NonNullable] extends [never] ? TFallback : NonNullable extends string ? NonNullable : TFallback : TFallback : never; export type CartesianChartMark = ChartMark, OptionScaleId>; interface InitializedMarkBase { id: string; channels: Readonly>; /** Scene-local motion policy resolved while this mark is initialized. */ motion?: ChartMotionDefinition; /** Overrides channel-inferred ownership of each continuous viewport axis. */ viewport?: Readonly>>; /** 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[]; }; /** Optional final mark-scene pass after chart-level domain-dependent filters. */ postDomain?: (scene: MarkScene) => MarkScene; layoutLabels?: (context: MarkRenderContext) => readonly SceneLabel[]; } export interface ResolvedMarkLayout { /** * 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>; states?: { data: readonly unknown[]; definitions: readonly ChartMarkState[]; }; postDomain?: (scene: MarkScene) => MarkScene; layoutLabels?: (context: MarkRenderContext) => readonly SceneLabel[]; render: (context: MarkRenderContext) => MarkScene; } export interface InitializedMark extends InitializedMarkBase { render: (context: MarkRenderContext) => MarkScene; resolveLayout?: (context: MarkResolvedLayoutContext) => ResolvedMarkLayout; } export interface ResolvedLayoutMarkInitialization extends InitializedMarkBase { render?: never; resolveLayout: (context: MarkResolvedLayoutContext) => ResolvedMarkLayout; } export type MarkInitialization = InitializedMark | ResolvedLayoutMarkInitialization; export interface MarkScene { nodes: readonly SceneNode[]; points?: readonly ChartPoint[]; /** 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 & { /** 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 { 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'; /** Renderer selected by the guide's owning mark. */ renderer?: ChartMarkRenderer; 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; 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; /** Renderer selected by the mark that owns this scene subtree. */ renderer?: ChartMarkRenderer; 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[]; 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 extends ChartSize { margin: ChartMargin; chart: ChartBounds; nodes: readonly SceneNode[]; points: readonly ChartPoint[]; scales: Readonly>; 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 = (scene: ChartScene, 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 { className?: string; /** Overrides tooltip motion from the active motion renderer; `false` keeps it immediate. */ motion?: false | ChartMotionTransition; portal?: ChartTooltipPortalInput; items?: readonly ChartTooltipItem[]; sort?: ChartTooltipSort; anchor?: ChartTooltipAnchor; placement?: 'auto' | ChartTooltipPlacement | readonly ChartTooltipPlacement[]; offset?: number; content?: (points: readonly ChartPoint[], context: ChartTooltipContentContext) => ChartTooltipContent; format?: (point: ChartPoint, context: ChartTooltipContentContext) => string; formatGroup?: (points: readonly ChartPoint[], context: ChartTooltipContentContext) => string; sticky?: boolean; visibility?: 'focus' | 'pinned'; } export type ChartExtensionInput = TExtension | ({ use: TExtension; } & TOptions); export interface ChartTooltipExtensionToken { readonly id: string; readonly create: Function; readonly __chartExtensionType: 'tooltip'; readonly __chartTooltipHost: THost; } export type ChartTooltipInput = ChartExtensionInput, ChartTooltipOptions>; export type ChartTooltipPortalOptions = Record; export interface ChartTooltipPortalExtensionToken { readonly id: string; readonly create: Function; readonly __chartExtensionType?: 'tooltip-portal'; } export type ChartTooltipPortalInput = ChartExtensionInput; 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 { primary: ChartPoint; group: readonly ChartPoint[]; source: ChartFocusSource; pinned: boolean; } /** One or both Cartesian cursor coordinates. */ export type ChartCursorCoordinates = { 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 = { readonly x: TXValue; readonly y?: TYValue; } | { readonly x?: TXValue; readonly y: TYValue; }; interface ChartCursorStateBase { /** 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; /** Plot-relative coordinates where left/top are zero and right/bottom are one. */ readonly normalized?: ChartCursorCoordinates; /** Semantic values resolved by focus or a free cursor's scale/axis policy. */ readonly value?: ChartCursorValues; } /** * 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 = ChartCursorStateBase & ({ readonly anchor: 'scene'; readonly scene: ChartCursorCoordinates; } | { readonly anchor: 'normalized'; readonly normalized: ChartCursorCoordinates; } | { readonly anchor: 'value'; readonly value: ChartCursorValues; }); export type ChartCursorStateUpdater = ChartCursorState | null | ((previous: ChartCursorState | null) => ChartCursorState | null); /** Framework-neutral observable state shared by one or more chart hosts. */ export interface ChartCursorController { getState: () => ChartCursorState | null; subscribe: (listener: () => void) => () => void; setState: (next: ChartCursorStateUpdater) => void; } export interface ChartCursorExtensionToken { readonly id: string; readonly create: Function; readonly __chartExtensionType?: 'cursor'; } export interface ChartCursorAxisContext { axis: 'x' | 'y'; scene: ChartScene; /** 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 { /** * Overrides resolved-scale inversion for a free scene coordinate. Use this * for explicit snapping or another semantic mapping policy. */ valueAt?: (context: ChartCursorAxisContext) => TValue | undefined; } export interface ChartFocusCursorBinding { use: ChartCursorExtensionToken; controller: ChartCursorController; 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 { use: ChartCursorExtensionToken; controller: ChartCursorController; mode: 'free'; /** Click or tap may pin and dismiss this cursor. */ pin?: boolean; x?: ChartCursorAxisOptions; y?: ChartCursorAxisOptions; } export type ChartCursorBinding = ChartFocusCursorBinding | ChartFreeCursorBinding; export interface ChartCursorAxisPresentation { position: number; normalized: number; value?: TValue; } /** Host-local projection of app-owned cursor state into the current scene. */ export interface ChartCursorPresentation { state: ChartCursorState; /** Axes enabled by the consuming binding after focus-match policy. */ axes: 'x' | 'y' | 'xy'; x?: ChartCursorAxisPresentation; y?: ChartCursorAxisPresentation; } 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 { focus: ChartFocusState; pointer: ChartTooltipPosition | null; plot: ChartBounds; surface: ChartSize; scales: Readonly>; } export type ChartTooltipAnchor = 'point' | 'pointer' | 'group-center' | ChartTooltipAxisAnchor | ((points: readonly ChartPoint[], context: ChartTooltipAnchorContext) => ChartTooltipPosition | null | undefined); export type ChartTooltipSort = 'visual' | 'color-domain' | 'focus' | ((left: ChartPoint, right: ChartPoint) => number); export type ChartTooltipItem = 'x' | 'y' | 'group' | ChartTooltipChannelItem | ChartTooltipDatumItem | ChartTooltipDerivedItem; export interface ChartTooltipItemBase { label?: string; text?: (point: ChartPoint, context: ChartTooltipContentContext) => string | null | undefined; } type ChartTooltipScalarDatumKey = { [TKey in keyof TDatum]-?: NonNullable extends ChartValue ? TKey : never; }[keyof TDatum] & string; export interface ChartTooltipChannelItem extends ChartTooltipItemBase { channel: 'x' | 'y' | 'group'; } export interface ChartTooltipDatumItem extends ChartTooltipItemBase { field: ChartTooltipScalarDatumKey; } export interface ChartTooltipDerivedItem extends ChartTooltipItemBase { id: string; text: (point: ChartPoint, context: ChartTooltipContentContext) => string | null | undefined; } export interface ChartTooltipContent { title?: string; color?: string; rows: readonly ChartTooltipRow[]; } export interface ChartTooltipContentContext { pinned: boolean; xLabel: string; yLabel: string; formatX: (value: ChartValue) => string; formatY: (value: ChartValue) => string; } export interface ChartTooltipRow { label: string; value: string; color?: string; } export interface ChartTooltipBodyContext { points: readonly ChartPoint[]; content: ChartTooltipContent | string; pinned: boolean; dismiss: () => void; } export interface ChartFocusStrategy { resolve: (points: readonly ChartPoint[], context: ChartFocusResolveContext) => readonly ChartPoint[]; group: (points: readonly ChartPoint[], context: ChartFocusGroupContext) => readonly ChartPoint[]; navigation: (points: readonly ChartPoint[]) => readonly ChartPoint[]; } export interface ChartFocusResolveContext { x: number; y: number; maxDistance: number; } export interface ChartFocusGroupContext { point: ChartPoint; } export type ChartFocusPreset = 'nearest' | 'nearest-x' | 'nearest-y' | 'group-x' | 'group-y'; export type ChartFocusMode = false | ChartFocusPreset | ChartFocusStrategy; export interface ChartSpatialIndex { findNearest: (x: number, y: number, maxDistance?: number) => ChartPoint | null; } export interface ChartSpatialIndexFactoryContext { scene: ChartScene; } export type ChartSpatialIndexFactory = (points: readonly ChartPoint[], context: ChartSpatialIndexFactoryContext) => ChartSpatialIndex; export interface ChartRuntime { render: (definition: ChartDefinition, size: ChartSize, layout?: ChartLayoutOptions) => ChartScene; destroy: () => void; } export {};