@devexperts/dxcharts-lite 2.5.0 → 2.5.2

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.
@@ -6,13 +6,13 @@
6
6
  export default (devexpertsPromoLink) => '<div data-element="chartResizer" style="position: relative; min-height: 20px; height: 100%; width: 100%; touch-action: manipulation; z-index: 0;">\n' +
7
7
  '\t<div data-element="chartContainer" style="position: absolute; height: 100%; width: 100%;" class="chart chartArea--graph">\n' +
8
8
  '\t\t<div data-element="canvasArea" style="position: relative; height: 100%; width: 100%; touch-action: manipulation;">\n' +
9
- '\t\t\t<canvas data-element="snapshotCanvas" style="z-index: 0"></canvas>\n' +
10
- '\t\t\t<canvas data-element="backgroundCanvas" style="z-index: 1"></canvas>\n' +
11
- '\t\t\t<canvas data-element="mainCanvas" style="z-index: 2"></canvas>\n' +
12
- '\t\t\t<canvas data-element="dynamicObjectsCanvas" style="z-index: 3"></canvas>\n' +
13
- '\t\t\t<canvas data-element="yAxisLabelsCanvas" style="z-index: 4"></canvas>\n' +
14
- '\t\t\t<canvas data-element="crossToolCanvas" style="z-index: 5"></canvas>\n' +
15
- '\t\t\t<canvas data-element="hitTestCanvas" style="z-index: 6"></canvas>\n' +
9
+ '\t\t\t<canvas data-element="snapshotCanvas"></canvas>\n' +
10
+ '\t\t\t<canvas data-element="backgroundCanvas"></canvas>\n' +
11
+ '\t\t\t<canvas data-element="mainCanvas"></canvas>\n' +
12
+ '\t\t\t<canvas data-element="yAxisLabelsCanvas"></canvas>\n' +
13
+ '\t\t\t<canvas data-element="dynamicObjectsCanvas"></canvas>\n' +
14
+ '\t\t\t<canvas data-element="crossToolCanvas"></canvas>\n' +
15
+ '\t\t\t<canvas data-element="hitTestCanvas"></canvas>\n' +
16
16
  `${devexpertsPromoLink
17
17
  ? '\t\t\t<p style="position: absolute; visibility: hidden;">If you have any suggestions or are experiencing any issues, please feel free to contact us at <a href="https://devexperts.com/dxcharts/">devexperts.com</a></p>\n'
18
18
  : ''}` +
