acttrader-charts 1.0.20 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{MACD-BDbBplVs.d.cts → MACD-DGFPNwy9.d.cts} +21 -0
- package/dist/{MACD-BDbBplVs.d.ts → MACD-DGFPNwy9.d.ts} +21 -0
- package/dist/index.cjs +381 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -5
- package/dist/index.d.ts +103 -5
- package/dist/index.js +381 -139
- package/dist/index.js.map +1 -1
- package/dist/indicators/index.d.cts +2 -2
- package/dist/indicators/index.d.ts +2 -2
- package/dist/webview/chart.html +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -11388,6 +11388,14 @@ var DrawingManager = class {
|
|
|
11388
11388
|
this.onDrawingsChange = null;
|
|
11389
11389
|
/** Called when a text-type drawing is first completed — ChartEngine shows the text input. */
|
|
11390
11390
|
this.onStartTextEdit = null;
|
|
11391
|
+
/**
|
|
11392
|
+
* Called after drag/resize mutates a drawing's points so ChartEngine can
|
|
11393
|
+
* refresh each point's `timestamp` from the current dataStore. Required
|
|
11394
|
+
* because DrawingManager has no access to the data layer, and drag
|
|
11395
|
+
* reconstructs points from raw (barIndex, price) deltas that strip the
|
|
11396
|
+
* original point's timestamp.
|
|
11397
|
+
*/
|
|
11398
|
+
this.onPointsMutated = null;
|
|
11391
11399
|
/** Set to true by ChartEngine while the text input is focused — prevents Delete/Backspace from deleting the drawing. */
|
|
11392
11400
|
this.editingText = false;
|
|
11393
11401
|
// Drag state (move whole drawing)
|
|
@@ -11482,14 +11490,18 @@ var DrawingManager = class {
|
|
|
11482
11490
|
const dBar = point.barIndex - this.dragStartMouse.barIndex;
|
|
11483
11491
|
const dPrice = point.price - this.dragStartMouse.price;
|
|
11484
11492
|
for (let i = 0; i < this.dragDrawing.points.length; i++) {
|
|
11493
|
+
const start = this.dragStartPoints[i];
|
|
11494
|
+
if (!start) continue;
|
|
11485
11495
|
this.dragDrawing.points[i] = {
|
|
11486
|
-
barIndex:
|
|
11487
|
-
price:
|
|
11496
|
+
barIndex: start.barIndex + dBar,
|
|
11497
|
+
price: start.price + dPrice
|
|
11488
11498
|
};
|
|
11489
11499
|
}
|
|
11500
|
+
this.onPointsMutated?.(this.dragDrawing);
|
|
11490
11501
|
}
|
|
11491
11502
|
if (this.resizeDrawing && this.resizePointIndex >= 0) {
|
|
11492
11503
|
this.resizeDrawing.points[this.resizePointIndex] = { ...point };
|
|
11504
|
+
this.onPointsMutated?.(this.resizeDrawing);
|
|
11493
11505
|
}
|
|
11494
11506
|
}
|
|
11495
11507
|
/**
|
|
@@ -17666,6 +17678,13 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17666
17678
|
/** Pending resize — committed only after mouse moves past DRAW_GESTURE_THRESHOLD. */
|
|
17667
17679
|
this._pendingDrawResize = null;
|
|
17668
17680
|
// px
|
|
17681
|
+
/**
|
|
17682
|
+
* Pending trade-level drag — committed only after mouse moves past TRADE_DRAG_PIXEL_THRESHOLD.
|
|
17683
|
+
* A mouseup before the threshold means it was a click: route to _openTradeLevelEditForm instead
|
|
17684
|
+
* of finishing a zero-delta drag (which would otherwise stage a spurious pending change).
|
|
17685
|
+
*/
|
|
17686
|
+
this._pendingTradeDrag = null;
|
|
17687
|
+
// px
|
|
17669
17688
|
// Pane resize
|
|
17670
17689
|
this.paneHeightOverrides = /* @__PURE__ */ new Map();
|
|
17671
17690
|
this.separatorDrag = null;
|
|
@@ -17681,6 +17700,21 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17681
17700
|
// lerp target; null = snap on next render
|
|
17682
17701
|
this._lockedAutoFitBase = null;
|
|
17683
17702
|
// non-null = user has manually adjusted Y-axis
|
|
17703
|
+
/**
|
|
17704
|
+
* When setState() restores a persisted viewport but data isn't loaded yet,
|
|
17705
|
+
* we stash the desired time window here and apply it from loadData() once
|
|
17706
|
+
* bars are available. Cleared after one application.
|
|
17707
|
+
*/
|
|
17708
|
+
this._pendingViewportRestore = null;
|
|
17709
|
+
/**
|
|
17710
|
+
* Debounces viewport/priceScale stateChange emissions during continuous
|
|
17711
|
+
* gestures (pan, wheel-zoom, price-scale drag). Pan/zoom don't emit
|
|
17712
|
+
* stateChange directly because doing so per-frame would be wasteful; this
|
|
17713
|
+
* timer coalesces bursts so the persisted viewport catches up a few ms
|
|
17714
|
+
* after the user stops interacting. Paired with the consumer's own
|
|
17715
|
+
* debounce (e.g. CRM's 800ms save) for minimal traffic.
|
|
17716
|
+
*/
|
|
17717
|
+
this._viewportPersistTimer = null;
|
|
17684
17718
|
this.axisDrag = null;
|
|
17685
17719
|
this.subPaneScaleFactors = /* @__PURE__ */ new Map();
|
|
17686
17720
|
this._mobileCrosshairActive = false;
|
|
@@ -17841,14 +17875,14 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17841
17875
|
const dx = x - this._pendingDrawDrag.startX;
|
|
17842
17876
|
const dy = y - this._pendingDrawDrag.startY;
|
|
17843
17877
|
if (Math.sqrt(dx * dx + dy * dy) > _ChartEngine.DRAW_GESTURE_THRESHOLD) {
|
|
17844
|
-
this.drawingManager.startDrag(
|
|
17845
|
-
|
|
17846
|
-
price
|
|
17847
|
-
|
|
17878
|
+
this.drawingManager.startDrag(
|
|
17879
|
+
this._pendingDrawDrag.drawing,
|
|
17880
|
+
this.makeDrawingPoint(rawBarIndex, price)
|
|
17881
|
+
);
|
|
17848
17882
|
this._pendingDrawDrag = null;
|
|
17849
17883
|
}
|
|
17850
17884
|
}
|
|
17851
|
-
this.drawingManager.onMouseMove(
|
|
17885
|
+
this.drawingManager.onMouseMove(this.makeDrawingPoint(rawBarIndex, price));
|
|
17852
17886
|
if (this.axisDrag) {
|
|
17853
17887
|
if (this.axisDrag.axis === "price") {
|
|
17854
17888
|
const delta = y - this.axisDrag.startPos;
|
|
@@ -17890,6 +17924,18 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17890
17924
|
this.scheduleRender();
|
|
17891
17925
|
return;
|
|
17892
17926
|
}
|
|
17927
|
+
if (this._pendingTradeDrag) {
|
|
17928
|
+
const dx = x - this._pendingTradeDrag.startX;
|
|
17929
|
+
const dy = y - this._pendingTradeDrag.startY;
|
|
17930
|
+
if (Math.sqrt(dx * dx + dy * dy) > _ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD) {
|
|
17931
|
+
const { dragHit, level, hitY } = this._pendingTradeDrag;
|
|
17932
|
+
this._pendingTradeDrag = null;
|
|
17933
|
+
this._startTradeDragFromHit(dragHit, level, hitY);
|
|
17934
|
+
this.drawCanvas.style.cursor = "ns-resize";
|
|
17935
|
+
} else {
|
|
17936
|
+
return;
|
|
17937
|
+
}
|
|
17938
|
+
}
|
|
17893
17939
|
if (this.tradeDragHandler?.active) {
|
|
17894
17940
|
const moveResult = this.tradeDragHandler.onMouseMove(
|
|
17895
17941
|
y,
|
|
@@ -17930,6 +17976,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17930
17976
|
const dragHover = hitTestTradeLevelDrag(x, y, this.tradeLevelDragAreas);
|
|
17931
17977
|
if (dragHover) {
|
|
17932
17978
|
this.drawCanvas.style.cursor = "ns-resize";
|
|
17979
|
+
if (dragHover.label !== this.hoveredTradeLabel) {
|
|
17980
|
+
this.hoveredTradeLabel = dragHover.label;
|
|
17981
|
+
this.renderTradeLayer();
|
|
17982
|
+
}
|
|
17933
17983
|
this.scheduleRender();
|
|
17934
17984
|
return;
|
|
17935
17985
|
}
|
|
@@ -17999,6 +18049,16 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17999
18049
|
this.scheduleRender();
|
|
18000
18050
|
};
|
|
18001
18051
|
this.onMouseUp = () => {
|
|
18052
|
+
if (this._pendingTradeDrag) {
|
|
18053
|
+
const { level } = this._pendingTradeDrag;
|
|
18054
|
+
this._pendingTradeDrag = null;
|
|
18055
|
+
if (this._editOpenLabel === level.label && !this.pendingLevelDrags.has(level.label)) {
|
|
18056
|
+
this._cancelEditAndDeselect(level.label);
|
|
18057
|
+
return;
|
|
18058
|
+
}
|
|
18059
|
+
this._openTradeLevelEditForm(level);
|
|
18060
|
+
return;
|
|
18061
|
+
}
|
|
18002
18062
|
if (this.tradeDragHandler?.active) {
|
|
18003
18063
|
const result = this.tradeDragHandler.endDrag();
|
|
18004
18064
|
if (result) {
|
|
@@ -18015,9 +18075,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18015
18075
|
return;
|
|
18016
18076
|
}
|
|
18017
18077
|
if (this.axisDrag) {
|
|
18078
|
+
const wasPriceAxis = this.axisDrag.axis === "price" || this.axisDrag.axis === "time";
|
|
18018
18079
|
this.axisDrag = null;
|
|
18019
18080
|
this.drawCanvas.style.cursor = "default";
|
|
18020
18081
|
this.scheduleRender();
|
|
18082
|
+
if (wasPriceAxis) this.scheduleViewportPersist();
|
|
18021
18083
|
}
|
|
18022
18084
|
if (this.separatorDrag) {
|
|
18023
18085
|
this.separatorDrag = null;
|
|
@@ -18179,22 +18241,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18179
18241
|
if (editHit) {
|
|
18180
18242
|
e.preventDefault();
|
|
18181
18243
|
const level = this.levels.find((l) => l.label === editHit.label);
|
|
18182
|
-
if (level)
|
|
18183
|
-
this.selectedTradeLabel = level.label;
|
|
18184
|
-
this.renderTradeLayer();
|
|
18185
|
-
const { slPrice: _slE, tpPrice: _tpE } = this._resolveBracketPrices(level);
|
|
18186
|
-
this._editOpenLabel = level.label;
|
|
18187
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
18188
|
-
label: level.label,
|
|
18189
|
-
type: level.type,
|
|
18190
|
-
data: level.data,
|
|
18191
|
-
price: level.price,
|
|
18192
|
-
side: level.side,
|
|
18193
|
-
stopLossPrice: _slE,
|
|
18194
|
-
takeProfitPrice: _tpE,
|
|
18195
|
-
isFullscreen: this.isFullscreen
|
|
18196
|
-
});
|
|
18197
|
-
}
|
|
18244
|
+
if (level) this._openTradeLevelEditForm(level);
|
|
18198
18245
|
return;
|
|
18199
18246
|
}
|
|
18200
18247
|
}
|
|
@@ -18209,23 +18256,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18209
18256
|
if (this.hideLevelConfirmCancel && !isMobileDraggable) {
|
|
18210
18257
|
const hasPending = this.pendingLevelDrags.has(level.label);
|
|
18211
18258
|
if (this.selectedTradeLabel === level.label && !hasPending) {
|
|
18212
|
-
this.
|
|
18213
|
-
|
|
18214
|
-
this.selectedTradeLabel = level.label;
|
|
18259
|
+
this._cancelEditAndDeselect(level.label);
|
|
18260
|
+
return;
|
|
18215
18261
|
}
|
|
18216
|
-
this.
|
|
18217
|
-
const { slPrice: _sl1, tpPrice: _tp1 } = this._resolveBracketPrices(level);
|
|
18218
|
-
this._editOpenLabel = level.label;
|
|
18219
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
18220
|
-
label: level.label,
|
|
18221
|
-
type: level.type,
|
|
18222
|
-
data: level.data,
|
|
18223
|
-
price: level.price,
|
|
18224
|
-
side: level.side,
|
|
18225
|
-
stopLossPrice: _sl1,
|
|
18226
|
-
takeProfitPrice: _tp1,
|
|
18227
|
-
isFullscreen: this.isFullscreen
|
|
18228
|
-
});
|
|
18262
|
+
this._openTradeLevelEditForm(level);
|
|
18229
18263
|
return;
|
|
18230
18264
|
}
|
|
18231
18265
|
this._startTradeDragFromHit(dragHit, level, y);
|
|
@@ -18249,23 +18283,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18249
18283
|
if (level) {
|
|
18250
18284
|
const hasPending = this.pendingLevelDrags.has(hoverHit.label);
|
|
18251
18285
|
if (this.selectedTradeLabel === hoverHit.label && !hasPending) {
|
|
18252
|
-
this.
|
|
18253
|
-
|
|
18254
|
-
this.selectedTradeLabel = hoverHit.label;
|
|
18286
|
+
this._cancelEditAndDeselect(hoverHit.label);
|
|
18287
|
+
return;
|
|
18255
18288
|
}
|
|
18256
|
-
|
|
18257
|
-
this._editOpenLabel = level.label;
|
|
18258
|
-
this.renderTradeLayer();
|
|
18259
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
18260
|
-
label: level.label,
|
|
18261
|
-
type: level.type,
|
|
18262
|
-
data: level.data,
|
|
18263
|
-
price: level.price,
|
|
18264
|
-
side: level.side,
|
|
18265
|
-
stopLossPrice: _slH,
|
|
18266
|
-
takeProfitPrice: _tpH,
|
|
18267
|
-
isFullscreen: this.isFullscreen
|
|
18268
|
-
});
|
|
18289
|
+
this._openTradeLevelEditForm(level);
|
|
18269
18290
|
return;
|
|
18270
18291
|
}
|
|
18271
18292
|
}
|
|
@@ -18464,8 +18485,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18464
18485
|
return;
|
|
18465
18486
|
}
|
|
18466
18487
|
if (this.axisDrag) {
|
|
18488
|
+
const wasPriceAxis = this.axisDrag.axis === "price" || this.axisDrag.axis === "time";
|
|
18467
18489
|
this.axisDrag = null;
|
|
18468
18490
|
this.scheduleRender();
|
|
18491
|
+
if (wasPriceAxis) this.scheduleViewportPersist();
|
|
18469
18492
|
return;
|
|
18470
18493
|
}
|
|
18471
18494
|
const wasAnchorTouch = this._drawTouchAnchorActive;
|
|
@@ -18550,22 +18573,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18550
18573
|
const editHit = hitTestTradeLevelEdit(x, y, this.tradeLevelEditAreas);
|
|
18551
18574
|
if (editHit) {
|
|
18552
18575
|
const level = this.levels.find((l) => l.label === editHit.label);
|
|
18553
|
-
if (level)
|
|
18554
|
-
this.selectedTradeLabel = level.label;
|
|
18555
|
-
this.renderTradeLayer();
|
|
18556
|
-
const { slPrice: _sl2, tpPrice: _tp2 } = this._resolveBracketPrices(level);
|
|
18557
|
-
this._editOpenLabel = level.label;
|
|
18558
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
18559
|
-
label: level.label,
|
|
18560
|
-
type: level.type,
|
|
18561
|
-
data: level.data,
|
|
18562
|
-
price: level.price,
|
|
18563
|
-
side: level.side,
|
|
18564
|
-
stopLossPrice: _sl2,
|
|
18565
|
-
takeProfitPrice: _tp2,
|
|
18566
|
-
isFullscreen: this.isFullscreen
|
|
18567
|
-
});
|
|
18568
|
-
}
|
|
18576
|
+
if (level) this._openTradeLevelEditForm(level);
|
|
18569
18577
|
return;
|
|
18570
18578
|
}
|
|
18571
18579
|
}
|
|
@@ -18583,8 +18591,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18583
18591
|
const level = this.levels.find((l) => l.label === dragHit.label);
|
|
18584
18592
|
if (level) {
|
|
18585
18593
|
if (!(this.hideLevelConfirmCancel && !dragHit.isBracket)) {
|
|
18586
|
-
this.
|
|
18587
|
-
this.drawCanvas.style.cursor = "ns-resize";
|
|
18594
|
+
this._pendingTradeDrag = { dragHit, level, hitY: y, startX: x, startY: y };
|
|
18588
18595
|
return;
|
|
18589
18596
|
}
|
|
18590
18597
|
}
|
|
@@ -18594,49 +18601,24 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18594
18601
|
if (hoverHit) {
|
|
18595
18602
|
const hasPending = this.pendingLevelDrags.has(hoverHit.label);
|
|
18596
18603
|
if (this.selectedTradeLabel === hoverHit.label && !hasPending) {
|
|
18597
|
-
this.
|
|
18604
|
+
this._cancelEditAndDeselect(hoverHit.label);
|
|
18605
|
+
return;
|
|
18606
|
+
}
|
|
18607
|
+
const level = this.levels.find((l) => l.label === hoverHit.label);
|
|
18608
|
+
if (level) {
|
|
18609
|
+
this._openTradeLevelEditForm(level);
|
|
18598
18610
|
} else {
|
|
18599
18611
|
this.selectedTradeLabel = hoverHit.label;
|
|
18600
|
-
|
|
18601
|
-
this.renderTradeLayer();
|
|
18602
|
-
if (this.hideLevelConfirmCancel) {
|
|
18603
|
-
const level = this.levels.find((l) => l.label === hoverHit.label);
|
|
18604
|
-
if (level) {
|
|
18605
|
-
const { slPrice: _sl3, tpPrice: _tp3 } = this._resolveBracketPrices(level);
|
|
18606
|
-
this._editOpenLabel = level.label;
|
|
18607
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
18608
|
-
label: level.label,
|
|
18609
|
-
type: level.type,
|
|
18610
|
-
data: level.data,
|
|
18611
|
-
price: level.price,
|
|
18612
|
-
side: level.side,
|
|
18613
|
-
stopLossPrice: _sl3,
|
|
18614
|
-
takeProfitPrice: _tp3,
|
|
18615
|
-
isFullscreen: this.isFullscreen
|
|
18616
|
-
});
|
|
18617
|
-
}
|
|
18612
|
+
this.renderTradeLayer();
|
|
18618
18613
|
}
|
|
18619
18614
|
return;
|
|
18620
18615
|
} else if (this.selectedTradeLabel !== null) {
|
|
18621
18616
|
if (!this.hideLevelConfirmCancel) {
|
|
18622
18617
|
const lbl = this.selectedTradeLabel;
|
|
18623
18618
|
const pending = this.pendingLevelDrags.get(lbl);
|
|
18624
|
-
const isEditFormOpen = this._editOpenLabel === lbl;
|
|
18625
18619
|
if (!pending || pending.length === 0) {
|
|
18626
|
-
|
|
18627
|
-
|
|
18628
|
-
this._editOpenLabel = null;
|
|
18629
|
-
if (lvl) {
|
|
18630
|
-
this.emitter.emit("tradeLevelEditCancelled", {
|
|
18631
|
-
label: lbl,
|
|
18632
|
-
type: lvl.type,
|
|
18633
|
-
isFullscreen: this.isFullscreen
|
|
18634
|
-
});
|
|
18635
|
-
}
|
|
18636
|
-
}
|
|
18637
|
-
this.selectedTradeLabel = null;
|
|
18638
|
-
this.renderTradeLayer();
|
|
18639
|
-
} else if (pending.every((c) => c.isNew)) {
|
|
18620
|
+
this._cancelEditAndDeselect(lbl);
|
|
18621
|
+
} else {
|
|
18640
18622
|
this.revertPendingChanges(lbl);
|
|
18641
18623
|
}
|
|
18642
18624
|
}
|
|
@@ -18691,10 +18673,9 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18691
18673
|
this.scheduleRender();
|
|
18692
18674
|
return;
|
|
18693
18675
|
}
|
|
18694
|
-
const consumed = this.drawingManager.onMouseDown(
|
|
18695
|
-
|
|
18696
|
-
|
|
18697
|
-
});
|
|
18676
|
+
const consumed = this.drawingManager.onMouseDown(
|
|
18677
|
+
this.makeDrawingPoint(rawBarIndex, price)
|
|
18678
|
+
);
|
|
18698
18679
|
if (!consumed) {
|
|
18699
18680
|
const handleHit = this.drawingManager.getHandleAtPoint(
|
|
18700
18681
|
x,
|
|
@@ -19393,6 +19374,12 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19393
19374
|
this.drawCanvas.style.cursor = "default";
|
|
19394
19375
|
});
|
|
19395
19376
|
this.drawingManager.onDrawingsChange = () => this.emitStateChange();
|
|
19377
|
+
this.drawingManager.onPointsMutated = (drawing) => {
|
|
19378
|
+
for (const pt of drawing.points) {
|
|
19379
|
+
const t = this.barIndexToTimestamp(pt.barIndex);
|
|
19380
|
+
if (t != null) pt.timestamp = t;
|
|
19381
|
+
}
|
|
19382
|
+
};
|
|
19396
19383
|
this.drawingManager.onStartTextEdit = (drawing) => this.showTextInput(drawing);
|
|
19397
19384
|
this.panHandler = new PanHandler(
|
|
19398
19385
|
this.drawCanvas,
|
|
@@ -19402,6 +19389,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19402
19389
|
this.clearDuration();
|
|
19403
19390
|
this.checkNeedMoreData();
|
|
19404
19391
|
this.scheduleRender();
|
|
19392
|
+
this.scheduleViewportPersist();
|
|
19405
19393
|
},
|
|
19406
19394
|
() => this.dataStore.length,
|
|
19407
19395
|
() => this.scheduleRender(),
|
|
@@ -19413,6 +19401,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19413
19401
|
const pricePerPixel = (currentRange.max - currentRange.min) / priceH;
|
|
19414
19402
|
this.pricePanOffset += deltaY * pricePerPixel;
|
|
19415
19403
|
this.scheduleRender();
|
|
19404
|
+
this.scheduleViewportPersist();
|
|
19416
19405
|
},
|
|
19417
19406
|
() => {
|
|
19418
19407
|
this._isPanning = true;
|
|
@@ -19435,6 +19424,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19435
19424
|
this.clearDuration();
|
|
19436
19425
|
this.checkNeedMoreData();
|
|
19437
19426
|
this.scheduleRender();
|
|
19427
|
+
this.scheduleViewportPersist();
|
|
19438
19428
|
},
|
|
19439
19429
|
() => this.dataStore.length,
|
|
19440
19430
|
() => this.scheduleRender(),
|
|
@@ -19455,6 +19445,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19455
19445
|
this.clearDuration();
|
|
19456
19446
|
this.checkNeedMoreData();
|
|
19457
19447
|
this.scheduleRender();
|
|
19448
|
+
this.scheduleViewportPersist();
|
|
19458
19449
|
}
|
|
19459
19450
|
);
|
|
19460
19451
|
this.drawCanvas.addEventListener("mousemove", this.onMouseMove);
|
|
@@ -19492,6 +19483,15 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19492
19483
|
return true;
|
|
19493
19484
|
}).map((s) => s.indicator);
|
|
19494
19485
|
}
|
|
19486
|
+
/** Schedules a stateChange emission so viewport/priceScale changes made by
|
|
19487
|
+
* pan/zoom/price-scale drag reach persistence. Coalesces rapid calls. */
|
|
19488
|
+
scheduleViewportPersist() {
|
|
19489
|
+
if (this._viewportPersistTimer) clearTimeout(this._viewportPersistTimer);
|
|
19490
|
+
this._viewportPersistTimer = setTimeout(() => {
|
|
19491
|
+
this._viewportPersistTimer = null;
|
|
19492
|
+
this.emitStateChange();
|
|
19493
|
+
}, 500);
|
|
19494
|
+
}
|
|
19495
19495
|
get isFullscreen() {
|
|
19496
19496
|
return this._isCssFullscreen || !!document.fullscreenElement;
|
|
19497
19497
|
}
|
|
@@ -19583,6 +19583,22 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19583
19583
|
}
|
|
19584
19584
|
this.recomputeIndicators();
|
|
19585
19585
|
this.reanchorDrawingsByTimestamp();
|
|
19586
|
+
if (this._pendingViewportRestore) {
|
|
19587
|
+
const pending = this._pendingViewportRestore;
|
|
19588
|
+
this._pendingViewportRestore = null;
|
|
19589
|
+
if (pending.priceScale) {
|
|
19590
|
+
this.priceScaleFactor = pending.priceScale.factor;
|
|
19591
|
+
this.pricePanOffset = pending.priceScale.panOffset;
|
|
19592
|
+
this._displayedPriceRange = null;
|
|
19593
|
+
this._lockedAutoFitBase = null;
|
|
19594
|
+
}
|
|
19595
|
+
if (pending.viewport) {
|
|
19596
|
+
this.applyViewportFromTimestamps(
|
|
19597
|
+
pending.viewport.startTime,
|
|
19598
|
+
pending.viewport.endTime
|
|
19599
|
+
);
|
|
19600
|
+
}
|
|
19601
|
+
}
|
|
19586
19602
|
this.updateCountdownState();
|
|
19587
19603
|
this.scheduleRender();
|
|
19588
19604
|
return this;
|
|
@@ -19603,6 +19619,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19603
19619
|
};
|
|
19604
19620
|
this._targetViewport = { ...this.viewport };
|
|
19605
19621
|
this.drawingManager.shiftBarIndices(n);
|
|
19622
|
+
this.reanchorDrawingsByTimestamp();
|
|
19606
19623
|
this.recomputeIndicators();
|
|
19607
19624
|
this.scheduleRender();
|
|
19608
19625
|
}
|
|
@@ -20126,15 +20143,44 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20126
20143
|
...d,
|
|
20127
20144
|
points: d.points.map((p) => ({
|
|
20128
20145
|
...p,
|
|
20129
|
-
timestamp
|
|
20146
|
+
// Prefer the point's own timestamp (captured at placement / drag /
|
|
20147
|
+
// resize); only derive from current bars as a fallback for legacy
|
|
20148
|
+
// points. Re-deriving on every export would otherwise corrupt the
|
|
20149
|
+
// anchor after a timeframe change has staled the cached barIndex.
|
|
20150
|
+
timestamp: p.timestamp ?? this.barIndexToTimestamp(p.barIndex)
|
|
20130
20151
|
}))
|
|
20131
20152
|
})),
|
|
20132
20153
|
positionRenderStyle: this.positionRenderStyle,
|
|
20133
20154
|
tradeDisplayFilter: this.tradeDisplayFilter,
|
|
20134
20155
|
canvasColors: Object.keys(this.settingsCanvasColors).length > 0 ? { ...this.settingsCanvasColors } : void 0,
|
|
20135
|
-
tfcEnabled: this.tfcActive
|
|
20156
|
+
tfcEnabled: this.tfcActive,
|
|
20157
|
+
viewport: this.getViewportTimestamps(),
|
|
20158
|
+
priceScale: this.priceScaleFactor !== 1 || this.pricePanOffset !== 0 ? { factor: this.priceScaleFactor, panOffset: this.pricePanOffset } : void 0
|
|
20136
20159
|
};
|
|
20137
20160
|
}
|
|
20161
|
+
/**
|
|
20162
|
+
* Captures the current viewport as a {startTime, endTime} timestamp pair,
|
|
20163
|
+
* preserving sub-bar fractional position. Returns undefined only when no
|
|
20164
|
+
* bars are loaded (viewport has no meaningful time anchor).
|
|
20165
|
+
*/
|
|
20166
|
+
getViewportTimestamps() {
|
|
20167
|
+
if (this.dataStore.all.length === 0) return void 0;
|
|
20168
|
+
const startTime = this.barIndexToTimestamp(this.viewport.startIndex);
|
|
20169
|
+
const endTime = this.barIndexToTimestamp(this.viewport.endIndex);
|
|
20170
|
+
if (startTime == null || endTime == null) return void 0;
|
|
20171
|
+
return { startTime, endTime };
|
|
20172
|
+
}
|
|
20173
|
+
/**
|
|
20174
|
+
* Restores the viewport from a timestamp pair, converting each edge back
|
|
20175
|
+
* into a fractional bar index relative to the current dataStore. Callers
|
|
20176
|
+
* must ensure bars are loaded before invoking.
|
|
20177
|
+
*/
|
|
20178
|
+
applyViewportFromTimestamps(startTime, endTime) {
|
|
20179
|
+
const startIndex = this.timestampToBarIndex(startTime);
|
|
20180
|
+
const endIndex = this.timestampToBarIndex(endTime);
|
|
20181
|
+
this.viewport = { ...this.viewport, startIndex, endIndex };
|
|
20182
|
+
this._targetViewport = { ...this.viewport };
|
|
20183
|
+
}
|
|
20138
20184
|
/**
|
|
20139
20185
|
* Restores chart configuration from a previously captured `ChartState`.
|
|
20140
20186
|
* Call this after construction to hydrate a chart from persisted state.
|
|
@@ -20181,18 +20227,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20181
20227
|
}
|
|
20182
20228
|
}
|
|
20183
20229
|
}
|
|
20184
|
-
const bars = this.dataStore.all;
|
|
20185
20230
|
const resolvedDrawings = state.drawings.map((d) => ({
|
|
20186
20231
|
...d,
|
|
20187
20232
|
points: d.points.map((p) => {
|
|
20188
|
-
if (
|
|
20189
|
-
|
|
20190
|
-
while (lo < hi) {
|
|
20191
|
-
const mid = lo + hi >> 1;
|
|
20192
|
-
if ((bars[mid]?.time ?? 0) < p.timestamp) lo = mid + 1;
|
|
20193
|
-
else hi = mid;
|
|
20194
|
-
}
|
|
20195
|
-
return { ...p, barIndex: lo + (p.barIndex - Math.floor(p.barIndex)) };
|
|
20233
|
+
if (p.timestamp == null || this.dataStore.all.length === 0) return p;
|
|
20234
|
+
return { ...p, barIndex: this.timestampToBarIndex(p.timestamp) };
|
|
20196
20235
|
})
|
|
20197
20236
|
}));
|
|
20198
20237
|
this.drawingManager.loadDrawings(resolvedDrawings);
|
|
@@ -20210,6 +20249,25 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20210
20249
|
this.tfcActive = state.tfcEnabled;
|
|
20211
20250
|
this.topBar?.setTfcActive(state.tfcEnabled);
|
|
20212
20251
|
}
|
|
20252
|
+
if (state.priceScale) {
|
|
20253
|
+
this.priceScaleFactor = state.priceScale.factor;
|
|
20254
|
+
this.pricePanOffset = state.priceScale.panOffset;
|
|
20255
|
+
this._displayedPriceRange = null;
|
|
20256
|
+
this._lockedAutoFitBase = null;
|
|
20257
|
+
}
|
|
20258
|
+
if (state.viewport) {
|
|
20259
|
+
if (this.dataStore.all.length > 0) {
|
|
20260
|
+
this.applyViewportFromTimestamps(
|
|
20261
|
+
state.viewport.startTime,
|
|
20262
|
+
state.viewport.endTime
|
|
20263
|
+
);
|
|
20264
|
+
} else {
|
|
20265
|
+
this._pendingViewportRestore = {
|
|
20266
|
+
viewport: state.viewport,
|
|
20267
|
+
priceScale: state.priceScale
|
|
20268
|
+
};
|
|
20269
|
+
}
|
|
20270
|
+
}
|
|
20213
20271
|
} finally {
|
|
20214
20272
|
this.isRestoringState = false;
|
|
20215
20273
|
}
|
|
@@ -20218,23 +20276,120 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20218
20276
|
return this;
|
|
20219
20277
|
}
|
|
20220
20278
|
/**
|
|
20221
|
-
*
|
|
20222
|
-
*
|
|
20223
|
-
*
|
|
20279
|
+
* Converts a fractional barIndex into an absolute unix-ms timestamp by
|
|
20280
|
+
* interpolating across the containing bar. Extrapolates linearly for indexes
|
|
20281
|
+
* outside [0, bars.length-1] using the adjacent bar's width.
|
|
20282
|
+
*
|
|
20283
|
+
* This is the inverse of `timestampToBarIndex` and the authoritative way to
|
|
20284
|
+
* capture "where in time" a drawing point sits at placement, drag, or resize.
|
|
20285
|
+
* Returns undefined only when no bars are loaded.
|
|
20286
|
+
*/
|
|
20287
|
+
barIndexToTimestamp(idx) {
|
|
20288
|
+
const bars = this.dataStore.all;
|
|
20289
|
+
const n = bars.length;
|
|
20290
|
+
if (n === 0) return void 0;
|
|
20291
|
+
const first = bars[0];
|
|
20292
|
+
if (!first) return void 0;
|
|
20293
|
+
if (n === 1) return first.time;
|
|
20294
|
+
const floorIdx = Math.floor(idx);
|
|
20295
|
+
const frac = idx - floorIdx;
|
|
20296
|
+
if (floorIdx < 0) {
|
|
20297
|
+
const second = bars[1];
|
|
20298
|
+
if (!second) return first.time;
|
|
20299
|
+
return first.time + idx * (second.time - first.time);
|
|
20300
|
+
}
|
|
20301
|
+
if (floorIdx >= n - 1) {
|
|
20302
|
+
const last = bars[n - 1];
|
|
20303
|
+
const prev = bars[n - 2];
|
|
20304
|
+
if (!last || !prev) return first.time;
|
|
20305
|
+
return last.time + (idx - (n - 1)) * (last.time - prev.time);
|
|
20306
|
+
}
|
|
20307
|
+
const lo = bars[floorIdx];
|
|
20308
|
+
const hi = bars[floorIdx + 1];
|
|
20309
|
+
if (!lo || !hi) return first.time;
|
|
20310
|
+
return lo.time + frac * (hi.time - lo.time);
|
|
20311
|
+
}
|
|
20312
|
+
/**
|
|
20313
|
+
* Converts a unix-ms timestamp into a fractional barIndex using floor-bar
|
|
20314
|
+
* containment: the result is `floorBar + (ts - floorBar.time) / (nextBar.time - floorBar.time)`.
|
|
20315
|
+
* Extrapolates linearly for timestamps outside the loaded range using the edge
|
|
20316
|
+
* bar width, so drawings outside the visible data still render at the correct
|
|
20317
|
+
* relative offset rather than snapping to an edge.
|
|
20318
|
+
*
|
|
20319
|
+
* Matches TradingView's floor-bar semantics and preserves sub-bar time
|
|
20320
|
+
* precision across timeframe changes.
|
|
20321
|
+
*/
|
|
20322
|
+
timestampToBarIndex(ts) {
|
|
20323
|
+
const bars = this.dataStore.all;
|
|
20324
|
+
const n = bars.length;
|
|
20325
|
+
if (n === 0) return 0;
|
|
20326
|
+
const first = bars[0];
|
|
20327
|
+
if (!first) return 0;
|
|
20328
|
+
if (n === 1) return 0;
|
|
20329
|
+
if (ts <= first.time) {
|
|
20330
|
+
const second = bars[1];
|
|
20331
|
+
if (!second) return 0;
|
|
20332
|
+
const barMs2 = second.time - first.time;
|
|
20333
|
+
return barMs2 > 0 ? (ts - first.time) / barMs2 : 0;
|
|
20334
|
+
}
|
|
20335
|
+
const lastIdx = n - 1;
|
|
20336
|
+
const lastBar = bars[lastIdx];
|
|
20337
|
+
const prevBar = bars[lastIdx - 1];
|
|
20338
|
+
if (!lastBar || !prevBar) return 0;
|
|
20339
|
+
if (ts >= lastBar.time) {
|
|
20340
|
+
const barMs2 = lastBar.time - prevBar.time;
|
|
20341
|
+
return lastIdx + (barMs2 > 0 ? (ts - lastBar.time) / barMs2 : 0);
|
|
20342
|
+
}
|
|
20343
|
+
let lo = 0, hi = lastIdx;
|
|
20344
|
+
while (lo < hi) {
|
|
20345
|
+
const mid = lo + hi + 1 >> 1;
|
|
20346
|
+
if ((bars[mid]?.time ?? 0) <= ts) lo = mid;
|
|
20347
|
+
else hi = mid - 1;
|
|
20348
|
+
}
|
|
20349
|
+
const loBar = bars[lo];
|
|
20350
|
+
const hiBar = bars[lo + 1];
|
|
20351
|
+
if (!loBar || !hiBar) return lo;
|
|
20352
|
+
const barMs = hiBar.time - loBar.time;
|
|
20353
|
+
return lo + (barMs > 0 ? (ts - loBar.time) / barMs : 0);
|
|
20354
|
+
}
|
|
20355
|
+
/**
|
|
20356
|
+
* Re-anchors all loaded drawings to the current dataset. For points with a
|
|
20357
|
+
* stored timestamp, barIndex is recomputed via `timestampToBarIndex`; for
|
|
20358
|
+
* points missing a timestamp (legacy saves or freshly placed points whose
|
|
20359
|
+
* timestamp capture was skipped), the timestamp is backfilled from the
|
|
20360
|
+
* current barIndex so subsequent data reloads remain accurate.
|
|
20361
|
+
*
|
|
20362
|
+
* Called after loadData(), prependData(), and whenever the underlying bars
|
|
20363
|
+
* change such that cached barIndex values would otherwise drift.
|
|
20224
20364
|
*/
|
|
20225
20365
|
reanchorDrawingsByTimestamp() {
|
|
20226
20366
|
const bars = this.dataStore.all;
|
|
20227
20367
|
if (bars.length === 0) return;
|
|
20228
20368
|
for (const drawing of this.drawingManager.getDrawings()) {
|
|
20229
20369
|
for (const pt of drawing.points) {
|
|
20230
|
-
if (
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
if ((bars[mid]?.time ?? 0) < pt.timestamp) lo = mid + 1;
|
|
20235
|
-
else hi = mid;
|
|
20370
|
+
if (pt.timestamp == null) {
|
|
20371
|
+
const t = this.barIndexToTimestamp(pt.barIndex);
|
|
20372
|
+
if (t != null) pt.timestamp = t;
|
|
20373
|
+
continue;
|
|
20236
20374
|
}
|
|
20237
|
-
pt.barIndex =
|
|
20375
|
+
pt.barIndex = this.timestampToBarIndex(pt.timestamp);
|
|
20376
|
+
}
|
|
20377
|
+
}
|
|
20378
|
+
}
|
|
20379
|
+
/**
|
|
20380
|
+
* Captures a timestamp on any drawing point that is missing one, using the
|
|
20381
|
+
* current bars. Call this BEFORE mutating `dataStore.all` (insert/splice)
|
|
20382
|
+
* so that points still pointing at a stale barIndex get frozen to the
|
|
20383
|
+
* correct time before the mutation invalidates the index. Safe no-op for
|
|
20384
|
+
* points that already have a timestamp.
|
|
20385
|
+
*/
|
|
20386
|
+
backfillDrawingTimestamps() {
|
|
20387
|
+
if (this.dataStore.all.length === 0) return;
|
|
20388
|
+
for (const drawing of this.drawingManager.getDrawings()) {
|
|
20389
|
+
for (const pt of drawing.points) {
|
|
20390
|
+
if (pt.timestamp != null) continue;
|
|
20391
|
+
const t = this.barIndexToTimestamp(pt.barIndex);
|
|
20392
|
+
if (t != null) pt.timestamp = t;
|
|
20238
20393
|
}
|
|
20239
20394
|
}
|
|
20240
20395
|
}
|
|
@@ -21272,7 +21427,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
21272
21427
|
return;
|
|
21273
21428
|
}
|
|
21274
21429
|
const { chartW, priceH } = this.getChartLayout();
|
|
21275
|
-
const priceRange = this.getScaledPriceRange(viewport);
|
|
21430
|
+
const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(viewport);
|
|
21276
21431
|
const bracketOrders = this.levels.filter(
|
|
21277
21432
|
(l) => l.type === "pending" && l.ToClose != null
|
|
21278
21433
|
);
|
|
@@ -21337,7 +21492,12 @@ var _ChartEngine = class _ChartEngine {
|
|
|
21337
21492
|
this.tradeDragHandler?.dragLabel ?? null,
|
|
21338
21493
|
this.tradeDragNewPrice,
|
|
21339
21494
|
this.selectedTradeLabel,
|
|
21340
|
-
|
|
21495
|
+
// Include _editOpenLabel so confirm/cancel buttons appear while the edit form is open
|
|
21496
|
+
// even before any change is staged — e.g. editing a position with no SL/TP.
|
|
21497
|
+
/* @__PURE__ */ new Set([
|
|
21498
|
+
...this.pendingLevelDrags.keys(),
|
|
21499
|
+
...this._editOpenLabel !== null ? [this._editOpenLabel] : []
|
|
21500
|
+
]),
|
|
21341
21501
|
this.positionRenderStyle,
|
|
21342
21502
|
this.hideLevelConfirmCancel,
|
|
21343
21503
|
this.draftOrderLabel,
|
|
@@ -21540,6 +21700,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
21540
21700
|
}
|
|
21541
21701
|
}
|
|
21542
21702
|
}
|
|
21703
|
+
if (result.newPrice === oldPrice) {
|
|
21704
|
+
this.tradeDragNewPrice = null;
|
|
21705
|
+
this.renderTradeLayer();
|
|
21706
|
+
return;
|
|
21707
|
+
}
|
|
21543
21708
|
const field = result.bracketType ?? "main";
|
|
21544
21709
|
const isNew = this._isBracketNew;
|
|
21545
21710
|
this._isBracketNew = false;
|
|
@@ -21645,7 +21810,22 @@ var _ChartEngine = class _ChartEngine {
|
|
|
21645
21810
|
}
|
|
21646
21811
|
applyPendingChanges(label) {
|
|
21647
21812
|
const changes = this.pendingLevelDrags.get(label);
|
|
21648
|
-
if (!changes)
|
|
21813
|
+
if (!changes) {
|
|
21814
|
+
if (this._editOpenLabel === label) {
|
|
21815
|
+
const level2 = this.levels.find((l) => l.label === label);
|
|
21816
|
+
this._editOpenLabel = null;
|
|
21817
|
+
this.selectedTradeLabel = null;
|
|
21818
|
+
this.renderTradeLayer();
|
|
21819
|
+
if (level2) {
|
|
21820
|
+
this.emitter.emit("tradeLevelConfirmed", {
|
|
21821
|
+
label,
|
|
21822
|
+
type: level2.type,
|
|
21823
|
+
isFullscreen: this.isFullscreen
|
|
21824
|
+
});
|
|
21825
|
+
}
|
|
21826
|
+
}
|
|
21827
|
+
return;
|
|
21828
|
+
}
|
|
21649
21829
|
const level = this.levels.find((l) => l.label === label);
|
|
21650
21830
|
if (!level) return;
|
|
21651
21831
|
if (this._editOpenLabel === label) this._editOpenLabel = null;
|
|
@@ -21724,6 +21904,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
21724
21904
|
if (!label && this.draftOrderLabel) {
|
|
21725
21905
|
this.removeDraftOrder();
|
|
21726
21906
|
}
|
|
21907
|
+
if (label && !this.pendingLevelDrags.has(label) && this._editOpenLabel === label) {
|
|
21908
|
+
this._cancelEditAndDeselect(label);
|
|
21909
|
+
return;
|
|
21910
|
+
}
|
|
21727
21911
|
const toRevert = label ? this.pendingLevelDrags.has(label) ? [[label, this.pendingLevelDrags.get(label) ?? []]] : [] : [...this.pendingLevelDrags.entries()];
|
|
21728
21912
|
for (const [lbl] of toRevert) {
|
|
21729
21913
|
this.undoIsNewBracketMutations(lbl);
|
|
@@ -22387,10 +22571,21 @@ var _ChartEngine = class _ChartEngine {
|
|
|
22387
22571
|
const y = touch.clientY - rect.top;
|
|
22388
22572
|
const { chartW, priceH } = this.getChartLayout();
|
|
22389
22573
|
const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(this.viewport);
|
|
22390
|
-
return
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
|
|
22574
|
+
return this.makeDrawingPoint(
|
|
22575
|
+
this.scaleManager.pixelToBarIndex(x, this.viewport, chartW),
|
|
22576
|
+
this.scaleManager.pixelToPrice(y, priceRange, priceH)
|
|
22577
|
+
);
|
|
22578
|
+
}
|
|
22579
|
+
/**
|
|
22580
|
+
* Builds a fully-populated DrawingPoint from raw float coordinates. Captures
|
|
22581
|
+
* the absolute timestamp at this fractional barIndex so the anchor survives
|
|
22582
|
+
* timeframe changes, data reloads, and serialization round-trips.
|
|
22583
|
+
*/
|
|
22584
|
+
makeDrawingPoint(barIndex, price) {
|
|
22585
|
+
const point = { barIndex, price };
|
|
22586
|
+
const t = this.barIndexToTimestamp(barIndex);
|
|
22587
|
+
if (t != null) point.timestamp = t;
|
|
22588
|
+
return point;
|
|
22394
22589
|
}
|
|
22395
22590
|
/** Shared close-button handler used by both mouse click and touch tap. */
|
|
22396
22591
|
_handleTradeLevelClose(closedLevel) {
|
|
@@ -22500,6 +22695,49 @@ var _ChartEngine = class _ChartEngine {
|
|
|
22500
22695
|
}
|
|
22501
22696
|
this.scheduleRender();
|
|
22502
22697
|
}
|
|
22698
|
+
/**
|
|
22699
|
+
* Closes the edit form, clears selection, and emits `tradeLevelEditCancelled` when
|
|
22700
|
+
* the edit form was actually open for this label. Shared between click-outside,
|
|
22701
|
+
* repeat-click toggle, and the ✗ button so all exit paths produce the same payload.
|
|
22702
|
+
*/
|
|
22703
|
+
_cancelEditAndDeselect(label) {
|
|
22704
|
+
const wasEditFormOpen = this._editOpenLabel === label;
|
|
22705
|
+
const level = this.levels.find((l) => l.label === label);
|
|
22706
|
+
this._editOpenLabel = null;
|
|
22707
|
+
this.selectedTradeLabel = null;
|
|
22708
|
+
this.draftBracketPnl = {};
|
|
22709
|
+
this.renderTradeLayer();
|
|
22710
|
+
if (wasEditFormOpen && level) {
|
|
22711
|
+
this.emitter.emit("tradeLevelEditCancelled", {
|
|
22712
|
+
label,
|
|
22713
|
+
type: level.type,
|
|
22714
|
+
isFullscreen: this.isFullscreen
|
|
22715
|
+
});
|
|
22716
|
+
}
|
|
22717
|
+
}
|
|
22718
|
+
/**
|
|
22719
|
+
* Opens the external edit form for a trade level by emitting `tradeLevelEditOpen`.
|
|
22720
|
+
* Shared between pencil click, bare box click, and drag start so every entry
|
|
22721
|
+
* point fires the same payload. No-op when the form is already open for this
|
|
22722
|
+
* label, so repeat opens (e.g. pencil click followed by drag) don't spam the event.
|
|
22723
|
+
*/
|
|
22724
|
+
_openTradeLevelEditForm(level) {
|
|
22725
|
+
if (this._editOpenLabel === level.label) return;
|
|
22726
|
+
this.selectedTradeLabel = level.label;
|
|
22727
|
+
this._editOpenLabel = level.label;
|
|
22728
|
+
const { slPrice, tpPrice } = this._resolveBracketPrices(level);
|
|
22729
|
+
this.renderTradeLayer();
|
|
22730
|
+
this.emitter.emit("tradeLevelEditOpen", {
|
|
22731
|
+
label: level.label,
|
|
22732
|
+
type: level.type,
|
|
22733
|
+
data: level.data,
|
|
22734
|
+
price: level.price,
|
|
22735
|
+
side: level.side,
|
|
22736
|
+
stopLossPrice: slPrice,
|
|
22737
|
+
takeProfitPrice: tpPrice,
|
|
22738
|
+
isFullscreen: this.isFullscreen
|
|
22739
|
+
});
|
|
22740
|
+
}
|
|
22503
22741
|
/** Shared helper: initiate a trade level main-line drag from a hit area. */
|
|
22504
22742
|
_startTradeDragFromHit(dragHit, level, hitY) {
|
|
22505
22743
|
if (!this.tradeDragHandler) return;
|
|
@@ -22575,6 +22813,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
22575
22813
|
);
|
|
22576
22814
|
}
|
|
22577
22815
|
}
|
|
22816
|
+
this._openTradeLevelEditForm(level);
|
|
22578
22817
|
}
|
|
22579
22818
|
/** Shared helper: initiate a +SL/+TP add-bracket drag from a hit area. */
|
|
22580
22819
|
_startAddBracketFromHit(addHit, hitY) {
|
|
@@ -22688,6 +22927,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
22688
22927
|
this.dataLoader({ start: new Date(fromTime), end: new Date(toTime), interval }).then((rawBars) => {
|
|
22689
22928
|
const bars = this.maybeAggregate(rawBars);
|
|
22690
22929
|
if (bars.length === 0) return;
|
|
22930
|
+
this.backfillDrawingTimestamps();
|
|
22691
22931
|
const all = this.dataStore.all;
|
|
22692
22932
|
const liveBar = all[all.length - 1];
|
|
22693
22933
|
let insertCount = 0;
|
|
@@ -22708,6 +22948,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
22708
22948
|
startIndex: this.viewport.startIndex + insertCount,
|
|
22709
22949
|
endIndex: this.viewport.endIndex + insertCount
|
|
22710
22950
|
};
|
|
22951
|
+
this.reanchorDrawingsByTimestamp();
|
|
22711
22952
|
this.recomputeIndicators();
|
|
22712
22953
|
this.scheduleRender();
|
|
22713
22954
|
}
|
|
@@ -22902,6 +23143,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
22902
23143
|
}
|
|
22903
23144
|
};
|
|
22904
23145
|
_ChartEngine.DRAW_GESTURE_THRESHOLD = 4;
|
|
23146
|
+
_ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD = 3;
|
|
22905
23147
|
var ChartEngine = _ChartEngine;
|
|
22906
23148
|
|
|
22907
23149
|
// src/data/WebSocketAdapter.ts
|