acttrader-charts 1.0.11 → 1.0.13

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/index.cjs CHANGED
@@ -428,6 +428,9 @@ var DEFAULT_TOP_BAR_CONFIG = {
428
428
  dropBtnFontFamily: INTER_FONT,
429
429
  drawBtnSize: "26px",
430
430
  drawBtnIconFontSize: "14px",
431
+ iconBtnSize: "26px",
432
+ mobileIconBtnSize: "28px",
433
+ mobileDrawBtnIconSize: "16px",
431
434
  flyoutRowFontSize: "12px",
432
435
  flyoutRowFontFamily: INTER_FONT,
433
436
  flyoutCategoryFontSize: "10px",
@@ -465,6 +468,12 @@ var DEFAULT_INDICATOR_OVERLAY_CONFIG = {
465
468
  dropdownRowFontFamily: INTER_FONT,
466
469
  dropdownIconFontSize: "10px"
467
470
  };
471
+ var DEFAULT_TRADE_BUTTON_CONFIG = {
472
+ size: 22,
473
+ mobileSize: 24,
474
+ iconSize: 14,
475
+ mobileIconSize: 16
476
+ };
468
477
  var DEFAULT_UI_CONFIG = {
469
478
  drawingToolbar: DEFAULT_DRAWING_TOOLBAR_CONFIG,
470
479
  topBar: DEFAULT_TOP_BAR_CONFIG,
@@ -472,7 +481,8 @@ var DEFAULT_UI_CONFIG = {
472
481
  priceAxis: DEFAULT_PRICE_AXIS_CONFIG,
473
482
  timeAxis: DEFAULT_TIME_AXIS_CONFIG,
474
483
  crosshair: DEFAULT_CROSSHAIR_CONFIG,
475
- indicatorOverlay: DEFAULT_INDICATOR_OVERLAY_CONFIG
484
+ indicatorOverlay: DEFAULT_INDICATOR_OVERLAY_CONFIG,
485
+ tradeButton: DEFAULT_TRADE_BUTTON_CONFIG
476
486
  };
477
487
  function resolveUiConfig(override) {
478
488
  if (!override) return DEFAULT_UI_CONFIG;
@@ -483,7 +493,8 @@ function resolveUiConfig(override) {
483
493
  priceAxis: { ...DEFAULT_PRICE_AXIS_CONFIG, ...override.priceAxis },
484
494
  timeAxis: { ...DEFAULT_TIME_AXIS_CONFIG, ...override.timeAxis },
485
495
  crosshair: { ...DEFAULT_CROSSHAIR_CONFIG, ...override.crosshair },
486
- indicatorOverlay: { ...DEFAULT_INDICATOR_OVERLAY_CONFIG, ...override.indicatorOverlay }
496
+ indicatorOverlay: { ...DEFAULT_INDICATOR_OVERLAY_CONFIG, ...override.indicatorOverlay },
497
+ tradeButton: { ...DEFAULT_TRADE_BUTTON_CONFIG, ...override.tradeButton }
487
498
  };
488
499
  }
489
500
 
@@ -2100,7 +2111,7 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
2100
2111
  ctx.restore();
2101
2112
  if (draggable) {
2102
2113
  drawGripHandle(ctx, gripX, boxY, boxH, colors, hovered);
2103
- const BTN_ZONE_W = EDIT_R * 2 + EDIT_GAP + CLOSE_R * 2 + BOX_PAD_X / 2;
2114
+ const BTN_ZONE_W = hideConfirmCancel ? 0 : EDIT_R * 2 + EDIT_GAP + CLOSE_R * 2 + BOX_PAD_X / 2;
2104
2115
  dragAreas.push({
2105
2116
  label: level.label,
2106
2117
  type: level.type,
@@ -2110,7 +2121,8 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
2110
2121
  y: boxY,
2111
2122
  w: GRIP_W + 2 + boxW - BTN_ZONE_W,
2112
2123
  h: boxH,
2113
- isBracket: false
2124
+ isBracket: false,
2125
+ isBox: true
2114
2126
  });
2115
2127
  dragAreas.push({
2116
2128
  label: level.label,
@@ -2123,6 +2135,19 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
2123
2135
  h: 8,
2124
2136
  isBracket: false
2125
2137
  });
2138
+ } else if (hideConfirmCancel && boxW > 0) {
2139
+ dragAreas.push({
2140
+ label: level.label,
2141
+ type: level.type,
2142
+ data: level.data,
2143
+ price: level.price,
2144
+ x: boxX,
2145
+ y: boxY,
2146
+ w: boxW,
2147
+ h: boxH,
2148
+ isBracket: false,
2149
+ isBox: true
2150
+ });
2126
2151
  }
2127
2152
  hoverAreas.push({
2128
2153
  label: level.label,
@@ -3223,6 +3248,9 @@ var TopBar = class {
3223
3248
  this.fullscreenBtn.innerHTML = active ? ICON_FULLSCREEN_EXIT : ICON_FULLSCREEN_ENTER;
3224
3249
  this.fullscreenBtn.title = active ? this.labels.exitFullscreenTitle : this.labels.toggleFullscreenTitle;
3225
3250
  this.applyIconBtnStyles(this.fullscreenBtn, this.theme, active);
3251
+ if (this.isMobileLayout) {
3252
+ this.resizeSvg(this.fullscreenBtn, this.getSvgSize(this.fullscreenBtn) + 2);
3253
+ }
3226
3254
  }
3227
3255
  setDrawingsBtnActive(active) {
3228
3256
  if (!this.drawingBtn) return;
@@ -3275,6 +3303,21 @@ var TopBar = class {
3275
3303
  }
3276
3304
  this.durationSep.style.display = btnDisplay;
3277
3305
  this.durationDropBtn.style.display = isMobile ? "" : "none";
3306
+ const containerSize = isMobile ? this.cfg.mobileIconBtnSize : this.cfg.iconBtnSize;
3307
+ for (const btn of [this.fullscreenBtn, this.settingsBtn, this.drawingBtn]) {
3308
+ if (!btn) continue;
3309
+ btn.style.width = containerSize;
3310
+ btn.style.height = containerSize;
3311
+ }
3312
+ const delta = isMobile ? 2 : -2;
3313
+ this.resizeSvg(this.fullscreenBtn, this.getSvgSize(this.fullscreenBtn) + delta);
3314
+ if (this.settingsBtn) {
3315
+ this.resizeSvg(this.settingsBtn, this.getSvgSize(this.settingsBtn) + delta);
3316
+ }
3317
+ if (this.drawingBtn) {
3318
+ const drawIconSize = isMobile ? parseInt(this.cfg.mobileDrawBtnIconSize, 10) : parseInt(this.cfg.drawBtnIconFontSize, 10);
3319
+ this.resizeSvg(this.drawingBtn, drawIconSize);
3320
+ }
3278
3321
  }
3279
3322
  setIndicatorOptions(options) {
3280
3323
  this.indicatorOptions = options;
@@ -3681,13 +3724,14 @@ var TopBar = class {
3681
3724
  }
3682
3725
  applyIconBtnStyles(btn, theme, active) {
3683
3726
  btn.dataset.active = String(active);
3727
+ const size = this.isMobileLayout ? this.cfg.mobileIconBtnSize : this.cfg.iconBtnSize;
3684
3728
  Object.assign(btn.style, {
3685
3729
  background: active ? theme.ui.accentBg : "transparent",
3686
3730
  color: active ? theme.ui.accentText : theme.topBar.btnColor,
3687
3731
  border: `1px solid ${active ? theme.ui.accent : theme.axisBorder}`,
3688
3732
  borderRadius: "5px",
3689
- width: "26px",
3690
- height: "26px",
3733
+ width: size,
3734
+ height: size,
3691
3735
  cursor: "pointer",
3692
3736
  outline: "none",
3693
3737
  display: "flex",
@@ -3698,6 +3742,20 @@ var TopBar = class {
3698
3742
  transition: "border-color 0.12s, background 0.12s"
3699
3743
  });
3700
3744
  }
3745
+ resizeSvg(btn, size) {
3746
+ if (!btn) return;
3747
+ const svg = btn.querySelector("svg");
3748
+ if (svg) {
3749
+ svg.setAttribute("width", String(size));
3750
+ svg.setAttribute("height", String(size));
3751
+ }
3752
+ }
3753
+ getSvgSize(btn) {
3754
+ if (!btn) return 0;
3755
+ const svg = btn.querySelector("svg");
3756
+ if (!svg) return 0;
3757
+ return parseInt(svg.getAttribute("width") ?? "0", 10);
3758
+ }
3701
3759
  };
3702
3760
 
3703
3761
  // src/ui/BottomBar.ts
@@ -12214,9 +12272,11 @@ var ChartSettingsDialog = class {
12214
12272
 
12215
12273
  // src/ui/TradeButton.ts
12216
12274
  var TradeButton = class {
12217
- constructor(theme, priceAxisWidth, onClick, labels = DEFAULT_LABELS.trade) {
12275
+ constructor(theme, priceAxisWidth, onClick, labels = DEFAULT_LABELS.trade, cfg = DEFAULT_TRADE_BUTTON_CONFIG) {
12218
12276
  this._visible = false;
12277
+ this._isMobile = false;
12219
12278
  this.theme = theme;
12279
+ this.cfg = cfg;
12220
12280
  this.el = document.createElement("button");
12221
12281
  this.el.innerHTML = ICON_PLUS_CIRCLE;
12222
12282
  this.el.title = labels.placeOrderTitle;
@@ -12224,8 +12284,8 @@ var TradeButton = class {
12224
12284
  position: "absolute",
12225
12285
  right: `${priceAxisWidth + 26}px`,
12226
12286
  top: "0",
12227
- width: "22px",
12228
- height: "22px",
12287
+ width: `${cfg.size}px`,
12288
+ height: `${cfg.size}px`,
12229
12289
  borderRadius: "50%",
12230
12290
  border: `1px solid ${theme.axisBorder}`,
12231
12291
  background: theme.tooltip.background,
@@ -12244,6 +12304,7 @@ var TradeButton = class {
12244
12304
  pointerEvents: "auto",
12245
12305
  userSelect: "none"
12246
12306
  });
12307
+ this.applyIconSize(cfg.iconSize);
12247
12308
  this.el.addEventListener("click", (e) => {
12248
12309
  e.stopPropagation();
12249
12310
  onClick();
@@ -12291,6 +12352,27 @@ var TradeButton = class {
12291
12352
  right: `${priceAxisWidth + 26}px`
12292
12353
  });
12293
12354
  }
12355
+ /**
12356
+ * Toggle mobile sizing. When `isMobile` is true, the button diameter and
12357
+ * inner SVG icon are swapped for the configured mobile values; when false,
12358
+ * they revert to desktop values.
12359
+ */
12360
+ setMobileMode(isMobile) {
12361
+ if (this._isMobile === isMobile) return;
12362
+ this._isMobile = isMobile;
12363
+ const size = isMobile ? this.cfg.mobileSize : this.cfg.size;
12364
+ const iconSize = isMobile ? this.cfg.mobileIconSize : this.cfg.iconSize;
12365
+ this.el.style.width = `${size}px`;
12366
+ this.el.style.height = `${size}px`;
12367
+ this.applyIconSize(iconSize);
12368
+ }
12369
+ applyIconSize(size) {
12370
+ const svg = this.el.querySelector("svg");
12371
+ if (svg) {
12372
+ svg.setAttribute("width", String(size));
12373
+ svg.setAttribute("height", String(size));
12374
+ }
12375
+ }
12294
12376
  };
12295
12377
 
12296
12378
  // src/ui/TradePopover.ts
@@ -16472,7 +16554,7 @@ var _ChartEngine = class _ChartEngine {
16472
16554
  Math.min(Math.ceil(this.viewport.endIndex), Math.round(rawBarIndex))
16473
16555
  );
16474
16556
  const bar = this.dataStore.all[clamped] ?? null;
16475
- this.crosshair = isInChartArea && !this.tradePopover?.isVisible ? { x, y, barIndex: clamped, price, bar } : null;
16557
+ this.crosshair = isInChartArea && !this.tradePopover?.isVisible && this.selectedTradeLabel === null ? { x, y, barIndex: clamped, price, bar } : null;
16476
16558
  if (this.crosshair) this.emitter.emit("crosshair", this.crosshair);
16477
16559
  const hoveringDotLevel = this.positionRenderStyle === "dot" && this.hoveredTradeLabel !== null;
16478
16560
  if (this.tradeButton) {
@@ -16811,8 +16893,9 @@ var _ChartEngine = class _ChartEngine {
16811
16893
  e.preventDefault();
16812
16894
  const level = this.levels.find((l) => l.label === dragHit.label);
16813
16895
  if (level) {
16814
- const isLimitStopDraftLevel = level.data?._isDraft === true && level.orderType !== "market";
16815
- if (this.hideLevelConfirmCancel && !dragHit.isBracket && !isLimitStopDraftLevel) {
16896
+ const isAlreadySelected = this.selectedTradeLabel === level.label;
16897
+ const isMobileDraggable = dragHit.isBracket || level.type === "pending" && level.orderType !== "market" && (isAlreadySelected || !dragHit.isBox);
16898
+ if (this.hideLevelConfirmCancel && !isMobileDraggable) {
16816
16899
  const hasPending = this.pendingLevelDrags.has(level.label);
16817
16900
  if (this.selectedTradeLabel === level.label && !hasPending) {
16818
16901
  this.selectedTradeLabel = null;
@@ -16990,7 +17073,7 @@ var _ChartEngine = class _ChartEngine {
16990
17073
  Math.min(Math.ceil(this.viewport.endIndex), Math.round(rawBarIndex))
16991
17074
  );
16992
17075
  const bar = this.dataStore.all[clamped] ?? null;
16993
- this.crosshair = { x, y, barIndex: clamped, price, bar };
17076
+ this.crosshair = this.selectedTradeLabel === null ? { x, y, barIndex: clamped, price, bar } : null;
16994
17077
  if (this.tradeButton) {
16995
17078
  this.tradeButton.setY(y);
16996
17079
  if (this._isTradingActive) {
@@ -17600,30 +17683,32 @@ var _ChartEngine = class _ChartEngine {
17600
17683
  padding: "0 2px"
17601
17684
  });
17602
17685
  this.canvasWrap.appendChild(this.textInputEl);
17603
- this.loadingOverlayEl = document.createElement("div");
17604
- Object.assign(this.loadingOverlayEl.style, {
17605
- display: "none",
17606
- position: "absolute",
17607
- top: "0",
17608
- left: "0",
17609
- width: "100%",
17610
- height: "100%",
17611
- alignItems: "center",
17612
- justifyContent: "center",
17613
- pointerEvents: "none",
17614
- zIndex: "20"
17615
- });
17616
- const loadingLabel = document.createElement("span");
17617
- loadingLabel.textContent = this.labels.chart.loading;
17618
- Object.assign(loadingLabel.style, {
17619
- color: "rgba(200,200,200,0.9)",
17620
- fontSize: "13px",
17621
- background: "rgba(0,0,0,0.45)",
17622
- padding: "4px 12px",
17623
- borderRadius: "4px"
17624
- });
17625
- this.loadingOverlayEl.appendChild(loadingLabel);
17626
- this.canvasWrap.appendChild(this.loadingOverlayEl);
17686
+ if (showUI) {
17687
+ this.loadingOverlayEl = document.createElement("div");
17688
+ Object.assign(this.loadingOverlayEl.style, {
17689
+ display: "none",
17690
+ position: "absolute",
17691
+ top: "0",
17692
+ left: "0",
17693
+ width: "100%",
17694
+ height: "100%",
17695
+ alignItems: "center",
17696
+ justifyContent: "center",
17697
+ pointerEvents: "none",
17698
+ zIndex: "20"
17699
+ });
17700
+ const loadingLabel = document.createElement("span");
17701
+ loadingLabel.textContent = this.labels.chart.loading;
17702
+ Object.assign(loadingLabel.style, {
17703
+ color: "rgba(200,200,200,0.9)",
17704
+ fontSize: "13px",
17705
+ background: "rgba(0,0,0,0.45)",
17706
+ padding: "4px 12px",
17707
+ borderRadius: "4px"
17708
+ });
17709
+ this.loadingOverlayEl.appendChild(loadingLabel);
17710
+ this.canvasWrap.appendChild(this.loadingOverlayEl);
17711
+ }
17627
17712
  if (showUI) {
17628
17713
  this.siEl = document.createElement("div");
17629
17714
  Object.assign(this.siEl.style, {
@@ -17755,7 +17840,8 @@ var _ChartEngine = class _ChartEngine {
17755
17840
  );
17756
17841
  }
17757
17842
  },
17758
- this.labels.trade
17843
+ this.labels.trade,
17844
+ this.uiConfig.tradeButton
17759
17845
  );
17760
17846
  this.canvasWrap.appendChild(this.tradeButton.el);
17761
17847
  this.tradeButton.el.addEventListener("mouseenter", () => {
@@ -18355,6 +18441,30 @@ var _ChartEngine = class _ChartEngine {
18355
18441
  this.emitStateChange();
18356
18442
  return this;
18357
18443
  }
18444
+ /** Programmatically change the active timeframe and reload data. */
18445
+ setTimeframe(tf) {
18446
+ this.timeframe = tf;
18447
+ this.topBar?.setTimeframe(tf);
18448
+ this.emitter.emit("timeframeChange", { timeframe: tf });
18449
+ this.emitStateChange();
18450
+ this.updateCountdownState();
18451
+ this.triggerLoad("timeframe");
18452
+ return this;
18453
+ }
18454
+ /** Update per-theme deep-partial color overrides and rebuild the active theme. */
18455
+ setThemeOverrides(overrides) {
18456
+ this.themeOverrides = overrides;
18457
+ this.theme = this.buildTheme(this.themeName);
18458
+ this.topBar?.setTheme(this.theme);
18459
+ this.bottomBar?.setTheme(this.theme);
18460
+ this.leftBar?.setTheme(this.theme);
18461
+ this.tradeButton?.setTheme(this.theme, this.priceAxisWidth);
18462
+ this.tradePopover?.setTheme(this.theme);
18463
+ this.indicatorSettingsDialog?.setTheme(this.theme);
18464
+ this.chartSettingsDialog?.setTheme(this.theme);
18465
+ this.scheduleRender();
18466
+ return this;
18467
+ }
18358
18468
  setSeries(series) {
18359
18469
  this.series = series;
18360
18470
  this.topBar?.setSeries(series);
@@ -18883,8 +18993,10 @@ var _ChartEngine = class _ChartEngine {
18883
18993
  addLevelBracket(label, type) {
18884
18994
  const level = this.levels.find((l) => l.label === label);
18885
18995
  if (!level) return this;
18886
- const alreadyHas = type === "sl" ? level.stopLossPrice !== void 0 : level.takeProfitPrice !== void 0;
18887
- if (alreadyHas) return this;
18996
+ const dragField = type === "sl" ? "stopLoss" : "takeProfit";
18997
+ const alreadyHasInLevel = type === "sl" ? level.stopLossPrice !== void 0 : level.takeProfitPrice !== void 0;
18998
+ const alreadyHasPending = this.pendingLevelDrags.get(label)?.some((c) => c.field === dragField && !c.isRemoval) ?? false;
18999
+ if (alreadyHasInLevel || alreadyHasPending) return this;
18888
19000
  const { priceH } = this.getChartLayout();
18889
19001
  const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(this.viewport);
18890
19002
  const entryY = this.scaleManager.yToPixel(level.price, priceRange, priceH);
@@ -18893,6 +19005,10 @@ var _ChartEngine = class _ChartEngine {
18893
19005
  const bracketY = entryY + (isBelowEntry ? 30 : -30);
18894
19006
  const defaultPrice = this.scaleManager.pixelToPrice(bracketY, priceRange, priceH);
18895
19007
  this.updateLevelBracket(label, type, defaultPrice);
19008
+ const existingDrags = this.pendingLevelDrags.get(label) ?? [];
19009
+ const filteredDrags = existingDrags.filter((c) => c.field !== dragField);
19010
+ filteredDrags.push({ field: dragField, newPrice: defaultPrice, oldPrice: defaultPrice, isNew: true });
19011
+ this.pendingLevelDrags.set(label, filteredDrags);
18896
19012
  this.emitter.emit("tradeLevelBracketActivated", {
18897
19013
  label,
18898
19014
  bracketType: type,
@@ -18955,7 +19071,20 @@ var _ChartEngine = class _ChartEngine {
18955
19071
  * No-op if the target doesn't exist or doesn't have the bracket type.
18956
19072
  */
18957
19073
  removeBracket(type, label) {
18958
- if (label) return this.updateLevelBracket(label, type, null);
19074
+ if (label) {
19075
+ const field = type === "sl" ? "stopLoss" : "takeProfit";
19076
+ const level = this.levels.find((l) => l.label === label);
19077
+ if (!level) return this;
19078
+ const existingChange = this.pendingLevelDrags.get(label)?.find((c) => c.field === field);
19079
+ const effectivePrice = existingChange && !existingChange.isRemoval ? existingChange.newPrice : type === "sl" ? level.stopLossPrice : level.takeProfitPrice;
19080
+ if (effectivePrice === void 0) return this;
19081
+ const existingDrags = this.pendingLevelDrags.get(label) ?? [];
19082
+ const filteredDrags = existingDrags.filter((c) => c.field !== field);
19083
+ filteredDrags.push({ field, newPrice: effectivePrice, oldPrice: effectivePrice, isRemoval: true });
19084
+ this.pendingLevelDrags.set(label, filteredDrags);
19085
+ this.renderTradeLayer();
19086
+ return this;
19087
+ }
18959
19088
  if (type === "sl") {
18960
19089
  this.draftBracketPnl = { ...this.draftBracketPnl, sl: void 0 };
18961
19090
  } else {
@@ -18977,12 +19106,13 @@ var _ChartEngine = class _ChartEngine {
18977
19106
  return this;
18978
19107
  }
18979
19108
  /**
18980
- * Set or clear the estimated PNL text shown on a draft order's SL or TP bracket line.
18981
- * Pass `null` to clear the text. No-op if no draft order is active.
19109
+ * Set or clear the estimated PNL text shown on the SL or TP bracket line.
19110
+ * Applies to the currently active bracket host — a draft order while drafting,
19111
+ * or the selected/hovered level while editing an existing pending order or position.
19112
+ * Pass `null` to clear the text.
18982
19113
  * Use this to display the consumer-calculated estimated profit/loss next to the bracket.
18983
19114
  */
18984
19115
  setDraftBracketPnl(type, pnlText) {
18985
- if (!this.draftOrderLabel) return this;
18986
19116
  if (type === "sl") {
18987
19117
  this.draftBracketPnl = { ...this.draftBracketPnl, sl: pnlText ?? void 0 };
18988
19118
  } else {
@@ -19060,6 +19190,7 @@ var _ChartEngine = class _ChartEngine {
19060
19190
  */
19061
19191
  selectLevel(label) {
19062
19192
  this.selectedTradeLabel = label;
19193
+ if (label !== null) this.crosshair = null;
19063
19194
  this.renderTradeLayer();
19064
19195
  return this;
19065
19196
  }
@@ -19367,25 +19498,31 @@ var _ChartEngine = class _ChartEngine {
19367
19498
  );
19368
19499
  const processedLevels = this.levels.filter((l) => !(l.type === "pending" && l.ToClose != null)).map((l) => {
19369
19500
  const draftChanges = this.pendingLevelDrags.get(l.label) ?? [];
19370
- const slDraft = draftChanges.find((c) => c.field === "stopLoss")?.newPrice;
19371
- const tpDraft = draftChanges.find((c) => c.field === "takeProfit")?.newPrice;
19501
+ const slChange = draftChanges.find((c) => c.field === "stopLoss");
19502
+ const tpChange = draftChanges.find((c) => c.field === "takeProfit");
19503
+ const slDraft = slChange && !slChange.isRemoval ? slChange.newPrice : void 0;
19504
+ const tpDraft = tpChange && !tpChange.isRemoval ? tpChange.newPrice : void 0;
19505
+ const slIsRemoval = slChange?.isRemoval ?? false;
19506
+ const tpIsRemoval = tpChange?.isRemoval ?? false;
19372
19507
  const mainDraft = draftChanges.find((c) => c.field === "main")?.newPrice;
19373
19508
  const linked = bracketOrders.filter((b) => b.ToClose === l.label);
19374
- if (linked.length === 0 && slDraft === void 0 && tpDraft === void 0 && mainDraft === void 0)
19509
+ if (linked.length === 0 && slDraft === void 0 && tpDraft === void 0 && mainDraft === void 0 && !slIsRemoval && !tpIsRemoval)
19375
19510
  return l;
19376
19511
  const clone = { ...l };
19377
19512
  if (mainDraft !== void 0) clone.price = mainDraft;
19378
19513
  if (slDraft !== void 0) clone.stopLossPrice = slDraft;
19514
+ else if (slIsRemoval) clone.stopLossPrice = void 0;
19379
19515
  if (tpDraft !== void 0) clone.takeProfitPrice = tpDraft;
19516
+ else if (tpIsRemoval) clone.takeProfitPrice = void 0;
19380
19517
  if (linked.length > 0) {
19381
19518
  const parentSide = l.side ?? "buy";
19382
19519
  for (const b of linked) {
19383
19520
  const isSL = parentSide === "buy" ? b.price < l.price : b.price > l.price;
19384
19521
  if (isSL) {
19385
- clone.stopLossPrice = slDraft ?? clone.stopLossPrice ?? b.price;
19522
+ clone.stopLossPrice = slIsRemoval ? void 0 : slDraft ?? clone.stopLossPrice ?? b.price;
19386
19523
  clone._slOrderLabel = b.label;
19387
19524
  } else {
19388
- clone.takeProfitPrice = tpDraft ?? clone.takeProfitPrice ?? b.price;
19525
+ clone.takeProfitPrice = tpIsRemoval ? void 0 : tpDraft ?? clone.takeProfitPrice ?? b.price;
19389
19526
  clone._tpOrderLabel = b.label;
19390
19527
  }
19391
19528
  }
@@ -19447,8 +19584,9 @@ var _ChartEngine = class _ChartEngine {
19447
19584
  const parentPrice = this._effectiveEntryPrice(result.label, level?.price ?? oldPrice);
19448
19585
  const isEntryOrder = level?.entryPriceEditable === true;
19449
19586
  const isDraft = level?.data?._isDraft === true;
19587
+ const isMobilePendingOrder = this.hideLevelConfirmCancel && result.type === "pending" && !isDraft;
19450
19588
  const all = this.dataStore.all;
19451
- const marketPrice = isEntryOrder || isDraft ? 0 : all[all.length - 1]?.close ?? 0;
19589
+ const marketPrice = isEntryOrder || isDraft || isMobilePendingOrder ? 0 : all[all.length - 1]?.close ?? 0;
19452
19590
  if (!TradeDragHandler.isBracketPriceValid(
19453
19591
  result.newPrice,
19454
19592
  result.bracketType,
@@ -19578,16 +19716,14 @@ var _ChartEngine = class _ChartEngine {
19578
19716
  if (!level) return;
19579
19717
  if (label === this.draftOrderLabel) {
19580
19718
  const mainEntry = changes.find((c) => c.field === "main");
19581
- const slEntry = changes.find((c) => c.field === "stopLoss" && c.isNew);
19582
- const tpEntry = changes.find((c) => c.field === "takeProfit" && c.isNew);
19583
19719
  const draft = level;
19584
19720
  this._onOrderSubmit?.({
19585
19721
  price: mainEntry?.newPrice ?? draft.price,
19586
19722
  lots: this._minLots,
19587
19723
  side: draft.side,
19588
19724
  orderType: draft.orderType,
19589
- ...slEntry ? { stopLoss: slEntry.newPrice } : {},
19590
- ...tpEntry ? { takeProfit: tpEntry.newPrice } : {}
19725
+ ...draft.stopLossPrice !== void 0 ? { stopLoss: draft.stopLossPrice } : {},
19726
+ ...draft.takeProfitPrice !== void 0 ? { takeProfit: draft.takeProfitPrice } : {}
19591
19727
  });
19592
19728
  this.emitter.emit("tradeLevelConfirmed", {
19593
19729
  label,
@@ -20157,6 +20293,7 @@ var _ChartEngine = class _ChartEngine {
20157
20293
  this._mobileLayout = isMobile;
20158
20294
  this.leftBar?.setMobileMode(isMobile);
20159
20295
  this.topBar?.setMobileLayout(isMobile);
20296
+ this.tradeButton?.setMobileMode(isMobile);
20160
20297
  if (this.siEl) {
20161
20298
  this.siEl.style.flexDirection = isMobile ? "column" : "row";
20162
20299
  this.siEl.style.alignItems = isMobile ? "flex-start" : "center";
@@ -20259,24 +20396,20 @@ var _ChartEngine = class _ChartEngine {
20259
20396
  const isDraftBracket = parentLevel?.data?._isDraft === true;
20260
20397
  if (isDraftBracket && parentLevel) {
20261
20398
  const bracketKey = closedLevel.bracketType === "stopLoss" ? "sl" : "tp";
20262
- const { priceH } = this.getChartLayout();
20263
- const priceRange = this._displayedPriceRange ?? this.getScaledPriceRange(this.viewport);
20264
- const entryY = this.scaleManager.yToPixel(
20265
- parentLevel.price,
20266
- priceRange,
20267
- priceH
20268
- );
20269
- const side = parentLevel.side ?? "buy";
20270
- const isBelowEntry = side === "buy" && bracketKey === "sl" || side === "sell" && bracketKey === "tp";
20271
- const bracketY = entryY + (isBelowEntry ? 30 : -30);
20272
- const defaultPrice = this.scaleManager.pixelToPrice(bracketY, priceRange, priceH);
20273
- this.updateDraftOrderBracket(bracketKey, defaultPrice);
20274
- this.emitter.emit("tradeLevelDrag", {
20399
+ this.updateDraftOrderBracket(bracketKey, null);
20400
+ this.emitter.emit("tradeLevelEdit", {
20275
20401
  label: closedLevel.label,
20276
- newPrice: defaultPrice,
20277
- data: parentLevel?.data,
20278
- bracketType: closedLevel.bracketType,
20279
- isFullscreen: this.isFullscreen
20402
+ type: "pending",
20403
+ data: parentLevel.data,
20404
+ isFullscreen: this.isFullscreen,
20405
+ changes: [
20406
+ {
20407
+ field: closedLevel.bracketType === "stopLoss" ? "REMOVE_SL" : "REMOVE_TP",
20408
+ newPrice: 0,
20409
+ data: parentLevel.data,
20410
+ bracketOrderLabel: void 0
20411
+ }
20412
+ ]
20280
20413
  });
20281
20414
  return;
20282
20415
  }
@@ -20325,7 +20458,7 @@ var _ChartEngine = class _ChartEngine {
20325
20458
  Math.min(Math.ceil(this.viewport.endIndex), Math.round(rawBarIndex))
20326
20459
  );
20327
20460
  const bar = this.dataStore.all[clamped] ?? null;
20328
- this.crosshair = { x, y, barIndex: clamped, price, bar };
20461
+ this.crosshair = this.selectedTradeLabel === null ? { x, y, barIndex: clamped, price, bar } : null;
20329
20462
  if (this.tradeButton) {
20330
20463
  this.tradeButton.setY(y);
20331
20464
  if (this._isTradingActive) {
@@ -20349,8 +20482,9 @@ var _ChartEngine = class _ChartEngine {
20349
20482
  this._isBracketNew = false;
20350
20483
  const isEntryOrder = level.entryPriceEditable === true;
20351
20484
  const isDraft = level.data?._isDraft === true;
20485
+ const isMobilePendingOrder = this.hideLevelConfirmCancel && level.type === "pending" && !isDraft;
20352
20486
  const all = this.dataStore.all;
20353
- const marketPrice = isEntryOrder || isDraft ? 0 : all[all.length - 1]?.close ?? 0;
20487
+ const marketPrice = isEntryOrder || isDraft || isMobilePendingOrder ? 0 : all[all.length - 1]?.close ?? 0;
20354
20488
  this.tradeDragHandler.startBracketDrag(
20355
20489
  level,
20356
20490
  dragHit.bracketType,
@@ -20408,20 +20542,9 @@ var _ChartEngine = class _ChartEngine {
20408
20542
  const initialPrice = this.scaleManager.pixelToPrice(btnCenterY, priceRange, priceH);
20409
20543
  const isEntryOrder = level.entryPriceEditable === true;
20410
20544
  const isDraft = level.data?._isDraft === true;
20545
+ const isMobilePendingOrder = this.hideLevelConfirmCancel && level.type === "pending" && !isDraft;
20411
20546
  const all = this.dataStore.all;
20412
- const marketPrice = isEntryOrder || isDraft ? 0 : all[all.length - 1]?.close ?? 0;
20413
- if (!isDraft) {
20414
- this.emitter.emit("tradeLevelEditOpen", {
20415
- label: level.label,
20416
- type: level.type,
20417
- data: level.data,
20418
- price: level.price,
20419
- side: level.side,
20420
- stopLossPrice: level.stopLossPrice,
20421
- takeProfitPrice: level.takeProfitPrice,
20422
- isFullscreen: this.isFullscreen
20423
- });
20424
- }
20547
+ const marketPrice = isEntryOrder || isDraft || isMobilePendingOrder ? 0 : all[all.length - 1]?.close ?? 0;
20425
20548
  this._pendingBracketOrderLabel = null;
20426
20549
  this._isBracketNew = true;
20427
20550
  this.tradeDragHandler.startBracketDrag(
@@ -20811,6 +20934,7 @@ exports.DEFAULT_LABELS = DEFAULT_LABELS;
20811
20934
  exports.DEFAULT_PRICE_AXIS_CONFIG = DEFAULT_PRICE_AXIS_CONFIG;
20812
20935
  exports.DEFAULT_TIME_AXIS_CONFIG = DEFAULT_TIME_AXIS_CONFIG;
20813
20936
  exports.DEFAULT_TOP_BAR_CONFIG = DEFAULT_TOP_BAR_CONFIG;
20937
+ exports.DEFAULT_TRADE_BUTTON_CONFIG = DEFAULT_TRADE_BUTTON_CONFIG;
20814
20938
  exports.DEFAULT_UI_CONFIG = DEFAULT_UI_CONFIG;
20815
20939
  exports.DatePriceRange = DatePriceRange;
20816
20940
  exports.DateRange = DateRange;