@devexperts/dxcharts-lite
Version:
277 lines (276 loc) • 12.7 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 { Subject } from 'rxjs';
import { CanvasElement } from '../../canvas/canvas-bounds-container';
import { ChartBaseElement } from '../../model/chart-base-element';
import { uuid } from '../../utils/uuid.utils';
import { resolveColorForArea, resolveColorForBar, resolveColorForBaseLine, resolveColorForCandle, resolveColorForHistogram, resolveColorForLine, resolveColorForScatterPlot, resolveColorForTrendAndHollow, resolveDefaultColorForLabel, } from './label-color.functions';
import { LabelsGroups } from './price_labels/y-axis-labels.model';
import { YAxisScaleHandler } from './y-axis-scale.handler';
import { YAxisModel } from './y-axis.model';
import { merge as mergeObj } from '../../utils/merge.utils';
import { cloneUnsafe } from '../../utils/object.utils';
/**
* Y axis component. Contains all Y axis related logic.
*/
export class YAxisComponent extends ChartBaseElement {
constructor(eventBus, config, mainCanvasModel, canvasModel, scale, canvasInputListeners, canvasBoundsContainer, chartPanComponent, cursors, valueFormatter, dataSeriesProvider, paneUUID, extentIdx, hitTestCanvasModel, chartResizeHandler, initialState) {
super();
this.eventBus = eventBus;
this.config = config;
this.mainCanvasModel = mainCanvasModel;
this.canvasModel = canvasModel;
this.scale = scale;
this.canvasInputListeners = canvasInputListeners;
this.canvasBoundsContainer = canvasBoundsContainer;
this.chartPanComponent = chartPanComponent;
this.cursors = cursors;
this.paneUUID = paneUUID;
this.extentIdx = extentIdx;
this.hitTestCanvasModel = hitTestCanvasModel;
this.chartResizeHandler = chartResizeHandler;
this.labelsColorByChartTypeMap = {};
this.axisTypeSetSubject = new Subject();
this.axisAlignSetSubject = new Subject();
this.axisAlignMovedSubject = new Subject();
/* 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 auto scale on YAxis
*/
this.setDblClickCallback = (cb) => this.yAxisScaleHandler.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 auto scale on YAxis
*/
this.setDblTapCallback = (cb) => this.yAxisScaleHandler.setDblTapCallback(cb);
const initialStateCopy = initialState ? cloneUnsafe(initialState) : {};
this.state = mergeObj(initialStateCopy, config.components.yAxis, {
overrideExisting: false,
addIfMissing: true,
});
//#region init yAxisScaleHandler
this.yAxisScaleHandler = new YAxisScaleHandler(eventBus, this.state, chartPanComponent, scale, canvasInputListeners, canvasBoundsContainer, canvasBoundsContainer.getBoundsHitTest(CanvasElement.PANE_UUID_Y_AXIS(paneUUID, extentIdx)), hitTestCanvasModel);
this.addChildEntity(this.yAxisScaleHandler);
//#endregion
this.model = new YAxisModel(this.paneUUID, eventBus, this.state, canvasBoundsContainer, canvasModel, scale, valueFormatter, dataSeriesProvider, extentIdx, this.chartResizeHandler);
this.addChildEntity(this.model);
this.updateCursor();
this.registerDefaultLabelColorResolvers();
}
setExtentIdx(extentIdx) {
this.extentIdx = extentIdx;
this.model.extentIdx = extentIdx;
this.yAxisScaleHandler.deactivate();
this.removeChildEntity(this.yAxisScaleHandler);
this.yAxisScaleHandler = new YAxisScaleHandler(this.eventBus, this.state, this.chartPanComponent, this.scale, this.canvasInputListeners, this.canvasBoundsContainer, this.canvasBoundsContainer.getBoundsHitTest(CanvasElement.PANE_UUID_Y_AXIS(this.paneUUID, extentIdx)), this.hitTestCanvasModel);
this.addChildEntity(this.yAxisScaleHandler);
}
/**
* Registers default label color resolvers for different chart types.
* @private
* @function
* @name registerDefaultLabelColorResolver
* @returns {void}
*/
registerDefaultLabelColorResolvers() {
this.registerLabelColorResolver('candle', resolveColorForCandle);
this.registerLabelColorResolver('bar', resolveColorForBar);
this.registerLabelColorResolver('line', resolveColorForLine);
this.registerLabelColorResolver('area', resolveColorForArea);
this.registerLabelColorResolver('scatterPlot', resolveColorForScatterPlot);
this.registerLabelColorResolver('histogram', resolveColorForHistogram);
this.registerLabelColorResolver('baseline', resolveColorForBaseLine);
this.registerLabelColorResolver('trend', resolveColorForTrendAndHollow);
this.registerLabelColorResolver('hollow', resolveColorForTrendAndHollow);
}
doActivate() {
this.addRxSubscription(this.scale.beforeStartAnimationSubject.subscribe(() => this.state.type === 'percent' && this.scale.haltAnimation()));
}
updateCursor() {
if (this.state.type === 'percent') {
this.cursors.setCursorForCanvasEl(CanvasElement.PANE_UUID_Y_AXIS(this.paneUUID, this.extentIdx), this.state.resizeDisabledCursor);
}
else {
this.cursors.setCursorForCanvasEl(CanvasElement.PANE_UUID_Y_AXIS(this.paneUUID, this.extentIdx), this.state.cursor);
}
}
/**
* Updates labels visual appearance on canvas
*/
updateOrderedLabels(adjustYAxisWidth = false) {
this.model.fancyLabelsModel.updateLabels(adjustYAxisWidth);
}
/**
* Registers a label color resolver for a specific chart type.
*
* @param {BarType} chartType - The type of chart for which the label color resolver is being registered.
* @param {LabelColorResolver} resolver - The function that will be used to resolve the color of the labels for the specified chart type.
* @returns {void}
*/
registerLabelColorResolver(chartType, resolver) {
this.labelsColorByChartTypeMap[chartType] = resolver;
}
/**
* Returns a function that resolves the color for a label based on the type of data series.
* @param {DataSeriesType} candlesType - The type of data series.
* @returns {Function} - A function that resolves the color for a label.
* If there is no color mapping for the given data series type, it returns the default color resolver function.
*/
getLabelsColorResolver(candlesType) {
var _a;
return (_a = this.labelsColorByChartTypeMap[candlesType]) !== null && _a !== void 0 ? _a : resolveDefaultColorForLabel;
}
//#region public methods
/**
* You can add a custom labels provider for additional labels on YAxis (like for drawings, symbol last price, studies, etc..)
* @param groupName - a group in which labels position recalculation algorithm will be applied, usually it's subchart name
* @param provider
* @param id
*/
registerYAxisLabelsProvider(provider, groupName = LabelsGroups.MAIN, id = uuid()) {
this.model.fancyLabelsModel.registerYAxisLabelsProvider(groupName, provider, id);
return id;
}
/**
* An easier way to manage custom y-axis labels, than y-axis labels providers.
* However, overlapping avoidance is not supported
* deprecated because of naming, use updateCustomYAxisLabel instead
* @param name
* @param label
*/
addSimpleYAxisLabel(name, label) {
this.model.fancyLabelsModel.customLabels[name] = label;
this.canvasModel.fireDraw();
}
/**
* Update custom y-axis label
* @param name
* @param label
*/
updateCustomYAxisLabel(name, label) {
this.model.fancyLabelsModel.customLabels[name] = label;
this.canvasModel.fireDraw();
}
/**
* @param name
*/
deleteSimpleYAxisLabel(name) {
delete this.model.fancyLabelsModel.customLabels[name];
this.canvasModel.fireDraw();
}
getAxisType() {
return this.state.type;
}
/**
* Unregister a Y axis labels provider from the specified group.
* @param {string} groupName - The name of the group from which to unregister the provider. Defaults to LabelsGroups.MAIN.
* @param {string} id - The ID of the provider to unregister.
* @returns {string} - The ID of the unregistered provider.
*/
unregisterYAxisLabelsProvider(groupName = LabelsGroups.MAIN, id) {
this.model.fancyLabelsModel.unregisterYAxisLabelsProvider(groupName, id);
return id;
}
getBounds() {
return this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID_Y_AXIS(this.paneUUID, this.extentIdx));
}
/**
* If custom pane has y-axis it has to register width contributor to correctly calculate overall y-axis width.
* @param contributor
*/
registerYAxisWidthContributor(contributor) {
this.canvasBoundsContainer.yAxisBoundsContainer.registerYAxisWidthContributor(contributor);
}
/**
* Sets the type of axis: percent, regular or logarithmic.
* @param type - the type of axis
*/
setAxisType(type) {
if (type !== this.state.type) {
this.state.type = type;
this.config.components.yAxis.type = type;
this.axisTypeSetSubject.next(type);
this.scale.autoScale(true);
this.model.fancyLabelsModel.updateLabels(true);
this.updateCursor();
this.mainCanvasModel.fireDraw();
}
}
/**
* Change YAxis position to left or to right
* @param align
*/
setYAxisAlign(align) {
this.state.align = align;
this.canvasBoundsContainer.updateYAxisWidths();
this.axisAlignSetSubject.next(align);
this.eventBus.fireDraw();
}
/**
* Controls visibility of the y-axis (additionally disable/enable component)
*/
setVisible(isVisible) {
this.state.visible = isVisible;
this.config.components.yAxis.visible = isVisible;
isVisible ? this.activate() : this.deactivate();
this.model.fancyLabelsModel.updateLabels();
this.model.baseLabelsModel.updateLabels();
}
/**
* If visible, when you can see the y-axis on the chart
*/
isVisible() {
return this.state.visible;
}
/**
* Controls lockPriceToBarRatio of the y-axis
*/
setLockPriceToBarRatio(value = false) {
this.scale.setLockPriceToBarRatio(value);
}
/**
* Changes the visual type of particular label.
* @param type - label type
* @param mode - visual mode
*/
changeLabelMode(type, mode) {
this.state.labels.settings[type].mode = mode;
this.model.fancyLabelsModel.updateLabels();
}
/**
* Changes the visual type of particular label.
* @param type - label type
* @param mode - visual mode
*/
changeLabelAppearance(type, mode) {
this.state.labels.settings[type].type = mode;
this.model.fancyLabelsModel.updateLabels();
}
/**
* Sets the inverse price scale mode. Inverts Y axis vertically.
* Inversion also works for candles, drawings and overlay studies.
* @param inverse - true or false
*/
togglePriceScaleInverse(inverse = false) {
this.scale.state.inverse = inverse;
this.scale.inverseY = inverse;
this.model.fancyLabelsModel.updateLabels();
this.scale.scaleInversedSubject.next(inverse);
this.canvasModel.fireDraw();
}
/**
* Changes the visibility of the labels' descriptions.
* @param {boolean} descVisibility - A boolean value indicating whether the descriptions should be visible or not.
* @returns {void}
*/
changeLabelsDescriptionVisibility(descVisibility) {
this.state.labels.descriptions = descVisibility;
// recalculating labels is not needed, so just redraw YAxis
this.canvasModel.fireDraw();
}
}