@devexperts/dxcharts-lite 2.2.0 → 2.3.0

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 (28) hide show
  1. package/dist/chart/canvas/canvas-bounds-container.d.ts +2 -2
  2. package/dist/chart/canvas/canvas-bounds-container.js +2 -2
  3. package/dist/chart/chart.config.d.ts +17 -4
  4. package/dist/chart/chart.config.js +6 -2
  5. package/dist/chart/components/chart/chart-area-pan.handler.js +10 -5
  6. package/dist/chart/components/cross_tool/cross-tool.model.js +1 -1
  7. package/dist/chart/components/snapshot/snapshot.component.js +1 -0
  8. package/dist/chart/components/volumes/separate-volumes.component.js +2 -1
  9. package/dist/chart/components/volumes/volumes.formatter.d.ts +1 -1
  10. package/dist/chart/components/volumes/volumes.formatter.js +28 -25
  11. package/dist/chart/components/y_axis/y-axis-scale.handler.d.ts +5 -4
  12. package/dist/chart/components/y_axis/y-axis-scale.handler.js +9 -9
  13. package/dist/chart/components/y_axis/y-axis.component.js +1 -0
  14. package/dist/chart/drawers/data-series.drawer.js +1 -1
  15. package/dist/chart/inputlisteners/canvas-input-listener.component.d.ts +9 -0
  16. package/dist/chart/inputlisteners/canvas-input-listener.component.js +38 -1
  17. package/dist/chart/model/scale.model.d.ts +3 -2
  18. package/dist/chart/model/scale.model.js +23 -5
  19. package/dist/chart/model/scaling/constrait.functions.d.ts +1 -1
  20. package/dist/chart/model/scaling/constrait.functions.js +1 -2
  21. package/dist/chart/model/scaling/lock-ratio.model.d.ts +8 -2
  22. package/dist/chart/model/scaling/lock-ratio.model.js +18 -3
  23. package/dist/chart/utils/device/touchpad.utils.d.ts +3 -3
  24. package/dist/chart/utils/device/touchpad.utils.js +3 -5
  25. package/dist/chart/utils/math.utils.d.ts +0 -1
  26. package/dist/chart/utils/math.utils.js +1 -1
  27. package/dist/dxchart.min.js +4 -4
  28. package/package.json +1 -1
@@ -35,11 +35,11 @@ export declare class CanvasElement {
35
35
  static CHART_WITH_Y_AXIS: string;
36
36
  static EVENTS: string;
37
37
  /**
38
- * @deprecated - use CanvasElement.PANE_UUID(CHART_UUID) instead
38
+ * @returns pane bounds for the main chart
39
39
  */
40
40
  static CHART: string;
41
41
  /**
42
- * @deprecated - use CanvasElement.PANE_UUID_Y_AXIS(CHART_UUID) instead
42
+ * @returns y-axis bounds for the main chart
43
43
  */
44
44
  static Y_AXIS: string;
45
45
  }
@@ -31,11 +31,11 @@ CanvasElement.ALL_PANES = 'ALL_PANES';
31
31
  CanvasElement.CHART_WITH_Y_AXIS = 'CHART_WITH_Y_AXIS';
32
32
  CanvasElement.EVENTS = 'EVENTS';
33
33
  /**
34
- * @deprecated - use CanvasElement.PANE_UUID(CHART_UUID) instead
34
+ * @returns pane bounds for the main chart
35
35
  */
36
36
  CanvasElement.CHART = CanvasElement.PANE_UUID(CHART_UUID);
37
37
  /**
38
- * @deprecated - use CanvasElement.PANE_UUID_Y_AXIS(CHART_UUID) instead
38
+ * @returns y-axis bounds for the main chart
39
39
  */
40
40
  CanvasElement.Y_AXIS = CanvasElement.PANE_UUID_Y_AXIS(CHART_UUID);
41
41
  export { CanvasElement };
