@luxalgo/vela 0.8.0 → 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/NOTICE +1 -1
- package/README.md +16 -8
- 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-KA77UTRF.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-2IAETM2I.js → chunk-NLDTVFOI.js} +79 -6
- package/dist/{chunk-E3GU6AUI.js → chunk-R2JMNIHY.js} +68 -23
- package/dist/{chunk-QBPUT5SU.js → chunk-T3DZIR5Y.js} +211 -54
- 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 +317 -62
- 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 +115 -5
- 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 +317 -62
- package/dist/vela.global.min.js +61 -68
- package/dist/widget.cjs +476 -85
- package/dist/widget.d.cts +20 -10
- package/dist/widget.d.ts +20 -10
- package/dist/widget.js +7 -7
- package/dist/workspace.cjs +476 -85
- package/dist/workspace.d.cts +6 -6
- package/dist/workspace.d.ts +6 -6
- package/dist/workspace.js +6 -6
- package/package.json +3 -3
package/NOTICE
CHANGED
package/README.md
CHANGED
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
<
|
|
5
|
+
<a href="https://velacharts.dev">
|
|
6
|
+
<picture>
|
|
7
|
+
<source media="(prefers-color-scheme: dark)" srcset=".github/banner.png">
|
|
8
|
+
<source media="(prefers-color-scheme: light)" srcset=".github/banner-light.png">
|
|
9
|
+
<img src=".github/banner.png" alt="Vela™ by LuxAlgo" width="100%">
|
|
10
|
+
</picture>
|
|
11
|
+
</a>
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+
# Vela™
|
|
14
|
+
|
|
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>
|
|
8
16
|
Headless core · native WebGL2 renderer · batteries-included workspace · plugin SDK</p>
|
|
9
17
|
|
|
10
18
|
[![npm version][npm-version-img]][npm-link]
|
|
@@ -12,9 +20,9 @@
|
|
|
12
20
|
[![License][license-img]][license-link]
|
|
13
21
|
|
|
14
22
|
<p>
|
|
15
|
-
<a href="https://
|
|
23
|
+
<a href="https://velacharts.dev">Homepage</a> ·
|
|
16
24
|
<a href="#quick-start">Quick start</a> ·
|
|
17
|
-
<a href="docs/
|
|
25
|
+
<a href="https://docs.luxalgo.com/vela">Documentation</a> ·
|
|
18
26
|
<a href="#extending-plugin-sdk">Plugin SDK</a> ·
|
|
19
27
|
<a href="CHANGELOG.md">Changelog</a> ·
|
|
20
28
|
<a href="#license-and-attribution">License</a>
|
|
@@ -41,7 +49,7 @@ touching the rest.
|
|
|
41
49
|
- **`@luxalgo/vela/workspace`**: the full chart app. One chart or a grid of them under
|
|
42
50
|
one shared topbar (symbol / timeframe / style / indicators), status line, symbol
|
|
43
51
|
watermark, bottom bar (ranges, clock, timezone), object tree, keyboard-first UX
|
|
44
|
-
(type a letter for symbol search, a digit for timeframe entry, `?` for the shortcuts
|
|
52
|
+
(type a letter or `0` for symbol search, a digit 1–9 for timeframe entry, `?` for the shortcuts
|
|
45
53
|
panel), named cells, sync groups, and one persisted state document.
|
|
46
54
|
- **`@luxalgo/vela/ui`**: the component kit the app is built on. Design tokens, overlay
|
|
47
55
|
chrome ([Zag.js](https://zagjs.com) menu/dialog/drawer/tooltip), form primitives, and
|
|
@@ -108,7 +116,7 @@ alongside them.
|
|
|
108
116
|
|
|
109
117
|
Custom scripts still run through pluggable engines. Vela™ **ships none**: install the
|
|
110
118
|
addon for the language you want, or write one against the public `ScriptingEngine` port.
|
|
111
|
-
Pine Script lives in [`@luxalgo/vela-pinets`](https://github.com/LuxAlgo/Vela-pinets)
|
|
119
|
+
Pine Script® lives in [`@luxalgo/vela-pinets`](https://github.com/LuxAlgo/Vela-pinets)
|
|
112
120
|
(`npm i @luxalgo/vela-pinets pinets`), which is **AGPL-3.0** because the PineTS runtime it
|
|
113
121
|
executes is. Vela™ itself stays Apache-2.0 and carries no Pine code:
|
|
114
122
|
|
|
@@ -198,10 +206,10 @@ include it per Apache-2.0 §4(d):
|
|
|
198
206
|
This is the same licensing model used by other popular charting libraries, and we're
|
|
199
207
|
grateful when the watermark stays in a visible spot.
|
|
200
208
|
|
|
201
|
-
No scripting engine ships with this package; the Pine Script addon
|
|
209
|
+
No scripting engine ships with this package; the Pine Script® addon
|
|
202
210
|
(`@luxalgo/vela-pinets`) is AGPL-3.0 and licensed separately (see *Indicators*).
|
|
203
211
|
|
|
204
|
-
[homepage]: https://
|
|
212
|
+
[homepage]: https://velacharts.dev
|
|
205
213
|
|
|
206
214
|
[npm-version-img]: https://img.shields.io/npm/v/%40luxalgo%2Fvela.svg
|
|
207
215
|
[npm-downloads-img]: https://img.shields.io/npm/dm/%40luxalgo%2Fvela.svg
|
|
@@ -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
|
}
|
|
@@ -2506,14 +2544,49 @@ function buildColorPicker(color, theme, onChange, options = {}) {
|
|
|
2506
2544
|
const knob = document.createElement("div");
|
|
2507
2545
|
knob.style.cssText = "position:absolute;top:50%;width:15px;height:15px;border-radius:50%;background:var(--vela-selected-bg);box-shadow:0 1px 3px rgba(0,0,0,0.55);transform:translate(-50%,-50%);pointer-events:none;";
|
|
2508
2546
|
track.appendChild(knob);
|
|
2509
|
-
const pctBox = document.createElement("
|
|
2510
|
-
pctBox.
|
|
2547
|
+
const pctBox = document.createElement("input");
|
|
2548
|
+
pctBox.type = "text";
|
|
2549
|
+
pctBox.inputMode = "numeric";
|
|
2550
|
+
pctBox.title = "Opacity (%)";
|
|
2551
|
+
pctBox.style.cssText = `width:46px;box-sizing:border-box;text-align:center;font:var(--vela-font-size-md) ${theme.fontFamily};color:var(--vela-fg);background:transparent;border:1px solid var(--vela-border);border-radius:5px;padding:3px 4px;outline:none;`;
|
|
2552
|
+
pctBox.addEventListener("focus", () => {
|
|
2553
|
+
pctBox.style.borderColor = "var(--vela-border-strong)";
|
|
2554
|
+
pctBox.select();
|
|
2555
|
+
});
|
|
2511
2556
|
opRow.append(track, pctBox);
|
|
2512
2557
|
const paintOpacity = () => {
|
|
2513
2558
|
track.style.background = `linear-gradient(to right, ${curHex}00, ${curHex}ff), ${CHECKER}`;
|
|
2514
2559
|
knob.style.left = `${curAlpha * 100}%`;
|
|
2515
|
-
pctBox.
|
|
2560
|
+
pctBox.value = `${Math.round(curAlpha * 100)}%`;
|
|
2516
2561
|
};
|
|
2562
|
+
const commitTyped = () => {
|
|
2563
|
+
pctBox.style.borderColor = "var(--vela-border)";
|
|
2564
|
+
const n = parseFloat(pctBox.value.replace("%", ""));
|
|
2565
|
+
if (Number.isFinite(n)) {
|
|
2566
|
+
const next = Math.max(0, Math.min(100, n)) / 100;
|
|
2567
|
+
if (Math.round(next * 100) !== Math.round(curAlpha * 100)) {
|
|
2568
|
+
curAlpha = next;
|
|
2569
|
+
paintOpacity();
|
|
2570
|
+
emit();
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
paintOpacity();
|
|
2575
|
+
};
|
|
2576
|
+
pctBox.addEventListener("change", commitTyped);
|
|
2577
|
+
pctBox.addEventListener("blur", () => {
|
|
2578
|
+
pctBox.style.borderColor = "var(--vela-border)";
|
|
2579
|
+
paintOpacity();
|
|
2580
|
+
});
|
|
2581
|
+
pctBox.addEventListener("keydown", (e) => {
|
|
2582
|
+
if (e.key === "Escape") return;
|
|
2583
|
+
e.stopPropagation();
|
|
2584
|
+
if (e.key === "Enter") {
|
|
2585
|
+
e.preventDefault();
|
|
2586
|
+
commitTyped();
|
|
2587
|
+
pctBox.blur();
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2517
2590
|
let dragging = false;
|
|
2518
2591
|
const onDrag = (clientX) => {
|
|
2519
2592
|
const r = track.getBoundingClientRect();
|