acttrader-charts 1.2.0-beta.9 → 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 +281 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +281 -9
- package/dist/index.js.map +1 -1
- package/dist/webview/chart.html +23 -21
- package/package.json +1 -1
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 !==
|
|
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 !==
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
|
@@ -6574,6 +6645,21 @@ var MobileToolsSheet = class {
|
|
|
6574
6645
|
};
|
|
6575
6646
|
|
|
6576
6647
|
// src/ui/MobileHeader.ts
|
|
6648
|
+
var SERIES_OPTIONS = [
|
|
6649
|
+
{ value: "candlestick", label: "Candlestick" },
|
|
6650
|
+
{ value: "hollow", label: "Hollow Candles" },
|
|
6651
|
+
{ value: "ohlc", label: "OHLC Bars" },
|
|
6652
|
+
{ value: "line", label: "Line" },
|
|
6653
|
+
{ value: "area", label: "Area" },
|
|
6654
|
+
{ value: "heikinashi", label: "Heikin Ashi" },
|
|
6655
|
+
{ value: "volumecandles", label: "Volume Candles" },
|
|
6656
|
+
{ value: "linemarkers", label: "Line with Markers" },
|
|
6657
|
+
{ value: "step", label: "Step Line" },
|
|
6658
|
+
{ value: "hlcarea", label: "HLC Area" },
|
|
6659
|
+
{ value: "baseline", label: "Baseline" },
|
|
6660
|
+
{ value: "columns", label: "Columns" },
|
|
6661
|
+
{ value: "highlow", label: "High Low" }
|
|
6662
|
+
];
|
|
6577
6663
|
var TF_PILLS3 = ["1m", "5m", "15m", "1h", "4h", "1D", "1W"];
|
|
6578
6664
|
var TF_LABEL3 = {
|
|
6579
6665
|
"1m": "1m",
|
|
@@ -6618,14 +6704,21 @@ var IC_SETTINGS2 = (sz = 16) => lucide4(
|
|
|
6618
6704
|
1.75
|
|
6619
6705
|
);
|
|
6620
6706
|
var MobileHeader = class {
|
|
6621
|
-
constructor(onTimeframeChange, toolsController, onAskAiClick, onSettingsClick, currentTf, theme, fontFamily) {
|
|
6707
|
+
constructor(onTimeframeChange, toolsController, onAskAiClick, onSettingsClick, onSeriesChange, currentTf, currentSeries, theme, fontFamily) {
|
|
6622
6708
|
this.toolsBtn = null;
|
|
6623
6709
|
this.tfBtns = /* @__PURE__ */ new Map();
|
|
6624
6710
|
this.toolsActive = false;
|
|
6711
|
+
this.seriesBtn = null;
|
|
6712
|
+
this.seriesMenu = null;
|
|
6713
|
+
this.seriesMenuOpen = false;
|
|
6714
|
+
this.onDocPointerDown = null;
|
|
6715
|
+
this.onWinReposition = null;
|
|
6625
6716
|
this.onTimeframeChange = onTimeframeChange;
|
|
6626
6717
|
this.onAskAiClick = onAskAiClick;
|
|
6627
6718
|
this.onSettingsClick = onSettingsClick;
|
|
6719
|
+
this.onSeriesChange = onSeriesChange;
|
|
6628
6720
|
this.currentTf = currentTf;
|
|
6721
|
+
this.currentSeries = currentSeries;
|
|
6629
6722
|
this.theme = theme;
|
|
6630
6723
|
this.fontFamily = fontFamily ?? MONO_FONT2;
|
|
6631
6724
|
this.sheet = toolsController ? new MobileToolsSheet(theme, toolsController, this.fontFamily) : null;
|
|
@@ -6636,9 +6729,11 @@ var MobileHeader = class {
|
|
|
6636
6729
|
}
|
|
6637
6730
|
// ── Build ──────────────────────────────────────────────────────────────────
|
|
6638
6731
|
build() {
|
|
6732
|
+
this.closeSeriesMenu();
|
|
6639
6733
|
this.el.innerHTML = "";
|
|
6640
6734
|
this.tfBtns.clear();
|
|
6641
6735
|
this.toolsBtn = null;
|
|
6736
|
+
this.seriesBtn = null;
|
|
6642
6737
|
Object.assign(this.el.style, {
|
|
6643
6738
|
display: "flex",
|
|
6644
6739
|
alignItems: "center",
|
|
@@ -6692,6 +6787,41 @@ var MobileHeader = class {
|
|
|
6692
6787
|
});
|
|
6693
6788
|
this.el.appendChild(divider);
|
|
6694
6789
|
}
|
|
6790
|
+
if (this.onSeriesChange) {
|
|
6791
|
+
const btn = document.createElement("button");
|
|
6792
|
+
btn.setAttribute("data-testid", "m-chart-type-btn");
|
|
6793
|
+
btn.title = "Chart type";
|
|
6794
|
+
btn.innerHTML = this.seriesIconHtml(this.currentSeries);
|
|
6795
|
+
Object.assign(btn.style, {
|
|
6796
|
+
display: "flex",
|
|
6797
|
+
alignItems: "center",
|
|
6798
|
+
justifyContent: "center",
|
|
6799
|
+
padding: "6px",
|
|
6800
|
+
borderRadius: "4px",
|
|
6801
|
+
border: "none",
|
|
6802
|
+
whiteSpace: "nowrap",
|
|
6803
|
+
flexShrink: "0",
|
|
6804
|
+
cursor: "pointer",
|
|
6805
|
+
outline: "none",
|
|
6806
|
+
background: "transparent",
|
|
6807
|
+
color: this.theme.axisText,
|
|
6808
|
+
transition: "color 0.15s, background 0.15s"
|
|
6809
|
+
});
|
|
6810
|
+
btn.addEventListener("click", (e) => {
|
|
6811
|
+
e.stopPropagation();
|
|
6812
|
+
this.toggleSeriesMenu();
|
|
6813
|
+
});
|
|
6814
|
+
this.seriesBtn = btn;
|
|
6815
|
+
this.el.appendChild(btn);
|
|
6816
|
+
const d2 = document.createElement("div");
|
|
6817
|
+
Object.assign(d2.style, {
|
|
6818
|
+
width: "1px",
|
|
6819
|
+
height: "20px",
|
|
6820
|
+
background: this.theme.axisBorder,
|
|
6821
|
+
flexShrink: "0"
|
|
6822
|
+
});
|
|
6823
|
+
this.el.appendChild(d2);
|
|
6824
|
+
}
|
|
6695
6825
|
for (const tf of TF_PILLS3) {
|
|
6696
6826
|
const btn = document.createElement("button");
|
|
6697
6827
|
btn.setAttribute("data-testid", `m-tf-${TF_LABEL3[tf] ?? tf}`);
|
|
@@ -6792,6 +6922,135 @@ var MobileHeader = class {
|
|
|
6792
6922
|
btn.style.color = active ? t.ui.accent : t.topBar.btnColor;
|
|
6793
6923
|
}
|
|
6794
6924
|
}
|
|
6925
|
+
// ── Chart-type (series) picker ───────────────────────────────────────────────
|
|
6926
|
+
/** Current-series icon, sized for the header button (mirrors AdvancedToolbar). */
|
|
6927
|
+
seriesIconHtml(series) {
|
|
6928
|
+
const raw = ICON_SERIES[series] ?? ICON_SERIES.candlestick;
|
|
6929
|
+
return raw.replace(/width="\d+" height="\d+"/, 'width="18" height="18"');
|
|
6930
|
+
}
|
|
6931
|
+
toggleSeriesMenu() {
|
|
6932
|
+
if (this.seriesMenuOpen) this.closeSeriesMenu();
|
|
6933
|
+
else this.openSeriesMenu();
|
|
6934
|
+
}
|
|
6935
|
+
openSeriesMenu() {
|
|
6936
|
+
if (this.seriesMenuOpen || !this.seriesBtn || typeof document === "undefined") return;
|
|
6937
|
+
this.seriesMenu = this.buildSeriesMenu();
|
|
6938
|
+
document.body.appendChild(this.seriesMenu);
|
|
6939
|
+
this.seriesMenuOpen = true;
|
|
6940
|
+
this.positionSeriesMenu();
|
|
6941
|
+
this.styleSeriesBtnActive(true);
|
|
6942
|
+
this.onDocPointerDown = (e) => {
|
|
6943
|
+
const t = e.target;
|
|
6944
|
+
if (this.seriesMenu?.contains(t) || this.seriesBtn?.contains(t)) return;
|
|
6945
|
+
this.closeSeriesMenu();
|
|
6946
|
+
};
|
|
6947
|
+
this.onWinReposition = () => this.closeSeriesMenu();
|
|
6948
|
+
setTimeout(() => {
|
|
6949
|
+
if (!this.seriesMenuOpen) return;
|
|
6950
|
+
document.addEventListener("pointerdown", this.onDocPointerDown, true);
|
|
6951
|
+
window.addEventListener("resize", this.onWinReposition, true);
|
|
6952
|
+
window.addEventListener("scroll", this.onWinReposition, true);
|
|
6953
|
+
this.el.addEventListener("scroll", this.onWinReposition, true);
|
|
6954
|
+
}, 0);
|
|
6955
|
+
}
|
|
6956
|
+
closeSeriesMenu() {
|
|
6957
|
+
if (this.onDocPointerDown) {
|
|
6958
|
+
document.removeEventListener("pointerdown", this.onDocPointerDown, true);
|
|
6959
|
+
this.onDocPointerDown = null;
|
|
6960
|
+
}
|
|
6961
|
+
if (this.onWinReposition) {
|
|
6962
|
+
window.removeEventListener("resize", this.onWinReposition, true);
|
|
6963
|
+
window.removeEventListener("scroll", this.onWinReposition, true);
|
|
6964
|
+
this.el.removeEventListener("scroll", this.onWinReposition, true);
|
|
6965
|
+
this.onWinReposition = null;
|
|
6966
|
+
}
|
|
6967
|
+
this.seriesMenu?.remove();
|
|
6968
|
+
this.seriesMenu = null;
|
|
6969
|
+
this.seriesMenuOpen = false;
|
|
6970
|
+
this.styleSeriesBtnActive(false);
|
|
6971
|
+
}
|
|
6972
|
+
buildSeriesMenu() {
|
|
6973
|
+
const t = this.theme;
|
|
6974
|
+
const menu = document.createElement("div");
|
|
6975
|
+
menu.setAttribute("data-testid", "m-chart-type-menu");
|
|
6976
|
+
Object.assign(menu.style, {
|
|
6977
|
+
position: "fixed",
|
|
6978
|
+
zIndex: "2147483000",
|
|
6979
|
+
minWidth: "184px",
|
|
6980
|
+
maxHeight: "60vh",
|
|
6981
|
+
overflowY: "auto",
|
|
6982
|
+
padding: "4px",
|
|
6983
|
+
borderRadius: "8px",
|
|
6984
|
+
border: `1px solid ${t.axisBorder}`,
|
|
6985
|
+
background: t.background,
|
|
6986
|
+
boxShadow: "0 8px 28px rgba(0,0,0,0.35)",
|
|
6987
|
+
fontFamily: this.fontFamily,
|
|
6988
|
+
WebkitOverflowScrolling: "touch"
|
|
6989
|
+
});
|
|
6990
|
+
for (const opt of SERIES_OPTIONS) {
|
|
6991
|
+
const active = opt.value === this.currentSeries;
|
|
6992
|
+
const row = document.createElement("button");
|
|
6993
|
+
row.type = "button";
|
|
6994
|
+
row.setAttribute("data-testid", `m-chart-type-${opt.value}`);
|
|
6995
|
+
Object.assign(row.style, {
|
|
6996
|
+
display: "flex",
|
|
6997
|
+
alignItems: "center",
|
|
6998
|
+
gap: "10px",
|
|
6999
|
+
width: "100%",
|
|
7000
|
+
padding: "9px 10px",
|
|
7001
|
+
borderRadius: "6px",
|
|
7002
|
+
border: "none",
|
|
7003
|
+
cursor: "pointer",
|
|
7004
|
+
outline: "none",
|
|
7005
|
+
textAlign: "left",
|
|
7006
|
+
fontSize: "13px",
|
|
7007
|
+
fontFamily: this.fontFamily,
|
|
7008
|
+
background: active ? t.ui.accentBg : "transparent",
|
|
7009
|
+
color: active ? t.ui.accent : t.axisText
|
|
7010
|
+
});
|
|
7011
|
+
const icon = (ICON_SERIES[opt.value] ?? "").replace(/width="\d+" height="\d+"/, 'width="16" height="16"');
|
|
7012
|
+
const check = active ? ICON_CHECKMARK.replace(/width="\d+" height="\d+"/, 'width="16" height="16"') : "";
|
|
7013
|
+
row.innerHTML = `<span style="display:flex;flex-shrink:0;">${icon}</span><span style="flex:1;">${opt.label}</span><span style="display:flex;flex-shrink:0;width:16px;">${check}</span>`;
|
|
7014
|
+
row.addEventListener("mouseenter", () => {
|
|
7015
|
+
if (opt.value !== this.currentSeries) row.style.background = t.ui.accentBg;
|
|
7016
|
+
});
|
|
7017
|
+
row.addEventListener("mouseleave", () => {
|
|
7018
|
+
if (opt.value !== this.currentSeries) row.style.background = "transparent";
|
|
7019
|
+
});
|
|
7020
|
+
row.addEventListener("click", (e) => {
|
|
7021
|
+
e.stopPropagation();
|
|
7022
|
+
this.selectSeries(opt.value);
|
|
7023
|
+
});
|
|
7024
|
+
menu.appendChild(row);
|
|
7025
|
+
}
|
|
7026
|
+
return menu;
|
|
7027
|
+
}
|
|
7028
|
+
positionSeriesMenu() {
|
|
7029
|
+
if (!this.seriesMenu || !this.seriesBtn) return;
|
|
7030
|
+
const r = this.seriesBtn.getBoundingClientRect();
|
|
7031
|
+
const m = this.seriesMenu;
|
|
7032
|
+
m.style.top = `${r.bottom + 6}px`;
|
|
7033
|
+
m.style.left = `${r.left}px`;
|
|
7034
|
+
const mr = m.getBoundingClientRect();
|
|
7035
|
+
if (mr.right > window.innerWidth - 8) {
|
|
7036
|
+
m.style.left = `${Math.max(8, window.innerWidth - 8 - mr.width)}px`;
|
|
7037
|
+
}
|
|
7038
|
+
if (mr.bottom > window.innerHeight - 8) {
|
|
7039
|
+
m.style.top = `${Math.max(8, r.top - 6 - mr.height)}px`;
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
7042
|
+
selectSeries(series) {
|
|
7043
|
+
this.closeSeriesMenu();
|
|
7044
|
+
if (series === this.currentSeries) return;
|
|
7045
|
+
this.currentSeries = series;
|
|
7046
|
+
if (this.seriesBtn) this.seriesBtn.innerHTML = this.seriesIconHtml(series);
|
|
7047
|
+
this.onSeriesChange?.(series);
|
|
7048
|
+
}
|
|
7049
|
+
styleSeriesBtnActive(active) {
|
|
7050
|
+
if (!this.seriesBtn) return;
|
|
7051
|
+
this.seriesBtn.style.color = active ? this.theme.ui.accent : this.theme.axisText;
|
|
7052
|
+
this.seriesBtn.style.background = active ? this.theme.ui.accentBg : "transparent";
|
|
7053
|
+
}
|
|
6795
7054
|
// ── Public surface (drop-in parity with CompactToolbar) ──────────────────────
|
|
6796
7055
|
setTimeframe(tf) {
|
|
6797
7056
|
this.currentTf = tf;
|
|
@@ -6809,10 +7068,12 @@ var MobileHeader = class {
|
|
|
6809
7068
|
this.sheet?.setTheme(theme);
|
|
6810
7069
|
}
|
|
6811
7070
|
destroy() {
|
|
7071
|
+
this.closeSeriesMenu();
|
|
6812
7072
|
this.sheet?.destroy();
|
|
6813
7073
|
this.el.remove();
|
|
6814
7074
|
this.tfBtns.clear();
|
|
6815
7075
|
this.toolsBtn = null;
|
|
7076
|
+
this.seriesBtn = null;
|
|
6816
7077
|
}
|
|
6817
7078
|
// ── Forwarded to the "Draw & Indicators" sheet so its toggles stay in sync ───
|
|
6818
7079
|
/**
|
|
@@ -6828,15 +7089,24 @@ var MobileHeader = class {
|
|
|
6828
7089
|
this.sheet?.syncVolume(show);
|
|
6829
7090
|
}
|
|
6830
7091
|
// ── No-op stubs — controls this variant intentionally does not render ────────
|
|
6831
|
-
|
|
7092
|
+
/** Engine → header sync: reflect an external series change (restored layout
|
|
7093
|
+
* state, keyboard shortcut, etc.) on the chart-type button. */
|
|
7094
|
+
setSeries(series) {
|
|
7095
|
+
this.currentSeries = series;
|
|
7096
|
+
if (this.seriesBtn) this.seriesBtn.innerHTML = this.seriesIconHtml(series);
|
|
7097
|
+
if (this.seriesMenuOpen) {
|
|
7098
|
+
this.closeSeriesMenu();
|
|
7099
|
+
this.openSeriesMenu();
|
|
7100
|
+
}
|
|
6832
7101
|
}
|
|
6833
7102
|
setIndicatorOptions(_options) {
|
|
6834
7103
|
}
|
|
6835
7104
|
closeDropdown() {
|
|
6836
7105
|
this.sheet?.close();
|
|
7106
|
+
this.closeSeriesMenu();
|
|
6837
7107
|
}
|
|
6838
7108
|
get hasOpenDropdown() {
|
|
6839
|
-
return this.sheet?.isOpen ?? false;
|
|
7109
|
+
return (this.sheet?.isOpen ?? false) || this.seriesMenuOpen;
|
|
6840
7110
|
}
|
|
6841
7111
|
setFullscreen(_active) {
|
|
6842
7112
|
}
|
|
@@ -23726,7 +23996,9 @@ var _ChartEngine = class _ChartEngine {
|
|
|
23726
23996
|
toolsController,
|
|
23727
23997
|
config.onAskAiClick ?? null,
|
|
23728
23998
|
this.settingsEnabled ? () => this.openChartSettings() : null,
|
|
23999
|
+
(s) => this.setSeries(s),
|
|
23729
24000
|
this.timeframe,
|
|
24001
|
+
this.series,
|
|
23730
24002
|
this.theme,
|
|
23731
24003
|
this.uiConfig.topBar?.dropBtnFontFamily
|
|
23732
24004
|
);
|