@devexperts/dxcharts-lite 2.3.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/chart/bootstrap.js +4 -3
  2. package/dist/chart/canvas/canvas-bounds-container.js +5 -3
  3. package/dist/chart/canvas/cursor.handler.js +2 -2
  4. package/dist/chart/components/chart/chart-area-pan.handler.js +3 -3
  5. package/dist/chart/components/chart/chart.component.d.ts +6 -3
  6. package/dist/chart/components/chart/chart.component.js +18 -7
  7. package/dist/chart/components/chart/chart.model.d.ts +1 -2
  8. package/dist/chart/components/chart/chart.model.js +2 -3
  9. package/dist/chart/components/cross_tool/types/cross-and-labels.drawer.js +4 -2
  10. package/dist/chart/components/high_low/high-low.drawer.js +9 -2
  11. package/dist/chart/components/highlights/highlights.drawer.js +1 -1
  12. package/dist/chart/components/pane/pane.component.js +6 -3
  13. package/dist/chart/components/volumes/volumes.drawer.js +1 -1
  14. package/dist/chart/components/x_axis/time/parser/time-formats-matchers.functions.js +1 -1
  15. package/dist/chart/components/x_axis/time/parser/time-formats.model.d.ts +1 -1
  16. package/dist/chart/components/x_axis/time/x-axis-weights.functions.js +2 -2
  17. package/dist/chart/components/x_axis/time/x-axis-weights.generator.js +6 -7
  18. package/dist/chart/components/x_axis/x-axis-labels.generator.d.ts +11 -3
  19. package/dist/chart/components/x_axis/x-axis-labels.generator.js +47 -19
  20. package/dist/chart/components/x_axis/x-axis-time-labels.drawer.js +0 -4
  21. package/dist/chart/components/x_axis/x-axis.component.js +7 -11
  22. package/dist/chart/components/y_axis/price_labels/last-candle-labels.provider.js +5 -5
  23. package/dist/chart/components/y_axis/price_labels/price-label.drawer.js +21 -22
  24. package/dist/chart/components/y_axis/y-axis-labels.drawer.d.ts +18 -6
  25. package/dist/chart/components/y_axis/y-axis-labels.drawer.js +23 -12
  26. package/dist/chart/components/y_axis/y-axis-scale.handler.d.ts +4 -0
  27. package/dist/chart/components/y_axis/y-axis-scale.handler.js +13 -6
  28. package/dist/chart/components/y_axis/y-axis.drawer.js +1 -1
  29. package/dist/chart/drawers/chart-background.drawer.d.ts +2 -3
  30. package/dist/chart/drawers/chart-background.drawer.js +16 -22
  31. package/dist/chart/drawers/data-series-drawers/candle-series-wrapper.js +1 -1
  32. package/dist/chart/drawers/data-series.drawer.d.ts +0 -2
  33. package/dist/chart/drawers/data-series.drawer.js +1 -6
  34. package/dist/chart/drawers/ht-data-series.drawer.js +1 -1
  35. package/dist/chart/inputhandlers/hover-producer.component.d.ts +1 -1
  36. package/dist/chart/model/compare-series-hover.d.ts +1 -1
  37. package/dist/chart/model/compare-series-hover.js +5 -0
  38. package/dist/chart/model/data-series.model.js +1 -1
  39. package/dist/chart/model/hit-test-canvas.model.js +2 -2
  40. package/dist/chart/model/scale.model.d.ts +3 -1
  41. package/dist/chart/model/scale.model.js +23 -4
  42. package/dist/chart/model/scaling/viewport.model.d.ts +2 -0
  43. package/dist/chart/model/scaling/viewport.model.js +13 -4
  44. package/dist/chart/model/time-zone.model.d.ts +0 -14
  45. package/dist/chart/model/time-zone.model.js +13 -33
  46. package/dist/chart/utils/canvas/canvas-drawing-functions.utils.d.ts +12 -1
  47. package/dist/chart/utils/canvas/canvas-drawing-functions.utils.js +16 -1
  48. package/dist/chart/utils/canvas/canvas-text-functions.utils.d.ts +3 -2
  49. package/dist/chart/utils/canvas/canvas-text-functions.utils.js +8 -3
  50. package/dist/chart/utils/math.utils.d.ts +1 -0
  51. package/dist/chart/utils/math.utils.js +3 -0
  52. package/dist/chart/utils/timezone.utils.d.ts +13 -0
  53. package/dist/chart/utils/timezone.utils.js +48 -0
  54. package/dist/dxchart.min.js +6 -6
  55. package/package.json +1 -2
@@ -106,8 +106,9 @@ export default class ChartBootstrap {
106
106
  // canvas animation container
107
107
  const canvasAnimation = new CanvasAnimation(eventBus);
108
108
  this.canvasAnimation = canvasAnimation;
109
+ const chartPaneId = CanvasElement.PANE_UUID(CHART_UUID);
109
110
  //#region ScaleModel init
110
- const scaleModel = new ScaleModel(config, () => canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(CHART_UUID)), canvasAnimation);
111
+ const scaleModel = new ScaleModel(config, () => canvasBoundsContainer.getBounds(chartPaneId), canvasAnimation);
111
112
  this.scaleModel = scaleModel;
112
113
  //#endregion