@@ -41,7 +41,7 @@ export class PaneHitTestController {
41
41
  * @returns {void}
42
42
  */
43
43
  onHover(model) {
44
- this.allDataSeries.forEach(d => (d.hovered = d.htId === (model === null || model === void 0 ? void 0 : model.htId)));
44
+ this.allDataSeries.forEach(d => (d.highlighted = d.htId === (model === null || model === void 0 ? void 0 : model.htId)));
45
45
  this.canvasModel.fireDraw();
46
46
  }
47
47
  onMouseDown(model) {
@@ -58,5 +58,5 @@ export declare class VolumesComponent extends ChartBaseElement {
58
58
  * @returns {void}
59
59
  */
60
60
  setVisible(visible?: boolean): void;
61
- private addVolumesToDynamicObjects;
61
+ private syncVolumesDynamicObject;
62
62
  }
@@ -25,7 +25,7 @@ export class VolumesComponent extends ChartBaseElement {
25
25
  this.addChildEntity(volumesModel);
26
26
  this.separateVolumes = new SeparateVolumesComponent(chartComponent, drawingManager, config, volumesModel, paneManager);
27
27
  this.volumesDrawer = new VolumesDrawer(config, this.volumesModel, chartComponent.chartModel, () => { var _a, _b; return (this.config.components.volumes.showSeparately ? (_b = (_a = this.separateVolumes.pane) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : scale : scale); }, this.volumesColorByChartTypeMap, () => true);
28
- config.components.volumes.visible && this.addVolumesToDynamicObjects();
28
+ config.components.volumes.visible && this.syncVolumesDynamicObject();
29
29
  this.addChildEntity(this.separateVolumes);
30
30
  this.registerDefaultVolumeColorResolvers();
31
31
  this.volumeVisibilityChangedSubject.next(config.components.volumes.visible);
@@ -58,6 +58,7 @@ export class VolumesComponent extends ChartBaseElement {
58
58
  this.separateVolumes.deactiveSeparateVolumes();
59
59
  }
60
60
  this.volumeIsSeparateModeChangedSubject.next(separate);
61
+ this.syncVolumesDynamicObject();
61
62
  }
62
63
  }
63
64
  /**
@@ -84,9 +85,6 @@ export class VolumesComponent extends ChartBaseElement {
84
85
  setVisible(visible = true) {
85
86
  this.config.components.volumes.visible = visible;
86
87
  this.volumeVisibilityChangedSubject.next(visible);
87
- visible
88
- ? this.addVolumesToDynamicObjects()
89
- : this.dynamicObjectsComponent.model.removeObject(this.volumesModel.id);
90
88
  if (this.config.components.volumes.showSeparately) {
91
89
  if (visible) {
92
90
  this.separateVolumes.activateSeparateVolumes();
@@ -98,19 +96,28 @@ export class VolumesComponent extends ChartBaseElement {
98
96
  }
99
97
  }
100
98
  this.canvasBoundsContainer.recalculatePanesHeightRatios();
99
+ this.syncVolumesDynamicObject();
101
100
  this.canvasModel.fireDraw();
102
101
  }
103
- addVolumesToDynamicObjects() {
104
- // check if the volumes dynamic object is already added
105
- const position = this.dynamicObjectsComponent.model.getObjectPosition(this.volumesModel.id);
106
- if (position !== -1) {
102
+ syncVolumesDynamicObject() {
103
+ const visible = this.config.components.volumes.visible;
104
+ if (!visible) {
105
+ this.dynamicObjectsComponent.model.removeObject(this.volumesModel.id);
107
106
  return;
108
107
  }
109
- this.dynamicObjectsComponent.model.addObject({
108
+ const paneId = this.config.components.volumes.showSeparately ? VOLUMES_UUID : CHART_UUID;
109
+ const volumesDynamicObject = {
110
110
  id: this.volumesModel.id,
111
- paneId: this.config.components.volumes.showSeparately ? VOLUMES_UUID : CHART_UUID,
111
+ paneId,
112
112
  drawer: this.volumesDrawer,
113
113
  model: this.volumesModel,
114
- });
114
+ };
115
+ // check if the volumes dynamic object is already added
116
+ const position = this.dynamicObjectsComponent.model.getObjectPosition(this.volumesModel.id);
117
+ if (position === -1) {
118
+ this.dynamicObjectsComponent.model.addObject(volumesDynamicObject);
119
+ return;
120
+ }
121
+ this.dynamicObjectsComponent.model.updateObject(volumesDynamicObject);
115
122
  }
116
123
  }
@@ -12,6 +12,7 @@ import { LabelsGroups } from './price_labels/y-axis-labels.model';
12
12
  import { YAxisScaleHandler } from './y-axis-scale.handler';
13
13
  import { YAxisModel } from './y-axis.model';
14
14
  import { merge as mergeObj } from '../../utils/merge.utils';
15
+ import { cloneUnsafe } from '../../utils/object.utils';
15
16
  /**
16
17
  * Y axis component. Contains all Y axis related logic.
17
18
  */
@@ -28,7 +29,8 @@ export class YAxisComponent extends ChartBaseElement {
28
29
  this.extentIdx = extentIdx;
29
30
  this.labelsColorByChartTypeMap = {};
30
31
  this.axisTypeSetSubject = new Subject();
31
- this.state = mergeObj(initialState !== null && initialState !== void 0 ? initialState : {}, config.components.yAxis, {
32
+ const initialStateCopy = initialState ? cloneUnsafe(initialState) : {};
33
+ this.state = mergeObj(initialStateCopy, config.components.yAxis, {
32
34
  overrideExisting: false,
33
35
  addIfMissing: true,
34
36
  });
@@ -23,7 +23,7 @@ export class AreaDrawer {
23
23
  else {
24
24
  ctx.strokeStyle = model.colors.areaTheme.lineColor;
25
25
  }
26
- if (model.hovered) {
26
+ if (model.highlighted) {
27
27
  ctx.lineWidth = this.config.selectedWidth;
28
28
  }
29
29
  else {
@@ -47,7 +47,7 @@ export const setLineWidth = (ctx, lineWidth, dataSeries, drawerConfig, seriesSel
47
47
  if (drawerConfig.forceBold) {
48
48
  ctx.lineWidth = drawerConfig.forceBold;
49
49
  }
50
- else if (dataSeries.hovered) {
50
+ else if (dataSeries.highlighted) {
51
51
  ctx.lineWidth = lineWidth !== seriesSelectedWidth ? lineWidth + 1 : seriesSelectedWidth;
52
52
  }
53
53
  else {
@@ -16,8 +16,6 @@ export declare class MainCanvasTouchHandler extends ChartBaseElement {
16
16
  private canvasInputListeners;
17
17
  private mainCanvasParent;
18
18
  private touchedCandleIndexes;
19
- private pinchDistance;
20
- private isDraggable;
21
19
  constructor(chartAreaPanHandler: ChartAreaPanHandler, scale: ScaleModel, canvasInputListeners: CanvasInputListenerComponent, mainCanvasParent: Element);
22
20
  /**
23
21
  * Activates canvas input listeners for touch start and touch move events.
@@ -4,7 +4,6 @@
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
6
  import { ChartBaseElement } from '../model/chart-base-element';
7
- const MIN_PINCH_DISTANCE = 30;
8
7
  /**
9
8
  * Handles chart touch events.
10
9
  */
@@ -17,10 +16,6 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
17
16
  this.mainCanvasParent = mainCanvasParent;
18
17
  // 2 candles indexes touched by 2 fingers when pinching
19
18
  this.touchedCandleIndexes = [0, 0];
20
- // number of px between touch events
21
- this.pinchDistance = 0;
22
- // used when maximum zoom in/out reached, can't use deactivate because it unsubscribes from all touch events
23
- this.isDraggable = true;
24
19
  }
25
20
  /**
26
21
  * Activates canvas input listeners for touch start and touch move events.
@@ -39,7 +34,6 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
39
34
  */
40
35
  handleTouchStartEvent(e) {
41
36
  if (e.touches.length === 2) {
42
- this.isDraggable = true;
43
37
  this.chartAreaPanHandler.deactivate();
44
38
  // @ts-ignore
45
39
  // TODO rework this
@@ -89,16 +83,6 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
89
83
  * @returns {void}
90
84
  */
91
85
  pinchHandler(candleIndexes, touchPositions) {
92
- const diff = Math.abs(touchPositions[0]) - Math.abs(touchPositions[1]);
93
- const pinchDistance = Math.abs(diff);
94
- const zoomIn = pinchDistance > this.pinchDistance;
95
- if (this.scale.isMaxZoomXReached(zoomIn)) {
96
- this.isDraggable = false;
97
- }
98
- if (!this.isDraggable || pinchDistance < MIN_PINCH_DISTANCE) {
99
- return;
100
- }
101
- this.pinchDistance = pinchDistance;
102
86
  const first = (touchPositions[0] * candleIndexes[1] - touchPositions[1] * candleIndexes[0]) /
103
87
  (touchPositions[0] - touchPositions[1]);
104
88
  const last = first +
@@ -48,7 +48,7 @@ export declare class DataSeriesModel<D extends DataSeriesPoint = DataSeriesPoint
48
48
  id: string;
49
49
  htId: number;
50
50
  name: string;
51
- hovered: boolean;
51
+ highlighted: boolean;
52
52
  yAxisLabelProvider: DataSeriesYAxisLabelsProvider;
53
53
  readonly config: DataSeriesConfig;
54
54
  scale: ScaleModel;
@@ -72,7 +72,7 @@ export class DataSeriesModel extends ChartBaseElement {
72
72
  this.id = id;
73
73
  this.htId = htId;
74
74
  this.name = '';
75
- this.hovered = false;
75
+ this.highlighted = false;
76
76
  this._dataPoints = [];
77
77
  this.pricePrecisions = [2];
78
78
  /**
@@ -25,6 +25,10 @@ export interface ScaleHistoryItem {
25
25
  export declare const getDefaultHighLowWithIndex: () => HighLowWithIndex;
26
26
  export type ViewportPercent = number;
27
27
  type Constraints = (initialState: ViewportModelState, state: ViewportModelState) => ViewportModelState;
28
+ export interface ZoomReached {
29
+ max: boolean;
30
+ min: boolean;
31
+ }
28
32
  /**
29
33
  * The ScaleModel class represents the state of a chart's scale, including the current viewport, zoom level, and auto-scaling settings.
30
34
  * It extends the ViewportModel class, which provides the underlying implementation for handling viewports and zoom levels.
@@ -41,16 +45,13 @@ export declare class ScaleModel extends ViewportModel {
41
45
  private canvasAnimation;
42
46
  scaleInversedSubject: Subject<boolean>;
43
47
  beforeStartAnimationSubject: Subject<void>;
44
- history: ScaleHistoryItem[];
45
48
  autoScaleModel: AutoScaleViewportSubModel;
46
49
  zoomXYRatio: ZoomXToZoomYRatio;
47
- offsets: ChartConfigComponentsOffsets;
48
- xConstraints: Constraints[];
49
- maxZoomReached: {
50
- zoomIn: boolean;
51
- zoomOut: boolean;
52
- };
50
+ zoomReached: ZoomReached;
53
51
  readonly state: ChartScale;
52
+ history: ScaleHistoryItem[];
53
+ offsets: ChartConfigComponentsOffsets;
54
+ private xConstraints;
54
55
  constructor(config: FullChartConfig, getBounds: BoundsProvider, canvasAnimation: CanvasAnimation);
55
56
  protected doActivate(): void;
56
57
  protected doDeactivate(): void;
@@ -86,7 +87,10 @@ export declare class ScaleModel extends ViewportModel {
86
87
  zoomXToEnd(zoomIn: boolean, zoomSensitivity: number): void;
87
88
  haltAnimation(): void;
88
89
  private zoomXTo;
89
- isMaxZoomXReached(zoomIn: boolean): boolean;
90
+ calculateZoomReached(zoomX: Unit, zoomIn?: boolean): {
91
+ max: boolean;
92
+ min: boolean;
93
+ };
90
94
  /**
91
95
  * Moves the viewport to exactly xStart..xEnd place.
92
96
  * (you need to fire DRAW event after this)
@@ -7,10 +7,9 @@ import { Subject } from 'rxjs';
7
7
  import { startViewportModelAnimation } from '../animation/viewport-model-animation';
8
8
  import { cloneUnsafe } from '../utils/object.utils';
9
9
  import { AutoScaleViewportSubModel } from './scaling/auto-scale.model';
10
- import { zoomConstraint } from './scaling/constrait.functions';
11
10
  import { changeXToKeepRatio, changeYToKeepRatio, ratioFromZoomXY } from './scaling/lock-ratio.model';
12
11
  import { moveXStart, moveYStart } from './scaling/move-chart.functions';
13
- import { ViewportModel, compareStates } from './scaling/viewport.model';
12
+ import { ViewportModel, calculateZoom, compareStates, } from './scaling/viewport.model';
14
13
  import { zoomXToEndViewportCalculator, zoomXToPercentViewportCalculator } from './scaling/x-zooming.functions';
15
14
  export const getDefaultHighLowWithIndex = () => ({
16
15
  high: Number.NEGATIVE_INFINITY,
@@ -37,11 +36,11 @@ export class ScaleModel extends ViewportModel {
37
36
  this.scaleInversedSubject = new Subject();
38
37
  // y-axis component needs this subject in order to halt prev animation if axis type is percent
39
38
  this.beforeStartAnimationSubject = new Subject();
39
+ this.zoomXYRatio = 0;
40
+ this.zoomReached = { min: false, max: false };
40
41
  // TODO rework, make a new history based on units
41
42
  this.history = [];
42
- this.zoomXYRatio = 0;
43
43
  this.xConstraints = [];
44
- this.maxZoomReached = { zoomIn: false, zoomOut: false };
45
44
  this.scalePostProcessor = (initialState, state) => {
46
45
  // for now <s>reduceRight<s/> reduce bcs ChartModel#getZoomConstrait should be invoked first
47
46
  // if we will need more complex order handling -> add some managing
@@ -50,16 +49,12 @@ export class ScaleModel extends ViewportModel {
50
49
  this.state = cloneUnsafe(config.scale);
51
50
  this.autoScaleModel = new AutoScaleViewportSubModel(this);
52
51
  this.offsets = this.config.components.offsets;
53
- this.addXConstraint((initialState, state) => {
54
- const { maxZoomReached, newState } = zoomConstraint(initialState, state, this.config.components.chart, this.getBounds);
55
- this.maxZoomReached = maxZoomReached;
56
- return newState;
57
- });
58
52
  }
59
53
  doActivate() {
60
54
  super.doActivate();
61
55
  this.scaleInversedSubject = new Subject();
62
56
  this.beforeStartAnimationSubject = new Subject();
57
+ this.zoomReached = this.calculateZoomReached(this.export().zoomX);
63
58
  this.addRxSubscription(this.scaleInversedSubject.subscribe(() => {
64
59
  this.fireChanged();
65
60
  }));
@@ -107,7 +102,7 @@ export class ScaleModel extends ViewportModel {
107
102
  this.beforeStartAnimationSubject.next();
108
103
  const state = this.export();
109
104
  zoomXToPercentViewportCalculator(this, state, viewportPercent, zoomSensitivity, zoomIn);
110
- this.zoomXTo(state, disabledAnimations);
105
+ this.zoomXTo(state, zoomIn, disabledAnimations);
111
106
  }
112
107
  /**
113
108
  * Zooms the X axis of the chart relativly to the end of the data range.
@@ -121,7 +116,7 @@ export class ScaleModel extends ViewportModel {
121
116
  this.beforeStartAnimationSubject.next();
122
117
  const state = this.export();
123
118
  zoomXToEndViewportCalculator(this, state, zoomSensitivity, zoomIn);
124
- this.zoomXTo(state, this.config.scale.disableAnimations);
119
+ this.zoomXTo(state, zoomIn, this.config.scale.disableAnimations);
125
120
  }
126
121
  haltAnimation() {
127
122
  var _a;
@@ -130,9 +125,13 @@ export class ScaleModel extends ViewportModel {
130
125
  this.doAutoScale();
131
126
  }
132
127
  }
133
- zoomXTo(state, forceNoAnimation) {
128
+ zoomXTo(state, zoomIn, forceNoAnimation) {
134
129
  const initialStateCopy = Object.assign({}, state);
135
130
  const constrainedState = this.scalePostProcessor(initialStateCopy, state);
131
+ this.zoomReached = this.calculateZoomReached(constrainedState.zoomX, zoomIn);
132
+ if (this.zoomReached.max || this.zoomReached.min) {
133
+ return;
134
+ }
136
135
  if (this.state.lockPriceToBarRatio) {
137
136
  changeYToKeepRatio(constrainedState, this.zoomXYRatio);
138
137
  }
@@ -146,8 +145,19 @@ export class ScaleModel extends ViewportModel {
146
145
  startViewportModelAnimation(this.canvasAnimation, this, constrainedState);
147
146
  }
148
147
  }
149
- isMaxZoomXReached(zoomIn) {
150
- return (this.maxZoomReached.zoomIn && zoomIn) || (this.maxZoomReached.zoomOut && !zoomIn);
148
+ calculateZoomReached(zoomX, zoomIn = true) {
149
+ const chartWidth = this.getBounds().width;
150
+ const delta = 0.001; // zoom values are very precise and should be compared with some precision delta
151
+ if (chartWidth > 0) {
152
+ const maxZoomReached = zoomX - calculateZoom(this.config.components.chart.minCandles, chartWidth) <= delta;
153
+ // max zoom reached and trying to zoom in further
154
+ const maxZoomDisabled = maxZoomReached && zoomIn;
155
+ const minZoomReached = zoomX - calculateZoom(chartWidth / this.config.components.chart.minWidth, chartWidth) >= delta;
156
+ // min zoom reached and trying to zoom out further
157
+ const minZoomDisabled = minZoomReached && !zoomIn;
158
+ return { max: maxZoomDisabled, min: minZoomDisabled };
159
+ }
160
+ return { max: false, min: false };
151
161
  }
152
162
  /**
153
163
  * Moves the viewport to exactly xStart..xEnd place.
@@ -160,8 +170,16 @@ export class ScaleModel extends ViewportModel {
160
170
  setXScale(xStart, xEnd, forceNoAnimation = true) {
161
171
  const initialState = this.export();
162
172
  const zoomX = this.calculateZoomX(xStart, xEnd);
173
+ if (initialState.xStart === xStart && initialState.xEnd === xEnd && initialState.zoomX > 0) {
174
+ return;
175
+ }
163
176
  const state = Object.assign(Object.assign({}, initialState), { zoomX, xStart, xEnd });
164
177
  const constrainedState = this.scalePostProcessor(initialState, state);
178
+ const zoomIn = constrainedState.xEnd - constrainedState.xStart < initialState.xEnd - initialState.xStart;
179
+ this.zoomReached = this.calculateZoomReached(zoomX, zoomIn);
180
+ if (this.zoomReached.max || this.zoomReached.min) {
181
+ return;
182
+ }
165
183
  if (this.state.lockPriceToBarRatio) {
166
184
  changeYToKeepRatio(constrainedState, this.zoomXYRatio);
167
185
  }
@@ -178,6 +196,9 @@ export class ScaleModel extends ViewportModel {
178
196
  }
179
197
  setYScale(yStart, yEnd, fire = false) {
180
198
  const initialState = this.export();
199
+ if (initialState.yStart === yStart && initialState.yEnd === yEnd && initialState.zoomY > 0) {
200
+ return;
201
+ }
181
202
  super.setYScale(yStart, yEnd, fire);
182
203
  const state = this.export();
183
204
  const constrainedState = this.scalePostProcessor(initialState, state);
@@ -3,8 +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 { ChartConfigComponentsChart } from '../../chart.config';
7
- import { Bounds, BoundsProvider } from '../bounds.model';
6
+ import { Bounds } from '../bounds.model';
8
7
  import VisualCandle from '../visual-candle';
9
8
  import { ViewportModelState } from './viewport.model';
10
9
  /**
@@ -27,15 +26,3 @@ export declare const candleEdgesConstrait: (state: ViewportModelState, visualCan
27
26
  zoomY: number;
28
27
  inverseY: boolean;
29
28
  };
30
- /**
31
- * This function limits minimum and maximum chart viewport dependening on visible x-units
32
- * @returns
33
- * @doc-tags viewport,zoom,scaling
34
- */
35
- export declare const zoomConstraint: (_: ViewportModelState, state: ViewportModelState, chartConfig: ChartConfigComponentsChart, boundsProvider: BoundsProvider) => {
36
- newState: ViewportModelState;
37
- maxZoomReached: {
38
- zoomIn: boolean;
39
- zoomOut: boolean;
40
- };
41
- };
@@ -4,7 +4,7 @@
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
6
  import { at } from '../../utils/array.utils';
7
- import { calculateZoom, pixelsToUnits } from './viewport.model';
7
+ import { pixelsToUnits } from './viewport.model';
8
8
  /**
9
9
  * Return constraited state that handled zooming and moving chart near first/last candles
10
10
  * this and other constraits that works with state should mutate and return state
@@ -35,31 +35,3 @@ export const candleEdgesConstrait = (state, visualCandlesCoordinates, candleLimi
35
35
  newState.xEnd = normalizedXEnd;
36
36
  return newState;
37
37
  };
38
- /**
39
- * This function limits minimum and maximum chart viewport dependening on visible x-units
40
- * @returns
41
- * @doc-tags viewport,zoom,scaling
42
- */
43
- export const zoomConstraint = (_, state, chartConfig, boundsProvider) => {
44
- const newState = Object.assign({}, state);
45
- const bounds = boundsProvider();
46
- // 1 - is an average candle width: newXEnd - newXStart = avg candles amount in the viewport
47
- const avgCandlesInViewport = newState.xEnd - newState.xStart;
48
- const minViewportReached = avgCandlesInViewport < chartConfig.minCandles;
49
- const maxCandlesInViewport = bounds.width / chartConfig.minWidth;
50
- const maxViewportReached = avgCandlesInViewport > maxCandlesInViewport;
51
- // rules work only if chart is shown
52
- if (bounds.width > 0) {
53
- if (maxViewportReached) {
54
- newState.xStart = newState.xEnd - maxCandlesInViewport;
55
- newState.zoomX = calculateZoom(newState.xEnd - newState.xStart, bounds.width);
56
- return { newState, maxZoomReached: { zoomOut: true, zoomIn: false } };
57
- }
58
- if (minViewportReached) {
59
- newState.xStart = newState.xEnd - chartConfig.minCandles;
60
- newState.zoomX = calculateZoom(newState.xEnd - newState.xStart, bounds.width);
61
- return { newState, maxZoomReached: { zoomOut: false, zoomIn: true } };
62
- }
63
- }
64
- return { newState, maxZoomReached: { zoomOut: false, zoomIn: false } };
65
- };