@devexperts/dxcharts-lite 2.7.29 → 2.7.30

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.
@@ -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 ? this.chartModel.toX((_a = this.chartModel.candleFromX(hover.x, true).idx) !== null && _a !== void 0 ? _a : 0) : hover.x;
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
  }
@@ -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) => (Object.assign(Object.assign({}, label), {
170
- // save original y
171
- lineY: label.y, y: points[idx] }))) });
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';
@@ -130,6 +130,10 @@ export declare class HoverProducerComponent extends ChartBaseElement {
130
130
  */
131
131
  fireLastCross(): void;
132
132
  setLongTouchCrosshairSuppressed(value: boolean): void;
133
+ /**
134
+ * Resets the current crosshair/touch state back to the default mobile idle state.
135
+ */
136
+ resetCrossTool(clearMobile?: boolean): void;
133
137
  /**
134
138
  * Registers a hover producer part with the given id.
135
139
  *
@@ -134,7 +134,7 @@ export class HoverProducerComponent extends ChartBaseElement {
134
134
  this.paneManager.chartPanComponent.setChartPanningOptions(false, false);
135
135
  }));
136
136
  this.addRxSubscription(this.canvasInputListener.observeTouchEndDocument().subscribe(event => {
137
- var _a, _b;
137
+ var _a, _b, _c, _d, _e, _f, _g, _h;
138
138
  const { clientX, clientY } = event.changedTouches[0];
139
139
  const { fixed, temp } = this.crossEventProducer.crossToolTouchInfo;
140
140
  const x = clientX - this.canvasBoundsContainer.canvasOnPageLocation.x;
@@ -142,28 +142,32 @@ export class HoverProducerComponent extends ChartBaseElement {
142
142
  // common tap without moving, hide crosstool
143
143
  if (this.crossEventProducer.crossToolTouchInfo.isCommonTap &&
144
144
  !checkChartIsMoving(x, temp.x, y, temp.y)) {
145
- this.paneManager.chartPanComponent.setChartPanningOptions(true, true);
146
- this.longTouchActivatedSubject.next(false);
147
- this.crossEventProducer.fireCrossClose();
148
- this.crossEventProducer.crossToolHover = null;
149
- this.crossEventProducer.crossToolTouchInfo.isSet = false;
145
+ this.resetCrossTool();
150
146
  return;
151
147
  }
152
148
  if (!this.crossEventProducer.crossToolTouchInfo.isSet) {
153
149
  this.crossEventProducer.crossToolTouchInfo.isSet = true;
154
- // previous fixed goes to temporary to prevent crosstool jumps outside the candle on candle update tick
150
+ const crossToolHover = this.crossEventProducer.crossToolHover;
151
+ const anchorX = (_a = crossToolHover === null || crossToolHover === void 0 ? void 0 : crossToolHover.x) !== null && _a !== void 0 ? _a : x;
152
+ const anchorY = (_b = crossToolHover === null || crossToolHover === void 0 ? void 0 : crossToolHover.y) !== null && _b !== void 0 ? _b : y;
155
153
  this.crossEventProducer.crossToolTouchInfo.temp = {
156
- x: this.crossEventProducer.crossToolTouchInfo.fixed.x,
157
- y: this.crossEventProducer.crossToolTouchInfo.fixed.y,
154
+ x: anchorX,
155
+ y: anchorY,
158
156
  };
159
157
  this.crossEventProducer.crossToolTouchInfo.fixed = {
160
- x,
161
- y,
158
+ x: anchorX,
159
+ y: anchorY,
162
160
  };
163
- this.crossEventProducer.crossToolTouchInfo.isSet = true;
161
+ const paneId = (_e = (_c = crossToolHover === null || crossToolHover === void 0 ? void 0 : crossToolHover.paneId) !== null && _c !== void 0 ? _c : (_d = this.paneManager.getPaneIfHit({ x: anchorX, y: anchorY })) === null || _d === void 0 ? void 0 : _d.uuid) !== null && _e !== void 0 ? _e : '';
162
+ const hover = crossToolHover !== null && crossToolHover !== void 0 ? crossToolHover : this.hover;
163
+ if (!hover) {
164
+ return;
165
+ }
166
+ this.crossEventProducer.crossToolHover = Object.assign(Object.assign({}, hover), { x: anchorX, y: anchorY, paneId });
167
+ this.crossEventProducer.crossSubject.next([anchorX, anchorY, paneId]);
164
168
  }
165
169
  else {
166
- const pane = (_b = (_a = this.crossEventProducer.crossToolHover) === null || _a === void 0 ? void 0 : _a.paneId) !== null && _b !== void 0 ? _b : 'CHART';
170
+ const pane = (_g = (_f = this.crossEventProducer.crossToolHover) === null || _f === void 0 ? void 0 : _f.paneId) !== null && _g !== void 0 ? _g : 'CHART';
167
171
  const paneBounds = this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(pane));
168
172
  const paneYStart = paneBounds.y + 5;
169
173
  const paneYEnd = paneBounds.y + paneBounds.height - 5;
@@ -172,6 +176,13 @@ export class HoverProducerComponent extends ChartBaseElement {
172
176
  const newX = fixed.x < 0 ? 0 : fixed.x > paneBounds.width ? paneBounds.width : (fixed.x += xDiff);
173
177
  const newY = fixed.y < paneYStart ? paneYStart : fixed.y > paneYEnd ? paneYEnd : (fixed.y += yDiff);
174
178
  this.crossEventProducer.crossToolTouchInfo.fixed = { x: newX, y: newY };
179
+ this.crossEventProducer.crossToolTouchInfo.temp = { x: newX, y: newY };
180
+ const hover = (_h = this.crossEventProducer.crossToolHover) !== null && _h !== void 0 ? _h : this.hover;
181
+ if (!hover) {
182
+ return;
183
+ }
184
+ this.crossEventProducer.crossToolHover = Object.assign(Object.assign({}, hover), { x: newX, y: newY, paneId: pane });
185
+ this.crossEventProducer.crossSubject.next([newX, newY, pane]);
175
186
  }
176
187
  }));
177
188
  //#endregion
@@ -236,9 +247,16 @@ export class HoverProducerComponent extends ChartBaseElement {
236
247
  * @param {VisualCandle} candle - The VisualCandle object to create the hover from.
237
248
  */
238
249
  updateHover(candle) {
250
+ var _a, _b;
239
251
  const updatedHover = this.createHoverFromCandle(candle);
240
252
  if (this.hover && updatedHover) {
241
- const hover = Object.assign(Object.assign({}, updatedHover), { x: this.hover.x, y: this.hover.y });
253
+ const shouldKeepSetTouchCrosshair = isMobile() &&
254
+ this.longTouchActivatedSubject.getValue() &&
255
+ this.crossEventProducer.crossSubject.getValue() !== null;
256
+ const crossToolHover = this.crossEventProducer.crossToolHover;
257
+ const hoverX = shouldKeepSetTouchCrosshair ? ((_a = crossToolHover === null || crossToolHover === void 0 ? void 0 : crossToolHover.x) !== null && _a !== void 0 ? _a : this.hover.x) : this.hover.x;
258
+ const hoverY = shouldKeepSetTouchCrosshair ? ((_b = crossToolHover === null || crossToolHover === void 0 ? void 0 : crossToolHover.y) !== null && _b !== void 0 ? _b : this.hover.y) : this.hover.y;
259
+ const hover = Object.assign(Object.assign({}, updatedHover), { x: hoverX, y: hoverY });
242
260
  this.fireHover(hover);
243
261
  }
244
262
  }
@@ -293,6 +311,21 @@ export class HoverProducerComponent extends ChartBaseElement {
293
311
  setLongTouchCrosshairSuppressed(value) {
294
312
  this.longTouchCrosshairSuppressed = value;
295
313
  }
314
+ /**
315
+ * Resets the current crosshair/touch state back to the default mobile idle state.
316
+ */
317
+ resetCrossTool(clearMobile = false) {
318
+ this.paneManager.chartPanComponent.setChartPanningOptions(true, true);
319
+ this.longTouchActivatedSubject.next(false);
320
+ this.crossEventProducer.fireCrossClose();
321
+ this.crossEventProducer.crossToolHover = null;
322
+ this.crossEventProducer.crossToolTouchInfo.isSet = false;
323
+ if (clearMobile) {
324
+ this.crossEventProducer.crossToolTouchInfo.isCommonTap = false;
325
+ this.crossEventProducer.crossToolTouchInfo.fixed = { x: 0, y: 0 };
326
+ this.crossEventProducer.crossToolTouchInfo.temp = { x: 0, y: 0 };
327
+ }
328
+ }
296
329
  /**
297
330
  * Registers a hover producer part with the given id.
298
331
  *
@@ -7,7 +7,7 @@ import { Subject } from 'rxjs';
7
7
  import { ChartConfigComponentsOffsets, ChartScale, FullChartConfig } from '../chart.config';
8
8
  import { CanvasAnimation } from '../animation/canvas-animation';
9
9
  import { AutoScaleViewportSubModel } from './scaling/auto-scale.model';
10
- import { Price, Unit, ViewportModel, ViewportModelState, Zoom } from './scaling/viewport.model';
10
+ import { Price, Unit, ViewportModel, ViewportModelState, XScale, YScale, Zoom } from './scaling/viewport.model';
11
11
  import { BoundsProvider } from './bounds.model';
12
12
  import VisualCandle from './visual-candle';
13
13
  export interface HighLowWithIndex {
@@ -16,11 +16,7 @@ export interface HighLowWithIndex {
16
16
  highIdx: number;
17
17
  lowIdx: number;
18
18
  }
19
- export interface ScaleHistoryItem {
20
- xStart: Unit;
21
- xEnd: Unit;
22
- yStart: Unit;
23
- yEnd: Unit;
19
+ export interface ScaleHistoryItem extends XScale, YScale {
24
20
  }
25
21
  export declare const getDefaultHighLowWithIndex: () => HighLowWithIndex;
26
22
  export type ViewportPercent = number;
@@ -57,6 +57,8 @@ export interface ViewportModelState {
57
57
  zoomY: Zoom;
58
58
  inverseY: boolean;
59
59
  }
60
+ export type XScale = Pick<ViewportModelState, 'xStart' | 'xEnd'>;
61
+ export type YScale = Pick<ViewportModelState, 'yStart' | 'yEnd'>;
60
62
  /**
61
63
  * Abstract viewport model.
62
64
  * Viewport has 4 coordinates: xStart, xEnd, yStart and yEnd - all stored in {@link Unit}.
@@ -0,0 +1,6 @@
1
+ /*
2
+ * Copyright (C) 2019 - 2026 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
+ export {};
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright (C) 2019 - 2026 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 { isCanvasPointInLowerHoverStrip } from '../canvas-lower-hover-strip.utils';
7
+ const baseBounds = () => ({
8
+ x: 0,
9
+ y: 0,
10
+ pageX: 0,
11
+ pageY: 0,
12
+ width: 1000,
13
+ height: 1000,
14
+ });
15
+ describe('canvas-lower-hover-strip.utils', () => {
16
+ it('returns true for a point in the lower strip (bottom offset 0)', () => {
17
+ const b = baseBounds();
18
+ expect(isCanvasPointInLowerHoverStrip({ x: 500, y: 900 }, b, 0)).toBe(true);
19
+ });
20
+ it('returns false for a point in the upper area', () => {
21
+ const b = baseBounds();
22
+ expect(isCanvasPointInLowerHoverStrip({ x: 500, y: 100 }, b, 0)).toBe(false);
23
+ });
24
+ it('shifts the strip when bottomOffsetPx is non-zero', () => {
25
+ const b = baseBounds();
26
+ const yAtStripStartNoOffset = b.height * 0.85 + 10;
27
+ expect(isCanvasPointInLowerHoverStrip({ x: 1, y: yAtStripStartNoOffset - 1 }, b, 0)).toBe(false);
28
+ expect(isCanvasPointInLowerHoverStrip({ x: 1, y: yAtStripStartNoOffset }, b, 0)).toBe(true);
29
+ const bottomOffset = 50;
30
+ const yAtStripStartWithOffset = b.height * 0.85 - bottomOffset + 10;
31
+ expect(isCanvasPointInLowerHoverStrip({ x: 1, y: yAtStripStartWithOffset - 1 }, b, bottomOffset)).toBe(false);
32
+ expect(isCanvasPointInLowerHoverStrip({ x: 1, y: yAtStripStartWithOffset }, b, bottomOffset)).toBe(true);
33
+ });
34
+ });
@@ -46,7 +46,7 @@ export declare function drawPriceLabel(ctx: CanvasRenderingContext2D, x0: number
46
46
  *
47
47
  * @returns {void}
48
48
  */
49
- export declare function drawLine(ctx: CanvasRenderingContext2D, x0: number, y0: number, x1: number, y1: number, thickness?: number, lineDash?: Array<number>): void;
49
+ export declare function drawLine(ctx: CanvasRenderingContext2D, x0: number, y0: number, x1: number, y1: number, thickness?: number, lineDash?: Array<number>, lineCap?: CanvasLineCap): void;
50
50
  /**
51
51
  * Try to avoid anti-aliasing
52
52
  */
@@ -109,10 +109,13 @@ export function drawPriceLabel(ctx, x0, y0, x1, y1, x2, y2, _width, rounded, ali
109
109
  *
110
110
  * @returns {void}
111
111
  */
112
- export function drawLine(ctx, x0, y0, x1, y1, thickness = 1, lineDash = []) {
112
+ export function drawLine(ctx, x0, y0, x1, y1, thickness = 1, lineDash = [], lineCap) {
113
113
  ctx.save();
114
114
  ctx.setLineDash(lineDash);
115
115
  ctx.lineWidth = thickness;
116
+ if (lineCap !== undefined) {
117
+ ctx.lineCap = lineCap;
118
+ }
116
119
  ctx.beginPath();
117
120
  ctx.moveTo(x0, y0);
118
121
  ctx.lineTo(x1, y1);
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright (C) 2019 - 2026 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 { Bounds } from '../model/bounds.model';
7
+ export interface CanvasPointerPoint {
8
+ readonly x: number;
9
+ readonly y: number;
10
+ }
11
+ /**
12
+ * Canvas-space hit test for the lower band (time scale / volume strip).
13
+ * Reusable for any UI that should react when the pointer is near the bottom of the chart.
14
+ *
15
+ * Rectangle matches the Figma hover region (full width, lower ~15% of canvas height plus offsets).
16
+ */
17
+ export declare function isCanvasPointInLowerHoverStrip(point: CanvasPointerPoint, canvasBounds: Bounds, bottomOffsetPx: number): boolean;
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Copyright (C) 2019 - 2026 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
+ /**
7
+ * Canvas-space hit test for the lower band (time scale / volume strip).
8
+ * Reusable for any UI that should react when the pointer is near the bottom of the chart.
9
+ *
10
+ * Rectangle matches the Figma hover region (full width, lower ~15% of canvas height plus offsets).
11
+ */
12
+ export function isCanvasPointInLowerHoverStrip(point, canvasBounds, bottomOffsetPx) {
13
+ const rectX = 0;
14
+ const rectY = canvasBounds.height * 0.85 - bottomOffsetPx + 10;
15
+ const rectWidth = canvasBounds.width;
16
+ const rectHeight = canvasBounds.height * 0.15 + bottomOffsetPx + 10;
17
+ const dx = point.x - rectX;
18
+ const dy = point.y - rectY;
19
+ return dx >= 0 && dx < rectWidth && dy >= 0 && dy < rectHeight;
20
+ }