@devexperts/dxcharts-lite 2.7.29 → 2.7.31
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/canvas/canvas-chart-html.js +1 -1
- package/dist/chart/chart.config.d.ts +7 -0
- package/dist/chart/chart.config.js +2 -0
- package/dist/chart/components/chart/chart-base.model.d.ts +2 -0
- package/dist/chart/components/chart/chart-base.model.js +2 -1
- package/dist/chart/components/chart/chart.model.d.ts +2 -0
- package/dist/chart/components/chart/chart.model.js +20 -8
- package/dist/chart/components/cross_tool/cross-tool.model.d.ts +1 -0
- package/dist/chart/components/cross_tool/cross-tool.model.js +15 -1
- package/dist/chart/components/dynamic-objects/dynamic-objects.model.d.ts +7 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.model.js +60 -7
- package/dist/chart/components/highlights/highlights.drawer.d.ts +4 -0
- package/dist/chart/components/highlights/highlights.drawer.js +27 -5
- package/dist/chart/components/y_axis/price_labels/price-label.drawer.js +1 -1
- package/dist/chart/components/y_axis/price_labels/y-axis-labels.model.js +6 -3
- package/dist/chart/components/y_axis/y-axis-labels.drawer.d.ts +8 -1
- package/dist/chart/components/y_axis/y-axis-labels.drawer.js +1 -1
- package/dist/chart/inputhandlers/hover-producer.component.d.ts +8 -2
- package/dist/chart/inputhandlers/hover-producer.component.js +86 -22
- package/dist/chart/model/scale.model.d.ts +2 -6
- package/dist/chart/model/scaling/viewport.model.d.ts +2 -0
- package/dist/chart/utils/__tests__/canvas-lower-hover-strip.utils.test.d.ts +6 -0
- package/dist/chart/utils/__tests__/canvas-lower-hover-strip.utils.test.js +34 -0
- package/dist/chart/utils/candles.utils.d.ts +4 -0
- package/dist/chart/utils/candles.utils.js +80 -1
- package/dist/chart/utils/canvas/canvas-drawing-functions.utils.d.ts +1 -1
- package/dist/chart/utils/canvas/canvas-drawing-functions.utils.js +4 -1
- package/dist/chart/utils/canvas-lower-hover-strip.utils.d.ts +17 -0
- package/dist/chart/utils/canvas-lower-hover-strip.utils.js +20 -0
- package/dist/dxchart.min.js +5 -5
- package/package.json +1 -1
|
@@ -9,8 +9,8 @@ export default (devexpertsPromoLink) => '<div data-element="chartResizer" style=
|
|
|
9
9
|
'\t\t\t<canvas data-element="snapshotCanvas"></canvas>\n' +
|
|
10
10
|
'\t\t\t<canvas data-element="backgroundCanvas"></canvas>\n' +
|
|
11
11
|
'\t\t\t<canvas data-element="mainCanvas"></canvas>\n' +
|
|
12
|
-
'\t\t\t<canvas data-element="yAxisLabelsCanvas"></canvas>\n' +
|
|
13
12
|
'\t\t\t<canvas data-element="dynamicObjectsCanvas"></canvas>\n' +
|
|
13
|
+
'\t\t\t<canvas data-element="yAxisLabelsCanvas"></canvas>\n' +
|
|
14
14
|
'\t\t\t<canvas data-element="yAxisDescriptionsCanvas"></canvas>\n' +
|
|
15
15
|
'\t\t\t<canvas data-element="crossToolCanvas"></canvas>\n' +
|
|
16
16
|
'\t\t\t<canvas data-element="hitTestCanvas"></canvas>\n' +
|
|
@@ -289,6 +289,8 @@ export interface ChartComponents {
|
|
|
289
289
|
*/
|
|
290
290
|
paneResizer: ChartConfigComponentsPaneResizer;
|
|
291
291
|
}
|
|
292
|
+
export declare const candleTimestampAnchor: readonly ["open", "close"];
|
|
293
|
+
export type CandleTimestampAnchor = (typeof candleTimestampAnchor)[number];
|
|
292
294
|
export interface ChartConfigComponentsChart {
|
|
293
295
|
/**
|
|
294
296
|
* The type of chart. Candle, bar, area and others.
|
|
@@ -335,6 +337,11 @@ export interface ChartConfigComponentsChart {
|
|
|
335
337
|
y: boolean;
|
|
336
338
|
};
|
|
337
339
|
sortCandles?: (candles: Candle[]) => Candle[];
|
|
340
|
+
/**
|
|
341
|
+
* Defines whether `Candle.timestamp` represents candle open (start) or close (end) time.
|
|
342
|
+
* @default 'open'
|
|
343
|
+
*/
|
|
344
|
+
candleTimestampAnchor?: CandleTimestampAnchor;
|
|
338
345
|
}
|
|
339
346
|
export interface ChartConfigComponentsEvents {
|
|
340
347
|
/**
|
|
@@ -81,6 +81,7 @@ export const getDefaultConfig = () => ({
|
|
|
81
81
|
y: true,
|
|
82
82
|
},
|
|
83
83
|
sortCandles: defaultSortCandles,
|
|
84
|
+
candleTimestampAnchor: 'open',
|
|
84
85
|
},
|
|
85
86
|
yAxis: {
|
|
86
87
|
type: 'regular',
|
|
@@ -682,4 +683,5 @@ export function immutableMerge(base, override, options) {
|
|
|
682
683
|
});
|
|
683
684
|
return result;
|
|
684
685
|
}
|
|
686
|
+
export const candleTimestampAnchor = ['open', 'close'];
|
|
685
687
|
export const getFontFromConfig = (config) => `${config.fontSize}px ${config.fontFamily}`;
|
|
@@ -8,6 +8,7 @@ import { Candle } from '../../model/candle.model';
|
|
|
8
8
|
import { DataSeriesPoint, VisualSeriesPoint } from '../../model/data-series.model';
|
|
9
9
|
import { Index, Timestamp } from '../../model/scaling/viewport.model';
|
|
10
10
|
import VisualCandle from '../../model/visual-candle';
|
|
11
|
+
import { CandleTimestampAnchor } from '../../chart.config';
|
|
11
12
|
export type BaseType = 'candle' | 'point';
|
|
12
13
|
type DataPoint<T extends BaseType> = T extends 'candle' ? Candle : DataSeriesPoint;
|
|
13
14
|
type VisualPoint<T extends BaseType> = T extends 'candle' ? VisualCandle : VisualSeriesPoint;
|
|
@@ -35,6 +36,7 @@ export declare class ChartBaseModel<T extends BaseType = 'point'> {
|
|
|
35
36
|
* Candles aggregation period in ms. Required for future candles calculation.
|
|
36
37
|
*/
|
|
37
38
|
period: number;
|
|
39
|
+
candleTimestampAnchor: CandleTimestampAnchor;
|
|
38
40
|
constructor(type: T);
|
|
39
41
|
/**
|
|
40
42
|
* For given timestamp finds the closest candle in dataset.
|
|
@@ -28,6 +28,7 @@ export class ChartBaseModel {
|
|
|
28
28
|
* Candles aggregation period in ms. Required for future candles calculation.
|
|
29
29
|
*/
|
|
30
30
|
this.period = 1;
|
|
31
|
+
this.candleTimestampAnchor = 'open';
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
34
|
* For given timestamp finds the closest candle in dataset.
|
|
@@ -35,7 +36,7 @@ export class ChartBaseModel {
|
|
|
35
36
|
*/
|
|
36
37
|
// TODO think how to make this function like candleFromX
|
|
37
38
|
dataFromTimestamp(timestamp, options = { extrapolate: true }, selectedDataPoints = this.mainDataPoints) {
|
|
38
|
-
const result = searchCandleIndex(timestamp, options, selectedDataPoints, this.period);
|
|
39
|
+
const result = searchCandleIndex(timestamp, Object.assign(Object.assign({}, options), { candleTimestampAnchor: this.candleTimestampAnchor }), selectedDataPoints, this.period);
|
|
39
40
|
return this.dataFromIdx(result.index);
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
@@ -370,6 +370,8 @@ export declare class ChartModel extends ChartBaseElement {
|
|
|
370
370
|
*/
|
|
371
371
|
private reindexCandlesBasedOnSeries;
|
|
372
372
|
getPeriod(): number;
|
|
373
|
+
getCandleTimestampAnchor(): "close" | "open";
|
|
374
|
+
private candleSearchOptions;
|
|
373
375
|
/**
|
|
374
376
|
* Checks if a given candle is within the viewport.
|
|
375
377
|
* @param {number} idx - The index of the candle to check.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { Subject, merge } from 'rxjs';
|
|
7
7
|
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
|
8
8
|
import { CHART_UUID, CanvasElement, areBoundsChanged, } from '../../canvas/canvas-bounds-container';
|
|
9
|
-
import { getDefaultConfig } from '../../chart.config';
|
|
9
|
+
import { getDefaultConfig, } from '../../chart.config';
|
|
10
10
|
import { CandleSeriesModel } from '../../model/candle-series.model';
|
|
11
11
|
import { copyCandle } from '../../model/candle.model';
|
|
12
12
|
import { MIN_SUPPORTED_CANVAS_SIZE } from '../../model/canvas.model';
|
|
@@ -66,6 +66,7 @@ export class ChartModel extends ChartBaseElement {
|
|
|
66
66
|
return sortCandles ? sortCandles(prepared) : prepared;
|
|
67
67
|
};
|
|
68
68
|
this.chartTypeChanged.next(this.config.components.chart.type);
|
|
69
|
+
this.chartBaseModel.candleTimestampAnchor = this.getCandleTimestampAnchor();
|
|
69
70
|
this.secondaryChartColors = new SecondaryChartColorsPool(this.config);
|
|
70
71
|
const candleSeries = new MainCandleSeriesModel(this.chartBaseModel, this.paneManager.panes[CHART_UUID].mainExtent, uuid(), this.paneManager.hitTestController.getNewDataSeriesHitTestId(), this.bus, this.scale, new ChartInstrument(), this.candlesTransformersByChartType, this.candleWidthByChartType, Object.assign({}, this.config.colors));
|
|
71
72
|
candleSeries.config.type = this.config.components.chart.type;
|
|
@@ -305,7 +306,7 @@ export class ChartModel extends ChartBaseElement {
|
|
|
305
306
|
return;
|
|
306
307
|
}
|
|
307
308
|
const preparedCandles = this.prepareCandles(mainSeries.candles);
|
|
308
|
-
const updateResult = updateCandles(this.mainCandleSeries.dataPoints, preparedCandles);
|
|
309
|
+
const updateResult = updateCandles(this.mainCandleSeries.dataPoints, preparedCandles, this.getCandleTimestampAnchor());
|
|
309
310
|
const updatedCandles = updateResult.candles;
|
|
310
311
|
reindexCandles(updatedCandles);
|
|
311
312
|
this.mainCandleSeries.dataPoints = updatedCandles;
|
|
@@ -313,7 +314,7 @@ export class ChartModel extends ChartBaseElement {
|
|
|
313
314
|
secondarySeries.map(series => {
|
|
314
315
|
var _a, _b, _c, _d;
|
|
315
316
|
const preparedCandles = this.prepareCandles(series.candles);
|
|
316
|
-
const updatedCandles = updateCandles((_d = (_c = this.findSecondarySeriesBySymbol((_b = (_a = series.instrument) === null || _a === void 0 ? void 0 : _a.symbol) !== null && _b !== void 0 ? _b : '')) === null || _c === void 0 ? void 0 : _c.dataPoints) !== null && _d !== void 0 ? _d : [], preparedCandles).candles;
|
|
317
|
+
const updatedCandles = updateCandles((_d = (_c = this.findSecondarySeriesBySymbol((_b = (_a = series.instrument) === null || _a === void 0 ? void 0 : _a.symbol) !== null && _b !== void 0 ? _b : '')) === null || _c === void 0 ? void 0 : _c.dataPoints) !== null && _d !== void 0 ? _d : [], preparedCandles, this.getCandleTimestampAnchor()).candles;
|
|
317
318
|
return this.setSecondaryCandleSeries(updatedCandles, series.instrument, false);
|
|
318
319
|
});
|
|
319
320
|
// do visual recalculations
|
|
@@ -738,7 +739,7 @@ export class ChartModel extends ChartBaseElement {
|
|
|
738
739
|
return series.reduce((candles, candle) => {
|
|
739
740
|
const timestamp = candle.timestamp;
|
|
740
741
|
// find index of candle in baseSeries
|
|
741
|
-
const result = searchCandleIndex(timestamp,
|
|
742
|
+
const result = searchCandleIndex(timestamp, this.candleSearchOptions(false), baseSeries, this.chartBaseModel.period);
|
|
742
743
|
if (result.index >= 0 && result.index < baseSeries.length) {
|
|
743
744
|
candle.idx = result.index;
|
|
744
745
|
candles[result.index] = candle;
|
|
@@ -749,6 +750,17 @@ export class ChartModel extends ChartBaseElement {
|
|
|
749
750
|
getPeriod() {
|
|
750
751
|
return this.chartBaseModel.period;
|
|
751
752
|
}
|
|
753
|
+
getCandleTimestampAnchor() {
|
|
754
|
+
var _a;
|
|
755
|
+
return (_a = this.config.components.chart.candleTimestampAnchor) !== null && _a !== void 0 ? _a : 'open';
|
|
756
|
+
}
|
|
757
|
+
candleSearchOptions(extrapolate, isDaysPeriod) {
|
|
758
|
+
return {
|
|
759
|
+
extrapolate,
|
|
760
|
+
isDaysPeriod,
|
|
761
|
+
candleTimestampAnchor: this.getCandleTimestampAnchor(),
|
|
762
|
+
};
|
|
763
|
+
}
|
|
752
764
|
/**
|
|
753
765
|
* Checks if a given candle is within the viewport.
|
|
754
766
|
* @param {number} idx - The index of the candle to check.
|
|
@@ -793,7 +805,7 @@ export class ChartModel extends ChartBaseElement {
|
|
|
793
805
|
return;
|
|
794
806
|
}
|
|
795
807
|
// detect index of updating candle
|
|
796
|
-
const result = searchCandleIndex(candle.timestamp,
|
|
808
|
+
const result = searchCandleIndex(candle.timestamp, this.candleSearchOptions(true), curCandles, this.getPeriod());
|
|
797
809
|
const idx = Math.min(result.index, curCandles.length);
|
|
798
810
|
isNewCandle = isNewCandle || idx === curCandles.length;
|
|
799
811
|
// update the candle and index
|
|
@@ -898,7 +910,7 @@ export class ChartModel extends ChartBaseElement {
|
|
|
898
910
|
const targetCopy = target.slice();
|
|
899
911
|
const prepend = [];
|
|
900
912
|
prependUpdate.forEach(c => {
|
|
901
|
-
const result = searchCandleIndex(c.timestamp,
|
|
913
|
+
const result = searchCandleIndex(c.timestamp, this.candleSearchOptions(false), target);
|
|
902
914
|
const idx = result.index;
|
|
903
915
|
if (idx < 0) {
|
|
904
916
|
prepend.push(c);
|
|
@@ -1027,12 +1039,12 @@ const findFirstNotEmptyCandle = (candles, startIdx, iterateStep) => {
|
|
|
1027
1039
|
* @param target {Candle[]} - sorted candles
|
|
1028
1040
|
* @param update {Candle[]} - sorted candles
|
|
1029
1041
|
*/
|
|
1030
|
-
const updateCandles = (target, update) => {
|
|
1042
|
+
const updateCandles = (target, update, candleTimestampAnchor = 'open') => {
|
|
1031
1043
|
const targetCopy = target.slice();
|
|
1032
1044
|
const prepend = [];
|
|
1033
1045
|
const append = [];
|
|
1034
1046
|
update.forEach(c => {
|
|
1035
|
-
const result = searchCandleIndex(c.timestamp, { extrapolate: true }, target);
|
|
1047
|
+
const result = searchCandleIndex(c.timestamp, { extrapolate: true, candleTimestampAnchor }, target);
|
|
1036
1048
|
const idx = result.index;
|
|
1037
1049
|
if (idx < 0) {
|
|
1038
1050
|
prepend.push(c);
|
|
@@ -38,6 +38,7 @@ export declare class CrossToolModel extends ChartBaseElement {
|
|
|
38
38
|
* @returns {void}
|
|
39
39
|
*/
|
|
40
40
|
setType(type: CrossToolType): void;
|
|
41
|
+
private shouldKeepXForMobileTouchCrosshair;
|
|
41
42
|
/**
|
|
42
43
|
* Method to activate the cross tool.
|
|
43
44
|
* It subscribes to the hoverProducer's hover event and updates the crosstool.
|
|
@@ -35,6 +35,11 @@ export class CrossToolModel extends ChartBaseElement {
|
|
|
35
35
|
setType(type) {
|
|
36
36
|
this.config.type = type;
|
|
37
37
|
}
|
|
38
|
+
shouldKeepXForMobileTouchCrosshair() {
|
|
39
|
+
return (isMobile() &&
|
|
40
|
+
this.hoverProducer.longTouchActivatedSubject.getValue() &&
|
|
41
|
+
this.crossEventProducer.crossSubject.getValue() !== null);
|
|
42
|
+
}
|
|
38
43
|
/**
|
|
39
44
|
* Method to activate the cross tool.
|
|
40
45
|
* It subscribes to the hoverProducer's hover event and updates the crosstool.
|
|
@@ -100,7 +105,9 @@ export class CrossToolModel extends ChartBaseElement {
|
|
|
100
105
|
*/
|
|
101
106
|
updateCrossTool(hover, magnetTarget = this.config.magnetTarget, discrete = this.config.discrete) {
|
|
102
107
|
var _a;
|
|
103
|
-
const x = discrete
|
|
108
|
+
const x = discrete && !this.shouldKeepXForMobileTouchCrosshair()
|
|
109
|
+
? this.chartModel.toX((_a = this.chartModel.candleFromX(hover.x, true).idx) !== null && _a !== void 0 ? _a : 0)
|
|
110
|
+
: hover.x;
|
|
104
111
|
if (this.currentHover === null) {
|
|
105
112
|
this.currentHover = { x, y: 0, time: hover.timeFormatted, paneId: hover.paneId };
|
|
106
113
|
}
|
|
@@ -149,6 +156,13 @@ export class CrossToolModel extends ChartBaseElement {
|
|
|
149
156
|
this.updateCrossTool(hover);
|
|
150
157
|
return;
|
|
151
158
|
}
|
|
159
|
+
const anchoredHover = this.crossEventProducer.crossToolHover;
|
|
160
|
+
if (anchoredHover && hover.x === anchoredHover.x && hover.y === anchoredHover.y) {
|
|
161
|
+
const refreshedHover = Object.assign(Object.assign({}, hover), { x: anchoredHover.x, y: anchoredHover.y });
|
|
162
|
+
this.crossEventProducer.crossToolHover = refreshedHover;
|
|
163
|
+
this.updateCrossTool(refreshedHover);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
152
166
|
// additional crosstool move logic
|
|
153
167
|
const paneBounds = this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(hover.paneId));
|
|
154
168
|
const offset = 5;
|
|
@@ -3,6 +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 { Subject } from 'rxjs';
|
|
6
7
|
import { ChartBaseElement } from '../../model/chart-base-element';
|
|
7
8
|
import { LinkedList } from '../../utils/linkedList.utils';
|
|
8
9
|
import { DynamicModelDrawer } from './dynamic-objects.drawer';
|
|
@@ -21,6 +22,7 @@ export declare class DynamicObjectsModel extends ChartBaseElement {
|
|
|
21
22
|
private _objects;
|
|
22
23
|
private modelIdToObjectMap;
|
|
23
24
|
private _uniqueObjects;
|
|
25
|
+
orderChangedSubject: Subject<void>;
|
|
24
26
|
constructor(canvasModel: CanvasModel);
|
|
25
27
|
/**
|
|
26
28
|
* @returns the `DynamicObject` itself and pane `LinkedList` where the object is stored.
|
|
@@ -52,6 +54,11 @@ export declare class DynamicObjectsModel extends ChartBaseElement {
|
|
|
52
54
|
* Moves the object inside the associated LinkedList to the specified position
|
|
53
55
|
*/
|
|
54
56
|
moveToPosition(id: DynamicObjectId, position: number): void;
|
|
57
|
+
getPaneObjectIds(paneId: PaneId): DynamicObjectId[];
|
|
58
|
+
setPaneOrder(paneId: PaneId, orderedIds: DynamicObjectId[], forceUpdate?: boolean): void;
|
|
59
|
+
reorderObjectIds(paneId: PaneId, orderedIds: DynamicObjectId[], forceUpdate?: boolean): void;
|
|
60
|
+
private notifyOrderChanged;
|
|
61
|
+
private moveObjectToFront;
|
|
55
62
|
/**
|
|
56
63
|
* Moves the object inside the drawing order so it's being drawn before the other elements
|
|
57
64
|
* @param paneId
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
9
9
|
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
10
10
|
*/
|
|
11
|
-
import { BehaviorSubject } from 'rxjs';
|
|
11
|
+
import { BehaviorSubject, Subject } from 'rxjs';
|
|
12
12
|
import { ChartBaseElement } from '../../model/chart-base-element';
|
|
13
13
|
import { LinkedList, ListNode } from '../../utils/linkedList.utils';
|
|
14
14
|
export class DynamicObjectsModel extends ChartBaseElement {
|
|
@@ -17,6 +17,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
|
|
|
17
17
|
this.canvasModel = canvasModel;
|
|
18
18
|
this.modelIdToObjectMap = new Map();
|
|
19
19
|
this._uniqueObjects = {};
|
|
20
|
+
this.orderChangedSubject = new Subject();
|
|
20
21
|
this._objects = new BehaviorSubject({});
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
@@ -130,12 +131,52 @@ export class DynamicObjectsModel extends ChartBaseElement {
|
|
|
130
131
|
}
|
|
131
132
|
this.setDynamicObjects(this.objects);
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
getPaneObjectIds(paneId) {
|
|
135
|
+
const paneList = this.objects[paneId];
|
|
136
|
+
if (!paneList) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
return [...paneList].map(obj => obj.id);
|
|
140
|
+
}
|
|
141
|
+
setPaneOrder(paneId, orderedIds, forceUpdate = true) {
|
|
142
|
+
const paneList = this.objects[paneId];
|
|
143
|
+
if (!paneList || orderedIds.length === 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const currentIds = this.getPaneObjectIds(paneId);
|
|
147
|
+
const filteredOrderedIds = orderedIds.filter(id => currentIds.includes(id));
|
|
148
|
+
const missingIds = currentIds.filter(id => !filteredOrderedIds.includes(id));
|
|
149
|
+
const targetOrder = [...filteredOrderedIds, ...missingIds];
|
|
150
|
+
for (let i = 0; i < targetOrder.length; i++) {
|
|
151
|
+
const id = targetOrder[i];
|
|
152
|
+
const currentPos = this.getObjectPosition(id);
|
|
153
|
+
if (currentPos >= 0 && currentPos !== i) {
|
|
154
|
+
this.moveToPosition(id, i);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (forceUpdate) {
|
|
158
|
+
this.notifyOrderChanged();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
reorderObjectIds(paneId, orderedIds, forceUpdate = true) {
|
|
162
|
+
const orderedIdSet = new Set(orderedIds);
|
|
163
|
+
const paneObjectIds = this.getPaneObjectIds(paneId).filter(id => orderedIdSet.has(id));
|
|
164
|
+
if (paneObjectIds.length === 0) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
for (const id of orderedIds) {
|
|
168
|
+
if (paneObjectIds.includes(id)) {
|
|
169
|
+
this.moveObjectToFront(id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (forceUpdate) {
|
|
173
|
+
this.notifyOrderChanged();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
notifyOrderChanged() {
|
|
177
|
+
this.orderChangedSubject.next();
|
|
178
|
+
}
|
|
179
|
+
moveObjectToFront(id) {
|
|
139
180
|
const objInfo = this.getObjectInfoById(id);
|
|
140
181
|
if (!objInfo) {
|
|
141
182
|
return;
|
|
@@ -149,6 +190,15 @@ export class DynamicObjectsModel extends ChartBaseElement {
|
|
|
149
190
|
this.setDynamicObjects(this.objects);
|
|
150
191
|
}
|
|
151
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Moves the object inside the drawing order so it's being drawn before the other elements
|
|
195
|
+
* @param paneId
|
|
196
|
+
* @param listNode
|
|
197
|
+
*/
|
|
198
|
+
bringToFront(id) {
|
|
199
|
+
this.moveObjectToFront(id);
|
|
200
|
+
this.notifyOrderChanged();
|
|
201
|
+
}
|
|
152
202
|
/**
|
|
153
203
|
* Moves the object inside the drawing order so it's being drawn after the other elements
|
|
154
204
|
* @param paneId
|
|
@@ -166,6 +216,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
|
|
|
166
216
|
paneList.removeAt(targetPos);
|
|
167
217
|
paneList.insertAt(0, obj);
|
|
168
218
|
this.setDynamicObjects(this.objects);
|
|
219
|
+
this.notifyOrderChanged();
|
|
169
220
|
}
|
|
170
221
|
}
|
|
171
222
|
/**
|
|
@@ -185,6 +236,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
|
|
|
185
236
|
paneList.removeAt(targetPos);
|
|
186
237
|
paneList.insertAt(targetPos + 1, obj);
|
|
187
238
|
this.setDynamicObjects(this.objects);
|
|
239
|
+
this.notifyOrderChanged();
|
|
188
240
|
}
|
|
189
241
|
}
|
|
190
242
|
/**
|
|
@@ -204,6 +256,7 @@ export class DynamicObjectsModel extends ChartBaseElement {
|
|
|
204
256
|
paneList.removeAt(targetPos);
|
|
205
257
|
paneList.insertAt(targetPos - 1, obj);
|
|
206
258
|
this.setDynamicObjects(this.objects);
|
|
259
|
+
this.notifyOrderChanged();
|
|
207
260
|
}
|
|
208
261
|
}
|
|
209
262
|
/**
|
|
@@ -28,6 +28,10 @@ export declare class HighlightsDrawer implements Drawer {
|
|
|
28
28
|
* chartComponent.draw();
|
|
29
29
|
*/
|
|
30
30
|
draw(): void;
|
|
31
|
+
private findHighlightStartIndex;
|
|
32
|
+
private resolveHighlightFromCandle;
|
|
33
|
+
private resolveHighlightToCandle;
|
|
34
|
+
private getHighlightEndLookupTimestamp;
|
|
31
35
|
/**
|
|
32
36
|
* Calculates the position of the highlight label based on the given parameters.
|
|
33
37
|
* @param {HighlightTextPlacement} placement - The placement of the highlight text.
|
|
@@ -8,6 +8,7 @@ import { HIGHLIGHTS_TYPES } from './highlights.model';
|
|
|
8
8
|
import { CanvasElement } from '../../canvas/canvas-bounds-container';
|
|
9
9
|
import { unitToPixels } from '../../model/scaling/viewport.model';
|
|
10
10
|
import { clipToBounds } from '../../utils/canvas/canvas-drawing-functions.utils';
|
|
11
|
+
import { getCandleStart, searchCandleIndex } from '../../utils/candles.utils';
|
|
11
12
|
const LABEL_PADDINGS = [20, 10];
|
|
12
13
|
export class HighlightsDrawer {
|
|
13
14
|
constructor(highlightsModel, chartModel, canvasModel, canvasBoundsContainer, config) {
|
|
@@ -63,13 +64,13 @@ export class HighlightsDrawer {
|
|
|
63
64
|
ctx.strokeStyle = strokeStyle;
|
|
64
65
|
items.forEach(item => {
|
|
65
66
|
var _a, _b, _c;
|
|
66
|
-
const
|
|
67
|
+
const period = this.chartModel.chartBaseModel.period;
|
|
68
|
+
const anchor = this.chartModel.getCandleTimestampAnchor();
|
|
69
|
+
const candles = this.chartModel.getCandles();
|
|
70
|
+
const fromXCandle = this.resolveHighlightFromCandle(item.from, candles, period, anchor);
|
|
67
71
|
const fromXCandleWidth = unitToPixels(fromXCandle.width, this.chartModel.scale.zoomX);
|
|
68
72
|
const fromX = fromXCandle.xStart(this.chartModel.scale);
|
|
69
|
-
|
|
70
|
-
// so we have to take previous timestamp based on current period to exclude PRE_MARKET highlighting
|
|
71
|
-
const xCandleTimestamp = item.to - this.chartModel.chartBaseModel.period;
|
|
72
|
-
const toXCandle = this.chartModel.candleFromTimestamp(xCandleTimestamp);
|
|
73
|
+
const toXCandle = this.resolveHighlightToCandle(item.to, candles, period, anchor);
|
|
73
74
|
const toXCandleWidth = unitToPixels(toXCandle.width, this.chartModel.scale.zoomX);
|
|
74
75
|
const toX = toXCandle.xStart(this.chartModel.scale) + toXCandleWidth;
|
|
75
76
|
// draw highlight' borders
|
|
@@ -98,6 +99,27 @@ export class HighlightsDrawer {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
}
|
|
102
|
+
findHighlightStartIndex(candles, sessionFrom, periodMs, anchor) {
|
|
103
|
+
if (anchor === 'open') {
|
|
104
|
+
const result = searchCandleIndex(sessionFrom, { extrapolate: false, candleTimestampAnchor: 'open' }, candles, periodMs);
|
|
105
|
+
return result.index;
|
|
106
|
+
}
|
|
107
|
+
return candles.findIndex((_, index) => getCandleStart(candles, index, periodMs, 'close') === sessionFrom);
|
|
108
|
+
}
|
|
109
|
+
resolveHighlightFromCandle(sessionFrom, candles, period, anchor) {
|
|
110
|
+
const startIdx = this.findHighlightStartIndex(candles, sessionFrom, period, anchor);
|
|
111
|
+
if (startIdx >= 0) {
|
|
112
|
+
return this.chartModel.candleFromIdx(startIdx);
|
|
113
|
+
}
|
|
114
|
+
return this.chartModel.candleFromTimestamp(sessionFrom);
|
|
115
|
+
}
|
|
116
|
+
resolveHighlightToCandle(sessionTo, candles, period, anchor) {
|
|
117
|
+
const lookupTimestamp = this.getHighlightEndLookupTimestamp(sessionTo, period, anchor);
|
|
118
|
+
return this.chartModel.candleFromTimestamp(lookupTimestamp);
|
|
119
|
+
}
|
|
120
|
+
getHighlightEndLookupTimestamp(sessionTo, periodMs, anchor) {
|
|
121
|
+
return anchor === 'close' ? sessionTo - 1 : sessionTo - periodMs;
|
|
122
|
+
}
|
|
101
123
|
/**
|
|
102
124
|
* Calculates the position of the highlight label based on the given parameters.
|
|
103
125
|
* @param {HighlightTextPlacement} placement - The placement of the highlight text.
|
|
@@ -66,7 +66,7 @@ export function drawLabel(ctx, yAxisDescriptionsCtx, backgroundCtx, bounds, pane
|
|
|
66
66
|
}
|
|
67
67
|
const lineY = (_e = visualLabel.lineY) !== null && _e !== void 0 ? _e : visualLabel.y;
|
|
68
68
|
const _drawLine = () => showLine &&
|
|
69
|
-
avoidAntialiasing(ctx, () => drawLine(ctx, lineXStart, lineY, lineXEnd, lineY, 1, visualLabel.lineDash));
|
|
69
|
+
avoidAntialiasing(ctx, () => { var _a; return drawLine(ctx, lineXStart, lineY, lineXEnd, lineY, 1, (_a = visualLabel.lineDash) !== null && _a !== void 0 ? _a : [], visualLabel.lineCap); });
|
|
70
70
|
const _drawLabel = () => drawLabel(ctx, bounds, replaceMinusSign(text), centralY, visualLabel, config, colors.yAxis, false);
|
|
71
71
|
const drawLineLabel = () => {
|
|
72
72
|
_drawLine();
|
|
@@ -166,9 +166,12 @@ export class FancyYAxisLabelsModel extends ChartBaseElement {
|
|
|
166
166
|
* @returns {LabelGroup} - A new LabelGroup object with updated label coordinates.
|
|
167
167
|
*/
|
|
168
168
|
updateLabelsCoordinates(labels, points) {
|
|
169
|
-
return Object.assign(Object.assign({}, labels), { labels: labels.labels.map((label, idx) =>
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
return Object.assign(Object.assign({}, labels), { labels: labels.labels.map((label, idx) => {
|
|
170
|
+
var _a;
|
|
171
|
+
return (Object.assign(Object.assign({}, label), {
|
|
172
|
+
// Keep connector Y at the price level when the Y-axis price label box was shifted to avoid overlap
|
|
173
|
+
lineY: (_a = label.lineY) !== null && _a !== void 0 ? _a : label.y, y: points[idx] }));
|
|
174
|
+
}) });
|
|
172
175
|
}
|
|
173
176
|
/**
|
|
174
177
|
* Returns an Observable that emits a void value whenever the labels position is recalculated.
|
|
@@ -3,13 +3,14 @@
|
|
|
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 { YAxisConfig, FullChartColors } from '../../chart.config';
|
|
6
|
+
import { YAxisConfig, FullChartColors, YAxisLabelAppearanceType, YAxisLabelMode } from '../../chart.config';
|
|
7
7
|
import { Bounds } from '../../model/bounds.model';
|
|
8
8
|
export interface YAxisLabelDrawProps {
|
|
9
9
|
ctx: CanvasRenderingContext2D;
|
|
10
10
|
bounds: Bounds;
|
|
11
11
|
text: string;
|
|
12
12
|
centralY: number;
|
|
13
|
+
lineY?: number;
|
|
13
14
|
config: YAxisLabelDrawConfig;
|
|
14
15
|
subGroupId?: number;
|
|
15
16
|
}
|
|
@@ -26,6 +27,12 @@ export interface YAxisLabelDrawConfig {
|
|
|
26
27
|
paddingStart?: number;
|
|
27
28
|
rounded?: boolean;
|
|
28
29
|
lineDash?: Array<number>;
|
|
30
|
+
/** When set (e.g. `round` with short dash gaps), strokes render as dotted segments. */
|
|
31
|
+
lineCap?: CanvasLineCap;
|
|
32
|
+
/** Overrides chart-wide `components.yAxis.labels.settings.drawings.mode` for this label (e.g. drawings). */
|
|
33
|
+
yAxisLabelMode?: YAxisLabelMode;
|
|
34
|
+
/** Overrides chart-wide `components.yAxis.labels.settings.drawings.type` for this label (e.g. drawings). */
|
|
35
|
+
yAxisLabelType?: YAxisLabelAppearanceType;
|
|
29
36
|
}
|
|
30
37
|
export declare const DEFAULT_PRICE_LABEL_PADDING = 4;
|
|
31
38
|
/**
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
9
9
|
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
10
10
|
*/
|
|
11
|
-
import { getFontFromConfig } from '../../chart.config';
|
|
11
|
+
import { getFontFromConfig, } from '../../chart.config';
|
|
12
12
|
import { drawPriceLabel, drawRoundedRect } from '../../utils/canvas/canvas-drawing-functions.utils';
|
|
13
13
|
import { calculateSymbolHeight, calculateTextWidth } from '../../utils/canvas/canvas-font-measure-tool.utils';
|
|
14
14
|
import { getLabelTextColorByBackgroundColor } from '../../utils/canvas/canvas-text-functions.utils';
|
|
@@ -62,6 +62,7 @@ export declare class HoverProducerComponent extends ChartBaseElement {
|
|
|
62
62
|
* When true, mobile long-touch (e.g. 200ms) does not activate crosshair / disable pan.
|
|
63
63
|
*/
|
|
64
64
|
private longTouchCrosshairSuppressed;
|
|
65
|
+
private hoverOverLastCandle;
|
|
65
66
|
private hoverProducerParts;
|
|
66
67
|
xFormatter: DateTimeFormatter;
|
|
67
68
|
constructor(crossEventProducer: CrossEventProducerComponent, scale: ScaleModel, config: FullChartConfig, chartModel: ChartModel, canvasInputListener: CanvasInputListenerComponent, canvasBoundsContainer: CanvasBoundsContainer, paneManager: PaneManager, timeZoneModel: TimeZoneModel, mainCanvasTouchHandler: MainCanvasTouchHandler, formatterFactory: (format: string) => (timestamp: number) => string);
|
|
@@ -103,10 +104,11 @@ export declare class HoverProducerComponent extends ChartBaseElement {
|
|
|
103
104
|
*/
|
|
104
105
|
createAndFireHoverFromCandle(candle: VisualCandle): void;
|
|
105
106
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
107
|
+
* Refreshes hover data (candles, studies, etc.) for the current pointer position.
|
|
108
|
+
* Used when the last candle updates while the legend must stay aligned to crosshair X — not to the last candle center.
|
|
108
109
|
*/
|
|
109
110
|
updateHover(candle: VisualCandle): void;
|
|
111
|
+
private updateHoverLastCandle;
|
|
110
112
|
/**
|
|
111
113
|
* Creates a hover element at the specified coordinates and fires it with the option to show the cross tool
|
|
112
114
|
* @param {CrossEvent} [x,y] - The coordinates where the hover element will be created
|
|
@@ -130,6 +132,10 @@ export declare class HoverProducerComponent extends ChartBaseElement {
|
|
|
130
132
|
*/
|
|
131
133
|
fireLastCross(): void;
|
|
132
134
|
setLongTouchCrosshairSuppressed(value: boolean): void;
|
|
135
|
+
/**
|
|
136
|
+
* Resets the current crosshair/touch state back to the default mobile idle state.
|
|
137
|
+
*/
|
|
138
|
+
resetCrossTool(clearMobile?: boolean): void;
|
|
133
139
|
/**
|
|
134
140
|
* Registers a hover producer part with the given id.
|
|
135
141
|
*
|