Загрузка данных


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: 'Изм.' },