113
114
  const backgroundCanvasModel = createCanvasModel(eventBus, elements.backgroundCanvas, config, drawingManager, this.canvasModels, elements.chartResizer, {
@@ -135,7 +136,7 @@ export default class ChartBootstrap {
135
136
  this.chartComponents.push(this.dynamicObjects);
136
137
  this.chartModel = new ChartModel(chartBaseModel, paneManager, eventBus, this.dynamicObjectsCanvasModel, config, scaleModel, formatterFactory, mainCanvasParent, canvasBoundsContainer, chartResizeHandler);
137
138
  //#region main chart component init
138
- const chartComponent = new ChartComponent(this.chartModel, this.dynamicObjectsCanvasModel, config, scaleModel, canvasBoundsContainer, drawingManager, hitTestCanvasModel, canvasInputListener, backgroundCanvasModel, chartPanComponent, paneManager, this.cursorHandler, this.dynamicObjects);
139
+ const chartComponent = new ChartComponent(this.chartModel, this.dynamicObjectsCanvasModel, config, scaleModel, canvasBoundsContainer, drawingManager, hitTestCanvasModel, canvasInputListener, backgroundCanvasModel, chartPanComponent, paneManager, this.cursorHandler, this.dynamicObjects, this.chartResizeHandler);
139
140
  this.chartComponents.push(chartComponent);
140
141
  this.chartComponent = chartComponent;
141
142
  const chartModel = this.chartComponent.chartModel;
@@ -175,7 +176,7 @@ export default class ChartBootstrap {
175
176
  this.volumesComponent = new VolumesComponent(this.dynamicObjectsCanvasModel, chartComponent, scaleModel, canvasBoundsContainer, drawingManager, config, paneManager, this.dynamicObjects);
176
177
  this.chartComponents.push(this.volumesComponent);
177
178
  // grid component
178
- const mainChartGridComponent = new GridComponent(mainCanvasModel, scaleModel, config, this.yAxisComponent.state, 'GRID', drawingManager, () => this.canvasBoundsContainer.getBounds(CanvasElement.ALL_PANES), () => this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(CHART_UUID)), () => this.xAxisComponent.xAxisLabelsGenerator.labels, () => this.yAxisComponent.model.baseLabelsModel.labels, () => mainPane.mainExtent.toY(mainPane.mainExtent.getBaseline()), () => config.components.grid.visible);
179
+ const mainChartGridComponent = new GridComponent(mainCanvasModel, scaleModel, config, this.yAxisComponent.state, 'GRID', drawingManager, () => this.canvasBoundsContainer.getBounds(CanvasElement.ALL_PANES), () => this.canvasBoundsContainer.getBounds(chartPaneId), () => this.xAxisComponent.xAxisLabelsGenerator.labels, () => this.yAxisComponent.model.baseLabelsModel.labels, () => mainPane.mainExtent.toY(mainPane.mainExtent.getBaseline()), () => config.components.grid.visible);
179
180
  this.chartComponents.push(mainChartGridComponent);
180
181
  this.hoverProducer = new HoverProducerComponent(this.crossEventProducer, scaleModel, config, chartModel, canvasInputListener, this.canvasBoundsContainer, this.paneManager, timeZoneModel, formatterFactory);
181
182
  this.chartComponents.push(this.hoverProducer);
@@ -565,10 +565,12 @@ export class CanvasBoundsContainer {
565
565
  * @return {Bounds} bounds of element
566
566
  */
567
567
  getBounds(el) {
568
- if (this.bounds[el] === undefined) {
569
- this.bounds[el] = this.copyOf(DEFAULT_BOUNDS);
568
+ let bounds = this.bounds[el];
569
+ if (bounds === undefined) {
570
+ bounds = this.copyOf(DEFAULT_BOUNDS);
571
+ this.bounds[el] = bounds;
570
572
  }
571
- return this.bounds[el];
573
+ return bounds;
572
574
  }
573
575
  /**
574
576
  * Returns the position of CANVAS on whole page.
@@ -3,7 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
- import { Subject } from 'rxjs';
6
+ import { Subject, animationFrameScheduler } from 'rxjs';
7
7
  import { distinctUntilChanged, throttleTime } from 'rxjs/operators';
8
8
  import { ChartBaseElement } from '../model/chart-base-element';
9
9
  import { CanvasBoundsContainer } from './canvas-bounds-container';
@@ -29,7 +29,7 @@ export class CursorHandler extends ChartBaseElement {
29
29
  super.doActivate();
30
30
  this.canvasInputListener
31
31
  .observeMouseMoveNoDrag()
32
- .pipe(throttleTime(100, undefined, { trailing: true }))
32
+ .pipe(throttleTime(100, animationFrameScheduler, { trailing: true }))
33
33
  .subscribe(point => {
34
34
  const cursorFromHT = this.hitTestCanvasModel.resolveCursor(point);
35
35
  if (cursorFromHT !== undefined) {
@@ -3,7 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
- import { merge } from 'rxjs';
6
+ import { merge, animationFrameScheduler } from 'rxjs';
7
7
  import { throttleTime } from 'rxjs/operators';
8
8
  import { VIEWPORT_ANIMATION_ID } from '../../animation/canvas-animation';
9
9
  import { CanvasElement } from '../../canvas/canvas-bounds-container';
@@ -112,7 +112,7 @@ export class ChartAreaPanHandler extends ChartBaseElement {
112
112
  const allPanesHitTest = this.canvasBoundsContainer.getBoundsHitTest(CanvasElement.ALL_PANES);
113
113
  //#endregion
114
114
  this.addRxSubscription(merge(this.canvasInputListener.observeWheel(allPanesHitTest), this.canvasInputListener.observePinch(allPanesHitTest))
115
- .pipe(throttleTime(this.wheelTrottleTime, undefined, { trailing: true, leading: true }))
115
+ .pipe(throttleTime(this.wheelTrottleTime, animationFrameScheduler, { trailing: true, leading: true }))
116
116
  .subscribe(e => {
117
117
  const isTouchpad = touchpadDetector(e);
118
118
  const zoomSensitivity = isTouchpad
@@ -122,7 +122,7 @@ export class ChartAreaPanHandler extends ChartBaseElement {
122
122
  }));
123
123
  this.addRxSubscription(this.canvasInputListener
124
124
  .observeScrollGesture()
125
- .pipe(throttleTime(this.wheelTrottleTime, undefined, { trailing: true, leading: true }))
125
+ .pipe(throttleTime(this.wheelTrottleTime, animationFrameScheduler, { trailing: true, leading: true }))
126
126
  .subscribe((e) => {
127
127
  let direction = -1;
128
128
  const device = deviceDetector();
@@ -24,6 +24,7 @@ import { PaneManager } from '../pane/pane-manager.component';
24
24
  import { CandleWidthCalculator, ChartModel, LastCandleLabelHandler, VisualCandleCalculator } from './chart.model';
25
25
  import { PrependedCandlesData } from './chart-base.model';
26
26
  import { DynamicObjectsComponent } from '../dynamic-objects/dynamic-objects.component';
27
+ import { ChartResizeHandler } from '../../inputhandlers/chart-resize.handler';
27
28
  /**
28
29
  * Represents a financial instrument to be displayed on a chart
29
30
  * @class
@@ -59,11 +60,13 @@ export declare class ChartComponent extends ChartBaseElement {
59
60
  private canvasInputListener;
60
61
  private paneManager;
61
62
  private dynamicObjects;
63
+ private chartResizeHandler;
62
64
  readonly baselineModel: BaselineModel;
63
65
  private readonly backgroundDrawer;
64
66
  private readonly _dataSeriesDrawers;
65
67
  private readonly dataSeriesDrawer;
66
- constructor(chartModel: ChartModel, canvasModel: CanvasModel, config: FullChartConfig, scale: ScaleModel, canvasBoundsContainer: CanvasBoundsContainer, drawingManager: DrawingManager, hitTestCanvasModel: HitTestCanvasModel, canvasInputListener: CanvasInputListenerComponent, backgroundCanvasModel: CanvasModel, chartPanComponent: ChartPanComponent, paneManager: PaneManager, cursorHandler: CursorHandler, dynamicObjects: DynamicObjectsComponent);
68
+ private backgroundDrawPredicateSubj;
69
+ constructor(chartModel: ChartModel, canvasModel: CanvasModel, config: FullChartConfig, scale: ScaleModel, canvasBoundsContainer: CanvasBoundsContainer, drawingManager: DrawingManager, hitTestCanvasModel: HitTestCanvasModel, canvasInputListener: CanvasInputListenerComponent, backgroundCanvasModel: CanvasModel, chartPanComponent: ChartPanComponent, paneManager: PaneManager, cursorHandler: CursorHandler, dynamicObjects: DynamicObjectsComponent, chartResizeHandler: ChartResizeHandler);
67
70
  /**
68
71
  * This method overrides the doActivate method of the parent class and calls it.
69
72
  * It does not take any parameters and does not return anything.
@@ -118,13 +121,13 @@ export declare class ChartComponent extends ChartBaseElement {
118
121
  * @param {Timestamp} end - The end timestamp of the range.
119
122
  * @returns {void}
120
123
  */
121
- setTimestampRange(start: Timestamp, end: Timestamp): void;
124
+ setTimestampRange(start: Timestamp, end: Timestamp, forceNoAnimation?: boolean): void;
122
125
  /**
123
126
  * Moves the viewport to exactly xStart..xEnd place.
124
127
  * @param xStart - viewport start in units
125
128
  * @param xEnd - viewport end in units
126
129
  */
127
- setXScale(xStart: Unit, xEnd: Unit): void;
130
+ setXScale(xStart: Unit, xEnd: Unit, forceNoAnimation?: boolean): void;
128
131
  /**
129
132
  * Sets the visibility of the wicks in the chart.
130
133
  * @param {boolean} isShow - A boolean value indicating whether to show or hide the wicks.
@@ -3,6 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
+ import { BehaviorSubject } from 'rxjs';
6
7
  import { CHART_UUID, CanvasElement } from '../../canvas/canvas-bounds-container';
7
8
  import { ChartBaseElement } from '../../model/chart-base-element';
8
9
  import { BackgroundDrawer } from '../../drawers/chart-background.drawer';
@@ -48,7 +49,7 @@ export class ChartInstrument {
48
49
  }
49
50
  }
50
51
  export class ChartComponent extends ChartBaseElement {
51
- constructor(chartModel, canvasModel, config, scale, canvasBoundsContainer, drawingManager, hitTestCanvasModel, canvasInputListener, backgroundCanvasModel, chartPanComponent, paneManager, cursorHandler, dynamicObjects) {
52
+ constructor(chartModel, canvasModel, config, scale, canvasBoundsContainer, drawingManager, hitTestCanvasModel, canvasInputListener, backgroundCanvasModel, chartPanComponent, paneManager, cursorHandler, dynamicObjects, chartResizeHandler) {
52
53
  super();
53
54
  this.chartModel = chartModel;
54
55
  this.canvasModel = canvasModel;
@@ -60,7 +61,9 @@ export class ChartComponent extends ChartBaseElement {
60
61
  this.canvasInputListener = canvasInputListener;
61
62
  this.paneManager = paneManager;
62
63
  this.dynamicObjects = dynamicObjects;
64
+ this.chartResizeHandler = chartResizeHandler;
63
65
  this._dataSeriesDrawers = {};
66
+ this.backgroundDrawPredicateSubj = new BehaviorSubject(true);
64
67
  this.strToBarType = (str) => { var _a; return (_a = this.barTypeValues.find(t => t === str)) !== null && _a !== void 0 ? _a : 'candle'; };
65
68
  this.addChildEntity(this.chartModel);
66
69
  this.registerDefaultCandlesTransformers();
@@ -73,7 +76,7 @@ export class ChartComponent extends ChartBaseElement {
73
76
  //#region data series drawers
74
77
  this.registerDefaultDataSeriesDrawers();
75
78
  //#endregion
76
- this.backgroundDrawer = new BackgroundDrawer(backgroundCanvasModel, this.config);
79
+ this.backgroundDrawer = new BackgroundDrawer(backgroundCanvasModel, this.config, () => this.backgroundDrawPredicateSubj.getValue());
77
80
  drawingManager.addDrawer(this.backgroundDrawer, 'MAIN_BACKGROUND');
78
81
  cursorHandler.setCursorForCanvasEl(CanvasElement.PANE_UUID(CHART_UUID), config.components.chart.cursor);
79
82
  this.dataSeriesDrawer = new DataSeriesDrawer(paneManager, this.dataSeriesDrawers);
@@ -104,6 +107,13 @@ export class ChartComponent extends ChartBaseElement {
104
107
  this.addRxSubscription(this.paneManager.dataSeriesRemovedSubject.subscribe(series => {
105
108
  this.dynamicObjects.model.removeObject(series.id);
106
109
  }));
110
+ // redraw background only when chart is resized
111
+ this.addRxSubscription(this.canvasBoundsContainer.observeAnyBoundsChanged().subscribe(() => {
112
+ this.backgroundDrawPredicateSubj.next(false);
113
+ }));
114
+ this.addRxSubscription(this.chartResizeHandler.canvasResized.subscribe(() => {
115
+ this.backgroundDrawPredicateSubj.next(true);
116
+ }));
107
117
  }
108
118
  /**
109
119
  * Registers default candle transformers.
@@ -162,7 +172,8 @@ export class ChartComponent extends ChartBaseElement {
162
172
  this.registerDataSeriesTypeDrawer('area', new AreaDrawer(this.config.components.chart));
163
173
  this.registerDataSeriesTypeDrawer('baseline', new BaselineDrawer(this.baselineModel, this.canvasBoundsContainer));
164
174
  this.registerDataSeriesTypeDrawer('histogram', new MainChartHistogramDrawer(this.config.components.chart.histogram));
165
- const mainChartBoundsProvider = () => this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(CHART_UUID));
175
+ const chartPaneId = CanvasElement.PANE_UUID(CHART_UUID);
176
+ const mainChartBoundsProvider = () => this.canvasBoundsContainer.getBounds(chartPaneId);
166
177
  this.registerDataSeriesTypeDrawer('LINEAR', new LinearDrawer());
167
178
  this.registerDataSeriesTypeDrawer('HISTOGRAM', new HistogramDrawer());
168
179
  this.registerDataSeriesTypeDrawer('TREND_HISTOGRAM', new TrendHistogramDrawer());
@@ -196,16 +207,16 @@ export class ChartComponent extends ChartBaseElement {
196
207
  * @param {Timestamp} end - The end timestamp of the range.
197
208
  * @returns {void}
198
209
  */
199
- setTimestampRange(start, end) {
200
- return this.chartModel.setTimestampRange(start, end);
210
+ setTimestampRange(start, end, forceNoAnimation = true) {
211
+ return this.chartModel.setTimestampRange(start, end, forceNoAnimation);
201
212
  }
202
213
  /**
203
214
  * Moves the viewport to exactly xStart..xEnd place.
204
215
  * @param xStart - viewport start in units
205
216
  * @param xEnd - viewport end in units
206
217
  */
207
- setXScale(xStart, xEnd) {
208
- return this.scale.setXScale(xStart, xEnd);
218
+ setXScale(xStart, xEnd, forceNoAnimation = true) {
219
+ return this.scale.setXScale(xStart, xEnd, forceNoAnimation);
209
220
  }
210
221
  /**
211
222
  * Sets the visibility of the wicks in the chart.
@@ -49,7 +49,6 @@ export declare class ChartModel extends ChartBaseElement {
49
49
  readonly nextCandleTimeStampSubject: Subject<void>;
50
50
  readonly chartTypeChanged: Subject<BarType>;
51
51
  readonly mainInstrumentChangedSubject: Subject<ChartInstrument>;
52
- readonly scaleInversedSubject: Subject<void>;
53
52
  readonly offsetsChanged: Subject<void>;
54
53
  private candlesTransformersByChartType;
55
54
  lastCandleLabelsByChartType: Partial<Record<BarType, LastCandleLabelHandler>>;
@@ -319,7 +318,7 @@ export declare class ChartModel extends ChartBaseElement {
319
318
  * @param {Timestamp} end - The end timestamp of the range.
320
319
  * @returns {void}
321
320
  */
322
- setTimestampRange(start: Timestamp, end: Timestamp): void;
321
+ setTimestampRange(start: Timestamp, end: Timestamp, forceNoAnimation?: boolean): void;
323
322
  /**
324
323
  * Returns the last candle of the main candle series.
325
324
  * @returns {Candle | undefined} The last candle of the main candle series or undefined if the series is empty.
@@ -50,7 +50,6 @@ export class ChartModel extends ChartBaseElement {
50
50
  this.nextCandleTimeStampSubject = new Subject();
51
51
  this.chartTypeChanged = new Subject();
52
52
  this.mainInstrumentChangedSubject = new Subject();
53
- this.scaleInversedSubject = new Subject();
54
53
  this.offsetsChanged = new Subject();
55
54
  this.candlesTransformersByChartType = {};
56
55
  this.lastCandleLabelsByChartType = {};
@@ -656,11 +655,11 @@ export class ChartModel extends ChartBaseElement {
656
655
  * @param {Timestamp} end - The end timestamp of the range.
657
656
  * @returns {void}
658
657
  */
659
- setTimestampRange(start, end) {
658
+ setTimestampRange(start, end, forceNoAnimation = true) {
660
659
  const startUnit = this.candleFromTimestamp(start).startUnit;
661
660
  const endCandle = this.candleFromTimestamp(end);
662
661
  const endUnit = endCandle.startUnit + endCandle.width;
663
- return this.scale.setXScale(startUnit, endUnit);
662
+ return this.scale.setXScale(startUnit, endUnit, forceNoAnimation);
664
663
  }
665
664
  /**
666
665
  * Returns the last candle of the main candle series.
@@ -122,6 +122,7 @@ export class CrossAndLabelsDrawerType {
122
122
  if (this.config.components.yAxis.visible) {
123
123
  const pane = this.paneManager.panes[point.paneId];
124
124
  const y = point.y;
125
+ const type = this.config.components.crossTool.yLabel.type;
125
126
  if (!pane) {
126
127
  return;
127
128
  }
@@ -129,9 +130,10 @@ export class CrossAndLabelsDrawerType {
129
130
  const price = extent.regularValueFromY(y);
130
131
  const label = extent.valueFormatter(price);
131
132
  const bounds = this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID_Y_AXIS(pane.uuid, extent.idx));
132
- const drawYLabel = priceLabelDrawersMap[this.config.components.crossTool.yLabel.type];
133
+ const drawYLabel = priceLabelDrawersMap[type];
134
+ const textColor = type === 'plain' ? crossToolColors.lineColor : crossToolColors.labelTextColor;
133
135
  drawYLabel(ctx, bounds, label, y, {
134
- textColor: crossToolColors.labelTextColor,
136
+ textColor,
135
137
  bgColor: crossToolColors.labelBoxColor,
136
138
  paddingBottom: yLabelPadding === null || yLabelPadding === void 0 ? void 0 : yLabelPadding.bottom,
137
139
  paddingEnd: yLabelPadding === null || yLabelPadding === void 0 ? void 0 : yLabelPadding.end,
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { CanvasElement } from '../../canvas/canvas-bounds-container';
7
7
  import { getTextLineHeight } from '../../utils/canvas/canvas-text-functions.utils';
8
+ import { clipToBounds } from '../../utils/canvas/canvas-drawing-functions.utils';
8
9
  export class HighLowDrawer {
9
10
  constructor(canvasModel, chartModel, config, canvasBoundsContainer) {
10
11
  this.canvasModel = canvasModel;
@@ -34,7 +35,10 @@ export class HighLowDrawer {
34
35
  ctx.font = this.config.components.highLow.font;
35
36
  this.drawMarkerLabel(ctx, finalHighIdx, high, 'high');
36
37
  this.drawMarkerLabel(ctx, finalLowIdx, low, 'low');
38
+ const chartBounds = this.canvasBoundsContainer.getBounds('PANE_CHART');
37
39
  ctx.restore();
40
+ // We need clip here so lowLabel won't overlap other panes
41
+ clipToBounds(ctx, chartBounds);
38
42
  }
39
43
  }
40
44
  /**
@@ -47,7 +51,10 @@ export class HighLowDrawer {
47
51
  */
48
52
  drawMarkerLabel(ctx, candleIdx, yValue, type) {
49
53
  const y = this.getMarkerY(ctx, yValue, type === 'low');
50
- if (!this.checkMarkerInBounds(y)) {
54
+ const fontSize = getTextLineHeight(ctx, false);
55
+ // we need to measure fit into the bounds for low label by its top point
56
+ const yForBoundsTrack = type === 'low' ? y - fontSize : y;
57
+ if (!this.checkMarkerInBounds(yForBoundsTrack)) {
51
58
  return;
52
59
  }
53
60
  const text = this.getMarkerText(yValue, type);
@@ -79,7 +86,7 @@ export class HighLowDrawer {
79
86
  getMarkerY(ctx, yValue, offset = false) {
80
87
  const y = this.chartModel.toY(yValue);
81
88
  if (offset) {
82
- const fontSize = getTextLineHeight(ctx);
89
+ const fontSize = getTextLineHeight(ctx, false);
83
90
  return y + fontSize;
84
91
  }
85
92
  return y;
@@ -7,7 +7,7 @@ import { calculateTextWidth } from '../../utils/canvas/canvas-font-measure-tool.
7
7
  import { HIGHLIGHTS_TYPES } from './highlights.model';
8
8
  import { CanvasElement } from '../../canvas/canvas-bounds-container';
9
9
  import { unitToPixels } from '../../model/scaling/viewport.model';
10
- import { clipToBounds } from '../../drawers/data-series.drawer';
10
+ import { clipToBounds } from '../../utils/canvas/canvas-drawing-functions.utils';
11
11
  const LABEL_PADDINGS = [20, 10];
12
12
  export class HighlightsDrawer {
13
13
  constructor(highlightsModel, chartModel, canvasModel, canvasBoundsContainer, config) {
@@ -85,7 +85,8 @@ export class PaneComponent extends ChartBaseElement {
85
85
  * @returns {GridComponent} - The newly created GridComponent instance.
86
86
  */
87
87
  createGridComponent(uuid, scale, yAxisLabelsGenerator, yAxisState) {
88
- const gridComponent = new GridComponent(this.mainCanvasModel, scale, this.config, yAxisState, `PANE_${uuid}_grid_drawer`, this.drawingManager, () => this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(uuid)), () => this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(uuid)), () => [], () => yAxisLabelsGenerator.generateNumericLabels());
88
+ const chartPaneId = CanvasElement.PANE_UUID(uuid);
89
+ const gridComponent = new GridComponent(this.mainCanvasModel, scale, this.config, yAxisState, `PANE_${uuid}_grid_drawer`, this.drawingManager, () => this.canvasBoundsContainer.getBounds(chartPaneId), () => this.canvasBoundsContainer.getBounds(chartPaneId), () => [], () => yAxisLabelsGenerator.generateNumericLabels());
89
90
  return gridComponent;
90
91
  }
91
92
  /**
@@ -96,7 +97,8 @@ export class PaneComponent extends ChartBaseElement {
96
97
  * @returns {Unsubscriber}
97
98
  */
98
99
  createYPanHandler(uuid, scale) {
99
- const dragNDropComponent = this.chartPanComponent.chartAreaPanHandler.registerChartYPanHandler(scale, this.canvasBoundsContainer.getBoundsHitTest(CanvasElement.PANE_UUID(uuid)));
100
+ const chartPaneId = CanvasElement.PANE_UUID(uuid);
101
+ const dragNDropComponent = this.chartPanComponent.chartAreaPanHandler.registerChartYPanHandler(scale, this.canvasBoundsContainer.getBoundsHitTest(chartPaneId));
100
102
  return [
101
103
  () => {
102
104
  this.chartPanComponent.chartAreaPanHandler.removeChildEntity(dragNDropComponent);
@@ -113,7 +115,8 @@ export class PaneComponent extends ChartBaseElement {
113
115
  createExtentComponent(options) {
114
116
  var _a, _b;
115
117
  const extentIdx = this.yExtentComponents.length;
116
- const getBounds = () => this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(this.uuid));
118
+ const chartPaneId = CanvasElement.PANE_UUID(this.uuid);
119
+ const getBounds = () => this.canvasBoundsContainer.getBounds(chartPaneId);
117
120
  const scaleModel = (_a = options === null || options === void 0 ? void 0 : options.scale) !== null && _a !== void 0 ? _a : new SyncedByXScaleModel(this.mainScale, this.config, getBounds, this.canvasAnimation);
118
121
  const [unsub, dragNDrop] = this.createYPanHandler(this.uuid, scaleModel);
119
122
  // creating partially resolved constructor except formatter & dataSeriesProvider - bcs it's not possible to provide formatter
@@ -3,7 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
- import { clipToBounds } from '../../drawers/data-series.drawer';
6
+ import { clipToBounds } from '../../utils/canvas/canvas-drawing-functions.utils';
7
7
  import { unitToPixels } from '../../model/scaling/viewport.model';
8
8
  import { flat } from '../../utils/array.utils';
9
9
  import { ceilToDPR, floorToDPR } from '../../utils/device/device-pixel-ratio.utils';
@@ -3,7 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
- import { eachDayOfInterval, isSameDay, isMonday, isTuesday, isWednesday, isThursday, isFriday, isSaturday, isSunday, startOfMonth, endOfMonth, } from 'date-fns/esm';
6
+ import { eachDayOfInterval, isSameDay, isMonday, isTuesday, isWednesday, isThursday, isFriday, isSaturday, isSunday, startOfMonth, endOfMonth, } from 'date-fns';
7
7
  /**
8
8
  * Factory that return a {@link TimeFormatMatcher} function for a given {@link ParsedTimeFormats}
9
9
  * @see
@@ -27,7 +27,7 @@ export interface ParsedNCTimeFormat {
27
27
  }
28
28
  export type ParsedTimeFormat = ParsedCTimeFormat | ParsedWeekFormat | ParsedNCTimeFormat;
29
29
  export type SpecialSymbol = '!' | '$';
30
- export declare const timeFormatConfirugableGuard: (key: string) => key is "second" | "minute" | "hour" | "day" | "month" | "year";
30
+ export declare const timeFormatConfirugableGuard: (key: string) => key is "year" | "month" | "day" | "hour" | "minute" | "second";
31
31
  export declare const weekWeekdayGuard: (key: string) => key is "week-weekday";
32
32
  export declare const timeFormatNoNConfirugableGuard: (key: string) => key is "lessThanSecond";
33
33
  export {};
@@ -51,7 +51,7 @@ export const getWeightFromTimeFormat = (format) => {
51
51
  */
52
52
  export const groupLabelsByWeight = (weightedLabels) => {
53
53
  const labelsGroupedByWeight = {};
54
- weightedLabels.forEach(weightedLabel => {
54
+ for (const weightedLabel of weightedLabels) {
55
55
  const labelsByWeight = labelsGroupedByWeight[weightedLabel.weight];
56
56
  if (labelsByWeight === undefined) {
57
57
  labelsGroupedByWeight[weightedLabel.weight] = [weightedLabel];
@@ -59,7 +59,7 @@ export const groupLabelsByWeight = (weightedLabels) => {
59
59
  else {
60
60
  labelsByWeight.push(weightedLabel);
61
61
  }
62
- });
62
+ }
63
63
  return labelsGroupedByWeight;
64
64
  };
65
65
  /**
@@ -18,12 +18,10 @@ import { getWeightFromTimeFormat } from './x-axis-weights.functions';
18
18
  * @see
19
19
  * returns `null` if date doesn't match any condition
20
20
  */
21
- const getWeightByDate = (currentDate, previousDate, sortedWeights, tzOffset) => {
21
+ const getWeightByDate = (currentDate, previousDate, sortedWeights) => {
22
22
  var _a, _b;
23
- const offsetCurrentDate = tzOffset(currentDate.getTime());
24
- const offsetPrevDate = tzOffset(previousDate.getTime());
25
23
  for (const [weight, timeMatcher] of sortedWeights) {
26
- if (timeMatcher(offsetCurrentDate, offsetPrevDate)) {
24
+ if (timeMatcher(currentDate, previousDate)) {
27
25
  return weight;
28
26
  }
29
27
  }
@@ -36,12 +34,13 @@ const getWeightByDate = (currentDate, previousDate, sortedWeights, tzOffset) =>
36
34
  export function mapCandlesToWeightedPoints(visualCandles, sortedWeights, tzOffset) {
37
35
  const result = new Array(visualCandles.length);
38
36
  // assume that candles` timestamp before the first visual candle is unreachable
39
- let prevDate = new Date(0);
37
+ let prevDate = tzOffset(0);
40
38
  for (let i = 0; i < visualCandles.length; i++) {
41
39
  const currentCandle = visualCandles[i];
42
- const currentDate = new Date(currentCandle.candle.timestamp);
40
+ // calculate timstamp to Date in acordance with provided timezone and time
41
+ const currentDate = tzOffset(currentCandle.candle.timestamp);
43
42
  const currentWeightedPoint = {
44
- weight: getWeightByDate(currentDate, prevDate, sortedWeights, tzOffset),
43
+ weight: getWeightByDate(currentDate, prevDate, sortedWeights),
45
44
  };
46
45
  result[i] = currentWeightedPoint;
47
46
  prevDate = currentDate;
@@ -3,6 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
+ import { CanvasBoundsContainer } from '../../canvas/canvas-bounds-container';
6
7
  import { FullChartConfig } from '../../chart.config';
7
8
  import EventBus from '../../events/event-bus';
8
9
  import { CanvasModel } from '../../model/canvas.model';
@@ -48,6 +49,7 @@ export declare class XAxisTimeLabelsGenerator implements XAxisLabelsGenerator {
48
49
  private scale;
49
50
  private timeZoneModel;
50
51
  private canvasModel;
52
+ private canvasBoundsContainer;
51
53
  private labelsGroupedByWeight;
52
54
  private weightedCache?;
53
55
  private levelsCache;
@@ -56,7 +58,8 @@ export declare class XAxisTimeLabelsGenerator implements XAxisLabelsGenerator {
56
58
  private weightToTimeFormatMatcherArray;
57
59
  private weightToTimeFormatsDict;
58
60
  private extendedLabelsFilterConfig;
59
- constructor(eventBus: EventBus, config: FullChartConfig, chartModel: ChartModel, scale: ScaleModel, timeZoneModel: TimeZoneModel, canvasModel: CanvasModel);
61
+ constructor(eventBus: EventBus, config: FullChartConfig, chartModel: ChartModel, scale: ScaleModel, timeZoneModel: TimeZoneModel, canvasModel: CanvasModel, canvasBoundsContainer: CanvasBoundsContainer);
62
+ private filterLabelsInViewport;
60
63
  private getLabelsFromChartType;
61
64
  /**
62
65
  * Make a prediction(750) about how many candles we need to fake to fill all X axis by labels
@@ -110,11 +113,16 @@ export declare class XAxisTimeLabelsGenerator implements XAxisLabelsGenerator {
110
113
  * @returns {void}
111
114
  */
112
115
  recalculateLabels(): void;
116
+ /**
117
+ * Calculates the cover up level based on the maximum label width and the mean candle width.
118
+ * Cover up level is based on maximum label width, which is based on the font size and the maximum format length.
119
+ * Used to group labels.
120
+ */
121
+ private calculateCoverUpLevel;
113
122
  /**
114
123
  * Recalculates cached labels based on the current configuration and zoom level.
115
124
  * If there are no grouped labels, the cache is not set.
116
- * Calculates the maximum label width based on the font size and the maximum format length.
117
- * Calculates the cover up level based on the maximum label width and the mean candle width.
125
+ * Recalculating depends on cover up level.
118
126
  * If the cover up level is negative, the cache is not updated.
119
127
  * If the cover up level has not changed, the cached labels are returned.
120
128
  * Otherwise, the labels are filtered by extended rules and grouped by cover up level.
@@ -3,6 +3,7 @@
3
3
  * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
4
  * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
  */
6
+ import { CanvasElement } from '../../canvas/canvas-bounds-container';
6
7
  import { unitToPixels } from '../../model/scaling/viewport.model';
7
8
  import { cloneUnsafe, typedEntries_UNSAFE } from '../../utils/object.utils';
8
9
  import { fakeVisualCandle } from '../chart/fake-candles';
@@ -11,15 +12,16 @@ import { filterMapGroupedLabelsByCoverUpLevel, getWeightFromTimeFormat, groupLab
11
12
  import { generateWeightsMapForConfig, mapCandlesToWeightedPoints, } from './time/x-axis-weights.generator';
12
13
  export class XAxisTimeLabelsGenerator {
13
14
  get labels() {
14
- return this.getLabelsFromChartType();
15
+ return this.filterLabelsInViewport(this.getLabelsFromChartType());
15
16
  }
16
- constructor(eventBus, config, chartModel, scale, timeZoneModel, canvasModel) {
17
+ constructor(eventBus, config, chartModel, scale, timeZoneModel, canvasModel, canvasBoundsContainer) {
17
18
  this.eventBus = eventBus;
18
19
  this.config = config;
19
20
  this.chartModel = chartModel;
20
21
  this.scale = scale;
21
22
  this.timeZoneModel = timeZoneModel;
22
23
  this.canvasModel = canvasModel;
24
+ this.canvasBoundsContainer = canvasBoundsContainer;
23
25
  this.labelsGroupedByWeight = {};
24
26
  this.levelsCache = {};
25
27
  this.weightToTimeFormatMatcherArray = [];
@@ -27,6 +29,29 @@ export class XAxisTimeLabelsGenerator {
27
29
  this.extendedLabelsFilterConfig = {
28
30
  minute_1: (coverUpLevel) => coverUpLevel >= 2,
29
31
  };
32
+ /**
33
+ * Calculates the cover up level based on the maximum label width and the mean candle width.
34
+ * Cover up level is based on maximum label width, which is based on the font size and the maximum format length.
35
+ * Used to group labels.
36
+ */
37
+ this.calculateCoverUpLevel = () => {
38
+ const animation = this.scale.currentAnimation;
39
+ const meanCandleWidthInUnits = this.chartModel.mainCandleSeries.meanCandleWidth;
40
+ if (Object.getOwnPropertyNames(this.labelsGroupedByWeight).length === 0) {
41
+ return -1;
42
+ }
43
+ // calculate coverUpLevel for target zoomX to prevent extra labels calculations on every animation tick
44
+ const meanCandleWidthInPixels = (animation === null || animation === void 0 ? void 0 : animation.animationInProgress)
45
+ ? unitToPixels(meanCandleWidthInUnits, animation.animationConfig.targetZoomX)
46
+ : unitToPixels(meanCandleWidthInUnits, this.scale.zoomX);
47
+ if (!isFinite(meanCandleWidthInPixels)) {
48
+ return -1;
49
+ }
50
+ const fontSize = this.config.components.xAxis.fontSize;
51
+ const maxFormatLength = Object.values(this.formatsByWeightMap).reduce((max, item) => Math.max(item.length, max), 1);
52
+ const maxLabelWidth = fontSize * maxFormatLength;
53
+ return Math.round(maxLabelWidth / meanCandleWidthInPixels);
54
+ };
30
55
  this.formatsByWeightMap = config.components.xAxis.formatsForLabelsConfig;
31
56
  const { weightToTimeFormatsDict, weightToTimeFormatMatcherDict } = generateWeightsMapForConfig(this.formatsByWeightMap);
32
57
  this.weightToTimeFormatMatcherArray = Object.entries(weightToTimeFormatMatcherDict)
@@ -34,6 +59,19 @@ export class XAxisTimeLabelsGenerator {
34
59
  .sort(([a], [b]) => b - a);
35
60
  this.weightToTimeFormatsDict = weightToTimeFormatsDict;
36
61
  }
62
+ filterLabelsInViewport(labels) {
63
+ const bounds = this.canvasBoundsContainer.getBounds(CanvasElement.X_AXIS);
64
+ const filteredLabels = [];
65
+ for (const label of labels) {
66
+ const x = this.scale.toX(label.value);
67
+ // skip labels outside viewport
68
+ if (x < 0 || x > bounds.width) {
69
+ continue;
70
+ }
71
+ filteredLabels.push(label);
72
+ }
73
+ return filteredLabels;
74
+ }
37
75
  getLabelsFromChartType() {
38
76
  var _a, _b;
39
77
  const labels = (_b = (_a = this.weightedCache) === null || _a === void 0 ? void 0 : _a.labels) !== null && _b !== void 0 ? _b : [];
@@ -64,9 +102,10 @@ export class XAxisTimeLabelsGenerator {
64
102
  * @returns {XAxisLabelWeighted[]} An array of XAxisLabelWeighted objects.
65
103
  */
66
104
  mapWeightedPointsToLabels(weightedPoints, allCandlesWithFake) {
105
+ var _a;
67
106
  const arr = new Array(weightedPoints.length);
68
- weightedPoints.forEach((point, index) => {
69
- var _a;
107
+ for (let index = 0; index < weightedPoints.length; ++index) {
108
+ const point = weightedPoints[index];
70
109
  const visualCandle = allCandlesWithFake[index];
71
110
  const labelFormat = this.weightToTimeFormatsDict[point.weight];
72
111
  const formattedLabel = this.timeZoneModel.getDateTimeFormatter(labelFormat)(visualCandle.candle.timestamp);
@@ -77,7 +116,7 @@ export class XAxisTimeLabelsGenerator {
77
116
  time: visualCandle.candle.timestamp,
78
117
  text: formattedLabel,
79
118
  };
80
- });
119
+ }
81
120
  return arr;
82
121
  }
83
122
  /**
@@ -227,27 +266,16 @@ export class XAxisTimeLabelsGenerator {
227
266
  /**
228
267
  * Recalculates cached labels based on the current configuration and zoom level.
229
268
  * If there are no grouped labels, the cache is not set.
230
- * Calculates the maximum label width based on the font size and the maximum format length.
231
- * Calculates the cover up level based on the maximum label width and the mean candle width.
269
+ * Recalculating depends on cover up level.
232
270
  * If the cover up level is negative, the cache is not updated.
233
271
  * If the cover up level has not changed, the cached labels are returned.
234
272
  * Otherwise, the labels are filtered by extended rules and grouped by cover up level.
235
273
  * The filtered labels are then cached and returned.
236
274
  */
237
275
  recalculateCachedLabels() {
238
- // skip cache setting if we don't have grouped labels
239
- if (Object.getOwnPropertyNames(this.labelsGroupedByWeight).length === 0) {
240
- return;
241
- }
242
- const fontSize = this.config.components.xAxis.fontSize;
243
- const maxFormatLength = Object.values(this.formatsByWeightMap).reduce((max, item) => {
244
- return Math.max(item.length, max);
245
- }, 1);
246
- const maxLabelWidth = fontSize * maxFormatLength;
247
- const meanCandleWidthPx = unitToPixels(this.chartModel.mainCandleSeries.meanCandleWidth, this.scale.zoomX);
248
- const coverUpLevel = Math.round(maxLabelWidth / meanCandleWidthPx);
276
+ const coverUpLevel = this.calculateCoverUpLevel();
249
277
  // for some reason sometimes `this.scale.zoomX` is negative so we dont want to update labels
250
- if (coverUpLevel < 0 && !isFinite(meanCandleWidthPx)) {
278
+ if (coverUpLevel < 0) {
251
279
  return;
252
280
  }
253
281
  if (this.weightedCache === undefined || coverUpLevel !== this.weightedCache.coverUpLevel) {
@@ -58,10 +58,6 @@ export class XAxisTimeLabelsDrawer {
58
58
  ctx.fillStyle = color;
59
59
  for (const label of labels) {
60
60
  const x = this.viewportModel.toX(label.value) - calculateTextWidth(label.text, ctx, font) / 2;
61
- // skip labels outside viewport
62
- if (x < 0 || x > bounds.width) {
63
- continue;
64
- }
65
61
  const y = bounds.y + fontHeight - 1 + offsetTop; // -1 for font drawing inconsistency
66
62
  const labelText = label.text;
67
63
  ctx.font = font;