@tradecanvas/chart 1.71.0 → 1.72.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/dist/LayoutSession-DO4bYkUi.cjs +2 -0
  2. package/dist/LayoutSession-DO4bYkUi.cjs.map +1 -0
  3. package/dist/{LayoutSession-BOQCN6yY.js → LayoutSession-qRYG8GTD.js} +539 -552
  4. package/dist/LayoutSession-qRYG8GTD.js.map +1 -0
  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-BeW8fX7r.js +474 -0
  10. package/dist/WidgetDrawingSettings-BeW8fX7r.js.map +1 -0
  11. package/dist/WidgetDrawingSettings-Cyl2u_u8.cjs +2 -0
  12. package/dist/WidgetDrawingSettings-Cyl2u_u8.cjs.map +1 -0
  13. package/dist/WidgetGoToDate-BNdngHlg.cjs +2 -0
  14. package/dist/WidgetGoToDate-BNdngHlg.cjs.map +1 -0
  15. package/dist/WidgetGoToDate-hcDtezRm.js +191 -0
  16. package/dist/WidgetGoToDate-hcDtezRm.js.map +1 -0
  17. package/dist/WidgetHotkeySheet-1lRFSdvt.js +416 -0
  18. package/dist/WidgetHotkeySheet-1lRFSdvt.js.map +1 -0
  19. package/dist/WidgetHotkeySheet-y5LWKSG6.cjs +2 -0
  20. package/dist/WidgetHotkeySheet-y5LWKSG6.cjs.map +1 -0
  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-C7vpco6e.cjs +2 -0
  26. package/dist/WidgetIndicatorSettings-C7vpco6e.cjs.map +1 -0
  27. package/dist/WidgetIndicatorSettings-dDkqzzYo.js +326 -0
  28. package/dist/WidgetIndicatorSettings-dDkqzzYo.js.map +1 -0
  29. package/dist/WidgetSettings-D7Pq42zW.cjs +2 -0
  30. package/dist/WidgetSettings-D7Pq42zW.cjs.map +1 -0
  31. package/dist/WidgetSettings-eOo93L7Z.js +546 -0
  32. package/dist/WidgetSettings-eOo93L7Z.js.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-Br9Zfje0.js +27 -0
  38. package/dist/focusTrap-Br9Zfje0.js.map +1 -0
  39. package/dist/focusTrap-DrhhmG0V.cjs +2 -0
  40. package/dist/focusTrap-DrhhmG0V.cjs.map +1 -0
  41. package/dist/i18n-C2nQBZTY.cjs +2 -0
  42. package/dist/i18n-C2nQBZTY.cjs.map +1 -0
  43. package/dist/{i18n-DUmd4Z_r.js → i18n-C5v6Tzp1.js} +3 -1
  44. package/dist/i18n-C5v6Tzp1.js.map +1 -0
  45. package/dist/index.cjs +1 -1
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -1
  49. package/dist/indicatorInputWords-Bd9Pt6qp.js +25 -0
  50. package/dist/indicatorInputWords-Bd9Pt6qp.js.map +1 -0
  51. package/dist/indicatorInputWords-CTA4IcJR.cjs +2 -0
  52. package/dist/indicatorInputWords-CTA4IcJR.cjs.map +1 -0
  53. package/dist/indicatorLabel-BNZvaOcl.cjs +2 -0
  54. package/dist/indicatorLabel-BNZvaOcl.cjs.map +1 -0
  55. package/dist/indicatorLabel-Sbegyhp2.js +17 -0
  56. package/dist/indicatorLabel-Sbegyhp2.js.map +1 -0
  57. package/dist/indicatorSettingsTabs-B0liE3ST.cjs +2 -0
  58. package/dist/indicatorSettingsTabs-B0liE3ST.cjs.map +1 -0
  59. package/dist/indicatorSettingsTabs-XcgMdbLp.js +161 -0
  60. package/dist/indicatorSettingsTabs-XcgMdbLp.js.map +1 -0
  61. package/dist/monthView-B-CieFDJ.cjs +2 -0
  62. package/dist/monthView-B-CieFDJ.cjs.map +1 -0
  63. package/dist/monthView-N8yDaTqZ.js +198 -0
  64. package/dist/monthView-N8yDaTqZ.js.map +1 -0
  65. package/dist/platformKeys-B82-j1Fs.cjs +2 -0
  66. package/dist/platformKeys-B82-j1Fs.cjs.map +1 -0
  67. package/dist/platformKeys-Dd1qOCjF.js +5 -0
  68. package/dist/platformKeys-Dd1qOCjF.js.map +1 -0
  69. package/dist/settingsControls-DIjzCFHI.js +379 -0
  70. package/dist/settingsControls-DIjzCFHI.js.map +1 -0
  71. package/dist/settingsControls-Tn8_vJJv.cjs +2 -0
  72. package/dist/settingsControls-Tn8_vJJv.cjs.map +1 -0
  73. package/dist/symbolCatalog-CA1fpz3s.js +90 -0
  74. package/dist/symbolCatalog-CA1fpz3s.js.map +1 -0
  75. package/dist/symbolCatalog-Dx1YnZgm.cjs +2 -0
  76. package/dist/symbolCatalog-Dx1YnZgm.cjs.map +1 -0
  77. package/dist/widget/ChartWidget.d.ts +102 -582
  78. package/dist/widget/ChartWidget.d.ts.map +1 -1
  79. package/dist/widget/WidgetAlerts.d.ts +35 -0
  80. package/dist/widget/WidgetAlerts.d.ts.map +1 -0
  81. package/dist/widget/WidgetChartSettings.d.ts +194 -0
  82. package/dist/widget/WidgetChartSettings.d.ts.map +1 -0
  83. package/dist/widget/WidgetCommands.d.ts +54 -0
  84. package/dist/widget/WidgetCommands.d.ts.map +1 -0
  85. package/dist/widget/WidgetCompares.d.ts +140 -0
  86. package/dist/widget/WidgetCompares.d.ts.map +1 -0
  87. package/dist/widget/WidgetDockArea.d.ts +89 -0
  88. package/dist/widget/WidgetDockArea.d.ts.map +1 -0
  89. package/dist/widget/WidgetDrawingEdit.d.ts +73 -0
  90. package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -0
  91. package/dist/widget/WidgetGoToDate.d.ts +1 -14
  92. package/dist/widget/WidgetGoToDate.d.ts.map +1 -1
  93. package/dist/widget/WidgetIndicatorSettings.d.ts.map +1 -1
  94. package/dist/widget/WidgetIntervals.d.ts +66 -0
  95. package/dist/widget/WidgetIntervals.d.ts.map +1 -0
  96. package/dist/widget/WidgetLayouts.d.ts +85 -0
  97. package/dist/widget/WidgetLayouts.d.ts.map +1 -0
  98. package/dist/widget/WidgetLegendView.d.ts +54 -0
  99. package/dist/widget/WidgetLegendView.d.ts.map +1 -0
  100. package/dist/widget/WidgetMenus.d.ts +79 -0
  101. package/dist/widget/WidgetMenus.d.ts.map +1 -0
  102. package/dist/widget/WidgetReplay.d.ts +199 -0
  103. package/dist/widget/WidgetReplay.d.ts.map +1 -0
  104. package/dist/widget/WidgetTrading.d.ts +48 -0
  105. package/dist/widget/WidgetTrading.d.ts.map +1 -0
  106. package/dist/widget/WidgetWatchlists.d.ts +77 -0
  107. package/dist/widget/WidgetWatchlists.d.ts.map +1 -0
  108. package/dist/widget/drawingSettingsTabs.d.ts +2 -19
  109. package/dist/widget/drawingSettingsTabs.d.ts.map +1 -1
  110. package/dist/widget/drawingText.d.ts +24 -0
  111. package/dist/widget/drawingText.d.ts.map +1 -0
  112. package/dist/widget/index.cjs +22 -23
  113. package/dist/widget/index.cjs.map +1 -1
  114. package/dist/widget/index.js +6193 -9868
  115. package/dist/widget/index.js.map +1 -1
  116. package/dist/widget/keyTarget.d.ts +4 -0
  117. package/dist/widget/keyTarget.d.ts.map +1 -1
  118. package/dist/widget/lazyPart.d.ts +47 -0
  119. package/dist/widget/lazyPart.d.ts.map +1 -0
  120. package/dist/widget/locales/ar.d.ts.map +1 -1
  121. package/dist/widget/locales/cs.d.ts.map +1 -1
  122. package/dist/widget/locales/da.d.ts.map +1 -1
  123. package/dist/widget/locales/de.d.ts.map +1 -1
  124. package/dist/widget/locales/el.d.ts.map +1 -1
  125. package/dist/widget/locales/en.d.ts +1 -0
  126. package/dist/widget/locales/en.d.ts.map +1 -1
  127. package/dist/widget/locales/es.d.ts.map +1 -1
  128. package/dist/widget/locales/et.d.ts.map +1 -1
  129. package/dist/widget/locales/fa.d.ts.map +1 -1
  130. package/dist/widget/locales/fr.d.ts.map +1 -1
  131. package/dist/widget/locales/he.d.ts.map +1 -1
  132. package/dist/widget/locales/hu.d.ts.map +1 -1
  133. package/dist/widget/locales/id.d.ts.map +1 -1
  134. package/dist/widget/locales/index.cjs +1 -1
  135. package/dist/widget/locales/index.cjs.map +1 -1
  136. package/dist/widget/locales/index.js +29 -1
  137. package/dist/widget/locales/index.js.map +1 -1
  138. package/dist/widget/locales/it.d.ts.map +1 -1
  139. package/dist/widget/locales/ja.d.ts.map +1 -1
  140. package/dist/widget/locales/ko.d.ts.map +1 -1
  141. package/dist/widget/locales/ms.d.ts.map +1 -1
  142. package/dist/widget/locales/nb.d.ts.map +1 -1
  143. package/dist/widget/locales/nl.d.ts.map +1 -1
  144. package/dist/widget/locales/pl.d.ts.map +1 -1
  145. package/dist/widget/locales/pt.d.ts.map +1 -1
  146. package/dist/widget/locales/ro.d.ts.map +1 -1
  147. package/dist/widget/locales/ru.d.ts.map +1 -1
  148. package/dist/widget/locales/sk.d.ts.map +1 -1
  149. package/dist/widget/locales/sv.d.ts.map +1 -1
  150. package/dist/widget/locales/th.d.ts.map +1 -1
  151. package/dist/widget/locales/tr.d.ts.map +1 -1
  152. package/dist/widget/locales/vi.d.ts.map +1 -1
  153. package/dist/widget/locales/zh.d.ts.map +1 -1
  154. package/dist/widget/locales/zhHant.d.ts.map +1 -1
  155. package/dist/widget/pageEnv.d.ts +5 -0
  156. package/dist/widget/pageEnv.d.ts.map +1 -0
  157. package/dist/widget/signals.d.ts +41 -0
  158. package/dist/widget/signals.d.ts.map +1 -0
  159. package/dist/widget/wallTime.d.ts +15 -0
  160. package/dist/widget/wallTime.d.ts.map +1 -0
  161. package/dist/widget/widgetDialogs.d.ts +17 -0
  162. package/dist/widget/widgetDialogs.d.ts.map +1 -0
  163. package/dist/widgetFeatures-BM6u0u74.js +186 -0
  164. package/dist/widgetFeatures-BM6u0u74.js.map +1 -0
  165. package/dist/widgetFeatures-BZ8OVPN2.cjs +3 -0
  166. package/dist/widgetFeatures-BZ8OVPN2.cjs.map +1 -0
  167. package/dist/widgetLocales-BPK5i0Ok.cjs +2 -0
  168. package/dist/widgetLocales-BPK5i0Ok.cjs.map +1 -0
  169. package/dist/widgetLocales-DgTR8JSu.js +838 -0
  170. package/dist/widgetLocales-DgTR8JSu.js.map +1 -0
  171. package/package.json +3 -3
  172. package/dist/LayoutSession-BOQCN6yY.js.map +0 -1
  173. package/dist/LayoutSession-RF8OqOXy.cjs +0 -2
  174. package/dist/LayoutSession-RF8OqOXy.cjs.map +0 -1
  175. package/dist/i18n-BsstZ9md.cjs +0 -2
  176. package/dist/i18n-BsstZ9md.cjs.map +0 -1
  177. package/dist/i18n-DUmd4Z_r.js.map +0 -1
