@devexperts/dxcharts-lite
Version:
140 lines (139 loc) • 8.16 kB
JavaScript
/*
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { merge, animationFrameScheduler } from 'rxjs';
import { distinctUntilChanged, map, throttleTime, filter } from 'rxjs/operators';
import { CanvasElement } from '../../canvas/canvas-bounds-container';
import { ChartBaseElement } from '../../model/chart-base-element';
import { CompositeDrawer } from '../../drawers/composite.drawer';
import { XAxisLabelsDrawer } from './x-axis-labels.drawer';
import { XAxisTimeLabelsGenerator } from './x-axis-labels.generator';
import { XAxisLabelsModel } from './x-axis-labels.model';
import { XAxisScaleHandler } from './x-axis-scale.handler';
import { XAxisTimeLabelsDrawer } from './x-axis-time-labels.drawer';
import { lastOf } from '../../utils/array.utils';
import { notEmpty } from '../../utils/function.utils';
import { availableBarTypes } from '../../chart.config';
/**
* X-axis component, contains all x-axis calculation and rendering logic.
*/
export class XAxisComponent extends ChartBaseElement {
constructor(eventBus, config, canvasModel, chartComponent, scale, canvasBoundsContainer, canvasInputListener, chartResizeHandler, drawingManager, timeZoneModel, chartPanComponent, cursorHandler, hitTestCanvasModel) {
super();
this.eventBus = eventBus;
this.config = config;
this.canvasModel = canvasModel;
this.chartComponent = chartComponent;
this.scale = scale;
this.chartResizeHandler = chartResizeHandler;
this.timeZoneModel = timeZoneModel;
/* This function assigns a callback to be executed when a double-click event is detected.
* It accepts one parameter `cb`, which is a callback function.
* When a double-click event occurs, the specified callback function `cb` will be invoked.
* By default it calls basic scale on XAxis
*/
this.setDblClickCallback = (cb) => this.xAxisScaleHandler.setDblClickCallback(cb);
/* This function assigns a callback to be executed when a double-tap event is detected.
* Similar to the dblclick function, it takes one parameter `cb`, which is a callback function.
* This function ensures that the callback `cb` is called whenever a double-tap event is triggered.
* By default it calls basic scale on XAxis
*/
this.setDblTapCallback = (cb) => this.xAxisScaleHandler.setDblTapCallback(cb);
const xAxisLabelsGenerator = new XAxisTimeLabelsGenerator(eventBus, config, chartComponent.chartModel, scale, timeZoneModel, this.canvasModel, canvasBoundsContainer);
this.xAxisLabelsGenerator = xAxisLabelsGenerator;
this.xAxisLabelsModel = new XAxisLabelsModel(eventBus, []);
const xAxisCompositeDrawer = new CompositeDrawer();
drawingManager.addDrawer(xAxisCompositeDrawer, 'X_AXIS');
this.xAxisDrawer = new XAxisTimeLabelsDrawer(config, canvasModel, scale, canvasBoundsContainer, () => this.xAxisLabelsGenerator.labels, () => config.components.xAxis.visible);
xAxisCompositeDrawer.addDrawer(this.xAxisDrawer);
this.xAxisLabelsDrawer = new XAxisLabelsDrawer(config, canvasModel, canvasBoundsContainer, this.xAxisLabelsModel);
xAxisCompositeDrawer.addDrawer(this.xAxisLabelsDrawer);
this.xAxisScaleHandler = new XAxisScaleHandler(scale, canvasInputListener, canvasBoundsContainer, chartPanComponent, this.chartComponent.chartModel, canvasBoundsContainer.getBoundsHitTest(CanvasElement.X_AXIS), hitTestCanvasModel);
this.addChildEntity(this.xAxisScaleHandler);
cursorHandler.setCursorForCanvasEl(CanvasElement.X_AXIS, config.components.xAxis.cursor);
}
/**
* This method is used to activate the chart and update the labels if there is a new data set or equivolume type.
* It subscribes to the chart type change, candles set subject, candles updated subject, and time zone change to generate new labels.
* It also subscribes to the x-axis scale change and canvas resize to recalculate the labels.
* @protected
* @returns {void}
*/
doActivate() {
super.doActivate();
// do update labels if new data set
this.addRxSubscription(merge(this.chartComponent.chartModel.candlesSetSubject, this.timeZoneModel.observeTimeZoneChanged()).subscribe(() => {
this.xAxisLabelsGenerator.generateLabels();
}));
this.addRxSubscription(this.chartComponent.chartModel.candlesPrependSubject
.pipe(filter(({ prependedCandles }) => prependedCandles.length !== 0), map(({ prependedCandles }) => this.chartComponent.chartModel.mainCandleSeries.visualPoints.slice(0, prependedCandles.length)))
.subscribe(newCandles => {
var _a, _b;
//@ts-ignore
if (availableBarTypes.includes(this.config.components.chart.type)) {
(_b = (_a = this.xAxisLabelsGenerator).updateHistoryLabels) === null || _b === void 0 ? void 0 : _b.call(_a, newCandles);
}
}));
// recalculate existing ones, the number of labels is static
if (this.config.components.chart.minCandlesOffset) {
this.addRxSubscription(merge(this.scale.xChanged, this.chartResizeHandler.canvasResized)
.pipe(throttleTime(50, animationFrameScheduler, { trailing: true, leading: true }))
.subscribe(() => this.xAxisLabelsGenerator.recalculateLabels()));
// generate visible viewport range labels (real and fake candles), the number of labels is dynamic
}
else {
this.addRxSubscription(merge(this.scale.xChanged, this.chartResizeHandler.canvasResized)
.pipe(throttleTime(150, animationFrameScheduler, { trailing: true, leading: true }))
.subscribe(() => this.xAxisLabelsGenerator.generateLabels(undefined, true)));
}
this.addRxSubscription(this.chartComponent.chartModel.candlesUpdatedSubject
.pipe(map(() => lastOf(this.chartComponent.chartModel.mainCandleSeries.visualPoints)), distinctUntilChanged((a, b) => { var _a, _b; return ((_a = a === null || a === void 0 ? void 0 : a.candle) === null || _a === void 0 ? void 0 : _a.id) === ((_b = b === null || b === void 0 ? void 0 : b.candle) === null || _b === void 0 ? void 0 : _b.id); }), filter(notEmpty))
.subscribe(x => { var _a, _b; return (_b = (_a = this.xAxisLabelsGenerator) === null || _a === void 0 ? void 0 : _a.updateLastLabel) === null || _b === void 0 ? void 0 : _b.call(_a, x); }));
}
/**
* Returns the xAxisDrawer object.
*
* @returns {Object} The xAxisDrawer object.
*/
getDrawer() {
return this.xAxisDrawer;
}
//#region public methods
/**
* You can add a custom labels provider for additional labels on XAxis (like for drawings)
* @param provider
*/
registerXAxisLabelsProvider(provider) {
this.xAxisLabelsModel.labelProviders.push(provider);
}
/**
* Controls visibility of the x-axis
*/
setVisible(isVisible) {
var _a;
if ((_a = this.config.components) === null || _a === void 0 ? void 0 : _a.xAxis) {
this.config.components.xAxis.visible = isVisible;
this.eventBus.fireDraw();
}
}
/**
* Set new config for x labels formatting
*/
setFormatsForLabelsConfig(newFormatsByWeightMap) {
if (this.xAxisLabelsGenerator instanceof XAxisTimeLabelsGenerator) {
this.xAxisLabelsGenerator.setFormatsForLabelsConfig(newFormatsByWeightMap);
}
else {
console.error('Format config for x-axis is not available');
}
}
/**
* If visible, when you can see the x-axis on the chart
*/
isVisible() {
var _a, _b;
return (_b = (_a = this.config.components) === null || _a === void 0 ? void 0 : _a.xAxis.visible) !== null && _b !== void 0 ? _b : false;
}
}