Skip to content

Commit

Permalink
Merge pull request #3396 from ag-grid/imoses/core-utils-3
Browse files Browse the repository at this point in the history
Move global utils to `ag-charts-core` (3)
  • Loading branch information
alantreadway authored Jan 21, 2025
2 parents 6814689 + 0273e04 commit 46f4d9c
Show file tree
Hide file tree
Showing 28 changed files with 235 additions and 213 deletions.
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];
}
57 changes: 45 additions & 12 deletions packages/ag-charts-community/src/chart/series/seriesAreaManager.ts
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

0 comments on commit 46f4d9c

Please sign in to comment.