@tradecanvas/chart 1.71.0 → 1.72.1
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/LayoutSession-DO4bYkUi.cjs +2 -0
- package/dist/LayoutSession-DO4bYkUi.cjs.map +1 -0
- package/dist/{LayoutSession-BOQCN6yY.js → LayoutSession-qRYG8GTD.js} +539 -552
- package/dist/LayoutSession-qRYG8GTD.js.map +1 -0
- package/dist/WidgetCommandPalette-CeCLHPy1.js +177 -0
- package/dist/WidgetCommandPalette-CeCLHPy1.js.map +1 -0
- package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs +2 -0
- package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs.map +1 -0
- package/dist/WidgetDrawingSettings-BeW8fX7r.js +474 -0
- package/dist/WidgetDrawingSettings-BeW8fX7r.js.map +1 -0
- package/dist/WidgetDrawingSettings-Cyl2u_u8.cjs +2 -0
- package/dist/WidgetDrawingSettings-Cyl2u_u8.cjs.map +1 -0
- package/dist/WidgetGoToDate-BNdngHlg.cjs +2 -0
- package/dist/WidgetGoToDate-BNdngHlg.cjs.map +1 -0
- package/dist/WidgetGoToDate-hcDtezRm.js +191 -0
- package/dist/WidgetGoToDate-hcDtezRm.js.map +1 -0
- package/dist/WidgetHotkeySheet-1lRFSdvt.js +416 -0
- package/dist/WidgetHotkeySheet-1lRFSdvt.js.map +1 -0
- package/dist/WidgetHotkeySheet-y5LWKSG6.cjs +2 -0
- package/dist/WidgetHotkeySheet-y5LWKSG6.cjs.map +1 -0
- package/dist/WidgetIndicatorPicker-CGvZ5kYd.js +245 -0
- package/dist/WidgetIndicatorPicker-CGvZ5kYd.js.map +1 -0
- package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs +2 -0
- package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs.map +1 -0
- package/dist/WidgetIndicatorSettings-C7vpco6e.cjs +2 -0
- package/dist/WidgetIndicatorSettings-C7vpco6e.cjs.map +1 -0
- package/dist/WidgetIndicatorSettings-dDkqzzYo.js +326 -0
- package/dist/WidgetIndicatorSettings-dDkqzzYo.js.map +1 -0
- package/dist/WidgetSettings-D7Pq42zW.cjs +2 -0
- package/dist/WidgetSettings-D7Pq42zW.cjs.map +1 -0
- package/dist/WidgetSettings-eOo93L7Z.js +546 -0
- package/dist/WidgetSettings-eOo93L7Z.js.map +1 -0
- package/dist/WidgetSymbolSearch-C4OR42nZ.cjs +2 -0
- package/dist/WidgetSymbolSearch-C4OR42nZ.cjs.map +1 -0
- package/dist/WidgetSymbolSearch-CvPpjIwa.js +580 -0
- package/dist/WidgetSymbolSearch-CvPpjIwa.js.map +1 -0
- package/dist/focusTrap-Br9Zfje0.js +27 -0
- package/dist/focusTrap-Br9Zfje0.js.map +1 -0
- package/dist/focusTrap-DrhhmG0V.cjs +2 -0
- package/dist/focusTrap-DrhhmG0V.cjs.map +1 -0
- package/dist/i18n-C2nQBZTY.cjs +2 -0
- package/dist/i18n-C2nQBZTY.cjs.map +1 -0
- package/dist/{i18n-DUmd4Z_r.js → i18n-C5v6Tzp1.js} +3 -1
- package/dist/i18n-C5v6Tzp1.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/indicatorInputWords-Bd9Pt6qp.js +25 -0
- package/dist/indicatorInputWords-Bd9Pt6qp.js.map +1 -0
- package/dist/indicatorInputWords-CTA4IcJR.cjs +2 -0
- package/dist/indicatorInputWords-CTA4IcJR.cjs.map +1 -0
- package/dist/indicatorLabel-BNZvaOcl.cjs +2 -0
- package/dist/indicatorLabel-BNZvaOcl.cjs.map +1 -0
- package/dist/indicatorLabel-Sbegyhp2.js +17 -0
- package/dist/indicatorLabel-Sbegyhp2.js.map +1 -0
- package/dist/indicatorSettingsTabs-B0liE3ST.cjs +2 -0
- package/dist/indicatorSettingsTabs-B0liE3ST.cjs.map +1 -0
- package/dist/indicatorSettingsTabs-XcgMdbLp.js +161 -0
- package/dist/indicatorSettingsTabs-XcgMdbLp.js.map +1 -0
- package/dist/monthView-B-CieFDJ.cjs +2 -0
- package/dist/monthView-B-CieFDJ.cjs.map +1 -0
- package/dist/monthView-N8yDaTqZ.js +198 -0
- package/dist/monthView-N8yDaTqZ.js.map +1 -0
- package/dist/platformKeys-B82-j1Fs.cjs +2 -0
- package/dist/platformKeys-B82-j1Fs.cjs.map +1 -0
- package/dist/platformKeys-Dd1qOCjF.js +5 -0
- package/dist/platformKeys-Dd1qOCjF.js.map +1 -0
- package/dist/settingsControls-DIjzCFHI.js +379 -0
- package/dist/settingsControls-DIjzCFHI.js.map +1 -0
- package/dist/settingsControls-Tn8_vJJv.cjs +2 -0
- package/dist/settingsControls-Tn8_vJJv.cjs.map +1 -0
- package/dist/symbolCatalog-CA1fpz3s.js +90 -0
- package/dist/symbolCatalog-CA1fpz3s.js.map +1 -0
- package/dist/symbolCatalog-Dx1YnZgm.cjs +2 -0
- package/dist/symbolCatalog-Dx1YnZgm.cjs.map +1 -0
- package/dist/widget/ChartWidget.d.ts +102 -582
- package/dist/widget/ChartWidget.d.ts.map +1 -1
- package/dist/widget/WidgetAlerts.d.ts +35 -0
- package/dist/widget/WidgetAlerts.d.ts.map +1 -0
- package/dist/widget/WidgetChartSettings.d.ts +194 -0
- package/dist/widget/WidgetChartSettings.d.ts.map +1 -0
- package/dist/widget/WidgetCommands.d.ts +54 -0
- package/dist/widget/WidgetCommands.d.ts.map +1 -0
- package/dist/widget/WidgetCompares.d.ts +140 -0
- package/dist/widget/WidgetCompares.d.ts.map +1 -0
- package/dist/widget/WidgetDockArea.d.ts +89 -0
- package/dist/widget/WidgetDockArea.d.ts.map +1 -0
- package/dist/widget/WidgetDrawingEdit.d.ts +73 -0
- package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -0
- package/dist/widget/WidgetGoToDate.d.ts +1 -14
- package/dist/widget/WidgetGoToDate.d.ts.map +1 -1
- package/dist/widget/WidgetIndicatorSettings.d.ts.map +1 -1
- package/dist/widget/WidgetIntervals.d.ts +66 -0
- package/dist/widget/WidgetIntervals.d.ts.map +1 -0
- package/dist/widget/WidgetLayouts.d.ts +85 -0
- package/dist/widget/WidgetLayouts.d.ts.map +1 -0
- package/dist/widget/WidgetLegendView.d.ts +54 -0
- package/dist/widget/WidgetLegendView.d.ts.map +1 -0
- package/dist/widget/WidgetMenus.d.ts +79 -0
- package/dist/widget/WidgetMenus.d.ts.map +1 -0
- package/dist/widget/WidgetReplay.d.ts +199 -0
- package/dist/widget/WidgetReplay.d.ts.map +1 -0
- package/dist/widget/WidgetTrading.d.ts +48 -0
- package/dist/widget/WidgetTrading.d.ts.map +1 -0
- package/dist/widget/WidgetWatchlists.d.ts +77 -0
- package/dist/widget/WidgetWatchlists.d.ts.map +1 -0
- package/dist/widget/drawingSettingsTabs.d.ts +2 -19
- package/dist/widget/drawingSettingsTabs.d.ts.map +1 -1
- package/dist/widget/drawingText.d.ts +24 -0
- package/dist/widget/drawingText.d.ts.map +1 -0
- package/dist/widget/index.cjs +22 -23
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.js +6193 -9868
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/keyTarget.d.ts +4 -0
- package/dist/widget/keyTarget.d.ts.map +1 -1
- package/dist/widget/lazyPart.d.ts +47 -0
- package/dist/widget/lazyPart.d.ts.map +1 -0
- package/dist/widget/locales/ar.d.ts.map +1 -1
- package/dist/widget/locales/cs.d.ts.map +1 -1
- package/dist/widget/locales/da.d.ts.map +1 -1
- package/dist/widget/locales/de.d.ts.map +1 -1
- package/dist/widget/locales/el.d.ts.map +1 -1
- package/dist/widget/locales/en.d.ts +1 -0
- package/dist/widget/locales/en.d.ts.map +1 -1
- package/dist/widget/locales/es.d.ts.map +1 -1
- package/dist/widget/locales/et.d.ts.map +1 -1
- package/dist/widget/locales/fa.d.ts.map +1 -1
- package/dist/widget/locales/fr.d.ts.map +1 -1
- package/dist/widget/locales/he.d.ts.map +1 -1
- package/dist/widget/locales/hu.d.ts.map +1 -1
- package/dist/widget/locales/id.d.ts.map +1 -1
- package/dist/widget/locales/index.cjs +1 -1
- package/dist/widget/locales/index.cjs.map +1 -1
- package/dist/widget/locales/index.js +29 -1
- package/dist/widget/locales/index.js.map +1 -1
- package/dist/widget/locales/it.d.ts.map +1 -1
- package/dist/widget/locales/ja.d.ts.map +1 -1
- package/dist/widget/locales/ko.d.ts.map +1 -1
- package/dist/widget/locales/ms.d.ts.map +1 -1
- package/dist/widget/locales/nb.d.ts.map +1 -1
- package/dist/widget/locales/nl.d.ts.map +1 -1
- package/dist/widget/locales/pl.d.ts.map +1 -1
- package/dist/widget/locales/pt.d.ts.map +1 -1
- package/dist/widget/locales/ro.d.ts.map +1 -1
- package/dist/widget/locales/ru.d.ts.map +1 -1
- package/dist/widget/locales/sk.d.ts.map +1 -1
- package/dist/widget/locales/sv.d.ts.map +1 -1
- package/dist/widget/locales/th.d.ts.map +1 -1
- package/dist/widget/locales/tr.d.ts.map +1 -1
- package/dist/widget/locales/vi.d.ts.map +1 -1
- package/dist/widget/locales/zh.d.ts.map +1 -1
- package/dist/widget/locales/zhHant.d.ts.map +1 -1
- package/dist/widget/pageEnv.d.ts +5 -0
- package/dist/widget/pageEnv.d.ts.map +1 -0
- package/dist/widget/signals.d.ts +41 -0
- package/dist/widget/signals.d.ts.map +1 -0
- package/dist/widget/wallTime.d.ts +15 -0
- package/dist/widget/wallTime.d.ts.map +1 -0
- package/dist/widget/widgetDialogs.d.ts +17 -0
- package/dist/widget/widgetDialogs.d.ts.map +1 -0
- package/dist/widgetFeatures-BM6u0u74.js +186 -0
- package/dist/widgetFeatures-BM6u0u74.js.map +1 -0
- package/dist/widgetFeatures-BZ8OVPN2.cjs +3 -0
- package/dist/widgetFeatures-BZ8OVPN2.cjs.map +1 -0
- package/dist/widgetLocales-BPK5i0Ok.cjs +2 -0
- package/dist/widgetLocales-BPK5i0Ok.cjs.map +1 -0
- package/dist/widgetLocales-DgTR8JSu.js +838 -0
- package/dist/widgetLocales-DgTR8JSu.js.map +1 -0
- package/package.json +3 -3
- package/dist/LayoutSession-BOQCN6yY.js.map +0 -1
- package/dist/LayoutSession-RF8OqOXy.cjs +0 -2
- package/dist/LayoutSession-RF8OqOXy.cjs.map +0 -1
- package/dist/i18n-BsstZ9md.cjs +0 -2
- package/dist/i18n-BsstZ9md.cjs.map +0 -1
- package/dist/i18n-DUmd4Z_r.js.map +0 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { i as e, n as t } from "./focusTrap-Br9Zfje0.js";
|
|
2
|
+
import { f as n, i as r, r as i, s as a, u as o } from "./settingsControls-DIjzCFHI.js";
|
|
3
|
+
import { c as s } from "./i18n-C5v6Tzp1.js";
|
|
4
|
+
import { n as c, t as l } from "./indicatorInputWords-Bd9Pt6qp.js";
|
|
5
|
+
import { n as u, t as d } from "./indicatorSettingsTabs-XcgMdbLp.js";
|
|
6
|
+
import { PRICE_SOURCES as f, parseIndicatorSource as p } from "@tradecanvas/commons";
|
|
7
|
+
import { describeInputs as m, isSeriesPalette as h } from "@tradecanvas/core";
|
|
8
|
+
//#region src/widget/WidgetIndicatorSettings.ts
|
|
9
|
+
var g = 1, _ = 1.5;
|
|
10
|
+
function v(e, t) {
|
|
11
|
+
return {
|
|
12
|
+
visible: !0,
|
|
13
|
+
lineStyle: e?.find((e) => e.key === t)?.lineStyle ?? "solid"
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
var y = (e, t) => JSON.stringify(e) === JSON.stringify(t), b = (e) => JSON.parse(JSON.stringify(e));
|
|
17
|
+
function x(e) {
|
|
18
|
+
return e.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (e) => e.toUpperCase());
|
|
19
|
+
}
|
|
20
|
+
function S(e, t, n) {
|
|
21
|
+
return Object.fromEntries([...new Set([...Object.keys(e), ...Object.keys(t)])].map((e) => [e, {
|
|
22
|
+
...v(n, e),
|
|
23
|
+
...t[e]
|
|
24
|
+
}]));
|
|
25
|
+
}
|
|
26
|
+
var C = class {
|
|
27
|
+
backdrop;
|
|
28
|
+
modal;
|
|
29
|
+
bodyEl;
|
|
30
|
+
titleEl;
|
|
31
|
+
tabsEl;
|
|
32
|
+
defaultsBtn;
|
|
33
|
+
menu = null;
|
|
34
|
+
callbacks;
|
|
35
|
+
target = null;
|
|
36
|
+
draft = {};
|
|
37
|
+
initial = null;
|
|
38
|
+
look = {
|
|
39
|
+
colors: [],
|
|
40
|
+
lineWidth: _,
|
|
41
|
+
plots: {},
|
|
42
|
+
levels: [],
|
|
43
|
+
intervals: null
|
|
44
|
+
};
|
|
45
|
+
tab = "inputs";
|
|
46
|
+
tabButtons = [];
|
|
47
|
+
returnFocus = null;
|
|
48
|
+
uid = g++;
|
|
49
|
+
t;
|
|
50
|
+
constructor(t, n, r) {
|
|
51
|
+
this.callbacks = n, this.t = r ?? ((e) => s[e]), this.backdrop = document.createElement("div"), this.backdrop.className = "tcw-modal-backdrop", this.backdrop.hidden = !0;
|
|
52
|
+
let i = !1;
|
|
53
|
+
this.backdrop.addEventListener("pointerdown", (e) => {
|
|
54
|
+
i = e.target === this.backdrop;
|
|
55
|
+
}), this.backdrop.addEventListener("click", (e) => {
|
|
56
|
+
e.target === this.backdrop && i && this.finish(!1), i = !1;
|
|
57
|
+
}), this.modal = document.createElement("div"), this.modal.className = "tcw-modal tcw-modal-narrow tcw-indi", this.modal.setAttribute("role", "dialog"), this.modal.setAttribute("aria-modal", "true"), this.modal.setAttribute("aria-labelledby", `tcw-indi-title-${this.uid}`), this.modal.tabIndex = -1, this.modal.addEventListener("keydown", (e) => this.onKeyDown(e));
|
|
58
|
+
let a = document.createElement("div");
|
|
59
|
+
a.className = "tcw-modal-header", this.titleEl = document.createElement("h3"), this.titleEl.id = `tcw-indi-title-${this.uid}`;
|
|
60
|
+
let o = document.createElement("button");
|
|
61
|
+
o.type = "button", o.className = "tcw-modal-close", o.setAttribute("aria-label", this.t("indicatorSettings.close")), o.innerHTML = e("x", 16), o.addEventListener("click", () => this.finish(!1)), a.append(this.titleEl, o), this.tabsEl = document.createElement("div"), this.tabsEl.className = "tcw-modal-tabs", this.tabsEl.setAttribute("role", "tablist"), this.tabsEl.addEventListener("keydown", (e) => this.onTabKey(e)), this.bodyEl = document.createElement("div"), this.bodyEl.className = "tcw-modal-body", this.bodyEl.id = `tcw-indi-panel-${this.uid}`, this.bodyEl.setAttribute("role", "tabpanel"), this.defaultsBtn = this.footerButton("defaults", "tcw-footer-menu", () => this.openDefaults()), this.modal.append(a, this.tabsEl, this.bodyEl, this.buildFooter()), this.backdrop.appendChild(this.modal), t.appendChild(this.backdrop);
|
|
62
|
+
}
|
|
63
|
+
buildFooter() {
|
|
64
|
+
this.defaultsBtn.setAttribute("aria-haspopup", "menu"), this.defaultsBtn.setAttribute("aria-expanded", "false"), this.defaultsBtn.innerHTML = `<span></span>${e("chevronDown", 12)}`, this.defaultsBtn.querySelector("span").textContent = this.t("indicatorSettings.defaults");
|
|
65
|
+
let t = document.createElement("div");
|
|
66
|
+
t.className = "tcw-footer-end", t.append(this.footerButton("cancel", "tcw-footer-cancel", () => this.finish(!1), this.t("common.cancel")), this.footerButton("ok", "tcw-done-btn", () => this.finish(!0), this.t("drawingSettings.ok")));
|
|
67
|
+
let n = document.createElement("div");
|
|
68
|
+
return n.className = "tcw-modal-footer", n.append(this.defaultsBtn, t), n;
|
|
69
|
+
}
|
|
70
|
+
open(e) {
|
|
71
|
+
this.target && this.finish(!0), this.backdrop.hidden && (this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null), this.target = e, this.titleEl.textContent = this.t("indicatorSettings.title").replace("{name}", e.name), this.draft = {}, this.look = {
|
|
72
|
+
colors: [...e.colors ?? []],
|
|
73
|
+
lineWidth: e.lineWidth ?? _,
|
|
74
|
+
plots: Object.fromEntries(Object.entries(e.plotStyles ?? {}).map(([e, t]) => [e, { ...t }])),
|
|
75
|
+
levels: [...e.levels ?? []],
|
|
76
|
+
scale: e.scale,
|
|
77
|
+
intervals: e.intervals ?? null
|
|
78
|
+
}, this.initial = {
|
|
79
|
+
params: this.currentParams(),
|
|
80
|
+
look: b(this.look)
|
|
81
|
+
}, this.tab = "inputs", this.renderTabs(), this.renderBody(), this.backdrop.hidden = !1;
|
|
82
|
+
let t = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;
|
|
83
|
+
(this.bodyEl.querySelector("button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])") ?? t ?? this.modal).focus();
|
|
84
|
+
}
|
|
85
|
+
close() {
|
|
86
|
+
this.finish(!0);
|
|
87
|
+
}
|
|
88
|
+
isOpen() {
|
|
89
|
+
return !this.backdrop.hidden;
|
|
90
|
+
}
|
|
91
|
+
destroy() {
|
|
92
|
+
this.menu?.destroy(), this.backdrop.remove();
|
|
93
|
+
}
|
|
94
|
+
finish(e) {
|
|
95
|
+
if (this.target) {
|
|
96
|
+
if (e) {
|
|
97
|
+
let e = document.activeElement;
|
|
98
|
+
(e instanceof HTMLInputElement || e instanceof HTMLSelectElement) && this.modal.contains(e) && e.dispatchEvent(new Event("change"));
|
|
99
|
+
} else this.revert();
|
|
100
|
+
this.target = null, this.menu?.close(), this.backdrop.hidden = !0, this.callbacks.onClose(), this.returnFocus?.isConnected && this.returnFocus.focus(), this.returnFocus = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
revert() {
|
|
104
|
+
let e = this.target, t = this.initial;
|
|
105
|
+
if (!e || !t) return;
|
|
106
|
+
let n = e.instanceId;
|
|
107
|
+
y(this.currentParams(), t.params) || this.callbacks.onApply(n, { ...t.params });
|
|
108
|
+
let r = t.look, i = {};
|
|
109
|
+
y(this.look.colors, r.colors) || (i.colors = [...r.colors]), this.look.lineWidth !== r.lineWidth && (i.lineWidths = [r.lineWidth]), y(this.look.plots, r.plots) || (i.plots = S(this.look.plots, r.plots, e.plots)), Object.keys(i).length > 0 && this.callbacks.onStyle?.(n, i), y(this.look.levels, r.levels) || this.callbacks.onLevels?.(n, e.ownLevels ? [...r.levels] : null), this.look.scale !== r.scale && r.scale && this.callbacks.onScale?.(n, r.scale), y(this.look.intervals, r.intervals) || this.callbacks.onIntervals?.(n, r.intervals);
|
|
110
|
+
}
|
|
111
|
+
currentParams() {
|
|
112
|
+
let e = this.target;
|
|
113
|
+
return e ? {
|
|
114
|
+
...e.defaults,
|
|
115
|
+
...e.params,
|
|
116
|
+
...this.draft
|
|
117
|
+
} : {};
|
|
118
|
+
}
|
|
119
|
+
tabs() {
|
|
120
|
+
let e = this.target;
|
|
121
|
+
if (!e) return [];
|
|
122
|
+
let t = !!(e.plots?.length && e.colors && this.callbacks.onStyle), n = !!(e.levels && this.callbacks.onLevels);
|
|
123
|
+
return [
|
|
124
|
+
"inputs",
|
|
125
|
+
...t || n ? ["style"] : [],
|
|
126
|
+
...this.callbacks.onIntervals ? ["visibility"] : []
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
renderTabs() {
|
|
130
|
+
let e = this.tabs();
|
|
131
|
+
this.tabsEl.hidden = e.length < 2, this.tabButtons = e.map((e) => {
|
|
132
|
+
let t = document.createElement("button");
|
|
133
|
+
return t.type = "button", t.className = "tcw-modal-tab", t.id = `tcw-indi-tab-${this.uid}-${e}`, t.setAttribute("role", "tab"), t.setAttribute("aria-controls", this.bodyEl.id), t.dataset.tab = e, t.textContent = this.t(`indicatorSettings.tab.${e}`), t.addEventListener("click", () => this.selectTab(e)), t;
|
|
134
|
+
}), this.tabsEl.replaceChildren(...this.tabButtons), this.markTabs();
|
|
135
|
+
}
|
|
136
|
+
markTabs() {
|
|
137
|
+
for (let e of this.tabButtons) {
|
|
138
|
+
let t = e.dataset.tab === this.tab;
|
|
139
|
+
e.classList.toggle("tcw-active", t), e.setAttribute("aria-selected", String(t)), e.tabIndex = t ? 0 : -1, t && this.bodyEl.setAttribute("aria-labelledby", e.id);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
selectTab(e, t = !1) {
|
|
143
|
+
this.tab = e, this.markTabs(), this.renderBody(), t && this.tabButtons.find((t) => t.dataset.tab === e)?.focus();
|
|
144
|
+
}
|
|
145
|
+
onTabKey(e) {
|
|
146
|
+
let t = this.tabButtons.map((e) => e.dataset.tab), n = t.indexOf(this.tab), [r, i] = this.modal.closest("[dir]")?.getAttribute("dir") === "rtl" ? ["ArrowLeft", "ArrowRight"] : ["ArrowRight", "ArrowLeft"], a = e.key === r ? (n + 1) % t.length : e.key === i ? (n - 1 + t.length) % t.length : e.key === "Home" ? 0 : e.key === "End" ? t.length - 1 : -1;
|
|
147
|
+
a < 0 || t.length === 0 || (e.preventDefault(), this.selectTab(t[a], !0));
|
|
148
|
+
}
|
|
149
|
+
onKeyDown(e) {
|
|
150
|
+
if (e.key === "Escape") {
|
|
151
|
+
e.preventDefault(), e.stopPropagation(), this.finish(!1);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
t(e, this.modal);
|
|
155
|
+
}
|
|
156
|
+
renderBody() {
|
|
157
|
+
this.bodyEl.replaceChildren();
|
|
158
|
+
let e = this.target;
|
|
159
|
+
e && (this.tab === "style" ? this.renderStyle(e) : this.tab === "visibility" ? u(this.bodyEl, this.look.intervals, (e) => this.setIntervals(e), this.t) : this.renderInputs());
|
|
160
|
+
}
|
|
161
|
+
currentValue(e) {
|
|
162
|
+
if (e in this.draft) return this.draft[e];
|
|
163
|
+
let t = this.target?.params[e], n = this.target?.defaults[e];
|
|
164
|
+
return t ?? n;
|
|
165
|
+
}
|
|
166
|
+
setValue(e, t) {
|
|
167
|
+
this.target && (this.draft[e] = t, this.callbacks.onApply(this.target.instanceId, { ...this.draft }));
|
|
168
|
+
}
|
|
169
|
+
renderInputs() {
|
|
170
|
+
let e = document.createElement("div");
|
|
171
|
+
e.className = "tcw-settings-section";
|
|
172
|
+
let t = m({
|
|
173
|
+
defaultConfig: this.target.defaults,
|
|
174
|
+
inputs: this.target.inputs
|
|
175
|
+
});
|
|
176
|
+
t.length === 0 && e.appendChild(this.note(this.t("indicatorSettings.noInputs")));
|
|
177
|
+
for (let n of t) e.appendChild(this.fieldRow(n));
|
|
178
|
+
this.bodyEl.appendChild(e);
|
|
179
|
+
}
|
|
180
|
+
fieldRow(e) {
|
|
181
|
+
let t = e.name, n = this.currentValue(t), s;
|
|
182
|
+
switch (e.kind) {
|
|
183
|
+
case "source":
|
|
184
|
+
s = this.sourceControl(t, String(n ?? "close"));
|
|
185
|
+
break;
|
|
186
|
+
case "choice":
|
|
187
|
+
s = this.choiceControl(t, e.options ?? [], n);
|
|
188
|
+
break;
|
|
189
|
+
case "boolean":
|
|
190
|
+
s = o(!!n, (e) => this.setValue(t, e));
|
|
191
|
+
break;
|
|
192
|
+
case "int":
|
|
193
|
+
case "number":
|
|
194
|
+
s = r(Number(n), {
|
|
195
|
+
min: e.min,
|
|
196
|
+
max: e.max,
|
|
197
|
+
step: e.step,
|
|
198
|
+
whole: e.kind === "int"
|
|
199
|
+
}, (e) => this.setValue(t, e));
|
|
200
|
+
break;
|
|
201
|
+
case "color":
|
|
202
|
+
s = i(String(n), (e) => this.setValue(t, e));
|
|
203
|
+
break;
|
|
204
|
+
default: s = this.textControl(t, String(n ?? ""));
|
|
205
|
+
}
|
|
206
|
+
return a(e.label ?? c(t, this.t) ?? x(t), s);
|
|
207
|
+
}
|
|
208
|
+
sourceControl(e, t) {
|
|
209
|
+
let n = document.createElement("select");
|
|
210
|
+
n.className = "tcw-indi-input tcw-indi-select";
|
|
211
|
+
let r = document.createElement("optgroup");
|
|
212
|
+
r.label = this.t("indicatorSettings.source.prices");
|
|
213
|
+
for (let e of f) r.appendChild(this.option(e, e));
|
|
214
|
+
n.appendChild(r);
|
|
215
|
+
let i = this.target?.lineSources ?? [];
|
|
216
|
+
if (i.length) {
|
|
217
|
+
let e = document.createElement("optgroup");
|
|
218
|
+
e.label = this.t("indicatorSettings.source.indicators");
|
|
219
|
+
for (let t of i) e.appendChild(this.option(t.value, t.label));
|
|
220
|
+
n.appendChild(e);
|
|
221
|
+
}
|
|
222
|
+
return n.value = t, n.value !== t && (n.value = "close"), n.addEventListener("change", () => this.setValue(e, n.value)), n;
|
|
223
|
+
}
|
|
224
|
+
choiceControl(e, t, n) {
|
|
225
|
+
let r = document.createElement("select");
|
|
226
|
+
r.className = "tcw-indi-input tcw-indi-select";
|
|
227
|
+
for (let n of t) r.appendChild(this.option(String(n), l(e, n, this.t) ?? String(n)));
|
|
228
|
+
return r.value = String(n), r.addEventListener("change", () => {
|
|
229
|
+
let n = t.find((e) => String(e) === r.value);
|
|
230
|
+
n !== void 0 && this.setValue(e, n);
|
|
231
|
+
}), r;
|
|
232
|
+
}
|
|
233
|
+
textControl(e, t) {
|
|
234
|
+
let n = document.createElement("input");
|
|
235
|
+
return n.type = "text", n.className = "tcw-indi-input", n.value = t, n.addEventListener("change", () => this.setValue(e, n.value)), n;
|
|
236
|
+
}
|
|
237
|
+
renderStyle(e) {
|
|
238
|
+
let t = e.instanceId, n = () => this.target === e, r = (e) => n() && this.callbacks.onStyle?.(t, e), i = (e) => n() && this.callbacks.onLevels?.(t, e), a = (e) => n() && this.callbacks.onScale?.(t, e);
|
|
239
|
+
d(this.bodyEl, {
|
|
240
|
+
plots: e.colors && this.callbacks.onStyle ? e.plots ?? [] : [],
|
|
241
|
+
colors: this.look.colors,
|
|
242
|
+
plotStyles: this.look.plots,
|
|
243
|
+
lineWidth: this.look.lineWidth,
|
|
244
|
+
scale: e.scale && this.callbacks.onScale ? this.look.scale : void 0,
|
|
245
|
+
levels: e.levels && this.callbacks.onLevels ? this.look.levels : void 0
|
|
246
|
+
}, {
|
|
247
|
+
color: (e, t) => {
|
|
248
|
+
if (!n()) return;
|
|
249
|
+
let i = [...this.look.colors];
|
|
250
|
+
for (; i.length <= e;) i.push(i[0] ?? t);
|
|
251
|
+
i[e] = t, this.look.colors = i, r({ colors: [...i] });
|
|
252
|
+
},
|
|
253
|
+
plot: (e, t) => {
|
|
254
|
+
n() && (this.look.plots = {
|
|
255
|
+
...this.look.plots,
|
|
256
|
+
[e]: {
|
|
257
|
+
...this.look.plots[e],
|
|
258
|
+
...t
|
|
259
|
+
}
|
|
260
|
+
}, r({ plots: { [e]: t } }));
|
|
261
|
+
},
|
|
262
|
+
lineWidth: (e) => {
|
|
263
|
+
n() && (this.look.lineWidth = e, r({ lineWidths: [e] }));
|
|
264
|
+
},
|
|
265
|
+
scale: (e) => {
|
|
266
|
+
n() && (this.look.scale = e, a(e));
|
|
267
|
+
},
|
|
268
|
+
levels: (e) => {
|
|
269
|
+
n() && (this.look.levels = e, i([...e]));
|
|
270
|
+
}
|
|
271
|
+
}, this.t, this.uid);
|
|
272
|
+
}
|
|
273
|
+
setIntervals(e) {
|
|
274
|
+
this.target && (this.look.intervals = e, this.callbacks.onIntervals?.(this.target.instanceId, e));
|
|
275
|
+
}
|
|
276
|
+
openDefaults() {
|
|
277
|
+
if (this.menu?.isOpen()) {
|
|
278
|
+
this.menu.close();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
this.menu ??= new n(this.modal, this.t("indicatorSettings.defaults"));
|
|
282
|
+
let e = this.defaultsBtn.getBoundingClientRect(), t = this.modal.getBoundingClientRect(), r = [{
|
|
283
|
+
id: "reset",
|
|
284
|
+
label: this.t("indicatorSettings.resetSettings"),
|
|
285
|
+
icon: "reset"
|
|
286
|
+
}, ...this.callbacks.onSaveDefault ? [{
|
|
287
|
+
id: "save",
|
|
288
|
+
label: this.t("indicatorSettings.saveAsDefault"),
|
|
289
|
+
icon: "save"
|
|
290
|
+
}] : []];
|
|
291
|
+
this.menu.open(r, e.left - t.left, e.top - t.top, (e) => {
|
|
292
|
+
e === "reset" ? this.resetAll() : this.saveDefault();
|
|
293
|
+
}, this.defaultsBtn);
|
|
294
|
+
}
|
|
295
|
+
resetAll() {
|
|
296
|
+
let e = this.target;
|
|
297
|
+
if (!e) return;
|
|
298
|
+
let t = e.instanceId, n = { ...e.defaults };
|
|
299
|
+
this.draft = { ...n }, this.callbacks.onApply(t, { ...n }), Object.keys(this.look.plots).length > 0 && (this.callbacks.onStyle?.(t, { plots: S(this.look.plots, {}, e.plots) }), this.look.plots = {}), e.levels && this.callbacks.onLevels && (this.callbacks.onLevels(t, null), this.look.levels = [...e.defaultLevels ?? []]), this.look.intervals && this.setIntervals(null), this.renderBody();
|
|
300
|
+
}
|
|
301
|
+
saveDefault() {
|
|
302
|
+
let e = this.target;
|
|
303
|
+
if (!e) return;
|
|
304
|
+
let t = Object.fromEntries(Object.entries(this.currentParams()).filter(([t, n]) => !y(n, e.defaults[t]) && !(typeof n == "string" && p(n)))), n = {};
|
|
305
|
+
h(this.look.colors) || (n.colors = [...this.look.colors]), this.look.lineWidth !== _ && (n.lineWidths = [this.look.lineWidth]), Object.keys(this.look.plots).length > 0 && (n.plots = b(this.look.plots)), this.callbacks.onSaveDefault?.(e.indicatorId, {
|
|
306
|
+
params: t,
|
|
307
|
+
style: n
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
footerButton(e, t, n, r) {
|
|
311
|
+
let i = document.createElement("button");
|
|
312
|
+
return i.type = "button", i.className = t, i.dataset.act = e, r && (i.textContent = r), i.addEventListener("click", n), i;
|
|
313
|
+
}
|
|
314
|
+
note(e) {
|
|
315
|
+
let t = document.createElement("div");
|
|
316
|
+
return t.className = "tcw-settings-label", t.textContent = e, t;
|
|
317
|
+
}
|
|
318
|
+
option(e, t) {
|
|
319
|
+
let n = document.createElement("option");
|
|
320
|
+
return n.value = e, n.textContent = t, n;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
//#endregion
|
|
324
|
+
export { C as WidgetIndicatorSettings };
|
|
325
|
+
|
|
326
|
+
//# sourceMappingURL=WidgetIndicatorSettings-dDkqzzYo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WidgetIndicatorSettings-dDkqzzYo.js","names":[],"sources":["../src/widget/WidgetIndicatorSettings.ts"],"sourcesContent":["import type { IndicatorInputSpec, IndicatorIntervals, IndicatorPlot, IndicatorPlotStyle, IndicatorStyleConfig } from '@tradecanvas/commons';\nimport { PRICE_SOURCES, parseIndicatorSource } from '@tradecanvas/commons';\nimport { describeInputs, isSeriesPalette, type IndicatorInputInfo } from '@tradecanvas/core';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { createIcon } from './icons.js';\nimport { EN_MESSAGES, type Translator } from './i18n.js';\nimport { WidgetContextMenu } from './WidgetContextMenu.js';\nimport { colorInput, numberInput, settingsRow, toggleSwitch } from './settingsControls.js';\nimport { renderStyleTab, renderVisibilityTab } from './indicatorSettingsTabs.js';\nimport { choiceLabel, inputLabel } from './indicatorInputWords.js';\n\nexport type IndicatorParamValue = number | string | boolean;\n\nexport interface IndicatorSettingsTarget {\n instanceId: string;\n /** The kind of indicator (`'rsi'`): what \"Save as default\" is for. */\n indicatorId: string;\n name: string;\n /** Default config — defines the editable keys and their types. */\n defaults: Record<string, unknown>;\n /** Current values, overlaid on defaults. */\n params: Record<string, unknown>;\n /** How parameters are edited (the descriptor's `inputs`). */\n inputs?: Readonly<Record<string, IndicatorInputSpec>>;\n /** Other indicators' lines a source can be: value `ind:<instanceId>:<key>`. */\n lineSources?: readonly { value: string; label: string }[];\n /** What it draws; with `colors`, the lines of the Style tab. */\n plots?: readonly IndicatorPlot[];\n colors?: readonly string[];\n lineWidth?: number;\n /** Each line's own dash and visibility, by its key. */\n plotStyles?: Readonly<Record<string, IndicatorPlotStyle>>;\n /** Its levels and its indicator's defaults; with them, the levels under the lines. */\n levels?: readonly number[];\n defaultLevels?: readonly number[];\n /** Whether `levels` are the instance's own; not: its indicator's defaults, which Cancel leaves it on. */\n ownLevels?: boolean;\n /** The price scale an overlay is on; set only for price-pane overlays. */\n scale?: 'right' | 'left';\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** What \"Save as default\" keeps: new indicators of the kind start with it. */\nexport interface IndicatorDefault {\n params: Record<string, IndicatorParamValue>;\n style: IndicatorStyleConfig;\n}\n\nexport interface IndicatorSettingsCallbacks {\n onApply: (instanceId: string, params: Record<string, IndicatorParamValue>) => void;\n onStyle?: (instanceId: string, style: IndicatorStyleConfig) => void;\n /** `null` restores the indicator's default levels. */\n onLevels?: (instanceId: string, levels: number[] | null) => void;\n /** Move an overlay to the left price scale or back to the price scale. */\n onScale?: (instanceId: string, scale: 'right' | 'left') => void;\n /** The intervals it shows on (`null`: all); with it, the Visibility tab. */\n onIntervals?: (instanceId: string, intervals: IndicatorIntervals | null) => void;\n /** \"Save as default\" in the Defaults menu. */\n onSaveDefault?: (indicatorId: string, saved: IndicatorDefault) => void;\n onClose: () => void;\n}\n\ntype Tab = 'inputs' | 'style' | 'visibility';\n\n/** What the dialog changes besides the inputs: as it was on opening, and as it is. */\ninterface Look {\n colors: string[];\n lineWidth: number;\n plots: Record<string, IndicatorPlotStyle>;\n levels: number[];\n scale?: 'right' | 'left';\n intervals: IndicatorIntervals | null;\n}\n\n/** Ids for the dialog's labelling (several widgets may share a page). */\nlet nextDialogId = 1;\n\nconst DEFAULT_LINE_WIDTH = 1.5;\n/** A line as it is with no style of its own: shown, and dashed as its indicator declares it. */\nfunction plainPlot(plots: readonly IndicatorPlot[] | undefined, key: string): IndicatorPlotStyle {\n return { visible: true, lineStyle: plots?.find((p) => p.key === key)?.lineStyle ?? 'solid' };\n}\n\nconst same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);\n/** A copy of plain values (numbers, strings, flags, lists of them). */\nconst copy = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nfunction titleCase(key: string): string {\n // camelCase / snake_case → \"Title Case\"\n return key\n .replace(/_/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/^./, (c) => c.toUpperCase());\n}\n\n/** Each line put back as `to` has it; one `to` has no style for goes back to plain. */\nfunction plotsBack(\n from: Record<string, IndicatorPlotStyle>,\n to: Record<string, IndicatorPlotStyle>,\n plots: readonly IndicatorPlot[] | undefined,\n): Record<string, IndicatorPlotStyle> {\n return Object.fromEntries([...new Set([...Object.keys(from), ...Object.keys(to)])].map((key) => [key, { ...plainPlot(plots, key), ...to[key] }]));\n}\n\n/**\n * Per-indicator settings: its inputs; its style (each line shown or not, its\n * colours and dash, the line width, the scale, the levels); and the\n * intervals it shows on. Every edit shows at once; Cancel (or Escape) puts\n * it all back as it was, Ok keeps it. The Defaults menu resets it, or keeps\n * it for the next ones of its kind.\n */\nexport class WidgetIndicatorSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private defaultsBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private callbacks: IndicatorSettingsCallbacks;\n private target: IndicatorSettingsTarget | null = null;\n private draft: Record<string, IndicatorParamValue> = {};\n private initial: { params: Record<string, IndicatorParamValue>; look: Look } | null = null;\n private look: Look = { colors: [], lineWidth: DEFAULT_LINE_WIDTH, plots: {}, levels: [], intervals: null };\n private tab: Tab = 'inputs';\n private tabButtons: HTMLButtonElement[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n private readonly t: Translator;\n\n constructor(host: HTMLElement, callbacks: IndicatorSettingsCallbacks, t?: Translator) {\n this.callbacks = callbacks;\n this.t = t ?? ((key) => EN_MESSAGES[key]);\n\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-indi';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-indi-title-${this.uid}`);\n // Focusable, so keys still reach it after a click on its blank parts.\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-indi-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('indicatorSettings.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-indi-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.defaultsBtn = this.footerButton('defaults', 'tcw-footer-menu', () => this.openDefaults());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** The Defaults menu on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.defaultsBtn.setAttribute('aria-haspopup', 'menu');\n this.defaultsBtn.setAttribute('aria-expanded', 'false');\n this.defaultsBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.defaultsBtn.querySelector('span')!.textContent = this.t('indicatorSettings.defaults');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.defaultsBtn, end);\n return footer;\n }\n\n open(target: IndicatorSettingsTarget): void {\n if (this.target) this.finish(true);\n if (this.backdrop.hidden) this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.titleEl.textContent = this.t('indicatorSettings.title').replace('{name}', target.name);\n this.draft = {};\n this.look = {\n colors: [...(target.colors ?? [])],\n lineWidth: target.lineWidth ?? DEFAULT_LINE_WIDTH,\n plots: Object.fromEntries(Object.entries(target.plotStyles ?? {}).map(([key, plot]) => [key, { ...plot }])),\n levels: [...(target.levels ?? [])],\n scale: target.scale,\n intervals: target.intervals ?? null,\n };\n this.initial = { params: this.currentParams(), look: copy(this.look) };\n this.tab = 'inputs';\n this.renderTabs();\n this.renderBody();\n this.backdrop.hidden = false;\n // Start on the first input, else the tabs, else the dialog itself.\n const firstTab = this.tabsEl.hidden ? null : this.tabButtons[0] ?? null;\n (this.bodyEl.querySelector<HTMLElement>(FOCUSABLE) ?? firstTab ?? this.modal).focus();\n }\n\n /** Close, keeping what was changed (as Ok does). */\n close(): void {\n this.finish(true);\n }\n\n isOpen(): boolean {\n return !this.backdrop.hidden;\n }\n\n destroy(): void {\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n /** Close with Ok (`keep`: a value still being typed applies) or Cancel (everything goes back). */\n private finish(keep: boolean): void {\n if (!this.target) return;\n if (keep) {\n const active = document.activeElement;\n if ((active instanceof HTMLInputElement || active instanceof HTMLSelectElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n } else {\n this.revert();\n }\n // No target: a field's change on its way out (its blur) no longer applies.\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onClose();\n // Back to where the dialog was opened from (a legend row, the object tree).\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n /** Everything as it was on opening: only what changed is sent back. */\n private revert(): void {\n const target = this.target;\n const initial = this.initial;\n if (!target || !initial) return;\n const id = target.instanceId;\n if (!same(this.currentParams(), initial.params)) this.callbacks.onApply(id, { ...initial.params });\n const was = initial.look;\n const style: IndicatorStyleConfig = {};\n if (!same(this.look.colors, was.colors)) style.colors = [...was.colors];\n if (this.look.lineWidth !== was.lineWidth) style.lineWidths = [was.lineWidth];\n if (!same(this.look.plots, was.plots)) style.plots = plotsBack(this.look.plots, was.plots, target.plots);\n if (Object.keys(style).length > 0) this.callbacks.onStyle?.(id, style);\n if (!same(this.look.levels, was.levels)) this.callbacks.onLevels?.(id, target.ownLevels ? [...was.levels] : null);\n if (this.look.scale !== was.scale && was.scale) this.callbacks.onScale?.(id, was.scale);\n if (!same(this.look.intervals, was.intervals)) this.callbacks.onIntervals?.(id, was.intervals);\n }\n\n private currentParams(): Record<string, IndicatorParamValue> {\n const target = this.target;\n if (!target) return {};\n return { ...target.defaults, ...target.params, ...this.draft } as Record<string, IndicatorParamValue>;\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n const lines = !!(target.plots?.length && target.colors && this.callbacks.onStyle);\n const levels = !!(target.levels && this.callbacks.onLevels);\n return [\n 'inputs',\n ...(lines || levels ? ['style' as const] : []),\n ...(this.callbacks.onIntervals ? ['visibility' as const] : []),\n ];\n }\n\n /** The tab buttons for this indicator; built on open, then only marked (focus stays put). */\n private renderTabs(): void {\n const tabs = this.tabs();\n this.tabsEl.hidden = tabs.length < 2;\n this.tabButtons = tabs.map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-indi-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`indicatorSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0 || tabs.length === 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n if (this.tab === 'style') this.renderStyle(target);\n else if (this.tab === 'visibility') renderVisibilityTab(this.bodyEl, this.look.intervals, (next) => this.setIntervals(next), this.t);\n else this.renderInputs();\n }\n\n // --- Inputs ---\n\n private currentValue(key: string): IndicatorParamValue {\n if (key in this.draft) return this.draft[key];\n const cur = this.target?.params[key];\n const def = this.target?.defaults[key];\n return (cur ?? def) as IndicatorParamValue;\n }\n\n private setValue(key: string, value: IndicatorParamValue): void {\n if (!this.target) return;\n this.draft[key] = value;\n this.callbacks.onApply(this.target.instanceId, { ...this.draft });\n }\n\n private renderInputs(): void {\n const section = document.createElement('div');\n section.className = 'tcw-settings-section';\n const inputs = describeInputs({ defaultConfig: this.target!.defaults, inputs: this.target!.inputs });\n if (inputs.length === 0) section.appendChild(this.note(this.t('indicatorSettings.noInputs')));\n for (const input of inputs) section.appendChild(this.fieldRow(input));\n this.bodyEl.appendChild(section);\n }\n\n /** One input's row: its control follows the kind the indicator declares, or the one its default implies. */\n private fieldRow(input: IndicatorInputInfo): HTMLLabelElement {\n const key = input.name;\n const value = this.currentValue(key);\n let control: HTMLElement;\n switch (input.kind) {\n case 'source': control = this.sourceControl(key, String(value ?? 'close')); break;\n case 'choice': control = this.choiceControl(key, input.options ?? [], value); break;\n case 'boolean': control = toggleSwitch(Boolean(value), (v) => this.setValue(key, v)); break;\n case 'int':\n case 'number':\n control = numberInput(Number(value), { min: input.min, max: input.max, step: input.step, whole: input.kind === 'int' }, (v) => this.setValue(key, v));\n break;\n case 'color': control = colorInput(String(value), (v) => this.setValue(key, v)); break;\n default: control = this.textControl(key, String(value ?? ''));\n }\n return settingsRow(input.label ?? inputLabel(key, this.t) ?? titleCase(key), control);\n }\n\n private sourceControl(key: string, value: string): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n const prices = document.createElement('optgroup');\n prices.label = this.t('indicatorSettings.source.prices');\n for (const source of PRICE_SOURCES) prices.appendChild(this.option(source, source));\n select.appendChild(prices);\n const lines = this.target?.lineSources ?? [];\n if (lines.length) {\n const group = document.createElement('optgroup');\n group.label = this.t('indicatorSettings.source.indicators');\n for (const line of lines) group.appendChild(this.option(line.value, line.label));\n select.appendChild(group);\n }\n select.value = value;\n if (select.value !== value) select.value = 'close'; // a line that is gone\n select.addEventListener('change', () => this.setValue(key, select.value));\n return select;\n }\n\n private choiceControl(key: string, options: readonly (string | number)[], value: IndicatorParamValue): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const opt of options) select.appendChild(this.option(String(opt), choiceLabel(key, opt, this.t) ?? String(opt)));\n select.value = String(value);\n select.addEventListener('change', () => {\n const picked = options.find((o) => String(o) === select.value);\n if (picked !== undefined) this.setValue(key, picked);\n });\n return select;\n }\n\n private textControl(key: string, value: string): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = value;\n input.addEventListener('change', () => this.setValue(key, input.value));\n return input;\n }\n\n // --- Style and visibility ---\n\n private renderStyle(target: IndicatorSettingsTarget): void {\n const id = target.instanceId;\n // Once closed, nothing: a field's change on its way out (its blur after a Cancel) no longer applies.\n const open = () => this.target === target;\n const onStyle = (style: IndicatorStyleConfig) => open() && this.callbacks.onStyle?.(id, style);\n const onLevels = (levels: number[]) => open() && this.callbacks.onLevels?.(id, levels);\n const onScale = (scale: 'right' | 'left') => open() && this.callbacks.onScale?.(id, scale);\n renderStyleTab(this.bodyEl, {\n plots: target.colors && this.callbacks.onStyle ? target.plots ?? [] : [],\n colors: this.look.colors,\n plotStyles: this.look.plots,\n lineWidth: this.look.lineWidth,\n scale: target.scale && this.callbacks.onScale ? this.look.scale : undefined,\n levels: target.levels && this.callbacks.onLevels ? this.look.levels : undefined,\n }, {\n color: (index, value) => {\n if (!open()) return;\n const next = [...this.look.colors];\n while (next.length <= index) next.push(next[0] ?? value);\n next[index] = value;\n this.look.colors = next;\n onStyle({ colors: [...next] });\n },\n plot: (key, patch) => {\n if (!open()) return;\n this.look.plots = { ...this.look.plots, [key]: { ...this.look.plots[key], ...patch } };\n onStyle({ plots: { [key]: patch } });\n },\n lineWidth: (width) => {\n if (!open()) return;\n this.look.lineWidth = width;\n onStyle({ lineWidths: [width] });\n },\n scale: (scale) => {\n if (!open()) return;\n this.look.scale = scale;\n onScale(scale);\n },\n levels: (levels) => {\n if (!open()) return;\n this.look.levels = levels;\n onLevels([...levels]);\n },\n }, this.t, this.uid);\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.look.intervals = intervals;\n this.callbacks.onIntervals?.(this.target.instanceId, intervals);\n }\n\n // --- Defaults ---\n\n private openDefaults(): void {\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n this.menu ??= new WidgetContextMenu(this.modal, this.t('indicatorSettings.defaults'));\n const at = this.defaultsBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n const entries = [\n { id: 'reset', label: this.t('indicatorSettings.resetSettings'), icon: 'reset' },\n ...(this.callbacks.onSaveDefault ? [{ id: 'save', label: this.t('indicatorSettings.saveAsDefault'), icon: 'save' }] : []),\n ];\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (pick) => {\n if (pick === 'reset') this.resetAll();\n else this.saveDefault();\n }, this.defaultsBtn);\n }\n\n /** The inputs, the lines' own styles, the levels and the intervals back to the indicator's own. */\n private resetAll(): void {\n const target = this.target;\n if (!target) return;\n const id = target.instanceId;\n const params = { ...target.defaults } as Record<string, IndicatorParamValue>;\n this.draft = { ...params };\n this.callbacks.onApply(id, { ...params });\n if (Object.keys(this.look.plots).length > 0) {\n this.callbacks.onStyle?.(id, { plots: plotsBack(this.look.plots, {}, target.plots) });\n this.look.plots = {};\n }\n if (target.levels && this.callbacks.onLevels) {\n this.callbacks.onLevels(id, null);\n this.look.levels = [...(target.defaultLevels ?? [])];\n }\n if (this.look.intervals) this.setIntervals(null);\n this.renderBody();\n }\n\n /**\n * Keep what was chosen for the next ones of the kind: the inputs that differ\n * from the indicator's own (not a line of another indicator, which the next\n * one won't have), and of the look only what someone picked. Colours the\n * chart gave stay its to give, so a second one still gets a colour of its own.\n */\n private saveDefault(): void {\n const target = this.target;\n if (!target) return;\n const params = Object.fromEntries(Object.entries(this.currentParams()).filter(([key, value]) =>\n !same(value, target.defaults[key]) && !(typeof value === 'string' && parseIndicatorSource(value)))) as Record<string, IndicatorParamValue>;\n const style: IndicatorStyleConfig = {};\n if (!isSeriesPalette(this.look.colors)) style.colors = [...this.look.colors];\n if (this.look.lineWidth !== DEFAULT_LINE_WIDTH) style.lineWidths = [this.look.lineWidth];\n if (Object.keys(this.look.plots).length > 0) style.plots = copy(this.look.plots);\n this.callbacks.onSaveDefault?.(target.indicatorId, { params, style });\n }\n\n // --- Pieces ---\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-label';\n el.textContent = text;\n return el;\n }\n\n private option(value: string, label: string): HTMLOptionElement {\n const opt = document.createElement('option');\n opt.value = value;\n opt.textContent = label;\n return opt;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./widgetLocales-BPK5i0Ok.cjs`),t=require(`./focusTrap-DrhhmG0V.cjs`),n=require(`./settingsControls-Tn8_vJJv.cjs`),r=require(`./i18n-C2nQBZTY.cjs`);let i=require(`@tradecanvas/commons`);var a=0,o=0,s=20;function c(e){let t=document.createElement(`div`);if(t.className=`tcw-settings-section`,e){let n=document.createElement(`div`);n.className=`tcw-settings-section-title`,n.textContent=e,t.appendChild(n),t.setAttribute(`role`,`group`),t.setAttribute(`aria-label`,e)}return t}function l(e){let t=document.createElement(`div`);t.className=`tcw-settings-row`;let n=document.createElement(`span`);return n.className=`tcw-settings-label`,n.textContent=e,t.appendChild(n),t}function u(e,t,r,i,o){let s=l(e),c=document.createElement(`div`);c.className=`tcw-color-picker-wrap`;let u=document.createElement(`input`);u.type=`color`,u.setAttribute(`aria-label`,e);let d=document.createElement(`span`);d.className=`tcw-color-hex`,d.id=`tcw-color-${++a}`,u.setAttribute(`aria-describedby`,d.id);let f=null,p=t=>{u.value=n.l(t??i.start?.()),d.textContent=t??o(`settings.auto`),s.classList.toggle(`tcw-color-auto`,t===null),t!==null&&i.start&&!f?(f=document.createElement(`button`),f.type=`button`,f.className=`tcw-color-auto-btn`,f.textContent=o(`settings.auto`),f.setAttribute(`aria-label`,`${e}: ${o(`settings.auto`)}`),f.addEventListener(`click`,()=>{r(null),u.focus(),p(i.read())}),c.appendChild(f)):t===null&&f&&(f.remove(),f=null)};return u.addEventListener(`input`,()=>{r(u.value),p(u.value)}),c.append(u,d),s.appendChild(c),p(t),{row:s,sync:()=>p(i.read())}}function d(e,t,n){let r=l(e),i=document.createElement(`button`);return i.type=`button`,i.className=`tcw-toggle${t?` tcw-on`:``}`,i.setAttribute(`role`,`switch`),i.setAttribute(`aria-checked`,String(t)),i.setAttribute(`aria-label`,e),i.addEventListener(`click`,()=>{let e=!i.classList.contains(`tcw-on`);i.classList.toggle(`tcw-on`,e),i.setAttribute(`aria-checked`,String(e)),n(e)}),r.appendChild(i),r}function f(e,t,n,r){let i=l(e),a=document.createElement(`select`);a.className=`tcw-settings-select`,a.setAttribute(`aria-label`,e);for(let e of n){let n=document.createElement(`option`);n.value=e.value,n.textContent=e.label,e.value===t&&(n.selected=!0),a.appendChild(n)}return n.some(e=>e.value===t)||(a.selectedIndex=-1),a.addEventListener(`change`,()=>r(a.value)),i.appendChild(a),i}function p(e,t,n,r,i=e=>String(e)){let a=l(e),o=document.createElement(`div`);o.className=`tcw-settings-range`;let s=document.createElement(`input`);s.type=`range`,s.min=String(n.min),s.max=String(n.max),s.step=String(n.step),s.value=String(t),s.setAttribute(`aria-label`,e);let c=document.createElement(`span`);return c.className=`tcw-settings-range-val`,c.textContent=i(t),s.addEventListener(`input`,()=>{let e=Number(s.value);c.textContent=i(e),r(e)}),o.append(s,c),a.appendChild(o),a}function m(e,t,n){let i=new Intl.NumberFormat(n,{maximumFractionDigits:s}).format;return e.step===`1`&&Number.isFinite(e.max)?r.r(t(`settings.numberWhole`),{min:i(Math.max(1,e.min??1)),max:i(e.max)}):r.r(t(`settings.numberAtLeast`),{min:i(e.min??0)})}function h(e,t,n,r,i,a=`en-US`){let s=document.createElement(`div`);s.className=`tcw-settings-row tcw-settings-row--number`;let c=document.createElement(`label`);c.className=`tcw-settings-label`,c.textContent=e,s.appendChild(c);let l=document.createElement(`input`);l.type=`number`,l.className=`tcw-settings-number`,l.min=`0`,l.step=n.step,Number.isFinite(n.max)&&(l.max=String(n.max)),l.placeholder=i(`settings.auto`),l.value=t===null?``:String(t),l.setAttribute(`aria-label`,e);let u=document.createElement(`div`);u.className=`tcw-settings-hint`,u.id=`tcw-number-hint-${++o}`,u.setAttribute(`role`,`status`),l.setAttribute(`aria-describedby`,u.id);let d=e=>{e?l.removeAttribute(`aria-invalid`):l.setAttribute(`aria-invalid`,`true`),u.textContent=e?``:m(n,i,a)};return l.addEventListener(`change`,()=>{if(l.value.trim()===``){d(!0),r(null);return}let e=Number(l.value),t=Number.isFinite(e)&&e>0&&e>=(n.min??0)&&e<=n.max&&(n.step!==`1`||Number.isInteger(e));d(t),t&&r(e)}),s.append(l,u),s}var g=[{id:`symbol`,icon:`chartType:candlestick`},{id:`statusLine`,icon:`type`},{id:`scales`,icon:`ruler`},{id:`appearance`,icon:`palette`},{id:`layers`,icon:`layers`},{id:`trading`,icon:`trendingUp`}],_=[1,2,3,4],v=`t:`,y={step:`any`,max:1/0,min:1e-8},b=1,x=class{callbacks;t;backdrop=null;modal=null;bodyEl=null;tabButtons=[];templateBtn=null;menu=null;currentTab=`symbol`;currentSettings=null;chartType=null;lookRows=[];returnFocus=null;uid=b++;constructor(e,t,n={},r=()=>document.body){this.available=n,this.host=r,this.callbacks=e,this.t=t}open(e,t=null,n=`symbol`){this.modal&&this.remove(),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.currentSettings={...e},this.chartType=t,this.currentTab=n,this.buildModal(),this.tabButtons.find(e=>e.dataset.tab===n)?.focus()}isOpen(){return this.modal!==null}refresh(e,t){if(!this.modal)return;this.currentSettings={...e},t!==void 0&&(this.chartType=t);let n=this.focusedControl();this.renderTabContent(),n!==null&&this.focusControl(n)}focusedControl(){let e=document.activeElement;if(!this.bodyEl||!(e instanceof HTMLElement)||!this.bodyEl.contains(e))return null;let t=this.panelControls().indexOf(e);return t<0?null:t}focusControl(e){let t=this.panelControls();(t[Math.min(e,t.length-1)]??this.tabButtons.find(e=>e.dataset.tab===this.currentTab))?.focus()}panelControls(){return this.bodyEl?[...this.bodyEl.querySelectorAll(t.t)]:[]}close(){this.finish(!0)}destroy(){this.remove()}finish(e){this.modal&&(e?this.callbacks.onClose():this.callbacks.onCancel?.(),this.remove(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null)}remove(){this.menu?.destroy(),this.menu=null,this.backdrop?.remove(),this.backdrop=this.modal=this.bodyEl=this.templateBtn=null,this.tabButtons=[]}buildModal(){let e=document.createElement(`div`);e.className=`tcw-modal-backdrop`;let n=!1;e.addEventListener(`pointerdown`,t=>{n=t.target===e}),e.addEventListener(`click`,t=>{t.target===e&&n&&this.finish(!1),n=!1});let r=document.createElement(`div`);r.className=`tcw-modal tcw-set`,r.setAttribute(`role`,`dialog`),r.setAttribute(`aria-modal`,`true`),r.setAttribute(`aria-labelledby`,`tcw-set-title-${this.uid}`),r.tabIndex=-1,r.addEventListener(`keydown`,e=>{if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),this.finish(!1);return}t.n(e,r)}),this.backdrop=e,this.modal=r,r.append(this.buildHeader(),this.buildBody(),this.buildFooter()),e.appendChild(r),this.host().appendChild(e),this.markTabs(),this.renderTabContent()}buildHeader(){let e=document.createElement(`div`);e.className=`tcw-modal-header`;let n=document.createElement(`h3`);n.id=`tcw-set-title-${this.uid}`,n.textContent=this.t(`settings.title`);let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-modal-close`,r.setAttribute(`aria-label`,this.t(`common.close`)),r.innerHTML=t.i(`x`,16),r.addEventListener(`click`,()=>this.finish(!1)),e.append(n,r),e}buildBody(){let e=document.createElement(`div`);e.className=`tcw-set-body`;let n=document.createElement(`div`);n.className=`tcw-set-nav`,n.setAttribute(`role`,`tablist`),n.setAttribute(`aria-orientation`,`vertical`),n.setAttribute(`aria-label`,this.t(`settings.title`)),n.addEventListener(`keydown`,e=>this.onTabKey(e)),this.tabButtons=g.map(({id:e,icon:n})=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-set-tab`,r.id=`tcw-set-tab-${this.uid}-${e}`,r.dataset.tab=e,r.setAttribute(`role`,`tab`),r.setAttribute(`aria-controls`,`tcw-set-panel-${this.uid}`),r.innerHTML=`${n.startsWith(`chartType:`)?t.r(n.slice(10),16):t.i(n,16)}<span></span>`,r.querySelector(`span`).textContent=this.t(`settings.tab.${e}`),r.addEventListener(`click`,()=>this.selectTab(e)),r}),n.append(...this.tabButtons);let r=document.createElement(`div`);return r.className=`tcw-modal-body tcw-set-panel`,r.id=`tcw-set-panel-${this.uid}`,r.setAttribute(`role`,`tabpanel`),this.bodyEl=r,e.append(n,r),e}buildFooter(){let e=document.createElement(`div`);e.className=`tcw-modal-footer`;let n=this.footerButton(`template`,`tcw-footer-menu`,()=>this.openTemplates());n.setAttribute(`aria-haspopup`,`menu`),n.setAttribute(`aria-expanded`,`false`),n.innerHTML=`<span></span>${t.i(`chevronDown`,12)}`,n.querySelector(`span`).textContent=this.t(`settings.template`),this.templateBtn=n;let r=document.createElement(`div`);return r.className=`tcw-footer-end`,r.append(this.footerButton(`cancel`,`tcw-footer-cancel`,()=>this.finish(!1),this.t(`common.cancel`)),this.footerButton(`ok`,`tcw-done-btn`,()=>this.finish(!0),this.t(`drawingSettings.ok`))),e.append(n,r),e}footerButton(e,t,n,r){let i=document.createElement(`button`);return i.type=`button`,i.className=t,i.dataset.act=e,r&&(i.textContent=r),i.addEventListener(`click`,n),i}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.currentTab;e.classList.toggle(`tcw-active`,t),e.setAttribute(`aria-selected`,String(t)),e.tabIndex=t?0:-1,t&&this.bodyEl?.setAttribute(`aria-labelledby`,e.id)}}selectTab(e,t=!1){this.currentTab=e,this.markTabs(),this.renderTabContent(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){let t=g.findIndex(e=>e.id===this.currentTab),n=e.key===`ArrowDown`||e.key===`ArrowRight`?(t+1)%g.length:e.key===`ArrowUp`||e.key===`ArrowLeft`?(t-1+g.length)%g.length:e.key===`Home`?0:e.key===`End`?g.length-1:-1;n<0||(e.preventDefault(),this.selectTab(g[n].id,!0))}openTemplates(){let e=this.templateBtn;if(!this.modal||!e)return;if(this.menu?.isOpen()){this.menu.close();return}let t=this.callbacks.templates,i=(t?.list()??[]).map(e=>({id:`${v}${e}`,label:e,icon:`layers`,remove:r.r(this.t(`templates.delete`),{name:e})})),a=[...i,...i.length>0?[`separator`]:[],...t?[{id:`save`,label:this.t(`settings.saveTemplate`),icon:`save`}]:[],{id:`reset`,label:this.t(`settings.resetToDefaults`),icon:`reset`}];this.menu??=new n.f(this.modal,this.t(`settings.template`));let o=e.getBoundingClientRect(),s=this.modal.getBoundingClientRect();this.menu.open(a,o.left-s.left,o.top-s.top,e=>{e===`reset`?this.callbacks.onReset():e===`save`?t?.save():e.startsWith(v)&&t?.apply(e.slice(2))},e,e=>t?.remove(e.slice(2)))}renderTabContent(){if(!(!this.bodyEl||!this.currentSettings))switch(this.bodyEl.replaceChildren(),this.lookRows=[],this.currentTab){case`symbol`:this.renderSymbolTab(this.currentSettings);break;case`statusLine`:this.renderStatusLineTab(this.currentSettings);break;case`scales`:this.renderScalesTab(this.currentSettings);break;case`appearance`:this.renderAppearanceTab(this.currentSettings);break;case`layers`:this.renderLayersTab(this.currentSettings);break;case`trading`:for(let t of e.d)(!t.trading||this.available.trading!==!1)&&this.renderLookSection(t);break}}renderSymbolTab(t){this.renderChartTypeSection(),this.addSection(void 0,[d(this.t(`settings.mainSeries`),t.mainSeriesVisible,e=>this.patch({mainSeriesVisible:e})),d(this.t(`settings.extendedHours`),t.extendedHours,e=>this.patch({extendedHours:e}))]);let n=this.t(`common.show`);this.renderLookSection(e.o),this.available.volume!==!1&&this.renderLookSection(e.f,[d(n,t.volumeVisible,e=>this.patch({volumeVisible:e}))]),this.renderLookSection(e.a,[d(n,t.highLowLines,e=>this.patch({highLowLines:e}))])}renderStatusLineTab(t){let n=this.available.legend!==!1,r=n?e.u:e.u.filter(t=>!e.s.includes(t));this.addSection(void 0,[...n?[d(this.t(`settings.ohlcLegend`),t.legendVisible,e=>this.patch({legendVisible:e}))]:[],...r.map(e=>this.lookRow(e))])}renderScalesTab(t){let n=[{value:`regular`,label:this.t(`settings.scale.regular`)},{value:`logarithmic`,label:this.t(`settings.scale.logarithmic`)},{value:`percentage`,label:this.t(`settings.scale.percentage`)},{value:`indexedTo100`,label:this.t(`settings.scale.indexedTo100`)}].filter(e=>e.value!==`logarithmic`||this.available.logScale!==!1),r=this.available.exchangeZone?.(),i=e.y(t.timezone,Date.now(),this.t(`settings.timezone.local`),r===void 0?void 0:{label:this.t(`settings.timezone.exchange`),zone:r});this.addSection(void 0,[f(this.t(`settings.priceScale`),t.scaleMode,n,e=>this.patch({scaleMode:e})),d(this.t(`settings.autoScale`),t.autoScale,e=>this.patch({autoScale:e})),d(this.t(`settings.invertScale`),t.invertScale,e=>this.patch({invertScale:e})),d(this.t(`settings.leftScale`),t.leftPriceScale,e=>this.patch({leftPriceScale:e})),d(this.t(`settings.indicatorValues`),t.indicatorValueLabels,e=>this.patch({indicatorValueLabels:e})),d(this.t(`settings.alignAxisLabels`),t.alignAxisLabels,e=>this.patch({alignAxisLabels:e})),...this.available.barCountdown===!1?[]:[d(this.t(`settings.barCountdown`),t.barCountdown,e=>this.patch({barCountdown:e}))],f(this.t(`settings.timezone`),t.timezone,i,e=>this.patch({timezone:e})),f(this.t(`settings.numberLocale`),t.numberLocale,e.r(t.numberLocale),e=>this.patch({numberLocale:e}))]),this.addSection(this.t(`settings.section.axes`),e.c.map(e=>this.lookRow(e)))}renderAppearanceTab(t){let[n,r,i,...a]=e.i;this.renderLookSection(n),this.renderLookSection(r,[d(this.t(`common.show`),t.gridVisible,e=>this.patch({gridVisible:e}))]),this.renderLookSection(i,[f(this.t(`settings.crosshairMode`),t.crosshairMode,[{label:this.t(`settings.crosshair.magnet`),value:`magnet`},{label:this.t(`settings.crosshair.normal`),value:`normal`},{label:this.t(`settings.crosshair.hidden`),value:`hidden`}],e=>this.patch({crosshairMode:e}))]);for(let e of a)this.renderLookSection(e)}renderLayersTab(e){let t=e=>`${Math.round(e*100)}%`,n=e=>this.t(e);this.addSection(void 0,[d(n(`settings.volumeProfile`),e.volumeProfileVisible,e=>this.patch({volumeProfileVisible:e}))]),this.addSection(void 0,[d(n(`settings.marketProfile`),e.marketProfileVisible,e=>this.patch({marketProfileVisible:e})),d(n(`settings.marketProfileSplit`),e.marketProfileSplit,e=>this.patch({marketProfileSplit:e})),d(n(`settings.marketProfileLetters`),e.marketProfileLetters,e=>this.patch({marketProfileLetters:e})),p(n(`settings.marketProfileBuckets`),e.marketProfileBuckets,{min:8,max:200,step:1},e=>this.patch({marketProfileBuckets:e})),p(n(`settings.marketProfileOpacity`),e.marketProfileOpacity,{min:.05,max:1,step:.05},e=>this.patch({marketProfileOpacity:e}),t)]),this.addSection(void 0,[d(n(`settings.depthHeatmap`),e.depthHeatmapVisible,e=>this.patch({depthHeatmapVisible:e})),p(n(`settings.depthHeatmapOpacity`),e.depthHeatmapOpacity,{min:.1,max:1,step:.05},e=>this.patch({depthHeatmapOpacity:e}),t)]),this.addSection(void 0,[d(n(`settings.swingMarkers`),e.pivotMarkersVisible,e=>this.patch({pivotMarkersVisible:e})),p(n(`settings.swingStrength`),e.pivotStrength,{min:2,max:20,step:1},e=>this.patch({pivotStrength:e})),d(n(`settings.swingStructure`),e.pivotStructureLabels,e=>this.patch({pivotStructureLabels:e}))]),this.addSection(void 0,[d(n(`settings.sessionShading`),e.sessionShadingVisible,e=>this.patch({sessionShadingVisible:e})),d(n(`settings.periodLevels`),e.periodLevelsVisible,e=>this.patch({periodLevelsVisible:e})),f(n(`settings.periodLevelsBasis`),e.periodLevelsPeriod,[{value:`day`,label:n(`settings.priorDay`)},{value:`week`,label:n(`settings.priorWeek`)}],e=>this.patch({periodLevelsPeriod:e}))])}addSection(e,t){let n=c(e);return n.append(...t),this.bodyEl?.appendChild(n),n}renderLookSection(e,t=[]){this.addSection(this.t(e.title),[...t,...e.rows.map(e=>this.lookRow(e))])}lookRow(e){let t=this.t(e.label),n=()=>this.callbacks.styleValue(e.keys[0]),r=()=>{let t=e.keys.map(e=>this.callbacks.styleValue(e));return t.every(e=>e===t[0])&&t[0]!==null?String(t[0]):``},a,o=t=>{this.callbacks.onStyleChange(Object.fromEntries(e.keys.map(e=>[e,t])));for(let e of this.lookRows)e.el!==a&&e.sync()},s=e.auto?[{value:``,label:this.t(`settings.auto`)}]:[],c;switch(i.CHART_STYLE_KEYS[e.keys[0]]){case`boolean`:{a=d(t,n()===!0,o);let e=a.querySelector(`button`);c=()=>{let t=n()===!0;e.classList.toggle(`tcw-on`,t),e.setAttribute(`aria-checked`,String(t))};break}case`lineStyle`:a=f(t,r(),[...s,...this.lineStyles()],e=>o(e===``?null:C(e))),c=()=>{a.querySelector(`select`).value=r()};break;case`width`:{let e=n(),i=typeof e==`number`&&!_.includes(e)?[..._,e].sort((e,t)=>e-t):_;a=f(t,r(),[...s,...i.map(e=>({value:String(e),label:`${e}px`}))],e=>o(e===``?null:Number(e))),c=()=>{a.querySelector(`select`).value=r()};break}default:{let r=n(),i=u(t,typeof r==`string`?r:null,o,{read:()=>{let e=n();return typeof e==`string`?e:null},start:e.auto?()=>(e.auto?.from?this.callbacks.styleValue(e.auto.from):e.auto?.hint)??null:void 0},this.t);a=i.row,c=i.sync}}return this.lookRows.push({el:a,sync:c}),a}lineStyles(){return[{value:`solid`,label:this.t(`drawingSettings.lineStyle.solid`)},{value:`dashed`,label:this.t(`drawingSettings.lineStyle.dashed`)},{value:`dotted`,label:this.t(`drawingSettings.lineStyle.dotted`)}]}renderChartTypeSection(){if(!this.bodyEl||!this.currentSettings||!this.chartType)return;let t=this.chartType,n=this.currentSettings.chartTypeOptions??{},r=e.m.find(e=>e.value===t),i=c(r?e.t(r,this.t):t),a=()=>this.currentSettings?.chartTypeOptions??{},o=(e,t)=>this.patch({chartTypeOptions:{...a(),[e]:t}}),s=(e,t,n,r)=>i.appendChild(h(this.t(e),t,n,r,this.t,this.currentSettings?.numberLocale||void 0));switch(t){case`renko`:{let e=n.renko??{};s(`settings.boxSize`,typeof e.boxSize==`number`?e.boxSize:null,y,e=>o(`renko`,{...a().renko??{},boxSize:e??`atr`})),s(`settings.atrPeriod`,e.atrPeriod??null,{step:`1`,max:500},e=>o(`renko`,S(a().renko,`atrPeriod`,e)));break}case`lineBreak`:s(`settings.lineBreakLines`,n.lineBreak?.lines??null,{step:`1`,max:10},e=>o(`lineBreak`,S(a().lineBreak,`lines`,e)));break;case`kagi`:s(`settings.reversal`,n.kagi?.reversal??null,y,e=>o(`kagi`,S(a().kagi,`reversal`,e))),i.appendChild(f(this.t(`settings.reversalType`),n.kagi?.reversalType??`percent`,[{value:`percent`,label:this.t(`settings.reversalType.percent`)},{value:`price`,label:this.t(`settings.reversalType.price`)}],e=>o(`kagi`,{...a().kagi??{},reversalType:e===`price`?`price`:`percent`})));break;case`pointAndFigure`:s(`settings.boxSize`,typeof n.pointAndFigure?.boxSize==`number`?n.pointAndFigure.boxSize:null,y,e=>o(`pointAndFigure`,{...a().pointAndFigure??{},boxSize:e??`auto`})),s(`settings.reversalBoxes`,n.pointAndFigure?.reversal??null,{step:`1`,max:10},e=>o(`pointAndFigure`,S(a().pointAndFigure,`reversal`,e)));break;case`rangeBars`:s(`settings.barRange`,typeof n.rangeBars?.range==`number`?n.rangeBars.range:null,y,e=>o(`rangeBars`,{range:e??`auto`}));break;default:break}for(let n of e.p(t))i.appendChild(this.lookRow(n));this.bodyEl.appendChild(i)}patch(e){this.currentSettings&&Object.assign(this.currentSettings,e),this.callbacks.onChange(e)}};function S(e,t,n){let r={...e??{}};return n===null?delete r[t]:r[t]=n,r}function C(e){return e===`dashed`||e===`dotted`?e:`solid`}exports.WidgetSettings=x;
|
|
2
|
+
//# sourceMappingURL=WidgetSettings-D7Pq42zW.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WidgetSettings-D7Pq42zW.cjs","names":[],"sources":["../src/widget/settingsRows.ts","../src/widget/WidgetSettings.ts"],"sourcesContent":["import { toHex } from './settingsControls.js';\nimport { fill, type Translator } from './i18n.js';\n\n/**\n * The rows of the chart settings: a section, a colour, a switch, a choice,\n * a slider and a number, each with its label and calling back on change.\n */\n\n/** Ids for the colour rows' descriptions. */\nlet colorRowCount = 0;\n/** Ids for the number rows' hints. */\nlet numberRowCount = 0;\n/** The most decimals a number in a hint shows: the smallest size taken, 0.00000001, has 8. */\nconst HINT_MAX_DECIMALS = 20;\n\n/** A group of rows; a title names it to a screen reader too (rows named alike under several titles). */\nexport function settingsSection(title?: string): HTMLDivElement {\n const div = document.createElement('div');\n div.className = 'tcw-settings-section';\n if (title) {\n const heading = document.createElement('div');\n heading.className = 'tcw-settings-section-title';\n heading.textContent = title;\n div.appendChild(heading);\n div.setAttribute('role', 'group');\n div.setAttribute('aria-label', title);\n }\n return div;\n}\n\nfunction labelledRow(label: string): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row';\n const lbl = document.createElement('span');\n lbl.className = 'tcw-settings-label';\n lbl.textContent = label;\n row.appendChild(lbl);\n return row;\n}\n\n/**\n * A colour of the look, shown again from `look.read()`. With `look.start`,\n * null is the part's own colour: shown as Auto, the picker starting from\n * `start()`, and once set a button takes it back.\n */\nexport function colorRow(\n label: string,\n value: string | null,\n onChange: (v: string | null) => void,\n look: { read: () => string | null; start?: () => string | number | boolean | null },\n t: Translator,\n): { row: HTMLDivElement; sync: () => void } {\n const row = labelledRow(label);\n const wrap = document.createElement('div');\n wrap.className = 'tcw-color-picker-wrap';\n const input = document.createElement('input');\n input.type = 'color';\n input.setAttribute('aria-label', label);\n // What the swatch can't say (Auto, a colour with transparency) is read with it.\n const hex = document.createElement('span');\n hex.className = 'tcw-color-hex';\n hex.id = `tcw-color-${++colorRowCount}`;\n input.setAttribute('aria-describedby', hex.id);\n\n let back: HTMLButtonElement | null = null;\n const show = (v: string | null) => {\n input.value = toHex(v ?? look.start?.());\n hex.textContent = v ?? t('settings.auto');\n row.classList.toggle('tcw-color-auto', v === null);\n if (v !== null && look.start && !back) {\n back = document.createElement('button');\n back.type = 'button';\n back.className = 'tcw-color-auto-btn';\n back.textContent = t('settings.auto');\n back.setAttribute('aria-label', `${label}: ${t('settings.auto')}`);\n back.addEventListener('click', () => {\n onChange(null);\n // Focus stays in the row as the button goes; the chart may still draw a colour the host set.\n input.focus();\n show(look.read());\n });\n wrap.appendChild(back);\n } else if (v === null && back) {\n back.remove();\n back = null;\n }\n };\n input.addEventListener('input', () => {\n onChange(input.value);\n show(input.value);\n });\n wrap.append(input, hex);\n row.appendChild(wrap);\n show(value);\n return { row, sync: () => show(look.read()) };\n}\n\nexport function toggleRow(label: string, value: boolean, onChange: (v: boolean) => void): HTMLDivElement {\n const row = labelledRow(label);\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = `tcw-toggle${value ? ' tcw-on' : ''}`;\n toggle.setAttribute('role', 'switch');\n toggle.setAttribute('aria-checked', String(value));\n toggle.setAttribute('aria-label', label);\n toggle.addEventListener('click', () => {\n const next = !toggle.classList.contains('tcw-on');\n toggle.classList.toggle('tcw-on', next);\n toggle.setAttribute('aria-checked', String(next));\n onChange(next);\n });\n row.appendChild(toggle);\n return row;\n}\n\nexport function selectRow(\n label: string,\n value: string,\n options: readonly { label: string; value: string }[],\n onChange: (v: string) => void,\n): HTMLDivElement {\n const row = labelledRow(label);\n const select = document.createElement('select');\n select.className = 'tcw-settings-select';\n select.setAttribute('aria-label', label);\n for (const opt of options) {\n const o = document.createElement('option');\n o.value = opt.value;\n o.textContent = opt.label;\n if (opt.value === value) o.selected = true;\n select.appendChild(o);\n }\n // A value none of the options has (two keys that differ): nothing chosen, so any pick is a change.\n if (!options.some((opt) => opt.value === value)) select.selectedIndex = -1;\n select.addEventListener('change', () => onChange(select.value));\n row.appendChild(select);\n return row;\n}\n\nexport function rangeRow(\n label: string,\n value: number,\n range: { min: number; max: number; step: number },\n onChange: (v: number) => void,\n format: (v: number) => string = (v) => String(v),\n): HTMLDivElement {\n const row = labelledRow(label);\n const wrap = document.createElement('div');\n wrap.className = 'tcw-settings-range';\n const input = document.createElement('input');\n input.type = 'range';\n input.min = String(range.min);\n input.max = String(range.max);\n input.step = String(range.step);\n input.value = String(value);\n input.setAttribute('aria-label', label);\n const out = document.createElement('span');\n out.className = 'tcw-settings-range-val';\n out.textContent = format(value);\n input.addEventListener('input', () => {\n const v = Number(input.value);\n out.textContent = format(v);\n onChange(v);\n });\n wrap.append(input, out);\n row.appendChild(wrap);\n return row;\n}\n\n/** What a number field takes. */\nexport interface NumberLimits {\n /** The field's step: '1' takes whole counts only, 'any' any number. */\n step: string;\n /** The largest number taken (Infinity: no limit). */\n max: number;\n /** The smallest number taken; without one, any number above 0. */\n min?: number;\n}\n\n/** What a number field takes, in words: the hint under it once it turned a number away. */\nfunction limitsText(limits: NumberLimits, t: Translator, locale: string): string {\n const format = new Intl.NumberFormat(locale, { maximumFractionDigits: HINT_MAX_DECIMALS }).format;\n if (limits.step === '1' && Number.isFinite(limits.max)) {\n return fill(t('settings.numberWhole'), { min: format(Math.max(1, limits.min ?? 1)), max: format(limits.max) });\n }\n return fill(t('settings.numberAtLeast'), { min: format(limits.min ?? 0) });\n}\n\n/**\n * A number field; empty means \"worked out from the data\" (shown as Auto). Only numbers above 0, and from\n * `limits.min` on, are taken; one turned away is marked invalid, and a hint under it, in `locale`, says what is.\n */\nexport function numberRow(\n label: string,\n value: number | null,\n limits: NumberLimits,\n onChange: (v: number | null) => void,\n t: Translator,\n locale = 'en-US',\n): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row tcw-settings-row--number';\n const lbl = document.createElement('label');\n lbl.className = 'tcw-settings-label';\n lbl.textContent = label;\n row.appendChild(lbl);\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'tcw-settings-number';\n // The min attribute is also where the arrows step from: at 1e-8, the up arrow turns 5 into 5.00000001 and an\n // empty field into 1e-8. It stays 0, and the smallest number taken is checked on change.\n input.min = '0';\n input.step = limits.step;\n if (Number.isFinite(limits.max)) input.max = String(limits.max);\n input.placeholder = t('settings.auto');\n input.value = value === null ? '' : String(value);\n input.setAttribute('aria-label', label);\n // Written when a number is turned away, and heard with the field: red alone says nothing to a screen reader.\n const hint = document.createElement('div');\n hint.className = 'tcw-settings-hint';\n hint.id = `tcw-number-hint-${++numberRowCount}`;\n hint.setAttribute('role', 'status');\n input.setAttribute('aria-describedby', hint.id);\n const mark = (ok: boolean) => {\n if (ok) input.removeAttribute('aria-invalid');\n else input.setAttribute('aria-invalid', 'true');\n hint.textContent = ok ? '' : limitsText(limits, t, locale);\n };\n input.addEventListener('change', () => {\n if (input.value.trim() === '') {\n mark(true);\n onChange(null);\n return;\n }\n const v = Number(input.value);\n const ok = Number.isFinite(v) && v > 0 && v >= (limits.min ?? 0) && v <= limits.max && (limits.step !== '1' || Number.isInteger(v));\n mark(ok);\n if (ok) onChange(v);\n });\n row.append(input, hint);\n return row;\n}\n","import { CHART_STYLE_KEYS, type ChartType, type ChartTypeOptions, type LineStyle } from '@tradecanvas/commons';\nimport type { ChartSettingsState, SettingsCallbacks, SettingsStylePatch } from './types.js';\nimport { CHART_TYPES } from './widgetConfig.js';\nimport {\n APPEARANCE_SECTIONS, HIGH_LOW_SECTION, LAST_PRICE_SECTION, LEGEND_ROWS, SCALE_ROWS, STATUS_LINE_ROWS, TRADING_SECTIONS, VOLUME_SECTION,\n seriesRows, type LookRow, type LookSection,\n} from './widgetSettingsLook.js';\nimport { chartTypeLabel, numberLocaleOptions } from './widgetLocales.js';\nimport { createChartTypeIcon, createIcon } from './icons.js';\nimport { timezoneOptions } from './widgetTimezones.js';\nimport { FOCUSABLE, keepTabInside } from './focusTrap.js';\nimport { WidgetContextMenu, type ContextMenuEntry } from './WidgetContextMenu.js';\nimport { colorRow, numberRow, rangeRow, selectRow, settingsSection, toggleRow, type NumberLimits } from './settingsRows.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\n\nexport type SettingsTab = 'symbol' | 'statusLine' | 'scales' | 'appearance' | 'layers' | 'trading';\ntype Tab = SettingsTab;\n\n/** The tabs down the side, in the order a trader looks: the symbol first, the trading colours last. */\nconst TABS: readonly { id: Tab; icon: string }[] = [\n { id: 'symbol', icon: 'chartType:candlestick' },\n { id: 'statusLine', icon: 'type' },\n { id: 'scales', icon: 'ruler' },\n { id: 'appearance', icon: 'palette' },\n { id: 'layers', icon: 'layers' },\n { id: 'trading', icon: 'trendingUp' },\n];\n/** The widths a line takes in the settings, in pixels. */\nconst LINE_WIDTHS = [1, 2, 3, 4];\n/** A template's entry in the Template menu, by its name. */\nconst TEMPLATE_ID = 't:';\n/**\n * The smallest box, reversal or range the chart type's settings take: a\n * hundred-millionth, the finest price step markets commonly quote. A size\n * far under the prices is no use; the chart raises one that would make too\n * many bars anyway, but a slip like 1e-9 is turned away here.\n */\nconst MIN_TYPE_SIZE = 1e-8;\n/** A box, reversal or range: any number from MIN_TYPE_SIZE up. */\nconst SIZE_LIMITS: NumberLimits = { step: 'any', max: Infinity, min: MIN_TYPE_SIZE };\n\nlet nextSettingsId = 1;\n\n/**\n * The chart's settings: tabs down the side (Symbol, Status line, Scales,\n * Appearance, Layers, Trading), each change shown at once. Cancel (Escape,\n * the close button, a click outside) puts it all back, OK keeps it; the\n * Template menu applies, saves and deletes templates, and resets.\n */\nexport class WidgetSettings {\n private callbacks: SettingsCallbacks;\n private t: Translator;\n private backdrop: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private templateBtn: HTMLButtonElement | null = null;\n private menu: WidgetContextMenu | null = null;\n private currentTab: Tab = 'symbol';\n private currentSettings: ChartSettingsState | null = null;\n /** The chart's type, whose settings the Symbol tab offers. */\n private chartType: ChartType | null = null;\n /** The look's rows on view, each showing the chart's value again (a change to one moves others: a wick with its body). */\n private lookRows: { el: HTMLElement; sync: () => void }[] = [];\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextSettingsId++;\n\n /**\n * `barCountdown` / `logScale` / `legend` / `volume` false leave out the\n * controls for features the chart has switched off: there they would do\n * nothing.\n */\n constructor(\n callbacks: SettingsCallbacks,\n t: Translator,\n private readonly available: {\n barCountdown?: boolean;\n logScale?: boolean;\n legend?: boolean;\n volume?: boolean;\n /** The exchange's time zone to offer (null while unknown), or undefined when there is none to offer. */\n exchangeZone?: () => string | null | undefined;\n /** false: the chart doesn't trade, so the orders' colours aren't offered. */\n trading?: boolean;\n } = {},\n /** Where the panel mounts (the widget's portal: themed, and inside it when fullscreen). */\n private readonly host: () => HTMLElement = () => document.body,\n ) {\n this.callbacks = callbacks;\n this.t = t;\n }\n\n /** Open on `tab` (the Symbol tab unless asked: the price axis's menu asks for Scales). */\n open(currentSettings: ChartSettingsState, chartType: ChartType | null = null, tab: SettingsTab = 'symbol'): void {\n if (this.modal) this.remove();\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.currentSettings = { ...currentSettings };\n this.chartType = chartType;\n this.currentTab = tab;\n this.buildModal();\n this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n isOpen(): boolean {\n return this.modal !== null;\n }\n\n /** The settings as they are now (after an undo, a template), on the tab that's open; `chartType` when it changed. */\n refresh(settings: ChartSettingsState, chartType?: ChartType | null): void {\n if (!this.modal) return;\n this.currentSettings = { ...settings };\n if (chartType !== undefined) this.chartType = chartType;\n // A change from outside (a compare's bars, the host's setSettings) builds the panel again under the\n // control in focus: focus goes to the one in its place, not to the page outside the dialog.\n const focused = this.focusedControl();\n this.renderTabContent();\n if (focused !== null) this.focusControl(focused);\n }\n\n /** The place of the control in focus among the panel's, or null when focus is not in the panel. */\n private focusedControl(): number | null {\n const active = document.activeElement;\n if (!this.bodyEl || !(active instanceof HTMLElement) || !this.bodyEl.contains(active)) return null;\n const at = this.panelControls().indexOf(active);\n return at < 0 ? null : at;\n }\n\n /** Focus the panel's control at `index`, or its last when it has fewer; the tab when it has none. */\n private focusControl(index: number): void {\n const controls = this.panelControls();\n const control = controls[Math.min(index, controls.length - 1)] ?? this.tabButtons.find((b) => b.dataset.tab === this.currentTab);\n control?.focus();\n }\n\n private panelControls(): HTMLElement[] {\n return this.bodyEl ? [...this.bodyEl.querySelectorAll<HTMLElement>(FOCUSABLE)] : [];\n }\n\n /** Close, keeping what was changed (as OK does). */\n close(): void {\n this.finish(true);\n }\n\n destroy(): void {\n this.remove();\n }\n\n /** OK (`keep`) or Cancel, then closed, focus back where the dialog was opened from. */\n private finish(keep: boolean): void {\n if (!this.modal) return;\n if (keep) this.callbacks.onClose();\n else this.callbacks.onCancel?.();\n this.remove();\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n private remove(): void {\n this.menu?.destroy();\n this.menu = null;\n this.backdrop?.remove();\n this.backdrop = this.modal = this.bodyEl = this.templateBtn = null;\n this.tabButtons = [];\n }\n\n private buildModal(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n // A press that starts in the dialog (a slider dragged, text selected) and ends on the backdrop doesn't cancel it.\n let pressedBackdrop = false;\n backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === backdrop;\n });\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop && pressedBackdrop) this.finish(false);\n pressedBackdrop = false;\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-set';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-set-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.finish(false);\n return;\n }\n keepTabInside(e, modal);\n });\n this.backdrop = backdrop;\n this.modal = modal;\n modal.append(this.buildHeader(), this.buildBody(), this.buildFooter());\n backdrop.appendChild(modal);\n this.host().appendChild(backdrop);\n this.markTabs();\n this.renderTabContent();\n }\n\n private buildHeader(): HTMLDivElement {\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n const title = document.createElement('h3');\n title.id = `tcw-set-title-${this.uid}`;\n title.textContent = this.t('settings.title');\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('common.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.finish(false));\n header.append(title, closeBtn);\n return header;\n }\n\n /** The tabs down the side, and the open one's settings beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-set-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-set-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('settings.title'));\n nav.addEventListener('keydown', (e) => this.onTabKey(e));\n this.tabButtons = TABS.map(({ id, icon }) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-set-tab';\n btn.id = `tcw-set-tab-${this.uid}-${id}`;\n btn.dataset.tab = id;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', `tcw-set-panel-${this.uid}`);\n const svg = icon.startsWith('chartType:') ? createChartTypeIcon(icon.slice('chartType:'.length) as ChartType, 16) : createIcon(icon, 16);\n btn.innerHTML = `${svg}<span></span>`;\n btn.querySelector('span')!.textContent = this.t(`settings.tab.${id}` as MessageKey);\n btn.addEventListener('click', () => this.selectTab(id));\n return btn;\n });\n nav.append(...this.tabButtons);\n const panel = document.createElement('div');\n panel.className = 'tcw-modal-body tcw-set-panel';\n panel.id = `tcw-set-panel-${this.uid}`;\n panel.setAttribute('role', 'tabpanel');\n this.bodyEl = panel;\n body.append(nav, panel);\n return body;\n }\n\n /** Template on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n const template = this.footerButton('template', 'tcw-footer-menu', () => this.openTemplates());\n template.setAttribute('aria-haspopup', 'menu');\n template.setAttribute('aria-expanded', 'false');\n template.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n template.querySelector('span')!.textContent = this.t('settings.template');\n this.templateBtn = template;\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.finish(false), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.finish(true), this.t('drawingSettings.ok')),\n );\n footer.append(template, end);\n return footer;\n }\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const on = btn.dataset.tab === this.currentTab;\n btn.classList.toggle('tcw-active', on);\n btn.setAttribute('aria-selected', String(on));\n btn.tabIndex = on ? 0 : -1;\n if (on) this.bodyEl?.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.currentTab = tab;\n this.markTabs();\n this.renderTabContent();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Through the tabs (Home, End), picking as they go: up and down, or along the row of chips on a phone. */\n private onTabKey(e: KeyboardEvent): void {\n const at = TABS.findIndex((tab) => tab.id === this.currentTab);\n const next = e.key === 'ArrowDown' || e.key === 'ArrowRight' ? (at + 1) % TABS.length\n : e.key === 'ArrowUp' || e.key === 'ArrowLeft' ? (at - 1 + TABS.length) % TABS.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? TABS.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.selectTab(TABS[next].id, true);\n }\n\n /** The Template menu: the saved templates (each removable), Save as template, Reset to defaults. */\n private openTemplates(): void {\n const btn = this.templateBtn;\n if (!this.modal || !btn) return;\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n const templates = this.callbacks.templates;\n const saved: ContextMenuEntry[] = (templates?.list() ?? []).map((name) => ({\n id: `${TEMPLATE_ID}${name}`, label: name, icon: 'layers', remove: fill(this.t('templates.delete'), { name }),\n }));\n const entries: ContextMenuEntry[] = [\n ...saved,\n ...(saved.length > 0 ? ['separator' as const] : []),\n ...(templates ? [{ id: 'save', label: this.t('settings.saveTemplate'), icon: 'save' }] : []),\n { id: 'reset', label: this.t('settings.resetToDefaults'), icon: 'reset' },\n ];\n this.menu ??= new WidgetContextMenu(this.modal, this.t('settings.template'));\n const at = btn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (id) => {\n if (id === 'reset') this.callbacks.onReset();\n else if (id === 'save') templates?.save();\n else if (id.startsWith(TEMPLATE_ID)) templates?.apply(id.slice(TEMPLATE_ID.length));\n }, btn, (id) => templates?.remove(id.slice(TEMPLATE_ID.length)));\n }\n\n private renderTabContent(): void {\n if (!this.bodyEl || !this.currentSettings) return;\n this.bodyEl.replaceChildren();\n this.lookRows = [];\n switch (this.currentTab) {\n case 'symbol': this.renderSymbolTab(this.currentSettings); break;\n case 'statusLine': this.renderStatusLineTab(this.currentSettings); break;\n case 'scales': this.renderScalesTab(this.currentSettings); break;\n case 'appearance': this.renderAppearanceTab(this.currentSettings); break;\n case 'layers': this.renderLayersTab(this.currentSettings); break;\n case 'trading':\n for (const section of TRADING_SECTIONS) {\n if (!section.trading || this.available.trading !== false) this.renderLookSection(section);\n }\n break;\n }\n }\n\n /** The chart type's own settings and colours, the main series, the last price, the volume, the high and low. */\n private renderSymbolTab(s: ChartSettingsState): void {\n this.renderChartTypeSection();\n this.addSection(undefined, [\n toggleRow(this.t('settings.mainSeries'), s.mainSeriesVisible, (v) => this.patch({ mainSeriesVisible: v })),\n toggleRow(this.t('settings.extendedHours'), s.extendedHours, (v) => this.patch({ extendedHours: v })),\n ]);\n // A section's own switch says \"Show\": its title names what it shows.\n const show = this.t('common.show');\n this.renderLookSection(LAST_PRICE_SECTION);\n if (this.available.volume !== false) {\n this.renderLookSection(VOLUME_SECTION, [toggleRow(show, s.volumeVisible, (v) => this.patch({ volumeVisible: v }))]);\n }\n this.renderLookSection(HIGH_LOW_SECTION, [toggleRow(show, s.highLowLines, (v) => this.patch({ highLowLines: v }))]);\n }\n\n /** The legend over the chart: shown or not, its colours; the panes' titles. */\n private renderStatusLineTab(s: ChartSettingsState): void {\n const legend = this.available.legend !== false;\n const rows = legend ? STATUS_LINE_ROWS : STATUS_LINE_ROWS.filter((row) => !LEGEND_ROWS.includes(row));\n this.addSection(undefined, [\n ...(legend ? [toggleRow(this.t('settings.ohlcLegend'), s.legendVisible, (v) => this.patch({ legendVisible: v }))] : []),\n ...rows.map((row) => this.lookRow(row)),\n ]);\n }\n\n /** The price scale's mode and behaviour, its labels, the time zone, how numbers are written, the axes' colours. */\n private renderScalesTab(s: ChartSettingsState): void {\n const scales = [\n { value: 'regular', label: this.t('settings.scale.regular') },\n { value: 'logarithmic', label: this.t('settings.scale.logarithmic') },\n { value: 'percentage', label: this.t('settings.scale.percentage') },\n { value: 'indexedTo100', label: this.t('settings.scale.indexedTo100') },\n ].filter((o) => o.value !== 'logarithmic' || this.available.logScale !== false);\n const exchangeZone = this.available.exchangeZone?.();\n const zones = timezoneOptions(s.timezone, Date.now(), this.t('settings.timezone.local'),\n exchangeZone !== undefined ? { label: this.t('settings.timezone.exchange'), zone: exchangeZone } : undefined);\n this.addSection(undefined, [\n selectRow(this.t('settings.priceScale'), s.scaleMode, scales, (v) => this.patch({ scaleMode: v as ChartSettingsState['scaleMode'] })),\n toggleRow(this.t('settings.autoScale'), s.autoScale, (v) => this.patch({ autoScale: v })),\n toggleRow(this.t('settings.invertScale'), s.invertScale, (v) => this.patch({ invertScale: v })),\n toggleRow(this.t('settings.leftScale'), s.leftPriceScale, (v) => this.patch({ leftPriceScale: v })),\n toggleRow(this.t('settings.indicatorValues'), s.indicatorValueLabels, (v) => this.patch({ indicatorValueLabels: v })),\n toggleRow(this.t('settings.alignAxisLabels'), s.alignAxisLabels, (v) => this.patch({ alignAxisLabels: v })),\n ...(this.available.barCountdown !== false\n ? [toggleRow(this.t('settings.barCountdown'), s.barCountdown, (v) => this.patch({ barCountdown: v }))]\n : []),\n selectRow(this.t('settings.timezone'), s.timezone, zones, (v) => this.patch({ timezone: v })),\n selectRow(this.t('settings.numberLocale'), s.numberLocale, numberLocaleOptions(s.numberLocale), (v) => this.patch({ numberLocale: v })),\n ]);\n this.addSection(this.t('settings.section.axes'), SCALE_ROWS.map((row) => this.lookRow(row)));\n }\n\n /** The background, the grid, the crosshair, the session breaks, the drawings' handles. */\n private renderAppearanceTab(s: ChartSettingsState): void {\n const [background, grid, crosshair, ...rest] = APPEARANCE_SECTIONS;\n this.renderLookSection(background);\n this.renderLookSection(grid, [toggleRow(this.t('common.show'), s.gridVisible, (v) => this.patch({ gridVisible: v }))]);\n this.renderLookSection(crosshair, [selectRow(this.t('settings.crosshairMode'), s.crosshairMode, [\n { label: this.t('settings.crosshair.magnet'), value: 'magnet' },\n { label: this.t('settings.crosshair.normal'), value: 'normal' },\n { label: this.t('settings.crosshair.hidden'), value: 'hidden' },\n ], (v) => this.patch({ crosshairMode: v as ChartSettingsState['crosshairMode'] }))]);\n for (const section of rest) this.renderLookSection(section);\n }\n\n /** What the chart can draw over the bars: the profiles, the depth, the swings, the sessions, the prior levels. */\n private renderLayersTab(s: ChartSettingsState): void {\n const percent = (v: number) => `${Math.round(v * 100)}%`;\n const t = (key: MessageKey) => this.t(key);\n this.addSection(undefined, [toggleRow(t('settings.volumeProfile'), s.volumeProfileVisible, (v) => this.patch({ volumeProfileVisible: v }))]);\n this.addSection(undefined, [\n toggleRow(t('settings.marketProfile'), s.marketProfileVisible, (v) => this.patch({ marketProfileVisible: v })),\n toggleRow(t('settings.marketProfileSplit'), s.marketProfileSplit, (v) => this.patch({ marketProfileSplit: v })),\n toggleRow(t('settings.marketProfileLetters'), s.marketProfileLetters, (v) => this.patch({ marketProfileLetters: v })),\n rangeRow(t('settings.marketProfileBuckets'), s.marketProfileBuckets, { min: 8, max: 200, step: 1 }, (v) => this.patch({ marketProfileBuckets: v })),\n rangeRow(t('settings.marketProfileOpacity'), s.marketProfileOpacity, { min: 0.05, max: 1, step: 0.05 }, (v) => this.patch({ marketProfileOpacity: v }), percent),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.depthHeatmap'), s.depthHeatmapVisible, (v) => this.patch({ depthHeatmapVisible: v })),\n rangeRow(t('settings.depthHeatmapOpacity'), s.depthHeatmapOpacity, { min: 0.1, max: 1, step: 0.05 }, (v) => this.patch({ depthHeatmapOpacity: v }), percent),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.swingMarkers'), s.pivotMarkersVisible, (v) => this.patch({ pivotMarkersVisible: v })),\n rangeRow(t('settings.swingStrength'), s.pivotStrength, { min: 2, max: 20, step: 1 }, (v) => this.patch({ pivotStrength: v })),\n toggleRow(t('settings.swingStructure'), s.pivotStructureLabels, (v) => this.patch({ pivotStructureLabels: v })),\n ]);\n this.addSection(undefined, [\n toggleRow(t('settings.sessionShading'), s.sessionShadingVisible, (v) => this.patch({ sessionShadingVisible: v })),\n toggleRow(t('settings.periodLevels'), s.periodLevelsVisible, (v) => this.patch({ periodLevelsVisible: v })),\n selectRow(t('settings.periodLevelsBasis'), s.periodLevelsPeriod, [\n { value: 'day', label: t('settings.priorDay') },\n { value: 'week', label: t('settings.priorWeek') },\n ], (v) => this.patch({ periodLevelsPeriod: v as ChartSettingsState['periodLevelsPeriod'] })),\n ]);\n }\n\n private addSection(title: string | undefined, rows: readonly HTMLElement[]): HTMLDivElement {\n const section = settingsSection(title);\n section.append(...rows);\n this.bodyEl?.appendChild(section);\n return section;\n }\n\n /** A section of the look, after `lead` (the switch it goes with, say). */\n private renderLookSection(section: LookSection, lead: readonly HTMLElement[] = []): void {\n this.addSection(this.t(section.title), [...lead, ...section.rows.map((row) => this.lookRow(row))]);\n }\n\n /** A row of the look: a colour, a switch, a dash or a width, as its key takes it, on the user's layer. */\n private lookRow(row: LookRow): HTMLDivElement {\n const label = this.t(row.label);\n const read = () => this.callbacks.styleValue(row.keys[0]);\n /** The keys' value, or '' when they differ (the host set each way apart): any pick then sets them all. */\n const shared = (): string => {\n const values = row.keys.map((key) => this.callbacks.styleValue(key));\n return values.every((v) => v === values[0]) && values[0] !== null ? String(values[0]) : '';\n };\n let el: HTMLDivElement;\n const set = (v: string | number | boolean | null) => {\n this.callbacks.onStyleChange(Object.fromEntries(row.keys.map((key) => [key, v])) as SettingsStylePatch);\n for (const other of this.lookRows) if (other.el !== el) other.sync();\n };\n const auto = row.auto ? [{ value: '', label: this.t('settings.auto') }] : [];\n let sync: () => void;\n switch (CHART_STYLE_KEYS[row.keys[0]]) {\n case 'boolean': {\n el = toggleRow(label, read() === true, set);\n const toggle = el.querySelector('button')!;\n sync = () => {\n const on = read() === true;\n toggle.classList.toggle('tcw-on', on);\n toggle.setAttribute('aria-checked', String(on));\n };\n break;\n }\n case 'lineStyle':\n el = selectRow(label, shared(), [...auto, ...this.lineStyles()], (v) => set(v === '' ? null : asLineStyle(v)));\n sync = () => { el.querySelector('select')!.value = shared(); };\n break;\n case 'width': {\n const value = read();\n const widths = typeof value === 'number' && !LINE_WIDTHS.includes(value) ? [...LINE_WIDTHS, value].sort((a, b) => a - b) : LINE_WIDTHS;\n el = selectRow(label, shared(), [...auto, ...widths.map((w) => ({ value: String(w), label: `${w}px` }))],\n (v) => set(v === '' ? null : Number(v)));\n sync = () => { el.querySelector('select')!.value = shared(); };\n break;\n }\n default: {\n const value = read();\n const colour = colorRow(label, typeof value === 'string' ? value : null, set, {\n read: () => { const v = read(); return typeof v === 'string' ? v : null; },\n start: row.auto ? () => (row.auto?.from ? this.callbacks.styleValue(row.auto.from) : row.auto?.hint) ?? null : undefined,\n }, this.t);\n el = colour.row;\n sync = colour.sync;\n }\n }\n this.lookRows.push({ el, sync });\n return el;\n }\n\n private lineStyles(): { value: string; label: string }[] {\n return [\n { value: 'solid', label: this.t('drawingSettings.lineStyle.solid') },\n { value: 'dashed', label: this.t('drawingSettings.lineStyle.dashed') },\n { value: 'dotted', label: this.t('drawingSettings.lineStyle.dotted') },\n ];\n }\n\n /** The chart's type: its settings (Renko's box, Kagi's reversal…) and its colours and widths. */\n private renderChartTypeSection(): void {\n if (!this.bodyEl || !this.currentSettings || !this.chartType) return;\n const type = this.chartType;\n const all = this.currentSettings.chartTypeOptions ?? {};\n const def = CHART_TYPES.find((c) => c.value === type);\n const section = settingsSection(def ? chartTypeLabel(def, this.t) : type);\n const own = () => this.currentSettings?.chartTypeOptions ?? {};\n /** Change one type's settings: the rest of its own stay. */\n const set = <K extends keyof ChartTypeOptions>(key: K, value: NonNullable<ChartTypeOptions[K]>) =>\n this.patch({ chartTypeOptions: { ...own(), [key]: value } });\n const number = (label: MessageKey, value: number | null, limits: NumberLimits, onChange: (v: number | null) => void) =>\n section.appendChild(numberRow(this.t(label), value, limits, onChange, this.t, this.currentSettings?.numberLocale || undefined));\n switch (type) {\n case 'renko': {\n const o = all.renko ?? {};\n number('settings.boxSize', typeof o.boxSize === 'number' ? o.boxSize : null, SIZE_LIMITS,\n (v) => set('renko', { ...(own().renko ?? {}), boxSize: v ?? 'atr' }));\n number('settings.atrPeriod', o.atrPeriod ?? null, { step: '1', max: 500 }, (v) => set('renko', withValue(own().renko, 'atrPeriod', v)));\n break;\n }\n case 'lineBreak':\n number('settings.lineBreakLines', all.lineBreak?.lines ?? null, { step: '1', max: 10 }, (v) => set('lineBreak', withValue(own().lineBreak, 'lines', v)));\n break;\n case 'kagi':\n number('settings.reversal', all.kagi?.reversal ?? null, SIZE_LIMITS, (v) => set('kagi', withValue(own().kagi, 'reversal', v)));\n section.appendChild(selectRow(this.t('settings.reversalType'), all.kagi?.reversalType ?? 'percent', [\n { value: 'percent', label: this.t('settings.reversalType.percent') },\n { value: 'price', label: this.t('settings.reversalType.price') },\n ], (v) => set('kagi', { ...(own().kagi ?? {}), reversalType: v === 'price' ? 'price' : 'percent' })));\n break;\n case 'pointAndFigure':\n number('settings.boxSize', typeof all.pointAndFigure?.boxSize === 'number' ? all.pointAndFigure.boxSize : null, SIZE_LIMITS,\n (v) => set('pointAndFigure', { ...(own().pointAndFigure ?? {}), boxSize: v ?? 'auto' }));\n number('settings.reversalBoxes', all.pointAndFigure?.reversal ?? null, { step: '1', max: 10 },\n (v) => set('pointAndFigure', withValue(own().pointAndFigure, 'reversal', v)));\n break;\n case 'rangeBars':\n number('settings.barRange', typeof all.rangeBars?.range === 'number' ? all.rangeBars.range : null, SIZE_LIMITS,\n (v) => set('rangeBars', { range: v ?? 'auto' }));\n break;\n default:\n break;\n }\n for (const row of seriesRows(type)) section.appendChild(this.lookRow(row));\n this.bodyEl.appendChild(section);\n }\n\n private patch(partial: Partial<ChartSettingsState>): void {\n if (this.currentSettings) Object.assign(this.currentSettings, partial);\n this.callbacks.onChange(partial);\n }\n}\n\n/** `settings` with `key` set to `value`, or without it (back to its default) for null. */\nfunction withValue<T extends object, K extends string>(settings: T | undefined, key: K, value: number | null): T {\n const next = { ...(settings ?? {}) } as Record<string, unknown>;\n if (value === null) delete next[key];\n else next[key] = value;\n return next as T;\n}\n\nfunction asLineStyle(v: string): LineStyle {\n return v === 'dashed' || v === 'dotted' ? v : 'solid';\n}\n"],"mappings":"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"}
|