@devexperts/dxcharts-lite 2.7.25 → 2.7.27

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/README.md CHANGED
@@ -39,15 +39,15 @@
39
39
 
40
40
  Get started with DXCharts Lite by installing the library and configuring it to your needs.
41
41
 
42
- - 🚀 [Quick start](#quick-start)
43
- - 📦 [Installation](#installation)
44
- - 🛠️ [Create Chart](#create-chart)
45
- - 📈 [Set data](#set-data)
46
- - 📄 [HTML markup](#html-markup)
47
- - ⚙️ [Configuration](#configuration)
48
- - 🔌 [API](#api)
49
- - 📒 [Examples](#examples)
50
- - 🧮 [Demo](#demo)
42
+ - 🚀 [Quick start](#quick-start)
43
+ - 📦 [Installation](#installation)
44
+ - 🛠️ [Create Chart](#create-chart)
45
+ - 📈 [Set data](#set-data)
46
+ - 📄 [HTML markup](#html-markup)
47
+ - ⚙️ [Configuration](#configuration)
48
+ - 🔌 [API](#api)
49
+ - 📒 [Examples](#examples)
50
+ - 🧮 [Demo](#demo)
51
51
 
52
52
  ## Quick-start
53
53
 
@@ -84,8 +84,8 @@ The `createChart` method creates a new chart instance using `ChartBootstrap` cla
84
84
 
85
85
  It accepts two parameters:
86
86
 
87
- - `element`: The HTML element where the chart will be rendered.
88
- - `config` (optional): An instance of [ChartConfig](/chart/chart-config/overview).
87
+ - `element`: The HTML element where the chart will be rendered.
88
+ - `config` (optional): An instance of [ChartConfig](/chart/chart-config/overview).
89
89
 
90
90
  > Note: Set `width: 100%` and `height: 100%` for the parent container.
91
91
  > By default, the chart auto-resizes to the parent, but you can change this by setting `fixedSize` in the config.
@@ -149,7 +149,7 @@ You can configure the chart using a `ChartConfig` object (optional).
149
149
  `ChartConfig` sets the initial properties for the chart instance.
150
150
  Below is a table of available options (see documentation for details).
151
151
 
152
- - [`FullChartConfig`](docs/generated/FullChartConfig.md)
152
+ - [`FullChartConfig`](docs/generated/FullChartConfig.md)
153
153
 
154
154
  ## API
155
155
 
@@ -157,18 +157,18 @@ Each chart component has an API that allows you to change many parameters on the
157
157
 
158
158
  Main components include:
159
159
 
160
- - [`ChartComponent`](docs/generated/ChartComponent.md): Set data, subscribe to data changes
161
- - [`XAxisComponent`](docs/generated/XAxisComponent.md): Configure x-axis labels
162
- - [`YAxisComponent`](docs/generated/YAxisComponent.md): Configure y-axis labels and type
163
- - [`CrossToolComponent`](docs/generated/CrossToolComponent.md): Crosshair type, OHLC magnet
164
- - [`EventsComponent`](docs/generated/EventsComponent.md): Set events data
165
- - [`VolumesComponent`](docs/generated/VolumesComponent.md): Set volume data
166
- - [`WaterMarkComponent`](docs/generated/WaterMarkComponent.md): Create and manage a watermark
167
- - [`NavigationMapComponent`](docs/generated/NavigationMapComponent.md): Create a navigation map
168
- - [`SnapshotComponent`](docs/generated/SnapshotComponent.md): Create a snapshot
169
- - [`HighlightsComponent`](docs/generated/HighlightsComponent.md): Draw highlights on canvas objects such as labels
170
- - [`PaneManager`](docs/generated/PaneManager.md): Create and manage panes
171
- - [`PaneComponent`](docs/generated/PaneComponent.md): Create and manage pane data series
160
+ - [`ChartComponent`](docs/generated/ChartComponent.md): Set data, subscribe to data changes
161
+ - [`XAxisComponent`](docs/generated/XAxisComponent.md): Configure x-axis labels
162
+ - [`YAxisComponent`](docs/generated/YAxisComponent.md): Configure y-axis labels and type
163
+ - [`CrossToolComponent`](docs/generated/CrossToolComponent.md): Crosshair type, OHLC magnet
164
+ - [`EventsComponent`](docs/generated/EventsComponent.md): Set events data
165
+ - [`VolumesComponent`](docs/generated/VolumesComponent.md): Set volume data
166
+ - [`WaterMarkComponent`](docs/generated/WaterMarkComponent.md): Create and manage a watermark
167
+ - [`NavigationMapComponent`](docs/generated/NavigationMapComponent.md): Create a navigation map
168
+ - [`SnapshotComponent`](docs/generated/SnapshotComponent.md): Create a snapshot
169
+ - [`HighlightsComponent`](docs/generated/HighlightsComponent.md): Draw highlights on canvas objects such as labels
170
+ - [`PaneManager`](docs/generated/PaneManager.md): Create and manage panes
171
+ - [`PaneComponent`](docs/generated/PaneComponent.md): Create and manage pane data series
172
172
 
173
173
  ## Examples
174
174
 
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { DEFAULT_ANIMATION_TIME_MS, VIEWPORT_ANIMATION_ID } from './canvas-animation';
7
7
  const VIEWPORT_ANIMATION_DURATION_MS = DEFAULT_ANIMATION_TIME_MS;
8
- const STUCK_ANIMATION_THRESHOLD = 0.90; // Force finish animations stuck at 90%+
8
+ const STUCK_ANIMATION_THRESHOLD = 0.9; // Force finish animations stuck at 90%+
9
9
  /**
10
10
  * Starts the animation for chart viewport movement for safari.
11
11
  * @param canvasAnimation
@@ -545,7 +545,7 @@ export class CanvasBoundsContainer {
545
545
  const nMap = this.getBounds(CanvasElement.N_MAP);
546
546
  const { height, width } = this.config.components.navigationMap.knots;
547
547
  const knotHeightFromConfig = height !== null && height !== void 0 ? height : 0;
548
- const knotWidthFromConfig = isMobile() ? width * KNOTS_W_MOBILE_MULTIPLIER : width !== null && width !== void 0 ? width : 0;
548
+ const knotWidthFromConfig = isMobile() ? width * KNOTS_W_MOBILE_MULTIPLIER : (width !== null && width !== void 0 ? width : 0);
549
549
  const knotY = !knotHeightFromConfig ? nMap.y : nMap.y + (nMap.height - knotHeightFromConfig) / 2;
550
550
  // time labels
551
551
  const timeLabelsVisible = (_b = (_a = this.config.components.navigationMap) === null || _a === void 0 ? void 0 : _a.timeLabels) === null || _b === void 0 ? void 0 : _b.visible;
@@ -13,6 +13,7 @@ import { deviceDetector } from '../../utils/device/device-detector.utils';
13
13
  import { getTouchpadSensitivity } from '../../utils/device/touchpad.utils';
14
14
  import { DragNDropXComponent } from '../dran-n-drop_helper/drag-n-drop-x.component';
15
15
  import { DragNDropYComponent } from '../dran-n-drop_helper/drag-n-drop-y.component';
16
+ import { ONE_FRAME_MS } from '../../utils/numeric-constants.utils';
16
17
  /**
17
18
  * ChartAreaPanHandler is a class that handles the panning and zooming of the chart area.
18
19
  * It extends the ChartBaseElement class and has the following properties:
@@ -45,7 +46,7 @@ export class ChartAreaPanHandler extends ChartBaseElement {
45
46
  // number of candles delta changed during X dragging: 1, 5 or -3 for ex.
46
47
  this.xDraggedCandlesDelta = 0;
47
48
  this.lastXStart = 0;
48
- this.wheelThrottleTime = 15; // in ms
49
+ this.wheelThrottleTime = ONE_FRAME_MS;
49
50
  this.chartPanningOptions = {
50
51
  horizontal: true,
51
52
  vertical: true,
@@ -3,7 +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 { treasuryPriceFormatter, parseTreasuryPrice, isTreasuryPriceFormat, TREASURY_32ND } from '../treasury-price.formatter';
6
+ import { treasuryPriceFormatter, parseTreasuryPrice, isTreasuryPriceFormat, TREASURY_32ND, } from '../treasury-price.formatter';
7
7
  describe('Treasury Price Formatter', () => {
8
8
  describe('treasuryPriceFormatter', () => {
9
9
  it('should format decimal prices to treasury format', () => {
@@ -33,12 +33,12 @@ describe('Treasury Price Formatter', () => {
33
33
  expect(parseTreasuryPrice("1'00")).toBe(1.0);
34
34
  });
35
35
  it('should return NaN for invalid formats', () => {
36
- expect(parseTreasuryPrice("invalid")).toBe(NaN);
36
+ expect(parseTreasuryPrice('invalid')).toBe(NaN);
37
37
  expect(parseTreasuryPrice("132'")).toBe(NaN);
38
38
  expect(parseTreasuryPrice("'10")).toBe(NaN);
39
39
  expect(parseTreasuryPrice("132'1")).toBe(NaN);
40
40
  expect(parseTreasuryPrice("132'100")).toBe(NaN);
41
- expect(parseTreasuryPrice("abc123")).toBe(NaN);
41
+ expect(parseTreasuryPrice('abc123')).toBe(NaN);
42
42
  });
43
43
  });
44
44
  describe('isTreasuryPriceFormat', () => {
@@ -49,8 +49,8 @@ describe('Treasury Price Formatter', () => {
49
49
  expect(isTreasuryPriceFormat("1'31")).toBe(true);
50
50
  });
51
51
  it('should reject invalid formats', () => {
52
- expect(isTreasuryPriceFormat("invalid")).toBe(false);
53
- expect(isTreasuryPriceFormat("132")).toBe(false);
52
+ expect(isTreasuryPriceFormat('invalid')).toBe(false);
53
+ expect(isTreasuryPriceFormat('132')).toBe(false);
54
54
  expect(isTreasuryPriceFormat("132'")).toBe(false);
55
55
  expect(isTreasuryPriceFormat("'10")).toBe(false);
56
56
  expect(isTreasuryPriceFormat("132'1")).toBe(false);
@@ -31,6 +31,9 @@ export const createRegularPriceFormatter = (extent, config) => (value) => {
31
31
  return `${checkedValue}`;
32
32
  };
33
33
  export const createPercentFormatter = (extent) => (value, dataSeries) => {
34
+ if (value === 0) {
35
+ return '0.00 %';
36
+ }
34
37
  const [mainDataSeries] = extent.dataSeries;
35
38
  let valueUnit = value;
36
39
  const series = dataSeries !== null && dataSeries !== void 0 ? dataSeries : mainDataSeries;
@@ -40,7 +40,7 @@ export const parseTreasuryPrice = (value) => {
40
40
  const integerPart = parseInt(match[1], 10);
41
41
  const thirtySeconds = parseInt(match[2], 10);
42
42
  // Convert 32nds to decimal
43
- const decimalPart = (thirtySeconds * TREASURY_32ND);
43
+ const decimalPart = thirtySeconds * TREASURY_32ND;
44
44
  return integerPart + decimalPart;
45
45
  }
46
46
  // Return Number(value) for non-treasury formats (this will be NaN for invalid strings)
@@ -8,7 +8,7 @@ import { ChartBaseElement } from '../../model/chart-base-element';
8
8
  import { ChartModel } from '../chart/chart.model';
9
9
  export type HighlightTextPlacement = 'left-left' | 'left-right' | 'right-left' | 'right-right';
10
10
  export declare const HIGHLIGHTS_TYPES: readonly ["AFTER_MARKET", "PRE_MARKET", "NO_TRADING", "REGULAR"];
11
- export type HighlightType = typeof HIGHLIGHTS_TYPES[number];
11
+ export type HighlightType = (typeof HIGHLIGHTS_TYPES)[number];
12
12
  export type VisualHighlights = Partial<Record<HighlightType, Highlight[]>>;
13
13
  export interface Highlight {
14
14
  type: HighlightType;
@@ -140,9 +140,18 @@ export class YExtentComponent extends ChartBaseElement {
140
140
  export const createDefaultYExtentHighLowProvider = (extent) => ({
141
141
  isHighLowActive: () => true,
142
142
  calculateHighLow: state => {
143
- const highLows = Array.from(extent.dataSeries)
144
- .filter(ds => ds.highLowProvider.isHighLowActive())
145
- .map(ds => ds.highLowProvider.calculateHighLow(state));
143
+ const allDataSeries = Array.from(extent.dataSeries);
144
+ const activeDataSeries = allDataSeries.filter(ds => ds.highLowProvider.isHighLowActive());
145
+ const seriesToUse = activeDataSeries.length > 0 ? activeDataSeries : allDataSeries;
146
+ if (seriesToUse.length === 0) {
147
+ return { low: 0, high: 100 };
148
+ }
149
+ const highLows = seriesToUse
150
+ .map(ds => ds.highLowProvider.calculateHighLow(state))
151
+ .filter(hl => hl.high >= hl.low);
152
+ if (highLows.length === 0) {
153
+ return { low: 0, high: 100 };
154
+ }
146
155
  return mergeHighLow(highLows);
147
156
  },
148
157
  });
@@ -29,7 +29,7 @@ export class VolumesComponent extends ChartBaseElement {
29
29
  this.volumesModel = volumesModel;
30
30
  this.addChildEntity(volumesModel);
31
31
  this.separateVolumes = new SeparateVolumesComponent(chartComponent, config, volumesModel, paneManager);
32
- this.volumesDrawer = new VolumesDrawer(config, this.volumesModel, chartComponent.chartModel, () => { var _a, _b; return (this.config.components.volumes.showSeparately ? (_b = (_a = this.separateVolumes.pane) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : scale : scale); }, this.volumesColorByChartTypeMap, () => true);
32
+ this.volumesDrawer = new VolumesDrawer(config, this.volumesModel, chartComponent.chartModel, () => { var _a, _b; return (this.config.components.volumes.showSeparately ? ((_b = (_a = this.separateVolumes.pane) === null || _a === void 0 ? void 0 : _a.scale) !== null && _b !== void 0 ? _b : scale) : scale); }, this.volumesColorByChartTypeMap, () => true);
33
33
  config.components.volumes.visible && this.syncVolumesDynamicObject();
34
34
  this.addChildEntity(this.separateVolumes);
35
35
  this.registerDefaultVolumeColorResolvers();
@@ -7,8 +7,8 @@ declare const weekWeekday: "week-weekday";
7
7
  declare const timeFormatsConfirugable: readonly ["second", "minute", "hour", "day", "month", "year"];
8
8
  declare const timeFormatsNoNConfirugable: readonly ["lessThanSecond"];
9
9
  type TimeFormatWeekWeekdayType = typeof weekWeekday;
10
- type TimeFormatConfirugableType = typeof timeFormatsConfirugable[number];
11
- type TimeFormatNonConfirugableType = typeof timeFormatsNoNConfirugable[number];
10
+ type TimeFormatConfirugableType = (typeof timeFormatsConfirugable)[number];
11
+ type TimeFormatNonConfirugableType = (typeof timeFormatsNoNConfirugable)[number];
12
12
  export type TimeFormat = TimeFormatConfirugableType | TimeFormatNonConfirugableType | TimeFormatWeekWeekdayType;
13
13
  type TimeFormatConfirugableWithDurationType = `${TimeFormatConfirugableType}_${number}` | `${TimeFormatConfirugableType}_${number}!` | `${TimeFormatWeekWeekdayType}_${number | '$'}_${number}`;
14
14
  export type TimeFormatWithDuration = TimeFormatConfirugableWithDurationType | TimeFormatNonConfirugableType;
@@ -35,12 +35,12 @@ export class DifferenceCloudDrawer {
35
35
  if (isDifferenceTool(linkedSeries.config.type) &&
36
36
  isDifferenceTool(model.config.type) &&
37
37
  linkedShouldDrawCloud) {
38
- const differencePoints = [];
39
38
  const mainSeries = model;
40
39
  const allPointsMain = mainSeries.getSeriesInViewport(mainSeries.scale.xStart - 1, mainSeries.scale.xEnd + 1);
41
40
  const allPointsLinked = linkedSeries.getSeriesInViewport(linkedSeries.scale.xStart - 1, linkedSeries.scale.xEnd + 1);
42
41
  allPointsMain.forEach((points, idx) => {
43
42
  var _a, _b;
43
+ const differencePoints = [];
44
44
  const to = Math.min(points.length, (_b = (_a = allPointsLinked[idx]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);
45
45
  for (let k = 0; k < to; k++) {
46
46
  const diffPoints = [
@@ -7,7 +7,7 @@ import EventBus from '../events/event-bus';
7
7
  import { ChartResizeHandler } from '../inputhandlers/chart-resize.handler';
8
8
  export declare const HIT_TEST_PREFIX = "HIT_TEST_";
9
9
  declare const drawerTypes: readonly ["MAIN_BACKGROUND", "MAIN_CLEAR", "HIT_TEST_CLEAR", "YAXIS_CLEAR", "SERIES_CLEAR", "OVER_SERIES_CLEAR", "HIT_TEST_DRAWINGS", "GRID", "X_AXIS", "Y_AXIS", "HIGH_LOW", "DYNAMIC_OBJECTS", "N_MAP_CHART", "PL_CHART", "WATERMARK", "EMPTY_CHART", "OFFLINE_CHART", "LABELS", "EVENTS", "HIT_TEST_EVENTS", "ZERO_LINE", "PL_ZERO_LINE_BACKGROUND", "CROSS_TOOL"];
10
- export type DrawerType = typeof drawerTypes[number];
10
+ export type DrawerType = (typeof drawerTypes)[number];
11
11
  /**
12
12
  * Manages the drawing process.
13
13
  * Can re-order drawers to make one be on top of the other.
@@ -27,6 +27,7 @@ export declare class CanvasModel {
27
27
  height: number;
28
28
  prevHeight: number;
29
29
  prevWidth: number;
30
+ private lastDPR;
30
31
  private readonly _canvasId;
31
32
  type: CanvasBarType;
32
33
  constructor(eventBus: EventBus, canvas: HTMLCanvasElement, drawingManager: DrawingManager, canvasModels: CanvasModel[], resizer?: HTMLElement | undefined, options?: CanvasRenderingContext2DSettings);
@@ -23,6 +23,7 @@ export class CanvasModel {
23
23
  this.height = 0;
24
24
  this.prevHeight = 0;
25
25
  this.prevWidth = 0;
26
+ this.lastDPR = 0;
26
27
  this.type = CANDLE_TYPE;
27
28
  canvasModels.push(this);
28
29
  this.parent = findHeightParent(canvas);
@@ -42,6 +43,10 @@ export class CanvasModel {
42
43
  updateDPR(bcr) {
43
44
  const { width, height } = bcr;
44
45
  const dpi = window.devicePixelRatio;
46
+ if (width === this.width && height === this.height && dpi === this.lastDPR) {
47
+ return;
48
+ }
49
+ this.lastDPR = dpi;
45
50
  this.canvas.style.height = height + 'px';
46
51
  this.canvas.style.width = width + 'px';
47
52
  this.canvas.width = width * dpi;
@@ -303,7 +303,7 @@ export const calculateDataSeriesHighLow = (visualCandles) => {
303
303
  return result;
304
304
  };
305
305
  const createDataSeriesModelHighLowProvider = (dataSeries) => ({
306
- isHighLowActive: () => dataSeries.config.highLowActive,
306
+ isHighLowActive: () => dataSeries.config.highLowActive && dataSeries.config.visible,
307
307
  calculateHighLow: state => {
308
308
  const highLow = calculateDataSeriesHighLow(dataSeries.getSeriesInViewport(state === null || state === void 0 ? void 0 : state.xStart, state === null || state === void 0 ? void 0 : state.xEnd).flat());
309
309
  return Object.assign(Object.assign({}, highLow), { high: dataSeries.view.toAxisUnits(highLow.high), low: dataSeries.view.toAxisUnits(highLow.low) });
@@ -128,13 +128,14 @@ export class ScaleModel extends ViewportModel {
128
128
  * @param zoomSensitivity - The sensitivity of the zoom. Default value is taken from the configuration object.
129
129
  */
130
130
  zoomXToEnd(zoomIn, zoomSensitivity) {
131
- if (this.config.scale.disableAnimations) {
131
+ const disableAnimations = this.config.scale.disableAnimations || this.config.components.yAxis.type === 'percent';
132
+ if (disableAnimations) {
132
133
  this.haltAnimation();
133
134
  }
134
135
  this.beforeStartAnimationSubject.next();
135
136
  const state = this.export();
136
137
  zoomXToEndViewportCalculator(this, state, zoomSensitivity, zoomIn);
137
- this.zoomXTo(state, zoomIn, this.config.scale.disableAnimations);
138
+ this.zoomXTo(state, zoomIn, disableAnimations);
138
139
  }
139
140
  haltAnimation() {
140
141
  var _a;
@@ -232,7 +233,7 @@ export class ScaleModel extends ViewportModel {
232
233
  const vCandles = visualCandleSource.slice(Math.max(visualCandleSource.length - this.state.defaultViewportItems, 0));
233
234
  const endCandle = vCandles[vCandles.length - 1];
234
235
  const xEnd = endCandle.startUnit + endCandle.width + this.offsets.right;
235
- const xStart = xEnd - (this.getBounds().width * this.zoomX);
236
+ const xStart = xEnd - this.getBounds().width * this.zoomX;
236
237
  const state = Object.assign(Object.assign({}, initialStateCopy), { xStart, xEnd });
237
238
  const constrainedState = this.scalePostProcessor(initialStateCopy, state);
238
239
  if (this.state.auto) {
@@ -8,7 +8,7 @@ export declare const MouseButton: {
8
8
  middle: number;
9
9
  right: number;
10
10
  };
11
- export type MouseButtonType = typeof MouseButton[keyof typeof MouseButton];
11
+ export type MouseButtonType = (typeof MouseButton)[keyof typeof MouseButton];
12
12
  /**
13
13
  * @param {Element} element
14
14
  * @param {function} listener