Aggrid custom

This commit is contained in:
2026-03-17 08:44:54 +01:00
commit 739374d83f
3234 changed files with 1533165 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

57189
node_modules/ag-charts-community/dist/package/main.cjs.js generated vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

58386
node_modules/ag-charts-community/dist/package/main.esm.mjs generated vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
import type { AgChartInstance, AgChartOptions, AgFinancialChartOptions, AgGaugeOptions, AgSparklineOptions, DatumDefault } from 'ag-charts-types';
import { type ChartInternalOptionMetadata } from '../module/optionsModule';
/**
* Factory for creating and updating instances of AgChartInstance.
*
* @docsInterface
*/
export declare abstract class AgCharts {
private static licenseManager?;
private static licenseChecked;
private static licenseCheck;
/** @private - for use by Charts website dark-mode support. */
static readonly optionsMutationFn?: (opts: AgChartOptions, preset?: string) => AgChartOptions;
static getLicenseDetails(licenseKey: string): object | undefined;
/**
* Returns the `AgChartInstance` for a DOM node, if there is one.
*/
static getInstance(element: HTMLElement): AgChartInstance | undefined;
/**
* Create a new `AgChartInstance` based upon the given configuration options.
*/
static create<O extends AgChartOptions<DatumDefault, any>>(// set TContext=any for backward-compatibility
userOptions: O, optionsMetadata?: ChartInternalOptionMetadata): AgChartInstance<O>;
static createFinancialChart(options: AgFinancialChartOptions): AgChartInstance<AgFinancialChartOptions>;
static createGauge(options: AgGaugeOptions): AgChartInstance<AgGaugeOptions>;
static __createSparkline(options: AgSparklineOptions): AgChartInstance<AgSparklineOptions>;
}

View File

@@ -0,0 +1,13 @@
import type { MementoOriginator } from 'ag-charts-core';
import type { AgInitialStateChartType } from 'ag-charts-types';
import type { ChartService } from '../../chart/chartService';
type ChartTypeMemento = AgInitialStateChartType;
export declare class ChartTypeOriginator implements MementoOriginator<ChartTypeMemento> {
private readonly chartService;
mementoOriginatorKey: "chartType";
constructor(chartService: ChartService);
createMemento(): import("ag-charts-types").AgPriceVolumeChartType;
guardMemento(blob: unknown): blob is ChartTypeMemento | undefined;
restoreMemento(_version: string, _mementoVersion: string, memento: ChartTypeMemento | undefined): void;
}
export {};

View File

@@ -0,0 +1,3 @@
import { type PresetModuleDefinition } from 'ag-charts-core';
import type { AgSparklineOptions } from 'ag-charts-types';
export declare const SparklinePresetModule: PresetModuleDefinition<AgSparklineOptions>;

View File

@@ -0,0 +1,10 @@
import type { AgCartesianChartOptions, AgSparklineOptions } from 'ag-charts-types';
export declare function sparklineDataPreset(data: any[] | undefined): {
data: any[] | undefined;
series?: {
xKey: string;
yKey: string;
}[];
datumKey?: string;
};
export declare function sparkline(opts: AgSparklineOptions): AgCartesianChartOptions;

View File

@@ -0,0 +1,21 @@
import type { MementoOriginator } from 'ag-charts-core';
import type { EventsHub } from '../../core/eventsHub';
export declare class HistoryManager {
private history;
private historyIndex;
private readonly originators;
private readonly clearState;
private readonly maxHistoryLength;
private readonly debug;
private readonly cleanup;
constructor(eventsHub: EventsHub);
destroy(): void;
addMementoOriginator(originator: MementoOriginator): void;
clear(): void;
record(label: string, ...originators: Array<MementoOriginator>): void;
undo(): void;
redo(): void;
private findPreviousMemento;
private restoreMemento;
private debugEvent;
}

View File

@@ -0,0 +1,8 @@
import { type MementoOriginator } from 'ag-charts-core';
export declare class StateManager {
private readonly caretaker;
private readonly state;
setState(originator: MementoOriginator, value: any): void;
setStateAndRestore(originator: MementoOriginator, value: any): void;
restoreState(originator: MementoOriginator): void;
}

View File

@@ -0,0 +1,26 @@
import type { MementoOriginator } from 'ag-charts-core';
import type { AgAnnotation, AgAnnotationsThemeableOptions } from 'ag-charts-types';
import type { EventsHub } from '../../core/eventsHub';
import type { Group } from '../../scene/group';
import type { Node } from '../../scene/node';
import type { TypedEvent } from '../../util/observable';
type AnnotationsMemento = AgAnnotation[];
export declare class AnnotationManager implements MementoOriginator<AnnotationsMemento> {
private readonly eventsHub;
private readonly annotationRoot;
private readonly fireChartEvent;
mementoOriginatorKey: "annotations";
private annotations;
private styles?;
constructor(eventsHub: EventsHub, annotationRoot: Group, fireChartEvent: <TEvent extends TypedEvent>(event: TEvent) => void);
createMemento(): AnnotationsMemento;
guardMemento(blob: unknown): blob is AnnotationsMemento | undefined;
restoreMemento(_version: string, _mementoVersion: string, memento: AnnotationsMemento | undefined): void;
updateData(annotations?: AnnotationsMemento): void;
fireChangedEvent(): void;
attachNode(node: Node): () => this;
setAnnotationStyles(styles: AgAnnotationsThemeableOptions): void;
getAnnotationTypeStyles(type: keyof Omit<AgAnnotationsThemeableOptions, 'axesButtons' | 'enabled' | 'optionsToolbar' | 'toolbar' | 'snap'>): import("ag-charts-types").AgFibonacciAnnotationStyles | import("ag-charts-types").AgLineAnnotationStyles | import("ag-charts-types").AgCalloutAnnotationStyles | import("ag-charts-types").AgTextAnnotationStyles | import("ag-charts-types").AgCommentAnnotationStyles | import("ag-charts-types").AgNoteAnnotationStyles | import("ag-charts-types").AgCrossLineAnnotationStyles | import("ag-charts-types").AgDisjointChannelAnnotationStyles | import("ag-charts-types").AgParallelChannelAnnotationStyles | import("ag-charts-types").AgShapeAnnotationStyles | import("ag-charts-types").AgMeasurerAnnotationStyles | import("ag-charts-types").AgQuickMeasurerAnnotationStyles | undefined;
private cleanData;
}
export {};

View File

@@ -0,0 +1,27 @@
import { type OptionsDefs, type Validator } from 'ag-charts-core';
import type { AgAxisBaseIntervalOptions, AgAxisDiscreteTimeIntervalOptions, AgBandHighlightOptions, AgBaseAxisLabelOptions, AgBaseAxisOptions, AgBaseCartesianAxisLabelOptions, AgBaseCartesianAxisOptions, AgBaseCrossLineLabelOptions, AgBaseCrossLineOptions, AgBaseCrosshairLabel, AgCartesianAxisLabelOptions, AgCartesianCrossLineOptions, AgCategoryAxisOptions, AgContinuousAxisOptions, AgCrosshairLabel, AgCrosshairOptions, AgGroupedCategoryAxisOptions, AgLogAxisOptions, AgNumberAxisOptions, AgTimeAxisFormattableLabelFormat, AgTimeAxisOptions, AgTimeAxisParentLevel, AgUnitTimeAxisOptions } from 'ag-charts-types';
export declare const timeIntervalUnit: Validator;
export declare const timeInterval: Validator;
export declare const commonCrossLineLabelOptionsDefs: OptionsDefs<AgBaseCrossLineLabelOptions>;
export declare const commonCrossLineOptionsDefs: OptionsDefs<AgBaseCrossLineOptions<AgBaseCrossLineLabelOptions>>;
export declare const cartesianCrossLineOptionsDefs: OptionsDefs<AgCartesianCrossLineOptions>;
export declare const commonAxisLabelOptionsDefs: OptionsDefs<AgBaseAxisLabelOptions>;
export declare const cartesianAxisLabelOptionsDefs: OptionsDefs<AgBaseCartesianAxisLabelOptions>;
export declare const cartesianNumericAxisLabel: OptionsDefs<AgCartesianAxisLabelOptions>;
export declare const cartesianTimeAxisLabel: OptionsDefs<AgCartesianAxisLabelOptions>;
export declare const cartesianTimeAxisParentLevel: OptionsDefs<AgTimeAxisParentLevel>;
export declare const commonAxisIntervalOptionsDefs: OptionsDefs<AgAxisBaseIntervalOptions>;
export declare const commonAxisOptionsDefs: OptionsDefs<Omit<AgBaseAxisOptions, 'type'>>;
export declare const cartesianAxisOptionsDefs: OptionsDefs<Omit<AgBaseCartesianAxisOptions<any>, 'type' | 'label' | 'primaryLabel' | 'crosshair'>>;
export declare const cartesianAxisBandHighlightOptions: OptionsDefs<AgBandHighlightOptions>;
export declare function cartesianAxisCrosshairOptions(): OptionsDefs<AgCrosshairOptions<AgBaseCrosshairLabel>>;
export declare function cartesianAxisCrosshairOptions(canFormat: true): OptionsDefs<AgCrosshairOptions<AgCrosshairLabel<string>>>;
export declare function cartesianAxisCrosshairOptions(canFormat: true, timeFormat: true): OptionsDefs<AgCrosshairOptions<AgCrosshairLabel<AgTimeAxisFormattableLabelFormat>>>;
export declare function continuousAxisOptions(validDatum: Validator, supportTimeInterval?: boolean): OptionsDefs<AgContinuousAxisOptions>;
export declare const discreteTimeAxisIntervalOptionsDefs: OptionsDefs<AgAxisDiscreteTimeIntervalOptions>;
export declare const categoryAxisOptionsDefs: OptionsDefs<AgCategoryAxisOptions>;
export declare const groupedCategoryAxisOptionsDefs: OptionsDefs<AgGroupedCategoryAxisOptions>;
export declare const numberAxisOptionsDefs: OptionsDefs<AgNumberAxisOptions>;
export declare const logAxisOptionsDefs: OptionsDefs<AgLogAxisOptions>;
export declare const timeAxisOptionsDefs: OptionsDefs<AgTimeAxisOptions>;
export declare const unitTimeAxisOptionsDefs: OptionsDefs<AgUnitTimeAxisOptions>;

View File

@@ -0,0 +1,7 @@
import { type OptionsDefs } from 'ag-charts-core';
import type { AgAngleCategoryAxisOptions, AgAngleNumberAxisOptions, AgOrdinalTimeAxisOptions, AgRadiusCategoryAxisOptions, AgRadiusNumberAxisOptions } from 'ag-charts-types';
export declare const ordinalTimeAxisOptionsDefs: OptionsDefs<AgOrdinalTimeAxisOptions>;
export declare const angleNumberAxisOptionsDefs: OptionsDefs<AgAngleNumberAxisOptions>;
export declare const angleCategoryAxisOptionsDefs: OptionsDefs<AgAngleCategoryAxisOptions>;
export declare const radiusNumberAxisOptionsDefs: OptionsDefs<AgRadiusNumberAxisOptions>;
export declare const radiusCategoryAxisOptionsDefs: OptionsDefs<AgRadiusCategoryAxisOptions>;

View File

@@ -0,0 +1,275 @@
import type { AxisID, Callback, CallbackParam, ChartAnimationPhase, DomainWithMetadata, Point, Scale } from 'ag-charts-core';
import { ChartAxisDirection, ChartUpdateType, CleanupRegistry } from 'ag-charts-core';
import type { AgAxisBoundSeries, AgBaseAxisLabelStyleOptions, AgTimeInterval, AgTimeIntervalUnit, CssColor, DateFormatterStyle, FormatterParams, TextOrSegments } from 'ag-charts-types';
import type { AxisLayout } from '../../core/eventsHub';
import type { AxisBandDatum, AxisBandMeasurement, AxisContext, AxisFormattableLabel } from '../../module/axisContext';
import type { ModuleContext, ModuleContextWithParent } from '../../module/moduleContext';
import { ModuleMap } from '../../module/moduleMap';
import { BBox } from '../../scene/bbox';
import { Group, TransformableGroup, TranslatableGroup } from '../../scene/group';
import type { Node } from '../../scene/node';
import { Selection } from '../../scene/selection';
import { type TextBoxingProperties, type TextSizeProperties, TransformableText } from '../../scene/shape/text';
import type { AxisPrimaryTickCount } from '../../util/secondaryAxisTicks';
import type { AxisGroups, ChartAxis, ChartLayout, FormatDatumParams } from '../chartAxis';
import type { CrossLine } from '../crossline/crossLine';
import type { DatumIndexType, ISeries } from '../series/seriesTypes';
import { AxisGridLine } from './axisGridLine';
import { AxisInterval } from './axisInterval';
import { AxisLabel } from './axisLabel';
import { AxisLine } from './axisLine';
import { AxisTick, type TickInterval } from './axisTick';
import { AxisTitle } from './axisTitle';
import { NiceMode } from './axisUtil';
import type { AnyTimeInterval } from './generateTicksUtils';
export interface LabelNodeDatum extends TextSizeProperties, TextBoxingProperties {
color?: CssColor;
tickId: string;
rotation: number;
text: TextOrSegments;
textBaseline: CanvasTextBaseline;
textUntruncated?: string;
visible: boolean;
x: number;
y: number;
rotationCenterX: number;
rotationCenterY: number;
range: number[];
}
export declare enum AxisGroupZIndexMap {
TickLines = 0,
AxisLine = 1,
TickLabels = 2
}
export type AxisTickFormatParams = {
type: 'number';
fractionDigits: number | undefined;
visibleDomain?: [number, number];
} | {
type: 'date';
unit: AgTimeIntervalUnit;
step: number;
epoch: Date | undefined;
truncateDate?: 'year' | 'month' | 'day';
} | {
type: 'category';
};
interface TickLayout<D, TickLayoutMeta> {
niceDomain: D[];
tickDomain: D[];
ticks: D[];
rawTickCount: number | undefined;
fractionDigits: number;
timeInterval: AnyTimeInterval | undefined;
bbox?: BBox;
layout?: TickLayoutMeta;
}
/**
* A general purpose linear axis with no notion of orientation.
* The axis is always rendered vertically, with horizontal labels positioned to the left
* of the axis line by default. The axis can be {@link rotation | rotated} by an arbitrary angle,
* so that it can be used as a top, right, bottom, left, radial or any other kind
* of linear axis.
* The generic `D` parameter is the type of the domain of the axis' scale.
* The output range of the axis' scale is always numeric (screen coordinates).
*/
export declare abstract class Axis<S extends Scale<D, number, TickInterval<S>> = Scale<any, number, any>, D = any, TickLayoutMeta = any> implements ChartAxis {
protected readonly moduleCtx: ModuleContext;
readonly scale: S;
static readonly defaultTickMinSpacing = 50;
protected static CrossLineConstructor: new () => CrossLine<any>;
id: AxisID;
private _crossLines;
set crossLines(value: CrossLine[]);
get crossLines(): CrossLine[];
context?: unknown;
nice: boolean;
/** Reverse the axis scale domain. */
reverse: boolean;
readonly interval: AxisInterval<unknown>;
dataDomain: {
domain: D[];
clipped: boolean;
};
private allowNull;
readonly title: AxisTitle;
/**
* The length of the grid. The grid is only visible in case of a non-zero value.
*/
gridLength: number;
/**
* The distance between the grid ticks and the axis ticks.
*/
gridPadding: number;
/**
* Is used to avoid collisions between axis labels and series.
*/
seriesAreaPadding: number;
get type(): string;
abstract get direction(): ChartAxisDirection;
layoutConstraints: ChartAxis['layoutConstraints'];
requiredRange?: number;
boundSeries: ISeries<DatumIndexType, unknown, unknown>[];
includeInvisibleDomains: boolean;
interactionEnabled: boolean;
protected readonly axisGroup: Group<unknown>;
protected readonly tickLineGroup: TransformableGroup;
protected readonly tickLabelGroup: TransformableGroup;
protected readonly labelGroup: Group<unknown>;
readonly gridGroup: TranslatableGroup;
protected readonly gridFillGroup: Group<unknown>;
protected readonly gridLineGroup: Group<unknown>;
protected readonly crossLineRangeGroup: TransformableGroup;
protected readonly crossLineLineGroup: TransformableGroup;
protected readonly crossLineLabelGroup: TransformableGroup;
protected tickLabelGroupSelection: Selection<TransformableText, LabelNodeDatum>;
readonly line: AxisLine;
readonly tick: AxisTick;
readonly gridLine: AxisGridLine;
readonly label: AxisLabel;
protected get primaryLabel(): AxisLabel | undefined;
protected get primaryTick(): AxisTick | undefined;
isCategoryLike(): boolean;
defaultTickMinSpacing: number;
readonly translation: {
x: number;
y: number;
};
protected readonly layout: Pick<AxisLayout, 'label'> & Partial<Pick<AxisLayout, 'labelThickness' | 'scrollbar'>>;
protected axisContext: AxisContext | undefined;
protected readonly cleanup: CleanupRegistry;
constructor(moduleCtx: ModuleContext, scale: S);
resetAnimation(_phase: ChartAnimationPhase): void;
private isHovering;
private onMouseMove;
private endHovering;
private attachCrossLine;
private detachCrossLine;
destroy(): void;
private setScaleRange;
protected updateScale(): void;
setCrossLinesVisible(visible: boolean): void;
attachAxis(groups: AxisGroups): void;
detachAxis(): void;
attachLabel(axisLabelNode: Node): void;
range: [number, number];
visibleRange: [number, number];
/**
* Checks if a point or an object is in range.
* @param value A point (or object's starting point).
* @param tolerance Expands the range on both ends by this amount.
*/
inRange(value: number, tolerance?: number): boolean;
/**
* Get a point's overflow on the range, expanded to include the non-visible range.
* @param value Point
* @returns Overflow
*/
getRangeOverflow(value: number): number;
protected onGridLengthChange(value: number, prevValue: number): void;
protected onGridVisibilityChange(): void;
protected createLabel(): AxisLabel;
/**
* Creates/removes/updates the scene graph nodes that constitute the axis.
*/
update(): void;
protected getLabelStyles(params: {
value: number;
formattedValue: TextOrSegments | undefined;
depth?: number;
}, additionalStyles?: AgBaseAxisLabelStyleOptions, label?: AxisLabel): {
border: import("ag-charts-types").BorderOptions & import("../label").LabelBorder;
color: string | undefined;
cornerRadius: number | undefined;
fill: (import("ag-charts-types").AgColorType & string) | undefined;
fillOpacity: number | undefined;
fontFamily: (import("ag-charts-types").FontFamilyFull | undefined) & string;
fontSize: number;
fontStyle: import("ag-charts-types").FontStyle | undefined;
fontWeight: import("ag-charts-types").FontWeight | undefined;
padding: import("ag-charts-types").Padding | undefined;
spacing: number;
};
protected getTickSize(tick?: AxisTick): number;
protected getTickSpacing(tick?: AxisTick): number;
processData(): void;
getDomainExtentsNice(): [boolean, boolean];
protected animatable: boolean;
setDomains(...domains: DomainWithMetadata<D>[]): void;
protected chartLayout?: ChartLayout;
private unzoomedTickLayoutCache;
calculateDomain(initialPrimaryTickCount?: AxisPrimaryTickCount, scrollbarKey?: string): {
unzoomedTickLayout: TickLayout<D, TickLayoutMeta>;
domain: D[];
};
private tickLayoutCache;
protected tickLayout: TickLayoutMeta | undefined;
calculateLayout(initialPrimaryTickCount?: AxisPrimaryTickCount, chartLayout?: ChartLayout): {
primaryTickCount?: AxisPrimaryTickCount;
bbox?: BBox;
};
private invalidateLayoutCache;
private getScrollbarLayoutCacheKey;
abstract layoutCrossLines(): void;
abstract calculateTickLayout(domain: D[], niceMode: NiceMode[], visibleRange: [number, number], primaryTickCount?: AxisPrimaryTickCount): {
niceDomain: D[];
tickDomain: D[];
ticks: D[];
rawTickCount: number | undefined;
fractionDigits: number;
timeInterval: AnyTimeInterval | undefined;
bbox?: BBox;
};
abstract hasDefinedDomain(): boolean;
protected updateCrossLines(): void;
protected updatePosition(): void;
protected abstract updateSelections(): void;
protected abstract updateLabels(): void;
abstract tickFormatParams(domain: D[], ticks: D[], fractionDigits: number | undefined, timeInterval: AgTimeInterval | AgTimeIntervalUnit | undefined): AxisTickFormatParams;
abstract datumFormatParams(value: any, params: FormatDatumParams, fractionDigits: number | undefined, timeInterval: AgTimeInterval | AgTimeIntervalUnit | undefined, dateStyle: 'long' | 'component'): FormatterParams<any>;
tickFormatter(domain: D[], ticks: D[], primary: boolean, inputFractionDigits?: number, inputTimeInterval?: AgTimeInterval | AgTimeIntervalUnit, dateStyle?: DateFormatterStyle): (value: any, index: number) => TextOrSegments;
formatDatum(contextProvider: {
context?: unknown;
}, value: any, source: 'tooltip' | 'series-label', seriesId: string, legendItemName: string | undefined, datum: any, key: string): string;
formatDatum<Params extends object>(contextProvider: {
context?: unknown;
} | undefined, value: any, source: 'crosshair' | 'annotation-label', seriesId: undefined, legendItemName: undefined, datum: undefined, key: undefined, domain: undefined, label?: AxisFormattableLabel<Params, FormatterParams<any>>, params?: undefined, allowNull?: boolean): string;
formatDatum<Params extends object>(contextProvider: {
context?: unknown;
} | undefined, value: any, source: 'tooltip' | 'series-label', seriesId: string, legendItemName: string | undefined, datum: any, key: string, domain: any[], label: AxisFormattableLabel<Params>, labelParams: Params): string;
getBBox(): BBox;
private initCrossLine;
protected hasVisibleSeries(): boolean;
clipTickLines(x: number, y: number, width: number, height: number): void;
clipGrid(x: number, y: number, width: number, height: number): void;
private readonly formatterBoundSeries;
private getFormatterProperty;
protected getTitleFormatterParams(domain: D[]): {
domain: D[];
direction: ChartAxisDirection;
boundSeries: AgAxisBoundSeries[];
defaultValue: string | undefined;
};
protected normaliseDataDomain(d: DomainWithMetadata<D>): {
domain: D[];
clipped: boolean;
};
protected getLayoutTranslation(): {
x: number;
y: number;
};
getLayoutState(): AxisLayout;
private readonly moduleMap;
getModuleMap(): ModuleMap<import("ag-charts-core").ModuleInstance>;
getUpdateTypeOnResize(): ChartUpdateType;
createModuleContext(): ModuleContextWithParent<AxisContext>;
createAxisContext(): AxisContext;
pickBand(point: Point): AxisBandDatum | undefined;
measureBand(value: string): AxisBandMeasurement | undefined;
private isVertical;
isReversed(): boolean;
protected cachedCallWithContext<F extends Callback>(fn: F, params: CallbackParam<F>): ReturnType<F> | undefined;
private uncachedCallWithContext;
private createCallWithContext;
}
export {};

View File

@@ -0,0 +1,6 @@
import type { AgAxisGridStyle } from 'ag-charts-types';
export declare class AxisGridLine {
enabled: boolean;
width: number;
style: AgAxisGridStyle[];
}

View File

@@ -0,0 +1,9 @@
import { BaseProperties } from 'ag-charts-core';
import type { TickInterval } from './axisTick';
export declare class AxisInterval<S> extends BaseProperties {
placement?: 'on' | 'between';
step?: TickInterval<S>;
values?: any[];
minSpacing?: number;
maxSpacing?: number;
}

View File

