@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.
Files changed (45) hide show
  1. package/dist/{DataProvider-DGesjOJT.d.ts → DataProvider-DTOEXKTI.d.ts} +1 -1
  2. package/dist/{DataProvider-BBQ3Hr21.d.cts → DataProvider-gnzv-dgc.d.cts} +1 -1
  3. package/dist/{chunk-ULIX6AE2.js → chunk-ATPU5CI6.js} +1 -1
  4. package/dist/{chunk-S6F6SQ2L.js → chunk-B5TIKQJ5.js} +60 -7
  5. package/dist/{chunk-EP6NDJYK.js → chunk-LJLZR44Y.js} +4 -2
  6. package/dist/{chunk-AIW3EJUE.js → chunk-P556H6EA.js} +329 -10
  7. package/dist/{chunk-FNOMKHEZ.js → chunk-RRFGWZNJ.js} +1321 -363
  8. package/dist/{chunk-5SE4PL3H.js → chunk-Y7TK4ZWK.js} +1363 -48
  9. package/dist/{contributions-BlV3kb0O.d.cts → contributions-Ci_i3IN2.d.cts} +33 -2
  10. package/dist/{contributions-6N0rbrPC.d.ts → contributions-b6AVrqqG.d.ts} +33 -2
  11. package/dist/{history-DX3Zne3a.d.cts → history-Cf1lVsap.d.cts} +15 -3
  12. package/dist/{history-BTd52YMA.d.ts → history-Ci3M0xLV.d.ts} +15 -3
  13. package/dist/index.cjs +1374 -366
  14. package/dist/index.d.cts +52 -19
  15. package/dist/index.d.ts +52 -19
  16. package/dist/index.js +4 -4
  17. package/dist/{options-DbQholFV.d.cts → options-XTBpbx0s.d.cts} +47 -1
  18. package/dist/{options-DbQholFV.d.ts → options-XTBpbx0s.d.ts} +47 -1
  19. package/dist/{plugin-TN3n-9YN.d.ts → plugin-CnNd7cuF.d.ts} +172 -25
  20. package/dist/{plugin-O34MbXf-.d.cts → plugin-DfH4Y-Om.d.cts} +172 -25
  21. package/dist/plugin.cjs +53 -7
  22. package/dist/plugin.d.cts +4 -4
  23. package/dist/plugin.d.ts +4 -4
  24. package/dist/plugin.js +2 -2
  25. package/dist/providers/binance.d.cts +2 -2
  26. package/dist/providers/binance.d.ts +2 -2
  27. package/dist/providers/coinbase.d.cts +2 -2
  28. package/dist/providers/coinbase.d.ts +2 -2
  29. package/dist/providers/hyperliquid.d.cts +2 -2
  30. package/dist/providers/hyperliquid.d.ts +2 -2
  31. package/dist/ui.cjs +332 -9
  32. package/dist/ui.d.cts +64 -2
  33. package/dist/ui.d.ts +64 -2
  34. package/dist/ui.js +3 -3
  35. package/dist/vela.global.js +1374 -366
  36. package/dist/vela.global.min.js +84 -34
  37. package/dist/widget.cjs +3348 -550
  38. package/dist/widget.d.cts +65 -7
  39. package/dist/widget.d.ts +65 -7
  40. package/dist/widget.js +169 -18
  41. package/dist/workspace.cjs +4251 -1400
  42. package/dist/workspace.d.cts +45 -9
  43. package/dist/workspace.d.ts +45 -9
  44. package/dist/workspace.js +226 -22
  45. package/package.json +1 -1
package/dist/ui.cjs CHANGED
@@ -264,14 +264,15 @@ registerIcon(
264
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"/>')
265
265
  );
266
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"/>'));
267
268
  registerIcon("chevron-down", S('<path d="M3.5 6 8 10.5 12.5 6"/>'));
268
269
  registerIcon("chevron-up", S('<path d="M3.5 10 8 5.5 12.5 10"/>'));
269
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"/>'));
270
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"/>'));
271
272
  registerIcon("check", S('<path d="m2.8 8.4 3.4 3.4 7-7.6"/>'));
272
273
  registerIcon("close", S('<path d="m3.8 3.8 8.4 8.4M12.2 3.8l-8.4 8.4"/>'));
273
- registerIcon("eye", S('<path d="M1.5 8s2.5-4.2 6.5-4.2S14.5 8 14.5 8 12 12.2 8 12.2 1.5 8 1.5 8z"/><circle cx="8" cy="8" r="1.8"/>'));
274
- registerIcon("eye-off", S('<path d="M1.5 8s2.5-4.2 6.5-4.2S14.5 8 14.5 8 12 12.2 8 12.2 1.5 8 1.5 8z" opacity="0.45"/><path d="m3 13 10-10"/>'));
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"/>'));
275
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"/>'));
276
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"/>'));
277
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"/>'));
@@ -294,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
294
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"'));
295
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"'));
296
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"/>'));
297
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"/>'));
298
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"/>'));
299
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"/>'));
@@ -349,8 +351,8 @@ function iconEl(id, doc = document) {
349
351
  if (svg) span.innerHTML = svg;
350
352
  return span;
351
353
  }
