@luxalgo/vela 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataProvider-DKNDHpNv.d.cts → DataProvider-BBQ3Hr21.d.cts} +1 -1
- package/dist/{DataProvider-Dzd-Erlk.d.ts → DataProvider-DGesjOJT.d.ts} +1 -1
- package/dist/{chunk-OVQKKXLZ.js → chunk-5SE4PL3H.js} +402 -60
- package/dist/{chunk-RHIDOUFL.js → chunk-AIW3EJUE.js} +2 -2
- package/dist/{chunk-GYD2THPV.js → chunk-EP6NDJYK.js} +5 -0
- package/dist/{chunk-KCCZNKH7.js → chunk-FNOMKHEZ.js} +668 -78
- package/dist/{chunk-7UFX5ZIG.js → chunk-S6F6SQ2L.js} +7 -2
- package/dist/{chunk-KM6LHB3Y.js → chunk-ULIX6AE2.js} +6 -1
- package/dist/{contributions-hX3EUyjG.d.ts → contributions-6N0rbrPC.d.ts} +46 -31
- package/dist/{contributions-o1GRKPI_.d.cts → contributions-BlV3kb0O.d.cts} +46 -31
- package/dist/{history-Dzxz-MQj.d.ts → history-BTd52YMA.d.ts} +10 -5
- package/dist/{history-LJkz4-sS.d.cts → history-DX3Zne3a.d.cts} +10 -5
- package/dist/index.cjs +680 -75
- package/dist/index.d.cts +37 -12
- package/dist/index.d.ts +37 -12
- package/dist/index.js +4 -4
- package/dist/{options-Q-576hIi.d.cts → options-DbQholFV.d.cts} +14 -0
- package/dist/{options-Q-576hIi.d.ts → options-DbQholFV.d.ts} +14 -0
- package/dist/{plugin-D94muTV-.d.cts → plugin-O34MbXf-.d.cts} +157 -7
- package/dist/{plugin-aGUD1epn.d.ts → plugin-TN3n-9YN.d.ts} +157 -7
- package/dist/plugin.cjs +5 -0
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +2 -2
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/ui.cjs +10 -0
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +680 -75
- package/dist/vela.global.min.js +59 -28
- package/dist/widget.cjs +1081 -134
- package/dist/widget.d.cts +30 -16
- package/dist/widget.d.ts +30 -16
- package/dist/widget.js +9 -9
- package/dist/workspace.cjs +1372 -183
- package/dist/workspace.d.cts +66 -8
- package/dist/workspace.d.ts +66 -8
- package/dist/workspace.js +275 -37
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { parseSymbol, priceStyleIds, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-
|
|
2
|
-
import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-
|
|
3
|
-
import { Tooltip, Menu, Dialog } from './chunk-
|
|
4
|
-
import { injectStyles, iconEl, iconMarkup, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, registerIcon, categoricalColor } from './chunk-
|
|
1
|
+
import { parseSymbol, priceStyleIds, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-FNOMKHEZ.js';
|
|
2
|
+
import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-S6F6SQ2L.js';
|
|
3
|
+
import { Tooltip, Menu, Dialog } from './chunk-AIW3EJUE.js';
|
|
4
|
+
import { injectStyles, iconEl, iconMarkup, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, registerIcon, categoricalColor } from './chunk-EP6NDJYK.js';
|
|
5
5
|
|
|
6
6
|
// src/widget/timeframe.ts
|
|
7
7
|
var UNIT_MS = {
|
|
@@ -87,9 +87,329 @@ function timeframeLabel(value) {
|
|
|
87
87
|
return parsed.short;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// src/widget/
|
|
91
|
-
var STYLE_ID = "vela-
|
|
90
|
+
// src/widget/layout-picker.ts
|
|
91
|
+
var STYLE_ID = "vela-widget-layout-picker-v14";
|
|
92
92
|
var CSS = `
|
|
93
|
+
.vela-lp-layer { position: absolute; z-index: var(--vela-z-menu); }
|
|
94
|
+
.vela-lp {
|
|
95
|
+
background: var(--vela-surface-elev);
|
|
96
|
+
color: var(--vela-fg);
|
|
97
|
+
border: 1px solid var(--vela-border-strong);
|
|
98
|
+
border-radius: 8px;
|
|
99
|
+
box-shadow: var(--vela-shadow);
|
|
100
|
+
padding: 10px 12px 10px;
|
|
101
|
+
font-size: 13px;
|
|
102
|
+
user-select: none;
|
|
103
|
+
transform-origin: top;
|
|
104
|
+
animation: vela-lp-in var(--vela-dur-fast) var(--vela-ease);
|
|
105
|
+
}
|
|
106
|
+
@keyframes vela-lp-in {
|
|
107
|
+
from { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
|
108
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
109
|
+
}
|
|
110
|
+
.vela-lp-cols { display: flex; align-items: stretch; gap: 14px; }
|
|
111
|
+
.vela-lp-heading {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: 8px;
|
|
115
|
+
font-size: 10px;
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
letter-spacing: 1.2px;
|
|
118
|
+
text-transform: uppercase;
|
|
119
|
+
color: var(--vela-fg-faint);
|
|
120
|
+
margin-bottom: 10px;
|
|
121
|
+
}
|
|
122
|
+
/* "?" help badge: hover it for the canvas explainer. */
|
|
123
|
+
.vela-lp-badge {
|
|
124
|
+
display: inline-flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
width: 14px;
|
|
128
|
+
height: 14px;
|
|
129
|
+
border-radius: 50%;
|
|
130
|
+
border: 1px solid var(--vela-border-strong);
|
|
131
|
+
color: var(--vela-fg-muted);
|
|
132
|
+
font-size: 9px;
|
|
133
|
+
font-weight: 600;
|
|
134
|
+
letter-spacing: 0;
|
|
135
|
+
cursor: default;
|
|
136
|
+
transition: color var(--vela-dur-fast) var(--vela-ease), border-color var(--vela-dur-fast) var(--vela-ease);
|
|
137
|
+
}
|
|
138
|
+
.vela-lp-badge:hover { color: var(--vela-fg-bright); border-color: var(--vela-fg-muted); }
|
|
139
|
+
.vela-lp-tip { display: flex; flex-direction: column; gap: 4px; max-width: 230px; white-space: normal; }
|
|
140
|
+
.vela-lp-vsep { width: 1px; flex: none; align-self: stretch; background: var(--vela-border-faint); }
|
|
141
|
+
.vela-lp-layout { width: 132px; display: flex; flex-direction: column; align-items: center; }
|
|
142
|
+
.vela-lp-layout > .vela-lp-heading,
|
|
143
|
+
.vela-lp-layout > .vela-lp-presets { align-self: stretch; }
|
|
144
|
+
.vela-lp-grid { display: grid; grid-template-columns: repeat(4, 24px); grid-auto-rows: 24px; gap: 4px; }
|
|
145
|
+
.vela-lp-sq {
|
|
146
|
+
all: unset;
|
|
147
|
+
box-sizing: border-box;
|
|
148
|
+
border-radius: 4px;
|
|
149
|
+
background: var(--vela-hover);
|
|
150
|
+
border: 1px solid var(--vela-border-faint);
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
transition: background var(--vela-dur-fast) var(--vela-ease), border-color var(--vela-dur-fast) var(--vela-ease);
|
|
153
|
+
}
|
|
154
|
+
.vela-lp-sq:hover { background: var(--vela-hover-strong); border-color: var(--vela-border-strong); }
|
|
155
|
+
/* Selected/previewed squares \u2014 the shared monochrome selection chip. */
|
|
156
|
+
.vela-lp-sq[data-on='1'] {
|
|
157
|
+
background: var(--vela-selected-bg);
|
|
158
|
+
border-color: var(--vela-selected-bg);
|
|
159
|
+
}
|
|
160
|
+
.vela-lp-presets { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
|
|
161
|
+
.vela-lp-preset { all: unset; padding: 5px 8px; border-radius: 4px; cursor: pointer; color: var(--vela-fg-muted); font-size: 12px; white-space: nowrap; transition: transform 120ms var(--vela-ease); }
|
|
162
|
+
.vela-lp-preset:hover { background: var(--vela-hover); }
|
|
163
|
+
.vela-lp-preset:active { transform: scale(0.98); }
|
|
164
|
+
.vela-lp-preset[data-checked='1'] { background: var(--vela-hover-strong); color: var(--vela-fg-bright); }
|
|
165
|
+
.vela-lp-sync { display: flex; flex-direction: column; gap: 4px; min-width: 118px; }
|
|
166
|
+
.vela-lp-sync-row { all: unset; display: flex; align-items: center; gap: 14px; padding: 6px 8px; border-radius: 5px; cursor: pointer; }
|
|
167
|
+
.vela-lp-sync-row:hover { background: var(--vela-hover); }
|
|
168
|
+
.vela-lp-sync-row .vela-lp-label { flex: 1 1 auto; }
|
|
169
|
+
/* Toggle pill \u2014 same control language as the menu's switch rows, monochrome ON state. */
|
|
170
|
+
.vela-lp-switch {
|
|
171
|
+
position: relative;
|
|
172
|
+
flex: none;
|
|
173
|
+
width: 34px;
|
|
174
|
+
height: 18px;
|
|
175
|
+
border-radius: 9px;
|
|
176
|
+
background: var(--vela-hover);
|
|
177
|
+
border: 1px solid var(--vela-border-soft);
|
|
178
|
+
transition: background 0.16s ease, border-color 0.16s ease;
|
|
179
|
+
}
|
|
180
|
+
.vela-lp-switch::after {
|
|
181
|
+
content: '';
|
|
182
|
+
position: absolute;
|
|
183
|
+
top: 2px;
|
|
184
|
+
left: 2px;
|
|
185
|
+
width: 12px;
|
|
186
|
+
height: 12px;
|
|
187
|
+
border-radius: 50%;
|
|
188
|
+
background: var(--vela-fg-muted);
|
|
189
|
+
transition: transform 0.16s ease, background 0.16s ease;
|
|
190
|
+
}
|
|
191
|
+
.vela-lp-switch.on { background: var(--vela-selected-bg); border-color: var(--vela-selected-bg); }
|
|
192
|
+
.vela-lp-switch.on::after { transform: translateX(16px); background: var(--vela-selected-fg); }
|
|
193
|
+
`;
|
|
194
|
+
var GRID = 4;
|
|
195
|
+
var LayoutPicker = class {
|
|
196
|
+
constructor(opts) {
|
|
197
|
+
this.squares = [];
|
|
198
|
+
this.isOpen = false;
|
|
199
|
+
/** Hover preview (1-based rows/cols), null = show current shape. */
|
|
200
|
+
this.hover = null;
|
|
201
|
+
this.onDocPointerDown = (e) => {
|
|
202
|
+
const t = e.target;
|
|
203
|
+
if (t && (this.layer.contains(t) || this.opts.trigger.contains(t))) return;
|
|
204
|
+
this.close();
|
|
205
|
+
};
|
|
206
|
+
this.onDocKeydown = (e) => {
|
|
207
|
+
if (e.key === "Escape") this.close();
|
|
208
|
+
};
|
|
209
|
+
this.opts = opts;
|
|
210
|
+
this.doc = opts.host.ownerDocument;
|
|
211
|
+
injectStyles(STYLE_ID, CSS, this.doc);
|
|
212
|
+
const doc = this.doc;
|
|
213
|
+
this.layer = doc.createElement("div");
|
|
214
|
+
this.layer.className = "vela-ui-layer vela-lp-layer";
|
|
215
|
+
this.layer.style.display = "none";
|
|
216
|
+
const panel = doc.createElement("div");
|
|
217
|
+
panel.className = "vela-lp";
|
|
218
|
+
this.layer.appendChild(panel);
|
|
219
|
+
const cols = doc.createElement("div");
|
|
220
|
+
cols.className = "vela-lp-cols";
|
|
221
|
+
panel.appendChild(cols);
|
|
222
|
+
const layoutCol = doc.createElement("div");
|
|
223
|
+
layoutCol.className = "vela-lp-layout";
|
|
224
|
+
const layoutHeading = doc.createElement("div");
|
|
225
|
+
layoutHeading.className = "vela-lp-heading";
|
|
226
|
+
const headingText = doc.createElement("span");
|
|
227
|
+
headingText.textContent = "Layout";
|
|
228
|
+
const badge = doc.createElement("span");
|
|
229
|
+
badge.className = "vela-lp-badge";
|
|
230
|
+
badge.textContent = "?";
|
|
231
|
+
layoutHeading.append(headingText, badge);
|
|
232
|
+
layoutCol.appendChild(layoutHeading);
|
|
233
|
+
this.infoTip = new Tooltip(badge, {
|
|
234
|
+
host: opts.host,
|
|
235
|
+
placement: "bottom",
|
|
236
|
+
content: () => this.tipNode()
|
|
237
|
+
});
|
|
238
|
+
const grid = doc.createElement("div");
|
|
239
|
+
grid.className = "vela-lp-grid";
|
|
240
|
+
for (let r = 0; r < GRID; r += 1) {
|
|
241
|
+
for (let c = 0; c < GRID; c += 1) {
|
|
242
|
+
const sq = doc.createElement("button");
|
|
243
|
+
sq.className = "vela-lp-sq";
|
|
244
|
+
sq.dataset.r = String(r);
|
|
245
|
+
sq.dataset.c = String(c);
|
|
246
|
+
sq.setAttribute("aria-label", `${c + 1} \xD7 ${r + 1}`);
|
|
247
|
+
this.squares.push(sq);
|
|
248
|
+
grid.appendChild(sq);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
layoutCol.appendChild(grid);
|
|
252
|
+
this.presetsEl = doc.createElement("div");
|
|
253
|
+
this.presetsEl.className = "vela-lp-presets";
|
|
254
|
+
layoutCol.appendChild(this.presetsEl);
|
|
255
|
+
cols.appendChild(layoutCol);
|
|
256
|
+
const vsep = doc.createElement("div");
|
|
257
|
+
vsep.className = "vela-lp-vsep";
|
|
258
|
+
cols.appendChild(vsep);
|
|
259
|
+
const syncCol = doc.createElement("div");
|
|
260
|
+
const syncHeading = doc.createElement("div");
|
|
261
|
+
syncHeading.className = "vela-lp-heading";
|
|
262
|
+
syncHeading.textContent = "Sync";
|
|
263
|
+
syncCol.appendChild(syncHeading);
|
|
264
|
+
this.syncEl = doc.createElement("div");
|
|
265
|
+
this.syncEl.className = "vela-lp-sync";
|
|
266
|
+
syncCol.appendChild(this.syncEl);
|
|
267
|
+
cols.appendChild(syncCol);
|
|
268
|
+
grid.addEventListener("pointerdown", (e) => {
|
|
269
|
+
const sq = this.squareAt(e);
|
|
270
|
+
if (!sq) return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
const { r, c } = this.squarePos(sq);
|
|
273
|
+
this.close();
|
|
274
|
+
this.opts.onSelectGrid(r + 1, c + 1);
|
|
275
|
+
});
|
|
276
|
+
grid.addEventListener("pointermove", (e) => {
|
|
277
|
+
const sq = this.squareAt(e);
|
|
278
|
+
if (!sq) return;
|
|
279
|
+
const { r, c } = this.squarePos(sq);
|
|
280
|
+
if (this.hover?.rows !== r + 1 || this.hover?.cols !== c + 1) {
|
|
281
|
+
this.hover = { rows: r + 1, cols: c + 1 };
|
|
282
|
+
this.render();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
grid.addEventListener("pointerleave", () => {
|
|
286
|
+
if (this.hover) {
|
|
287
|
+
this.hover = null;
|
|
288
|
+
this.render();
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
opts.trigger.addEventListener("click", () => this.toggle());
|
|
292
|
+
opts.trigger.setAttribute("aria-haspopup", "true");
|
|
293
|
+
opts.trigger.setAttribute("aria-expanded", "false");
|
|
294
|
+
opts.host.appendChild(this.layer);
|
|
295
|
+
}
|
|
296
|
+
toggle() {
|
|
297
|
+
if (this.isOpen) this.close();
|
|
298
|
+
else this.open();
|
|
299
|
+
}
|
|
300
|
+
open() {
|
|
301
|
+
if (this.isOpen) return;
|
|
302
|
+
this.isOpen = true;
|
|
303
|
+
this.hover = null;
|
|
304
|
+
this.refresh();
|
|
305
|
+
this.layer.style.display = "";
|
|
306
|
+
this.position();
|
|
307
|
+
this.opts.trigger.setAttribute("aria-expanded", "true");
|
|
308
|
+
this.doc.addEventListener("pointerdown", this.onDocPointerDown, true);
|
|
309
|
+
this.doc.addEventListener("keydown", this.onDocKeydown, true);
|
|
310
|
+
this.opts.onOpenChange?.(true);
|
|
311
|
+
}
|
|
312
|
+
close() {
|
|
313
|
+
if (!this.isOpen) return;
|
|
314
|
+
this.isOpen = false;
|
|
315
|
+
this.layer.style.display = "none";
|
|
316
|
+
this.opts.trigger.setAttribute("aria-expanded", "false");
|
|
317
|
+
this.doc.removeEventListener("pointerdown", this.onDocPointerDown, true);
|
|
318
|
+
this.doc.removeEventListener("keydown", this.onDocKeydown, true);
|
|
319
|
+
this.opts.onOpenChange?.(false);
|
|
320
|
+
}
|
|
321
|
+
/** Re-read shape/presets/syncs and re-render (no-op while closed — `open` re-reads). */
|
|
322
|
+
refresh() {
|
|
323
|
+
if (!this.isOpen) return;
|
|
324
|
+
this.renderPresets();
|
|
325
|
+
this.renderSyncs();
|
|
326
|
+
this.render();
|
|
327
|
+
}
|
|
328
|
+
destroy() {
|
|
329
|
+
this.close();
|
|
330
|
+
this.infoTip.destroy();
|
|
331
|
+
this.layer.remove();
|
|
332
|
+
}
|
|
333
|
+
// ── internals ──
|
|
334
|
+
squareAt(e) {
|
|
335
|
+
const sq = e.target?.closest?.(".vela-lp-sq");
|
|
336
|
+
return sq instanceof HTMLButtonElement ? sq : null;
|
|
337
|
+
}
|
|
338
|
+
squarePos(sq) {
|
|
339
|
+
return { r: Number(sq.dataset.r), c: Number(sq.dataset.c) };
|
|
340
|
+
}
|
|
341
|
+
/** Help copy for the LAYOUT "?" badge. */
|
|
342
|
+
tipNode() {
|
|
343
|
+
const tip = this.doc.createElement("div");
|
|
344
|
+
tip.className = "vela-lp-tip";
|
|
345
|
+
tip.textContent = "Click a square to apply that columns \xD7 rows layout.";
|
|
346
|
+
return tip;
|
|
347
|
+
}
|
|
348
|
+
/** Project the interaction state onto the DOM (the hover/current rectangle). */
|
|
349
|
+
render() {
|
|
350
|
+
const preview = this.hover ?? this.opts.shape();
|
|
351
|
+
for (const sq of this.squares) {
|
|
352
|
+
const { r, c } = this.squarePos(sq);
|
|
353
|
+
const on = preview !== null && r < preview.rows && c < preview.cols;
|
|
354
|
+
if (on) sq.dataset.on = "1";
|
|
355
|
+
else delete sq.dataset.on;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
renderPresets() {
|
|
359
|
+
const doc = this.doc;
|
|
360
|
+
this.presetsEl.replaceChildren();
|
|
361
|
+
const presets = this.opts.presets();
|
|
362
|
+
this.presetsEl.style.display = presets.length > 0 ? "" : "none";
|
|
363
|
+
for (const p of presets) {
|
|
364
|
+
const b = doc.createElement("button");
|
|
365
|
+
b.className = "vela-lp-preset";
|
|
366
|
+
b.textContent = p.label;
|
|
367
|
+
if (p.checked) b.dataset.checked = "1";
|
|
368
|
+
b.addEventListener("click", () => {
|
|
369
|
+
this.close();
|
|
370
|
+
this.opts.onSelectPreset(p.id);
|
|
371
|
+
});
|
|
372
|
+
this.presetsEl.appendChild(b);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
renderSyncs() {
|
|
376
|
+
const doc = this.doc;
|
|
377
|
+
this.syncEl.replaceChildren();
|
|
378
|
+
for (const s of this.opts.syncs()) {
|
|
379
|
+
const row = doc.createElement("button");
|
|
380
|
+
row.className = "vela-lp-sync-row";
|
|
381
|
+
const label = doc.createElement("span");
|
|
382
|
+
label.className = "vela-lp-label";
|
|
383
|
+
label.textContent = s.label;
|
|
384
|
+
const pill = doc.createElement("span");
|
|
385
|
+
pill.className = "vela-lp-switch" + (s.checked ? " on" : "");
|
|
386
|
+
pill.setAttribute("aria-hidden", "true");
|
|
387
|
+
row.setAttribute("role", "switch");
|
|
388
|
+
row.setAttribute("aria-checked", String(s.checked));
|
|
389
|
+
row.append(label, pill);
|
|
390
|
+
row.addEventListener("click", () => {
|
|
391
|
+
this.opts.onToggleSync(s.id);
|
|
392
|
+
this.renderSyncs();
|
|
393
|
+
});
|
|
394
|
+
this.syncEl.appendChild(row);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/** Anchor under the trigger, clamped to the host's right edge. */
|
|
398
|
+
position() {
|
|
399
|
+
const hostRect = this.opts.host.getBoundingClientRect();
|
|
400
|
+
const trigRect = this.opts.trigger.getBoundingClientRect();
|
|
401
|
+
let left = trigRect.left - hostRect.left;
|
|
402
|
+
const top = trigRect.bottom - hostRect.top + 4;
|
|
403
|
+
const width = this.layer.offsetWidth;
|
|
404
|
+
if (left + width > hostRect.width - 8) left = Math.max(8, hostRect.width - 8 - width);
|
|
405
|
+
this.layer.style.left = `${left}px`;
|
|
406
|
+
this.layer.style.top = `${top}px`;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// src/widget/topbar.ts
|
|
411
|
+
var STYLE_ID2 = "vela-topbar";
|
|
412
|
+
var CSS2 = `
|
|
93
413
|
.vela-widget-topbar {
|
|
94
414
|
display: flex;
|
|
95
415
|
align-items: center;
|
|
@@ -201,7 +521,7 @@ function priceStyleIcon(id) {
|
|
|
201
521
|
var Topbar = class {
|
|
202
522
|
constructor(host, opts) {
|
|
203
523
|
this.layoutButton = null;
|
|
204
|
-
this.
|
|
524
|
+
this.layoutPicker = null;
|
|
205
525
|
this.layoutId = null;
|
|
206
526
|
this.tooltips = [];
|
|
207
527
|
this.panelBtns = /* @__PURE__ */ new Map();
|
|
@@ -221,7 +541,7 @@ var Topbar = class {
|
|
|
221
541
|
this.timeframe = opts.timeframe;
|
|
222
542
|
this.priceStyle = opts.priceStyle;
|
|
223
543
|
const doc = host.ownerDocument;
|
|
224
|
-
injectStyles(
|
|
544
|
+
injectStyles(STYLE_ID2, CSS2, doc);
|
|
225
545
|
this.el = doc.createElement("div");
|
|
226
546
|
this.el.className = "vela-widget-topbar";
|
|
227
547
|
this.symbolEl = doc.createElement("button");
|
|
@@ -278,19 +598,16 @@ var Topbar = class {
|
|
|
278
598
|
this.tooltips.push(new Tooltip(this.styleButton, { content: "Chart style", triggerId: "vela-topbar-style", host }));
|
|
279
599
|
if (this.layoutButton && opts.layout) {
|
|
280
600
|
this.tooltips.push(new Tooltip(this.layoutButton, { content: "Layout", triggerId: "vela-topbar-layout", host }));
|
|
281
|
-
|
|
601
|
+
const layout = opts.layout;
|
|
602
|
+
this.layoutPicker = new LayoutPicker({
|
|
282
603
|
trigger: this.layoutButton,
|
|
283
|
-
triggerId: "vela-topbar-layout",
|
|
284
604
|
host,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
opts.layout.onSelect(id);
|
|
293
|
-
}
|
|
605
|
+
shape: () => layout.shape(),
|
|
606
|
+
presets: () => layout.presets().map((p) => ({ ...p, checked: p.id === this.layoutId })),
|
|
607
|
+
onSelectGrid: (rows, cols) => layout.onSelectGrid(rows, cols),
|
|
608
|
+
onSelectPreset: (id) => layout.onSelectPreset(id),
|
|
609
|
+
syncs: () => layout.syncs(),
|
|
610
|
+
onToggleSync: (id) => layout.onToggleSync(id)
|
|
294
611
|
});
|
|
295
612
|
}
|
|
296
613
|
this.tfMenu = new Menu({
|
|
@@ -329,7 +646,7 @@ var Topbar = class {
|
|
|
329
646
|
if (!this.layoutButton) return;
|
|
330
647
|
this.layoutId = id;
|
|
331
648
|
this.renderLayoutButton(this.layoutButton.ownerDocument);
|
|
332
|
-
this.
|
|
649
|
+
this.layoutPicker?.refresh();
|
|
333
650
|
}
|
|
334
651
|
renderLayoutButton(doc) {
|
|
335
652
|
if (!this.layoutButton) return;
|
|
@@ -338,11 +655,6 @@ var Topbar = class {
|
|
|
338
655
|
else this.layoutButton.appendChild(doc.createTextNode(this.layoutId ?? ""));
|
|
339
656
|
this.layoutButton.setAttribute("aria-label", `Layout \u2014 ${this.layoutId ?? ""}`);
|
|
340
657
|
}
|
|
341
|
-
layoutItems() {
|
|
342
|
-
const presets = (this.opts.layout?.options() ?? []).map((l) => ({ id: l.id, label: l.label, checked: l.id === this.layoutId }));
|
|
343
|
-
const toggles = (this.opts.layout?.toggles?.() ?? []).map((t, i) => ({ id: `toggle:${t.id}`, label: t.label, checked: t.checked, toggle: true, separatorBefore: i === 0 }));
|
|
344
|
-
return [...presets, ...toggles];
|
|
345
|
-
}
|
|
346
658
|
renderStyleButton(doc) {
|
|
347
659
|
this.styleButton.replaceChildren();
|
|
348
660
|
const icon2 = priceStyleIcon(this.priceStyle);
|
|
@@ -407,7 +719,7 @@ var Topbar = class {
|
|
|
407
719
|
if (this.hairlineRaf) win?.cancelAnimationFrame(this.hairlineRaf);
|
|
408
720
|
this.tfMenu.destroy();
|
|
409
721
|
this.styleMenu.destroy();
|
|
410
|
-
this.
|
|
722
|
+
this.layoutPicker?.destroy();
|
|
411
723
|
for (const t of [...this.tooltips, ...this.panelTooltips]) t.destroy();
|
|
412
724
|
this.el.remove();
|
|
413
725
|
}
|
|
@@ -509,8 +821,8 @@ function tickerIconEl(doc, base, name, className) {
|
|
|
509
821
|
}
|
|
510
822
|
|
|
511
823
|
// src/widget/statusline.ts
|
|
512
|
-
var
|
|
513
|
-
var
|
|
824
|
+
var STYLE_ID3 = "vela-widget-statusline";
|
|
825
|
+
var CSS3 = `
|
|
514
826
|
.vela-statusline {
|
|
515
827
|
position: absolute;
|
|
516
828
|
top: var(--vela-space-2);
|
|
@@ -645,7 +957,7 @@ var Statusline = class {
|
|
|
645
957
|
/** 'ohlc' for bar-shaped styles; 'value' (the single plotted close) for line styles. */
|
|
646
958
|
this.readout = "ohlc";
|
|
647
959
|
const doc = host.ownerDocument;
|
|
648
|
-
injectStyles(
|
|
960
|
+
injectStyles(STYLE_ID3, CSS3, doc);
|
|
649
961
|
host.classList.add("vela-has-statusline");
|
|
650
962
|
this.el = doc.createElement("div");
|
|
651
963
|
this.el.className = "vela-statusline";
|
|
@@ -768,11 +1080,21 @@ var Statusline = class {
|
|
|
768
1080
|
};
|
|
769
1081
|
|
|
770
1082
|
// src/widget/watermark.ts
|
|
771
|
-
var
|
|
772
|
-
var
|
|
1083
|
+
var MAX_FONT_PX = 72;
|
|
1084
|
+
var MIN_FONT_PX = 12;
|
|
1085
|
+
var FILL = 0.9;
|
|
1086
|
+
var STYLE_ID4 = "vela-widget-watermark";
|
|
1087
|
+
var CSS4 = `
|
|
773
1088
|
.vela-watermark {
|
|
774
1089
|
position: absolute;
|
|
775
|
-
|
|
1090
|
+
/* Insets follow the renderer-published gutters (mount container), so the mark
|
|
1091
|
+
* centers and fits within the PLOT \u2014 never bleeding into the drawings toolbar
|
|
1092
|
+
* on the left or the price scale on the right (visible in small multi-chart
|
|
1093
|
+
* cells, where the scale is a large share of the width). */
|
|
1094
|
+
top: 0;
|
|
1095
|
+
bottom: 0;
|
|
1096
|
+
left: var(--vela-toolbar-gutter, 0px);
|
|
1097
|
+
right: var(--vela-scale-gutter, 0px);
|
|
776
1098
|
display: flex;
|
|
777
1099
|
align-items: center;
|
|
778
1100
|
justify-content: center;
|
|
@@ -780,28 +1102,48 @@ var CSS3 = `
|
|
|
780
1102
|
z-index: 1;
|
|
781
1103
|
color: var(--vela-fg);
|
|
782
1104
|
opacity: 0.05;
|
|
783
|
-
font-size:
|
|
1105
|
+
font-size: ${MAX_FONT_PX}px;
|
|
784
1106
|
font-weight: 700;
|
|
785
1107
|
letter-spacing: 0.04em;
|
|
786
1108
|
user-select: none;
|
|
787
1109
|
white-space: nowrap;
|
|
788
1110
|
}
|
|
789
1111
|
`;
|
|
1112
|
+
function watermarkFontPx(availPx, textPxAtMax) {
|
|
1113
|
+
if (textPxAtMax <= 0) return MAX_FONT_PX;
|
|
1114
|
+
return Math.max(MIN_FONT_PX, Math.min(MAX_FONT_PX, Math.floor(MAX_FONT_PX * availPx * FILL / textPxAtMax)));
|
|
1115
|
+
}
|
|
790
1116
|
var Watermark = class {
|
|
791
1117
|
constructor(host, symbol, timeframe) {
|
|
792
|
-
|
|
1118
|
+
this.resizeObserver = null;
|
|
1119
|
+
injectStyles(STYLE_ID4, CSS4, host.ownerDocument);
|
|
793
1120
|
this.el = host.ownerDocument.createElement("div");
|
|
794
1121
|
this.el.className = "vela-watermark";
|
|
1122
|
+
this.text = host.ownerDocument.createElement("span");
|
|
1123
|
+
this.el.appendChild(this.text);
|
|
795
1124
|
host.appendChild(this.el);
|
|
1125
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
1126
|
+
this.resizeObserver = new ResizeObserver(() => this.fit());
|
|
1127
|
+
this.resizeObserver.observe(this.el);
|
|
1128
|
+
}
|
|
796
1129
|
this.update(symbol, timeframe);
|
|
797
1130
|
}
|
|
798
1131
|
setVisible(visible) {
|
|
799
1132
|
this.el.style.display = visible ? "" : "none";
|
|
800
1133
|
}
|
|
801
1134
|
update(symbol, timeframe) {
|
|
802
|
-
this.
|
|
1135
|
+
this.text.textContent = symbol ? `${parseSymbol(symbol).ticker} \xB7 ${timeframeLabel(timeframe)}` : "";
|
|
1136
|
+
this.fit();
|
|
1137
|
+
}
|
|
1138
|
+
/** Measure the text at the cap and shrink it to the chart's own width. */
|
|
1139
|
+
fit() {
|
|
1140
|
+
if (this.el.clientWidth <= 0 || !this.text.textContent) return;
|
|
1141
|
+
this.el.style.fontSize = `${MAX_FONT_PX}px`;
|
|
1142
|
+
const px = watermarkFontPx(this.el.clientWidth, this.text.getBoundingClientRect().width);
|
|
1143
|
+
if (px !== MAX_FONT_PX) this.el.style.fontSize = `${px}px`;
|
|
803
1144
|
}
|
|
804
1145
|
destroy() {
|
|
1146
|
+
this.resizeObserver?.disconnect();
|
|
805
1147
|
this.el.remove();
|
|
806
1148
|
}
|
|
807
1149
|
};
|
|
@@ -827,8 +1169,8 @@ var RANGE_PRESETS = [
|
|
|
827
1169
|
{ id: "ALL", tf: "W", preset: "ALL", bars: 5e3 }
|
|
828
1170
|
// everything the provider serves
|
|
829
1171
|
];
|
|
830
|
-
var
|
|
831
|
-
var
|
|
1172
|
+
var STYLE_ID5 = "vela-widget-bottombar";
|
|
1173
|
+
var CSS5 = `
|
|
832
1174
|
.vela-widget-bottombar {
|
|
833
1175
|
display: flex;
|
|
834
1176
|
align-items: center;
|
|
@@ -903,7 +1245,7 @@ var Bottombar = class {
|
|
|
903
1245
|
this.timer = null;
|
|
904
1246
|
this.timezone = opts.timezone;
|
|
905
1247
|
const doc = host.ownerDocument;
|
|
906
|
-
injectStyles(
|
|
1248
|
+
injectStyles(STYLE_ID5, CSS5, doc);
|
|
907
1249
|
this.el = doc.createElement("div");
|
|
908
1250
|
this.el.className = "vela-widget-bottombar";
|
|
909
1251
|
for (const preset of RANGE_PRESETS) {
|
|
@@ -1041,8 +1383,8 @@ function filterSymbols(list, query, limit = 100) {
|
|
|
1041
1383
|
}
|
|
1042
1384
|
return [...prefix, ...substr, ...desc, ...venue].slice(0, limit);
|
|
1043
1385
|
}
|
|
1044
|
-
var
|
|
1045
|
-
var
|
|
1386
|
+
var STYLE_ID6 = "vela-widget-symbolpicker";
|
|
1387
|
+
var CSS6 = `
|
|
1046
1388
|
.vela-sp-searchrow {
|
|
1047
1389
|
display: flex;
|
|
1048
1390
|
align-items: center;
|
|
@@ -1132,7 +1474,7 @@ var SymbolPicker = class {
|
|
|
1132
1474
|
this.seed = "";
|
|
1133
1475
|
this.activeTab = "All";
|
|
1134
1476
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
1135
|
-
injectStyles(
|
|
1477
|
+
injectStyles(STYLE_ID6, CSS6, doc);
|
|
1136
1478
|
this.input = doc.createElement("input");
|
|
1137
1479
|
this.input.className = "vela-sp-input";
|
|
1138
1480
|
this.input.placeholder = "Search symbol\u2026";
|
|
@@ -1454,8 +1796,8 @@ function treeIsEmpty(panes) {
|
|
|
1454
1796
|
}
|
|
1455
1797
|
|
|
1456
1798
|
// src/widget/object-tree.ts
|
|
1457
|
-
var
|
|
1458
|
-
var
|
|
1799
|
+
var STYLE_ID7 = "vela-widget-objtree";
|
|
1800
|
+
var CSS7 = `
|
|
1459
1801
|
/* One pane's block. The transparent border reserves the drop-target outline. */
|
|
1460
1802
|
.vela-ot-pane {
|
|
1461
1803
|
border: 1px solid transparent;
|
|
@@ -1693,7 +2035,7 @@ var ObjectTree = class extends SidePanel {
|
|
|
1693
2035
|
this.menu = null;
|
|
1694
2036
|
this.menuActions = /* @__PURE__ */ new Map();
|
|
1695
2037
|
this.drag = null;
|
|
1696
|
-
injectStyles(
|
|
2038
|
+
injectStyles(STYLE_ID7, CSS7, host.ownerDocument);
|
|
1697
2039
|
this.body.addEventListener("contextmenu", (e) => this.onContextMenu(e));
|
|
1698
2040
|
this.body.addEventListener("pointerdown", (e) => this.onPointerDown(e));
|
|
1699
2041
|
}
|
|
@@ -2672,8 +3014,8 @@ var ObjectTree = class extends SidePanel {
|
|
|
2672
3014
|
};
|
|
2673
3015
|
|
|
2674
3016
|
// src/widget/data-window.ts
|
|
2675
|
-
var
|
|
2676
|
-
var
|
|
3017
|
+
var STYLE_ID8 = "vela-widget-datawindow";
|
|
3018
|
+
var CSS8 = `
|
|
2677
3019
|
.vela-dw-group {
|
|
2678
3020
|
padding: 10px 8px 4px;
|
|
2679
3021
|
margin-top: 4px;
|
|
@@ -2732,7 +3074,7 @@ var DataWindow = class extends SidePanel {
|
|
|
2732
3074
|
super(host, "Data window", "vela-dw");
|
|
2733
3075
|
this.chart = null;
|
|
2734
3076
|
this.unsubs = [];
|
|
2735
|
-
injectStyles(
|
|
3077
|
+
injectStyles(STYLE_ID8, CSS8, host.ownerDocument);
|
|
2736
3078
|
}
|
|
2737
3079
|
toggle(open = this.el.hidden) {
|
|
2738
3080
|
super.toggle(open);
|
|
@@ -2929,8 +3271,8 @@ var PanelDock = class {
|
|
|
2929
3271
|
};
|
|
2930
3272
|
|
|
2931
3273
|
// src/widget/shortcuts-help.ts
|
|
2932
|
-
var
|
|
2933
|
-
var
|
|
3274
|
+
var STYLE_ID9 = "vela-widget-shortcuts";
|
|
3275
|
+
var CSS9 = `
|
|
2934
3276
|
.vela-sh-cat {
|
|
2935
3277
|
color: var(--vela-fg-muted);
|
|
2936
3278
|
font-size: var(--vela-font-size-sm);
|
|
@@ -2955,7 +3297,7 @@ var ShortcutsHelp = class {
|
|
|
2955
3297
|
constructor(keymap, host, onOpenChange) {
|
|
2956
3298
|
this.keymap = keymap;
|
|
2957
3299
|
const doc = (host ?? document.body).ownerDocument;
|
|
2958
|
-
injectStyles(
|
|
3300
|
+
injectStyles(STYLE_ID9, CSS9, doc);
|
|
2959
3301
|
this.list = doc.createElement("div");
|
|
2960
3302
|
this.dialog = new Dialog({
|
|
2961
3303
|
title: "Keyboard shortcuts",
|
|
@@ -3243,8 +3585,8 @@ var ChartContextMenu = class {
|
|
|
3243
3585
|
};
|
|
3244
3586
|
|
|
3245
3587
|
// src/widget/indicator-picker.ts
|
|
3246
|
-
var
|
|
3247
|
-
var
|
|
3588
|
+
var STYLE_ID10 = "vela-widget-indpicker";
|
|
3589
|
+
var CSS10 = `
|
|
3248
3590
|
.vela-ip-searchrow {
|
|
3249
3591
|
display: flex;
|
|
3250
3592
|
align-items: center;
|
|
@@ -3318,7 +3660,7 @@ var IndicatorPicker = class {
|
|
|
3318
3660
|
this.isOpen = false;
|
|
3319
3661
|
this.opts = opts;
|
|
3320
3662
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
3321
|
-
injectStyles(
|
|
3663
|
+
injectStyles(STYLE_ID10, CSS10, doc);
|
|
3322
3664
|
this.search = doc.createElement("input");
|
|
3323
3665
|
this.search.className = "vela-ip-search";
|
|
3324
3666
|
this.search.placeholder = "Search\u2026";
|
|
@@ -3446,8 +3788,8 @@ var IndicatorPicker = class {
|
|
|
3446
3788
|
};
|
|
3447
3789
|
|
|
3448
3790
|
// src/widget/timeframe-quick.ts
|
|
3449
|
-
var
|
|
3450
|
-
var
|
|
3791
|
+
var STYLE_ID11 = "vela-widget-tfquick";
|
|
3792
|
+
var CSS11 = `
|
|
3451
3793
|
.vela-tq-input {
|
|
3452
3794
|
display: block;
|
|
3453
3795
|
margin: 0 auto;
|
|
@@ -3470,7 +3812,7 @@ var CSS10 = `
|
|
|
3470
3812
|
var TimeframeQuick = class {
|
|
3471
3813
|
constructor(opts) {
|
|
3472
3814
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
3473
|
-
injectStyles(
|
|
3815
|
+
injectStyles(STYLE_ID11, CSS11, doc);
|
|
3474
3816
|
this.input = doc.createElement("input");
|
|
3475
3817
|
this.input.className = "vela-tq-input";
|
|
3476
3818
|
this.input.setAttribute("spellcheck", "false");
|
|
@@ -3667,7 +4009,7 @@ function sanitizeSync(raw) {
|
|
|
3667
4009
|
if (raw == null || typeof raw !== "object") return null;
|
|
3668
4010
|
const s = raw;
|
|
3669
4011
|
const out = {};
|
|
3670
|
-
for (const kind of ["viewport", "symbol", "timeframe", "crosshair"]) {
|
|
4012
|
+
for (const kind of ["viewport", "symbol", "timeframe", "crosshair", "drawings"]) {
|
|
3671
4013
|
const v = s[kind];
|
|
3672
4014
|
if (v === true) out[kind] = true;
|
|
3673
4015
|
else if (v != null && typeof v === "object") {
|
|
@@ -3920,8 +4262,8 @@ var WidgetHistory = class {
|
|
|
3920
4262
|
};
|
|
3921
4263
|
|
|
3922
4264
|
// src/widget/toast.ts
|
|
3923
|
-
var
|
|
3924
|
-
var
|
|
4265
|
+
var STYLE_ID12 = "vela-widget-toast";
|
|
4266
|
+
var CSS12 = `
|
|
3925
4267
|
.vela-toast {
|
|
3926
4268
|
position: absolute;
|
|
3927
4269
|
left: 50%;
|
|
@@ -3951,7 +4293,7 @@ var CSS11 = `
|
|
|
3951
4293
|
var Toast = class {
|
|
3952
4294
|
constructor(host) {
|
|
3953
4295
|
this.timer = null;
|
|
3954
|
-
injectStyles(
|
|
4296
|
+
injectStyles(STYLE_ID12, CSS12, host.ownerDocument);
|
|
3955
4297
|
this.el = host.ownerDocument.createElement("div");
|
|
3956
4298
|
this.el.className = "vela-toast";
|
|
3957
4299
|
host.appendChild(this.el);
|