@devexperts/dxcharts-lite
Version:
118 lines (117 loc) • 6.55 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 } from 'rxjs';
import { filter, pairwise } from 'rxjs/operators';
import { ChartBaseElement } from '../../model/chart-base-element';
import { CanvasElement } from '../../canvas/canvas-bounds-container';
import { EVENT_RESIZED } from '../../events/events';
import { NavigationMapDrawer } from './navigation-map.drawer';
import { NavigationMapMoveHandler } from './navigation-map-move.handler';
import { floor } from '../../utils/math.utils';
/**
* Navigation map component for chart.
* Controls navigation map in the bottom.
*/
export class NavigationMapComponent extends ChartBaseElement {
constructor(eventBus, chartModel, canvasModel, config, canvasInputListeners, canvasBoundsContainer, drawingManager, formatterFactory, chartPanComponent, cursorHandler) {
super();
this.eventBus = eventBus;
this.chartModel = chartModel;
this.canvasModel = canvasModel;
this.config = config;
this.canvasInputListeners = canvasInputListeners;
this.canvasBoundsContainer = canvasBoundsContainer;
this.chartPanComponent = chartPanComponent;
this.visualCandles = [];
const navigationMapDrawer = new NavigationMapDrawer(config, chartModel, canvasModel, canvasBoundsContainer, formatterFactory, () => this.visualCandles);
drawingManager.addDrawer(navigationMapDrawer, CanvasElement.N_MAP_CHART);
this.navigationMapMoveHandler = new NavigationMapMoveHandler(this.eventBus, this.chartModel, this.chartModel.scale, this.canvasInputListeners, this.canvasBoundsContainer, this.chartPanComponent);
cursorHandler.setCursorForCanvasEl(CanvasElement.N_MAP_CHART, config.components.navigationMap.cursors.chart);
cursorHandler.setCursorForCanvasEl(CanvasElement.N_MAP_BTN_L, config.components.navigationMap.cursors.buttonLeft);
cursorHandler.setCursorForCanvasEl(CanvasElement.N_MAP_BTN_R, config.components.navigationMap.cursors.buttonRight);
cursorHandler.setCursorForCanvasEl(CanvasElement.N_MAP_KNOT_L, config.components.navigationMap.cursors.leftResizer);
cursorHandler.setCursorForCanvasEl(CanvasElement.N_MAP_KNOT_R, config.components.navigationMap.cursors.rightResizer);
cursorHandler.setCursorForCanvasEl(CanvasElement.N_MAP_SLIDER_WINDOW, config.components.navigationMap.cursors.slider);
}
/**
* Method to activate the chart. It subscribes to the observables of the chartModel and canvasBoundsContainer.
* It also subscribes to the xChanged observable of the chartModel's scaleModel and filters the values to check
* if the previous viewport had no-candles area and current viewport contains only candles or if the current viewport
* has no-candles area. If the navigationMap component is visible, it makes visual candles and fires the draw event
* of the canvasModel.
*/
doActivate() {
super.doActivate();
this.addRxSubscription(merge(this.chartModel.observeCandlesChanged(), this.canvasBoundsContainer.observeBoundsChanged(CanvasElement.N_MAP), this.chartModel.scale.xChanged.pipe(pairwise(), filter(([prevScale, curScale]) => {
// TODO rework
const itemsCount = 0; //this.chartModel.scale.getItemsCount();
const prev = prevScale.start < 0 || prevScale.end > itemsCount;
const cur = curScale.start < 0 || curScale.end > itemsCount;
// trigger recalculation visual candles for nav map if previous viewport had
// no-candles area and current viewport contains only candles.
// OR if current viewport has no-candles area.
return (prev && !cur) || cur;
}))).subscribe(() => {
if (this.config.components.navigationMap.visible) {
this.visualCandles = this.makeVisualCandles();
this.canvasModel.fireDraw();
}
}));
}
/**
* This function generates an array of visual candles based on the data provided by the chartModel.
* It calculates the maximum and minimum values of the candles and maps them to the canvas bounds.
* @returns {Array<[number, number]>} An array of tuples containing the x and y coordinates of each visual candle.
*/
makeVisualCandles() {
var _a;
const candles = this.chartModel.getCandles();
if (!candles.length) {
return [];
}
const firstId = this.chartModel.mainCandleSeries.dataIdxStart;
const lastId = this.chartModel.mainCandleSeries.dataIdxEnd;
const leftSideOffsetVisible = Math.round(Math.max(-firstId, 0));
// take into account left offset and a new rule using which we can scroll to right, so only two candles on left are visible
const len = Math.max(this.chartModel.getCandlesCountWithRightOffset(), lastId) + leftSideOffsetVisible;
let max = Number.NEGATIVE_INFINITY;
let min = Number.POSITIVE_INFINITY;
const nMapChart = this.canvasBoundsContainer.getBounds(CanvasElement.N_MAP_CHART);
const width = nMapChart.width;
const res = [];
let candleClose = 0;
let x;
let idx;
for (x = 0; x < width; x++) {
idx = floor((x * len) / width) - leftSideOffsetVisible;
if (idx in candles) {
res[x] = candles[idx].close;
candleClose = (_a = res[x]) !== null && _a !== void 0 ? _a : 0;
max = Math.max(max, candleClose);
min = Math.min(min, candleClose);
}
else {
res[x] = 0;
}
}
max -= min;
return res.map((y, x) => {
return [x + nMapChart.x, ((max + min - y) * nMapChart.height) / max + nMapChart.y];
});
}
/**
* Sets the visibility of the navigation map component.
* @param {boolean} visible - Whether the navigation map component should be visible or not. Default is true.
*/
setVisible(visible = true) {
var _a;
if ((_a = this.config.components) === null || _a === void 0 ? void 0 : _a.navigationMap) {
this.config.components.navigationMap.visible = visible;
this.eventBus.fire(EVENT_RESIZED);
this.canvasModel.fireDraw();
}
}
}