Загрузка данных
diff --git a/src/components/DOM/index.module.scss b/src/components/DOM/index.module.scss
index 8fca17d4858f82d49ae99c5412fe1a582dbadc85..0a2c8ac8b99677777b3457870d727363083538d2 100644
--- a/src/components/DOM/index.module.scss
+++ b/src/components/DOM/index.module.scss
@@ -4,6 +4,13 @@
stroke-opacity: 0.3;
}
+.divider {
+ width: 100%;
+ height: var(--borderWidth-md);
+ margin: auto auto auto 0;
+ background-color: var(--neutral-7);
+}
+
.domRow {
display: flex;
width: 100%;
diff --git a/src/components/DOM/index.tsx b/src/components/DOM/index.tsx
index a6f7a0ee55918b63f744e48dfe5ad5e0b2b509d1..80cdb1b50879d66e5e834c1d3f71ed7adf9e38bc 100644
--- a/src/components/DOM/index.tsx
+++ b/src/components/DOM/index.tsx
@@ -1,4 +1,4 @@
-import { Button } from 'exchange-elements/v2';
+import { Button, Divider } from 'exchange-elements/v2';
import { Observable } from 'rxjs';
@@ -12,7 +12,7 @@ import EyeBrushIcon from '../Icon/EyeBrush';
import styles from './index.module.scss';
interface ObjectTreeProps {
- elementsObs: Observable<IDOMObject[]>;
+ elementsObs: Observable<[number, IDOMObject[]][]>;
}
interface DOMRowProps {
@@ -75,12 +75,24 @@ export function DOM({ elementsObs }: ObjectTreeProps) {
return (
<div>
- {elements?.map((elem) => (
- <DOMRow
- key={elem.id}
- elem={elem}
- />
- ))}
+ {elements.map(([paneId, elems], index) => {
+ return (
+ <>
+ {index !== 0 && (
+ <Divider
+ direction="horizontal"
+ pt={{ divider: { className: styles.divider } }}
+ />
+ )}
+ {elems.map((elem: IDOMObject) => (
+ <DOMRow
+ key={elem.id}
+ elem={elem}
+ />
+ ))}
+ </>
+ );
+ })}
</div>
);
}
diff --git a/src/constants/drawing.ts b/src/constants/drawing.ts
index 044f9d24d05290ff0ed6b160203880f5853d39be..86f887cd05a39abcd71fe30373d2a797d1cf86ff 100644
--- a/src/constants/drawing.ts
+++ b/src/constants/drawing.ts
@@ -12,7 +12,7 @@ import { Text } from '@src/core/Drawings/text';
import { Traectory } from '@src/core/Drawings/traectory';
import { VolumeProfile } from '@src/core/Drawings/volumeProfile';
import { t } from '@src/translations';
-import { DrawingConfig, LineMarker } from '@src/types';
+import { DrawingConfig, DrawingParams, LineMarker } from '@src/types';
export enum DrawingsNames {
'trendLine' = 'trendLine',
@@ -64,24 +64,22 @@ export const drawingLabelById = (): Record<DrawingsNames, string> => ({
export const drawingsMap: Record<DrawingsNames, DrawingConfig> = {
[DrawingsNames.trendLine]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new LineDrawing(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new LineDrawing({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.arrow]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new LineDrawing(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new LineDrawing({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
defaultMarkers: {
endMarker: LineMarker.arrow,
},
@@ -89,197 +87,186 @@ export const drawingsMap: Record<DrawingsNames, DrawingConfig> = {
},
},
[DrawingsNames.parallelChannel]: {
- construct: ({ chart, series, container, eventManager, interaction, openSettings }) => {
- return new ParallelChannel(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new ParallelChannel({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- openSettings,
});
},
},
[DrawingsNames.regressionTrend]: {
- construct: ({ chart, series, container, eventManager, interaction, openSettings }) => {
- return new RegressionTrend(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new RegressionTrend({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- openSettings,
});
},
},
[DrawingsNames.ray]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new Ray(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Ray({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.horizontalLine]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new AxisLine(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new AxisLine({
+ ...rest,
direction: 'horizontal',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.horizontalRay]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new LineDrawing(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new LineDrawing({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
+ defaultMarkers: {
+ endMarker: LineMarker.arrow,
+ },
});
},
},
[DrawingsNames.verticalLine]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new AxisLine(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new AxisLine({
+ ...rest,
direction: 'vertical',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.sliderLong]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new SliderPosition(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new SliderPosition({
+ ...rest,
side: 'long',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.fibonacciRetracement]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new FibonacciRetracement(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new FibonacciRetracement({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.sliderShort]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new SliderPosition(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new SliderPosition({
+ ...rest,
side: 'short',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.diapsonDates]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new Diapson(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Diapson({
+ ...rest,
rangeMode: 'date',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.diapsonPrices]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new Diapson(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Diapson({
+ ...rest,
rangeMode: 'price',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.fixedRangeProfile]: {
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new VolumeProfile(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new VolumeProfile({
+ ...rest,
profileKind: 'fixedRange',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.visibleRangeProfile]: {
singleInstance: true,
- construct: ({ chart, series, container, eventManager, interaction, removeSelf, openSettings }) => {
- return new VolumeProfile(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new VolumeProfile({
+ ...rest,
profileKind: 'visibleRange',
- container,
- interaction,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.rectangle]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new Rectangle(chart, series, {
- container,
- interaction,
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Rectangle({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.ruler]: {
singleInstance: true,
- construct: ({ chart, series, eventManager, container, interaction, removeSelf }) => {
- return new Ruler(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Ruler({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- container,
- interaction,
resetTriggers: [eventManager.getTimeframeObs(), eventManager.getInterval()],
- removeSelf,
});
},
},
[DrawingsNames.traectory]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new Traectory(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Traectory({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- container,
- interaction,
- removeSelf,
- openSettings,
});
},
},
[DrawingsNames.text]: {
- construct: ({ chart, series, eventManager, container, interaction, removeSelf, openSettings }) => {
- return new Text(chart, series, {
+ construct: (params: DrawingParams) => {
+ const { eventManager, ...rest } = params;
+
+ return new Text({
+ ...rest,
formatObservable: eventManager.getChartOptionsModel(),
- container,
- interaction,
- removeSelf,
- openSettings,
});
},
},
diff --git a/src/core/Chart.ts b/src/core/Chart.ts
index 0cdaf1058d4538dd39f45fb5da7bbca7a30fad22..7847be273b69c6603836dfcd9078da5b1368af46 100644
--- a/src/core/Chart.ts
+++ b/src/core/Chart.ts
@@ -20,7 +20,7 @@ import { map, withLatestFrom } from 'rxjs/operators';
import { ChartMouseEvents } from '@core/ChartMouseEvents';
import { DataSource } from '@core/DataSource';
import { DOMModel } from '@core/DOMModel';
-import { DrawingsManager } from '@core/DrawingsManager';
+import { DrawingsManagerCollection } from '@core/DrawingsManagerCollection';
import { EventManager } from '@core/EventManager';
import { Hotkeys } from '@core/Hotkeys';
import { IndicatorManager } from '@core/IndicatorManager';
@@ -237,8 +237,8 @@ export class Chart implements ISerializable<ChartSnapshot> {
}
}
- public getDrawingsManager = (): DrawingsManager => {
- return this.paneManager.getDrawingsManager();
+ public getDrawingsCollectionManager = (): DrawingsManagerCollection => {
+ return this.paneManager.getDrawingsCollectionManager();
};
public getIndicatorManager = (): IndicatorManager => {
diff --git a/src/core/CompareManager.ts b/src/core/CompareManager.ts
index 6c12e9991457f56f8d25bf14030e80f0901fabdc..6bb16f9a47f84e249e749a25156d30022dc8d8b6 100644
--- a/src/core/CompareManager.ts
+++ b/src/core/CompareManager.ts
@@ -146,6 +146,7 @@ export class CompareManager {
series: [
{
...config.series[0],
+ actLikeMainSerie: true,
seriesOptions: {
...config.series[0]?.seriesOptions,
priceScaleId: mode === CompareMode.NewScale ? Direction.Left : Direction.Right,
diff --git a/src/core/DOMModel.tsx b/src/core/DOMModel.tsx
index 236e9ad44263641a4319ad5d66b738073826619b..f289461eca49ad84338f9e4338b24d734b139c4c 100644
--- a/src/core/DOMModel.tsx
+++ b/src/core/DOMModel.tsx
@@ -17,8 +17,9 @@ export class DOMModel {
private modalRenderer: ModalRenderer;
private lastZIndex = 0;
+ // todo: заменить на мапу, где ключами будут id пейнов
private entities: BehaviorSubject<IDOMObject[]> = new BehaviorSubject<IDOMObject[]>([]); // drawings/indicators/series
- // private panes: Panes[]; // todo: пока что на каждый пейн будет один objectTree
+ // private entitiesMap: BehaviorSubject<Map<number, IDOMObject[]>> = new BehaviorSubject<Map<number, IDOMObject[]>>(new Map());
constructor({ modalRenderer }: DOMModelParams) {
this.modalRenderer = modalRenderer;
@@ -73,6 +74,25 @@ export class DOMModel {
this.entities.next(entities.sort((left, right) => left.zIndex - right.zIndex));
}
+ public getEntitiesByPanes = (): Observable<[number, IDOMObject[]][]> => {
+ return this.entities.pipe(
+ map((entities) => {
+ const mapByPanes = new Map();
+ entities
+ .filter((entity) => entity.shouldShowInObjectTree())
+ .forEach((entity) => {
+ if (mapByPanes.has(entity.paneId)) {
+ mapByPanes.set(entity.paneId, [...mapByPanes.get(entity.paneId), entity]);
+ } else {
+ mapByPanes.set(entity.paneId, [entity]);
+ }
+ });
+
+ return Array.from(mapByPanes).sort(([a1, a2], [b1, b2]) => a1 - b1);
+ }),
+ );
+ };
+
public getEntities = (): Observable<IDOMObject[]> => {
return this.entities.pipe(map((entities) => entities.filter((entity) => entity.shouldShowInObjectTree())));
};
@@ -82,7 +102,7 @@ export class DOMModel {
};
public toggleDOM = () => {
- this.modalRenderer.renderComponent(<DOM elementsObs={this.getEntities()} />, {
+ this.modalRenderer.renderComponent(<DOM elementsObs={this.getEntitiesByPanes()} />, {
title: t('DOM tree'),
onSave: () => console.warn('dom state saved'),
acceptLabel: '',
diff --git a/src/core/DOMObject.ts b/src/core/DOMObject.ts
index 5e3c1eea1321a51aa2ed5d24389549e478213b33..080157093c2c6e916f7bbbfd00ecbd8414d9dc0e 100644
--- a/src/core/DOMObject.ts
+++ b/src/core/DOMObject.ts
@@ -1,6 +1,6 @@
import { BehaviorSubject } from 'rxjs';
-import { DOMObjectSnapshot, IndicatorSnapshot, ISerializable } from '@src/types/snapshot';
+import { DOMObjectSnapshot, ISerializable } from '@src/types/snapshot';
enum DOMObjectType {
Drawing = 'Drawing',
@@ -13,6 +13,7 @@ export interface IDOMObject {
zIndex: number;
type: DOMObjectType;
name: string;
+ paneId: number;
delete(): void;
hide(): void;
show(): void;
diff --git a/src/core/Drawings.ts b/src/core/Drawings.ts
index abafeec044b2470a7787b00c176d37e1fb245dfb..47a873bf138401e441898e5dc2667d494cd0f600 100644
--- a/src/core/Drawings.ts
+++ b/src/core/Drawings.ts
@@ -8,7 +8,7 @@ import { DrawingsNames } from '@src/constants';
import { SeriesStrategies } from '@src/modules/series-strategies/SeriesFactory';
import { SettingsTab, SettingsValues, ToolbarSettingField } from '@src/types/settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
+import type { DrawingInteraction, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
interface DrawingParams extends DOMObjectParams {
drawingName: DrawingsNames;
diff --git a/src/core/Drawings/common.ts b/src/core/Drawings/SeriesDrawingBase.ts
similarity index 93%
rename from src/core/Drawings/common.ts
rename to src/core/Drawings/SeriesDrawingBase.ts
index be986da501421b114a9690a893549c2493e7b8ca..00ab6e6c2f29c7f8f5498d4b243b1384e97a9607 100644
--- a/src/core/Drawings/common.ts
+++ b/src/core/Drawings/SeriesDrawingBase.ts
@@ -7,18 +7,20 @@ import {
ISeriesPrimitive,
ISeriesPrimitiveAxisView,
Logical,
+ MouseEventParams,
PrimitiveHoveredItem,
SeriesAttachedParameter,
SeriesOptionsMap,
SeriesType,
Time,
+ TouchMouseEventData,
} from 'lightweight-charts';
import { Observable, Subject, Subscription } from 'rxjs';
import { getPointerPoint as getPointerPointFromEvent } from '@core/Drawings/helpers';
import { AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
-import { SettingsTab, SettingsValues } from '@src/types';
+import { ChartOptionsModel, SettingsTab, SettingsValues } from '@src/types';
export interface DrawingInteraction {
selected$: Observable<boolean>;
@@ -53,6 +55,8 @@ export interface ISeriesDrawing extends ISeriesPrimitive<Time> {
getRenderData(): unknown;
}
+export type StartPoint = unknown;
+
interface SeriesDrawingBaseParams {
container: HTMLElement;
chart: IChartApi;
@@ -60,6 +64,17 @@ interface SeriesDrawingBaseParams {
interaction: DrawingInteraction;
}
+export interface BaseDrawingParams {
+ chart: IChartApi;
+ series: SeriesApi;
+ container: HTMLElement;
+ interaction: DrawingInteraction;
+ formatObservable?: Observable<ChartOptionsModel>;
+ removeSelf?: () => void;
+ openSettings?: () => void;
+ initialEvent?: MouseEventParams;
+}
+
export abstract class SeriesDrawingBase<TSettings extends SettingsValues = SettingsValues> implements ISeriesDrawing {
protected hidden = false;
protected chart: IChartApi;
@@ -242,7 +257,7 @@ export abstract class SeriesDrawingBase<TSettings extends SettingsValues = Setti
});
}
- protected getEventPoint(event: PointerEvent): Point {
+ protected getEventPoint(event: PointerEvent | TouchMouseEventData): Point {
return getPointerPointFromEvent(this.container, event);
}
@@ -281,9 +296,9 @@ export abstract class SeriesDrawingBase<TSettings extends SettingsValues = Setti
// todo: хочется общую реализацию для каждой кнопки
protected handleContextMenu(event: MouseEvent): void {}
protected handleDoubleClick(event: MouseEvent): void {}
- protected handlePointerDown(event: PointerEvent): void {}
protected handlePointerMove(event: PointerEvent): void {}
protected handlePointerUp(event: PointerEvent): void {}
+ protected handlePointerDown(event: PointerEvent | TouchMouseEventData): void {}
protected abstract getHoveredItem(x: number, y: number): PrimitiveHoveredItem | null;
protected abstract getGeometry(): unknown; // todo: make proper type
@@ -316,7 +331,7 @@ export abstract class SeriesDrawingBase<TSettings extends SettingsValues = Setti
);
}
- private handlePointerDownEvent = (event: PointerEvent): void => {
+ protected handlePointerDownEvent = (event: PointerEvent): void => {
if (!this.isLocked() || this.isCreationPending() || event.button !== 0) {
this.handlePointerDown(event);
diff --git a/src/core/Drawings/axisLine/axisLine.ts b/src/core/Drawings/axisLine/axisLine.ts
index c2f20962917b2ec97fe5b1f8e4890b1f6377cfaa..ba1e2969f04a484ecf89ea225d41364ee89c9fb5 100644
--- a/src/core/Drawings/axisLine/axisLine.ts
+++ b/src/core/Drawings/axisLine/axisLine.ts
@@ -1,8 +1,6 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
-import { Observable } from 'rxjs';
+import { IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
import { CustomPriceAxisView, CustomTimeAxisView } from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
getPriceFromYCoordinate,
getTimeFromXCoordinate,
@@ -12,6 +10,7 @@ import {
} from '@core/Drawings/helpers';
import { AxisSegment } from '@core/Drawings/types';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { Defaults } from '@src/types/defaults';
@@ -28,21 +27,16 @@ import {
getAxisLineSettingsTabs,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { AxisLabel, Point, SeriesApi } from '@core/Drawings/types';
+import type { AxisLabel, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
export type AxisLineDirection = 'vertical' | 'horizontal';
type AxisLineMode = 'idle' | 'ready' | 'dragging';
-interface AxisLineParams {
+interface AxisLineParams extends BaseDrawingParams {
direction: AxisLineDirection;
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
}
interface AxisLineState {
@@ -86,11 +80,17 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
private readonly timeAxisView: CustomTimeAxisView;
private readonly priceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { direction, container, interaction, formatObservable, removeSelf, openSettings }: AxisLineParams,
- ) {
+ constructor({
+ chart,
+ series,
+ direction,
+ container,
+ interaction,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ initialEvent,
+ }: AxisLineParams) {
super({ chart, series, container, interaction });
this.direction = direction;
@@ -119,6 +119,11 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
@@ -345,11 +350,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
event.preventDefault();
event.stopPropagation();
- this.updateLine(point);
- this.mode = 'ready';
- this.resolveReady?.();
-
- this.render();
+ this.startDrawing(point);
return;
}
@@ -417,6 +418,14 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
this.render();
};
+ private startDrawing(point: Point): void {
+ this.updateLine(point);
+ this.mode = 'ready';
+ this.resolveReady?.();
+
+ this.render();
+ }
+
private updateLine(point: Point): void {
if (this.direction === 'vertical') {
this.time = getTimeFromXCoordinate(this.chart, point.x);
diff --git a/src/core/Drawings/diapson/diapson.ts b/src/core/Drawings/diapson/diapson.ts
index b241e8dbba56bb72c948e45527ada160c40ad47c..2d703cb91f6ea78d9e35aff3860a13abe7b45c4c 100644
--- a/src/core/Drawings/diapson/diapson.ts
+++ b/src/core/Drawings/diapson/diapson.ts
@@ -1,5 +1,4 @@
import { clamp } from 'lodash-es';
-import { Observable } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -7,7 +6,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
clampPointToContainer as clampPointToContainerInElement,
getAnchorFromPoint,
@@ -18,6 +16,7 @@ import {
isPointInBounds,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { t } from '@src/translations';
@@ -35,10 +34,10 @@ import {
getDiapsonSettingsTabs,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
+import type { Anchor, AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
-import type { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
+import type { IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
export type DiapsonRangeMode = 'date' | 'price';
@@ -47,13 +46,8 @@ type DiapsonHandle = 'body' | 'start' | 'end' | null;
type TimeLabelKind = 'left' | 'right';
type PriceLabelKind = 'top' | 'bottom';
-interface DiapsonParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
+interface DiapsonParams extends BaseDrawingParams {
rangeMode: DiapsonRangeMode;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
stepSize?: number;
stepLabel?: string;
}
@@ -140,16 +134,26 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
private readonly topPriceAxisView: CustomPriceAxisView;
private readonly bottomPriceAxisView: CustomPriceAxisView;
- constructor(chart: IChartApi, series: SeriesApi, params: DiapsonParams) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ rangeMode,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ stepSize = 1,
+ stepLabel = '',
+ initialEvent,
+ }: DiapsonParams) {
super({
chart,
series,
- container: params.container,
- interaction: params.interaction,
+ container,
+ interaction,
});
- const { rangeMode, formatObservable, removeSelf, openSettings, stepSize = 1, stepLabel = '' } = params;
-
this.rangeMode = rangeMode;
this.removeSelf = removeSelf;
this.openSettings = openSettings;
@@ -196,6 +200,11 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts b/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts
index 44ab2bc3fa7e170071990cb5aeba9a682eda1c71..cf12fe3039726b6e57ae1706e8a5ae75175c1a06 100644
--- a/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts
+++ b/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts
@@ -1,5 +1,4 @@
import { clamp } from 'lodash-es';
-import { Observable } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -7,7 +6,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
clampPointToContainer as clampPointToContainerInElement,
getAnchorFromPoint,
@@ -20,6 +18,7 @@ import {
shiftTimeByPixels,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { Defaults } from '@src/types/defaults';
@@ -38,10 +37,10 @@ import {
mergeFibonacciRetracementSettings,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { AxisLabel, AxisSegment, Bounds, Point, SeriesApi } from '@core/Drawings/types';
+import type { AxisLabel, AxisSegment, Bounds, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab, SettingsValues } from '@src/types';
-import type { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
+import type { IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
type FibonacciRetracementMode = 'idle' | 'drawing' | 'ready' | 'dragging';
type FibonacciRetracementHandle = 'body' | 'start' | 'end' | null;
@@ -49,13 +48,7 @@ type FibonacciRetracementHandleKey = Exclude<FibonacciRetracementHandle, 'body'
type TimeLabelKind = 'start' | 'end';
type PriceLabelKind = 'top' | 'bottom';
-interface FibonacciRetracementParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
-}
+type FibonacciRetracementParams = BaseDrawingParams
interface FibonacciRetracementState {
hidden: boolean;
@@ -144,11 +137,16 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
private readonly topPriceAxisView: CustomPriceAxisView;
private readonly bottomPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { container, interaction, formatObservable, removeSelf, openSettings }: FibonacciRetracementParams,
- ) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ initialEvent,
+ }: FibonacciRetracementParams) {
super({ chart, series, container, interaction });
this.removeSelf = removeSelf;
@@ -194,6 +192,11 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/helpers.ts b/src/core/Drawings/helpers.ts
index 951ca6de4f377e109e84ea43c65e1119d0fd141a..8ba05801913c2b90080912f718892932fe4330f1 100644
--- a/src/core/Drawings/helpers.ts
+++ b/src/core/Drawings/helpers.ts
@@ -2,7 +2,7 @@ import { clamp } from 'lodash-es';
import type { Anchor, Bounds, ContainerSize, Point, SeriesApi } from './types';
-import type { Coordinate, IChartApi, Logical, Time } from 'lightweight-charts';
+import type { Coordinate, IChartApi, Logical, Time, TouchMouseEventData } from 'lightweight-charts';
interface SeriesTimeItem {
time: Time;
@@ -69,7 +69,7 @@ export function clampPointToContainer(point: Point, container: HTMLElement): Poi
};
}
-export function getPointerPoint(container: HTMLElement, event: PointerEvent): Point {
+export function getPointerPoint(container: HTMLElement, event: PointerEvent | TouchMouseEventData): Point {
const rect = container.getBoundingClientRect();
return clampPointToContainer(
diff --git a/src/core/Drawings/line/lineDrawing.ts b/src/core/Drawings/line/lineDrawing.ts
index 66f0a45b58b4256ca8e4f5727780f5b6109f3a09..52331ea34f722940589d66cd2553c468cc76d2ea 100644
--- a/src/core/Drawings/line/lineDrawing.ts
+++ b/src/core/Drawings/line/lineDrawing.ts
@@ -1,5 +1,4 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, UTCTimestamp } from 'lightweight-charts';
-import { Observable } from 'rxjs';
+import { IPrimitivePaneView, PrimitiveHoveredItem, UTCTimestamp } from 'lightweight-charts';
import {
CustomPriceAxisPaneView,
@@ -7,7 +6,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
getAnchorFromPoint,
getPriceDelta as getPriceDeltaFromCoordinates,
@@ -17,6 +15,7 @@ import {
shiftTimeByPixels,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { type ChartOptionsModel, LineMarker, type SettingsTab } from '@src/types';
@@ -34,19 +33,14 @@ import {
LineDrawingTextStyle,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
+import type { Anchor, AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
type LineDrawingMode = 'idle' | 'drawing' | 'ready' | 'dragging-start' | 'dragging-end' | 'dragging-body';
type TimeLabelKind = 'start' | 'end';
type PriceLabelKind = 'start' | 'end';
-interface LineDrawingParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
+interface LineDrawingParams extends BaseDrawingParams {
defaultMarkers?: Partial<LineDrawingMarkers>;
}
@@ -103,11 +97,17 @@ export class LineDrawing extends SeriesDrawingBase<LineDrawingSettings> implemen
private readonly startPriceAxisView: CustomPriceAxisView;
private readonly endPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { container, interaction, formatObservable, removeSelf, openSettings, defaultMarkers = {} }: LineDrawingParams,
- ) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ initialEvent,
+ defaultMarkers = {},
+ }: LineDrawingParams) {
super({ chart, series, container, interaction });
this.removeSelf = removeSelf;
@@ -161,6 +161,11 @@ export class LineDrawing extends SeriesDrawingBase<LineDrawingSettings> implemen
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/parallelChannel/parallelChannel.ts b/src/core/Drawings/parallelChannel/parallelChannel.ts
index 9a127b66dd30a87fc50fc49e4891fc165e9d345b..4821ba360df8f70fae02e0e2c93e97711d2d18a3 100644
--- a/src/core/Drawings/parallelChannel/parallelChannel.ts
+++ b/src/core/Drawings/parallelChannel/parallelChannel.ts
@@ -1,4 +1,10 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, UTCTimestamp } from 'lightweight-charts';
+import {
+ IChartApi,
+ IPrimitivePaneView,
+ PrimitiveHoveredItem,
+ TouchMouseEventData,
+ UTCTimestamp,
+} from 'lightweight-charts';
import { Observable } from 'rxjs';
import {
@@ -7,7 +13,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
getAnchorFromPoint,
getPriceDelta as getPriceDeltaFromCoordinates,
@@ -18,6 +23,7 @@ import {
shiftTimeByPixels,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { Defaults } from '@src/types/defaults';
@@ -33,8 +39,8 @@ import {
ParallelChannelTextStyle,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
+import type { Anchor, AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
type ParallelChannelMode = 'idle' | 'drawing-line' | 'drawing-channel' | 'ready' | 'dragging';
@@ -52,12 +58,7 @@ type TimeLabelKind = 'start' | 'end';
type PriceLabelKind = 'main-start' | 'main-end' | 'parallel-start' | 'parallel-end';
-interface ParallelChannelParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- openSettings?: () => void;
-}
+type ParallelChannelParams = BaseDrawingParams
interface ParallelChannelState {
hidden: boolean;
@@ -129,11 +130,15 @@ export class ParallelChannel extends SeriesDrawingBase<ParallelChannelSettings>
private parallelStartPriceAxisView: CustomPriceAxisView;
private parallelEndPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { container, interaction, formatObservable, openSettings }: ParallelChannelParams,
- ) {
+ constructor({
+ container,
+ interaction,
+ formatObservable,
+ openSettings,
+ chart,
+ series,
+ initialEvent,
+ }: ParallelChannelParams) {
super({
chart,
series,
@@ -193,6 +198,11 @@ export class ParallelChannel extends SeriesDrawingBase<ParallelChannelSettings>
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/ray/ray.ts b/src/core/Drawings/ray/ray.ts
index e37a6062b900e56e976ebe8c12eaac26bb86e106..0c5d08e078cdea2b44568a1ad126ec5965512169 100644
--- a/src/core/Drawings/ray/ray.ts
+++ b/src/core/Drawings/ray/ray.ts
@@ -1,5 +1,4 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, UTCTimestamp } from 'lightweight-charts';
-import { Observable } from 'rxjs';
+import { IPrimitivePaneView, PrimitiveHoveredItem, TouchMouseEventData, UTCTimestamp } from 'lightweight-charts';
import {
CustomPriceAxisPaneView,
@@ -7,7 +6,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
getAnchorFromPoint,
getPriceDelta as getPriceDeltaFromCoordinates,
@@ -17,6 +15,7 @@ import {
shiftTimeByPixels,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { Defaults } from '@src/types/defaults';
@@ -27,21 +26,15 @@ import { RayPaneView } from './paneView';
import { createDefaultSettings, getRaySettingTabs, RaySettings, RayStyle, RayTextStyle } from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
+import type { Anchor, AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
type RayMode = 'idle' | 'drawing' | 'ready' | 'dragging-start' | 'dragging-direction' | 'dragging-body';
type TimeLabelKind = 'start' | 'direction';
type PriceLabelKind = 'start' | 'direction';
-interface RayParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
-}
+type RayParams = BaseDrawingParams
interface RayState {
hidden: boolean;
@@ -98,11 +91,16 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
private readonly startPriceAxisView: CustomPriceAxisView;
private readonly directionPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { container, interaction, formatObservable, removeSelf, openSettings }: RayParams,
- ) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ initialEvent,
+ }: RayParams) {
super({ chart, series, container, interaction });
this.removeSelf = removeSelf;
@@ -148,6 +146,11 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/rectangle/rectangle.ts b/src/core/Drawings/rectangle/rectangle.ts
index ff6fa8be2ee99849f75c326ad9be5f17347c581f..1ac1119c84cc7512fb4b3c2e29857baa1a348ba0 100644
--- a/src/core/Drawings/rectangle/rectangle.ts
+++ b/src/core/Drawings/rectangle/rectangle.ts
@@ -1,5 +1,4 @@
import { clamp } from 'lodash-es';
-import { Observable } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -7,7 +6,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
clampPointToContainer as clampPointToContainerInElement,
getAnchorFromPoint,
@@ -23,6 +21,7 @@ import {
shiftTimeByPixels,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { Defaults } from '@src/types/defaults';
@@ -38,10 +37,10 @@ import {
RectangleTextStyle,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
+import type { AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
-import type { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
+import type { IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
type RectangleMode = 'idle' | 'drawing' | 'ready' | 'dragging';
type RectangleHandle = 'body' | 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | null;
@@ -49,13 +48,7 @@ type RectangleHandleKey = Exclude<RectangleHandle, 'body' | null>;
type TimeLabelKind = 'left' | 'right';
type PriceLabelKind = 'top' | 'bottom';
-interface RectangleParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
-}
+type RectangleParams = BaseDrawingParams
interface RectangleState {
hidden: boolean;
@@ -119,11 +112,16 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
private readonly topPriceAxisView: CustomPriceAxisView;
private readonly bottomPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { container, interaction, formatObservable, removeSelf, openSettings }: RectangleParams,
- ) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ initialEvent,
+ }: RectangleParams) {
super({ chart, series, container, interaction });
this.removeSelf = removeSelf;
@@ -169,6 +167,11 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/regressionTrend/regressionTrend.ts b/src/core/Drawings/regressionTrend/regressionTrend.ts
index eb65afa1e4e36102f162f22402344e14d3b0e3e4..d11a54c756f0c6e6a2b721a485d634b0332ab95d 100644
--- a/src/core/Drawings/regressionTrend/regressionTrend.ts
+++ b/src/core/Drawings/regressionTrend/regressionTrend.ts
@@ -1,12 +1,9 @@
-import { Observable } from 'rxjs';
-
import {
CustomPriceAxisPaneView,
CustomPriceAxisView,
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
getTimeFromXCoordinate,
getXCoordinateFromTime,
@@ -14,6 +11,7 @@ import {
isNearPoint,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { Defaults } from '@src/types/defaults';
@@ -28,22 +26,17 @@ import {
RegressionTrendStyle,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { AxisLabel, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
+import type { AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
-import type { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
+import type { IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
type RegressionTrendMode = 'idle' | 'drawing' | 'ready' | 'dragging';
type DragTarget = 'start' | 'end' | 'body' | null;
type TimeLabelKind = 'start' | 'end';
type PriceLabelKind = 'start' | 'end';
-interface RegressionTrendParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- openSettings?: () => void;
-}
+type RegressionTrendParams = BaseDrawingParams
interface RegressionTrendState {
hidden: boolean;
@@ -133,11 +126,15 @@ export class RegressionTrend extends SeriesDrawingBase<RegressionTrendSettings>
private startPriceAxisView: CustomPriceAxisView;
private endPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { container, interaction, formatObservable, openSettings }: RegressionTrendParams,
- ) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ formatObservable,
+ openSettings,
+ initialEvent,
+ }: RegressionTrendParams) {
super({
chart,
series,
@@ -187,6 +184,11 @@ export class RegressionTrend extends SeriesDrawingBase<RegressionTrendSettings>
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
@@ -455,20 +457,9 @@ export class RegressionTrend extends SeriesDrawingBase<RegressionTrendSettings>
const point = this.getEventPoint(event);
if (this.mode === 'idle') {
- const time = this.getBarTime(point.x);
-
- if (time === null) {
- return;
- }
-
event.preventDefault();
event.stopPropagation();
-
- this.startTime = time;
- this.endTime = time;
- this.mode = 'drawing';
-
- this.render();
+ this.startDrawing(point);
return;
}
@@ -627,6 +618,20 @@ export class RegressionTrend extends SeriesDrawingBase<RegressionTrendSettings>
};
}
+ private startDrawing(point: Point): void {
+ const time = this.getBarTime(point.x);
+
+ if (time === null) {
+ return;
+ }
+
+ this.startTime = time;
+ this.endTime = time;
+ this.mode = 'drawing';
+
+ this.render();
+ }
+
private applyDrag(point: Point): void {
const snapshot = this.dragStateSnapshot;
diff --git a/src/core/Drawings/ruler/ruler.ts b/src/core/Drawings/ruler/ruler.ts
index af6595db15a77326e6bdbef63282f607157c501e..da9c7cc48e39566285de9d8f9702217eee7614e8 100644
--- a/src/core/Drawings/ruler/ruler.ts
+++ b/src/core/Drawings/ruler/ruler.ts
@@ -7,9 +7,14 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
-import { getPriceFromYCoordinate, getXCoordinateFromTime, getYCoordinateFromPrice } from '@core/Drawings/helpers';
+import {
+ getAnchorFromPoint,
+ getPriceFromYCoordinate,
+ getXCoordinateFromTime,
+ getYCoordinateFromPrice,
+} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { t } from '@src/translations';
@@ -21,12 +26,11 @@ import { formatDate } from '@src/utils/formatter';
import { RulerPaneView } from './paneView';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
import type { Anchor, AxisLabel, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type {
AutoscaleInfo,
Coordinate,
- IChartApi,
IPrimitivePaneView,
ISeriesApi,
ISeriesPrimitiveAxisView,
@@ -48,12 +52,8 @@ interface RulerState {
endAnchor: Anchor | null;
}
-interface RulerParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
+interface RulerParams extends BaseDrawingParams {
resetTriggers?: Observable<unknown>[];
- removeSelf?: () => void;
}
export interface RulerRenderData {
@@ -94,11 +94,16 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
private readonly startPriceAxisView: CustomPriceAxisView;
private readonly endPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- { resetTriggers = [], formatObservable, removeSelf, container, interaction }: RulerParams,
- ) {
+ constructor({
+ chart,
+ series,
+ resetTriggers = [],
+ formatObservable,
+ removeSelf,
+ container,
+ interaction,
+ initialEvent,
+ }: RulerParams) {
super({
chart,
series,
@@ -159,6 +164,11 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
});
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
@@ -381,6 +391,10 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
return formatPrice(Number(anchor.price)) ?? '';
}
+ public shouldShowInObjectTree(): boolean {
+ return false;
+ }
+
protected getHoveredItem(x: number, y: number): PrimitiveHoveredItem | null {
return null;
}
@@ -504,7 +518,7 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
}
private handleClick(params: MouseEventParams<Time>): void {
- if (this.hidden || !params.point) {
+ if (this.hidden || !params.point || !params.sourceEvent) {
return;
}
@@ -513,19 +527,21 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
return;
}
+ if (!params.sourceEvent) {
+ return;
+ }
+
const anchor = this.createAnchor(params);
if (!anchor) {
return;
}
+ const point = this.getEventPoint(params.sourceEvent);
+
if (this.mode === 'idle') {
- this.startAnchor = anchor;
- this.endAnchor = anchor;
- this.mode = 'placingEnd';
+ this.startDrawing(point);
- this.hideCrosshair();
- this.render();
return;
}
@@ -539,6 +555,16 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
}
}
+ private startDrawing(point: Point): void {
+ const anchor = getAnchorFromPoint(this.chart, this.series, point);
+ this.startAnchor = anchor;
+ this.endAnchor = anchor;
+ this.mode = 'placingEnd';
+
+ this.hideCrosshair();
+ this.render();
+ }
+
private handleMove(params: MouseEventParams<Time>): void {
if (this.hidden || !params.point) {
return;
diff --git a/src/core/Drawings/sliderPosition/sliderPosition.ts b/src/core/Drawings/sliderPosition/sliderPosition.ts
index f30eb283fa1a05d78ceb1906807608138c235253..d6c9450cdbd8013b1757808c5c5dcdb42af82238 100644
--- a/src/core/Drawings/sliderPosition/sliderPosition.ts
+++ b/src/core/Drawings/sliderPosition/sliderPosition.ts
@@ -6,7 +6,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
getPriceDelta as getPriceDeltaFromCoordinates,
getPriceFromYCoordinate,
@@ -19,6 +18,7 @@ import {
shiftTimeByPixels,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { t } from '@src/translations';
@@ -36,11 +36,10 @@ import {
SliderPositionTextStyle,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { AxisLabel, AxisSegment, Bounds, Point, SeriesApi } from '@core/Drawings/types';
+import type { AxisLabel, AxisSegment, Bounds, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel, SettingsTab } from '@src/types';
import type {
- IChartApi,
IPrimitivePaneView,
MouseEventHandler,
MouseEventParams,
@@ -55,14 +54,9 @@ type DragTarget = 'body' | 'entry' | 'target' | 'stop' | 'end' | null;
type TimeLabelKind = 'start' | 'end';
type PriceLabelKind = 'target' | 'entry' | 'stop';
-interface SliderPositionParams {
+interface SliderPositionParams extends BaseDrawingParams {
side: SliderSide;
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
resetTriggers?: Observable<unknown>[];
- removeSelf?: () => void;
- openSettings?: () => void;
}
interface SliderPositionState {
@@ -151,19 +145,18 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
private entryPriceAxisView: CustomPriceAxisView;
private stopPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- {
- side,
- container,
- interaction,
- formatObservable,
- resetTriggers = [],
- removeSelf,
- openSettings,
- }: SliderPositionParams,
- ) {
+ constructor({
+ chart,
+ series,
+ side,
+ container,
+ interaction,
+ formatObservable,
+ resetTriggers = [],
+ removeSelf,
+ openSettings,
+ initialEvent,
+ }: SliderPositionParams) {
super({
chart,
series,
@@ -230,6 +223,10 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
});
this.series.attachPrimitive(this);
+
+ if (initialEvent) {
+ this.handleChartClick(initialEvent);
+ }
}
public isCreationPending(): boolean {
@@ -630,6 +627,7 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
};
private handleChartClick(params: MouseEventParams<Time>): void {
+ // todo: привести к общему виду дровингов как handleChartClick => startDrawing
if (this.hidden || !params.point || this.mode !== 'idle') {
return;
}
diff --git a/src/core/Drawings/text/text.ts b/src/core/Drawings/text/text.ts
index 9cab4515b48be5194fe5b6b97da67ba49a1eac1e..a336352dc13bf7a9c33d844527a981acd886fbf5 100644
--- a/src/core/Drawings/text/text.ts
+++ b/src/core/Drawings/text/text.ts
@@ -1,9 +1,7 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, UTCTimestamp } from 'lightweight-charts';
+import { IPrimitivePaneView, PrimitiveHoveredItem, TouchMouseEventData, UTCTimestamp } from 'lightweight-charts';
import { clamp } from 'lodash-es';
-import { Observable } from 'rxjs';
import { CustomPriceAxisView, CustomTimeAxisView } from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
clampPointToContainer as clampPointToContainerInElement,
getAnchorFromPoint,
@@ -14,6 +12,7 @@ import {
} from '@core/Drawings/helpers';
import { AxisSegment } from '@core/Drawings/types';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { SettingsTab } from '@src/types';
@@ -23,8 +22,8 @@ import { formatDate, formatPrice } from '@src/utils';
import { TextPaneView } from './paneView';
import { createDefaultSettings, getTextSettingsTabs, TextContentStyle, TextSettings, TextStyle } from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisLabel, Point, SeriesApi } from '@core/Drawings/types';
+import type { Anchor, AxisLabel, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel } from '@src/types';
type TextMode = 'idle' | 'dragging' | 'ready';
@@ -53,13 +52,7 @@ export interface TextRenderData extends TextGeometry, TextStyle, TextContentStyl
showSelectionBorder: boolean;
}
-interface TextParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
-}
+type TextParams = BaseDrawingParams
const UI = {
padding: 6,
@@ -90,16 +83,23 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
private readonly timeAxisView: CustomTimeAxisView;
private readonly priceAxisView: CustomPriceAxisView;
- constructor(chart: IChartApi, series: SeriesApi, params: TextParams) {
+ constructor({
+ chart,
+ series,
+ formatObservable,
+ removeSelf,
+ openSettings,
+ container,
+ interaction,
+ initialEvent,
+ }: TextParams) {
super({
chart,
series,
- container: params.container,
- interaction: params.interaction,
+ container,
+ interaction,
});
- const { formatObservable, removeSelf, openSettings } = params;
-
this.removeSelf = removeSelf;
this.openSettings = openSettings;
@@ -125,6 +125,11 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
@@ -289,7 +294,7 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
event.preventDefault();
event.stopPropagation();
- this.setPoint(point);
+ this.startDrawing(point);
return;
}
@@ -360,7 +365,8 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
}
};
- private setPoint(point: Point): void {
+ private startDrawing(point: Point): void {
+ // todo: вынести в абстрактный класс абстрактным методом (и в соседних классах)
const anchor = this.createAnchor(this.clampPointToContainer(point));
if (!anchor) {
diff --git a/src/core/Drawings/traectory/traectory.ts b/src/core/Drawings/traectory/traectory.ts
index 54133aaa7292e6d1c099d3043444051db3eb348a..d4df66e84760d58e7fa2dff9a934ecb0107f92d7 100644
--- a/src/core/Drawings/traectory/traectory.ts
+++ b/src/core/Drawings/traectory/traectory.ts
@@ -1,9 +1,7 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem } from 'lightweight-charts';
+import { IPrimitivePaneView, PrimitiveHoveredItem } from 'lightweight-charts';
import { clamp } from 'lodash-es';
-import { Observable } from 'rxjs';
import { CustomPriceAxisPaneView, CustomTimeAxisPaneView } from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
clampPointToContainer as clampPointToContainerInElement,
getAnchorFromPoint,
@@ -14,6 +12,7 @@ import {
} from '@core/Drawings/helpers';
import { AxisLabel } from '@core/Drawings/types';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
@@ -21,19 +20,13 @@ import { TraectoryPaneView } from './paneView';
import { createDefaultSettings, getTraectorySettingsTabs, TraectorySettings, TraectoryStyle } from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisSegment, Point, SeriesApi } from '@core/Drawings/types';
-import type { ChartOptionsModel, SettingsTab } from '@src/types';
+import type { Anchor, AxisSegment, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
+import type { SettingsTab } from '@src/types';
type TraectoryMode = 'idle' | 'drawing' | 'ready' | 'dragging-point' | 'dragging-body';
-interface TraectoryParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
-}
+type TraectoryParams = BaseDrawingParams
export interface TraectoryState {
hidden: boolean;
@@ -80,16 +73,14 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
private readonly timeAxisPaneView: CustomTimeAxisPaneView;
private readonly priceAxisPaneView: CustomPriceAxisPaneView;
- constructor(chart: IChartApi, series: SeriesApi, params: TraectoryParams) {
+ constructor({ chart, series, container, interaction, removeSelf, openSettings, initialEvent }: TraectoryParams) {
super({
chart,
series,
- container: params.container,
- interaction: params.interaction,
+ container,
+ interaction,
});
- const { removeSelf, openSettings } = params;
-
this.removeSelf = removeSelf;
this.openSettings = openSettings;
@@ -104,6 +95,11 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
});
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public isCreationPending(): boolean {
diff --git a/src/core/Drawings/volumeProfile/volumeProfile.ts b/src/core/Drawings/volumeProfile/volumeProfile.ts
index d254f7d9d706e9b098e88ad0fd13a465ac6e4924..60066238661b2fbde4dd259f51336449ea4e66a4 100644
--- a/src/core/Drawings/volumeProfile/volumeProfile.ts
+++ b/src/core/Drawings/volumeProfile/volumeProfile.ts
@@ -1,6 +1,5 @@
-import { IChartApi, IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
+import { IPrimitivePaneView, PrimitiveHoveredItem, Time, UTCTimestamp } from 'lightweight-charts';
import { clamp } from 'lodash-es';
-import { Observable } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -8,7 +7,6 @@ import {
CustomTimeAxisPaneView,
CustomTimeAxisView,
} from '@core/Drawings/axis';
-import { SeriesDrawingBase } from '@core/Drawings/common';
import {
clampPointToContainer as clampPointToContainerInElement,
getAnchorFromPoint,
@@ -19,6 +17,7 @@ import {
isPointInBounds,
} from '@core/Drawings/helpers';
import { updateViews } from '@core/Drawings/utils';
+import { SeriesDrawingBase } from '@src/core/Drawings/SeriesDrawingBase';
import { getThemeStore } from '@src/theme';
import { SettingsTab } from '@src/types';
@@ -35,8 +34,8 @@ import {
VolumeProfileStyle,
} from './settings';
-import type { DrawingInteraction, ISeriesDrawing } from '@core/Drawings/common';
-import type { Anchor, AxisLabel, AxisSegment, Bounds, Point, SeriesApi } from '@core/Drawings/types';
+import type { Anchor, AxisLabel, AxisSegment, Bounds, Point } from '@core/Drawings/types';
+import type { BaseDrawingParams, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
import type { ChartOptionsModel } from '@src/types';
export type VolumeProfileKind = 'fixedRange' | 'visibleRange';
@@ -44,13 +43,8 @@ export type VolumeProfileKind = 'fixedRange' | 'visibleRange';
type VolumeProfileMode = 'idle' | 'drawing' | 'ready' | 'dragging';
type DragTarget = 'body' | 'poc' | 'start' | 'end' | null;
-interface VolumeProfileParams {
- container: HTMLElement;
- interaction: DrawingInteraction;
+interface VolumeProfileParams extends BaseDrawingParams {
profileKind?: VolumeProfileKind;
- formatObservable?: Observable<ChartOptionsModel>;
- removeSelf?: () => void;
- openSettings?: () => void;
}
interface SeriesCandleData {
@@ -151,18 +145,17 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
private readonly startPriceAxisView: CustomPriceAxisView;
private readonly endPriceAxisView: CustomPriceAxisView;
- constructor(
- chart: IChartApi,
- series: SeriesApi,
- {
- container,
- interaction,
- profileKind = 'fixedRange',
- formatObservable,
- removeSelf,
- openSettings,
- }: VolumeProfileParams,
- ) {
+ constructor({
+ chart,
+ series,
+ container,
+ interaction,
+ profileKind = 'fixedRange',
+ formatObservable,
+ removeSelf,
+ openSettings,
+ initialEvent,
+ }: VolumeProfileParams) {
super({ chart, series, container, interaction });
this.profileKind = profileKind;
@@ -218,6 +211,11 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
}
this.series.attachPrimitive(this);
+
+ if (initialEvent && initialEvent.sourceEvent) {
+ const point = this.getEventPoint(initialEvent.sourceEvent);
+ this.startDrawing(point);
+ }
}
public destroy(): void {
diff --git a/src/core/DrawingsManager.tsx b/src/core/DrawingsManager.tsx
index 984c71649b0cf950255249491e5693ef443f0285..b532e6e0de8669029385c37fb57fc7c5d6f0b121 100644
--- a/src/core/DrawingsManager.tsx
+++ b/src/core/DrawingsManager.tsx
@@ -1,4 +1,4 @@
-import { IChartApi, ISeriesApi, SeriesType } from 'lightweight-charts';
+import { IChartApi, ISeriesApi, MouseEventParams, SeriesType } from 'lightweight-charts';
import { cloneDeep, isEqual } from 'lodash-es';
import { BehaviorSubject, distinctUntilChanged, map, Observable, Subscription } from 'rxjs';
@@ -13,7 +13,9 @@ import { ModalRenderer } from '@src/core/ModalRenderer';
import { SeriesStrategies } from '@src/modules/series-strategies/SeriesFactory';
import { ActiveDrawingTool, DOMObjectSnapshot } from '@src/types';
-import type { DrawingInteraction } from '@src/core/Drawings/common';
+import { Pane } from './Pane';
+
+import type { DrawingInteraction } from '@src/core/Drawings/SeriesDrawingBase';
import type { SettingsValues } from '@src/types/settings';
interface DrawingsManagerParams {
@@ -25,6 +27,7 @@ interface DrawingsManagerParams {
modalRenderer: ModalRenderer;
paneId: number;
hotkeys: Hotkeys;
+ pane: Pane;
}
export interface DrawingSnapshotItem extends Partial<DOMObjectSnapshot> {
@@ -57,12 +60,13 @@ export class DrawingsManager {
private mainSeries: SeriesStrategies | null = null;
private subscriptions = new Subscription();
private drawings$ = new BehaviorSubject<Drawing[]>([]);
- private selectedDrawing$ = new BehaviorSubject<Drawing | null>(null);
- private activeTool$ = new BehaviorSubject<ActiveDrawingTool>('crosshair');
- private endlessMode$ = new BehaviorSubject(false);
+ private selectedDrawing$ = new BehaviorSubject<Drawing | null>(null); // todo: переместить в DrawingsManagerCollection
+ private activeTool$ = new BehaviorSubject<ActiveDrawingTool>('crosshair'); // todo: переместить в DrawingsManagerCollection
+ private endlessMode$ = new BehaviorSubject(false); // todo: переместить в DrawingsManagerCollection
private pendingSnapshot: DrawingsManagerSnapshot | null = null;
private selectedDrawingSnapshot: DrawingSnapshotItem | null = null;
private recreateScheduled = false;
+ private pane: Pane;
private copyPasteBuffer: DrawingSnapshotItem | null = null;
private escapeUnregisterHash: string | null = null;
@@ -76,10 +80,12 @@ export class DrawingsManager {
modalRenderer,
paneId,
hotkeys,
+ pane,
}: DrawingsManagerParams) {
this.DOM = DOM;
this.eventManager = eventManager;
this.paneId = paneId;
+ this.pane = pane;
this.lwcChart = lwcChart;
this.container = container;
this.modalRenderer = modalRenderer;
@@ -97,7 +103,7 @@ export class DrawingsManager {
if (this.pendingSnapshot) {
const snapshot = this.pendingSnapshot;
this.pendingSnapshot = null;
- this.setSnapshot(snapshot);
+ doAfterPromise(() => this.setSnapshot(snapshot), this.pane.isReady());
}
}),
);
@@ -268,11 +274,14 @@ export class DrawingsManager {
return existingDrawing;
}
- return this.createDrawing(snapshot.drawingName, {
- id: snapshot.id,
- state: cloneDeep(snapshot.state),
- isLocked: snapshot.isLocked,
- zIndex: snapshot.zIndex,
+ return this.createDrawing({
+ name: snapshot.drawingName,
+ options: {
+ id: snapshot.id,
+ state: cloneDeep(snapshot.state),
+ isLocked: snapshot.isLocked,
+ zIndex: snapshot.zIndex,
+ },
});
}
@@ -384,7 +393,7 @@ export class DrawingsManager {
this.DOM.refreshEntities();
}
- public addDrawingForce = async (name: DrawingsNames): Promise<void> => {
+ public addDrawingForce = async (name: DrawingsNames, event?: MouseEventParams): Promise<void> => {
this.removePendingDrawings(false);
const previousDrawing = drawingsMap[name].singleInstance
@@ -398,7 +407,7 @@ export class DrawingsManager {
}
this.activeTool$.next(name);
- const drawing = this.createDrawing(name);
+ const drawing = this.createDrawing({ name, event });
this.DOM.refreshEntities();
await drawing.waitForCreation();
@@ -414,7 +423,15 @@ export class DrawingsManager {
this.pushDrawingChange(previousSnapshot, this.createDrawingSnapshot(drawing));
};
- private createDrawing(name: DrawingsNames, options: CreateDrawingOptions = {}): Drawing {
+ private createDrawing({
+ name,
+ options = {},
+ event,
+ }: {
+ name: DrawingsNames;
+ options?: CreateDrawingOptions;
+ event?: MouseEventParams;
+ }): Drawing {
const { mainSeries } = this;
if (!mainSeries) {
@@ -440,7 +457,7 @@ export class DrawingsManager {
);
const construct = (chart: IChartApi, series: ISeriesApi<SeriesType>, interaction: DrawingInteraction) => {
- const paneElement = series.getPane().getHTMLElement();
+ const paneElement = this.pane.getHTMLElement();
if (!paneElement) {
throw new Error('[Drawing Manager]: cannot place drawing, there is no pane');
@@ -461,6 +478,7 @@ export class DrawingsManager {
this.openSettings(createdDrawing);
}
},
+ initialEvent: event,
});
};
@@ -558,12 +576,15 @@ export class DrawingsManager {
}
drawings.push(
- this.createDrawing(item.drawingName, {
- id: item.id,
- state: cloneDeep(item.state),
- isLocked: item.isLocked,
- zIndex: item.zIndex,
- shouldUpdateDrawingsList: false,
+ this.createDrawing({
+ name: item.drawingName,
+ options: {
+ id: item.id,
+ state: cloneDeep(item.state),
+ isLocked: item.isLocked,
+ zIndex: item.zIndex,
+ shouldUpdateDrawingsList: false,
+ },
}),
);
@@ -732,3 +753,8 @@ export class DrawingsManager {
this.endlessMode$.complete();
}
}
+
+async function doAfterPromise(cb: () => void, waiter: Promise<void>) {
+ await waiter;
+ cb();
+}
diff --git a/src/core/DrawingsManagerCollection.ts b/src/core/DrawingsManagerCollection.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb929d0625c4ebe48f061faa573ecdbbc7398594
--- /dev/null
+++ b/src/core/DrawingsManagerCollection.ts
@@ -0,0 +1,65 @@
+import { Observable } from 'rxjs';
+
+import { DrawingsNames } from '@src/constants';
+import { ActiveDrawingTool, SettingsValues } from '@src/types';
+
+import { Drawing } from './Drawings';
+import { DrawingsManager } from './DrawingsManager';
+import { PaneManager } from './PaneManager';
+// todo: нужно дописывать класс)
+export class DrawingsManagerCollection {
+ private managers: DrawingsManager[] = [];
+ private paneCollection: PaneManager;
+
+ constructor({ paneCollection }: { paneCollection: PaneManager }) {
+ this.paneCollection = paneCollection;
+ }
+
+ public addDrawingManager(manager: DrawingsManager) {
+ this.managers.push(manager);
+ }
+
+ public addDrawingForce = async (name: DrawingsNames): Promise<void> => {
+ this.paneCollection.listenPanesToAddDrawing(name);
+ };
+
+ public setEndlessDrawingMode = (value: boolean): void => {
+ this.managers.forEach((manager) => {
+ manager.setEndlessDrawingMode(value);
+ });
+ };
+
+ public isEndlessDrawingsMode(): Observable<boolean> {
+ return this.managers[0].isEndlessDrawingsMode();
+ }
+
+ public activateCrosshair(): void {
+ this.managers.forEach((manager) => {
+ manager.activateCrosshair();
+ });
+ }
+
+ public getActiveTool(): Observable<ActiveDrawingTool> {
+ return this.managers[0].getActiveTool();
+ }
+
+ public selectedDrawing(): Observable<Drawing | null> {
+ return this.managers[0].selectedDrawing();
+ }
+
+ public updateSelectedDrawingSettings = (settings: SettingsValues): void => {
+ this.managers[0].updateSelectedDrawingSettings(settings);
+ };
+
+ public toggleSelectedDrawingLock = (): void => {
+ this.managers[0].toggleSelectedDrawingLock();
+ };
+
+ public openSelectedDrawingSettings = (): void => {
+ this.managers[0].openSelectedDrawingSettings();
+ };
+
+ public deleteSelectedDrawing = (): void => {
+ this.managers[0].deleteSelectedDrawing();
+ };
+}
diff --git a/src/core/Indicator.ts b/src/core/Indicator.ts
index 18188c779e2cf07877277d4d6a47f33da57da479..c232d4eb4fd9dbb102c3cde42070996fa73f0cb5 100644
--- a/src/core/Indicator.ts
+++ b/src/core/Indicator.ts
@@ -210,42 +210,53 @@ export class Indicator extends DOMObject implements ISerializable<IndicatorSnaps
}
private createSeries(): void {
- this.config.series.forEach(({ name, id: serieId, dataFormatter, seriesOptions, priceScaleOptions }) => {
- const serie = SeriesFactory.create(name!)({
- lwcChart: this.lwcChart,
- dataSource: this.dataSource,
- customFormatter: dataFormatter
- ? (params) =>
- dataFormatter({
- ...params,
- settings: this.settings,
- indicatorReference: this,
- })
- : undefined,
- seriesOptions,
- priceScaleOptions,
- mainSymbolId$: this.mainSymbolId$,
- mainSymbol$: this.mainSymbol$,
- mainSerie$: this.associatedPane.getMainSerie(),
- showSymbolLabel: false,
- paneIndex: this.associatedPane.paneIndex(),
- indicatorReference: this,
- });
-
- const handleDataChanged = () => {
- this.notifyDataChanged();
- };
-
- serie.subscribeDataChanged(handleDataChanged);
-
- this.seriesSubscriptions.add(() => {
- serie.unsubscribeDataChanged(handleDataChanged);
- });
-
- this.seriesMap.set(serieId, serie);
-
- this.series.push(serie);
- });
+ this.config.series.forEach(
+ ({ name, id: serieId, dataFormatter, seriesOptions, priceScaleOptions, actLikeMainSerie }) => {
+ const serie = SeriesFactory.create(name!)({
+ lwcChart: this.lwcChart,
+ dataSource: this.dataSource,
+ customFormatter: dataFormatter
+ ? (params) =>
+ dataFormatter({
+ ...params,
+ settings: this.settings,
+ indicatorReference: this,
+ })
+ : undefined,
+ seriesOptions,
+ priceScaleOptions,
+ mainSymbolId$: this.mainSymbolId$,
+ mainSymbol$: this.mainSymbol$,
+ mainSerie$: this.associatedPane.getMainSerie(),
+ showSymbolLabel: false,
+ paneIndex: this.associatedPane.paneIndex(),
+ indicatorReference: this,
+ actLikeMainSerie,
+ });
+
+ const handleDataChanged = () => {
+ this.notifyDataChanged();
+ };
+
+ serie.subscribeDataChanged(handleDataChanged);
+
+ this.seriesSubscriptions.add(() => {
+ serie.unsubscribeDataChanged(handleDataChanged);
+ });
+
+ this.seriesMap.set(serieId, serie);
+
+ this.series.push(serie);
+ },
+ );
+
+ if (!this.associatedPane.getLocalMainSeries()) {
+ const mainSeriesCandidates = this.series.filter((serie) => serie.actLikeMainSerie);
+ if (mainSeriesCandidates.length < 1) {
+ throw new Error('[Indicator]: there is no mainSerie in this indicator');
+ }
+ this.associatedPane.setLocalMainSeries(mainSeriesCandidates[0]);
+ }
}
private destroySeries(): void {
diff --git a/src/core/Indicators/index.tsx b/src/core/Indicators/index.tsx
index 27131180366979f4afd8f3140ab7adb72b21776d..1a61e79c863352811f8f3c8c756987f04555a4d0 100644
--- a/src/core/Indicators/index.tsx
+++ b/src/core/Indicators/index.tsx
@@ -134,6 +134,7 @@ export const indicatorsMap = (): Partial<Record<IndicatorsIds, IndicatorConfig>>
{
name: 'Line', // todo: change with enum
id: 'macdLine',
+ actLikeMainSerie: true,
priceScaleOptions: {
mode: PriceScaleMode.Normal,
},
@@ -145,6 +146,7 @@ export const indicatorsMap = (): Partial<Record<IndicatorsIds, IndicatorConfig>>
},
{
name: 'Line', // todo: change with enum
+ actLikeMainSerie: true,
id: 'signalLine',
priceScaleOptions: {
mode: PriceScaleMode.Normal,
@@ -157,6 +159,7 @@ export const indicatorsMap = (): Partial<Record<IndicatorsIds, IndicatorConfig>>
},
{
name: 'Histogram', // todo: change with enum
+ actLikeMainSerie: true,
id: 'histogram',
priceScaleOptions: {
autoScale: true,
diff --git a/src/core/MoexChart.tsx b/src/core/MoexChart.tsx
index 350012ab7fcd7f5d3fceef3be1abd3f66f14c8ca..622fad50d0cbb4d97cd91e5de76d23597ead0786 100644
--- a/src/core/MoexChart.tsx
+++ b/src/core/MoexChart.tsx
@@ -300,15 +300,16 @@ export class MoexChart implements ISerializable<MoexChartSnapshot> {
}
private renderAttachments(config: IMoexChart, toggleToolbar: () => boolean) {
- const drawingsManager = this.chart.getDrawingsManager();
+ const drawingsCollectionManager = this.chart.getDrawingsCollectionManager();
this.drawingToolbarRenderer.renderComponent(
+ // этот тулбар к drawingManger бы прибить, сильно на него завязан
<FloatingDrawingToolbar
- selectedDrawing$={drawingsManager.selectedDrawing()}
- onUpdateSettings={drawingsManager.updateSelectedDrawingSettings}
- onToggleLock={() => drawingsManager.toggleSelectedDrawingLock()}
- onOpenSettings={() => drawingsManager.openSelectedDrawingSettings()}
- onDelete={() => drawingsManager.deleteSelectedDrawing()}
+ selectedDrawing$={drawingsCollectionManager.selectedDrawing()}
+ onUpdateSettings={drawingsCollectionManager.updateSelectedDrawingSettings}
+ onToggleLock={() => drawingsCollectionManager.toggleSelectedDrawingLock()}
+ onOpenSettings={() => drawingsCollectionManager.openSelectedDrawingSettings()}
+ onDelete={() => drawingsCollectionManager.deleteSelectedDrawing()}
/>,
);
@@ -357,14 +358,15 @@ export class MoexChart implements ISerializable<MoexChartSnapshot> {
);
if (this.toolbarRenderer && config.chartCollectionPreset.showMenuButton) {
+ const drawingCollection = this.chart.getDrawingsCollectionManager();
this.toolbarRenderer.renderComponent(
<Toolbar
toggleDOM={this.chart.getDom().toggleDOM}
- addDrawing={this.chart.getDrawingsManager().addDrawingForce} // todo: deal with new panes logic
- setEndlessDrawingsMode={this.chart.getDrawingsManager().setEndlessDrawingMode}
- isEndlessDrawingsMode$={this.chart.getDrawingsManager().isEndlessDrawingsMode()}
- activateCrosshair={() => this.chart.getDrawingsManager().activateCrosshair()}
- activeTool$={this.chart.getDrawingsManager().getActiveTool()}
+ addDrawing={drawingCollection.addDrawingForce}
+ setEndlessDrawingsMode={drawingCollection.setEndlessDrawingMode}
+ isEndlessDrawingsMode$={drawingCollection.isEndlessDrawingsMode()}
+ activateCrosshair={() => drawingCollection.activateCrosshair()}
+ activeTool$={drawingCollection.getActiveTool()}
hotkeys={this.hotkeys}
/>,
);
diff --git a/src/core/Pane.tsx b/src/core/Pane.tsx
index e24286fb3413675bcfaf735c87ff716b7c8eaaed..b048ecf5e20583723385de57c0b02aeafd0201a4 100644
--- a/src/core/Pane.tsx
+++ b/src/core/Pane.tsx
@@ -1,4 +1,4 @@
-import { IChartApi, IPaneApi, PriceScaleMode, Time } from 'lightweight-charts';
+import { IChartApi, IPaneApi, MouseEventParams, PriceScaleMode, Time } from 'lightweight-charts';
import { BehaviorSubject, Subscription } from 'rxjs';
@@ -53,6 +53,7 @@ export interface PaneParams {
leftPriceScaleVisible: boolean;
rightPriceScaleVisible: boolean;
hotkeys: Hotkeys;
+ addDrawingManager: (manager: DrawingsManager) => void;
}
// todo: Pane, ему должна принадлежать mainSerie, а также IndicatorManager и drawingsManager, mouseEvents. Также перекинуть соответствующие/необходимые свойства из чарта, и из чарта удалить
@@ -65,6 +66,11 @@ export class Pane implements ISerializable<PaneSnapshot> {
private readonly id: number;
private readonly isMain: boolean;
private mainSeries = new BehaviorSubject<SeriesStrategies | null>(null); // Main Series. Exists in a single copy
+
+ // todo: Отвратительный нейминг. Нужно оставить главной только эту серию и переименовать её localMainSeries => mainSerie
+ // Если вдруг понадобится главная серия главного пейна, то брать из КоллекцииПейнов(-сейчас PaneManager)
+ // serie to attach drawings
+ private localMainSeries = new BehaviorSubject<SeriesStrategies | null>(null);
private legend!: Legend;
private tooltip: TooltipService | undefined;
private readonly indicatorsMap = new BehaviorSubject<Map<string, Indicator>>(new Map());
@@ -86,6 +92,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
private readonly onPriceScaleStateChange: () => void;
private readonly subscriptions = new Subscription();
private paneContainerSyncFrameId: number | null = null;
+ private initialDrawingClickListener: (event: MouseEventParams) => void = () => {};
constructor({
lwcChart,
@@ -106,6 +113,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
leftPriceScaleVisible,
rightPriceScaleVisible,
hotkeys,
+ addDrawingManager,
}: PaneParams) {
this.onDelete = onDelete;
this.onPriceScaleStateChange = onPriceScaleStateChange;
@@ -166,9 +174,10 @@ export class Pane implements ISerializable<PaneSnapshot> {
this.drawingsManager = new DrawingsManager({
// todo: менеджер дровингов должен быть один на чарт, не на пейн
+ pane: this,
eventManager,
DOM,
- mainSeries$: this.mainSeries.asObservable(),
+ mainSeries$: this.localMainSeries.asObservable(),
lwcChart,
container: chartContainer,
modalRenderer: this.modalRenderer,
@@ -176,6 +185,8 @@ export class Pane implements ISerializable<PaneSnapshot> {
hotkeys,
});
+ addDrawingManager(this.drawingsManager);
+
this.subscriptions.add(
this.drawingsManager.entities().subscribe((drawings) => {
const hasRuler = drawings.some((drawing) => drawing.getDrawingName() === DrawingsNames.ruler);
@@ -185,6 +196,31 @@ export class Pane implements ISerializable<PaneSnapshot> {
);
}
+ public fireClick = (event: MouseEventParams) => {
+ this.initialDrawingClickListener(event);
+ };
+
+ public unsubscribeInitialDrawingClick = () => {
+ this.initialDrawingClickListener = () => {};
+ };
+
+ public subscribeInitialDrawingClick = (cb: () => void, name: DrawingsNames) => {
+ this.initialDrawingClickListener = (event: MouseEventParams) => {
+ this.drawingsManager.addDrawingForce(name, event);
+ cb();
+ };
+ };
+
+ public getLocalMainSeries = () => {
+ return this.localMainSeries.value;
+ };
+
+ public setLocalMainSeries = (next: SeriesStrategies) => {
+ if (!this.isMain) {
+ this.localMainSeries.next(next);
+ }
+ };
+
public isMainPane = () => {
return this.isMain;
};
@@ -205,6 +241,23 @@ export class Pane implements ISerializable<PaneSnapshot> {
return this.id;
};
+ public isReady = async (interval = 50): Promise<void> => {
+ return new Promise((resolve) => {
+ const check = () => {
+ if (this.lwcPane.getHTMLElement() === null) {
+ setTimeout(check, interval);
+ } else {
+ resolve();
+ }
+ };
+ check();
+ });
+ };
+
+ public getHTMLElement = () => {
+ return this.lwcPane.getHTMLElement();
+ };
+
public paneIndex = () => {
return this.lwcPane.paneIndex();
};
@@ -279,6 +332,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
cancelAnimationFrame(this.paneContainerSyncFrameId);
this.paneContainerSyncFrameId = null;
}
+ this.drawingsManager.destroy();
this.subscriptions.unsubscribe();
this.tooltip?.destroy();
@@ -290,6 +344,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
this.paneOverlayContainer.remove();
this.indicatorsMap.complete();
this.mainSerieSub?.unsubscribe();
+ this.localMainSeries.complete();
if (this.isMain) {
this.mainSeries.value?.destroy();
@@ -406,6 +461,7 @@ export class Pane implements ISerializable<PaneSnapshot> {
}
private initializeMainSerie({ lwcChart, dataSource }: { lwcChart: IChartApi; dataSource: DataSource }): void {
+ this.localMainSeries = this.mainSeries;
this.mainSerieSub = this.eventManager.subscribeSeriesSelected((nextSeries) => {
this.mainSeries.value?.destroy();
diff --git a/src/core/PaneManager.ts b/src/core/PaneManager.ts
index 131ceb7c64c1b1f6b9ffc789daf0b8a33ce22377..2ab15d493720d996389ba2d081aa8e55568d186f 100644
--- a/src/core/PaneManager.ts
+++ b/src/core/PaneManager.ts
@@ -1,13 +1,16 @@
+import { type Observable, Subscription } from 'rxjs';
+
import { DataSource } from '@core/DataSource';
import { DrawingsManager, DrawingsManagerSnapshot } from '@core/DrawingsManager';
+import { DrawingsManagerCollection } from '@core/DrawingsManagerCollection';
import { Pane, PaneParams } from '@core/Pane';
import { PriceAxisLabels } from '@core/PriceAxisLabels';
+import { DrawingsNames } from '@src/constants';
import { Direction } from '@src/types';
import { ISerializable, PaneSnapshot, PriceScaleSide, PriceScaleSnapshot } from '@src/types/snapshot';
import type { Indicator } from '@core/Indicator';
-import type { LogicalRange } from 'lightweight-charts';
-import type { Observable } from 'rxjs';
+import type { IChartApi, LogicalRange, MouseEventParams } from 'lightweight-charts';
interface PaneManagerParams
extends Omit<
@@ -20,6 +23,7 @@ interface PaneManagerParams
| 'onPriceScaleStateChange'
| 'leftPriceScaleVisible'
| 'rightPriceScaleVisible'
+ | 'addDrawingManager'
> {
panesSnapshot: PaneSnapshot[];
}
@@ -39,6 +43,7 @@ type SharedPaneParams = Omit<PaneManagerParams, 'panesSnapshot'>;
export class PaneManager implements ISerializable<PaneSnapshot[]> {
private readonly sharedPaneParams: SharedPaneParams;
+ private readonly lwcChart: IChartApi;
private readonly panesMap = new Map<number, Pane>();
private mainPane: Pane;
@@ -46,22 +51,26 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
private priceAxisLabels: PriceAxisLabels | null = null;
private leftPriceScaleVisible = false;
private rightPriceScaleVisible = true;
+ private drawingsManagerCollection: DrawingsManagerCollection;
+ private readonly subscriptions = new Subscription();
constructor({ panesSnapshot, ...sharedPaneParams }: PaneManagerParams) {
- this.sharedPaneParams = sharedPaneParams;
-
+ this.sharedPaneParams = {
+ ...sharedPaneParams,
+ };
+ this.lwcChart = sharedPaneParams.lwcChart;
const mainPaneSnapshot = panesSnapshot.find((paneSnapshot) => paneSnapshot.isMain);
const mainPaneId = mainPaneSnapshot?.id ?? 0;
+ this.drawingsManagerCollection = new DrawingsManagerCollection({ paneCollection: this });
+
this.mainPane = new Pane({
...this.sharedPaneParams,
+ ...this.getCollectionDependencies(),
id: mainPaneId,
isMainPane: true,
onDelete: () => {},
- initialPriceScales: mainPaneSnapshot?.priceScales,
- onPriceScaleStateChange: this.handlePriceScaleStateChange,
- leftPriceScaleVisible: this.leftPriceScaleVisible,
- rightPriceScaleVisible: this.rightPriceScaleVisible,
+ initialPriceScales: mainPaneSnapshot?.priceScales, // todo: add to sharedParams?
});
this.panesMap.set(mainPaneId, this.mainPane);
@@ -87,6 +96,8 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
pane.setDrawingsSnapshot(paneSnapshot.drawings);
});
+ this.initClickListener();
+
this.syncPaneContainers();
}
@@ -101,6 +112,23 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
});
}
+ public listenPanesToAddDrawing(name: DrawingsNames) {
+ const panes = Array.from(this.panesMap.values());
+ const cb = () => {
+ panes.forEach((pane) => {
+ pane.unsubscribeInitialDrawingClick();
+ });
+ };
+
+ return panes.forEach((pane) => {
+ pane.subscribeInitialDrawingClick(cb, name);
+ });
+ }
+
+ public getDrawingsCollectionManager(): DrawingsManagerCollection {
+ return this.drawingsManagerCollection;
+ }
+
public setVisibleLogicalRange(logicalRange: LogicalRange | null): void {
this.priceAxisLabels?.setVisibleLogicalRange(logicalRange);
}
@@ -149,15 +177,13 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
const pane = new Pane({
...this.sharedPaneParams,
+ ...this.getCollectionDependencies(),
id,
isMainPane: false,
dataSource: dataSource ?? null,
basedOn: dataSource ? undefined : this.mainPane,
onDelete: () => this.destroyPane(id),
initialPriceScales,
- onPriceScaleStateChange: this.handlePriceScaleStateChange,
- leftPriceScaleVisible: this.leftPriceScaleVisible,
- rightPriceScaleVisible: this.rightPriceScaleVisible,
});
this.panesMap.set(id, pane);
@@ -173,11 +199,6 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
});
}
- public getDrawingsManager(): DrawingsManager {
- // todo: temp
- return this.mainPane.getDrawingManager();
- }
-
public getSnapshot(): PaneSnapshot[] {
const snapshot: PaneSnapshot[] = [];
@@ -199,6 +220,14 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
this.panesMap.clear();
}
+ private initClickListener(): void {
+ this.lwcChart.subscribeClick((param: MouseEventParams) => {
+ if (param.paneIndex === undefined) return;
+ const clickedPane = this.getPaneById(param.paneIndex);
+ clickedPane?.fireClick(param);
+ });
+ }
+
private refreshPriceScaleControls(): void {
this.panesMap.forEach((pane) => {
pane.refreshPriceScaleControls();
@@ -231,6 +260,18 @@ export class PaneManager implements ISerializable<PaneSnapshot[]> {
});
}
+ private getCollectionDependencies(): Pick<
+ PaneParams,
+ 'onPriceScaleStateChange' | 'leftPriceScaleVisible' | 'rightPriceScaleVisible' | 'addDrawingManager'
+ > {
+ return {
+ onPriceScaleStateChange: this.handlePriceScaleStateChange,
+ leftPriceScaleVisible: this.leftPriceScaleVisible,
+ rightPriceScaleVisible: this.rightPriceScaleVisible,
+ addDrawingManager: (manager) => this.drawingsManagerCollection.addDrawingManager(manager),
+ };
+ }
+
private handlePriceScaleStateChange = (): void => {
this.priceAxisLabels?.invalidate();
};
diff --git a/src/core/Series/BaseSeries.ts b/src/core/Series/BaseSeries.ts
index d1d52087bc682e1075bffbceabbcce842f64aa39..7c3f28232e07f63ffd004f12760f307bb9256a2e 100644
--- a/src/core/Series/BaseSeries.ts
+++ b/src/core/Series/BaseSeries.ts
@@ -71,6 +71,7 @@ export interface BaseSeriesParams<TSeries extends SeriesType = SeriesType> {
mainSymbolId$: Observable<string>;
mainSymbol$: Observable<string>;
mainSerie$: BehaviorSubject<SeriesStrategies | null>;
+ actLikeMainSerie?: boolean;
customFormatter?: (params: IndicatorDataFormatter<TSeries>) => SeriesDataItemTypeMap<Time>[TSeries][];
seriesOptions?: SeriesPartialOptionsMap[TSeries];
priceScaleOptions?: DeepPartial<PriceScaleOptions>;
@@ -94,6 +95,7 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
private subscriptions = new Subscription();
private dataSub: Subscription | null = null;
private realtimeSub: Subscription | null = null;
+ public actLikeMainSerie = false;
constructor({
lwcChart,
@@ -106,6 +108,7 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
showSymbolLabel = true,
paneIndex,
indicatorReference,
+ actLikeMainSerie,
}: BaseSeriesParams<TSeries>) {
this.lwcSeries = this.createSeries({
chart: lwcChart,
@@ -113,6 +116,7 @@ export abstract class BaseSeries<TSeries extends SeriesType> implements IBaseSer
paneIndex,
priceScaleOptions,
});
+ this.actLikeMainSerie = actLikeMainSerie ?? false;
this.lwcChart = lwcChart;
this.customFormatter = customFormatter;
diff --git a/src/modules/series-strategies/SeriesFactory.ts b/src/modules/series-strategies/SeriesFactory.ts
index 2e384d5ff7e1da5743211c67f949c1289d3330c4..301d2cc4236550ff1fdfc93b45445ba616a14042 100644
--- a/src/modules/series-strategies/SeriesFactory.ts
+++ b/src/modules/series-strategies/SeriesFactory.ts
@@ -11,10 +11,12 @@ export type SeriesStrategies =
| CandlestickSeriesStrategy
| LineSeriesStrategy
| HistogramSeriesStrategy
- | BarSeriesStrategy
- | ISeries<'Baseline'>
- | ISeries<'Area'>
- | ISeries<'Custom'>;
+ | BarSeriesStrategy;
+// ISeries<'Baseline'> теперь несовместима с *SeriesStrategy
+// todo: расширить ISeries<Baseline | Area | Custom>
+// | ISeries<'Baseline'>
+// | ISeries<'Area'>
+// | ISeries<'Custom'>;
/**
x * Фабрика для создания стратегий серий
* Реализует паттерн Factory для создания нужной стратегии по типу графика
diff --git a/src/types/drawing.ts b/src/types/drawing.ts
index be9a620c644e773197f1ad243ea9535e5997c278..7ab5844883ff2637b657f1b06094524123d0f42d 100644
--- a/src/types/drawing.ts
+++ b/src/types/drawing.ts
@@ -1,9 +1,9 @@
-import { IChartApi, ISeriesApi, SeriesType } from 'lightweight-charts';
+import { IChartApi, ISeriesApi, MouseEventParams, SeriesType } from 'lightweight-charts';
import { DrawingsNames } from '@src/constants';
import { EventManager } from '@src/core';
-import type { DrawingInteraction, ISeriesDrawing } from '@src/core/Drawings/common';
+import type { DrawingInteraction, ISeriesDrawing } from '@src/core/Drawings/SeriesDrawingBase';
export enum LineMarker {
normal = 'normal',
@@ -12,7 +12,7 @@ export enum LineMarker {
export type ActiveDrawingTool = DrawingsNames | 'crosshair';
-interface DrawingParams {
+export interface DrawingParams {
chart: IChartApi;
series: ISeriesApi<SeriesType>;
eventManager: EventManager;
@@ -20,6 +20,7 @@ interface DrawingParams {
interaction: DrawingInteraction;
removeSelf: () => void;
openSettings: () => void;
+ initialEvent?: MouseEventParams;
}
export interface DrawingConfig {
diff --git a/src/types/indicator.ts b/src/types/indicator.ts
index 7e4580f4c5d52d1809db94c4e7d4cdf9a6ada850..036fd00f8357362b3f809ab866e3909e0dd154d8 100644
--- a/src/types/indicator.ts
+++ b/src/types/indicator.ts
@@ -36,6 +36,7 @@ export interface IndicatorStateConfig {
export interface IndicatorSerie {
id: string;
name: ChartSeriesType;
+ actLikeMainSerie?: boolean;
seriesOptions?: SeriesPartialOptionsMap[ChartSeriesType];
priceScaleOptions?: DeepPartial<PriceScaleOptions>;
priceScaleId?: string;