352
- function runMachine(machine4, props, render) {
353
- const m = new vanilla.VanillaMachine(machine4, props);
354
+ function runMachine(machine5, props, render) {
355
+ const m = new vanilla.VanillaMachine(machine5, props);
354
356
  const unsub = m.subscribe(render);
355
357
  m.start();
356
358
  render(m.service);
@@ -638,10 +640,13 @@ var MENU_CSS = `
638
640
  .vela-menu-item .vela-icon { width: 16px; height: 16px; font-size: 16px; justify-content: center; color: var(--vela-fg-muted); }
639
641
  .vela-menu-item .vela-menu-label { flex: 1 1 auto; }
640
642
  .vela-menu-item .vela-menu-hint { color: var(--vela-fg-faint); font-size: var(--vela-font-size-sm); }
641
- /* Active entry: a brighter row surface + bright ink \u2014 no accent recolor, no glyph.
642
- Declared after [data-highlighted] so the selected surface wins while hovered. */
643
- .vela-menu-item[data-checked] { background: var(--vela-hover-strong); color: var(--vela-fg-bright); }
644
- .vela-menu-item[data-checked] .vela-icon { color: var(--vela-fg-bright); }
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
+ }
645
650
  /* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
646
651
  same control language as the settings dialog's toggles. */
647
652
  .vela-menu-switch {
@@ -859,6 +864,7 @@ function dialogController(opts = {}) {
859
864
  modal: opts.modal ?? true,
860
865
  closeOnEscape: opts.closeOnEscape ?? true,
861
866
  closeOnInteractOutside: opts.closeOnInteractOutside ?? false,
867
+ initialFocusEl: opts.initialFocusEl,
862
868
  onOpenChange: (d) => opts.onOpenChange?.(d.open)
863
869
  },
864
870
  connect: (service) => dialog__namespace.connect(service, vanilla.normalizeProps)
@@ -935,6 +941,29 @@ var DIALOG_CSS = `
935
941
  border: 2px solid transparent;
936
942
  background-clip: padding-box;
937
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)); }
938
967
  `;
939
968
  var Dialog = class {
940
969
  constructor(opts = {}) {
@@ -948,6 +977,7 @@ var Dialog = class {
948
977
  this.positioner.className = "vela-dialog-positioner vela-ui-layer";
949
978
  this.panel = doc.createElement("div");
950
979
  this.panel.className = "vela-dialog";
980
+ this.panel.tabIndex = -1;
951
981
  const header = doc.createElement("div");
952
982
  header.className = "vela-dialog-header";
953
983
  if (opts.draggable) {
@@ -982,7 +1012,18 @@ var Dialog = class {
982
1012
  this.panel.append(header, this.body);
983
1013
  this.positioner.appendChild(this.panel);
984
1014
  host.append(this.backdrop, this.positioner);
985
- this.ctrl = dialogController(opts);
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
+ });
986
1027
  const mid = String(this.ctrl.props.id);
987
1028
  this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
988
1029
  const api = this.ctrl.connect(service);
@@ -1010,6 +1051,286 @@ var Dialog = class {
1010
1051
  this.positioner.remove();
1011
1052
  }
1012
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
+ };
1013
1334
 
1014
1335
  Object.defineProperty(exports, "normalizeProps", {
1015
1336
  enumerable: true,
@@ -1020,12 +1341,14 @@ Object.defineProperty(exports, "spreadProps", {
1020
1341
  get: function () { return vanilla.spreadProps; }
1021
1342
  });
1022
1343
  exports.Dialog = Dialog;
1344
+ exports.Drawer = Drawer;
1023
1345
  exports.KeymapManager = KeymapManager;
1024
1346
  exports.Menu = Menu;
1025
1347
  exports.Tooltip = Tooltip;
1026
1348
  exports.applyPlotOverlayTokens = applyPlotOverlayTokens;
1027
1349
  exports.applyThemeTokens = applyThemeTokens;
1028
1350
  exports.dialogController = dialogController;
1351
+ exports.drawerController = drawerController;
1029
1352
  exports.ensureUIHost = ensureUIHost;
1030
1353
  exports.iconEl = iconEl;
1031
1354
  exports.iconMarkup = iconMarkup;
package/dist/ui.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as VelaTheme } from './options-DbQholFV.cjs';
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
- export { Dialog, type DialogApi, type DialogControllerOptions, type DialogOptions, type DialogService, 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, ensureUIHost, iconEl, injectStyles, menuController, nextUid, runMachine, tooltipController, withAlpha };
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-DbQholFV.js';
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
- export { Dialog, type DialogApi, type DialogControllerOptions, type DialogOptions, type DialogService, 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, ensureUIHost, iconEl, injectStyles, menuController, nextUid, runMachine, tooltipController, withAlpha };
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-AIW3EJUE.js';
2
- import './chunk-ULIX6AE2.js';
3
- export { iconEl, iconMarkup, injectStyles, registerIcon, svg16, svg24, withAlpha } from './chunk-EP6NDJYK.js';
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';