@devexperts/dxcharts-lite 2.4.6 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chart/__tests__/model/date-time.formatter.test.d.ts +6 -0
- package/dist/chart/__tests__/model/date-time.formatter.test.js +30 -0
- package/dist/chart/canvas/canvas-bounds-container.d.ts +7 -3
- package/dist/chart/canvas/canvas-bounds-container.js +149 -105
- package/dist/chart/canvas/canvas-chart-html.js +7 -7
- package/dist/chart/canvas/y-axis-bounds.container.js +4 -0
- package/dist/chart/chart.config.js +4 -4
- package/dist/chart/components/chart/chart-area-pan.handler.d.ts +1 -5
- package/dist/chart/components/chart/chart-area-pan.handler.js +1 -8
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-x.component.d.ts +0 -6
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-x.component.js +0 -8
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-y.component.d.ts +0 -5
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-y.component.js +0 -7
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop.component.d.ts +0 -11
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop.component.js +0 -16
- package/dist/chart/components/pan/chart-pan.component.d.ts +3 -14
- package/dist/chart/components/pan/chart-pan.component.js +7 -21
- package/dist/chart/components/pane/pane-hit-test.controller.js +1 -1
- package/dist/chart/components/pane/pane-manager.component.d.ts +29 -0
- package/dist/chart/components/pane/pane-manager.component.js +71 -8
- package/dist/chart/components/pane/pane.component.d.ts +5 -17
- package/dist/chart/components/pane/pane.component.js +11 -28
- package/dist/chart/components/resizer/bar-resizer.component.d.ts +1 -1
- package/dist/chart/components/resizer/bar-resizer.component.js +3 -1
- package/dist/chart/components/volumes/volumes.component.d.ts +1 -1
- package/dist/chart/components/volumes/volumes.component.js +18 -11
- package/dist/chart/components/y_axis/y-axis-scale.handler.js +3 -3
- package/dist/chart/components/y_axis/y-axis.component.js +1 -1
- package/dist/chart/drawers/chart-type-drawers/area.drawer.js +1 -1
- package/dist/chart/drawers/data-series.drawer.js +1 -1
- package/dist/chart/inputhandlers/hover-producer.component.d.ts +1 -1
- package/dist/chart/inputhandlers/main-canvas-touch.handler.d.ts +9 -2
- package/dist/chart/inputhandlers/main-canvas-touch.handler.js +18 -2
- package/dist/chart/model/baseline.model.d.ts +3 -0
- package/dist/chart/model/baseline.model.js +5 -0
- package/dist/chart/model/chart-base-element.d.ts +2 -0
- package/dist/chart/model/chart-base-element.js +2 -0
- package/dist/chart/model/data-series.model.d.ts +1 -1
- package/dist/chart/model/data-series.model.js +1 -1
- package/dist/chart/model/date-time.formatter.d.ts +20 -3
- package/dist/chart/model/date-time.formatter.js +62 -14
- package/dist/chart/model/scale.model.d.ts +12 -3
- package/dist/chart/model/scale.model.js +36 -7
- package/dist/chart/model/scaling/constrait.functions.d.ts +1 -16
- package/dist/chart/model/scaling/constrait.functions.js +1 -29
- package/dist/chart/model/time-zone.model.js +3 -1
- package/dist/dxchart.min.js +11 -11
- package/package.json +1 -1
|
@@ -6,15 +6,17 @@
|
|
|
6
6
|
import { ChartBaseElement } from '../model/chart-base-element';
|
|
7
7
|
import { CanvasInputListenerComponent } from '../inputlisteners/canvas-input-listener.component';
|
|
8
8
|
import { ScaleModel } from '../model/scale.model';
|
|
9
|
+
import { ChartAreaPanHandler } from '../components/chart/chart-area-pan.handler';
|
|
9
10
|
/**
|
|
10
11
|
* Handles chart touch events.
|
|
11
12
|
*/
|
|
12
13
|
export declare class MainCanvasTouchHandler extends ChartBaseElement {
|
|
14
|
+
private chartAreaPanHandler;
|
|
13
15
|
private scale;
|
|
14
16
|
private canvasInputListeners;
|
|
15
17
|
private mainCanvasParent;
|
|
16
18
|
private touchedCandleIndexes;
|
|
17
|
-
constructor(scale: ScaleModel, canvasInputListeners: CanvasInputListenerComponent, mainCanvasParent: Element);
|
|
19
|
+
constructor(chartAreaPanHandler: ChartAreaPanHandler, scale: ScaleModel, canvasInputListeners: CanvasInputListenerComponent, mainCanvasParent: Element);
|
|
18
20
|
/**
|
|
19
21
|
* Activates canvas input listeners for touch start and touch move events.
|
|
20
22
|
* @protected
|
|
@@ -31,8 +33,13 @@ export declare class MainCanvasTouchHandler extends ChartBaseElement {
|
|
|
31
33
|
* Handles touch move event
|
|
32
34
|
* @param {TouchEvent} e - The touch event object
|
|
33
35
|
* @returns {void}
|
|
34
|
-
|
|
36
|
+
*/
|
|
35
37
|
private handleTouchMoveEvent;
|
|
38
|
+
/**
|
|
39
|
+
* Handles touch end event
|
|
40
|
+
* @returns {void}
|
|
41
|
+
*/
|
|
42
|
+
private handleTouchEndEvent;
|
|
36
43
|
/**
|
|
37
44
|
* Gets candle positions touched by user in pixels.
|
|
38
45
|
* @param e - touch event with "touches" array
|
|
@@ -8,8 +8,9 @@ import { ChartBaseElement } from '../model/chart-base-element';
|
|
|
8
8
|
* Handles chart touch events.
|
|
9
9
|
*/
|
|
10
10
|
export class MainCanvasTouchHandler extends ChartBaseElement {
|
|
11
|
-
constructor(scale, canvasInputListeners, mainCanvasParent) {
|
|
11
|
+
constructor(chartAreaPanHandler, scale, canvasInputListeners, mainCanvasParent) {
|
|
12
12
|
super();
|
|
13
|
+
this.chartAreaPanHandler = chartAreaPanHandler;
|
|
13
14
|
this.scale = scale;
|
|
14
15
|
this.canvasInputListeners = canvasInputListeners;
|
|
15
16
|
this.mainCanvasParent = mainCanvasParent;
|
|
@@ -24,6 +25,7 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
|
|
|
24
25
|
doActivate() {
|
|
25
26
|
this.addRxSubscription(this.canvasInputListeners.observeTouchStart().subscribe(e => this.handleTouchStartEvent(e)));
|
|
26
27
|
this.addRxSubscription(this.canvasInputListeners.observeTouchMove().subscribe(e => this.handleTouchMoveEvent(e)));
|
|
28
|
+
this.addRxSubscription(this.canvasInputListeners.observeTouchEndDocument().subscribe(e => this.handleTouchEndEvent(e)));
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* Handles the touch start event.
|
|
@@ -32,6 +34,7 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
|
|
|
32
34
|
*/
|
|
33
35
|
handleTouchStartEvent(e) {
|
|
34
36
|
if (e.touches.length === 2) {
|
|
37
|
+
this.chartAreaPanHandler.deactivate();
|
|
35
38
|
// @ts-ignore
|
|
36
39
|
// TODO rework this
|
|
37
40
|
this.touchedCandleIndexes = this.getXPositions(e).map(x => this.scale.fromX(x));
|
|
@@ -41,12 +44,22 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
|
|
|
41
44
|
* Handles touch move event
|
|
42
45
|
* @param {TouchEvent} e - The touch event object
|
|
43
46
|
* @returns {void}
|
|
44
|
-
|
|
47
|
+
*/
|
|
45
48
|
handleTouchMoveEvent(e) {
|
|
46
49
|
if (e.touches.length === 2) {
|
|
47
50
|
this.pinchHandler(this.touchedCandleIndexes, this.getXPositions(e));
|
|
48
51
|
}
|
|
49
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Handles touch end event
|
|
55
|
+
* @returns {void}
|
|
56
|
+
*/
|
|
57
|
+
handleTouchEndEvent(e) {
|
|
58
|
+
// zero touches means the user stopped resizing completely (both fingers are up)
|
|
59
|
+
if (e.touches.length === 0) {
|
|
60
|
+
this.chartAreaPanHandler.activate();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
50
63
|
/**
|
|
51
64
|
* Gets candle positions touched by user in pixels.
|
|
52
65
|
* @param e - touch event with "touches" array
|
|
@@ -75,6 +88,9 @@ export class MainCanvasTouchHandler extends ChartBaseElement {
|
|
|
75
88
|
const last = first +
|
|
76
89
|
((candleIndexes[0] - candleIndexes[1]) / (touchPositions[0] - touchPositions[1])) *
|
|
77
90
|
this.scale.getBounds().width;
|
|
91
|
+
if (first >= last) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
78
94
|
this.scale.setXScale(first, last);
|
|
79
95
|
}
|
|
80
96
|
}
|
|
@@ -19,6 +19,7 @@ export declare const BASELINE_RESIZER_UUID = "BASELINE_RESIZER";
|
|
|
19
19
|
*/
|
|
20
20
|
export declare class BaselineModel extends ChartBaseElement {
|
|
21
21
|
private chartModel;
|
|
22
|
+
private chartPanComponent;
|
|
22
23
|
private canvasModel;
|
|
23
24
|
private canvasInputListener;
|
|
24
25
|
private config;
|
|
@@ -29,6 +30,8 @@ export declare class BaselineModel extends ChartBaseElement {
|
|
|
29
30
|
ht: HitBoundsTest;
|
|
30
31
|
constructor(chartModel: ChartModel, chartPanComponent: ChartPanComponent, canvasModel: CanvasModel, canvasInputListener: CanvasInputListenerComponent, config: FullChartConfig, canvasBoundContainer: CanvasBoundsContainer, cursorHandler: CursorHandler);
|
|
31
32
|
protected doActivate(): void;
|
|
33
|
+
private dragStartCb;
|
|
34
|
+
private dragEndCb;
|
|
32
35
|
private dragTickCb;
|
|
33
36
|
/**
|
|
34
37
|
* Recalculates the bounds of the baseline resizer based on the current chart and y-axis bounds.
|
|
@@ -15,6 +15,7 @@ export class BaselineModel extends ChartBaseElement {
|
|
|
15
15
|
constructor(chartModel, chartPanComponent, canvasModel, canvasInputListener, config, canvasBoundContainer, cursorHandler) {
|
|
16
16
|
super();
|
|
17
17
|
this.chartModel = chartModel;
|
|
18
|
+
this.chartPanComponent = chartPanComponent;
|
|
18
19
|
this.canvasModel = canvasModel;
|
|
19
20
|
this.canvasInputListener = canvasInputListener;
|
|
20
21
|
this.config = config;
|
|
@@ -26,6 +27,8 @@ export class BaselineModel extends ChartBaseElement {
|
|
|
26
27
|
this.ht = CanvasBoundsContainer.hitTestOf(this.resizerBounds, {
|
|
27
28
|
extensionY: this.config.components.paneResizer.dragZone,
|
|
28
29
|
});
|
|
30
|
+
this.dragStartCb = () => this.chartPanComponent.deactivatePanHandlers();
|
|
31
|
+
this.dragEndCb = () => this.chartPanComponent.activateChartPanHandlers();
|
|
29
32
|
this.dragTickCb = (dragInfo) => {
|
|
30
33
|
const { delta: yDelta } = dragInfo;
|
|
31
34
|
const chart = this.canvasBoundContainer.getBounds(CanvasElement.CHART);
|
|
@@ -37,6 +40,8 @@ export class BaselineModel extends ChartBaseElement {
|
|
|
37
40
|
};
|
|
38
41
|
const dndHelper = new DragNDropYComponent(this.ht, {
|
|
39
42
|
onDragTick: this.dragTickCb,
|
|
43
|
+
onDragStart: this.dragStartCb,
|
|
44
|
+
onDragEnd: this.dragEndCb,
|
|
40
45
|
}, canvasInputListener, chartPanComponent);
|
|
41
46
|
this.addChildEntity(dndHelper);
|
|
42
47
|
}
|
|
@@ -46,12 +46,14 @@ export declare abstract class ChartBaseElement implements ChartEntity {
|
|
|
46
46
|
* Enables the functionality of an object.
|
|
47
47
|
* If the object is not currently active, it sets the state to 'deactivated' and activates it.
|
|
48
48
|
* @returns {void}
|
|
49
|
+
* @deprecated use `ChartBaseElement.activate()` instead
|
|
49
50
|
*/
|
|
50
51
|
enable(): void;
|
|
51
52
|
/**
|
|
52
53
|
* Disables the current object if it is not already disabled.
|
|
53
54
|
* If the object is not disabled, it will be deactivated and its state will be set to 'disabled'.
|
|
54
55
|
* @returns {void}
|
|
56
|
+
* @deprecated use `ChartBaseElement.deactivate()` instead
|
|
55
57
|
*/
|
|
56
58
|
disable(): void;
|
|
57
59
|
/**
|
|
@@ -34,6 +34,7 @@ export class ChartBaseElement {
|
|
|
34
34
|
* Enables the functionality of an object.
|
|
35
35
|
* If the object is not currently active, it sets the state to 'deactivated' and activates it.
|
|
36
36
|
* @returns {void}
|
|
37
|
+
* @deprecated use `ChartBaseElement.activate()` instead
|
|
37
38
|
*/
|
|
38
39
|
enable() {
|
|
39
40
|
if (this._state !== 'active') {
|
|
@@ -45,6 +46,7 @@ export class ChartBaseElement {
|
|
|
45
46
|
* Disables the current object if it is not already disabled.
|
|
46
47
|
* If the object is not disabled, it will be deactivated and its state will be set to 'disabled'.
|
|
47
48
|
* @returns {void}
|
|
49
|
+
* @deprecated use `ChartBaseElement.deactivate()` instead
|
|
48
50
|
*/
|
|
49
51
|
disable() {
|
|
50
52
|
if (this._state !== 'disabled') {
|
|
@@ -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
|
-
|
|
51
|
+
highlighted: boolean;
|
|
52
52
|
yAxisLabelProvider: DataSeriesYAxisLabelsProvider;
|
|
53
53
|
readonly config: DataSeriesConfig;
|
|
54
54
|
scale: ScaleModel;
|
|
@@ -9,10 +9,10 @@ export interface TimeFormatterConfig {
|
|
|
9
9
|
shortMonths?: string[];
|
|
10
10
|
}
|
|
11
11
|
export interface DateTimeFormatter {
|
|
12
|
-
(date:
|
|
12
|
+
(date: number): string;
|
|
13
13
|
}
|
|
14
14
|
export type DateTimeFormatterFactory = (format: string) => DateTimeFormatter;
|
|
15
|
-
export declare const defaultDateTimeFormatter: () => (date:
|
|
15
|
+
export declare const defaultDateTimeFormatter: () => (date: number) => string;
|
|
16
16
|
/**
|
|
17
17
|
* Default chart-core time formatter.
|
|
18
18
|
* @param config
|
|
@@ -20,4 +20,21 @@ export declare const defaultDateTimeFormatter: () => (date: Date | number) => st
|
|
|
20
20
|
* @doc-tags chart-core,default-config,date-formatter
|
|
21
21
|
*/
|
|
22
22
|
export declare const dateTimeFormatterFactory: (config: FullChartConfig, offsetFunction: (timezone: string) => (time: number) => Date) => DateTimeFormatterFactory;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Returns an array of short day names based on the provided configuration object.
|
|
25
|
+
* If the configuration object does not contain shortDays property, it returns an array of default short day names.
|
|
26
|
+
*
|
|
27
|
+
* @param {TimeFormatterConfig} config - The configuration object containing shortDays property.
|
|
28
|
+
* @returns {string[]} - An array of short day names.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getShortDays(config: TimeFormatterConfig): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Returns an array of short month names based on the provided configuration object.
|
|
33
|
+
* If the configuration object does not have a 'shortMonths' property, it returns the default array of short month names.
|
|
34
|
+
*
|
|
35
|
+
* @param {TimeFormatterConfig} config - The configuration object that contains the shortMonths property.
|
|
36
|
+
* @returns {string[]} - An array of short month names.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getShortMonths(config: TimeFormatterConfig): string[];
|
|
39
|
+
export declare const recalculateXFormatter: (xAxisLabelFormat: string | Array<DateTimeFormatConfig>, period: number, formatterFactory: (format: string) => (timestamp: number) => string) => DateTimeFormatter;
|
|
40
|
+
export declare const formatDate: (date: Date, patternStr: string, daysOfWeek: string[], months: string[]) => string;
|
|
@@ -31,7 +31,6 @@ export const dateTimeFormatterFactory = (config, offsetFunction) => {
|
|
|
31
31
|
['s', 'm', 'H', 'd', 'M'].forEach(function (k) {
|
|
32
32
|
patterns[k + k] = 'this.' + 'addZero' + '(' + patterns[k] + ')';
|
|
33
33
|
});
|
|
34
|
-
const re = new RegExp(Object.keys(patterns).sort().reverse().join('|'), 'g');
|
|
35
34
|
/**
|
|
36
35
|
* Returns a string that concatenates the result of the function getPrefix with the input string fn and the string '()'.
|
|
37
36
|
* @param {string} fn - The input string to concatenate.
|
|
@@ -58,14 +57,6 @@ export const dateTimeFormatterFactory = (config, offsetFunction) => {
|
|
|
58
57
|
function upperCase(fn) {
|
|
59
58
|
return fn + '.toUpperCase()';
|
|
60
59
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Returns a string that concatenates a pattern from an object with a string
|
|
63
|
-
* @param {string} pattern - The key of the pattern to be concatenated
|
|
64
|
-
* @returns {string} - A string that concatenates a pattern from an object with a string
|
|
65
|
-
*/
|
|
66
|
-
function applyPattern(pattern) {
|
|
67
|
-
return "' + (" + patterns[pattern] + ") + '";
|
|
68
|
-
}
|
|
69
60
|
return function (pattern) {
|
|
70
61
|
var _a;
|
|
71
62
|
const context = {
|
|
@@ -89,11 +80,10 @@ export const dateTimeFormatterFactory = (config, offsetFunction) => {
|
|
|
89
80
|
},
|
|
90
81
|
tzDate: offsetFunction((_a = config.timezone) !== null && _a !== void 0 ? _a : '') ||
|
|
91
82
|
function (date) {
|
|
92
|
-
return
|
|
83
|
+
return new Date(+date);
|
|
93
84
|
},
|
|
94
85
|
};
|
|
95
|
-
|
|
96
|
-
return new Function('date', 'date=this.' + 'tzDate' + "(date); return '" + pattern.replace(re, applyPattern) + "'").bind(context);
|
|
86
|
+
return (date) => formatDate(context.tzDate(date), pattern, context.shortDays, context.shortMonths);
|
|
97
87
|
};
|
|
98
88
|
};
|
|
99
89
|
/**
|
|
@@ -103,7 +93,7 @@ export const dateTimeFormatterFactory = (config, offsetFunction) => {
|
|
|
103
93
|
* @param {TimeFormatterConfig} config - The configuration object containing shortDays property.
|
|
104
94
|
* @returns {string[]} - An array of short day names.
|
|
105
95
|
*/
|
|
106
|
-
function getShortDays(config) {
|
|
96
|
+
export function getShortDays(config) {
|
|
107
97
|
var _a;
|
|
108
98
|
return (_a = config.shortDays) !== null && _a !== void 0 ? _a : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
109
99
|
}
|
|
@@ -114,7 +104,7 @@ function getShortDays(config) {
|
|
|
114
104
|
* @param {TimeFormatterConfig} config - The configuration object that contains the shortMonths property.
|
|
115
105
|
* @returns {string[]} - An array of short month names.
|
|
116
106
|
*/
|
|
117
|
-
function getShortMonths(config) {
|
|
107
|
+
export function getShortMonths(config) {
|
|
118
108
|
var _a;
|
|
119
109
|
return (_a = config.shortMonths) !== null && _a !== void 0 ? _a : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
120
110
|
}
|
|
@@ -154,3 +144,61 @@ export const recalculateXFormatter = (xAxisLabelFormat, period, formatterFactory
|
|
|
154
144
|
}
|
|
155
145
|
return defaultDateTimeFormatter();
|
|
156
146
|
};
|
|
147
|
+
//#region Custom date pattern parser, transforms Date object to string by given pattern
|
|
148
|
+
// examples: dd.MM => 15.12, YYYY => 2024, HH:mm => 15:56
|
|
149
|
+
export const formatDate = (date, patternStr, daysOfWeek, months) => {
|
|
150
|
+
if (!patternStr) {
|
|
151
|
+
patternStr = 'M/d/yyyy';
|
|
152
|
+
}
|
|
153
|
+
const day = date.getDate();
|
|
154
|
+
const month = date.getMonth();
|
|
155
|
+
const year = date.getFullYear();
|
|
156
|
+
const hour = date.getHours();
|
|
157
|
+
const minute = date.getMinutes();
|
|
158
|
+
const second = date.getSeconds();
|
|
159
|
+
const miliseconds = date.getMilliseconds();
|
|
160
|
+
const h = hour % 12;
|
|
161
|
+
const hh = twoDigitPad(h);
|
|
162
|
+
const HH = twoDigitPad(hour);
|
|
163
|
+
const mm = twoDigitPad(minute);
|
|
164
|
+
const ss = twoDigitPad(second);
|
|
165
|
+
const aaa = hour < 12 ? 'AM' : 'PM';
|
|
166
|
+
const EEEE = daysOfWeek[date.getDay()];
|
|
167
|
+
const EEE = EEEE.substring(0, 3);
|
|
168
|
+
const dd = twoDigitPad(day);
|
|
169
|
+
const M = month + 1;
|
|
170
|
+
const MM = twoDigitPad(M);
|
|
171
|
+
const MMMM = months[month];
|
|
172
|
+
const MMM = MMMM.substring(0, 3);
|
|
173
|
+
const yyyy = year + '';
|
|
174
|
+
const yy = yyyy.substring(2, 4);
|
|
175
|
+
// checks to see if month name will be used
|
|
176
|
+
patternStr = patternStr
|
|
177
|
+
.replace('hh', hh + '')
|
|
178
|
+
.replace('h', h + '')
|
|
179
|
+
.replace('HH', HH + '')
|
|
180
|
+
.replace('H', hour + '')
|
|
181
|
+
.replace('mm', mm + '')
|
|
182
|
+
.replace('m', minute + '')
|
|
183
|
+
.replace('ss', ss + '')
|
|
184
|
+
.replace('s', second + '')
|
|
185
|
+
.replace('S', miliseconds + '')
|
|
186
|
+
.replace('dd', dd + '')
|
|
187
|
+
.replace('d', day + '')
|
|
188
|
+
.replace('EEEE', EEEE)
|
|
189
|
+
.replace('EEE', EEE)
|
|
190
|
+
.replace('YYYY', yyyy)
|
|
191
|
+
.replace('yyyy', yyyy)
|
|
192
|
+
.replace('YY', yy)
|
|
193
|
+
.replace('yy', yy)
|
|
194
|
+
.replace('aaa', aaa);
|
|
195
|
+
if (patternStr.indexOf('MMM') > -1) {
|
|
196
|
+
patternStr = patternStr.replace('MMMM', MMMM).replace('MMM', MMM);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
patternStr = patternStr.replace('MM', MM + '').replace('M', M + '');
|
|
200
|
+
}
|
|
201
|
+
return patternStr;
|
|
202
|
+
};
|
|
203
|
+
const twoDigitPad = (num) => (typeof num === 'number' && num < 10 ? '0' + num : num);
|
|
204
|
+
//#endregion
|
|
@@ -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,12 +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
|
-
|
|
48
|
-
xConstraints: Constraints[];
|
|
50
|
+
zoomReached: ZoomReached;
|
|
49
51
|
readonly state: ChartScale;
|
|
52
|
+
history: ScaleHistoryItem[];
|
|
53
|
+
offsets: ChartConfigComponentsOffsets;
|
|
54
|
+
private xConstraints;
|
|
50
55
|
constructor(config: FullChartConfig, getBounds: BoundsProvider, canvasAnimation: CanvasAnimation);
|
|
51
56
|
protected doActivate(): void;
|
|
52
57
|
protected doDeactivate(): void;
|
|
@@ -82,6 +87,10 @@ export declare class ScaleModel extends ViewportModel {
|
|
|
82
87
|
zoomXToEnd(zoomIn: boolean, zoomSensitivity: number): void;
|
|
83
88
|
haltAnimation(): void;
|
|
84
89
|
private zoomXTo;
|
|
90
|
+
calculateZoomReached(zoomX: Unit, zoomIn?: boolean): {
|
|
91
|
+
max: boolean;
|
|
92
|
+
min: boolean;
|
|
93
|
+
};
|
|
85
94
|
/**
|
|
86
95
|
* Moves the viewport to exactly xStart..xEnd place.
|
|
87
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,9 +36,10 @@ 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
44
|
this.scalePostProcessor = (initialState, state) => {
|
|
45
45
|
// for now <s>reduceRight<s/> reduce bcs ChartModel#getZoomConstrait should be invoked first
|
|
@@ -49,12 +49,12 @@ export class ScaleModel extends ViewportModel {
|
|
|
49
49
|
this.state = cloneUnsafe(config.scale);
|
|
50
50
|
this.autoScaleModel = new AutoScaleViewportSubModel(this);
|
|
51
51
|
this.offsets = this.config.components.offsets;
|
|
52
|
-
this.addXConstraint((initialState, state) => zoomConstraint(initialState, state, this.config.components.chart, this.getBounds));
|
|
53
52
|
}
|
|
54
53
|
doActivate() {
|
|
55
54
|
super.doActivate();
|
|
56
55
|
this.scaleInversedSubject = new Subject();
|
|
57
56
|
this.beforeStartAnimationSubject = new Subject();
|
|
57
|
+
this.zoomReached = this.calculateZoomReached(this.export().zoomX);
|
|
58
58
|
this.addRxSubscription(this.scaleInversedSubject.subscribe(() => {
|
|
59
59
|
this.fireChanged();
|
|
60
60
|
}));
|
|
@@ -102,7 +102,7 @@ export class ScaleModel extends ViewportModel {
|
|
|
102
102
|
this.beforeStartAnimationSubject.next();
|
|
103
103
|
const state = this.export();
|
|
104
104
|
zoomXToPercentViewportCalculator(this, state, viewportPercent, zoomSensitivity, zoomIn);
|
|
105
|
-
this.zoomXTo(state, disabledAnimations);
|
|
105
|
+
this.zoomXTo(state, zoomIn, disabledAnimations);
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
108
|
* Zooms the X axis of the chart relativly to the end of the data range.
|
|
@@ -116,7 +116,7 @@ export class ScaleModel extends ViewportModel {
|
|
|
116
116
|
this.beforeStartAnimationSubject.next();
|
|
117
117
|
const state = this.export();
|
|
118
118
|
zoomXToEndViewportCalculator(this, state, zoomSensitivity, zoomIn);
|
|
119
|
-
this.zoomXTo(state, this.config.scale.disableAnimations);
|
|
119
|
+
this.zoomXTo(state, zoomIn, this.config.scale.disableAnimations);
|
|
120
120
|
}
|
|
121
121
|
haltAnimation() {
|
|
122
122
|
var _a;
|
|
@@ -125,9 +125,13 @@ export class ScaleModel extends ViewportModel {
|
|
|
125
125
|
this.doAutoScale();
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
zoomXTo(state, forceNoAnimation) {
|
|
128
|
+
zoomXTo(state, zoomIn, forceNoAnimation) {
|
|
129
129
|
const initialStateCopy = Object.assign({}, state);
|
|
130
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
|
+
}
|
|
131
135
|
if (this.state.lockPriceToBarRatio) {
|
|
132
136
|
changeYToKeepRatio(constrainedState, this.zoomXYRatio);
|
|
133
137
|
}
|
|
@@ -141,6 +145,20 @@ export class ScaleModel extends ViewportModel {
|
|
|
141
145
|
startViewportModelAnimation(this.canvasAnimation, this, constrainedState);
|
|
142
146
|
}
|
|
143
147
|
}
|
|
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 };
|
|
161
|
+
}
|
|
144
162
|
/**
|
|
145
163
|
* Moves the viewport to exactly xStart..xEnd place.
|
|
146
164
|
* (you need to fire DRAW event after this)
|
|
@@ -152,8 +170,16 @@ export class ScaleModel extends ViewportModel {
|
|
|
152
170
|
setXScale(xStart, xEnd, forceNoAnimation = true) {
|
|
153
171
|
const initialState = this.export();
|
|
154
172
|
const zoomX = this.calculateZoomX(xStart, xEnd);
|
|
173
|
+
if (initialState.xStart === xStart && initialState.xEnd === xEnd) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
155
176
|
const state = Object.assign(Object.assign({}, initialState), { zoomX, xStart, xEnd });
|
|
156
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
|
+
}
|
|
157
183
|
if (this.state.lockPriceToBarRatio) {
|
|
158
184
|
changeYToKeepRatio(constrainedState, this.zoomXYRatio);
|
|
159
185
|
}
|
|
@@ -170,6 +196,9 @@ export class ScaleModel extends ViewportModel {
|
|
|
170
196
|
}
|
|
171
197
|
setYScale(yStart, yEnd, fire = false) {
|
|
172
198
|
const initialState = this.export();
|
|
199
|
+
if (initialState.yStart === yStart && initialState.yEnd === yEnd) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
173
202
|
super.setYScale(yStart, yEnd, fire);
|
|
174
203
|
const state = this.export();
|
|
175
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 {
|
|
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,17 +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
|
-
xStart: number;
|
|
37
|
-
xEnd: number;
|
|
38
|
-
yStart: number;
|
|
39
|
-
yEnd: number;
|
|
40
|
-
zoomX: number;
|
|
41
|
-
zoomY: number;
|
|
42
|
-
inverseY: boolean;
|
|
43
|
-
};
|
|
@@ -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 {
|
|
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;
|
|
57
|
-
}
|
|
58
|
-
if (minViewportReached) {
|
|
59
|
-
newState.xStart = newState.xEnd - chartConfig.minCandles;
|
|
60
|
-
newState.zoomX = calculateZoom(newState.xEnd - newState.xStart, bounds.width);
|
|
61
|
-
return newState;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return newState;
|
|
65
|
-
};
|
|
@@ -87,7 +87,9 @@ export class TimeZoneModel {
|
|
|
87
87
|
// In JS Date object is created with local tz offset,
|
|
88
88
|
// so we have to subtract localOffset from current time
|
|
89
89
|
return time => {
|
|
90
|
-
return new Date(time +
|
|
90
|
+
return new Date(time +
|
|
91
|
+
getTimezoneOffset(timezone, time) +
|
|
92
|
+
new Date(time).getTimezoneOffset() * timeMultiplier);
|
|
91
93
|
};
|
|
92
94
|
}
|
|
93
95
|
}
|