@devexperts/dxcharts-lite 2.1.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.
- package/README.md +1 -1
- package/dist/chart/canvas/canvas-bounds-container.d.ts +2 -2
- package/dist/chart/canvas/canvas-bounds-container.js +2 -2
- package/dist/chart/chart.config.d.ts +17 -4
- package/dist/chart/chart.config.js +6 -2
- package/dist/chart/components/chart/chart-area-pan.handler.js +10 -5
- package/dist/chart/components/cross_tool/cross-tool.model.js +1 -1
- package/dist/chart/components/snapshot/snapshot.component.js +1 -0
- package/dist/chart/components/volumes/separate-volumes.component.js +2 -1
- package/dist/chart/components/volumes/volumes.formatter.d.ts +1 -1
- package/dist/chart/components/volumes/volumes.formatter.js +28 -25
- package/dist/chart/components/y_axis/y-axis-scale.handler.d.ts +5 -4
- package/dist/chart/components/y_axis/y-axis-scale.handler.js +9 -9
- package/dist/chart/components/y_axis/y-axis.component.js +1 -0
- package/dist/chart/drawers/data-series.drawer.js +1 -1
- package/dist/chart/inputhandlers/cross-event-producer.component.js +2 -1
- package/dist/chart/inputhandlers/hover-producer.component.js +1 -1
- package/dist/chart/inputlisteners/canvas-input-listener.component.d.ts +19 -3
- package/dist/chart/inputlisteners/canvas-input-listener.component.js +102 -44
- package/dist/chart/model/scale.model.d.ts +3 -2
- package/dist/chart/model/scale.model.js +23 -5
- package/dist/chart/model/scaling/constrait.functions.d.ts +1 -1
- package/dist/chart/model/scaling/constrait.functions.js +1 -2
- package/dist/chart/model/scaling/lock-ratio.model.d.ts +8 -2
- package/dist/chart/model/scaling/lock-ratio.model.js +18 -3
- package/dist/chart/utils/device/touchpad.utils.d.ts +3 -3
- package/dist/chart/utils/device/touchpad.utils.js +3 -5
- package/dist/chart/utils/math.utils.d.ts +0 -1
- package/dist/chart/utils/math.utils.js +1 -1
- package/dist/dxchart.min.js +4 -4
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<img src="https://img.shields.io/static/v1?label=PRs&message=Welcome&color=blue" alt="PRs: Welcome" />
|
|
24
24
|
</a>
|
|
25
25
|
<a href="https://devexperts.com/dxcharts-demo/?lang=en">
|
|
26
|
-
<img src="https://img.shields.io/static/v1?label=Latest%20version&message=2.
|
|
26
|
+
<img src="https://img.shields.io/static/v1?label=Latest%20version&message=2.2.0&color=blue" alt="Version" />
|
|
27
27
|
</a>
|
|
28
28
|
</p>
|
|
29
29
|
|
|
@@ -35,11 +35,11 @@ export declare class CanvasElement {
|
|
|
35
35
|
static CHART_WITH_Y_AXIS: string;
|
|
36
36
|
static EVENTS: string;
|
|
37
37
|
/**
|
|
38
|
-
* @
|
|
38
|
+
* @returns pane bounds for the main chart
|
|
39
39
|
*/
|
|
40
40
|
static CHART: string;
|
|
41
41
|
/**
|
|
42
|
-
* @
|
|
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
|
-
* @
|
|
34
|
+
* @returns pane bounds for the main chart
|
|
35
35
|
*/
|
|
36
36
|
CanvasElement.CHART = CanvasElement.PANE_UUID(CHART_UUID);
|
|
37
37
|
/**
|
|
38
|
-
* @
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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:
|
|
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(
|
|
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.
|
|
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 =
|
|
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
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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
|
|
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
|
|
21
|
-
private
|
|
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,
|
|
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,
|
|
17
|
+
constructor(bus, config, panning, scale, canvasInputListener, bounds, hitTest, autoScaleCallback) {
|
|
18
18
|
super();
|
|
19
19
|
this.bus = bus;
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
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.
|
|
30
|
-
this.lastYEnd = this.
|
|
31
|
-
this.lastYHeight = this.
|
|
32
|
-
this.lastYPxHeight = this.
|
|
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.
|
|
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,
|
|
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;
|
|
@@ -3,7 +3,8 @@
|
|
|
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, combineLatest
|
|
6
|
+
import { BehaviorSubject, combineLatest } from 'rxjs';
|
|
7
|
+
import { filter } from 'rxjs/operators';
|
|
7
8
|
import { ChartBaseElement } from '../model/chart-base-element';
|
|
8
9
|
export class CrossEventProducerComponent extends ChartBaseElement {
|
|
9
10
|
constructor(canvasInputListener, canvasBoundsContainer) {
|
|
@@ -84,7 +84,7 @@ export class HoverProducerComponent extends ChartBaseElement {
|
|
|
84
84
|
// special handling for mobile
|
|
85
85
|
// on long touch - disable panning and show cross tool
|
|
86
86
|
const hitTest = this.canvasBoundsContainer.getBoundsHitTest(CanvasElement.ALL_PANES);
|
|
87
|
-
this.addRxSubscription(this.canvasInputListener.
|
|
87
|
+
this.addRxSubscription(this.canvasInputListener.observeLongTouchStart(hitTest).subscribe(event => {
|
|
88
88
|
this.paneManager.chartPanComponent.deactivatePanHandlers();
|
|
89
89
|
this.longTouchActivatedSubject.next(true);
|
|
90
90
|
const x = event.touches[0].clientX - this.canvasBoundsContainer.canvasOnPageLocation.x;
|
|
@@ -44,13 +44,16 @@ 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;
|
|
51
|
+
private longTouchStartSubject;
|
|
52
|
+
private longTouchEndSubject;
|
|
50
53
|
private contextMenuSubject;
|
|
51
|
-
private longTouchSubject;
|
|
52
54
|
private pinchSubject;
|
|
53
55
|
private scrollGestureSubject;
|
|
56
|
+
private fastTouchScroll;
|
|
54
57
|
mouseLeavesCanvasSubject: Subject<boolean>;
|
|
55
58
|
dragStartPoint: Point;
|
|
56
59
|
prevDragPoint: Point;
|
|
@@ -300,11 +303,24 @@ export declare class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
300
303
|
*/
|
|
301
304
|
observeContextMenu(hitBoundsTest?: HitBoundsTest): Observable<MouseEvent>;
|
|
302
305
|
/**
|
|
303
|
-
* Returns an Observable that emits a TouchEvent when a long touch is detected on the current element.
|
|
306
|
+
* Returns an Observable that emits a TouchEvent when a long touch start is detected on the current element.
|
|
304
307
|
* @param {HitBoundsTest} [hitBoundsTest=() => true] - A function that returns a boolean indicating whether the touch event occurred within the bounds of the current element.
|
|
305
308
|
* @returns {Observable<TouchEvent>} - An Observable that emits a TouchEvent when a long touch is detected on the current element.
|
|
306
309
|
*/
|
|
307
|
-
|
|
310
|
+
observeLongTouchStart(hitBoundsTest?: HitBoundsTest): Observable<TouchEvent>;
|
|
311
|
+
/**
|
|
312
|
+
* Returns an Observable that emits a TouchEvent when a long touch end is detected on the current element.
|
|
313
|
+
* @param {HitBoundsTest} [hitBoundsTest=() => true] - A function that returns a boolean indicating whether the touch event occurred within the bounds of the current element.
|
|
314
|
+
* @returns {Observable<TouchEvent>} - An Observable that emits a TouchEvent when a long touch is detected on the current element.
|
|
315
|
+
*/
|
|
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>;
|
|
308
324
|
/**
|
|
309
325
|
* Returns the current point of the object.
|
|
310
326
|
* @returns {Point} The current point of the object.
|
|
@@ -8,8 +8,8 @@ import { merge, Subject } from 'rxjs';
|
|
|
8
8
|
import { ChartBaseElement } from '../model/chart-base-element';
|
|
9
9
|
import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators';
|
|
10
10
|
import { EVENT_RESIZED } from '../events/events';
|
|
11
|
-
import { deviceDetector } from '../utils/device/device-detector.utils';
|
|
12
11
|
import { touchpadDetector } from '../utils/device/touchpad.utils';
|
|
12
|
+
import { deviceDetector } from '../utils/device/device-detector.utils';
|
|
13
13
|
/**
|
|
14
14
|
* Gathers user input on canvas element:
|
|
15
15
|
* Chart update order should be following:
|
|
@@ -46,13 +46,16 @@ 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();
|
|
53
|
+
this.longTouchStartSubject = new Subject();
|
|
54
|
+
this.longTouchEndSubject = new Subject();
|
|
52
55
|
this.contextMenuSubject = new Subject();
|
|
53
|
-
this.longTouchSubject = new Subject();
|
|
54
56
|
this.pinchSubject = new Subject();
|
|
55
57
|
this.scrollGestureSubject = new Subject();
|
|
58
|
+
this.fastTouchScroll = new Subject();
|
|
56
59
|
this.mouseLeavesCanvasSubject = new Subject();
|
|
57
60
|
// point at which start dragging
|
|
58
61
|
this.dragStartPoint = { x: 0, y: 0 };
|
|
@@ -184,52 +187,84 @@ class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
184
187
|
const device = deviceDetector();
|
|
185
188
|
if (device === 'apple' || device === 'mobile') {
|
|
186
189
|
// workaround to handle double taps for iOS
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
let dbTapTimeout = null;
|
|
191
|
+
const doubleTapListenerProducer = (e) => {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
if (dbTapTimeout) {
|
|
194
|
+
this.dbTapSubject.next(this.currentPoint);
|
|
195
|
+
clearTimeout(dbTapTimeout);
|
|
196
|
+
dbTapTimeout = null;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
dbTapTimeout = window.setTimeout(() => {
|
|
200
|
+
dbTapTimeout = null;
|
|
201
|
+
}, 250);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
this.addSubscription(subscribeListener(this.element, doubleTapListenerProducer, 'touchend'));
|
|
205
|
+
// workaround to handle long touch start/end for iOS
|
|
206
|
+
const longTouchListeners = (e, delay = 700, pixelsForMoveReset = 3) => {
|
|
207
|
+
e.preventDefault();
|
|
208
|
+
const initialCoords = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
|
209
|
+
let coords = { x: 0, y: 0 };
|
|
210
|
+
let longTouchStart = false;
|
|
211
|
+
let timerLongTouchStart = null;
|
|
212
|
+
// start timeout for long touch
|
|
213
|
+
timerLongTouchStart = setTimeout(() => {
|
|
214
|
+
longTouchStart = true;
|
|
215
|
+
this.longTouchStartSubject.next(e);
|
|
216
|
+
}, delay);
|
|
217
|
+
const touchMoveHandler = (e) => {
|
|
218
|
+
e.preventDefault();
|
|
219
|
+
coords = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
|
220
|
+
// clear long touch timeout if move area is bigger than pixelsForMove
|
|
221
|
+
if (Math.sqrt(Math.pow(coords.x - initialCoords.x, 2) + Math.pow(coords.y - initialCoords.y, 2)) >
|
|
222
|
+
pixelsForMoveReset ||
|
|
223
|
+
e.touches.length > 1) {
|
|
224
|
+
timerLongTouchStart && clearTimeout(timerLongTouchStart);
|
|
194
225
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
226
|
+
};
|
|
227
|
+
const touchEndHandler = (e) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
timerLongTouchStart && clearTimeout(timerLongTouchStart);
|
|
230
|
+
if (longTouchStart) {
|
|
231
|
+
longTouchStart = false;
|
|
232
|
+
this.longTouchEndSubject.next(e);
|
|
199
233
|
}
|
|
234
|
+
this.element.removeEventListener('touchend', touchEndHandler);
|
|
235
|
+
this.element.removeEventListener('touchmove', touchMoveHandler);
|
|
200
236
|
};
|
|
237
|
+
this.element.addEventListener('touchmove', touchMoveHandler);
|
|
238
|
+
this.element.addEventListener('touchend', touchEndHandler);
|
|
201
239
|
};
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
timer = null;
|
|
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
|
+
}
|
|
222
259
|
}
|
|
223
|
-
this.element.removeEventListener('touchend', touchEnd);
|
|
224
|
-
this.element.removeEventListener('touchmove', touchMoveHandler);
|
|
225
260
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
this.element.addEventListener('touchend', touchEnd);
|
|
229
|
-
};
|
|
230
|
-
this.addSubscription(subscribeListener(this.element, (e) => longTouchListener(e), 'touchstart'));
|
|
261
|
+
this.addSubscription(subscribeListener(this.element, fastScrollListenerProducer, 'touchend'));
|
|
262
|
+
}
|
|
231
263
|
this.addSubscription(subscribeListener(this.element, leftMouseButtonListener(() => this.dbClickSubject.next(this.currentPoint)), 'dblclick'));
|
|
232
|
-
this.addSubscription(subscribeListener(this.element, (e) =>
|
|
264
|
+
this.addSubscription(subscribeListener(this.element, (e) => {
|
|
265
|
+
this.touchStartSubject.next(e);
|
|
266
|
+
this.touchStartTimestamp = Date.now();
|
|
267
|
+
}, 'touchstart'));
|
|
233
268
|
this.addSubscription(subscribeListener(this.element, (e) => this.touchMoveSubject.next(e), 'touchmove', true));
|
|
234
269
|
this.addSubscription(subscribeListener(this.element, (e) => this.touchEndSubject.next(e), 'touchend'));
|
|
235
270
|
this.addSubscription(subscribeListener(this.element, (e) => this.touchCancelSubject.next(e), 'touchcancel'));
|
|
@@ -615,12 +650,35 @@ class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
615
650
|
return this.contextMenuSubject.asObservable().pipe(filter(() => hitBoundsTest(this.currentPoint.x, this.currentPoint.y)), tap(p => p.preventDefault()));
|
|
616
651
|
}
|
|
617
652
|
/**
|
|
618
|
-
* Returns an Observable that emits a TouchEvent when a long touch is detected on the current element.
|
|
653
|
+
* Returns an Observable that emits a TouchEvent when a long touch start is detected on the current element.
|
|
619
654
|
* @param {HitBoundsTest} [hitBoundsTest=() => true] - A function that returns a boolean indicating whether the touch event occurred within the bounds of the current element.
|
|
620
655
|
* @returns {Observable<TouchEvent>} - An Observable that emits a TouchEvent when a long touch is detected on the current element.
|
|
621
656
|
*/
|
|
622
|
-
|
|
623
|
-
return this.
|
|
657
|
+
observeLongTouchStart(hitBoundsTest = () => true) {
|
|
658
|
+
return this.longTouchStartSubject
|
|
659
|
+
.asObservable()
|
|
660
|
+
.pipe(filter(() => hitBoundsTest(this.currentPoint.x, this.currentPoint.y)));
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Returns an Observable that emits a TouchEvent when a long touch end is detected on the current element.
|
|
664
|
+
* @param {HitBoundsTest} [hitBoundsTest=() => true] - A function that returns a boolean indicating whether the touch event occurred within the bounds of the current element.
|
|
665
|
+
* @returns {Observable<TouchEvent>} - An Observable that emits a TouchEvent when a long touch is detected on the current element.
|
|
666
|
+
*/
|
|
667
|
+
observeLongTouchEnd(hitBoundsTest = () => true) {
|
|
668
|
+
return this.longTouchEndSubject
|
|
669
|
+
.asObservable()
|
|
670
|
+
.pipe(filter(() => hitBoundsTest(this.currentPoint.x, this.currentPoint.y)));
|
|
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)));
|
|
624
682
|
}
|
|
625
683
|
/**
|
|
626
684
|
* Returns 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
|
|
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
|
|
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.
|