@@ -0,0 +1,84 @@
import { BaseProperties } from 'ag-charts-core';
import type { AgAxisLabelFormatterParams, AgAxisLabelStylerParams, AgBaseAxisLabelStyleOptions, DateFormatterStyle, FontStyle, FontWeight, FormatterParams, Padding, RichFormatter, Styler, TextOrSegments, TextWrap } from 'ag-charts-types';
import type { ChartAxisLabel, ChartAxisLabelFlipFlag } from '../chartAxis';
import { LabelBorder } from '../label';
export declare class AxisLabel extends BaseProperties implements ChartAxisLabel {
enabled: boolean;
border: LabelBorder;
cornerRadius?: number;
fill?: string;
fillOpacity?: number;
fontStyle?: FontStyle;
fontWeight?: FontWeight;
fontSize: number;
fontFamily: string;
wrapping: TextWrap;
truncate: boolean;
/**
* The padding between the labels and the ticks.
*/
spacing: number;
/**
* Minimum gap in pixels between the axis labels before being removed to avoid collisions.
*/
minSpacing?: number;
/**
* The colour of the labels.
* Use `undefined` rather than `rgba(0, 0, 0, 0)` to make labels invisible.
*/
color?: string;
/**
* Custom label rotation in degrees.
* Labels are rendered perpendicular to the axis line by default.
* Or parallel to the axis line, if the {@link parallel} is set to `true`.
* The value of this config is used as the angular offset/deflection
* from the default rotation.
*/
rotation?: number;
/**
* Avoid axis label collision by automatically reducing the number of ticks displayed. If set to `false`, axis labels may collide.
*/
avoidCollisions: boolean;
/**
* By default, labels and ticks are positioned to the left of the axis line.
* `true` positions the labels to the right of the axis line.
* However, if the axis is rotated, it's easier to think in terms
* of this side or the opposite side, rather than left and right.
* We use the term `mirror` for conciseness, although it's not
* true mirroring - for example, when a label is rotated, so that
* it is inclined at the 45 degree angle, text flowing from north-west
* to south-east, ending at the tick to the left of the axis line,
* and then we set this config to `true`, the text will still be flowing
* from north-west to south-east, _starting_ at the tick to the right
* of the axis line.
*/
mirrored: boolean;
/**
* The side of the axis line to position the labels on.
* -1 = left (default)
* 1 = right
*/
getSideFlag(): ChartAxisLabelFlipFlag;
padding?: Padding;
/**
* Labels are rendered perpendicular to the axis line by default.
* Setting this config to `true` makes labels render parallel to the axis line
* and centre aligns labels' text at the ticks.
*/
parallel: boolean;
itemStyler?: Styler<AgAxisLabelStylerParams, AgBaseAxisLabelStyleOptions>;
/**
* In case {@param value} is a number, the {@param fractionDigits} parameter will
* be provided as well. The `fractionDigits` corresponds to the number of fraction
* digits used by the tick step. For example, if the tick step is `0.0005`,
* the `fractionDigits` is 4.
*/
formatter?: RichFormatter<AgAxisLabelFormatterParams>;
format?: string | Record<string, string>;
private _formatters;
formatValue(callWithContext: (formatter: (params: AgAxisLabelFormatterParams) => TextOrSegments | undefined, params: AgAxisLabelFormatterParams) => TextOrSegments | undefined, params: FormatterParams<any>, index: number, options?: {
specifier?: string | Record<string, string>;
dateStyle: DateFormatterStyle;
truncateDate: 'year' | 'month' | 'day' | undefined;
}): TextOrSegments | undefined;
}

View File

@@ -0,0 +1,5 @@
export declare class AxisLine {
enabled: boolean;
width: number;
stroke?: string;
}

View File

@@ -0,0 +1,36 @@
import type { AxisID, ChartAxisDirection } from 'ag-charts-core';
import type { EventsHub } from '../../core/eventsHub';
import type { AxisContext } from '../../module/axisContext';
import { Group } from '../../scene/group';
import { Node } from '../../scene/node';
interface AxisNodes {
axisNode: Node;
gridNode: Node;
crossLineRangeNode: Node;
crossLineLineNode: Node;
crossLineLabelNode: Node;
labelNode: Node;
}
interface Axis {
createAxisContext(): AxisContext;
attachAxis(nodes: AxisNodes): void;
detachAxis(nodes: AxisNodes): void;
destroy(): void;
}
export declare class AxisManager {
private readonly eventsHub;
private readonly sceneRoot;
private readonly axes;
readonly axisGridGroup: Group<unknown>;
readonly axisGroup: Group<unknown>;
readonly axisLabelGroup: Group<unknown>;
readonly axisCrosslineRangeGroup: Group<unknown>;
readonly axisCrosslineLineGroup: Group<unknown>;
readonly axisCrosslineLabelGroup: Group<unknown>;
constructor(eventsHub: EventsHub, sceneRoot: Group);
updateAxes(oldAxes: Axis[], newAxes: Axis[]): void;
getAxisIdContext(id: AxisID): AxisContext | undefined;
getAxisContext(direction: ChartAxisDirection): AxisContext[];
destroy(): void;
}
export {};

View File

@@ -0,0 +1,15 @@
import { BaseProperties } from 'ag-charts-core';
import type { AgTimeInterval, AgTimeIntervalUnit } from 'ag-charts-types';
import type { OrdinalTimeScale } from '../../scale/ordinalTimeScale';
import type { TimeScale } from '../../scale/timeScale';
import type { UnitTimeScale } from '../../scale/unitTimeScale';
export type TickInterval<S> = S extends TimeScale | OrdinalTimeScale | UnitTimeScale ? number | AgTimeInterval | AgTimeIntervalUnit : number;
export declare class AxisTick extends BaseProperties {
enabled: boolean;
/** The line width to be used by axis ticks. */
width: number;
/** The line length to be used by axis ticks. */
size: number;
/** The colour of the axis ticks. */
stroke?: string;
}

View File

@@ -0,0 +1,16 @@
import { BaseProperties } from 'ag-charts-core';
import type { AgAxisCaptionFormatterParams, AgAxisCaptionOptions, FontStyle, FontWeight, RichFormatter, TextWrap } from 'ag-charts-types';
import { Caption } from '../caption';
export declare class AxisTitle extends BaseProperties implements AgAxisCaptionOptions {
readonly caption: Caption;
enabled: boolean;
text?: string;
spacing?: number;
fontStyle?: FontStyle;
fontWeight?: FontWeight;
fontSize: number;
fontFamily: string;
color?: string;
wrapping: TextWrap;
formatter?: RichFormatter<AgAxisCaptionFormatterParams>;
}

View File

@@ -0,0 +1,96 @@
import type { Size } from 'ag-charts-core';
import type { TextOrSegments } from 'ag-charts-types';
import type { FromToFns } from '../../motion/fromToMotion';
import type { Group, TranslatableGroup } from '../../scene/group';
import type { Line } from '../../scene/shape/line';
import type { Rect } from '../../scene/shape/rect';
import type { RotatableText } from '../../scene/shape/text';
export declare enum NiceMode {
TickAndDomain = 0,
TicksOnly = 1,
Off = 2
}
export interface TickDatum {
index: number;
tickLabel: TextOrSegments | undefined;
tick: any;
tickId: string;
translation: number;
textUntruncated: string | undefined;
isPrimary: boolean;
textMetrics: Size;
}
export interface AxisLineDatum {
tickId: string;
offset: number;
x1: number;
x2: number;
y1: number;
y2: number;
stroke: string | undefined;
strokeWidth: number;
lineDash: number[] | undefined;
}
export interface AxisFillDatum {
tickId: string;
x1: number;
x2: number;
y1: number;
y2: number;
fill: string | undefined;
fillOpacity: number | undefined;
}
export interface AxisAnimationContext {
visible: boolean;
min: number;
max: number;
}
interface AxisGroupDatum {
rotation: number;
rotationCenterX: number;
rotationCenterY: number;
translationX: number;
translationY: number;
}
export interface AxisLabelDatum {
tickId: string;
x: number;
y: number;
rotationCenterX: number;
rotationCenterY: number;
rotation: number;
range: number[];
}
export declare function prepareAxisAnimationContext(axis: {
range: number[];
}): AxisAnimationContext;
export declare function prepareAxisAnimationFunctions(ctx: AxisAnimationContext): {
tick: FromToFns<Line, any, AxisLineDatum>;
line: FromToFns<Line, any, AxisLineDatum>;
label: FromToFns<RotatableText, Partial<Omit<AxisLabelDatum, "range">>, AxisLabelDatum>;
group: FromToFns<TranslatableGroup, any, AxisGroupDatum>;
};
export declare function resetAxisGroupFn(): (_node: Group, datum: AxisGroupDatum) => {
translationX: number;
translationY: number;
};
export declare function resetAxisLabelSelectionFn(): (_node: RotatableText, datum: AxisLabelDatum) => {
x: number;
y: number;
rotationCenterX: number;
rotationCenterY: number;
rotation: number;
};
export declare function resetAxisLineSelectionFn(): (_node: Line, datum: AxisLineDatum) => {
x1: number;
x2: number;
y1: number;
y2: number;
};
export declare function resetAxisFillSelectionFn(): (_node: Rect, datum: AxisFillDatum) => {
x: number;
y: number;
width: number;
height: number;
};
export {};

View File

@@ -0,0 +1,160 @@
import type { ChartAnimationPhase, Scale } from 'ag-charts-core';
import { ChartAxisDirection, StateMachine } from 'ag-charts-core';
import type { AgCartesianAxisPosition, AgTimeInterval, AgTimeIntervalUnit } from 'ag-charts-types';
import type { AxisContext } from '../../module/axisContext';
import type { ModuleContext } from '../../module/moduleContext';
import { type FromToDiff } from '../../motion/fromToMotion';
import { BBox } from '../../scene/bbox';
import { TranslatableGroup } from '../../scene/group';
import { Selection } from '../../scene/selection';
import { Line } from '../../scene/shape/line';
import { Rect } from '../../scene/shape/rect';
import type { AxisPrimaryTickCount } from '../../util/secondaryAxisTicks';
import { Caption } from '../caption';
import type { ChartLayout } from '../chartAxis';
import type { AnimationManager } from '../interaction/animationManager';
import type { ScrollbarLayout } from '../layout/layoutManager';
import { Axis, type LabelNodeDatum } from './axis';
import { type AxisFillDatum, type AxisLineDatum, NiceMode, type TickDatum } from './axisUtil';
import { CartesianAxisLabel } from './cartesianAxisLabel';
type AxisAnimationState = 'empty' | 'ready';
type AxisAnimationEvent = {
reset: undefined;
resize: undefined;
update: FromToDiff;
};
interface GeneratedTicks {
ticks: TickDatum[];
tickLines: AxisLineDatum[];
gridLines: AxisLineDatum[];
gridFills: AxisFillDatum[];
labels: LabelNodeDatum[];
spacing: number;
}
export type GridLineStyleTickDatum = Pick<TickDatum, 'index' | 'tickId' | 'translation'>;
export declare abstract class CartesianAxis<S extends Scale<D, number, any> = Scale<any, number, any>, D = any> extends Axis<S, D, GeneratedTicks> {
static is(value: unknown): value is CartesianAxis<any>;
thickness?: number;
maxThicknessRatio: number;
position: AgCartesianAxisPosition;
crossAt?: {
value: D;
sticky?: boolean;
};
readonly crossAxisTranslation: {
x: number;
y: number;
};
minimumTimeGranularity: AgTimeIntervalUnit | undefined;
lineRange?: [number, number];
protected animationManager: AnimationManager;
protected readonly headingLabelGroup: TranslatableGroup;
protected readonly lineNodeGroup: TranslatableGroup;
protected readonly lineNode: Line;
protected tickLineGroupSelection: Selection<Line, AxisLineDatum>;
protected gridLineGroupSelection: Selection<Line, AxisLineDatum>;
protected gridFillGroupSelection: Selection<Rect<any>, AxisFillDatum>;
private readonly tempText;
private readonly tempCaption;
protected readonly animationState: StateMachine<AxisAnimationState, AxisAnimationEvent>;
protected get horizontal(): boolean;
constructor(moduleCtx: ModuleContext, scale: S);
protected onGridVisibilityChange(): void;
resetAnimation(phase: ChartAnimationPhase): void;
get direction(): ChartAxisDirection.X | ChartAxisDirection.Y;
createAxisContext(): AxisContext;
protected createLabel(): CartesianAxisLabel;
protected updateDirection(): void;
calculateLayout(primaryTickCount?: AxisPrimaryTickCount, chartLayout?: ChartLayout): {
primaryTickCount?: AxisPrimaryTickCount | undefined;
bbox?: BBox | undefined;
};
layoutCrossLines(): void;
calculateTickLayout(domain: D[], niceMode: NiceMode[], visibleRange: [number, number], initialPrimaryTickCount?: AxisPrimaryTickCount): {
niceDomain: D[];
tickDomain: D[];
ticks: D[];
rawTickCount: number | undefined;
fractionDigits: number;
timeInterval: AgTimeInterval | AgTimeIntervalUnit | undefined;
bbox: BBox;
layout: GeneratedTicks;
};
protected calculateGridLines(ticks: GridLineStyleTickDatum[], p1: number, p2: number): AxisLineDatum[];
protected calculateGridLine({ index: tickIndex, tickId, translation: offset }: GridLineStyleTickDatum, _index: number, p1: number, p2: number, _ticks: GridLineStyleTickDatum[]): AxisLineDatum;
protected calculateGridFills(ticks: GridLineStyleTickDatum[], p1: number, p2: number): AxisFillDatum[];
protected calculateGridFill({ tickId, translation }: Pick<GridLineStyleTickDatum, 'tickId' | 'translation'>, index: number, gridFillIndex: number, p1: number, p2: number, ticks: GridLineStyleTickDatum[]): AxisFillDatum;
protected calculateTickLines(ticks: TickDatum[], direction: number, scrollbarThickness?: number): AxisLineDatum[];
protected calculateTickLine({ isPrimary, tickId, translation: offset }: Pick<TickDatum, 'isPrimary' | 'tickId' | 'translation'>, _index: number, direction: number, _ticks: TickDatum[], scrollbarThickness?: number): AxisLineDatum;
update(): void;
private getAxisTransform;
protected getLayoutTranslation(): {
x: number;
y: number;
};
getLayoutState(): {
position: AgCartesianAxisPosition;
id: string;
rect: BBox;
translation: {
x: number;
y: number;
};
gridPadding: number;
seriesAreaPadding: number;
tickSize: number;
labelThickness?: number | undefined;
scrollbar?: {
enabled: boolean;
placement: import("ag-charts-types").AgScrollbarPlacement;
spacing: number;
thickness: number;
offset: number;
} | undefined;
label: {
fractionDigits: number;
spacing: number;
format?: string | Record<string, string> | undefined;
};
direction: ChartAxisDirection;
domain: any[];
scale: Scale<any, any, number | AgTimeInterval | AgTimeIntervalUnit>;
};
protected updatePosition(): void;
setAxisVisible(visible: boolean): void;
private getAxisLineCoordinates;
private getTickLineBBox;
protected lineNodeBBox(): BBox;
protected titleBBox(domain: D[], spacing: number): BBox;
protected getScrollbarThickness(scrollbar?: ScrollbarLayout): number;
protected resolveScrollbarLayout(scrollbar: ScrollbarLayout | undefined, labelThickness: number): (ScrollbarLayout & {
offset: number;
}) | undefined;
protected applyScrollbarLayout(boxes: BBox[], labelThickness: number, scrollbar: ScrollbarLayout | undefined): {
spacing: number;
scrollbarLayout: (ScrollbarLayout & {
offset: number;
}) | undefined;
};
private measureAxisLayout;
protected titleProps(caption: Caption, domain: D[], spacing: number): {
visible: boolean;
text: import("ag-charts-types").TextOrSegments | undefined;
textBaseline: "top" | "bottom";
x: number;
y: number;
rotationCenterX: number;
rotationCenterY: number;
rotation: number;
};
private getTickLabelProps;
protected updateSelections(): void;
protected updateGridLines(): void;
protected updateGridFills(): void;
protected updateTickLines(): void;
protected updateTitle(domain: D[], spacing: number): void;
protected updateLabels(): void;
private animateReadyUpdate;
protected resetSelectionNodes(): void;
}
export {};

View File

@@ -0,0 +1,12 @@
import { AxisLabel } from './axisLabel';
export declare class CartesianAxisLabel extends AxisLabel {
/**
* If specified and axis labels may collide, they are rotated to reduce collisions. If the
* `rotation` property is specified, it takes precedence.
*/
autoRotate?: boolean;
/**
* Rotation angle to use when autoRotate is applied.
*/
autoRotateAngle: number;
}

View File

@@ -0,0 +1,39 @@
import type { DomainWithMetadata } from 'ag-charts-core';
import { ChartUpdateType } from 'ag-charts-core';
import type { AgTimeInterval, AgTimeIntervalUnit, DateFormatterStyle, FormatterParams } from 'ag-charts-types';
import type { ModuleContext } from '../../module/moduleContext';
import { CategoryScale } from '../../scale/categoryScale';
import type { OrdinalTimeScale } from '../../scale/ordinalTimeScale';
import type { UnitTimeScale } from '../../scale/unitTimeScale';
import type { FormatDatumParams } from '../chartAxis';
import type { AxisTickFormatParams } from './axis';
import type { AxisFillDatum, AxisLineDatum, TickDatum } from './axisUtil';
import { CartesianAxis, type GridLineStyleTickDatum } from './cartesianAxis';
export declare class CategoryAxis<S extends CategoryScale<string | object> | UnitTimeScale | OrdinalTimeScale = CategoryScale<string | object>> extends CartesianAxis<S> {
static is(this: void, value: unknown): value is CategoryAxis<any>;
static readonly className: string;
static readonly type: 'category' | 'grouped-category' | 'unit-time' | 'ordinal-time';
groupPaddingInner: number;
paddingInner?: number;
paddingOuter?: number;
bandAlignment?: 'justify' | 'start' | 'center' | 'end';
requiredRange?: number;
constructor(moduleCtx: ModuleContext, scale?: S, includeInvisibleDomains?: boolean);
isCategoryLike(): boolean;
hasDefinedDomain(): boolean;
normaliseDataDomain(d: DomainWithMetadata<string | object>): {
domain: (string | object)[];
clipped: boolean;
};
getUpdateTypeOnResize(): ChartUpdateType;
protected updateScale(): void;
protected calculateGridLines(ticks: GridLineStyleTickDatum[], p1: number, p2: number): AxisLineDatum[];
protected calculateGridLine({ index: tickIndex, tickId, translation }: GridLineStyleTickDatum, index: number, p1: number, p2: number, ticks: GridLineStyleTickDatum[]): AxisLineDatum;
protected calculateGridFills(ticks: GridLineStyleTickDatum[], p1: number, p2: number): AxisFillDatum[];
protected calculateGridFill({ tickId, translation }: Pick<GridLineStyleTickDatum, 'tickId' | 'translation'>, index: number, gridFillIndex: number, p1: number, p2: number, ticks: GridLineStyleTickDatum[]): AxisFillDatum;
protected calculateTickLines(ticks: TickDatum[], direction: number, scrollbarThickness?: number): AxisLineDatum[];
protected calculateTickLine({ isPrimary, tickId, translation }: Pick<TickDatum, 'tickId' | 'translation' | 'isPrimary'>, index: number, direction: number, ticks: TickDatum[], scrollbarThickness?: number): AxisLineDatum;
private reduceBandScalePadding;
tickFormatParams(_domain: any[], _ticks: any[], _fractionDigits?: number, _timeInterval?: AgTimeInterval | AgTimeIntervalUnit): AxisTickFormatParams;
datumFormatParams(value: any, params: FormatDatumParams, _fractionDigits: number | undefined, _timeInterval: AgTimeInterval | AgTimeIntervalUnit | undefined, _style: DateFormatterStyle): FormatterParams<any>;
}

View File

@@ -0,0 +1,12 @@
import type { CategoryScale } from '../../scale/categoryScale';
import type { OrdinalTimeScale } from '../../scale/ordinalTimeScale';
import { UnitTimeScale } from '../../scale/unitTimeScale';
import type { AxisFillDatum, AxisLineDatum, TickDatum } from './axisUtil';
import type { GridLineStyleTickDatum } from './cartesianAxis';
import { CategoryAxis } from './categoryAxis';
export declare class DiscreteTimeAxis<S extends CategoryScale<string | object> | UnitTimeScale | OrdinalTimeScale = CategoryScale<string | object>> extends CategoryAxis<S> {
protected calculateGridLine({ index: tickIndex, tickId, translation }: GridLineStyleTickDatum, index: number, p1: number, p2: number, ticks: GridLineStyleTickDatum[]): AxisLineDatum;
protected calculateGridFills(ticks: GridLineStyleTickDatum[], p1: number, p2: number): AxisFillDatum[];
protected calculateGridFill({ tickId, translation }: Pick<GridLineStyleTickDatum, 'tickId' | 'translation'>, index: number, gridFillIndex: number, p1: number, p2: number, ticks: GridLineStyleTickDatum[]): AxisFillDatum;
protected calculateTickLine({ isPrimary, tickId, translation }: Pick<TickDatum, 'tickId' | 'translation' | 'isPrimary'>, index: number, direction: number, ticks: TickDatum[], scrollbarThickness?: number): AxisLineDatum;
}

View File

@@ -0,0 +1,9 @@
import { type Scale } from 'ag-charts-core';
import type { TickInterval } from './axisTick';
import { type GenerateTicksOptions, type TickData } from './generateTicksUtils';
export declare function generateTicks<TScale extends Scale<TDatum, number, TickInterval<TScale>>, TDatum>(options: GenerateTicksOptions<TScale, TDatum>): {
tickData: TickData<any>;
textAlign: CanvasTextAlign;
textBaseline: CanvasTextBaseline;
rotation: number;
};

View File

@@ -0,0 +1,69 @@
import { type BoxBounds, type ITextMeasurer, type Scale, ScaleAlignment, type ScaleTickParams } from 'ag-charts-core';
import type { AgTimeInterval, AgTimeIntervalUnit, DateFormatterStyle, TextOrSegments } from 'ag-charts-types';
import type { AxisPrimaryTickCount } from '../../util/secondaryAxisTicks';
import type { ChartAxisLabel, ChartAxisLabelFlipFlag } from '../chartAxis';
import type { AxisInterval } from './axisInterval';
import type { TickInterval } from './axisTick';
import { NiceMode, type TickDatum } from './axisUtil';
export type AnyTimeInterval = AgTimeInterval | AgTimeIntervalUnit;
export interface GenerateTicksOptions<TScale extends Scale<TDatum, number, TickInterval<TScale>>, TDatum> {
label: ChartAxisLabel;
scale: TScale;
domain: TDatum[];
range: [number, number];
visibleRange: [number, number];
niceMode: NiceMode[];
reverse: boolean;
primaryTickCount: AxisPrimaryTickCount | undefined;
defaultTickMinSpacing: number;
axisRotation: number;
labelOffset: number;
sideFlag: ChartAxisLabelFlipFlag;
primaryLabel?: ChartAxisLabel;
interval: AxisInterval<TScale>;
minimumTimeGranularity?: AgTimeIntervalUnit;
sizeLimit?: number;
isVertical?: boolean;
inRange?: (value: number) => boolean;
tickFormatter(this: void, domain: TDatum[], ticks: TDatum[], primary: boolean, fractionDigits: number | undefined, timeInterval: AnyTimeInterval | undefined, dateStyle: DateFormatterStyle): (value: any, index: number) => TextOrSegments | undefined;
}
export interface TickData<D = any> {
niceDomain: D[];
tickDomain: D[];
rawTicks: D[];
rawTickCount: number | undefined;
fractionDigits: number;
ticks: TickDatum[];
timeInterval: AnyTimeInterval | undefined;
}
export declare function axisLabelsOverlap(data: readonly BoxBounds[], padding?: number): boolean;
export declare function ticksEqual(a: unknown[], b: unknown[]): boolean;
export declare function ticksSpacing(ticks: TickDatum[]): number;
export declare function formatTicks<S extends Scale<D, number, TickInterval<S>>, D>(options: GenerateTicksOptions<S, D>, { niceDomain, rawTicks, rawFirstTickIndex, generatePrimaryTicks, primaryTicksIndices, alignment, fractionDigits, timeInterval, }: {
niceDomain: D[];
rawTicks: any[];
rawFirstTickIndex: number | undefined;
generatePrimaryTicks: boolean;
primaryTicksIndices: Set<number> | undefined;
alignment: ScaleAlignment | undefined;
fractionDigits: number;
timeInterval: AnyTimeInterval | undefined;
}): TickDatum[];
export declare function withTemporaryDomain<S extends Scale<D, number, TickInterval<S>>, D>(scale: S, temporaryDomain: D[], callback: () => void): void;
export declare function getTimeIntervalTicks<S extends Scale<D, number, TickInterval<S>>, D>(scale: S, visibleRange: [number, number], tickCount: number, maxTickCount: number, tickParams: Readonly<ScaleTickParams<any>>, timeInterval: AnyTimeInterval, reverse: boolean, minimumTimeGranularity?: AgTimeIntervalUnit): {
ticks: Date[];
primaryTicksIndices: Set<number> | undefined;
alignment: ScaleAlignment | undefined;
} | undefined;
export declare function timeIntervalMaxLabelSize(label: ChartAxisLabel, primaryLabel: ChartAxisLabel | undefined, domain: Date[], timeInterval: AgTimeInterval | AgTimeIntervalUnit, textMeasurer: ITextMeasurer): {
width: number;
height: number;
};
export declare function getTextBaseline(parallel: boolean, labelRotation: number, sideFlag: ChartAxisLabelFlipFlag, parallelFlipFlag: ChartAxisLabelFlipFlag): CanvasTextBaseline;
export declare function getTextAlign(parallel: boolean, labelRotation: number, labelAutoRotation: number, sideFlag: ChartAxisLabelFlipFlag, regularFlipFlag: ChartAxisLabelFlipFlag): CanvasTextAlign;
export declare function calculateLabelRotation(rotation?: number, parallel?: boolean, axisRotation?: number): {
configuredRotation: number;
defaultRotation: number;
parallelFlipFlag: ChartAxisLabelFlipFlag;
regularFlipFlag: ChartAxisLabelFlipFlag;
};

View File

