@luxalgo/vela 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataProvider-DGesjOJT.d.ts → DataProvider-DTOEXKTI.d.ts} +1 -1
- package/dist/{DataProvider-BBQ3Hr21.d.cts → DataProvider-gnzv-dgc.d.cts} +1 -1
- package/dist/{chunk-ULIX6AE2.js → chunk-ATPU5CI6.js} +1 -1
- package/dist/{chunk-S6F6SQ2L.js → chunk-B5TIKQJ5.js} +60 -7
- package/dist/{chunk-EP6NDJYK.js → chunk-LJLZR44Y.js} +4 -2
- package/dist/{chunk-AIW3EJUE.js → chunk-P556H6EA.js} +329 -10
- package/dist/{chunk-FNOMKHEZ.js → chunk-RRFGWZNJ.js} +1321 -363
- package/dist/{chunk-5SE4PL3H.js → chunk-Y7TK4ZWK.js} +1363 -48
- package/dist/{contributions-BlV3kb0O.d.cts → contributions-Ci_i3IN2.d.cts} +33 -2
- package/dist/{contributions-6N0rbrPC.d.ts → contributions-b6AVrqqG.d.ts} +33 -2
- package/dist/{history-DX3Zne3a.d.cts → history-Cf1lVsap.d.cts} +15 -3
- package/dist/{history-BTd52YMA.d.ts → history-Ci3M0xLV.d.ts} +15 -3
- package/dist/index.cjs +1374 -366
- package/dist/index.d.cts +52 -19
- package/dist/index.d.ts +52 -19
- package/dist/index.js +4 -4
- package/dist/{options-DbQholFV.d.cts → options-XTBpbx0s.d.cts} +47 -1
- package/dist/{options-DbQholFV.d.ts → options-XTBpbx0s.d.ts} +47 -1
- package/dist/{plugin-TN3n-9YN.d.ts → plugin-CnNd7cuF.d.ts} +172 -25
- package/dist/{plugin-O34MbXf-.d.cts → plugin-DfH4Y-Om.d.cts} +172 -25
- package/dist/plugin.cjs +53 -7
- 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 +332 -9
- package/dist/ui.d.cts +64 -2
- package/dist/ui.d.ts +64 -2
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +1374 -366
- package/dist/vela.global.min.js +84 -34
- package/dist/widget.cjs +3348 -550
- package/dist/widget.d.cts +65 -7
- package/dist/widget.d.ts +65 -7
- package/dist/widget.js +169 -18
- package/dist/workspace.cjs +4251 -1400
- package/dist/workspace.d.cts +45 -9
- package/dist/workspace.d.ts +45 -9
- package/dist/workspace.js +226 -22
- package/package.json +1 -1
|
@@ -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 } from './chunk-LJLZR44Y.js';
|
|
2
2
|
|
|
3
3
|
// src/core/native-indicators/NativeIndicator.ts
|
|
4
4
|
var REGISTRY = /* @__PURE__ */ new Map();
|
|
@@ -1490,10 +1490,10 @@ function polylineHit(px, py, pts, tol) {
|
|
|
1490
1490
|
}
|
|
1491
1491
|
|
|
1492
1492
|
// src/core/drawings/types/FixedRangeVolumeProfile.ts
|
|
1493
|
-
var OUTSIDE_UP = `${BULLISH}
|
|
1494
|
-
var OUTSIDE_DOWN = `${BEARISH}
|
|
1495
|
-
var VA_UP = `${BULLISH}
|
|
1496
|
-
var VA_DOWN = `${BEARISH}
|
|
1493
|
+
var OUTSIDE_UP = `${BULLISH}66`;
|
|
1494
|
+
var OUTSIDE_DOWN = `${BEARISH}66`;
|
|
1495
|
+
var VA_UP = `${BULLISH}BF`;
|
|
1496
|
+
var VA_DOWN = `${BEARISH}BF`;
|
|
1497
1497
|
function defaultFrvpStyle() {
|
|
1498
1498
|
return {
|
|
1499
1499
|
rows: 24,
|
|
@@ -1511,7 +1511,8 @@ function defaultFrvpStyle() {
|
|
|
1511
1511
|
valColor: NEUTRAL,
|
|
1512
1512
|
valStyle: "solid",
|
|
1513
1513
|
showPoc: true,
|
|
1514
|
-
pocColor:
|
|
1514
|
+
pocColor: void 0,
|
|
1515
|
+
// theme contrast ink until the user picks a color
|
|
1515
1516
|
pocStyle: "solid",
|
|
1516
1517
|
showDevelopingPoc: false,
|
|
1517
1518
|
developingPocColor: ACCENT,
|
|
@@ -5647,6 +5648,47 @@ registerDrawingType({
|
|
|
5647
5648
|
});
|
|
5648
5649
|
|
|
5649
5650
|
// src/chart-types/registry.ts
|
|
5651
|
+
function normalizeSettingsRow(r) {
|
|
5652
|
+
switch (r.kind) {
|
|
5653
|
+
case "row":
|
|
5654
|
+
return { label: r.label, toggle: r.toggle, controls: r.controls, when: r.when };
|
|
5655
|
+
case "toggle": {
|
|
5656
|
+
const controls = [];
|
|
5657
|
+
if (r.number) controls.push({ kind: "number", ...r.number });
|
|
5658
|
+
for (const c of r.colors ?? []) controls.push({ kind: "color", ...c });
|
|
5659
|
+
if (r.width) controls.push({ kind: "width", ...r.width });
|
|
5660
|
+
return { label: r.label, toggle: { key: r.key, defval: r.defval }, controls, when: r.when };
|
|
5661
|
+
}
|
|
5662
|
+
case "number":
|
|
5663
|
+
return { label: r.label, controls: [{ kind: "number", key: r.key, label: r.label, defval: r.defval, min: r.min, max: r.max, step: r.step }], when: r.when };
|
|
5664
|
+
case "color":
|
|
5665
|
+
return { label: r.label, controls: [{ kind: "color", key: r.key, label: r.label, defval: r.defval }], when: r.when };
|
|
5666
|
+
case "select":
|
|
5667
|
+
return { label: r.label, controls: [{ kind: "select", key: r.key, label: r.label, options: r.options, defval: r.defval }], when: r.when };
|
|
5668
|
+
case "range":
|
|
5669
|
+
return {
|
|
5670
|
+
label: r.label,
|
|
5671
|
+
controls: [
|
|
5672
|
+
{ kind: "number", key: r.minKey, label: `${r.label} \u2014 min`, defval: r.defval, min: r.min, max: r.max, step: r.step, placeholder: r.placeholder },
|
|
5673
|
+
{ kind: "hint", text: "\u2013" },
|
|
5674
|
+
{ kind: "number", key: r.maxKey, label: `${r.label} \u2014 max`, defval: r.defval, min: r.min, max: r.max, step: r.step, placeholder: r.placeholder }
|
|
5675
|
+
],
|
|
5676
|
+
when: r.when
|
|
5677
|
+
};
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
function settingsRowValueKeys(r) {
|
|
5681
|
+
if (r.kind === "heading" || r.kind === "header") return [];
|
|
5682
|
+
const n = normalizeSettingsRow(r);
|
|
5683
|
+
const keys = [];
|
|
5684
|
+
if (n.toggle) keys.push({ key: n.toggle.key, type: "boolean", defval: n.toggle.defval });
|
|
5685
|
+
for (const c of n.controls) {
|
|
5686
|
+
if (c.kind === "hint") continue;
|
|
5687
|
+
if (c.kind === "color" || c.kind === "select") keys.push({ key: c.key, type: "string", defval: c.defval });
|
|
5688
|
+
else keys.push({ key: c.key, type: "number", defval: c.defval });
|
|
5689
|
+
}
|
|
5690
|
+
return keys;
|
|
5691
|
+
}
|
|
5650
5692
|
function settingsRowVisible(when, bag) {
|
|
5651
5693
|
if (!when) return true;
|
|
5652
5694
|
const conds = Array.isArray(when) ? when : [when];
|
|
@@ -5850,6 +5892,17 @@ var CSS = `
|
|
|
5850
5892
|
}
|
|
5851
5893
|
.vela-panel-resizer:hover::after,
|
|
5852
5894
|
.vela-panel-resizer[data-dragging]::after { background: var(--vela-accent); }
|
|
5895
|
+
/* Mobile: a 280px column would crush a phone-width chart \u2014 the panel overlays the
|
|
5896
|
+
chart area instead (its flex parent is position:relative), full-bleed, closed by
|
|
5897
|
+
the same header \u2715. Width dragging is a pointer affordance; off on mobile. */
|
|
5898
|
+
[data-layout='mobile'] .vela-panel {
|
|
5899
|
+
position: absolute;
|
|
5900
|
+
inset: 0;
|
|
5901
|
+
width: auto;
|
|
5902
|
+
z-index: 25;
|
|
5903
|
+
border-left: none;
|
|
5904
|
+
}
|
|
5905
|
+
[data-layout='mobile'] .vela-panel-resizer { display: none; }
|
|
5853
5906
|
`;
|
|
5854
5907
|
function clampPanelWidth(px, min = DEFAULT_PANEL_MIN_WIDTH, max = DEFAULT_PANEL_MAX_WIDTH) {
|
|
5855
5908
|
const lo = Number.isFinite(min) && min > 0 ? min : DEFAULT_PANEL_MIN_WIDTH;
|
|
@@ -5978,4 +6031,4 @@ var SidePanel = class {
|
|
|
5978
6031
|
}
|
|
5979
6032
|
};
|
|
5980
6033
|
|
|
5981
|
-
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, MachFigure, MeasureBox, Note, PatternDrawing, PositionTool, PriceLabel, PriceNote, RadialFib, RegressionChannel, STAMP_SIZE_OPTIONS, SegmentDrawing, SidePanel, Signpost, TEXT_SIZE_OPTIONS, TextLabel, chartType, chartTypes, clampPanelWidth, createDrawing, deserializeDrawing, drawingTypes, formatDuration, getDrawingType, getNativeIndicator, legendActions, legendActionsProviderFor, lineSegmentIntersection, nativeIndicatorDescriptors, nativeIndicatorTypes, registerChartType, registerDefaultEngine, registerDrawingType, registerLegendAction, registerNativeIndicator, registerRendererDefaults, registerRendererLayer, registerSidePanel, registerWidgetAction, registerWidgetAttachment, rendererDefaults, rendererLayers, resetDrawingSettings, resolveEngines, settingsRowVisible, sidePanels, tickerModifierIds, unregisterChartType, unregisterDefaultEngine, unregisterLegendAction, unregisterNativeIndicator, unregisterRendererDefaults, unregisterRendererLayer, unregisterSidePanel, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments };
|
|
6034
|
+
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, MachFigure, MeasureBox, Note, PatternDrawing, PositionTool, PriceLabel, PriceNote, RadialFib, RegressionChannel, STAMP_SIZE_OPTIONS, SegmentDrawing, SidePanel, Signpost, TEXT_SIZE_OPTIONS, TextLabel, chartType, chartTypes, clampPanelWidth, createDrawing, deserializeDrawing, drawingTypes, formatDuration, getDrawingType, getNativeIndicator, legendActions, legendActionsProviderFor, lineSegmentIntersection, nativeIndicatorDescriptors, nativeIndicatorTypes, normalizeSettingsRow, registerChartType, registerDefaultEngine, registerDrawingType, registerLegendAction, registerNativeIndicator, registerRendererDefaults, registerRendererLayer, registerSidePanel, registerWidgetAction, registerWidgetAttachment, rendererDefaults, rendererLayers, resetDrawingSettings, resolveEngines, settingsRowValueKeys, settingsRowVisible, sidePanels, tickerModifierIds, unregisterChartType, unregisterDefaultEngine, unregisterLegendAction, unregisterNativeIndicator, unregisterRendererDefaults, unregisterRendererLayer, unregisterSidePanel, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments };
|
|
@@ -106,14 +106,15 @@ registerIcon(
|
|
|
106
106
|
svg24('<path d="m10 20-1.25-2.5L6 16"/><path d="M10 4 8.75 6.5 6 8"/><path d="m14 20 1.25-2.5L18 16"/><path d="m14 4 1.25 2.5L18 8"/><path d="m17 10-5 5-5-5"/><path d="M4 12h16"/>')
|
|
107
107
|
);
|
|
108
108
|
registerIcon("chevron-right", S('<path d="m6 3.5 4.5 4.5L6 12.5"/>'));
|
|
109
|
+
registerIcon("chevron-left", S('<path d="M10 3.5 5.5 8l4.5 4.5"/>'));
|
|
109
110
|
registerIcon("chevron-down", S('<path d="M3.5 6 8 10.5 12.5 6"/>'));
|
|
110
111
|
registerIcon("chevron-up", S('<path d="M3.5 10 8 5.5 12.5 10"/>'));
|
|
111
112
|
registerIcon("chevrons-right", S('<path d="m4 3.5 4.5 4.5L4 12.5"/><path d="m8.5 3.5 4.5 4.5-4.5 4.5"/>'));
|
|
112
113
|
registerIcon("chevrons-left", S('<path d="M12 3.5 7.5 8l4.5 4.5"/><path d="M7.5 3.5 3 8l4.5 4.5"/>'));
|
|
113
114
|
registerIcon("check", S('<path d="m2.8 8.4 3.4 3.4 7-7.6"/>'));
|
|
114
115
|
registerIcon("close", S('<path d="m3.8 3.8 8.4 8.4M12.2 3.8l-8.4 8.4"/>'));
|
|
115
|
-
registerIcon("eye", S('<path d="M1.5 8s2.5-4
|
|
116
|
-
registerIcon("eye-off", S('<path d="M1.5 8s2.5-4
|
|
116
|
+
registerIcon("eye", S('<path d="M1.5 8s2.5-5.4 6.5-5.4S14.5 8 14.5 8 12 13.4 8 13.4 1.5 8 1.5 8z"/><circle cx="8" cy="8" r="1.8"/>'));
|
|
117
|
+
registerIcon("eye-off", S('<path d="M1.5 8s2.5-5.4 6.5-5.4S14.5 8 14.5 8 12 13.4 8 13.4 1.5 8 1.5 8z" opacity="0.45"/><path d="m3 13 10-10"/>'));
|
|
117
118
|
registerIcon("lock", S('<rect x="3.2" y="7" width="9.6" height="6.6" rx="1.2"/><path d="M5.6 7V5.2a2.4 2.4 0 0 1 4.8 0V7"/>'));
|
|
118
119
|
registerIcon("unlock", S('<rect x="3.2" y="7" width="9.6" height="6.6" rx="1.2"/><path d="M5.6 7V5.2a2.4 2.4 0 0 1 4.7-.7"/>'));
|
|
119
120
|
registerIcon("trash", S('<path d="M2.5 4.3h11M6.2 4.3V3.1a1 1 0 0 1 1-1h1.6a1 1 0 0 1 1 1v1.2M4.2 4.3l.5 9.1a1.15 1.15 0 0 0 1.15 1.1h4.3a1.15 1.15 0 0 0 1.15-1.1l.5-9.1M6.5 7v4.5M9.5 7v4.5"/>'));
|
|
@@ -136,6 +137,7 @@ registerIcon("star", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l
|
|
|
136
137
|
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"'));
|
|
137
138
|
registerIcon("grip", S('<circle cx="6" cy="3.5" r="1"/><circle cx="10" cy="3.5" r="1"/><circle cx="6" cy="8" r="1"/><circle cx="10" cy="8" r="1"/><circle cx="6" cy="12.5" r="1"/><circle cx="10" cy="12.5" r="1"/>', 'fill="currentColor" stroke="none"'));
|
|
138
139
|
registerIcon("kebab", S('<circle cx="8" cy="3.2" r="1.2"/><circle cx="8" cy="8" r="1.2"/><circle cx="8" cy="12.8" r="1.2"/>', 'fill="currentColor" stroke="none"'));
|
|
140
|
+
registerIcon("burger", S('<path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/>'));
|
|
139
141
|
registerIcon("reset", S('<rect x="6" y="6" width="4" height="4" rx="0.8"/><path d="M2.2 8a5.8 5.8 0 1 0 1.9-4.3L2.2 5.3"/><path d="M2.2 2.2v3.1h3.1"/>'));
|
|
140
142
|
registerIcon("pane-collapse", S('<path d="M2.6 9.4h4v4M13.4 6.6h-4v-4"/><path d="m9.4 6.6 4.2-4.2M2.4 13.6l4.2-4.2"/>'));
|
|
141
143
|
registerIcon("pane-expand", S('<path d="M9.8 2.4h3.8v3.8M6.2 13.6H2.4V9.8"/><path d="m13.6 2.4-4.4 4.4M2.4 13.6l4.4-4.4"/>'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { STATIC_TOKENS, themeTokens } from './chunk-
|
|
2
|
-
import { injectStyles, iconEl } from './chunk-
|
|
1
|
+
import { STATIC_TOKENS, themeTokens } from './chunk-ATPU5CI6.js';
|
|
2
|
+
import { injectStyles, iconEl } from './chunk-LJLZR44Y.js';
|
|
3
3
|
import { VanillaMachine, normalizeProps, spreadProps } from '@zag-js/vanilla';
|
|
4
4
|
export { normalizeProps, spreadProps } from '@zag-js/vanilla';
|
|
5
5
|
import * as tooltip from '@zag-js/tooltip';
|
|
@@ -33,8 +33,8 @@ function ensureUIHost(el, theme) {
|
|
|
33
33
|
el.classList.add("vela-ui");
|
|
34
34
|
if (theme) applyThemeTokens(el, theme);
|
|
35
35
|
}
|
|
36
|
-
function runMachine(
|
|
37
|
-
const m = new VanillaMachine(
|
|
36
|
+
function runMachine(machine5, props, render) {
|
|
37
|
+
const m = new VanillaMachine(machine5, props);
|
|
38
38
|
const unsub = m.subscribe(render);
|
|
39
39
|
m.start();
|
|
40
40
|
render(m.service);
|
|
@@ -322,10 +322,13 @@ var MENU_CSS = `
|
|
|
322
322
|
.vela-menu-item .vela-icon { width: 16px; height: 16px; font-size: 16px; justify-content: center; color: var(--vela-fg-muted); }
|
|
323
323
|
.vela-menu-item .vela-menu-label { flex: 1 1 auto; }
|
|
324
324
|
.vela-menu-item .vela-menu-hint { color: var(--vela-fg-faint); font-size: var(--vela-font-size-sm); }
|
|
325
|
-
/* Active entry:
|
|
326
|
-
Declared after
|
|
327
|
-
|
|
328
|
-
.vela-menu-item[data-checked]
|
|
325
|
+
/* Active entry: the row surface carries the selection \u2014 a stronger background wash
|
|
326
|
+
than the hover one, plus bright ink. Declared after the hover rule so a selected
|
|
327
|
+
row stays visibly selected while highlighted. */
|
|
328
|
+
.vela-menu-item[data-checked] {
|
|
329
|
+
background: var(--vela-hover-strong);
|
|
330
|
+
color: var(--vela-fg-bright);
|
|
331
|
+
}
|
|
329
332
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
330
333
|
same control language as the settings dialog's toggles. */
|
|
331
334
|
.vela-menu-switch {
|
|
@@ -543,6 +546,7 @@ function dialogController(opts = {}) {
|
|
|
543
546
|
modal: opts.modal ?? true,
|
|
544
547
|
closeOnEscape: opts.closeOnEscape ?? true,
|
|
545
548
|
closeOnInteractOutside: opts.closeOnInteractOutside ?? false,
|
|
549
|
+
initialFocusEl: opts.initialFocusEl,
|
|
546
550
|
onOpenChange: (d) => opts.onOpenChange?.(d.open)
|
|
547
551
|
},
|
|
548
552
|
connect: (service) => dialog.connect(service, normalizeProps)
|
|
@@ -619,6 +623,29 @@ var DIALOG_CSS = `
|
|
|
619
623
|
border: 2px solid transparent;
|
|
620
624
|
background-clip: padding-box;
|
|
621
625
|
}
|
|
626
|
+
/* \u2500\u2500 mobile chrome: dialogs fill the shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
627
|
+
Inside a shell in the mobile size class ([data-layout='mobile'] on the widget root,
|
|
628
|
+
which is position:relative) every kit dialog presents fullscreen: desktop cards are
|
|
629
|
+
unusable at phone widths, and the shell's bounds \u2014 not the viewport \u2014 are the honest
|
|
630
|
+
"screen" for an embedded chart. */
|
|
631
|
+
[data-layout='mobile'] .vela-dialog-backdrop { position: absolute; }
|
|
632
|
+
[data-layout='mobile'] .vela-dialog-positioner {
|
|
633
|
+
position: absolute;
|
|
634
|
+
padding: 0;
|
|
635
|
+
align-items: stretch;
|
|
636
|
+
}
|
|
637
|
+
[data-layout='mobile'] .vela-dialog {
|
|
638
|
+
width: 100%;
|
|
639
|
+
min-width: 0;
|
|
640
|
+
max-width: none;
|
|
641
|
+
max-height: none;
|
|
642
|
+
flex: 1 1 auto;
|
|
643
|
+
border: none;
|
|
644
|
+
border-radius: 0;
|
|
645
|
+
transform: none !important; /* a desktop drag offset must not survive the flip */
|
|
646
|
+
}
|
|
647
|
+
[data-layout='mobile'] .vela-dialog-close { width: 40px; height: 40px; }
|
|
648
|
+
[data-layout='mobile'] .vela-dialog-body { padding-bottom: calc(var(--vela-space-4) + env(safe-area-inset-bottom, 0px)); }
|
|
622
649
|
`;
|
|
623
650
|
var Dialog = class {
|
|
624
651
|
constructor(opts = {}) {
|
|
@@ -632,6 +659,7 @@ var Dialog = class {
|
|
|
632
659
|
this.positioner.className = "vela-dialog-positioner vela-ui-layer";
|
|
633
660
|
this.panel = doc.createElement("div");
|
|
634
661
|
this.panel.className = "vela-dialog";
|
|
662
|
+
this.panel.tabIndex = -1;
|
|
635
663
|
const header = doc.createElement("div");
|
|
636
664
|
header.className = "vela-dialog-header";
|
|
637
665
|
if (opts.draggable) {
|
|
@@ -666,7 +694,18 @@ var Dialog = class {
|
|
|
666
694
|
this.panel.append(header, this.body);
|
|
667
695
|
this.positioner.appendChild(this.panel);
|
|
668
696
|
host.append(this.backdrop, this.positioner);
|
|
669
|
-
this.ctrl = dialogController(
|
|
697
|
+
this.ctrl = dialogController({
|
|
698
|
+
...opts,
|
|
699
|
+
// Mobile chrome (fullscreen presentation): opening must never land focus on
|
|
700
|
+
// an input — that pops the on-screen keyboard over the just-opened dialog.
|
|
701
|
+
// Focus goes to the panel; a caller that WANTS the keyboard focuses its
|
|
702
|
+
// input explicitly. Desktop keeps the caller's pick, else the machine's
|
|
703
|
+
// first-tabbable default.
|
|
704
|
+
initialFocusEl: () => {
|
|
705
|
+
if (this.positioner.closest('[data-layout="mobile"]')) return this.panel;
|
|
706
|
+
return opts.initialFocusEl?.() ?? null;
|
|
707
|
+
}
|
|
708
|
+
});
|
|
670
709
|
const mid = String(this.ctrl.props.id);
|
|
671
710
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
672
711
|
const api = this.ctrl.connect(service);
|
|
@@ -694,5 +733,285 @@ var Dialog = class {
|
|
|
694
733
|
this.positioner.remove();
|
|
695
734
|
}
|
|
696
735
|
};
|
|
736
|
+
function drawerController(opts = {}) {
|
|
737
|
+
return {
|
|
738
|
+
machine: dialog.machine,
|
|
739
|
+
props: {
|
|
740
|
+
id: nextUid("vela-drawer"),
|
|
741
|
+
modal: true,
|
|
742
|
+
closeOnEscape: opts.closeOnEscape ?? true,
|
|
743
|
+
closeOnInteractOutside: opts.closeOnInteractOutside ?? true,
|
|
744
|
+
initialFocusEl: opts.initialFocusEl,
|
|
745
|
+
onOpenChange: (d) => opts.onOpenChange?.(d.open)
|
|
746
|
+
},
|
|
747
|
+
connect: (service) => dialog.connect(service, normalizeProps)
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/ui/components/drawer/styles.ts
|
|
752
|
+
var DRAWER_STYLE_ID = "vela-ui-drawer";
|
|
753
|
+
var DRAWER_CSS = `
|
|
754
|
+
.vela-drawer-backdrop {
|
|
755
|
+
position: fixed;
|
|
756
|
+
inset: 0;
|
|
757
|
+
background: var(--vela-backdrop);
|
|
758
|
+
z-index: var(--vela-z-dialog);
|
|
759
|
+
}
|
|
760
|
+
.vela-drawer-positioner {
|
|
761
|
+
position: fixed;
|
|
762
|
+
inset: 0;
|
|
763
|
+
display: flex;
|
|
764
|
+
align-items: flex-end;
|
|
765
|
+
justify-content: center;
|
|
766
|
+
z-index: var(--vela-z-dialog);
|
|
767
|
+
}
|
|
768
|
+
/* Inside a shell that declares a size class, the sheet scopes to the SHELL's bounds
|
|
769
|
+
(the widget root is position:relative) instead of the whole viewport \u2014 an embedded
|
|
770
|
+
chart must not curtain the host page. */
|
|
771
|
+
[data-layout] .vela-drawer-backdrop, [data-layout] .vela-drawer-positioner { position: absolute; }
|
|
772
|
+
.vela-drawer {
|
|
773
|
+
background: var(--vela-surface);
|
|
774
|
+
color: var(--vela-fg);
|
|
775
|
+
border: 1px solid var(--vela-border-strong);
|
|
776
|
+
border-bottom: none;
|
|
777
|
+
border-radius: 14px 14px 0 0;
|
|
778
|
+
box-shadow: var(--vela-shadow-dialog);
|
|
779
|
+
font-size: 13px;
|
|
780
|
+
width: 100%;
|
|
781
|
+
max-height: 85%;
|
|
782
|
+
display: flex;
|
|
783
|
+
flex-direction: column;
|
|
784
|
+
overflow: hidden;
|
|
785
|
+
outline: none;
|
|
786
|
+
}
|
|
787
|
+
.vela-drawer[data-state='open'] { animation: vela-drawer-in var(--vela-dur-med) var(--vela-ease); }
|
|
788
|
+
@keyframes vela-drawer-in {
|
|
789
|
+
from { transform: translateY(100%); }
|
|
790
|
+
to { transform: translateY(0); }
|
|
791
|
+
}
|
|
792
|
+
/* The grab zone owns its touches (drag-to-dismiss), so the browser must not scroll it. */
|
|
793
|
+
.vela-drawer-grab {
|
|
794
|
+
flex: none;
|
|
795
|
+
display: flex;
|
|
796
|
+
align-items: center;
|
|
797
|
+
justify-content: center;
|
|
798
|
+
padding: 10px 0 6px;
|
|
799
|
+
cursor: grab;
|
|
800
|
+
touch-action: none;
|
|
801
|
+
user-select: none;
|
|
802
|
+
}
|
|
803
|
+
.vela-drawer-grab::before {
|
|
804
|
+
content: '';
|
|
805
|
+
width: 36px;
|
|
806
|
+
height: 4px;
|
|
807
|
+
border-radius: 2px;
|
|
808
|
+
background: var(--vela-border-strong);
|
|
809
|
+
}
|
|
810
|
+
.vela-drawer-title {
|
|
811
|
+
flex: none;
|
|
812
|
+
padding: 0 16px 10px;
|
|
813
|
+
font-size: 15px;
|
|
814
|
+
font-weight: 600;
|
|
815
|
+
letter-spacing: 0.2px;
|
|
816
|
+
color: var(--vela-fg-bright);
|
|
817
|
+
user-select: none;
|
|
818
|
+
}
|
|
819
|
+
.vela-drawer-title:empty { display: none; }
|
|
820
|
+
.vela-drawer-body {
|
|
821
|
+
flex: 1 1 auto;
|
|
822
|
+
min-height: 0;
|
|
823
|
+
overflow-y: auto;
|
|
824
|
+
overscroll-behavior: contain;
|
|
825
|
+
-webkit-overflow-scrolling: touch;
|
|
826
|
+
/* Vertical pans stay native scrolling; horizontal moves reach the sheet's gesture
|
|
827
|
+
recognizer as pointer events (tab swipes). Without this the browser claims a
|
|
828
|
+
sideways touch as a scroll attempt and CANCELS the pointer stream, so swipes
|
|
829
|
+
never registered on real touch devices. Sideways-scrolling strips inside the
|
|
830
|
+
body opt back in with their own touch-action: pan-x. */
|
|
831
|
+
touch-action: pan-y;
|
|
832
|
+
padding: 0 var(--vela-space-3) calc(var(--vela-space-3) + env(safe-area-inset-bottom, 0px));
|
|
833
|
+
}
|
|
834
|
+
.vela-drawer-body::-webkit-scrollbar { width: 8px; }
|
|
835
|
+
.vela-drawer-body::-webkit-scrollbar-thumb {
|
|
836
|
+
background: var(--vela-scroll);
|
|
837
|
+
border-radius: 4px;
|
|
838
|
+
border: 2px solid transparent;
|
|
839
|
+
background-clip: padding-box;
|
|
840
|
+
}
|
|
841
|
+
`;
|
|
842
|
+
var DISMISS_FRACTION = 0.33;
|
|
843
|
+
var DISMISS_PX = 96;
|
|
844
|
+
var SLOP_PX = 8;
|
|
845
|
+
var HSWIPE_MIN_PX = 48;
|
|
846
|
+
function classifyGesture(dx, dy, ctx) {
|
|
847
|
+
if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP_PX) return "pending";
|
|
848
|
+
if (Math.abs(dy) > Math.abs(dx)) return dy > 0 && !ctx.scrolled ? "drag" : "scroll";
|
|
849
|
+
return ctx.canSwipe && !ctx.hScrollable ? "hswipe" : "scroll";
|
|
850
|
+
}
|
|
851
|
+
function dragDismisses(dy, panelHeightPx) {
|
|
852
|
+
return dy >= Math.min(DISMISS_PX, panelHeightPx * DISMISS_FRACTION);
|
|
853
|
+
}
|
|
854
|
+
function swipeDirection(dx, dy) {
|
|
855
|
+
if (Math.abs(dx) < HSWIPE_MIN_PX || Math.abs(dx) <= Math.abs(dy)) return null;
|
|
856
|
+
return dx < 0 ? "left" : "right";
|
|
857
|
+
}
|
|
858
|
+
var Drawer = class {
|
|
859
|
+
constructor(opts = {}) {
|
|
860
|
+
const doc = (opts.host ?? document.body).ownerDocument;
|
|
861
|
+
injectStyles(DRAWER_STYLE_ID, DRAWER_CSS, doc);
|
|
862
|
+
const host = opts.host ?? doc.body;
|
|
863
|
+
this.backdrop = doc.createElement("div");
|
|
864
|
+
this.backdrop.className = "vela-drawer-backdrop vela-ui-layer";
|
|
865
|
+
this.positioner = doc.createElement("div");
|
|
866
|
+
this.positioner.className = "vela-drawer-positioner vela-ui-layer";
|
|
867
|
+
this.panel = doc.createElement("div");
|
|
868
|
+
this.panel.className = "vela-drawer";
|
|
869
|
+
this.panel.tabIndex = -1;
|
|
870
|
+
const grab = doc.createElement("div");
|
|
871
|
+
grab.className = "vela-drawer-grab";
|
|
872
|
+
this.titleEl = doc.createElement("div");
|
|
873
|
+
this.titleEl.className = "vela-drawer-title";
|
|
874
|
+
this.titleEl.textContent = opts.title ?? "";
|
|
875
|
+
this.body = doc.createElement("div");
|
|
876
|
+
this.body.className = "vela-drawer-body";
|
|
877
|
+
if (opts.content instanceof Node) this.body.appendChild(opts.content);
|
|
878
|
+
else if (typeof opts.content === "function") opts.content(this.body);
|
|
879
|
+
this.panel.append(grab, this.titleEl, this.body);
|
|
880
|
+
this.positioner.appendChild(this.panel);
|
|
881
|
+
host.append(this.backdrop, this.positioner);
|
|
882
|
+
this.wireGestures(grab, opts.onSwipe);
|
|
883
|
+
this.ctrl = drawerController({ ...opts, initialFocusEl: () => this.panel });
|
|
884
|
+
const mid = String(this.ctrl.props.id);
|
|
885
|
+
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
886
|
+
const api = this.ctrl.connect(service);
|
|
887
|
+
spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
888
|
+
spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
889
|
+
spreadProps(this.panel, api.getContentProps(), mid);
|
|
890
|
+
spreadProps(this.titleEl, api.getTitleProps(), mid);
|
|
891
|
+
this.backdrop.style.display = api.open ? "" : "none";
|
|
892
|
+
this.positioner.style.display = api.open ? "" : "none";
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
/** Any element between `from` and the panel that has already been scrolled down —
|
|
896
|
+
* a downward pull there must scroll it back up, never drag the sheet. */
|
|
897
|
+
scrolledAncestor(from) {
|
|
898
|
+
let el = from instanceof Element ? from : null;
|
|
899
|
+
while (el && el !== this.panel) {
|
|
900
|
+
if (el.scrollTop > 0) return true;
|
|
901
|
+
el = el.parentElement;
|
|
902
|
+
}
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
/** Any element between `from` and the panel that scrolls horizontally on its own
|
|
906
|
+
* (the tab strip, chip rows) — a sideways move there is ITS scroll, not a swipe. */
|
|
907
|
+
hScrollableAncestor(from) {
|
|
908
|
+
let el = from instanceof Element ? from : null;
|
|
909
|
+
while (el && el !== this.panel) {
|
|
910
|
+
if (el.scrollWidth > el.clientWidth + 1) return true;
|
|
911
|
+
el = el.parentElement;
|
|
912
|
+
}
|
|
913
|
+
return false;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* One gesture recognizer for the whole sheet. A downward pull dismisses from
|
|
917
|
+
* anywhere — the grab handle immediately, the content once it is decidedly vertical
|
|
918
|
+
* and its scroller is at rest (a scrolled list keeps native scrolling). A decidedly
|
|
919
|
+
* horizontal move becomes an `onSwipe` (tabbed drawers flip pages with it). The
|
|
920
|
+
* non-passive touchmove hook is what keeps the browser from claiming the pull as a
|
|
921
|
+
* scroll once the sheet is (or may become) the drag target.
|
|
922
|
+
*/
|
|
923
|
+
wireGestures(grab, onSwipe) {
|
|
924
|
+
let startX = 0;
|
|
925
|
+
let startY = 0;
|
|
926
|
+
let dx = 0;
|
|
927
|
+
let dy = 0;
|
|
928
|
+
let mode = "idle";
|
|
929
|
+
const beginDrag = (e) => {
|
|
930
|
+
mode = "drag";
|
|
931
|
+
startY = e.clientY;
|
|
932
|
+
this.panel.style.transition = "none";
|
|
933
|
+
try {
|
|
934
|
+
this.panel.setPointerCapture(e.pointerId);
|
|
935
|
+
} catch {
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
this.panel.addEventListener("pointerdown", (e) => {
|
|
939
|
+
if (e.isPrimary === false) return;
|
|
940
|
+
startX = e.clientX;
|
|
941
|
+
startY = e.clientY;
|
|
942
|
+
dx = 0;
|
|
943
|
+
dy = 0;
|
|
944
|
+
if (grab.contains(e.target)) beginDrag(e);
|
|
945
|
+
else mode = "pending";
|
|
946
|
+
});
|
|
947
|
+
this.panel.addEventListener("pointermove", (e) => {
|
|
948
|
+
if (mode === "idle" || mode === "scroll") return;
|
|
949
|
+
dx = e.clientX - startX;
|
|
950
|
+
dy = e.clientY - startY;
|
|
951
|
+
if (mode === "pending") {
|
|
952
|
+
const intent = classifyGesture(dx, dy, {
|
|
953
|
+
canSwipe: !!onSwipe,
|
|
954
|
+
scrolled: this.scrolledAncestor(e.target),
|
|
955
|
+
hScrollable: this.hScrollableAncestor(e.target)
|
|
956
|
+
});
|
|
957
|
+
if (intent === "pending") return;
|
|
958
|
+
if (intent === "drag") beginDrag(e);
|
|
959
|
+
else mode = intent;
|
|
960
|
+
}
|
|
961
|
+
if (mode === "drag") {
|
|
962
|
+
dy = Math.max(0, e.clientY - startY);
|
|
963
|
+
this.panel.style.transform = dy > 0 ? `translateY(${dy}px)` : "";
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
this.panel.addEventListener(
|
|
967
|
+
"touchmove",
|
|
968
|
+
(e) => {
|
|
969
|
+
if (mode === "drag" || mode === "hswipe") {
|
|
970
|
+
e.preventDefault();
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
if (mode !== "pending") return;
|
|
974
|
+
const t = e.touches[0];
|
|
975
|
+
if (!t) return;
|
|
976
|
+
const mdx = t.clientX - startX;
|
|
977
|
+
const mdy = t.clientY - startY;
|
|
978
|
+
if (mdy > Math.abs(mdx) && !this.scrolledAncestor(e.target)) e.preventDefault();
|
|
979
|
+
},
|
|
980
|
+
{ passive: false }
|
|
981
|
+
);
|
|
982
|
+
const settle = () => {
|
|
983
|
+
if (mode === "idle") return;
|
|
984
|
+
const finished = mode;
|
|
985
|
+
mode = "idle";
|
|
986
|
+
if (finished === "drag") {
|
|
987
|
+
this.panel.style.transition = "";
|
|
988
|
+
this.panel.style.transform = "";
|
|
989
|
+
if (dragDismisses(dy, this.panel.getBoundingClientRect().height)) this.hide();
|
|
990
|
+
} else if (finished === "hswipe") {
|
|
991
|
+
const dir = swipeDirection(dx, dy);
|
|
992
|
+
if (dir) onSwipe?.(dir);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
this.panel.addEventListener("pointerup", settle);
|
|
996
|
+
this.panel.addEventListener("pointercancel", settle);
|
|
997
|
+
}
|
|
998
|
+
setTitle(title) {
|
|
999
|
+
this.titleEl.textContent = title;
|
|
1000
|
+
}
|
|
1001
|
+
get open() {
|
|
1002
|
+
return this.ctrl.connect(this.handle.service).open;
|
|
1003
|
+
}
|
|
1004
|
+
show() {
|
|
1005
|
+
this.ctrl.connect(this.handle.service).setOpen(true);
|
|
1006
|
+
}
|
|
1007
|
+
hide() {
|
|
1008
|
+
this.ctrl.connect(this.handle.service).setOpen(false);
|
|
1009
|
+
}
|
|
1010
|
+
destroy() {
|
|
1011
|
+
this.handle.stop();
|
|
1012
|
+
this.backdrop.remove();
|
|
1013
|
+
this.positioner.remove();
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
697
1016
|
|
|
698
|
-
export { Dialog, KeymapManager, Menu, Tooltip, applyPlotOverlayTokens, applyThemeTokens, dialogController, ensureUIHost, isEditableTarget, menuController, nextUid, runMachine, tooltipController };
|
|
1017
|
+
export { Dialog, Drawer, KeymapManager, Menu, Tooltip, applyPlotOverlayTokens, applyThemeTokens, dialogController, drawerController, ensureUIHost, isEditableTarget, menuController, nextUid, runMachine, tooltipController };
|