acttrader-charts 1.3.0-beta.1 → 1.3.0-beta.2

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
@@ -1890,6 +1890,8 @@ var FONT_BRACKET = "12px 'Inter', system-ui, sans-serif";
1890
1890
  var BRACKET_BASE_RATIO = 0.68;
1891
1891
  var BRACKET_COL_OFFSETS = [0, -0.08, 0.08];
1892
1892
  var ADD_BRACKET_BTN_OFFSET_PX = 30;
1893
+ var __bracketDraws = [];
1894
+ var __lastDupeSignature = "";
1893
1895
  function hasPnl(level) {
1894
1896
  return level.type === "position" && !!level.pnlText;
1895
1897
  }
@@ -1934,8 +1936,9 @@ function getLevelDisplayText(level, price, precision, omitLots = false) {
1934
1936
  }
1935
1937
  function computeBracketLineX(levels, scale, priceRange, priceH, chartW, draggingLabel, dragNewPrice, hoveredLabel, selectedLabel, bracketBoxH = BRACKET_BOX_H) {
1936
1938
  const items = [];
1939
+ const lineXOwner = hoveredLabel ?? selectedLabel;
1937
1940
  for (const level of levels) {
1938
- if (level.label !== hoveredLabel && level.label !== selectedLabel) continue;
1941
+ if (level.label !== lineXOwner) continue;
1939
1942
  const bh = level;
1940
1943
  if (bh.stopLossPrice !== void 0) {
1941
1944
  const key = `${level.label}:stopLoss`;
@@ -2031,6 +2034,9 @@ function isDraggable(level) {
2031
2034
  return !!level.entryPriceEditable;
2032
2035
  return false;
2033
2036
  }
2037
+ function passiveBracketsVisible(alwaysShowBrackets, activeLabel) {
2038
+ return alwaysShowBrackets && activeLabel === null;
2039
+ }
2034
2040
  function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH, precision, hoveredLabel = null, draggingLabel = null, dragNewPrice = null, selectedLabel = null, pendingLabels = /* @__PURE__ */ new Set(), positionStyle = "line", hideConfirmCancel = false, draftOrderLabel = null, draftBracketPnl = {}, expandedClusters = /* @__PURE__ */ new Set(), clusterThresholdPx = 0, showQuantityField = false, dragNewSLPrice = null, dragNewTPPrice = null, buttonScale = 1, alwaysShowBrackets = false, anchorXResolver = null, timeDragEnabled = false) {
2035
2041
  if (levels.length === 0)
2036
2042
  return {
@@ -2044,6 +2050,7 @@ function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH
2044
2050
  clusters: []
2045
2051
  };
2046
2052
  const colors = theme.tradeLevels;
2053
+ __bracketDraws = [];
2047
2054
  const s = Math.min(Math.max(buttonScale, 1), 3);
2048
2055
  const closeR = CLOSE_R * s;
2049
2056
  const editR = EDIT_R * s;
@@ -2144,8 +2151,9 @@ function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH
2144
2151
  );
2145
2152
  const bracketItems = [];
2146
2153
  const fallbackX = chartW * BRACKET_BASE_RATIO;
2154
+ const cascadeOwner = hoveredLabel ?? selectedLabel;
2147
2155
  for (const level of levels) {
2148
- if (level.label !== hoveredLabel && level.label !== selectedLabel) continue;
2156
+ if (level.label !== cascadeOwner) continue;
2149
2157
  const bh = level;
2150
2158
  const mainDrag = draggingLabel === level.label;
2151
2159
  if (bh.stopLossPrice !== void 0) {
@@ -2192,6 +2200,10 @@ function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH
2192
2200
  }
2193
2201
  const activeLabel = hoveredLabel ?? selectedLabel;
2194
2202
  const bracketPnlHostLabel = draftOrderLabel ?? selectedLabel;
2203
+ const effectiveAlwaysShowBrackets = passiveBracketsVisible(
2204
+ alwaysShowBrackets,
2205
+ activeLabel
2206
+ );
2195
2207
  const visibleMeasured = measured.filter((m) => !collapsedLabels.has(m.level.label));
2196
2208
  const drawOrder = [
2197
2209
  ...visibleMeasured.filter((m) => m.level.label !== activeLabel),
@@ -2243,9 +2255,10 @@ function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH
2243
2255
  dragNewTPPrice,
2244
2256
  buttonScale,
2245
2257
  bracketBoxYMap,
2246
- alwaysShowBrackets,
2258
+ effectiveAlwaysShowBrackets,
2247
2259
  timeDragEnabled && anchorXResolver ? anchorXResolver(level) : null,
2248
- timeDragEnabled
2260
+ timeDragEnabled,
2261
+ level.label === activeLabel
2249
2262
  );
2250
2263
  const ec = labelToExpandedCluster.get(level.label);
2251
2264
  if (ec && layout.boxY !== layout.y - boxH / 2) {
@@ -2327,6 +2340,50 @@ function renderTradeLevels(ctx, levels, scale, priceRange, theme, chartW, priceH
2327
2340
  }
2328
2341
  }
2329
2342
  }
2343
+ {
2344
+ globalThis.__bracketDrawsLast = __bracketDraws.slice();
2345
+ const counts = /* @__PURE__ */ new Map();
2346
+ for (const d of __bracketDraws) {
2347
+ const k = `${d.bracketType}@${d.price}`;
2348
+ counts.set(k, (counts.get(k) ?? 0) + 1);
2349
+ }
2350
+ const dupeKeys = [...counts.entries()].filter(([, n]) => n > 1).map(([k]) => k);
2351
+ if (dupeKeys.length > 0) {
2352
+ const signature = dupeKeys.join(",") + "|" + __bracketDraws.map((d) => `${d.label}/${d.levelType}/${d.bW}`).join(",");
2353
+ if (signature !== __lastDupeSignature) {
2354
+ __lastDupeSignature = signature;
2355
+ const frame = {
2356
+ hoveredLabel,
2357
+ selectedLabel,
2358
+ draftOrderLabel,
2359
+ draftBracketPnl,
2360
+ levels: levels.map((l) => ({
2361
+ label: l.label,
2362
+ type: l.type,
2363
+ price: l.price,
2364
+ sl: l.stopLossPrice,
2365
+ tp: l.takeProfitPrice,
2366
+ isDraft: !!l.data?._isDraft
2367
+ }))
2368
+ };
2369
+ console.warn(
2370
+ `[ActCharts] DUPLICATE BRACKET BOX \u2192 ${dupeKeys.join(", ")}
2371
+ hovered=${hoveredLabel ?? "null"} selected=${selectedLabel ?? "null"} draft=${draftOrderLabel ?? "null"}
2372
+ ` + __bracketDraws.map(
2373
+ (d, i) => ` box#${i + 1} label=${d.label} type=${d.levelType} draft=${d.isDraft} ${d.bracketType}@${d.price} bX=${d.bX} bW=${d.bW} right=${d.rightEdge} boxY=${d.boxY} pnl=${d.pnlText === null ? "none" : JSON.stringify(d.pnlText)} hideCC=${d.hideConfirmCancel}`
2374
+ ).join("\n") + `
2375
+ levels this frame: ` + frame.levels.map((l) => `${l.label}/${l.type}${l.isDraft ? "/DRAFT" : ""} price=${l.price} sl=${l.sl} tp=${l.tp}`).join(" | ")
2376
+ );
2377
+ if (typeof window !== "undefined") {
2378
+ window.__bracketDupe = {
2379
+ dupeKeys,
2380
+ frame,
2381
+ draws: __bracketDraws.slice()
2382
+ };
2383
+ }
2384
+ }
2385
+ }
2386
+ }
2330
2387
  ctx.restore();
2331
2388
  return {
2332
2389
  hitAreas,
@@ -2482,7 +2539,7 @@ function drawDotHoverBox(ctx, level, y, dotX, dotR, theme, precision) {
2482
2539
  });
2483
2540
  ctx.restore();
2484
2541
  }
2485
- function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered, selected, scale, priceRange, priceH, draggingLabel, dragNewPrice, bracketLineXMap, hasPendingChanges, positionStyle, hitAreas, editAreas, hoverAreas, dragAreas, confirmAreas, addBracketAreas, hideConfirmCancel = false, draftBracketPnl, showQuantityField = false, draftQtyPillAreas, dragNewSLPrice = null, dragNewTPPrice = null, buttonScale = 1, bracketBoxYMap, alwaysShowBrackets = false, anchorX = null, timeDragEnabled = false) {
2542
+ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered, selected, scale, priceRange, priceH, draggingLabel, dragNewPrice, bracketLineXMap, hasPendingChanges, positionStyle, hitAreas, editAreas, hoverAreas, dragAreas, confirmAreas, addBracketAreas, hideConfirmCancel = false, draftBracketPnl, showQuantityField = false, draftQtyPillAreas, dragNewSLPrice = null, dragNewTPPrice = null, buttonScale = 1, bracketBoxYMap, alwaysShowBrackets = false, anchorX = null, timeDragEnabled = false, isBracketOwner) {
2486
2543
  const { y, boxY, boxW, boxH } = layout;
2487
2544
  const color = levelColor(level, colors, theme);
2488
2545
  const active = hovered || selected;
@@ -2532,7 +2589,8 @@ function drawLevel(ctx, level, layout, chartW, colors, theme, precision, hovered
2532
2589
  const boxX = draggable ? groupX + GRIP_W + 2 : groupX;
2533
2590
  const badgeTimeDrag = timeDragEnabled && level.timeDraggable === true;
2534
2591
  const bracketHost = level;
2535
- if (active || isMarketDraft || passiveBracketShow) {
2592
+ const bracketOwner = isBracketOwner ?? active;
2593
+ if (bracketOwner || isMarketDraft || passiveBracketShow) {
2536
2594
  const slDragKey = `${level.label}:stopLoss`;
2537
2595
  const tpDragKey = `${level.label}:takeProfit`;
2538
2596
  const mainDragActive = draggingLabel === level.label;
@@ -2994,6 +3052,19 @@ function drawBracketLine(ctx, level, bracketType, bracketPrice, scale, priceRang
2994
3052
  const bY = boxYOverride ?? naturalBoxY;
2995
3053
  const boxCenterY = bY + bH / 2;
2996
3054
  const displaced = Math.abs(bY - naturalBoxY) >= 2;
3055
+ __bracketDraws.push({
3056
+ label: level.label,
3057
+ levelType: level.type,
3058
+ isDraft: !!level.data?._isDraft,
3059
+ bracketType,
3060
+ price,
3061
+ bX: Math.round(bX),
3062
+ bW: Math.round(bW),
3063
+ rightEdge: Math.round(bX + bW),
3064
+ boxY: Math.round(bY),
3065
+ pnlText: pnlText ?? null,
3066
+ hideConfirmCancel
3067
+ });
2997
3068
  if (displaced) {
2998
3069
  drawBracketLeader(ctx, bX + bW / 2, boxCenterY, y, theme);
2999
3070
  }