@tradecanvas/chart 1.72.0 → 1.72.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/{LayoutSession-r7Rgc5Sw.cjs → LayoutSession-BrXo314m.cjs} +2 -2
  2. package/dist/{LayoutSession-r7Rgc5Sw.cjs.map → LayoutSession-BrXo314m.cjs.map} +1 -1
  3. package/dist/{LayoutSession-DjHF7tmL.js → LayoutSession-BrazGVET.js} +2 -2
  4. package/dist/{LayoutSession-DjHF7tmL.js.map → LayoutSession-BrazGVET.js.map} +1 -1
  5. package/dist/WidgetCommandPalette-CeCLHPy1.js +177 -0
  6. package/dist/WidgetCommandPalette-CeCLHPy1.js.map +1 -0
  7. package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs +2 -0
  8. package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs.map +1 -0
  9. package/dist/{WidgetDrawingSettings-MSpeHFhV.js → WidgetDrawingSettings-BeW8fX7r.js} +5 -5
  10. package/dist/{WidgetDrawingSettings-MSpeHFhV.js.map → WidgetDrawingSettings-BeW8fX7r.js.map} +1 -1
  11. package/dist/{WidgetDrawingSettings-B3KAH9Ag.cjs → WidgetDrawingSettings-Cyl2u_u8.cjs} +2 -2
  12. package/dist/{WidgetDrawingSettings-B3KAH9Ag.cjs.map → WidgetDrawingSettings-Cyl2u_u8.cjs.map} +1 -1
  13. package/dist/WidgetGoToDate-BNdngHlg.cjs +2 -0
  14. package/dist/{WidgetGoToDate-CPzfStIm.cjs.map → WidgetGoToDate-BNdngHlg.cjs.map} +1 -1
  15. package/dist/{WidgetGoToDate-R2teTLpJ.js → WidgetGoToDate-hcDtezRm.js} +6 -5
  16. package/dist/{WidgetGoToDate-R2teTLpJ.js.map → WidgetGoToDate-hcDtezRm.js.map} +1 -1
  17. package/dist/{WidgetHotkeySheet-BE0glkVA.js → WidgetHotkeySheet-1lRFSdvt.js} +3 -3
  18. package/dist/{WidgetHotkeySheet-BE0glkVA.js.map → WidgetHotkeySheet-1lRFSdvt.js.map} +1 -1
  19. package/dist/{WidgetHotkeySheet-pnsXreK6.cjs → WidgetHotkeySheet-y5LWKSG6.cjs} +2 -2
  20. package/dist/{WidgetHotkeySheet-pnsXreK6.cjs.map → WidgetHotkeySheet-y5LWKSG6.cjs.map} +1 -1
  21. package/dist/WidgetIndicatorPicker-CGvZ5kYd.js +245 -0
  22. package/dist/WidgetIndicatorPicker-CGvZ5kYd.js.map +1 -0
  23. package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs +2 -0
  24. package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs.map +1 -0
  25. package/dist/{WidgetIndicatorSettings-BJUjACzj.cjs → WidgetIndicatorSettings-C7vpco6e.cjs} +2 -2
  26. package/dist/{WidgetIndicatorSettings-BJUjACzj.cjs.map → WidgetIndicatorSettings-C7vpco6e.cjs.map} +1 -1
  27. package/dist/{WidgetIndicatorSettings-DgbsZk0h.js → WidgetIndicatorSettings-dDkqzzYo.js} +6 -6
  28. package/dist/{WidgetIndicatorSettings-DgbsZk0h.js.map → WidgetIndicatorSettings-dDkqzzYo.js.map} +1 -1
  29. package/dist/{WidgetSettings-D35fHGXL.js → WidgetSettings--XzDx515.js} +24 -11
  30. package/dist/WidgetSettings--XzDx515.js.map +1 -0
  31. package/dist/WidgetSettings-C0fnWGdY.cjs +2 -0
  32. package/dist/WidgetSettings-C0fnWGdY.cjs.map +1 -0
  33. package/dist/WidgetSymbolSearch-C4OR42nZ.cjs +2 -0
  34. package/dist/WidgetSymbolSearch-C4OR42nZ.cjs.map +1 -0
  35. package/dist/WidgetSymbolSearch-CvPpjIwa.js +580 -0
  36. package/dist/WidgetSymbolSearch-CvPpjIwa.js.map +1 -0
  37. package/dist/{focusTrap-Hm7AUTBw.js → focusTrap-Br9Zfje0.js} +1 -1
  38. package/dist/{focusTrap-Hm7AUTBw.js.map → focusTrap-Br9Zfje0.js.map} +1 -1
  39. package/dist/{focusTrap-B8OoGwHb.cjs → focusTrap-DrhhmG0V.cjs} +1 -1
  40. package/dist/{focusTrap-B8OoGwHb.cjs.map → focusTrap-DrhhmG0V.cjs.map} +1 -1
  41. package/dist/{i18n-DFR1Oukq.cjs → i18n-C2nQBZTY.cjs} +1 -1
  42. package/dist/{i18n-DFR1Oukq.cjs.map → i18n-C2nQBZTY.cjs.map} +1 -1
  43. package/dist/{i18n-CvnQcfor.js → i18n-C5v6Tzp1.js} +1 -1
  44. package/dist/{i18n-CvnQcfor.js.map → i18n-C5v6Tzp1.js.map} +1 -1
  45. package/dist/index.cjs +1 -1
  46. package/dist/index.js +1 -1
  47. package/dist/{indicatorInputWords-D19kY9NO.js → indicatorInputWords-Bd9Pt6qp.js} +2 -2
  48. package/dist/{indicatorInputWords-D19kY9NO.js.map → indicatorInputWords-Bd9Pt6qp.js.map} +1 -1
  49. package/dist/{indicatorInputWords-DzrKBU2I.cjs → indicatorInputWords-CTA4IcJR.cjs} +2 -2
  50. package/dist/{indicatorInputWords-DzrKBU2I.cjs.map → indicatorInputWords-CTA4IcJR.cjs.map} +1 -1
  51. package/dist/{indicatorSettingsTabs-BLEULMJJ.cjs → indicatorSettingsTabs-B0liE3ST.cjs} +2 -2
  52. package/dist/{indicatorSettingsTabs-BLEULMJJ.cjs.map → indicatorSettingsTabs-B0liE3ST.cjs.map} +1 -1
  53. package/dist/{indicatorSettingsTabs-Wxx2v_y4.js → indicatorSettingsTabs-XcgMdbLp.js} +3 -3
  54. package/dist/{indicatorSettingsTabs-Wxx2v_y4.js.map → indicatorSettingsTabs-XcgMdbLp.js.map} +1 -1
  55. package/dist/monthView-B-CieFDJ.cjs +2 -0
  56. package/dist/monthView-B-CieFDJ.cjs.map +1 -0
  57. package/dist/monthView-N8yDaTqZ.js +198 -0
  58. package/dist/monthView-N8yDaTqZ.js.map +1 -0
  59. package/dist/{platformKeys-BAPueZ1I.cjs → platformKeys-B82-j1Fs.cjs} +1 -1
  60. package/dist/{platformKeys-BAPueZ1I.cjs.map → platformKeys-B82-j1Fs.cjs.map} +1 -1
  61. package/dist/{platformKeys-D_x_6ZrV.js → platformKeys-Dd1qOCjF.js} +1 -1
  62. package/dist/{platformKeys-D_x_6ZrV.js.map → platformKeys-Dd1qOCjF.js.map} +1 -1
  63. package/dist/{settingsControls-ef4j_clQ.js → settingsControls-DIjzCFHI.js} +2 -2
  64. package/dist/{settingsControls-ef4j_clQ.js.map → settingsControls-DIjzCFHI.js.map} +1 -1
  65. package/dist/{settingsControls-jNn0If4b.cjs → settingsControls-Tn8_vJJv.cjs} +2 -2
  66. package/dist/{settingsControls-jNn0If4b.cjs.map → settingsControls-Tn8_vJJv.cjs.map} +1 -1
  67. package/dist/symbolCatalog-CA1fpz3s.js +90 -0
  68. package/dist/symbolCatalog-CA1fpz3s.js.map +1 -0
  69. package/dist/symbolCatalog-Dx1YnZgm.cjs +2 -0
  70. package/dist/symbolCatalog-Dx1YnZgm.cjs.map +1 -0
  71. package/dist/widget/ChartWidget.d.ts +102 -580
  72. package/dist/widget/ChartWidget.d.ts.map +1 -1
  73. package/dist/widget/WidgetAlerts.d.ts +35 -0
  74. package/dist/widget/WidgetAlerts.d.ts.map +1 -0
  75. package/dist/widget/WidgetChartSettings.d.ts +211 -0
  76. package/dist/widget/WidgetChartSettings.d.ts.map +1 -0
  77. package/dist/widget/WidgetCommands.d.ts +54 -0
  78. package/dist/widget/WidgetCommands.d.ts.map +1 -0
  79. package/dist/widget/WidgetCompares.d.ts +140 -0
  80. package/dist/widget/WidgetCompares.d.ts.map +1 -0
  81. package/dist/widget/WidgetDockArea.d.ts +90 -0
  82. package/dist/widget/WidgetDockArea.d.ts.map +1 -0
  83. package/dist/widget/WidgetDrawingEdit.d.ts +73 -0
  84. package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -0
  85. package/dist/widget/WidgetIntervals.d.ts +66 -0
  86. package/dist/widget/WidgetIntervals.d.ts.map +1 -0
  87. package/dist/widget/WidgetLayouts.d.ts +85 -0
  88. package/dist/widget/WidgetLayouts.d.ts.map +1 -0
  89. package/dist/widget/WidgetLegendView.d.ts +54 -0
  90. package/dist/widget/WidgetLegendView.d.ts.map +1 -0
  91. package/dist/widget/WidgetMenus.d.ts +79 -0
  92. package/dist/widget/WidgetMenus.d.ts.map +1 -0
  93. package/dist/widget/WidgetReplay.d.ts +199 -0
  94. package/dist/widget/WidgetReplay.d.ts.map +1 -0
  95. package/dist/widget/WidgetSettings.d.ts +2 -0
  96. package/dist/widget/WidgetSettings.d.ts.map +1 -1
  97. package/dist/widget/WidgetTrading.d.ts +48 -0
  98. package/dist/widget/WidgetTrading.d.ts.map +1 -0
  99. package/dist/widget/WidgetWatchlists.d.ts +77 -0
  100. package/dist/widget/WidgetWatchlists.d.ts.map +1 -0
  101. package/dist/widget/index.cjs +43 -29
  102. package/dist/widget/index.cjs.map +1 -1
  103. package/dist/widget/index.js +9628 -9812
  104. package/dist/widget/index.js.map +1 -1
  105. package/dist/widget/keyTarget.d.ts +4 -0
  106. package/dist/widget/keyTarget.d.ts.map +1 -1
  107. package/dist/widget/locales/index.cjs +1 -1
  108. package/dist/widget/locales/index.js +1 -1
  109. package/dist/widget/pageEnv.d.ts +5 -0
  110. package/dist/widget/pageEnv.d.ts.map +1 -0
  111. package/dist/widget/widgetDialogs.d.ts +3 -0
  112. package/dist/widget/widgetDialogs.d.ts.map +1 -1
  113. package/dist/widgetFeatures-BM6u0u74.js +186 -0
  114. package/dist/widgetFeatures-BM6u0u74.js.map +1 -0
  115. package/dist/widgetFeatures-BZ8OVPN2.cjs +3 -0
  116. package/dist/widgetFeatures-BZ8OVPN2.cjs.map +1 -0
  117. package/package.json +3 -3
  118. package/dist/WidgetGoToDate-CPzfStIm.cjs +0 -2
  119. package/dist/WidgetSettings-D35fHGXL.js.map +0 -1
  120. package/dist/WidgetSettings-Pgt3p6vv.cjs +0 -2
  121. package/dist/WidgetSettings-Pgt3p6vv.cjs.map +0 -1
  122. package/dist/widgetFeatures-BvkTbsQn.js +0 -378
  123. package/dist/widgetFeatures-BvkTbsQn.js.map +0 -1
  124. package/dist/widgetFeatures-Cy0fs3Nl.cjs +0 -3
  125. package/dist/widgetFeatures-Cy0fs3Nl.cjs.map +0 -1