@@ -0,0 +1,79 @@
import { BaseProperties, PropertiesArray } from 'ag-charts-core';
import type { FontStyle, FontWeight, Padding, TextWrap } from 'ag-charts-types';
import type { ModuleContext } from '../../module/moduleContext';
import { GroupedCategoryScale } from '../../scale/groupedCategoryScale';
import { BBox } from '../../scene/bbox';
import type { ShapeColor } from '../../scene/shape/shape';
import type { AxisPrimaryTickCount } from '../../util/secondaryAxisTicks';
import type { ChartLayout } from '../chartAxis';
import { LabelBorder } from '../label';
import { CategoryAxis } from './categoryAxis';
import { type GroupedCategoryKey } from './tree';
export declare const MIN_CATEGORY_SPACING = 5;
declare class DepthLabelProperties extends BaseProperties {
enabled: boolean;
avoidCollisions?: boolean;
border: LabelBorder;
color?: string;
cornerRadius?: number;
spacing?: number;
rotation?: number;
wrapping?: TextWrap;
truncate?: boolean;
fill?: ShapeColor;
fontStyle?: FontStyle;
fontWeight?: FontWeight;
fontSize?: number;
fontFamily?: string;
padding?: Padding;
}
declare class DepthTickProperties extends BaseProperties {
enabled: boolean;
width?: number;
stroke?: string;
}
declare class DepthProperties extends BaseProperties {
label: DepthLabelProperties;
tick: DepthTickProperties;
}
export declare class GroupedCategoryAxis extends CategoryAxis<GroupedCategoryScale<GroupedCategoryKey>> {
static readonly className = "GroupedCategoryAxis";
static readonly type: "grouped-category";
readonly tickScale: GroupedCategoryScale<GroupedCategoryKey, number>;
private computedLayout?;
private tickTreeLayout?;
private tickNodes?;
depthOptions: PropertiesArray<DepthProperties>;
constructor(moduleCtx: ModuleContext);
private resizeTickTree;
private getDepthOptionsMap;
private updateCategoryLabels;
private updateAxisLine;
private computeLayout;
/**
* Creates/removes/updates the scene graph nodes that constitute the axis.
* Supposed to be called _manually_ after changing _any_ of the axis properties.
* This allows to bulk set axis properties before updating the nodes.
* The node changes made by this method are rendered on the next animation frame.
* We could schedule this method call automatically on the next animation frame
* when any of the axis properties change (the way we do when properties of scene graph's
* nodes change), but this will mean that we first wait for the next animation
* frame to make changes to the nodes of the axis, then wait for another animation
* frame to render those changes. It's nice to have everything update automatically,
* but this extra level of async indirection will not just introduce an unwanted delay,
* it will also make it harder to reason about the program.
*/
update(): void;
calculateLayout(_primaryTickCount?: AxisPrimaryTickCount, chartLayout?: ChartLayout): {
bbox: BBox;
niceDomain: GroupedCategoryKey[];
};
/**
* The length of the grid. The grid is only visible in case of a non-zero value.
*/
onGridVisibilityChange(): void;
protected updateScale(): void;
processData(): void;
filterDuplicateArrays(array: GroupedCategoryKey[]): GroupedCategoryKey[];
}
export {};

View File

@@ -0,0 +1,15 @@
import type { DomainWithMetadata } from 'ag-charts-core';
import type { ModuleContext } from '../../module/moduleContext';
import { NumberAxis } from './numberAxis';
export declare class LogAxis extends NumberAxis {
static readonly className = "LogAxis";
static readonly type: "log";
protected getVisibleDomain(domain: number[]): [number, number];
normaliseDataDomain(d: DomainWithMetadata<number>): {
domain: number[];
clipped: boolean;
};
set base(value: number);
get base(): number;
constructor(moduleCtx: ModuleContext);
}

View File

@@ -0,0 +1,26 @@
import type { DomainWithMetadata } from 'ag-charts-core';
import type { FormatterParams } from 'ag-charts-types';
import type { ModuleContext } from '../../module/moduleContext';
import { LinearScale } from '../../scale/linearScale';
import type { LogScale } from '../../scale/logScale';
import type { FormatDatumParams } from '../chartAxis';
import type { AxisTickFormatParams } from './axis';
import { CartesianAxis } from './cartesianAxis';
export declare class NumberAxis extends CartesianAxis<LinearScale | LogScale, number> {
static readonly className: string;
static readonly type: string;
min?: number;
max?: number;
preferredMin?: number;
preferredMax?: number;
constructor(moduleCtx: ModuleContext, scale?: LinearScale | LogScale);
hasDefinedDomain(): boolean;
normaliseDataDomain(d: DomainWithMetadata<number>): {
domain: number[];
clipped: boolean;
};
getDomainExtentsNice(): [boolean, boolean];
protected getVisibleDomain(domain: number[]): [number, number];
tickFormatParams(domain: number[], _ticks: number[], fractionDigits?: number): AxisTickFormatParams;
datumFormatParams(value: any, params: FormatDatumParams, fractionDigits?: number): FormatterParams<any>;
}

View File

@@ -0,0 +1,36 @@
import type { Scale } from 'ag-charts-core';
import type { BBox } from '../../scene/bbox';
import { Axis } from './axis';
import type { TickInterval } from './axisTick';
export interface PolarAxisPathPoint {
x: number;
y: number;
moveTo: boolean;
radius?: number;
startAngle?: number;
endAngle?: number;
arc?: boolean;
}
export declare abstract class PolarAxis<S extends Scale<D, number, TickInterval<S>> = Scale<any, number, any>, D = any> extends Axis<S, D> {
gridAngles: number[] | undefined;
gridRange: number[] | undefined;
shape: 'polygon' | 'circle';
innerRadiusRatio: number;
defaultTickMinSpacing: number;
abstract calculateRotations(): {
rotation: number;
parallelFlipRotation: number;
regularFlipRotation: number;
};
update(): void;
layoutCrossLines(): void;
updatePosition(): void;
computeLabelsBBox(_options: {
hideWhenNecessary: boolean;
}, _seriesRect: BBox): BBox | null;
computeRange(): void;
getAxisLinePoints(): {
points: PolarAxisPathPoint[];
closePath: boolean;
} | undefined;
}

View File

@@ -0,0 +1,42 @@
import type { ChartAxisDirection, DomainWithMetadata } from 'ag-charts-core';
import { BaseProperties } from 'ag-charts-core';
import type { AgTimeInterval, AgTimeIntervalUnit, DateFormatterStyle, FormatterParams } from 'ag-charts-types';
import type { ModuleContext } from '../../module/moduleContext';
import { TimeScale } from '../../scale/timeScale';
import type { FormatDatumParams } from '../chartAxis';
import type { DatumIndexType, ISeries } from '../series/seriesTypes';
import type { AxisTickFormatParams } from './axis';
import { AxisLabel } from './axisLabel';
import { AxisTick } from './axisTick';
import { CartesianAxis } from './cartesianAxis';
export declare class TimeAxisParentLevel extends BaseProperties {
enabled: boolean;
readonly label: AxisLabel;
readonly tick: AxisTick;
}
export declare class TimeAxis extends CartesianAxis<TimeScale, number | Date> {
static readonly className = "TimeAxis";
static readonly type: "time";
readonly parentLevel: TimeAxisParentLevel;
min?: Date | number;
max?: Date | number;
preferredMin?: Date | number;
preferredMax?: Date | number;
get _unit(): AgTimeInterval | AgTimeIntervalUnit | undefined;
set _unit(_unit: AgTimeInterval | AgTimeIntervalUnit | undefined);
unit: AgTimeInterval | AgTimeIntervalUnit | undefined;
constructor(moduleCtx: ModuleContext);
hasDefinedDomain(): boolean;
isCategoryLike(): boolean;
get primaryLabel(): AxisLabel | undefined;
get primaryTick(): AxisTick | undefined;
normaliseDataDomain(d: DomainWithMetadata<Date>): {
domain: Date[];
clipped: boolean;
};
processData(): void;
tickFormatParams(domain: (number | Date)[], ticks: (number | Date)[], _fractionDigits?: number, timeInterval?: AgTimeInterval | AgTimeIntervalUnit): AxisTickFormatParams;
datumFormatParams(value: number | Date, params: FormatDatumParams, _fractionDigits: number | undefined, timeInterval: AgTimeInterval | AgTimeIntervalUnit | undefined, style: DateFormatterStyle): FormatterParams<any>;
}
export declare function minimumTimeAxisDatumGranularity(boundSeries: ISeries<DatumIndexType, unknown, unknown, unknown>[], direction: ChartAxisDirection, min: Date | number | undefined, max: Date | number | undefined): AgTimeIntervalUnit | undefined;
export declare function calculateDefaultUnit(boundSeries: ISeries<DatumIndexType, unknown, unknown, unknown>[], direction: ChartAxisDirection, min: Date | number | undefined, max: Date | number | undefined): AgTimeInterval | undefined;

View File

@@ -0,0 +1,3 @@
import type { AgTimeIntervalUnit } from 'ag-charts-types';
export declare const defaultTimeFormats: Record<AgTimeIntervalUnit, string>;
export declare function deriveTimeSpecifier(format: string | Partial<Record<string, string>> | undefined, unit: AgTimeIntervalUnit, truncateDate?: 'year' | 'month' | 'day'): string;

View File

@@ -0,0 +1,39 @@
/** A key in a grouped category axis — an array of nullable labels representing the path through the hierarchy. */
export type GroupedCategoryKey = (string | null)[];
declare class TreeNode {
label: string | null;
parent?: TreeNode | undefined;
refId?: number | undefined;
position: number;
subtreeLeft: number;
subtreeRight: number;
children: TreeNode[];
leafCount: number;
depth: number;
prelim: number;
mod: number;
ancestor: this;
change: number;
shift: number;
index: number;
screen: number;
constructor(label?: string | null, parent?: TreeNode | undefined, refId?: number | undefined);
insertTick(tick: GroupedCategoryKey, index: number): TreeNode | undefined;
getLeftSibling(): TreeNode | undefined;
getLeftmostSibling(): TreeNode | undefined;
nextLeft(): TreeNode | undefined;
nextRight(): TreeNode | undefined;
getSiblings(): TreeNode[];
}
export declare function treeLayout(ticks: GroupedCategoryKey[]): {
layout: TreeLayout;
tickNodes: Map<GroupedCategoryKey, TreeNode>;
};
export declare class TreeLayout {
private readonly dimensions;
nodes: TreeNode[];
depth: number;
insertNode(node: TreeNode): void;
scaling(extent: number, flip?: boolean): number;
}
export {};

View File

@@ -0,0 +1,34 @@
import type { DomainWithMetadata } from 'ag-charts-core';
import type { AgTimeInterval, AgTimeIntervalUnit, DateFormatterStyle, FormatterParams } from 'ag-charts-types';
import type { ModuleContext } from '../../module/moduleContext';
import { UnitTimeScale } from '../../scale/unitTimeScale';
import type { FormatDatumParams } from '../chartAxis';
import type { AxisTickFormatParams } from './axis';
import { AxisLabel } from './axisLabel';
import { AxisTick } from './axisTick';
import { DiscreteTimeAxis } from './discreteTimeAxis';
import { TimeAxisParentLevel } from './timeAxis';
export declare class UnitTimeAxis extends DiscreteTimeAxis<UnitTimeScale> {
static readonly className: "UnitTimeAxis";
static readonly type: "unit-time";
readonly parentLevel: TimeAxisParentLevel;
min?: Date | number;
max?: Date | number;
preferredMin?: Date | number;
preferredMax?: Date | number;
unit: AgTimeInterval | AgTimeIntervalUnit | undefined;
get primaryLabel(): AxisLabel | undefined;
get primaryTick(): AxisTick | undefined;
constructor(moduleCtx: ModuleContext);
hasDefinedDomain(): boolean;
isCategoryLike(): boolean;
private defaultUnit;
processData(): void;
protected updateScale(): void;
normaliseDataDomain(d: DomainWithMetadata<Date>): {
domain: Date[];
clipped: boolean;
};
tickFormatParams(domain: (number | Date)[], ticks: (number | Date)[], _fractionDigits?: number, timeInterval?: AgTimeInterval | AgTimeIntervalUnit): AxisTickFormatParams;
datumFormatParams(value: Date | number, params: FormatDatumParams, _fractionDigits: number | undefined, timeInterval: AgTimeInterval | AgTimeIntervalUnit | undefined, style: DateFormatterStyle): FormatterParams<any>;
}

View File

@@ -0,0 +1,19 @@
import { AbstractModuleInstance } from 'ag-charts-core';
import type { LayoutCompleteEvent } from '../../core/eventsHub';
import type { ModuleContext } from '../../module/moduleContext';
import { Group } from '../../scene/group';
import { Rect } from '../../scene/shape/rect';
import { Text } from '../../scene/shape/text';
export declare class Background<TImage = never> extends AbstractModuleInstance {
protected readonly ctx: ModuleContext;
protected readonly node: Group<unknown>;
protected readonly rectNode: Rect<any>;
protected readonly textNode: Text<any>;
visible: boolean;
fill?: string;
image?: TImage;
text?: string;
constructor(ctx: ModuleContext);
protected createNode(): Group<unknown>;
protected onLayoutComplete(e: LayoutCompleteEvent): void;
}

View File

@@ -0,0 +1,37 @@
import { BaseProperties } from 'ag-charts-core';
import type { FontStyle, FontWeight, TextAlign, TextOrSegments, TextWrap } from 'ag-charts-types';
import type { ModuleContext } from '../module/moduleContext';
import { RotatableText } from '../scene/shape/text';
import type { CaptionLike } from './captionLike';
export declare class Caption extends BaseProperties implements CaptionLike {
static readonly className = "Caption";
static readonly SMALL_PADDING = 10;
readonly id: string;
readonly node: RotatableText;
enabled: boolean;
text?: TextOrSegments;
textAlign: TextAlign;
fontStyle?: FontStyle;
fontWeight?: FontWeight;
fontSize: number;
fontFamily: string;
color?: string;
spacing?: number;
maxWidth?: number;
maxHeight?: number;
wrapping: TextWrap;
padding: number;
layoutStyle: 'block' | 'overlay';
private truncated;
private proxyText?;
private proxyTextListeners?;
private lastProxyTextContent?;
private lastProxyBBox?;
registerInteraction(moduleCtx: ModuleContext, where: 'beforebegin' | 'afterend'): () => void;
computeTextWrap(containerWidth: number, containerHeight: number): void;
private updateA11yText;
private handleMouseMove;
private handleMouseLeave;
destroy(): void;
private destroyProxyText;
}

View File

@@ -0,0 +1,8 @@
import type { TextOrSegments } from 'ag-charts-types';
import type { Text } from '../scene/shape/text';
export interface CaptionLike {
enabled: boolean;
text?: TextOrSegments;
padding: number;
node: Text;
}

View File

@@ -0,0 +1,51 @@
import type { ChartOptions } from '../module/optionsModule';
import type { BBox } from '../scene/bbox';
import { CartesianAxis } from './axis/cartesianAxis';
import type { TransferableResources } from './chart';
import { Chart } from './chart';
import { CartesianChartAxes } from './chartAxes';
import type { LayoutContext } from './layout/layoutManager';
import type { UnknownSeries } from './series/series';
export declare class CartesianChart extends Chart {
static readonly className = "CartesianChart";
static readonly type = "cartesian";
private static readonly AxesPadding;
/** Integrated Charts feature state - not used in Standalone Charts. */
readonly paired: boolean;
axes: CartesianChartAxes;
createChartAxes(): CartesianChartAxes;
private lastAreaWidths?;
constructor(options: ChartOptions, resources?: TransferableResources);
onAxisChange(newValue: CartesianAxis[], oldValue?: CartesianAxis[]): void;
destroySeries(series: UnknownSeries[]): void;
getChartType(): "cartesian";
private setRootClipRects;
private lastUpdateClipRect;
processData(): Promise<void>;
processDomains(): Promise<void>;
private lastLayoutWidth;
private lastLayoutHeight;
protected performLayout(ctx: LayoutContext): void;
updateAxes(layoutContext: LayoutContext): {
clipSeries: boolean;
seriesRect: BBox;
visible: boolean;
};
private resolveAxesLayout;
private updateAxesPass;
private calculateAxesCrossPositions;
private calculateAxisCrossPosition;
private adjustAxisWidth;
private calculateAxisBleedingWidth;
private applyAxisCrossing;
private buildCrossLinePadding;
private clampToOutsideSeriesRect;
private getSyncedDomain;
private syncAxisChanges;
private sizeAxis;
private positionAxes;
private shouldFlipXY;
private getDefaultState;
private isLayoutStable;
private clipAxis;
}

View File

@@ -0,0 +1,3 @@
import { type ChartModuleDefinition } from 'ag-charts-core';
import type { AgCartesianChartOptions } from 'ag-charts-types';
export declare const CartesianChartModule: ChartModuleDefinition<AgCartesianChartOptions>;

View File

@@ -0,0 +1,2 @@
import type { DatumIndexType, ISeries } from './series/seriesTypes';
export declare function stackCartesianSeries(series: ISeries<DatumIndexType, unknown, unknown>[]): void;

View File

@@ -0,0 +1,202 @@
import { type ChartAnimationPhase, ChartUpdateType, Debug, type ModuleInstance, Padding } from 'ag-charts-core';
import type { AgChartInstance, AgChartOptions, AgDataTransaction, FormatterConfiguration } from 'ag-charts-types';
import type { ModuleContext } from '../module/moduleContext';
import type { ChartOptions } from '../module/optionsModule';
import { BBox } from '../scene/bbox';
import { TranslatableGroup } from '../scene/group';
import type { Scene } from '../scene/scene';
import type { TypedEvent } from '../util/observable';
import { Observable } from '../util/observable';
import { Background } from './background/background';
import { Caption } from './caption';
import { ChartAxes } from './chartAxes';
import type { ChartAxis } from './chartAxis';
import { ChartContext } from './chartContext';
import { ChartHighlight } from './chartHighlight';
import type { ChartMode } from './chartMode';
import type { ChartService } from './chartService';
import { DataSet } from './data/dataSet';
import type { ChartType } from './factory/expectedModules';
import { type SyncStatus } from './interaction/syncManager';
import { Keyboard } from './keyboard';
import { type LayoutContext } from './layout/layoutManager';
import { ModulesManager } from './modulesManager';
import { ChartOverlays } from './overlay/chartOverlays';
import { SeriesArea } from './series-area/seriesArea';
import { Series, type UnknownSeries } from './series/series';
import { SeriesAreaManager } from './series/seriesAreaManager';
import { SeriesLayerManager } from './series/seriesLayerManager';
import type { DatumIndexType, ISeries } from './series/seriesTypes';
import { Tooltip, type TooltipContent } from './tooltip/tooltip';
import { Touch } from './touch';
import type { UpdateOpts } from './updateService';
export type TransferableResources = {
container?: HTMLElement;
styleContainer?: HTMLElement;
scene: Scene;
};
export declare abstract class Chart extends Observable implements ModuleInstance, ChartService {
static readonly className: string;
private static readonly chartsInstances;
static getInstance(element: HTMLElement): Chart | undefined;
readonly id: string;
className?: string;
readonly seriesRoot: TranslatableGroup;
readonly annotationRoot: TranslatableGroup;
private readonly titleGroup;
readonly tooltip: Tooltip;
readonly overlays: ChartOverlays;
readonly highlight: ChartHighlight;
readonly background: Background<any>;
readonly seriesArea: SeriesArea;
foreground?: Background<any>;
protected readonly debug: Debug.DebugLogger;
private extraDebugStats;
container?: HTMLElement;
data: DataSet;
width?: number;
height?: number;
minWidth?: number;
minHeight?: number;
overrideDevicePixelRatio?: number;
/** NOTE: This is exposed for use by Integrated charts only. */
get canvasElement(): HTMLCanvasElement;
private _lastAutoSize?;
private _firstAutoSize;
private readonly _autoSizeNotify;
private _requiredRange;
private _requiredRangeDirection;
download(fileName?: string, fileFormat?: string): void;
getCanvasDataURL(fileFormat?: string): string;
toSVG(): string | undefined;
private readonly chartCaptions;
readonly padding: Padding;
get seriesAreaBoundingBox(): BBox;
readonly keyboard: Keyboard;
readonly touch: Touch;
mode: ChartMode;
styleNonce: string | undefined;
readonly title: Caption;
readonly subtitle: Caption;
readonly footnote: Caption;
formatter: FormatterConfiguration<any> | undefined;
suppressFieldDotNotation: boolean;
loadGoogleFonts: boolean;
context?: unknown;
destroyed: boolean;
private readonly cleanup;
chartAnimationPhase: ChartAnimationPhase;
readonly modulesManager: ModulesManager;
readonly ctx: ChartContext;
protected readonly seriesLayerManager: SeriesLayerManager;
protected readonly seriesAreaManager: SeriesAreaManager;
private readonly processors;
queuedUserOptions: AgChartOptions[];
queuedChartOptions: ChartOptions[];
chartOptions: ChartOptions;
private firstApply;
/**
* Public API for this Chart instance. NOTE: This is initialized after construction by the
* wrapping class that implements AgChartInstance.
*/
publicApi?: AgChartInstance;
syncStatus: SyncStatus;
getOptions(): import("ag-charts-types").AgCartesianChartOptions<any, unknown> | import("ag-charts-types").AgPolarChartOptions<any, unknown> | import("ag-charts-types").AgTopologyChartOptions<any, unknown> | import("ag-charts-types").AgStandaloneChartOptions<any, unknown>;
getChartOptions(): ChartOptions<AgChartOptions>;
isDataTransactionSupported(): boolean;
constructor(options: ChartOptions, resources?: TransferableResources);
overrideFocusVisible(visible: boolean | undefined): void;
private readonly fireEventWrapper;
protected fireEvent<TEvent extends TypedEvent>(event: TEvent): void;
private initSeriesAreaDependencies;
getModuleContext(): ModuleContext;
abstract getChartType(): ChartType;
getTooltipContent(series: ISeries<DatumIndexType, any, any>, datumIndex: DatumIndexType, removeMeDatum: unknown, purpose: 'aria-label' | 'tooltip'): TooltipContent[];
protected getCaptionText(): string;
protected getAriaLabel(): string;
private refreshSeriesUserVisibility;
resetAnimations(): void;
skipAnimations(): void;
detachAndClear(): void;
destroy(opts?: {
keepTransferableResources: boolean;
}): TransferableResources | undefined;
requestFactoryUpdate(cb: (chart: Chart) => Promise<void> | void): void;
private clearCallbackCache;
private apiUpdate;
private pendingLocaleText?;
private _pendingFactoryUpdatesCount;
private _performUpdateSkipAnimations;
private readonly _performUpdateNotify;
private performUpdateType;
private runningUpdateType;
private updateShortcutCount;
private readonly seriesToUpdate;
private readonly updateMutex;
private clearCallbackCacheOnUpdate;
private updateRequestors;
private readonly performUpdateTrigger;
update(type?: ChartUpdateType, opts?: UpdateOpts): void;
private readonly _performUpdateSplits;
private _previousSplit;
private updateSplits;
private tryPerformUpdate;
private performUpdate;
private updateThemeClassName;
private updateDOM;
private updateAriaLabels;
private checkUpdateShortcut;
private checkFirstAutoSize;
axes: ChartAxes;
createChartAxes(): ChartAxes;
series: Series<DatumIndexType, any, any, any>[];
protected onAxisChange(newValue: ChartAxis[], oldValue?: ChartAxis[]): void;
protected onSeriesChange(newValue: UnknownSeries[], oldValue?: UnknownSeries[]): void;
protected destroySeries(allSeries: UnknownSeries[]): void;
private addSeriesListeners;
protected assignSeriesToAxes(): void;
protected assignAxesToSeries(): void;
private parentResize;
private resize;
updateData(): void;
private _cachedData;
processData(): Promise<void>;
processDomains(): Promise<void>;
processRanges(): void;
private updateLegends;
private setCategoryLegendData;
private processLayout;
protected abstract performLayout(ctx: LayoutContext): Promise<void> | void;
protected seriesRect?: BBox;
protected animationRect?: BBox;
protected getDebugColors(): {
background?: string;
foreground?: string;
} | undefined;
protected preSeriesUpdate(): void;
protected updateSeries(seriesToUpdate: ISeries<DatumIndexType, unknown, unknown>[]): Promise<void>;
private readonly onSeriesNodeClick;
private readonly onSeriesNodeDoubleClick;
private readonly onSeriesVisibilityChange;
private readonly seriesGroupingChanged;
waitForUpdate(timeoutMs?: number, failOnTimeout?: boolean): Promise<void>;
private filterMiniChartSeries;
applyOptions(newChartOptions: ChartOptions): void;
private applyInitialState;
private maybeResetAnimations;
private shouldForceNodeDataRefresh;
private shouldClearLegendData;
private applyMiniChartOptions;
private applyModules;
private initSeriesDeclarationOrder;
private applySeries;
private applyAxes;
private createSeries;
private applySeriesOptionModules;
private applySeriesValues;
private createAxes;
private applyAxisModules;
private registerListeners;
applyTransaction(transaction: AgDataTransaction): Promise<void>;
onSyncActiveClear(): void;
}

View File

@@ -0,0 +1,19 @@
import { ChartAxisDirection } from 'ag-charts-core';
import type { CartesianAxis } from './axis/cartesianAxis';
import type { PolarAxis } from './axis/polarAxis';
import type { ChartAxis } from './chartAxis';
export declare class ChartAxes<T extends ChartAxis = ChartAxis> extends Array<T> {
destroy(): void;
findById(id: string): T | undefined;
matches(comparison: Record<string, unknown>): boolean;
protected getById(id: string): T;
}
export declare class CartesianChartAxes extends ChartAxes<CartesianAxis> {
get [ChartAxisDirection.X](): CartesianAxis;
get [ChartAxisDirection.Y](): CartesianAxis;
perpendicular(to: CartesianAxis): CartesianAxis;
}
export declare class PolarChartAxes extends ChartAxes<PolarAxis> {
get [ChartAxisDirection.Angle](): PolarAxis;
get [ChartAxisDirection.Radius](): PolarAxis;
}