@@ -0,0 +1,474 @@
1
+ import { i as e, n as t } from "./focusTrap-Br9Zfje0.js";
2
+ import { d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u } from "./settingsControls-DIjzCFHI.js";
3
+ import { r as d, t as f } from "./i18n-C5v6Tzp1.js";
4
+ import { n as p } from "./indicatorSettingsTabs-XcgMdbLp.js";
5
+ import { sanitizeDrawingOptions as m } from "@tradecanvas/commons";
6
+ //#region src/widget/drawingSettingsTabs.ts
7
+ var h = [
8
+ 1,
9
+ 2,
10
+ 3,
11
+ 4
12
+ ], g = [
13
+ "solid",
14
+ "dashed",
15
+ "dotted"
16
+ ], _ = [
17
+ 10,
18
+ 11,
19
+ 12,
20
+ 13,
21
+ 14,
22
+ 16,
23
+ 18,
24
+ 20,
25
+ 24,
26
+ 28,
27
+ 32
28
+ ], v = 12, y = 48, b = .5, x = .1, S = ["leftEnd", "rightEnd"];
29
+ function C(e, t, n) {
30
+ let r = `drawingOption.${t}`, i = e(r);
31
+ return i === r ? n : i;
32
+ }
33
+ function w() {
34
+ let e = document.createElement("div");
35
+ return e.className = "tcw-settings-section", e;
36
+ }
37
+ function T(e, ...t) {
38
+ let n = document.createElement("div");
39
+ n.className = "tcw-settings-row";
40
+ let r = document.createElement("span");
41
+ r.className = "tcw-settings-label", r.textContent = e;
42
+ let i = document.createElement("span");
43
+ return i.className = "tcw-indi-plot-controls", i.append(...t), n.append(r, i), n;
44
+ }
45
+ function E(e, t) {
46
+ return e.setAttribute("aria-label", t), e;
47
+ }
48
+ function D(e, t, n, r) {
49
+ let i = [], a = w();
50
+ a.appendChild(O(t.style(), n, i, r));
51
+ let o = S.filter((e) => t.defs[e]?.kind === "choice");
52
+ o.length > 0 && a.appendChild(T(r("drawingSettings.lineEnds"), ...o.map((e) => E(s([{
53
+ value: "normal",
54
+ label: r("drawingSettings.lineEnd.normal")
55
+ }, {
56
+ value: "arrow",
57
+ label: r("drawingSettings.lineEnd.arrow")
58
+ }], String(t.options()[e] ?? "normal"), (t) => n.options({ [e]: t })), C(r, e, t.defs[e].label))))), t.fill && a.appendChild(k(t.style(), n, i, r)), e.appendChild(a);
59
+ let c = Object.entries(t.defs).filter(([e, t]) => t.kind !== "levels" && !o.includes(e));
60
+ if (c.length > 0) {
61
+ let i = w();
62
+ for (let [e, a] of c) i.appendChild(A(e, a, t.options(), n, r));
63
+ e.appendChild(i);
64
+ }
65
+ for (let [a, o] of Object.entries(t.defs)) o.kind === "levels" && e.appendChild(j(a, o, t, n, i, r));
66
+ }
67
+ function O(e, t, n, r) {
68
+ let i = E(c(e.color, (e) => {
69
+ t.style({ color: e });
70
+ for (let t of n) t(e);
71
+ }), r("drawingSettings.color")), a = E(s(h.map((e) => ({
72
+ value: String(e),
73
+ label: `${e} px`
74
+ })), String(e.lineWidth), (e) => t.style({ lineWidth: Number(e) })), r("drawingSettings.lineWidth")), o = E(s(g.map((e) => ({
75
+ value: e,
76
+ label: r(`drawingSettings.lineStyle.${e}`)
77
+ })), e.lineStyle, (e) => t.style({ lineStyle: e })), r("drawingSettings.lineStyle"));
78
+ return T(r("drawingSettings.line"), i, a, o);
79
+ }
80
+ function k(e, t, r, s) {
81
+ let l = e.fillColor ?? n(e.color, x), u = a(l), d = o(l), f = e.fillColor !== void 0, p = () => {
82
+ f = !0, t.style({
83
+ fillColor: n(u, d),
84
+ fillOpacity: d
85
+ });
86
+ }, m = E(c(u, (e) => {
87
+ u = e, p();
88
+ }), s("drawingSettings.fill"));
89
+ r.push((e) => {
90
+ f || (u = a(e), m.value = u);
91
+ });
92
+ let h = E(i(Math.round(d * 100), {
93
+ min: 0,
94
+ max: 100,
95
+ step: 5
96
+ }, (e) => {
97
+ d = e / 100, p();
98
+ }), s("drawingSettings.opacity")), g = document.createElement("span");
99
+ return g.className = "tcw-unit", g.setAttribute("aria-hidden", "true"), g.textContent = "%", T(s("drawingSettings.fill"), m, h, g);
100
+ }
101
+ function A(e, t, n, r, a) {
102
+ let o = C(a, e, t.label), c = n[e];
103
+ switch (t.kind) {
104
+ case "boolean": return u(o, !!c, (t) => r.options({ [e]: t }));
105
+ case "number": return l(o, i(Number(c), {
106
+ min: t.min,
107
+ max: t.max,
108
+ step: t.step
109
+ }, (t) => r.options({ [e]: t })));
110
+ case "choice": return l(o, s(t.choices.map((t) => ({
111
+ value: t.value,
112
+ label: C(a, `${e}.${t.value}`, t.label)
113
+ })), String(c), (t) => r.options({ [e]: t })));
114
+ case "text": {
115
+ let t = document.createElement("input");
116
+ return t.type = "text", t.className = "tcw-indi-input", t.value = String(c ?? ""), t.addEventListener("change", () => r.options({ [e]: t.value })), l(o, t);
117
+ }
118
+ }
119
+ }
120
+ function j(t, n, r, o, s, l) {
121
+ let u = document.createElement("div");
122
+ u.className = "tcw-settings-section tcw-levels-editor";
123
+ let d = document.createElement("div");
124
+ d.className = "tcw-settings-section-title", d.textContent = C(l, t, n.label), u.appendChild(d);
125
+ let f = () => {
126
+ let e = r.options()[t];
127
+ return Array.isArray(e) ? e : [];
128
+ }, p = (e) => o.options({ [t]: e }), m = document.createElement("div");
129
+ m.className = "tcw-levels-grid", f().forEach((t, n) => {
130
+ let u = document.createElement("div");
131
+ u.className = "tcw-level-row";
132
+ let d = ` ${n + 1}`, h = document.createElement("input");
133
+ h.type = "checkbox", h.checked = t.visible, h.setAttribute("aria-label", l("drawingSettings.levelVisible") + d), h.addEventListener("change", () => p(f().map((e, t) => t === n ? {
134
+ ...e,
135
+ visible: h.checked
136
+ } : e)));
137
+ let g = E(i(t.value, { step: .001 }, (e) => p(f().map((t, r) => r === n ? {
138
+ ...t,
139
+ value: e
140
+ } : t))), l("drawingSettings.levelValue") + d), _ = E(c(t.color ?? r.style().color, (e) => p(f().map((t, r) => r === n ? {
141
+ ...t,
142
+ color: e
143
+ } : t))), l("drawingSettings.levelColor") + d);
144
+ s.push((e) => {
145
+ f()[n]?.color === void 0 && (_.value = a(e));
146
+ });
147
+ let v = document.createElement("button");
148
+ v.type = "button", v.className = "tcw-level-remove", v.setAttribute("aria-label", l("drawingSettings.removeLevel") + d), v.innerHTML = e("x", 12), v.addEventListener("click", () => {
149
+ p(f().filter((e, t) => t !== n)), o.redraw([
150
+ `.tcw-level-row:nth-child(${n + 1}) .tcw-level-remove`,
151
+ ".tcw-level-row:last-child .tcw-level-remove",
152
+ ".tcw-level-add"
153
+ ]);
154
+ }), u.append(h, g, _, v), m.appendChild(u);
155
+ }), u.appendChild(m);
156
+ let h = document.createElement("button");
157
+ return h.type = "button", h.className = "tcw-reset-link tcw-level-add", h.textContent = l("drawingSettings.addLevel"), h.disabled = f().length >= y, h.addEventListener("click", () => {
158
+ let e = f().at(-1)?.value ?? 0;
159
+ p([...f(), {
160
+ value: Math.round((e + b) * 1e3) / 1e3,
161
+ visible: !0
162
+ }]), o.redraw([".tcw-level-row:last-child input[type=\"number\"]"]);
163
+ }), u.appendChild(h), u;
164
+ }
165
+ var M = 240, N = 40, P = 4e3, F = 10, I = [
166
+ "left",
167
+ "center",
168
+ "right"
169
+ ], L = [
170
+ "top",
171
+ "middle",
172
+ "bottom"
173
+ ];
174
+ function R(e, t, n, r = !1) {
175
+ let i = document.createElement("label");
176
+ i.className = "tcw-inline-check";
177
+ let a = document.createElement("input");
178
+ if (a.type = "checkbox", a.checked = t, a.setAttribute("aria-label", e), a.addEventListener("change", () => n(a.checked)), i.append(a), !r) {
179
+ let t = document.createElement("span");
180
+ t.textContent = e, i.append(t);
181
+ }
182
+ return i;
183
+ }
184
+ function z(e, t, n, r, i, a, o = !1) {
185
+ let u = w(), d = document.createElement("textarea");
186
+ d.className = "tcw-indi-input tcw-drawing-text", d.rows = 3, d.value = t.text ?? "", d.addEventListener("input", () => n({ text: d.value }));
187
+ let f = l(r("drawingSettings.text"), d);
188
+ f.classList.add("tcw-text-row"), u.appendChild(f);
189
+ let p = E(s(_.map((e) => ({
190
+ value: String(e),
191
+ label: `${e} px`
192
+ })), String(t.fontSize ?? i?.fontSize ?? v), (e) => n({ fontSize: Number(e) })), r("drawingSettings.fontSize"));
193
+ u.appendChild(T(r("drawingSettings.fontSize"), p, R(r("drawingSettings.bold"), t.bold === !0, (e) => n({ bold: e })), R(r("drawingSettings.italic"), t.italic === !0, (e) => n({ italic: e })))), u.appendChild(T(r("drawingSettings.textColor"), E(c(t.textColor ?? a ?? t.color, (e) => n({ textColor: e })), r("drawingSettings.textColor")))), e.appendChild(u), i && e.appendChild(V(t, n, r, i)), o && e.appendChild(B(t, n, r));
194
+ }
195
+ function B(e, t, n) {
196
+ let r = w();
197
+ return r.appendChild(T(n("drawingSettings.align"), E(s(I.map((e) => ({
198
+ value: e,
199
+ label: n(`drawingSettings.align.${e}`)
200
+ })), e.textAlign ?? "center", (e) => t({ textAlign: e })), n("drawingSettings.align")))), r.appendChild(T(n("drawingSettings.vAlign"), E(s(L.map((e) => ({
201
+ value: e,
202
+ label: n(`drawingSettings.vAlign.${e}`)
203
+ })), e.textVAlign ?? "top", (e) => t({ textVAlign: e })), n("drawingSettings.vAlign")))), r;
204
+ }
205
+ function V(e, t, r, a) {
206
+ let l = w();
207
+ l.appendChild(T(r("drawingSettings.align"), E(s(I.map((e) => ({
208
+ value: e,
209
+ label: r(`drawingSettings.align.${e}`)
210
+ })), e.textAlign ?? "left", (e) => t({ textAlign: e })), r("drawingSettings.align"))));
211
+ let u = e.fillColor ?? n(e.color, x);
212
+ l.appendChild(T(r("drawingSettings.background"), R(r("drawingSettings.background"), e.textBackground ?? a.box, (e) => t({ textBackground: e }), !0), E(c(u, (e) => t({ fillColor: n(e, o(u)) })), r("drawingSettings.backgroundColor")))), l.appendChild(T(r("drawingSettings.border"), R(r("drawingSettings.border"), e.textBorder ?? a.border, (e) => t({ textBorder: e }), !0), E(c(e.borderColor ?? e.color, (e) => t({ borderColor: e })), r("drawingSettings.borderColor"))));
213
+ let d = e.textWrap ?? a.wrap, f = d > 0 ? d : a.wrap > 0 ? a.wrap : M, p = d > 0, m = E(i(f, {
214
+ min: N,
215
+ max: P,
216
+ step: F
217
+ }, (e) => {
218
+ f = e, p && t({ textWrap: e });
219
+ }), r("drawingSettings.wrapWidth"));
220
+ return m.disabled = !p, l.appendChild(T(r("drawingSettings.wrap"), R(r("drawingSettings.wrap"), p, (e) => {
221
+ p = e, m.disabled = !e, t({ textWrap: e ? f : 0 });
222
+ }, !0), m)), l;
223
+ }
224
+ function H(e, t, n, r) {
225
+ let i = w();
226
+ t.forEach((e, t) => {
227
+ let a = d(r("drawingSettings.point"), { n: t + 1 }), o = n.toWallTime(e.time), s = document.createElement("input");
228
+ s.type = "date", s.className = "tcw-indi-input", s.value = o.date;
229
+ let c = document.createElement("input");
230
+ c.type = "time", c.className = "tcw-indi-input", c.value = o.time;
231
+ let l = () => {
232
+ let e = n.fromWallTime(s.value, c.value);
233
+ e !== null && n.move(t, { time: e });
234
+ };
235
+ s.addEventListener("change", l), c.addEventListener("change", l);
236
+ let u = T(a, E(U(e.price, n, (e) => n.move(t, { price: e })), `${a}: ${r("drawingSettings.price")}`), E(s, `${a}: ${r("drawingSettings.date")}`), E(c, `${a}: ${r("drawingSettings.time")}`));
237
+ u.classList.add("tcw-draw-point"), i.appendChild(u);
238
+ }), e.appendChild(i);
239
+ }
240
+ function U(e, t, n) {
241
+ let r = t.roundPrice ?? ((e) => e), a = t.priceStep?.(), o = i(r(e), { step: a !== void 0 && a > 0 ? a : "any" }, (e) => {
242
+ let t = r(e);
243
+ o.value = String(t), n(t);
244
+ });
245
+ return o;
246
+ }
247
+ //#endregion
248
+ //#region src/widget/WidgetDrawingSettings.ts
249
+ var W = 12, G = "t:", K = 1, q = class {
250
+ backdrop;
251
+ modal;
252
+ titleEl;
253
+ tabsEl;
254
+ bodyEl;
255
+ templateBtn;
256
+ menu = null;
257
+ tabButtons = [];
258
+ target = null;
259
+ style;
260
+ options = {};
261
+ anchors = [];
262
+ intervals = null;
263
+ tab = "style";
264
+ returnFocus = null;
265
+ uid = K++;
266
+ constructor(t, n, r = f) {
267
+ this.callbacks = n, this.t = r, this.backdrop = document.createElement("div"), this.backdrop.className = "tcw-modal-backdrop", this.backdrop.hidden = !0;
268
+ let i = !1;
269
+ this.backdrop.addEventListener("pointerdown", (e) => {
270
+ i = e.target === this.backdrop;
271
+ }), this.backdrop.addEventListener("click", (e) => {
272
+ e.target === this.backdrop && i && this.close(!0), i = !1;
273
+ }), this.modal = document.createElement("div"), this.modal.className = "tcw-modal tcw-modal-narrow tcw-drawing-settings", this.modal.setAttribute("role", "dialog"), this.modal.setAttribute("aria-modal", "true"), this.modal.setAttribute("aria-labelledby", `tcw-draw-title-${this.uid}`), this.modal.tabIndex = -1, this.modal.addEventListener("keydown", (e) => this.onKeyDown(e));
274
+ let a = document.createElement("div");
275
+ a.className = "tcw-modal-header", this.titleEl = document.createElement("h3"), this.titleEl.id = `tcw-draw-title-${this.uid}`;
276
+ let o = document.createElement("button");
277
+ o.type = "button", o.className = "tcw-modal-close", o.setAttribute("aria-label", this.t("common.close")), o.innerHTML = e("x", 16), o.addEventListener("click", () => this.close(!0)), a.append(this.titleEl, o), this.tabsEl = document.createElement("div"), this.tabsEl.className = "tcw-modal-tabs", this.tabsEl.setAttribute("role", "tablist"), this.tabsEl.addEventListener("keydown", (e) => this.onTabKey(e)), this.bodyEl = document.createElement("div"), this.bodyEl.className = "tcw-modal-body", this.bodyEl.id = `tcw-draw-panel-${this.uid}`, this.bodyEl.setAttribute("role", "tabpanel"), this.templateBtn = this.footerButton("template", "tcw-footer-menu", () => this.openTemplates()), this.modal.append(a, this.tabsEl, this.bodyEl, this.buildFooter()), this.backdrop.appendChild(this.modal), t.appendChild(this.backdrop);
278
+ }
279
+ buildFooter() {
280
+ this.templateBtn.setAttribute("aria-haspopup", "menu"), this.templateBtn.setAttribute("aria-expanded", "false"), this.templateBtn.innerHTML = `<span></span>${e("chevronDown", 12)}`, this.templateBtn.querySelector("span").textContent = this.t("settings.template");
281
+ let t = document.createElement("div");
282
+ t.className = "tcw-footer-end", t.append(this.footerButton("cancel", "tcw-footer-cancel", () => this.close(!0), this.t("common.cancel")), this.footerButton("ok", "tcw-done-btn", () => this.close(!1), this.t("drawingSettings.ok")));
283
+ let n = document.createElement("div");
284
+ return n.className = "tcw-modal-footer", n.append(this.templateBtn, t), n;
285
+ }
286
+ open(e, t = {}) {
287
+ this.target && this.close(!1), this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null, this.target = e, this.style = { ...e.style }, this.options = structuredClone(e.options), this.anchors = e.anchors.map((e) => ({ ...e })), this.intervals = e.intervals ?? null, this.titleEl.textContent = e.name, this.tab = t.tab === "text" && e.text ? "text" : "style", this.callbacks.onBegin(e.id);
288
+ try {
289
+ this.renderTabs(), this.renderBody();
290
+ } catch (t) {
291
+ throw this.target = null, this.callbacks.onEnd(e.id, !0), t;
292
+ }
293
+ this.backdrop.hidden = !1;
294
+ let n = this.tab === "text" ? this.bodyEl.querySelector("textarea") : null;
295
+ n ? (n.focus(), n.select()) : (this.tabButtons[0] ?? this.modal).focus();
296
+ }
297
+ close(e) {
298
+ let t = this.target;
299
+ if (!t) return;
300
+ let n = document.activeElement;
301
+ !e && (n instanceof HTMLInputElement || n instanceof HTMLTextAreaElement) && this.modal.contains(n) && n.dispatchEvent(new Event("change")), this.target = null, this.menu?.close(), this.backdrop.hidden = !0, this.callbacks.onEnd(t.id, e), this.returnFocus?.isConnected && this.returnFocus.focus(), this.returnFocus = null;
302
+ }
303
+ isOpen() {
304
+ return this.target !== null;
305
+ }
306
+ editing() {
307
+ return this.target?.id ?? null;
308
+ }
309
+ destroy() {
310
+ this.close(!0), this.menu?.destroy(), this.backdrop.remove();
311
+ }
312
+ tabs() {
313
+ let e = this.target;
314
+ return e ? [
315
+ "style",
316
+ ...e.text ? ["text"] : [],
317
+ ...e.coordinates !== !1 && e.anchors.length <= W ? ["coordinates"] : [],
318
+ "visibility"
319
+ ] : [];
320
+ }
321
+ renderTabs() {
322
+ this.tabButtons = this.tabs().map((e) => {
323
+ let t = document.createElement("button");
324
+ return t.type = "button", t.className = "tcw-modal-tab", t.id = `tcw-draw-tab-${this.uid}-${e}`, t.setAttribute("role", "tab"), t.setAttribute("aria-controls", this.bodyEl.id), t.dataset.tab = e, t.textContent = this.t(`drawingSettings.tab.${e}`), t.addEventListener("click", () => this.selectTab(e)), t;
325
+ }), this.tabsEl.replaceChildren(...this.tabButtons), this.markTabs();
326
+ }
327
+ markTabs() {
328
+ for (let e of this.tabButtons) {
329
+ let t = e.dataset.tab === this.tab;
330
+ 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);
331
+ }
332
+ }
333
+ selectTab(e, t = !1) {
334
+ this.tab = e, this.markTabs(), this.renderBody(), t && this.tabButtons.find((t) => t.dataset.tab === e)?.focus();
335
+ }
336
+ onTabKey(e) {
337
+ 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;
338
+ a < 0 || (e.preventDefault(), this.selectTab(t[a], !0));
339
+ }
340
+ onKeyDown(e) {
341
+ if (e.key === "Escape") {
342
+ e.preventDefault(), e.stopPropagation(), this.close(!0);
343
+ return;
344
+ }
345
+ t(e, this.modal);
346
+ }
347
+ renderBody() {
348
+ this.bodyEl.replaceChildren();
349
+ let e = this.target;
350
+ if (e) switch (this.tab) {
351
+ case "text":
352
+ z(this.bodyEl, this.style, (e) => this.setStyle(e), this.t, e.textLayout, e.textColor, e.textPlace);
353
+ break;
354
+ case "coordinates":
355
+ H(this.bodyEl, this.anchors, {
356
+ toWallTime: this.callbacks.toWallTime,
357
+ fromWallTime: this.callbacks.fromWallTime,
358
+ roundPrice: this.callbacks.roundPrice,
359
+ priceStep: this.callbacks.priceStep,
360
+ move: (e, t) => this.setAnchor(e, t)
361
+ }, this.t);
362
+ break;
363
+ case "visibility":
364
+ p(this.bodyEl, this.intervals, (e) => this.setIntervals(e), this.t);
365
+ break;
366
+ default: D(this.bodyEl, {
367
+ style: () => this.style,
368
+ options: () => this.options,
369
+ defs: e.defs,
370
+ fill: e.fill
371
+ }, {
372
+ style: (e) => this.setStyle(e),
373
+ options: (e) => this.setOptions(e),
374
+ redraw: (e = []) => {
375
+ this.renderBody();
376
+ for (let t of e) {
377
+ let e = this.bodyEl.querySelector(t);
378
+ if (e) {
379
+ e.focus();
380
+ break;
381
+ }
382
+ }
383
+ }
384
+ }, this.t);
385
+ }
386
+ }
387
+ change(e) {
388
+ this.target && this.callbacks.onChange(this.target.id, e);
389
+ }
390
+ setStyle(e) {
391
+ this.target && (this.style = {
392
+ ...this.style,
393
+ ...e
394
+ }, this.change({ style: e }));
395
+ }
396
+ setOptions(e) {
397
+ this.target && (this.options = {
398
+ ...this.options,
399
+ ...structuredClone(e)
400
+ }, this.change({ options: e }));
401
+ }
402
+ setAnchor(e, t) {
403
+ this.target && (this.anchors = this.anchors.map((n, r) => r === e ? {
404
+ ...n,
405
+ ...t
406
+ } : n), this.change({ anchors: this.anchors.map((e) => ({ ...e })) }));
407
+ }
408
+ setIntervals(e) {
409
+ this.target && (this.intervals = e, this.change({ intervals: e }));
410
+ }
411
+ openTemplates() {
412
+ let e = this.target;
413
+ if (!e) return;
414
+ if (this.menu?.isOpen()) {
415
+ this.menu.close();
416
+ return;
417
+ }
418
+ let t = this.callbacks.templates, n = t?.list(e.type) ?? [], i = [
419
+ ...n.map((e, t) => ({
420
+ id: `${G}${t}`,
421
+ label: e.name,
422
+ icon: "layers",
423
+ remove: d(this.t("templates.delete"), { name: e.name })
424
+ })),
425
+ ...n.length > 0 ? ["separator"] : [],
426
+ ...t ? [{
427
+ id: "save",
428
+ label: this.t("settings.saveTemplate"),
429
+ icon: "save"
430
+ }] : [],
431
+ ...this.callbacks.onSaveDefault ? [{
432
+ id: "default",
433
+ label: this.t("drawingSettings.saveDefault"),
434
+ icon: "pin"
435
+ }] : [],
436
+ {
437
+ id: "reset",
438
+ label: this.t("settings.resetToDefaults"),
439
+ icon: "reset"
440
+ }
441
+ ], a = (e) => n[Number(e.slice(2))];
442
+ this.menu ??= new r(this.modal, this.t("settings.template"));
443
+ let o = this.templateBtn.getBoundingClientRect(), s = this.modal.getBoundingClientRect();
444
+ this.menu.open(i, o.left - s.left, o.top - s.top, (n) => {
445
+ n === "save" ? t?.save({
446
+ type: e.type,
447
+ style: { ...this.style },
448
+ options: structuredClone(this.options)
449
+ }) : n === "default" ? this.callbacks.onSaveDefault?.(e.type, structuredClone(this.options)) : n === "reset" ? this.resetOptions() : n.startsWith(G) && this.applyTemplate(a(n));
450
+ }, this.templateBtn, (e) => {
451
+ let n = a(e);
452
+ n && t?.remove(n.name, n.type);
453
+ });
454
+ }
455
+ applyTemplate(e) {
456
+ let t = this.target;
457
+ !e || !t || (Object.keys(e.style).length > 0 && this.setStyle(e.style), e.options && this.setOptions(m(t.defs, e.options)), this.renderBody());
458
+ }
459
+ resetOptions() {
460
+ let e = this.target;
461
+ if (!e) return;
462
+ let t = {};
463
+ for (let [n, r] of Object.entries(e.defs)) t[n] = structuredClone(r.default);
464
+ this.setOptions(t), this.renderBody();
465
+ }
466
+ footerButton(e, t, n, r) {
467
+ let i = document.createElement("button");
468
+ return i.type = "button", i.className = t, i.dataset.act = e, r && (i.textContent = r), i.addEventListener("click", n), i;
469
+ }
470
+ };
471
+ //#endregion
472
+ export { q as WidgetDrawingSettings };
473
+
474
+ //# sourceMappingURL=WidgetDrawingSettings-BeW8fX7r.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WidgetDrawingSettings-BeW8fX7r.js","names":[],"sources":["../src/widget/drawingSettingsTabs.ts","../src/widget/WidgetDrawingSettings.ts"],"sourcesContent":["import type { AnchorPoint, DrawingLevel, DrawingOptionDef, DrawingOptionDefs, DrawingOptions, DrawingStyle } from '@tradecanvas/commons';\nimport { createIcon } from './icons.js';\nimport { fill, type MessageKey, type Translator } from './i18n.js';\nimport type { TextLayout } from './drawingText.js';\nimport { checkRow, colorAlpha, colorInput, numberInput, selectInput, settingsRow, toHex, withAlpha } from './settingsControls.js';\n\nexport type { TextLayout };\n\nconst LINE_WIDTHS = [1, 2, 3, 4];\nconst LINE_STYLES: DrawingStyle['lineStyle'][] = ['solid', 'dashed', 'dotted'];\nconst FONT_SIZES = [10, 11, 12, 13, 14, 16, 18, 20, 24, 28, 32];\n/** The size a text tool without a layout of its own draws at (a price label, an arrow's label). */\nconst DEFAULT_FONT_SIZE = 12;\n/** Most levels the editor offers to add, like the chart keeps. */\nconst MAX_LEVELS = 48;\n/** A new level sits this far past the last one. */\nconst LEVEL_STEP = 0.5;\n/** The fill's opacity a drawing without one starts from. */\nconst DEFAULT_FILL_ALPHA = 0.1;\n/** Options set in the line's own row, not listed with the tool's others. */\nconst END_KEYS = ['leftEnd', 'rightEnd'] as const;\n\n/** What the Style tab shows. */\nexport interface StyleTabModel {\n /** The style and the options as they are now (a row edits what the last edit left). */\n style: () => DrawingStyle;\n options: () => DrawingOptions;\n defs: DrawingOptionDefs;\n /** Whether the tool fills an area (the Fill row). */\n fill: boolean;\n}\n\n/** What the Style tab changes. */\nexport interface StyleTabActions {\n style(patch: Partial<DrawingStyle>): void;\n options(patch: DrawingOptions): void;\n /** Draw the tab again (a level added or removed), the keyboard on the first of `focus` there is. */\n redraw(focus?: readonly string[]): void;\n}\n\n/** Told the line's new colour: what takes it while it has none of its own (the fill, a level). */\ntype ColorFollower = (color: string) => void;\n\n/** A label from the widget's strings (`drawingOption.<key>`), else the tool's own. */\nfunction optionLabel(t: Translator, key: string, fallback: string): string {\n const messageKey = `drawingOption.${key}` as MessageKey;\n const text = t(messageKey);\n return text === messageKey ? fallback : text;\n}\n\nfunction section(): HTMLDivElement {\n const el = document.createElement('div');\n el.className = 'tcw-settings-section';\n return el;\n}\n\n/** A row of several controls after a label (a `<label>` would only reach the first). */\nfunction controlsRow(label: string, ...controls: HTMLElement[]): HTMLDivElement {\n const row = document.createElement('div');\n row.className = 'tcw-settings-row';\n const text = document.createElement('span');\n text.className = 'tcw-settings-label';\n text.textContent = label;\n const box = document.createElement('span');\n box.className = 'tcw-indi-plot-controls';\n box.append(...controls);\n row.append(text, box);\n return row;\n}\n\nfunction named<T extends HTMLElement>(el: T, label: string): T {\n el.setAttribute('aria-label', label);\n return el;\n}\n\n/**\n * The Style tab: the line (colour, width, dash), its ends, the fill, then\n * the tool's own switches and choices, and its levels last.\n */\nexport function renderStyleTab(body: HTMLElement, model: StyleTabModel, actions: StyleTabActions, t: Translator): void {\n const followers: ColorFollower[] = [];\n const look = section();\n look.appendChild(lineRow(model.style(), actions, followers, t));\n // A tool's ends only when they are the choice of plain or arrow; another option of that name is listed with the rest.\n const ends: string[] = END_KEYS.filter((key) => model.defs[key]?.kind === 'choice');\n if (ends.length > 0) {\n look.appendChild(controlsRow(t('drawingSettings.lineEnds'), ...ends.map((key) => named(selectInput(\n [{ value: 'normal', label: t('drawingSettings.lineEnd.normal') }, { value: 'arrow', label: t('drawingSettings.lineEnd.arrow') }],\n String(model.options()[key] ?? 'normal'),\n (v) => actions.options({ [key]: v }),\n ), optionLabel(t, key, model.defs[key].label)))));\n }\n if (model.fill) look.appendChild(fillRow(model.style(), actions, followers, t));\n body.appendChild(look);\n\n const rest = Object.entries(model.defs).filter(([key, def]) => def.kind !== 'levels' && !ends.includes(key));\n if (rest.length > 0) {\n const own = section();\n for (const [key, def] of rest) own.appendChild(optionRow(key, def as Exclude<DrawingOptionDef, { kind: 'levels' }>, model.options(), actions, t));\n body.appendChild(own);\n }\n for (const [key, def] of Object.entries(model.defs)) {\n if (def.kind === 'levels') body.appendChild(levelsEditor(key, def, model, actions, followers, t));\n }\n}\n\nfunction lineRow(style: DrawingStyle, actions: StyleTabActions, followers: readonly ColorFollower[], t: Translator): HTMLDivElement {\n const color = named(colorInput(style.color, (v) => {\n actions.style({ color: v });\n for (const follow of followers) follow(v);\n }), t('drawingSettings.color'));\n const width = named(selectInput(\n LINE_WIDTHS.map((w) => ({ value: String(w), label: `${w} px` })),\n String(style.lineWidth),\n (v) => actions.style({ lineWidth: Number(v) }),\n ), t('drawingSettings.lineWidth'));\n const dash = named(selectInput(\n LINE_STYLES.map((s) => ({ value: s, label: t(`drawingSettings.lineStyle.${s}`) })),\n style.lineStyle,\n (v) => actions.style({ lineStyle: v as DrawingStyle['lineStyle'] }),\n ), t('drawingSettings.lineStyle'));\n return controlsRow(t('drawingSettings.line'), color, width, dash);\n}\n\nfunction fillRow(style: DrawingStyle, actions: StyleTabActions, followers: ColorFollower[], t: Translator): HTMLDivElement {\n const current = style.fillColor ?? withAlpha(style.color, DEFAULT_FILL_ALPHA);\n let hex = toHex(current);\n let alpha = colorAlpha(current);\n // Without a fill of its own the drawing fills in its line's colour, until one is set here.\n let own = style.fillColor !== undefined;\n const apply = () => {\n own = true;\n actions.style({ fillColor: withAlpha(hex, alpha), fillOpacity: alpha });\n };\n const color = named(colorInput(hex, (v) => {\n hex = v;\n apply();\n }), t('drawingSettings.fill'));\n followers.push((line) => {\n if (own) return;\n hex = toHex(line);\n color.value = hex;\n });\n const opacity = named(numberInput(Math.round(alpha * 100), { min: 0, max: 100, step: 5 }, (v) => {\n alpha = v / 100;\n apply();\n }), t('drawingSettings.opacity'));\n const percent = document.createElement('span');\n percent.className = 'tcw-unit';\n percent.setAttribute('aria-hidden', 'true');\n percent.textContent = '%';\n return controlsRow(t('drawingSettings.fill'), color, opacity, percent);\n}\n\nfunction optionRow(\n key: string,\n def: Exclude<DrawingOptionDef, { kind: 'levels' }>,\n options: DrawingOptions,\n actions: StyleTabActions,\n t: Translator,\n): HTMLElement {\n const label = optionLabel(t, key, def.label);\n const value = options[key];\n switch (def.kind) {\n case 'boolean':\n return checkRow(label, Boolean(value), (v) => actions.options({ [key]: v }));\n case 'number':\n return settingsRow(label, numberInput(Number(value), { min: def.min, max: def.max, step: def.step }, (v) => actions.options({ [key]: v })));\n case 'choice':\n return settingsRow(label, selectInput(\n def.choices.map((c) => ({ value: c.value, label: optionLabel(t, `${key}.${c.value}`, c.label) })),\n String(value),\n (v) => actions.options({ [key]: v }),\n ));\n case 'text': {\n const input = document.createElement('input');\n input.type = 'text';\n input.className = 'tcw-indi-input';\n input.value = String(value ?? '');\n input.addEventListener('change', () => actions.options({ [key]: input.value }));\n return settingsRow(label, input);\n }\n }\n}\n\n/** One row per level: shown or not, its ratio, its colour, remove; and a button to add one. */\nfunction levelsEditor(\n key: string,\n def: Extract<DrawingOptionDef, { kind: 'levels' }>,\n model: StyleTabModel,\n actions: StyleTabActions,\n followers: ColorFollower[],\n t: Translator,\n): HTMLElement {\n const wrap = document.createElement('div');\n wrap.className = 'tcw-settings-section tcw-levels-editor';\n const head = document.createElement('div');\n head.className = 'tcw-settings-section-title';\n head.textContent = optionLabel(t, key, def.label);\n wrap.appendChild(head);\n\n const levels = (): DrawingLevel[] => {\n const value = model.options()[key];\n return Array.isArray(value) ? (value as DrawingLevel[]) : [];\n };\n const commit = (next: DrawingLevel[]) => actions.options({ [key]: next });\n const grid = document.createElement('div');\n grid.className = 'tcw-levels-grid';\n levels().forEach((level, index) => {\n const row = document.createElement('div');\n row.className = 'tcw-level-row';\n const nth = ` ${index + 1}`; // rows share labels; the number tells them apart\n const visible = document.createElement('input');\n visible.type = 'checkbox';\n visible.checked = level.visible;\n visible.setAttribute('aria-label', t('drawingSettings.levelVisible') + nth);\n visible.addEventListener('change', () => commit(levels().map((l, i) => (i === index ? { ...l, visible: visible.checked } : l))));\n const value = named(numberInput(level.value, { step: 0.001 }, (v) => commit(levels().map((l, i) => (i === index ? { ...l, value: v } : l)))), t('drawingSettings.levelValue') + nth);\n const color = named(colorInput(level.color ?? model.style().color, (v) => commit(levels().map((l, i) => (i === index ? { ...l, color: v } : l)))), t('drawingSettings.levelColor') + nth);\n // A level without a colour of its own is drawn in the line's.\n followers.push((line) => {\n if (levels()[index]?.color === undefined) color.value = toHex(line);\n });\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-level-remove';\n remove.setAttribute('aria-label', t('drawingSettings.removeLevel') + nth);\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => {\n commit(levels().filter((_, i) => i !== index));\n // The keyboard stays where it was: on the next level's remove button, else the last one's, else Add.\n actions.redraw([\n `.tcw-level-row:nth-child(${index + 1}) .tcw-level-remove`,\n '.tcw-level-row:last-child .tcw-level-remove',\n '.tcw-level-add',\n ]);\n });\n row.append(visible, value, color, remove);\n grid.appendChild(row);\n });\n wrap.appendChild(grid);\n\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'tcw-reset-link tcw-level-add';\n add.textContent = t('drawingSettings.addLevel');\n add.disabled = levels().length >= MAX_LEVELS;\n add.addEventListener('click', () => {\n const last = levels().at(-1)?.value ?? 0;\n commit([...levels(), { value: Math.round((last + LEVEL_STEP) * 1000) / 1000, visible: true }]);\n actions.redraw(['.tcw-level-row:last-child input[type=\"number\"]']);\n });\n wrap.appendChild(add);\n return wrap;\n}\n\n/** The width a text wraps at when wrapping is turned on and it has none of its own. */\nconst DEFAULT_WRAP = 240;\nconst MIN_WRAP = 40;\nconst MAX_WRAP = 4000;\nconst WRAP_STEP = 10;\nconst ALIGNS = ['left', 'center', 'right'] as const;\nconst VALIGNS = ['top', 'middle', 'bottom'] as const;\n\n/**\n * A box ticked or not inside a row of controls, its name written after it\n * unless the row already says it (`said`): a screen reader hears it either way.\n */\nfunction inlineCheck(label: string, value: boolean, onChange: (value: boolean) => void, said = false): HTMLLabelElement {\n const wrap = document.createElement('label');\n wrap.className = 'tcw-inline-check';\n const box = document.createElement('input');\n box.type = 'checkbox';\n box.checked = value;\n box.setAttribute('aria-label', label);\n box.addEventListener('change', () => onChange(box.checked));\n wrap.append(box);\n if (!said) {\n const text = document.createElement('span');\n text.textContent = label;\n wrap.append(text);\n }\n return wrap;\n}\n\n/**\n * The Text tab: what the drawing writes, its size, bold and italics, and its\n * colour; for a text box (`layout`), its alignment, background, border and\n * the width it wraps at; for a line's or a box's text (`place`), where it sits.\n */\nexport function renderTextTab(\n body: HTMLElement,\n style: DrawingStyle,\n onStyle: (patch: Partial<DrawingStyle>) => void,\n t: Translator,\n layout?: TextLayout,\n textColor?: string,\n place = false,\n): void {\n const block = section();\n const text = document.createElement('textarea');\n text.className = 'tcw-indi-input tcw-drawing-text';\n text.rows = 3;\n text.value = style.text ?? '';\n text.addEventListener('input', () => onStyle({ text: text.value }));\n const textRow = settingsRow(t('drawingSettings.text'), text);\n textRow.classList.add('tcw-text-row');\n block.appendChild(textRow);\n const size = named(selectInput(\n FONT_SIZES.map((s) => ({ value: String(s), label: `${s} px` })),\n String(style.fontSize ?? layout?.fontSize ?? DEFAULT_FONT_SIZE),\n (v) => onStyle({ fontSize: Number(v) }),\n ), t('drawingSettings.fontSize'));\n block.appendChild(controlsRow(\n t('drawingSettings.fontSize'),\n size,\n inlineCheck(t('drawingSettings.bold'), style.bold === true, (on) => onStyle({ bold: on })),\n inlineCheck(t('drawingSettings.italic'), style.italic === true, (on) => onStyle({ italic: on })),\n ));\n block.appendChild(controlsRow(t('drawingSettings.textColor'), named(\n colorInput(style.textColor ?? textColor ?? style.color, (v) => onStyle({ textColor: v })),\n t('drawingSettings.textColor'),\n )));\n body.appendChild(block);\n if (layout) body.appendChild(textBoxSection(style, onStyle, t, layout));\n if (place) body.appendChild(textPlaceSection(style, onStyle, t));\n}\n\n/** Where a line's or a box's text sits: at an end or the middle, and above, on or under (at the top, middle or bottom). */\nfunction textPlaceSection(style: DrawingStyle, onStyle: (patch: Partial<DrawingStyle>) => void, t: Translator): HTMLElement {\n const place = section();\n place.appendChild(controlsRow(t('drawingSettings.align'), named(selectInput(\n ALIGNS.map((a) => ({ value: a, label: t(`drawingSettings.align.${a}`) })),\n style.textAlign ?? 'center',\n (v) => onStyle({ textAlign: v as DrawingStyle['textAlign'] }),\n ), t('drawingSettings.align'))));\n place.appendChild(controlsRow(t('drawingSettings.vAlign'), named(selectInput(\n VALIGNS.map((a) => ({ value: a, label: t(`drawingSettings.vAlign.${a}`) })),\n style.textVAlign ?? 'top',\n (v) => onStyle({ textVAlign: v as DrawingStyle['textVAlign'] }),\n ), t('drawingSettings.vAlign'))));\n return place;\n}\n\n/** A text box's rows: alignment, background, border, and the width it wraps at. */\nfunction textBoxSection(style: DrawingStyle, onStyle: (patch: Partial<DrawingStyle>) => void, t: Translator, layout: TextLayout): HTMLElement {\n const box = section();\n box.appendChild(controlsRow(t('drawingSettings.align'), named(selectInput(\n ALIGNS.map((a) => ({ value: a, label: t(`drawingSettings.align.${a}`) })),\n style.textAlign ?? 'left',\n (v) => onStyle({ textAlign: v as DrawingStyle['textAlign'] }),\n ), t('drawingSettings.align'))));\n\n const background = style.fillColor ?? withAlpha(style.color, DEFAULT_FILL_ALPHA);\n box.appendChild(controlsRow(\n t('drawingSettings.background'),\n inlineCheck(t('drawingSettings.background'), style.textBackground ?? layout.box, (on) => onStyle({ textBackground: on }), true),\n named(colorInput(background, (v) => onStyle({ fillColor: withAlpha(v, colorAlpha(background)) })), t('drawingSettings.backgroundColor')),\n ));\n box.appendChild(controlsRow(\n t('drawingSettings.border'),\n inlineCheck(t('drawingSettings.border'), style.textBorder ?? layout.border, (on) => onStyle({ textBorder: on }), true),\n named(colorInput(style.borderColor ?? style.color, (v) => onStyle({ borderColor: v })), t('drawingSettings.borderColor')),\n ));\n\n // The width shows what wrapping on would use: the text's own, else the tool's, else a default.\n const own = style.textWrap ?? layout.wrap;\n let width = own > 0 ? own : layout.wrap > 0 ? layout.wrap : DEFAULT_WRAP;\n let wrapping = own > 0;\n const widthInput = named(numberInput(width, { min: MIN_WRAP, max: MAX_WRAP, step: WRAP_STEP }, (v) => {\n width = v;\n if (wrapping) onStyle({ textWrap: v });\n }), t('drawingSettings.wrapWidth'));\n // A width means something only while the text wraps.\n widthInput.disabled = !wrapping;\n box.appendChild(controlsRow(\n t('drawingSettings.wrap'),\n inlineCheck(t('drawingSettings.wrap'), wrapping, (on) => {\n wrapping = on;\n widthInput.disabled = !on;\n onStyle({ textWrap: on ? width : 0 });\n }, true),\n widthInput,\n ));\n return box;\n}\n\n/** What the Coordinates tab reads and changes. */\nexport interface CoordinatesTabActions {\n /** An anchor's time as the date and time fields show it (the chart's zone), and back. */\n toWallTime(time: number): { date: string; time: string };\n fromWallTime(date: string, time: string): number | null;\n /** A price on the market's grid. */\n roundPrice?(price: number): number;\n /** The market's smallest price step, for the price fields' arrows. */\n priceStep?(): number;\n move(index: number, patch: Partial<AnchorPoint>): void;\n}\n\n/** The Coordinates tab: a row per point, its price, date and time. */\nexport function renderCoordinatesTab(body: HTMLElement, anchors: readonly AnchorPoint[], actions: CoordinatesTabActions, t: Translator): void {\n const block = section();\n anchors.forEach((anchor, index) => {\n const point = fill(t('drawingSettings.point'), { n: index + 1 });\n const wall = actions.toWallTime(anchor.time);\n const date = document.createElement('input');\n date.type = 'date';\n date.className = 'tcw-indi-input';\n date.value = wall.date;\n const time = document.createElement('input');\n time.type = 'time';\n time.className = 'tcw-indi-input';\n time.value = wall.time;\n const onTime = () => {\n const at = actions.fromWallTime(date.value, time.value);\n if (at !== null) actions.move(index, { time: at });\n };\n date.addEventListener('change', onTime);\n time.addEventListener('change', onTime);\n const price = priceInput(anchor.price, actions, (p) => actions.move(index, { price: p }));\n const row = controlsRow(point,\n named(price, `${point}: ${t('drawingSettings.price')}`),\n named(date, `${point}: ${t('drawingSettings.date')}`),\n named(time, `${point}: ${t('drawingSettings.time')}`));\n row.classList.add('tcw-draw-point');\n block.appendChild(row);\n });\n body.appendChild(block);\n}\n\n/**\n * A price field on the market's grid. It shows the price rounded, and leaves\n * the point where it is until the field is edited.\n */\nfunction priceInput(price: number, actions: CoordinatesTabActions, onChange: (price: number) => void): HTMLInputElement {\n const round = actions.roundPrice ?? ((p: number) => p);\n const step = actions.priceStep?.();\n const input = numberInput(round(price), { step: step !== undefined && step > 0 ? step : 'any' }, (typed) => {\n const onGrid = round(typed);\n input.value = String(onGrid);\n onChange(onGrid);\n });\n return input;\n}\n","import type {\n AnchorPoint,\n DrawingOptionDefs,\n DrawingOptions,\n DrawingStyle,\n DrawingToolType,\n IndicatorIntervals,\n} from '@tradecanvas/commons';\nimport { sanitizeDrawingOptions } from '@tradecanvas/commons';\nimport { createIcon } from './icons.js';\nimport { keepTabInside } from './focusTrap.js';\nimport { EN_TRANSLATOR, fill, type Translator } from './i18n.js';\nimport { WidgetContextMenu, type ContextMenuEntry } from './WidgetContextMenu.js';\nimport type { DrawingStyleTemplate } from './DrawingTemplateStore.js';\nimport { renderCoordinatesTab, renderStyleTab, renderTextTab, type TextLayout } from './drawingSettingsTabs.js';\nimport { renderVisibilityTab } from './indicatorSettingsTabs.js';\n\n/** The drawing a settings dialog edits. */\nexport interface DrawingSettingsTarget {\n id: string;\n type: DrawingToolType;\n /** The tool's name in the widget's language. */\n name: string;\n style: DrawingStyle;\n /** Every option, defaults filled in. */\n options: DrawingOptions;\n defs: DrawingOptionDefs;\n anchors: AnchorPoint[];\n /** Whether the tool fills an area and draws text (the Fill row, the Text tab). */\n fill: boolean;\n text: boolean;\n /** How the tool lays its text out in a box (the Text tab's alignment, background, border and wrap rows); unset: font rows only. */\n textLayout?: TextLayout;\n /** The colour the tool writes in without one of its own, when it isn't the line's. */\n textColor?: string;\n /** It writes its text along a line or in a box: the Text tab offers where (`textAlign`, `textVAlign`). */\n textPlace?: boolean;\n /** False for a drawing kept to the screen: its points are places on the plot, not times and prices to edit. */\n coordinates?: boolean;\n /** The intervals it shows on; unset or null: all of them. */\n intervals?: IndicatorIntervals | null;\n}\n\nexport interface DrawingSettingsPatch {\n anchors?: AnchorPoint[];\n style?: Partial<DrawingStyle>;\n options?: DrawingOptions;\n /** Null: every interval. */\n intervals?: IndicatorIntervals | null;\n}\n\n/** A drawing's look as a template keeps it: its style, and its tool's options. */\nexport interface DrawingTemplateLook {\n type: DrawingToolType;\n style: Partial<DrawingStyle>;\n options: DrawingOptions;\n}\n\nexport interface DrawingSettingsCallbacks {\n /** The dialog opened on a drawing: edits until `onEnd` are one undo step. */\n onBegin: (id: string) => void;\n /** An edit, shown on the chart at once. */\n onChange: (id: string, patch: DrawingSettingsPatch) => void;\n /** OK keeps the edits; Cancel, Escape or the close button put the drawing back. */\n onEnd: (id: string, cancel: boolean) => void;\n /** An anchor's time as the date and time fields show it (the chart's zone), and back. */\n toWallTime: (time: number) => { date: string; time: string };\n fromWallTime: (date: string, time: string) => number | null;\n /** A price on the market's grid; the Coordinates tab shows and takes prices on it. */\n roundPrice?: (price: number) => number;\n /** The market's smallest price step, for the price fields' arrows. */\n priceStep?: () => number;\n /** Make these the options new drawings of the tool start with. */\n onSaveDefault?: (type: DrawingToolType, options: DrawingOptions) => void;\n /** Saved templates: listed and deleted in the Template menu, and \"Save as template…\" (which asks for a name). */\n templates?: {\n list: (type: DrawingToolType) => DrawingStyleTemplate[];\n save: (look: DrawingTemplateLook) => void;\n remove: (name: string, type?: DrawingToolType) => void;\n };\n}\n\ntype Tab = 'style' | 'text' | 'coordinates' | 'visibility';\n\n/** Most points the Coordinates tab lists. */\nconst MAX_COORDINATE_ROWS = 12;\n/** A saved template's entry in the Template menu, by its place in the list. */\nconst TEMPLATE_ID = 't:';\n\nlet nextDialogId = 1;\n\n/**\n * A drawing's settings, in tabs: Style (the line, its ends, the fill, the\n * tool's own switches and levels), Text, Coordinates (its points by price,\n * date and time) and Visibility (the intervals it shows on). Edits show on\n * the chart at once; OK keeps them as one undo step, Cancel puts it back.\n * The Template menu applies, saves and deletes templates, keeps the options\n * as the tool's default, or resets them.\n */\nexport class WidgetDrawingSettings {\n private backdrop: HTMLDivElement;\n private modal: HTMLDivElement;\n private titleEl: HTMLHeadingElement;\n private tabsEl: HTMLDivElement;\n private bodyEl: HTMLDivElement;\n private templateBtn: HTMLButtonElement;\n private menu: WidgetContextMenu | null = null;\n private tabButtons: HTMLButtonElement[] = [];\n private target: DrawingSettingsTarget | null = null;\n private style!: DrawingStyle;\n private options: DrawingOptions = {};\n private anchors: AnchorPoint[] = [];\n private intervals: IndicatorIntervals | null = null;\n private tab: Tab = 'style';\n private returnFocus: HTMLElement | null = null;\n private readonly uid = nextDialogId++;\n\n constructor(\n host: HTMLElement,\n private readonly callbacks: DrawingSettingsCallbacks,\n private readonly t: Translator = EN_TRANSLATOR,\n ) {\n this.backdrop = document.createElement('div');\n this.backdrop.className = 'tcw-modal-backdrop';\n this.backdrop.hidden = true;\n // A press that starts in the dialog (selecting text, say) and ends on the\n // backdrop doesn't close it.\n let pressedBackdrop = false;\n this.backdrop.addEventListener('pointerdown', (e) => {\n pressedBackdrop = e.target === this.backdrop;\n });\n this.backdrop.addEventListener('click', (e) => {\n if (e.target === this.backdrop && pressedBackdrop) this.close(true);\n pressedBackdrop = false;\n });\n\n this.modal = document.createElement('div');\n this.modal.className = 'tcw-modal tcw-modal-narrow tcw-drawing-settings';\n this.modal.setAttribute('role', 'dialog');\n this.modal.setAttribute('aria-modal', 'true');\n this.modal.setAttribute('aria-labelledby', `tcw-draw-title-${this.uid}`);\n this.modal.tabIndex = -1;\n this.modal.addEventListener('keydown', (e) => this.onKeyDown(e));\n\n const header = document.createElement('div');\n header.className = 'tcw-modal-header';\n this.titleEl = document.createElement('h3');\n this.titleEl.id = `tcw-draw-title-${this.uid}`;\n const closeBtn = document.createElement('button');\n closeBtn.type = 'button';\n closeBtn.className = 'tcw-modal-close';\n closeBtn.setAttribute('aria-label', this.t('common.close'));\n closeBtn.innerHTML = createIcon('x', 16);\n closeBtn.addEventListener('click', () => this.close(true));\n header.append(this.titleEl, closeBtn);\n\n this.tabsEl = document.createElement('div');\n this.tabsEl.className = 'tcw-modal-tabs';\n this.tabsEl.setAttribute('role', 'tablist');\n this.tabsEl.addEventListener('keydown', (e) => this.onTabKey(e));\n\n this.bodyEl = document.createElement('div');\n this.bodyEl.className = 'tcw-modal-body';\n this.bodyEl.id = `tcw-draw-panel-${this.uid}`;\n this.bodyEl.setAttribute('role', 'tabpanel');\n\n this.templateBtn = this.footerButton('template', 'tcw-footer-menu', () => this.openTemplates());\n this.modal.append(header, this.tabsEl, this.bodyEl, this.buildFooter());\n this.backdrop.appendChild(this.modal);\n host.appendChild(this.backdrop);\n }\n\n /** Template on the left; Cancel and OK on the right. */\n private buildFooter(): HTMLDivElement {\n this.templateBtn.setAttribute('aria-haspopup', 'menu');\n this.templateBtn.setAttribute('aria-expanded', 'false');\n this.templateBtn.innerHTML = `<span></span>${createIcon('chevronDown', 12)}`;\n this.templateBtn.querySelector('span')!.textContent = this.t('settings.template');\n const end = document.createElement('div');\n end.className = 'tcw-footer-end';\n end.append(\n this.footerButton('cancel', 'tcw-footer-cancel', () => this.close(true), this.t('common.cancel')),\n this.footerButton('ok', 'tcw-done-btn', () => this.close(false), this.t('drawingSettings.ok')),\n );\n const footer = document.createElement('div');\n footer.className = 'tcw-modal-footer';\n footer.append(this.templateBtn, end);\n return footer;\n }\n\n /**\n * Open on `target`. With `tab: 'text'` it opens on the Text tab (a tool\n * that writes), the text selected so typing replaces it.\n */\n open(target: DrawingSettingsTarget, how: { tab?: 'text' } = {}): void {\n if (this.target) this.close(false);\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.target = target;\n this.style = { ...target.style };\n this.options = structuredClone(target.options);\n this.anchors = target.anchors.map((a) => ({ ...a }));\n this.intervals = target.intervals ?? null;\n this.titleEl.textContent = target.name;\n this.tab = how.tab === 'text' && target.text ? 'text' : 'style';\n this.callbacks.onBegin(target.id);\n try {\n this.renderTabs();\n this.renderBody();\n } catch (err) {\n // A drawing the dialog can't show: end the edit it began.\n this.target = null;\n this.callbacks.onEnd(target.id, true);\n throw err;\n }\n this.backdrop.hidden = false;\n const text = this.tab === 'text' ? this.bodyEl.querySelector<HTMLTextAreaElement>('textarea') : null;\n if (text) {\n text.focus();\n text.select();\n } else {\n (this.tabButtons[0] ?? this.modal).focus();\n }\n }\n\n /** Close the dialog, keeping the edits or (with `cancel`) putting the drawing back. */\n close(cancel: boolean): void {\n const target = this.target;\n if (!target) return;\n // A value typed but not yet committed still applies on OK.\n const active = document.activeElement;\n if (!cancel && (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement) && this.modal.contains(active)) {\n active.dispatchEvent(new Event('change'));\n }\n this.target = null;\n this.menu?.close();\n this.backdrop.hidden = true;\n this.callbacks.onEnd(target.id, cancel);\n if (this.returnFocus?.isConnected) this.returnFocus.focus();\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return this.target !== null;\n }\n\n /** The drawing being edited, if any. */\n editing(): string | null {\n return this.target?.id ?? null;\n }\n\n destroy(): void {\n this.close(true);\n this.menu?.destroy();\n this.backdrop.remove();\n }\n\n // --- Tabs ---\n\n private tabs(): Tab[] {\n const target = this.target;\n if (!target) return [];\n return [\n 'style',\n ...(target.text ? ['text' as const] : []),\n // A freehand stroke has hundreds of points: no table of them.\n ...(target.coordinates !== false && target.anchors.length <= MAX_COORDINATE_ROWS ? ['coordinates' as const] : []),\n 'visibility',\n ];\n }\n\n private renderTabs(): void {\n this.tabButtons = this.tabs().map((tab) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-modal-tab';\n btn.id = `tcw-draw-tab-${this.uid}-${tab}`;\n btn.setAttribute('role', 'tab');\n btn.setAttribute('aria-controls', this.bodyEl.id);\n btn.dataset.tab = tab;\n btn.textContent = this.t(`drawingSettings.tab.${tab}`);\n btn.addEventListener('click', () => this.selectTab(tab));\n return btn;\n });\n this.tabsEl.replaceChildren(...this.tabButtons);\n this.markTabs();\n }\n\n private markTabs(): void {\n for (const btn of this.tabButtons) {\n const selected = btn.dataset.tab === this.tab;\n btn.classList.toggle('tcw-active', selected);\n btn.setAttribute('aria-selected', String(selected));\n btn.tabIndex = selected ? 0 : -1;\n if (selected) this.bodyEl.setAttribute('aria-labelledby', btn.id);\n }\n }\n\n private selectTab(tab: Tab, focus = false): void {\n this.tab = tab;\n this.markTabs();\n this.renderBody();\n if (focus) this.tabButtons.find((b) => b.dataset.tab === tab)?.focus();\n }\n\n /** Arrow keys (the other way right to left), Home and End move between tabs. */\n private onTabKey(e: KeyboardEvent): void {\n const tabs = this.tabButtons.map((b) => b.dataset.tab as Tab);\n const at = tabs.indexOf(this.tab);\n const rtl = this.modal.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const [forward, back] = rtl ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n const next = e.key === forward ? (at + 1) % tabs.length\n : e.key === back ? (at - 1 + tabs.length) % tabs.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? tabs.length - 1\n : -1;\n if (next < 0) return;\n e.preventDefault();\n this.selectTab(tabs[next], true);\n }\n\n /** Escape cancels; Tab stays inside the dialog. */\n private onKeyDown(e: KeyboardEvent): void {\n if (e.key === 'Escape') {\n e.preventDefault();\n e.stopPropagation();\n this.close(true);\n return;\n }\n keepTabInside(e, this.modal);\n }\n\n private renderBody(): void {\n this.bodyEl.replaceChildren();\n const target = this.target;\n if (!target) return;\n switch (this.tab) {\n case 'text':\n renderTextTab(this.bodyEl, this.style, (patch) => this.setStyle(patch), this.t, target.textLayout, target.textColor, target.textPlace);\n break;\n case 'coordinates':\n renderCoordinatesTab(this.bodyEl, this.anchors, {\n toWallTime: this.callbacks.toWallTime,\n fromWallTime: this.callbacks.fromWallTime,\n roundPrice: this.callbacks.roundPrice,\n priceStep: this.callbacks.priceStep,\n move: (index, patch) => this.setAnchor(index, patch),\n }, this.t);\n break;\n case 'visibility':\n renderVisibilityTab(this.bodyEl, this.intervals, (next) => this.setIntervals(next), this.t);\n break;\n default:\n renderStyleTab(this.bodyEl, { style: () => this.style, options: () => this.options, defs: target.defs, fill: target.fill }, {\n style: (patch) => this.setStyle(patch),\n options: (patch) => this.setOptions(patch),\n redraw: (focus = []) => {\n this.renderBody();\n for (const selector of focus) {\n const el = this.bodyEl.querySelector<HTMLElement>(selector);\n if (el) {\n el.focus();\n break;\n }\n }\n },\n }, this.t);\n }\n }\n\n // --- Edits ---\n\n private change(patch: DrawingSettingsPatch): void {\n if (this.target) this.callbacks.onChange(this.target.id, patch);\n }\n\n private setStyle(style: Partial<DrawingStyle>): void {\n if (!this.target) return;\n this.style = { ...this.style, ...style };\n this.change({ style });\n }\n\n private setOptions(options: DrawingOptions): void {\n if (!this.target) return;\n this.options = { ...this.options, ...structuredClone(options) };\n this.change({ options });\n }\n\n private setAnchor(index: number, patch: Partial<AnchorPoint>): void {\n if (!this.target) return;\n this.anchors = this.anchors.map((a, i) => (i === index ? { ...a, ...patch } : a));\n this.change({ anchors: this.anchors.map((a) => ({ ...a })) });\n }\n\n private setIntervals(intervals: IndicatorIntervals | null): void {\n if (!this.target) return;\n this.intervals = intervals;\n this.change({ intervals });\n }\n\n // --- Template ---\n\n /** The Template menu: the saved templates (each removable), Save as template, Save as default, Reset to defaults. */\n private openTemplates(): void {\n const target = this.target;\n if (!target) return;\n if (this.menu?.isOpen()) {\n this.menu.close();\n return;\n }\n const store = this.callbacks.templates;\n const saved = store?.list(target.type) ?? [];\n const entries: ContextMenuEntry[] = [\n ...saved.map((template, i): ContextMenuEntry => ({\n id: `${TEMPLATE_ID}${i}`, label: template.name, icon: 'layers', remove: fill(this.t('templates.delete'), { name: template.name }),\n })),\n ...(saved.length > 0 ? ['separator' as const] : []),\n ...(store ? [{ id: 'save', label: this.t('settings.saveTemplate'), icon: 'save' }] : []),\n ...(this.callbacks.onSaveDefault ? [{ id: 'default', label: this.t('drawingSettings.saveDefault'), icon: 'pin' }] : []),\n { id: 'reset', label: this.t('settings.resetToDefaults'), icon: 'reset' },\n ];\n const savedAt = (id: string) => saved[Number(id.slice(TEMPLATE_ID.length))];\n this.menu ??= new WidgetContextMenu(this.modal, this.t('settings.template'));\n const at = this.templateBtn.getBoundingClientRect();\n const box = this.modal.getBoundingClientRect();\n // From the button's top: in the footer there is no room below, so it opens above the button.\n this.menu.open(entries, at.left - box.left, at.top - box.top, (id) => {\n if (id === 'save') store?.save({ type: target.type, style: { ...this.style }, options: structuredClone(this.options) });\n else if (id === 'default') this.callbacks.onSaveDefault?.(target.type, structuredClone(this.options));\n else if (id === 'reset') this.resetOptions();\n else if (id.startsWith(TEMPLATE_ID)) this.applyTemplate(savedAt(id));\n }, this.templateBtn, (id) => {\n const template = savedAt(id);\n if (template) store?.remove(template.name, template.type);\n });\n }\n\n private applyTemplate(template: DrawingStyleTemplate | undefined): void {\n const target = this.target;\n if (!template || !target) return;\n if (Object.keys(template.style).length > 0) this.setStyle(template.style);\n // Templates come from storage: only options the tool takes apply.\n if (template.options) this.setOptions(sanitizeDrawingOptions(target.defs, template.options));\n this.renderBody();\n }\n\n /** The tool's options back to its defaults. */\n private resetOptions(): void {\n const target = this.target;\n if (!target) return;\n const defaults: DrawingOptions = {};\n for (const [key, def] of Object.entries(target.defs)) defaults[key] = structuredClone(def.default) as DrawingOptions[string];\n this.setOptions(defaults);\n this.renderBody();\n }\n\n private footerButton(act: string, className: string, onClick: () => void, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = className;\n btn.dataset.act = act;\n if (text) btn.textContent = text;\n btn.addEventListener('click', onClick);\n return btn;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require(`./focusTrap-DrhhmG0V.cjs`),t=require(`./settingsControls-Tn8_vJJv.cjs`),n=require(`./i18n-C2nQBZTY.cjs`),r=require(`./indicatorSettingsTabs-B0liE3ST.cjs`);let i=require(`@tradecanvas/commons`);var a=[1,2,3,4],o=[`solid`,`dashed`,`dotted`],s=[10,11,12,13,14,16,18,20,24,28,32],c=12,l=48,u=.5,d=.1,f=[`leftEnd`,`rightEnd`];function p(e,t,n){let r=`drawingOption.${t}`,i=e(r);return i===r?n:i}function m(){let e=document.createElement(`div`);return e.className=`tcw-settings-section`,e}function h(e,...t){let n=document.createElement(`div`);n.className=`tcw-settings-row`;let r=document.createElement(`span`);r.className=`tcw-settings-label`,r.textContent=e;let i=document.createElement(`span`);return i.className=`tcw-indi-plot-controls`,i.append(...t),n.append(r,i),n}function g(e,t){return e.setAttribute(`aria-label`,t),e}function _(e,n,r,i){let a=[],o=m();o.appendChild(v(n.style(),r,a,i));let s=f.filter(e=>n.defs[e]?.kind===`choice`);s.length>0&&o.appendChild(h(i(`drawingSettings.lineEnds`),...s.map(e=>g(t.o([{value:`normal`,label:i(`drawingSettings.lineEnd.normal`)},{value:`arrow`,label:i(`drawingSettings.lineEnd.arrow`)}],String(n.options()[e]??`normal`),t=>r.options({[e]:t})),p(i,e,n.defs[e].label))))),n.fill&&o.appendChild(y(n.style(),r,a,i)),e.appendChild(o);let c=Object.entries(n.defs).filter(([e,t])=>t.kind!==`levels`&&!s.includes(e));if(c.length>0){let t=m();for(let[e,a]of c)t.appendChild(b(e,a,n.options(),r,i));e.appendChild(t)}for(let[t,o]of Object.entries(n.defs))o.kind===`levels`&&e.appendChild(x(t,o,n,r,a,i))}function v(e,n,r,i){let s=g(t.r(e.color,e=>{n.style({color:e});for(let t of r)t(e)}),i(`drawingSettings.color`)),c=g(t.o(a.map(e=>({value:String(e),label:`${e} px`})),String(e.lineWidth),e=>n.style({lineWidth:Number(e)})),i(`drawingSettings.lineWidth`)),l=g(t.o(o.map(e=>({value:e,label:i(`drawingSettings.lineStyle.${e}`)})),e.lineStyle,e=>n.style({lineStyle:e})),i(`drawingSettings.lineStyle`));return h(i(`drawingSettings.line`),s,c,l)}function y(e,n,r,i){let a=e.fillColor??t.d(e.color,d),o=t.l(a),s=t.n(a),c=e.fillColor!==void 0,l=()=>{c=!0,n.style({fillColor:t.d(o,s),fillOpacity:s})},u=g(t.r(o,e=>{o=e,l()}),i(`drawingSettings.fill`));r.push(e=>{c||(o=t.l(e),u.value=o)});let f=g(t.i(Math.round(s*100),{min:0,max:100,step:5},e=>{s=e/100,l()}),i(`drawingSettings.opacity`)),p=document.createElement(`span`);return p.className=`tcw-unit`,p.setAttribute(`aria-hidden`,`true`),p.textContent=`%`,h(i(`drawingSettings.fill`),u,f,p)}function b(e,n,r,i,a){let o=p(a,e,n.label),s=r[e];switch(n.kind){case`boolean`:return t.t(o,!!s,t=>i.options({[e]:t}));case`number`:return t.s(o,t.i(Number(s),{min:n.min,max:n.max,step:n.step},t=>i.options({[e]:t})));case`choice`:return t.s(o,t.o(n.choices.map(t=>({value:t.value,label:p(a,`${e}.${t.value}`,t.label)})),String(s),t=>i.options({[e]:t})));case`text`:{let n=document.createElement(`input`);return n.type=`text`,n.className=`tcw-indi-input`,n.value=String(s??``),n.addEventListener(`change`,()=>i.options({[e]:n.value})),t.s(o,n)}}}function x(n,r,i,a,o,s){let c=document.createElement(`div`);c.className=`tcw-settings-section tcw-levels-editor`;let d=document.createElement(`div`);d.className=`tcw-settings-section-title`,d.textContent=p(s,n,r.label),c.appendChild(d);let f=()=>{let e=i.options()[n];return Array.isArray(e)?e:[]},m=e=>a.options({[n]:e}),h=document.createElement(`div`);h.className=`tcw-levels-grid`,f().forEach((n,r)=>{let c=document.createElement(`div`);c.className=`tcw-level-row`;let l=` ${r+1}`,u=document.createElement(`input`);u.type=`checkbox`,u.checked=n.visible,u.setAttribute(`aria-label`,s(`drawingSettings.levelVisible`)+l),u.addEventListener(`change`,()=>m(f().map((e,t)=>t===r?{...e,visible:u.checked}:e)));let d=g(t.i(n.value,{step:.001},e=>m(f().map((t,n)=>n===r?{...t,value:e}:t))),s(`drawingSettings.levelValue`)+l),p=g(t.r(n.color??i.style().color,e=>m(f().map((t,n)=>n===r?{...t,color:e}:t))),s(`drawingSettings.levelColor`)+l);o.push(e=>{f()[r]?.color===void 0&&(p.value=t.l(e))});let _=document.createElement(`button`);_.type=`button`,_.className=`tcw-level-remove`,_.setAttribute(`aria-label`,s(`drawingSettings.removeLevel`)+l),_.innerHTML=e.i(`x`,12),_.addEventListener(`click`,()=>{m(f().filter((e,t)=>t!==r)),a.redraw([`.tcw-level-row:nth-child(${r+1}) .tcw-level-remove`,`.tcw-level-row:last-child .tcw-level-remove`,`.tcw-level-add`])}),c.append(u,d,p,_),h.appendChild(c)}),c.appendChild(h);let _=document.createElement(`button`);return _.type=`button`,_.className=`tcw-reset-link tcw-level-add`,_.textContent=s(`drawingSettings.addLevel`),_.disabled=f().length>=l,_.addEventListener(`click`,()=>{let e=f().at(-1)?.value??0;m([...f(),{value:Math.round((e+u)*1e3)/1e3,visible:!0}]),a.redraw([`.tcw-level-row:last-child input[type="number"]`])}),c.appendChild(_),c}var S=240,C=40,w=4e3,T=10,E=[`left`,`center`,`right`],D=[`top`,`middle`,`bottom`];function O(e,t,n,r=!1){let i=document.createElement(`label`);i.className=`tcw-inline-check`;let a=document.createElement(`input`);if(a.type=`checkbox`,a.checked=t,a.setAttribute(`aria-label`,e),a.addEventListener(`change`,()=>n(a.checked)),i.append(a),!r){let t=document.createElement(`span`);t.textContent=e,i.append(t)}return i}function k(e,n,r,i,a,o,l=!1){let u=m(),d=document.createElement(`textarea`);d.className=`tcw-indi-input tcw-drawing-text`,d.rows=3,d.value=n.text??``,d.addEventListener(`input`,()=>r({text:d.value}));let f=t.s(i(`drawingSettings.text`),d);f.classList.add(`tcw-text-row`),u.appendChild(f);let p=g(t.o(s.map(e=>({value:String(e),label:`${e} px`})),String(n.fontSize??a?.fontSize??c),e=>r({fontSize:Number(e)})),i(`drawingSettings.fontSize`));u.appendChild(h(i(`drawingSettings.fontSize`),p,O(i(`drawingSettings.bold`),n.bold===!0,e=>r({bold:e})),O(i(`drawingSettings.italic`),n.italic===!0,e=>r({italic:e})))),u.appendChild(h(i(`drawingSettings.textColor`),g(t.r(n.textColor??o??n.color,e=>r({textColor:e})),i(`drawingSettings.textColor`)))),e.appendChild(u),a&&e.appendChild(j(n,r,i,a)),l&&e.appendChild(A(n,r,i))}function A(e,n,r){let i=m();return i.appendChild(h(r(`drawingSettings.align`),g(t.o(E.map(e=>({value:e,label:r(`drawingSettings.align.${e}`)})),e.textAlign??`center`,e=>n({textAlign:e})),r(`drawingSettings.align`)))),i.appendChild(h(r(`drawingSettings.vAlign`),g(t.o(D.map(e=>({value:e,label:r(`drawingSettings.vAlign.${e}`)})),e.textVAlign??`top`,e=>n({textVAlign:e})),r(`drawingSettings.vAlign`)))),i}function j(e,n,r,i){let a=m();a.appendChild(h(r(`drawingSettings.align`),g(t.o(E.map(e=>({value:e,label:r(`drawingSettings.align.${e}`)})),e.textAlign??`left`,e=>n({textAlign:e})),r(`drawingSettings.align`))));let o=e.fillColor??t.d(e.color,d);a.appendChild(h(r(`drawingSettings.background`),O(r(`drawingSettings.background`),e.textBackground??i.box,e=>n({textBackground:e}),!0),g(t.r(o,e=>n({fillColor:t.d(e,t.n(o))})),r(`drawingSettings.backgroundColor`)))),a.appendChild(h(r(`drawingSettings.border`),O(r(`drawingSettings.border`),e.textBorder??i.border,e=>n({textBorder:e}),!0),g(t.r(e.borderColor??e.color,e=>n({borderColor:e})),r(`drawingSettings.borderColor`))));let s=e.textWrap??i.wrap,c=s>0?s:i.wrap>0?i.wrap:S,l=s>0,u=g(t.i(c,{min:C,max:w,step:T},e=>{c=e,l&&n({textWrap:e})}),r(`drawingSettings.wrapWidth`));return u.disabled=!l,a.appendChild(h(r(`drawingSettings.wrap`),O(r(`drawingSettings.wrap`),l,e=>{l=e,u.disabled=!e,n({textWrap:e?c:0})},!0),u)),a}function M(e,t,r,i){let a=m();t.forEach((e,t)=>{let o=n.r(i(`drawingSettings.point`),{n:t+1}),s=r.toWallTime(e.time),c=document.createElement(`input`);c.type=`date`,c.className=`tcw-indi-input`,c.value=s.date;let l=document.createElement(`input`);l.type=`time`,l.className=`tcw-indi-input`,l.value=s.time;let u=()=>{let e=r.fromWallTime(c.value,l.value);e!==null&&r.move(t,{time:e})};c.addEventListener(`change`,u),l.addEventListener(`change`,u);let d=h(o,g(N(e.price,r,e=>r.move(t,{price:e})),`${o}: ${i(`drawingSettings.price`)}`),g(c,`${o}: ${i(`drawingSettings.date`)}`),g(l,`${o}: ${i(`drawingSettings.time`)}`));d.classList.add(`tcw-draw-point`),a.appendChild(d)}),e.appendChild(a)}function N(e,n,r){let i=n.roundPrice??(e=>e),a=n.priceStep?.(),o=t.i(i(e),{step:a!==void 0&&a>0?a:`any`},e=>{let t=i(e);o.value=String(t),r(t)});return o}var P=12,F=`t:`,I=1,L=class{backdrop;modal;titleEl;tabsEl;bodyEl;templateBtn;menu=null;tabButtons=[];target=null;style;options={};anchors=[];intervals=null;tab=`style`;returnFocus=null;uid=I++;constructor(t,r,i=n.t){this.callbacks=r,this.t=i,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.close(!0),a=!1}),this.modal=document.createElement(`div`),this.modal.className=`tcw-modal tcw-modal-narrow tcw-drawing-settings`,this.modal.setAttribute(`role`,`dialog`),this.modal.setAttribute(`aria-modal`,`true`),this.modal.setAttribute(`aria-labelledby`,`tcw-draw-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-draw-title-${this.uid}`;let s=document.createElement(`button`);s.type=`button`,s.className=`tcw-modal-close`,s.setAttribute(`aria-label`,this.t(`common.close`)),s.innerHTML=e.i(`x`,16),s.addEventListener(`click`,()=>this.close(!0)),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-draw-panel-${this.uid}`,this.bodyEl.setAttribute(`role`,`tabpanel`),this.templateBtn=this.footerButton(`template`,`tcw-footer-menu`,()=>this.openTemplates()),this.modal.append(o,this.tabsEl,this.bodyEl,this.buildFooter()),this.backdrop.appendChild(this.modal),t.appendChild(this.backdrop)}buildFooter(){this.templateBtn.setAttribute(`aria-haspopup`,`menu`),this.templateBtn.setAttribute(`aria-expanded`,`false`),this.templateBtn.innerHTML=`<span></span>${e.i(`chevronDown`,12)}`,this.templateBtn.querySelector(`span`).textContent=this.t(`settings.template`);let t=document.createElement(`div`);t.className=`tcw-footer-end`,t.append(this.footerButton(`cancel`,`tcw-footer-cancel`,()=>this.close(!0),this.t(`common.cancel`)),this.footerButton(`ok`,`tcw-done-btn`,()=>this.close(!1),this.t(`drawingSettings.ok`)));let n=document.createElement(`div`);return n.className=`tcw-modal-footer`,n.append(this.templateBtn,t),n}open(e,t={}){this.target&&this.close(!1),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.target=e,this.style={...e.style},this.options=structuredClone(e.options),this.anchors=e.anchors.map(e=>({...e})),this.intervals=e.intervals??null,this.titleEl.textContent=e.name,this.tab=t.tab===`text`&&e.text?`text`:`style`,this.callbacks.onBegin(e.id);try{this.renderTabs(),this.renderBody()}catch(t){throw this.target=null,this.callbacks.onEnd(e.id,!0),t}this.backdrop.hidden=!1;let n=this.tab===`text`?this.bodyEl.querySelector(`textarea`):null;n?(n.focus(),n.select()):(this.tabButtons[0]??this.modal).focus()}close(e){let t=this.target;if(!t)return;let n=document.activeElement;!e&&(n instanceof HTMLInputElement||n instanceof HTMLTextAreaElement)&&this.modal.contains(n)&&n.dispatchEvent(new Event(`change`)),this.target=null,this.menu?.close(),this.backdrop.hidden=!0,this.callbacks.onEnd(t.id,e),this.returnFocus?.isConnected&&this.returnFocus.focus(),this.returnFocus=null}isOpen(){return this.target!==null}editing(){return this.target?.id??null}destroy(){this.close(!0),this.menu?.destroy(),this.backdrop.remove()}tabs(){let e=this.target;return e?[`style`,...e.text?[`text`]:[],...e.coordinates!==!1&&e.anchors.length<=P?[`coordinates`]:[],`visibility`]:[]}renderTabs(){this.tabButtons=this.tabs().map(e=>{let t=document.createElement(`button`);return t.type=`button`,t.className=`tcw-modal-tab`,t.id=`tcw-draw-tab-${this.uid}-${e}`,t.setAttribute(`role`,`tab`),t.setAttribute(`aria-controls`,this.bodyEl.id),t.dataset.tab=e,t.textContent=this.t(`drawingSettings.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||(e.preventDefault(),this.selectTab(t[a],!0))}onKeyDown(t){if(t.key===`Escape`){t.preventDefault(),t.stopPropagation(),this.close(!0);return}e.n(t,this.modal)}renderBody(){this.bodyEl.replaceChildren();let e=this.target;if(e)switch(this.tab){case`text`:k(this.bodyEl,this.style,e=>this.setStyle(e),this.t,e.textLayout,e.textColor,e.textPlace);break;case`coordinates`:M(this.bodyEl,this.anchors,{toWallTime:this.callbacks.toWallTime,fromWallTime:this.callbacks.fromWallTime,roundPrice:this.callbacks.roundPrice,priceStep:this.callbacks.priceStep,move:(e,t)=>this.setAnchor(e,t)},this.t);break;case`visibility`:r.n(this.bodyEl,this.intervals,e=>this.setIntervals(e),this.t);break;default:_(this.bodyEl,{style:()=>this.style,options:()=>this.options,defs:e.defs,fill:e.fill},{style:e=>this.setStyle(e),options:e=>this.setOptions(e),redraw:(e=[])=>{this.renderBody();for(let t of e){let e=this.bodyEl.querySelector(t);if(e){e.focus();break}}}},this.t)}}change(e){this.target&&this.callbacks.onChange(this.target.id,e)}setStyle(e){this.target&&(this.style={...this.style,...e},this.change({style:e}))}setOptions(e){this.target&&(this.options={...this.options,...structuredClone(e)},this.change({options:e}))}setAnchor(e,t){this.target&&(this.anchors=this.anchors.map((n,r)=>r===e?{...n,...t}:n),this.change({anchors:this.anchors.map(e=>({...e}))}))}setIntervals(e){this.target&&(this.intervals=e,this.change({intervals:e}))}openTemplates(){let e=this.target;if(!e)return;if(this.menu?.isOpen()){this.menu.close();return}let r=this.callbacks.templates,i=r?.list(e.type)??[],a=[...i.map((e,t)=>({id:`${F}${t}`,label:e.name,icon:`layers`,remove:n.r(this.t(`templates.delete`),{name:e.name})})),...i.length>0?[`separator`]:[],...r?[{id:`save`,label:this.t(`settings.saveTemplate`),icon:`save`}]:[],...this.callbacks.onSaveDefault?[{id:`default`,label:this.t(`drawingSettings.saveDefault`),icon:`pin`}]:[],{id:`reset`,label:this.t(`settings.resetToDefaults`),icon:`reset`}],o=e=>i[Number(e.slice(2))];this.menu??=new t.f(this.modal,this.t(`settings.template`));let s=this.templateBtn.getBoundingClientRect(),c=this.modal.getBoundingClientRect();this.menu.open(a,s.left-c.left,s.top-c.top,t=>{t===`save`?r?.save({type:e.type,style:{...this.style},options:structuredClone(this.options)}):t===`default`?this.callbacks.onSaveDefault?.(e.type,structuredClone(this.options)):t===`reset`?this.resetOptions():t.startsWith(F)&&this.applyTemplate(o(t))},this.templateBtn,e=>{let t=o(e);t&&r?.remove(t.name,t.type)})}applyTemplate(e){let t=this.target;!e||!t||(Object.keys(e.style).length>0&&this.setStyle(e.style),e.options&&this.setOptions((0,i.sanitizeDrawingOptions)(t.defs,e.options)),this.renderBody())}resetOptions(){let e=this.target;if(!e)return;let t={};for(let[n,r]of Object.entries(e.defs))t[n]=structuredClone(r.default);this.setOptions(t),this.renderBody()}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}};exports.WidgetDrawingSettings=L;
2
+ //# sourceMappingURL=WidgetDrawingSettings-Cyl2u_u8.cjs.map