Загрузка данных
diff --git a/src/components/Toolbar/index.tsx b/src/components/Toolbar/index.tsx
index 619d5de39d9a8843615a7fe69c5e5b197373d82a..6e9fddee71fe88acebab2b8060a7bb16700ca57a 100644
--- a/src/components/Toolbar/index.tsx
+++ b/src/components/Toolbar/index.tsx
@@ -35,6 +35,8 @@ import styles from './index.module.scss';
interface ToolbarProps {
toggleDOM: () => void;
addDrawing: (name: DrawingsNames) => void;
+ setEndlessDrawingsMode: (value: boolean) => void;
+ isEndlessDrawingsMode$: Observable<boolean>;
activateCrosshair: () => void;
activeTool$: Observable<ActiveDrawingTool>;
}
@@ -50,11 +52,20 @@ const implemented = {
icons: false,
};
-export default function Toolbar({ toggleDOM, addDrawing, activateCrosshair, activeTool$ }: ToolbarProps) {
+export default function Toolbar({
+ toggleDOM,
+ addDrawing,
+ setEndlessDrawingsMode,
+ isEndlessDrawingsMode$,
+ activateCrosshair,
+ activeTool$,
+}: ToolbarProps) {
const [selectedLineType, setSelectedLineType] = useState<DrawingsNames>(DrawingsNames.trendLine);
const [selectedMeasurementTool, setSelectedMeasurementTool] = useState(DrawingsNames.fixedProfile);
const [selectedGeometricShape, setSelectedGeometricShape] = useState(DrawingsNames.rectangle);
+ const isEndlessDrawingsMode = useObservable(isEndlessDrawingsMode$);
+
const activeTool = useObservable(activeTool$, 'crosshair');
const toolbarRef = useRef<HTMLDivElement | null>(null);
@@ -245,14 +256,15 @@ export default function Toolbar({ toggleDOM, addDrawing, activateCrosshair, acti
</div>
<Divider
direction="horizontal"
- pt={{ divider: { className: classNames(styles.divider, styles.notImplemented) } }}
+ pt={{ divider: { className: classNames(styles.divider) } }}
/>
- <div className={classNames(styles.group, styles.notImplemented)}>
+ <div className={classNames(styles.group)}>
<Tooltip
tooltipClassName={styles.tooltipHint}
showMessageOnFocus
label="Магнит позволяет притягивать точки объектов к ближайшим ценам (откр., макс., мин., закр.) баров"
location="right"
+ className={styles.notImplemented}
>
<Button
size="sm"
@@ -270,8 +282,10 @@ export default function Toolbar({ toggleDOM, addDrawing, activateCrosshair, acti
>
<Button
size="sm"
- className={styles.button}
- onClick={() => {}}
+ className={`${styles.button} ${isEndlessDrawingsMode ? styles.pressed : ''}`}
+ onClick={() => {
+ setEndlessDrawingsMode(!isEndlessDrawingsMode);
+ }}
label={<PencilLockerIcon />}
/>
</Tooltip>
@@ -281,6 +295,7 @@ export default function Toolbar({ toggleDOM, addDrawing, activateCrosshair, acti
showMessageOnFocus
label="Зафиксировать все объекты"
location="right"
+ className={styles.notImplemented}
>
<Button
size="sm"
@@ -295,6 +310,7 @@ export default function Toolbar({ toggleDOM, addDrawing, activateCrosshair, acti
showMessageOnFocus
label="Скрыть все объекты рисования"
location="right"
+ className={styles.notImplemented}
>
<Button
size="sm"
diff --git a/src/core/ContainerManager.ts b/src/core/ContainerManager.ts
index 3ce87ebe2d57635c92fc37106c313e51d43a28fe..8af4acf10e86c4fcdd11229e2b5bb117ae007df9 100644
--- a/src/core/ContainerManager.ts
+++ b/src/core/ContainerManager.ts
@@ -68,6 +68,7 @@ export class ContainerManager {
chartContainer.style.minHeight = '0';
chartContainer.style.display = 'grid';
chartContainer.style.columnGap = 'var(--space-0500)';
+ chartContainer.style.gridTemplateColumns = 'minmax(0, 1fr)';
const chartAreaContainer = document.createElement('div');
chartAreaContainer.style.position = 'relative';
@@ -97,12 +98,11 @@ export class ContainerManager {
modalContainer.style.zIndex = ZIndex.Modal;
modalContainer.style.pointerEvents = 'none';
- chartAreaContainer.append(controlBarContainer, modalContainer);
+ chartAreaContainer.append(controlBarContainer);
- chartContainer.style.gridTemplateColumns = 'minmax(0, 1fr)';
chartContainer.append(chartAreaContainer);
- parentContainer.append(headerContainer, chartContainer);
+ parentContainer.append(headerContainer, chartContainer, modalContainer);
if (showBottomPanel) {
parentContainer.append(footerContainer);
diff --git a/src/core/DOMModel.tsx b/src/core/DOMModel.tsx
index 704697bf9b7eed1d983fa937a8b049eb4a74826b..7ca2e015b9c04941ca19f905481074f8a04c65f5 100644
--- a/src/core/DOMModel.tsx
+++ b/src/core/DOMModel.tsx
@@ -1,4 +1,4 @@
-import { BehaviorSubject, Observable } from 'rxjs';
+import { BehaviorSubject, map, Observable } from 'rxjs';
import { DOM } from '@components/DOM';
import { IDOMObject } from '@core/DOMObject';
@@ -87,7 +87,13 @@ export class DOMModel {
};
public getEntities = (): Observable<IDOMObject[]> => {
- return this.entities.asObservable();
+ return this.entities.pipe(
+ map((arr) => {
+ return arr.filter((value) => {
+ return value.showInDOM;
+ });
+ }),
+ );
};
public toggleDOM = () => {
diff --git a/src/core/DOMObject.ts b/src/core/DOMObject.ts
index 14f477e018c714cfc49e5963b107ec300b55cbae..cd471521ce6ab1631de9b99590e1c0edf522337f 100644
--- a/src/core/DOMObject.ts
+++ b/src/core/DOMObject.ts
@@ -11,6 +11,7 @@ export interface IDOMObject {
zIndex: number;
type: DOMObjectType;
name: string;
+ showInDOM: boolean;
delete(): void;
hide(): void;
show(): void;
@@ -18,6 +19,7 @@ export interface IDOMObject {
moveUp(): void;
moveDown(): void;
setZIndex(next: number): void;
+ setReady(): void;
}
export interface DOMObjectParams {
@@ -36,6 +38,7 @@ export class DOMObject implements IDOMObject {
public hidden = new BehaviorSubject(false);
public moveUp: () => void;
public moveDown: () => void;
+ public showInDOM = false;
protected onDelete: (id: string) => void;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -68,4 +71,8 @@ export class DOMObject implements IDOMObject {
setZIndex(next: number): void {
this.zIndex = next;
}
+
+ setReady() {
+ this.showInDOM = true;
+ }
}
diff --git a/src/core/Drawings.ts b/src/core/Drawings.ts
index 0363e3d9dafdbf3715a5e5005b1b0e6aa04e18d9..2875dbedc243e99b44a1d0603e541fc7411ad1d1 100644
--- a/src/core/Drawings.ts
+++ b/src/core/Drawings.ts
@@ -1,5 +1,9 @@
import { IChartApi, ISeriesApi, SeriesType } from 'lightweight-charts';
+import { firstValueFrom } from 'rxjs';
+
+import { filter } from 'rxjs/operators';
+
import { DOMObject, DOMObjectParams } from '@core/DOMObject';
import { ISeriesDrawing } from '@core/Drawings/common';
import { SeriesStrategies } from '@src/modules/series-strategies/SeriesFactory';
@@ -24,6 +28,8 @@ export class Drawing extends DOMObject implements IDrawing {
this.onDelete = onDelete;
this.mainSeries = mainSeries;
+
+ this.awaitCreation().then(() => this.setReady());
}
public delete() {
@@ -54,6 +60,10 @@ export class Drawing extends DOMObject implements IDrawing {
return this.lwcDrawing.isCreationPending();
}
+ public awaitCreation(): Promise<boolean> {
+ return firstValueFrom(this.lwcDrawing.isCreationPendingObs().pipe(filter((value) => !value)));
+ }
+
public destroy() {
this.mainSeries.detachPrimitive(this.lwcDrawing);
this.lwcDrawing.destroy();
diff --git a/src/core/Drawings/axisLine/axisLine.ts b/src/core/Drawings/axisLine/axisLine.ts
index 841ed2208185c5ea945354ecef25bd0237023745..cd7d4626beb7e7ed1b5d1502c46d258f59617fba 100644
--- a/src/core/Drawings/axisLine/axisLine.ts
+++ b/src/core/Drawings/axisLine/axisLine.ts
@@ -10,7 +10,7 @@ import {
Time,
UTCTimestamp,
} from 'lightweight-charts';
-import { Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { CustomPriceAxisView, CustomTimeAxisView } from '@core/Drawings/axis';
import {
@@ -76,6 +76,7 @@ export class AxisLine implements ISeriesDrawing {
private hidden = false;
private isActive = false;
private mode: AxisLineMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private direction: AxisLineDirection;
private time: Time | null = null;
@@ -163,7 +164,11 @@ export class AxisLine implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): AxisLineState {
@@ -189,6 +194,8 @@ export class AxisLine implements ISeriesDrawing {
if ('mode' in nextState && nextState.mode) {
this.mode = nextState.mode;
+
+ this.isBusy.next(this.mode === 'idle');
}
if ('time' in nextState) {
@@ -371,6 +378,7 @@ export class AxisLine implements ISeriesDrawing {
this.updateLine(point);
this.isActive = true;
+ this.isBusy.next(false);
this.mode = 'ready';
this.render();
return;
@@ -412,6 +420,7 @@ export class AxisLine implements ISeriesDrawing {
event.stopPropagation();
this.mode = 'dragging';
+ this.isBusy.next(false);
this.dragPointerId = event.pointerId;
this.hideCrosshair();
this.render();
@@ -435,6 +444,7 @@ export class AxisLine implements ISeriesDrawing {
}
this.mode = 'ready';
+ this.isBusy.next(false);
this.dragPointerId = null;
this.showCrosshair();
this.render();
diff --git a/src/core/Drawings/common.ts b/src/core/Drawings/common.ts
index 0acfe0bfdc32ddae8bd8e492e335cb8a56443f67..1c67536a3b5a1c8928f2218818f838ac7baf05ba 100644
--- a/src/core/Drawings/common.ts
+++ b/src/core/Drawings/common.ts
@@ -1,4 +1,5 @@
import { Coordinate, ISeriesApi, ISeriesPrimitive, SeriesType, Time } from 'lightweight-charts';
+import { Observable } from 'rxjs';
export interface BitmapPositionLength {
/** coordinate for use with a bitmap rendering scope */
@@ -24,6 +25,7 @@ export interface ISeriesDrawing extends ISeriesPrimitive<Time> {
rebind(series: ISeriesApi<SeriesType>): void;
destroy(): void;
isCreationPending(): boolean;
+ isCreationPendingObs(): Observable<boolean>;
}
export function positionsBox(position1Media: number, position2Media: number, pixelRatio: number): BitmapPositionLength {
diff --git a/src/core/Drawings/diapson/diapson.ts b/src/core/Drawings/diapson/diapson.ts
index 3c4ba3d553f706a326eb7e39c3fcd0dafaded05b..a5bbef7dc01939a29762bec9477502b93d2aa811 100644
--- a/src/core/Drawings/diapson/diapson.ts
+++ b/src/core/Drawings/diapson/diapson.ts
@@ -1,4 +1,4 @@
-import { Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -117,6 +117,7 @@ export class Diapson implements ISeriesDrawing {
private isActive = false;
private interactionMode: InteractionMode = 'idle';
private rangeMode: DiapsonRangeMode;
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private startTime: Time | null = null;
private endTime: Time | null = null;
@@ -231,7 +232,11 @@ export class Diapson implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.interactionMode === 'idle' || this.interactionMode === 'drawing';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public setRangeMode(nextMode: DiapsonRangeMode): void {
@@ -266,6 +271,7 @@ export class Diapson implements ISeriesDrawing {
this.hidden = typeof nextState.hidden === 'boolean' ? nextState.hidden : this.hidden;
this.isActive = typeof nextState.isActive === 'boolean' ? nextState.isActive : this.isActive;
this.interactionMode = nextState.interactionMode ?? this.interactionMode;
+ this.isBusy.next(this.interactionMode === 'idle' || this.interactionMode === 'drawing');
this.rangeMode = nextState.rangeMode ?? this.rangeMode;
this.startTime = 'startTime' in nextState ? (nextState.startTime ?? null) : this.startTime;
this.endTime = 'endTime' in nextState ? (nextState.endTime ?? null) : this.endTime;
@@ -598,6 +604,7 @@ export class Diapson implements ISeriesDrawing {
this.isActive = true;
this.interactionMode = 'drawing';
+ this.isBusy.next(true);
this.render();
}
@@ -633,11 +640,13 @@ export class Diapson implements ISeriesDrawing {
}
this.interactionMode = 'ready';
+ this.isBusy.next(false);
this.render();
}
private startDragging(point: Point, pointerId: number, dragTarget: Exclude<DiapsonHandle, null>): void {
this.interactionMode = 'dragging';
+ this.isBusy.next(false);
this.activeDragTarget = dragTarget;
this.dragPointerId = pointerId;
this.dragStartPoint = point;
@@ -648,6 +657,7 @@ export class Diapson implements ISeriesDrawing {
private finishDragging(): void {
this.interactionMode = 'ready';
+ this.isBusy.next(false);
this.clearInteractionState();
this.render();
}
@@ -663,6 +673,7 @@ export class Diapson implements ISeriesDrawing {
this.hidden = false;
this.isActive = false;
this.interactionMode = 'idle';
+ this.isBusy.next(true);
this.startTime = null;
this.endTime = null;
this.startPrice = null;
diff --git a/src/core/Drawings/ray/ray.ts b/src/core/Drawings/ray/ray.ts
index a677f6e5c4fbe67c8d2c4408d3319832a5b42f32..a8a4160440ee6ea0ae153bc1003f0dca20fecc49 100644
--- a/src/core/Drawings/ray/ray.ts
+++ b/src/core/Drawings/ray/ray.ts
@@ -10,7 +10,7 @@ import {
Time,
UTCTimestamp,
} from 'lightweight-charts';
-import { Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -89,6 +89,7 @@ export class Ray implements ISeriesDrawing {
private hidden = false;
private isActive = false;
private mode: RayMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private startAnchor: Anchor | null = null;
private directionAnchor: Anchor | null = null;
@@ -195,7 +196,11 @@ export class Ray implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle' || this.mode === 'drawing';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): RayState {
@@ -221,6 +226,7 @@ export class Ray implements ISeriesDrawing {
if ('mode' in nextState && nextState.mode) {
this.mode = nextState.mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
}
if ('startAnchor' in nextState) {
@@ -560,6 +566,8 @@ export class Ray implements ISeriesDrawing {
this.directionAnchor = anchor;
this.isActive = true;
this.mode = 'drawing';
+ this.isBusy.next(true);
+
this.render();
}
@@ -592,11 +600,15 @@ export class Ray implements ISeriesDrawing {
}
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
}
private startDragging(mode: RayMode, point: Point, pointerId: number): void {
this.mode = mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
+
this.dragPointerId = pointerId;
this.dragStartPoint = point;
this.dragStateSnapshot = this.getState();
@@ -607,6 +619,7 @@ export class Ray implements ISeriesDrawing {
private finishDragging(): void {
this.mode = 'ready';
+ this.isBusy.next(false);
this.dragPointerId = null;
this.dragStartPoint = null;
this.dragStateSnapshot = null;
diff --git a/src/core/Drawings/rectangle/rectangle.ts b/src/core/Drawings/rectangle/rectangle.ts
index 922d139498fc497655deef675f6fd3d227552f43..58120f5bdd3c1874b252d56c6d5d342b2eb64c07 100644
--- a/src/core/Drawings/rectangle/rectangle.ts
+++ b/src/core/Drawings/rectangle/rectangle.ts
@@ -1,4 +1,4 @@
-import { Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -100,6 +100,7 @@ export class Rectangle implements ISeriesDrawing {
private hidden = false;
private isActive = false;
private mode: RectangleMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private startTime: Time | null = null;
private endTime: Time | null = null;
@@ -207,7 +208,11 @@ export class Rectangle implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle' || this.mode === 'drawing';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): RectangleState {
@@ -235,6 +240,7 @@ export class Rectangle implements ISeriesDrawing {
if ('mode' in nextState && nextState.mode) {
this.mode = nextState.mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
}
if ('startTime' in nextState) {
@@ -577,6 +583,8 @@ export class Rectangle implements ISeriesDrawing {
this.isActive = true;
this.mode = 'drawing';
+ this.isBusy.next(true);
+
this.render();
}
@@ -608,11 +616,15 @@ export class Rectangle implements ISeriesDrawing {
}
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
}
private startDragging(point: Point, pointerId: number, dragTarget: Exclude<RectangleHandle, null>): void {
this.mode = 'dragging';
+ this.isBusy.next(false);
+
this.activeDragTarget = dragTarget;
this.dragPointerId = pointerId;
this.dragStartPoint = point;
@@ -624,6 +636,8 @@ export class Rectangle implements ISeriesDrawing {
private finishDragging(): void {
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.clearInteractionState();
this.render();
}
@@ -640,6 +654,8 @@ export class Rectangle implements ISeriesDrawing {
this.hidden = false;
this.isActive = false;
this.mode = 'idle';
+ this.isBusy.next(true);
+
this.startTime = null;
this.endTime = null;
this.startPrice = null;
diff --git a/src/core/Drawings/ruler/ruler.ts b/src/core/Drawings/ruler/ruler.ts
index b1924f2ceb8c1efbac24c20f2f0366bceee52d5d..993ac07a36ed6f331d61b4eb1b710d6dbf3613ac 100644
--- a/src/core/Drawings/ruler/ruler.ts
+++ b/src/core/Drawings/ruler/ruler.ts
@@ -1,4 +1,4 @@
-import { Observable, skip, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, skip, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -99,6 +99,7 @@ export class Ruler implements ISeriesDrawing {
private hidden = false;
private mode: RulerMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private startAnchor: Anchor | null = null;
private endAnchor: Anchor | null = null;
@@ -196,6 +197,7 @@ export class Ruler implements ISeriesDrawing {
this.subscriptions.unsubscribe();
this.series.detachPrimitive(this);
this.requestUpdate = null;
+ this.isBusy.next(false)
}
public rebind(series: SeriesApi): void {
@@ -214,7 +216,11 @@ export class Ruler implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle' || this.mode === 'placingEnd';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): RulerState {
@@ -231,6 +237,8 @@ export class Ruler implements ISeriesDrawing {
this.hidden = nextState.hidden ?? this.hidden;
this.mode = nextState.mode ?? this.mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'placingEnd');
+
this.startAnchor = nextState.startAnchor ?? this.startAnchor;
this.endAnchor = nextState.endAnchor ?? this.endAnchor;
@@ -591,6 +599,8 @@ export class Ruler implements ISeriesDrawing {
this.startAnchor = anchor;
this.endAnchor = anchor;
this.mode = 'placingEnd';
+ this.isBusy.next(true);
+
this.setCrosshairVisible(false);
this.render();
return;
@@ -599,6 +609,8 @@ export class Ruler implements ISeriesDrawing {
if (this.mode === 'placingEnd') {
this.endAnchor = anchor;
this.mode = 'ready';
+ this.isBusy.next(true);
+
this.setCrosshairVisible(true);
this.render();
}
diff --git a/src/core/Drawings/sliderPosition/sliderPosition.ts b/src/core/Drawings/sliderPosition/sliderPosition.ts
index fd462d6ba06f244078b45500d910e6db4b937212..cb3003d46053a2a1d0da266a3ed0cc497cc312d3 100644
--- a/src/core/Drawings/sliderPosition/sliderPosition.ts
+++ b/src/core/Drawings/sliderPosition/sliderPosition.ts
@@ -1,4 +1,4 @@
-import { Observable, skip, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, skip, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -121,6 +121,7 @@ export class SliderPosition implements ISeriesDrawing {
private hidden = false;
private active = true;
private mode: SliderMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private side: SliderSide;
private startTime: Time | null = null;
@@ -252,7 +253,11 @@ export class SliderPosition implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): SliderPositionState {
@@ -277,6 +282,8 @@ export class SliderPosition implements ISeriesDrawing {
this.hidden = next.hidden ?? this.hidden;
this.active = next.active ?? this.active;
this.mode = next.mode ?? this.mode;
+ this.isBusy.next(this.mode === 'idle');
+
this.startTime = next.startTime ?? this.startTime;
this.endTime = next.endTime ?? this.endTime;
this.entryPrice = next.entryPrice ?? this.entryPrice;
@@ -689,6 +696,8 @@ export class SliderPosition implements ISeriesDrawing {
this.active = true;
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
return;
}
@@ -718,6 +727,7 @@ export class SliderPosition implements ISeriesDrawing {
this.dragStateSnapshot = this.getState();
this.didDrag = false;
this.mode = 'dragging';
+ this.isBusy.next(false);
};
private handlePointerMove = (event: PointerEvent): void => {
@@ -745,6 +755,8 @@ export class SliderPosition implements ISeriesDrawing {
this.dragStateSnapshot = null;
this.didDrag = false;
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
};
diff --git a/src/core/Drawings/traectory/traectory.ts b/src/core/Drawings/traectory/traectory.ts
index 5705640615949f0c1024ac1bab00104e664dc4dd..2db579f756fbdd549932e60c6684f191a331eb21 100644
--- a/src/core/Drawings/traectory/traectory.ts
+++ b/src/core/Drawings/traectory/traectory.ts
@@ -8,7 +8,7 @@ import {
SeriesOptionsMap,
Time,
} from 'lightweight-charts';
-import { Observable } from 'rxjs';
+import { BehaviorSubject, Observable } from 'rxjs';
import { CustomPriceAxisPaneView, CustomTimeAxisPaneView } from '@core/Drawings/axis';
import {
@@ -76,6 +76,7 @@ export class Traectory implements ISeriesDrawing {
private hidden = false;
private isActive = false;
private mode: TraectoryMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private points: Anchor[] = [];
private previewAnchor: Anchor | null = null;
@@ -145,7 +146,11 @@ export class Traectory implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle' || this.mode === 'drawing';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): TraectoryState {
@@ -167,6 +172,8 @@ export class Traectory implements ISeriesDrawing {
this.hidden = typeof nextState.hidden === 'boolean' ? nextState.hidden : this.hidden;
this.isActive = typeof nextState.isActive === 'boolean' ? nextState.isActive : this.isActive;
this.mode = nextState.mode ?? this.mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
+
this.points = Array.isArray(nextState.points) ? nextState.points : this.points;
this.render();
@@ -457,6 +464,8 @@ export class Traectory implements ISeriesDrawing {
this.previewAnchor = anchor;
this.isActive = true;
this.mode = 'drawing';
+ this.isBusy.next(true);
+
this.render();
}
@@ -505,11 +514,15 @@ export class Traectory implements ISeriesDrawing {
this.previewAnchor = null;
this.isActive = true;
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
}
private startDraggingPoint(point: Point, pointerId: number, pointIndex: number): void {
this.mode = 'dragging-point';
+ this.isBusy.next(false);
+
this.dragPointerId = pointerId;
this.dragStartPoint = point;
this.dragPointIndex = pointIndex;
@@ -521,6 +534,8 @@ export class Traectory implements ISeriesDrawing {
private startDraggingBody(point: Point, pointerId: number): void {
this.mode = 'dragging-body';
+ this.isBusy.next(false);
+
this.dragPointerId = pointerId;
this.dragStartPoint = point;
this.dragPointIndex = null;
@@ -532,6 +547,8 @@ export class Traectory implements ISeriesDrawing {
private finishDragging(): void {
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.dragPointerId = null;
this.dragStartPoint = null;
this.dragPointIndex = null;
@@ -544,6 +561,8 @@ export class Traectory implements ISeriesDrawing {
private resetToIdle(): void {
this.isActive = false;
this.mode = 'idle';
+ this.isBusy.next(true);
+
this.points = [];
this.previewAnchor = null;
this.dragPointerId = null;
diff --git a/src/core/Drawings/trendLine/trendLine.ts b/src/core/Drawings/trendLine/trendLine.ts
index ce78d84c0a73fc8a553db98d7e9390099f45b71c..17c593deb09f0d9efcc3eaf525da92d645a3a1e7 100644
--- a/src/core/Drawings/trendLine/trendLine.ts
+++ b/src/core/Drawings/trendLine/trendLine.ts
@@ -10,7 +10,7 @@ import {
Time,
UTCTimestamp,
} from 'lightweight-charts';
-import { Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -87,6 +87,7 @@ export class TrendLine implements ISeriesDrawing {
private hidden = false;
private isActive = false;
private mode: TrendLineMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private startAnchor: Anchor | null = null;
private endAnchor: Anchor | null = null;
@@ -193,7 +194,11 @@ export class TrendLine implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle' || this.mode === 'drawing';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): TrendLineState {
@@ -219,6 +224,7 @@ export class TrendLine implements ISeriesDrawing {
if ('mode' in nextState && nextState.mode) {
this.mode = nextState.mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
}
if ('startAnchor' in nextState) {
@@ -558,6 +564,8 @@ export class TrendLine implements ISeriesDrawing {
this.endAnchor = anchor;
this.isActive = true;
this.mode = 'drawing';
+ this.isBusy.next(true);
+
this.render();
}
@@ -590,11 +598,15 @@ export class TrendLine implements ISeriesDrawing {
}
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
}
private startDragging(mode: TrendLineMode, point: Point, pointerId: number): void {
this.mode = mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
+
this.dragPointerId = pointerId;
this.dragStartPoint = point;
this.dragStateSnapshot = this.getState();
@@ -605,6 +617,8 @@ export class TrendLine implements ISeriesDrawing {
private finishDragging(): void {
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.dragPointerId = null;
this.dragStartPoint = null;
this.dragStateSnapshot = null;
diff --git a/src/core/Drawings/volumeProfile/volumeProfile.ts b/src/core/Drawings/volumeProfile/volumeProfile.ts
index 086688853b2c59a41ccac83dd4cf263e47c19b2c..c4e388607986089b038ba814101f65be514f9008 100644
--- a/src/core/Drawings/volumeProfile/volumeProfile.ts
+++ b/src/core/Drawings/volumeProfile/volumeProfile.ts
@@ -11,7 +11,7 @@ import {
Time,
UTCTimestamp,
} from 'lightweight-charts';
-import { Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import {
CustomPriceAxisPaneView,
@@ -120,6 +120,7 @@ export class VolumeProfile implements ISeriesDrawing {
private hidden = false;
private isActive = false;
private mode: VolumeProfileMode = 'idle';
+ private isBusy: BehaviorSubject<boolean> = new BehaviorSubject(true);
private startAnchor: Anchor | null = null;
private endAnchor: Anchor | null = null;
@@ -229,7 +230,11 @@ export class VolumeProfile implements ISeriesDrawing {
}
public isCreationPending(): boolean {
- return this.mode === 'idle' || this.mode === 'drawing';
+ return this.isBusy.value;
+ }
+
+ public isCreationPendingObs(): Observable<boolean> {
+ return this.isBusy.asObservable();
}
public getState(): VolumeProfileState {
@@ -248,6 +253,8 @@ export class VolumeProfile implements ISeriesDrawing {
this.hidden = typeof nextState.hidden === 'boolean' ? nextState.hidden : this.hidden;
this.isActive = typeof nextState.isActive === 'boolean' ? nextState.isActive : this.isActive;
this.mode = nextState.mode ?? this.mode;
+ this.isBusy.next(this.mode === 'idle' || this.mode === 'drawing');
+
this.startAnchor = nextState.startAnchor ?? this.startAnchor;
this.endAnchor = nextState.endAnchor ?? this.endAnchor;
@@ -582,6 +589,7 @@ export class VolumeProfile implements ISeriesDrawing {
this.endAnchor = anchor;
this.isActive = true;
this.mode = 'drawing';
+ this.isBusy.next(true);
this.calculateProfile();
this.render();
@@ -614,11 +622,15 @@ export class VolumeProfile implements ISeriesDrawing {
}
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.render();
}
private startDragging(point: Point, pointerId: number, dragTarget: Exclude<DragTarget, null>): void {
this.mode = 'dragging';
+ this.isBusy.next(false);
+
this.activeDragTarget = dragTarget;
this.dragPointerId = pointerId;
this.dragStartPoint = point;
@@ -630,6 +642,8 @@ export class VolumeProfile implements ISeriesDrawing {
private finishDragging(): void {
this.mode = 'ready';
+ this.isBusy.next(false);
+
this.activeDragTarget = null;
this.dragPointerId = null;
this.dragStartPoint = null;
@@ -643,6 +657,8 @@ export class VolumeProfile implements ISeriesDrawing {
this.hidden = false;
this.isActive = false;
this.mode = 'idle';
+ this.isBusy.next(true);
+
this.startAnchor = null;
this.endAnchor = null;
this.profileRows = [];
diff --git a/src/core/DrawingsManager.ts b/src/core/DrawingsManager.ts
index 4d77a731eab7c71d910539595cfaeb46ce802a00..6fac706710b499ba638050f9ba058db807178eae 100644
--- a/src/core/DrawingsManager.ts
+++ b/src/core/DrawingsManager.ts
@@ -174,7 +174,6 @@ export const drawingsMap: Record<DrawingsNames, DrawingConfig> = {
export class DrawingsManager {
private eventManager: EventManager;
private lwcChart: IChartApi;
- // private chartOptions?: ChartTypeOptions;
private drawingsQty = 0; // todo: replace with hash
private DOM: DOMModel;
private container: HTMLElement;
@@ -182,13 +181,14 @@ export class DrawingsManager {
private mainSeries: SeriesStrategies | null = null;
private subscriptions = new Subscription();
private drawings$: BehaviorSubject<Drawing[]> = new BehaviorSubject<Drawing[]>([]);
+ private isEndlessDrawingMode$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
+ private abortDrawings: AbortController = new AbortController();
private activeTool$ = new BehaviorSubject<ActiveDrawingTool>('crosshair');
constructor({ eventManager, mainSeries$, lwcChart, DOM, container }: DrawingsManagerParams) {
this.DOM = DOM;
this.eventManager = eventManager;
this.lwcChart = lwcChart;
- // this.chartOptions = chartOptions;
this.container = container;
this.subscriptions.add(
@@ -223,7 +223,7 @@ export class DrawingsManager {
this.removeDrawings([objectToRemove]);
};
- private removeDrawingsByName(name: DrawingsNames): void {
+ private removeDrawingsByName(name: string): void {
const drawingsToRemove = this.drawings$.value.filter((drawing) => drawing.id.startsWith(name));
this.removeDrawings(drawingsToRemove);
@@ -250,7 +250,13 @@ export class DrawingsManager {
this.updateActiveTool();
}
- public addDrawing(name: DrawingsNames) {
+ public addDrawingForce = async (name: DrawingsNames) => {
+ this.abortDrawings.abort();
+ this.removePendingDrawings();
+ await this.addDrawing(name);
+ };
+
+ public addDrawing = async (name: DrawingsNames) => {
if (!this.mainSeries) {
throw new Error('[Drawings] main series is not defined');
}
@@ -288,10 +294,33 @@ export class DrawingsManager {
construct,
});
- const trendLine = this.DOM.setEntity<Drawing>(drawing);
+ const entity = this.DOM.setEntity<Drawing>(drawing);
this.drawingsQty++;
- this.drawings$.next([...this.drawings$.value, trendLine]);
+ this.drawings$.next([...this.drawings$.value, entity]);
+
+ if (this.isEndlessDrawingMode$.value) {
+ this.abortDrawings.abort();
+ this.abortDrawings = new AbortController();
+ try {
+ await promiseWithAbort(entity.awaitCreation(), this.abortDrawings.signal);
+ await promiseWithAbort(this.addDrawing(name), this.abortDrawings.signal);
+ } catch (e) {
+ console.log('[Drawing manager] - drawing aborted');
+ }
+ }
+ };
+
+ public setEndlessDrawingMode = (value: boolean) => {
+ this.isEndlessDrawingMode$.next(value);
+
+ if (!value) {
+ this.removePendingDrawings();
+ }
+ };
+
+ public isEndlessDrawingsMode(): Observable<boolean> {
+ return this.isEndlessDrawingMode$;
}
public getActiveTool(): Observable<ActiveDrawingTool> {
@@ -318,3 +347,14 @@ export class DrawingsManager {
this.container.removeEventListener('click', this.updateActiveTool);
}
}
+
+function promiseWithAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {
+ if (signal?.aborted) {
+ return Promise.reject(new DOMException('Aborted', 'AbortError'));
+ }
+ const abortPromise = new Promise<never>((_, reject) => {
+ const onAbort = () => reject(new DOMException('Aborted', 'AbortError'));
+ signal?.addEventListener('abort', onAbort, { once: true });
+ });
+ return Promise.race([promise, abortPromise]);
+}
diff --git a/src/core/Indicator.ts b/src/core/Indicator.ts
index 8e9ca72a7245e84f4f9aae7a284dbbf8d9116360..cff7c70a7d6cfcf9998cc0661e575bb94689ce9d 100644
--- a/src/core/Indicator.ts
+++ b/src/core/Indicator.ts
@@ -66,6 +66,7 @@ export class Indicator extends DOMObject implements IIndicator {
this.createSeries();
this.associatedPane.setIndicator(this.id, this);
+ this.setReady();
}
public subscribeDataChange(handler: () => void): Subscription {
diff --git a/src/core/MoexChart.tsx b/src/core/MoexChart.tsx
index 30b3642943e369788d4019c6ebae2240d7bbba4e..d11fc031a25ed0791f590eb685d73d063f5eeffe 100644
--- a/src/core/MoexChart.tsx
+++ b/src/core/MoexChart.tsx
@@ -229,8 +229,10 @@ export class MoexChart {
toggleDOM={this.chart.getDom().toggleDOM}
addDrawing={(name) => {
// todo: deal with new panes logic
- this.chart.getDrawingsManager().addDrawing(name);
+ this.chart.getDrawingsManager().addDrawingForce(name);
}}
+ setEndlessDrawingsMode={this.chart.getDrawingsManager().setEndlessDrawingMode}
+ isEndlessDrawingsMode$={this.chart.getDrawingsManager().isEndlessDrawingsMode()}
activateCrosshair={() => this.chart.getDrawingsManager().activateCrosshair()}
activeTool$={this.chart.getDrawingsManager().getActiveTool()}
/>,
diff --git a/stories/TradeRadar/TradeRadar.stories.tsx b/stories/TradeRadar/TradeRadar.stories.tsx
index 1c573acdf0b3ac4bd8bd57616d80f1e925918844..10e2e9a4787dedec575df335bd6ce3e436bd49bb 100644
--- a/stories/TradeRadar/TradeRadar.stories.tsx
+++ b/stories/TradeRadar/TradeRadar.stories.tsx
@@ -126,7 +126,7 @@ const args: TRProps = {
showSettingsButton: true,
showControlBar: true,
tooltipConfig: {
- showTooltip: true,
+ showTooltip: false,
time: { visible: true, label: 'Время' },
close: { visible: true, label: 'Закр.' },
change: { visible: true, label: 'Изм.' },