View File

@@ -0,0 +1,134 @@
import type { AxisID, ChartAnimationPhase, ChartAxisDirection, ChartUpdateType, DomainWithMetadata, Padding, Scale } from 'ag-charts-core';
import type { AgAxisLabelFormatterParams, AgAxisLabelStylerParams, AgBaseAxisLabelStyleOptions, AgCartesianAxisPosition, Padding as AgPadding, AgTimeIntervalUnit, FormatterParams, RichFormatter, Styler, TextOptions, TextWrap } from 'ag-charts-types';
import type { AxisLayout } from '../core/eventsHub';
import type { AxisContext, AxisFormattableLabel } from '../module/axisContext';
import type { ModuleContextWithParent } from '../module/moduleContext';
import type { ModuleMap } from '../module/moduleMap';
import type { BBox } from '../scene/bbox';
import type { Group } from '../scene/group';
import type { AxisPrimaryTickCount } from '../util/secondaryAxisTicks';
import type { AxisGridLine } from './axis/axisGridLine';
import type { AxisLine } from './axis/axisLine';
import type { AxisTick, TickInterval } from './axis/axisTick';
import type { CrossLine } from './crossline/crossLine';
import type { ScrollbarLayoutMap } from './layout/layoutManager';
import type { DatumIndexType, ISeries } from './series/seriesTypes';
export type ChartAxisLabelFlipFlag = 1 | -1;
interface AxisInterval {
step?: number | TickInterval<any>;
values?: any[];
minSpacing?: number;
maxSpacing?: number;
}
interface AxisLayoutConstraints {
stacked: boolean;
align: 'justify' | 'start' | 'center' | 'end';
width: number;
unit: 'percent' | 'px';
}
export interface AxisGroups {
axisNode: Group;
gridNode: Group;
crossLineRangeNode: Group;
crossLineLineNode: Group;
crossLineLabelNode: Group;
labelNode: Group;
}
export type FormatDatumParams = Omit<FormatterParams<any>, 'type' | 'value'>;
export interface ChartLayout {
padding: Padding;
sizeLimit: number;
scrollbars?: ScrollbarLayoutMap;
}
export interface ChartAxis {
attachAxis(opts: AxisGroups): void;
calculateLayout(primaryTickCount?: AxisPrimaryTickCount, chartLayout?: ChartLayout): {
primaryTickCount?: AxisPrimaryTickCount;
bbox?: BBox;
};
clipGrid(x: number, y: number, width: number, height: number): void;
clipTickLines(x: number, y: number, width: number, height: number): void;
createAxisContext(): AxisContext;
createModuleContext(): ModuleContextWithParent<AxisContext>;
destroy(): void;
detachAxis(): void;
formatDatum(contextProvider: {
context?: unknown;
}, value: any, source: 'tooltip' | 'series-label', seriesId: string, legendItemName: string | undefined, datum: any, key: string, domain?: undefined, label?: undefined, params?: undefined, allowNull?: boolean): string;
formatDatum<Params extends object>(contextProvider: {
context?: unknown;
} | undefined, value: any, source: 'crosshair' | 'annotation-label', seriesId: undefined, legendItemName: undefined, datum: undefined, key: undefined, domain: undefined, label: AxisFormattableLabel<Params>, params: Params, allowNull?: boolean): string;
formatDatum<Params extends object>(contextProvider: {
context?: unknown;
} | undefined, value: any, source: 'tooltip' | 'series-label', seriesId: string, legendItemName: string | undefined, datum: any, key: string, domain: any[], label: AxisFormattableLabel<Params>, params: Params, allowNull?: boolean): string;
getBBox(): BBox;
getLayoutState(): AxisLayout;
getModuleMap(): ModuleMap;
getUpdateTypeOnResize(): ChartUpdateType;
inRange(x: number, tolerance?: number): boolean;
isReversed(): boolean;
resetAnimation(chartAnimationPhase: ChartAnimationPhase): unknown;
setCrossLinesVisible(visible: boolean): void;
processData(): void;
update(animated?: boolean): void;
setDomains(...domains: DomainWithMetadata<unknown>[]): void;
isCategoryLike(): boolean;
boundSeries: ISeries<DatumIndexType, unknown, unknown>[];
crossLines?: CrossLine[];
dataDomain: {
domain: any[];
clipped: boolean;
};
direction: ChartAxisDirection;
gridLength: number;
gridLine: AxisGridLine;
gridPadding: number;
id: AxisID;
interactionEnabled: boolean;
interval: AxisInterval;
label: ChartAxisLabel;
layoutConstraints: AxisLayoutConstraints;
line: AxisLine;
nice: boolean;
position?: AgCartesianAxisPosition;
range: [number, number];
requiredRange?: number;
reverse: boolean;
scale: Scale<any, any, any>;
seriesAreaPadding: number;
thickness?: number;
maxThicknessRatio?: number;
minimumTimeGranularity?: AgTimeIntervalUnit;
tick: AxisTick;
translation: {
x: number;
y: number;
};
type: string;
visibleRange: [number, number];
}
export interface ChartAxisLabel extends TextOptions {
fontSize: number;
getSideFlag(): ChartAxisLabelFlipFlag;
set(props: object): void;
autoRotate?: boolean;
autoRotateAngle?: number;
avoidCollisions: boolean;
border: {
enabled: boolean;
stroke?: string;
};
enabled: boolean;
format?: string | Record<string, string>;
formatter?: RichFormatter<AgAxisLabelFormatterParams>;
itemStyler?: Styler<AgAxisLabelStylerParams, AgBaseAxisLabelStyleOptions>;
minSpacing?: number;
mirrored: boolean;
spacing: number;
padding?: AgPadding;
parallel: boolean;
rotation?: number;
truncate?: boolean;
wrapping?: TextWrap;
}
export {};

View File

@@ -0,0 +1,13 @@
import type { LayoutCompleteEvent } from '../core/eventsHub';
import { Caption } from './caption';
import type { LayoutContext } from './layout/layoutManager';
export declare class ChartCaptions {
readonly title: Caption;
readonly subtitle: Caption;
readonly footnote: Caption;
positionCaptions({ layoutBox }: LayoutContext): void;
positionAbsoluteCaptions(ctx: LayoutCompleteEvent): void;
private computeX;
private positionCaption;
private shrinkLayoutByCaption;
}

View File

@@ -0,0 +1,87 @@
import { CallbackCache, CleanupRegistry, EventEmitter } from 'ag-charts-core';
import { ChartTypeOriginator } from '../api/preset/chartTypeOriginator';
import { HistoryManager } from '../api/state/historyManager';
import { StateManager } from '../api/state/stateManager';
import type { EventsHubMap } from '../core/eventsHub';
import { DOMManager } from '../dom/domManager';
import { ProxyInteractionService } from '../dom/proxyInteractionService';
import { LocaleManager } from '../locale/localeManager';
import type { ModuleContext } from '../module/moduleContext';
import type { Group } from '../scene/group';
import { Scene } from '../scene/scene';
import type { Mutex } from '../util/mutex';
import type { TypedEvent } from '../util/observable';
import { AnnotationManager } from './annotation/annotationManager';
import { AxisManager } from './axis/axisManager';
import type { ChartService } from './chartService';
import { DataService } from './data/dataService';
import type { ChartType } from './factory/expectedModules';
import { FontManager } from './fonts/fontManager';
import { FormatManager } from './formatter/formatManager';
import { ActiveManager } from './interaction/activeManager';
import { AnimationManager } from './interaction/animationManager';
import { ContextMenuRegistry } from './interaction/contextMenuRegistry';
import { HighlightManager } from './interaction/highlightManager';
import { InteractionManager } from './interaction/interactionManager';
import type { SyncManager } from './interaction/syncManager';
import { TooltipManager } from './interaction/tooltipManager';
import { WidgetSet } from './interaction/widgetSet';
import { ZoomManager } from './interaction/zoomManager';
import { LayoutManager } from './layout/layoutManager';
import { SeriesLabelLayoutManager } from './layout/seriesLabelLayoutManager';
import { LegendManager } from './legend/legendManager';
import { OptionsGraphService } from './optionsGraphService';
import { SeriesStateManager } from './series/seriesStateManager';
import type { Tooltip } from './tooltip/tooltip';
import { type UpdateCallback, UpdateService } from './updateService';
export declare class ChartContext implements ModuleContext {
readonly eventsHub: EventEmitter<EventsHubMap>;
readonly callbackCache: CallbackCache;
readonly highlightManager: HighlightManager;
readonly formatManager: FormatManager;
readonly layoutManager: LayoutManager;
readonly localeManager: LocaleManager;
readonly seriesStateManager: SeriesStateManager;
readonly stateManager: StateManager;
readonly seriesLabelLayoutManager: SeriesLabelLayoutManager;
readonly cleanup: CleanupRegistry;
readonly activeManager: ActiveManager;
animationManager: AnimationManager;
annotationManager: AnnotationManager;
axisManager: AxisManager;
legendManager: LegendManager;
chartService: ChartService;
chartTypeOriginator: ChartTypeOriginator;
contextMenuRegistry: ContextMenuRegistry;
dataService: DataService<any>;
domManager: DOMManager;
fontManager: FontManager;
historyManager: HistoryManager;
interactionManager: InteractionManager;
optionsGraphService: OptionsGraphService;
proxyInteractionService: ProxyInteractionService;
scene: Scene;
syncManager: SyncManager;
tooltipManager: TooltipManager;
updateService: UpdateService;
widgets: WidgetSet;
zoomManager: ZoomManager;
constructor(chart: ChartService & {
annotationRoot: Group;
tooltip: Tooltip;
}, vars: {
chartType: ChartType;
scene?: Scene;
root: Group;
syncManager: SyncManager;
container?: HTMLElement;
styleContainer?: HTMLElement;
skipCss?: boolean;
domMode?: 'normal' | 'minimal';
withDragInterpretation: boolean;
fireEvent: <TEvent extends TypedEvent>(event: TEvent) => void;
updateCallback: UpdateCallback;
updateMutex: Mutex;
});
destroy(): void;
}

View File

@@ -0,0 +1,6 @@
import { BaseProperties } from 'ag-charts-core';
import type { AgDrawingMode } from 'ag-charts-types';
export declare class ChartHighlight extends BaseProperties {
range: 'tooltip' | 'node';
drawingMode: AgDrawingMode;
}

View File

@@ -0,0 +1 @@
export type ChartMode = 'standalone' | 'integrated';

View File

@@ -0,0 +1,24 @@
import { type OptionsDefs } from 'ag-charts-core';
import type { AgActiveState, AgCartesianChartOptions, AgPolarChartOptions, AgStandaloneChartOptions, AgTopologyChartOptions } from 'ag-charts-types';
export declare const initialStatePickedOptionsDef: OptionsDefs<AgActiveState>;
export declare const commonChartOptions: {
mode: import("ag-charts-core").Validator;
container: import("ag-charts-core").Validator;
context: () => boolean;
theme: import("ag-charts-core").Validator;
series: import("ag-charts-core").Validator;
annotations: import("ag-charts-core").Validator;
navigator: import("ag-charts-core").Validator;
scrollbar: import("ag-charts-core").Validator;
initialState: {
active: OptionsDefs<AgActiveState>;
chartType: import("ag-charts-core").Validator;
annotations: import("ag-charts-core").Validator;
legend: import("ag-charts-core").Validator;
zoom: import("ag-charts-core").Validator;
};
};
export declare const cartesianChartOptionsDefs: OptionsDefs<AgCartesianChartOptions>;
export declare const polarChartOptionsDefs: OptionsDefs<AgPolarChartOptions>;
export declare const topologyChartOptionsDefs: OptionsDefs<AgTopologyChartOptions>;
export declare const standaloneChartOptionsDefs: OptionsDefs<AgStandaloneChartOptions>;

View File

@@ -0,0 +1,44 @@
import type { MementoCaretaker } from 'ag-charts-core';
import { type DeepPartial, type LicenseManager } from 'ag-charts-core';
import type { AgChartInstance, AgChartOptions, AgChartState, AgDataTransaction, DownloadOptions, ImageDataUrlOptions } from 'ag-charts-types';
import { type ChartInternalOptionMetadata, type ChartSpecialOverrides } from '../module/optionsModule';
import type { Chart } from './chart';
import type { DataServiceRestoredData } from './data/dataService';
export interface AgChartProxy extends AgChartInstance {
chart?: Chart;
}
export interface FactoryApi {
caretaker: MementoCaretaker;
create(userOptions: AgChartOptions, processedOverrides?: Partial<AgChartOptions>, specialOverrides?: ChartSpecialOverrides, optionsMetadata?: ChartInternalOptionMetadata, data?: DataServiceRestoredData): AgChartProxy;
update(opts: AgChartOptions, chart?: AgChartInstance, specialOverrides?: ChartSpecialOverrides, apiStartTime?: number): AgChartProxy;
updateUserDelta(chart: AgChartInstance, deltaOptions: DeepPartial<AgChartOptions>, apiStartTime?: number): void;
}
/**
* Proxy class, to allow library users to keep a stable reference to their chart, even if we need
* to switch concrete class (e.g. when switching between CartesianChart vs. PolarChart).
*/
export declare class AgChartInstanceProxy implements AgChartProxy {
private readonly factoryApi;
private readonly licenseManager?;
static readonly chartInstances: WeakMap<Chart, AgChartInstanceProxy>;
chart?: Chart;
releaseChart?: () => void;
constructor(chart: Chart, factoryApi: FactoryApi, licenseManager?: LicenseManager | undefined);
update(options: AgChartOptions): Promise<void>;
updateDelta(deltaOptions: DeepPartial<AgChartOptions>): Promise<void>;
getOptions(): import("ag-charts-types").AgCartesianChartOptions<any, unknown> | import("ag-charts-types").AgPolarChartOptions<any, unknown> | import("ag-charts-types").AgTopologyChartOptions<any, unknown> | import("ag-charts-types").AgStandaloneChartOptions<any, unknown>;
waitForUpdate(): Promise<void>;
applyTransaction(transaction: AgDataTransaction): Promise<void>;
download(opts?: DownloadOptions): Promise<void>;
__toSVG(opts?: DownloadOptions): Promise<string | undefined>;
getImageDataURL(opts?: ImageDataUrlOptions): Promise<string>;
getState(): Record<"version", string> & Record<string, any>;
setState(state: AgChartState): Promise<void>;
resetAnimations(): void;
skipAnimations(): void;
destroy(): void;
private prepareResizedChart;
private syncLegend;
private getEnabledOriginators;
private setStateOriginators;
}

View File

@@ -0,0 +1,20 @@
import type { DeepRequired } from 'ag-charts-core';
import type { AgChartInstance, AgTouchOptions } from 'ag-charts-types';
import { Group } from '../scene/group';
import type { CaptionLike } from './captionLike';
import type { ChartHighlight } from './chartHighlight';
import type { ChartMode } from './chartMode';
import type { ISeries } from './series/seriesTypes';
export interface ChartService {
readonly id: string;
readonly mode: ChartMode;
readonly styleNonce?: string;
readonly title: CaptionLike;
readonly series: ISeries<any, any, any>[];
readonly seriesRoot: Group;
readonly publicApi?: AgChartInstance;
readonly touch: DeepRequired<AgTouchOptions>;
readonly context?: unknown;
readonly highlight?: ChartHighlight;
overrideFocusVisible(visible: boolean | undefined): void;
}

View File

@@ -0,0 +1,55 @@
import { BaseProperties, type Scale } from 'ag-charts-core';
import type { AgCartesianAxisPosition, AgCartesianCrossLineLabelOptions, AgCrossLineLabelPosition } from 'ag-charts-types';
import { Group } from '../../scene/group';
import { LabelStyle } from '../label';
import { type CrossLine, type CrossLineType } from './crossLine';
import type { CrossLineLabelPosition } from './crossLineLabelPosition';
declare class CartesianCrossLineLabel extends LabelStyle implements AgCartesianCrossLineLabelOptions {
enabled: boolean | undefined;
padding: number;
text?: string;
position?: CrossLineLabelPosition;
rotation?: number;
parallel?: boolean;
}
export declare class CartesianCrossLine extends BaseProperties implements CrossLine<CartesianCrossLineLabel> {
static readonly className = "CrossLine";
readonly id: string;
enabled?: boolean;
type: CrossLineType;
range?: [unknown, unknown];
value?: unknown;
defaultColorRange: string[];
fill: string;
fillOpacity?: number;
stroke?: string;
strokeWidth?: number;
strokeOpacity?: number;
lineDash?: [];
label: CartesianCrossLineLabel;
scale?: Scale<any, number>;
clippedRange: [number, number];
gridLength: number;
position: AgCartesianAxisPosition;
get defaultLabelPosition(): AgCrossLineLabelPosition;
readonly rangeGroup: Group<unknown>;
readonly lineGroup: Group<unknown>;
readonly labelGroup: Group<unknown>;
private readonly crossLineRange;
private readonly crossLineLabel;
private data;
private startLine;
private endLine;
constructor();
private _isRange;
update(visible: boolean): void;
calculateLayout(visible: boolean): void;
private updateNodes;
private updateRangeNode;
private updateLabel;
private get anchor();
private positionLabel;
private computeLabelSize;
calculatePadding(into: Partial<Record<AgCrossLineLabelPosition, number>>): void;
}
export {};

View File

@@ -0,0 +1,43 @@
import type { ChartAxisDirection, Scale } from 'ag-charts-core';
import type { AgBaseCrossLineLabelOptions, AgCrossLineLabelPosition, AgTimeInterval, AgTimeIntervalUnit } from 'ag-charts-types';
import type { Group } from '../../scene/group';
export type CrossLineType = 'line' | 'range';
interface ICrossLine {
type: CrossLineType;
range?: [unknown, unknown];
value?: unknown;
}
export declare function getCrossLineValue(crossLine: ICrossLine): unknown;
export declare function validateCrossLineValue(crossLine: ICrossLine, scale: Scale<any, number>): boolean;
export interface CrossLine<LabelType = AgBaseCrossLineLabelOptions> {
calculateLayout?(visible: boolean, reversedAxis?: boolean): void;
calculatePadding?(padding: Partial<Record<AgCrossLineLabelPosition, number>>): void;
clippedRange: [number, number];
enabled?: boolean;
defaultColorRange: string[];
fill?: string;
fillOpacity?: number;
gridLength: number;
lineGroup: Group;
rangeGroup: Group;
id: string;
label: LabelType;
labelGroup: Group;
lineDash?: number[];
range?: [any, any];
scale?: Scale<any, number, number | AgTimeInterval | AgTimeIntervalUnit>;
stroke?: string;
strokeOpacity?: number;
strokeWidth?: number;
type: CrossLineType;
update(visible: boolean): void;
value?: any;
set(properties: object): void;
}
export interface PolarCrossLine<LabelType = AgBaseCrossLineLabelOptions> extends CrossLine<LabelType> {
direction: ChartAxisDirection;
parallelFlipRotation: number;
regularFlipRotation: number;
sideFlag: 1 | -1;
}
export {};

View File

@@ -0,0 +1,11 @@
import type { BBox } from '../../scene/bbox';
export type CrossLineLabelPosition = 'top' | 'left' | 'right' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'inside' | 'inside-left' | 'inside-right' | 'inside-top' | 'inside-bottom' | 'inside-top-left' | 'inside-bottom-left' | 'inside-top-right' | 'inside-bottom-right';
export declare function calculateLabelTranslation({ yDirection, padding, position, bbox, }: {
yDirection: boolean;
padding: number;
position: CrossLineLabelPosition;
bbox: BBox;
}): {
xTranslation: number;
yTranslation: number;
};

View File

@@ -0,0 +1,18 @@
import type { AggregatePropertyDefinition, DatumPropertyDefinition } from './dataModel';
export declare function sumValues(values: any[], accumulator?: [number, number]): [number, number];
export declare function sum(id: string, matchGroupId: string): AggregatePropertyDefinition<any, any>;
export declare function groupSum(id: string, opts?: {
matchGroupId?: string;
visible?: boolean;
}): AggregatePropertyDefinition<any, any>;
export declare function range(id: string, matchGroupId: string): AggregatePropertyDefinition<any, any>;
export declare function groupCount(id: string, opts?: {
visible?: boolean;
}): AggregatePropertyDefinition<any, any>;
export declare function groupAverage(id: string, opts?: {
matchGroupId?: string;
visible?: boolean;
}): AggregatePropertyDefinition<any, any, [number, number], [number, number, number]>;
export declare function area(id: string, aggFn: AggregatePropertyDefinition<any, any>, matchGroupId?: string): AggregatePropertyDefinition<any, any>;
export declare function accumulatedValue(onlyPositive?: boolean): DatumPropertyDefinition<any>['processor'];
export declare function trailingAccumulatedValue(): DatumPropertyDefinition<any>['processor'];

View File

@@ -0,0 +1,13 @@
import type { DataModel, DataModelOptions, UngroupedData } from './dataModel';
import type { DataSet } from './dataSet';
interface CachedDataItem<D extends object, K extends keyof D & string = keyof D & string> {
ids: string[];
opts: DataModelOptions<K, any>;
dataSet: DataSet<D>;
dataLength: number;
dataModel: DataModel<any, any, any>;
processedData: UngroupedData<any> | undefined;
}
export type CachedData = CachedDataItem<any, any>[];
export declare function canReuseCachedData<D extends object, K extends keyof D & string = keyof D & string>(cachedDataItem: CachedDataItem<any, any>, dataSet: DataSet<D>, ids: string[], opts: DataModelOptions<K, any>): boolean;
export {};

View File

@@ -0,0 +1,39 @@
import type { GroupedData, UngroupedData } from '../../dataModelTypes';
import type { DataModelContext } from '../dataModelContext';
import type { DataModelResolvers } from '../utils/resolvers';
import type { ScopeCacheManager } from '../utils/scopeCache';
/**
* Aggregator handles aggregation of values within groups and post-processing of grouped data.
*
* AGGREGATION RESPONSIBILITIES:
* - Applies aggregate functions to group values
* - Calculates aggregate domains for visualization
* - Supports both ungrouped and grouped data aggregation
* - Handles group-level value processing
*
* UNGROUPED vs GROUPED AGGREGATION:
* - Ungrouped: Each datum gets its own aggregation result
* - Grouped: Multiple datums in a group share aggregation results
*/
export declare class Aggregator<D extends object, K extends keyof D & string> {
private readonly ctx;
private readonly scopeCacheManager;
private readonly resolvers;
constructor(ctx: DataModelContext<D, K>, scopeCacheManager: ScopeCacheManager<K>, resolvers: DataModelResolvers<D, K>);
/**
* Aggregates data for ungrouped datasets.
* Each datum gets its own aggregation result.
*/
aggregateUngroupedData(processedData: UngroupedData<any>): void;
/**
* Aggregates data for grouped datasets.
* Multiple datums in a group share aggregation results.
*/
aggregateGroupedData(processedData: GroupedData<any>): void;
/**
* Post-processes groups after grouping is complete.
* Applies group value processors to adjust group values and recompute domains.
*/
postProcessGroups(processedData: GroupedData<any>): void;
private valueGroupIdxLookup;
}

View File

@@ -0,0 +1,51 @@
import type { Debug } from 'ag-charts-core';
import type { ChartMode } from '../../chartMode';
import type { BandedDomainConfig } from '../dataDomain';
import type { AggregatePropertyDefinition, GroupValueProcessorDefinition, InternalDatumPropertyDefinition, InternalDefinition, ProcessorOutputPropertyDefinition, PropertyDefinition, PropertyValueProcessorDefinition, ReducerOutputPropertyDefinition } from '../dataModelTypes';
/**
* DataModelContext bundles shared state and configuration for DataModel subsystems.
*
* PURPOSE:
* - Reduces constructor parameter counts across data model classes
* - Makes dependencies explicit and easier to understand
* - Simplifies adding new shared infrastructure in the future
* - Improves testability by allowing easy context mocking
*
* CONTENTS:
* - Property definition arrays (keys, values, aggregates, processors, etc.)
* - Configuration (debug logger, chart mode, options)
* - Infrastructure (scope cache for property lookups)
*
* USAGE:
* Create once in DataModel constructor, then pass to all subsystem classes
* that need access to shared state/configuration.
*/
export interface DataModelContext<D extends object, K extends keyof D & string> {
/** Key property definitions for data grouping/indexing */
keys: InternalDatumPropertyDefinition<K>[];
/** Value property definitions for data visualization */
values: InternalDatumPropertyDefinition<K>[];
/** Aggregate property definitions for computing derived values */
aggregates: (AggregatePropertyDefinition<D, K> & InternalDefinition<false>)[];
/** Group-level value processors that operate on grouped data */
groupProcessors: (GroupValueProcessorDefinition<D, K> & InternalDefinition<false>)[];
/** Property-level value processors that transform individual values */
propertyProcessors: (PropertyValueProcessorDefinition<D> & InternalDefinition<true>)[];
/** Reducer output definitions for derived calculated properties */
reducers: (ReducerOutputPropertyDefinition & InternalDefinition<false>)[];
/** Processor output definitions for transformed properties */
processors: (ProcessorOutputPropertyDefinition & InternalDefinition<false>)[];
/** Debug logger for development/troubleshooting */
debug: Debug.DebugLogger;
/** Chart operating mode (standalone/integrated) */
mode: ChartMode;
/** Optional configuration for banded domain optimization */
bandingConfig?: BandedDomainConfig;
/** Whether to suppress dot notation in property field names */
suppressFieldDotNotation: boolean;
/**
* Scope cache: Maps scope IDs to property definition lookups.
* Enables fast resolution of property definitions by ID within a scope.
*/
scopeCache: Map<string, Map<string, PropertyDefinition<any> & InternalDefinition<false>>>;
}

