@devexperts/dxcharts-lite 2.7.15 → 2.7.17
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/animation/canvas-animation.d.ts +1 -0
- package/dist/chart/animation/canvas-animation.js +2 -1
- package/dist/chart/animation/viewport-model-animation.d.ts +4 -1
- package/dist/chart/animation/viewport-model-animation.js +21 -3
- package/dist/chart/bootstrap.js +17 -5
- package/dist/chart/chart.config.js +1 -1
- package/dist/chart/components/chart/chart-area-pan.handler.d.ts +16 -16
- package/dist/chart/components/chart/chart-area-pan.handler.js +8 -2
- package/dist/chart/components/chart/chart.component.js +2 -0
- package/dist/chart/components/chart/chart.model.d.ts +11 -0
- package/dist/chart/components/chart/chart.model.js +20 -0
- package/dist/chart/components/chart/price-formatters/__tests__/treasury-price.formatter.test.d.ts +6 -0
- package/dist/chart/components/chart/price-formatters/__tests__/treasury-price.formatter.test.js +94 -0
- package/dist/chart/components/chart/price-formatters/treasury-price.formatter.d.ts +9 -0
- package/dist/chart/components/chart/price-formatters/treasury-price.formatter.js +22 -1
- package/dist/chart/components/cross_tool/cross-tool.component.d.ts +3 -1
- package/dist/chart/components/cross_tool/cross-tool.component.js +3 -2
- package/dist/chart/components/cross_tool/cross-tool.model.d.ts +6 -3
- package/dist/chart/components/cross_tool/cross-tool.model.js +9 -5
- package/dist/chart/components/grid/grid.component.d.ts +1 -1
- package/dist/chart/components/grid/grid.component.js +2 -2
- package/dist/chart/components/grid/grid.drawer.d.ts +2 -1
- package/dist/chart/components/grid/grid.drawer.js +3 -2
- package/dist/chart/components/pan/chart-pan.component.js +17 -1
- package/dist/chart/components/pane/pane.component.js +3 -1
- package/dist/chart/drawers/data-series-drawers/trend-points.drawer.d.ts +12 -0
- package/dist/chart/drawers/data-series-drawers/trend-points.drawer.js +70 -0
- package/dist/chart/drawers/drawing-manager.js +0 -1
- package/dist/chart/inputhandlers/chart-resize.handler.d.ts +5 -0
- package/dist/chart/inputhandlers/chart-resize.handler.js +9 -1
- package/dist/chart/inputlisteners/canvas-input-listener.component.d.ts +28 -0
- package/dist/chart/inputlisteners/canvas-input-listener.component.js +104 -7
- package/dist/chart/model/data-series.config.d.ts +1 -1
- package/dist/chart/model/hit-test-canvas.model.d.ts +15 -13
- package/dist/chart/model/hit-test-canvas.model.js +2 -2
- package/dist/chart/model/scale.model.d.ts +5 -0
- package/dist/chart/model/scale.model.js +72 -6
- package/dist/chart/utils/device/touchpad.utils.d.ts +1 -0
- package/dist/chart/utils/device/touchpad.utils.js +1 -1
- package/dist/chart/utils/math.utils.js +1 -1
- package/dist/chart/utils/performance/queue-microtask.utils.d.ts +5 -0
- package/dist/chart/utils/performance/queue-microtask.utils.js +12 -0
- package/dist/chart/utils/performance/request-animation-frame-throttle.utils.d.ts +1 -0
- package/dist/chart/utils/performance/request-animation-frame-throttle.utils.js +3 -0
- package/dist/chart/utils/performance/safari/components/animations/safari-canvas-animation.d.ts +77 -0
- package/dist/chart/utils/performance/safari/components/animations/safari-canvas-animation.js +137 -0
- package/dist/chart/utils/performance/safari/components/chart-area-pan.handler/safari-chart-area-pan.handler.d.ts +54 -0
- package/dist/chart/utils/performance/safari/components/chart-area-pan.handler/safari-chart-area-pan.handler.js +180 -0
- package/dist/chart/utils/performance/safari/safari-performance.model.d.ts +32 -0
- package/dist/chart/utils/performance/safari/safari-performance.model.js +31 -0
- package/dist/chart/utils/performance/safari/safari-performance.utils.d.ts +18 -0
- package/dist/chart/utils/performance/safari/safari-performance.utils.js +34 -0
- package/dist/dxchart.min.js +4 -4
- package/package.json +2 -2
|
@@ -11,7 +11,7 @@ import { floorToDPR } from '../../utils/device/device-pixel-ratio.utils';
|
|
|
11
11
|
* Draws grid lines on chart.
|
|
12
12
|
*/
|
|
13
13
|
export class GridDrawer {
|
|
14
|
-
constructor(canvasModel, viewportModel, config, yAxisState, xBoundsProvider, yBoundsProvider, xLabelsProvider, yLabelsProvider, drawPredicate = () => true, extentIdx, getBaseline) {
|
|
14
|
+
constructor(canvasModel, viewportModel, config, yAxisState, xBoundsProvider, yBoundsProvider, xLabelsProvider, yLabelsProvider, drawPredicate = () => true, extentIdx, getBaseline, mainExtentIdx) {
|
|
15
15
|
this.canvasModel = canvasModel;
|
|
16
16
|
this.viewportModel = viewportModel;
|
|
17
17
|
this.config = config;
|
|
@@ -23,6 +23,7 @@ export class GridDrawer {
|
|
|
23
23
|
this.drawPredicate = drawPredicate;
|
|
24
24
|
this.extentIdx = extentIdx;
|
|
25
25
|
this.getBaseline = getBaseline;
|
|
26
|
+
this.mainExtentIdx = mainExtentIdx;
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Draws the chart on the canvas if the drawPredicate is true.
|
|
@@ -87,7 +88,7 @@ export class GridDrawer {
|
|
|
87
88
|
*/
|
|
88
89
|
drawHorizontal(ctx) {
|
|
89
90
|
const yAxisLabels = this.yLabelsProvider();
|
|
90
|
-
if (yAxisLabels.length && this.config.components.grid.horizontal) {
|
|
91
|
+
if (yAxisLabels.length > 0 && this.config.components.grid.horizontal && this.extentIdx === this.mainExtentIdx) {
|
|
91
92
|
ctx.lineWidth = this.config.components.grid.width;
|
|
92
93
|
ctx.strokeStyle = this.config.colors.chartAreaTheme.gridColor;
|
|
93
94
|
ctx.setLineDash(this.config.components.grid.dash || []);
|
|
@@ -7,6 +7,8 @@ import { CanvasElement } from '../../canvas/canvas-bounds-container';
|
|
|
7
7
|
import { ChartBaseElement } from '../../model/chart-base-element';
|
|
8
8
|
import { ChartAreaPanHandler } from '../chart/chart-area-pan.handler';
|
|
9
9
|
import { MainCanvasTouchHandler } from '../../inputhandlers/main-canvas-touch.handler';
|
|
10
|
+
import { isSafari } from '../../utils/device/touchpad.utils';
|
|
11
|
+
import { SafariChartAreaPanHandler } from '../../utils/performance/safari/components/chart-area-pan.handler/safari-chart-area-pan.handler';
|
|
10
12
|
export class ChartPanComponent extends ChartBaseElement {
|
|
11
13
|
constructor(eventBus, mainScale, canvasBoundsContainer, config, canvasAnimation, canvasInputListener, mainCanvasParent, chartBaseModel, hitTestCanvasModel) {
|
|
12
14
|
super();
|
|
@@ -19,7 +21,21 @@ export class ChartPanComponent extends ChartBaseElement {
|
|
|
19
21
|
this.mainCanvasParent = mainCanvasParent;
|
|
20
22
|
this.chartBaseModel = chartBaseModel;
|
|
21
23
|
this.hitTestCanvasModel = hitTestCanvasModel;
|
|
22
|
-
|
|
24
|
+
const chartAreaPanHandlerParams = [
|
|
25
|
+
this.eventBus,
|
|
26
|
+
this.config,
|
|
27
|
+
this.mainScale,
|
|
28
|
+
this.canvasInputListener,
|
|
29
|
+
this.canvasBoundsContainer,
|
|
30
|
+
this.canvasAnimation,
|
|
31
|
+
this,
|
|
32
|
+
this.hitTestCanvasModel,
|
|
33
|
+
];
|
|
34
|
+
// Different performance logic for Safari because of browser specifics
|
|
35
|
+
const chartAreaPanHandler = isSafari
|
|
36
|
+
? new SafariChartAreaPanHandler(...chartAreaPanHandlerParams)
|
|
37
|
+
: new ChartAreaPanHandler(...chartAreaPanHandlerParams);
|
|
38
|
+
this.chartAreaPanHandler = chartAreaPanHandler;
|
|
23
39
|
this.addChildEntity(this.chartAreaPanHandler);
|
|
24
40
|
this.mainCanvasTouchHandler = new MainCanvasTouchHandler(this.chartAreaPanHandler, this.mainScale, this.canvasInputListener, this.mainCanvasParent, this.canvasBoundsContainer.getBoundsHitTest(CanvasElement.ALL_PANES));
|
|
25
41
|
this.addChildEntity(this.mainCanvasTouchHandler);
|
|
@@ -95,8 +95,10 @@ export class PaneComponent extends ChartBaseElement {
|
|
|
95
95
|
* @returns {GridComponent} - The newly created GridComponent instance.
|
|
96
96
|
*/
|
|
97
97
|
createGridComponent(uuid, extentIdx, scale, yAxisState, yAxisLabelsGetter, yAxisBaselineGetter) {
|
|
98
|
+
var _a, _b;
|
|
98
99
|
const chartPaneId = CanvasElement.PANE_UUID(uuid);
|
|
99
|
-
const
|
|
100
|
+
const mainExtentIdx = (_b = (_a = this.mainExtent) === null || _a === void 0 ? void 0 : _a.idx) !== null && _b !== void 0 ? _b : 0;
|
|
101
|
+
const gridComponent = new GridComponent(this.mainCanvasModel, scale, this.config, yAxisState, `PANE_${uuid}_${extentIdx}_grid_drawer`, this.drawingManager, () => this.canvasBoundsContainer.getBounds(chartPaneId), () => this.canvasBoundsContainer.getBounds(chartPaneId), () => [], yAxisLabelsGetter, extentIdx, yAxisBaselineGetter, () => this.config.components.grid.visible, mainExtentIdx);
|
|
100
102
|
return gridComponent;
|
|
101
103
|
}
|
|
102
104
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
|
|
3
|
+
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
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
|
+
*/
|
|
6
|
+
import { VisualSeriesPoint, DataSeriesModel } from '../../model/data-series.model';
|
|
7
|
+
import { HTSeriesDrawerConfig, SeriesDrawer } from '../data-series.drawer';
|
|
8
|
+
export declare class TrendPointsDrawer implements SeriesDrawer {
|
|
9
|
+
constructor();
|
|
10
|
+
draw(ctx: CanvasRenderingContext2D, allPoints: VisualSeriesPoint[][], model: DataSeriesModel, hitTestDrawerConfig: HTSeriesDrawerConfig): void;
|
|
11
|
+
}
|
|
12
|
+
export declare const previousValue: (arr: VisualSeriesPoint[], idx: number) => number | undefined;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2019 - 2025 Devexperts Solutions IE Limited
|
|
3
|
+
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
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
|
+
*/
|
|
6
|
+
import { flat } from '../../utils/array.utils';
|
|
7
|
+
const DEFAULT_FILL_COLOR = '#FF00FF';
|
|
8
|
+
export class TrendPointsDrawer {
|
|
9
|
+
constructor() { }
|
|
10
|
+
draw(ctx, allPoints, model, hitTestDrawerConfig) {
|
|
11
|
+
const allPointsFlat = flat(allPoints);
|
|
12
|
+
const config = model.getPaintConfig(0);
|
|
13
|
+
const radius = hitTestDrawerConfig.hoverWidth ? hitTestDrawerConfig.hoverWidth / 2 : config.lineWidth;
|
|
14
|
+
allPointsFlat.forEach((point, idx) => {
|
|
15
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16
|
+
// 2 colors: Negative and Positive
|
|
17
|
+
const previousClose = previousValue(allPointsFlat, idx);
|
|
18
|
+
const isNegativeTrend = previousClose && point.close < previousClose;
|
|
19
|
+
if (((_a = config.multiplyColors) === null || _a === void 0 ? void 0 : _a.length) === 2) {
|
|
20
|
+
ctx.fillStyle =
|
|
21
|
+
(_b = hitTestDrawerConfig.color) !== null && _b !== void 0 ? _b : ((isNegativeTrend ? config.multiplyColors[1] : config.multiplyColors[0]) ||
|
|
22
|
+
config.color ||
|
|
23
|
+
DEFAULT_FILL_COLOR);
|
|
24
|
+
const x = model.view.toX(point.centerUnit);
|
|
25
|
+
const y = model.view.toY(point.close);
|
|
26
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
27
|
+
ctx.fill();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// 4 colors: Negative and Down, Negative and Up, Positive and Down, Positive and Up
|
|
31
|
+
if (((_c = config.multiplyColors) === null || _c === void 0 ? void 0 : _c.length) === 4) {
|
|
32
|
+
if (isNegativeTrend && point.close < 0) {
|
|
33
|
+
ctx.fillStyle =
|
|
34
|
+
(_d = hitTestDrawerConfig.color) !== null && _d !== void 0 ? _d : (config.multiplyColors[0] || config.color || DEFAULT_FILL_COLOR);
|
|
35
|
+
}
|
|
36
|
+
if (!isNegativeTrend && point.close < 0) {
|
|
37
|
+
ctx.fillStyle =
|
|
38
|
+
(_e = hitTestDrawerConfig.color) !== null && _e !== void 0 ? _e : (config.multiplyColors[1] || config.color || DEFAULT_FILL_COLOR);
|
|
39
|
+
}
|
|
40
|
+
if (isNegativeTrend && point.close > 0) {
|
|
41
|
+
ctx.fillStyle =
|
|
42
|
+
(_f = hitTestDrawerConfig.color) !== null && _f !== void 0 ? _f : (config.multiplyColors[2] || config.color || DEFAULT_FILL_COLOR);
|
|
43
|
+
}
|
|
44
|
+
if (!isNegativeTrend && point.close > 0) {
|
|
45
|
+
ctx.fillStyle =
|
|
46
|
+
(_g = hitTestDrawerConfig.color) !== null && _g !== void 0 ? _g : (config.multiplyColors[3] || config.color || DEFAULT_FILL_COLOR);
|
|
47
|
+
}
|
|
48
|
+
ctx.beginPath();
|
|
49
|
+
const x = model.view.toX(point.centerUnit);
|
|
50
|
+
const y = model.view.toY(point.close);
|
|
51
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
52
|
+
ctx.fill();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// 1 color
|
|
56
|
+
ctx.fillStyle = (_h = hitTestDrawerConfig.color) !== null && _h !== void 0 ? _h : (config.color || DEFAULT_FILL_COLOR);
|
|
57
|
+
ctx.beginPath();
|
|
58
|
+
const x = model.view.toX(point.centerUnit);
|
|
59
|
+
const y = model.view.toY(point.close);
|
|
60
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
61
|
+
ctx.fill();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export const previousValue = (arr, idx) => {
|
|
66
|
+
do {
|
|
67
|
+
idx--;
|
|
68
|
+
} while (idx >= 0 && !isFinite(arr[idx] && arr[idx].close));
|
|
69
|
+
return arr[idx] ? arr[idx].close : undefined;
|
|
70
|
+
};
|
|
@@ -45,7 +45,6 @@ export class DrawingManager {
|
|
|
45
45
|
this.drawersMap = {};
|
|
46
46
|
this.canvasIdsList = [];
|
|
47
47
|
this.animFrameId = `draw_${uuid()}`;
|
|
48
|
-
// eventBus.on(EVENT_DRAW_LAST_CANDLE, () => animationFrameThrottled(this.animFrameId + 'last', () => this.drawLastBar()));
|
|
49
48
|
this.drawHitTestCanvas = () => {
|
|
50
49
|
this.drawingOrder.forEach(drawer => {
|
|
51
50
|
if (drawer.indexOf(HIT_TEST_PREFIX) !== -1) {
|
|
@@ -21,6 +21,7 @@ export declare class ChartResizeHandler {
|
|
|
21
21
|
previousBCR: PickedDOMRect | undefined;
|
|
22
22
|
private animFrameId;
|
|
23
23
|
canvasResized: Subject<PickedDOMRect>;
|
|
24
|
+
private recentlyResized;
|
|
24
25
|
constructor(frameElement: HTMLElement, resizerElement: HTMLElement, bus: EventBus, canvasModels: CanvasModel[], config?: FullChartConfig | undefined);
|
|
25
26
|
/**
|
|
26
27
|
* Subscribe to resize events
|
|
@@ -43,6 +44,10 @@ export declare class ChartResizeHandler {
|
|
|
43
44
|
* @returns {void}
|
|
44
45
|
*/
|
|
45
46
|
fireUpdates(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Resets the resize flag after it has been processed
|
|
49
|
+
*/
|
|
50
|
+
clearResizedFlag(): void;
|
|
46
51
|
/**
|
|
47
52
|
* Checks if the dimensions of two PickedDOMRect objects are different.
|
|
48
53
|
* @param {PickedDOMRect} previousBCR - The previous bounding client rectangle.
|
|
@@ -20,6 +20,7 @@ export class ChartResizeHandler {
|
|
|
20
20
|
this.previousBCR = undefined;
|
|
21
21
|
this.animFrameId = `resize_${uuid()}`;
|
|
22
22
|
this.canvasResized = new Subject();
|
|
23
|
+
this.recentlyResized = false; // Flag to track if resize happened recently
|
|
23
24
|
this.elementResizeDetector = new ResizeObserver(() => this.handleResize());
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
@@ -59,12 +60,19 @@ export class ChartResizeHandler {
|
|
|
59
60
|
}
|
|
60
61
|
if (this.previousBCR === undefined || this.isBCRDimensionsDiffer(this.previousBCR, newBCR)) {
|
|
61
62
|
this.previousBCR = newBCR;
|
|
63
|
+
this.recentlyResized = true; // Set flag when actual resize happens
|
|
62
64
|
this.canvasModels.forEach(model => this.previousBCR && model.updateDPR(this.previousBCR));
|
|
63
65
|
this.canvasResized.next(newBCR);
|
|
64
66
|
this.bus.fire(EVENT_RESIZED, newBCR);
|
|
65
67
|
this.bus.fire(EVENT_DRAW);
|
|
66
68
|
}
|
|
67
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Resets the resize flag after it has been processed
|
|
72
|
+
*/
|
|
73
|
+
clearResizedFlag() {
|
|
74
|
+
this.recentlyResized = false;
|
|
75
|
+
}
|
|
68
76
|
/**
|
|
69
77
|
* Checks if the dimensions of two PickedDOMRect objects are different.
|
|
70
78
|
* @param {PickedDOMRect} previousBCR - The previous bounding client rectangle.
|
|
@@ -98,6 +106,6 @@ export class ChartResizeHandler {
|
|
|
98
106
|
* @returns {boolean} - Returns true if the canvas has been resized, false otherwise.
|
|
99
107
|
*/
|
|
100
108
|
wasResized() {
|
|
101
|
-
return this.
|
|
109
|
+
return this.recentlyResized;
|
|
102
110
|
}
|
|
103
111
|
}
|
|
@@ -60,8 +60,35 @@ export declare class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
60
60
|
dragging: boolean;
|
|
61
61
|
dragStartEvent?: CustomMouseEvent;
|
|
62
62
|
canvasBounds: Bounds;
|
|
63
|
+
private cachedElementRect;
|
|
64
|
+
private rectCacheValid;
|
|
63
65
|
constructor(eventBus: EventBus, element: HTMLElement);
|
|
64
66
|
private documentDragListeners;
|
|
67
|
+
/**
|
|
68
|
+
* Gets cached element bounding rect or calculates new one if cache is invalid.
|
|
69
|
+
* Caching prevents expensive getBoundingClientRect() calls during frequent operations.
|
|
70
|
+
*/
|
|
71
|
+
private getElementRect;
|
|
72
|
+
/**
|
|
73
|
+
* Creates fallback DOMRect for error cases or when element is unavailable
|
|
74
|
+
*/
|
|
75
|
+
private createFallbackRect;
|
|
76
|
+
/**
|
|
77
|
+
* Invalidates the cached bounding client rect, forcing recalculation on next access
|
|
78
|
+
*/
|
|
79
|
+
private invalidateRectCache;
|
|
80
|
+
/**
|
|
81
|
+
* Forces immediate initialization of the rect cache and canvas bounds
|
|
82
|
+
*/
|
|
83
|
+
initializeRectCache(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Safely cleans up document drag listeners to prevent memory leaks
|
|
86
|
+
*/
|
|
87
|
+
private cleanupDocumentDragListeners;
|
|
88
|
+
/**
|
|
89
|
+
* Override doDeactivate to ensure proper cleanup of cached data
|
|
90
|
+
*/
|
|
91
|
+
protected doDeactivate(): void;
|
|
65
92
|
private dragProcessListener;
|
|
66
93
|
private dragStartListener;
|
|
67
94
|
/**
|
|
@@ -113,6 +140,7 @@ export declare class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
113
140
|
private updateCurrentDocumentPoint;
|
|
114
141
|
/**
|
|
115
142
|
* Updates the current mouse point based on the provided CustomMouseEvent.
|
|
143
|
+
* Uses cached element rect to avoid frequent reflow in Safari.
|
|
116
144
|
* @param {CustomMouseEvent} e - The CustomMouseEvent object containing the mouse/touch coordinates.
|
|
117
145
|
* @returns {void}
|
|
118
146
|
* @private
|
|
@@ -71,6 +71,9 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
71
71
|
width: 0,
|
|
72
72
|
height: 0,
|
|
73
73
|
};
|
|
74
|
+
// Cached bounding client rect to avoid frequent reflow in Safari
|
|
75
|
+
this.cachedElementRect = null;
|
|
76
|
+
this.rectCacheValid = false;
|
|
74
77
|
this.documentDragListeners = [];
|
|
75
78
|
this.dragProcessListener = () => {
|
|
76
79
|
this.xDragSubject.next(this.currentPoint.x - this.dragStartPoint.x);
|
|
@@ -85,7 +88,8 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
85
88
|
this.updateCurrentPoints(e);
|
|
86
89
|
this.dragging = true;
|
|
87
90
|
this.dragStartEvent = e;
|
|
88
|
-
|
|
91
|
+
// Clean up any existing drag listeners before adding new ones
|
|
92
|
+
this.cleanupDocumentDragListeners();
|
|
89
93
|
this.dragStartPoint = CanvasInputListenerComponent.copyPoint(this.currentPoint);
|
|
90
94
|
this.xDragStartSubject.next(this.dragStartPoint);
|
|
91
95
|
this.yDragStartSubject.next(this.dragStartPoint);
|
|
@@ -104,7 +108,7 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
104
108
|
this.updateElementOffsetListener = () => this.mouseMoveSubject.next(this.currentPoint);
|
|
105
109
|
this.dragEndListener = () => {
|
|
106
110
|
this.dragging = false;
|
|
107
|
-
this.
|
|
111
|
+
this.cleanupDocumentDragListeners();
|
|
108
112
|
this.xDragEndSubject.next();
|
|
109
113
|
this.yDragEndSubject.next();
|
|
110
114
|
};
|
|
@@ -113,6 +117,97 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
113
117
|
this.updateCurrentMousePoint(e);
|
|
114
118
|
};
|
|
115
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Gets cached element bounding rect or calculates new one if cache is invalid.
|
|
122
|
+
* Caching prevents expensive getBoundingClientRect() calls during frequent operations.
|
|
123
|
+
*/
|
|
124
|
+
getElementRect() {
|
|
125
|
+
if (this.rectCacheValid && this.cachedElementRect) {
|
|
126
|
+
return this.cachedElementRect;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
if (!this.element || !this.element.isConnected) {
|
|
130
|
+
return this.createFallbackRect();
|
|
131
|
+
}
|
|
132
|
+
const rect = this.element.getBoundingClientRect();
|
|
133
|
+
this.cachedElementRect = rect;
|
|
134
|
+
this.rectCacheValid = true;
|
|
135
|
+
return rect;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
return this.createFallbackRect();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Creates fallback DOMRect for error cases or when element is unavailable
|
|
143
|
+
*/
|
|
144
|
+
createFallbackRect() {
|
|
145
|
+
// DOMRect might not be available in older browsers
|
|
146
|
+
if (typeof DOMRect !== 'undefined') {
|
|
147
|
+
return new DOMRect(0, 0, 0, 0);
|
|
148
|
+
}
|
|
149
|
+
// Fallback for older browsers - create object with same interface
|
|
150
|
+
const rect = {
|
|
151
|
+
x: 0,
|
|
152
|
+
y: 0,
|
|
153
|
+
width: 0,
|
|
154
|
+
height: 0,
|
|
155
|
+
top: 0,
|
|
156
|
+
right: 0,
|
|
157
|
+
bottom: 0,
|
|
158
|
+
left: 0,
|
|
159
|
+
toJSON: () => ({}),
|
|
160
|
+
};
|
|
161
|
+
return rect;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Invalidates the cached bounding client rect, forcing recalculation on next access
|
|
165
|
+
*/
|
|
166
|
+
invalidateRectCache() {
|
|
167
|
+
this.rectCacheValid = false;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Forces immediate initialization of the rect cache and canvas bounds
|
|
171
|
+
*/
|
|
172
|
+
initializeRectCache() {
|
|
173
|
+
this.invalidateRectCache();
|
|
174
|
+
const bcr = this.getElementRect();
|
|
175
|
+
this.canvasBounds.x = bcr.left;
|
|
176
|
+
this.canvasBounds.y = bcr.top;
|
|
177
|
+
this.canvasBounds.width = bcr.width;
|
|
178
|
+
this.canvasBounds.height = bcr.height;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Safely cleans up document drag listeners to prevent memory leaks
|
|
182
|
+
*/
|
|
183
|
+
cleanupDocumentDragListeners() {
|
|
184
|
+
if (!this.documentDragListeners || !Array.isArray(this.documentDragListeners)) {
|
|
185
|
+
this.documentDragListeners = [];
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.documentDragListeners.forEach(unsub => {
|
|
189
|
+
try {
|
|
190
|
+
unsub();
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
// Ignore errors during cleanup to prevent crashes
|
|
194
|
+
console.warn('Error cleaning up drag listener:', error);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
this.documentDragListeners = [];
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Override doDeactivate to ensure proper cleanup of cached data
|
|
201
|
+
*/
|
|
202
|
+
doDeactivate() {
|
|
203
|
+
// Clean up rect cache to prevent stale data
|
|
204
|
+
this.invalidateRectCache();
|
|
205
|
+
this.cachedElementRect = null;
|
|
206
|
+
// Clean up drag listeners safely
|
|
207
|
+
this.cleanupDocumentDragListeners();
|
|
208
|
+
// Call parent cleanup
|
|
209
|
+
super.doDeactivate();
|
|
210
|
+
}
|
|
116
211
|
/**
|
|
117
212
|
* This methods allows change scale, only if dragging using primary mouse button or touch.
|
|
118
213
|
* @param e
|
|
@@ -261,6 +356,7 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
261
356
|
this.addSubscription(subscribeListener(this.element, (e) => this.touchEndSubject.next(e), 'touchend'));
|
|
262
357
|
this.addSubscription(subscribeListener(this.element, (e) => this.touchCancelSubject.next(e), 'touchcancel'));
|
|
263
358
|
this.addSubscription(subscribeListener(this.element, (e) => {
|
|
359
|
+
this.updateCurrentPoints(e);
|
|
264
360
|
this.wheelSubject.next(e);
|
|
265
361
|
e.preventDefault(); // to disable the scroll over the document, if for example chart is used as widget
|
|
266
362
|
}, 'wheel'));
|
|
@@ -269,7 +365,9 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
269
365
|
this.addSubscription(subscribeListener(this.element, leftMouseButtonListener(() => this.mouseUpSubject.next(this.currentPoint)), 'mouseup'));
|
|
270
366
|
this.addSubscription(subscribeListener(document, leftMouseButtonListener(() => this.mouseUpDocumentSubject.next(this.currentPoint)), 'mouseup'));
|
|
271
367
|
this.addRxSubscription(this.eventBus.observe(EVENT_RESIZED).subscribe(() => {
|
|
272
|
-
|
|
368
|
+
// Force invalidate cache on resize to ensure fresh getBoundingClientRect data
|
|
369
|
+
this.invalidateRectCache();
|
|
370
|
+
const bcr = this.getElementRect();
|
|
273
371
|
this.canvasBounds.x = bcr.left;
|
|
274
372
|
this.canvasBounds.y = bcr.top;
|
|
275
373
|
this.canvasBounds.width = bcr.width;
|
|
@@ -322,15 +420,14 @@ export class CanvasInputListenerComponent extends ChartBaseElement {
|
|
|
322
420
|
}
|
|
323
421
|
/**
|
|
324
422
|
* Updates the current mouse point based on the provided CustomMouseEvent.
|
|
423
|
+
* Uses cached element rect to avoid frequent reflow in Safari.
|
|
325
424
|
* @param {CustomMouseEvent} e - The CustomMouseEvent object containing the mouse/touch coordinates.
|
|
326
425
|
* @returns {void}
|
|
327
426
|
* @private
|
|
328
427
|
*/
|
|
329
428
|
updateCurrentMousePoint(e) {
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
// but it's faster to cache BCR and recalculate when it's needed
|
|
333
|
-
const rect = this.element.getBoundingClientRect();
|
|
429
|
+
// Use cached rect to prevent frequent reflow/repaint in Safari
|
|
430
|
+
const rect = this.getElementRect();
|
|
334
431
|
if ('clientX' in e) {
|
|
335
432
|
this.currentPoint.x = e.clientX - rect.left;
|
|
336
433
|
}
|
|
@@ -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 { BarTypes, YAxisLabelAppearanceType, YAxisLabelMode } from '../chart.config';
|
|
7
|
-
export type DataSeriesType = 'POINTS' | 'LINEAR' | 'HISTOGRAM' | 'TREND_HISTOGRAM' | 'DIFFERENCE' | 'TEXT' | 'ABOVE_CANDLE_TEXT' | 'BELOW_CANDLE_TEXT' | 'ABOVE_CANDLE_TRIANGLE' | 'TRIANGLE' | 'COLOR_CANDLE' | 'RECTANGULAR' | 'EMA_CLOUD_LINE' | 'LINEAR_TREND' | keyof BarTypes | string;
|
|
7
|
+
export type DataSeriesType = 'POINTS' | 'TREND_POINTS' | 'LINEAR' | 'HISTOGRAM' | 'TREND_HISTOGRAM' | 'DIFFERENCE' | 'TEXT' | 'ABOVE_CANDLE_TEXT' | 'BELOW_CANDLE_TEXT' | 'ABOVE_CANDLE_TRIANGLE' | 'TRIANGLE' | 'COLOR_CANDLE' | 'RECTANGULAR' | 'EMA_CLOUD_LINE' | 'LINEAR_TREND' | 'SQUEEZE_POINTS' | keyof BarTypes | string;
|
|
8
8
|
export interface DataSeriesConfig {
|
|
9
9
|
paintConfig: Array<DataSeriesPaintConfig>;
|
|
10
10
|
visible: boolean;
|
|
@@ -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 { Observable, BehaviorSubject } from 'rxjs';
|
|
6
|
+
import { Observable, Subject, Subscription, BehaviorSubject } from 'rxjs';
|
|
7
7
|
import { CanvasBoundsContainer } from '../canvas/canvas-bounds-container';
|
|
8
8
|
import { CursorType, FullChartConfig } from '../chart.config';
|
|
9
9
|
import { CanvasModel } from './canvas.model';
|
|
10
10
|
import { DrawingManager } from '../drawers/drawing-manager';
|
|
11
11
|
import EventBus from '../events/event-bus';
|
|
12
12
|
import { CanvasInputListenerComponent, Point } from '../inputlisteners/canvas-input-listener.component';
|
|
13
|
+
export declare const bigPrimeNumber = 317;
|
|
13
14
|
export type HitTestEvents = 'mousedown' | 'hover' | 'touchstart' | 'dblclick' | 'contextmenu' | 'zoom' | 'mouseup';
|
|
14
15
|
type HitTestType = 'DRAWINGS' | 'DATA_SERIES' | 'EVENTS' | 'NEWS' | 'EXECUTED_ORDERS';
|
|
15
16
|
export declare const HIT_TEST_ID_RANGE: Record<HitTestType, [number, number]>;
|
|
@@ -20,14 +21,14 @@ export declare const HIT_TEST_ID_RANGE: Record<HitTestType, [number, number]>;
|
|
|
20
21
|
* @doc-tags chart-core,hit-test
|
|
21
22
|
*/
|
|
22
23
|
export declare class HitTestCanvasModel extends CanvasModel {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
protected canvasInputListener: CanvasInputListenerComponent;
|
|
25
|
+
protected canvasBoundsContainer: CanvasBoundsContainer;
|
|
26
|
+
protected hitTestSubscribers: HitTestSubscriber[];
|
|
27
|
+
protected eventsSubscriptions: Subscription[];
|
|
28
|
+
protected hoverSubject: Subject<HitTestEvent>;
|
|
29
|
+
protected touchStartSubject: Subject<HitTestEvent>;
|
|
30
|
+
protected dblClickSubject: Subject<HitTestEvent>;
|
|
31
|
+
protected rightClickSubject: Subject<HitTestEvent>;
|
|
31
32
|
hitTestDrawersPredicateSubject: BehaviorSubject<boolean>;
|
|
32
33
|
constructor(eventBus: EventBus, canvas: HTMLCanvasElement, canvasInputListener: CanvasInputListenerComponent, canvasBoundsContainer: CanvasBoundsContainer, drawingManager: DrawingManager, chartConfig: FullChartConfig, canvasModels: CanvasModel[], resizer?: HTMLElement);
|
|
33
34
|
/**
|
|
@@ -80,8 +81,8 @@ export declare class HitTestCanvasModel extends CanvasModel {
|
|
|
80
81
|
* Observes rightclicked on element event, provides rightclicked element model.
|
|
81
82
|
*/
|
|
82
83
|
observeRightClickOnElement(): Observable<HitTestEvent>;
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
protected curImgData: Uint8ClampedArray;
|
|
85
|
+
protected prevAnimationFrameId: number;
|
|
85
86
|
/**
|
|
86
87
|
* Retrieves the pixel data at the specified coordinates.
|
|
87
88
|
*
|
|
@@ -90,7 +91,7 @@ export declare class HitTestCanvasModel extends CanvasModel {
|
|
|
90
91
|
* @param {number} y - The y-coordinate of the pixel.
|
|
91
92
|
* @returns {Uint8ClampedArray} - The pixel data at the specified coordinates.
|
|
92
93
|
*/
|
|
93
|
-
|
|
94
|
+
protected getPixel(x: number, y: number): Uint8ClampedArray;
|
|
94
95
|
/**
|
|
95
96
|
* Resolves ht model based on the provided point
|
|
96
97
|
* @param point - The point for which to resolve model
|
|
@@ -108,7 +109,7 @@ export declare class HitTestCanvasModel extends CanvasModel {
|
|
|
108
109
|
* @param {HitTestEvents} event - The type of event that occurred.
|
|
109
110
|
* @returns {void}
|
|
110
111
|
*/
|
|
111
|
-
|
|
112
|
+
protected eventHandler(point: Point, event: HitTestEvents): void;
|
|
112
113
|
}
|
|
113
114
|
export interface HitTestSubscriber<T extends unknown = unknown> {
|
|
114
115
|
getIdRange(): [number, number];
|
|
@@ -126,4 +127,5 @@ export interface HitTestEvent<T = unknown> {
|
|
|
126
127
|
readonly point: Point;
|
|
127
128
|
readonly model: T;
|
|
128
129
|
}
|
|
130
|
+
export declare const sortSubscribers: (subs: HitTestSubscriber[], id: number) => [HitTestSubscriber | undefined, HitTestSubscriber[]];
|
|
129
131
|
export {};
|
|
@@ -8,7 +8,7 @@ import { map, throttleTime } from 'rxjs/operators';
|
|
|
8
8
|
import { CanvasElement } from '../canvas/canvas-bounds-container';
|
|
9
9
|
import { CanvasModel, initCanvasWithConfig } from './canvas.model';
|
|
10
10
|
import { animationFrameId } from '../utils/performance/request-animation-frame-throttle.utils';
|
|
11
|
-
const bigPrimeNumber = 317;
|
|
11
|
+
export const bigPrimeNumber = 317;
|
|
12
12
|
export const HIT_TEST_ID_RANGE = {
|
|
13
13
|
DRAWINGS: [1, 4999],
|
|
14
14
|
NEWS: [5000, 5999],
|
|
@@ -248,7 +248,7 @@ export class HitTestCanvasModel extends CanvasModel {
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
const sortSubscribers = (subs, id) => {
|
|
251
|
+
export const sortSubscribers = (subs, id) => {
|
|
252
252
|
let mainSubscriber = undefined;
|
|
253
253
|
const restSubs = [];
|
|
254
254
|
subs.forEach(sub => {
|
|
@@ -9,6 +9,7 @@ import { CanvasAnimation } from '../animation/canvas-animation';
|
|
|
9
9
|
import { AutoScaleViewportSubModel } from './scaling/auto-scale.model';
|
|
10
10
|
import { Price, Unit, ViewportModel, ViewportModelState, Zoom } from './scaling/viewport.model';
|
|
11
11
|
import { BoundsProvider } from './bounds.model';
|
|
12
|
+
import VisualCandle from './visual-candle';
|
|
12
13
|
export interface HighLowWithIndex {
|
|
13
14
|
high: Price;
|
|
14
15
|
low: Price;
|
|
@@ -50,6 +51,8 @@ export declare class ScaleModel extends ViewportModel {
|
|
|
50
51
|
history: ScaleHistoryItem[];
|
|
51
52
|
offsets: ChartConfigComponentsOffsets;
|
|
52
53
|
private xConstraints;
|
|
54
|
+
private autoScaleZoomSubscription;
|
|
55
|
+
private autoScalePanSubscription;
|
|
53
56
|
constructor(config: FullChartConfig, getBounds: BoundsProvider, canvasAnimation: CanvasAnimation);
|
|
54
57
|
protected doActivate(): void;
|
|
55
58
|
protected doDeactivate(): void;
|
|
@@ -98,6 +101,7 @@ export declare class ScaleModel extends ViewportModel {
|
|
|
98
101
|
* @param forceNoAutoScale - force NOT apply auto-scaling (for lazy loading)
|
|
99
102
|
*/
|
|
100
103
|
setXScale(xStart: Unit, xEnd: Unit, forceNoAnimation?: boolean): void;
|
|
104
|
+
setXScaleWithoutYScale(visualCandleSource: VisualCandle[]): void;
|
|
101
105
|
setYScale(yStart: Unit, yEnd: Unit, fire?: boolean): void;
|
|
102
106
|
private setLockedYScale;
|
|
103
107
|
/**
|
|
@@ -164,6 +168,7 @@ export declare class ScaleModel extends ViewportModel {
|
|
|
164
168
|
* @param value - If true, the price-to-bar ratio will be locked. If false, it will not be locked.
|
|
165
169
|
*/
|
|
166
170
|
setLockPriceToBarRatio(value?: boolean): void;
|
|
171
|
+
private initAutoScaleThrottling;
|
|
167
172
|
}
|
|
168
173
|
/**
|
|
169
174
|
* The SyncedByXScaleModel class extends the ScaleModel class and adds support for synchronization with other ScaleModel instance, so both instances maintain the same X-axis bounds.
|