@luxalgo/vela 0.8.1 → 0.8.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/README.md +1 -1
- package/dist/{DataProvider-BrlxntPN.d.cts → DataProvider-COGDs9Xg.d.cts} +1 -1
- package/dist/{DataProvider-DwFIoV4r.d.ts → DataProvider-DpOWZD8F.d.ts} +1 -1
- package/dist/{chunk-LMBYEGUR.js → chunk-27UTYE7T.js} +25 -2
- package/dist/{chunk-EZ5FWVLA.js → chunk-DPAA3SRT.js} +94 -13
- package/dist/{chunk-BZQM2XO7.js → chunk-HKNO24GD.js} +13 -1
- package/dist/{chunk-NELQJCGK.js → chunk-NLDTVFOI.js} +41 -3
- package/dist/{chunk-DBDXKZUF.js → chunk-R2JMNIHY.js} +53 -13
- package/dist/{chunk-ATGTCWHJ.js → chunk-T3DZIR5Y.js} +171 -32
- package/dist/{contributions-tjNExo1o.d.ts → contributions-Cgm9RJYP.d.cts} +38 -11
- package/dist/{contributions-CtlptxR8.d.cts → contributions-bWgwyiVi.d.ts} +38 -11
- package/dist/index.cjs +239 -37
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +4 -4
- package/dist/{options-BnEPUuVV.d.ts → options-NAUHiQEC.d.cts} +3 -3
- package/dist/{options-BnEPUuVV.d.cts → options-NAUHiQEC.d.ts} +3 -3
- package/dist/{plugin-BWxJRKzS.d.cts → plugin-CjjzZX2F.d.cts} +4 -4
- package/dist/{plugin-e2WUEtiI.d.ts → plugin-CqxWoDKS.d.ts} +4 -4
- package/dist/plugin.cjs +20 -6
- package/dist/plugin.d.cts +5 -5
- package/dist/plugin.d.ts +5 -5
- 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/{side-panel-HF0IAzwf.d.cts → side-panel-wMNfgyZb.d.cts} +13 -0
- package/dist/{side-panel-HF0IAzwf.d.ts → side-panel-wMNfgyZb.d.ts} +13 -0
- package/dist/{statusline-model-zKXtGIjj.d.ts → statusline-model-Dih6FGWP.d.ts} +3 -3
- package/dist/{statusline-model-DFY7tuHj.d.cts → statusline-model-Dpfpq54A.d.cts} +3 -3
- package/dist/ui.cjs +77 -2
- package/dist/ui.d.cts +28 -2
- package/dist/ui.d.ts +28 -2
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +239 -37
- package/dist/vela.global.min.js +52 -52
- package/dist/widget.cjs +383 -50
- package/dist/widget.d.cts +20 -10
- package/dist/widget.d.ts +20 -10
- package/dist/widget.js +7 -7
- package/dist/workspace.cjs +383 -50
- package/dist/workspace.d.cts +5 -5
- package/dist/workspace.d.ts +5 -5
- package/dist/workspace.js +6 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
# Vela™
|
|
14
14
|
|
|
15
|
-
<p><strong>Fast, extensible financial charts for the web.</strong><br>
|
|
15
|
+
<p><strong>Fast, extensible financial charts for the web: the open-source core of <a href="https://vela.luxalgo.com/chart">Vela</a> by LuxAlgo.</strong><br>
|
|
16
16
|
Headless core · native WebGL2 renderer · batteries-included workspace · plugin SDK</p>
|
|
17
17
|
|
|
18
18
|
[![npm version][npm-version-img]][npm-link]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizeProps, nextUid, runMachine, spreadProps } from './chunk-
|
|
2
|
-
import { injectStyles } from './chunk-
|
|
1
|
+
import { normalizeProps, nextUid, runMachine, spreadProps } from './chunk-NLDTVFOI.js';
|
|
2
|
+
import { injectStyles, announceSurface } from './chunk-HKNO24GD.js';
|
|
3
3
|
import * as tooltip from '@zag-js/tooltip';
|
|
4
4
|
import * as dialog from '@zag-js/dialog';
|
|
5
5
|
|
|
@@ -343,6 +343,8 @@ function swipeDirection(dx, dy) {
|
|
|
343
343
|
}
|
|
344
344
|
var Drawer = class {
|
|
345
345
|
constructor(opts = {}) {
|
|
346
|
+
this.opener = null;
|
|
347
|
+
this.wasOpen = false;
|
|
346
348
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
347
349
|
injectStyles(DRAWER_STYLE_ID, DRAWER_CSS, doc);
|
|
348
350
|
const host = opts.host ?? doc.body;
|
|
@@ -370,12 +372,16 @@ var Drawer = class {
|
|
|
370
372
|
const mid = String(this.ctrl.props.id);
|
|
371
373
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
372
374
|
const api = this.ctrl.connect(service);
|
|
375
|
+
if (this.wasOpen && !api.open) this.announce(false);
|
|
373
376
|
spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
374
377
|
spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
375
378
|
spreadProps(this.panel, api.getContentProps(), mid);
|
|
376
379
|
spreadProps(this.titleEl, api.getTitleProps(), mid);
|
|
377
380
|
this.backdrop.style.display = api.open ? "" : "none";
|
|
378
381
|
this.positioner.style.display = api.open ? "" : "none";
|
|
382
|
+
if (api.open && !this.wasOpen) this.announce(true);
|
|
383
|
+
if (this.wasOpen && !api.open) this.restoreOpener();
|
|
384
|
+
this.wasOpen = api.open;
|
|
379
385
|
});
|
|
380
386
|
}
|
|
381
387
|
/** Any element between `from` and the panel that has already been scrolled down —
|
|
@@ -484,10 +490,22 @@ var Drawer = class {
|
|
|
484
490
|
setTitle(title) {
|
|
485
491
|
this.titleEl.textContent = title;
|
|
486
492
|
}
|
|
493
|
+
/** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
|
|
494
|
+
* teardown that outruns the machine's close notification). */
|
|
495
|
+
restoreOpener() {
|
|
496
|
+
const opener = this.opener;
|
|
497
|
+
this.opener = null;
|
|
498
|
+
if (opener?.isConnected) opener.focus({ preventScroll: true });
|
|
499
|
+
}
|
|
500
|
+
announce(open) {
|
|
501
|
+
const opener = this.opener === this.panel.ownerDocument.body ? null : this.opener;
|
|
502
|
+
announceSurface(this.panel, open, "drawer", opener);
|
|
503
|
+
}
|
|
487
504
|
get open() {
|
|
488
505
|
return this.ctrl.connect(this.handle.service).open;
|
|
489
506
|
}
|
|
490
507
|
show() {
|
|
508
|
+
this.opener = this.panel.ownerDocument.activeElement;
|
|
491
509
|
this.ctrl.connect(this.handle.service).setOpen(true);
|
|
492
510
|
}
|
|
493
511
|
hide() {
|
|
@@ -495,6 +513,11 @@ var Drawer = class {
|
|
|
495
513
|
}
|
|
496
514
|
destroy() {
|
|
497
515
|
this.handle.stop();
|
|
516
|
+
if (this.wasOpen) {
|
|
517
|
+
this.wasOpen = false;
|
|
518
|
+
this.announce(false);
|
|
519
|
+
}
|
|
520
|
+
this.restoreOpener();
|
|
498
521
|
this.backdrop.remove();
|
|
499
522
|
this.positioner.remove();
|
|
500
523
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { svg24, NEUTRAL, BEARISH, WARNING, BULLISH, INFO, ACCENT_BRIGHT, VALID, INVALID, MARKER, ACCENT, injectStyles, iconEl } from './chunk-
|
|
1
|
+
import { svg24, NEUTRAL, BEARISH, WARNING, BULLISH, INFO, ACCENT_BRIGHT, VALID, INVALID, MARKER, ACCENT, injectStyles, iconEl, announceSurface } from './chunk-HKNO24GD.js';
|
|
2
2
|
|
|
3
3
|
// src/core/model/series.ts
|
|
4
4
|
function isLineLikeSeries(spec) {
|
|
@@ -2778,6 +2778,10 @@ var FibRatios = class extends Drawing {
|
|
|
2778
2778
|
|
|
2779
2779
|
// src/core/drawings/types/FibLevels.ts
|
|
2780
2780
|
var FibLevels = class extends FibRatios {
|
|
2781
|
+
/** Price of a level for the two anchor prices; subclasses override to change which anchor ratio 0 sits on. */
|
|
2782
|
+
levelPrice(ratio, p1, p2) {
|
|
2783
|
+
return p1 + ratio * (p2 - p1);
|
|
2784
|
+
}
|
|
2781
2785
|
/** Per-level pixel line + price for the ENABLED levels, spanning the anchors' time range. */
|
|
2782
2786
|
levelLines(proj) {
|
|
2783
2787
|
const a = this.anchors[0];
|
|
@@ -2787,11 +2791,10 @@ var FibLevels = class extends FibRatios {
|
|
|
2787
2791
|
const xb = proj.xOf(b.time);
|
|
2788
2792
|
const x1 = Math.min(xa, xb);
|
|
2789
2793
|
const x2 = Math.max(xa, xb);
|
|
2790
|
-
const delta = b.price - a.price;
|
|
2791
2794
|
const out = [];
|
|
2792
2795
|
for (const lv of this.levels) {
|
|
2793
2796
|
if (!lv.enabled) continue;
|
|
2794
|
-
const price =
|
|
2797
|
+
const price = this.levelPrice(lv.ratio, a.price, b.price);
|
|
2795
2798
|
const y = proj.yOf(price, this.paneId);
|
|
2796
2799
|
if (y == null) continue;
|
|
2797
2800
|
out.push({ ratio: lv.ratio, color: lv.color, label: lv.label, price, x1, x2, y });
|
|
@@ -2831,8 +2834,7 @@ var FibLevels = class extends FibRatios {
|
|
|
2831
2834
|
const a = this.anchors[0];
|
|
2832
2835
|
const b = this.anchors[1];
|
|
2833
2836
|
if (!a || !b) return null;
|
|
2834
|
-
const
|
|
2835
|
-
const prices = this.levels.filter((l) => l.enabled).map((l) => a.price + l.ratio * delta);
|
|
2837
|
+
const prices = this.levels.filter((l) => l.enabled).map((l) => this.levelPrice(l.ratio, a.price, b.price));
|
|
2836
2838
|
if (prices.length === 0) return null;
|
|
2837
2839
|
return { min: Math.min(...prices), max: Math.max(...prices) };
|
|
2838
2840
|
}
|
|
@@ -2918,13 +2920,24 @@ function cycleLevels(count, enabledCount = count) {
|
|
|
2918
2920
|
// src/core/drawings/types/FibRetracement.ts
|
|
2919
2921
|
var LEVELS = fibLevels([0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]);
|
|
2920
2922
|
var FibRetracement = class extends FibLevels {
|
|
2921
|
-
constructor() {
|
|
2922
|
-
super(
|
|
2923
|
+
constructor(init) {
|
|
2924
|
+
super(init);
|
|
2923
2925
|
this.type = "fibretracement";
|
|
2926
|
+
if (this.reverse === void 0) this.reverse = false;
|
|
2924
2927
|
}
|
|
2925
2928
|
defaultLevels() {
|
|
2926
2929
|
return LEVELS;
|
|
2927
2930
|
}
|
|
2931
|
+
levelPrice(ratio, p1, p2) {
|
|
2932
|
+
return this.reverse ? p1 + ratio * (p2 - p1) : p2 + ratio * (p1 - p2);
|
|
2933
|
+
}
|
|
2934
|
+
writeProps() {
|
|
2935
|
+
return { ...super.writeProps(), reverse: this.reverse };
|
|
2936
|
+
}
|
|
2937
|
+
readProps(props) {
|
|
2938
|
+
super.readProps(props);
|
|
2939
|
+
this.reverse = typeof props.reverse === "boolean" ? props.reverse : true;
|
|
2940
|
+
}
|
|
2928
2941
|
};
|
|
2929
2942
|
|
|
2930
2943
|
// src/core/drawings/types/FibExtension.ts
|
|
@@ -6023,10 +6036,12 @@ function legendActions() {
|
|
|
6023
6036
|
}
|
|
6024
6037
|
function legendActionsProviderFor(chart, context) {
|
|
6025
6038
|
return (indicatorId) => {
|
|
6026
|
-
const
|
|
6039
|
+
const find = () => chart.indicators().find((h) => h.id === indicatorId);
|
|
6040
|
+
const infoOf = (h) => ({ id: h.id, title: h.title, ...h.source !== void 0 ? { source: h.source } : {} });
|
|
6041
|
+
const handle = find();
|
|
6027
6042
|
if (!handle) return [];
|
|
6028
|
-
const info =
|
|
6029
|
-
return legendActions().filter((d) => !d.when || d.when(info)).map((d) => ({ id: d.id, icon: d.icon, tooltip: d.tooltip, run: () => d.run(context(),
|
|
6043
|
+
const info = infoOf(handle);
|
|
6044
|
+
return legendActions().filter((d) => !d.when || d.when(info)).map((d) => ({ id: d.id, icon: d.icon, tooltip: d.tooltip, run: () => d.run(context(), infoOf(find() ?? handle)) }));
|
|
6030
6045
|
};
|
|
6031
6046
|
}
|
|
6032
6047
|
var calloutRegistry = /* @__PURE__ */ new Map();
|
|
@@ -6177,6 +6192,32 @@ var CSS = `
|
|
|
6177
6192
|
.vela-panel-pin .vela-icon { width: 15px; height: 15px; }
|
|
6178
6193
|
.vela-panel-pin:hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
|
|
6179
6194
|
.vela-panel-pin[data-on='1'] { color: var(--vela-fg-bright); }
|
|
6195
|
+
.vela-panel-max {
|
|
6196
|
+
all: unset;
|
|
6197
|
+
cursor: pointer;
|
|
6198
|
+
width: 26px;
|
|
6199
|
+
height: 26px;
|
|
6200
|
+
flex: none;
|
|
6201
|
+
display: inline-flex;
|
|
6202
|
+
align-items: center;
|
|
6203
|
+
justify-content: center;
|
|
6204
|
+
border-radius: 4px;
|
|
6205
|
+
color: var(--vela-fg-muted);
|
|
6206
|
+
}
|
|
6207
|
+
.vela-panel-max .vela-icon { width: 15px; height: 15px; }
|
|
6208
|
+
.vela-panel-max:hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
|
|
6209
|
+
/* MAXIMIZED: the panel covers the whole dock host \u2014 every chart of the shell \u2014 whatever
|
|
6210
|
+
its placement; the width handle and the pin have nothing to do meanwhile. */
|
|
6211
|
+
.vela-panel[data-maximized] {
|
|
6212
|
+
position: absolute;
|
|
6213
|
+
inset: 0;
|
|
6214
|
+
width: auto;
|
|
6215
|
+
max-width: none;
|
|
6216
|
+
z-index: 26;
|
|
6217
|
+
border-left: none;
|
|
6218
|
+
}
|
|
6219
|
+
.vela-panel[data-maximized] .vela-panel-resizer,
|
|
6220
|
+
.vela-panel[data-maximized] .vela-panel-pin { display: none; }
|
|
6180
6221
|
.vela-panel-body { flex: 1; overflow: auto; padding: 8px; }
|
|
6181
6222
|
.vela-panel-body::-webkit-scrollbar { width: 8px; }
|
|
6182
6223
|
.vela-panel-body::-webkit-scrollbar-thumb { background: var(--vela-scroll); border-radius: 4px; border: 2px solid transparent; background-clip: padding-box; }
|
|
@@ -6229,6 +6270,7 @@ var CSS = `
|
|
|
6229
6270
|
border-left: none;
|
|
6230
6271
|
}
|
|
6231
6272
|
[data-layout='mobile'] .vela-panel-resizer { display: none; }
|
|
6273
|
+
[data-layout='mobile'] .vela-panel-max { display: none; }
|
|
6232
6274
|
`;
|
|
6233
6275
|
function clampPanelWidth(px, min = DEFAULT_PANEL_MIN_WIDTH, max = DEFAULT_PANEL_MAX_WIDTH) {
|
|
6234
6276
|
const lo = Number.isFinite(min) && min > 0 ? min : DEFAULT_PANEL_MIN_WIDTH;
|
|
@@ -6252,6 +6294,10 @@ var SidePanel = class {
|
|
|
6252
6294
|
* never on a programmatic {@link setOverlay}, for the same reason as widths. */
|
|
6253
6295
|
this.onPlacementChange = null;
|
|
6254
6296
|
this.pin = null;
|
|
6297
|
+
this.maxButton = null;
|
|
6298
|
+
this.maximizedOn = false;
|
|
6299
|
+
/** Set while the close is announced, so a listener's own close does not re-enter. */
|
|
6300
|
+
this.closing = false;
|
|
6255
6301
|
const doc = host.ownerDocument;
|
|
6256
6302
|
injectStyles(STYLE_ID, CSS, doc);
|
|
6257
6303
|
this.minWidth = opts.resizable ? opts.minWidth ?? DEFAULT_PANEL_MIN_WIDTH : 1;
|
|
@@ -6286,7 +6332,13 @@ var SidePanel = class {
|
|
|
6286
6332
|
});
|
|
6287
6333
|
this.refreshPin();
|
|
6288
6334
|
}
|
|
6289
|
-
|
|
6335
|
+
if (opts.maximizable) {
|
|
6336
|
+
this.maxButton = doc.createElement("button");
|
|
6337
|
+
this.maxButton.className = "vela-panel-max";
|
|
6338
|
+
this.maxButton.addEventListener("click", () => this.setMaximized(!this.maximizedOn));
|
|
6339
|
+
this.refreshMaxButton();
|
|
6340
|
+
}
|
|
6341
|
+
header.append(this.heading, this.slot, ...this.pin ? [this.pin] : [], ...this.maxButton ? [this.maxButton] : [], close);
|
|
6290
6342
|
this.body = doc.createElement("div");
|
|
6291
6343
|
this.body.className = "vela-panel-body";
|
|
6292
6344
|
this.el.append(header, this.body);
|
|
@@ -6298,9 +6350,37 @@ var SidePanel = class {
|
|
|
6298
6350
|
}
|
|
6299
6351
|
/** Open/close the panel — a bare call flips it. */
|
|
6300
6352
|
toggle(open = this.el.hidden) {
|
|
6301
|
-
if (open === !this.el.hidden) return;
|
|
6353
|
+
if (open === !this.el.hidden || !open && this.closing) return;
|
|
6354
|
+
if (!open) {
|
|
6355
|
+
this.closing = true;
|
|
6356
|
+
announceSurface(this.el, false, "panel");
|
|
6357
|
+
this.closing = false;
|
|
6358
|
+
}
|
|
6302
6359
|
this.el.hidden = !open;
|
|
6360
|
+
if (!open) this.setMaximized(false);
|
|
6303
6361
|
this.onOpenChange?.(open);
|
|
6362
|
+
if (open && !this.el.hidden) announceSurface(this.el, true, "panel");
|
|
6363
|
+
}
|
|
6364
|
+
/** Whether the panel covers every chart right now. */
|
|
6365
|
+
get maximized() {
|
|
6366
|
+
return this.maximizedOn;
|
|
6367
|
+
}
|
|
6368
|
+
/** Maximize the panel over the shell's charts, or restore it. A no-op on a panel not
|
|
6369
|
+
* declared `maximizable`. */
|
|
6370
|
+
setMaximized(maximized) {
|
|
6371
|
+
if (!this.maxButton || maximized === this.maximizedOn) return;
|
|
6372
|
+
this.maximizedOn = maximized;
|
|
6373
|
+
if (maximized) this.el.dataset.maximized = "1";
|
|
6374
|
+
else delete this.el.dataset.maximized;
|
|
6375
|
+
this.refreshMaxButton();
|
|
6376
|
+
}
|
|
6377
|
+
refreshMaxButton() {
|
|
6378
|
+
if (!this.maxButton) return;
|
|
6379
|
+
const doc = this.maxButton.ownerDocument;
|
|
6380
|
+
this.maxButton.replaceChildren(iconEl(this.maximizedOn ? "restore" : "maximize", doc));
|
|
6381
|
+
this.maxButton.title = this.maximizedOn ? "Restore" : "Maximize";
|
|
6382
|
+
this.maxButton.setAttribute("aria-label", this.maxButton.title);
|
|
6383
|
+
this.maxButton.setAttribute("aria-pressed", this.maximizedOn ? "true" : "false");
|
|
6304
6384
|
}
|
|
6305
6385
|
/** The scrolling body, for a panel filled from OUTSIDE the class — a contributed panel's
|
|
6306
6386
|
* `mount` receives exactly this element. Subclasses use the protected `body`. */
|
|
@@ -6352,6 +6432,7 @@ var SidePanel = class {
|
|
|
6352
6432
|
this.pin.setAttribute("aria-pressed", pinned ? "true" : "false");
|
|
6353
6433
|
}
|
|
6354
6434
|
destroy() {
|
|
6435
|
+
if (this.open) announceSurface(this.el, false, "panel");
|
|
6355
6436
|
this.el.remove();
|
|
6356
6437
|
}
|
|
6357
6438
|
/** The drag handle on the panel's inner (left) edge — the panel is docked right, so dragging
|
|
@@ -6396,4 +6477,4 @@ var SidePanel = class {
|
|
|
6396
6477
|
}
|
|
6397
6478
|
};
|
|
6398
6479
|
|
|
6399
|
-
export { AnchoredVwap, ArrowMark, Callout, CalloutBase, Comment, DEDEKIND_CURVATURE_OPTIONS, DEFAULT_DRAWING_COLOR, DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, DIRECTION_OPTIONS, DedekindTessellation, Drawing, FibRatios, FibSpiral, FixedRangeVolumeProfile, GANN_SQUARE_ARCS, GLYPH_OPTIONS, GannSquare, GlyphStamp, LINE_STYLE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, MAGNIFIER_TIMEFRAME_OPTIONS, MachFigure, Magnifier, MeasureBox, Note, OVERRIDABLE_TOPBAR_IDS, PatternDrawing, PositionTool, PriceLabel, PriceNote, RadialFib, RegressionChannel, STAMP_SIZE_OPTIONS, SegmentDrawing, SidePanel, Signpost, TEXT_SIZE_OPTIONS, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, TextLabel, chartType, chartTypes, clampPanelWidth, createDrawing, deserializeDrawing, drawingTypes, foldBaseModulation, formatDuration, getDrawingType, getNativeIndicator, inputDeltas, inputVisible, isLineLikeSeries, legendActions, legendActionsProviderFor, legendCallouts, legendCalloutsProviderFor, lineSegmentIntersection, magnifierTimeframeLabel, mobilePlacement, nativeIndicatorDescriptors, nativeIndicatorTypes, nativeInstanceChannel, normalizeSettingsRow, pinnedTopbarActionIds, registerChartType, registerDefaultEngine, registerDrawingType, registerLegendAction, registerLegendCallout, registerNativeIndicator, registerRendererDefaults, registerRendererLayer, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, rendererDefaults, rendererLayers, resetDrawingSettings, resolveEngines, resolveTopbarComposition, seriesInScale, seriesShownOn, settingsRowValueKeys, settingsRowVisible, sidePanels, stableSeriesId, statePersistenceHandlers, symbolRanking, tickerModifierIds, topbarActionOverride, topbarHas, unregisterChartType, unregisterDefaultEngine, unregisterLegendAction, unregisterLegendCallout, unregisterNativeIndicator, unregisterRendererDefaults, unregisterRendererLayer, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments };
|
|
6480
|
+
export { AnchoredVwap, ArrowMark, Callout, CalloutBase, Comment, DEDEKIND_CURVATURE_OPTIONS, DEFAULT_DRAWING_COLOR, DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, DIRECTION_OPTIONS, DedekindTessellation, Drawing, FibRatios, FibRetracement, FibSpiral, FixedRangeVolumeProfile, GANN_SQUARE_ARCS, GLYPH_OPTIONS, GannSquare, GlyphStamp, LINE_STYLE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, MAGNIFIER_TIMEFRAME_OPTIONS, MachFigure, Magnifier, MeasureBox, Note, OVERRIDABLE_TOPBAR_IDS, PatternDrawing, PositionTool, PriceLabel, PriceNote, RadialFib, RegressionChannel, STAMP_SIZE_OPTIONS, SegmentDrawing, SidePanel, Signpost, TEXT_SIZE_OPTIONS, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, TextLabel, chartType, chartTypes, clampPanelWidth, createDrawing, deserializeDrawing, drawingTypes, foldBaseModulation, formatDuration, getDrawingType, getNativeIndicator, inputDeltas, inputVisible, isLineLikeSeries, legendActions, legendActionsProviderFor, legendCallouts, legendCalloutsProviderFor, lineSegmentIntersection, magnifierTimeframeLabel, mobilePlacement, nativeIndicatorDescriptors, nativeIndicatorTypes, nativeInstanceChannel, normalizeSettingsRow, pinnedTopbarActionIds, registerChartType, registerDefaultEngine, registerDrawingType, registerLegendAction, registerLegendCallout, registerNativeIndicator, registerRendererDefaults, registerRendererLayer, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, rendererDefaults, rendererLayers, resetDrawingSettings, resolveEngines, resolveTopbarComposition, seriesInScale, seriesShownOn, settingsRowValueKeys, settingsRowVisible, sidePanels, stableSeriesId, statePersistenceHandlers, symbolRanking, tickerModifierIds, topbarActionOverride, topbarHas, unregisterChartType, unregisterDefaultEngine, unregisterLegendAction, unregisterLegendCallout, unregisterNativeIndicator, unregisterRendererDefaults, unregisterRendererLayer, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments };
|
|
@@ -146,6 +146,7 @@ registerIcon("expand", S('<rect x="2.2" y="2.2" width="11.6" height="11.6" rx="1
|
|
|
146
146
|
registerIcon("plus", S('<path d="M8 2.8v10.4M2.8 8h10.4"/>'));
|
|
147
147
|
registerIcon("minus", S('<path d="M2.8 8h10.4"/>'));
|
|
148
148
|
registerIcon("maximize", S('<path d="M2.5 6V3a.5.5 0 0 1 .5-.5h3M10 2.5h3a.5.5 0 0 1 .5.5v3M13.5 10v3a.5.5 0 0 1-.5.5h-3M6 13.5H3a.5.5 0 0 1-.5-.5v-3"/>'));
|
|
149
|
+
registerIcon("restore", S('<path d="M6 2.5v3a.5.5 0 0 1-.5.5h-3M13.5 6h-3a.5.5 0 0 1-.5-.5v-3M10 13.5v-3a.5.5 0 0 1 .5-.5h3M2.5 10h3a.5.5 0 0 1 .5.5v3"/>'));
|
|
149
150
|
registerIcon("restore", S('<path d="M6.2 2.5v3.7H2.5M9.8 13.5V9.8h3.7M13.5 6.2H9.8V2.5M2.5 9.8h3.7v3.7"/>'));
|
|
150
151
|
registerIcon("star", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l-3.5 1.85.65-3.9-2.8-2.75 3.9-.55z"/>'));
|
|
151
152
|
registerIcon("star-filled", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l-3.5 1.85.65-3.9-2.8-2.75 3.9-.55z"/>', 'fill="currentColor"'));
|
|
@@ -270,4 +271,15 @@ function iconEl(id, doc = document) {
|
|
|
270
271
|
return span;
|
|
271
272
|
}
|
|
272
273
|
|
|
273
|
-
|
|
274
|
+
// src/ui/surface-events.ts
|
|
275
|
+
var SURFACE_OPEN_EVENT = "vela:surface-open";
|
|
276
|
+
var SURFACE_CLOSE_EVENT = "vela:surface-close";
|
|
277
|
+
function announceSurface(el, open, kind, trigger = null) {
|
|
278
|
+
const View = el.ownerDocument.defaultView;
|
|
279
|
+
if (!View) return;
|
|
280
|
+
el.dispatchEvent(
|
|
281
|
+
new View.CustomEvent(open ? SURFACE_OPEN_EVENT : SURFACE_CLOSE_EVENT, { bubbles: true, composed: true, detail: { kind, trigger } })
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export { ACCENT, ACCENT_BRIGHT, BEARISH, BULLISH, CATEGORICAL, CHIP_PLATE, CROSSHAIR, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, HIGHLIGHT, INFO, INVALID, MARKER, NEUTRAL, SERIES_LINE, SESSION_OFF, SESSION_POST, SESSION_PRE, SLATE, SLATE_DEEP, SURFACE_CLOSE_EVENT, SURFACE_OPEN_EVENT, TRADE_EXIT, TRADE_LONG, TRADE_SHORT, VALID, WARNING, announceSurface, categoricalColor, icon, iconAt, iconEl, iconMarkup, injectStyles, isDarkColor, mix, overlayScrollbarCss, registerIcon, svg16, svg24, svg24Solid, withAlpha };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { overlayScrollbarCss, isDarkColor, mix, HIGHLIGHT, ACCENT_BRIGHT, ACCENT, withAlpha, injectStyles, iconAt, iconEl, BULLISH, BEARISH, WARNING, NEUTRAL } from './chunk-
|
|
1
|
+
import { overlayScrollbarCss, isDarkColor, mix, HIGHLIGHT, ACCENT_BRIGHT, ACCENT, withAlpha, injectStyles, announceSurface, iconAt, iconEl, BULLISH, BEARISH, WARNING, NEUTRAL } from './chunk-HKNO24GD.js';
|
|
2
2
|
import { VanillaMachine, normalizeProps, spreadProps } from '@zag-js/vanilla';
|
|
3
3
|
export { normalizeProps, spreadProps } from '@zag-js/vanilla';
|
|
4
4
|
import * as menu from '@zag-js/menu';
|
|
@@ -290,6 +290,8 @@ var Surface = class _Surface {
|
|
|
290
290
|
/** Trigger rect captured when this level opened — the list stays put if the
|
|
291
291
|
* trigger then moves (favorite chips shifting the caret). */
|
|
292
292
|
this.pinnedAnchor = null;
|
|
293
|
+
/** The open state last announced — see {@link announce}. */
|
|
294
|
+
this.shown = false;
|
|
293
295
|
this.doc = doc;
|
|
294
296
|
this.host = opts.host;
|
|
295
297
|
this.onSelect = opts.onSelect;
|
|
@@ -303,6 +305,7 @@ var Surface = class _Surface {
|
|
|
303
305
|
this.positioner.appendChild(this.list);
|
|
304
306
|
this.host.appendChild(this.positioner);
|
|
305
307
|
const trigger = opts.trigger;
|
|
308
|
+
this.opener = trigger ?? opts.opener ?? null;
|
|
306
309
|
this.ctrl = menuController({
|
|
307
310
|
items: [],
|
|
308
311
|
id: opts.id,
|
|
@@ -324,15 +327,21 @@ var Surface = class _Surface {
|
|
|
324
327
|
if (opts.triggerId && trigger) trigger.id = opts.triggerId;
|
|
325
328
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
326
329
|
const api = this.ctrl.connect(service);
|
|
330
|
+
if (this.shown && !api.open) this.announce(false);
|
|
327
331
|
if (trigger) spreadProps(trigger, api.getTriggerProps(), this.mid);
|
|
328
332
|
spreadProps(this.positioner, api.getPositionerProps(), this.mid);
|
|
329
333
|
spreadProps(this.list, api.getContentProps(), this.mid);
|
|
330
334
|
this.project(api);
|
|
335
|
+
if (api.open && !this.shown) this.announce(true);
|
|
331
336
|
});
|
|
332
337
|
}
|
|
333
338
|
get api() {
|
|
334
339
|
return this.ctrl.connect(this.handle.service);
|
|
335
340
|
}
|
|
341
|
+
announce(open2) {
|
|
342
|
+
this.shown = open2;
|
|
343
|
+
announceSurface(this.list, open2, "menu", this.opener);
|
|
344
|
+
}
|
|
336
345
|
setItems(items) {
|
|
337
346
|
this.items = items;
|
|
338
347
|
for (const sub of this.subs.values()) sub.destroy();
|
|
@@ -350,6 +359,7 @@ var Surface = class _Surface {
|
|
|
350
359
|
for (const sub of this.subs.values()) sub.destroy();
|
|
351
360
|
this.subs.clear();
|
|
352
361
|
this.handle.stop();
|
|
362
|
+
if (this.shown) this.announce(false);
|
|
353
363
|
this.positioner.remove();
|
|
354
364
|
}
|
|
355
365
|
byId(id) {
|
|
@@ -443,6 +453,7 @@ var Surface = class _Surface {
|
|
|
443
453
|
const sub = new _Surface(doc, {
|
|
444
454
|
host: this.host,
|
|
445
455
|
placement: "right-start",
|
|
456
|
+
opener: li,
|
|
446
457
|
onSelect: this.onSelect,
|
|
447
458
|
onFavorite: this.onFavorite,
|
|
448
459
|
checkmarks: this.checkmarks,
|
|
@@ -662,16 +673,21 @@ var Popover = class {
|
|
|
662
673
|
this.hide();
|
|
663
674
|
};
|
|
664
675
|
const onReflow = () => this.place();
|
|
665
|
-
setTimeout(() =>
|
|
676
|
+
setTimeout(() => {
|
|
677
|
+
if (this.onOutside === onOutside) document.addEventListener("pointerdown", onOutside, true);
|
|
678
|
+
}, 0);
|
|
666
679
|
document.addEventListener("keydown", onKey, true);
|
|
667
680
|
window.addEventListener("resize", onReflow, true);
|
|
668
681
|
document.addEventListener("scroll", onReflow, true);
|
|
669
682
|
this.onOutside = onOutside;
|
|
670
683
|
this.onKey = onKey;
|
|
671
684
|
this.onReflow = onReflow;
|
|
685
|
+
announceSurface(this.el, true, "popover", this.trigger);
|
|
672
686
|
}
|
|
673
687
|
hide() {
|
|
674
688
|
if (!this.shown) return;
|
|
689
|
+
this.shown = false;
|
|
690
|
+
announceSurface(this.el, false, "popover", this.trigger);
|
|
675
691
|
if (this.onOutside) document.removeEventListener("pointerdown", this.onOutside, true);
|
|
676
692
|
if (this.onKey) document.removeEventListener("keydown", this.onKey, true);
|
|
677
693
|
if (this.onReflow) {
|
|
@@ -691,7 +707,6 @@ var Popover = class {
|
|
|
691
707
|
} else {
|
|
692
708
|
this.el.remove();
|
|
693
709
|
}
|
|
694
|
-
this.shown = false;
|
|
695
710
|
if (open === this) open = null;
|
|
696
711
|
this.ctrl.onClose?.();
|
|
697
712
|
}
|
|
@@ -1377,6 +1392,8 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
1377
1392
|
`;
|
|
1378
1393
|
var Dialog = class {
|
|
1379
1394
|
constructor(opts = {}) {
|
|
1395
|
+
this.opener = null;
|
|
1396
|
+
this.wasOpen = false;
|
|
1380
1397
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
1381
1398
|
injectStyles(DIALOG_STYLE_ID, DIALOG_CSS, doc);
|
|
1382
1399
|
const host = opts.host ?? doc.body;
|
|
@@ -1473,6 +1490,7 @@ var Dialog = class {
|
|
|
1473
1490
|
const mid = String(this.ctrl.props.id);
|
|
1474
1491
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
1475
1492
|
const api = this.ctrl.connect(service);
|
|
1493
|
+
if (this.wasOpen && !api.open) this.announce(false);
|
|
1476
1494
|
spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
1477
1495
|
spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
1478
1496
|
spreadProps(this.panel, api.getContentProps(), mid);
|
|
@@ -1480,12 +1498,27 @@ var Dialog = class {
|
|
|
1480
1498
|
spreadProps(close, api.getCloseTriggerProps(), mid);
|
|
1481
1499
|
this.backdrop.style.display = api.open ? "" : "none";
|
|
1482
1500
|
this.positioner.style.display = api.open ? "" : "none";
|
|
1501
|
+
if (api.open && !this.wasOpen) this.announce(true);
|
|
1502
|
+
if (this.wasOpen && !api.open) this.restoreOpener();
|
|
1503
|
+
this.wasOpen = api.open;
|
|
1483
1504
|
});
|
|
1484
1505
|
}
|
|
1506
|
+
/** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
|
|
1507
|
+
* teardown that outruns the machine's close notification). */
|
|
1508
|
+
restoreOpener() {
|
|
1509
|
+
const opener = this.opener;
|
|
1510
|
+
this.opener = null;
|
|
1511
|
+
if (opener?.isConnected) opener.focus({ preventScroll: true });
|
|
1512
|
+
}
|
|
1513
|
+
announce(open2) {
|
|
1514
|
+
const opener = this.opener === this.panel.ownerDocument.body ? null : this.opener;
|
|
1515
|
+
announceSurface(this.panel, open2, "dialog", opener);
|
|
1516
|
+
}
|
|
1485
1517
|
get open() {
|
|
1486
1518
|
return this.ctrl.connect(this.handle.service).open;
|
|
1487
1519
|
}
|
|
1488
1520
|
show() {
|
|
1521
|
+
this.opener = this.panel.ownerDocument.activeElement;
|
|
1489
1522
|
this.ctrl.connect(this.handle.service).setOpen(true);
|
|
1490
1523
|
}
|
|
1491
1524
|
hide() {
|
|
@@ -1500,6 +1533,11 @@ var Dialog = class {
|
|
|
1500
1533
|
destroy() {
|
|
1501
1534
|
if (this.open) this.hide();
|
|
1502
1535
|
this.handle.stop();
|
|
1536
|
+
if (this.wasOpen) {
|
|
1537
|
+
this.wasOpen = false;
|
|
1538
|
+
this.announce(false);
|
|
1539
|
+
}
|
|
1540
|
+
this.restoreOpener();
|
|
1503
1541
|
this.backdrop.remove();
|
|
1504
1542
|
this.positioner.remove();
|
|
1505
1543
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, SecondClock, resolveTimezone, timezoneMenuRows, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, normalizeSession, Vela, normalizeTimezone, isExchangeTimezone, TypedEventBus, timeframeToMs, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore } from './chunk-
|
|
2
|
-
import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, mobilePlacement, widgetAttachments, getDrawingType, inputDeltas } from './chunk-
|
|
3
|
-
import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-
|
|
4
|
-
import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-
|
|
5
|
-
import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-
|
|
1
|
+
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, SecondClock, resolveTimezone, timezoneMenuRows, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, normalizeSession, Vela, normalizeTimezone, isExchangeTimezone, TypedEventBus, timeframeToMs, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore } from './chunk-T3DZIR5Y.js';
|
|
2
|
+
import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, mobilePlacement, widgetAttachments, getDrawingType, inputDeltas } from './chunk-DPAA3SRT.js';
|
|
3
|
+
import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-27UTYE7T.js';
|
|
4
|
+
import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-NLDTVFOI.js';
|
|
5
|
+
import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, announceSurface, icon, categoricalColor } from './chunk-HKNO24GD.js';
|
|
6
6
|
import { baseOf } from './chunk-W4EJWLEO.js';
|
|
7
7
|
|
|
8
8
|
// src/widget/timeframe.ts
|
|
@@ -261,6 +261,7 @@ var LayoutPicker = class {
|
|
|
261
261
|
const panel = doc.createElement("div");
|
|
262
262
|
panel.className = "vela-lp";
|
|
263
263
|
this.layer.appendChild(panel);
|
|
264
|
+
this.panel = panel;
|
|
264
265
|
const cols = doc.createElement("div");
|
|
265
266
|
cols.className = "vela-lp-cols";
|
|
266
267
|
panel.appendChild(cols);
|
|
@@ -342,10 +343,12 @@ var LayoutPicker = class {
|
|
|
342
343
|
this.doc.addEventListener("pointerdown", this.onDocPointerDown, true);
|
|
343
344
|
this.doc.addEventListener("keydown", this.onDocKeydown, true);
|
|
344
345
|
this.opts.onOpenChange?.(true);
|
|
346
|
+
if (this.isOpen) announceSurface(this.panel, true, "popover", this.opts.trigger);
|
|
345
347
|
}
|
|
346
348
|
close() {
|
|
347
349
|
if (!this.isOpen) return;
|
|
348
350
|
this.isOpen = false;
|
|
351
|
+
announceSurface(this.panel, false, "popover", this.opts.trigger);
|
|
349
352
|
this.layer.style.display = "none";
|
|
350
353
|
this.opts.trigger.setAttribute("aria-expanded", "false");
|
|
351
354
|
this.doc.removeEventListener("pointerdown", this.onDocPointerDown, true);
|
|
@@ -2813,19 +2816,22 @@ var PanelDock = class {
|
|
|
2813
2816
|
/**
|
|
2814
2817
|
* (Re)build the CONTRIBUTED panels from the registry — call once after the built-ins, and
|
|
2815
2818
|
* again on `refreshActions()` so a late registration appears. Contributed panels that are
|
|
2816
|
-
* gone from the registry are dropped
|
|
2817
|
-
* descriptor
|
|
2819
|
+
* gone from the registry are dropped and a REPLACED descriptor is rebuilt; a panel whose
|
|
2820
|
+
* descriptor is unchanged stays mounted as it is, so its content and state survive.
|
|
2818
2821
|
*/
|
|
2819
2822
|
refresh() {
|
|
2820
2823
|
const openBefore = this.openId;
|
|
2821
|
-
|
|
2822
|
-
for (const
|
|
2824
|
+
const registered = sidePanels();
|
|
2825
|
+
for (const entry of [...this.entries]) if (entry.contributed && !registered.includes(entry.desc)) this.drop(entry);
|
|
2826
|
+
for (const desc of registered) {
|
|
2827
|
+
if (this.entries.some((e) => e.desc === desc)) continue;
|
|
2823
2828
|
const panel = new SidePanel(this.host, desc.title, `vela-panel-${desc.id}`, {
|
|
2824
2829
|
width: desc.width,
|
|
2825
2830
|
resizable: desc.resizable,
|
|
2826
2831
|
minWidth: desc.minWidth,
|
|
2827
2832
|
maxWidth: desc.maxWidth,
|
|
2828
|
-
overlay: desc.overlay
|
|
2833
|
+
overlay: desc.overlay,
|
|
2834
|
+
maximizable: desc.maximizable
|
|
2829
2835
|
});
|
|
2830
2836
|
const entry = {
|
|
2831
2837
|
id: desc.id,
|
|
@@ -2833,10 +2839,19 @@ var PanelDock = class {
|
|
|
2833
2839
|
icon: desc.icon,
|
|
2834
2840
|
order: desc.order ?? DEFAULT_PANEL_ORDER,
|
|
2835
2841
|
panel,
|
|
2836
|
-
contributed: true
|
|
2842
|
+
contributed: true,
|
|
2843
|
+
desc
|
|
2837
2844
|
};
|
|
2838
2845
|
try {
|
|
2839
|
-
|
|
2846
|
+
const header = {
|
|
2847
|
+
slot: panel.headerSlot,
|
|
2848
|
+
setTitle: (t) => panel.setTitle(t),
|
|
2849
|
+
get maximized() {
|
|
2850
|
+
return panel.maximized;
|
|
2851
|
+
},
|
|
2852
|
+
setMaximized: (on) => panel.setMaximized(on)
|
|
2853
|
+
};
|
|
2854
|
+
entry.handle = desc.mount(this.deps.context(), panel.content, header) ?? void 0;
|
|
2840
2855
|
if (this.chart) entry.handle?.onChart?.(this.chart);
|
|
2841
2856
|
} catch (err) {
|
|
2842
2857
|
console.warn(`[vela] side panel "${desc.id}" failed to mount`, err);
|
|
@@ -4223,6 +4238,9 @@ var CSS10 = `
|
|
|
4223
4238
|
.vela-statusline .vela-sl-market { align-self: center; display: inline-flex; }
|
|
4224
4239
|
.vela-statusline .vela-sl-market > [hidden] { display: none !important; }
|
|
4225
4240
|
.vela-statusline .vela-sl-replay-badge, .vela-statusline .vela-sl-replay-badge svg { display: block; width: 16px; height: 16px; }
|
|
4241
|
+
/* Tabular figures: every digit has one advance, so a readout whose text changes only in
|
|
4242
|
+
* its digits keeps its width \u2014 render() skips the ladder for those (see widthKey). */
|
|
4243
|
+
.vela-statusline .vela-sl-values { font-variant-numeric: tabular-nums; }
|
|
4226
4244
|
.vela-statusline .vela-sl-ohlc { display: flex; gap: var(--vela-space-1); color: var(--vela-fg-muted); }
|
|
4227
4245
|
.vela-statusline .vela-sl-ohlc b { color: var(--vela-fg); font-weight: 500; }
|
|
4228
4246
|
/* The change value wears the SAME ink as the OHLC values (set inline per render) \u2014
|
|
@@ -4340,6 +4358,10 @@ var Statusline = class {
|
|
|
4340
4358
|
this.fitRO = null;
|
|
4341
4359
|
/** The ladder rung currently applied — see {@link fit}. */
|
|
4342
4360
|
this.layout = { stacked: false, level: "full" };
|
|
4361
|
+
/** The readout {@link widthKey} the applied rung was fitted to. */
|
|
4362
|
+
this.fitKey = null;
|
|
4363
|
+
/** Web fonts landing after the last fit change every width without a resize. */
|
|
4364
|
+
this.onFontsLoaded = () => this.fit();
|
|
4343
4365
|
this.onContextMenu = (e) => {
|
|
4344
4366
|
if (!this.menu || !this.menuHooks) return;
|
|
4345
4367
|
e.preventDefault();
|
|
@@ -4403,6 +4425,7 @@ var Statusline = class {
|
|
|
4403
4425
|
this.fitRO = new ResizeObserver(() => this.fit());
|
|
4404
4426
|
this.fitRO.observe(host);
|
|
4405
4427
|
}
|
|
4428
|
+
doc.fonts?.addEventListener("loadingdone", this.onFontsLoaded);
|
|
4406
4429
|
this.marketTip = new Tooltip(this.marketEl, { content: MARKET_LABELS.open, placement: "bottom" });
|
|
4407
4430
|
this.eyeTip = new Tooltip(this.eyeEl, { content: "Show chart", placement: "bottom" });
|
|
4408
4431
|
this.setMarketStatus("open");
|
|
@@ -4455,6 +4478,7 @@ var Statusline = class {
|
|
|
4455
4478
|
* (detached host, node tests) nothing overflows, so the widest rung stays.
|
|
4456
4479
|
*/
|
|
4457
4480
|
fit() {
|
|
4481
|
+
this.fitKey = this.widthKey();
|
|
4458
4482
|
this.syncParts();
|
|
4459
4483
|
const seg = segmentVisibility(this.parts, this.chartHidden);
|
|
4460
4484
|
const overflows = () => this.el.scrollWidth > this.el.clientWidth;
|
|
@@ -4611,6 +4635,7 @@ var Statusline = class {
|
|
|
4611
4635
|
this.detach();
|
|
4612
4636
|
this.fitRO?.disconnect();
|
|
4613
4637
|
this.fitRO = null;
|
|
4638
|
+
this.el.ownerDocument.fonts?.removeEventListener("loadingdone", this.onFontsLoaded);
|
|
4614
4639
|
this.el.removeEventListener("contextmenu", this.onContextMenu);
|
|
4615
4640
|
this.menu?.destroy();
|
|
4616
4641
|
this.menu = null;
|
|
@@ -4626,7 +4651,22 @@ var Statusline = class {
|
|
|
4626
4651
|
}
|
|
4627
4652
|
render() {
|
|
4628
4653
|
if (this.menuHooks) this.setChartHidden(!this.menuHooks.chartVisible());
|
|
4629
|
-
this.
|
|
4654
|
+
if (this.widthKey() === this.fitKey) this.renderValues();
|
|
4655
|
+
else this.fit();
|
|
4656
|
+
}
|
|
4657
|
+
/** Everything the readout's width depends on, at every rung: its shape, and each
|
|
4658
|
+
* value's sign, digit count and decimals (the digits themselves are tabular — see
|
|
4659
|
+
* the CSS; the grouping commas follow from the digit count). `toFixed` carries all
|
|
4660
|
+
* three at a fraction of the locale formatter's cost — this runs per pointer move.
|
|
4661
|
+
* The parts, the host's size and the fonts re-fit through their own paths. */
|
|
4662
|
+
widthKey() {
|
|
4663
|
+
const bar = this.hoverBar ?? this.lastBar;
|
|
4664
|
+
if (!bar) return this.readout;
|
|
4665
|
+
const dp = decimalsFor(bar.close);
|
|
4666
|
+
const diff = bar.close - bar.open;
|
|
4667
|
+
const text = [bar.open, bar.high, bar.low, bar.close, diff].map((v) => v.toFixed(dp));
|
|
4668
|
+
text.push((diff / bar.open * 100).toFixed(2));
|
|
4669
|
+
return `${this.readout} ${text.join(" ")}`.replace(/\d/g, "0");
|
|
4630
4670
|
}
|
|
4631
4671
|
/** Write the value readout for the current bar at the current ladder level. */
|
|
4632
4672
|
renderValues() {
|