View File

@@ -0,0 +1,31 @@
import { BandedDomain, type IDataDomain } from '../../dataDomain';
import type { InternalDatumPropertyDefinition, SortOrderEntry } from '../../dataModelTypes';
import type { DataModelContext } from '../dataModelContext';
/**
* Handles domain initialization and extension for the DataModel.
* Manages both discrete and continuous domains, including banded domain optimization.
*/
export declare class DomainInitializer<K extends string> {
private readonly ctx;
constructor(ctx: DataModelContext<any, K>);
/**
* Sets up the appropriate domain type for a property definition.
* Returns a BandedDomain wrapping DiscreteDomain for category values,
* or a BandedDomain wrapping ContinuousDomain for continuous values.
* Falls back to non-banded domains when banding is disabled.
*
* @param sortOrderEntry Optional sort order metadata from KEY_SORT_ORDERS.
* When data is sorted and unique, enables fast array concatenation optimization.
*/
setupDomainForDefinition(def: InternalDatumPropertyDefinition<K>, bandedDomains: Map<InternalDatumPropertyDefinition<any>, BandedDomain>, sortOrderEntry?: SortOrderEntry): IDataDomain;
/**
* Extends a domain from data array, using banded optimization if available.
* Note: For BandedDomain, bands should already be initialized before calling this method.
*/
extendDomainFromData(domain: IDataDomain, data: any[], invalidData?: boolean[]): void;
/**
* Initializes a banded domain if needed based on data size and state.
* This is a memory optimization that divides large datasets into bands.
*/
initializeBandedDomain(domain: IDataDomain, dataSize: number, propertyName?: string): void;
}

View File

@@ -0,0 +1,62 @@
import { type IDataDomain } from '../../dataDomain';
import { type InternalDatumPropertyDefinition, type ProcessedData, type ProcessedValue } from '../../dataModelTypes';
import type { DataModelContext } from '../dataModelContext';
import type { ScopeCacheManager } from '../utils/scopeCache';
import { DomainInitializer } from './domainInitializer';
import { type SpecializedProcessValueFn } from './processValueFactory';
/**
* Manages domain computation and processing for the DataModel.
* Handles both discrete and continuous domains, including banded domain optimization
* and value processing during data transformation.
*/
export declare class DomainManager<D extends object, K extends keyof D & string> {
private readonly ctx;
private readonly initializer;
private readonly scopeCacheManager;
private readonly processValueFactory;
constructor(ctx: DataModelContext<D, K>, initializer: DomainInitializer<K>, scopeCacheManager: ScopeCacheManager<K>);
/**
* Recomputes all domains from processed data.
* Uses BandedDomain optimization for continuous domains to avoid full rescans.
* Shares domains between keys and values when they reference the same property.
*/
recomputeDomains(processedData: ProcessedData<D>): void;
/**
* Creates domain instances for the given definitions, reusing banded domains when available.
* For key definitions, passes KEY_SORT_ORDERS metadata to enable fast array concatenation.
*/
private setupDefinitionDomains;
/**
* Initializes banded domains for each definition using the provided data length accessor.
*/
private initializeDomainBands;
/**
* Extends domains from data sources using a shared traversal.
* @param skipDomains Optional set of domains to skip (already extended via shared key processing)
*/
private extendDomainsFromData;
/**
* Sets up value domains, reusing key domains where properties match.
* This avoids duplicate domain computation for properties that appear as both key and value.
*/
private setupValueDomainsWithSharing;
/**
* Initializes domain processor for value processing during data transformation.
* Returns domain maps and processing functions used during data extraction.
* Uses specialized functions per property definition to eliminate branching in hot paths.
* Shares domains between keys and values when they reference the same property.
*/
initDataDomainProcessor(domainMode: 'extend' | 'skip'): {
dataDomain: Map<object, IDataDomain<any>>;
processValue: (def: InternalDatumPropertyDefinition<K>, datum: unknown, idx: number, valueScopes: string | string[]) => ProcessedValue;
getProcessValue: (def: InternalDatumPropertyDefinition<K>) => SpecializedProcessValueFn;
initDataDomain: () => void;
scopes: Set<string>;
allScopesHaveSameDefs: boolean;
};
/**
* Collects metadata about banded domain optimization for debugging and testing.
* Stores statistics about domain banding per key and value definition.
*/
private collectDomainBandingMetadata;
}

View File

@@ -0,0 +1,45 @@
import type { IDataDomain } from '../../dataDomain';
import type { InternalDatumPropertyDefinition, ProcessedValue, ProcessorFn } from '../../dataModelTypes';
import type { DataModelContext } from '../dataModelContext';
/**
* Specialized function type for processing property values.
* Generated per property definition to eliminate branching and optimize hot paths.
*/
export type SpecializedProcessValueFn = (datum: unknown, idx: number, valueScopes: string | string[]) => ProcessedValue;
/**
* Factory responsible for generating optimized processValue functions per property definition.
* Encapsulates the specialized variants to keep DomainManager focused on orchestration.
*/
export declare class ProcessValueFactory<D extends object, K extends keyof D & string> {
private readonly ctx;
constructor(ctx: DataModelContext<D, K>);
createProcessValueFn(def: InternalDatumPropertyDefinition<K>, accessor: ((datum: any) => any) | undefined, domain: IDataDomain, reusableResult: ProcessedValue, processorFns: Map<InternalDatumPropertyDefinition<K>, ProcessorFn>, domainMode: 'extend' | 'skip'): SpecializedProcessValueFn;
private createSpecializedProcessValue;
private createValidationMeta;
/**
* Creates a specialized processValue function optimized for key properties with validation.
* Eliminates all branching for the most common key property case (~30% of calls).
*/
private createSpecializedProcessValueKeyValidation;
/**
* Creates a specialized processValue function optimized for value properties with validation.
* Eliminates branching for the most common value property case (~50% of calls).
*/
private createSpecializedProcessValueValueValidation;
/**
* Creates a specialized processValue function for properties with forceValue.
* Optimized for invisible series (~5-10% of calls).
*/
private createSpecializedProcessValueForceValue;
/**
* Creates a specialized processValue function for properties with processors.
* Optimized for data transformations (~5-10% of calls).
*/
private createSpecializedProcessValueProcessor;
/**
* Creates the generic fallback processValue implementation used for edge cases
* and for skip mode (no domain extension). Generates a per-definition function
* so callers can resolve it once and reuse it without additional branching.
*/
private createGenericProcessValue;
}

View File

@@ -0,0 +1,28 @@
import { type UngroupedData } from '../../dataModelTypes';
import type { DataSet } from '../../dataSet';
import type { DataModelContext } from '../dataModelContext';
import type { DomainManager } from '../domain/domainManager';
/**
* DataExtractor handles data extraction from DataSet sources.
*
* EXTRACTION RESPONSIBILITIES:
* - Extracts key and value data from DataSet sources
* - Processes data through property definitions
* - Tracks data validity and invalid entries per scope
* - Builds initial ungrouped data structure for further processing
*
* DATA VALIDITY TRACKING:
* - Maintains invalid key/value flags per scope
* - Enables partial data rendering when some entries are invalid
* - Tracks partial valid data count for optimization decisions
*/
export declare class DataExtractor<D extends object, K extends keyof D & string> {
private readonly ctx;
private readonly domainManager;
constructor(ctx: DataModelContext<D, K>, domainManager: DomainManager<D, K>);
extractData(sources: Map<string, DataSet<unknown>>): UngroupedData<D>;
private extractKeys;
private readonly markScopeDatumInvalid;
private extractValues;
warnDataMissingProperties(sources: Map<string, DataSet<unknown>>): void;
}

View File

@@ -0,0 +1,66 @@
import type { GroupedData, GroupingFn, UngroupedData } from '../../dataModelTypes';
import type { DataModelContext } from '../dataModelContext';
/**
* DataGrouper handles grouping of extracted data into groups based on keys.
*
* GROUPING RESPONSIBILITIES:
* - Groups data by key values to aggregate multiple datums
* - Optimizes batch processing by merging compatible column batches
* - Maintains group validity tracking across scopes
* - Supports custom grouping functions for flexible aggregation
*
* BATCH MERGING OPTIMIZATION:
* - Identifies columns that share the same data characteristics
* - Merges compatible batches to reduce iteration overhead
* - Can reduce processing iterations by 30-50% for multi-scope datasets
*
* Compatibility criteria:
* - Same keys arrays (by reference)
* - Same invalidity arrays (by reference)
* - Scopes can be safely processed together
*/
export declare class DataGrouper<D extends object, K extends keyof D & string> {
private readonly ctx;
constructor(ctx: DataModelContext<D, K>);
/**
* Groups data by keys or custom grouping function.
*
* GROUPED DATA STRUCTURE AND INVARIANTS:
*
* When groupsUnique=true (each datum has distinct keys):
* - groups.length === columns[i].length for all columns
* - groups[i] corresponds to datum at columns[j][i]
* - All datumIndices arrays contain [0] (shared memory optimization)
* - Relative indexing: datumIndices contains offsets from group start
* - Absolute indexing: groupIndex + relativeDatumIndex gives column position
*
* When groupsUnique=false (data is aggregated):
* - groups.length <= columns[i].length
* - Multiple datums may map to same group
* - datumIndices contain actual relative offsets
*
* This design optimizes memory usage for high-frequency data updates
* where each datum typically has unique keys (e.g., time series data).
*/
groupData(data: UngroupedData<D>, customGroupingFn?: GroupingFn<D>): GroupedData<D>;
/**
* Groups and merges column batches for efficient processing.
*
* BATCH MERGING OPTIMIZATION:
* - Identifies columns that share the same data characteristics
* - Merges compatible batches to reduce iteration overhead
* - Can reduce processing iterations by 30-50% for multi-scope datasets
*
* Compatibility criteria:
* - Same keys arrays (by reference)
* - Same invalidity arrays (by reference)
* - Scopes can be safely processed together
*/
private groupBatches;
/**
* Checks if two column batches can be merged based on shared data characteristics.
*/
private areBatchesCompatible;
private mergeCompatibleBatches;
private findAndMergeCompatibleBatches;
}

View File

@@ -0,0 +1,178 @@
import { type GroupedData, type InternalDatumPropertyDefinition, type ProcessedData, type ScopeId } from '../../dataModelTypes';
import type { DataChangeDescription, DataSet } from '../../dataSet';
import type { DataModelContext } from '../dataModelContext';
import type { SpecializedProcessValueFn } from '../domain/processValueFactory';
import { ReducerManager } from '../reducers/reducerManager';
/**
* Handles incremental reprocessing of data when DataSets change.
*
* INCREMENTAL REPROCESSING OPTIMIZATION:
* Instead of reprocessing all data, we:
* 1. Apply change descriptions to transform existing arrays
* 2. Process only new insertions
* 3. Update only affected domain bands
* 4. Reuse existing group structures when possible
* This can reduce processing time by 90%+ for small updates to large datasets
*/
export declare class IncrementalProcessor<D extends object, K extends keyof D & string> {
private readonly ctx;
private readonly reducerManager;
constructor(ctx: DataModelContext<D, K>, reducerManager: ReducerManager);
/**
* Checks if incremental reprocessing is supported for the given data configuration.
*/
isReprocessingSupported(processedData: ProcessedData<D>): boolean;
/**
* Performs incremental reprocessing of data based on change descriptions.
*/
reprocessData(processedData: ProcessedData<D>, dataSets: Map<DataSet<any>, DataChangeDescription | undefined> | undefined, getProcessValue: (def: InternalDatumPropertyDefinition<K>) => SpecializedProcessValueFn, reprocessGroupProcessorsFn: (processedData: GroupedData<D>, scopeChanges: Map<ScopeId, DataChangeDescription>) => void, recomputeDomainsFn: (processedData: ProcessedData<D>) => void, collectOptimizationMetadataFn: (processedData: ProcessedData<D>, mode: 'reprocess') => void): ProcessedData<D>;
/**
* Updates banded domains based on pending changes.
*
* BANDING OPTIMIZATION:
* - Divides large datasets into bands (default ~100 bands)
* - Tracks which bands are "dirty" and need recalculation
* - During updates, only dirty bands are reprocessed
* - Significantly reduces domain calculation overhead for large datasets
*
* Example: 1M data points → 100 bands of 10K points each
* Adding 1000 points only dirties 1-2 bands instead of scanning all 1M points
*
* This optimizes domain recalculation by only marking affected bands as dirty.
* Deduplicates change descriptions to avoid processing the same changes multiple times
* when multiple scopes share the same DataSet.
*/
private updateBandsForChanges;
private reprocessBandedReducers;
/**
* Collects change descriptions from all DataSets before committing.
*/
private collectScopeChanges;
/**
* Commits all pending transactions to the data arrays.
* Deduplicates DataSets to avoid committing the same DataSet multiple times
* when multiple scopes share the same DataSet.
*/
private commitPendingTransactions;
private buildDefinitionProcessors;
/**
* Pre-processes all insertions once per scope to avoid redundant computation.
*/
private processAllInsertions;
/**
* Processes all updated items once per scope, adding them to the insertion cache.
* This ensures updated values are available when transforming columns/keys arrays.
*/
private processAllUpdates;
/**
* Processes all insertions for a given scope once, caching the results.
* Returns a map from ADJUSTED destIndex to processed values for all keys and values.
* The adjusted destIndex accounts for out-of-bounds insertions that need to be shifted.
*/
private processInsertionsOnce;
/**
* Processes a single datum for the given scope, returning cached key/value results.
* Shared between insert and update paths to keep behaviour consistent.
*/
private processDatum;
/**
* Generic utility to transform arrays using cached insertion results.
* This reduces duplication across transformKeysArrays, transformColumnsArrays, and transformInvalidityArrays.
*/
private transformArraysWithCache;
/**
* Transforms keys arrays using cached insertion results.
*/
private transformKeysArrays;
/**
* Transforms columns arrays using cached insertion results.
*/
private transformColumnsArrays;
/**
* Helper to transform a scope-based invalidity map.
*/
private transformInvalidityMap;
/**
* Transforms invalidity arrays using cached insertion results.
*/
private transformInvalidityArrays;
/**
* Applies a change description to an array using the provided cache-aware extractor.
* Shared by array transformation helpers to keep update logic consistent.
*/
private applyChangeDescWithCache;
/**
* Transforms the groups array for grouped data during reprocessing.
* Only called when groupsUnique=true and no invalid keys exist.
*
* This maintains the invariant: groups[i] corresponds to datum at columns[i].
*/
private transformGroupsArray;
/**
* Creates a new DataGroup for an inserted datum during reprocessing.
*
* When groupsUnique=true and no invalid keys exist, each datum has:
* - A unique set of keys
* - datumIndices[columnIdx] = [0] (relative offset is always 0)
* - All scopes are valid initially (unless invalid value detected)
*/
private createDataGroupForInsertion;
/**
* Generates diff metadata for animations and incremental rendering.
* This is an opt-in feature - only runs if diff tracking is already initialized.
*/
private generateDiffMetadata;
/**
* Updates metadata after array transformations.
* Uses intelligent cache management based on change patterns.
*/
private updateProcessedDataMetadata;
/**
* Updates sort order entry incrementally for appended values.
* Checks if new values maintain the existing ordering/uniqueness.
*/
private updateSortOrderForAppend;
/**
* Updates KEY_SORT_ORDERS incrementally after an append operation.
*/
private updateKeySortOrdersForAppend;
/**
* Invalidates sort order metadata BEFORE domain recomputation.
*
* This must be called BEFORE recomputeDomains() so that BandedDomain.setSortOrderMetadata()
* receives the correct (possibly cleared) metadata. Without this, rolling window operations
* would see stale sort order data and incorrectly configure sub-domains for sorted mode.
*
* @param anyKeyChanged - Whether any key values changed during update processing
*/
private invalidateSortOrdersForChanges;
/**
* Updates KEY_SORT_ORDERS incrementally after a rolling window operation.
* Rolling window = contiguous removals at start + appends at end.
*/
private updateKeySortOrdersForRollingWindow;
/**
* Invalidates domain range caches after domain recomputation.
*
* Called AFTER recomputeDomains() to mark domain ranges as dirty for lazy rebuild.
* Sort order invalidation is handled separately by invalidateSortOrdersForChanges().
*/
private invalidateCachesForChanges;
/**
* Marks all RangeLookup entries as dirty for lazy rebuild.
*/
private markDomainRangesDirty;
/**
* Recounts invalid entries for the given map into the provided counts map.
*/
private recountInvalid;
/**
* Recomputes processor outputs using their incrementalCalculate hook when available.
* Falls back to calculate to avoid stale reducer outputs if a processor lacks the hook.
*/
private reprocessProcessors;
/**
* Helper to get unique DataSets from processed data.
*/
private getUniqueDataSets;
}

View File

@@ -0,0 +1,96 @@
import type { BandedDomainConfig } from '../../dataDomain';
import { type BandLike, BandedStructure } from '../utils/bandedStructure';
export interface ReducerBand extends BandLike {
cachedResult: unknown;
}
export interface ReducerContext {
rawData: unknown[];
keyColumns: unknown[][];
keysParam: unknown[];
}
/**
* Minimal reducer definition interface for BandedReducer.
* This avoids circular dependency with dataModelTypes.ts.
*/
export interface ReducerDefinition<T = any> {
initialValue?: T;
reducer: () => (acc: T, keys: unknown[]) => T;
needsOverlap?: boolean;
combineResults?: (bandResults: T[]) => T;
}
/**
* Band-based structure for reducer aggregations.
* Each band maintains a cached result for efficient incremental updates.
*
* This class extends BandedStructure with reducer-specific functionality:
* - Caching aggregation results per band
* - Evaluating reducers across bands with overlap support
* - Combining band results into final aggregated values
* - Tracking scan ratios for performance metrics
*
* Symmetrical to BandedDomain which handles domain aggregation.
*/
export declare class BandedReducer extends BandedStructure<ReducerBand> {
private lastDirtyBandCount;
private lastScanRatio;
private statsCaptured;
constructor(config?: BandedDomainConfig);
/**
* Creates a new reducer band with undefined cached result.
*/
protected createBand(startIndex: number, endIndex: number): ReducerBand;
/**
* Initializes bands and resets stats capture flag.
*/
initializeBands(dataSize: number): void;
/**
* Gets the array of bands for direct access.
* For production code, prefer using evaluateFromData() and getResult() for symmetry with BandedDomain.
* This method is primarily for testing and debugging band structure.
*/
getBands(): ReducerBand[];
/**
* Evaluates a reducer across all bands, reusing cached results for clean bands.
* Symmetrical to BandedDomain.extendBandsFromData().
*
* @param def Reducer definition with reducer function, initial value, and overlap settings
* @param context Reducer context containing raw data and key columns
* @param reuseCleanBands Whether to reuse cached results for clean bands (default: false)
*/
evaluateFromData(def: ReducerDefinition, context: ReducerContext, reuseCleanBands?: boolean): void;
/**
* Combines all band results to get the final aggregated value.
* Symmetrical to BandedDomain.getDomain().
*
* @param def Reducer definition with combineResults function
* @returns Combined result from all bands
*/
getResult(def: ReducerDefinition): unknown;
/**
* Evaluates a reducer over a specific range of data indices.
* Symmetrical to BandedDomain's band scanning loop in extendBandsFromData().
*
* @param def Reducer definition with initial value
* @param reducer Reducer function to apply
* @param context Reducer context with data and keys
* @param startIndex Starting index (inclusive)
* @param endIndex Ending index (exclusive)
* @returns Accumulated reducer result for the range
*/
private evaluateRange;
/**
* Capture the current dirty state before processing.
* Call this before marking bands as clean to preserve stats for reporting.
*/
captureStatsBeforeProcessing(): void;
/**
* Returns reducer-specific statistics including cache hits and scan ratio.
*/
getStats(): {
totalBands: number;
dirtyBands: number;
dataSize: number;
scanRatio: number;
cacheHits: number;
};
}

View File

@@ -0,0 +1,73 @@
import type { BandedDomainConfig } from '../../dataDomain';
import type { InternalDefinition, ProcessedData, ReducerOutputPropertyDefinition, ScopeId } from '../../dataModelTypes';
import type { BandIndexMap } from '../utils/bandedStructure';
import { BandedReducer, type ReducerContext } from './bandedReducer';
/**
* Extended reducer context with scope information.
*/
export interface ReducerContextWithScope extends ReducerContext {
scopeId: ScopeId | undefined;
}
export interface ReducerEvaluationOptions {
reuseCleanBands?: boolean;
beforeEvaluate?: (bandManager: BandedReducer, context: ReducerContextWithScope) => void;
}
/**
* Manages reducer evaluation with band-based caching.
* Symmetrical to DomainManager which manages domain computation with band-based caching.
*
* Responsibilities:
* - Creating and managing BandedReducer instances per reducer property
* - Creating reducer contexts from processed data
* - Orchestrating reducer evaluation lifecycle
* - Applying index map updates to reducer bands
* - Collecting optimization metadata
* - Providing utility methods for non-banded reducer evaluation
*/
export declare class ReducerManager {
private readonly bandingConfig;
constructor(bandingConfig?: BandedDomainConfig);
/**
* Evaluates a reducer over a specific range of data indices.
* Used for non-banded reducer evaluation (fallback path).
*
* This is a static utility method for cases where banding is not applicable
* (e.g., grouped data, small datasets, reducers that don't support banding).
*
* @param def Reducer definition with initial value
* @param reducer Reducer function to apply
* @param context Reducer context with data and keys
* @param startIndex Starting index (inclusive)
* @param endIndex Ending index (exclusive)
* @returns Accumulated reducer result for the range
*/
static evaluateRange(def: ReducerOutputPropertyDefinition, reducer: ReturnType<ReducerOutputPropertyDefinition['reducer']>, context: ReducerContext, startIndex: number, endIndex: number): unknown;
/**
* Evaluates a banded reducer and returns the aggregated result.
* Symmetrical to DomainManager.recomputeDomains().
*
* @param def Reducer definition
* @param processedData Processed data containing raw data and keys
* @param options Evaluation options including band reuse settings
* @returns Aggregated reducer result
*/
evaluate(def: ReducerOutputPropertyDefinition & InternalDefinition<false>, processedData: ProcessedData<any>, options?: ReducerEvaluationOptions): unknown;
/**
* Applies index map transformations to all reducer bands.
* Symmetrical to DomainManager's band update logic.
*
* @param processedData Processed data containing reducer bands
* @param indexMap Index map with splice operations and updated indices
*/
applyIndexMap(processedData: ProcessedData<any>, indexMap: BandIndexMap): void;
/**
* Creates a reducer context from processed data.
* Extracts raw data and key columns for the appropriate scope.
* Symmetrical to domain context creation in DomainManager.
*
* @param def Reducer definition
* @param processedData Processed data
* @returns Reducer context with scope information, or undefined if not applicable
*/
private createContext;
}

View File

