@luxalgo/vela 0.5.0 → 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-Dzd-Erlk.d.ts → DataProvider-DTOEXKTI.d.ts} +1 -1
- package/dist/{DataProvider-DKNDHpNv.d.cts → DataProvider-gnzv-dgc.d.cts} +1 -1
- package/dist/{chunk-KM6LHB3Y.js → chunk-ATPU5CI6.js} +6 -1
- package/dist/{chunk-7UFX5ZIG.js → chunk-B5TIKQJ5.js} +65 -7
- package/dist/{chunk-GYD2THPV.js → chunk-LJLZR44Y.js} +9 -2
- package/dist/{chunk-RHIDOUFL.js → chunk-P556H6EA.js} +329 -10
- package/dist/{chunk-KCCZNKH7.js → chunk-RRFGWZNJ.js} +1915 -367
- package/dist/{chunk-OVQKKXLZ.js → chunk-Y7TK4ZWK.js} +1810 -153
- package/dist/{contributions-o1GRKPI_.d.cts → contributions-Ci_i3IN2.d.cts} +77 -31
- package/dist/{contributions-hX3EUyjG.d.ts → contributions-b6AVrqqG.d.ts} +77 -31
- package/dist/{history-LJkz4-sS.d.cts → history-Cf1lVsap.d.cts} +22 -5
- package/dist/{history-Dzxz-MQj.d.ts → history-Ci3M0xLV.d.ts} +22 -5
- package/dist/index.cjs +2010 -397
- package/dist/index.d.cts +83 -25
- package/dist/index.d.ts +83 -25
- package/dist/index.js +4 -4
- package/dist/{options-Q-576hIi.d.cts → options-XTBpbx0s.d.cts} +61 -1
- package/dist/{options-Q-576hIi.d.ts → options-XTBpbx0s.d.ts} +61 -1
- package/dist/{plugin-aGUD1epn.d.ts → plugin-CnNd7cuF.d.ts} +305 -8
- package/dist/{plugin-D94muTV-.d.cts → plugin-DfH4Y-Om.d.cts} +305 -8
- package/dist/plugin.cjs +58 -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 +342 -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 +2010 -397
- package/dist/vela.global.min.js +115 -34
- package/dist/widget.cjs +5987 -2242
- package/dist/widget.d.cts +89 -17
- package/dist/widget.d.ts +89 -17
- package/dist/widget.js +169 -18
- package/dist/workspace.cjs +5627 -1587
- package/dist/workspace.d.cts +106 -12
- package/dist/workspace.d.ts +106 -12
- package/dist/workspace.js +494 -52
- package/package.json +1 -1
package/dist/ui.cjs
CHANGED
|
@@ -141,6 +141,11 @@ function themeTokens(t) {
|
|
|
141
141
|
"--vela-hover-strong": wash(0.16),
|
|
142
142
|
"--vela-focus": withAlpha(t.textColor, 0.5),
|
|
143
143
|
"--vela-focus-soft": withAlpha(t.textColor, 0.12),
|
|
144
|
+
// Separator hover — the SAME wash the chart's pane separators paint on hover
|
|
145
|
+
// (soft full-thickness band + solid 2px center line), so DOM-drawn dividers
|
|
146
|
+
// (the workspace grid) and canvas-drawn ones read as one family.
|
|
147
|
+
"--vela-separator-hover-band": withAlpha(t.textColor, 0.1),
|
|
148
|
+
"--vela-separator-hover-line": withAlpha(t.textColor, 0.55),
|
|
144
149
|
"--vela-scroll": withAlpha(t.textColor, 0.3),
|
|
145
150
|
"--vela-accent": ACCENT,
|
|
146
151
|
"--vela-accent-bright": ACCENT_BRIGHT,
|
|
@@ -259,14 +264,15 @@ registerIcon(
|
|
|
259
264
|
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"/>')
|
|
260
265
|
);
|
|
261
266
|
registerIcon("chevron-right", S('<path d="m6 3.5 4.5 4.5L6 12.5"/>'));
|
|
267
|
+
registerIcon("chevron-left", S('<path d="M10 3.5 5.5 8l4.5 4.5"/>'));
|
|
262
268
|
registerIcon("chevron-down", S('<path d="M3.5 6 8 10.5 12.5 6"/>'));
|
|
263
269
|
registerIcon("chevron-up", S('<path d="M3.5 10 8 5.5 12.5 10"/>'));
|
|
264
270
|
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"/>'));
|
|
265
271
|
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"/>'));
|
|
266
272
|
registerIcon("check", S('<path d="m2.8 8.4 3.4 3.4 7-7.6"/>'));
|
|
267
273
|
registerIcon("close", S('<path d="m3.8 3.8 8.4 8.4M12.2 3.8l-8.4 8.4"/>'));
|
|
268
|
-
registerIcon("eye", S('<path d="M1.5 8s2.5-4
|
|
269
|
-
registerIcon("eye-off", S('<path d="M1.5 8s2.5-4
|
|
274
|
+
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"/>'));
|
|
275
|
+
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"/>'));
|
|
270
276
|
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"/>'));
|
|
271
277
|
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"/>'));
|
|
272
278
|
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"/>'));
|
|
@@ -289,6 +295,7 @@ registerIcon("star", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l
|
|
|
289
295
|
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"'));
|
|
290
296
|
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"'));
|
|
291
297
|
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"'));
|
|
298
|
+
registerIcon("burger", S('<path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/>'));
|
|
292
299
|
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"/>'));
|
|
293
300
|
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"/>'));
|
|
294
301
|
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"/>'));
|
|
@@ -309,6 +316,11 @@ registerIcon(
|
|
|
309
316
|
"pen-lock",
|
|
310
317
|
svg24('<path d="M13.8 4.2a2.1 2.1 0 0 1 3 3L8.5 15.5l-3.5 1 1-3.5Z"/><rect x="13" y="14.5" width="8" height="6" rx="1.2"/><path d="M15 14.5v-1.6a2 2 0 0 1 4 0v1.6"/>')
|
|
311
318
|
);
|
|
319
|
+
registerIcon(
|
|
320
|
+
"pen-sync",
|
|
321
|
+
// Pen + two stacked panes — drawing onto every linked chart at once.
|
|
322
|
+
svg24('<path d="M13.8 4.2a2.1 2.1 0 0 1 3 3L8.5 15.5l-3.5 1 1-3.5Z"/><rect x="12.5" y="13.5" width="6" height="4.8" rx="1"/><path d="M15.5 18.3v1a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1v-2.8a1 1 0 0 0-1-1h-1"/>')
|
|
323
|
+
);
|
|
312
324
|
registerIcon("brush", svg24('<path d="M9.5 12 17 4.5a2.12 2.12 0 0 1 3 3L12.5 15"/><path d="M7 14a3 3 0 0 0-3 3c0 1.3-1.2 1.5-1.5 2 .8.9 2 1.5 3.5 1.5a3.5 3.5 0 0 0 3.5-3.5 3 3 0 0 0-2.5-3Z"/>'));
|
|
313
325
|
registerIcon(
|
|
314
326
|
"bucket",
|
|
@@ -339,8 +351,8 @@ function iconEl(id, doc = document) {
|
|
|
339
351
|
if (svg) span.innerHTML = svg;
|
|
340
352
|
return span;
|
|
341
353
|
}
|
|
342
|
-
function runMachine(
|
|
343
|
-
const m = new vanilla.VanillaMachine(
|
|
354
|
+
function runMachine(machine5, props, render) {
|
|
355
|
+
const m = new vanilla.VanillaMachine(machine5, props);
|
|
344
356
|
const unsub = m.subscribe(render);
|
|
345
357
|
m.start();
|
|
346
358
|
render(m.service);
|
|
@@ -628,10 +640,13 @@ var MENU_CSS = `
|
|
|
628
640
|
.vela-menu-item .vela-icon { width: 16px; height: 16px; font-size: 16px; justify-content: center; color: var(--vela-fg-muted); }
|
|
629
641
|
.vela-menu-item .vela-menu-label { flex: 1 1 auto; }
|
|
630
642
|
.vela-menu-item .vela-menu-hint { color: var(--vela-fg-faint); font-size: var(--vela-font-size-sm); }
|
|
631
|
-
/* Active entry:
|
|
632
|
-
Declared after
|
|
633
|
-
|
|
634
|
-
.vela-menu-item[data-checked]
|
|
643
|
+
/* Active entry: the row surface carries the selection \u2014 a stronger background wash
|
|
644
|
+
than the hover one, plus bright ink. Declared after the hover rule so a selected
|
|
645
|
+
row stays visibly selected while highlighted. */
|
|
646
|
+
.vela-menu-item[data-checked] {
|
|
647
|
+
background: var(--vela-hover-strong);
|
|
648
|
+
color: var(--vela-fg-bright);
|
|
649
|
+
}
|
|
635
650
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
636
651
|
same control language as the settings dialog's toggles. */
|
|
637
652
|
.vela-menu-switch {
|
|
@@ -849,6 +864,7 @@ function dialogController(opts = {}) {
|
|
|
849
864
|
modal: opts.modal ?? true,
|
|
850
865
|
closeOnEscape: opts.closeOnEscape ?? true,
|
|
851
866
|
closeOnInteractOutside: opts.closeOnInteractOutside ?? false,
|
|
867
|
+
initialFocusEl: opts.initialFocusEl,
|
|
852
868
|
onOpenChange: (d) => opts.onOpenChange?.(d.open)
|
|
853
869
|
},
|
|
854
870
|
connect: (service) => dialog__namespace.connect(service, vanilla.normalizeProps)
|
|
@@ -925,6 +941,29 @@ var DIALOG_CSS = `
|
|
|
925
941
|
border: 2px solid transparent;
|
|
926
942
|
background-clip: padding-box;
|
|
927
943
|
}
|
|
944
|
+
/* \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
|
|
945
|
+
Inside a shell in the mobile size class ([data-layout='mobile'] on the widget root,
|
|
946
|
+
which is position:relative) every kit dialog presents fullscreen: desktop cards are
|
|
947
|
+
unusable at phone widths, and the shell's bounds \u2014 not the viewport \u2014 are the honest
|
|
948
|
+
"screen" for an embedded chart. */
|
|
949
|
+
[data-layout='mobile'] .vela-dialog-backdrop { position: absolute; }
|
|
950
|
+
[data-layout='mobile'] .vela-dialog-positioner {
|
|
951
|
+
position: absolute;
|
|
952
|
+
padding: 0;
|
|
953
|
+
align-items: stretch;
|
|
954
|
+
}
|
|
955
|
+
[data-layout='mobile'] .vela-dialog {
|
|
956
|
+
width: 100%;
|
|
957
|
+
min-width: 0;
|
|
958
|
+
max-width: none;
|
|
959
|
+
max-height: none;
|
|
960
|
+
flex: 1 1 auto;
|
|
961
|
+
border: none;
|
|
962
|
+
border-radius: 0;
|
|
963
|
+
transform: none !important; /* a desktop drag offset must not survive the flip */
|
|
964
|
+
}
|
|
965
|
+
[data-layout='mobile'] .vela-dialog-close { width: 40px; height: 40px; }
|
|
966
|
+
[data-layout='mobile'] .vela-dialog-body { padding-bottom: calc(var(--vela-space-4) + env(safe-area-inset-bottom, 0px)); }
|
|
928
967
|
`;
|
|
929
968
|
var Dialog = class {
|
|
930
969
|
constructor(opts = {}) {
|
|
@@ -938,6 +977,7 @@ var Dialog = class {
|
|
|
938
977
|
this.positioner.className = "vela-dialog-positioner vela-ui-layer";
|
|
939
978
|
this.panel = doc.createElement("div");
|
|
940
979
|
this.panel.className = "vela-dialog";
|
|
980
|
+
this.panel.tabIndex = -1;
|
|
941
981
|
const header = doc.createElement("div");
|
|
942
982
|
header.className = "vela-dialog-header";
|
|
943
983
|
if (opts.draggable) {
|
|
@@ -972,7 +1012,18 @@ var Dialog = class {
|
|
|
972
1012
|
this.panel.append(header, this.body);
|
|
973
1013
|
this.positioner.appendChild(this.panel);
|
|
974
1014
|
host.append(this.backdrop, this.positioner);
|
|
975
|
-
this.ctrl = dialogController(
|
|
1015
|
+
this.ctrl = dialogController({
|
|
1016
|
+
...opts,
|
|
1017
|
+
// Mobile chrome (fullscreen presentation): opening must never land focus on
|
|
1018
|
+
// an input — that pops the on-screen keyboard over the just-opened dialog.
|
|
1019
|
+
// Focus goes to the panel; a caller that WANTS the keyboard focuses its
|
|
1020
|
+
// input explicitly. Desktop keeps the caller's pick, else the machine's
|
|
1021
|
+
// first-tabbable default.
|
|
1022
|
+
initialFocusEl: () => {
|
|
1023
|
+
if (this.positioner.closest('[data-layout="mobile"]')) return this.panel;
|
|
1024
|
+
return opts.initialFocusEl?.() ?? null;
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
976
1027
|
const mid = String(this.ctrl.props.id);
|
|
977
1028
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
978
1029
|
const api = this.ctrl.connect(service);
|
|
@@ -1000,6 +1051,286 @@ var Dialog = class {
|
|
|
1000
1051
|
this.positioner.remove();
|
|
1001
1052
|
}
|
|
1002
1053
|
};
|
|
1054
|
+
function drawerController(opts = {}) {
|
|
1055
|
+
return {
|
|
1056
|
+
machine: dialog__namespace.machine,
|
|
1057
|
+
props: {
|
|
1058
|
+
id: nextUid("vela-drawer"),
|
|
1059
|
+
modal: true,
|
|
1060
|
+
closeOnEscape: opts.closeOnEscape ?? true,
|
|
1061
|
+
closeOnInteractOutside: opts.closeOnInteractOutside ?? true,
|
|
1062
|
+
initialFocusEl: opts.initialFocusEl,
|
|
1063
|
+
onOpenChange: (d) => opts.onOpenChange?.(d.open)
|
|
1064
|
+
},
|
|
1065
|
+
connect: (service) => dialog__namespace.connect(service, vanilla.normalizeProps)
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/ui/components/drawer/styles.ts
|
|
1070
|
+
var DRAWER_STYLE_ID = "vela-ui-drawer";
|
|
1071
|
+
var DRAWER_CSS = `
|
|
1072
|
+
.vela-drawer-backdrop {
|
|
1073
|
+
position: fixed;
|
|
1074
|
+
inset: 0;
|
|
1075
|
+
background: var(--vela-backdrop);
|
|
1076
|
+
z-index: var(--vela-z-dialog);
|
|
1077
|
+
}
|
|
1078
|
+
.vela-drawer-positioner {
|
|
1079
|
+
position: fixed;
|
|
1080
|
+
inset: 0;
|
|
1081
|
+
display: flex;
|
|
1082
|
+
align-items: flex-end;
|
|
1083
|
+
justify-content: center;
|
|
1084
|
+
z-index: var(--vela-z-dialog);
|
|
1085
|
+
}
|
|
1086
|
+
/* Inside a shell that declares a size class, the sheet scopes to the SHELL's bounds
|
|
1087
|
+
(the widget root is position:relative) instead of the whole viewport \u2014 an embedded
|
|
1088
|
+
chart must not curtain the host page. */
|
|
1089
|
+
[data-layout] .vela-drawer-backdrop, [data-layout] .vela-drawer-positioner { position: absolute; }
|
|
1090
|
+
.vela-drawer {
|
|
1091
|
+
background: var(--vela-surface);
|
|
1092
|
+
color: var(--vela-fg);
|
|
1093
|
+
border: 1px solid var(--vela-border-strong);
|
|
1094
|
+
border-bottom: none;
|
|
1095
|
+
border-radius: 14px 14px 0 0;
|
|
1096
|
+
box-shadow: var(--vela-shadow-dialog);
|
|
1097
|
+
font-size: 13px;
|
|
1098
|
+
width: 100%;
|
|
1099
|
+
max-height: 85%;
|
|
1100
|
+
display: flex;
|
|
1101
|
+
flex-direction: column;
|
|
1102
|
+
overflow: hidden;
|
|
1103
|
+
outline: none;
|
|
1104
|
+
}
|
|
1105
|
+
.vela-drawer[data-state='open'] { animation: vela-drawer-in var(--vela-dur-med) var(--vela-ease); }
|
|
1106
|
+
@keyframes vela-drawer-in {
|
|
1107
|
+
from { transform: translateY(100%); }
|
|
1108
|
+
to { transform: translateY(0); }
|
|
1109
|
+
}
|
|
1110
|
+
/* The grab zone owns its touches (drag-to-dismiss), so the browser must not scroll it. */
|
|
1111
|
+
.vela-drawer-grab {
|
|
1112
|
+
flex: none;
|
|
1113
|
+
display: flex;
|
|
1114
|
+
align-items: center;
|
|
1115
|
+
justify-content: center;
|
|
1116
|
+
padding: 10px 0 6px;
|
|
1117
|
+
cursor: grab;
|
|
1118
|
+
touch-action: none;
|
|
1119
|
+
user-select: none;
|
|
1120
|
+
}
|
|
1121
|
+
.vela-drawer-grab::before {
|
|
1122
|
+
content: '';
|
|
1123
|
+
width: 36px;
|
|
1124
|
+
height: 4px;
|
|
1125
|
+
border-radius: 2px;
|
|
1126
|
+
background: var(--vela-border-strong);
|
|
1127
|
+
}
|
|
1128
|
+
.vela-drawer-title {
|
|
1129
|
+
flex: none;
|
|
1130
|
+
padding: 0 16px 10px;
|
|
1131
|
+
font-size: 15px;
|
|
1132
|
+
font-weight: 600;
|
|
1133
|
+
letter-spacing: 0.2px;
|
|
1134
|
+
color: var(--vela-fg-bright);
|
|
1135
|
+
user-select: none;
|
|
1136
|
+
}
|
|
1137
|
+
.vela-drawer-title:empty { display: none; }
|
|
1138
|
+
.vela-drawer-body {
|
|
1139
|
+
flex: 1 1 auto;
|
|
1140
|
+
min-height: 0;
|
|
1141
|
+
overflow-y: auto;
|
|
1142
|
+
overscroll-behavior: contain;
|
|
1143
|
+
-webkit-overflow-scrolling: touch;
|
|
1144
|
+
/* Vertical pans stay native scrolling; horizontal moves reach the sheet's gesture
|
|
1145
|
+
recognizer as pointer events (tab swipes). Without this the browser claims a
|
|
1146
|
+
sideways touch as a scroll attempt and CANCELS the pointer stream, so swipes
|
|
1147
|
+
never registered on real touch devices. Sideways-scrolling strips inside the
|
|
1148
|
+
body opt back in with their own touch-action: pan-x. */
|
|
1149
|
+
touch-action: pan-y;
|
|
1150
|
+
padding: 0 var(--vela-space-3) calc(var(--vela-space-3) + env(safe-area-inset-bottom, 0px));
|
|
1151
|
+
}
|
|
1152
|
+
.vela-drawer-body::-webkit-scrollbar { width: 8px; }
|
|
1153
|
+
.vela-drawer-body::-webkit-scrollbar-thumb {
|
|
1154
|
+
background: var(--vela-scroll);
|
|
1155
|
+
border-radius: 4px;
|
|
1156
|
+
border: 2px solid transparent;
|
|
1157
|
+
background-clip: padding-box;
|
|
1158
|
+
}
|
|
1159
|
+
`;
|
|
1160
|
+
var DISMISS_FRACTION = 0.33;
|
|
1161
|
+
var DISMISS_PX = 96;
|
|
1162
|
+
var SLOP_PX = 8;
|
|
1163
|
+
var HSWIPE_MIN_PX = 48;
|
|
1164
|
+
function classifyGesture(dx, dy, ctx) {
|
|
1165
|
+
if (Math.max(Math.abs(dx), Math.abs(dy)) < SLOP_PX) return "pending";
|
|
1166
|
+
if (Math.abs(dy) > Math.abs(dx)) return dy > 0 && !ctx.scrolled ? "drag" : "scroll";
|
|
1167
|
+
return ctx.canSwipe && !ctx.hScrollable ? "hswipe" : "scroll";
|
|
1168
|
+
}
|
|
1169
|
+
function dragDismisses(dy, panelHeightPx) {
|
|
1170
|
+
return dy >= Math.min(DISMISS_PX, panelHeightPx * DISMISS_FRACTION);
|
|
1171
|
+
}
|
|
1172
|
+
function swipeDirection(dx, dy) {
|
|
1173
|
+
if (Math.abs(dx) < HSWIPE_MIN_PX || Math.abs(dx) <= Math.abs(dy)) return null;
|
|
1174
|
+
return dx < 0 ? "left" : "right";
|
|
1175
|
+
}
|
|
1176
|
+
var Drawer = class {
|
|
1177
|
+
constructor(opts = {}) {
|
|
1178
|
+
const doc = (opts.host ?? document.body).ownerDocument;
|
|
1179
|
+
injectStyles(DRAWER_STYLE_ID, DRAWER_CSS, doc);
|
|
1180
|
+
const host = opts.host ?? doc.body;
|
|
1181
|
+
this.backdrop = doc.createElement("div");
|
|
1182
|
+
this.backdrop.className = "vela-drawer-backdrop vela-ui-layer";
|
|
1183
|
+
this.positioner = doc.createElement("div");
|
|
1184
|
+
this.positioner.className = "vela-drawer-positioner vela-ui-layer";
|
|
1185
|
+
this.panel = doc.createElement("div");
|
|
1186
|
+
this.panel.className = "vela-drawer";
|
|
1187
|
+
this.panel.tabIndex = -1;
|
|
1188
|
+
const grab = doc.createElement("div");
|
|
1189
|
+
grab.className = "vela-drawer-grab";
|
|
1190
|
+
this.titleEl = doc.createElement("div");
|
|
1191
|
+
this.titleEl.className = "vela-drawer-title";
|
|
1192
|
+
this.titleEl.textContent = opts.title ?? "";
|
|
1193
|
+
this.body = doc.createElement("div");
|
|
1194
|
+
this.body.className = "vela-drawer-body";
|
|
1195
|
+
if (opts.content instanceof Node) this.body.appendChild(opts.content);
|
|
1196
|
+
else if (typeof opts.content === "function") opts.content(this.body);
|
|
1197
|
+
this.panel.append(grab, this.titleEl, this.body);
|
|
1198
|
+
this.positioner.appendChild(this.panel);
|
|
1199
|
+
host.append(this.backdrop, this.positioner);
|
|
1200
|
+
this.wireGestures(grab, opts.onSwipe);
|
|
1201
|
+
this.ctrl = drawerController({ ...opts, initialFocusEl: () => this.panel });
|
|
1202
|
+
const mid = String(this.ctrl.props.id);
|
|
1203
|
+
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
1204
|
+
const api = this.ctrl.connect(service);
|
|
1205
|
+
vanilla.spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
1206
|
+
vanilla.spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
1207
|
+
vanilla.spreadProps(this.panel, api.getContentProps(), mid);
|
|
1208
|
+
vanilla.spreadProps(this.titleEl, api.getTitleProps(), mid);
|
|
1209
|
+
this.backdrop.style.display = api.open ? "" : "none";
|
|
1210
|
+
this.positioner.style.display = api.open ? "" : "none";
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
/** Any element between `from` and the panel that has already been scrolled down —
|
|
1214
|
+
* a downward pull there must scroll it back up, never drag the sheet. */
|
|
1215
|
+
scrolledAncestor(from) {
|
|
1216
|
+
let el = from instanceof Element ? from : null;
|
|
1217
|
+
while (el && el !== this.panel) {
|
|
1218
|
+
if (el.scrollTop > 0) return true;
|
|
1219
|
+
el = el.parentElement;
|
|
1220
|
+
}
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
/** Any element between `from` and the panel that scrolls horizontally on its own
|
|
1224
|
+
* (the tab strip, chip rows) — a sideways move there is ITS scroll, not a swipe. */
|
|
1225
|
+
hScrollableAncestor(from) {
|
|
1226
|
+
let el = from instanceof Element ? from : null;
|
|
1227
|
+
while (el && el !== this.panel) {
|
|
1228
|
+
if (el.scrollWidth > el.clientWidth + 1) return true;
|
|
1229
|
+
el = el.parentElement;
|
|
1230
|
+
}
|
|
1231
|
+
return false;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* One gesture recognizer for the whole sheet. A downward pull dismisses from
|
|
1235
|
+
* anywhere — the grab handle immediately, the content once it is decidedly vertical
|
|
1236
|
+
* and its scroller is at rest (a scrolled list keeps native scrolling). A decidedly
|
|
1237
|
+
* horizontal move becomes an `onSwipe` (tabbed drawers flip pages with it). The
|
|
1238
|
+
* non-passive touchmove hook is what keeps the browser from claiming the pull as a
|
|
1239
|
+
* scroll once the sheet is (or may become) the drag target.
|
|
1240
|
+
*/
|
|
1241
|
+
wireGestures(grab, onSwipe) {
|
|
1242
|
+
let startX = 0;
|
|
1243
|
+
let startY = 0;
|
|
1244
|
+
let dx = 0;
|
|
1245
|
+
let dy = 0;
|
|
1246
|
+
let mode = "idle";
|
|
1247
|
+
const beginDrag = (e) => {
|
|
1248
|
+
mode = "drag";
|
|
1249
|
+
startY = e.clientY;
|
|
1250
|
+
this.panel.style.transition = "none";
|
|
1251
|
+
try {
|
|
1252
|
+
this.panel.setPointerCapture(e.pointerId);
|
|
1253
|
+
} catch {
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
this.panel.addEventListener("pointerdown", (e) => {
|
|
1257
|
+
if (e.isPrimary === false) return;
|
|
1258
|
+
startX = e.clientX;
|
|
1259
|
+
startY = e.clientY;
|
|
1260
|
+
dx = 0;
|
|
1261
|
+
dy = 0;
|
|
1262
|
+
if (grab.contains(e.target)) beginDrag(e);
|
|
1263
|
+
else mode = "pending";
|
|
1264
|
+
});
|
|
1265
|
+
this.panel.addEventListener("pointermove", (e) => {
|
|
1266
|
+
if (mode === "idle" || mode === "scroll") return;
|
|
1267
|
+
dx = e.clientX - startX;
|
|
1268
|
+
dy = e.clientY - startY;
|
|
1269
|
+
if (mode === "pending") {
|
|
1270
|
+
const intent = classifyGesture(dx, dy, {
|
|
1271
|
+
canSwipe: !!onSwipe,
|
|
1272
|
+
scrolled: this.scrolledAncestor(e.target),
|
|
1273
|
+
hScrollable: this.hScrollableAncestor(e.target)
|
|
1274
|
+
});
|
|
1275
|
+
if (intent === "pending") return;
|
|
1276
|
+
if (intent === "drag") beginDrag(e);
|
|
1277
|
+
else mode = intent;
|
|
1278
|
+
}
|
|
1279
|
+
if (mode === "drag") {
|
|
1280
|
+
dy = Math.max(0, e.clientY - startY);
|
|
1281
|
+
this.panel.style.transform = dy > 0 ? `translateY(${dy}px)` : "";
|
|
1282
|
+
}
|
|
1283
|
+
});
|
|
1284
|
+
this.panel.addEventListener(
|
|
1285
|
+
"touchmove",
|
|
1286
|
+
(e) => {
|
|
1287
|
+
if (mode === "drag" || mode === "hswipe") {
|
|
1288
|
+
e.preventDefault();
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
if (mode !== "pending") return;
|
|
1292
|
+
const t = e.touches[0];
|
|
1293
|
+
if (!t) return;
|
|
1294
|
+
const mdx = t.clientX - startX;
|
|
1295
|
+
const mdy = t.clientY - startY;
|
|
1296
|
+
if (mdy > Math.abs(mdx) && !this.scrolledAncestor(e.target)) e.preventDefault();
|
|
1297
|
+
},
|
|
1298
|
+
{ passive: false }
|
|
1299
|
+
);
|
|
1300
|
+
const settle = () => {
|
|
1301
|
+
if (mode === "idle") return;
|
|
1302
|
+
const finished = mode;
|
|
1303
|
+
mode = "idle";
|
|
1304
|
+
if (finished === "drag") {
|
|
1305
|
+
this.panel.style.transition = "";
|
|
1306
|
+
this.panel.style.transform = "";
|
|
1307
|
+
if (dragDismisses(dy, this.panel.getBoundingClientRect().height)) this.hide();
|
|
1308
|
+
} else if (finished === "hswipe") {
|
|
1309
|
+
const dir = swipeDirection(dx, dy);
|
|
1310
|
+
if (dir) onSwipe?.(dir);
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
this.panel.addEventListener("pointerup", settle);
|
|
1314
|
+
this.panel.addEventListener("pointercancel", settle);
|
|
1315
|
+
}
|
|
1316
|
+
setTitle(title) {
|
|
1317
|
+
this.titleEl.textContent = title;
|
|
1318
|
+
}
|
|
1319
|
+
get open() {
|
|
1320
|
+
return this.ctrl.connect(this.handle.service).open;
|
|
1321
|
+
}
|
|
1322
|
+
show() {
|
|
1323
|
+
this.ctrl.connect(this.handle.service).setOpen(true);
|
|
1324
|
+
}
|
|
1325
|
+
hide() {
|
|
1326
|
+
this.ctrl.connect(this.handle.service).setOpen(false);
|
|
1327
|
+
}
|
|
1328
|
+
destroy() {
|
|
1329
|
+
this.handle.stop();
|
|
1330
|
+
this.backdrop.remove();
|
|
1331
|
+
this.positioner.remove();
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1003
1334
|
|
|
1004
1335
|
Object.defineProperty(exports, "normalizeProps", {
|
|
1005
1336
|
enumerable: true,
|
|
@@ -1010,12 +1341,14 @@ Object.defineProperty(exports, "spreadProps", {
|
|
|
1010
1341
|
get: function () { return vanilla.spreadProps; }
|
|
1011
1342
|
});
|
|
1012
1343
|
exports.Dialog = Dialog;
|
|
1344
|
+
exports.Drawer = Drawer;
|
|
1013
1345
|
exports.KeymapManager = KeymapManager;
|
|
1014
1346
|
exports.Menu = Menu;
|
|
1015
1347
|
exports.Tooltip = Tooltip;
|
|
1016
1348
|
exports.applyPlotOverlayTokens = applyPlotOverlayTokens;
|
|
1017
1349
|
exports.applyThemeTokens = applyThemeTokens;
|
|
1018
1350
|
exports.dialogController = dialogController;
|
|
1351
|
+
exports.drawerController = drawerController;
|
|
1019
1352
|
exports.ensureUIHost = ensureUIHost;
|
|
1020
1353
|
exports.iconEl = iconEl;
|
|
1021
1354
|
exports.iconMarkup = iconMarkup;
|
package/dist/ui.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as VelaTheme } from './options-
|
|
1
|
+
import { c as VelaTheme } from './options-XTBpbx0s.cjs';
|
|
2
2
|
import { MachineSchema, Service, Machine } from '@zag-js/core';
|
|
3
3
|
export { a as KeyBindingDescriptor, K as KeymapManager, b as KeymapOptions, R as ResolvedBinding } from './keymap-CGOz5F5f.cjs';
|
|
4
4
|
import * as tooltip from '@zag-js/tooltip';
|
|
@@ -148,6 +148,8 @@ interface DialogControllerOptions {
|
|
|
148
148
|
modal?: boolean;
|
|
149
149
|
closeOnEscape?: boolean;
|
|
150
150
|
closeOnInteractOutside?: boolean;
|
|
151
|
+
/** Element to focus on open (default: the machine's first-tabbable pick). */
|
|
152
|
+
initialFocusEl?: () => HTMLElement | null;
|
|
151
153
|
onOpenChange?: (open: boolean) => void;
|
|
152
154
|
}
|
|
153
155
|
type DialogService = dialog.Service;
|
|
@@ -185,4 +187,64 @@ declare class Dialog {
|
|
|
185
187
|
destroy(): void;
|
|
186
188
|
}
|
|
187
189
|
|
|
188
|
-
|
|
190
|
+
interface DrawerControllerOptions {
|
|
191
|
+
closeOnEscape?: boolean;
|
|
192
|
+
/** Tap outside (on the backdrop) dismisses — default true. */
|
|
193
|
+
closeOnInteractOutside?: boolean;
|
|
194
|
+
/** Element to focus on open (default: the machine's first-tabbable pick). The view
|
|
195
|
+
* points this at the sheet itself so opening never focuses an input — on touch
|
|
196
|
+
* devices that would pop the on-screen keyboard over the sheet. */
|
|
197
|
+
initialFocusEl?: () => HTMLElement | null;
|
|
198
|
+
onOpenChange?: (open: boolean) => void;
|
|
199
|
+
}
|
|
200
|
+
type DrawerService = dialog.Service;
|
|
201
|
+
type DrawerApi = dialog.Api;
|
|
202
|
+
interface DrawerController {
|
|
203
|
+
machine: typeof dialog.machine;
|
|
204
|
+
props: Partial<dialog.Props>;
|
|
205
|
+
connect(service: DrawerService): DrawerApi;
|
|
206
|
+
}
|
|
207
|
+
declare function drawerController(opts?: DrawerControllerOptions): DrawerController;
|
|
208
|
+
|
|
209
|
+
interface DrawerOptions extends DrawerControllerOptions {
|
|
210
|
+
title?: string;
|
|
211
|
+
content?: Node | ((body: HTMLElement) => void);
|
|
212
|
+
host?: HTMLElement;
|
|
213
|
+
/** Horizontal swipe across the sheet (fires on release; `'left'` = the finger moved
|
|
214
|
+
* left). Gestures that start inside a horizontally scrollable strip (tabs, chip
|
|
215
|
+
* rows) keep their native scroll instead. */
|
|
216
|
+
onSwipe?: (dir: 'left' | 'right') => void;
|
|
217
|
+
}
|
|
218
|
+
declare class Drawer {
|
|
219
|
+
/** Caller-owned content area — append your rows/lists here. */
|
|
220
|
+
readonly body: HTMLElement;
|
|
221
|
+
private readonly backdrop;
|
|
222
|
+
private readonly positioner;
|
|
223
|
+
private readonly panel;
|
|
224
|
+
private readonly titleEl;
|
|
225
|
+
private readonly handle;
|
|
226
|
+
private readonly ctrl;
|
|
227
|
+
constructor(opts?: DrawerOptions);
|
|
228
|
+
/** Any element between `from` and the panel that has already been scrolled down —
|
|
229
|
+
* a downward pull there must scroll it back up, never drag the sheet. */
|
|
230
|
+
private scrolledAncestor;
|
|
231
|
+
/** Any element between `from` and the panel that scrolls horizontally on its own
|
|
232
|
+
* (the tab strip, chip rows) — a sideways move there is ITS scroll, not a swipe. */
|
|
233
|
+
private hScrollableAncestor;
|
|
234
|
+
/**
|
|
235
|
+
* One gesture recognizer for the whole sheet. A downward pull dismisses from
|
|
236
|
+
* anywhere — the grab handle immediately, the content once it is decidedly vertical
|
|
237
|
+
* and its scroller is at rest (a scrolled list keeps native scrolling). A decidedly
|
|
238
|
+
* horizontal move becomes an `onSwipe` (tabbed drawers flip pages with it). The
|
|
239
|
+
* non-passive touchmove hook is what keeps the browser from claiming the pull as a
|
|
240
|
+
* scroll once the sheet is (or may become) the drag target.
|
|
241
|
+
*/
|
|
242
|
+
private wireGestures;
|
|
243
|
+
setTitle(title: string): void;
|
|
244
|
+
get open(): boolean;
|
|
245
|
+
show(): void;
|
|
246
|
+
hide(): void;
|
|
247
|
+
destroy(): void;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { Dialog, type DialogApi, type DialogControllerOptions, type DialogOptions, type DialogService, Drawer, type DrawerApi, type DrawerControllerOptions, type DrawerOptions, type DrawerService, type MachineHandle, Menu, type MenuApi, type MenuControllerOptions, type MenuItemDescriptor, type MenuOptions, type MenuService, Tooltip, type TooltipApi, type TooltipControllerOptions, type TooltipOptions, type TooltipService, applyPlotOverlayTokens, applyThemeTokens, dialogController, drawerController, ensureUIHost, iconEl, injectStyles, menuController, nextUid, runMachine, tooltipController, withAlpha };
|
package/dist/ui.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as VelaTheme } from './options-
|
|
1
|
+
import { c as VelaTheme } from './options-XTBpbx0s.js';
|
|
2
2
|
import { MachineSchema, Service, Machine } from '@zag-js/core';
|
|
3
3
|
export { a as KeyBindingDescriptor, K as KeymapManager, b as KeymapOptions, R as ResolvedBinding } from './keymap-CGOz5F5f.js';
|
|
4
4
|
import * as tooltip from '@zag-js/tooltip';
|
|
@@ -148,6 +148,8 @@ interface DialogControllerOptions {
|
|
|
148
148
|
modal?: boolean;
|
|
149
149
|
closeOnEscape?: boolean;
|
|
150
150
|
closeOnInteractOutside?: boolean;
|
|
151
|
+
/** Element to focus on open (default: the machine's first-tabbable pick). */
|
|
152
|
+
initialFocusEl?: () => HTMLElement | null;
|
|
151
153
|
onOpenChange?: (open: boolean) => void;
|
|
152
154
|
}
|
|
153
155
|
type DialogService = dialog.Service;
|
|
@@ -185,4 +187,64 @@ declare class Dialog {
|
|
|
185
187
|
destroy(): void;
|
|
186
188
|
}
|
|
187
189
|
|
|
188
|
-
|
|
190
|
+
interface DrawerControllerOptions {
|
|
191
|
+
closeOnEscape?: boolean;
|
|
192
|
+
/** Tap outside (on the backdrop) dismisses — default true. */
|
|
193
|
+
closeOnInteractOutside?: boolean;
|
|
194
|
+
/** Element to focus on open (default: the machine's first-tabbable pick). The view
|
|
195
|
+
* points this at the sheet itself so opening never focuses an input — on touch
|
|
196
|
+
* devices that would pop the on-screen keyboard over the sheet. */
|
|
197
|
+
initialFocusEl?: () => HTMLElement | null;
|
|
198
|
+
onOpenChange?: (open: boolean) => void;
|
|
199
|
+
}
|
|
200
|
+
type DrawerService = dialog.Service;
|
|
201
|
+
type DrawerApi = dialog.Api;
|
|
202
|
+
interface DrawerController {
|
|
203
|
+
machine: typeof dialog.machine;
|
|
204
|
+
props: Partial<dialog.Props>;
|
|
205
|
+
connect(service: DrawerService): DrawerApi;
|
|
206
|
+
}
|
|
207
|
+
declare function drawerController(opts?: DrawerControllerOptions): DrawerController;
|
|
208
|
+
|
|
209
|
+
interface DrawerOptions extends DrawerControllerOptions {
|
|
210
|
+
title?: string;
|
|
211
|
+
content?: Node | ((body: HTMLElement) => void);
|
|
212
|
+
host?: HTMLElement;
|
|
213
|
+
/** Horizontal swipe across the sheet (fires on release; `'left'` = the finger moved
|
|
214
|
+
* left). Gestures that start inside a horizontally scrollable strip (tabs, chip
|
|
215
|
+
* rows) keep their native scroll instead. */
|
|
216
|
+
onSwipe?: (dir: 'left' | 'right') => void;
|
|
217
|
+
}
|
|
218
|
+
declare class Drawer {
|
|
219
|
+
/** Caller-owned content area — append your rows/lists here. */
|
|
220
|
+
readonly body: HTMLElement;
|
|
221
|
+
private readonly backdrop;
|
|
222
|
+
private readonly positioner;
|
|
223
|
+
private readonly panel;
|
|
224
|
+
private readonly titleEl;
|
|
225
|
+
private readonly handle;
|
|
226
|
+
private readonly ctrl;
|
|
227
|
+
constructor(opts?: DrawerOptions);
|
|
228
|
+
/** Any element between `from` and the panel that has already been scrolled down —
|
|
229
|
+
* a downward pull there must scroll it back up, never drag the sheet. */
|
|
230
|
+
private scrolledAncestor;
|
|
231
|
+
/** Any element between `from` and the panel that scrolls horizontally on its own
|
|
232
|
+
* (the tab strip, chip rows) — a sideways move there is ITS scroll, not a swipe. */
|
|
233
|
+
private hScrollableAncestor;
|
|
234
|
+
/**
|
|
235
|
+
* One gesture recognizer for the whole sheet. A downward pull dismisses from
|
|
236
|
+
* anywhere — the grab handle immediately, the content once it is decidedly vertical
|
|
237
|
+
* and its scroller is at rest (a scrolled list keeps native scrolling). A decidedly
|
|
238
|
+
* horizontal move becomes an `onSwipe` (tabbed drawers flip pages with it). The
|
|
239
|
+
* non-passive touchmove hook is what keeps the browser from claiming the pull as a
|
|
240
|
+
* scroll once the sheet is (or may become) the drag target.
|
|
241
|
+
*/
|
|
242
|
+
private wireGestures;
|
|
243
|
+
setTitle(title: string): void;
|
|
244
|
+
get open(): boolean;
|
|
245
|
+
show(): void;
|
|
246
|
+
hide(): void;
|
|
247
|
+
destroy(): void;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { Dialog, type DialogApi, type DialogControllerOptions, type DialogOptions, type DialogService, Drawer, type DrawerApi, type DrawerControllerOptions, type DrawerOptions, type DrawerService, type MachineHandle, Menu, type MenuApi, type MenuControllerOptions, type MenuItemDescriptor, type MenuOptions, type MenuService, Tooltip, type TooltipApi, type TooltipControllerOptions, type TooltipOptions, type TooltipService, applyPlotOverlayTokens, applyThemeTokens, dialogController, drawerController, ensureUIHost, iconEl, injectStyles, menuController, nextUid, runMachine, tooltipController, withAlpha };
|
package/dist/ui.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Dialog, KeymapManager, Menu, Tooltip, applyPlotOverlayTokens, applyThemeTokens, dialogController, ensureUIHost, menuController, nextUid, normalizeProps, runMachine, spreadProps, tooltipController } from './chunk-
|
|
2
|
-
import './chunk-
|
|
3
|
-
export { iconEl, iconMarkup, injectStyles, registerIcon, svg16, svg24, withAlpha } from './chunk-
|
|
1
|
+
export { Dialog, Drawer, KeymapManager, Menu, Tooltip, applyPlotOverlayTokens, applyThemeTokens, dialogController, drawerController, ensureUIHost, menuController, nextUid, normalizeProps, runMachine, spreadProps, tooltipController } from './chunk-P556H6EA.js';
|
|
2
|
+
import './chunk-ATPU5CI6.js';
|
|
3
|
+
export { iconEl, iconMarkup, injectStyles, registerIcon, svg16, svg24, withAlpha } from './chunk-LJLZR44Y.js';
|