UNPKG

@devexperts/dxcharts-lite

Version:
140 lines (139 loc) 8.16 kB
/* * 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; } }