@@ -0,0 +1,194 @@
/**
* Minimal interface for band-like structures.
* Both domain bands and reducer bands must have these properties.
*/
export interface BandLike {
startIndex: number;
endIndex: number;
isDirty: boolean;
}
/**
* Configuration for band initialization.
*/
export interface BandConfig {
enableBanding: boolean;
minDataSizeForBanding: number;
targetBandCount: number;
}
/**
* Configuration options for banded structures.
* This is the base config used by BandedStructure and extended by BandedDomainConfig.
*/
export interface BandedStructureConfig {
/** Minimum data size to activate banding (default: 1000) */
minDataSizeForBanding?: number;
/** Target number of bands to create (default: 10) */
targetBandCount?: number;
/** Maximum items per band before splitting (default: undefined - no limit) */
maxBandSize?: number;
/** Enable banding optimization (default: true) */
enableBanding?: boolean;
}
/**
* Shared index map type for splice/update operations applied to banded structures.
*/
export interface BandIndexMap {
spliceOps: Array<{
index: number;
insertCount: number;
deleteCount: number;
}>;
updatedIndices: Set<number>;
}
/**
* Adjusts band indices for an insertion operation.
* Handles three cases:
* 1. Insertion before band - shift both boundaries
* 2. Insertion within band or at end of last band - extend end boundary
* 3. Insertion after band - no changes
*
* @param band The band to adjust
* @param insertIndex Index where insertion occurs
* @param insertCount Number of items inserted
* @param isLastBand Whether this is the last band in the array
* @returns true if band was modified and should be marked dirty
*/
export declare function adjustBandForInsertion<T extends BandLike>(band: T, insertIndex: number, insertCount: number, isLastBand: boolean): boolean;
/**
* Adjusts band indices for a removal operation.
* Handles multiple overlap cases:
* 1. Removal before band - shift both boundaries
* 2. Removal fully contains band - collapse to empty
* 3. Removal overlaps start of band - shrink from start
* 4. Removal overlaps end of band - shrink from end
* 5. Removal within band - shrink end boundary
*
* @param band The band to adjust
* @param removeIndex Index where removal starts
* @param removeCount Number of items removed
* @returns true if band was affected and should be marked dirty
*/
export declare function adjustBandForRemoval<T extends BandLike>(band: T, removeIndex: number, removeCount: number): boolean;
/**
* Calculates target band count based on data size.
* Uses 1000 items per band as a baseline, with a configurable minimum.
*
* @param dataSize Total number of data items
* @param minBandCount Minimum number of bands to create
* @returns Target number of bands
*/
export declare function calculateTargetBandCount(dataSize: number, minBandCount: number): number;
/**
* Calculates ideal band size given data size and target band count.
*
* @param dataSize Total number of data items
* @param targetBandCount Target number of bands
* @returns Ideal size for each band
*/
export declare function calculateIdealBandSize(dataSize: number, targetBandCount: number): number;
/**
* Filters out empty bands (where endIndex <= startIndex).
* Empty bands can occur after removals that eliminate all data in a band.
*/
export declare function filterEmptyBands<T extends BandLike>(bands: T[]): T[];
/**
* Creates an array of bands with the given configuration.
* For small datasets or when banding is disabled, creates a single band.
* Otherwise, divides data into approximately equal-sized bands.
*/
export declare function initializeBandArray<T extends BandLike>(dataSize: number, config: BandConfig, bandFactory: (startIndex: number, endIndex: number) => T): T[];
/**
* Marks bands containing the given index as dirty.
* Used for update operations where data changes but dataset size doesn't.
*/
export declare function markBandDirtyAtIndex<T extends BandLike>(bands: T[], index: number): void;
/**
* Apply splice operations (insertions and deletions) to a band-like structure.
* This is a generic helper for any structure with handleInsertion/handleRemoval methods.
*/
export declare function applySpliceOperations(bandHandler: {
handleInsertion(index: number, count: number): void;
handleRemoval(index: number, count: number): void;
}, spliceOps: Array<{
index: number;
insertCount: number;
deleteCount: number;
}>): void;
/**
* Mark bands dirty at updated indices by using handleInsertion with 0 count.
* This avoids changing band structure while marking them for recalculation.
*/
export declare function markUpdatedIndices(bandHandler: {
handleInsertion(index: number, count: number): void;
}, updatedIndices: Set<number>): void;
/**
* Applies a change description (insertions, deletions, updates) to a band-like structure.
* Combines splice operations and updated-index handling to keep band dirty-state logic consistent.
*/
export declare function applyIndexMapToBandHandler(bandHandler: {
handleInsertion(index: number, count: number): void;
handleRemoval(index: number, count: number): void;
}, indexMap: BandIndexMap): void;
/**
* Abstract base class for band-based data structures.
* Provides common functionality for dividing data into bands for efficient incremental updates.
*
* Both BandedReducer and BandedDomain share the same banding logic:
* - Initialize bands based on data size
* - Handle insertions with proactive splitting
* - Handle removals with band cleanup
* - Track dirty state for incremental processing
*
* Subclasses differ only in what they store per band:
* - BandedReducer: stores cachedResult for aggregations
* - BandedDomain: stores subDomain for domain calculations
*/
export declare abstract class BandedStructure<TBand extends BandLike> {
protected bands: TBand[];
protected dataSize: number;
protected readonly config: Required<BandedStructureConfig>;
constructor(config?: BandedStructureConfig);
applyIndexMap(indexMap: BandIndexMap): void;
/**
* Abstract method to create a band with subclass-specific data.
* @param startIndex Starting index of the band
* @param endIndex Ending index of the band
* @returns A new band instance
*/
protected abstract createBand(startIndex: number, endIndex: number): TBand;
/**
* Initializes or rebalances bands based on current data size.
*/
initializeBands(dataSize: number): void;
/**
* Returns the number of bands currently in this structure.
* Useful for checking if bands need initialization.
*/
getBandCount(): number;
/**
* Handles insertion of new data by adjusting band indices.
* Uses proactive band splitting to maintain optimal band sizes.
*/
handleInsertion(insertIndex: number, insertCount: number): void;
/**
* Handles removal of data by adjusting band indices.
* Uses shared utilities for consistent band manipulation.
*/
handleRemoval(removeIndex: number, removeCount: number): void;
/**
* Split an oversized band into two smaller bands.
* Called when a band exceeds maxBandSize during insertion.
*
* Strategy:
* - Split the band as evenly as possible
* - Both halves marked as dirty (need recalculation)
* - No cache preservation (splitting indicates data changed)
*/
protected splitBand(bandIndex: number, idealSize: number): void;
/**
* Returns statistics about the banded structure for debugging.
* Subclasses can override to add domain-specific stats.
*/
getStats(): Record<string, number>;
markRangeDirty(startIndex: number, endIndex: number): void;
}

View File

@@ -0,0 +1,53 @@
import type { DataChangeDescription } from '../../dataChangeDescription';
import type { MissMap, ScopeId, ScopeProvider } from '../../dataModelTypes';
export declare const NULL_KEY_STRING = "\0__AG_NULL__\0";
export declare const UNDEFINED_KEY_STRING = "\0__AG_UNDEFINED__\0";
/**
* Converts an array of keys to a string representation.
* Objects are JSON-stringified, other values are joined with '-'.
* Null and undefined values use distinct sentinel strings to avoid collision with each other
* and with the literal strings "null" and "undefined".
* Arrays are recursively processed to preserve null/undefined distinction within elements.
*/
export declare function toKeyString(keys: any[]): string;
/**
* Fixes a numeric extent to ensure both values are finite numbers.
* Returns empty array if extent is null or contains non-finite values.
*/
export declare function fixNumericExtent(extent: Array<number | Date> | null): [] | [number, number];
/**
* Gets the miss count for a given scope provider from the miss map.
*/
export declare function getMissCount(scopeProvider: ScopeProvider, missMap: MissMap | undefined): number;
/**
* Type guard to check if an object has a scopes property.
*/
export declare function isScoped<T extends object>(obj: T): obj is T & {
scopes: string[];
};
/**
* Creates an array of a given length filled with a value.
* More efficient than Array.fill for large arrays.
*/
export declare function createArray<T>(length: number, value: T): T[];
/**
* Deduplicate change descriptions (multiple scopes can share the same DataSet/change descriptor).
*/
export declare function uniqueChangeDescriptions(scopeChanges: Map<ScopeId, DataChangeDescription>): Set<DataChangeDescription>;
/**
* Extracts keys from arrays at a specific datum index.
* Returns undefined if any key is null or undefined (unless allowNull is true).
* When allowNull is true, both null and undefined are allowed as valid keys.
*/
export declare function datumKeys(keys: Array<unknown[] | undefined>, datumIndex: number, allowNull?: boolean): any[] | undefined;
/**
* Parses a property path string into components.
* Supports dot notation (a.b), bracket notation (a['b']), and array indices (a[0]).
* Returns undefined if the path is invalid.
*/
export declare function getPathComponents(path: string): string[] | undefined;
/**
* Creates an accessor function for a given property path.
* The accessor traverses the path components to retrieve nested values.
*/
export declare function createPathAccessor(components: string[]): (datum: any) => any;

View File

