Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move global utils to ag-charts-core (3) #3396

Merged
merged 8 commits into from
Jan 21, 2025
8 changes: 1 addition & 7 deletions packages/ag-charts-community/src/chart/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,6 @@ type SeriesChangeType =
| 'series-count-changed'
| 'updated';

type ObservableLike = {
addEventListener(key: string, cb: TypedEventListener): void;
clearEventListeners(): void;
};

class SeriesArea extends BaseProperties {
@Validate(BOOLEAN, { optional: true })
clip?: boolean;
Expand Down Expand Up @@ -1077,7 +1072,6 @@ export abstract class Chart extends Observable {
}

private filterMiniChartSeries(series: AgChartOptions['series'] | undefined): AgChartOptions['series'] | undefined;
private filterMiniChartSeries(series: AgChartOptions['series']): AgChartOptions['series'];
private filterMiniChartSeries(series: any[] | undefined): any[] | undefined {
return series?.filter((s) => s.showInMiniChart !== false);
}
Expand Down Expand Up @@ -1547,7 +1541,7 @@ export abstract class Chart extends Observable {
}
}

private registerListeners(source: ObservableLike, listeners: Record<string, TypedEventListener>) {
private registerListeners(source: Observable, listeners: Record<string, TypedEventListener>) {
source.clearEventListeners();
for (const [property, listener] of Object.entries(listeners)) {
if (isFunction(listener)) {
Expand Down
3 changes: 0 additions & 3 deletions packages/ag-charts-community/src/chart/chartContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import type { ChartType } from './factory/chartTypes';
import { AnimationManager } from './interaction/animationManager';
import { ChartEventManager } from './interaction/chartEventManager';
import { ContextMenuRegistry } from './interaction/contextMenuRegistry';
import { CursorManager } from './interaction/cursorManager';
import { HighlightManager } from './interaction/highlightManager';
import { InteractionManager } from './interaction/interactionManager';
import type { SyncManager } from './interaction/syncManager';
Expand Down Expand Up @@ -52,7 +51,6 @@ export class ChartContext implements ModuleContext {
chartService: ChartService;
chartTypeOriginator: ChartTypeOriginator;
contextMenuRegistry: ContextMenuRegistry;
cursorManager: CursorManager;
dataService: DataService<any>;
domManager: DOMManager;
historyManager: HistoryManager;
Expand Down Expand Up @@ -114,7 +112,6 @@ export class ChartContext implements ModuleContext {
this.legendManager = new LegendManager();
this.annotationManager = new AnnotationManager(chart.annotationRoot);
this.chartTypeOriginator = new ChartTypeOriginator(chart);
this.cursorManager = new CursorManager(this.domManager);
this.interactionManager = new InteractionManager();
this.contextMenuRegistry = new ContextMenuRegistry();
this.updateService = new UpdateService(updateCallback);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Logger } from 'ag-charts-core';
import { EventEmitter, type EventListener, Logger } from 'ag-charts-core';

import { getWindow } from '../../core';
import type { AdditionalAnimationOptions, AnimationOptions, AnimationValue, IAnimation } from '../../motion/animation';
import { Animation } from '../../motion/animation';
import { Debug } from '../../util/debug';
import { EventEmitter, type EventListener } from '../../util/eventEmitter';
import type { Mutex } from '../../util/mutex';
import { AnimationBatch } from './animationBatch';
import { InteractionManager, InteractionState } from './interactionManager';
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import type { DOMManager } from '../../dom/domManager';
import { StateTracker } from '../../util/stateTracker';
import type { MouseWidgetEvent } from '../../widget/widgetEvents';
import type { SeriesTooltip } from '../series/seriesTooltip';
import {
type ErrorBoundSeriesNodeDatum,
type ISeries,
type SeriesNodeDatum,
getDatumRefPoint,
} from '../series/seriesTypes';
import type { ErrorBoundSeriesNodeDatum, ISeries, SeriesNodeDatum } from '../series/seriesTypes';
import { getDatumRefPoint } from '../series/util';
import type { Tooltip, TooltipContent, TooltipMeta, TooltipPointerEvent } from '../tooltip/tooltip';

interface TooltipState {
Expand Down
3 changes: 2 additions & 1 deletion packages/ag-charts-community/src/chart/keyboardUtil.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { BBox } from '../scene/bbox';
import type { Path } from '../scene/shape/path';
import { Transformable } from '../scene/transformable';
import { type ISeries, getDatumRefPoint } from './series/seriesTypes';
import type { ISeries } from './series/seriesTypes';
import { getDatumRefPoint } from './series/util';
import type { TooltipPointerEvent } from './tooltip/tooltip';

type PickProperties = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { EventEmitter, type EventListener } from 'ag-charts-core';

import type { LayoutContext as ILayoutContext } from '../../module/baseModule';
import type { Scale } from '../../scale/scale';
import { BBox } from '../../scene/bbox';
import { EventEmitter, type EventListener } from '../../util/eventEmitter';
import type { TimeInterval } from '../../util/time';
import type { ChartAxisDirection } from '../chartAxisDirection';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { findMaxIndex, findMinIndex } from 'ag-charts-core';

import type { AnimationValue } from '../../../motion/animation';
import { resetMotion } from '../../../motion/resetMotion';
import { BandScale } from '../../../scale/bandScale';
Expand All @@ -16,7 +18,10 @@ import { Debug } from '../../../util/debug';
import { StateMachine } from '../../../util/stateMachine';
import { BOOLEAN, STRING, Validate } from '../../../util/validation';
import { CategoryAxis } from '../../axis/categoryAxis';
import { NumberAxis } from '../../axis/numberAxis';
import { TimeAxis } from '../../axis/timeAxis';
import type { ChartAnimationPhase } from '../../chartAnimationPhase';
import type { ChartAxis } from '../../chartAxis';
import { ChartAxisDirection } from '../../chartAxisDirection';
import { Marker } from '../../marker/marker';
import {
Expand All @@ -33,7 +38,7 @@ import type {
import { SeriesNodeEvent } from '../series';
import { SeriesProperties } from '../seriesProperties';
import type { ISeries, SeriesNodeDatum } from '../seriesTypes';
import { axisExtent, clippedRangeIndices, countExpandingSearch, visibleRangeIndices } from '../util';
import { countExpandingSearch, visibleRangeIndices } from '../util';
import type { Scaling } from './scaling';

export interface CartesianSeriesNodeDatum extends DataModelSeriesNodeDatum {
Expand Down Expand Up @@ -1121,3 +1126,42 @@ export abstract class CartesianSeries<
return result;
}
}

function axisExtent(axis: ChartAxis): [number | Date, number | Date] | undefined {
let min: number | Date | undefined;
let max: number | Date | undefined;
if (axis instanceof NumberAxis && (Number.isFinite(axis.min) || Number.isFinite(axis.max))) {
min = Number.isFinite(axis.min) ? axis.min : undefined;
max = Number.isFinite(axis.max) ? axis.max : undefined;
} else if (axis instanceof TimeAxis && (axis.min != null || axis.max != null)) {
({ min, max } = axis);
}

if (min == null && max == null) return;

min ??= -Infinity;
max ??= Infinity;

return [min, max];
}

function clippedRangeIndices(length: number, range: [any, any], xValue: (index: number) => any): [number, number] {
const range0 = range[0].valueOf();
const range1 = range[1].valueOf();

const xMinIndex = findMinIndex(0, length - 1, (index) => {
const x = xValue(index)?.valueOf();
return !Number.isFinite(x) || x >= range0;
});

let xMaxIndex = findMaxIndex(0, length - 1, (index) => {
const x = xValue(index)?.valueOf();
return !Number.isFinite(x) || x! <= range1;
});

if (xMinIndex == null || xMaxIndex == null) return [0, 0];

xMaxIndex = Math.min(xMaxIndex + 1, length);

return [xMinIndex, xMaxIndex];
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FocusIndicator } from '../../dom/focusIndicator';
import { FocusSwapChain } from '../../dom/focusSwapChain';
import { BBox } from '../../scene/bbox';
import type { TranslatableGroup } from '../../scene/group';
import type { Point } from '../../scene/point';
import { Transformable } from '../../scene/transformable';
import { BaseManager } from '../../util/baseManager';
import { createId } from '../../util/id';
Expand Down Expand Up @@ -31,10 +32,9 @@ import type { LayoutCompleteEvent } from '../layout/layoutManager';
import type { ChartOverlays } from '../overlay/chartOverlays';
import { DEFAULT_TOOLTIP_CLASS, Tooltip, type TooltipContent, tooltipContentAriaLabel } from '../tooltip/tooltip';
import type { UpdateOpts } from '../updateService';
import { type PickFocusOutputs, type Series } from './series';
import { type PickFocusOutputs, type Series, type SeriesNodePickIntent } from './series';
import type { SeriesProperties } from './seriesProperties';
import type { ISeries, SeriesNodeDatum } from './seriesTypes';
import { pickNode } from './util';

export interface SeriesAreaChartDependencies {
fireEvent<TEvent extends TypedEvent>(event: TEvent): void;
Expand All @@ -51,6 +51,12 @@ export interface SeriesAreaChartDependencies {
type TooltipWidgetEvent = MouseWidgetEvent<'mousemove' | 'click' | 'dblclick'>;
type HighlightWidgetEvent = MouseWidgetEvent<'mousemove' | 'click' | 'dblclick'> | DragWidgetEvent<'drag-move'>;

type PickedNode = {
series: Series<unknown, any, any>;
datum: SeriesNodeDatum<unknown>;
distance: number;
};

export class SeriesAreaManager extends BaseManager {
readonly id = createId(this);

Expand Down Expand Up @@ -231,7 +237,7 @@ export class SeriesAreaManager extends BaseManager {
);
}
} else if (this.isState(InteractionState.ContextMenuable)) {
const match = pickNode(this.series, { x: event.currentX, y: event.currentY }, 'context-menu');
const match = this.pickNode({ x: event.currentX, y: event.currentY }, 'context-menu');
if (match) {
this.chart.ctx.highlightManager.updateHighlight(this.id);
pickedNode = match.datum;
Expand Down Expand Up @@ -264,7 +270,7 @@ export class SeriesAreaManager extends BaseManager {
return;
}

this.chart.ctx.cursorManager.updateCursor(this.id);
this.chart.ctx.domManager.updateCursor(this.id);
if (!this.focusIndicator.isFocusVisible()) this.clearAll();
}

Expand Down Expand Up @@ -296,11 +302,11 @@ export class SeriesAreaManager extends BaseManager {

if (this.isState(InteractionState.Default)) {
const { currentX: x, currentY: y } = event;
const found = pickNode(this.series, { x, y }, 'event');
const found = this.pickNode({ x, y }, 'event');
if (found?.series.hasEventListener('nodeClick') || found?.series.hasEventListener('nodeDoubleClick')) {
this.chart.ctx.cursorManager.updateCursor(this.id, 'pointer');
this.chart.ctx.domManager.updateCursor(this.id, 'pointer');
} else {
this.chart.ctx.cursorManager.updateCursor(this.id);
this.chart.ctx.domManager.updateCursor(this.id);
}
}
}
Expand Down Expand Up @@ -408,7 +414,7 @@ export class SeriesAreaManager extends BaseManager {
}

private checkSeriesNodeClick(event: MouseWidgetEvent<'click' | 'dblclick'> & { preventZoomDblClick?: boolean }) {
const result = pickNode(this.series, { x: event.currentX, y: event.currentY }, 'event');
const result = this.pickNode({ x: event.currentX, y: event.currentY }, 'event');
if (result == null) return false;

if (event.type === 'click') {
Expand Down Expand Up @@ -608,7 +614,7 @@ export class SeriesAreaManager extends BaseManager {

const { range } = this.chart.highlight;
const intent = range === 'tooltip' ? 'highlight-tooltip' : 'highlight';
const found = pickNode(this.series, { x: currentX, y: currentY }, intent);
const found = this.pickNode({ x: currentX, y: currentY }, intent);
if (found) {
this.chart.ctx.highlightManager.updateHighlight(this.id, found.datum);
this.hoverDevice = 'mouse';
Expand Down Expand Up @@ -639,7 +645,7 @@ export class SeriesAreaManager extends BaseManager {
return;
}

const pick = pickNode(this.series, { x: event.currentX, y: event.currentY }, 'tooltip');
const pick = this.pickNode({ x: event.currentX, y: event.currentY }, 'tooltip');
if (!pick) {
if (this.hoverDevice == 'mouse') this.clearTooltip();
return;
Expand Down Expand Up @@ -670,10 +676,10 @@ export class SeriesAreaManager extends BaseManager {

// Adjust cursor if a specific datum is highlighted, rather than just a series.
if (lastSeries?.properties.cursor && lastDatum) {
this.chart.ctx.cursorManager.updateCursor(lastSeries.id);
this.chart.ctx.domManager.updateCursor(lastSeries.id);
}
if (newSeries?.properties.cursor && newSeries?.properties.cursor !== 'default' && newDatum) {
this.chart.ctx.cursorManager.updateCursor(newSeries.id, newSeries.properties.cursor);
this.chart.ctx.domManager.updateCursor(newSeries.id, newSeries.properties.cursor);
}

const updateAll = newSeries == null || lastSeries == null;
Expand All @@ -683,6 +689,33 @@ export class SeriesAreaManager extends BaseManager {
this.update(ChartUpdateType.SERIES_UPDATE, { seriesToUpdate });
}
}

private pickNode(point: Point, intent: SeriesNodePickIntent, exactMatchOnly?: boolean): PickedNode | undefined {
// Iterate through series in reverse, as later declared series appears on top of earlier
// declared series.
const reverseSeries = [...this.series].reverse();

let result:
| { series: Series<unknown, any, any>; datum: SeriesNodeDatum<unknown>; distance: number }
| undefined;
for (const series of reverseSeries) {
if (!series.visible || !series.contentGroup.visible) {
continue;
}
const { match, distance } = series.pickNode(point, intent, exactMatchOnly) ?? {};
if (!match || distance == null) {
continue;
}
if (!result || result.distance > distance) {
result = { series, distance, datum: match };
}
if (distance === 0) {
break;
}
}

return result;
}
}

function excludesType<T extends string, O extends { type: T }, X extends T>(
Expand Down
14 changes: 0 additions & 14 deletions packages/ag-charts-community/src/chart/series/seriesTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { AgContextMenuOptions } from 'ag-charts-types';
import type { BBox } from '../../scene/bbox';
import type { Group } from '../../scene/group';
import type { Point, SizedPoint } from '../../scene/point';
import { Transformable } from '../../scene/transformable';
import type { PlacedLabel, PointLabelDatum } from '../../scene/util/labelPlacement';
import type { ChartAxisDirection } from '../chartAxisDirection';
import type { ChartLegendDatum, ChartLegendType } from '../legend/legendDatum';
Expand Down Expand Up @@ -78,16 +77,3 @@ export interface ErrorBoundSeriesNodeDatum {

export type NodeDataDependencies = { seriesRectWidth: number; seriesRectHeight: number };
export type NodeDataDependant = { readonly nodeDataDependencies: NodeDataDependencies };

export function getDatumRefPoint(
series: ISeries<any, any>,
datum: SeriesNodeDatum<unknown> & Pick<ErrorBoundSeriesNodeDatum, 'yBar'>
): { canvasX: number; canvasY: number } | undefined {
// On `line` and `scatter` series, the tooltip covers the top of error-bars when using datum.midPoint.
// Using datum.yBar.upperPoint renders the tooltip higher up.
const refPoint = datum.yBar?.upperPoint ?? datum.midPoint ?? series.datumMidPoint?.(datum);
if (refPoint) {
const { x, y } = Transformable.toCanvasPoint(series.contentGroup, refPoint.x, refPoint.y);
return { canvasX: Math.round(x), canvasY: Math.round(y) };
}
}
Loading
Loading