@devexperts/dxcharts-lite 2.5.4 → 2.5.6

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.
@@ -83,7 +83,7 @@ export class CanvasBoundsContainer {
83
83
  this.rightRatio = 0;
84
84
  this.boundsChangedSubject = new Subject();
85
85
  this.barResizerChangedSubject = new Subject();
86
- this._graphsHeightRatio = { chart: 1 };
86
+ this._graphsHeightRatio = { [CHART_UUID]: 1 };
87
87
  this.graphsHeightRatioChangedSubject = new Subject();
88
88
  this.boundsChangedSubscriptions = {};
89
89
  chartResizeHandler.canvasResized.subscribe(bcr => {
@@ -15,7 +15,6 @@ export const createBasicScaleViewportTransformer = (scale) => (visualCandleSourc
15
15
  const endCandle = vCandles[vCandles.length - 1];
16
16
  scale.setXScale(startCandle.startUnit, endCandle.startUnit + endCandle.width + scale.offsets.right);
17
17
  scale.doAutoScale(true);
18
- scale.recalculateZoomXYRatio();
19
18
  scale.fireChanged();
20
19
  }
21
20
  };
@@ -10,10 +10,11 @@ import { PartialCandle } from './chart.component';
10
10
  * so there is not enough information to build a candle: only Open/Close value available.
11
11
  * In this case Daily candle, which we receive, must be completed to full OHLC with equal values.
12
12
  */
13
- export declare const prepareCandle: (candle: PartialCandle) => Candle;
13
+ export declare const prepareCandle: (candle: PartialCandle) => Candle | undefined;
14
14
  /**
15
15
  * Adds index to candles according to their array index.
16
16
  * @param candles
17
17
  */
18
18
  export declare const reindexCandles: (candles: Array<Candle>) => void;
19
19
  export declare const deleteCandlesIndex: (candles: Array<Candle>) => void;
20
+ export declare const isCandle: (value: Candle | undefined) => value is Candle;
@@ -11,27 +11,33 @@ import { finite } from '../../utils/math.utils';
11
11
  */
12
12
  export const prepareCandle = (candle) => {
13
13
  var _a;
14
- const settlementPrice = finite(candle.close, candle.open, candle.hi, candle.lo);
15
- if (!isFinite(settlementPrice)) {
16
- throw new Error('Received candle without any price');
14
+ try {
15
+ const settlementPrice = finite(candle.close, candle.open, candle.hi, candle.lo);
16
+ if (!isFinite(settlementPrice)) {
17
+ throw new Error('Received candle without any price');
18
+ }
19
+ // @ts-ignore
20
+ const preparedCandleHi = finite(candle.hi, Math.max(candle.open, candle.close), settlementPrice);
21
+ // @ts-ignore
22
+ const preparedCandleLo = finite(candle.lo, Math.min(candle.open, candle.close), settlementPrice);
23
+ const preparedCandleOpen = finite(candle.open, candle.lo, settlementPrice);
24
+ const preparedCandleClose = finite(candle.close, candle.hi, settlementPrice);
25
+ return {
26
+ hi: preparedCandleHi,
27
+ lo: preparedCandleLo,
28
+ open: preparedCandleOpen,
29
+ close: preparedCandleClose,
30
+ timestamp: candle.timestamp,
31
+ volume: (_a = candle.volume) !== null && _a !== void 0 ? _a : 0,
32
+ expansion: candle.expansion,
33
+ idx: candle.idx,
34
+ impVolatility: candle.impVolatility,
35
+ };
36
+ }
37
+ catch (e) {
38
+ console.warn(e);
39
+ return;
17
40
  }
18
- // @ts-ignore
19
- const preparedCandleHi = finite(candle.hi, Math.max(candle.open, candle.close), settlementPrice);
20
- // @ts-ignore
21
- const preparedCandleLo = finite(candle.lo, Math.min(candle.open, candle.close), settlementPrice);
22
- const preparedCandleOpen = finite(candle.open, candle.lo, settlementPrice);
23
- const preparedCandleClose = finite(candle.close, candle.hi, settlementPrice);
24
- return {
25
- hi: preparedCandleHi,
26
- lo: preparedCandleLo,
27
- open: preparedCandleOpen,
28
- close: preparedCandleClose,
29
- timestamp: candle.timestamp,
30
- volume: (_a = candle.volume) !== null && _a !== void 0 ? _a : 0,
31
- expansion: candle.expansion,
32
- idx: candle.idx,
33
- impVolatility: candle.impVolatility,
34
- };
35
41
  };
36
42
  /**
37
43
  * Adds index to candles according to their array index.
@@ -47,3 +53,4 @@ export const deleteCandlesIndex = (candles) => {
47
53
  candle.idx = undefined;
48
54
  });
49
55
  };
56
+ export const isCandle = (value) => value !== undefined;
@@ -100,6 +100,7 @@ export class ChartComponent extends ChartBaseElement {
100
100
  this.dynamicObjects.model.addObject({
101
101
  id: series.id,
102
102
  paneId: series.extentComponent.paneUUID,
103
+ parentId: series.parentId,
103
104
  model: series,
104
105
  drawer: this.dataSeriesDrawer,
105
106
  });
@@ -20,7 +20,7 @@ import { floor, round } from '../../utils/math.utils';
20
20
  import { merge as mergeObj } from '../../utils/merge.utils';
21
21
  import { createBasicScaleViewportTransformer, createTimeFrameViewportTransformer } from './basic-scale';
22
22
  import { calculateCandleWidth } from './candle-width-calculator.functions';
23
- import { deleteCandlesIndex, prepareCandle, reindexCandles } from './candle.functions';
23
+ import { deleteCandlesIndex, isCandle, prepareCandle, reindexCandles } from './candle.functions';
24
24
  import { ChartInstrument } from './chart.component';
25
25
  import { fakeCandle } from './fake-candles';
26
26
  import { SecondaryChartColorsPool } from './secondary-chart-colors-pool';
@@ -162,9 +162,9 @@ export class ChartModel extends ChartBaseElement {
162
162
  * @returns {CandleSeriesModel | undefined} - The newly created secondary candle series model or undefined if it could not be created.
163
163
  */
164
164
  setSecondaryCandleSeries(candles, instrument = this.mainCandleSeries.instrument, recalculateAndUpdate = true) {
165
- const prepareCandleCandles = sortCandles(candles.map(prepareCandle));
165
+ const preparedCandles = prepareCandles(candles);
166
166
  // set correct indexes based on main candles timestamp
167
- const reindexCandles = this.reindexCandlesBasedOnSeries(this.mainCandleSeries.dataPoints, prepareCandleCandles);
167
+ const reindexCandles = this.reindexCandlesBasedOnSeries(this.mainCandleSeries.dataPoints, preparedCandles);
168
168
  // ensure there are no gaps in new candles
169
169
  const secondaryCandles = this.secondarySeriesAdjustments(this.mainCandleSeries.dataPoints, reindexCandles);
170
170
  // create a new secondary series model if it doesn't already exist
@@ -198,10 +198,10 @@ export class ChartModel extends ChartBaseElement {
198
198
  this.mainInstrumentChangedSubject.next(mainSeries.instrument);
199
199
  }
200
200
  this.rememberCurrentTimeframe();
201
- const prepareCandleCandles = sortCandles(mainSeries.candles.map(prepareCandle));
201
+ const preparedCandles = prepareCandles(mainSeries.candles);
202
202
  this.mainCandleSeries.clearData();
203
- reindexCandles(prepareCandleCandles);
204
- this.mainCandleSeries.dataPoints = prepareCandleCandles;
203
+ reindexCandles(preparedCandles);
204
+ this.mainCandleSeries.dataPoints = preparedCandles;
205
205
  // deactivate deleted series
206
206
  this.secondaryCandleSeries
207
207
  .filter(series => {
@@ -282,7 +282,7 @@ export class ChartModel extends ChartBaseElement {
282
282
  console.error('All series update failed. Instruments for series are different.');
283
283
  return;
284
284
  }
285
- const preparedCandles = sortCandles(mainSeries.candles.map(prepareCandle));
285
+ const preparedCandles = prepareCandles(mainSeries.candles);
286
286
  const updateResult = updateCandles(this.mainCandleSeries.dataPoints, preparedCandles);
287
287
  const updatedCandles = updateResult.candles;
288
288
  reindexCandles(updatedCandles);
@@ -290,7 +290,7 @@ export class ChartModel extends ChartBaseElement {
290
290
  // re-create series
291
291
  secondarySeries.map(series => {
292
292
  var _a, _b, _c, _d;
293
- const preparedCandles = sortCandles(series.candles.map(prepareCandle));
293
+ const preparedCandles = prepareCandles(series.candles);
294
294
  const updatedCandles = updateCandles((_d = (_c = this.findSecondarySeriesBySymbol((_b = (_a = series.instrument) === null || _a === void 0 ? void 0 : _a.symbol) !== null && _b !== void 0 ? _b : '')) === null || _c === void 0 ? void 0 : _c.dataPoints) !== null && _d !== void 0 ? _d : [], preparedCandles).candles;
295
295
  return this.setSecondaryCandleSeries(updatedCandles, series.instrument, false);
296
296
  });
@@ -932,6 +932,7 @@ export class ChartModel extends ChartBaseElement {
932
932
  }
933
933
  }
934
934
  const sortCandles = (candles) => candles.slice().sort((a, b) => (a.timestamp === b.timestamp ? 0 : a.timestamp > b.timestamp ? 1 : -1));
935
+ const prepareCandles = (candles) => sortCandles(candles.map(prepareCandle).filter(isCandle));
935
936
  const findFirstNotEmptyCandle = (candles, startIdx, iterateStep) => {
936
937
  if (startIdx >= candles.length) {
937
938
  return candles[candles.length - 1];
@@ -14,17 +14,19 @@ export interface DynamicObject<T = unknown> {
14
14
  readonly drawer: DynamicModelDrawer<T>;
15
15
  readonly paneId: PaneId;
16
16
  readonly model?: T;
17
+ readonly parentId?: DynamicObjectId;
17
18
  }
18
19
  export declare class DynamicObjectsModel extends ChartBaseElement {
19
20
  private canvasModel;
20
21
  private _objects;
21
22
  private modelIdToObjectMap;
23
+ private _uniqueObjects;
22
24
  constructor(canvasModel: CanvasModel);
23
25
  /**
24
26
  * @returns the `DynamicObject` itself and pane `LinkedList` where the object is stored.
25
27
  *
26
28
  */
27
- private getObjectInfoById;
29
+ getObjectInfoById(id: DynamicObjectId): [DynamicObject, LinkedList<DynamicObject>] | undefined;
28
30
  /**
29
31
  * @returns `DynamicObject` position in associated pane `LinkedList`
30
32
  * @returns `-1` if an object was not found
@@ -78,6 +80,10 @@ export declare class DynamicObjectsModel extends ChartBaseElement {
78
80
  * Getter for the objects
79
81
  */
80
82
  get objects(): Record<string, LinkedList<DynamicObject<unknown>>>;
83
+ /**
84
+ * Getter for the unique objects, unique object is an entity which is either dynamic object itself, or its parent
85
+ */
86
+ get uniqueObjects(): Record<string, Set<DynamicObjectId>>;
81
87
  /**
82
88
  * Sets the objects
83
89
  * @param objects
@@ -11,6 +11,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
11
11
  super();
12
12
  this.canvasModel = canvasModel;
13
13
  this.modelIdToObjectMap = new Map();
14
+ this._uniqueObjects = {};
14
15
  this._objects = new BehaviorSubject({});
15
16
  }
16
17
  /**
@@ -49,7 +50,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
49
50
  * @param obj
50
51
  */
51
52
  addObject(obj) {
52
- var _a;
53
+ var _a, _b;
53
54
  const paneId = obj.paneId;
54
55
  const objects = this.objects;
55
56
  const paneList = (_a = objects[paneId]) !== null && _a !== void 0 ? _a : new LinkedList();
@@ -58,6 +59,10 @@ export class DynamicObjectsModel extends ChartBaseElement {
58
59
  }
59
60
  paneList.insertAtEnd(obj);
60
61
  this.modelIdToObjectMap.set(obj.id, obj);
62
+ if (!this.uniqueObjects[paneId]) {
63
+ this.uniqueObjects[paneId] = new Set();
64
+ }
65
+ this.uniqueObjects[paneId].add((_b = obj.parentId) !== null && _b !== void 0 ? _b : obj.id);
61
66
  this.setDynamicObjects(objects);
62
67
  }
63
68
  /**
@@ -65,6 +70,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
65
70
  * @param id
66
71
  */
67
72
  removeObject(id) {
73
+ var _a;
68
74
  const objInfo = this.getObjectInfoById(id);
69
75
  if (!objInfo) {
70
76
  return;
@@ -74,6 +80,9 @@ export class DynamicObjectsModel extends ChartBaseElement {
74
80
  const targetPos = paneList.getNodePosition(targetNode);
75
81
  paneList.removeAt(targetPos);
76
82
  this.modelIdToObjectMap.delete(id);
83
+ if (this.uniqueObjects[obj.paneId]) {
84
+ this.uniqueObjects[obj.paneId].delete((_a = obj.parentId) !== null && _a !== void 0 ? _a : obj.id);
85
+ }
77
86
  if (paneList.size() === 0) {
78
87
  delete this.objects[obj.paneId];
79
88
  }
@@ -198,6 +207,12 @@ export class DynamicObjectsModel extends ChartBaseElement {
198
207
  get objects() {
199
208
  return this._objects.getValue();
200
209
  }
210
+ /**
211
+ * Getter for the unique objects, unique object is an entity which is either dynamic object itself, or its parent
212
+ */
213
+ get uniqueObjects() {
214
+ return this._uniqueObjects;
215
+ }
201
216
  /**
202
217
  * Sets the objects
203
218
  * @param objects
@@ -47,6 +47,7 @@ export declare class DataSeriesModel<D extends DataSeriesPoint = DataSeriesPoint
47
47
  extentComponent: YExtentComponent;
48
48
  id: string;
49
49
  htId: number;
50
+ parentId?: string | number | undefined;
50
51
  name: string;
51
52
  highlighted: boolean;
52
53
  yAxisLabelProvider: DataSeriesYAxisLabelsProvider;
@@ -71,7 +72,7 @@ export declare class DataSeriesModel<D extends DataSeriesPoint = DataSeriesPoint
71
72
  get visualPoints(): V[];
72
73
  get visualPoints2D(): V[][];
73
74
  set visualPoints(points: V[][] | V[]);
74
- constructor(extentComponent: YExtentComponent, id: string, htId: number, _config?: AtLeastOne<DataSeriesConfig>);
75
+ constructor(extentComponent: YExtentComponent, id: string, htId: number, parentId?: string | number | undefined, _config?: AtLeastOne<DataSeriesConfig>);
75
76
  protected doActivate(): void;
76
77
  /**
77
78
  * Sets the data points and recalculates internal state
@@ -65,12 +65,13 @@ export class DataSeriesModel extends ChartBaseElement {
65
65
  this._visualPoints = create2DArray(points);
66
66
  this._visualPointsFlat = this._visualPoints.flat();
67
67
  }
68
- constructor(extentComponent, id, htId, _config = cloneUnsafe(DEFAULT_DATA_SERIES_CONFIG)) {
68
+ constructor(extentComponent, id, htId, parentId, _config = cloneUnsafe(DEFAULT_DATA_SERIES_CONFIG)) {
69
69
  var _a;
70
70
  super();
71
71
  this.extentComponent = extentComponent;
72
72
  this.id = id;
73
73
  this.htId = htId;
74
+ this.parentId = parentId;
74
75
  this.name = '';
75
76
  this.highlighted = false;
76
77
  this._dataPoints = [];
@@ -7,7 +7,6 @@ import { Subject } from 'rxjs';
7
7
  import { ChartConfigComponentsOffsets, ChartScale, FullChartConfig } from '../chart.config';
8
8
  import { CanvasAnimation } from '../animation/canvas-animation';
9
9
  import { AutoScaleViewportSubModel } from './scaling/auto-scale.model';
10
- import { ZoomXToZoomYRatio } from './scaling/lock-ratio.model';
11
10
  import { Price, Unit, ViewportModel, ViewportModelState, Zoom } from './scaling/viewport.model';
12
11
  import { BoundsProvider } from './bounds.model';
13
12
  export interface HighLowWithIndex {
@@ -46,7 +45,6 @@ export declare class ScaleModel extends ViewportModel {
46
45
  scaleInversedSubject: Subject<boolean>;
47
46
  beforeStartAnimationSubject: Subject<void>;
48
47
  autoScaleModel: AutoScaleViewportSubModel;
49
- zoomXYRatio: ZoomXToZoomYRatio;
50
48
  zoomReached: ZoomReached;
51
49
  readonly state: ChartScale;
52
50
  history: ScaleHistoryItem[];
@@ -101,6 +99,7 @@ export declare class ScaleModel extends ViewportModel {
101
99
  */
102
100
  setXScale(xStart: Unit, xEnd: Unit, forceNoAnimation?: boolean): void;
103
101
  setYScale(yStart: Unit, yEnd: Unit, fire?: boolean): void;
102
+ private setLockedYScale;
104
103
  /**
105
104
  * Moves both xStart and xEnd without changing the viewport width (zoom).
106
105
  * Works without animation.
@@ -165,10 +164,6 @@ export declare class ScaleModel extends ViewportModel {
165
164
  * @param value - If true, the price-to-bar ratio will be locked. If false, it will not be locked.
166
165
  */
167
166
  setLockPriceToBarRatio(value?: boolean): void;
168
- /**
169
- * Recalculates the zoom X/Y ratio based on the current zoom levels.
170
- */
171
- recalculateZoomXYRatio(): void;
172
167
  }
173
168
  /**
174
169
  * The SyncedByXScaleModel class extends the ScaleModel class and adds support for synchronization with other ScaleModel instance, so both instances maintain the same X-axis bounds.
@@ -7,7 +7,7 @@ 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 { changeXToKeepRatio, changeYToKeepRatio, ratioFromZoomXY } from './scaling/lock-ratio.model';
10
+ import { changeXToKeepRatio, changeYToKeepRatio } from './scaling/lock-ratio.model';
11
11
  import { moveXStart, moveYStart } from './scaling/move-chart.functions';
12
12
  import { ViewportModel, calculateZoom, compareStates, } from './scaling/viewport.model';
13
13
  import { zoomXToEndViewportCalculator, zoomXToPercentViewportCalculator } from './scaling/x-zooming.functions';
@@ -36,7 +36,6 @@ export class ScaleModel extends ViewportModel {
36
36
  this.scaleInversedSubject = new Subject();
37
37
  // y-axis component needs this subject in order to halt prev animation if axis type is percent
38
38
  this.beforeStartAnimationSubject = new Subject();
39
- this.zoomXYRatio = 0;
40
39
  this.zoomReached = { min: false, max: false };
41
40
  // TODO rework, make a new history based on units
42
41
  this.history = [];
@@ -133,7 +132,7 @@ export class ScaleModel extends ViewportModel {
133
132
  return;
134
133
  }
135
134
  if (this.state.lockPriceToBarRatio) {
136
- changeYToKeepRatio(constrainedState, this.zoomXYRatio);
135
+ changeYToKeepRatio(initialStateCopy, constrainedState);
137
136
  }
138
137
  if (this.state.auto) {
139
138
  this.autoScaleModel.doAutoYScale(constrainedState);
@@ -181,7 +180,7 @@ export class ScaleModel extends ViewportModel {
181
180
  return;
182
181
  }
183
182
  if (this.state.lockPriceToBarRatio) {
184
- changeYToKeepRatio(constrainedState, this.zoomXYRatio);
183
+ changeYToKeepRatio(initialState, constrainedState);
185
184
  }
186
185
  if (this.state.auto) {
187
186
  this.autoScaleModel.doAutoYScale(constrainedState);
@@ -199,21 +198,30 @@ export class ScaleModel extends ViewportModel {
199
198
  if (initialState.yStart === yStart && initialState.yEnd === yEnd && initialState.zoomY > 0) {
200
199
  return;
201
200
  }
201
+ if (this.state.lockPriceToBarRatio) {
202
+ this.setLockedYScale(yStart, yEnd, fire, initialState);
203
+ return;
204
+ }
202
205
  super.setYScale(yStart, yEnd, fire);
203
206
  const state = this.export();
204
207
  const constrainedState = this.scalePostProcessor(initialState, state);
205
- if (this.state.lockPriceToBarRatio) {
206
- changeXToKeepRatio(constrainedState, this.zoomXYRatio);
207
- this.setXScale(constrainedState.xStart, constrainedState.xEnd);
208
- // TODO: rewrite logic for applying constraints to consider both axes, now constraints on Y may not work correctly
209
- return;
208
+ if (this.state.auto) {
209
+ this.autoScaleModel.doAutoYScale(constrainedState);
210
210
  }
211
- else {
212
- if (this.state.auto) {
213
- this.autoScaleModel.doAutoYScale(constrainedState);
214
- }
215
- this.apply(constrainedState);
211
+ this.apply(constrainedState);
212
+ }
213
+ setLockedYScale(yStart, yEnd, fire = false, initialState) {
214
+ const zoomIn = yEnd < initialState.yEnd;
215
+ if ((this.zoomReached.min && zoomIn === false) || (this.zoomReached.max && zoomIn === true)) {
216
+ return;
216
217
  }
218
+ super.setYScale(yStart, yEnd, fire);
219
+ const state = this.export();
220
+ const constrainedState = this.scalePostProcessor(initialState, state);
221
+ changeXToKeepRatio(initialState, constrainedState);
222
+ this.zoomReached = this.calculateZoomReached(constrainedState.zoomX, zoomIn);
223
+ this.apply(constrainedState);
224
+ this.fireChanged();
217
225
  }
218
226
  /**
219
227
  * Moves both xStart and xEnd without changing the viewport width (zoom).
@@ -342,17 +350,8 @@ export class ScaleModel extends ViewportModel {
342
350
  this.state.lockPriceToBarRatio = false;
343
351
  return;
344
352
  }
345
- if (value) {
346
- this.recalculateZoomXYRatio();
347
- }
348
353
  this.state.lockPriceToBarRatio = value;
349
354
  }
350
- /**
351
- * Recalculates the zoom X/Y ratio based on the current zoom levels.
352
- */
353
- recalculateZoomXYRatio() {
354
- this.zoomXYRatio = ratioFromZoomXY(this.zoomX, this.zoomY);
355
- }
356
355
  }
357
356
  /**
358
357
  * The SyncedByXScaleModel class extends the ScaleModel class and adds support for synchronization with other ScaleModel instance, so both instances maintain the same X-axis bounds.
@@ -10,13 +10,13 @@ export declare const zoomXToZoomY: (zoomX: Zoom, ratio: ZoomXToZoomYRatio) => Zo
10
10
  export declare const zoomYToZoomX: (zoomY: Zoom, ratio: ZoomXToZoomYRatio) => Zoom;
11
11
  /**
12
12
  * Locks the zoomY with zoomX and zooms y-scale depending on x-scale.
13
- * @param state
14
- * @param zoomXYRatio
13
+ * @param prevState
14
+ * @param newState
15
15
  */
16
- export declare const changeYToKeepRatio: (state: ViewportModelState, zoomXYRatio: ZoomXToZoomYRatio) => void;
16
+ export declare const changeYToKeepRatio: (prevState: ViewportModelState, newState: ViewportModelState) => void;
17
17
  /**
18
18
  * Locks the zoomY with zoomX and zooms x-scale depending on y-scale.
19
- * @param state
20
- * @param zoomXYRatio
19
+ * @param prevState
20
+ * @param newState
21
21
  */
22
- export declare const changeXToKeepRatio: (state: ViewportModelState, zoomXYRatio: ZoomXToZoomYRatio) => void;
22
+ export declare const changeXToKeepRatio: (prevState: ViewportModelState, newState: ViewportModelState) => void;
@@ -8,30 +8,36 @@ export const zoomXToZoomY = (zoomX, ratio) => zoomX / ratio;
8
8
  export const zoomYToZoomX = (zoomY, ratio) => zoomY * ratio;
9
9
  /**
10
10
  * Locks the zoomY with zoomX and zooms y-scale depending on x-scale.
11
- * @param state
12
- * @param zoomXYRatio
11
+ * @param prevState
12
+ * @param newState
13
13
  */
14
- export const changeYToKeepRatio = (state, zoomXYRatio) => {
15
- const prevZoomY = state.zoomY;
16
- state.zoomY = zoomXToZoomY(state.zoomX, zoomXYRatio);
17
- const zoomYMult = state.zoomY / prevZoomY;
18
- const lastYHeight = state.yEnd - state.yStart;
19
- const newYHeight = lastYHeight * zoomYMult;
20
- const delta = newYHeight - lastYHeight;
21
- state.yEnd = state.yEnd + delta / 2;
22
- state.yStart = state.yStart - delta / 2;
14
+ export const changeYToKeepRatio = (prevState, newState) => {
15
+ const prevZoomX = prevState.zoomX;
16
+ const prevZoomY = prevState.zoomY;
17
+ const prevZoomXY = ratioFromZoomXY(prevZoomX, prevZoomY);
18
+ const prevYHeight = prevState.yEnd - prevState.yStart;
19
+ // recalculate zoomY
20
+ newState.zoomY = zoomXToZoomY(newState.zoomX, prevZoomXY);
21
+ const zoomYMult = newState.zoomY / prevZoomY;
22
+ const newYHeight = prevYHeight * zoomYMult;
23
+ const delta = newYHeight - prevYHeight;
24
+ newState.yEnd = newState.yEnd + delta / 2;
25
+ newState.yStart = newState.yStart - delta / 2;
23
26
  };
24
27
  /**
25
28
  * Locks the zoomY with zoomX and zooms x-scale depending on y-scale.
26
- * @param state
27
- * @param zoomXYRatio
29
+ * @param prevState
30
+ * @param newState
28
31
  */
29
- export const changeXToKeepRatio = (state, zoomXYRatio) => {
30
- const prevZoomX = state.zoomX;
31
- state.zoomX = zoomYToZoomX(state.zoomY, zoomXYRatio);
32
- const zoomXMult = state.zoomX / prevZoomX;
33
- const lastXWidth = state.xEnd - state.xStart;
34
- const newXWidth = lastXWidth * zoomXMult;
35
- const delta = newXWidth - lastXWidth;
36
- state.xStart = state.xStart - delta;
32
+ export const changeXToKeepRatio = (prevState, newState) => {
33
+ const prevZoomX = prevState.zoomX;
34
+ const prevZoomY = prevState.zoomY;
35
+ const prevZoomXY = ratioFromZoomXY(prevZoomX, prevZoomY);
36
+ const prevXWidth = prevState.xEnd - prevState.xStart;
37
+ // recalculate zoomX
38
+ newState.zoomX = zoomYToZoomX(newState.zoomY, prevZoomXY);
39
+ const zoomXMult = newState.zoomX / prevZoomX;
40
+ const newXWidth = prevXWidth * zoomXMult;
41
+ const delta = newXWidth - prevXWidth;
42
+ newState.xStart = newState.xStart - delta;
37
43
  };
@@ -287,7 +287,12 @@ export class ViewportModel extends ChartBaseElement {
287
287
  * @returns {boolean} - Returns true if the viewport is valid, false otherwise.
288
288
  */
289
289
  isViewportValid() {
290
- return this.xStart !== this.xEnd && this.yStart !== this.yEnd && isFinite(this.yStart) && isFinite(this.yEnd) && this.zoomX > 0 && this.zoomY > 0;
290
+ return (this.xStart !== this.xEnd &&
291
+ this.yStart !== this.yEnd &&
292
+ isFinite(this.yStart) &&
293
+ isFinite(this.yEnd) &&
294
+ this.zoomX > 0 &&
295
+ this.zoomY > 0);
291
296
  }
292
297
  }
293
298
  export const compareStates = (a, b) => !keys(a).some(k => a[k] !== b[k]);