@@ -0,0 +1,245 @@
1
+ import { o as e } from "./widgetFeatures-BM6u0u74.js";
2
+ import { i as t, n } from "./focusTrap-Br9Zfje0.js";
3
+ import { r } from "./i18n-C5v6Tzp1.js";
4
+ import { INDICATOR_CATEGORIES as i } from "@tradecanvas/core";
5
+ //#region src/widget/indicatorSearch.ts
6
+ function a(e) {
7
+ return e.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
8
+ }
9
+ var o = /[\s/()%-]+/;
10
+ function s(e, t) {
11
+ let n = a(e.shortName), r = a(e.name);
12
+ return n === t || a(e.id) === t ? 0 : n.startsWith(t) || r.startsWith(t) ? 1 : [...r.split(o), ...n.split(o)].some((e) => e.startsWith(t)) ? 2 : r.includes(t) || n.includes(t) ? 3 : null;
13
+ }
14
+ function c(e, t) {
15
+ let n = a(t.trim());
16
+ return n ? e.map((e) => ({
17
+ item: e,
18
+ rank: s(e, n)
19
+ })).filter((e) => e.rank !== null).sort((e, t) => e.rank - t.rank || e.item.name.localeCompare(t.item.name)).map((e) => e.item) : [];
20
+ }
21
+ //#endregion
22
+ //#region src/widget/WidgetIndicatorPicker.ts
23
+ var l = 1, u = (e, t) => e.name.localeCompare(t.name), d = class {
24
+ backdrop = null;
25
+ listEl = null;
26
+ navEl = null;
27
+ searchEl = null;
28
+ liveEl = null;
29
+ content = {
30
+ items: [],
31
+ favorites: [],
32
+ templates: []
33
+ };
34
+ favorites = /* @__PURE__ */ new Set();
35
+ counts = /* @__PURE__ */ new Map();
36
+ view = "all";
37
+ lastView = null;
38
+ query = "";
39
+ returnFocus = null;
40
+ uid = l++;
41
+ constructor(e, t, n) {
42
+ this.layer = e, this.callbacks = t, this.t = n;
43
+ }
44
+ open(e) {
45
+ this.backdrop && this.close(!1), this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null, this.content = e, this.favorites = new Set(e.favorites), this.counts = e.counts ?? /* @__PURE__ */ new Map(), this.query = "", this.view = this.startView(), this.build(), this.searchEl?.focus();
46
+ }
47
+ close(e = !0) {
48
+ this.backdrop && (this.backdrop.remove(), this.backdrop = this.listEl = this.navEl = this.searchEl = this.liveEl = null, this.callbacks.onClose?.(), e && this.returnFocus?.isConnected && this.returnFocus.focus(), this.returnFocus = null);
49
+ }
50
+ isOpen() {
51
+ return this.backdrop !== null;
52
+ }
53
+ setTemplates(e) {
54
+ if (this.content = {
55
+ ...this.content,
56
+ templates: [...e]
57
+ }, !this.listEl || this.view !== "templates" || this.query) return;
58
+ let t = this.listEl.contains(document.activeElement);
59
+ this.renderList(), t && !this.listEl.contains(document.activeElement) && this.listEl.querySelector("[data-tpl-save]")?.focus();
60
+ }
61
+ setCounts(e) {
62
+ this.counts = e, this.listEl?.querySelectorAll(".tcw-ip-row[data-ind]").forEach((e) => this.markCount(e));
63
+ }
64
+ destroy() {
65
+ this.close(!1);
66
+ }
67
+ startView() {
68
+ let e = this.views();
69
+ return this.lastView && e.includes(this.lastView) && !(this.lastView === "favorites" && this.favorites.size === 0) ? this.lastView : this.favorites.size > 0 ? "favorites" : "all";
70
+ }
71
+ views() {
72
+ let e = new Set(this.content.items.map((e) => e.category));
73
+ return [
74
+ "favorites",
75
+ ...this.callbacks.templates && this.content.templates ? ["templates"] : [],
76
+ "all",
77
+ ...i.filter((t) => e.has(t))
78
+ ];
79
+ }
80
+ viewLabel(e) {
81
+ return e === "favorites" ? this.t("indicatorPicker.favorites") : e === "templates" ? this.t("toolbar.indicators.templates") : e === "all" ? this.t("toolbar.indicators.all") : this.t(`indicatorCategory.${e}`);
82
+ }
83
+ build() {
84
+ let e = document.createElement("div");
85
+ e.className = "tcw-modal-backdrop", e.addEventListener("click", (t) => {
86
+ t.target === e && this.close();
87
+ });
88
+ let t = document.createElement("div");
89
+ t.className = "tcw-modal tcw-ip", t.setAttribute("role", "dialog"), t.setAttribute("aria-modal", "true"), t.setAttribute("aria-labelledby", `tcw-ip-title-${this.uid}`), t.tabIndex = -1, t.addEventListener("keydown", (e) => this.onKeyDown(e, t));
90
+ let n = document.createElement("div");
91
+ n.className = "tcw-visually-hidden", n.setAttribute("aria-live", "polite"), t.append(this.buildHeader(), this.buildSearch(), this.buildBody(), n), e.appendChild(t), this.layer().appendChild(e), this.backdrop = e, this.liveEl = n, this.renderNav(), this.renderList();
92
+ }
93
+ buildHeader() {
94
+ let e = document.createElement("div");
95
+ e.className = "tcw-modal-header";
96
+ let n = document.createElement("h3");
97
+ n.id = `tcw-ip-title-${this.uid}`, n.textContent = this.t("toolbar.indicators");
98
+ let r = document.createElement("button");
99
+ return r.type = "button", r.className = "tcw-modal-close", r.setAttribute("aria-label", this.t("indicatorSettings.close")), r.innerHTML = t("x", 16), r.addEventListener("click", () => this.close()), e.append(n, r), e;
100
+ }
101
+ buildSearch() {
102
+ let e = document.createElement("label");
103
+ e.className = "tcw-ip-search";
104
+ let n = document.createElement("input");
105
+ return n.type = "search", n.id = `tcw-ip-search-${this.uid}`, n.placeholder = this.t("indicatorPicker.search"), n.setAttribute("aria-label", this.t("indicatorPicker.search")), n.autocomplete = "off", n.spellcheck = !1, n.addEventListener("input", () => {
106
+ this.query = n.value, this.renderList();
107
+ }), n.addEventListener("keydown", (e) => this.onSearchKey(e)), e.innerHTML = t("search", 16), e.appendChild(n), this.searchEl = n, e;
108
+ }
109
+ buildBody() {
110
+ let e = document.createElement("div");
111
+ e.className = "tcw-ip-body";
112
+ let t = document.createElement("div");
113
+ t.className = "tcw-ip-nav", t.setAttribute("role", "tablist"), t.setAttribute("aria-orientation", "vertical"), t.setAttribute("aria-label", this.t("indicatorPicker.groups")), t.addEventListener("keydown", (e) => this.onNavKey(e));
114
+ let n = document.createElement("div");
115
+ return n.className = "tcw-ip-list", n.id = `tcw-ip-list-${this.uid}`, n.setAttribute("role", "tabpanel"), n.addEventListener("keydown", (e) => this.onListKey(e)), n.addEventListener("focusin", (e) => this.rove(e.target)), e.append(t, n), this.navEl = t, this.listEl = n, e;
116
+ }
117
+ renderNav() {
118
+ let n = this.navEl;
119
+ n && (n.replaceChildren(...this.views().map((n) => {
120
+ let r = document.createElement("button");
121
+ return r.type = "button", r.className = "tcw-ip-tab", r.id = `tcw-ip-tab-${this.uid}-${n}`, r.dataset.view = n, r.setAttribute("role", "tab"), r.setAttribute("aria-controls", `tcw-ip-list-${this.uid}`), r.innerHTML = `${n === "favorites" ? t("star", 14) : n === "templates" ? t("layers", 14) : ""}<span></span>`, r.querySelector("span").textContent = this.viewLabel(n), n === "templates" && e(r, "indicatorTemplates"), r.addEventListener("click", () => this.select(n)), r;
122
+ })), this.markNav());
123
+ }
124
+ markNav() {
125
+ this.navEl?.querySelectorAll(".tcw-ip-tab").forEach((e) => {
126
+ let t = e.dataset.view === this.view;
127
+ e.setAttribute("aria-selected", String(t)), e.classList.toggle("tcw-active", t), e.tabIndex = t ? 0 : -1;
128
+ }), this.labelList();
129
+ }
130
+ labelList() {
131
+ let e = this.query.trim() !== "";
132
+ this.listEl?.setAttribute("aria-labelledby", e ? `tcw-ip-search-${this.uid}` : `tcw-ip-tab-${this.uid}-${this.view}`);
133
+ }
134
+ select(e, t = !1) {
135
+ this.view = this.lastView = e, this.searchEl && this.query && (this.searchEl.value = this.query = ""), this.markNav(), this.renderList(), t && this.navEl?.querySelector(`[data-view="${e}"]`)?.focus();
136
+ }
137
+ renderList() {
138
+ let e = this.listEl;
139
+ if (!e) return;
140
+ e.replaceChildren();
141
+ let t = this.content.items;
142
+ if (this.navEl?.classList.toggle("tcw-ip-nav--searching", this.query.trim() !== ""), this.labelList(), this.query.trim()) {
143
+ let n = c(t, this.query);
144
+ n.length === 0 ? e.appendChild(this.note(r(this.t("indicatorPicker.noMatch"), { query: this.query.trim() }))) : e.append(...n.map((e) => this.row(e)));
145
+ } else if (this.view === "templates") this.renderTemplates(e);
146
+ else if (this.view === "favorites") {
147
+ let n = t.filter((e) => this.favorites.has(e.id)).sort(u);
148
+ n.length === 0 ? e.appendChild(this.note(this.t("indicatorPicker.noFavorites"))) : e.append(...n.map((e) => this.row(e)));
149
+ } else if (this.view === "all") for (let n of i) {
150
+ let r = t.filter((e) => e.category === n).sort(u);
151
+ if (r.length === 0) continue;
152
+ let i = document.createElement("div");
153
+ i.className = "tcw-ip-heading", i.textContent = this.viewLabel(n), e.append(i, ...r.map((e) => this.row(e)));
154
+ }
155
+ else e.append(...t.filter((e) => e.category === this.view).sort(u).map((e) => this.row(e)));
156
+ e.querySelector(".tcw-ip-add")?.setAttribute("tabindex", "0");
157
+ }
158
+ row(e) {
159
+ let n = document.createElement("div");
160
+ n.className = "tcw-ip-row", n.dataset.ind = e.id;
161
+ let r = document.createElement("button");
162
+ r.type = "button", r.className = "tcw-ip-add", r.dataset.ind = e.id, r.tabIndex = -1;
163
+ let i = document.createElement("span");
164
+ i.className = "tcw-ip-name", i.textContent = e.name, r.appendChild(i), e.shortName && e.shortName !== e.name && r.appendChild(this.tag("tcw-ip-short", e.shortName)), r.appendChild(this.tag("tcw-ip-kind", this.t(`indicatorType.${e.placement}`))), r.addEventListener("click", () => this.add(e));
165
+ let a = document.createElement("button");
166
+ return a.type = "button", a.className = "tcw-ip-aside tcw-ip-star", a.tabIndex = -1, a.title = this.t("indicatorPicker.favorite"), a.setAttribute("aria-label", `${this.t("indicatorPicker.favorite")}: ${e.name}`), a.setAttribute("aria-pressed", String(this.favorites.has(e.id))), a.innerHTML = t("star", 14), a.addEventListener("click", () => this.toggleFavorite(e.id, a)), n.append(r, a), this.markCount(n), n;
167
+ }
168
+ renderTemplates(e) {
169
+ let n = document.createElement("button");
170
+ n.type = "button", n.className = "tcw-ip-add tcw-ip-save", n.tabIndex = -1, n.dataset.tplSave = "", n.innerHTML = `${t("save", 14)}<span class="tcw-ip-name"></span>`, n.querySelector("span").textContent = this.t("templates.save"), n.addEventListener("click", () => {
171
+ this.close(), this.callbacks.templates?.onSave();
172
+ }), e.appendChild(n);
173
+ let i = this.content.templates ?? [];
174
+ i.length === 0 && e.appendChild(this.note(this.t("indicatorPicker.noTemplates")));
175
+ for (let n of i) {
176
+ let i = document.createElement("div");
177
+ i.className = "tcw-ip-row";
178
+ let a = document.createElement("button");
179
+ a.type = "button", a.className = "tcw-ip-add", a.tabIndex = -1, a.dataset.tplApply = n, a.innerHTML = `${t("layers", 14)}<span class="tcw-ip-name"></span>`, a.querySelector("span").textContent = n, a.addEventListener("click", () => {
180
+ this.close(), this.callbacks.templates?.onApply(n);
181
+ });
182
+ let o = document.createElement("button");
183
+ o.type = "button", o.className = "tcw-ip-aside tcw-ip-remove", o.tabIndex = -1, o.dataset.tplDelete = n;
184
+ let s = r(this.t("templates.delete"), { name: n });
185
+ o.title = s, o.setAttribute("aria-label", s), o.innerHTML = t("x", 12), o.addEventListener("click", () => this.callbacks.templates?.onDelete(n)), i.append(a, o), e.appendChild(i);
186
+ }
187
+ }
188
+ add(e) {
189
+ if (this.callbacks.onAdd(e.id) === !1 || !this.liveEl) return;
190
+ let t = r(this.t("indicatorPicker.added"), { name: e.name });
191
+ this.liveEl.textContent = this.liveEl.textContent === t ? `${t}\u00a0` : t;
192
+ }
193
+ toggleFavorite(e, t) {
194
+ let n = !this.favorites.has(e);
195
+ if (n ? this.favorites.add(e) : this.favorites.delete(e), this.callbacks.onFavorite(e, n), t.setAttribute("aria-pressed", String(n)), this.view !== "favorites" || this.query || n) return;
196
+ let r = [...this.listEl?.querySelectorAll(".tcw-ip-star") ?? []].indexOf(t), i = document.activeElement === t;
197
+ if (this.renderList(), !i) return;
198
+ let a = [...this.listEl?.querySelectorAll(".tcw-ip-star") ?? []];
199
+ (a[r] ?? a[a.length - 1] ?? this.searchEl)?.focus();
200
+ }
201
+ markCount(e) {
202
+ let t = this.counts.get(e.dataset.ind ?? "") ?? 0, n = e.querySelector(".tcw-ip-add"), i = e.querySelector(".tcw-ip-count");
203
+ if (t === 0) {
204
+ i?.remove();
205
+ return;
206
+ }
207
+ i || (i = this.tag("tcw-ip-count", ""), n?.appendChild(i)), i.textContent = String(t), i.title = r(this.t("indicatorPicker.onChart"), { count: String(t) });
208
+ }
209
+ tag(e, t) {
210
+ let n = document.createElement("span");
211
+ return n.className = e, n.textContent = t, n;
212
+ }
213
+ note(e) {
214
+ let t = document.createElement("div");
215
+ return t.className = "tcw-ip-empty", t.textContent = e, t;
216
+ }
217
+ rove(e) {
218
+ !(e instanceof HTMLElement) || !e.classList.contains("tcw-ip-add") || (this.listEl?.querySelectorAll(".tcw-ip-add[tabindex=\"0\"]").forEach((t) => {
219
+ t !== e && (t.tabIndex = -1);
220
+ }), e.tabIndex = 0);
221
+ }
222
+ onKeyDown(e, t) {
223
+ if (e.key === "Escape") {
224
+ e.preventDefault(), e.stopPropagation(), this.close();
225
+ return;
226
+ }
227
+ n(e, t);
228
+ }
229
+ onSearchKey(e) {
230
+ let t = this.listEl?.querySelector(".tcw-ip-add");
231
+ e.key === "ArrowDown" && t ? (e.preventDefault(), t.focus()) : e.key === "Enter" && this.query.trim() && t && (e.preventDefault(), t.click());
232
+ }
233
+ onNavKey(e) {
234
+ let t = this.views(), n = t.indexOf(this.view), r = e.key === "ArrowDown" ? Math.min(t.length - 1, n + 1) : e.key === "ArrowUp" ? Math.max(0, n - 1) : e.key === "Home" ? 0 : e.key === "End" ? t.length - 1 : -1;
235
+ r < 0 || (e.preventDefault(), this.select(t[r], !0));
236
+ }
237
+ onListKey(e) {
238
+ let t = e.target, n = t.closest(".tcw-ip-row, .tcw-ip-save"), r = [...this.listEl?.querySelectorAll(".tcw-ip-add") ?? []], i = n?.classList.contains("tcw-ip-save") ? n : n?.querySelector(".tcw-ip-add"), a = i ? r.indexOf(i) : -1, o = null;
239
+ e.key === "ArrowDown" ? o = r[a + 1] : e.key === "ArrowUp" ? o = a > 0 ? r[a - 1] : this.searchEl : e.key === "Home" ? o = r[0] : e.key === "End" ? o = r[r.length - 1] : e.key === "ArrowRight" && t === i ? o = n?.querySelector(".tcw-ip-aside") : e.key === "ArrowLeft" && t !== i && (o = i), o && (e.preventDefault(), o.focus());
240
+ }
241
+ };
242
+ //#endregion
243
+ export { d as WidgetIndicatorPicker };
244
+
245
+ //# sourceMappingURL=WidgetIndicatorPicker-CGvZ5kYd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WidgetIndicatorPicker-CGvZ5kYd.js","names":[],"sources":["../src/widget/indicatorSearch.ts","../src/widget/WidgetIndicatorPicker.ts"],"sourcesContent":["/** What a search reads of an indicator. */\nexport interface SearchableIndicator {\n id: string;\n name: string;\n shortName: string;\n}\n\n/** Lower case without accents, so \"mômentum\" finds Momentum. */\nfunction fold(text: string): string {\n return text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n}\n\nconst WORD_BREAK = /[\\s/()%-]+/;\n\n/** How well an indicator matches `q` (folded): 0 best, null not at all. */\nfunction score(item: SearchableIndicator, q: string): number | null {\n const short = fold(item.shortName);\n const name = fold(item.name);\n if (short === q || fold(item.id) === q) return 0;\n if (short.startsWith(q) || name.startsWith(q)) return 1;\n if ([...name.split(WORD_BREAK), ...short.split(WORD_BREAK)].some((word) => word.startsWith(q))) return 2;\n if (name.includes(q) || short.includes(q)) return 3;\n return null;\n}\n\n/**\n * The indicators matching `query`, best first: the short name typed in full\n * (\"rsi\"), then names starting with it, then a word starting with it, then\n * any containing it. Ties go by name. Nothing typed finds nothing.\n */\nexport function searchIndicators<T extends SearchableIndicator>(items: readonly T[], query: string): T[] {\n const q = fold(query.trim());\n if (!q) return [];\n return items\n .map((item) => ({ item, rank: score(item, q) }))\n .filter((hit): hit is { item: T; rank: number } => hit.rank !== null)\n .sort((a, b) => a.rank - b.rank || a.item.name.localeCompare(b.item.name))\n .map((hit) => hit.item);\n}\n","import type { IndicatorCategory } from '@tradecanvas/commons';\nimport { INDICATOR_CATEGORIES } from '@tradecanvas/core';\nimport { createIcon } from './icons.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { searchIndicators } from './indicatorSearch.js';\nimport { markPart } from './widgetFeatures.js';\n\n/** An indicator as the dialog lists it. */\nexport interface IndicatorPickerItem {\n id: string;\n name: string;\n shortName: string;\n placement: 'overlay' | 'panel';\n category: IndicatorCategory;\n}\n\nexport interface IndicatorPickerCallbacks {\n /** Add one; false when it wasn't added (nothing is announced then). */\n onAdd: (id: string) => boolean | void;\n onFavorite: (id: string, on: boolean) => void;\n /** The indicator templates; without them there is no Templates tab. */\n templates?: {\n onApply: (name: string) => void;\n onDelete: (name: string) => void;\n onSave: () => void;\n };\n onClose?: () => void;\n}\n\nexport interface IndicatorPickerContent {\n items: readonly IndicatorPickerItem[];\n favorites: readonly string[];\n /** The templates' names; null where they are switched off. */\n templates: readonly string[] | null;\n /** How many of each indicator are on the chart. */\n counts?: ReadonlyMap<string, number>;\n}\n\ntype View = 'favorites' | 'templates' | 'all' | IndicatorCategory;\n\nlet nextPickerId = 1;\n\nconst byName = (a: IndicatorPickerItem, b: IndicatorPickerItem) => a.name.localeCompare(b.name);\n\n/**\n * The indicators dialog: a search, the groups down the side (favourites,\n * templates, all, then each kind) and the list. A row adds its indicator and\n * the dialog stays open for the next; the star beside it keeps it in the\n * favourites. Mounts in the widget's overlay layer.\n */\nexport class WidgetIndicatorPicker {\n private backdrop: HTMLDivElement | null = null;\n private listEl: HTMLDivElement | null = null;\n private navEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n private liveEl: HTMLDivElement | null = null;\n private content: IndicatorPickerContent = { items: [], favorites: [], templates: [] };\n private favorites = new Set<string>();\n private counts: ReadonlyMap<string, number> = new Map();\n private view: View = 'all';\n /** The tab picked last, for the next opening. */\n private lastView: View | null = null;\n private query = '';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextPickerId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly callbacks: IndicatorPickerCallbacks,\n private readonly t: Translator,\n ) {}\n\n open(content: IndicatorPickerContent): void {\n if (this.backdrop) this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.content = content;\n this.favorites = new Set(content.favorites);\n this.counts = content.counts ?? new Map();\n this.query = '';\n this.view = this.startView();\n this.build();\n this.searchEl?.focus();\n }\n\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.listEl = this.navEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose?.();\n if (giveFocusBack && this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n /** The templates after one is saved or deleted; focus left on a deleted one goes to \"Save…\". */\n setTemplates(names: readonly string[]): void {\n this.content = { ...this.content, templates: [...names] };\n if (!this.listEl || this.view !== 'templates' || this.query) return;\n const hadFocus = this.listEl.contains(document.activeElement);\n this.renderList();\n if (hadFocus && !this.listEl.contains(document.activeElement)) {\n this.listEl.querySelector<HTMLButtonElement>('[data-tpl-save]')?.focus();\n }\n }\n\n /** How many of each indicator are on the chart, shown on their rows. */\n setCounts(counts: ReadonlyMap<string, number>): void {\n this.counts = counts;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-row[data-ind]').forEach((row) => this.markCount(row));\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private startView(): View {\n const views = this.views();\n if (this.lastView && views.includes(this.lastView) && !(this.lastView === 'favorites' && this.favorites.size === 0)) return this.lastView;\n return this.favorites.size > 0 ? 'favorites' : 'all';\n }\n\n /** The tabs down the side: favourites, templates, all, and each kind that has indicators. */\n private views(): View[] {\n const kinds = new Set(this.content.items.map((item) => item.category));\n return [\n 'favorites',\n ...(this.callbacks.templates && this.content.templates ? ['templates' as const] : []),\n 'all',\n ...INDICATOR_CATEGORIES.filter((category) => kinds.has(category)),\n ];\n }\n\n private viewLabel(view: View): string {\n if (view === 'favorites') return this.t('indicatorPicker.favorites');\n if (view === 'templates') return this.t('toolbar.indicators.templates');\n if (view === 'all') return this.t('toolbar.indicators.all');\n return this.t(`indicatorCategory.${view}` as MessageKey);\n }\n\n private build(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop) this.close();\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-ip';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-ip-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => this.onKeyDown(e, modal));\n\n const live = document.createElement('div');\n live.className = 'tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n modal.append(this.buildHeader(), this.buildSearch(), this.buildBody(), live);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.liveEl = live;\n this.renderNav();\n this.renderList();\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-ip-title-${this.uid}`;\n title.textContent = this.t('toolbar.indicators');\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.close());\n header.append(title, closeBtn);\n return header;\n }\n\n private buildSearch(): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-ip-search';\n const search = document.createElement('input');\n search.type = 'search';\n search.id = `tcw-ip-search-${this.uid}`;\n search.placeholder = this.t('indicatorPicker.search');\n search.setAttribute('aria-label', this.t('indicatorPicker.search'));\n search.autocomplete = 'off';\n search.spellcheck = false;\n search.addEventListener('input', () => {\n this.query = search.value;\n this.renderList();\n });\n search.addEventListener('keydown', (e) => this.onSearchKey(e));\n row.innerHTML = createIcon('search', 16);\n row.appendChild(search);\n this.searchEl = search;\n return row;\n }\n\n /** The groups down the side, and the list beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-ip-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-ip-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('indicatorPicker.groups'));\n nav.addEventListener('keydown', (e) => this.onNavKey(e));\n const list = document.createElement('div');\n list.className = 'tcw-ip-list';\n list.id = `tcw-ip-list-${this.uid}`;\n list.setAttribute('role', 'tabpanel');\n list.addEventListener('keydown', (e) => this.onListKey(e));\n list.addEventListener('focusin', (e) => this.rove(e.target));\n body.append(nav, list);\n this.navEl = nav;\n this.listEl = list;\n return body;\n }\n\n private renderNav(): void {\n const nav = this.navEl;\n if (!nav) return;\n nav.replaceChildren(...this.views().map((view) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.className = 'tcw-ip-tab';\n tab.id = `tcw-ip-tab-${this.uid}-${view}`;\n tab.dataset.view = view;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', `tcw-ip-list-${this.uid}`);\n const icon = view === 'favorites' ? createIcon('star', 14) : view === 'templates' ? createIcon('layers', 14) : '';\n tab.innerHTML = `${icon}<span></span>`;\n tab.querySelector('span')!.textContent = this.viewLabel(view);\n if (view === 'templates') markPart(tab, 'indicatorTemplates');\n tab.addEventListener('click', () => this.select(view));\n return tab;\n }));\n this.markNav();\n }\n\n private markNav(): void {\n this.navEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-tab').forEach((tab) => {\n const on = tab.dataset.view === this.view;\n tab.setAttribute('aria-selected', String(on));\n tab.classList.toggle('tcw-active', on);\n tab.tabIndex = on ? 0 : -1;\n });\n this.labelList();\n }\n\n /** The list is named by its tab, or by the search while it shows the matches. */\n private labelList(): void {\n const searching = this.query.trim() !== '';\n this.listEl?.setAttribute('aria-labelledby', searching ? `tcw-ip-search-${this.uid}` : `tcw-ip-tab-${this.uid}-${this.view}`);\n }\n\n private select(view: View, focusTab = false): void {\n this.view = this.lastView = view;\n if (this.searchEl && this.query) this.searchEl.value = this.query = '';\n this.markNav();\n this.renderList();\n if (focusTab) this.navEl?.querySelector<HTMLButtonElement>(`[data-view=\"${view}\"]`)?.focus();\n }\n\n private renderList(): void {\n const list = this.listEl;\n if (!list) return;\n list.replaceChildren();\n const items = this.content.items;\n // While searching, the results come from every group: the one picked steps back.\n this.navEl?.classList.toggle('tcw-ip-nav--searching', this.query.trim() !== '');\n this.labelList();\n if (this.query.trim()) {\n const hits = searchIndicators(items, this.query);\n if (hits.length === 0) list.appendChild(this.note(fill(this.t('indicatorPicker.noMatch'), { query: this.query.trim() })));\n else list.append(...hits.map((item) => this.row(item)));\n } else if (this.view === 'templates') {\n this.renderTemplates(list);\n } else if (this.view === 'favorites') {\n const favorites = items.filter((item) => this.favorites.has(item.id)).sort(byName);\n if (favorites.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noFavorites')));\n else list.append(...favorites.map((item) => this.row(item)));\n } else if (this.view === 'all') {\n for (const category of INDICATOR_CATEGORIES) {\n const group = items.filter((item) => item.category === category).sort(byName);\n if (group.length === 0) continue;\n const heading = document.createElement('div');\n heading.className = 'tcw-ip-heading';\n heading.textContent = this.viewLabel(category);\n list.append(heading, ...group.map((item) => this.row(item)));\n }\n } else {\n list.append(...items.filter((item) => item.category === this.view).sort(byName).map((item) => this.row(item)));\n }\n list.querySelector<HTMLButtonElement>('.tcw-ip-add')?.setAttribute('tabindex', '0');\n }\n\n private row(item: IndicatorPickerItem): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n row.dataset.ind = item.id;\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'tcw-ip-add';\n add.dataset.ind = item.id;\n add.tabIndex = -1;\n const name = document.createElement('span');\n name.className = 'tcw-ip-name';\n name.textContent = item.name;\n add.appendChild(name);\n if (item.shortName && item.shortName !== item.name) add.appendChild(this.tag('tcw-ip-short', item.shortName));\n add.appendChild(this.tag('tcw-ip-kind', this.t(`indicatorType.${item.placement}`)));\n add.addEventListener('click', () => this.add(item));\n const star = document.createElement('button');\n star.type = 'button';\n star.className = 'tcw-ip-aside tcw-ip-star';\n star.tabIndex = -1;\n star.title = this.t('indicatorPicker.favorite');\n star.setAttribute('aria-label', `${this.t('indicatorPicker.favorite')}: ${item.name}`);\n star.setAttribute('aria-pressed', String(this.favorites.has(item.id)));\n star.innerHTML = createIcon('star', 14);\n star.addEventListener('click', () => this.toggleFavorite(item.id, star));\n row.append(add, star);\n this.markCount(row);\n return row;\n }\n\n private renderTemplates(list: HTMLElement): void {\n const save = document.createElement('button');\n save.type = 'button';\n save.className = 'tcw-ip-add tcw-ip-save';\n save.tabIndex = -1;\n save.dataset.tplSave = '';\n save.innerHTML = `${createIcon('save', 14)}<span class=\"tcw-ip-name\"></span>`;\n save.querySelector('span')!.textContent = this.t('templates.save');\n save.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onSave();\n });\n list.appendChild(save);\n const names = this.content.templates ?? [];\n if (names.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noTemplates')));\n for (const name of names) {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n const apply = document.createElement('button');\n apply.type = 'button';\n apply.className = 'tcw-ip-add';\n apply.tabIndex = -1;\n apply.dataset.tplApply = name;\n apply.innerHTML = `${createIcon('layers', 14)}<span class=\"tcw-ip-name\"></span>`;\n apply.querySelector('span')!.textContent = name;\n apply.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onApply(name);\n });\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-ip-aside tcw-ip-remove';\n remove.tabIndex = -1;\n remove.dataset.tplDelete = name;\n const label = fill(this.t('templates.delete'), { name });\n remove.title = label;\n remove.setAttribute('aria-label', label);\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => this.callbacks.templates?.onDelete(name));\n row.append(apply, remove);\n list.appendChild(row);\n }\n }\n\n private add(item: IndicatorPickerItem): void {\n if (this.callbacks.onAdd(item.id) === false || !this.liveEl) return;\n const text = fill(this.t('indicatorPicker.added'), { name: item.name });\n // The same words again (a second RSI) are only read out when they change: a space tells them apart.\n this.liveEl.textContent = this.liveEl.textContent === text ? `${text}\\u00a0` : text;\n }\n\n private toggleFavorite(id: string, star: HTMLButtonElement): void {\n const on = !this.favorites.has(id);\n if (on) this.favorites.add(id);\n else this.favorites.delete(id);\n this.callbacks.onFavorite(id, on);\n star.setAttribute('aria-pressed', String(on));\n if (this.view !== 'favorites' || this.query || on) return;\n // Gone from the favourites: the keyboard moves to the next row's star.\n const stars = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n const at = stars.indexOf(star);\n const hadFocus = document.activeElement === star;\n this.renderList();\n if (!hadFocus) return;\n const left = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n (left[at] ?? left[left.length - 1] ?? this.searchEl)?.focus();\n }\n\n private markCount(row: HTMLElement): void {\n const n = this.counts.get(row.dataset.ind ?? '') ?? 0;\n const add = row.querySelector('.tcw-ip-add');\n let badge = row.querySelector<HTMLElement>('.tcw-ip-count');\n if (n === 0) {\n badge?.remove();\n return;\n }\n if (!badge) {\n badge = this.tag('tcw-ip-count', '');\n add?.appendChild(badge);\n }\n badge.textContent = String(n);\n badge.title = fill(this.t('indicatorPicker.onChart'), { count: String(n) });\n }\n\n private tag(className: string, text: string): HTMLSpanElement {\n const span = document.createElement('span');\n span.className = className;\n span.textContent = text;\n return span;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-ip-empty';\n el.textContent = text;\n return el;\n }\n\n /** One row in the list takes Tab: the one focused last. */\n private rove(target: EventTarget | null): void {\n if (!(target instanceof HTMLElement) || !target.classList.contains('tcw-ip-add')) return;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-add[tabindex=\"0\"]').forEach((el) => {\n if (el !== target) el.tabIndex = -1;\n });\n target.tabIndex = 0;\n }\n\n private onKeyDown(e: KeyboardEvent, modal: HTMLElement): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n }\n\n /** Down goes into the list; Enter adds the best match. */\n private onSearchKey(e: KeyboardEvent): void {\n const first = this.listEl?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n if (e.key === 'ArrowDown' && first) {\n e.preventDefault();\n first.focus();\n } else if (e.key === 'Enter' && this.query.trim() && first) {\n e.preventDefault();\n first.click();\n }\n }\n\n /** Up and down through the tabs (Home, End), picking as they go. */\n private onNavKey(e: KeyboardEvent): void {\n const views = this.views();\n const at = views.indexOf(this.view);\n const next = e.key === 'ArrowDown' ? Math.min(views.length - 1, at + 1)\n : e.key === 'ArrowUp' ? Math.max(0, at - 1)\n : e.key === 'Home' ? 0\n : e.key === 'End' ? views.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.select(views[next], true);\n }\n\n /** Up and down through the rows (from the top, back to the search); right to a row's star, left back. */\n private onListKey(e: KeyboardEvent): void {\n const target = e.target as HTMLElement;\n const row = target.closest('.tcw-ip-row, .tcw-ip-save');\n const adds = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-add') ?? [])];\n const main = row?.classList.contains('tcw-ip-save') ? row as HTMLButtonElement : row?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n const at = main ? adds.indexOf(main) : -1;\n let to: HTMLElement | null | undefined = null;\n if (e.key === 'ArrowDown') to = adds[at + 1];\n else if (e.key === 'ArrowUp') to = at > 0 ? adds[at - 1] : this.searchEl;\n else if (e.key === 'Home') to = adds[0];\n else if (e.key === 'End') to = adds[adds.length - 1];\n else if (e.key === 'ArrowRight' && target === main) to = row?.querySelector<HTMLButtonElement>('.tcw-ip-aside');\n else if (e.key === 'ArrowLeft' && target !== main) to = main;\n if (!to) return;\n e.preventDefault();\n to.focus();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require(`./widgetFeatures-BZ8OVPN2.cjs`),t=require(`./focusTrap-DrhhmG0V.cjs`),n=require(`./i18n-C2nQBZTY.cjs`);let r=require(`@tradecanvas/core`);function i(e){return e.normalize(`NFD`).replace(/[̀-ͯ]/g,``).toLowerCase()}var a=/[\s/()%-]+/;function o(e,t){let n=i(e.shortName),r=i(e.name);return n===t||i(e.id)===t?0:n.startsWith(t)||r.startsWith(t)?1:[...r.split(a),...n.split(a)].some(e=>e.startsWith(t))?2:r.includes(t)||n.includes(t)?3:null}function s(e,t){let n=i(t.trim());return n?e.map(e=>({item:e,rank:o(e,n)})).filter(e=>e.rank!==null).sort((e,t)=>e.rank-t.rank||e.item.name.localeCompare(t.item.name)).map(e=>e.item):[]}var c=1,l=(e,t)=>e.name.localeCompare(t.name),u=class{backdrop=null;listEl=null;navEl=null;searchEl=null;liveEl=null;content={items:[],favorites:[],templates:[]};favorites=new Set;counts=new Map;view=`all`;lastView=null;query=``;returnFocus=null;uid=c++;constructor(e,t,n){this.layer=e,this.callbacks=t,this.t=n}open(e){this.backdrop&&this.close(!1),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.content=e,this.favorites=new Set(e.favorites),this.counts=e.counts??new Map,this.query=``,this.view=this.startView(),this.build(),this.searchEl?.focus()}close(e=!0){this.backdrop&&(this.backdrop.remove(),this.backdrop=this.listEl=this.navEl=this.searchEl=this.liveEl=null,this.callbacks.onClose?.(),e&&this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null)}isOpen(){return this.backdrop!==null}setTemplates(e){if(this.content={...this.content,templates:[...e]},!this.listEl||this.view!==`templates`||this.query)return;let t=this.listEl.contains(document.activeElement);this.renderList(),t&&!this.listEl.contains(document.activeElement)&&this.listEl.querySelector(`[data-tpl-save]`)?.focus()}setCounts(e){this.counts=e,this.listEl?.querySelectorAll(`.tcw-ip-row[data-ind]`).forEach(e=>this.markCount(e))}destroy(){this.close(!1)}startView(){let e=this.views();return this.lastView&&e.includes(this.lastView)&&!(this.lastView===`favorites`&&this.favorites.size===0)?this.lastView:this.favorites.size>0?`favorites`:`all`}views(){let e=new Set(this.content.items.map(e=>e.category));return[`favorites`,...this.callbacks.templates&&this.content.templates?[`templates`]:[],`all`,...r.INDICATOR_CATEGORIES.filter(t=>e.has(t))]}viewLabel(e){return e===`favorites`?this.t(`indicatorPicker.favorites`):e===`templates`?this.t(`toolbar.indicators.templates`):e===`all`?this.t(`toolbar.indicators.all`):this.t(`indicatorCategory.${e}`)}build(){let e=document.createElement(`div`);e.className=`tcw-modal-backdrop`,e.addEventListener(`click`,t=>{t.target===e&&this.close()});let t=document.createElement(`div`);t.className=`tcw-modal tcw-ip`,t.setAttribute(`role`,`dialog`),t.setAttribute(`aria-modal`,`true`),t.setAttribute(`aria-labelledby`,`tcw-ip-title-${this.uid}`),t.tabIndex=-1,t.addEventListener(`keydown`,e=>this.onKeyDown(e,t));let n=document.createElement(`div`);n.className=`tcw-visually-hidden`,n.setAttribute(`aria-live`,`polite`),t.append(this.buildHeader(),this.buildSearch(),this.buildBody(),n),e.appendChild(t),this.layer().appendChild(e),this.backdrop=e,this.liveEl=n,this.renderNav(),this.renderList()}buildHeader(){let e=document.createElement(`div`);e.className=`tcw-modal-header`;let n=document.createElement(`h3`);n.id=`tcw-ip-title-${this.uid}`,n.textContent=this.t(`toolbar.indicators`);let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-modal-close`,r.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),r.innerHTML=t.i(`x`,16),r.addEventListener(`click`,()=>this.close()),e.append(n,r),e}buildSearch(){let e=document.createElement(`label`);e.className=`tcw-ip-search`;let n=document.createElement(`input`);return n.type=`search`,n.id=`tcw-ip-search-${this.uid}`,n.placeholder=this.t(`indicatorPicker.search`),n.setAttribute(`aria-label`,this.t(`indicatorPicker.search`)),n.autocomplete=`off`,n.spellcheck=!1,n.addEventListener(`input`,()=>{this.query=n.value,this.renderList()}),n.addEventListener(`keydown`,e=>this.onSearchKey(e)),e.innerHTML=t.i(`search`,16),e.appendChild(n),this.searchEl=n,e}buildBody(){let e=document.createElement(`div`);e.className=`tcw-ip-body`;let t=document.createElement(`div`);t.className=`tcw-ip-nav`,t.setAttribute(`role`,`tablist`),t.setAttribute(`aria-orientation`,`vertical`),t.setAttribute(`aria-label`,this.t(`indicatorPicker.groups`)),t.addEventListener(`keydown`,e=>this.onNavKey(e));let n=document.createElement(`div`);return n.className=`tcw-ip-list`,n.id=`tcw-ip-list-${this.uid}`,n.setAttribute(`role`,`tabpanel`),n.addEventListener(`keydown`,e=>this.onListKey(e)),n.addEventListener(`focusin`,e=>this.rove(e.target)),e.append(t,n),this.navEl=t,this.listEl=n,e}renderNav(){let n=this.navEl;n&&(n.replaceChildren(...this.views().map(n=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-ip-tab`,r.id=`tcw-ip-tab-${this.uid}-${n}`,r.dataset.view=n,r.setAttribute(`role`,`tab`),r.setAttribute(`aria-controls`,`tcw-ip-list-${this.uid}`),r.innerHTML=`${n===`favorites`?t.i(`star`,14):n===`templates`?t.i(`layers`,14):``}<span></span>`,r.querySelector(`span`).textContent=this.viewLabel(n),n===`templates`&&e.o(r,`indicatorTemplates`),r.addEventListener(`click`,()=>this.select(n)),r})),this.markNav())}markNav(){this.navEl?.querySelectorAll(`.tcw-ip-tab`).forEach(e=>{let t=e.dataset.view===this.view;e.setAttribute(`aria-selected`,String(t)),e.classList.toggle(`tcw-active`,t),e.tabIndex=t?0:-1}),this.labelList()}labelList(){let e=this.query.trim()!==``;this.listEl?.setAttribute(`aria-labelledby`,e?`tcw-ip-search-${this.uid}`:`tcw-ip-tab-${this.uid}-${this.view}`)}select(e,t=!1){this.view=this.lastView=e,this.searchEl&&this.query&&(this.searchEl.value=this.query=``),this.markNav(),this.renderList(),t&&this.navEl?.querySelector(`[data-view="${e}"]`)?.focus()}renderList(){let e=this.listEl;if(!e)return;e.replaceChildren();let t=this.content.items;if(this.navEl?.classList.toggle(`tcw-ip-nav--searching`,this.query.trim()!==``),this.labelList(),this.query.trim()){let r=s(t,this.query);r.length===0?e.appendChild(this.note(n.r(this.t(`indicatorPicker.noMatch`),{query:this.query.trim()}))):e.append(...r.map(e=>this.row(e)))}else if(this.view===`templates`)this.renderTemplates(e);else if(this.view===`favorites`){let n=t.filter(e=>this.favorites.has(e.id)).sort(l);n.length===0?e.appendChild(this.note(this.t(`indicatorPicker.noFavorites`))):e.append(...n.map(e=>this.row(e)))}else if(this.view===`all`)for(let n of r.INDICATOR_CATEGORIES){let r=t.filter(e=>e.category===n).sort(l);if(r.length===0)continue;let i=document.createElement(`div`);i.className=`tcw-ip-heading`,i.textContent=this.viewLabel(n),e.append(i,...r.map(e=>this.row(e)))}else e.append(...t.filter(e=>e.category===this.view).sort(l).map(e=>this.row(e)));e.querySelector(`.tcw-ip-add`)?.setAttribute(`tabindex`,`0`)}row(e){let n=document.createElement(`div`);n.className=`tcw-ip-row`,n.dataset.ind=e.id;let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-ip-add`,r.dataset.ind=e.id,r.tabIndex=-1;let i=document.createElement(`span`);i.className=`tcw-ip-name`,i.textContent=e.name,r.appendChild(i),e.shortName&&e.shortName!==e.name&&r.appendChild(this.tag(`tcw-ip-short`,e.shortName)),r.appendChild(this.tag(`tcw-ip-kind`,this.t(`indicatorType.${e.placement}`))),r.addEventListener(`click`,()=>this.add(e));let a=document.createElement(`button`);return a.type=`button`,a.className=`tcw-ip-aside tcw-ip-star`,a.tabIndex=-1,a.title=this.t(`indicatorPicker.favorite`),a.setAttribute(`aria-label`,`${this.t(`indicatorPicker.favorite`)}: ${e.name}`),a.setAttribute(`aria-pressed`,String(this.favorites.has(e.id))),a.innerHTML=t.i(`star`,14),a.addEventListener(`click`,()=>this.toggleFavorite(e.id,a)),n.append(r,a),this.markCount(n),n}renderTemplates(e){let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-ip-add tcw-ip-save`,r.tabIndex=-1,r.dataset.tplSave=``,r.innerHTML=`${t.i(`save`,14)}<span class="tcw-ip-name"></span>`,r.querySelector(`span`).textContent=this.t(`templates.save`),r.addEventListener(`click`,()=>{this.close(),this.callbacks.templates?.onSave()}),e.appendChild(r);let i=this.content.templates??[];i.length===0&&e.appendChild(this.note(this.t(`indicatorPicker.noTemplates`)));for(let r of i){let i=document.createElement(`div`);i.className=`tcw-ip-row`;let a=document.createElement(`button`);a.type=`button`,a.className=`tcw-ip-add`,a.tabIndex=-1,a.dataset.tplApply=r,a.innerHTML=`${t.i(`layers`,14)}<span class="tcw-ip-name"></span>`,a.querySelector(`span`).textContent=r,a.addEventListener(`click`,()=>{this.close(),this.callbacks.templates?.onApply(r)});let o=document.createElement(`button`);o.type=`button`,o.className=`tcw-ip-aside tcw-ip-remove`,o.tabIndex=-1,o.dataset.tplDelete=r;let s=n.r(this.t(`templates.delete`),{name:r});o.title=s,o.setAttribute(`aria-label`,s),o.innerHTML=t.i(`x`,12),o.addEventListener(`click`,()=>this.callbacks.templates?.onDelete(r)),i.append(a,o),e.appendChild(i)}}add(e){if(this.callbacks.onAdd(e.id)===!1||!this.liveEl)return;let t=n.r(this.t(`indicatorPicker.added`),{name:e.name});this.liveEl.textContent=this.liveEl.textContent===t?`${t}\u00a0`:t}toggleFavorite(e,t){let n=!this.favorites.has(e);if(n?this.favorites.add(e):this.favorites.delete(e),this.callbacks.onFavorite(e,n),t.setAttribute(`aria-pressed`,String(n)),this.view!==`favorites`||this.query||n)return;let r=[...this.listEl?.querySelectorAll(`.tcw-ip-star`)??[]].indexOf(t),i=document.activeElement===t;if(this.renderList(),!i)return;let a=[...this.listEl?.querySelectorAll(`.tcw-ip-star`)??[]];(a[r]??a[a.length-1]??this.searchEl)?.focus()}markCount(e){let t=this.counts.get(e.dataset.ind??``)??0,r=e.querySelector(`.tcw-ip-add`),i=e.querySelector(`.tcw-ip-count`);if(t===0){i?.remove();return}i||(i=this.tag(`tcw-ip-count`,``),r?.appendChild(i)),i.textContent=String(t),i.title=n.r(this.t(`indicatorPicker.onChart`),{count:String(t)})}tag(e,t){let n=document.createElement(`span`);return n.className=e,n.textContent=t,n}note(e){let t=document.createElement(`div`);return t.className=`tcw-ip-empty`,t.textContent=e,t}rove(e){!(e instanceof HTMLElement)||!e.classList.contains(`tcw-ip-add`)||(this.listEl?.querySelectorAll(`.tcw-ip-add[tabindex="0"]`).forEach(t=>{t!==e&&(t.tabIndex=-1)}),e.tabIndex=0)}onKeyDown(e,n){if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),this.close();return}t.n(e,n)}onSearchKey(e){let t=this.listEl?.querySelector(`.tcw-ip-add`);e.key===`ArrowDown`&&t?(e.preventDefault(),t.focus()):e.key===`Enter`&&this.query.trim()&&t&&(e.preventDefault(),t.click())}onNavKey(e){let t=this.views(),n=t.indexOf(this.view),r=e.key===`ArrowDown`?Math.min(t.length-1,n+1):e.key===`ArrowUp`?Math.max(0,n-1):e.key===`Home`?0:e.key===`End`?t.length-1:-1;r<0||(e.preventDefault(),this.select(t[r],!0))}onListKey(e){let t=e.target,n=t.closest(`.tcw-ip-row, .tcw-ip-save`),r=[...this.listEl?.querySelectorAll(`.tcw-ip-add`)??[]],i=n?.classList.contains(`tcw-ip-save`)?n:n?.querySelector(`.tcw-ip-add`),a=i?r.indexOf(i):-1,o=null;e.key===`ArrowDown`?o=r[a+1]:e.key===`ArrowUp`?o=a>0?r[a-1]:this.searchEl:e.key===`Home`?o=r[0]:e.key===`End`?o=r[r.length-1]:e.key===`ArrowRight`&&t===i?o=n?.querySelector(`.tcw-ip-aside`):e.key===`ArrowLeft`&&t!==i&&(o=i),o&&(e.preventDefault(),o.focus())}};exports.WidgetIndicatorPicker=u;
2
+ //# sourceMappingURL=WidgetIndicatorPicker-DuKJFPRC.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WidgetIndicatorPicker-DuKJFPRC.cjs","names":[],"sources":["../src/widget/indicatorSearch.ts","../src/widget/WidgetIndicatorPicker.ts"],"sourcesContent":["/** What a search reads of an indicator. */\nexport interface SearchableIndicator {\n id: string;\n name: string;\n shortName: string;\n}\n\n/** Lower case without accents, so \"mômentum\" finds Momentum. */\nfunction fold(text: string): string {\n return text.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase();\n}\n\nconst WORD_BREAK = /[\\s/()%-]+/;\n\n/** How well an indicator matches `q` (folded): 0 best, null not at all. */\nfunction score(item: SearchableIndicator, q: string): number | null {\n const short = fold(item.shortName);\n const name = fold(item.name);\n if (short === q || fold(item.id) === q) return 0;\n if (short.startsWith(q) || name.startsWith(q)) return 1;\n if ([...name.split(WORD_BREAK), ...short.split(WORD_BREAK)].some((word) => word.startsWith(q))) return 2;\n if (name.includes(q) || short.includes(q)) return 3;\n return null;\n}\n\n/**\n * The indicators matching `query`, best first: the short name typed in full\n * (\"rsi\"), then names starting with it, then a word starting with it, then\n * any containing it. Ties go by name. Nothing typed finds nothing.\n */\nexport function searchIndicators<T extends SearchableIndicator>(items: readonly T[], query: string): T[] {\n const q = fold(query.trim());\n if (!q) return [];\n return items\n .map((item) => ({ item, rank: score(item, q) }))\n .filter((hit): hit is { item: T; rank: number } => hit.rank !== null)\n .sort((a, b) => a.rank - b.rank || a.item.name.localeCompare(b.item.name))\n .map((hit) => hit.item);\n}\n","import type { IndicatorCategory } from '@tradecanvas/commons';\nimport { INDICATOR_CATEGORIES } from '@tradecanvas/core';\nimport { createIcon } from './icons.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { searchIndicators } from './indicatorSearch.js';\nimport { markPart } from './widgetFeatures.js';\n\n/** An indicator as the dialog lists it. */\nexport interface IndicatorPickerItem {\n id: string;\n name: string;\n shortName: string;\n placement: 'overlay' | 'panel';\n category: IndicatorCategory;\n}\n\nexport interface IndicatorPickerCallbacks {\n /** Add one; false when it wasn't added (nothing is announced then). */\n onAdd: (id: string) => boolean | void;\n onFavorite: (id: string, on: boolean) => void;\n /** The indicator templates; without them there is no Templates tab. */\n templates?: {\n onApply: (name: string) => void;\n onDelete: (name: string) => void;\n onSave: () => void;\n };\n onClose?: () => void;\n}\n\nexport interface IndicatorPickerContent {\n items: readonly IndicatorPickerItem[];\n favorites: readonly string[];\n /** The templates' names; null where they are switched off. */\n templates: readonly string[] | null;\n /** How many of each indicator are on the chart. */\n counts?: ReadonlyMap<string, number>;\n}\n\ntype View = 'favorites' | 'templates' | 'all' | IndicatorCategory;\n\nlet nextPickerId = 1;\n\nconst byName = (a: IndicatorPickerItem, b: IndicatorPickerItem) => a.name.localeCompare(b.name);\n\n/**\n * The indicators dialog: a search, the groups down the side (favourites,\n * templates, all, then each kind) and the list. A row adds its indicator and\n * the dialog stays open for the next; the star beside it keeps it in the\n * favourites. Mounts in the widget's overlay layer.\n */\nexport class WidgetIndicatorPicker {\n private backdrop: HTMLDivElement | null = null;\n private listEl: HTMLDivElement | null = null;\n private navEl: HTMLDivElement | null = null;\n private searchEl: HTMLInputElement | null = null;\n private liveEl: HTMLDivElement | null = null;\n private content: IndicatorPickerContent = { items: [], favorites: [], templates: [] };\n private favorites = new Set<string>();\n private counts: ReadonlyMap<string, number> = new Map();\n private view: View = 'all';\n /** The tab picked last, for the next opening. */\n private lastView: View | null = null;\n private query = '';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextPickerId++;\n\n constructor(\n private readonly layer: () => HTMLElement,\n private readonly callbacks: IndicatorPickerCallbacks,\n private readonly t: Translator,\n ) {}\n\n open(content: IndicatorPickerContent): void {\n if (this.backdrop) this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.content = content;\n this.favorites = new Set(content.favorites);\n this.counts = content.counts ?? new Map();\n this.query = '';\n this.view = this.startView();\n this.build();\n this.searchEl?.focus();\n }\n\n close(giveFocusBack = true): void {\n if (!this.backdrop) return;\n this.backdrop.remove();\n this.backdrop = this.listEl = this.navEl = this.searchEl = this.liveEl = null;\n this.callbacks.onClose?.();\n if (giveFocusBack && this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return this.backdrop !== null;\n }\n\n /** The templates after one is saved or deleted; focus left on a deleted one goes to \"Save…\". */\n setTemplates(names: readonly string[]): void {\n this.content = { ...this.content, templates: [...names] };\n if (!this.listEl || this.view !== 'templates' || this.query) return;\n const hadFocus = this.listEl.contains(document.activeElement);\n this.renderList();\n if (hadFocus && !this.listEl.contains(document.activeElement)) {\n this.listEl.querySelector<HTMLButtonElement>('[data-tpl-save]')?.focus();\n }\n }\n\n /** How many of each indicator are on the chart, shown on their rows. */\n setCounts(counts: ReadonlyMap<string, number>): void {\n this.counts = counts;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-row[data-ind]').forEach((row) => this.markCount(row));\n }\n\n destroy(): void {\n this.close(false);\n }\n\n private startView(): View {\n const views = this.views();\n if (this.lastView && views.includes(this.lastView) && !(this.lastView === 'favorites' && this.favorites.size === 0)) return this.lastView;\n return this.favorites.size > 0 ? 'favorites' : 'all';\n }\n\n /** The tabs down the side: favourites, templates, all, and each kind that has indicators. */\n private views(): View[] {\n const kinds = new Set(this.content.items.map((item) => item.category));\n return [\n 'favorites',\n ...(this.callbacks.templates && this.content.templates ? ['templates' as const] : []),\n 'all',\n ...INDICATOR_CATEGORIES.filter((category) => kinds.has(category)),\n ];\n }\n\n private viewLabel(view: View): string {\n if (view === 'favorites') return this.t('indicatorPicker.favorites');\n if (view === 'templates') return this.t('toolbar.indicators.templates');\n if (view === 'all') return this.t('toolbar.indicators.all');\n return this.t(`indicatorCategory.${view}` as MessageKey);\n }\n\n private build(): void {\n const backdrop = document.createElement('div');\n backdrop.className = 'tcw-modal-backdrop';\n backdrop.addEventListener('click', (e) => {\n if (e.target === backdrop) this.close();\n });\n const modal = document.createElement('div');\n modal.className = 'tcw-modal tcw-ip';\n modal.setAttribute('role', 'dialog');\n modal.setAttribute('aria-modal', 'true');\n modal.setAttribute('aria-labelledby', `tcw-ip-title-${this.uid}`);\n modal.tabIndex = -1;\n modal.addEventListener('keydown', (e) => this.onKeyDown(e, modal));\n\n const live = document.createElement('div');\n live.className = 'tcw-visually-hidden';\n live.setAttribute('aria-live', 'polite');\n modal.append(this.buildHeader(), this.buildSearch(), this.buildBody(), live);\n backdrop.appendChild(modal);\n this.layer().appendChild(backdrop);\n this.backdrop = backdrop;\n this.liveEl = live;\n this.renderNav();\n this.renderList();\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-ip-title-${this.uid}`;\n title.textContent = this.t('toolbar.indicators');\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.close());\n header.append(title, closeBtn);\n return header;\n }\n\n private buildSearch(): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-ip-search';\n const search = document.createElement('input');\n search.type = 'search';\n search.id = `tcw-ip-search-${this.uid}`;\n search.placeholder = this.t('indicatorPicker.search');\n search.setAttribute('aria-label', this.t('indicatorPicker.search'));\n search.autocomplete = 'off';\n search.spellcheck = false;\n search.addEventListener('input', () => {\n this.query = search.value;\n this.renderList();\n });\n search.addEventListener('keydown', (e) => this.onSearchKey(e));\n row.innerHTML = createIcon('search', 16);\n row.appendChild(search);\n this.searchEl = search;\n return row;\n }\n\n /** The groups down the side, and the list beside them. */\n private buildBody(): HTMLDivElement {\n const body = document.createElement('div');\n body.className = 'tcw-ip-body';\n const nav = document.createElement('div');\n nav.className = 'tcw-ip-nav';\n nav.setAttribute('role', 'tablist');\n nav.setAttribute('aria-orientation', 'vertical');\n nav.setAttribute('aria-label', this.t('indicatorPicker.groups'));\n nav.addEventListener('keydown', (e) => this.onNavKey(e));\n const list = document.createElement('div');\n list.className = 'tcw-ip-list';\n list.id = `tcw-ip-list-${this.uid}`;\n list.setAttribute('role', 'tabpanel');\n list.addEventListener('keydown', (e) => this.onListKey(e));\n list.addEventListener('focusin', (e) => this.rove(e.target));\n body.append(nav, list);\n this.navEl = nav;\n this.listEl = list;\n return body;\n }\n\n private renderNav(): void {\n const nav = this.navEl;\n if (!nav) return;\n nav.replaceChildren(...this.views().map((view) => {\n const tab = document.createElement('button');\n tab.type = 'button';\n tab.className = 'tcw-ip-tab';\n tab.id = `tcw-ip-tab-${this.uid}-${view}`;\n tab.dataset.view = view;\n tab.setAttribute('role', 'tab');\n tab.setAttribute('aria-controls', `tcw-ip-list-${this.uid}`);\n const icon = view === 'favorites' ? createIcon('star', 14) : view === 'templates' ? createIcon('layers', 14) : '';\n tab.innerHTML = `${icon}<span></span>`;\n tab.querySelector('span')!.textContent = this.viewLabel(view);\n if (view === 'templates') markPart(tab, 'indicatorTemplates');\n tab.addEventListener('click', () => this.select(view));\n return tab;\n }));\n this.markNav();\n }\n\n private markNav(): void {\n this.navEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-tab').forEach((tab) => {\n const on = tab.dataset.view === this.view;\n tab.setAttribute('aria-selected', String(on));\n tab.classList.toggle('tcw-active', on);\n tab.tabIndex = on ? 0 : -1;\n });\n this.labelList();\n }\n\n /** The list is named by its tab, or by the search while it shows the matches. */\n private labelList(): void {\n const searching = this.query.trim() !== '';\n this.listEl?.setAttribute('aria-labelledby', searching ? `tcw-ip-search-${this.uid}` : `tcw-ip-tab-${this.uid}-${this.view}`);\n }\n\n private select(view: View, focusTab = false): void {\n this.view = this.lastView = view;\n if (this.searchEl && this.query) this.searchEl.value = this.query = '';\n this.markNav();\n this.renderList();\n if (focusTab) this.navEl?.querySelector<HTMLButtonElement>(`[data-view=\"${view}\"]`)?.focus();\n }\n\n private renderList(): void {\n const list = this.listEl;\n if (!list) return;\n list.replaceChildren();\n const items = this.content.items;\n // While searching, the results come from every group: the one picked steps back.\n this.navEl?.classList.toggle('tcw-ip-nav--searching', this.query.trim() !== '');\n this.labelList();\n if (this.query.trim()) {\n const hits = searchIndicators(items, this.query);\n if (hits.length === 0) list.appendChild(this.note(fill(this.t('indicatorPicker.noMatch'), { query: this.query.trim() })));\n else list.append(...hits.map((item) => this.row(item)));\n } else if (this.view === 'templates') {\n this.renderTemplates(list);\n } else if (this.view === 'favorites') {\n const favorites = items.filter((item) => this.favorites.has(item.id)).sort(byName);\n if (favorites.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noFavorites')));\n else list.append(...favorites.map((item) => this.row(item)));\n } else if (this.view === 'all') {\n for (const category of INDICATOR_CATEGORIES) {\n const group = items.filter((item) => item.category === category).sort(byName);\n if (group.length === 0) continue;\n const heading = document.createElement('div');\n heading.className = 'tcw-ip-heading';\n heading.textContent = this.viewLabel(category);\n list.append(heading, ...group.map((item) => this.row(item)));\n }\n } else {\n list.append(...items.filter((item) => item.category === this.view).sort(byName).map((item) => this.row(item)));\n }\n list.querySelector<HTMLButtonElement>('.tcw-ip-add')?.setAttribute('tabindex', '0');\n }\n\n private row(item: IndicatorPickerItem): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n row.dataset.ind = item.id;\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'tcw-ip-add';\n add.dataset.ind = item.id;\n add.tabIndex = -1;\n const name = document.createElement('span');\n name.className = 'tcw-ip-name';\n name.textContent = item.name;\n add.appendChild(name);\n if (item.shortName && item.shortName !== item.name) add.appendChild(this.tag('tcw-ip-short', item.shortName));\n add.appendChild(this.tag('tcw-ip-kind', this.t(`indicatorType.${item.placement}`)));\n add.addEventListener('click', () => this.add(item));\n const star = document.createElement('button');\n star.type = 'button';\n star.className = 'tcw-ip-aside tcw-ip-star';\n star.tabIndex = -1;\n star.title = this.t('indicatorPicker.favorite');\n star.setAttribute('aria-label', `${this.t('indicatorPicker.favorite')}: ${item.name}`);\n star.setAttribute('aria-pressed', String(this.favorites.has(item.id)));\n star.innerHTML = createIcon('star', 14);\n star.addEventListener('click', () => this.toggleFavorite(item.id, star));\n row.append(add, star);\n this.markCount(row);\n return row;\n }\n\n private renderTemplates(list: HTMLElement): void {\n const save = document.createElement('button');\n save.type = 'button';\n save.className = 'tcw-ip-add tcw-ip-save';\n save.tabIndex = -1;\n save.dataset.tplSave = '';\n save.innerHTML = `${createIcon('save', 14)}<span class=\"tcw-ip-name\"></span>`;\n save.querySelector('span')!.textContent = this.t('templates.save');\n save.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onSave();\n });\n list.appendChild(save);\n const names = this.content.templates ?? [];\n if (names.length === 0) list.appendChild(this.note(this.t('indicatorPicker.noTemplates')));\n for (const name of names) {\n const row = document.createElement('div');\n row.className = 'tcw-ip-row';\n const apply = document.createElement('button');\n apply.type = 'button';\n apply.className = 'tcw-ip-add';\n apply.tabIndex = -1;\n apply.dataset.tplApply = name;\n apply.innerHTML = `${createIcon('layers', 14)}<span class=\"tcw-ip-name\"></span>`;\n apply.querySelector('span')!.textContent = name;\n apply.addEventListener('click', () => {\n this.close();\n this.callbacks.templates?.onApply(name);\n });\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-ip-aside tcw-ip-remove';\n remove.tabIndex = -1;\n remove.dataset.tplDelete = name;\n const label = fill(this.t('templates.delete'), { name });\n remove.title = label;\n remove.setAttribute('aria-label', label);\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => this.callbacks.templates?.onDelete(name));\n row.append(apply, remove);\n list.appendChild(row);\n }\n }\n\n private add(item: IndicatorPickerItem): void {\n if (this.callbacks.onAdd(item.id) === false || !this.liveEl) return;\n const text = fill(this.t('indicatorPicker.added'), { name: item.name });\n // The same words again (a second RSI) are only read out when they change: a space tells them apart.\n this.liveEl.textContent = this.liveEl.textContent === text ? `${text}\\u00a0` : text;\n }\n\n private toggleFavorite(id: string, star: HTMLButtonElement): void {\n const on = !this.favorites.has(id);\n if (on) this.favorites.add(id);\n else this.favorites.delete(id);\n this.callbacks.onFavorite(id, on);\n star.setAttribute('aria-pressed', String(on));\n if (this.view !== 'favorites' || this.query || on) return;\n // Gone from the favourites: the keyboard moves to the next row's star.\n const stars = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n const at = stars.indexOf(star);\n const hadFocus = document.activeElement === star;\n this.renderList();\n if (!hadFocus) return;\n const left = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-star') ?? [])];\n (left[at] ?? left[left.length - 1] ?? this.searchEl)?.focus();\n }\n\n private markCount(row: HTMLElement): void {\n const n = this.counts.get(row.dataset.ind ?? '') ?? 0;\n const add = row.querySelector('.tcw-ip-add');\n let badge = row.querySelector<HTMLElement>('.tcw-ip-count');\n if (n === 0) {\n badge?.remove();\n return;\n }\n if (!badge) {\n badge = this.tag('tcw-ip-count', '');\n add?.appendChild(badge);\n }\n badge.textContent = String(n);\n badge.title = fill(this.t('indicatorPicker.onChart'), { count: String(n) });\n }\n\n private tag(className: string, text: string): HTMLSpanElement {\n const span = document.createElement('span');\n span.className = className;\n span.textContent = text;\n return span;\n }\n\n private note(text: string): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-ip-empty';\n el.textContent = text;\n return el;\n }\n\n /** One row in the list takes Tab: the one focused last. */\n private rove(target: EventTarget | null): void {\n if (!(target instanceof HTMLElement) || !target.classList.contains('tcw-ip-add')) return;\n this.listEl?.querySelectorAll<HTMLElement>('.tcw-ip-add[tabindex=\"0\"]').forEach((el) => {\n if (el !== target) el.tabIndex = -1;\n });\n target.tabIndex = 0;\n }\n\n private onKeyDown(e: KeyboardEvent, modal: HTMLElement): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.close();\n return;\n }\n keepTabInside(e, modal);\n }\n\n /** Down goes into the list; Enter adds the best match. */\n private onSearchKey(e: KeyboardEvent): void {\n const first = this.listEl?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n if (e.key === 'ArrowDown' && first) {\n e.preventDefault();\n first.focus();\n } else if (e.key === 'Enter' && this.query.trim() && first) {\n e.preventDefault();\n first.click();\n }\n }\n\n /** Up and down through the tabs (Home, End), picking as they go. */\n private onNavKey(e: KeyboardEvent): void {\n const views = this.views();\n const at = views.indexOf(this.view);\n const next = e.key === 'ArrowDown' ? Math.min(views.length - 1, at + 1)\n : e.key === 'ArrowUp' ? Math.max(0, at - 1)\n : e.key === 'Home' ? 0\n : e.key === 'End' ? views.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.select(views[next], true);\n }\n\n /** Up and down through the rows (from the top, back to the search); right to a row's star, left back. */\n private onListKey(e: KeyboardEvent): void {\n const target = e.target as HTMLElement;\n const row = target.closest('.tcw-ip-row, .tcw-ip-save');\n const adds = [...(this.listEl?.querySelectorAll<HTMLButtonElement>('.tcw-ip-add') ?? [])];\n const main = row?.classList.contains('tcw-ip-save') ? row as HTMLButtonElement : row?.querySelector<HTMLButtonElement>('.tcw-ip-add');\n const at = main ? adds.indexOf(main) : -1;\n let to: HTMLElement | null | undefined = null;\n if (e.key === 'ArrowDown') to = adds[at + 1];\n else if (e.key === 'ArrowUp') to = at > 0 ? adds[at - 1] : this.searchEl;\n else if (e.key === 'Home') to = adds[0];\n else if (e.key === 'End') to = adds[adds.length - 1];\n else if (e.key === 'ArrowRight' && target === main) to = row?.querySelector<HTMLButtonElement>('.tcw-ip-aside');\n else if (e.key === 'ArrowLeft' && target !== main) to = main;\n if (!to) return;\n e.preventDefault();\n to.focus();\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`./focusTrap-B8OoGwHb.cjs`),t=require(`./settingsControls-jNn0If4b.cjs`),n=require(`./i18n-DFR1Oukq.cjs`),r=require(`./indicatorInputWords-DzrKBU2I.cjs`),i=require(`./indicatorSettingsTabs-BLEULMJJ.cjs`);let a=require(`@tradecanvas/commons`),o=require(`@tradecanvas/core`);var s=1,c=1.5;function l(e,t){return{visible:!0,lineStyle:e?.find(e=>e.key===t)?.lineStyle??`solid`}}var u=(e,t)=>JSON.stringify(e)===JSON.stringify(t),d=e=>JSON.parse(JSON.stringify(e));function f(e){return e.replace(/_/g,` `).replace(/([a-z0-9])([A-Z])/g,`$1 $2`).replace(/^./,e=>e.toUpperCase())}function p(e,t,n){return Object.fromEntries([...new Set([...Object.keys(e),...Object.keys(t)])].map(e=>[e,{...l(n,e),...t[e]}]))}var m=class{backdrop;modal;bodyEl;titleEl;tabsEl;defaultsBtn;menu=null;callbacks;target=null;draft={};initial=null;look={colors:[],lineWidth:c,plots:{},levels:[],intervals:null};tab=`inputs`;tabButtons=[];returnFocus=null;uid=s++;t;constructor(t,r,i){this.callbacks=r,this.t=i??(e=>n.c[e]),this.backdrop=document.createElement(`div`),this.backdrop.className=`tcw-modal-backdrop`,this.backdrop.hidden=!0;let a=!1;this.backdrop.addEventListener(`pointerdown`,e=>{a=e.target===this.backdrop}),this.backdrop.addEventListener(`click`,e=>{e.target===this.backdrop&&a&&this.finish(!1),a=!1}),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));let o=document.createElement(`div`);o.className=`tcw-modal-header`,this.titleEl=document.createElement(`h3`),this.titleEl.id=`tcw-indi-title-${this.uid}`;let s=document.createElement(`button`);s.type=`button`,s.className=`tcw-modal-close`,s.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),s.innerHTML=e.i(`x`,16),s.addEventListener(`click`,()=>this.finish(!1)),o.append(this.titleEl,s),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(o,this.tabsEl,this.bodyEl,this.buildFooter()),this.backdrop.appendChild(this.modal),t.appendChild(this.backdrop)}buildFooter(){this.defaultsBtn.setAttribute(`aria-haspopup`,`menu`),this.defaultsBtn.setAttribute(`aria-expanded`,`false`),this.defaultsBtn.innerHTML=`<span></span>${e.i(`chevronDown`,12)}`,this.defaultsBtn.querySelector(`span`).textContent=this.t(`indicatorSettings.defaults`);let t=document.createElement(`div`);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`)));let n=document.createElement(`div`);return n.className=`tcw-modal-footer`,n.append(this.defaultsBtn,t),n}open(e){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={colors:[...e.colors??[]],lineWidth:e.lineWidth??c,plots:Object.fromEntries(Object.entries(e.plotStyles??{}).map(([e,t])=>[e,{...t}])),levels:[...e.levels??[]],scale:e.scale,intervals:e.intervals??null},this.initial={params:this.currentParams(),look:d(this.look)},this.tab=`inputs`,this.renderTabs(),this.renderBody(),this.backdrop.hidden=!1;let t=this.tabsEl.hidden?null:this.tabButtons[0]??null;(this.bodyEl.querySelector(`button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])`)??t??this.modal).focus()}close(){this.finish(!0)}isOpen(){return!this.backdrop.hidden}destroy(){this.menu?.destroy(),this.backdrop.remove()}finish(e){if(this.target){if(e){let e=document.activeElement;(e instanceof HTMLInputElement||e instanceof HTMLSelectElement)&&this.modal.contains(e)&&e.dispatchEvent(new Event(`change`))}else this.revert();this.target=null,this.menu?.close(),this.backdrop.hidden=!0,this.callbacks.onClose(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null}}revert(){let e=this.target,t=this.initial;if(!e||!t)return;let n=e.instanceId;u(this.currentParams(),t.params)||this.callbacks.onApply(n,{...t.params});let r=t.look,i={};u(this.look.colors,r.colors)||(i.colors=[...r.colors]),this.look.lineWidth!==r.lineWidth&&(i.lineWidths=[r.lineWidth]),u(this.look.plots,r.plots)||(i.plots=p(this.look.plots,r.plots,e.plots)),Object.keys(i).length>0&&this.callbacks.onStyle?.(n,i),u(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),u(this.look.intervals,r.intervals)||this.callbacks.onIntervals?.(n,r.intervals)}currentParams(){let e=this.target;return e?{...e.defaults,...e.params,...this.draft}:{}}tabs(){let e=this.target;if(!e)return[];let t=!!(e.plots?.length&&e.colors&&this.callbacks.onStyle),n=!!(e.levels&&this.callbacks.onLevels);return[`inputs`,...t||n?[`style`]:[],...this.callbacks.onIntervals?[`visibility`]:[]]}renderTabs(){let e=this.tabs();this.tabsEl.hidden=e.length<2,this.tabButtons=e.map(e=>{let t=document.createElement(`button`);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}),this.tabsEl.replaceChildren(...this.tabButtons),this.markTabs()}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.tab;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.tab=e,this.markTabs(),this.renderBody(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){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;a<0||t.length===0||(e.preventDefault(),this.selectTab(t[a],!0))}onKeyDown(t){if(t.key===`Escape`){t.preventDefault(),t.stopPropagation(),this.finish(!1);return}e.n(t,this.modal)}renderBody(){this.bodyEl.replaceChildren();let e=this.target;e&&(this.tab===`style`?this.renderStyle(e):this.tab===`visibility`?i.n(this.bodyEl,this.look.intervals,e=>this.setIntervals(e),this.t):this.renderInputs())}currentValue(e){if(e in this.draft)return this.draft[e];let t=this.target?.params[e],n=this.target?.defaults[e];return t??n}setValue(e,t){this.target&&(this.draft[e]=t,this.callbacks.onApply(this.target.instanceId,{...this.draft}))}renderInputs(){let e=document.createElement(`div`);e.className=`tcw-settings-section`;let t=(0,o.describeInputs)({defaultConfig:this.target.defaults,inputs:this.target.inputs});t.length===0&&e.appendChild(this.note(this.t(`indicatorSettings.noInputs`)));for(let n of t)e.appendChild(this.fieldRow(n));this.bodyEl.appendChild(e)}fieldRow(e){let n=e.name,i=this.currentValue(n),a;switch(e.kind){case`source`:a=this.sourceControl(n,String(i??`close`));break;case`choice`:a=this.choiceControl(n,e.options??[],i);break;case`boolean`:a=t.u(!!i,e=>this.setValue(n,e));break;case`int`:case`number`:a=t.i(Number(i),{min:e.min,max:e.max,step:e.step,whole:e.kind===`int`},e=>this.setValue(n,e));break;case`color`:a=t.r(String(i),e=>this.setValue(n,e));break;default:a=this.textControl(n,String(i??``))}return t.s(e.label??r.n(n,this.t)??f(n),a)}sourceControl(e,t){let n=document.createElement(`select`);n.className=`tcw-indi-input tcw-indi-select`;let r=document.createElement(`optgroup`);r.label=this.t(`indicatorSettings.source.prices`);for(let e of a.PRICE_SOURCES)r.appendChild(this.option(e,e));n.appendChild(r);let i=this.target?.lineSources??[];if(i.length){let e=document.createElement(`optgroup`);e.label=this.t(`indicatorSettings.source.indicators`);for(let t of i)e.appendChild(this.option(t.value,t.label));n.appendChild(e)}return n.value=t,n.value!==t&&(n.value=`close`),n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}choiceControl(e,t,n){let i=document.createElement(`select`);i.className=`tcw-indi-input tcw-indi-select`;for(let n of t)i.appendChild(this.option(String(n),r.t(e,n,this.t)??String(n)));return i.value=String(n),i.addEventListener(`change`,()=>{let n=t.find(e=>String(e)===i.value);n!==void 0&&this.setValue(e,n)}),i}textControl(e,t){let n=document.createElement(`input`);return n.type=`text`,n.className=`tcw-indi-input`,n.value=t,n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}renderStyle(e){let t=e.instanceId,n=()=>this.target===e,r=e=>n()&&this.callbacks.onStyle?.(t,e),a=e=>n()&&this.callbacks.onLevels?.(t,e),o=e=>n()&&this.callbacks.onScale?.(t,e);i.t(this.bodyEl,{plots:e.colors&&this.callbacks.onStyle?e.plots??[]:[],colors:this.look.colors,plotStyles:this.look.plots,lineWidth:this.look.lineWidth,scale:e.scale&&this.callbacks.onScale?this.look.scale:void 0,levels:e.levels&&this.callbacks.onLevels?this.look.levels:void 0},{color:(e,t)=>{if(!n())return;let i=[...this.look.colors];for(;i.length<=e;)i.push(i[0]??t);i[e]=t,this.look.colors=i,r({colors:[...i]})},plot:(e,t)=>{n()&&(this.look.plots={...this.look.plots,[e]:{...this.look.plots[e],...t}},r({plots:{[e]:t}}))},lineWidth:e=>{n()&&(this.look.lineWidth=e,r({lineWidths:[e]}))},scale:e=>{n()&&(this.look.scale=e,o(e))},levels:e=>{n()&&(this.look.levels=e,a([...e]))}},this.t,this.uid)}setIntervals(e){this.target&&(this.look.intervals=e,this.callbacks.onIntervals?.(this.target.instanceId,e))}openDefaults(){if(this.menu?.isOpen()){this.menu.close();return}this.menu??=new t.f(this.modal,this.t(`indicatorSettings.defaults`));let e=this.defaultsBtn.getBoundingClientRect(),n=this.modal.getBoundingClientRect(),r=[{id:`reset`,label:this.t(`indicatorSettings.resetSettings`),icon:`reset`},...this.callbacks.onSaveDefault?[{id:`save`,label:this.t(`indicatorSettings.saveAsDefault`),icon:`save`}]:[]];this.menu.open(r,e.left-n.left,e.top-n.top,e=>{e===`reset`?this.resetAll():this.saveDefault()},this.defaultsBtn)}resetAll(){let e=this.target;if(!e)return;let t=e.instanceId,n={...e.defaults};this.draft={...n},this.callbacks.onApply(t,{...n}),Object.keys(this.look.plots).length>0&&(this.callbacks.onStyle?.(t,{plots:p(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()}saveDefault(){let e=this.target;if(!e)return;let t=Object.fromEntries(Object.entries(this.currentParams()).filter(([t,n])=>!u(n,e.defaults[t])&&!(typeof n==`string`&&(0,a.parseIndicatorSource)(n)))),n={};(0,o.isSeriesPalette)(this.look.colors)||(n.colors=[...this.look.colors]),this.look.lineWidth!==c&&(n.lineWidths=[this.look.lineWidth]),Object.keys(this.look.plots).length>0&&(n.plots=d(this.look.plots)),this.callbacks.onSaveDefault?.(e.indicatorId,{params:t,style:n})}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}note(e){let t=document.createElement(`div`);return t.className=`tcw-settings-label`,t.textContent=e,t}option(e,t){let n=document.createElement(`option`);return n.value=e,n.textContent=t,n}};exports.WidgetIndicatorSettings=m;
2
- //# sourceMappingURL=WidgetIndicatorSettings-BJUjACzj.cjs.map
1
+ const e=require(`./focusTrap-DrhhmG0V.cjs`),t=require(`./settingsControls-Tn8_vJJv.cjs`),n=require(`./i18n-C2nQBZTY.cjs`),r=require(`./indicatorInputWords-CTA4IcJR.cjs`),i=require(`./indicatorSettingsTabs-B0liE3ST.cjs`);let a=require(`@tradecanvas/commons`),o=require(`@tradecanvas/core`);var s=1,c=1.5;function l(e,t){return{visible:!0,lineStyle:e?.find(e=>e.key===t)?.lineStyle??`solid`}}var u=(e,t)=>JSON.stringify(e)===JSON.stringify(t),d=e=>JSON.parse(JSON.stringify(e));function f(e){return e.replace(/_/g,` `).replace(/([a-z0-9])([A-Z])/g,`$1 $2`).replace(/^./,e=>e.toUpperCase())}function p(e,t,n){return Object.fromEntries([...new Set([...Object.keys(e),...Object.keys(t)])].map(e=>[e,{...l(n,e),...t[e]}]))}var m=class{backdrop;modal;bodyEl;titleEl;tabsEl;defaultsBtn;menu=null;callbacks;target=null;draft={};initial=null;look={colors:[],lineWidth:c,plots:{},levels:[],intervals:null};tab=`inputs`;tabButtons=[];returnFocus=null;uid=s++;t;constructor(t,r,i){this.callbacks=r,this.t=i??(e=>n.c[e]),this.backdrop=document.createElement(`div`),this.backdrop.className=`tcw-modal-backdrop`,this.backdrop.hidden=!0;let a=!1;this.backdrop.addEventListener(`pointerdown`,e=>{a=e.target===this.backdrop}),this.backdrop.addEventListener(`click`,e=>{e.target===this.backdrop&&a&&this.finish(!1),a=!1}),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));let o=document.createElement(`div`);o.className=`tcw-modal-header`,this.titleEl=document.createElement(`h3`),this.titleEl.id=`tcw-indi-title-${this.uid}`;let s=document.createElement(`button`);s.type=`button`,s.className=`tcw-modal-close`,s.setAttribute(`aria-label`,this.t(`indicatorSettings.close`)),s.innerHTML=e.i(`x`,16),s.addEventListener(`click`,()=>this.finish(!1)),o.append(this.titleEl,s),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(o,this.tabsEl,this.bodyEl,this.buildFooter()),this.backdrop.appendChild(this.modal),t.appendChild(this.backdrop)}buildFooter(){this.defaultsBtn.setAttribute(`aria-haspopup`,`menu`),this.defaultsBtn.setAttribute(`aria-expanded`,`false`),this.defaultsBtn.innerHTML=`<span></span>${e.i(`chevronDown`,12)}`,this.defaultsBtn.querySelector(`span`).textContent=this.t(`indicatorSettings.defaults`);let t=document.createElement(`div`);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`)));let n=document.createElement(`div`);return n.className=`tcw-modal-footer`,n.append(this.defaultsBtn,t),n}open(e){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={colors:[...e.colors??[]],lineWidth:e.lineWidth??c,plots:Object.fromEntries(Object.entries(e.plotStyles??{}).map(([e,t])=>[e,{...t}])),levels:[...e.levels??[]],scale:e.scale,intervals:e.intervals??null},this.initial={params:this.currentParams(),look:d(this.look)},this.tab=`inputs`,this.renderTabs(),this.renderBody(),this.backdrop.hidden=!1;let t=this.tabsEl.hidden?null:this.tabButtons[0]??null;(this.bodyEl.querySelector(`button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])`)??t??this.modal).focus()}close(){this.finish(!0)}isOpen(){return!this.backdrop.hidden}destroy(){this.menu?.destroy(),this.backdrop.remove()}finish(e){if(this.target){if(e){let e=document.activeElement;(e instanceof HTMLInputElement||e instanceof HTMLSelectElement)&&this.modal.contains(e)&&e.dispatchEvent(new Event(`change`))}else this.revert();this.target=null,this.menu?.close(),this.backdrop.hidden=!0,this.callbacks.onClose(),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null}}revert(){let e=this.target,t=this.initial;if(!e||!t)return;let n=e.instanceId;u(this.currentParams(),t.params)||this.callbacks.onApply(n,{...t.params});let r=t.look,i={};u(this.look.colors,r.colors)||(i.colors=[...r.colors]),this.look.lineWidth!==r.lineWidth&&(i.lineWidths=[r.lineWidth]),u(this.look.plots,r.plots)||(i.plots=p(this.look.plots,r.plots,e.plots)),Object.keys(i).length>0&&this.callbacks.onStyle?.(n,i),u(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),u(this.look.intervals,r.intervals)||this.callbacks.onIntervals?.(n,r.intervals)}currentParams(){let e=this.target;return e?{...e.defaults,...e.params,...this.draft}:{}}tabs(){let e=this.target;if(!e)return[];let t=!!(e.plots?.length&&e.colors&&this.callbacks.onStyle),n=!!(e.levels&&this.callbacks.onLevels);return[`inputs`,...t||n?[`style`]:[],...this.callbacks.onIntervals?[`visibility`]:[]]}renderTabs(){let e=this.tabs();this.tabsEl.hidden=e.length<2,this.tabButtons=e.map(e=>{let t=document.createElement(`button`);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}),this.tabsEl.replaceChildren(...this.tabButtons),this.markTabs()}markTabs(){for(let e of this.tabButtons){let t=e.dataset.tab===this.tab;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.tab=e,this.markTabs(),this.renderBody(),t&&this.tabButtons.find(t=>t.dataset.tab===e)?.focus()}onTabKey(e){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;a<0||t.length===0||(e.preventDefault(),this.selectTab(t[a],!0))}onKeyDown(t){if(t.key===`Escape`){t.preventDefault(),t.stopPropagation(),this.finish(!1);return}e.n(t,this.modal)}renderBody(){this.bodyEl.replaceChildren();let e=this.target;e&&(this.tab===`style`?this.renderStyle(e):this.tab===`visibility`?i.n(this.bodyEl,this.look.intervals,e=>this.setIntervals(e),this.t):this.renderInputs())}currentValue(e){if(e in this.draft)return this.draft[e];let t=this.target?.params[e],n=this.target?.defaults[e];return t??n}setValue(e,t){this.target&&(this.draft[e]=t,this.callbacks.onApply(this.target.instanceId,{...this.draft}))}renderInputs(){let e=document.createElement(`div`);e.className=`tcw-settings-section`;let t=(0,o.describeInputs)({defaultConfig:this.target.defaults,inputs:this.target.inputs});t.length===0&&e.appendChild(this.note(this.t(`indicatorSettings.noInputs`)));for(let n of t)e.appendChild(this.fieldRow(n));this.bodyEl.appendChild(e)}fieldRow(e){let n=e.name,i=this.currentValue(n),a;switch(e.kind){case`source`:a=this.sourceControl(n,String(i??`close`));break;case`choice`:a=this.choiceControl(n,e.options??[],i);break;case`boolean`:a=t.u(!!i,e=>this.setValue(n,e));break;case`int`:case`number`:a=t.i(Number(i),{min:e.min,max:e.max,step:e.step,whole:e.kind===`int`},e=>this.setValue(n,e));break;case`color`:a=t.r(String(i),e=>this.setValue(n,e));break;default:a=this.textControl(n,String(i??``))}return t.s(e.label??r.n(n,this.t)??f(n),a)}sourceControl(e,t){let n=document.createElement(`select`);n.className=`tcw-indi-input tcw-indi-select`;let r=document.createElement(`optgroup`);r.label=this.t(`indicatorSettings.source.prices`);for(let e of a.PRICE_SOURCES)r.appendChild(this.option(e,e));n.appendChild(r);let i=this.target?.lineSources??[];if(i.length){let e=document.createElement(`optgroup`);e.label=this.t(`indicatorSettings.source.indicators`);for(let t of i)e.appendChild(this.option(t.value,t.label));n.appendChild(e)}return n.value=t,n.value!==t&&(n.value=`close`),n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}choiceControl(e,t,n){let i=document.createElement(`select`);i.className=`tcw-indi-input tcw-indi-select`;for(let n of t)i.appendChild(this.option(String(n),r.t(e,n,this.t)??String(n)));return i.value=String(n),i.addEventListener(`change`,()=>{let n=t.find(e=>String(e)===i.value);n!==void 0&&this.setValue(e,n)}),i}textControl(e,t){let n=document.createElement(`input`);return n.type=`text`,n.className=`tcw-indi-input`,n.value=t,n.addEventListener(`change`,()=>this.setValue(e,n.value)),n}renderStyle(e){let t=e.instanceId,n=()=>this.target===e,r=e=>n()&&this.callbacks.onStyle?.(t,e),a=e=>n()&&this.callbacks.onLevels?.(t,e),o=e=>n()&&this.callbacks.onScale?.(t,e);i.t(this.bodyEl,{plots:e.colors&&this.callbacks.onStyle?e.plots??[]:[],colors:this.look.colors,plotStyles:this.look.plots,lineWidth:this.look.lineWidth,scale:e.scale&&this.callbacks.onScale?this.look.scale:void 0,levels:e.levels&&this.callbacks.onLevels?this.look.levels:void 0},{color:(e,t)=>{if(!n())return;let i=[...this.look.colors];for(;i.length<=e;)i.push(i[0]??t);i[e]=t,this.look.colors=i,r({colors:[...i]})},plot:(e,t)=>{n()&&(this.look.plots={...this.look.plots,[e]:{...this.look.plots[e],...t}},r({plots:{[e]:t}}))},lineWidth:e=>{n()&&(this.look.lineWidth=e,r({lineWidths:[e]}))},scale:e=>{n()&&(this.look.scale=e,o(e))},levels:e=>{n()&&(this.look.levels=e,a([...e]))}},this.t,this.uid)}setIntervals(e){this.target&&(this.look.intervals=e,this.callbacks.onIntervals?.(this.target.instanceId,e))}openDefaults(){if(this.menu?.isOpen()){this.menu.close();return}this.menu??=new t.f(this.modal,this.t(`indicatorSettings.defaults`));let e=this.defaultsBtn.getBoundingClientRect(),n=this.modal.getBoundingClientRect(),r=[{id:`reset`,label:this.t(`indicatorSettings.resetSettings`),icon:`reset`},...this.callbacks.onSaveDefault?[{id:`save`,label:this.t(`indicatorSettings.saveAsDefault`),icon:`save`}]:[]];this.menu.open(r,e.left-n.left,e.top-n.top,e=>{e===`reset`?this.resetAll():this.saveDefault()},this.defaultsBtn)}resetAll(){let e=this.target;if(!e)return;let t=e.instanceId,n={...e.defaults};this.draft={...n},this.callbacks.onApply(t,{...n}),Object.keys(this.look.plots).length>0&&(this.callbacks.onStyle?.(t,{plots:p(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()}saveDefault(){let e=this.target;if(!e)return;let t=Object.fromEntries(Object.entries(this.currentParams()).filter(([t,n])=>!u(n,e.defaults[t])&&!(typeof n==`string`&&(0,a.parseIndicatorSource)(n)))),n={};(0,o.isSeriesPalette)(this.look.colors)||(n.colors=[...this.look.colors]),this.look.lineWidth!==c&&(n.lineWidths=[this.look.lineWidth]),Object.keys(this.look.plots).length>0&&(n.plots=d(this.look.plots)),this.callbacks.onSaveDefault?.(e.indicatorId,{params:t,style:n})}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}note(e){let t=document.createElement(`div`);return t.className=`tcw-settings-label`,t.textContent=e,t}option(e,t){let n=document.createElement(`option`);return n.value=e,n.textContent=t,n}};exports.WidgetIndicatorSettings=m;
2
+ //# sourceMappingURL=WidgetIndicatorSettings-C7vpco6e.cjs.map