@@ -233,10 +233,23 @@ export interface ChartScale {
233
233
  * When dragging chart under specific angle - will automatically disable auto-scale.
234
234
  */
235
235
  autoScaleDisableOnDrag: AutoScaleDisableOnDrag;
236
- /**
237
- * 0..1 ratio of full viewport; 0.5 = middle, 0.75 = 3/4 of viewport
238
- */
239
- zoomSensitivity: number;
236
+ zoomSensitivity: {
237
+ /**
238
+ * Value is related to zoom event (zooming chart via mouse wheel)
239
+ * 0..1 ratio of full viewport; 0.5 = middle, 0.75 = 3/4 of viewport
240
+ */
241
+ wheel: number;
242
+ /**
243
+ * Value is related to pinch touchpad event (zooming chart via touchpad)
244
+ * 0..1 ratio of full viewport; 0.5 = middle, 0.75 = 3/4 of viewport
245
+ */
246
+ pinch: number;
247
+ /**
248
+ * Value is related to glide touchpad event (scrolling chart via touchpad)
249
+ * 0..1 ratio of full viewport; 0.5 = middle, 0.75 = 3/4 of viewport
250
+ */
251
+ glide: number;
252
+ };
240
253
  /**
241
254
  * Defines how much items (candles) will be in viewport when chart applies basic scale
242
255
  */
@@ -41,9 +41,13 @@ export const getDefaultConfig = () => ({
41
41
  yDiff: 80,
42
42
  },
43
43
  inverse: false,
44
- zoomSensitivity: 0.25,
44
+ zoomSensitivity: {
45
+ wheel: 0.25,
46
+ pinch: 0.05,
47
+ glide: 0.05,
48
+ },
45
49
  defaultViewportItems: 100,
46
- disableAnimations: false
50
+ disableAnimations: false,
47
51
  },
48
52
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
49
53
  components: {
@@ -60,10 +60,8 @@ export class ChartAreaPanHandler extends ChartBaseElement {
60
60
  * @param {WheelEvent} e - Wheel event
61
61
  * @returns {void}
62
62
  */
63
- this.zoomXHandler = (e) => {
64
- const isTouchpad = touchpadDetector(e);
63
+ this.zoomXHandler = (e, zoomSensitivity) => {
65
64
  const zoomIn = e.deltaY < 0;
66
- const zoomSensitivity = isTouchpad ? getTouchpadSensitivity(this.config) : this.config.scale.zoomSensitivity;
67
65
  if (this.config.scale.zoomToCursor) {
68
66
  const b = this.canvasBoundsContainer.getBounds(CanvasElement.CANVAS);
69
67
  const canvasW = b.width;
@@ -115,7 +113,13 @@ export class ChartAreaPanHandler extends ChartBaseElement {
115
113
  //#endregion
116
114
  this.addRxSubscription(merge(this.canvasInputListener.observeWheel(allPanesHitTest), this.canvasInputListener.observePinch(allPanesHitTest))
117
115
  .pipe(throttleTime(this.wheelTrottleTime, undefined, { trailing: true, leading: true }))
118
- .subscribe(this.zoomXHandler));
116
+ .subscribe(e => {
117
+ const isTouchpad = touchpadDetector(e);
118
+ const zoomSensitivity = isTouchpad
119
+ ? getTouchpadSensitivity(this.config.components.yAxis.type, this.config.scale.zoomSensitivity.pinch)
120
+ : this.config.scale.zoomSensitivity.wheel;
121
+ this.zoomXHandler(e, zoomSensitivity);
122
+ }));
119
123
  this.addRxSubscription(this.canvasInputListener
120
124
  .observeScrollGesture()
121
125
  .pipe(throttleTime(this.wheelTrottleTime, undefined, { trailing: true, leading: true }))
@@ -134,7 +138,8 @@ export class ChartAreaPanHandler extends ChartBaseElement {
134
138
  this.scale.moveXStart(this.scale.xStart - unitsDelta);
135
139
  }
136
140
  else if (deltaY !== 0 && Math.abs(deltaY) > Math.abs(deltaX)) {
137
- this.zoomXHandler(e);
141
+ const zoomSensitivity = getTouchpadSensitivity(this.config.components.yAxis.type, this.config.scale.zoomSensitivity.glide);
142
+ this.zoomXHandler(e, zoomSensitivity);
138
143
  }
139
144
  this.bus.fireDraw();
140
145
  }));
@@ -107,7 +107,7 @@ export class CrossToolModel extends ChartBaseElement {
107
107
  else {
108
108
  this.currentHover.y = hover.y;
109
109
  }
110
- this.currentHover.paneId = CHART_UUID;
110
+ this.currentHover.paneId = hover.paneId;
111
111
  this.currentHoverSubject.next(this.currentHover);
112
112
  }
113
113
  }
@@ -40,6 +40,7 @@ export class SnapshotComponent extends ChartBaseElement {
40
40
  ctx.drawImage(this.elements.mainCanvas, 0, 0, width, height);
41
41
  ctx.drawImage(this.elements.dynamicObjectsCanvas, 0, 0, width, height);
42
42
  ctx.drawImage(this.elements.crossToolCanvas, 0, 0, width, height);
43
+ ctx.drawImage(this.elements.yAxisLabelsCanvas, 0, 0, width, height);
43
44
  userDrawCallback && userDrawCallback(ctx);
44
45
  return new Promise((resolve, fail) => this.elements.snapshotCanvas.toBlob(blob => {
45
46
  return blob ? resolve(blob) : fail('Blob is null');
@@ -31,9 +31,10 @@ class SeparateVolumesComponent extends ChartBaseElement {
31
31
  */
32
32
  activateSeparateVolumes() {
33
33
  if (this.paneManager.panes[SeparateVolumesComponent.UUID] === undefined) {
34
+ const precision = 1;
34
35
  const volumePane = this.paneManager.createPane(SeparateVolumesComponent.UUID, {
35
36
  paneFormatters: {
36
- regular: volumeFormatter,
37
+ regular: (value) => volumeFormatter(value, precision),
37
38
  },
38
39
  useDefaultHighLow: false,
39
40
  increment: 1,
@@ -3,4 +3,4 @@
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
- export declare const volumeFormatter: (value: number) => string;
6
+ export declare const volumeFormatter: (value: number, precision?: number) => string;
@@ -3,34 +3,37 @@
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
- const DEFAULT_PRECISION = 5;
7
- const MIN_VALUE = 0.0000000001;
8
- const TRAILLING_ZEROES_REGEXP = /\.?0+$/;
9
- const TRAILLING_FRACTION_WITH_ZEROES_REGEXP = /(\.[1-9]*)0+$/;
10
- export const volumeFormatter = (value) => {
11
- const formatValue = (value, amountToCut) => {
12
- const rounder = Math.pow(10, DEFAULT_PRECISION);
13
- const roundedValue = Math.round(value * rounder) / rounder;
14
- if (roundedValue < 1 && roundedValue >= MIN_VALUE) {
15
- return roundedValue
16
- .toFixed(DEFAULT_PRECISION)
17
- .replace(TRAILLING_ZEROES_REGEXP, '')
18
- .replace(TRAILLING_FRACTION_WITH_ZEROES_REGEXP, '');
6
+ export const volumeFormatter = (value, precision = 1) => {
7
+ function formatNumber(value) {
8
+ let formattedResult;
9
+ const priceScale = Math.pow(10, precision);
10
+ value = Math.round(value * priceScale) / priceScale;
11
+ if (value >= 1e-15 && value < 1) {
12
+ formattedResult = value.toFixed(precision).replace(/\.?0+$/, ''); // regex removes trailing zeroes
19
13
  }
20
14
  else {
21
- return (roundedValue + '').replace(TRAILLING_FRACTION_WITH_ZEROES_REGEXP, '') + (amountToCut || '');
15
+ formattedResult = String(value);
22
16
  }
23
- };
24
- if (Math.abs(value) > 999999999) {
25
- value = Math.round(value / 1000000) * 1000000;
26
- return formatValue(value / 1000000000, 'B');
17
+ return formattedResult.replace(/(\.[1-9]*)0+$/, (e, p1) => p1);
27
18
  }
28
- if (Math.abs(value) > 999999) {
29
- value = 1000 * Math.round(value / 1000);
30
- return formatValue(value / 1000000, 'M');
31
- }
32
- if (Math.abs(value) > 9999) {
33
- return formatValue(value / 1000, 'K');
19
+ function format(value) {
20
+ let sign = '';
21
+ if (value < 0) {
22
+ sign = '-';
23
+ value = -value;
24
+ }
25
+ if (value < 995) {
26
+ return sign + formatNumber(value);
27
+ }
28
+ if (value < 999995) {
29
+ return sign + formatNumber(value / 1000) + 'K';
30
+ }
31
+ if (value < 999999995) {
32
+ value = 1000 * Math.round(value / 1000);
33
+ return sign + formatNumber(value / 1000000) + 'M';
34
+ }
35
+ value = 1000000 * Math.round(value / 1000000);
36
+ return sign + formatNumber(value / 1000000000) + 'B';
34
37
  }
35
- return formatValue(value);
38
+ return format(value);
36
39
  };
@@ -9,23 +9,24 @@ import { YAxisConfig } from '../../chart.config';
9
9
  import EventBus from '../../events/event-bus';
10
10
  import { ChartBaseElement } from '../../model/chart-base-element';
11
11
  import { CanvasInputListenerComponent } from '../../inputlisteners/canvas-input-listener.component';
12
- import { Pixel, Unit, ViewportModel } from '../../model/scaling/viewport.model';
12
+ import { Pixel, Unit } from '../../model/scaling/viewport.model';
13
13
  import { ChartPanComponent } from '../pan/chart-pan.component';
14
+ import { ScaleModel } from '../../model/scale.model';
14
15
  /**
15
16
  * Handles the mouse drag on Y axis - to zoom the viewport vertically.
16
17
  * @doc-tags scaling,zoom,viewport
17
18
  */
18
19
  export declare class YAxisScaleHandler extends ChartBaseElement {
19
20
  private bus;
20
- private viewportModel;
21
- private canvasBoundsContainer;
21
+ private scale;
22
+ private bounds;
22
23
  private autoScaleCallback;
23
24
  yAxisDragEndSubject: Subject<void>;
24
25
  lastYStart: Unit;
25
26
  lastYEnd: Unit;
26
27
  lastYHeight: Unit;
27
28
  lastYPxHeight: Pixel;
28
- constructor(bus: EventBus, config: YAxisConfig, chartPanComponent: ChartPanComponent, viewportModel: ViewportModel, canvasInputListener: CanvasInputListenerComponent, canvasBoundsContainer: CanvasBoundsContainer, hitTest: HitBoundsTest, autoScaleCallback: (auto: boolean) => void);
29
+ constructor(bus: EventBus, config: YAxisConfig, panning: ChartPanComponent, scale: ScaleModel, canvasInputListener: CanvasInputListenerComponent, bounds: CanvasBoundsContainer, hitTest: HitBoundsTest, autoScaleCallback: (auto: boolean) => void);
29
30
  private onYDragStart;
30
31
  private onYDragTick;
31
32
  private onYDragEnd;
@@ -14,11 +14,11 @@ const FULL_Y_HEIGHT_ZOOM_FACTOR = 10;
14
14
  * @doc-tags scaling,zoom,viewport
15
15
  */
16
16
  export class YAxisScaleHandler extends ChartBaseElement {
17
- constructor(bus, config, chartPanComponent, viewportModel, canvasInputListener, canvasBoundsContainer, hitTest, autoScaleCallback) {
17
+ constructor(bus, config, panning, scale, canvasInputListener, bounds, hitTest, autoScaleCallback) {
18
18
  super();
19
19
  this.bus = bus;
20
- this.viewportModel = viewportModel;
21
- this.canvasBoundsContainer = canvasBoundsContainer;
20
+ this.scale = scale;
21
+ this.bounds = bounds;
22
22
  this.autoScaleCallback = autoScaleCallback;
23
23
  this.yAxisDragEndSubject = new Subject();
24
24
  this.lastYStart = 0;
@@ -26,10 +26,10 @@ export class YAxisScaleHandler extends ChartBaseElement {
26
26
  this.lastYHeight = 0;
27
27
  this.lastYPxHeight = 0;
28
28
  this.onYDragStart = () => {
29
- this.lastYStart = this.viewportModel.yStart;
30
- this.lastYEnd = this.viewportModel.yEnd;
31
- this.lastYHeight = this.viewportModel.yEnd - this.viewportModel.yStart;
32
- this.lastYPxHeight = this.canvasBoundsContainer.getBounds(CanvasElement.Y_AXIS).height;
29
+ this.lastYStart = this.scale.yStart;
30
+ this.lastYEnd = this.scale.yEnd;
31
+ this.lastYHeight = this.scale.yEnd - this.scale.yStart;
32
+ this.lastYPxHeight = this.bounds.getBounds(CanvasElement.Y_AXIS).height;
33
33
  };
34
34
  this.onYDragTick = (dragInfo) => {
35
35
  const { delta: absoluteYDelta } = dragInfo;
@@ -48,7 +48,7 @@ export class YAxisScaleHandler extends ChartBaseElement {
48
48
  const newYStart = this.lastYStart - delta;
49
49
  const newYEnd = this.lastYEnd + delta;
50
50
  this.autoScaleCallback(false);
51
- this.viewportModel.setYScale(newYStart, newYEnd);
51
+ this.scale.setYScale(newYStart, newYEnd);
52
52
  this.bus.fireDraw();
53
53
  };
54
54
  this.onYDragEnd = () => {
@@ -61,7 +61,7 @@ export class YAxisScaleHandler extends ChartBaseElement {
61
61
  onDragTick: callIfPredicateTrue(this.onYDragTick, dragPredicate),
62
62
  onDragStart: callIfPredicateTrue(this.onYDragStart, dragPredicate),
63
63
  onDragEnd: callIfPredicateTrue(this.onYDragEnd, dragPredicate),
64
- }, canvasInputListener, chartPanComponent, {
64
+ }, canvasInputListener, panning, {
65
65
  disableChartPanning: false,
66
66
  });
67
67
  this.addChildEntity(dragNDropYComponent);
@@ -151,6 +151,7 @@ export class YAxisComponent extends ChartBaseElement {
151
151
  setAxisType(type) {
152
152
  if (type !== this.state.type) {
153
153
  this.state.type = type;
154
+ this.config.components.yAxis.type = type;
154
155
  this.axisTypeSetSubject.next(type);
155
156
  this.scale.autoScale(true);
156
157
  this.model.fancyLabelsModel.updateLabels(true);
@@ -53,7 +53,7 @@ export const setLineWidth = (ctx, lineWidth, dataSeries, drawerConfig, seriesSel
53
53
  ctx.lineWidth = drawerConfig.forceBold;
54
54
  }
55
55
  else if (dataSeries.hovered) {
56
- ctx.lineWidth = seriesSelectedWidth;
56
+ ctx.lineWidth = lineWidth !== seriesSelectedWidth ? lineWidth + 1 : seriesSelectedWidth;
57
57
  }
58
58
  else {
59
59
  ctx.lineWidth = lineWidth;
@@ -44,6 +44,7 @@ export declare class CanvasInputListenerComponent extends ChartBaseElement {
44
44
  private mouseUpDocumentSubject;
45
45
  private wheelSubject;
46
46
  private touchStartSubject;
47
+ private touchStartTimestamp;
47
48
  private touchMoveSubject;
48
49
  private touchEndSubject;
49
50
  private touchCancelSubject;
@@ -52,6 +53,7 @@ export declare class CanvasInputListenerComponent extends ChartBaseElement {
52
53
  private contextMenuSubject;
53
54
  private pinchSubject;
54
55
  private scrollGestureSubject;
56
+ private fastTouchScroll;
55
57
  mouseLeavesCanvasSubject: Subject<boolean>;
56
58
  dragStartPoint: Point;
57
59
  prevDragPoint: Point;
@@ -312,6 +314,13 @@ export declare class CanvasInputListenerComponent extends ChartBaseElement {
312
314
  * @returns {Observable<TouchEvent>} - An Observable that emits a TouchEvent when a long touch is detected on the current element.
313
315
  */
314
316
  observeLongTouchEnd(hitBoundsTest?: HitBoundsTest): Observable<TouchEvent>;
317
+ /**
318
+ * Returns an Observable that emits a Touch whenever a fast scroll is detected.
319
+ * Fast scroll happens whenever chart or any other pane were moved faster than usual
320
+ * The Observable is created from a Subject that is subscribed to by the component's template.
321
+ * @returns {Observable<TouchEvent>} An Observable that emits a TouchEvent whenever a fast scroll is detected.
322
+ */
323
+ observeFastTouchScroll(hitBoundsTest?: HitBoundsTest): Observable<TouchEvent>;
315
324
  /**
316
325
  * Returns the current point of the object.
317
326
  * @returns {Point} The current point of the object.
@@ -46,6 +46,7 @@ class CanvasInputListenerComponent extends ChartBaseElement {
46
46
  this.mouseUpDocumentSubject = new Subject();
47
47
  this.wheelSubject = new Subject();
48
48
  this.touchStartSubject = new Subject();
49
+ this.touchStartTimestamp = 0;
49
50
  this.touchMoveSubject = new Subject();
50
51
  this.touchEndSubject = new Subject();
51
52
  this.touchCancelSubject = new Subject();
@@ -54,6 +55,7 @@ class CanvasInputListenerComponent extends ChartBaseElement {
54
55
  this.contextMenuSubject = new Subject();
55
56
  this.pinchSubject = new Subject();
56
57
  this.scrollGestureSubject = new Subject();
58
+ this.fastTouchScroll = new Subject();
57
59
  this.mouseLeavesCanvasSubject = new Subject();
58
60
  // point at which start dragging
59
61
  this.dragStartPoint = { x: 0, y: 0 };
@@ -236,9 +238,33 @@ class CanvasInputListenerComponent extends ChartBaseElement {
236
238
  this.element.addEventListener('touchend', touchEndHandler);
237
239
  };
238
240
  this.addSubscription(subscribeListener(this.element, (e) => longTouchListeners(e), 'touchstart'));
241
+ const fastScrollListenerProducer = (e) => {
242
+ e.preventDefault();
243
+ // should work only if dragged to the left
244
+ if (this.prevDragPoint.x > this.dragStartPoint.x) {
245
+ // in percent, perhaps should be changed to just pixels,
246
+ // because landscape and portait orientations would give different % results
247
+ const minDistance = 35;
248
+ // in ms, should be lower to detect as "fast"
249
+ const maxTime = 250;
250
+ const touchStartTs = this.touchStartTimestamp;
251
+ const touchEndTs = Date.now();
252
+ const time = touchEndTs - touchStartTs;
253
+ const distance = ((this.prevDragPoint.x - this.dragStartPoint.x) / this.canvasBounds.width) * 100;
254
+ const isRightDistance = distance > minDistance;
255
+ const isRightTime = time <= maxTime;
256
+ if (isRightDistance && isRightTime) {
257
+ this.fastTouchScroll.next(e);
258
+ }
259
+ }
260
+ };
261
+ this.addSubscription(subscribeListener(this.element, fastScrollListenerProducer, 'touchend'));
239
262
  }
240
263
  this.addSubscription(subscribeListener(this.element, leftMouseButtonListener(() => this.dbClickSubject.next(this.currentPoint)), 'dblclick'));
241
- this.addSubscription(subscribeListener(this.element, (e) => this.touchStartSubject.next(e), 'touchstart'));
264
+ this.addSubscription(subscribeListener(this.element, (e) => {
265
+ this.touchStartSubject.next(e);
266
+ this.touchStartTimestamp = Date.now();
267
+ }, 'touchstart'));
242
268
  this.addSubscription(subscribeListener(this.element, (e) => this.touchMoveSubject.next(e), 'touchmove', true));
243
269
  this.addSubscription(subscribeListener(this.element, (e) => this.touchEndSubject.next(e), 'touchend'));
244
270
  this.addSubscription(subscribeListener(this.element, (e) => this.touchCancelSubject.next(e), 'touchcancel'));
@@ -643,6 +669,17 @@ class CanvasInputListenerComponent extends ChartBaseElement {
643
669
  .asObservable()
644
670
  .pipe(filter(() => hitBoundsTest(this.currentPoint.x, this.currentPoint.y)));
645
671
  }
672
+ /**
673
+ * Returns an Observable that emits a Touch whenever a fast scroll is detected.
674
+ * Fast scroll happens whenever chart or any other pane were moved faster than usual
675
+ * The Observable is created from a Subject that is subscribed to by the component's template.
676
+ * @returns {Observable<TouchEvent>} An Observable that emits a TouchEvent whenever a fast scroll is detected.
677
+ */
678
+ observeFastTouchScroll(hitBoundsTest = () => true) {
679
+ return this.fastTouchScroll
680
+ .asObservable()
681
+ .pipe(filter(() => hitBoundsTest(this.currentPoint.x, this.currentPoint.y)));
682
+ }
646
683
  /**
647
684
  * Returns the current point of the object.
648
685
  * @returns {Point} The current point of the object.
@@ -71,13 +71,13 @@ export declare class ScaleModel extends ViewportModel {
71
71
  * @param forceNoAnimation Whether to skip animation.
72
72
  * @param zoomSensitivity The sensitivity of the zoom.
73
73
  */
74
- zoomXToPercent(viewportPercent: ViewportPercent, zoomIn: boolean, forceNoAnimation?: boolean, zoomSensitivity?: number): void;
74
+ zoomXToPercent(viewportPercent: ViewportPercent, zoomIn: boolean, forceNoAnimation: boolean | undefined, zoomSensitivity: number): void;
75
75
  /**
76
76
  * Zooms the X axis of the chart relativly to the end of the data range.
77
77
  * @param zoomIn - If true, the chart will be zoomed in. If false, the chart will be zoomed out.
78
78
  * @param zoomSensitivity - The sensitivity of the zoom. Default value is taken from the configuration object.
79
79
  */
80
- zoomXToEnd(zoomIn: boolean, zoomSensitivity?: number): void;
80
+ zoomXToEnd(zoomIn: boolean, zoomSensitivity: number): void;
81
81
  haltAnimation(): void;
82
82
  private zoomXTo;
83
83
  /**
@@ -89,6 +89,7 @@ export declare class ScaleModel extends ViewportModel {
89
89
  * @param forceNoAutoScale - force NOT apply auto-scaling (for lazy loading)
90
90
  */
91
91
  setXScale(xStart: Unit, xEnd: Unit): void;
92
+ setYScale(yStart: Unit, yEnd: Unit, fire?: boolean): void;
92
93
  /**
93
94
  * Moves both xStart and xEnd without changing the viewport width (zoom).
94
95
  * Works without animation.
@@ -8,7 +8,7 @@ import { startViewportModelAnimation } from '../animation/viewport-model-animati
8
8
  import { cloneUnsafe } from '../utils/object.utils';
9
9
  import { AutoScaleViewportSubModel } from './scaling/auto-scale.model';
10
10
  import { zoomConstraint } from './scaling/constrait.functions';
11
- import { lockedYEndViewportCalculator, ratioFromZoomXY } from './scaling/lock-ratio.model';
11
+ import { changeXToKeepRatio, changeYToKeepRatio, ratioFromZoomXY } from './scaling/lock-ratio.model';
12
12
  import { moveXStart, moveYStart } from './scaling/move-chart.functions';
13
13
  import { ViewportModel, compareStates } from './scaling/viewport.model';
14
14
  import { zoomXToEndViewportCalculator, zoomXToPercentViewportCalculator } from './scaling/x-zooming.functions';
@@ -81,7 +81,7 @@ export class ScaleModel extends ViewportModel {
81
81
  * @param forceNoAnimation Whether to skip animation.
82
82
  * @param zoomSensitivity The sensitivity of the zoom.
83
83
  */
84
- zoomXToPercent(viewportPercent, zoomIn, forceNoAnimation = false, zoomSensitivity = this.config.scale.zoomSensitivity) {
84
+ zoomXToPercent(viewportPercent, zoomIn, forceNoAnimation = false, zoomSensitivity) {
85
85
  const disabledAnimations = this.config.scale.disableAnimations || forceNoAnimation;
86
86
  if (disabledAnimations) {
87
87
  this.haltAnimation();
@@ -96,7 +96,7 @@ export class ScaleModel extends ViewportModel {
96
96
  * @param zoomIn - If true, the chart will be zoomed in. If false, the chart will be zoomed out.
97
97
  * @param zoomSensitivity - The sensitivity of the zoom. Default value is taken from the configuration object.
98
98
  */
99
- zoomXToEnd(zoomIn, zoomSensitivity = this.config.scale.zoomSensitivity) {
99
+ zoomXToEnd(zoomIn, zoomSensitivity) {
100
100
  if (this.config.scale.disableAnimations) {
101
101
  this.haltAnimation();
102
102
  }
@@ -116,7 +116,7 @@ export class ScaleModel extends ViewportModel {
116
116
  const initialStateCopy = Object.assign({}, state);
117
117
  const constrainedState = this.scalePostProcessor(initialStateCopy, state);
118
118
  if (this.state.lockPriceToBarRatio) {
119
- lockedYEndViewportCalculator(constrainedState, this.zoomXYRatio);
119
+ changeYToKeepRatio(constrainedState, this.zoomXYRatio);
120
120
  }
121
121
  if (this.state.auto) {
122
122
  this.autoScaleModel.doAutoYScale(constrainedState);
@@ -142,13 +142,31 @@ export class ScaleModel extends ViewportModel {
142
142
  const state = this.export();
143
143
  const constrainedState = this.scalePostProcessor(initialState, state);
144
144
  if (this.state.lockPriceToBarRatio) {
145
- lockedYEndViewportCalculator(constrainedState, this.zoomXYRatio);
145
+ changeYToKeepRatio(constrainedState, this.zoomXYRatio);
146
146
  }
147
147
  if (this.state.auto) {
148
148
  this.autoScaleModel.doAutoYScale(constrainedState);
149
149
  }
150
150
  this.apply(constrainedState);
151
151
  }
152
+ setYScale(yStart, yEnd, fire = false) {
153
+ const initialState = this.export();
154
+ super.setYScale(yStart, yEnd, fire);
155
+ const state = this.export();
156
+ const constrainedState = this.scalePostProcessor(initialState, state);
157
+ if (this.state.lockPriceToBarRatio) {
158
+ changeXToKeepRatio(constrainedState, this.zoomXYRatio);
159
+ this.setXScale(constrainedState.xStart, constrainedState.xEnd);
160
+ // TODO: rewrite logic for applying constraints to consider both axes, now constraints on Y may not work correctly
161
+ return;
162
+ }
163
+ else {
164
+ if (this.state.auto) {
165
+ this.autoScaleModel.doAutoYScale(constrainedState);
166
+ }
167
+ this.apply(constrainedState);
168
+ }
169
+ }
152
170
  /**
153
171
  * Moves both xStart and xEnd without changing the viewport width (zoom).
154
172
  * Works without animation.
@@ -32,7 +32,7 @@ export declare const candleEdgesConstrait: (state: ViewportModelState, visualCan
32
32
  * @returns
33
33
  * @doc-tags viewport,zoom,scaling
34
34
  */
35
- export declare const zoomConstraint: (initialState: ViewportModelState, state: ViewportModelState, chartConfig: ChartConfigComponentsChart, boundsProvider: BoundsProvider) => {
35
+ export declare const zoomConstraint: (_: ViewportModelState, state: ViewportModelState, chartConfig: ChartConfigComponentsChart, boundsProvider: BoundsProvider) => {
36
36
  xStart: number;
37
37
  xEnd: number;
38
38
  yStart: number;
@@ -40,7 +40,7 @@ export const candleEdgesConstrait = (state, visualCandlesCoordinates, candleLimi
40
40
  * @returns
41
41
  * @doc-tags viewport,zoom,scaling
42
42
  */
43
- export const zoomConstraint = (initialState, state, chartConfig, boundsProvider) => {
43
+ export const zoomConstraint = (_, state, chartConfig, boundsProvider) => {
44
44
  const newState = Object.assign({}, state);
45
45
  const bounds = boundsProvider();
46
46
  // 1 - is an average candle width: newXEnd - newXStart = avg candles amount in the viewport
@@ -56,7 +56,6 @@ export const zoomConstraint = (initialState, state, chartConfig, boundsProvider)
56
56
  return newState;
57
57
  }
58
58
  if (minViewportReached) {
59
- newState.xEnd = initialState.xEnd;
60
59
  newState.xStart = newState.xEnd - chartConfig.minCandles;
61
60
  newState.zoomX = calculateZoom(newState.xEnd - newState.xStart, bounds.width);
62
61
  return newState;
@@ -9,8 +9,14 @@ export declare const ratioFromZoomXY: (zoomX: Zoom, zoomY: Zoom) => ZoomXToZoomY
9
9
  export declare const zoomXToZoomY: (zoomX: Zoom, ratio: ZoomXToZoomYRatio) => Zoom;
10
10
  export declare const zoomYToZoomX: (zoomY: Zoom, ratio: ZoomXToZoomYRatio) => Zoom;
11
11
  /**
12
- * Locks the zoomY with zoomX and moves yEnd according to ratio changes.
12
+ * Locks the zoomY with zoomX and zooms y-scale depending on x-scale.
13
13
  * @param state
14
14
  * @param zoomXYRatio
15
15
  */
16
- export declare const lockedYEndViewportCalculator: (state: ViewportModelState, zoomXYRatio: ZoomXToZoomYRatio) => void;
16
+ export declare const changeYToKeepRatio: (state: ViewportModelState, zoomXYRatio: ZoomXToZoomYRatio) => void;
17
+ /**
18
+ * Locks the zoomY with zoomX and zooms x-scale depending on y-scale.
19
+ * @param state
20
+ * @param zoomXYRatio
21
+ */
22
+ export declare const changeXToKeepRatio: (state: ViewportModelState, zoomXYRatio: ZoomXToZoomYRatio) => void;
@@ -7,16 +7,31 @@ export const ratioFromZoomXY = (zoomX, zoomY) => zoomX / zoomY;
7
7
  export const zoomXToZoomY = (zoomX, ratio) => zoomX / ratio;
8
8
  export const zoomYToZoomX = (zoomY, ratio) => zoomY * ratio;
9
9
  /**
10
- * Locks the zoomY with zoomX and moves yEnd according to ratio changes.
10
+ * Locks the zoomY with zoomX and zooms y-scale depending on x-scale.
11
11
  * @param state
12
12
  * @param zoomXYRatio
13
13
  */
14
- export const lockedYEndViewportCalculator = (state, zoomXYRatio) => {
14
+ export const changeYToKeepRatio = (state, zoomXYRatio) => {
15
15
  const prevZoomY = state.zoomY;
16
16
  state.zoomY = zoomXToZoomY(state.zoomX, zoomXYRatio);
17
17
  const zoomYMult = state.zoomY / prevZoomY;
18
18
  const lastYHeight = state.yEnd - state.yStart;
19
19
  const newYHeight = lastYHeight * zoomYMult;
20
20
  const delta = newYHeight - lastYHeight;
21
- state.yEnd = state.yEnd + delta;
21
+ state.yEnd = state.yEnd + delta / 2;
22
+ state.yStart = state.yStart - delta / 2;
23
+ };
24
+ /**
25
+ * Locks the zoomY with zoomX and zooms x-scale depending on y-scale.
26
+ * @param state
27
+ * @param zoomXYRatio
28
+ */
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;
22
37
  };
@@ -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 { FullChartConfig } from '../../chart.config';
6
+ import { PriceAxisType } from '../../components/labels_generator/numeric-axis-labels.generator';
7
7
  export declare const isFirefox: boolean;
8
8
  /**
9
9
  * this function determines whether the event was triggered with the mouse or the touchpad
@@ -20,11 +20,11 @@ export declare const isFirefox: boolean;
20
20
  */
21
21
  export declare const touchpadDetector: (e: WheelEvent) => boolean;
22
22
  /**
23
- * this function returns different ящщь sensitivity for the percent axis and the others
23
+ * this function returns different zoom sensitivity for the percent axis and the others
24
24
  * @param config
25
25
  * @param isTouchpad
26
26
  * @returns {number}
27
27
  *
28
28
  * @doc-tags chart-core, zoom
29
29
  */
30
- export declare const getTouchpadSensitivity: (config: FullChartConfig) => number;
30
+ export declare const getTouchpadSensitivity: (type: PriceAxisType, zoomSensitivity: number) => number;
@@ -82,15 +82,13 @@ export const touchpadDetector = (e) => {
82
82
  return isTouchpad;
83
83
  };
84
84
  /**
85
- * this function returns different ящщь sensitivity for the percent axis and the others
85
+ * this function returns different zoom sensitivity for the percent axis and the others
86
86
  * @param config
87
87
  * @param isTouchpad
88
88
  * @returns {number}
89
89
  *
90
90
  * @doc-tags chart-core, zoom
91
91
  */
92
- export const getTouchpadSensitivity = (config) => {
93
- const isPercentAxisType = config.components.yAxis.type === 'percent';
94
- const zoomSensitivity = isPercentAxisType ? config.scale.zoomSensitivity / 4 : config.scale.zoomSensitivity;
95
- return zoomSensitivity;
92
+ export const getTouchpadSensitivity = (type, zoomSensitivity) => {
93
+ return type === 'percent' ? zoomSensitivity / 4 : zoomSensitivity;
96
94
  };
@@ -3,7 +3,6 @@
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
- export declare const MAX_DECIMAL_DIGITS = 14;
7
6
  export type NumberFormatLabels = 'K' | 'M' | 'B';
8
7
  export declare class MathUtils {
9
8
  static roundToNearest(value: number, precision: number): number;