@@ -0,0 +1,53 @@
import type { GroupedData, ProcessedData, ProcessedDataDef, PropertyDefinition, ScopeProvider, SortOrderEntry, UngroupedData } from '../../dataModelTypes';
import { type SortOrder } from '../../sortOrder';
import type { DataModelContext } from '../dataModelContext';
/**
* DataModelResolvers handles lookups and resolution of processed data.
*
* RESOLVER RESPONSIBILITIES:
* - Resolves property definitions by ID within a scope
* - Looks up keys, columns, and values from processed data
* - Manages domain and sort order resolution
* - Provides range lookups for data access
*
* SCOPE-AWARE RESOLUTION:
* - All lookups are scoped to specific data sources
* - Uses internal scope cache for fast property definition lookup
* - Throws clear errors when definitions are not found
*/
export declare class DataModelResolvers<D extends object, K extends keyof D & string> {
private readonly ctx;
private readonly rangeBetweenBuffer;
constructor(ctx: DataModelContext<D, K>);
resolveMissingDataCount(scope: ScopeProvider): number;
resolveProcessedDataDefById(scope: ScopeProvider, searchId: string): ProcessedDataDef | never;
resolveProcessedDataIndexById(scope: ScopeProvider, searchId: string): number;
resolveKeysById<T = string>(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): T[];
hasColumnById(scope: ScopeProvider, searchId: string): boolean;
resolveColumnById<T = any>(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): T[];
resolveColumnNeedsValueOf(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): boolean;
/**
* Converts a relative datum index to an absolute column index.
*
* INDEXING STRATEGY:
* - Relative index: Offset from the start of a group (stored in datumIndices)
* - Absolute index: Position in the full column array
* - Conversion: absoluteIndex = groupIndex + relativeIndex
*
* When groupsUnique=true, relativeIndex is always 0, making this a simple
* identity mapping. This optimization reduces memory usage significantly
* for large datasets with unique keys.
*
* @param groupIndex index of the group in ProcessedData.groups
* @param relativeDatumIndex relative index stored in group.datumIndices
* @returns absolute index for accessing columns
*/
resolveAbsoluteIndex(groupIndex: number, relativeDatumIndex: number): number;
getDomain(scope: ScopeProvider, searchId: string, type: PropertyDefinition<any>['type'], processedData: ProcessedData<K>): any[] | [number, number] | [];
getDomainBetweenRange(scope: ScopeProvider, searchIds: string[], [i0, i1]: [number, number], processedData: ProcessedData<K>): [number, number];
private getSortOrder;
getKeySortOrder(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrder;
getKeySortEntry(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrderEntry | undefined;
getColumnSortOrder(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrder;
private getDomainsByType;
}

View File

@@ -0,0 +1,26 @@
import type { PropertyId, PropertySelectors } from '../../dataModelTypes';
import type { DataModelContext } from '../dataModelContext';
/**
* ScopeCacheManager manages the scope cache for property definitions.
*
* SCOPE CACHE RESPONSIBILITIES:
* - Maintains mapping of property IDs to definitions per scope
* - Validates uniqueness of property IDs within each scope
* - Provides lookup functions for keys, values, and aggregates
* - Handles property ID resolution with dot notation support
*
* SCOPE CONCEPT:
* Scopes allow multiple data sources (series) to use the same DataModel
* while maintaining separate property definitions. Each scope has its own
* namespace for property IDs.
*/
export declare class ScopeCacheManager<K extends string> {
private readonly ctx;
constructor(ctx: DataModelContext<any, K>);
processScopeCache(): void;
valueGroupIdxLookup({ matchGroupIds }: PropertySelectors): number[];
valueIdxLookup(scopes: string[] | undefined, prop: PropertyId<string>): number[];
buildAccessors(defs: Iterable<{
property: string;
}>): Map<string, (d: any) => any>;
}

View File

@@ -0,0 +1,281 @@
/**
* Represents a splice operation to be applied to an array.
* Designed for direct use with Array.splice() for efficient mutations.
*/
export interface SpliceOperation {
/** Index at which to start the splice (in current array state) */
index: number;
/** Number of elements to delete from this position */
deleteCount: number;
/** Number of new elements to insert at this position */
insertCount: number;
/** Optional source indices for preserved elements (for tracking) */
sourceIndices?: number[];
}
/**
* Tracks transformations from original array indices to final array indices.
* Optimized for efficient array mutations using splice operations.
*/
export interface IndexTransformationMap {
/** Original array length before any transactions */
originalLength: number;
/** Final array length after all transactions */
finalLength: number;
/**
* Splice operations to transform the array.
* Applied from back to front to avoid index shifting issues.
* Each operation represents a single splice call.
*/
spliceOps: SpliceOperation[];
/** Set of removed original indices */
removedIndices: Set<number>;
/** Set of updated final indices (indices in the final array that have updated items) */
updatedIndices: Set<number>;
/** Total number of prepended items */
totalPrependCount: number;
/** Total number of appended items */
totalAppendCount: number;
}
/**
* Helper functions to compute derived properties from IndexTransformationMap.
* These avoid storing redundant computed values and enable optimization checks.
*/
/**
* Check if the change is append-only (no prepends, no removals).
*
* **Optimization:** Append-only operations don't shift existing indices,
* so preserved items don't need to be marked as "moved".
*
* @param indexMap - The index transformation map to check
* @returns True if only appends occurred
*
* @example
* ```typescript
* if (isAppendOnly(changeDesc.indexMap)) {
* // Skip tracking moved items - nothing moved
* }
* ```
*/
export declare function isAppendOnly(indexMap: IndexTransformationMap): boolean;
/**
* Check if the change is prepend-only (no appends, no removals).
*
* **Optimization:** Prepend-only operations shift ALL existing items by a fixed offset,
* so we can bulk-mark them as moved without iteration.
*
* @param indexMap - The index transformation map to check
* @returns True if only prepends occurred
*
* @example
* ```typescript
* if (isPrependOnly(changeDesc.indexMap)) {
* // All items shifted by totalPrependCount
* markAllAsMoved(changeDesc.indexMap.totalPrependCount);
* }
* ```
*/
export declare function isPrependOnly(indexMap: IndexTransformationMap): boolean;
/**
* Check if no items were removed (may have prepends/appends).
*
* **Optimization:** When no removals occurred, items are either:
* - In their original positions (if no prepends)
* - Shifted by a fixed offset (if prepends occurred)
*
* @param indexMap - The index transformation map to check
* @returns True if no removals occurred
*
* @example
* ```typescript
* if (hasNoRemovals(changeDesc.indexMap) && indexMap.totalPrependCount > 0) {
* // All original items shifted by prepend count
* }
* ```
*/
export declare function hasNoRemovals(indexMap: IndexTransformationMap): boolean;
/** Check if only in-place updates occurred (no adds/removes). */
export declare function isUpdateOnly(indexMap: IndexTransformationMap): boolean;
/**
* Check if only removals occurred (no prepends, appends, or other insertions).
*
* **Optimization:** Removal-only operations preserve sort order and uniqueness:
* - Removing values can't create duplicates (only reduce them)
* - Removing values can't change ascending to descending order
* - KEY_SORT_ORDERS metadata can be preserved
*/
export declare function hasOnlyRemovals(indexMap: IndexTransformationMap): boolean;
/** Check if all removals are contiguous starting at index 0. */
export declare function hasContiguousRemovalsAtStart(indexMap: IndexTransformationMap): boolean;
/** Check for rolling window pattern: contiguous removals at start + appends at end. */
export declare function isRollingWindow(indexMap: IndexTransformationMap): boolean;
/**
* Abstract description of changes to be applied to source data.
* Provides precise index mapping for optimized incremental updates.
*
* **Responsibilities:**
* - Describes what changed (splice operations, index mappings, prepend/append counts)
* - Provides transformation methods for applying changes to arrays
* - Enables iteration over preserved/moved elements
*
* **Usage Pattern:**
* 1. DataSet builds DataChangeDescription from pending transactions
* 2. DataController passes it to DataModel for incremental updates
* 3. DataModel uses methods below to transform keys, columns, and invalidity arrays
*
* **Design Note:**
* This class intentionally separates "change description" from "transaction management" (DataSet)
* and "data processing" (DataModel). This enables:
* - Multiple consumers (DataController, DataModel)
* - Independent testing
* - Clear separation of concerns
*/
export declare class DataChangeDescription {
/**
* Map from original to final indices.
*
* Contains splice operations, removed indices, and counts needed for transformations.
* Access directly for low-level operations (e.g., updating banded domains).
*/
readonly indexMap: IndexTransformationMap;
private readonly prependValues;
private readonly appendValues;
private readonly insertionValues;
constructor(indexMap: IndexTransformationMap, insertions: {
prependValues: unknown[];
appendValues: unknown[];
insertionValues: unknown[];
});
/**
* Get all indices that were removed from the original array, sorted ascending.
*
* @returns Array of removed indices (e.g., [2, 5, 8])
*
* @example
* ```typescript
* const removed = changeDesc.getRemovedIndices();
* console.log(`Removed ${removed.length} items at indices: ${removed}`);
* ```
*/
getRemovedIndices(): number[];
/**
* Get all indices that were updated in the final array, sorted ascending.
*
* @returns Array of updated indices (e.g., [1, 3, 7])
*
* @example
* ```typescript
* const updated = changeDesc.getUpdatedIndices();
* console.log(`Updated ${updated.length} items at indices: ${updated}`);
* ```
*/
getUpdatedIndices(): number[];
/**
* Iterate over preserved elements, mapping source index to destination index.
* Only calls callback for elements that were NOT removed.
*
* **Use this for:**
* - Tracking which elements moved (when sourceIndex !== destIndex)
* - Generating diff metadata (added/removed/moved items)
* - Understanding index shifts caused by prepends/removes
*
* @param callback - Called for each preserved element with (sourceIndex, destIndex)
*
* @example Detecting moved items
* ```typescript
* const movedItems = new Set<number>();
* changeDesc.forEachPreservedIndex((srcIdx, destIdx) => {
* if (srcIdx !== destIdx) {
* movedItems.add(destIdx);
* }
* });
* ```
*/
forEachPreservedIndex(callback: (sourceIndex: number, destIndex: number) => void): void;
/**
* Get the values that were prepended to the beginning of the array.
*
* These values are stored during change description construction and can be used
* to avoid reprocessing prepended data.
*
* @returns Array of prepended values in order
*
* @example Processing prepended data
* ```typescript
* const prependedData = changeDesc.getPrependedValues<DataRow>();
* for (const row of prependedData) {
* processRow(row);
* }
* ```
*/
getPrependedValues<T = unknown>(): T[];
/**
* Get the values that were appended to the end of the array.
*
* These values are stored during change description construction and can be used
* to avoid reprocessing appended data.
*
* @returns Array of appended values in order
*
* @example Processing appended data
* ```typescript
* const appendedData = changeDesc.getAppendedValues<DataRow>();
* for (const row of appendedData) {
* processRow(row);
* }
* ```
*/
getAppendedValues<T = unknown>(): T[];
/**
* Get the values that were inserted at arbitrary indices.
*
* These values are stored during change description construction and can be used
* to avoid reprocessing inserted data.
*
* @returns Array of insertion values in the order they appear in splice operations
*
* @example Processing inserted data
* ```typescript
* const insertedData = changeDesc.getInsertionValues<DataRow>();
* for (const row of insertedData) {
* processRow(row);
* }
* ```
*/
getInsertionValues<T = unknown>(): T[];
/**
* Applies the transformation to an array in-place using native Array operations.
* This is a zero-copy operation that mutates the array directly.
*
* **Use this for:**
* - Transforming processed data arrays (keys, columns, invalidity)
* - Applying prepends, removals, and appends in a single pass
* - Maintaining synchronization between data and processed arrays
*
* **How it works:**
* 1. Applies splice operations in order (prepends, removals, appends)
* 2. Calls processInsertion callback for each inserted element
* 3. Mutates the array in-place for zero-copy efficiency
*
* @param array - The array to transform in-place (will be mutated)
* @param processInsertion - Callback to generate values for inserted indices
*
* @example Transforming a column array
* ```typescript
* // Transform processed column to match new data layout
* const insertionCache = new Map(); // Pre-computed processed values
* changeDesc.applyToArray(columnArray, (destIndex) => {
* return insertionCache.get(destIndex) ?? defaultValue;
* });
* ```
*
* @example Transforming an invalidity array
* ```typescript
* // Transform invalidity flags to match new data
* changeDesc.applyToArray(invalidityArray, (destIndex) => {
* const cached = insertionCache.get(destIndex);
* return cached?.hasInvalidKey ?? false;
* });
* ```
*/
applyToArray<T>(array: T[], processInsertion: (destIndex: number) => T, onRemove?: (removed: T[], op: SpliceOperation) => void): void;
}

View File

@@ -0,0 +1,33 @@
import type { EventsHub } from '../../core/eventsHub';
import type { ChartMode } from '../chartMode';
import { type CachedData } from './caching';
import { DataModel, type DataModelOptions, type ProcessedData } from './dataModel';
import type { DataSet } from './dataSet';
type Result<D extends object, K extends keyof D & string = keyof D & string, G extends boolean | undefined = undefined> = {
processedData: ProcessedData<D>;
dataModel: DataModel<D, K, G>;
};
/** Implements cross-series data model coordination. */
export declare class DataController {
private readonly mode;
readonly suppressFieldDotNotation: boolean;
private readonly eventsHub;
private readonly debug;
private readonly requested;
private status;
constructor(mode: ChartMode, suppressFieldDotNotation: boolean, eventsHub: EventsHub | undefined);
request<D extends object, K extends keyof D & string = keyof D & string, G extends boolean | undefined = undefined>(id: string, dataSet: DataSet<D>, opts: DataModelOptions<K, any, false>): Promise<Result<D, K, G>>;
execute(cachedData?: CachedData): CachedData;
private validateRequests;
private mergeRequested;
private static groupMatch;
private static readonly crossScopeMergableTypes;
private static mergeRequests;
private static mergeIdsMap;
private static createIdsMap;
static readonly skipKeys: Set<string>;
static deepEqual<T>(a: T, b: T): boolean;
/** JSON replacer for serializing non-JSON-serializable objects like Map and Set */
private static jsonReplacer;
}
export {};

View File

@@ -0,0 +1,147 @@
import { type BandLike, BandedStructure, type BandedStructureConfig } from './data-model/utils/bandedStructure';
export interface IDataDomain<D = any> {
extend(val: any): void;
getDomain(): D[];
}
type SortOrder = 1 | -1;
export declare class DiscreteDomain implements IDataDomain {
private readonly domain;
private readonly dateTimestamps;
private hasDateValues;
private sortedValues;
private sortOrder;
private isSortedUnique;
static is(value: unknown): value is DiscreteDomain;
/**
* Configure domain for sorted unique mode.
* When enabled, uses a single array for O(1) append.
* Call this before extending with data.
*/
setSortedUniqueMode(sortOrder: 1 | -1, isUnique: boolean): void;
extend(val: any): void;
getDomain(): unknown[];
/** Returns true if this domain contains Date values stored as timestamps */
isDateDomain(): boolean;
/** Returns true if this domain is in sorted unique mode */
isSortedUniqueMode(): boolean;
/** Returns the sort order if in sorted mode, undefined otherwise */
getSortOrder(): SortOrder | undefined;
/** Merges another DiscreteDomain's values into this one */
mergeFrom(other: DiscreteDomain): void;
/** Converts from sorted array mode to Set mode (one-way transition) */
private convertToSetMode;
}
export declare class ContinuousDomain<T extends number | Date> implements IDataDomain<T> {
private domain;
static is<T extends number | Date = any>(value: unknown): value is ContinuousDomain<T>;
static extendDomain(values: unknown[], domain?: [number, number]): [number, number];
extend(value: T): void;
getDomain(): T[];
}
/**
* Represents a single band within a BandedDomain.
* Each band maintains its own sub-domain for a range of data indices.
*/
interface DomainBand<T> extends BandLike {
/** The sub-domain for values in this band */
subDomain: IDataDomain<T>;
}
/**
* Configuration options for BandedDomain optimization.
* Extends BandedStructureConfig for backward compatibility.
*/
export interface BandedDomainConfig extends BandedStructureConfig {
}
/**
* A domain implementation that divides data into bands for efficient incremental updates.
* Each band maintains its own sub-domain, allowing targeted updates without full rescans.
*
* Banding trades memory for performance. Each band maintains its own sub-domain,
* so memory usage scales with targetBandCount × number of properties.
* For datasets < minDataSizeForBanding, banding is disabled to avoid overhead.
*/
export declare class BandedDomain<T = any> extends BandedStructure<DomainBand<T>> implements IDataDomain<T> {
private readonly domainFactory;
private fullDomainCache;
private readonly isDiscrete;
private sortOrder;
private isUnique;
constructor(domainFactory: () => IDataDomain<T>, config?: BandedDomainConfig, isDiscrete?: boolean);
/**
* Set sort order metadata from KEY_SORT_ORDERS.
* When data is sorted and unique, enables fast array concatenation in getDomain().
*/
setSortOrderMetadata(sortOrder: 1 | -1 | undefined, isUnique: boolean): void;
/**
* Creates a new domain band with its own sub-domain instance.
* Configures sub-domain for sorted mode if applicable.
*/
protected createBand(startIndex: number, endIndex: number): DomainBand<T>;
/**
* Initializes bands and clears the full domain cache.
*/
initializeBands(dataSize: number): void;
/**
* Handles insertion and clears the full domain cache.
*/
handleInsertion(insertIndex: number, insertCount: number): void;
/**
* Handles removal and clears the full domain cache.
*/
handleRemoval(removeIndex: number, removeCount: number): void;
/**
* Split an oversized band into two smaller bands.
* Override to handle band splitting for large datasets where banding is beneficial.
*/
protected splitBand(bandIndex: number, idealSize: number): void;
/**
* Marks bands as dirty that need rescanning.
*/
markBandsDirty(startIndex: number, endIndex: number): void;
/**
* Marks all bands as dirty, forcing a full rescan.
*/
private markAllBandsDirty;
/**
* Extends the domain with values from specified bands.
* This is called after dirty bands have been rescanned.
*/
extendBandsFromData(data: T[], invalidData?: boolean[]): void;
/**
* Gets the bands that need rescanning.
*/
getDirtyBands(): DomainBand<T>[];
/**
* Standard IDataDomain interface - extends domain with a single value.
* Note: This is less efficient than batch operations with bands.
*/
extend(_value: T): void;
/**
* Check if all sub-domains support fast sorted concatenation.
*/
private canUseSortedConcatenation;
/**
* Concatenate sorted domains efficiently.
* Only valid when canUseSortedConcatenation() returns true.
*/
private concatenateSortedDomains;
/**
* Deduplicate nulls and Invalid Dates in a domain result array.
* These represent invalid data and may appear from multiple bands.
*/
private deduplicateNulls;
/**
* Combines all band sub-domains to get the overall domain.
*/
getDomain(): T[];
/**
* Returns statistics about the banded domain for debugging.
*/
getStats(): {
bandCount: number;
dirtyBandCount: number;
averageBandSize: number;
dataSize: number;
};
}
export {};

View File

@@ -0,0 +1,162 @@
import { type DomainWithMetadata } from 'ag-charts-core';
import type { EventsHub } from '../../core/eventsHub';
import type { ChartMode } from '../chartMode';
import type { DataGroup, DataModelOptions, GroupDatumIteratorOutput, GroupedData, ProcessedData, ProcessedDataDef, PropertyDefinition, ScopeProvider, UngroupedData } from './dataModelTypes';
import type { DataChangeDescription, DataSet } from './dataSet';
import { type SortOrder } from './sortOrder';
export * from './dataModelTypes';
export { fixNumericExtent, getMissCount, datumKeys, getPathComponents, NULL_KEY_STRING, UNDEFINED_KEY_STRING, } from './data-model/utils/helpers';
/**
* Transforms raw chart data into a structured, renderable format for series visualization.
*
* The DataModel is responsible for processing data through a multi-stage pipeline:
* - Extracting and validating data from input datasets
* - Grouping data by keys (for categorical axes or stacked series)
* - Computing aggregations (sum, average, etc.) for grouped data
* - Calculating domains (value ranges) for axes and scales
* - Managing scoped data processing for multi-series charts
* - Supporting incremental updates when data changes
*
* The class orchestrates several specialized subsystems:
* - DataExtractor: Extracts and validates raw data
* - DataGrouper: Groups data by keys
* - Aggregator: Computes aggregations over grouped data
* - DomainManager: Calculates and maintains value domains
* - IncrementalProcessor: Handles efficient data updates
*
* Performance optimizations:
*
* 1. SHARED MEMORY OPTIMIZATION (groupsUnique=true):
* When each datum has unique keys, all groups share the same datumIndices array
* containing [0], since each datum's relative offset from its group is always 0.
*
* 2. BANDED DOMAIN PROCESSING:
* Large datasets are divided into bands for efficient domain calculation.
* Only dirty bands are recalculated during incremental updates.
*
* 3. BATCH MERGING:
* Column batches with identical characteristics (keys, invalidity) are merged
* to reduce processing overhead.
*
* 4. INCREMENTAL REPROCESSING:
* When supported, only changed data is reprocessed instead of full recalculation.
*/
export declare class DataModel<D extends object, K extends keyof D & string = keyof D & string, Grouped extends boolean | undefined = undefined> {
private readonly opts;
private readonly mode;
private readonly suppressFieldDotNotation;
private readonly eventsHub?;
private readonly debug;
private readonly scopeCache;
private readonly keys;
private readonly values;
private readonly resolvers;
private readonly scopeCacheManager;
private readonly domainInitializer;
private readonly domainManager;
private readonly reducerManager;
private readonly dataExtractor;
private readonly dataGrouper;
private readonly aggregator;
private readonly incrementalProcessor;
private readonly aggregates;
private readonly groupProcessors;
private readonly propertyProcessors;
private readonly reducers;
private readonly processors;
constructor(opts: DataModelOptions<K, Grouped, true>, mode?: ChartMode, suppressFieldDotNotation?: boolean, eventsHub?: EventsHub | undefined);
resolveProcessedDataDefById(scope: ScopeProvider, searchId: string): ProcessedDataDef | never;
resolveProcessedDataIndexById(scope: ScopeProvider, searchId: string): number;
resolveKeysById<T = string>(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): T[];
hasColumnById(scope: ScopeProvider, searchId: string): boolean;
resolveColumnById<T = any>(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): T[];
resolveColumnNeedsValueOf(scope: ScopeProvider, searchId: string, processedData: UngroupedData<any> | GroupedData<any>): boolean;
resolveMissingDataCount(scope: ScopeProvider): number;
/**
* Provides a convenience iterator to iterate over all of the extract datum values in a
* specific DataGroup.
*
* @param scope to which datums should belong
* @param group containing the datums
* @param processedData containing the group
* @param groupIndex index of the group in processedData.groups
*/
forEachDatum(scope: ScopeProvider, processedData: GroupedData<any>, group: DataGroup, groupIndex: number): Generator<any, void, unknown>;
private getUniqueDataSets;
/**
* Provides a convenience iterator to iterate over all of the extracted datum values in a
* GroupedData.
*
* @param scope to which datums should belong
* @param processedData to iterate through
*/
forEachGroupDatum(scope: ScopeProvider, processedData: GroupedData<any>): Generator<GroupDatumIteratorOutput, void, unknown>;
getDomain(scope: ScopeProvider, searchId: string, type: PropertyDefinition<any>['type'], processedData: ProcessedData<K>): DomainWithMetadata<any>;
getDomainBetweenRange(scope: ScopeProvider, searchIds: string[], [i0, i1]: [number, number], processedData: ProcessedData<K>): [number, number];
getKeySortOrder(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrder;
getColumnSortOrder(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): SortOrder;
/**
* Get sort metadata for a key column if available.
* Returns undefined if metadata is not available, is dirty, or data is unsorted.
*/
getKeySortMetadata(scope: ScopeProvider, searchId: string, processedData: ProcessedData<K>): {
sortOrder: 1 | -1 | undefined;
isUnique?: boolean;
} | undefined;
processData(sources: Map<string, DataSet<unknown>>): (Grouped extends true ? GroupedData<D> : UngroupedData<D>) | undefined;
/**
* Determines if incremental reprocessing is supported for the given data.
*
* Reprocessing is supported when:
* - For ungrouped data: No aggregates, reducers, processors, or property processors
* - For grouped data: Additionally requires:
* - groupsUnique=true (each datum has unique keys)
* - Single data source (all scopes share same DataSet)
* - No invalid keys (to maintain groups.length === columns.length invariant)
* - All group processors support reprocessing
*
* When unsupported, falls back to full reprocessing automatically.
*
* @returns true if incremental reprocessing can be used, false otherwise
*/
isReprocessingSupported(processedData: ProcessedData<D>): boolean;
reprocessData(processedData: ProcessedData<D>, dataSets?: Map<DataSet<any>, DataChangeDescription | undefined>): ProcessedData<D>;
/**
* Recomputes domains from transformed arrays.
* Uses BandedDomain optimization for continuous domains to avoid full rescans.
*/
private recomputeDomains;
private warnDataMissingProperties;
private processScopeCache;
private valueGroupIdxLookup;
private valueIdxLookup;
private extractData;
/**
* Reprocesses group processors for incremental updates.
* Only processes newly inserted groups to avoid double-processing.
* This is safe only when all group processors support reprocessing.
* Deduplicates change descriptions to avoid processing the same groups multiple times
* when multiple scopes share the same DataSet.
*/
private reprocessGroupProcessors;
private postProcessProperties;
private reduceData;
private shouldUseReducerBanding;
private reduceWithBands;
private reduceStandard;
private postProcessData;
private initDataDomainProcessor;
/**
* Collects optimization metadata for debugging purposes.
* Only called when debug mode is enabled.
*/
private collectOptimizationMetadata;
/**
* Collects reducer banding metadata for debugging purposes.
* Tracks which reducers used banding and their performance stats.
*/
private collectReducerBandingMetadata;
buildAccessors(defs: Iterable<{
property: string;
}>): Map<string, (d: any) => any>;
}

View File

@@ -0,0 +1,296 @@
import type { BandedReducer } from './data-model/reducers/bandedReducer';
import type { DataChangeDescription } from './dataChangeDescription';
import type { BandedDomain, BandedDomainConfig } from './dataDomain';
import type { DataSet } from './dataSet';
import type { RangeLookup } from './rangeLookup';
import type { SortOrder } from './sortOrder';
export interface ScopeProvider {
id: string;
}
export interface DataGroup {
keys: any[];
datumIndices: readonly (readonly number[])[];
aggregation: any[][];
validScopes: Set<ScopeId>;
}
export interface UngroupedDataItem<I, D, V> {
index: I;
keys: any[];
values: V;
aggValues?: [number, number][];
datum: D;
validScopes?: Set<string>;
}
export declare const KEY_SORT_ORDERS: unique symbol;
export declare const COLUMN_SORT_ORDERS: unique symbol;
export declare const DOMAIN_RANGES: unique symbol;
export declare const DOMAIN_BANDS: unique symbol;
export declare const REDUCER_BANDS: unique symbol;
export interface BandedReducerStats extends Record<string, number> {
totalBands: number;
dirtyBands: number;
dataSize: number;
scanRatio: number;
cacheHits: number;
}
export declare const SHARED_ZERO_INDICES: readonly number[];
export type ScopeId = string;
export type ProcessedValue = {
value: unknown;
missing: boolean;
valid: boolean;
};
export type SortOrderEntry = {
sortOrder: SortOrder;
isUnique?: boolean;
isDirty?: boolean;
};
export type ProcessedValueEntry = {
value: any;
valid: boolean;
};
export interface GroupDatumIteratorOutput {
group: DataGroup;
groupIndex: number;
columnIndex: number;
datumIndex: number;
}
export type InsertionCacheValue = {
keys: Map<number, ProcessedValueEntry>;
values: Map<number, ProcessedValueEntry>;
hasInvalidKey: boolean;
hasInvalidValue: boolean;
};
export type InsertionCache = Map<number, InsertionCacheValue>;
export type ColumnBatch = [ScopeId, number[], unknown[][], Set<ScopeId>, boolean[] | undefined, boolean[] | undefined];
export type MergedColumnBatch = [
ScopeId[],
number[],
unknown[][],
Set<ScopeId>,
boolean[] | undefined,
boolean[] | undefined
];
export interface CommonMetadata<D> {
input: {
count: number;
};
scopes: Set<ScopeId>;
dataSources: Map<ScopeId, DataSet<unknown>>;
invalidKeys: Map<ScopeId, boolean[]> | undefined;
invalidKeyCount: Map<ScopeId, number> | undefined;
invalidData: Map<ScopeId, boolean[]> | undefined;
invalidDataCount: Map<ScopeId, number> | undefined;
keys: Map<ScopeId, unknown[]>[];
columns: any[][];
columnScopes: Set<ScopeId>[];
columnNeedValueOf?: boolean[];
domain: {
keys: any[][];
values: any[][];
groups?: any[][];
aggValues?: [number, number][];
};
reduced?: {
diff?: Record<string, ProcessedOutputDiff>;
smallestKeyInterval?: number;
largestKeyInterval?: number;
sortedGroupDomain?: any[][];
animationValidation?: {
uniqueKeys: boolean;
orderedKeys: boolean;
};
};
defs: {
keys: (Scoped & DatumPropertyDefinition<keyof D>)[];
values: (Scoped & DatumPropertyDefinition<keyof D>)[];
allScopesHaveSameDefs: boolean;
};
partialValidDataCount: number;
time: number;
/** Monotonically increasing version counter, incremented on every processing cycle */
version: number;
optimizations?: OptimizationMetadata;
[DOMAIN_RANGES]: Map<string, RangeLookup>;
[KEY_SORT_ORDERS]: Map<number, SortOrderEntry>;
[COLUMN_SORT_ORDERS]: Map<number, SortOrderEntry>;
[DOMAIN_BANDS]: Map<InternalDatumPropertyDefinition<any>, BandedDomain>;
[REDUCER_BANDS]?: Map<ReducerBandKey, BandedReducer>;
changeDescription?: DataChangeDescription;
}
export interface UngroupedData<D> extends CommonMetadata<D> {
type: 'ungrouped';
aggregation?: [number, number][][];
}
export interface GroupedData<D> extends CommonMetadata<D> {
type: 'grouped';
groups: DataGroup[];
groupsUnique: boolean;
}
export type ProcessedOutputDiff = {
changed: boolean;
added: Set<string>;
updated: Set<string>;
removed: Set<string>;
moved: Set<string>;
};
export interface ProcessedDataDef {
index: number;
def: PropertyDefinition<any>;
}
export type ProcessedData<D> = UngroupedData<D> | GroupedData<D>;
/** Metadata about applied/skipped optimizations for debugging */
export interface OptimizationMetadata {
/** Was reprocessing path used? */
reprocessing?: {
applied: boolean;
reason?: string;
};
/** Domain banding optimization per definition */
domainBanding?: {
keyDefs: Array<{
property: string;
applied: boolean;
reason?: string;
stats?: {
totalBands: number;
dirtyBands: number;
dataSize: number;
scanRatio: number;
};
}>;
valueDefs: Array<{
property: string;
applied: boolean;
reason?: string;
stats?: {
totalBands: number;
dirtyBands: number;
dataSize: number;
scanRatio: number;
};
}>;
};
/** Shared datum indices optimization (grouped data only) */
sharedDatumIndices?: {
applied: boolean;
sharedGroupCount: number;
totalGroupCount: number;
};
/** Batch merging optimization */
batchMerging?: {
originalBatchCount: number;
mergedBatchCount: number;
mergeRatio: number;
};
/** Reducer banding optimization */
reducerBanding?: {
reducers: Array<{
property: string;
applied: boolean;
reason?: string;
stats?: BandedReducerStats;
}>;
};
/** Overall performance metrics */
performance?: {
processingTime: number;
pathTaken: 'full-process' | 'reprocess';
};
}
export type DatumPropertyType = 'range' | 'category';
export type MissMap = Map<string, number>;
export type GroupingFn<K> = (keys: unknown[]) => K[];
export type GroupByFn = (extractedData: UngroupedData<any>) => GroupingFn<any>;
export type DataModelOptions<K, Grouped extends boolean | undefined, IsScoped extends boolean = true> = {
props: PropertyDefinition<K, IsScoped>[];
groupByKeys?: Grouped;
groupByData?: Grouped;
groupByFn?: GroupByFn;
domainBandingConfig?: BandedDomainConfig;
};
export type PropertyDefinition<K, IsScoped = false> = (DatumPropertyDefinition<K> & (IsScoped extends true ? Scoped : unknown)) | AggregatePropertyDefinition<any, any, any> | (PropertyValueProcessorDefinition<any> & (IsScoped extends true ? Scoped : unknown)) | GroupValueProcessorDefinition<any, any> | ReducerOutputPropertyDefinition<any> | ProcessorOutputPropertyDefinition<any>;
export type ProcessorFn = (datum: unknown, index: number) => unknown;
export type PropertyId<K extends string> = K | {
id: string;
};
export type Scoped = {
/** Scope(s) a property definition belongs to (typically the defining entities unique identifier). */
scopes: ScopeId[];
};
export type PropertyIdentifiers = {
id?: string;
/** Map<Scope, Set<Id>> */
idsMap?: Map<string, Set<string>>;
/** Optional group a property belongs to, for cross-scope combination. */
groupId?: string;
};
export type PropertySelectors = {
/** Optional group a property belongs to, for cross-scope combination. */
matchGroupIds?: string[];
};
export type DatumPropertyDefinition<K> = PropertyIdentifiers & {
type: 'key' | 'value';
valueType: DatumPropertyType;
property: K;
forceValue?: any;
includeProperty?: boolean;
invalidValue?: any;
missing?: MissMap;
missingValue?: any;
separateNegative?: boolean;
validation?: (value: any, datum: any, index: number) => boolean;
processor?: () => ProcessorFn;
allowNullKey?: boolean;
};
export type InternalDefinition<IsScoped extends boolean> = {
index: number;
} & (IsScoped extends true ? Scoped : unknown);
export type InternalDatumPropertyDefinition<K> = DatumPropertyDefinition<K> & InternalDefinition<true> & {
missing: MissMap;
};
export type AggregatePropertyDefinition<D, K extends keyof D & string, R = [number, number], R2 = R> = Omit<PropertyIdentifiers, 'scopes'> & PropertySelectors & {
type: 'aggregate';
aggregateFunction: (values: D[K][], keys?: D[K][]) => R;
groupAggregateFunction?: (next?: R, acc?: R2) => R2;
finalFunction?: (result: R2) => [number, number];
};
export type GroupValueAdjustFn<D, K extends keyof D & string> = (columns: D[K][][], indexes: number[], dataGroup: DataGroup, groupIndex: number) => void;
export type GroupValueProcessorDefinition<D, K extends keyof D & string> = PropertyIdentifiers & PropertySelectors & {
type: 'group-value-processor';
/**
* Outer function called once per all data processing; inner function called once per group;
* innermost called once per datum.
*/
adjust: () => () => GroupValueAdjustFn<D, K>;
/**
* Indicates whether this processor supports incremental reprocessing.
* When true, the processor can safely be reapplied to modified data without
* causing double-processing issues.
*/
supportsReprocessing?: boolean;
};
export type PropertyValueAdjustFn<D> = (processedData: ProcessedData<D>, valueIndex: number) => void;
export type PropertyValueProcessorDefinition<D> = PropertyIdentifiers & {
type: 'property-value-processor';
property: string;
adjust: () => PropertyValueAdjustFn<D>;
};
export type ReducerOutputTypes = NonNullable<UngroupedData<any>['reduced']>;
export type ReducerOutputKeys = keyof ReducerOutputTypes;
export type ReducerBandKey = Extract<ReducerOutputKeys, string>;
export type ReducerOutputPropertyDefinition<P extends ReducerOutputKeys = ReducerOutputKeys> = PropertyIdentifiers & {
type: 'reducer';
property: P;
initialValue?: ReducerOutputTypes[P];
reducer: () => (acc: ReducerOutputTypes[P], keys: unknown[]) => ReducerOutputTypes[P];
supportsBanding?: boolean;
combineResults?: (bandResults: ReducerOutputTypes[P][]) => ReducerOutputTypes[P];
needsOverlap?: boolean;
};
export type ProcessorOutputPropertyDefinition<P extends ReducerOutputKeys = ReducerOutputKeys> = PropertyIdentifiers & {
type: 'processor';
property: P;
calculate: (data: ProcessedData<any>, previousValue: ReducerOutputTypes[P] | undefined) => ReducerOutputTypes[P];
incrementalCalculate?: (data: ProcessedData<any>, previousValue: ReducerOutputTypes[P] | undefined) => ReducerOutputTypes[P];
};

View File

@@ -0,0 +1,41 @@
import type { AgDataSourceCallbackParams } from 'ag-charts-types';
import type { EventsHub } from '../../core/eventsHub';
import type { AnimationManager } from '../interaction/animationManager';
type DataSourceCallback = (params: AgDataSourceCallbackParams<unknown>) => Promise<unknown>;
export interface DataServiceRestoredData {
params: AgDataSourceCallbackParams;
data: unknown;
}
export declare class DataService<D extends object> {
private readonly eventsHub;
private readonly caller;
private readonly animationManager;
dispatchOnlyLatest: boolean;
dispatchThrottle: number;
requestThrottle: number;
private dataSourceCallback?;
private isLoadingInitialData;
private isLoadingData;
private latestRequest?;
private freshRequests;
private requestCounter;
private pendingData;
private readonly debug;
private throttledFetch;
private throttledDispatch;
constructor(eventsHub: EventsHub, caller: {
readonly context?: unknown;
}, animationManager: AnimationManager);
updateCallback(dataSourceCallback: DataSourceCallback): void;
clearCallback(): void;
load(params: AgDataSourceCallbackParams): void;
isLazy(): boolean;
isLoading(): boolean;
getData(): Promise<DataServiceRestoredData | undefined>;
restoreData(data: DataServiceRestoredData): void;
private createThrottledFetch;
private createThrottledDispatch;
private dispatch;
private fetch;
}
export {};

View File

@@ -0,0 +1,104 @@
import { DataChangeDescription } from './dataChangeDescription';
export { DataChangeDescription } from './dataChangeDescription';
/**
* Encapsulates a single transaction to be applied to a DataSet.
* Supports both the AG Grid-compatible API (add/addIndex) and the internal format (prepend/append/insertions).
*/
export interface DataSetTransaction<T> {
/** Items to add at the specified index (AG Grid-compatible API). */
add?: T[];
/** Zero-based index for add operation. If undefined or >= length, items are appended. */
addIndex?: number;
/** Items to prepend to the beginning (internal format, converted from add with addIndex=0). */
prepend?: T[];
/** Items to append to the end (internal format, converted from add with no addIndex). */
append?: T[];
/** Items to remove by referential equality. */
remove?: T[];
/** Items to update by referential equality. Items should be mutated in place before calling update. */
update?: T[];
/** Arbitrary insertions at specific indices (internal format, converted from add with 0 < addIndex < length). */
insertions?: Array<{
index: number;
items: T[];
}>;
}
/**
* Manages transactional updates to an array of data with optimized batch processing.
* Transactions are queued and can be applied in batch for efficient data transformations.
*/
export declare class DataSet<T = unknown> {
data: T[];
private pendingTransactions;
private cachedChangeDescription;
private itemToIndexCache;
constructor(data: T[]);
/**
* Creates an empty DataSet.
*/
static empty<U = unknown>(): DataSet<U>;
/**
* Wraps existing data in a DataSet.
*/
static wrap<U = unknown>(data: U[]): DataSet<U>;
netSize(): number;
/**
* Queues a transaction (applied on commit).
* Normalizes AG Grid-compatible format (add/addIndex) to internal format (prepend/append).
*/
addTransaction(transaction: DataSetTransaction<T>): void;
/**
* @returns A deep clone of the DataSet.
*/
deepClone(): DataSet<T>;
/**
* Converts AG Grid-compatible transaction format to internal format.
* Maps `add` + `addIndex` to prepend, append, or arbitrary insertion based on the index.
*/
private normalizeTransaction;
hasPendingTransactions(): boolean;
getPendingTransactionCount(): number;
/** Applies all pending transactions to the data array. */
commitPendingTransactions(): boolean;
/** Updates item→index cache incrementally, or invalidates for complex changes. */
private updateItemToIndexCache;
clearPendingTransactions(): number;
getPendingTransactions(): DataSetTransaction<T>[];
/** Custom JSON serialization (avoids snapshot bloat). */
toJSON(): T[];
/** Builds a DataChangeDescription from pending transactions (does not modify data). */
getChangeDescription(): DataChangeDescription | undefined;
/**
* Helper method to remove items from a list of groups.
* Mutates the groups in-place and removes found items from toRemove set.
* @param groups List of groups to search and remove from
* @param toRemove Set of items to remove (modified in-place)
*/
private removeFromGroups;
/**
* Builds the index transformation map by sequentially applying all pending transactions.
* Optimized to:
* - Track operation boundaries instead of individual items
* - Only scan for values that are actually being removed
* - Stop scanning early when all removed values are found
* - Support arbitrary insertions at any index
* - Track updated items by referential equality
*/
private buildIndexMap;
private getSortedRemovedIndices;
private collectTransactionEffects;
private applyPrepends;
private applyInsertions;
private applyAppends;
private applyRemovals;
private applyUpdates;
private collectUpdatedIndicesFromGroups;
/** Lazy item→index map for O(1) lookups. */
private getItemToIndexMap;
private collectUpdatedOriginalIndices;
private removeFromTrackedInsertions;
private adjustLaterInsertionsAfterRemoval;
private buildSpliceOperations;
private countRemovalsBeforeIndex;
private resolveUpdatedIndices;
}

View File

@@ -0,0 +1,32 @@
import { type ScaleType } from 'ag-charts-core';
import { type DatumPropertyDefinition, type GroupValueProcessorDefinition, type ProcessedData, type ProcessorOutputPropertyDefinition, type PropertyValueProcessorDefinition, type ReducerOutputPropertyDefinition } from './dataModel';
export declare const MAX_ANIMATABLE_NODES = 1000;
export declare function processedDataIsAnimatable(processedData: ProcessedData<any>): boolean;
export declare function keyProperty<K>(propName: K, scaleType?: ScaleType, opts?: Partial<DatumPropertyDefinition<K>>): DatumPropertyDefinition<K>;
export declare function valueProperty<K>(propName: K, scaleType?: ScaleType, opts?: Partial<DatumPropertyDefinition<K>>): DatumPropertyDefinition<K>;
export declare function rowCountProperty<K>(propName: K, opts?: Partial<DatumPropertyDefinition<K>>): DatumPropertyDefinition<K>;
export declare function rangedValueProperty<K>(propName: K, opts?: Partial<DatumPropertyDefinition<K>> & {
min?: number;
max?: number;
}): DatumPropertyDefinition<K>;
export declare function accumulativeValueProperty<K>(propName: K, scaleType?: ScaleType, opts?: Partial<DatumPropertyDefinition<K>> & {
onlyPositive?: boolean;
}): DatumPropertyDefinition<K>;
export declare function trailingAccumulatedValueProperty<K>(propName: K, scaleType?: ScaleType, opts?: Partial<DatumPropertyDefinition<K>>): DatumPropertyDefinition<K>;
export declare function groupAccumulativeValueProperty<K>(propName: K, mode: 'normal' | 'trailing', opts: Partial<DatumPropertyDefinition<K>> & {
rangeId?: string;
groupId: string;
}, scaleType?: ScaleType): (GroupValueProcessorDefinition<any, any> | import("./dataModelTypes").AggregatePropertyDefinition<any, any> | DatumPropertyDefinition<K>)[];
export declare const SMALLEST_KEY_INTERVAL: ReducerOutputPropertyDefinition<'smallestKeyInterval'>;
export declare const LARGEST_KEY_INTERVAL: ReducerOutputPropertyDefinition<'largestKeyInterval'>;
export declare const SORT_DOMAIN_GROUPS: ProcessorOutputPropertyDefinition<'sortedGroupDomain'>;
export declare function normaliseGroupTo(matchGroupIds: string[], normaliseTo: number): GroupValueProcessorDefinition<any, any>;
export declare function normalisePropertyTo(property: string, normaliseTo: [number, number], zeroDomain: number, rangeMin?: number, rangeMax?: number): PropertyValueProcessorDefinition<any>;
export declare function animationValidation(valueKeyIds?: string[]): ProcessorOutputPropertyDefinition;
export declare function accumulateGroup(matchGroupId: string, mode: 'normal' | 'trailing', separateNegative?: boolean): GroupValueProcessorDefinition<any, any>;
export declare function diff(id: string, previousData: ProcessedData<any>, updateMovedData?: boolean): ProcessorOutputPropertyDefinition<'diff'>;
type KeyType = string | number | boolean | null | undefined;
export declare function createDatumId(key: number): number;
export declare function createDatumId(key: boolean): boolean;
export declare function createDatumId(...keys: KeyType[]): string;
export {};

View File

@@ -0,0 +1,45 @@
export declare class RangeLookup {
private maxLevelSize;
private buffer;
private dataLength;
/** When true, the lookup needs to be rebuilt before use */
isDirty: boolean;
constructor(allValues: number[][]);
private static computeMaxLevelSize;
private static createBuffer;
private populateBuffer;
/**
* Rebuild the segment tree with new values, reusing the buffer if possible.
* Only allocates a new buffer if the data length requires a different maxLevelSize.
*/
rebuild(allValues: number[][]): void;
/**
* Update values at a specific data index - O(k log n) where k is number of columns.
* After updating the leaf, propagates changes up to the root.
*
* @param dataIndex - Index in the data array (0-based)
* @param newValues - New values for this index from all columns
*/
updateValue(dataIndex: number, newValues: number[]): void;
/**
* Batch update multiple values - O(k log n) per update.
* More efficient than individual updateValue calls when tracking dirty nodes.
*
* @param updates - Array of {index, values} pairs to update
*/
updateValues(updates: Array<{
index: number;
values: number[];
}>): void;
/**
* Propagate min/max changes from a leaf up to the root.
* Each level recalculates its min/max from its children.
*/
private propagateUp;
private computeRangeInto;
rangeBetween(start: number, end: number, into?: [number, number]): [number, number];
getRange(into?: [number, number]): [number, number];
get range(): [number, number];
/** The number of data elements in the segment tree */
get length(): number;
}

View File

@@ -0,0 +1,2 @@
export type SortOrder = -1 | 1 | undefined;
export declare function valuesSortOrder(values: any[], needsValueOf: boolean): SortOrder;

View File

@@ -0,0 +1,15 @@
import { ModuleType } from 'ag-charts-core';
export type ChartType = 'cartesian' | 'polar' | 'topology' | 'standalone';
export interface ModulePlaceholder {
type: `${ModuleType}` | ModuleType;
name: string;
moduleId: string;
chartType?: ChartType;
enterprise?: boolean;
}
/**
* IMPORTANT: Make sure to update libraries/ag-charts-eslint-rules/rules/module-mappings.mjs
* when adding/removing modules to keep example validation in sync.
*/
export declare const ExpectedModules: Map<string, ModulePlaceholder>;
export declare function getSeriesExpectedChartType(seriesName: string): string | undefined;

View File

@@ -0,0 +1,7 @@
import type { AgChartOptions } from 'ag-charts-types';
import { ChartTheme } from '../themes/chartTheme';
import { type ModulePlaceholder } from './expectedModules';
export declare function sanitizeThemeModules(theme: ChartTheme): ChartTheme;
export declare function processModuleOptions<T extends Partial<AgChartOptions>>(chartType: string | undefined, options: T, additionalMissingModules: ModulePlaceholder[]): void;
export declare function removeUnregisteredModuleOptions<T extends Partial<AgChartOptions>>(chartType: string | undefined, options: T): ModulePlaceholder[];
export declare function removeIncompatibleModuleOptions<T extends Partial<AgChartOptions>>(chartType: string | undefined, options: T): string[];

View File

@@ -0,0 +1,12 @@
import type { DOMManager } from '../../dom/domManager';
import type { UpdateService } from '../updateService';
export declare class FontManager {
private readonly domManager;
private readonly updateService;
private observers;
constructor(domManager: DOMManager, updateService: UpdateService);
updateFonts(fonts?: Set<string>): void;
destroy(): void;
private loadFonts;
private observeFontStatus;
}

View File

@@ -0,0 +1,27 @@
import { type AgTimeIntervalUnit, type CategoryFormatterParams, type DateFormatterParams, type DateFormatterStyle, type FormatterConfiguration, type NumberFormatterParams, type TextValue } from 'ag-charts-types';
import { Listeners } from '../../util/listeners';
export type GlobalContextlessFormatterParams = Omit<NumberFormatterParams<any, any>, 'context'> | Omit<DateFormatterParams<any, any>, 'context'> | Omit<CategoryFormatterParams<any, any>, 'context'>;
export type GlobalContextFormatter = (fn: (params: GlobalContextlessFormatterParams) => TextValue | undefined, params: GlobalContextlessFormatterParams, contextProvider?: {
context?: unknown;
}) => TextValue | undefined;
type Specifier = Record<AgTimeIntervalUnit, string> | string;
interface FormatParams {
specifier?: Record<string, string> | string;
truncateDate?: 'year' | 'month' | 'day';
allowNull?: boolean;
}
export declare class FormatManager extends Listeners<'format-changed', () => void> {
private readonly formats;
private readonly dateFormatter;
formatter: FormatterConfiguration<any> | undefined;
static mergeSpecifiers(a: Specifier | undefined, ...specifiers: Array<Specifier>): Specifier;
static mergeSpecifiers(a: Specifier, ...specifiers: Array<Specifier | undefined>): Specifier;
static mergeSpecifiers(...specifiers: Array<Specifier | undefined>): Specifier | undefined;
static getFormatter(type: 'number' | 'date' | 'category', specifier: string | Partial<Record<AgTimeIntervalUnit, string>>, unit?: AgTimeIntervalUnit, style?: DateFormatterStyle, { truncateDate }?: {
truncateDate?: FormatParams['truncateDate'];
}): ((value: any, fractionDigits?: number) => string) | undefined;
setFormatter(formatter: FormatterConfiguration<any> | undefined): void;
format(formatInContext: GlobalContextFormatter, params: GlobalContextlessFormatterParams, { specifier, truncateDate, allowNull }?: FormatParams): string | undefined;
defaultFormat(params: GlobalContextlessFormatterParams, { specifier, truncateDate }?: FormatParams): string;
}
export {};

View File

@@ -0,0 +1,29 @@
import type { AgChartLegendOrientation } from 'ag-charts-types';
import type { BBox } from '../scene/bbox';
export type Page = {
columns: Column[];
pageWidth: number;
pageHeight: number;
startIndex: number;
endIndex: number;
};
type Column = {
columnWidth: number;
columnHeight: number;
indices: number[];
bboxes: BBox[];
};
export declare function gridLayout({ orientation, bboxes, maxHeight, maxWidth, itemPaddingY, itemPaddingX, forceResult, }: {
orientation: AgChartLegendOrientation;
bboxes: BBox[];
maxHeight: number;
maxWidth: number;
itemPaddingY?: number;
itemPaddingX?: number;
forceResult?: boolean;
}): {
pages: Page[];
maxPageWidth: number;
maxPageHeight: number;
} | undefined;
export {};

View File

@@ -0,0 +1,35 @@
import type { MementoOriginator } from 'ag-charts-core';
import type { AgActiveChangeEvent, AgActiveItemState, AgActiveState } from 'ag-charts-types';
import type { EventsHub } from '../../core/eventsHub';
import type { DatumIndexType, SeriesNodeDatum } from '../series/seriesTypes';
import type { UpdateService } from '../updateService';
import type { InteractionManager } from './interactionManager';
type ActiveItem = AgActiveItemState | undefined;
type ActiveChangeEvent = Omit<AgActiveChangeEvent<unknown, unknown>, 'context'>;
type DatumArg = Readonly<SeriesNodeDatum<DatumIndexType>> | undefined;
/**
* This class implements the (de-)serialisation of `AgChartState['active']`.
*/
export declare class ActiveManager implements MementoOriginator<AgActiveState> {
private readonly chartService;
private readonly eventsHub;
private readonly interactionManager;
private readonly fireEvent;
mementoOriginatorKey: string;
private currentItem?;
private updateable;
private didLayout;
private pendingMemento;
constructor(chartService: {
readonly id: string;
}, eventsHub: EventsHub, updateService: UpdateService, interactionManager: InteractionManager, fireEvent: (event: ActiveChangeEvent) => void);
private isFrozen;
clear(): void;
update(newItemState: ActiveItem, nodeDatum: DatumArg): void;
private performUpdate;
createMemento(): AgActiveState;
guardMemento(blob: unknown, messages: string[]): blob is AgActiveState | undefined;
restoreMemento(version: string, mementoVersion: string, memento: AgActiveState | undefined): void;
private performRestoration;
}
export {};

View File

@@ -0,0 +1,36 @@
import { type AnimationPhase, type IAnimation } from '../../motion/animation';
/**
* A batch of animations that are synchronised together. Can be skipped independently of other batches and the main
* animation skipping status.
*/
export declare class AnimationBatch {
private readonly maxAnimationTime;
private readonly debug;
private readonly controllers;
readonly stoppedCbs: Set<() => void>;
private currentPhase;
private readonly phases;
private skipAnimations;
private animationTimeConsumed;
/** Guard against premature animation execution. */
private isReady;
constructor(maxAnimationTime: number);
get size(): number;
get consumedTimeMs(): number;
isActive(): boolean;
getActiveControllers(): IAnimation[];
checkOverlappingId(id: string): void;
addAnimation(animation: IAnimation): void;
removeAnimation(animation: IAnimation): void;
progress(deltaTime: number): void;
ready(): void;
skip(skip?: boolean): void;
play(): void;
stop(): void;
stopByAnimationId(id: string): void;
stopByAnimationGroupId(id: string): void;
private dispatchStopped;
isSkipped(): boolean;
getRemainingTime(restrictPhase?: AnimationPhase): number;
destroy(): void;
}

View File

@@ -0,0 +1,64 @@
import { type EventListener } from 'ag-charts-core';
import type { AdditionalAnimationOptions, AnimationOptions, AnimationPhase, AnimationValue, IAnimation } from '../../motion/animation';
import { Animation } from '../../motion/animation';
import type { Mutex } from '../../util/mutex';
import { InteractionManager } from './interactionManager';
type AnimationEventType = 'animation-frame' | 'animation-start' | 'animation-stop';
export interface AnimationEvent {
readonly type: AnimationEventType;
readonly deltaMs: number;
}
type AnimationEventMap = {
[K in AnimationEventType]: AnimationEvent;
};
/**
* Manage animations across a chart, running all animations through only one `requestAnimationFrame` callback,
* preventing duplicate animations and handling their lifecycle.
*/
export declare class AnimationManager {
private readonly interactionManager;
private readonly chartUpdateMutex;
defaultDuration: number;
maxAnimatableItems: number;
private batch;
private readonly debug;
private readonly events;
private readonly rafAvailable;
private isPlaying;
private requestId;
private skipAnimations;
private currentAnonymousAnimationId;
private cumulativeAnimationTime;
constructor(interactionManager: InteractionManager, chartUpdateMutex: Mutex);
addListener<K extends keyof AnimationEventMap>(eventName: K, listener: EventListener<AnimationEventMap[K]>): () => void;
/**
* Create an animation to tween a value between the `from` and `to` properties. If an animation already exists
* with the same `id`, immediately stop it.
*/
animate<T extends AnimationValue>(opts: AnimationOptions<T> & AdditionalAnimationOptions): Animation<T> | undefined;
play(): void;
stop(): void;
stopByAnimationId(id: string): void;
stopByAnimationGroupId(id: string): void;
reset(): void;
skip(skip?: boolean): void;
isSkipped(): boolean;
isActive(): boolean;
getRemainingTime(phase?: AnimationPhase): number;
getCumulativeAnimationTime(): number;
skipCurrentBatch(): void;
/** Mocking point for tests to guarantee that animation updates happen. */
isSkippingFrames(): boolean;
/** Mocking point for tests to capture requestAnimationFrame callbacks. */
scheduleAnimationFrame(cb: (time: number) => Promise<void>): void;
/** Mocking point for tests to skip animations to a specific point in time. */
forceTimeJump(_animation: IAnimation, _defaultDuration: number): boolean;
private requestAnimation;
private cancelAnimation;
private failsafeOnError;
startBatch(skipAnimations?: boolean): void;
endBatch(): void;
onBatchStop(cb: () => void): void;
destroy(): void;
}
export {};

View File

@@ -0,0 +1,23 @@
import type { AgContextMenuItemLiteral, AgContextMenuItemShowOn } from 'ag-charts-types';
import type { ContextMenuEvent, EventsHub } from '../../core/eventsHub';
import type { MouseWidgetEvent } from '../../widget/widgetEvents';
import type { ContextMenuCallback, ContextShowOnMap } from './contextMenuTypes';
import { ContextMenuBuiltins } from './contextMenuTypes';
export declare class ContextMenuRegistry {
private readonly eventsHub;
readonly builtins: ContextMenuBuiltins;
private readonly hiddenActions;
constructor(eventsHub: EventsHub);
static check<T extends AgContextMenuItemShowOn>(showOn: T, event: ContextMenuEvent): event is ContextMenuEvent<T>;
static checkCallback<T extends AgContextMenuItemShowOn>(desiredShowOn: T, showOn: AgContextMenuItemShowOn, _callback: ContextMenuCallback<AgContextMenuItemShowOn>): _callback is ContextMenuCallback<T>;
dispatchContext<T extends AgContextMenuItemShowOn>(showOn: T, pointerEvent: {
widgetEvent: MouseWidgetEvent<'contextmenu'>;
canvasX: number;
canvasY: number;
}, context: ContextShowOnMap[T]['context'], position?: {
x: number;
y: number;
}): void;
isVisible(id: AgContextMenuItemLiteral): boolean;
toggle(id: AgContextMenuItemLiteral, action?: 'show' | 'hide'): void;
}

View File

@@ -0,0 +1,85 @@
import type { RequireOptional } from 'ag-charts-core';
import type { AgContextMenuItem, AgContextMenuItemAlways, AgContextMenuItemLegendItem, AgContextMenuItemSeriesArea, AgContextMenuItemShowOn } from 'ag-charts-types';
import type { CategoryLegendDatum } from '../legend/legendDatum';
import type { DatumIndexType, ISeries, SeriesNodeDatum } from '../series/seriesTypes';
type InferTEvent<T extends AgContextMenuItemShowOn> = Extract<AgContextMenuItem, {
showOn?: T;
action?: (...args: any[]) => any;
}> extends {
action?: (event: infer E) => any;
} ? E : never;
type ContextShowOnMapRule = {
[K in AgContextMenuItemShowOn]: {
event: InferTEvent<K>;
callback: (param: InferTEvent<K>) => void;
};
};
export interface ContextShowOnMap extends ContextShowOnMapRule {
always: {
event: InferTEvent<'always'>;
callback: (param: InferTEvent<'always'>) => void;
context: undefined;
};
'legend-item': {
event: InferTEvent<'legend-item'>;
callback: (param: InferTEvent<'legend-item'>) => void;
context: {
legendItem: CategoryLegendDatum | undefined;
};
};
'series-area': {
event: InferTEvent<'series-area'>;
callback: (param: InferTEvent<'series-area'>) => void;
context: undefined;
};
'series-node': {
event: InferTEvent<'series-node'>;
callback: (param: InferTEvent<'series-node'>) => void;
context: {
pickedSeries: ISeries<any, any, any> | undefined;
pickedNode: SeriesNodeDatum<DatumIndexType> | undefined;
};
};
}
export type ContextMenuCallback<K extends AgContextMenuItemShowOn> = ContextShowOnMap[K]['callback'];
/**
* Merge a union of objects into one object with all the properties. This is just to check at compile-time that
* ContextMenuItem implements all properties of AgContextMenuItem API contract.
*/
type MergeUnion<T, CanBeUndefined extends keyof T> = {
[K in T extends any ? keyof T : never]: T extends {
[P in K]?: infer V;
} ? K extends CanBeUndefined ? V | undefined : V : never;
};
/**
* The type of `contextMenu.items[]` recursively references its own type, but our compile-time check only needs a depth
* of 1. Therefore, limit this depth to 1:
*/
export type ContextMenuItemContractNonRecursive = Omit<MergeUnion<Extract<AgContextMenuItem, object>, 'action'>, 'items'>;
export type ContextMenuItemContract = ContextMenuItemContractNonRecursive & {
items: ContextMenuItemContractNonRecursive[];
};
type BuiltinItemListKeys = 'defaults';
type ContextMenuBuiltinItemsRules = {
readonly [K in Exclude<AgContextMenuItem, object | BuiltinItemListKeys>]: RequireOptional<AgContextMenuItem>;
};
type ContextMenuBuiltinItemListsRules = {
readonly [K in BuiltinItemListKeys]: readonly (keyof ContextMenuBuiltinItemsRules)[];
};
declare class ContextMenuBuiltinItems implements ContextMenuBuiltinItemsRules {
readonly download: RequireOptional<AgContextMenuItemAlways>;
readonly 'zoom-to-cursor': RequireOptional<AgContextMenuItemSeriesArea>;
readonly 'pan-to-cursor': RequireOptional<AgContextMenuItemSeriesArea>;
readonly 'reset-zoom': RequireOptional<AgContextMenuItemSeriesArea>;
readonly 'toggle-series-visibility': RequireOptional<AgContextMenuItemLegendItem>;
readonly 'toggle-other-series': RequireOptional<AgContextMenuItemLegendItem>;
readonly 'separator': RequireOptional<AgContextMenuItemAlways>;
}
declare class ContextMenuBuiltinItemLists implements ContextMenuBuiltinItemListsRules {
readonly defaults: readonly (keyof ContextMenuBuiltinItemsRules)[];
}
export declare class ContextMenuBuiltins {
readonly items: ContextMenuBuiltinItems;
readonly lists: ContextMenuBuiltinItemLists;
}
export {};

View File

@@ -0,0 +1,48 @@
import { EventEmitter } from 'ag-charts-core';
import type { Widget } from '../../widget/widget';
import type { NativeMouseWidgetEvent, TouchSyntheticMouseWidgetEvent, WidgetEventMap } from '../../widget/widgetEvents';
/**
* A `DragInterpreterClickEvent` is either a native 'click' MouseEvent, or a synthetic click event fired by a single
* finger 'touchstart' and 'touchend'.
*/
export type DragInterpreterClickEvent = NativeMouseWidgetEvent<'click'> | TouchSyntheticMouseWidgetEvent<'click'>;
/**
* A `DragInterpreterDblClickEvent` is either a native 'dblclick' MouseEvent, or a synthetic click event fired by two
* finger 'touchstart' and 'touchend' in quick succession (DOUBLE_TAP_TIMER_MS).
*/
export type DragInterpreterDblClickEvent = NativeMouseWidgetEvent<'dblclick'> | TouchSyntheticMouseWidgetEvent<'dblclick'>;
type EventMap = Omit<WidgetEventMap, 'click' | 'dblclick'> & {
click: DragInterpreterClickEvent;
dblclick: DragInterpreterDblClickEvent;
};
/**
* In the interest of robustness (and simplicity), the Widget class always dispatches these events after mousedown &
* mouseup events for the left-button:
*
* - One 'drag-start'
* - Zero or more 'drag-move'
* - One 'drag-end'
* - Zero or one 'click' (only dispatched if the mouseup event is on target).
*
* To distinguish between drag and click actions, use this class. It ensure that for each mousedown-mouseup pair, it
* dispatches either a set of 'drag-*' events or a single 'click' event but not both.
*/
export declare class DragInterpreter {
private readonly cleanup;
readonly events: EventEmitter<EventMap>;
private dragStartEvent?;
private isDragging;
private lastClick?;
private readonly touch;
constructor(widget: Widget);
destroy(): void;
private onTouchStart;
private onTouchMove;
private onTouchEnd;
private onMouseMove;
private onDblClick;
private onDragStart;
private onDragMove;
private onDragEnd;
}
export {};

View File

@@ -0,0 +1,20 @@
import type { EventsHub, HighlightNodeDatum } from '../../core/eventsHub';
/**
* Manages the actively highlighted series/datum for a chart. Tracks the requested highlights from
* distinct dependents and handles conflicting highlight requests.
*/
export declare class HighlightManager {
private readonly eventsHub;
private readonly highlightStates;
private readonly pendingUnhighlights;
unhighlightDelay: number;
private static readonly HIGHLIGHT_CHANGE_EVENT;
constructor(eventsHub: EventsHub);
updateHighlight(callerId: string, highlightedDatum?: HighlightNodeDatum, delayed?: boolean): void;
private maybeEmitChange;
private applyPendingUnhighlight;
getActiveHighlight(): HighlightNodeDatum | undefined;
destroy(): void;
private isEqual;
private idsMatch;
}

View File

@@ -0,0 +1,25 @@
export declare enum InteractionState {
Default = 64,
ZoomDrag = 32,
Annotations = 16,
ContextMenu = 8,
Animation = 4,
AnnotationsSelected = 2,
Frozen = 1,
Clickable = 82,
Focusable = 68,
Keyable = 86,
ContextMenuable = 72,// AG-10233
AnnotationsMoveable = 18,
AnnotationsDraggable = 114,
ZoomDraggable = 100,
ZoomClickable = 68,
ZoomWheelable = 118,
All = 126
}
export declare class InteractionManager {
private stateQueue;
pushState(state: InteractionState): void;
popState(state: InteractionState): void;
isState(allowedStates: InteractionState): boolean;
}

View File

@@ -0,0 +1,7 @@
type KeyAction = {
name: KeyActionName;
activatesFocusIndicator: boolean;
};
type KeyActionName = 'arrowdown' | 'arrowleft' | 'arrowright' | 'arrowup' | 'delete' | 'undo' | 'redo' | 'submit' | 'zoomin' | 'zoomout';
export declare function mapKeyboardEventToAction(event: KeyboardEvent): KeyAction | undefined;
export {};

View File

@@ -0,0 +1,77 @@
import type { ChartAxisDirection, EventEmitter } from 'ag-charts-core';
import type { EventsHubMap } from '../../core/eventsHub';
import type { ModuleMap } from '../../module/moduleMap';
import type { BBox } from '../../scene/bbox';
import type { DatumIndexType, ISeries } from '../series/seriesTypes';
import type { TooltipContent } from '../tooltip/tooltip';
import type { UpdateService } from '../updateService';
import type { HighlightManager } from './highlightManager';
import type { TooltipManager } from './tooltipManager';
import type { ZoomManager } from './zoomManager';
type GroupId = string | symbol;
/** Breaks circular dependencies which occur when importing ChartAxis. */
export type SyncAxisLike = {
boundSeries: ISeries<any, any, any>[];
direction: ChartAxisDirection;
reverse?: boolean;
nice: boolean;
min?: number;
max?: number;
};
export type SyncStatus = 'init' | 'domains-calculated' | 'ready';
/** Breaks circular dependencies which occur when importing Chart. */
export type SyncChartLike = {
id: string;
axes: SyncAxisLike[];
series: ISeries<any, any, any>[];
syncStatus: SyncStatus;
modulesManager: ModuleMap;
seriesAreaBoundingBox: BBox;
tooltip: {
enabled: boolean;
};
ctx: {
eventsHub: EventEmitter<EventsHubMap>;
highlightManager: HighlightManager;
tooltipManager: TooltipManager;
updateService: UpdateService;
zoomManager: ZoomManager;
};
getTooltipContent(series: ISeries<DatumIndexType, unknown, unknown>, datumIndex: unknown, removeThisDatum: unknown, purpose: 'aria-label' | 'tooltip'): TooltipContent[];
onSyncActiveClear(): void;
};
type ChartDomainState = {
[id: string]: Record<string, unknown[]>;
};
export type SyncDerivedDomain = {
derived: unknown[];
sources: ChartDomainState;
dirty: boolean;
};
export type SyncGroupState = {
members: Set<SyncChartLike>;
domains?: {
[key in 'x' | 'y']?: SyncDerivedDomain;
};
domainsById?: {
[key: string]: SyncDerivedDomain;
};
domainsByPosition?: {
[key: string]: SyncDerivedDomain;
};
};
export declare class SyncManager {
protected chart: SyncChartLike;
private static readonly chartsGroups;
private static readonly DEFAULT_GROUP;
constructor(chart: SyncChartLike);
subscribe(groupId?: GroupId): this;
unsubscribe(groupId?: GroupId): this;
getChart(): SyncChartLike;
getGroupState(groupId?: GroupId): SyncGroupState | undefined;
getGroupMembers(groupId?: GroupId): SyncChartLike[];
getGroupSiblings(groupId?: GroupId): SyncChartLike[];
getGroupSyncMode(groupId?: GroupId): "multi-series" | "single-series";
private get;
}
export {};

Some files were not shown because too many files have changed in this diff Show More