ag-charts-community
Version:
Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue
411 lines (410 loc) • 23.6 kB
TypeScript
import type { BoxBounds, ChartAnimationPhase, DomainWithMetadata, DynamicContext, NormalisedColorType, NormalisedSeriesMarkerStyle, NormalisedTextOrSegments, PlacedLabel, PointLabelDatum } from 'ag-charts-core';
import { type Callback, type CallbackParam, ChartAxisDirection, CleanupRegistry, type DistantObject, EventEmitter, LRUCache, type Point, type RequireOptional, type SeriesPluginModuleInstance } from 'ag-charts-core';
import type { AgActiveItemState, AgChartLabelFormatterParams, AgDrawingMode, AgInitialStateLegendOptions, AgNumericValue, AgSeriesTooltipRendererParams, AgSeriesVisibilityChange, FormatterPropertyType, HighlightState as PublicHighlightState, SelectionState as PublicSelectionState, SeriesType } from 'ag-charts-types';
import type { HighlightChangeEvent, HighlightNodeDatum, LegendItemClickEvent, LegendItemDoubleClickEvent } from '../../core/eventsHub';
import type { AxisFormattableLabel } from '../../module/axisContext';
import type { ChartRegistry, ChartSeriesRegistry } from '../../module/moduleContext';
import { ModuleMap } from '../../module/moduleMap';
import type { SeriesGrouping } from '../../module/seriesGrouping';
import { BBox } from '../../scene/bbox';
import { Group, TranslatableGroup } from '../../scene/group';
import { type Node } from '../../scene/node';
import type { Selection } from '../../scene/selection';
import type { Path } from '../../scene/shape/path';
import type { TypedEvent, TypedEventListener } from '../../util/observable';
import { Observable } from '../../util/observable';
import type { ChartAxis } from '../chartAxis';
import type { ChartMode } from '../chartMode';
import type { DataController } from '../data/dataController';
import type { DataModel, ProcessedData } from '../data/dataModel';
import { DataSet } from '../data/dataSet';
import type { ChartLegendDatum, ChartLegendType } from '../legend/legendDatum';
import type { Marker } from '../marker/marker';
import type { TooltipContent, TooltipStructuredContent } from '../tooltip/tooltip';
import type { SeriesMarker } from './seriesMarker';
import type { SeriesProperties } from './seriesProperties';
import type { SeriesTooltip } from './seriesTooltip';
import { type BucketLookupFeature, type DatumIndex, HighlightState, type INodeEvent, type ISeries, type ISeriesProperties, type NodeDataDependencies, SelectionState, type SeriesNodeDatum, type SeriesNodeEventTypes } from './seriesTypes';
import { type ShapeFillBBox } from './shapeUtil';
export interface SeriesDataEvent {
readonly dataModel: DataModel<any, any, any>;
readonly processedData: ProcessedData<any>;
}
/** Modes of matching user interactions to rendered nodes (e.g. hover or click) */
export declare enum SeriesNodePickMode {
/** Pick matches based upon pick coordinates being inside a matching shape/marker. */
EXACT_SHAPE_MATCH = 0,
/** Pick matches based upon distance to ideal position */
NEAREST_NODE = 1,
/** Pick matches based upon distance from axis */
AXIS_ALIGNED = 2
}
export type SeriesNodePickIntent = 'tooltip' | 'highlight' | 'highlight-tooltip' | 'context-menu' | 'event';
export type SeriesNodePickMatch = {
datum: SeriesNodeDatum;
distance: number;
};
export type PickFocusInputs = {
readonly datumIndex: number;
readonly datumIndexDelta: number;
readonly otherIndex: number;
readonly otherIndexDelta: number;
readonly seriesRect?: BBox;
};
export type PickViewportFocusInputs = {
readonly otherIndex: number;
readonly where: 'data-start' | 'data-end' | 'viewport-start' | 'viewport-end';
readonly hoverRect: Readonly<BoxBounds>;
};
export type PickFocusOutputs = {
datumIndex: number;
datum: SeriesNodeDatum;
otherIndex?: number;
bounds: BBox | Path;
movedBounds?: BBox;
clipFocusBox: boolean;
};
export type PickResult = {
pickMode: SeriesNodePickMode;
datums: SeriesNodeDatum[];
distance: number;
};
export type PickNodesInBBoxPredicate = (selectionBox: BoxBounds, node: Node<unknown>) => boolean;
export type INodeEventConstructor<TDatum extends SeriesNodeDatum, TSeries extends Series<TDatum, object, any>, TEvent extends string = SeriesNodeEventTypes> = new <T extends TEvent>(type: T, event: Event, nodeDatum: TDatum, series: TSeries, selectionState: PublicSelectionState | undefined) => INodeEvent<T>;
export declare class SeriesNodeEvent<TDatum extends SeriesNodeDatum, TEvent extends string = SeriesNodeEventTypes> implements INodeEvent<TEvent> {
readonly type: TEvent;
readonly event: Event;
readonly datum: unknown;
readonly datums?: unknown[];
readonly totalValue?: AgNumericValue;
readonly seriesId: string;
readonly itemId: string | number;
readonly dataIdKey: string | undefined;
readonly selectionState: PublicSelectionState | undefined;
defaultPrevented: boolean;
constructor(type: TEvent, event: Event, nodeDatum: TDatum, series: ISeries<TDatum, ISeriesProperties, unknown>, selectionState: PublicSelectionState | undefined);
preventDefault(): void;
}
export type SeriesNodeDataContext<S = SeriesNodeDatum, L = S> = {
itemId: string;
nodeData: S[];
labelData: L[];
};
export type SeriesNodeStyleContext<TStyle> = {
[HighlightState.None]: TStyle;
[HighlightState.Item]: TStyle;
[HighlightState.Series]: TStyle;
[HighlightState.OtherSeries]: TStyle;
[HighlightState.OtherItem]: TStyle;
};
/** Cache-miss callback for `runMarkerStylePass` — computes a value keyed by (highlight, selection[, keyExtra]). */
export type MarkerStyleCompute<TSeries, TCtx, TDatum, TCache> = (this: void, series: TSeries, ctx: TCtx, highlightState: HighlightState, selectionState: SelectionState | undefined, datum: TDatum) => TCache;
/** Per-non-garbage-datum callback for `runMarkerStylePass`. */
export type MarkerStyleApply<TSeries, TCtx, TDatum, TCache> = (this: void, series: TSeries, ctx: TCtx, datum: TDatum, highlightState: HighlightState, selectionState: SelectionState | undefined, cached: TCache) => void;
export type SeriesDirectionKeysMapping<P extends SeriesProperties<any>> = {
[key in ChartAxisDirection | FormatterPropertyType]?: (keyof P & string)[];
};
export declare class SeriesGroupingChangedEvent implements TypedEvent {
series: Series<any, object, any>;
seriesGrouping: SeriesGrouping | undefined;
type: string;
constructor(series: Series<any, object, any>, seriesGrouping: SeriesGrouping | undefined);
}
export type SeriesConstructorOpts<TProps extends SeriesProperties<any>> = {
moduleCtx: DynamicContext<ChartRegistry>;
pickModes: SeriesNodePickMode[];
propertyKeys?: SeriesDirectionKeysMapping<TProps>;
propertyNames?: SeriesDirectionKeysMapping<TProps>;
canHaveAxes?: boolean;
usesPlacedLabels?: boolean;
alwaysClip?: boolean;
supportsStandaloneZoom?: boolean;
};
export type UnknownSeries = Series<SeriesNodeDatum, object, SeriesProperties<object>>;
export declare abstract class Series<TDatum extends SeriesNodeDatum, TOpts extends object, TProps extends SeriesProperties<TOpts>, TLabel = TDatum, TContext extends SeriesNodeDataContext<TDatum, TLabel> = SeriesNodeDataContext<TDatum, TLabel>> extends Observable implements ISeries<TDatum, TProps, TLabel> {
static readonly className: string;
protected cleanup: CleanupRegistry;
abstract readonly properties: TProps;
pickModes: SeriesNodePickMode[];
usesPlacedLabels: boolean;
readonly alwaysClip: boolean;
/** Opts into StandaloneChart zoom (axis registration + scale/translate viewport). */
readonly supportsStandaloneZoom: boolean;
protected hasChangesOnHighlight: boolean;
protected hasChangesOnSelection: boolean;
get pickModeAxis(): 'main' | 'main-category' | undefined;
seriesGrouping: SeriesGrouping | undefined;
protected readonly NodeEvent: INodeEventConstructor<TDatum, any>;
readonly internalId: string;
get id(): string;
readonly canHaveAxes: boolean;
get type(): SeriesType;
readonly contentGroup: TranslatableGroup<unknown>;
readonly highlightGroup: TranslatableGroup<unknown>;
readonly highlightNodeGroup: Group<TDatum>;
readonly highlightLabelGroup: Group<TLabel>;
readonly annotationGroup: TranslatableGroup<unknown>;
readonly labelGroup: TranslatableGroup<TLabel>;
chart?: {
mode: ChartMode;
isMiniChart: boolean;
flashOnUpdateEnabled: boolean;
seriesRect?: BBox;
};
axes: {
[K in ChartAxisDirection]?: ChartAxis;
};
directions: ChartAxisDirection[];
private readonly propertyKeys;
private readonly propertyNames;
protected nodeDataRefresh: boolean;
protected processedDataUpdated: boolean;
protected readonly moduleMap: ModuleMap<SeriesPluginModuleInstance>;
protected _data?: DataSet<any>;
protected _chartData?: DataSet<any>;
private _dataConnected;
private readonly datumCallbackCache;
connectsToYAxis: boolean;
get focusable(): boolean;
get data(): DataSet<any> | undefined;
set visible(newVisibility: boolean);
get visible(): boolean;
get hasData(): boolean;
get tooltipEnabled(): boolean | undefined;
protected onDataChange(): void;
setOptionsData(input: DataSet | undefined): void;
isHighlightEnabled(): boolean;
isSelectionEnabled(): boolean;
isDatumSelectable(_datumIndex: DatumIndex): boolean;
setChartData(input: DataSet | undefined): void;
private onSeriesGroupingChange;
getBandScalePadding(): {
inner: number;
outer: number;
};
readonly ctx: DynamicContext<ChartRegistry>;
private moduleContext?;
constructor(seriesOpts: SeriesConstructorOpts<TProps>);
attachSeries(seriesContentNode: Group, seriesNode: Group, annotationNode: Group | undefined): void;
detachSeries(_seriesContentNode: Group | undefined, _seriesNode: Group, _annotationNode: Group | undefined): void;
declarationOrder: number;
private _broughtToFront;
setSeriesIndex(index: number, forceUpdate?: boolean): boolean;
setZIndex(zIndex: number): void;
renderToOffscreenCanvas(): boolean;
disconnectData(): void;
reconnectData(): void;
protected hasHighlightOpacity(): boolean;
protected getDrawingMode(isHighlight?: boolean, highlightDrawingMode?: AgDrawingMode): AgDrawingMode;
protected getAnimationDrawingModes(): {
start: {
drawingMode: AgDrawingMode;
};
finish: {
drawingMode: AgDrawingMode;
};
};
readonly events: EventEmitter<{
'data-update': SeriesDataEvent;
'data-processed': SeriesDataEvent;
'data-selection-change': null;
}>;
addEventListener(type: 'seriesVisibilityChange', listener: (e: AgSeriesVisibilityChange) => void): void;
addEventListener(type: 'seriesNodeClick', listener: (e: SeriesNodeEvent<any>) => void): void;
addEventListener(type: 'seriesNodeDoubleClick', listener: (e: SeriesNodeEvent<any>) => void): void;
addEventListener(type: string, listener: TypedEventListener): void;
removeEventListener(type: 'seriesVisibilityChange', listener: (e: AgSeriesVisibilityChange) => void): void;
removeEventListener(type: 'seriesNodeClick', listener: (e: SeriesNodeEvent<any>) => void): void;
removeEventListener(type: 'seriesNodeDoubleClick', listener: (e: SeriesNodeEvent<any>) => void): void;
removeEventListener(type: string, listener: TypedEventListener): void;
hasEventListener(type: 'seriesVisibilityChange'): boolean;
hasEventListener(type: 'seriesNodeClick'): boolean;
hasEventListener(type: 'seriesNodeDoubleClick'): boolean;
hasEventListener(type: string): boolean;
updatedDomains(): void;
destroy(): void;
abstract resetAnimation(chartAnimationPhase: ChartAnimationPhase): void;
private getPropertyValues;
getKeyAxis(_direction: ChartAxisDirection): string | undefined;
getKeys(direction: ChartAxisDirection): string[];
getKeyProperties(direction: ChartAxisDirection): (keyof TProps & string)[];
getNames(direction: ChartAxisDirection): (string | undefined)[];
getFormatterContext(property: FormatterPropertyType): Array<{
seriesId: string;
key: string;
name: string | undefined;
}>;
resolveKeyDirection(direction: ChartAxisDirection): ChartAxisDirection;
getDomain(direction: ChartAxisDirection): DomainWithMetadata<any>;
getRange(direction: ChartAxisDirection, visibleRange: [number, number]): [number, number] | [];
getMinimumRangeSeries(_range: number[]): void;
getMinimumRangeChart(_ranges: number[]): number;
getZoomRangeFittingItems(_xVisibleRange: [number, number], _yVisibleRange: [number, number] | undefined, _minVisibleItems: number): {
x: [number, number];
y: [number, number] | undefined;
} | undefined;
getVisibleItems(_xVisibleRange: [number, number], _yVisibleRange: [number, number] | undefined, _minVisibleItems: number): number;
abstract dataCount(): number;
abstract getSeriesDomain(direction: ChartAxisDirection): DomainWithMetadata<any>;
abstract getSeriesRange(direction: ChartAxisDirection, visibleRange: [number, number]): [number, number] | [];
abstract processData(dataController: DataController): Promise<void> | void;
abstract createNodeData(): TContext | undefined;
abstract findNodeDatum(itemIdOrIndex: AgActiveItemState['itemId']): SeriesNodeDatum | undefined;
toCanvasFromMidPoint(nodeDatum: {
midPoint?: Point;
}): Point;
markNodeDataDirty(): void;
private visibleMaybeChanged;
abstract update(opts: {
seriesRect?: BBox;
}): Promise<void> | void;
getOpacity(): number;
getHighlightState(highlightedDatum: HighlightNodeDatum | undefined, isHighlight?: boolean, datumIndex?: DatumIndex, legendItemValues?: string[]): HighlightState;
getDataSelectionState(datumIndex: DatumIndex | undefined): SelectionState | undefined;
getDataCandidacyState(datumIndex: DatumIndex | undefined): SelectionState | undefined;
/**
* Per-series aggregation-aware bucket lookup. Optional — populated
* lazily for aggregating series only. Owns both the per-bucket SELECTED
* roll-up and the bucket→datum-range mapping used by the data-selection
* drag handler. `DataModelSeries.getDataSelectionState` consults this
* for marker styling; `data-selection-change` and aggregation rebuilds
* keep the roll-up in sync.
*/
protected bucketLookup?: BucketLookupFeature;
/**
* Construct the series-specific {@link BucketLookupFeature}. Default
* `undefined` for non-aggregating series. Aggregating series override to
* return either {@link BucketLookupManager} (single `indexData`) or
* {@link SplitBucketLookupManager} (split positive/negative).
*/
protected createBucketLookupFeature(): BucketLookupFeature | undefined;
/**
* Lazy-init `bucketLookup` on first access. The just-created feature is
* refreshed once so it reflects the current selection bitset and
* aggregation filters — without this any `filtersChanged` events emitted
* before the lazy-init would have been missed.
*/
ensureBucketLookupFeature(): BucketLookupFeature | undefined;
getHighlightStateString(datum: HighlightNodeDatum | undefined, isHighlight?: boolean, datumIndex?: DatumIndex, legendItemValues?: string[]): PublicHighlightState;
getSelectionStateString(datumIndex: DatumIndex | undefined, selectionState?: SelectionState): PublicSelectionState | undefined;
getCandidateStateString(datumIndex: DatumIndex | undefined, candidateState?: SelectionState): PublicSelectionState | undefined;
protected onChangeHighlight(event: HighlightChangeEvent): void;
bringToFront(): boolean;
isSeriesHighlighted(highlightedDatum: HighlightNodeDatum | undefined, _legendItemValues?: string[]): boolean;
protected isItemHighlighted(highlightedDatum?: HighlightNodeDatum, datumIndex?: DatumIndex): boolean | undefined;
private hasDataSelection;
getHighlightStyle(isHighlight?: boolean, datumIndex?: DatumIndex, highlightState?: HighlightState, legendItemValues?: string[]): Partial<TOpts & {
fill: NormalisedColorType;
fillOpacity: number;
stroke: string;
strokeWidth: number;
strokeOpacity: number;
lineDash: number[];
lineDashOffset: number;
opacity: number;
}>;
getSelectionStyle(datumIndex?: DatumIndex, selectionState?: SelectionState, candidateState?: SelectionState): Partial<TOpts & {
fill: NormalisedColorType;
fillOpacity: number;
stroke: string;
strokeWidth: number;
strokeOpacity: number;
lineDash: number[];
lineDashOffset: number;
opacity: number;
}> | undefined;
protected resolveMarkerDrawingModeForState(drawingMode: AgDrawingMode, style?: NormalisedSeriesMarkerStyle): AgDrawingMode;
protected abstract hasItemStylers(): boolean;
filterItemStylerFillParams(fill: NormalisedColorType | undefined): NormalisedColorType | undefined;
protected getModuleTooltipParams(): object;
abstract getTooltipContent(datumIndex: DatumIndex, removeThisDatum: TDatum | undefined): TooltipContent | undefined;
protected _pickNodeCache: LRUCache<PickResult | undefined, string>;
pickNodes(point: Point, intent: SeriesNodePickIntent, exactMatchOnly?: boolean): PickResult | undefined;
protected pickNodesExactShape(point: Point): SeriesNodeDatum[];
protected pickNodeClosestDatum(_point: Point): SeriesNodePickMatch | undefined;
pickNodeNearestDistantObject<T extends Node & DistantObject>(point: Point, items: Iterable<T>): SeriesNodePickMatch | undefined;
protected pickNodeMainAxisFirst(_point: Point, _requireCategoryAxis: boolean): SeriesNodePickMatch | undefined;
protected pickNodesInBBoxPredicate(): PickNodesInBBoxPredicate;
pickNodesInBBox(selectionBox: BoxBounds): Iterable<TDatum>;
isPointInArea?(x: number, y: number): boolean;
getLabelData(): (TLabel & PointLabelDatum)[];
updatePlacedLabelData(_labels: PlacedLabel<TLabel>[]): void;
private readonly fireEventWrapper;
protected fireEvent<TEvent extends TypedEvent>(event: TEvent): void;
fireNodeClickEvent(event: Event, datum: TDatum): boolean;
fireNodeDoubleClickEvent(event: Event, datum: TDatum): boolean;
createNodeContextMenuActionEvent(event: Event, datum: TDatum): INodeEvent<'nodeContextMenuAction'>;
onLegendInitialState(legendType: ChartLegendType, initialState: AgInitialStateLegendOptions | undefined): void;
onLegendItemClick(event: LegendItemClickEvent): void;
onLegendItemDoubleClick(event: LegendItemDoubleClickEvent): void;
abstract getLegendData<T extends ChartLegendType>(legendType: T): ChartLegendDatum<T>[];
abstract getLegendData(legendType: ChartLegendType): ChartLegendDatum<ChartLegendType>[];
protected toggleSeriesItem(enabled: boolean, legendType: ChartLegendType, itemId: string | number | undefined, legendItemName: string | undefined, legendEvent?: {
legendItemName?: string;
}): void;
isEnabled(): boolean;
getModuleMap(): ModuleMap<SeriesPluginModuleInstance>;
createModuleContext(): DynamicContext<ChartSeriesRegistry>;
protected getAxisValueText(axis: ChartAxis, source: 'tooltip', value: any, datum: any, key: string, legendItemName: string | undefined, allowNull?: boolean): string;
protected getLabelText<TParams extends object>(value: any, datum: any, key: string, property: FormatterPropertyType, domain: any[], label: AxisFormattableLabel<AgChartLabelFormatterParams<any> & RequireOptional<TParams>>, baseParams: RequireOptional<TParams> & Omit<AgChartLabelFormatterParams<any>, 'seriesId'>, allowNullValue?: boolean): NormalisedTextOrSegments;
getMarkerStyle<TParams>(marker: SeriesMarker<TParams>, { datumIndex, datum, point }: Partial<TDatum>, params?: TParams, opts?: {
highlightState?: HighlightState;
/** Pre-resolved by per-datum hot paths to skip the getDataSelectionState() lookup. */
selectionState?: SelectionState;
isHighlight?: boolean;
checkForHighlight?: boolean;
resolveMarkerSubPath?: string[];
resolveStyler?: boolean;
hideWithSize0?: boolean;
}, defaultOverrideStyle?: NormalisedSeriesMarkerStyle & {
size: number;
}, inheritedStyle?: NormalisedSeriesMarkerStyle): Omit<import("ag-charts-types").AgSeriesMarkerStyle & {}, keyof import("ag-charts-core").FillStrokeMorph> & import("ag-charts-core").FillStrokeMorph & {
size: number;
opacity?: number;
};
protected applyMarkerStyle(style: NormalisedSeriesMarkerStyle, markerNode: Marker, point: {
x: number;
y: number;
size?: number;
focusSize?: number;
} | undefined, fillBBox: ShapeFillBBox | undefined, opts: {
applyPosition?: boolean;
crossFilterSelected?: boolean;
hideWithSize0: boolean;
}): void;
/**
* Per-datum marker-style pass with a state-keyed cache, shared across all marker-rendering series.
* Callbacks take `series` and `ctx` explicitly so callers pass static methods (stable function
* identity) and keep V8's inline cache monomorphic across series sharing this helper.
*
* @param opts.keyExtra - optional extra cache-key dimension (e.g. range-area's `datum.itemType`)
* @param opts.cacheable - set false when `compute` returns a value mutated per datum (e.g. bubble + colorScale)
* @param opts.compute - called once per distinct (highlight, selection[, keyExtra]) tuple
* @param opts.apply - called per non-garbage datum with the cached value
*/
protected runMarkerStylePass<TCtx, TPassDatum extends SeriesNodeDatum, TCache, TSeries = this>(datumSelection: Selection<TPassDatum, Marker<TPassDatum>>, isHighlight: boolean, ctx: TCtx, opts: {
keyExtra?: (datum: TPassDatum) => string;
cacheable?: boolean;
compute: MarkerStyleCompute<TSeries, TCtx, TPassDatum, TCache>;
apply: MarkerStyleApply<TSeries, TCtx, TPassDatum, TCache>;
}): void;
/** Default `apply` for the no-itemStyler path — writes the cached style straight onto the datum. */
protected static readonly assignCachedStyle: MarkerStyleApply<unknown, unknown, {
style?: unknown;
}, unknown>;
protected _nodeDataDependencies?: NodeDataDependencies;
get nodeDataDependencies(): NodeDataDependencies;
protected checkResize(newSeriesRect?: BBox): boolean;
pickFocus(_opts: PickFocusInputs): PickFocusOutputs | undefined;
pickViewportFocus(_opts: PickViewportFocusInputs): PickFocusOutputs | undefined;
mapFocusBBoxToPanTarget(_seriesRect: BoxBounds, focusBBox: Readonly<BBox>): BoxBounds;
resetDatumCallbackCache(): void;
cachedDatumCallback<T>(id: any, fn: () => T): T | undefined;
cachedCallWithContext<F extends Callback>(fn: F, params: CallbackParam<F>, cacheKey?: string): ReturnType<F> | undefined;
callWithContext<F extends Callback>(fn: F, params: CallbackParam<F>): ReturnType<F>;
protected formatTooltipWithContext<P extends AgSeriesTooltipRendererParams<any>, Tooltip extends SeriesTooltip<P>>(tooltip: Tooltip, content: TooltipStructuredContent, params: RequireOptional<P>): TooltipContent;
abstract getCategoryValue(datumIndex: DatumIndex): any;
abstract datumIndexForCategoryValue(categoryValue: any): DatumIndex | undefined;
minTimeInterval(): number | undefined;
needsDataModelDiff(): boolean;
}