@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.
- package/dist/{LayoutSession-r7Rgc5Sw.cjs → LayoutSession-BrXo314m.cjs} +2 -2
- package/dist/{LayoutSession-r7Rgc5Sw.cjs.map → LayoutSession-BrXo314m.cjs.map} +1 -1
- package/dist/{LayoutSession-DjHF7tmL.js → LayoutSession-BrazGVET.js} +2 -2
- package/dist/{LayoutSession-DjHF7tmL.js.map → LayoutSession-BrazGVET.js.map} +1 -1
- package/dist/WidgetCommandPalette-CeCLHPy1.js +177 -0
- package/dist/WidgetCommandPalette-CeCLHPy1.js.map +1 -0
- package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs +2 -0
- package/dist/WidgetCommandPalette-ZZNbWwCZ.cjs.map +1 -0
- package/dist/{WidgetDrawingSettings-MSpeHFhV.js → WidgetDrawingSettings-BeW8fX7r.js} +5 -5
- package/dist/{WidgetDrawingSettings-MSpeHFhV.js.map → WidgetDrawingSettings-BeW8fX7r.js.map} +1 -1
- package/dist/{WidgetDrawingSettings-B3KAH9Ag.cjs → WidgetDrawingSettings-Cyl2u_u8.cjs} +2 -2
- package/dist/{WidgetDrawingSettings-B3KAH9Ag.cjs.map → WidgetDrawingSettings-Cyl2u_u8.cjs.map} +1 -1
- package/dist/WidgetGoToDate-BNdngHlg.cjs +2 -0
- package/dist/{WidgetGoToDate-CPzfStIm.cjs.map → WidgetGoToDate-BNdngHlg.cjs.map} +1 -1
- package/dist/{WidgetGoToDate-R2teTLpJ.js → WidgetGoToDate-hcDtezRm.js} +6 -5
- package/dist/{WidgetGoToDate-R2teTLpJ.js.map → WidgetGoToDate-hcDtezRm.js.map} +1 -1
- package/dist/{WidgetHotkeySheet-BE0glkVA.js → WidgetHotkeySheet-1lRFSdvt.js} +3 -3
- package/dist/{WidgetHotkeySheet-BE0glkVA.js.map → WidgetHotkeySheet-1lRFSdvt.js.map} +1 -1
- package/dist/{WidgetHotkeySheet-pnsXreK6.cjs → WidgetHotkeySheet-y5LWKSG6.cjs} +2 -2
- package/dist/{WidgetHotkeySheet-pnsXreK6.cjs.map → WidgetHotkeySheet-y5LWKSG6.cjs.map} +1 -1
- package/dist/WidgetIndicatorPicker-CGvZ5kYd.js +245 -0
- package/dist/WidgetIndicatorPicker-CGvZ5kYd.js.map +1 -0
- package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs +2 -0
- package/dist/WidgetIndicatorPicker-DuKJFPRC.cjs.map +1 -0
- package/dist/{WidgetIndicatorSettings-BJUjACzj.cjs → WidgetIndicatorSettings-C7vpco6e.cjs} +2 -2
- package/dist/{WidgetIndicatorSettings-BJUjACzj.cjs.map → WidgetIndicatorSettings-C7vpco6e.cjs.map} +1 -1
- package/dist/{WidgetIndicatorSettings-DgbsZk0h.js → WidgetIndicatorSettings-dDkqzzYo.js} +6 -6
- package/dist/{WidgetIndicatorSettings-DgbsZk0h.js.map → WidgetIndicatorSettings-dDkqzzYo.js.map} +1 -1
- package/dist/{WidgetSettings-D35fHGXL.js → WidgetSettings--XzDx515.js} +24 -11
- package/dist/WidgetSettings--XzDx515.js.map +1 -0
- package/dist/WidgetSettings-C0fnWGdY.cjs +2 -0
- package/dist/WidgetSettings-C0fnWGdY.cjs.map +1 -0
- package/dist/WidgetSymbolSearch-C4OR42nZ.cjs +2 -0
- package/dist/WidgetSymbolSearch-C4OR42nZ.cjs.map +1 -0
- package/dist/WidgetSymbolSearch-CvPpjIwa.js +580 -0
- package/dist/WidgetSymbolSearch-CvPpjIwa.js.map +1 -0
- package/dist/{focusTrap-Hm7AUTBw.js → focusTrap-Br9Zfje0.js} +1 -1
- package/dist/{focusTrap-Hm7AUTBw.js.map → focusTrap-Br9Zfje0.js.map} +1 -1
- package/dist/{focusTrap-B8OoGwHb.cjs → focusTrap-DrhhmG0V.cjs} +1 -1
- package/dist/{focusTrap-B8OoGwHb.cjs.map → focusTrap-DrhhmG0V.cjs.map} +1 -1
- package/dist/{i18n-DFR1Oukq.cjs → i18n-C2nQBZTY.cjs} +1 -1
- package/dist/{i18n-DFR1Oukq.cjs.map → i18n-C2nQBZTY.cjs.map} +1 -1
- package/dist/{i18n-CvnQcfor.js → i18n-C5v6Tzp1.js} +1 -1
- package/dist/{i18n-CvnQcfor.js.map → i18n-C5v6Tzp1.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{indicatorInputWords-D19kY9NO.js → indicatorInputWords-Bd9Pt6qp.js} +2 -2
- package/dist/{indicatorInputWords-D19kY9NO.js.map → indicatorInputWords-Bd9Pt6qp.js.map} +1 -1
- package/dist/{indicatorInputWords-DzrKBU2I.cjs → indicatorInputWords-CTA4IcJR.cjs} +2 -2
- package/dist/{indicatorInputWords-DzrKBU2I.cjs.map → indicatorInputWords-CTA4IcJR.cjs.map} +1 -1
- package/dist/{indicatorSettingsTabs-BLEULMJJ.cjs → indicatorSettingsTabs-B0liE3ST.cjs} +2 -2
- package/dist/{indicatorSettingsTabs-BLEULMJJ.cjs.map → indicatorSettingsTabs-B0liE3ST.cjs.map} +1 -1
- package/dist/{indicatorSettingsTabs-Wxx2v_y4.js → indicatorSettingsTabs-XcgMdbLp.js} +3 -3
- package/dist/{indicatorSettingsTabs-Wxx2v_y4.js.map → indicatorSettingsTabs-XcgMdbLp.js.map} +1 -1
- package/dist/monthView-B-CieFDJ.cjs +2 -0
- package/dist/monthView-B-CieFDJ.cjs.map +1 -0
- package/dist/monthView-N8yDaTqZ.js +198 -0
- package/dist/monthView-N8yDaTqZ.js.map +1 -0
- package/dist/{platformKeys-BAPueZ1I.cjs → platformKeys-B82-j1Fs.cjs} +1 -1
- package/dist/{platformKeys-BAPueZ1I.cjs.map → platformKeys-B82-j1Fs.cjs.map} +1 -1
- package/dist/{platformKeys-D_x_6ZrV.js → platformKeys-Dd1qOCjF.js} +1 -1
- package/dist/{platformKeys-D_x_6ZrV.js.map → platformKeys-Dd1qOCjF.js.map} +1 -1
- package/dist/{settingsControls-ef4j_clQ.js → settingsControls-DIjzCFHI.js} +2 -2
- package/dist/{settingsControls-ef4j_clQ.js.map → settingsControls-DIjzCFHI.js.map} +1 -1
- package/dist/{settingsControls-jNn0If4b.cjs → settingsControls-Tn8_vJJv.cjs} +2 -2
- package/dist/{settingsControls-jNn0If4b.cjs.map → settingsControls-Tn8_vJJv.cjs.map} +1 -1
- package/dist/symbolCatalog-CA1fpz3s.js +90 -0
- package/dist/symbolCatalog-CA1fpz3s.js.map +1 -0
- package/dist/symbolCatalog-Dx1YnZgm.cjs +2 -0
- package/dist/symbolCatalog-Dx1YnZgm.cjs.map +1 -0
- package/dist/widget/ChartWidget.d.ts +102 -580
- package/dist/widget/ChartWidget.d.ts.map +1 -1
- package/dist/widget/WidgetAlerts.d.ts +35 -0
- package/dist/widget/WidgetAlerts.d.ts.map +1 -0
- package/dist/widget/WidgetChartSettings.d.ts +211 -0
- package/dist/widget/WidgetChartSettings.d.ts.map +1 -0
- package/dist/widget/WidgetCommands.d.ts +54 -0
- package/dist/widget/WidgetCommands.d.ts.map +1 -0
- package/dist/widget/WidgetCompares.d.ts +140 -0
- package/dist/widget/WidgetCompares.d.ts.map +1 -0
- package/dist/widget/WidgetDockArea.d.ts +90 -0
- package/dist/widget/WidgetDockArea.d.ts.map +1 -0
- package/dist/widget/WidgetDrawingEdit.d.ts +73 -0
- package/dist/widget/WidgetDrawingEdit.d.ts.map +1 -0
- package/dist/widget/WidgetIntervals.d.ts +66 -0
- package/dist/widget/WidgetIntervals.d.ts.map +1 -0
- package/dist/widget/WidgetLayouts.d.ts +85 -0
- package/dist/widget/WidgetLayouts.d.ts.map +1 -0
- package/dist/widget/WidgetLegendView.d.ts +54 -0
- package/dist/widget/WidgetLegendView.d.ts.map +1 -0
- package/dist/widget/WidgetMenus.d.ts +79 -0
- package/dist/widget/WidgetMenus.d.ts.map +1 -0
- package/dist/widget/WidgetReplay.d.ts +199 -0
- package/dist/widget/WidgetReplay.d.ts.map +1 -0
- package/dist/widget/WidgetSettings.d.ts +2 -0
- package/dist/widget/WidgetSettings.d.ts.map +1 -1
- package/dist/widget/WidgetTrading.d.ts +48 -0
- package/dist/widget/WidgetTrading.d.ts.map +1 -0
- package/dist/widget/WidgetWatchlists.d.ts +77 -0
- package/dist/widget/WidgetWatchlists.d.ts.map +1 -0
- package/dist/widget/index.cjs +43 -29
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.js +9628 -9812
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/keyTarget.d.ts +4 -0
- package/dist/widget/keyTarget.d.ts.map +1 -1
- package/dist/widget/locales/index.cjs +1 -1
- package/dist/widget/locales/index.js +1 -1
- package/dist/widget/pageEnv.d.ts +5 -0
- package/dist/widget/pageEnv.d.ts.map +1 -0
- package/dist/widget/widgetDialogs.d.ts +3 -0
- package/dist/widget/widgetDialogs.d.ts.map +1 -1
- package/dist/widgetFeatures-BM6u0u74.js +186 -0
- package/dist/widgetFeatures-BM6u0u74.js.map +1 -0
- package/dist/widgetFeatures-BZ8OVPN2.cjs +3 -0
- package/dist/widgetFeatures-BZ8OVPN2.cjs.map +1 -0
- package/package.json +3 -3
- package/dist/WidgetGoToDate-CPzfStIm.cjs +0 -2
- package/dist/WidgetSettings-D35fHGXL.js.map +0 -1
- package/dist/WidgetSettings-Pgt3p6vv.cjs +0 -2
- package/dist/WidgetSettings-Pgt3p6vv.cjs.map +0 -1
- package/dist/widgetFeatures-BvkTbsQn.js +0 -378
- package/dist/widgetFeatures-BvkTbsQn.js.map +0 -1
- package/dist/widgetFeatures-Cy0fs3Nl.cjs +0 -3
- package/dist/widgetFeatures-Cy0fs3Nl.cjs.map +0 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
//#region src/widget/monthView.ts
|
|
2
|
+
var e = 42, t = 7, n = new Set([
|
|
3
|
+
"US",
|
|
4
|
+
"CA",
|
|
5
|
+
"MX",
|
|
6
|
+
"BR",
|
|
7
|
+
"JP",
|
|
8
|
+
"KR",
|
|
9
|
+
"TW",
|
|
10
|
+
"HK",
|
|
11
|
+
"IL",
|
|
12
|
+
"PH",
|
|
13
|
+
"IN",
|
|
14
|
+
"ZA",
|
|
15
|
+
"AU",
|
|
16
|
+
"TH",
|
|
17
|
+
"SA",
|
|
18
|
+
"PE",
|
|
19
|
+
"CO"
|
|
20
|
+
]);
|
|
21
|
+
function r(e) {
|
|
22
|
+
try {
|
|
23
|
+
let t = new Intl.Locale(e), r = t.getWeekInfo?.() ?? t.weekInfo;
|
|
24
|
+
return r ? r.firstDay === 7 ? 0 : 1 : +!n.has(t.maximize().region ?? "");
|
|
25
|
+
} catch {
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function i(n, r, i) {
|
|
30
|
+
let a = (new Date(Date.UTC(n, r, 1)).getUTCDay() - i + t) % t, o = [];
|
|
31
|
+
for (let t = 0; t < e; t++) {
|
|
32
|
+
let e = new Date(Date.UTC(n, r, 1 - a + t));
|
|
33
|
+
o.push({
|
|
34
|
+
year: e.getUTCFullYear(),
|
|
35
|
+
month: e.getUTCMonth(),
|
|
36
|
+
day: e.getUTCDate()
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return o;
|
|
40
|
+
}
|
|
41
|
+
var a = (e) => String(e).padStart(2, "0"), o = (e) => `${String(e.year).padStart(4, "0")}-${a(e.month + 1)}-${a(e.day)}`;
|
|
42
|
+
function s(e) {
|
|
43
|
+
let t = /^(\d{4})-(\d{2})-(\d{2})$/.exec(e ?? "");
|
|
44
|
+
return t ? {
|
|
45
|
+
year: Number(t[1]),
|
|
46
|
+
month: Number(t[2]) - 1,
|
|
47
|
+
day: Number(t[3])
|
|
48
|
+
} : null;
|
|
49
|
+
}
|
|
50
|
+
function c() {
|
|
51
|
+
let e = /* @__PURE__ */ new Date();
|
|
52
|
+
return {
|
|
53
|
+
year: e.getFullYear(),
|
|
54
|
+
month: e.getMonth(),
|
|
55
|
+
day: e.getDate()
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function l(e, t) {
|
|
59
|
+
let n = new Date(Date.UTC(e.year, e.month, e.day + t));
|
|
60
|
+
return {
|
|
61
|
+
year: n.getUTCFullYear(),
|
|
62
|
+
month: n.getUTCMonth(),
|
|
63
|
+
day: n.getUTCDate()
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
var u = class {
|
|
67
|
+
element;
|
|
68
|
+
grid;
|
|
69
|
+
titleEl;
|
|
70
|
+
chosen = "";
|
|
71
|
+
limits = {};
|
|
72
|
+
today = o(c());
|
|
73
|
+
view = {
|
|
74
|
+
year: 0,
|
|
75
|
+
month: 0
|
|
76
|
+
};
|
|
77
|
+
focusDay = c();
|
|
78
|
+
constructor(e, n, i) {
|
|
79
|
+
this.locale = n, this.onPick = i;
|
|
80
|
+
let a = document.createElement("div");
|
|
81
|
+
a.className = "tcw-calendar";
|
|
82
|
+
let o = document.createElement("div");
|
|
83
|
+
o.className = "tcw-calendar-head";
|
|
84
|
+
let s = document.createElement("div");
|
|
85
|
+
s.className = "tcw-calendar-title", s.setAttribute("aria-live", "polite"), o.append(this.navButton(e.prevMonth, -1), s, this.navButton(e.nextMonth, 1));
|
|
86
|
+
let c = document.createElement("div");
|
|
87
|
+
c.className = "tcw-calendar-weekdays", c.setAttribute("aria-hidden", "true");
|
|
88
|
+
let l = r(n), u = new Intl.DateTimeFormat(n, {
|
|
89
|
+
weekday: "narrow",
|
|
90
|
+
timeZone: "UTC"
|
|
91
|
+
});
|
|
92
|
+
for (let e = 0; e < t; e++) {
|
|
93
|
+
let n = document.createElement("span");
|
|
94
|
+
n.className = "tcw-calendar-weekday", n.textContent = u.format(new Date(Date.UTC(2023, 0, 1 + (l + e) % t))), c.appendChild(n);
|
|
95
|
+
}
|
|
96
|
+
let d = document.createElement("div");
|
|
97
|
+
d.className = "tcw-calendar-grid", d.setAttribute("role", "grid"), d.addEventListener("keydown", (e) => this.onKey(e)), d.addEventListener("click", (e) => {
|
|
98
|
+
let t = e.target.closest(".tcw-calendar-day");
|
|
99
|
+
t && !t.disabled && t.dataset.date && this.pick(t.dataset.date);
|
|
100
|
+
});
|
|
101
|
+
let f = document.createElement("div");
|
|
102
|
+
f.className = "tcw-calendar-foot";
|
|
103
|
+
let p = document.createElement("button");
|
|
104
|
+
p.type = "button", p.className = "tcw-calendar-today", p.textContent = e.today, p.addEventListener("click", () => this.pick(this.today)), f.appendChild(p), a.append(o, c, d, f), this.element = a, this.grid = d, this.titleEl = s;
|
|
105
|
+
}
|
|
106
|
+
show(e, t = {}) {
|
|
107
|
+
this.chosen = e, this.limits = t, this.today = s(t.today) ? t.today : o(c());
|
|
108
|
+
let n = this.clamp(s(e) ?? s(this.today));
|
|
109
|
+
this.focusDay = n, this.view = {
|
|
110
|
+
year: n.year,
|
|
111
|
+
month: n.month
|
|
112
|
+
}, this.render();
|
|
113
|
+
}
|
|
114
|
+
focusGrid() {
|
|
115
|
+
this.grid.querySelector("[tabindex=\"0\"]")?.focus();
|
|
116
|
+
}
|
|
117
|
+
navButton(e, t) {
|
|
118
|
+
let n = document.createElement("button");
|
|
119
|
+
return n.type = "button", n.className = "tcw-calendar-nav", n.setAttribute("aria-label", e), n.innerHTML = t < 0 ? "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M10 3.5L5.5 8l4.5 4.5\"/></svg>" : "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M6 3.5L10.5 8 6 12.5\"/></svg>", n.addEventListener("click", () => this.turn(t)), n;
|
|
120
|
+
}
|
|
121
|
+
turn(e) {
|
|
122
|
+
let t = new Date(Date.UTC(this.view.year, this.view.month + e, 1));
|
|
123
|
+
this.view = {
|
|
124
|
+
year: t.getUTCFullYear(),
|
|
125
|
+
month: t.getUTCMonth()
|
|
126
|
+
};
|
|
127
|
+
let n = new Date(Date.UTC(this.view.year, this.view.month + 1, 0)).getUTCDate();
|
|
128
|
+
this.focusDay = this.clamp({
|
|
129
|
+
...this.view,
|
|
130
|
+
day: Math.min(this.focusDay.day, n)
|
|
131
|
+
}), this.view = {
|
|
132
|
+
year: this.focusDay.year,
|
|
133
|
+
month: this.focusDay.month
|
|
134
|
+
}, this.render();
|
|
135
|
+
}
|
|
136
|
+
clamp(e) {
|
|
137
|
+
let t = o(e), n = s(this.limits.min), r = s(this.limits.max);
|
|
138
|
+
return n && t < o(n) ? n : r && t > o(r) ? r : e;
|
|
139
|
+
}
|
|
140
|
+
allowed(e) {
|
|
141
|
+
let { min: t, max: n } = this.limits;
|
|
142
|
+
return !(t && e < t) && !(n && e > n);
|
|
143
|
+
}
|
|
144
|
+
render() {
|
|
145
|
+
let e = this.locale;
|
|
146
|
+
this.titleEl.textContent = new Intl.DateTimeFormat(e, {
|
|
147
|
+
month: "long",
|
|
148
|
+
year: "numeric",
|
|
149
|
+
timeZone: "UTC"
|
|
150
|
+
}).format(new Date(Date.UTC(this.view.year, this.view.month, 1)));
|
|
151
|
+
let n = this.today, a = o(this.focusDay), s = new Intl.DateTimeFormat(e, {
|
|
152
|
+
dateStyle: "full",
|
|
153
|
+
timeZone: "UTC"
|
|
154
|
+
});
|
|
155
|
+
this.grid.textContent = "";
|
|
156
|
+
let c = null;
|
|
157
|
+
for (let [l, u] of i(this.view.year, this.view.month, r(e)).entries()) {
|
|
158
|
+
l % t === 0 && (c = document.createElement("div"), c.className = "tcw-calendar-week", c.setAttribute("role", "row"), this.grid.appendChild(c));
|
|
159
|
+
let e = o(u), r = document.createElement("button");
|
|
160
|
+
r.type = "button", r.className = "tcw-calendar-day", r.dataset.date = e, r.setAttribute("role", "gridcell"), r.textContent = String(u.day), r.setAttribute("aria-label", s.format(new Date(Date.UTC(u.year, u.month, u.day)))), r.setAttribute("aria-selected", String(e === this.chosen)), r.tabIndex = e === a ? 0 : -1, u.month !== this.view.month && r.classList.add("tcw-outside"), e === n && r.classList.add("tcw-today"), this.allowed(e) || (r.disabled = !0), c?.appendChild(r);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
onKey(e) {
|
|
164
|
+
let n = {
|
|
165
|
+
ArrowLeft: -1,
|
|
166
|
+
ArrowRight: 1,
|
|
167
|
+
ArrowUp: -t,
|
|
168
|
+
ArrowDown: t
|
|
169
|
+
}, r = this.element.isConnected && getComputedStyle(this.element).direction === "rtl";
|
|
170
|
+
if (e.key in n) {
|
|
171
|
+
let t = r && (e.key === "ArrowLeft" || e.key === "ArrowRight") ? -n[e.key] : n[e.key];
|
|
172
|
+
this.moveFocus(l(this.focusDay, t));
|
|
173
|
+
} else if (e.key === "PageUp" || e.key === "PageDown") this.turn(e.key === "PageUp" ? -1 : 1), this.focusGrid();
|
|
174
|
+
else if (e.key === "Home" || e.key === "End") {
|
|
175
|
+
let t = new Date(Date.UTC(this.view.year, this.view.month + 1, 0)).getUTCDate();
|
|
176
|
+
this.moveFocus({
|
|
177
|
+
...this.view,
|
|
178
|
+
day: e.key === "Home" ? 1 : t
|
|
179
|
+
});
|
|
180
|
+
} else if (e.key === "Enter" || e.key === " ") this.pick(o(this.focusDay));
|
|
181
|
+
else return;
|
|
182
|
+
e.preventDefault();
|
|
183
|
+
}
|
|
184
|
+
moveFocus(e) {
|
|
185
|
+
let t = this.clamp(e);
|
|
186
|
+
this.focusDay = t, (t.year !== this.view.year || t.month !== this.view.month) && (this.view = {
|
|
187
|
+
year: t.year,
|
|
188
|
+
month: t.month
|
|
189
|
+
}), this.render(), this.focusGrid();
|
|
190
|
+
}
|
|
191
|
+
pick(e) {
|
|
192
|
+
this.allowed(e) && this.onPick(e);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
//#endregion
|
|
196
|
+
export { u as t };
|
|
197
|
+
|
|
198
|
+
//# sourceMappingURL=monthView-N8yDaTqZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monthView-N8yDaTqZ.js","names":[],"sources":["../src/widget/monthView.ts"],"sourcesContent":["export interface CalendarLabels {\n prevMonth: string;\n nextMonth: string;\n today: string;\n}\n\nexport interface CalendarDay {\n year: number;\n /** 0–11. */\n month: number;\n day: number;\n}\n\n/** The first and last day that can be picked, and today, YYYY-MM-DD; each may be left out. */\nexport interface DayLimits {\n min?: string;\n max?: string;\n /** Today where the dates are read (the chart's time zone); the browser's when left out. */\n today?: string;\n}\n\nconst DAYS_SHOWN = 42;\nconst DAYS_IN_WEEK = 7;\n\n/** Regions whose weeks start on Sunday, for engines without week data. */\nconst SUNDAY_REGIONS = new Set(['US', 'CA', 'MX', 'BR', 'JP', 'KR', 'TW', 'HK', 'IL', 'PH', 'IN', 'ZA', 'AU', 'TH', 'SA', 'PE', 'CO']);\n\n/** The first day of the week where `locale` is spoken: 0 Sunday, 1 Monday. */\nexport function weekStart(locale: string): 0 | 1 {\n try {\n const loc = new Intl.Locale(locale) as Intl.Locale & {\n getWeekInfo?: () => { firstDay: number };\n weekInfo?: { firstDay: number };\n };\n const info = loc.getWeekInfo?.() ?? loc.weekInfo;\n if (info) return info.firstDay === 7 ? 0 : 1;\n return SUNDAY_REGIONS.has(loc.maximize().region ?? '') ? 0 : 1;\n } catch {\n return 1;\n }\n}\n\n/** Six whole weeks around month `month` of `year`, from `firstDay` (0 Sunday, 1 Monday). */\nexport function monthGrid(year: number, month: number, firstDay: 0 | 1): CalendarDay[] {\n const first = new Date(Date.UTC(year, month, 1));\n const lead = (first.getUTCDay() - firstDay + DAYS_IN_WEEK) % DAYS_IN_WEEK;\n const days: CalendarDay[] = [];\n for (let i = 0; i < DAYS_SHOWN; i++) {\n const d = new Date(Date.UTC(year, month, 1 - lead + i));\n days.push({ year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() });\n }\n return days;\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0');\nconst iso = (d: CalendarDay) => `${String(d.year).padStart(4, '0')}-${pad(d.month + 1)}-${pad(d.day)}`;\n\nfunction parseIso(value: string | undefined): CalendarDay | null {\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(value ?? '');\n if (!m) return null;\n return { year: Number(m[1]), month: Number(m[2]) - 1, day: Number(m[3]) };\n}\n\nfunction todayDay(): CalendarDay {\n const now = new Date();\n return { year: now.getFullYear(), month: now.getMonth(), day: now.getDate() };\n}\n\n/** `d` moved by `days` (any sign), across months and years. */\nfunction addDays(d: CalendarDay, days: number): CalendarDay {\n const t = new Date(Date.UTC(d.year, d.month, d.day + days));\n return { year: t.getUTCFullYear(), month: t.getUTCMonth(), day: t.getUTCDate() };\n}\n\n/**\n * A month of days drawn by the widget: arrows to turn it, today marked, the\n * chosen day marked, a button for today, and the keys a date grid takes. It\n * only tells which day was picked; what that day goes into is its owner's\n * business (a date field's calendar, the Go to dialog).\n */\nexport class MonthView {\n readonly element: HTMLDivElement;\n private readonly grid: HTMLDivElement;\n private readonly titleEl: HTMLDivElement;\n private chosen = '';\n private limits: DayLimits = {};\n private today = iso(todayDay());\n /** The month shown and the day the keys are on. */\n private view = { year: 0, month: 0 };\n private focusDay: CalendarDay = todayDay();\n\n constructor(\n labels: CalendarLabels,\n private readonly locale: string,\n private readonly onPick: (value: string) => void,\n ) {\n const element = document.createElement('div');\n element.className = 'tcw-calendar';\n\n const head = document.createElement('div');\n head.className = 'tcw-calendar-head';\n const title = document.createElement('div');\n title.className = 'tcw-calendar-title';\n title.setAttribute('aria-live', 'polite');\n head.append(this.navButton(labels.prevMonth, -1), title, this.navButton(labels.nextMonth, 1));\n\n const weekdays = document.createElement('div');\n weekdays.className = 'tcw-calendar-weekdays';\n weekdays.setAttribute('aria-hidden', 'true');\n const firstDay = weekStart(locale);\n const dayName = new Intl.DateTimeFormat(locale, { weekday: 'narrow', timeZone: 'UTC' });\n for (let i = 0; i < DAYS_IN_WEEK; i++) {\n // 2023-01-01 was a Sunday.\n const cell = document.createElement('span');\n cell.className = 'tcw-calendar-weekday';\n cell.textContent = dayName.format(new Date(Date.UTC(2023, 0, 1 + ((firstDay + i) % DAYS_IN_WEEK))));\n weekdays.appendChild(cell);\n }\n\n const grid = document.createElement('div');\n grid.className = 'tcw-calendar-grid';\n grid.setAttribute('role', 'grid');\n grid.addEventListener('keydown', (e) => this.onKey(e));\n grid.addEventListener('click', (e) => {\n const day = (e.target as Element).closest<HTMLButtonElement>('.tcw-calendar-day');\n if (day && !day.disabled && day.dataset.date) this.pick(day.dataset.date);\n });\n\n const foot = document.createElement('div');\n foot.className = 'tcw-calendar-foot';\n const today = document.createElement('button');\n today.type = 'button';\n today.className = 'tcw-calendar-today';\n today.textContent = labels.today;\n today.addEventListener('click', () => this.pick(this.today));\n foot.appendChild(today);\n\n element.append(head, weekdays, grid, foot);\n this.element = element;\n this.grid = grid;\n this.titleEl = title;\n }\n\n /** Show the month of `value` (today's when it is no date), that day marked, days outside `limits` greyed. */\n show(value: string, limits: DayLimits = {}): void {\n this.chosen = value;\n this.limits = limits;\n this.today = parseIso(limits.today) ? limits.today! : iso(todayDay());\n const start = this.clamp(parseIso(value) ?? parseIso(this.today)!);\n this.focusDay = start;\n this.view = { year: start.year, month: start.month };\n this.render();\n }\n\n /** Moves keyboard focus onto the days. */\n focusGrid(): void {\n this.grid.querySelector<HTMLButtonElement>('[tabindex=\"0\"]')?.focus();\n }\n\n private navButton(label: string, by: -1 | 1): HTMLButtonElement {\n const b = document.createElement('button');\n b.type = 'button';\n b.className = 'tcw-calendar-nav';\n b.setAttribute('aria-label', label);\n b.innerHTML = by < 0\n ? '<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M10 3.5L5.5 8l4.5 4.5\"/></svg>'\n : '<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M6 3.5L10.5 8 6 12.5\"/></svg>';\n b.addEventListener('click', () => this.turn(by));\n return b;\n }\n\n private turn(by: number): void {\n const t = new Date(Date.UTC(this.view.year, this.view.month + by, 1));\n this.view = { year: t.getUTCFullYear(), month: t.getUTCMonth() };\n // The keys' day follows into the new month, on the same date where it has one.\n const last = new Date(Date.UTC(this.view.year, this.view.month + 1, 0)).getUTCDate();\n this.focusDay = this.clamp({ ...this.view, day: Math.min(this.focusDay.day, last) });\n this.view = { year: this.focusDay.year, month: this.focusDay.month };\n this.render();\n }\n\n /** `day` moved inside the limits, so the keys never rest on a greyed day. */\n private clamp(day: CalendarDay): CalendarDay {\n const value = iso(day);\n const min = parseIso(this.limits.min);\n const max = parseIso(this.limits.max);\n if (min && value < iso(min)) return min;\n if (max && value > iso(max)) return max;\n return day;\n }\n\n private allowed(value: string): boolean {\n const { min, max } = this.limits;\n return !(min && value < min) && !(max && value > max);\n }\n\n private render(): void {\n const locale = this.locale;\n this.titleEl.textContent = new Intl.DateTimeFormat(locale, { month: 'long', year: 'numeric', timeZone: 'UTC' })\n .format(new Date(Date.UTC(this.view.year, this.view.month, 1)));\n const today = this.today;\n const focus = iso(this.focusDay);\n const fullDate = new Intl.DateTimeFormat(locale, { dateStyle: 'full', timeZone: 'UTC' });\n this.grid.textContent = '';\n let row: HTMLElement | null = null;\n for (const [i, day] of monthGrid(this.view.year, this.view.month, weekStart(locale)).entries()) {\n if (i % DAYS_IN_WEEK === 0) {\n row = document.createElement('div');\n row.className = 'tcw-calendar-week';\n row.setAttribute('role', 'row');\n this.grid.appendChild(row);\n }\n const value = iso(day);\n const b = document.createElement('button');\n b.type = 'button';\n b.className = 'tcw-calendar-day';\n b.dataset.date = value;\n b.setAttribute('role', 'gridcell');\n b.textContent = String(day.day);\n b.setAttribute('aria-label', fullDate.format(new Date(Date.UTC(day.year, day.month, day.day))));\n b.setAttribute('aria-selected', String(value === this.chosen));\n b.tabIndex = value === focus ? 0 : -1;\n if (day.month !== this.view.month) b.classList.add('tcw-outside');\n if (value === today) b.classList.add('tcw-today');\n if (!this.allowed(value)) b.disabled = true;\n row?.appendChild(b);\n }\n }\n\n private onKey(e: KeyboardEvent): void {\n const moves: Record<string, number> = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -DAYS_IN_WEEK, ArrowDown: DAYS_IN_WEEK };\n const rtl = this.element.isConnected && getComputedStyle(this.element).direction === 'rtl';\n if (e.key in moves) {\n const by = rtl && (e.key === 'ArrowLeft' || e.key === 'ArrowRight') ? -moves[e.key] : moves[e.key];\n this.moveFocus(addDays(this.focusDay, by));\n } else if (e.key === 'PageUp' || e.key === 'PageDown') {\n this.turn(e.key === 'PageUp' ? -1 : 1);\n this.focusGrid();\n } else if (e.key === 'Home' || e.key === 'End') {\n const last = new Date(Date.UTC(this.view.year, this.view.month + 1, 0)).getUTCDate();\n this.moveFocus({ ...this.view, day: e.key === 'Home' ? 1 : last });\n } else if (e.key === 'Enter' || e.key === ' ') {\n this.pick(iso(this.focusDay));\n } else {\n return;\n }\n e.preventDefault();\n }\n\n private moveFocus(target: CalendarDay): void {\n const day = this.clamp(target);\n this.focusDay = day;\n if (day.year !== this.view.year || day.month !== this.view.month) {\n this.view = { year: day.year, month: day.month };\n }\n this.render();\n this.focusGrid();\n }\n\n private pick(value: string): void {\n if (this.allowed(value)) this.onPick(value);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
var e=typeof navigator<`u`&&/mac|iphone|ipad|ipod/i.test(navigator.platform||navigator.userAgent)?`⌘`:`Ctrl`,t=e=>e.toLowerCase().replace(/[\s+]+/g,``).replace(/[\u2010-\u2015\u2212]/g,`-`);Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return t}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return e}});
|
|
2
|
-
//# sourceMappingURL=platformKeys-
|
|
2
|
+
//# sourceMappingURL=platformKeys-B82-j1Fs.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformKeys-
|
|
1
|
+
{"version":3,"file":"platformKeys-B82-j1Fs.cjs","names":[],"sources":["../src/widget/platformKeys.ts"],"sourcesContent":["/** Whether the keyboard is a Mac's, where Ctrl's shortcuts take ⌘. */\nconst isMac =\n typeof navigator !== 'undefined' && /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent);\n\n/** The key Ctrl stands for here: ⌘ on a Mac. */\nexport const MOD_KEY = isMac ? '⌘' : 'Ctrl';\n\n/** Text as a search compares it: lower case, no spaces or pluses (\"alt g\", \"Alt+G\" alike), any dash a hyphen (\"0–9\"). */\nexport const searchForm = (text: string): string =>\n text.toLowerCase().replace(/[\\s+]+/g, '').replace(/[\\u2010-\\u2015\\u2212]/g, '-');\n"],"mappings":"AAKA,IAAa,EAHX,OAAO,UAAc,KAAe,wBAAwB,KAAK,UAAU,UAAY,UAAU,UAAU,CAG9E,IAAM,OAGxB,EAAc,GACzB,EAAK,aAAa,CAAC,QAAQ,UAAW,GAAG,CAAC,QAAQ,yBAA0B,IAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platformKeys-
|
|
1
|
+
{"version":3,"file":"platformKeys-Dd1qOCjF.js","names":[],"sources":["../src/widget/platformKeys.ts"],"sourcesContent":["/** Whether the keyboard is a Mac's, where Ctrl's shortcuts take ⌘. */\nconst isMac =\n typeof navigator !== 'undefined' && /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent);\n\n/** The key Ctrl stands for here: ⌘ on a Mac. */\nexport const MOD_KEY = isMac ? '⌘' : 'Ctrl';\n\n/** Text as a search compares it: lower case, no spaces or pluses (\"alt g\", \"Alt+G\" alike), any dash a hyphen (\"0–9\"). */\nexport const searchForm = (text: string): string =>\n text.toLowerCase().replace(/[\\s+]+/g, '').replace(/[\\u2010-\\u2015\\u2212]/g, '-');\n"],"mappings":"AAKA,IAAa,IAHX,OAAO,YAAc,OAAe,wBAAwB,KAAK,UAAU,YAAY,UAAU,UAAU,GAG9E,MAAM,QAGxB,KAAc,MACzB,EAAK,aAAa,CAAC,QAAQ,WAAW,GAAG,CAAC,QAAQ,0BAA0B,IAAI"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as e } from "./focusTrap-
|
|
1
|
+
import { i as e } from "./focusTrap-Br9Zfje0.js";
|
|
2
2
|
import { stepDecimals as t } from "@tradecanvas/commons";
|
|
3
3
|
//#region src/widget/WidgetContextMenu.ts
|
|
4
4
|
var n = 4, r = 2, i = 150, a = "[role=menuitem], [role=menuitemcheckbox], [role=menuitemradio]", o = "(max-width: 640px)", s = "tcw:back", c = /* @__PURE__ */ new Set();
|
|
@@ -376,4 +376,4 @@ function D(t, n) {
|
|
|
376
376
|
//#endregion
|
|
377
377
|
export { T as a, D as c, C as d, f, g as i, y as l, S as n, v as o, _ as r, p as s, m as t, h as u };
|
|
378
378
|
|
|
379
|
-
//# sourceMappingURL=settingsControls-
|
|
379
|
+
//# sourceMappingURL=settingsControls-DIjzCFHI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settingsControls-ef4j_clQ.js","names":[],"sources":["../src/widget/WidgetContextMenu.ts","../src/widget/settingsControls.ts"],"sourcesContent":["import { createIcon } from './icons.js';\n\nexport type ContextMenuEntry =\n /**\n * `checked` makes it a switch (ticked when on); with `radio` it is one\n * choice among the radios beside it. `shortcut` (\"Alt+I\") is written at\n * the row's end. `submenu` opens more entries beside this one. `remove`\n * (its action's name, \"Delete template Swing\") gives it a remove button\n * and the Delete key; the menu's `onRemove` hears it.\n */\n | {\n id: string;\n label: string;\n icon?: string;\n danger?: boolean;\n checked?: boolean;\n radio?: boolean;\n shortcut?: string;\n /** How many things it acts on (\"Remove drawings 3\"), at the row's end and read with its name. */\n count?: number;\n submenu?: readonly ContextMenuEntry[];\n remove?: string;\n }\n | 'separator';\n\ntype MenuItem = Exclude<ContextMenuEntry, 'separator'>;\n\n/** Room kept between the menu and the edge it would touch. */\nconst MENU_MARGIN = 4;\n/** Room between a menu and the button it hangs under. */\nconst MENU_GAP = 2;\n/**\n * How long the pointer rests on an entry before its submenu opens, or the\n * open one closes: crossing an entry on the way into a submenu does neither.\n */\nconst HOVER_DELAY_MS = 150;\nconst ITEM_SELECTOR = '[role=menuitem], [role=menuitemcheckbox], [role=menuitemradio]';\n/** Where menus open as sheets from the bottom (the stylesheet's phone width): a submenu covers its menu. */\nconst SHEET_QUERY = '(max-width: 640px)';\n/** The way back from a submenu opened as a sheet. */\nconst BACK_ID = 'tcw:back';\n\n/**\n * The menus open now, on every widget of the page: a button that several\n * menus hang from (\"more\", for those picked from it) closes whichever is open.\n */\nconst openMenus = new Set<WidgetContextMenu>();\n\n/** Where a menu opens along each axis, in the host's coordinates. */\ninterface MenuPlace {\n x: number;\n y: number;\n /** Where it ends instead, when there is no room after `x` or below `y`. */\n xBack: number;\n yBack: number;\n /** Ending at `xBack` first, and starting at `x` only without room there (a submenu, right to left). */\n before?: boolean;\n}\n\n/**\n * Where a menu `size` long starts along one axis: at `at`, or ending at\n * `back` when it would pass `end`; with `before`, the other way round.\n */\nfunction place(at: number, size: number, end: number, back = at, before = false): number {\n const start = before\n ? (back - size >= 0 ? back - size : at)\n : (at + size > end ? back - size : at);\n return Math.max(0, Math.min(start, end - size));\n}\n\nfunction itemRole(entry: MenuItem): string {\n if (entry.radio) return 'menuitemradio';\n return entry.checked === undefined ? 'menuitem' : 'menuitemcheckbox';\n}\n\n/** What leads an entry: a tick on a switch that is on, a dot on the choice made, else its icon or room for one. */\nfunction leadMark(entry: MenuItem): string {\n const blank = '<span class=\"tcw-context-icon\"></span>';\n if (entry.radio) return entry.checked ? '<span class=\"tcw-context-icon tcw-context-dot\"></span>' : blank;\n // A switch that is off shows its icon, as an action does.\n if (entry.checked) return createIcon('check', 14);\n return (entry.icon && createIcon(entry.icon, 14)) || blank;\n}\n\n/**\n * A small menu opened at a point (a right-click on a drawing, say): arrow\n * keys, Home and End move through it, Enter picks, Escape, Tab or a click\n * outside close it. Focus returns to where it was. An entry with a submenu\n * opens it beside itself on a click, Enter, the right arrow (the left one\n * right to left) or a rest of the pointer; the other arrow or Escape closes\n * it again.\n */\nexport class WidgetContextMenu {\n private el: HTMLDivElement;\n private onPick: ((id: string) => void) | null = null;\n private onRemove: ((id: string) => void) | null = null;\n private returnFocus: HTMLElement | null = null;\n /** The button that opened it: pressing it again is its click's to handle (a toggle). */\n private anchor: HTMLElement | null = null;\n /** The submenu, made the first time an entry opens one. */\n private child: WidgetContextMenu | null = null;\n /** The menu this one is the submenu of. */\n private parent: WidgetContextMenu | null = null;\n private hoverTimer: ReturnType<typeof setTimeout> | null = null;\n /** The entry `hoverTimer` counts the rest on: moving on it counts on, not again. */\n private hoverItem: HTMLElement | null = null;\n private readonly onDocPointer = (e: Event) => {\n const target = e.target as Node;\n if (this.holds(target) || this.anchor?.contains(target)) return;\n this.close();\n };\n /** Scrolling a long menu keeps it; scrolling anywhere else moves the chart from under it. */\n private readonly onWheel = (e: Event) => {\n if (!this.holds(e.target as Node)) this.close();\n };\n /** Resizing or leaving the window moves the chart from under the menu. */\n private readonly onAway = () => this.close();\n\n /**\n * `backLabel` (\"Back\") names the first entry of a submenu on a phone, which\n * leads back to the menu under it.\n */\n constructor(private readonly host: HTMLElement, label: string, private readonly backLabel = '') {\n this.el = document.createElement('div');\n this.el.className = 'tcw-context-menu';\n this.el.setAttribute('role', 'menu');\n this.el.setAttribute('aria-label', label);\n this.el.hidden = true;\n this.el.addEventListener('keydown', (e) => this.onKeyDown(e));\n host.appendChild(this.el);\n }\n\n /**\n * Open at (`x`, `y`) in the host's coordinates. It opens up or to the left\n * of the point when there is no room below or to the right, ending at\n * `xBack` then (a menu under a button ends at the button's far edge), and\n * stays inside both the host and the window. With `before` it ends at\n * `xBack` first, and starts at `x` only without room before it.\n */\n open(\n entries: readonly ContextMenuEntry[],\n x: number,\n y: number,\n onPick: (id: string) => void,\n anchor?: HTMLElement,\n onRemove?: (id: string) => void,\n xBack = x,\n before = false,\n ): void {\n this.show(entries, { x, y, xBack, yBack: y, before }, onPick, anchor, onRemove, true);\n }\n\n /**\n * Open under `anchor`, `gap` px below it, lined up with it as the\n * toolbar's own menus are, mirrored right to left: from its start edge\n * (`'start'`), or ending at its end edge (`'end'`: \"more\", at the\n * toolbar's end); from its other edge when there is no room that way.\n */\n openUnder(\n entries: readonly ContextMenuEntry[],\n anchor: HTMLElement,\n onPick: (id: string) => void,\n align: 'start' | 'end' = 'start',\n gap = MENU_GAP,\n ): void {\n const at = anchor.getBoundingClientRect();\n const box = this.host.getBoundingClientRect();\n // Ending at the button's right edge first: its end left to right, its start right to left.\n const before = (align === 'end') !== this.rtl();\n this.open(entries, at.left - box.left, at.bottom - box.top + gap, onPick, anchor, undefined, at.right - box.left, before);\n }\n\n /**\n * Open beside `anchor` (a button of the drawing bar), level with its top:\n * past its end edge, or before its start edge without room there; ending\n * at its bottom when there is no room below.\n */\n openBeside(entries: readonly ContextMenuEntry[], anchor: HTMLElement, onPick: (id: string) => void): void {\n const at = anchor.getBoundingClientRect();\n const box = this.host.getBoundingClientRect();\n // Starting past its right edge, or ending before its left one: right to left, the left first.\n const right = at.right - box.left + MENU_GAP;\n const left = at.left - box.left - MENU_GAP;\n this.show(entries, { x: right, y: at.top - box.top, xBack: left, yBack: at.bottom - box.top, before: this.rtl() }, onPick, anchor, undefined, true);\n // Safari doesn't focus a pressed button: the keyboard goes back to it all the same.\n this.returnFocus = anchor;\n }\n\n /**\n * Close every menu open under `anchor`, and say whether one was. Its\n * button shows it open (aria-expanded), so a press on it closes it.\n */\n static closeAnchoredTo(anchor: HTMLElement): boolean {\n const under = [...openMenus].filter((menu) => menu.anchor === anchor);\n for (const menu of under) menu.close();\n return under.length > 0;\n }\n\n /** The button an open menu that holds the keyboard hangs from; null when none does. */\n static focusedAnchor(): HTMLElement | null {\n return [...openMenus].find((menu) => menu.anchor !== null && menu.hasFocus())?.anchor ?? null;\n }\n\n close(): void {\n this.clearHover();\n this.child?.close();\n if (this.el.hidden) return;\n openMenus.delete(this);\n // A submenu the pointer closes leaves the keyboard where it is, in the menu.\n const giveBack = !this.parent || this.el.contains(document.activeElement);\n this.el.hidden = true;\n this.onPick = null;\n this.onRemove = null;\n this.anchor?.setAttribute('aria-expanded', 'false');\n this.anchor = null;\n document.removeEventListener('pointerdown', this.onDocPointer, true);\n document.removeEventListener('wheel', this.onWheel, { capture: true });\n window.removeEventListener('resize', this.onAway);\n window.removeEventListener('blur', this.onAway);\n if (giveBack && this.returnFocus?.isConnected) this.returnFocus.focus({ preventScroll: true });\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return !this.el.hidden;\n }\n\n /** Whether the keyboard is in the menu, or in a submenu of it. */\n hasFocus(): boolean {\n return document.activeElement !== null && this.holds(document.activeElement);\n }\n\n destroy(): void {\n this.close();\n this.child?.destroy();\n this.el.remove();\n }\n\n private show(\n entries: readonly ContextMenuEntry[],\n at: MenuPlace,\n onPick: (id: string) => void,\n anchor: HTMLElement | undefined,\n onRemove: ((id: string) => void) | undefined,\n focusFirst: boolean,\n ): void {\n this.clearHover();\n this.child?.close();\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.onPick = onPick;\n this.onRemove = onRemove ?? null;\n this.anchor = anchor ?? null;\n this.anchor?.setAttribute('aria-expanded', 'true');\n this.el.replaceChildren(...entries.map((entry) => this.entry(entry)));\n this.el.hidden = false;\n openMenus.add(this);\n const hostRect = this.host.getBoundingClientRect();\n const right = Math.min(this.host.clientWidth, window.innerWidth - hostRect.left) - MENU_MARGIN;\n const bottom = Math.min(this.host.clientHeight, window.innerHeight - hostRect.top) - MENU_MARGIN;\n // Taller than the room (a short widget, the time zones), it scrolls, its padding and border in the room too.\n this.el.style.maxHeight = `${Math.max(0, bottom - this.frameHeight())}px`;\n this.el.style.left = `${place(at.x, this.el.offsetWidth, right, at.xBack, at.before)}px`;\n this.el.style.top = `${place(at.y, this.el.offsetHeight, bottom, at.yBack)}px`;\n document.addEventListener('pointerdown', this.onDocPointer, true);\n document.addEventListener('wheel', this.onWheel, { capture: true, passive: true });\n window.addEventListener('resize', this.onAway);\n window.addEventListener('blur', this.onAway);\n const chosen = this.revealChoice();\n if (focusFirst) (chosen ?? this.items()[0])?.focus({ preventScroll: true });\n }\n\n /** What its padding and border add to a max height, which sizes only its rows unless its box counts them. */\n private frameHeight(): number {\n const style = getComputedStyle(this.el);\n if (style.boxSizing === 'border-box') return 0;\n const px = (value: string) => parseFloat(value) || 0;\n return px(style.paddingTop) + px(style.paddingBottom) + px(style.borderTopWidth) + px(style.borderBottomWidth);\n }\n\n /**\n * A long list of choices (the time zones) opens scrolled to the one made,\n * returned to take the keyboard; null when it is in sight already.\n */\n private revealChoice(): HTMLElement | null {\n const chosen = this.el.querySelector<HTMLElement>('[role=menuitemradio][aria-checked=true]');\n if (!chosen || chosen.offsetTop + chosen.offsetHeight <= this.el.clientHeight) return null;\n this.el.scrollTop = chosen.offsetTop - (this.el.clientHeight - chosen.offsetHeight) / 2;\n return chosen;\n }\n\n /** The widget reads right to left (`dir=\"rtl\"`): submenus open to the left, and the arrows swap. */\n private rtl(): boolean {\n return this.host.closest('[dir]')?.getAttribute('dir') === 'rtl';\n }\n\n /** Whether `node` is in this menu or its open submenu. */\n private holds(node: Node): boolean {\n return this.el.contains(node) || (this.child?.holds(node) ?? false);\n }\n\n private entry(entry: ContextMenuEntry): HTMLElement {\n if (entry === 'separator') {\n const sep = document.createElement('div');\n sep.className = 'tcw-context-sep';\n sep.setAttribute('role', 'separator');\n return sep;\n }\n const item = document.createElement('button');\n item.type = 'button';\n item.className = `tcw-context-item${entry.danger ? ' tcw-context-danger' : ''}`;\n item.setAttribute('role', itemRole(entry));\n if (entry.radio || entry.checked !== undefined) item.setAttribute('aria-checked', String(entry.checked ?? false));\n item.tabIndex = -1;\n item.dataset.id = entry.id;\n item.innerHTML = leadMark(entry);\n const text = document.createElement('span');\n text.className = 'tcw-context-label';\n text.textContent = entry.label;\n item.appendChild(text);\n this.appendEnd(item, entry);\n const submenu = entry.submenu;\n // A finger's tap is a click: only a pointer resting on an entry opens its submenu. A pointer that\n // moved there, not one that entered: a menu that opens under a still pointer (a right-click near\n // a phone's foot, where it opens as a sheet) is entered by it, and opened a submenu unasked.\n item.addEventListener('pointermove', (e) => {\n if ((e as PointerEvent).pointerType !== 'touch') this.hover(item, entry.label, submenu);\n });\n item.addEventListener('click', () => {\n if (submenu) this.openSubmenu(item, entry.label, submenu, true);\n else this.pick(entry.id);\n });\n return entry.remove ? this.removableRow(item, entry.remove) : item;\n }\n\n /** The count, the shortcut, or the mark of a submenu, at the row's end. */\n private appendEnd(item: HTMLButtonElement, entry: MenuItem): void {\n if (entry.count !== undefined) {\n const count = document.createElement('span');\n count.className = 'tcw-context-count';\n count.textContent = String(entry.count);\n item.appendChild(count);\n }\n if (entry.shortcut) {\n // Named once, by aria-keyshortcuts: the key is for the eye.\n item.setAttribute('aria-keyshortcuts', entry.shortcut);\n const key = document.createElement('kbd');\n key.className = 'tcw-context-key';\n key.setAttribute('aria-hidden', 'true');\n key.textContent = entry.shortcut;\n item.appendChild(key);\n }\n if (entry.submenu) {\n item.setAttribute('aria-haspopup', 'menu');\n item.setAttribute('aria-expanded', 'false');\n item.insertAdjacentHTML('beforeend', createIcon('chevronRight', 12));\n item.lastElementChild?.classList.add('tcw-context-more');\n }\n }\n\n /** The keyboard removes it with Delete; the button is for a pointer. */\n private removableRow(item: HTMLButtonElement, action: string): HTMLElement {\n item.setAttribute('aria-keyshortcuts', 'Delete');\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-context-remove';\n remove.tabIndex = -1;\n remove.setAttribute('aria-hidden', 'true');\n remove.title = action;\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => this.remove(item));\n const row = document.createElement('div');\n row.className = 'tcw-context-row';\n row.setAttribute('role', 'none');\n row.append(item, remove);\n return row;\n }\n\n /** Open `entries` beside `item`, after it (to its right, or left in RTL) or, without room there, before it. */\n private openSubmenu(item: HTMLButtonElement, label: string, entries: readonly ContextMenuEntry[], focusFirst: boolean): void {\n this.clearHover();\n const child = this.child ?? this.makeChild();\n if (child.isOpen() && child.anchor === item) {\n if (focusFirst) child.items()[0]?.focus({ preventScroll: true });\n return;\n }\n child.close();\n child.el.setAttribute('aria-label', label);\n const hostRect = this.host.getBoundingClientRect();\n const r = item.getBoundingClientRect();\n const at: MenuPlace = {\n x: r.right - hostRect.left,\n y: r.top - hostRect.top,\n xBack: r.left - hostRect.left,\n yBack: r.bottom - hostRect.top,\n before: this.rtl(),\n };\n // As a sheet it hides the menu under it: its first entry leads back there.\n const sheet = window.matchMedia?.(SHEET_QUERY).matches ?? false;\n const list: readonly ContextMenuEntry[] = sheet\n ? [{ id: BACK_ID, label, icon: 'chevronLeft' }, 'separator', ...entries]\n : entries;\n child.show(list, at, (id) => {\n if (id !== BACK_ID) this.pick(id);\n }, item, (id) => this.onRemove?.(id), focusFirst);\n const back = sheet ? child.el.querySelector<HTMLElement>(`[data-id=\"${BACK_ID}\"]`) : null;\n back?.classList.add('tcw-context-back');\n if (back && this.backLabel) back.setAttribute('aria-label', `${this.backLabel}: ${label}`);\n // Closed, it gives the keyboard back to its entry (a click on a button doesn't focus it everywhere).\n child.returnFocus = item;\n }\n\n private makeChild(): WidgetContextMenu {\n const child = new WidgetContextMenu(this.host, '', this.backLabel);\n child.parent = this;\n // Reaching the submenu keeps it, whatever the pointer crossed on the way.\n child.el.addEventListener('pointerenter', () => this.clearHover());\n this.child = child;\n return child;\n }\n\n /** After a rest on `item`: open its submenu, or close the one open for another entry. */\n private hover(item: HTMLButtonElement, label: string, submenu?: readonly ContextMenuEntry[]): void {\n if (this.hoverTimer !== null && this.hoverItem === item) return;\n this.clearHover();\n const open = this.child?.isOpen() ? this.child.anchor : null;\n if (submenu ? open === item : open === null) return;\n this.hoverItem = item;\n this.hoverTimer = setTimeout(() => {\n this.hoverTimer = null;\n this.hoverItem = null;\n if (submenu) this.openSubmenu(item, label, submenu, false);\n else this.child?.close();\n }, HOVER_DELAY_MS);\n }\n\n private clearHover(): void {\n this.hoverItem = null;\n if (this.hoverTimer === null) return;\n clearTimeout(this.hoverTimer);\n this.hoverTimer = null;\n }\n\n /** Take an entry out (its `onRemove` told), the keyboard on the one after it; the menu closes when none is left. */\n private remove(item: HTMLButtonElement): void {\n const id = item.dataset.id;\n const row = item.closest('.tcw-context-row');\n if (!id || !row) return;\n const items = this.items();\n const at = items.indexOf(item);\n this.onRemove?.(id);\n row.remove();\n // A separator left first, last or next to another goes too.\n const seps = [...this.el.children].filter((el, i, all) => el.classList.contains('tcw-context-sep')\n && (i === 0 || i === all.length - 1 || all[i - 1].classList.contains('tcw-context-sep')));\n for (const sep of seps) sep.remove();\n const left = this.items();\n if (left.length === 0) {\n this.close();\n return;\n }\n left[Math.min(at, left.length - 1)].focus({ preventScroll: true });\n }\n\n private pick(id: string): void {\n const onPick = this.onPick;\n this.close();\n onPick?.(id);\n }\n\n private items(): HTMLButtonElement[] {\n return [...this.el.querySelectorAll<HTMLButtonElement>(ITEM_SELECTOR)];\n }\n\n private onKeyDown(e: KeyboardEvent): void {\n const items = this.items();\n const at = items.indexOf(document.activeElement as HTMLButtonElement);\n const [openKey, backKey] = this.rtl() ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n let next = -1;\n if (e.key === 'ArrowDown') next = (at + 1) % items.length;\n else if (e.key === 'ArrowUp') next = (at - 1 + items.length) % items.length;\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = items.length - 1;\n else if (e.key === openKey && items[at]?.hasAttribute('aria-haspopup')) {\n e.preventDefault();\n items[at].click();\n return;\n } else if (e.key === backKey && this.parent) {\n e.preventDefault();\n this.close();\n return;\n } else if (e.key === 'Delete' && at >= 0) {\n e.preventDefault();\n this.remove(items[at]);\n return;\n } else if (e.key === 'Escape' || e.key === 'Tab') {\n e.preventDefault();\n e.stopPropagation();\n // Escape steps back out of a submenu; Tab leaves the menus altogether.\n let menu: WidgetContextMenu = this;\n while (e.key === 'Tab' && menu.parent) menu = menu.parent;\n menu.close();\n return;\n } else return;\n e.preventDefault();\n // Moving on from the entry a resting pointer opened a submenu for closes it.\n this.child?.close();\n items[next]?.focus({ preventScroll: true });\n }\n}\n","import { stepDecimals } from '@tradecanvas/commons';\nimport { createIcon } from './icons.js';\n\n/**\n * Form controls shared by the settings dialogs and the order ticket: a\n * labelled row, a ticked row, a switch, a number field, a colour picker, a\n * select, a row of choices and a number between − and +. Each calls back on\n * change.\n */\n\n/** A labelled control: clicking the label reaches the control. */\nexport function settingsRow(label: string, control: HTMLElement): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-settings-row';\n const text = document.createElement('span');\n text.className = 'tcw-settings-label';\n text.textContent = label;\n row.append(text, control);\n return row;\n}\n\n/** A box ticked or not, its label after it: the whole row ticks it. */\nexport function checkRow(label: string, value: boolean, onChange: (value: boolean) => void): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-settings-row tcw-check-row';\n const box = document.createElement('input');\n box.type = 'checkbox';\n box.checked = value;\n box.addEventListener('change', () => onChange(box.checked));\n const text = document.createElement('span');\n text.className = 'tcw-settings-label';\n text.textContent = label;\n row.append(box, text);\n return row;\n}\n\nexport function toggleSwitch(value: boolean, onChange: (value: boolean) => void): HTMLButtonElement {\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = `tcw-toggle${value ? ' tcw-on' : ''}`;\n toggle.setAttribute('role', 'switch');\n toggle.setAttribute('aria-checked', String(value));\n toggle.addEventListener('click', () => {\n const next = !toggle.classList.contains('tcw-on');\n toggle.classList.toggle('tcw-on', next);\n toggle.setAttribute('aria-checked', String(next));\n onChange(next);\n });\n return toggle;\n}\n\nexport function numberInput(\n value: number,\n range: { min?: number; max?: number; step?: number | 'any'; whole?: boolean },\n onChange: (value: number) => void,\n): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'tcw-indi-input';\n input.value = String(value);\n input.step = String(range.step ?? (Number.isInteger(value) ? 1 : 0.001));\n if (range.min !== undefined) input.min = String(range.min);\n if (range.max !== undefined) input.max = String(range.max);\n input.addEventListener('change', () => {\n if (input.value.trim() === '') return;\n let n = Number(input.value);\n if (!Number.isFinite(n)) return;\n // A whole number (a period): what is typed is rounded, so the field shows the value used.\n if (range.whole) n = Math.round(n);\n if (range.min !== undefined) n = Math.max(range.min, n);\n if (range.max !== undefined) n = Math.min(range.max, n);\n input.value = String(n);\n onChange(n);\n });\n return input;\n}\n\nexport function colorInput(value: string, onInput: (value: string) => void): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'color';\n input.className = 'tcw-indi-color';\n input.value = toHex(value);\n input.addEventListener('input', () => onInput(input.value));\n return input;\n}\n\nexport function selectInput(\n choices: readonly { value: string; label: string }[],\n value: string,\n onChange: (value: string) => void,\n): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const choice of choices) {\n const opt = document.createElement('option');\n opt.value = choice.value;\n opt.textContent = choice.label;\n select.appendChild(opt);\n }\n select.value = value;\n select.addEventListener('change', () => onChange(select.value));\n return select;\n}\n\n/** A colour as `#rrggbb` (what a colour input takes); black if unreadable. */\nexport function toHex(color: unknown): string {\n if (typeof color !== 'string') return '#000000';\n const c = color.trim();\n if (/^#[0-9a-f]{6}$/i.test(c)) return c.toLowerCase();\n if (/^#[0-9a-f]{8}$/i.test(c)) return c.slice(0, 7).toLowerCase();\n if (/^#[0-9a-f]{3,4}$/i.test(c)) return `#${c[1]}${c[1]}${c[2]}${c[2]}${c[3]}${c[3]}`.toLowerCase();\n const m = /^rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)/i.exec(c);\n if (m) return `#${[m[1], m[2], m[3]].map((v) => Math.min(255, Number(v)).toString(16).padStart(2, '0')).join('')}`;\n // hsl(), oklch(), a colour's name…: as the browser reads it.\n const read = readByBrowser(c);\n return read !== null && read !== c ? toHex(read) : '#000000';\n}\n\nlet colourReader: CanvasRenderingContext2D | null | undefined;\n\n/** A colour as a canvas reads it (`#rrggbb` or `rgba(…)`); null where there is no canvas or it isn't a colour. */\nfunction readByBrowser(color: string): string | null {\n if (colourReader === undefined) {\n try {\n colourReader = typeof document !== 'undefined' ? document.createElement('canvas').getContext('2d') : null;\n } catch {\n colourReader = null;\n }\n }\n if (!colourReader) return null;\n colourReader.fillStyle = '#010203';\n colourReader.fillStyle = color;\n const out = String(colourReader.fillStyle);\n return out === '#010203' ? null : out;\n}\n\n/** The alpha of a colour (1 for `#rgb` / `#rrggbb`). */\nexport function colorAlpha(color: unknown): number {\n if (typeof color !== 'string') return 1;\n const m = /^rgba\\([^)]*,\\s*([\\d.]+)\\s*\\)$/i.exec(color.trim());\n return m ? Math.max(0, Math.min(1, Number(m[1]))) : 1;\n}\n\n/** `#rrggbb` with an alpha, as `rgba(…)`. */\nexport function withAlpha(hex: string, alpha: number): string {\n const h = toHex(hex);\n const [r, g, b] = [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));\n return `rgba(${r}, ${g}, ${b}, ${Math.round(alpha * 100) / 100})`;\n}\n\n/** Arrow keys and where they move a row of choices, as it reads left to right. */\nconst SEGMENT_MOVES: Record<string, 1 | -1> = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 };\n\n/**\n * One choice of a few, as a row of buttons (a radio group). The arrow keys\n * move the choice and the focus with it, as a radio group's do (left and\n * right the way the row reads, so mirrored right to left), wrapping at the\n * ends; Home and End go to the first and the last. With a modifier held the\n * keys stay the browser's (Alt+Left goes back).\n */\nexport function segmentedControl<T extends string>(\n choices: readonly { value: T; label: string }[],\n value: T | string,\n onChange: (value: T) => void,\n label: string,\n): HTMLDivElement {\n const group = document.createElement('div');\n group.className = 'tcw-segmented';\n group.setAttribute('role', 'radiogroup');\n group.setAttribute('aria-label', label);\n const buttons = choices.map((choice) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-segment';\n btn.setAttribute('role', 'radio');\n btn.dataset.value = choice.value;\n btn.textContent = choice.label;\n btn.addEventListener('click', () => choose(choice.value));\n return btn;\n });\n const mark = (current: string) => {\n const at = Math.max(0, buttons.findIndex((btn) => btn.dataset.value === current));\n buttons.forEach((btn, i) => {\n btn.setAttribute('aria-checked', String(btn.dataset.value === current));\n // One Tab stop: the chosen button, or the first when none is.\n btn.tabIndex = i === at ? 0 : -1;\n });\n };\n const choose = (next: T) => {\n mark(next);\n onChange(next);\n };\n group.addEventListener('keydown', (e) => {\n const at = buttons.findIndex((btn) => btn === e.target);\n if (at < 0 || e.altKey || e.ctrlKey || e.metaKey) return;\n const last = buttons.length - 1;\n let to: number;\n if (e.key === 'Home') to = 0;\n else if (e.key === 'End') to = last;\n else if (e.key in SEGMENT_MOVES) {\n const rtl = group.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const horizontal = e.key === 'ArrowLeft' || e.key === 'ArrowRight';\n const by = rtl && horizontal ? -SEGMENT_MOVES[e.key] : SEGMENT_MOVES[e.key];\n to = (at + by + buttons.length) % buttons.length;\n } else return;\n e.preventDefault();\n buttons[to].focus();\n choose(choices[to].value);\n });\n mark(value);\n group.append(...buttons);\n return group;\n}\n\n/**\n * `value` moved one `step` up (`dir` 1) or down (-1) on the step's grid: to\n * the next multiple of the step that way, so a value between steps lands on\n * one. Never below one step: there it stays. Written to the step's decimals,\n * so 0.1 + 0.2 is 0.3 and a 1/32 point is exact.\n */\nexport function stepValue(value: number, dir: 1 | -1, step: number): number {\n if (!(step > 0) || !Number.isFinite(value)) return value;\n const units = value / step;\n const near = Math.round(units);\n const onGrid = Math.abs(units - near) < 1e-9 * Math.max(1, Math.abs(units));\n const k = dir > 0 ? (onGrid ? near + 1 : Math.ceil(units)) : (onGrid ? near - 1 : Math.floor(units));\n if (k < 1) return value;\n return Number((k * step).toFixed(stepDecimals(step)));\n}\n\n/** What a stepper needs: its step each way, its buttons' names, how it writes and where an empty field starts. */\nexport interface StepperOptions {\n /** The step from `value` going `dir`: a price band's own below its edge. */\n step: (value: number, dir: 1 | -1) => number;\n labels: { down: string; up: string };\n /** How a stepped value is written; unset, as a plain number. */\n format?: (value: number) => string;\n /** Where an empty or unreadable field starts from. */\n fallback?: () => number;\n}\n\n/**\n * A number field between a − and a +, each moving it by a step on the step's\n * grid (`stepValue`) and telling the field as typing would (an `input`\n * event); the arrow keys in the field do the same. The buttons are no Tab\n * stops of their own: the field is. Figures read left to right in any widget.\n */\nexport function stepperField(input: HTMLInputElement, options: StepperOptions): { element: HTMLDivElement; down: HTMLButtonElement; up: HTMLButtonElement } {\n const wrap = document.createElement('div');\n wrap.className = 'tcw-stepper';\n wrap.dir = 'ltr';\n const move = (dir: 1 | -1) => {\n const typed = Number(input.value);\n const from = input.value.trim() !== '' && Number.isFinite(typed) ? typed : options.fallback?.() ?? 0;\n const next = stepValue(from, dir, options.step(from, dir));\n input.value = options.format ? options.format(next) : String(next);\n input.dispatchEvent(new Event('input'));\n };\n const button = (dir: 1 | -1, text: string) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-stepper-btn';\n btn.tabIndex = -1;\n btn.setAttribute('aria-label', text);\n btn.innerHTML = createIcon(dir > 0 ? 'plus' : 'minus', 14);\n btn.addEventListener('click', () => move(dir));\n return btn;\n };\n input.addEventListener('keydown', (e) => {\n if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;\n e.preventDefault();\n move(e.key === 'ArrowUp' ? 1 : -1);\n });\n const down = button(-1, options.labels.down);\n const up = button(1, options.labels.up);\n wrap.append(down, input, up);\n return { element: wrap, down, up };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"settingsControls-DIjzCFHI.js","names":[],"sources":["../src/widget/WidgetContextMenu.ts","../src/widget/settingsControls.ts"],"sourcesContent":["import { createIcon } from './icons.js';\n\nexport type ContextMenuEntry =\n /**\n * `checked` makes it a switch (ticked when on); with `radio` it is one\n * choice among the radios beside it. `shortcut` (\"Alt+I\") is written at\n * the row's end. `submenu` opens more entries beside this one. `remove`\n * (its action's name, \"Delete template Swing\") gives it a remove button\n * and the Delete key; the menu's `onRemove` hears it.\n */\n | {\n id: string;\n label: string;\n icon?: string;\n danger?: boolean;\n checked?: boolean;\n radio?: boolean;\n shortcut?: string;\n /** How many things it acts on (\"Remove drawings 3\"), at the row's end and read with its name. */\n count?: number;\n submenu?: readonly ContextMenuEntry[];\n remove?: string;\n }\n | 'separator';\n\ntype MenuItem = Exclude<ContextMenuEntry, 'separator'>;\n\n/** Room kept between the menu and the edge it would touch. */\nconst MENU_MARGIN = 4;\n/** Room between a menu and the button it hangs under. */\nconst MENU_GAP = 2;\n/**\n * How long the pointer rests on an entry before its submenu opens, or the\n * open one closes: crossing an entry on the way into a submenu does neither.\n */\nconst HOVER_DELAY_MS = 150;\nconst ITEM_SELECTOR = '[role=menuitem], [role=menuitemcheckbox], [role=menuitemradio]';\n/** Where menus open as sheets from the bottom (the stylesheet's phone width): a submenu covers its menu. */\nconst SHEET_QUERY = '(max-width: 640px)';\n/** The way back from a submenu opened as a sheet. */\nconst BACK_ID = 'tcw:back';\n\n/**\n * The menus open now, on every widget of the page: a button that several\n * menus hang from (\"more\", for those picked from it) closes whichever is open.\n */\nconst openMenus = new Set<WidgetContextMenu>();\n\n/** Where a menu opens along each axis, in the host's coordinates. */\ninterface MenuPlace {\n x: number;\n y: number;\n /** Where it ends instead, when there is no room after `x` or below `y`. */\n xBack: number;\n yBack: number;\n /** Ending at `xBack` first, and starting at `x` only without room there (a submenu, right to left). */\n before?: boolean;\n}\n\n/**\n * Where a menu `size` long starts along one axis: at `at`, or ending at\n * `back` when it would pass `end`; with `before`, the other way round.\n */\nfunction place(at: number, size: number, end: number, back = at, before = false): number {\n const start = before\n ? (back - size >= 0 ? back - size : at)\n : (at + size > end ? back - size : at);\n return Math.max(0, Math.min(start, end - size));\n}\n\nfunction itemRole(entry: MenuItem): string {\n if (entry.radio) return 'menuitemradio';\n return entry.checked === undefined ? 'menuitem' : 'menuitemcheckbox';\n}\n\n/** What leads an entry: a tick on a switch that is on, a dot on the choice made, else its icon or room for one. */\nfunction leadMark(entry: MenuItem): string {\n const blank = '<span class=\"tcw-context-icon\"></span>';\n if (entry.radio) return entry.checked ? '<span class=\"tcw-context-icon tcw-context-dot\"></span>' : blank;\n // A switch that is off shows its icon, as an action does.\n if (entry.checked) return createIcon('check', 14);\n return (entry.icon && createIcon(entry.icon, 14)) || blank;\n}\n\n/**\n * A small menu opened at a point (a right-click on a drawing, say): arrow\n * keys, Home and End move through it, Enter picks, Escape, Tab or a click\n * outside close it. Focus returns to where it was. An entry with a submenu\n * opens it beside itself on a click, Enter, the right arrow (the left one\n * right to left) or a rest of the pointer; the other arrow or Escape closes\n * it again.\n */\nexport class WidgetContextMenu {\n private el: HTMLDivElement;\n private onPick: ((id: string) => void) | null = null;\n private onRemove: ((id: string) => void) | null = null;\n private returnFocus: HTMLElement | null = null;\n /** The button that opened it: pressing it again is its click's to handle (a toggle). */\n private anchor: HTMLElement | null = null;\n /** The submenu, made the first time an entry opens one. */\n private child: WidgetContextMenu | null = null;\n /** The menu this one is the submenu of. */\n private parent: WidgetContextMenu | null = null;\n private hoverTimer: ReturnType<typeof setTimeout> | null = null;\n /** The entry `hoverTimer` counts the rest on: moving on it counts on, not again. */\n private hoverItem: HTMLElement | null = null;\n private readonly onDocPointer = (e: Event) => {\n const target = e.target as Node;\n if (this.holds(target) || this.anchor?.contains(target)) return;\n this.close();\n };\n /** Scrolling a long menu keeps it; scrolling anywhere else moves the chart from under it. */\n private readonly onWheel = (e: Event) => {\n if (!this.holds(e.target as Node)) this.close();\n };\n /** Resizing or leaving the window moves the chart from under the menu. */\n private readonly onAway = () => this.close();\n\n /**\n * `backLabel` (\"Back\") names the first entry of a submenu on a phone, which\n * leads back to the menu under it.\n */\n constructor(private readonly host: HTMLElement, label: string, private readonly backLabel = '') {\n this.el = document.createElement('div');\n this.el.className = 'tcw-context-menu';\n this.el.setAttribute('role', 'menu');\n this.el.setAttribute('aria-label', label);\n this.el.hidden = true;\n this.el.addEventListener('keydown', (e) => this.onKeyDown(e));\n host.appendChild(this.el);\n }\n\n /**\n * Open at (`x`, `y`) in the host's coordinates. It opens up or to the left\n * of the point when there is no room below or to the right, ending at\n * `xBack` then (a menu under a button ends at the button's far edge), and\n * stays inside both the host and the window. With `before` it ends at\n * `xBack` first, and starts at `x` only without room before it.\n */\n open(\n entries: readonly ContextMenuEntry[],\n x: number,\n y: number,\n onPick: (id: string) => void,\n anchor?: HTMLElement,\n onRemove?: (id: string) => void,\n xBack = x,\n before = false,\n ): void {\n this.show(entries, { x, y, xBack, yBack: y, before }, onPick, anchor, onRemove, true);\n }\n\n /**\n * Open under `anchor`, `gap` px below it, lined up with it as the\n * toolbar's own menus are, mirrored right to left: from its start edge\n * (`'start'`), or ending at its end edge (`'end'`: \"more\", at the\n * toolbar's end); from its other edge when there is no room that way.\n */\n openUnder(\n entries: readonly ContextMenuEntry[],\n anchor: HTMLElement,\n onPick: (id: string) => void,\n align: 'start' | 'end' = 'start',\n gap = MENU_GAP,\n ): void {\n const at = anchor.getBoundingClientRect();\n const box = this.host.getBoundingClientRect();\n // Ending at the button's right edge first: its end left to right, its start right to left.\n const before = (align === 'end') !== this.rtl();\n this.open(entries, at.left - box.left, at.bottom - box.top + gap, onPick, anchor, undefined, at.right - box.left, before);\n }\n\n /**\n * Open beside `anchor` (a button of the drawing bar), level with its top:\n * past its end edge, or before its start edge without room there; ending\n * at its bottom when there is no room below.\n */\n openBeside(entries: readonly ContextMenuEntry[], anchor: HTMLElement, onPick: (id: string) => void): void {\n const at = anchor.getBoundingClientRect();\n const box = this.host.getBoundingClientRect();\n // Starting past its right edge, or ending before its left one: right to left, the left first.\n const right = at.right - box.left + MENU_GAP;\n const left = at.left - box.left - MENU_GAP;\n this.show(entries, { x: right, y: at.top - box.top, xBack: left, yBack: at.bottom - box.top, before: this.rtl() }, onPick, anchor, undefined, true);\n // Safari doesn't focus a pressed button: the keyboard goes back to it all the same.\n this.returnFocus = anchor;\n }\n\n /**\n * Close every menu open under `anchor`, and say whether one was. Its\n * button shows it open (aria-expanded), so a press on it closes it.\n */\n static closeAnchoredTo(anchor: HTMLElement): boolean {\n const under = [...openMenus].filter((menu) => menu.anchor === anchor);\n for (const menu of under) menu.close();\n return under.length > 0;\n }\n\n /** The button an open menu that holds the keyboard hangs from; null when none does. */\n static focusedAnchor(): HTMLElement | null {\n return [...openMenus].find((menu) => menu.anchor !== null && menu.hasFocus())?.anchor ?? null;\n }\n\n close(): void {\n this.clearHover();\n this.child?.close();\n if (this.el.hidden) return;\n openMenus.delete(this);\n // A submenu the pointer closes leaves the keyboard where it is, in the menu.\n const giveBack = !this.parent || this.el.contains(document.activeElement);\n this.el.hidden = true;\n this.onPick = null;\n this.onRemove = null;\n this.anchor?.setAttribute('aria-expanded', 'false');\n this.anchor = null;\n document.removeEventListener('pointerdown', this.onDocPointer, true);\n document.removeEventListener('wheel', this.onWheel, { capture: true });\n window.removeEventListener('resize', this.onAway);\n window.removeEventListener('blur', this.onAway);\n if (giveBack && this.returnFocus?.isConnected) this.returnFocus.focus({ preventScroll: true });\n this.returnFocus = null;\n }\n\n isOpen(): boolean {\n return !this.el.hidden;\n }\n\n /** Whether the keyboard is in the menu, or in a submenu of it. */\n hasFocus(): boolean {\n return document.activeElement !== null && this.holds(document.activeElement);\n }\n\n destroy(): void {\n this.close();\n this.child?.destroy();\n this.el.remove();\n }\n\n private show(\n entries: readonly ContextMenuEntry[],\n at: MenuPlace,\n onPick: (id: string) => void,\n anchor: HTMLElement | undefined,\n onRemove: ((id: string) => void) | undefined,\n focusFirst: boolean,\n ): void {\n this.clearHover();\n this.child?.close();\n this.returnFocus = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n this.onPick = onPick;\n this.onRemove = onRemove ?? null;\n this.anchor = anchor ?? null;\n this.anchor?.setAttribute('aria-expanded', 'true');\n this.el.replaceChildren(...entries.map((entry) => this.entry(entry)));\n this.el.hidden = false;\n openMenus.add(this);\n const hostRect = this.host.getBoundingClientRect();\n const right = Math.min(this.host.clientWidth, window.innerWidth - hostRect.left) - MENU_MARGIN;\n const bottom = Math.min(this.host.clientHeight, window.innerHeight - hostRect.top) - MENU_MARGIN;\n // Taller than the room (a short widget, the time zones), it scrolls, its padding and border in the room too.\n this.el.style.maxHeight = `${Math.max(0, bottom - this.frameHeight())}px`;\n this.el.style.left = `${place(at.x, this.el.offsetWidth, right, at.xBack, at.before)}px`;\n this.el.style.top = `${place(at.y, this.el.offsetHeight, bottom, at.yBack)}px`;\n document.addEventListener('pointerdown', this.onDocPointer, true);\n document.addEventListener('wheel', this.onWheel, { capture: true, passive: true });\n window.addEventListener('resize', this.onAway);\n window.addEventListener('blur', this.onAway);\n const chosen = this.revealChoice();\n if (focusFirst) (chosen ?? this.items()[0])?.focus({ preventScroll: true });\n }\n\n /** What its padding and border add to a max height, which sizes only its rows unless its box counts them. */\n private frameHeight(): number {\n const style = getComputedStyle(this.el);\n if (style.boxSizing === 'border-box') return 0;\n const px = (value: string) => parseFloat(value) || 0;\n return px(style.paddingTop) + px(style.paddingBottom) + px(style.borderTopWidth) + px(style.borderBottomWidth);\n }\n\n /**\n * A long list of choices (the time zones) opens scrolled to the one made,\n * returned to take the keyboard; null when it is in sight already.\n */\n private revealChoice(): HTMLElement | null {\n const chosen = this.el.querySelector<HTMLElement>('[role=menuitemradio][aria-checked=true]');\n if (!chosen || chosen.offsetTop + chosen.offsetHeight <= this.el.clientHeight) return null;\n this.el.scrollTop = chosen.offsetTop - (this.el.clientHeight - chosen.offsetHeight) / 2;\n return chosen;\n }\n\n /** The widget reads right to left (`dir=\"rtl\"`): submenus open to the left, and the arrows swap. */\n private rtl(): boolean {\n return this.host.closest('[dir]')?.getAttribute('dir') === 'rtl';\n }\n\n /** Whether `node` is in this menu or its open submenu. */\n private holds(node: Node): boolean {\n return this.el.contains(node) || (this.child?.holds(node) ?? false);\n }\n\n private entry(entry: ContextMenuEntry): HTMLElement {\n if (entry === 'separator') {\n const sep = document.createElement('div');\n sep.className = 'tcw-context-sep';\n sep.setAttribute('role', 'separator');\n return sep;\n }\n const item = document.createElement('button');\n item.type = 'button';\n item.className = `tcw-context-item${entry.danger ? ' tcw-context-danger' : ''}`;\n item.setAttribute('role', itemRole(entry));\n if (entry.radio || entry.checked !== undefined) item.setAttribute('aria-checked', String(entry.checked ?? false));\n item.tabIndex = -1;\n item.dataset.id = entry.id;\n item.innerHTML = leadMark(entry);\n const text = document.createElement('span');\n text.className = 'tcw-context-label';\n text.textContent = entry.label;\n item.appendChild(text);\n this.appendEnd(item, entry);\n const submenu = entry.submenu;\n // A finger's tap is a click: only a pointer resting on an entry opens its submenu. A pointer that\n // moved there, not one that entered: a menu that opens under a still pointer (a right-click near\n // a phone's foot, where it opens as a sheet) is entered by it, and opened a submenu unasked.\n item.addEventListener('pointermove', (e) => {\n if ((e as PointerEvent).pointerType !== 'touch') this.hover(item, entry.label, submenu);\n });\n item.addEventListener('click', () => {\n if (submenu) this.openSubmenu(item, entry.label, submenu, true);\n else this.pick(entry.id);\n });\n return entry.remove ? this.removableRow(item, entry.remove) : item;\n }\n\n /** The count, the shortcut, or the mark of a submenu, at the row's end. */\n private appendEnd(item: HTMLButtonElement, entry: MenuItem): void {\n if (entry.count !== undefined) {\n const count = document.createElement('span');\n count.className = 'tcw-context-count';\n count.textContent = String(entry.count);\n item.appendChild(count);\n }\n if (entry.shortcut) {\n // Named once, by aria-keyshortcuts: the key is for the eye.\n item.setAttribute('aria-keyshortcuts', entry.shortcut);\n const key = document.createElement('kbd');\n key.className = 'tcw-context-key';\n key.setAttribute('aria-hidden', 'true');\n key.textContent = entry.shortcut;\n item.appendChild(key);\n }\n if (entry.submenu) {\n item.setAttribute('aria-haspopup', 'menu');\n item.setAttribute('aria-expanded', 'false');\n item.insertAdjacentHTML('beforeend', createIcon('chevronRight', 12));\n item.lastElementChild?.classList.add('tcw-context-more');\n }\n }\n\n /** The keyboard removes it with Delete; the button is for a pointer. */\n private removableRow(item: HTMLButtonElement, action: string): HTMLElement {\n item.setAttribute('aria-keyshortcuts', 'Delete');\n const remove = document.createElement('button');\n remove.type = 'button';\n remove.className = 'tcw-context-remove';\n remove.tabIndex = -1;\n remove.setAttribute('aria-hidden', 'true');\n remove.title = action;\n remove.innerHTML = createIcon('x', 12);\n remove.addEventListener('click', () => this.remove(item));\n const row = document.createElement('div');\n row.className = 'tcw-context-row';\n row.setAttribute('role', 'none');\n row.append(item, remove);\n return row;\n }\n\n /** Open `entries` beside `item`, after it (to its right, or left in RTL) or, without room there, before it. */\n private openSubmenu(item: HTMLButtonElement, label: string, entries: readonly ContextMenuEntry[], focusFirst: boolean): void {\n this.clearHover();\n const child = this.child ?? this.makeChild();\n if (child.isOpen() && child.anchor === item) {\n if (focusFirst) child.items()[0]?.focus({ preventScroll: true });\n return;\n }\n child.close();\n child.el.setAttribute('aria-label', label);\n const hostRect = this.host.getBoundingClientRect();\n const r = item.getBoundingClientRect();\n const at: MenuPlace = {\n x: r.right - hostRect.left,\n y: r.top - hostRect.top,\n xBack: r.left - hostRect.left,\n yBack: r.bottom - hostRect.top,\n before: this.rtl(),\n };\n // As a sheet it hides the menu under it: its first entry leads back there.\n const sheet = window.matchMedia?.(SHEET_QUERY).matches ?? false;\n const list: readonly ContextMenuEntry[] = sheet\n ? [{ id: BACK_ID, label, icon: 'chevronLeft' }, 'separator', ...entries]\n : entries;\n child.show(list, at, (id) => {\n if (id !== BACK_ID) this.pick(id);\n }, item, (id) => this.onRemove?.(id), focusFirst);\n const back = sheet ? child.el.querySelector<HTMLElement>(`[data-id=\"${BACK_ID}\"]`) : null;\n back?.classList.add('tcw-context-back');\n if (back && this.backLabel) back.setAttribute('aria-label', `${this.backLabel}: ${label}`);\n // Closed, it gives the keyboard back to its entry (a click on a button doesn't focus it everywhere).\n child.returnFocus = item;\n }\n\n private makeChild(): WidgetContextMenu {\n const child = new WidgetContextMenu(this.host, '', this.backLabel);\n child.parent = this;\n // Reaching the submenu keeps it, whatever the pointer crossed on the way.\n child.el.addEventListener('pointerenter', () => this.clearHover());\n this.child = child;\n return child;\n }\n\n /** After a rest on `item`: open its submenu, or close the one open for another entry. */\n private hover(item: HTMLButtonElement, label: string, submenu?: readonly ContextMenuEntry[]): void {\n if (this.hoverTimer !== null && this.hoverItem === item) return;\n this.clearHover();\n const open = this.child?.isOpen() ? this.child.anchor : null;\n if (submenu ? open === item : open === null) return;\n this.hoverItem = item;\n this.hoverTimer = setTimeout(() => {\n this.hoverTimer = null;\n this.hoverItem = null;\n if (submenu) this.openSubmenu(item, label, submenu, false);\n else this.child?.close();\n }, HOVER_DELAY_MS);\n }\n\n private clearHover(): void {\n this.hoverItem = null;\n if (this.hoverTimer === null) return;\n clearTimeout(this.hoverTimer);\n this.hoverTimer = null;\n }\n\n /** Take an entry out (its `onRemove` told), the keyboard on the one after it; the menu closes when none is left. */\n private remove(item: HTMLButtonElement): void {\n const id = item.dataset.id;\n const row = item.closest('.tcw-context-row');\n if (!id || !row) return;\n const items = this.items();\n const at = items.indexOf(item);\n this.onRemove?.(id);\n row.remove();\n // A separator left first, last or next to another goes too.\n const seps = [...this.el.children].filter((el, i, all) => el.classList.contains('tcw-context-sep')\n && (i === 0 || i === all.length - 1 || all[i - 1].classList.contains('tcw-context-sep')));\n for (const sep of seps) sep.remove();\n const left = this.items();\n if (left.length === 0) {\n this.close();\n return;\n }\n left[Math.min(at, left.length - 1)].focus({ preventScroll: true });\n }\n\n private pick(id: string): void {\n const onPick = this.onPick;\n this.close();\n onPick?.(id);\n }\n\n private items(): HTMLButtonElement[] {\n return [...this.el.querySelectorAll<HTMLButtonElement>(ITEM_SELECTOR)];\n }\n\n private onKeyDown(e: KeyboardEvent): void {\n const items = this.items();\n const at = items.indexOf(document.activeElement as HTMLButtonElement);\n const [openKey, backKey] = this.rtl() ? ['ArrowLeft', 'ArrowRight'] : ['ArrowRight', 'ArrowLeft'];\n let next = -1;\n if (e.key === 'ArrowDown') next = (at + 1) % items.length;\n else if (e.key === 'ArrowUp') next = (at - 1 + items.length) % items.length;\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = items.length - 1;\n else if (e.key === openKey && items[at]?.hasAttribute('aria-haspopup')) {\n e.preventDefault();\n items[at].click();\n return;\n } else if (e.key === backKey && this.parent) {\n e.preventDefault();\n this.close();\n return;\n } else if (e.key === 'Delete' && at >= 0) {\n e.preventDefault();\n this.remove(items[at]);\n return;\n } else if (e.key === 'Escape' || e.key === 'Tab') {\n e.preventDefault();\n e.stopPropagation();\n // Escape steps back out of a submenu; Tab leaves the menus altogether.\n let menu: WidgetContextMenu = this;\n while (e.key === 'Tab' && menu.parent) menu = menu.parent;\n menu.close();\n return;\n } else return;\n e.preventDefault();\n // Moving on from the entry a resting pointer opened a submenu for closes it.\n this.child?.close();\n items[next]?.focus({ preventScroll: true });\n }\n}\n","import { stepDecimals } from '@tradecanvas/commons';\nimport { createIcon } from './icons.js';\n\n/**\n * Form controls shared by the settings dialogs and the order ticket: a\n * labelled row, a ticked row, a switch, a number field, a colour picker, a\n * select, a row of choices and a number between − and +. Each calls back on\n * change.\n */\n\n/** A labelled control: clicking the label reaches the control. */\nexport function settingsRow(label: string, control: HTMLElement): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-settings-row';\n const text = document.createElement('span');\n text.className = 'tcw-settings-label';\n text.textContent = label;\n row.append(text, control);\n return row;\n}\n\n/** A box ticked or not, its label after it: the whole row ticks it. */\nexport function checkRow(label: string, value: boolean, onChange: (value: boolean) => void): HTMLLabelElement {\n const row = document.createElement('label');\n row.className = 'tcw-settings-row tcw-check-row';\n const box = document.createElement('input');\n box.type = 'checkbox';\n box.checked = value;\n box.addEventListener('change', () => onChange(box.checked));\n const text = document.createElement('span');\n text.className = 'tcw-settings-label';\n text.textContent = label;\n row.append(box, text);\n return row;\n}\n\nexport function toggleSwitch(value: boolean, onChange: (value: boolean) => void): HTMLButtonElement {\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.className = `tcw-toggle${value ? ' tcw-on' : ''}`;\n toggle.setAttribute('role', 'switch');\n toggle.setAttribute('aria-checked', String(value));\n toggle.addEventListener('click', () => {\n const next = !toggle.classList.contains('tcw-on');\n toggle.classList.toggle('tcw-on', next);\n toggle.setAttribute('aria-checked', String(next));\n onChange(next);\n });\n return toggle;\n}\n\nexport function numberInput(\n value: number,\n range: { min?: number; max?: number; step?: number | 'any'; whole?: boolean },\n onChange: (value: number) => void,\n): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'number';\n input.className = 'tcw-indi-input';\n input.value = String(value);\n input.step = String(range.step ?? (Number.isInteger(value) ? 1 : 0.001));\n if (range.min !== undefined) input.min = String(range.min);\n if (range.max !== undefined) input.max = String(range.max);\n input.addEventListener('change', () => {\n if (input.value.trim() === '') return;\n let n = Number(input.value);\n if (!Number.isFinite(n)) return;\n // A whole number (a period): what is typed is rounded, so the field shows the value used.\n if (range.whole) n = Math.round(n);\n if (range.min !== undefined) n = Math.max(range.min, n);\n if (range.max !== undefined) n = Math.min(range.max, n);\n input.value = String(n);\n onChange(n);\n });\n return input;\n}\n\nexport function colorInput(value: string, onInput: (value: string) => void): HTMLInputElement {\n const input = document.createElement('input');\n input.type = 'color';\n input.className = 'tcw-indi-color';\n input.value = toHex(value);\n input.addEventListener('input', () => onInput(input.value));\n return input;\n}\n\nexport function selectInput(\n choices: readonly { value: string; label: string }[],\n value: string,\n onChange: (value: string) => void,\n): HTMLSelectElement {\n const select = document.createElement('select');\n select.className = 'tcw-indi-input tcw-indi-select';\n for (const choice of choices) {\n const opt = document.createElement('option');\n opt.value = choice.value;\n opt.textContent = choice.label;\n select.appendChild(opt);\n }\n select.value = value;\n select.addEventListener('change', () => onChange(select.value));\n return select;\n}\n\n/** A colour as `#rrggbb` (what a colour input takes); black if unreadable. */\nexport function toHex(color: unknown): string {\n if (typeof color !== 'string') return '#000000';\n const c = color.trim();\n if (/^#[0-9a-f]{6}$/i.test(c)) return c.toLowerCase();\n if (/^#[0-9a-f]{8}$/i.test(c)) return c.slice(0, 7).toLowerCase();\n if (/^#[0-9a-f]{3,4}$/i.test(c)) return `#${c[1]}${c[1]}${c[2]}${c[2]}${c[3]}${c[3]}`.toLowerCase();\n const m = /^rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)/i.exec(c);\n if (m) return `#${[m[1], m[2], m[3]].map((v) => Math.min(255, Number(v)).toString(16).padStart(2, '0')).join('')}`;\n // hsl(), oklch(), a colour's name…: as the browser reads it.\n const read = readByBrowser(c);\n return read !== null && read !== c ? toHex(read) : '#000000';\n}\n\nlet colourReader: CanvasRenderingContext2D | null | undefined;\n\n/** A colour as a canvas reads it (`#rrggbb` or `rgba(…)`); null where there is no canvas or it isn't a colour. */\nfunction readByBrowser(color: string): string | null {\n if (colourReader === undefined) {\n try {\n colourReader = typeof document !== 'undefined' ? document.createElement('canvas').getContext('2d') : null;\n } catch {\n colourReader = null;\n }\n }\n if (!colourReader) return null;\n colourReader.fillStyle = '#010203';\n colourReader.fillStyle = color;\n const out = String(colourReader.fillStyle);\n return out === '#010203' ? null : out;\n}\n\n/** The alpha of a colour (1 for `#rgb` / `#rrggbb`). */\nexport function colorAlpha(color: unknown): number {\n if (typeof color !== 'string') return 1;\n const m = /^rgba\\([^)]*,\\s*([\\d.]+)\\s*\\)$/i.exec(color.trim());\n return m ? Math.max(0, Math.min(1, Number(m[1]))) : 1;\n}\n\n/** `#rrggbb` with an alpha, as `rgba(…)`. */\nexport function withAlpha(hex: string, alpha: number): string {\n const h = toHex(hex);\n const [r, g, b] = [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));\n return `rgba(${r}, ${g}, ${b}, ${Math.round(alpha * 100) / 100})`;\n}\n\n/** Arrow keys and where they move a row of choices, as it reads left to right. */\nconst SEGMENT_MOVES: Record<string, 1 | -1> = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 };\n\n/**\n * One choice of a few, as a row of buttons (a radio group). The arrow keys\n * move the choice and the focus with it, as a radio group's do (left and\n * right the way the row reads, so mirrored right to left), wrapping at the\n * ends; Home and End go to the first and the last. With a modifier held the\n * keys stay the browser's (Alt+Left goes back).\n */\nexport function segmentedControl<T extends string>(\n choices: readonly { value: T; label: string }[],\n value: T | string,\n onChange: (value: T) => void,\n label: string,\n): HTMLDivElement {\n const group = document.createElement('div');\n group.className = 'tcw-segmented';\n group.setAttribute('role', 'radiogroup');\n group.setAttribute('aria-label', label);\n const buttons = choices.map((choice) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-segment';\n btn.setAttribute('role', 'radio');\n btn.dataset.value = choice.value;\n btn.textContent = choice.label;\n btn.addEventListener('click', () => choose(choice.value));\n return btn;\n });\n const mark = (current: string) => {\n const at = Math.max(0, buttons.findIndex((btn) => btn.dataset.value === current));\n buttons.forEach((btn, i) => {\n btn.setAttribute('aria-checked', String(btn.dataset.value === current));\n // One Tab stop: the chosen button, or the first when none is.\n btn.tabIndex = i === at ? 0 : -1;\n });\n };\n const choose = (next: T) => {\n mark(next);\n onChange(next);\n };\n group.addEventListener('keydown', (e) => {\n const at = buttons.findIndex((btn) => btn === e.target);\n if (at < 0 || e.altKey || e.ctrlKey || e.metaKey) return;\n const last = buttons.length - 1;\n let to: number;\n if (e.key === 'Home') to = 0;\n else if (e.key === 'End') to = last;\n else if (e.key in SEGMENT_MOVES) {\n const rtl = group.closest('[dir]')?.getAttribute('dir') === 'rtl';\n const horizontal = e.key === 'ArrowLeft' || e.key === 'ArrowRight';\n const by = rtl && horizontal ? -SEGMENT_MOVES[e.key] : SEGMENT_MOVES[e.key];\n to = (at + by + buttons.length) % buttons.length;\n } else return;\n e.preventDefault();\n buttons[to].focus();\n choose(choices[to].value);\n });\n mark(value);\n group.append(...buttons);\n return group;\n}\n\n/**\n * `value` moved one `step` up (`dir` 1) or down (-1) on the step's grid: to\n * the next multiple of the step that way, so a value between steps lands on\n * one. Never below one step: there it stays. Written to the step's decimals,\n * so 0.1 + 0.2 is 0.3 and a 1/32 point is exact.\n */\nexport function stepValue(value: number, dir: 1 | -1, step: number): number {\n if (!(step > 0) || !Number.isFinite(value)) return value;\n const units = value / step;\n const near = Math.round(units);\n const onGrid = Math.abs(units - near) < 1e-9 * Math.max(1, Math.abs(units));\n const k = dir > 0 ? (onGrid ? near + 1 : Math.ceil(units)) : (onGrid ? near - 1 : Math.floor(units));\n if (k < 1) return value;\n return Number((k * step).toFixed(stepDecimals(step)));\n}\n\n/** What a stepper needs: its step each way, its buttons' names, how it writes and where an empty field starts. */\nexport interface StepperOptions {\n /** The step from `value` going `dir`: a price band's own below its edge. */\n step: (value: number, dir: 1 | -1) => number;\n labels: { down: string; up: string };\n /** How a stepped value is written; unset, as a plain number. */\n format?: (value: number) => string;\n /** Where an empty or unreadable field starts from. */\n fallback?: () => number;\n}\n\n/**\n * A number field between a − and a +, each moving it by a step on the step's\n * grid (`stepValue`) and telling the field as typing would (an `input`\n * event); the arrow keys in the field do the same. The buttons are no Tab\n * stops of their own: the field is. Figures read left to right in any widget.\n */\nexport function stepperField(input: HTMLInputElement, options: StepperOptions): { element: HTMLDivElement; down: HTMLButtonElement; up: HTMLButtonElement } {\n const wrap = document.createElement('div');\n wrap.className = 'tcw-stepper';\n wrap.dir = 'ltr';\n const move = (dir: 1 | -1) => {\n const typed = Number(input.value);\n const from = input.value.trim() !== '' && Number.isFinite(typed) ? typed : options.fallback?.() ?? 0;\n const next = stepValue(from, dir, options.step(from, dir));\n input.value = options.format ? options.format(next) : String(next);\n input.dispatchEvent(new Event('input'));\n };\n const button = (dir: 1 | -1, text: string) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tcw-stepper-btn';\n btn.tabIndex = -1;\n btn.setAttribute('aria-label', text);\n btn.innerHTML = createIcon(dir > 0 ? 'plus' : 'minus', 14);\n btn.addEventListener('click', () => move(dir));\n return btn;\n };\n input.addEventListener('keydown', (e) => {\n if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;\n e.preventDefault();\n move(e.key === 'ArrowUp' ? 1 : -1);\n });\n const down = button(-1, options.labels.down);\n const up = button(1, options.labels.up);\n wrap.append(down, input, up);\n return { element: wrap, down, up };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`./focusTrap-B8OoGwHb.cjs`);let t=require(`@tradecanvas/commons`);var n=4,r=2,i=150,a=`[role=menuitem], [role=menuitemcheckbox], [role=menuitemradio]`,o=`(max-width: 640px)`,s=`tcw:back`,c=new Set;function l(e,t,n,r=e,i=!1){let a=i?r-t>=0?r-t:e:e+t>n?r-t:e;return Math.max(0,Math.min(a,n-t))}function u(e){return e.radio?`menuitemradio`:e.checked===void 0?`menuitem`:`menuitemcheckbox`}function d(t){let n=`<span class="tcw-context-icon"></span>`;return t.radio?t.checked?`<span class="tcw-context-icon tcw-context-dot"></span>`:n:t.checked?e.i(`check`,14):t.icon&&e.i(t.icon,14)||n}var f=class t{el;onPick=null;onRemove=null;returnFocus=null;anchor=null;child=null;parent=null;hoverTimer=null;hoverItem=null;onDocPointer=e=>{let t=e.target;this.holds(t)||this.anchor?.contains(t)||this.close()};onWheel=e=>{this.holds(e.target)||this.close()};onAway=()=>this.close();constructor(e,t,n=``){this.host=e,this.backLabel=n,this.el=document.createElement(`div`),this.el.className=`tcw-context-menu`,this.el.setAttribute(`role`,`menu`),this.el.setAttribute(`aria-label`,t),this.el.hidden=!0,this.el.addEventListener(`keydown`,e=>this.onKeyDown(e)),e.appendChild(this.el)}open(e,t,n,r,i,a,o=t,s=!1){this.show(e,{x:t,y:n,xBack:o,yBack:n,before:s},r,i,a,!0)}openUnder(e,t,n,i=`start`,a=r){let o=t.getBoundingClientRect(),s=this.host.getBoundingClientRect(),c=i===`end`!==this.rtl();this.open(e,o.left-s.left,o.bottom-s.top+a,n,t,void 0,o.right-s.left,c)}openBeside(e,t,n){let i=t.getBoundingClientRect(),a=this.host.getBoundingClientRect(),o=i.right-a.left+r,s=i.left-a.left-r;this.show(e,{x:o,y:i.top-a.top,xBack:s,yBack:i.bottom-a.top,before:this.rtl()},n,t,void 0,!0),this.returnFocus=t}static closeAnchoredTo(e){let t=[...c].filter(t=>t.anchor===e);for(let e of t)e.close();return t.length>0}static focusedAnchor(){return[...c].find(e=>e.anchor!==null&&e.hasFocus())?.anchor??null}close(){if(this.clearHover(),this.child?.close(),this.el.hidden)return;c.delete(this);let e=!this.parent||this.el.contains(document.activeElement);this.el.hidden=!0,this.onPick=null,this.onRemove=null,this.anchor?.setAttribute(`aria-expanded`,`false`),this.anchor=null,document.removeEventListener(`pointerdown`,this.onDocPointer,!0),document.removeEventListener(`wheel`,this.onWheel,{capture:!0}),window.removeEventListener(`resize`,this.onAway),window.removeEventListener(`blur`,this.onAway),e&&this.returnFocus?.isConnected&&this.returnFocus.focus({preventScroll:!0}),this.returnFocus=null}isOpen(){return!this.el.hidden}hasFocus(){return document.activeElement!==null&&this.holds(document.activeElement)}destroy(){this.close(),this.child?.destroy(),this.el.remove()}show(e,t,r,i,a,o){this.clearHover(),this.child?.close(),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.onPick=r,this.onRemove=a??null,this.anchor=i??null,this.anchor?.setAttribute(`aria-expanded`,`true`),this.el.replaceChildren(...e.map(e=>this.entry(e))),this.el.hidden=!1,c.add(this);let s=this.host.getBoundingClientRect(),u=Math.min(this.host.clientWidth,window.innerWidth-s.left)-n,d=Math.min(this.host.clientHeight,window.innerHeight-s.top)-n;this.el.style.maxHeight=`${Math.max(0,d-this.frameHeight())}px`,this.el.style.left=`${l(t.x,this.el.offsetWidth,u,t.xBack,t.before)}px`,this.el.style.top=`${l(t.y,this.el.offsetHeight,d,t.yBack)}px`,document.addEventListener(`pointerdown`,this.onDocPointer,!0),document.addEventListener(`wheel`,this.onWheel,{capture:!0,passive:!0}),window.addEventListener(`resize`,this.onAway),window.addEventListener(`blur`,this.onAway);let f=this.revealChoice();o&&(f??this.items()[0])?.focus({preventScroll:!0})}frameHeight(){let e=getComputedStyle(this.el);if(e.boxSizing===`border-box`)return 0;let t=e=>parseFloat(e)||0;return t(e.paddingTop)+t(e.paddingBottom)+t(e.borderTopWidth)+t(e.borderBottomWidth)}revealChoice(){let e=this.el.querySelector(`[role=menuitemradio][aria-checked=true]`);return!e||e.offsetTop+e.offsetHeight<=this.el.clientHeight?null:(this.el.scrollTop=e.offsetTop-(this.el.clientHeight-e.offsetHeight)/2,e)}rtl(){return this.host.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`}holds(e){return this.el.contains(e)||(this.child?.holds(e)??!1)}entry(e){if(e===`separator`){let e=document.createElement(`div`);return e.className=`tcw-context-sep`,e.setAttribute(`role`,`separator`),e}let t=document.createElement(`button`);t.type=`button`,t.className=`tcw-context-item${e.danger?` tcw-context-danger`:``}`,t.setAttribute(`role`,u(e)),(e.radio||e.checked!==void 0)&&t.setAttribute(`aria-checked`,String(e.checked??!1)),t.tabIndex=-1,t.dataset.id=e.id,t.innerHTML=d(e);let n=document.createElement(`span`);n.className=`tcw-context-label`,n.textContent=e.label,t.appendChild(n),this.appendEnd(t,e);let r=e.submenu;return t.addEventListener(`pointermove`,n=>{n.pointerType!==`touch`&&this.hover(t,e.label,r)}),t.addEventListener(`click`,()=>{r?this.openSubmenu(t,e.label,r,!0):this.pick(e.id)}),e.remove?this.removableRow(t,e.remove):t}appendEnd(t,n){if(n.count!==void 0){let e=document.createElement(`span`);e.className=`tcw-context-count`,e.textContent=String(n.count),t.appendChild(e)}if(n.shortcut){t.setAttribute(`aria-keyshortcuts`,n.shortcut);let e=document.createElement(`kbd`);e.className=`tcw-context-key`,e.setAttribute(`aria-hidden`,`true`),e.textContent=n.shortcut,t.appendChild(e)}n.submenu&&(t.setAttribute(`aria-haspopup`,`menu`),t.setAttribute(`aria-expanded`,`false`),t.insertAdjacentHTML(`beforeend`,e.i(`chevronRight`,12)),t.lastElementChild?.classList.add(`tcw-context-more`))}removableRow(t,n){t.setAttribute(`aria-keyshortcuts`,`Delete`);let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-context-remove`,r.tabIndex=-1,r.setAttribute(`aria-hidden`,`true`),r.title=n,r.innerHTML=e.i(`x`,12),r.addEventListener(`click`,()=>this.remove(t));let i=document.createElement(`div`);return i.className=`tcw-context-row`,i.setAttribute(`role`,`none`),i.append(t,r),i}openSubmenu(e,t,n,r){this.clearHover();let i=this.child??this.makeChild();if(i.isOpen()&&i.anchor===e){r&&i.items()[0]?.focus({preventScroll:!0});return}i.close(),i.el.setAttribute(`aria-label`,t);let a=this.host.getBoundingClientRect(),c=e.getBoundingClientRect(),l={x:c.right-a.left,y:c.top-a.top,xBack:c.left-a.left,yBack:c.bottom-a.top,before:this.rtl()},u=window.matchMedia?.(o).matches??!1,d=u?[{id:s,label:t,icon:`chevronLeft`},`separator`,...n]:n;i.show(d,l,e=>{e!==s&&this.pick(e)},e,e=>this.onRemove?.(e),r);let f=u?i.el.querySelector(`[data-id="${s}"]`):null;f?.classList.add(`tcw-context-back`),f&&this.backLabel&&f.setAttribute(`aria-label`,`${this.backLabel}: ${t}`),i.returnFocus=e}makeChild(){let e=new t(this.host,``,this.backLabel);return e.parent=this,e.el.addEventListener(`pointerenter`,()=>this.clearHover()),this.child=e,e}hover(e,t,n){if(this.hoverTimer!==null&&this.hoverItem===e)return;this.clearHover();let r=this.child?.isOpen()?this.child.anchor:null;(n?r===e:r===null)||(this.hoverItem=e,this.hoverTimer=setTimeout(()=>{this.hoverTimer=null,this.hoverItem=null,n?this.openSubmenu(e,t,n,!1):this.child?.close()},i))}clearHover(){this.hoverItem=null,this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null)}remove(e){let t=e.dataset.id,n=e.closest(`.tcw-context-row`);if(!t||!n)return;let r=this.items().indexOf(e);this.onRemove?.(t),n.remove();let i=[...this.el.children].filter((e,t,n)=>e.classList.contains(`tcw-context-sep`)&&(t===0||t===n.length-1||n[t-1].classList.contains(`tcw-context-sep`)));for(let e of i)e.remove();let a=this.items();if(a.length===0){this.close();return}a[Math.min(r,a.length-1)].focus({preventScroll:!0})}pick(e){let t=this.onPick;this.close(),t?.(e)}items(){return[...this.el.querySelectorAll(a)]}onKeyDown(e){let t=this.items(),n=t.indexOf(document.activeElement),[r,i]=this.rtl()?[`ArrowLeft`,`ArrowRight`]:[`ArrowRight`,`ArrowLeft`],a=-1;if(e.key===`ArrowDown`)a=(n+1)%t.length;else if(e.key===`ArrowUp`)a=(n-1+t.length)%t.length;else if(e.key===`Home`)a=0;else if(e.key===`End`)a=t.length-1;else if(e.key===r&&t[n]?.hasAttribute(`aria-haspopup`)){e.preventDefault(),t[n].click();return}else if(e.key===i&&this.parent){e.preventDefault(),this.close();return}else if(e.key===`Delete`&&n>=0){e.preventDefault(),this.remove(t[n]);return}else if(e.key===`Escape`||e.key===`Tab`){e.preventDefault(),e.stopPropagation();let t=this;for(;e.key===`Tab`&&t.parent;)t=t.parent;t.close();return}else return;e.preventDefault(),this.child?.close(),t[a]?.focus({preventScroll:!0})}};function p(e,t){let n=document.createElement(`label`);n.className=`tcw-settings-row`;let r=document.createElement(`span`);return r.className=`tcw-settings-label`,r.textContent=e,n.append(r,t),n}function m(e,t,n){let r=document.createElement(`label`);r.className=`tcw-settings-row tcw-check-row`;let i=document.createElement(`input`);i.type=`checkbox`,i.checked=t,i.addEventListener(`change`,()=>n(i.checked));let a=document.createElement(`span`);return a.className=`tcw-settings-label`,a.textContent=e,r.append(i,a),r}function h(e,t){let n=document.createElement(`button`);return n.type=`button`,n.className=`tcw-toggle${e?` tcw-on`:``}`,n.setAttribute(`role`,`switch`),n.setAttribute(`aria-checked`,String(e)),n.addEventListener(`click`,()=>{let e=!n.classList.contains(`tcw-on`);n.classList.toggle(`tcw-on`,e),n.setAttribute(`aria-checked`,String(e)),t(e)}),n}function g(e,t,n){let r=document.createElement(`input`);return r.type=`number`,r.className=`tcw-indi-input`,r.value=String(e),r.step=String(t.step??(Number.isInteger(e)?1:.001)),t.min!==void 0&&(r.min=String(t.min)),t.max!==void 0&&(r.max=String(t.max)),r.addEventListener(`change`,()=>{if(r.value.trim()===``)return;let e=Number(r.value);Number.isFinite(e)&&(t.whole&&(e=Math.round(e)),t.min!==void 0&&(e=Math.max(t.min,e)),t.max!==void 0&&(e=Math.min(t.max,e)),r.value=String(e),n(e))}),r}function _(e,t){let n=document.createElement(`input`);return n.type=`color`,n.className=`tcw-indi-color`,n.value=y(e),n.addEventListener(`input`,()=>t(n.value)),n}function v(e,t,n){let r=document.createElement(`select`);r.className=`tcw-indi-input tcw-indi-select`;for(let t of e){let e=document.createElement(`option`);e.value=t.value,e.textContent=t.label,r.appendChild(e)}return r.value=t,r.addEventListener(`change`,()=>n(r.value)),r}function y(e){if(typeof e!=`string`)return`#000000`;let t=e.trim();if(/^#[0-9a-f]{6}$/i.test(t))return t.toLowerCase();if(/^#[0-9a-f]{8}$/i.test(t))return t.slice(0,7).toLowerCase();if(/^#[0-9a-f]{3,4}$/i.test(t))return`#${t[1]}${t[1]}${t[2]}${t[2]}${t[3]}${t[3]}`.toLowerCase();let n=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(t);if(n)return`#${[n[1],n[2],n[3]].map(e=>Math.min(255,Number(e)).toString(16).padStart(2,`0`)).join(``)}`;let r=x(t);return r!==null&&r!==t?y(r):`#000000`}var b;function x(e){if(b===void 0)try{b=typeof document<`u`?document.createElement(`canvas`).getContext(`2d`):null}catch{b=null}if(!b)return null;b.fillStyle=`#010203`,b.fillStyle=e;let t=String(b.fillStyle);return t===`#010203`?null:t}function S(e){if(typeof e!=`string`)return 1;let t=/^rgba\([^)]*,\s*([\d.]+)\s*\)$/i.exec(e.trim());return t?Math.max(0,Math.min(1,Number(t[1]))):1}function C(e,t){let n=y(e),[r,i,a]=[1,3,5].map(e=>parseInt(n.slice(e,e+2),16));return`rgba(${r}, ${i}, ${a}, ${Math.round(t*100)/100})`}var w={ArrowRight:1,ArrowDown:1,ArrowLeft:-1,ArrowUp:-1};function T(e,t,n,r){let i=document.createElement(`div`);i.className=`tcw-segmented`,i.setAttribute(`role`,`radiogroup`),i.setAttribute(`aria-label`,r);let a=e.map(e=>{let t=document.createElement(`button`);return t.type=`button`,t.className=`tcw-segment`,t.setAttribute(`role`,`radio`),t.dataset.value=e.value,t.textContent=e.label,t.addEventListener(`click`,()=>s(e.value)),t}),o=e=>{let t=Math.max(0,a.findIndex(t=>t.dataset.value===e));a.forEach((n,r)=>{n.setAttribute(`aria-checked`,String(n.dataset.value===e)),n.tabIndex=r===t?0:-1})},s=e=>{o(e),n(e)};return i.addEventListener(`keydown`,t=>{let n=a.findIndex(e=>e===t.target);if(n<0||t.altKey||t.ctrlKey||t.metaKey)return;let r=a.length-1,o;if(t.key===`Home`)o=0;else if(t.key===`End`)o=r;else if(t.key in w){let e=i.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`,r=t.key===`ArrowLeft`||t.key===`ArrowRight`;o=(n+(e&&r?-w[t.key]:w[t.key])+a.length)%a.length}else return;t.preventDefault(),a[o].focus(),s(e[o].value)}),o(t),i.append(...a),i}function E(e,n,r){if(!(r>0)||!Number.isFinite(e))return e;let i=e/r,a=Math.round(i),o=Math.abs(i-a)<1e-9*Math.max(1,Math.abs(i)),s=n>0?o?a+1:Math.ceil(i):o?a-1:Math.floor(i);return s<1?e:Number((s*r).toFixed((0,t.stepDecimals)(r)))}function D(t,n){let r=document.createElement(`div`);r.className=`tcw-stepper`,r.dir=`ltr`;let i=e=>{let r=Number(t.value),i=t.value.trim()!==``&&Number.isFinite(r)?r:n.fallback?.()??0,a=E(i,e,n.step(i,e));t.value=n.format?n.format(a):String(a),t.dispatchEvent(new Event(`input`))},a=(t,n)=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-stepper-btn`,r.tabIndex=-1,r.setAttribute(`aria-label`,n),r.innerHTML=e.i(t>0?`plus`:`minus`,14),r.addEventListener(`click`,()=>i(t)),r};t.addEventListener(`keydown`,e=>{e.key!==`ArrowUp`&&e.key!==`ArrowDown`||(e.preventDefault(),i(e.key===`ArrowUp`?1:-1))});let o=a(-1,n.labels.down),s=a(1,n.labels.up);return r.append(o,t,s),{element:r,down:o,up:s}}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return h}});
|
|
2
|
-
//# sourceMappingURL=settingsControls-
|
|
1
|
+
const e=require(`./focusTrap-DrhhmG0V.cjs`);let t=require(`@tradecanvas/commons`);var n=4,r=2,i=150,a=`[role=menuitem], [role=menuitemcheckbox], [role=menuitemradio]`,o=`(max-width: 640px)`,s=`tcw:back`,c=new Set;function l(e,t,n,r=e,i=!1){let a=i?r-t>=0?r-t:e:e+t>n?r-t:e;return Math.max(0,Math.min(a,n-t))}function u(e){return e.radio?`menuitemradio`:e.checked===void 0?`menuitem`:`menuitemcheckbox`}function d(t){let n=`<span class="tcw-context-icon"></span>`;return t.radio?t.checked?`<span class="tcw-context-icon tcw-context-dot"></span>`:n:t.checked?e.i(`check`,14):t.icon&&e.i(t.icon,14)||n}var f=class t{el;onPick=null;onRemove=null;returnFocus=null;anchor=null;child=null;parent=null;hoverTimer=null;hoverItem=null;onDocPointer=e=>{let t=e.target;this.holds(t)||this.anchor?.contains(t)||this.close()};onWheel=e=>{this.holds(e.target)||this.close()};onAway=()=>this.close();constructor(e,t,n=``){this.host=e,this.backLabel=n,this.el=document.createElement(`div`),this.el.className=`tcw-context-menu`,this.el.setAttribute(`role`,`menu`),this.el.setAttribute(`aria-label`,t),this.el.hidden=!0,this.el.addEventListener(`keydown`,e=>this.onKeyDown(e)),e.appendChild(this.el)}open(e,t,n,r,i,a,o=t,s=!1){this.show(e,{x:t,y:n,xBack:o,yBack:n,before:s},r,i,a,!0)}openUnder(e,t,n,i=`start`,a=r){let o=t.getBoundingClientRect(),s=this.host.getBoundingClientRect(),c=i===`end`!==this.rtl();this.open(e,o.left-s.left,o.bottom-s.top+a,n,t,void 0,o.right-s.left,c)}openBeside(e,t,n){let i=t.getBoundingClientRect(),a=this.host.getBoundingClientRect(),o=i.right-a.left+r,s=i.left-a.left-r;this.show(e,{x:o,y:i.top-a.top,xBack:s,yBack:i.bottom-a.top,before:this.rtl()},n,t,void 0,!0),this.returnFocus=t}static closeAnchoredTo(e){let t=[...c].filter(t=>t.anchor===e);for(let e of t)e.close();return t.length>0}static focusedAnchor(){return[...c].find(e=>e.anchor!==null&&e.hasFocus())?.anchor??null}close(){if(this.clearHover(),this.child?.close(),this.el.hidden)return;c.delete(this);let e=!this.parent||this.el.contains(document.activeElement);this.el.hidden=!0,this.onPick=null,this.onRemove=null,this.anchor?.setAttribute(`aria-expanded`,`false`),this.anchor=null,document.removeEventListener(`pointerdown`,this.onDocPointer,!0),document.removeEventListener(`wheel`,this.onWheel,{capture:!0}),window.removeEventListener(`resize`,this.onAway),window.removeEventListener(`blur`,this.onAway),e&&this.returnFocus?.isConnected&&this.returnFocus.focus({preventScroll:!0}),this.returnFocus=null}isOpen(){return!this.el.hidden}hasFocus(){return document.activeElement!==null&&this.holds(document.activeElement)}destroy(){this.close(),this.child?.destroy(),this.el.remove()}show(e,t,r,i,a,o){this.clearHover(),this.child?.close(),this.returnFocus=document.activeElement instanceof HTMLElement?document.activeElement:null,this.onPick=r,this.onRemove=a??null,this.anchor=i??null,this.anchor?.setAttribute(`aria-expanded`,`true`),this.el.replaceChildren(...e.map(e=>this.entry(e))),this.el.hidden=!1,c.add(this);let s=this.host.getBoundingClientRect(),u=Math.min(this.host.clientWidth,window.innerWidth-s.left)-n,d=Math.min(this.host.clientHeight,window.innerHeight-s.top)-n;this.el.style.maxHeight=`${Math.max(0,d-this.frameHeight())}px`,this.el.style.left=`${l(t.x,this.el.offsetWidth,u,t.xBack,t.before)}px`,this.el.style.top=`${l(t.y,this.el.offsetHeight,d,t.yBack)}px`,document.addEventListener(`pointerdown`,this.onDocPointer,!0),document.addEventListener(`wheel`,this.onWheel,{capture:!0,passive:!0}),window.addEventListener(`resize`,this.onAway),window.addEventListener(`blur`,this.onAway);let f=this.revealChoice();o&&(f??this.items()[0])?.focus({preventScroll:!0})}frameHeight(){let e=getComputedStyle(this.el);if(e.boxSizing===`border-box`)return 0;let t=e=>parseFloat(e)||0;return t(e.paddingTop)+t(e.paddingBottom)+t(e.borderTopWidth)+t(e.borderBottomWidth)}revealChoice(){let e=this.el.querySelector(`[role=menuitemradio][aria-checked=true]`);return!e||e.offsetTop+e.offsetHeight<=this.el.clientHeight?null:(this.el.scrollTop=e.offsetTop-(this.el.clientHeight-e.offsetHeight)/2,e)}rtl(){return this.host.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`}holds(e){return this.el.contains(e)||(this.child?.holds(e)??!1)}entry(e){if(e===`separator`){let e=document.createElement(`div`);return e.className=`tcw-context-sep`,e.setAttribute(`role`,`separator`),e}let t=document.createElement(`button`);t.type=`button`,t.className=`tcw-context-item${e.danger?` tcw-context-danger`:``}`,t.setAttribute(`role`,u(e)),(e.radio||e.checked!==void 0)&&t.setAttribute(`aria-checked`,String(e.checked??!1)),t.tabIndex=-1,t.dataset.id=e.id,t.innerHTML=d(e);let n=document.createElement(`span`);n.className=`tcw-context-label`,n.textContent=e.label,t.appendChild(n),this.appendEnd(t,e);let r=e.submenu;return t.addEventListener(`pointermove`,n=>{n.pointerType!==`touch`&&this.hover(t,e.label,r)}),t.addEventListener(`click`,()=>{r?this.openSubmenu(t,e.label,r,!0):this.pick(e.id)}),e.remove?this.removableRow(t,e.remove):t}appendEnd(t,n){if(n.count!==void 0){let e=document.createElement(`span`);e.className=`tcw-context-count`,e.textContent=String(n.count),t.appendChild(e)}if(n.shortcut){t.setAttribute(`aria-keyshortcuts`,n.shortcut);let e=document.createElement(`kbd`);e.className=`tcw-context-key`,e.setAttribute(`aria-hidden`,`true`),e.textContent=n.shortcut,t.appendChild(e)}n.submenu&&(t.setAttribute(`aria-haspopup`,`menu`),t.setAttribute(`aria-expanded`,`false`),t.insertAdjacentHTML(`beforeend`,e.i(`chevronRight`,12)),t.lastElementChild?.classList.add(`tcw-context-more`))}removableRow(t,n){t.setAttribute(`aria-keyshortcuts`,`Delete`);let r=document.createElement(`button`);r.type=`button`,r.className=`tcw-context-remove`,r.tabIndex=-1,r.setAttribute(`aria-hidden`,`true`),r.title=n,r.innerHTML=e.i(`x`,12),r.addEventListener(`click`,()=>this.remove(t));let i=document.createElement(`div`);return i.className=`tcw-context-row`,i.setAttribute(`role`,`none`),i.append(t,r),i}openSubmenu(e,t,n,r){this.clearHover();let i=this.child??this.makeChild();if(i.isOpen()&&i.anchor===e){r&&i.items()[0]?.focus({preventScroll:!0});return}i.close(),i.el.setAttribute(`aria-label`,t);let a=this.host.getBoundingClientRect(),c=e.getBoundingClientRect(),l={x:c.right-a.left,y:c.top-a.top,xBack:c.left-a.left,yBack:c.bottom-a.top,before:this.rtl()},u=window.matchMedia?.(o).matches??!1,d=u?[{id:s,label:t,icon:`chevronLeft`},`separator`,...n]:n;i.show(d,l,e=>{e!==s&&this.pick(e)},e,e=>this.onRemove?.(e),r);let f=u?i.el.querySelector(`[data-id="${s}"]`):null;f?.classList.add(`tcw-context-back`),f&&this.backLabel&&f.setAttribute(`aria-label`,`${this.backLabel}: ${t}`),i.returnFocus=e}makeChild(){let e=new t(this.host,``,this.backLabel);return e.parent=this,e.el.addEventListener(`pointerenter`,()=>this.clearHover()),this.child=e,e}hover(e,t,n){if(this.hoverTimer!==null&&this.hoverItem===e)return;this.clearHover();let r=this.child?.isOpen()?this.child.anchor:null;(n?r===e:r===null)||(this.hoverItem=e,this.hoverTimer=setTimeout(()=>{this.hoverTimer=null,this.hoverItem=null,n?this.openSubmenu(e,t,n,!1):this.child?.close()},i))}clearHover(){this.hoverItem=null,this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null)}remove(e){let t=e.dataset.id,n=e.closest(`.tcw-context-row`);if(!t||!n)return;let r=this.items().indexOf(e);this.onRemove?.(t),n.remove();let i=[...this.el.children].filter((e,t,n)=>e.classList.contains(`tcw-context-sep`)&&(t===0||t===n.length-1||n[t-1].classList.contains(`tcw-context-sep`)));for(let e of i)e.remove();let a=this.items();if(a.length===0){this.close();return}a[Math.min(r,a.length-1)].focus({preventScroll:!0})}pick(e){let t=this.onPick;this.close(),t?.(e)}items(){return[...this.el.querySelectorAll(a)]}onKeyDown(e){let t=this.items(),n=t.indexOf(document.activeElement),[r,i]=this.rtl()?[`ArrowLeft`,`ArrowRight`]:[`ArrowRight`,`ArrowLeft`],a=-1;if(e.key===`ArrowDown`)a=(n+1)%t.length;else if(e.key===`ArrowUp`)a=(n-1+t.length)%t.length;else if(e.key===`Home`)a=0;else if(e.key===`End`)a=t.length-1;else if(e.key===r&&t[n]?.hasAttribute(`aria-haspopup`)){e.preventDefault(),t[n].click();return}else if(e.key===i&&this.parent){e.preventDefault(),this.close();return}else if(e.key===`Delete`&&n>=0){e.preventDefault(),this.remove(t[n]);return}else if(e.key===`Escape`||e.key===`Tab`){e.preventDefault(),e.stopPropagation();let t=this;for(;e.key===`Tab`&&t.parent;)t=t.parent;t.close();return}else return;e.preventDefault(),this.child?.close(),t[a]?.focus({preventScroll:!0})}};function p(e,t){let n=document.createElement(`label`);n.className=`tcw-settings-row`;let r=document.createElement(`span`);return r.className=`tcw-settings-label`,r.textContent=e,n.append(r,t),n}function m(e,t,n){let r=document.createElement(`label`);r.className=`tcw-settings-row tcw-check-row`;let i=document.createElement(`input`);i.type=`checkbox`,i.checked=t,i.addEventListener(`change`,()=>n(i.checked));let a=document.createElement(`span`);return a.className=`tcw-settings-label`,a.textContent=e,r.append(i,a),r}function h(e,t){let n=document.createElement(`button`);return n.type=`button`,n.className=`tcw-toggle${e?` tcw-on`:``}`,n.setAttribute(`role`,`switch`),n.setAttribute(`aria-checked`,String(e)),n.addEventListener(`click`,()=>{let e=!n.classList.contains(`tcw-on`);n.classList.toggle(`tcw-on`,e),n.setAttribute(`aria-checked`,String(e)),t(e)}),n}function g(e,t,n){let r=document.createElement(`input`);return r.type=`number`,r.className=`tcw-indi-input`,r.value=String(e),r.step=String(t.step??(Number.isInteger(e)?1:.001)),t.min!==void 0&&(r.min=String(t.min)),t.max!==void 0&&(r.max=String(t.max)),r.addEventListener(`change`,()=>{if(r.value.trim()===``)return;let e=Number(r.value);Number.isFinite(e)&&(t.whole&&(e=Math.round(e)),t.min!==void 0&&(e=Math.max(t.min,e)),t.max!==void 0&&(e=Math.min(t.max,e)),r.value=String(e),n(e))}),r}function _(e,t){let n=document.createElement(`input`);return n.type=`color`,n.className=`tcw-indi-color`,n.value=y(e),n.addEventListener(`input`,()=>t(n.value)),n}function v(e,t,n){let r=document.createElement(`select`);r.className=`tcw-indi-input tcw-indi-select`;for(let t of e){let e=document.createElement(`option`);e.value=t.value,e.textContent=t.label,r.appendChild(e)}return r.value=t,r.addEventListener(`change`,()=>n(r.value)),r}function y(e){if(typeof e!=`string`)return`#000000`;let t=e.trim();if(/^#[0-9a-f]{6}$/i.test(t))return t.toLowerCase();if(/^#[0-9a-f]{8}$/i.test(t))return t.slice(0,7).toLowerCase();if(/^#[0-9a-f]{3,4}$/i.test(t))return`#${t[1]}${t[1]}${t[2]}${t[2]}${t[3]}${t[3]}`.toLowerCase();let n=/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i.exec(t);if(n)return`#${[n[1],n[2],n[3]].map(e=>Math.min(255,Number(e)).toString(16).padStart(2,`0`)).join(``)}`;let r=x(t);return r!==null&&r!==t?y(r):`#000000`}var b;function x(e){if(b===void 0)try{b=typeof document<`u`?document.createElement(`canvas`).getContext(`2d`):null}catch{b=null}if(!b)return null;b.fillStyle=`#010203`,b.fillStyle=e;let t=String(b.fillStyle);return t===`#010203`?null:t}function S(e){if(typeof e!=`string`)return 1;let t=/^rgba\([^)]*,\s*([\d.]+)\s*\)$/i.exec(e.trim());return t?Math.max(0,Math.min(1,Number(t[1]))):1}function C(e,t){let n=y(e),[r,i,a]=[1,3,5].map(e=>parseInt(n.slice(e,e+2),16));return`rgba(${r}, ${i}, ${a}, ${Math.round(t*100)/100})`}var w={ArrowRight:1,ArrowDown:1,ArrowLeft:-1,ArrowUp:-1};function T(e,t,n,r){let i=document.createElement(`div`);i.className=`tcw-segmented`,i.setAttribute(`role`,`radiogroup`),i.setAttribute(`aria-label`,r);let a=e.map(e=>{let t=document.createElement(`button`);return t.type=`button`,t.className=`tcw-segment`,t.setAttribute(`role`,`radio`),t.dataset.value=e.value,t.textContent=e.label,t.addEventListener(`click`,()=>s(e.value)),t}),o=e=>{let t=Math.max(0,a.findIndex(t=>t.dataset.value===e));a.forEach((n,r)=>{n.setAttribute(`aria-checked`,String(n.dataset.value===e)),n.tabIndex=r===t?0:-1})},s=e=>{o(e),n(e)};return i.addEventListener(`keydown`,t=>{let n=a.findIndex(e=>e===t.target);if(n<0||t.altKey||t.ctrlKey||t.metaKey)return;let r=a.length-1,o;if(t.key===`Home`)o=0;else if(t.key===`End`)o=r;else if(t.key in w){let e=i.closest(`[dir]`)?.getAttribute(`dir`)===`rtl`,r=t.key===`ArrowLeft`||t.key===`ArrowRight`;o=(n+(e&&r?-w[t.key]:w[t.key])+a.length)%a.length}else return;t.preventDefault(),a[o].focus(),s(e[o].value)}),o(t),i.append(...a),i}function E(e,n,r){if(!(r>0)||!Number.isFinite(e))return e;let i=e/r,a=Math.round(i),o=Math.abs(i-a)<1e-9*Math.max(1,Math.abs(i)),s=n>0?o?a+1:Math.ceil(i):o?a-1:Math.floor(i);return s<1?e:Number((s*r).toFixed((0,t.stepDecimals)(r)))}function D(t,n){let r=document.createElement(`div`);r.className=`tcw-stepper`,r.dir=`ltr`;let i=e=>{let r=Number(t.value),i=t.value.trim()!==``&&Number.isFinite(r)?r:n.fallback?.()??0,a=E(i,e,n.step(i,e));t.value=n.format?n.format(a):String(a),t.dispatchEvent(new Event(`input`))},a=(t,n)=>{let r=document.createElement(`button`);return r.type=`button`,r.className=`tcw-stepper-btn`,r.tabIndex=-1,r.setAttribute(`aria-label`,n),r.innerHTML=e.i(t>0?`plus`:`minus`,14),r.addEventListener(`click`,()=>i(t)),r};t.addEventListener(`keydown`,e=>{e.key!==`ArrowUp`&&e.key!==`ArrowDown`||(e.preventDefault(),i(e.key===`ArrowUp`?1:-1))});let o=a(-1,n.labels.down),s=a(1,n.labels.up);return r.append(o,t,s),{element:r,down:o,up:s}}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return h}});
|
|
2
|
+
//# sourceMappingURL=settingsControls-Tn8_vJJv.cjs.map
|