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.js
CHANGED
|
@@ -11389,6 +11389,14 @@ var DrawingManager = class {
|
|
|
11389
11389
|
this.onDrawingsChange = null;
|
|
11390
11390
|
/** Called when a text-type drawing is first completed — ChartEngine shows the text input. */
|
|
11391
11391
|
this.onStartTextEdit = null;
|
|
11392
|
+
/**
|
|
11393
|
+
* Called after drag/resize mutates a drawing's points so ChartEngine can
|
|
11394
|
+
* refresh each point's `timestamp` from the current dataStore. Required
|
|
11395
|
+
* because DrawingManager has no access to the data layer, and drag
|
|
11396
|
+
* reconstructs points from raw (barIndex, price) deltas that strip the
|
|
11397
|
+
* original point's timestamp.
|
|
11398
|
+
*/
|
|
11399
|
+
this.onPointsMutated = null;
|
|
11392
11400
|
/** Set to true by ChartEngine while the text input is focused — prevents Delete/Backspace from deleting the drawing. */
|
|
11393
11401
|
this.editingText = false;
|
|
11394
11402
|
// Drag state (move whole drawing)
|
|
@@ -11483,14 +11491,18 @@ var DrawingManager = class {
|
|
|
11483
11491
|
const dBar = point.barIndex - this.dragStartMouse.barIndex;
|
|
11484
11492
|
const dPrice = point.price - this.dragStartMouse.price;
|
|
11485
11493
|
for (let i = 0; i < this.dragDrawing.points.length; i++) {
|
|
11494
|
+
const start = this.dragStartPoints[i];
|
|
11495
|
+
if (!start) continue;
|
|
11486
11496
|
this.dragDrawing.points[i] = {
|
|
11487
|
-
barIndex:
|
|
11488
|
-
price:
|
|
11497
|
+
barIndex: start.barIndex + dBar,
|
|
11498
|
+
price: start.price + dPrice
|
|
11489
11499
|
};
|
|
11490
11500
|
}
|
|
11501
|
+
this.onPointsMutated?.(this.dragDrawing);
|
|
11491
11502
|
}
|
|
11492
11503
|
if (this.resizeDrawing && this.resizePointIndex >= 0) {
|
|
11493
11504
|
this.resizeDrawing.points[this.resizePointIndex] = { ...point };
|
|
11505
|
+
this.onPointsMutated?.(this.resizeDrawing);
|
|
11494
11506
|
}
|
|
11495
11507
|
}
|
|
11496
11508
|
/**
|
|
@@ -15158,6 +15170,13 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15158
15170
|
/** Pending resize — committed only after mouse moves past DRAW_GESTURE_THRESHOLD. */
|
|
15159
15171
|
this._pendingDrawResize = null;
|
|
15160
15172
|
// px
|
|
15173
|
+
/**
|
|
15174
|
+
* Pending trade-level drag — committed only after mouse moves past TRADE_DRAG_PIXEL_THRESHOLD.
|
|
15175
|
+
* A mouseup before the threshold means it was a click: route to _openTradeLevelEditForm instead
|
|
15176
|
+
* of finishing a zero-delta drag (which would otherwise stage a spurious pending change).
|
|
15177
|
+
*/
|
|
15178
|
+
this._pendingTradeDrag = null;
|
|
15179
|
+
// px
|
|
15161
15180
|
// Pane resize
|
|
15162
15181
|
this.paneHeightOverrides = /* @__PURE__ */ new Map();
|
|
15163
15182
|
this.separatorDrag = null;
|
|
@@ -15173,6 +15192,21 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15173
15192
|
// lerp target; null = snap on next render
|
|
15174
15193
|
this._lockedAutoFitBase = null;
|
|
15175
15194
|
// non-null = user has manually adjusted Y-axis
|
|
15195
|
+
/**
|
|
15196
|
+
* When setState() restores a persisted viewport but data isn't loaded yet,
|
|
15197
|
+
* we stash the desired time window here and apply it from loadData() once
|
|
15198
|
+
* bars are available. Cleared after one application.
|
|
15199
|
+
*/
|
|
15200
|
+
this._pendingViewportRestore = null;
|
|
15201
|
+
/**
|
|
15202
|
+
* Debounces viewport/priceScale stateChange emissions during continuous
|
|
15203
|
+
* gestures (pan, wheel-zoom, price-scale drag). Pan/zoom don't emit
|
|
15204
|
+
* stateChange directly because doing so per-frame would be wasteful; this
|
|
15205
|
+
* timer coalesces bursts so the persisted viewport catches up a few ms
|
|
15206
|
+
* after the user stops interacting. Paired with the consumer's own
|
|
15207
|
+
* debounce (e.g. CRM's 800ms save) for minimal traffic.
|
|
15208
|
+
*/
|
|
15209
|
+
this._viewportPersistTimer = null;
|
|
15176
15210
|
this.axisDrag = null;
|
|
15177
15211
|
this.subPaneScaleFactors = /* @__PURE__ */ new Map();
|
|
15178
15212
|
this._mobileCrosshairActive = false;
|
|
@@ -15333,14 +15367,14 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15333
15367
|
const dx = x - this._pendingDrawDrag.startX;
|
|
15334
15368
|
const dy = y - this._pendingDrawDrag.startY;
|
|
15335
15369
|
if (Math.sqrt(dx * dx + dy * dy) > _ChartEngine.DRAW_GESTURE_THRESHOLD) {
|
|
15336
|
-
this.drawingManager.startDrag(
|
|
15337
|
-
|
|
15338
|
-
price
|
|
15339
|
-
|
|
15370
|
+
this.drawingManager.startDrag(
|
|
15371
|
+
this._pendingDrawDrag.drawing,
|
|
15372
|
+
this.makeDrawingPoint(rawBarIndex, price)
|
|
15373
|
+
);
|
|
15340
15374
|
this._pendingDrawDrag = null;
|
|
15341
15375
|
}
|
|
15342
15376
|
}
|
|
15343
|
-
this.drawingManager.onMouseMove(
|
|
15377
|
+
this.drawingManager.onMouseMove(this.makeDrawingPoint(rawBarIndex, price));
|
|
15344
15378
|
if (this.axisDrag) {
|
|
15345
15379
|
if (this.axisDrag.axis === "price") {
|
|
15346
15380
|
const delta = y - this.axisDrag.startPos;
|
|
@@ -15382,6 +15416,18 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15382
15416
|
this.scheduleRender();
|
|
15383
15417
|
return;
|
|
15384
15418
|
}
|
|
15419
|
+
if (this._pendingTradeDrag) {
|
|
15420
|
+
const dx = x - this._pendingTradeDrag.startX;
|
|
15421
|
+
const dy = y - this._pendingTradeDrag.startY;
|
|
15422
|
+
if (Math.sqrt(dx * dx + dy * dy) > _ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD) {
|
|
15423
|
+
const { dragHit, level, hitY } = this._pendingTradeDrag;
|
|
15424
|
+
this._pendingTradeDrag = null;
|
|
15425
|
+
this._startTradeDragFromHit(dragHit, level, hitY);
|
|
15426
|
+
this.drawCanvas.style.cursor = "ns-resize";
|
|
15427
|
+
} else {
|
|
15428
|
+
return;
|
|
15429
|
+
}
|
|
15430
|
+
}
|
|
15385
15431
|
if (this.tradeDragHandler?.active) {
|
|
15386
15432
|
const moveResult = this.tradeDragHandler.onMouseMove(
|
|
15387
15433
|
y,
|
|
@@ -15422,6 +15468,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15422
15468
|
const dragHover = hitTestTradeLevelDrag(x, y, this.tradeLevelDragAreas);
|
|
15423
15469
|
if (dragHover) {
|
|
15424
15470
|
this.drawCanvas.style.cursor = "ns-resize";
|
|
15471
|
+
if (dragHover.label !== this.hoveredTradeLabel) {
|
|
15472
|
+
this.hoveredTradeLabel = dragHover.label;
|
|
15473
|
+
this.renderTradeLayer();
|
|
15474
|
+
}
|
|
15425
15475
|
this.scheduleRender();
|
|
15426
15476
|
return;
|
|
15427
15477
|
}
|
|
@@ -15491,6 +15541,16 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15491
15541
|
this.scheduleRender();
|
|
15492
15542
|
};
|
|
15493
15543
|
this.onMouseUp = () => {
|
|
15544
|
+
if (this._pendingTradeDrag) {
|
|
15545
|
+
const { level } = this._pendingTradeDrag;
|
|
15546
|
+
this._pendingTradeDrag = null;
|
|
15547
|
+
if (this._editOpenLabel === level.label && !this.pendingLevelDrags.has(level.label)) {
|
|
15548
|
+
this._cancelEditAndDeselect(level.label);
|
|
15549
|
+
return;
|
|
15550
|
+
}
|
|
15551
|
+
this._openTradeLevelEditForm(level);
|
|
15552
|
+
return;
|
|
15553
|
+
}
|
|
15494
15554
|
if (this.tradeDragHandler?.active) {
|
|
15495
15555
|
const result = this.tradeDragHandler.endDrag();
|
|
15496
15556
|
if (result) {
|
|
@@ -15507,9 +15567,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15507
15567
|
return;
|
|
15508
15568
|
}
|
|
15509
15569
|
if (this.axisDrag) {
|
|
15570
|
+
const wasPriceAxis = this.axisDrag.axis === "price" || this.axisDrag.axis === "time";
|
|
15510
15571
|
this.axisDrag = null;
|
|
15511
15572
|
this.drawCanvas.style.cursor = "default";
|
|
15512
15573
|
this.scheduleRender();
|
|
15574
|
+
if (wasPriceAxis) this.scheduleViewportPersist();
|
|
15513
15575
|
}
|
|
15514
15576
|
if (this.separatorDrag) {
|
|
15515
15577
|
this.separatorDrag = null;
|
|
@@ -15671,22 +15733,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15671
15733
|
if (editHit) {
|
|
15672
15734
|
e.preventDefault();
|
|
15673
15735
|
const level = this.levels.find((l) => l.label === editHit.label);
|
|
15674
|
-
if (level)
|
|
15675
|
-
this.selectedTradeLabel = level.label;
|
|
15676
|
-
this.renderTradeLayer();
|
|
15677
|
-
const { slPrice: _slE, tpPrice: _tpE } = this._resolveBracketPrices(level);
|
|
15678
|
-
this._editOpenLabel = level.label;
|
|
15679
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
15680
|
-
label: level.label,
|
|
15681
|
-
type: level.type,
|
|
15682
|
-
data: level.data,
|
|
15683
|
-
price: level.price,
|
|
15684
|
-
side: level.side,
|
|
15685
|
-
stopLossPrice: _slE,
|
|
15686
|
-
takeProfitPrice: _tpE,
|
|
15687
|
-
isFullscreen: this.isFullscreen
|
|
15688
|
-
});
|
|
15689
|
-
}
|
|
15736
|
+
if (level) this._openTradeLevelEditForm(level);
|
|
15690
15737
|
return;
|
|
15691
15738
|
}
|
|
15692
15739
|
}
|
|
@@ -15701,23 +15748,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15701
15748
|
if (this.hideLevelConfirmCancel && !isMobileDraggable) {
|
|
15702
15749
|
const hasPending = this.pendingLevelDrags.has(level.label);
|
|
15703
15750
|
if (this.selectedTradeLabel === level.label && !hasPending) {
|
|
15704
|
-
this.
|
|
15705
|
-
|
|
15706
|
-
this.selectedTradeLabel = level.label;
|
|
15751
|
+
this._cancelEditAndDeselect(level.label);
|
|
15752
|
+
return;
|
|
15707
15753
|
}
|
|
15708
|
-
this.
|
|
15709
|
-
const { slPrice: _sl1, tpPrice: _tp1 } = this._resolveBracketPrices(level);
|
|
15710
|
-
this._editOpenLabel = level.label;
|
|
15711
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
15712
|
-
label: level.label,
|
|
15713
|
-
type: level.type,
|
|
15714
|
-
data: level.data,
|
|
15715
|
-
price: level.price,
|
|
15716
|
-
side: level.side,
|
|
15717
|
-
stopLossPrice: _sl1,
|
|
15718
|
-
takeProfitPrice: _tp1,
|
|
15719
|
-
isFullscreen: this.isFullscreen
|
|
15720
|
-
});
|
|
15754
|
+
this._openTradeLevelEditForm(level);
|
|
15721
15755
|
return;
|
|
15722
15756
|
}
|
|
15723
15757
|
this._startTradeDragFromHit(dragHit, level, y);
|
|
@@ -15741,23 +15775,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15741
15775
|
if (level) {
|
|
15742
15776
|
const hasPending = this.pendingLevelDrags.has(hoverHit.label);
|
|
15743
15777
|
if (this.selectedTradeLabel === hoverHit.label && !hasPending) {
|
|
15744
|
-
this.
|
|
15745
|
-
|
|
15746
|
-
this.selectedTradeLabel = hoverHit.label;
|
|
15778
|
+
this._cancelEditAndDeselect(hoverHit.label);
|
|
15779
|
+
return;
|
|
15747
15780
|
}
|
|
15748
|
-
|
|
15749
|
-
this._editOpenLabel = level.label;
|
|
15750
|
-
this.renderTradeLayer();
|
|
15751
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
15752
|
-
label: level.label,
|
|
15753
|
-
type: level.type,
|
|
15754
|
-
data: level.data,
|
|
15755
|
-
price: level.price,
|
|
15756
|
-
side: level.side,
|
|
15757
|
-
stopLossPrice: _slH,
|
|
15758
|
-
takeProfitPrice: _tpH,
|
|
15759
|
-
isFullscreen: this.isFullscreen
|
|
15760
|
-
});
|
|
15781
|
+
this._openTradeLevelEditForm(level);
|
|
15761
15782
|
return;
|
|
15762
15783
|
}
|
|
15763
15784
|
}
|
|
@@ -15956,8 +15977,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
15956
15977
|
return;
|
|
15957
15978
|
}
|
|
15958
15979
|
if (this.axisDrag) {
|
|
15980
|
+
const wasPriceAxis = this.axisDrag.axis === "price" || this.axisDrag.axis === "time";
|
|
15959
15981
|
this.axisDrag = null;
|
|
15960
15982
|
this.scheduleRender();
|
|
15983
|
+
if (wasPriceAxis) this.scheduleViewportPersist();
|
|
15961
15984
|
return;
|
|
15962
15985
|
}
|
|
15963
15986
|
const wasAnchorTouch = this._drawTouchAnchorActive;
|
|
@@ -16042,22 +16065,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16042
16065
|
const editHit = hitTestTradeLevelEdit(x, y, this.tradeLevelEditAreas);
|
|
16043
16066
|
if (editHit) {
|
|
16044
16067
|
const level = this.levels.find((l) => l.label === editHit.label);
|
|
16045
|
-
if (level)
|
|
16046
|
-
this.selectedTradeLabel = level.label;
|
|
16047
|
-
this.renderTradeLayer();
|
|
16048
|
-
const { slPrice: _sl2, tpPrice: _tp2 } = this._resolveBracketPrices(level);
|
|
16049
|
-
this._editOpenLabel = level.label;
|
|
16050
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
16051
|
-
label: level.label,
|
|
16052
|
-
type: level.type,
|
|
16053
|
-
data: level.data,
|
|
16054
|
-
price: level.price,
|
|
16055
|
-
side: level.side,
|
|
16056
|
-
stopLossPrice: _sl2,
|
|
16057
|
-
takeProfitPrice: _tp2,
|
|
16058
|
-
isFullscreen: this.isFullscreen
|
|
16059
|
-
});
|
|
16060
|
-
}
|
|
16068
|
+
if (level) this._openTradeLevelEditForm(level);
|
|
16061
16069
|
return;
|
|
16062
16070
|
}
|
|
16063
16071
|
}
|
|
@@ -16075,8 +16083,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16075
16083
|
const level = this.levels.find((l) => l.label === dragHit.label);
|
|
16076
16084
|
if (level) {
|
|
16077
16085
|
if (!(this.hideLevelConfirmCancel && !dragHit.isBracket)) {
|
|
16078
|
-
this.
|
|
16079
|
-
this.drawCanvas.style.cursor = "ns-resize";
|
|
16086
|
+
this._pendingTradeDrag = { dragHit, level, hitY: y, startX: x, startY: y };
|
|
16080
16087
|
return;
|
|
16081
16088
|
}
|
|
16082
16089
|
}
|
|
@@ -16086,49 +16093,24 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16086
16093
|
if (hoverHit) {
|
|
16087
16094
|
const hasPending = this.pendingLevelDrags.has(hoverHit.label);
|
|
16088
16095
|
if (this.selectedTradeLabel === hoverHit.label && !hasPending) {
|
|
16089
|
-
this.
|
|
16096
|
+
this._cancelEditAndDeselect(hoverHit.label);
|
|
16097
|
+
return;
|
|
16098
|
+
}
|
|
16099
|
+
const level = this.levels.find((l) => l.label === hoverHit.label);
|
|
16100
|
+
if (level) {
|
|
16101
|
+
this._openTradeLevelEditForm(level);
|
|
16090
16102
|
} else {
|
|
16091
16103
|
this.selectedTradeLabel = hoverHit.label;
|
|
16092
|
-
|
|
16093
|
-
this.renderTradeLayer();
|
|
16094
|
-
if (this.hideLevelConfirmCancel) {
|
|
16095
|
-
const level = this.levels.find((l) => l.label === hoverHit.label);
|
|
16096
|
-
if (level) {
|
|
16097
|
-
const { slPrice: _sl3, tpPrice: _tp3 } = this._resolveBracketPrices(level);
|
|
16098
|
-
this._editOpenLabel = level.label;
|
|
16099
|
-
this.emitter.emit("tradeLevelEditOpen", {
|
|
16100
|
-
label: level.label,
|
|
16101
|
-
type: level.type,
|
|
16102
|
-
data: level.data,
|
|
16103
|
-
price: level.price,
|
|
16104
|
-
side: level.side,
|
|
16105
|
-
stopLossPrice: _sl3,
|
|
16106
|
-
takeProfitPrice: _tp3,
|
|
16107
|
-
isFullscreen: this.isFullscreen
|
|
16108
|
-
});
|
|
16109
|
-
}
|
|
16104
|
+
this.renderTradeLayer();
|
|
16110
16105
|
}
|
|
16111
16106
|
return;
|
|
16112
16107
|
} else if (this.selectedTradeLabel !== null) {
|
|
16113
16108
|
if (!this.hideLevelConfirmCancel) {
|
|
16114
16109
|
const lbl = this.selectedTradeLabel;
|
|
16115
16110
|
const pending = this.pendingLevelDrags.get(lbl);
|
|
16116
|
-
const isEditFormOpen = this._editOpenLabel === lbl;
|
|
16117
16111
|
if (!pending || pending.length === 0) {
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
this._editOpenLabel = null;
|
|
16121
|
-
if (lvl) {
|
|
16122
|
-
this.emitter.emit("tradeLevelEditCancelled", {
|
|
16123
|
-
label: lbl,
|
|
16124
|
-
type: lvl.type,
|
|
16125
|
-
isFullscreen: this.isFullscreen
|
|
16126
|
-
});
|
|
16127
|
-
}
|
|
16128
|
-
}
|
|
16129
|
-
this.selectedTradeLabel = null;
|
|
16130
|
-
this.renderTradeLayer();
|
|
16131
|
-
} else if (pending.every((c) => c.isNew)) {
|
|
16112
|
+
this._cancelEditAndDeselect(lbl);
|
|
16113
|
+
} else {
|
|
16132
16114
|
this.revertPendingChanges(lbl);
|
|
16133
16115
|
}
|
|
16134
16116
|
}
|
|
@@ -16183,10 +16165,9 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16183
16165
|
this.scheduleRender();
|
|
16184
16166
|
return;
|
|
16185
16167
|
}
|
|
16186
|
-
const consumed = this.drawingManager.onMouseDown(
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
});
|
|
16168
|
+
const consumed = this.drawingManager.onMouseDown(
|
|
16169
|
+
this.makeDrawingPoint(rawBarIndex, price)
|
|
16170
|
+
);
|
|
16190
16171
|
if (!consumed) {
|
|
16191
16172
|
const handleHit = this.drawingManager.getHandleAtPoint(
|
|
16192
16173
|
x,
|
|
@@ -16885,6 +16866,12 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16885
16866
|
this.drawCanvas.style.cursor = "default";
|
|
16886
16867
|
});
|
|
16887
16868
|
this.drawingManager.onDrawingsChange = () => this.emitStateChange();
|
|
16869
|
+
this.drawingManager.onPointsMutated = (drawing) => {
|
|
16870
|
+
for (const pt of drawing.points) {
|
|
16871
|
+
const t = this.barIndexToTimestamp(pt.barIndex);
|
|
16872
|
+
if (t != null) pt.timestamp = t;
|
|
16873
|
+
}
|
|
16874
|
+
};
|
|
16888
16875
|
this.drawingManager.onStartTextEdit = (drawing) => this.showTextInput(drawing);
|
|
16889
16876
|
this.panHandler = new PanHandler(
|
|
16890
16877
|
this.drawCanvas,
|
|
@@ -16894,6 +16881,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16894
16881
|
this.clearDuration();
|
|
16895
16882
|
this.checkNeedMoreData();
|
|
16896
16883
|
this.scheduleRender();
|
|
16884
|
+
this.scheduleViewportPersist();
|
|
16897
16885
|
},
|
|
16898
16886
|
() => this.dataStore.length,
|
|
16899
16887
|
() => this.scheduleRender(),
|
|
@@ -16905,6 +16893,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16905
16893
|
const pricePerPixel = (currentRange.max - currentRange.min) / priceH;
|
|
16906
16894
|
this.pricePanOffset += deltaY * pricePerPixel;
|
|
16907
16895
|
this.scheduleRender();
|
|
16896
|
+
this.scheduleViewportPersist();
|
|
16908
16897
|
},
|
|
16909
16898
|
() => {
|
|
16910
16899
|
this._isPanning = true;
|
|
@@ -16927,6 +16916,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16927
16916
|
this.clearDuration();
|
|
16928
16917
|
this.checkNeedMoreData();
|
|
16929
16918
|
this.scheduleRender();
|
|
16919
|
+
this.scheduleViewportPersist();
|
|
16930
16920
|
},
|
|
16931
16921
|
() => this.dataStore.length,
|
|
16932
16922
|
() => this.scheduleRender(),
|
|
@@ -16947,6 +16937,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16947
16937
|
this.clearDuration();
|
|
16948
16938
|
this.checkNeedMoreData();
|
|
16949
16939
|
this.scheduleRender();
|
|
16940
|
+
this.scheduleViewportPersist();
|
|
16950
16941
|
}
|
|
16951
16942
|
);
|
|
16952
16943
|
this.drawCanvas.addEventListener("mousemove", this.onMouseMove);
|
|
@@ -16984,6 +16975,15 @@ var _ChartEngine = class _ChartEngine {
|
|
|
16984
16975
|
return true;
|
|
16985
16976
|
}).map((s) => s.indicator);
|
|
16986
16977
|
}
|
|
16978
|
+
/** Schedules a stateChange emission so viewport/priceScale changes made by
|
|
16979
|
+
* pan/zoom/price-scale drag reach persistence. Coalesces rapid calls. */
|
|
16980
|
+
scheduleViewportPersist() {
|
|
16981
|
+
if (this._viewportPersistTimer) clearTimeout(this._viewportPersistTimer);
|
|
16982
|
+
this._viewportPersistTimer = setTimeout(() => {
|
|
16983
|
+
this._viewportPersistTimer = null;
|
|
16984
|
+
this.emitStateChange();
|
|
16985
|
+
}, 500);
|
|
16986
|
+
}
|
|
16987
16987
|
get isFullscreen() {
|
|
16988
16988
|
return this._isCssFullscreen || !!document.fullscreenElement;
|
|
16989
16989
|
}
|
|
@@ -17075,6 +17075,22 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17075
17075
|
}
|
|
17076
17076
|
this.recomputeIndicators();
|
|
17077
17077
|
this.reanchorDrawingsByTimestamp();
|
|
17078
|
+
if (this._pendingViewportRestore) {
|
|
17079
|
+
const pending = this._pendingViewportRestore;
|
|
17080
|
+
this._pendingViewportRestore = null;
|
|
17081
|
+
if (pending.priceScale) {
|
|
17082
|
+
this.priceScaleFactor = pending.priceScale.factor;
|
|
17083
|
+
this.pricePanOffset = pending.priceScale.panOffset;
|
|
17084
|
+
this._displayedPriceRange = null;
|
|
17085
|
+
this._lockedAutoFitBase = null;
|
|
17086
|
+
}
|
|
17087
|
+
if (pending.viewport) {
|
|
17088
|
+
this.applyViewportFromTimestamps(
|
|
17089
|
+
pending.viewport.startTime,
|
|
17090
|
+
pending.viewport.endTime
|
|
17091
|
+
);
|
|
17092
|
+
}
|
|
17093
|
+
}
|
|
17078
17094
|
this.updateCountdownState();
|
|
17079
17095
|
this.scheduleRender();
|
|
17080
17096
|
return this;
|
|
@@ -17095,6 +17111,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17095
17111
|
};
|
|
17096
17112
|
this._targetViewport = { ...this.viewport };
|
|
17097
17113
|
this.drawingManager.shiftBarIndices(n);
|
|
17114
|
+
this.reanchorDrawingsByTimestamp();
|
|
17098
17115
|
this.recomputeIndicators();
|
|
17099
17116
|
this.scheduleRender();
|
|
17100
17117
|
}
|
|
@@ -17618,15 +17635,44 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17618
17635
|
...d,
|
|
17619
17636
|
points: d.points.map((p) => ({
|
|
17620
17637
|
...p,
|
|
17621
|
-
timestamp
|
|
17638
|
+
// Prefer the point's own timestamp (captured at placement / drag /
|
|
17639
|
+
// resize); only derive from current bars as a fallback for legacy
|
|
17640
|
+
// points. Re-deriving on every export would otherwise corrupt the
|
|
17641
|
+
// anchor after a timeframe change has staled the cached barIndex.
|
|
17642
|
+
timestamp: p.timestamp ?? this.barIndexToTimestamp(p.barIndex)
|
|
17622
17643
|
}))
|
|
17623
17644
|
})),
|
|
17624
17645
|
positionRenderStyle: this.positionRenderStyle,
|
|
17625
17646
|
tradeDisplayFilter: this.tradeDisplayFilter,
|
|
17626
17647
|
canvasColors: Object.keys(this.settingsCanvasColors).length > 0 ? { ...this.settingsCanvasColors } : void 0,
|
|
17627
|
-
tfcEnabled: this.tfcActive
|
|
17648
|
+
tfcEnabled: this.tfcActive,
|
|
17649
|
+
viewport: this.getViewportTimestamps(),
|
|
17650
|
+
priceScale: this.priceScaleFactor !== 1 || this.pricePanOffset !== 0 ? { factor: this.priceScaleFactor, panOffset: this.pricePanOffset } : void 0
|
|
17628
17651
|
};
|
|
17629
17652
|
}
|
|
17653
|
+
/**
|
|
17654
|
+
* Captures the current viewport as a {startTime, endTime} timestamp pair,
|
|
17655
|
+
* preserving sub-bar fractional position. Returns undefined only when no
|
|
17656
|
+
* bars are loaded (viewport has no meaningful time anchor).
|
|
17657
|
+
*/
|
|
17658
|
+
getViewportTimestamps() {
|
|
17659
|
+
if (this.dataStore.all.length === 0) return void 0;
|
|
17660
|
+
const startTime = this.barIndexToTimestamp(this.viewport.startIndex);
|
|
17661
|
+
const endTime = this.barIndexToTimestamp(this.viewport.endIndex);
|
|
17662
|
+
if (startTime == null || endTime == null) return void 0;
|
|
17663
|
+
return { startTime, endTime };
|
|
17664
|
+
}
|
|
17665
|
+
/**
|
|
17666
|
+
* Restores the viewport from a timestamp pair, converting each edge back
|
|
17667
|
+
* into a fractional bar index relative to the current dataStore. Callers
|
|
17668
|
+
* must ensure bars are loaded before invoking.
|
|
17669
|
+
*/
|
|
17670
|
+
applyViewportFromTimestamps(startTime, endTime) {
|
|
17671
|
+
const startIndex = this.timestampToBarIndex(startTime);
|
|
17672
|
+
const endIndex = this.timestampToBarIndex(endTime);
|
|
17673
|
+
this.viewport = { ...this.viewport, startIndex, endIndex };
|
|
17674
|
+
this._targetViewport = { ...this.viewport };
|
|
17675
|
+
}
|
|
17630
17676
|
/**
|
|
17631
17677
|
* Restores chart configuration from a previously captured `ChartState`.
|
|
17632
17678
|
* Call this after construction to hydrate a chart from persisted state.
|
|
@@ -17673,18 +17719,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17673
17719
|
}
|
|
17674
17720
|
}
|
|
17675
17721
|
}
|
|
17676
|
-
const bars = this.dataStore.all;
|
|
17677
17722
|
const resolvedDrawings = state.drawings.map((d) => ({
|
|
17678
17723
|
...d,
|
|
17679
17724
|
points: d.points.map((p) => {
|
|
17680
|
-
if (
|
|
17681
|
-
|
|
17682
|
-
while (lo < hi) {
|
|
17683
|
-
const mid = lo + hi >> 1;
|
|
17684
|
-
if ((bars[mid]?.time ?? 0) < p.timestamp) lo = mid + 1;
|
|
17685
|
-
else hi = mid;
|
|
17686
|
-
}
|
|
17687
|
-
return { ...p, barIndex: lo + (p.barIndex - Math.floor(p.barIndex)) };
|
|
17725
|
+
if (p.timestamp == null || this.dataStore.all.length === 0) return p;
|
|
17726
|
+
return { ...p, barIndex: this.timestampToBarIndex(p.timestamp) };
|
|
17688
17727
|
})
|
|
17689
17728
|
}));
|
|
17690
17729
|
this.drawingManager.loadDrawings(resolvedDrawings);
|
|
@@ -17702,6 +17741,25 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17702
17741
|
this.tfcActive = state.tfcEnabled;
|
|
17703
17742
|
this.topBar?.setTfcActive(state.tfcEnabled);
|
|
17704
17743
|
}
|
|
17744
|
+
if (state.priceScale) {
|
|
17745
|
+
this.priceScaleFactor = state.priceScale.factor;
|
|
17746
|
+
this.pricePanOffset = state.priceScale.panOffset;
|
|
17747
|
+
this._displayedPriceRange = null;
|
|
17748
|
+
this._lockedAutoFitBase = null;
|
|
17749
|
+
}
|
|
17750
|
+
if (state.viewport) {
|
|
17751
|
+
if (this.dataStore.all.length > 0) {
|
|
17752
|
+
this.applyViewportFromTimestamps(
|
|
17753
|
+
state.viewport.startTime,
|
|
17754
|
+
state.viewport.endTime
|
|
17755
|
+
);
|
|
17756
|
+
} else {
|
|
17757
|
+
this._pendingViewportRestore = {
|
|
17758
|
+
viewport: state.viewport,
|
|
17759
|
+
priceScale: state.priceScale
|
|
17760
|
+
};
|
|
17761
|
+
}
|
|
17762
|
+
}
|
|
17705
17763
|
} finally {
|
|
17706
17764
|
this.isRestoringState = false;
|
|
17707
17765
|
}
|
|
@@ -17710,23 +17768,120 @@ var _ChartEngine = class _ChartEngine {
|
|
|
17710
17768
|
return this;
|
|
17711
17769
|
}
|
|
17712
17770
|
/**
|
|
17713
|
-
*
|
|
17714
|
-
*
|
|
17715
|
-
*
|
|
17771
|
+
* Converts a fractional barIndex into an absolute unix-ms timestamp by
|
|
17772
|
+
* interpolating across the containing bar. Extrapolates linearly for indexes
|
|
17773
|
+
* outside [0, bars.length-1] using the adjacent bar's width.
|
|
17774
|
+
*
|
|
17775
|
+
* This is the inverse of `timestampToBarIndex` and the authoritative way to
|
|
17776
|
+
* capture "where in time" a drawing point sits at placement, drag, or resize.
|
|
17777
|
+
* Returns undefined only when no bars are loaded.
|
|
17778
|
+
*/
|
|
17779
|
+
barIndexToTimestamp(idx) {
|
|
17780
|
+
const bars = this.dataStore.all;
|
|
17781
|
+
const n = bars.length;
|
|
17782
|
+
if (n === 0) return void 0;
|
|
17783
|
+
const first = bars[0];
|
|
17784
|
+
if (!first) return void 0;
|
|
17785
|
+
if (n === 1) return first.time;
|
|
17786
|
+
const floorIdx = Math.floor(idx);
|
|
17787
|
+
const frac = idx - floorIdx;
|
|
17788
|
+
if (floorIdx < 0) {
|
|
17789
|
+
const second = bars[1];
|
|
17790
|
+
if (!second) return first.time;
|
|
17791
|
+
return first.time + idx * (second.time - first.time);
|
|
17792
|
+
}
|
|
17793
|
+
if (floorIdx >= n - 1) {
|
|
17794
|
+
const last = bars[n - 1];
|
|
17795
|
+
const prev = bars[n - 2];
|
|
17796
|
+
if (!last || !prev) return first.time;
|
|
17797
|
+
return last.time + (idx - (n - 1)) * (last.time - prev.time);
|
|
17798
|
+
}
|
|
17799
|
+
const lo = bars[floorIdx];
|
|
17800
|
+
const hi = bars[floorIdx + 1];
|
|
17801
|
+
if (!lo || !hi) return first.time;
|
|
17802
|
+
return lo.time + frac * (hi.time - lo.time);
|
|
17803
|
+
}
|
|
17804
|
+
/**
|
|
17805
|
+
* Converts a unix-ms timestamp into a fractional barIndex using floor-bar
|
|
17806
|
+
* containment: the result is `floorBar + (ts - floorBar.time) / (nextBar.time - floorBar.time)`.
|
|
17807
|
+
* Extrapolates linearly for timestamps outside the loaded range using the edge
|
|
17808
|
+
* bar width, so drawings outside the visible data still render at the correct
|
|
17809
|
+
* relative offset rather than snapping to an edge.
|
|
17810
|
+
*
|
|
17811
|
+
* Matches TradingView's floor-bar semantics and preserves sub-bar time
|
|
17812
|
+
* precision across timeframe changes.
|
|
17813
|
+
*/
|
|
17814
|
+
timestampToBarIndex(ts) {
|
|
17815
|
+
const bars = this.dataStore.all;
|
|
17816
|
+
const n = bars.length;
|
|
17817
|
+
if (n === 0) return 0;
|
|
17818
|
+
const first = bars[0];
|
|
17819
|
+
if (!first) return 0;
|
|
17820
|
+
if (n === 1) return 0;
|
|
17821
|
+
if (ts <= first.time) {
|
|
17822
|
+
const second = bars[1];
|
|
17823
|
+
if (!second) return 0;
|
|
17824
|
+
const barMs2 = second.time - first.time;
|
|
17825
|
+
return barMs2 > 0 ? (ts - first.time) / barMs2 : 0;
|
|
17826
|
+
}
|
|
17827
|
+
const lastIdx = n - 1;
|
|
17828
|
+
const lastBar = bars[lastIdx];
|
|
17829
|
+
const prevBar = bars[lastIdx - 1];
|
|
17830
|
+
if (!lastBar || !prevBar) return 0;
|
|
17831
|
+
if (ts >= lastBar.time) {
|
|
17832
|
+
const barMs2 = lastBar.time - prevBar.time;
|
|
17833
|
+
return lastIdx + (barMs2 > 0 ? (ts - lastBar.time) / barMs2 : 0);
|
|
17834
|
+
}
|
|
17835
|
+
let lo = 0, hi = lastIdx;
|
|
17836
|
+
while (lo < hi) {
|
|
17837
|
+
const mid = lo + hi + 1 >> 1;
|
|
17838
|
+
if ((bars[mid]?.time ?? 0) <= ts) lo = mid;
|
|
17839
|
+
else hi = mid - 1;
|
|
17840
|
+
}
|
|
17841
|
+
const loBar = bars[lo];
|
|
17842
|
+
const hiBar = bars[lo + 1];
|
|
17843
|
+
if (!loBar || !hiBar) return lo;
|
|
17844
|
+
const barMs = hiBar.time - loBar.time;
|
|
17845
|
+
return lo + (barMs > 0 ? (ts - loBar.time) / barMs : 0);
|
|
17846
|
+
}
|
|
17847
|
+
/**
|
|
17848
|
+
* Re-anchors all loaded drawings to the current dataset. For points with a
|
|
17849
|
+
* stored timestamp, barIndex is recomputed via `timestampToBarIndex`; for
|
|
17850
|
+
* points missing a timestamp (legacy saves or freshly placed points whose
|
|
17851
|
+
* timestamp capture was skipped), the timestamp is backfilled from the
|
|
17852
|
+
* current barIndex so subsequent data reloads remain accurate.
|
|
17853
|
+
*
|
|
17854
|
+
* Called after loadData(), prependData(), and whenever the underlying bars
|
|
17855
|
+
* change such that cached barIndex values would otherwise drift.
|
|
17716
17856
|
*/
|
|
17717
17857
|
reanchorDrawingsByTimestamp() {
|
|
17718
17858
|
const bars = this.dataStore.all;
|
|
17719
17859
|
if (bars.length === 0) return;
|
|
17720
17860
|
for (const drawing of this.drawingManager.getDrawings()) {
|
|
17721
17861
|
for (const pt of drawing.points) {
|
|
17722
|
-
if (
|
|
17723
|
-
|
|
17724
|
-
|
|
17725
|
-
|
|
17726
|
-
if ((bars[mid]?.time ?? 0) < pt.timestamp) lo = mid + 1;
|
|
17727
|
-
else hi = mid;
|
|
17862
|
+
if (pt.timestamp == null) {
|
|
17863
|
+
const t = this.barIndexToTimestamp(pt.barIndex);
|
|
17864
|
+
if (t != null) pt.timestamp = t;
|
|
17865
|
+
continue;
|
|
17728
17866
|
}
|
|
17729
|
-
pt.barIndex =
|
|
17867
|
+
pt.barIndex = this.timestampToBarIndex(pt.timestamp);
|
|
17868
|
+
}
|
|
17869
|
+
}
|
|
17870
|
+
}
|
|
17871
|
+
/**
|
|
17872
|
+
* Captures a timestamp on any drawing point that is missing one, using the
|
|
17873
|
+
* current bars. Call this BEFORE mutating `dataStore.all` (insert/splice)
|
|
17874
|
+
* so that points still pointing at a stale barIndex get frozen to the
|
|
17875
|
+
* correct time before the mutation invalidates the index. Safe no-op for
|
|
17876
|
+
* points that already have a timestamp.
|
|
17877
|
+
*/
|
|
17878
|
+
backfillDrawingTimestamps() {
|
|
17879
|
+
if (this.dataStore.all.length === 0) return;
|
|
17880
|
+
for (const drawing of this.drawingManager.getDrawings()) {
|
|
17881
|
+
for (const pt of drawing.points) {
|
|
17882
|
+
if (pt.timestamp != null) continue;
|
|
17883
|
+
const t = this.barIndexToTimestamp(pt.barIndex);
|
|
17884
|
+
if (t != null) pt.timestamp = t;
|
|
17730
17885
|
}
|
|
17731
17886
|
}
|
|
17732
17887
|
}
|
|
@@ -18764,7 +18919,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18764
18919
|
return;
|
|
18765
18920
|
}
|
|
18766
18921
|
const { chartW, priceH } = this.getChartLayout();
|
|
18767
|
-
const priceRange = this.getScaledPriceRange(viewport);
|
|
18922
|
+
const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(viewport);
|
|
18768
18923
|
const bracketOrders = this.levels.filter(
|
|
18769
18924
|
(l) => l.type === "pending" && l.ToClose != null
|
|
18770
18925
|
);
|
|
@@ -18829,7 +18984,12 @@ var _ChartEngine = class _ChartEngine {
|
|
|
18829
18984
|
this.tradeDragHandler?.dragLabel ?? null,
|
|
18830
18985
|
this.tradeDragNewPrice,
|
|
18831
18986
|
this.selectedTradeLabel,
|
|
18832
|
-
|
|
18987
|
+
// Include _editOpenLabel so confirm/cancel buttons appear while the edit form is open
|
|
18988
|
+
// even before any change is staged — e.g. editing a position with no SL/TP.
|
|
18989
|
+
/* @__PURE__ */ new Set([
|
|
18990
|
+
...this.pendingLevelDrags.keys(),
|
|
18991
|
+
...this._editOpenLabel !== null ? [this._editOpenLabel] : []
|
|
18992
|
+
]),
|
|
18833
18993
|
this.positionRenderStyle,
|
|
18834
18994
|
this.hideLevelConfirmCancel,
|
|
18835
18995
|
this.draftOrderLabel,
|
|
@@ -19032,6 +19192,11 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19032
19192
|
}
|
|
19033
19193
|
}
|
|
19034
19194
|
}
|
|
19195
|
+
if (result.newPrice === oldPrice) {
|
|
19196
|
+
this.tradeDragNewPrice = null;
|
|
19197
|
+
this.renderTradeLayer();
|
|
19198
|
+
return;
|
|
19199
|
+
}
|
|
19035
19200
|
const field = result.bracketType ?? "main";
|
|
19036
19201
|
const isNew = this._isBracketNew;
|
|
19037
19202
|
this._isBracketNew = false;
|
|
@@ -19137,7 +19302,22 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19137
19302
|
}
|
|
19138
19303
|
applyPendingChanges(label) {
|
|
19139
19304
|
const changes = this.pendingLevelDrags.get(label);
|
|
19140
|
-
if (!changes)
|
|
19305
|
+
if (!changes) {
|
|
19306
|
+
if (this._editOpenLabel === label) {
|
|
19307
|
+
const level2 = this.levels.find((l) => l.label === label);
|
|
19308
|
+
this._editOpenLabel = null;
|
|
19309
|
+
this.selectedTradeLabel = null;
|
|
19310
|
+
this.renderTradeLayer();
|
|
19311
|
+
if (level2) {
|
|
19312
|
+
this.emitter.emit("tradeLevelConfirmed", {
|
|
19313
|
+
label,
|
|
19314
|
+
type: level2.type,
|
|
19315
|
+
isFullscreen: this.isFullscreen
|
|
19316
|
+
});
|
|
19317
|
+
}
|
|
19318
|
+
}
|
|
19319
|
+
return;
|
|
19320
|
+
}
|
|
19141
19321
|
const level = this.levels.find((l) => l.label === label);
|
|
19142
19322
|
if (!level) return;
|
|
19143
19323
|
if (this._editOpenLabel === label) this._editOpenLabel = null;
|
|
@@ -19216,6 +19396,10 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19216
19396
|
if (!label && this.draftOrderLabel) {
|
|
19217
19397
|
this.removeDraftOrder();
|
|
19218
19398
|
}
|
|
19399
|
+
if (label && !this.pendingLevelDrags.has(label) && this._editOpenLabel === label) {
|
|
19400
|
+
this._cancelEditAndDeselect(label);
|
|
19401
|
+
return;
|
|
19402
|
+
}
|
|
19219
19403
|
const toRevert = label ? this.pendingLevelDrags.has(label) ? [[label, this.pendingLevelDrags.get(label) ?? []]] : [] : [...this.pendingLevelDrags.entries()];
|
|
19220
19404
|
for (const [lbl] of toRevert) {
|
|
19221
19405
|
this.undoIsNewBracketMutations(lbl);
|
|
@@ -19879,10 +20063,21 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19879
20063
|
const y = touch.clientY - rect.top;
|
|
19880
20064
|
const { chartW, priceH } = this.getChartLayout();
|
|
19881
20065
|
const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(this.viewport);
|
|
19882
|
-
return
|
|
19883
|
-
|
|
19884
|
-
|
|
19885
|
-
|
|
20066
|
+
return this.makeDrawingPoint(
|
|
20067
|
+
this.scaleManager.pixelToBarIndex(x, this.viewport, chartW),
|
|
20068
|
+
this.scaleManager.pixelToPrice(y, priceRange, priceH)
|
|
20069
|
+
);
|
|
20070
|
+
}
|
|
20071
|
+
/**
|
|
20072
|
+
* Builds a fully-populated DrawingPoint from raw float coordinates. Captures
|
|
20073
|
+
* the absolute timestamp at this fractional barIndex so the anchor survives
|
|
20074
|
+
* timeframe changes, data reloads, and serialization round-trips.
|
|
20075
|
+
*/
|
|
20076
|
+
makeDrawingPoint(barIndex, price) {
|
|
20077
|
+
const point = { barIndex, price };
|
|
20078
|
+
const t = this.barIndexToTimestamp(barIndex);
|
|
20079
|
+
if (t != null) point.timestamp = t;
|
|
20080
|
+
return point;
|
|
19886
20081
|
}
|
|
19887
20082
|
/** Shared close-button handler used by both mouse click and touch tap. */
|
|
19888
20083
|
_handleTradeLevelClose(closedLevel) {
|
|
@@ -19992,6 +20187,49 @@ var _ChartEngine = class _ChartEngine {
|
|
|
19992
20187
|
}
|
|
19993
20188
|
this.scheduleRender();
|
|
19994
20189
|
}
|
|
20190
|
+
/**
|
|
20191
|
+
* Closes the edit form, clears selection, and emits `tradeLevelEditCancelled` when
|
|
20192
|
+
* the edit form was actually open for this label. Shared between click-outside,
|
|
20193
|
+
* repeat-click toggle, and the ✗ button so all exit paths produce the same payload.
|
|
20194
|
+
*/
|
|
20195
|
+
_cancelEditAndDeselect(label) {
|
|
20196
|
+
const wasEditFormOpen = this._editOpenLabel === label;
|
|
20197
|
+
const level = this.levels.find((l) => l.label === label);
|
|
20198
|
+
this._editOpenLabel = null;
|
|
20199
|
+
this.selectedTradeLabel = null;
|
|
20200
|
+
this.draftBracketPnl = {};
|
|
20201
|
+
this.renderTradeLayer();
|
|
20202
|
+
if (wasEditFormOpen && level) {
|
|
20203
|
+
this.emitter.emit("tradeLevelEditCancelled", {
|
|
20204
|
+
label,
|
|
20205
|
+
type: level.type,
|
|
20206
|
+
isFullscreen: this.isFullscreen
|
|
20207
|
+
});
|
|
20208
|
+
}
|
|
20209
|
+
}
|
|
20210
|
+
/**
|
|
20211
|
+
* Opens the external edit form for a trade level by emitting `tradeLevelEditOpen`.
|
|
20212
|
+
* Shared between pencil click, bare box click, and drag start so every entry
|
|
20213
|
+
* point fires the same payload. No-op when the form is already open for this
|
|
20214
|
+
* label, so repeat opens (e.g. pencil click followed by drag) don't spam the event.
|
|
20215
|
+
*/
|
|
20216
|
+
_openTradeLevelEditForm(level) {
|
|
20217
|
+
if (this._editOpenLabel === level.label) return;
|
|
20218
|
+
this.selectedTradeLabel = level.label;
|
|
20219
|
+
this._editOpenLabel = level.label;
|
|
20220
|
+
const { slPrice, tpPrice } = this._resolveBracketPrices(level);
|
|
20221
|
+
this.renderTradeLayer();
|
|
20222
|
+
this.emitter.emit("tradeLevelEditOpen", {
|
|
20223
|
+
label: level.label,
|
|
20224
|
+
type: level.type,
|
|
20225
|
+
data: level.data,
|
|
20226
|
+
price: level.price,
|
|
20227
|
+
side: level.side,
|
|
20228
|
+
stopLossPrice: slPrice,
|
|
20229
|
+
takeProfitPrice: tpPrice,
|
|
20230
|
+
isFullscreen: this.isFullscreen
|
|
20231
|
+
});
|
|
20232
|
+
}
|
|
19995
20233
|
/** Shared helper: initiate a trade level main-line drag from a hit area. */
|
|
19996
20234
|
_startTradeDragFromHit(dragHit, level, hitY) {
|
|
19997
20235
|
if (!this.tradeDragHandler) return;
|
|
@@ -20067,6 +20305,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20067
20305
|
);
|
|
20068
20306
|
}
|
|
20069
20307
|
}
|
|
20308
|
+
this._openTradeLevelEditForm(level);
|
|
20070
20309
|
}
|
|
20071
20310
|
/** Shared helper: initiate a +SL/+TP add-bracket drag from a hit area. */
|
|
20072
20311
|
_startAddBracketFromHit(addHit, hitY) {
|
|
@@ -20180,6 +20419,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20180
20419
|
this.dataLoader({ start: new Date(fromTime), end: new Date(toTime), interval }).then((rawBars) => {
|
|
20181
20420
|
const bars = this.maybeAggregate(rawBars);
|
|
20182
20421
|
if (bars.length === 0) return;
|
|
20422
|
+
this.backfillDrawingTimestamps();
|
|
20183
20423
|
const all = this.dataStore.all;
|
|
20184
20424
|
const liveBar = all[all.length - 1];
|
|
20185
20425
|
let insertCount = 0;
|
|
@@ -20200,6 +20440,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20200
20440
|
startIndex: this.viewport.startIndex + insertCount,
|
|
20201
20441
|
endIndex: this.viewport.endIndex + insertCount
|
|
20202
20442
|
};
|
|
20443
|
+
this.reanchorDrawingsByTimestamp();
|
|
20203
20444
|
this.recomputeIndicators();
|
|
20204
20445
|
this.scheduleRender();
|
|
20205
20446
|
}
|
|
@@ -20394,6 +20635,7 @@ var _ChartEngine = class _ChartEngine {
|
|
|
20394
20635
|
}
|
|
20395
20636
|
};
|
|
20396
20637
|
_ChartEngine.DRAW_GESTURE_THRESHOLD = 4;
|
|
20638
|
+
_ChartEngine.TRADE_DRAG_PIXEL_THRESHOLD = 3;
|
|
20397
20639
|
var ChartEngine = _ChartEngine;
|
|
20398
20640
|
|
|
20399
20641
|
// src/data/WebSocketAdapter.ts
|