@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.
Files changed (54) hide show
  1. package/dist/chart/animation/canvas-animation.d.ts +1 -0
  2. package/dist/chart/animation/canvas-animation.js +2 -1
  3. package/dist/chart/animation/viewport-model-animation.d.ts +4 -1
  4. package/dist/chart/animation/viewport-model-animation.js +21 -3
  5. package/dist/chart/bootstrap.js +17 -5
  6. package/dist/chart/chart.config.js +1 -1
  7. package/dist/chart/components/chart/chart-area-pan.handler.d.ts +16 -16
  8. package/dist/chart/components/chart/chart-area-pan.handler.js +8 -2
  9. package/dist/chart/components/chart/chart.component.js +2 -0
  10. package/dist/chart/components/chart/chart.model.d.ts +11 -0
  11. package/dist/chart/components/chart/chart.model.js +20 -0
  12. package/dist/chart/components/chart/price-formatters/__tests__/treasury-price.formatter.test.d.ts +6 -0
  13. package/dist/chart/components/chart/price-formatters/__tests__/treasury-price.formatter.test.js +94 -0
  14. package/dist/chart/components/chart/price-formatters/treasury-price.formatter.d.ts +9 -0
  15. package/dist/chart/components/chart/price-formatters/treasury-price.formatter.js +22 -1
  16. package/dist/chart/components/cross_tool/cross-tool.component.d.ts +3 -1
  17. package/dist/chart/components/cross_tool/cross-tool.component.js +3 -2
  18. package/dist/chart/components/cross_tool/cross-tool.model.d.ts +6 -3
  19. package/dist/chart/components/cross_tool/cross-tool.model.js +9 -5
  20. package/dist/chart/components/grid/grid.component.d.ts +1 -1
  21. package/dist/chart/components/grid/grid.component.js +2 -2
  22. package/dist/chart/components/grid/grid.drawer.d.ts +2 -1
  23. package/dist/chart/components/grid/grid.drawer.js +3 -2
  24. package/dist/chart/components/pan/chart-pan.component.js +17 -1
  25. package/dist/chart/components/pane/pane.component.js +3 -1
  26. package/dist/chart/drawers/data-series-drawers/trend-points.drawer.d.ts +12 -0
  27. package/dist/chart/drawers/data-series-drawers/trend-points.drawer.js +70 -0
  28. package/dist/chart/drawers/drawing-manager.js +0 -1
  29. package/dist/chart/inputhandlers/chart-resize.handler.d.ts +5 -0
  30. package/dist/chart/inputhandlers/chart-resize.handler.js +9 -1
  31. package/dist/chart/inputlisteners/canvas-input-listener.component.d.ts +28 -0
  32. package/dist/chart/inputlisteners/canvas-input-listener.component.js +104 -7
  33. package/dist/chart/model/data-series.config.d.ts +1 -1
  34. package/dist/chart/model/hit-test-canvas.model.d.ts +15 -13
  35. package/dist/chart/model/hit-test-canvas.model.js +2 -2
  36. package/dist/chart/model/scale.model.d.ts +5 -0
  37. package/dist/chart/model/scale.model.js +72 -6
  38. package/dist/chart/utils/device/touchpad.utils.d.ts +1 -0
  39. package/dist/chart/utils/device/touchpad.utils.js +1 -1
  40. package/dist/chart/utils/math.utils.js +1 -1
  41. package/dist/chart/utils/performance/queue-microtask.utils.d.ts +5 -0
  42. package/dist/chart/utils/performance/queue-microtask.utils.js +12 -0
  43. package/dist/chart/utils/performance/request-animation-frame-throttle.utils.d.ts +1 -0
  44. package/dist/chart/utils/performance/request-animation-frame-throttle.utils.js +3 -0
  45. package/dist/chart/utils/performance/safari/components/animations/safari-canvas-animation.d.ts +77 -0
  46. package/dist/chart/utils/performance/safari/components/animations/safari-canvas-animation.js +137 -0
  47. package/dist/chart/utils/performance/safari/components/chart-area-pan.handler/safari-chart-area-pan.handler.d.ts +54 -0
  48. package/dist/chart/utils/performance/safari/components/chart-area-pan.handler/safari-chart-area-pan.handler.js +180 -0
  49. package/dist/chart/utils/performance/safari/safari-performance.model.d.ts +32 -0
  50. package/dist/chart/utils/performance/safari/safari-performance.model.js +31 -0
  51. package/dist/chart/utils/performance/safari/safari-performance.utils.d.ts +18 -0
  52. package/dist/chart/utils/performance/safari/safari-performance.utils.js +34 -0
  53. package/dist/dxchart.min.js +4 -4
  54. 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
- this.chartAreaPanHandler = new ChartAreaPanHandler(this.eventBus, this.config, this.mainScale, this.canvasInputListener, this.canvasBoundsContainer, this.canvasAnimation, this, this.hitTestCanvasModel);
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 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);
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.previousBCR !== undefined;
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
- this.documentDragListeners.forEach(unsub => unsub());
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.documentDragListeners.forEach(unsub => unsub());
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
- const bcr = this.element.getBoundingClientRect();
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
- // this is a bad solution because BCR causes reflow
331
- // it's ok while document is not changed, styles and layout
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
- private canvasInputListener;
24
- private canvasBoundsContainer;
25
- private hitTestSubscribers;
26
- private eventsSubscriptions;
27
- private hoverSubject;
28
- private touchStartSubject;
29
- private dblClickSubject;
30
- private rightClickSubject;
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
- private curImgData;
84
- private prevAnimationFrameId;
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
- private getPixel;
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
- private eventHandler;
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.