@surdeddd/bottom-sheet 0.3.0 → 0.4.0

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/{BottomSheetEngine-CAymrH-P.d.cts → BottomSheetEngine-BJrH7qY3.d.cts} +39 -3
  2. package/dist/{BottomSheetEngine-DcqcuudN.d.ts → BottomSheetEngine-L05jGZvU.d.ts} +39 -3
  3. package/dist/element.cjs +480 -89
  4. package/dist/element.d.cts +23 -4
  5. package/dist/element.d.ts +23 -4
  6. package/dist/element.iife.global.js +2 -2
  7. package/dist/element.js +480 -89
  8. package/dist/index.cjs +341 -80
  9. package/dist/index.d.cts +3 -2
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +341 -80
  12. package/dist/integrations/formik.d.cts +1 -1
  13. package/dist/integrations/formik.d.ts +1 -1
  14. package/dist/integrations/react-hook-form.d.cts +1 -1
  15. package/dist/integrations/react-hook-form.d.ts +1 -1
  16. package/dist/overlay.cjs +13 -2
  17. package/dist/overlay.js +13 -2
  18. package/dist/preact.d.cts +1 -1
  19. package/dist/preact.d.ts +1 -1
  20. package/dist/qwik.cjs +352 -81
  21. package/dist/qwik.d.cts +4 -2
  22. package/dist/qwik.d.ts +4 -2
  23. package/dist/qwik.js +352 -81
  24. package/dist/react.cjs +427 -87
  25. package/dist/react.d.cts +29 -4
  26. package/dist/react.d.ts +29 -4
  27. package/dist/react.js +427 -87
  28. package/dist/solid.cjs +341 -80
  29. package/dist/solid.d.cts +38 -2
  30. package/dist/solid.d.ts +38 -2
  31. package/dist/solid.js +341 -80
  32. package/dist/solid.source.jsx +341 -80
  33. package/dist/styles.css +57 -56
  34. package/dist/svelte-core.cjs +346 -80
  35. package/dist/svelte-core.js +346 -80
  36. package/dist/svelte-src/BottomSheet.svelte +103 -5
  37. package/dist/svelte.cjs +421 -88
  38. package/dist/svelte.d.cts +21 -0
  39. package/dist/svelte.d.ts +21 -0
  40. package/dist/svelte.js +421 -88
  41. package/dist/vue.cjs +584 -91
  42. package/dist/vue.d.cts +51 -2
  43. package/dist/vue.d.ts +51 -2
  44. package/dist/vue.js +585 -92
  45. package/package.json +6 -6
package/dist/qwik.js CHANGED
@@ -38,6 +38,15 @@ var SheetStack = class {
38
38
  size() {
39
39
  return this.entries.length;
40
40
  }
41
+ depthOf(id) {
42
+ const open = [];
43
+ for (const entry of this.entries) {
44
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
45
+ }
46
+ const idx = open.findIndex((e) => e.id === id);
47
+ if (idx === -1) return 0;
48
+ return open.length - 1 - idx;
49
+ }
41
50
  clear() {
42
51
  this.entries = [];
43
52
  }
@@ -156,7 +165,9 @@ var PERCENT = /^(-?\d+(?:\.\d+)?)%$/;
156
165
  var resolveSnap = (point, mode, measureFit) => {
157
166
  if (typeof point === "number") return Math.max(0, point);
158
167
  if (point === "full") return viewportSize(mode);
159
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
168
+ if (point === "fit" || point === "content") {
169
+ return Math.max(0, measureFit?.() ?? 0);
170
+ }
160
171
  const pctMatch = point.match(PERCENT);
161
172
  if (pctMatch) {
162
173
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -393,6 +404,42 @@ function notifyLinkedSheets(sheets, self) {
393
404
  }
394
405
 
395
406
  // src/core/features/route.ts
407
+ var closers = [];
408
+ var handlerInstalled = false;
409
+ var suppress = 0;
410
+ var ensureHandler = () => {
411
+ if (handlerInstalled || typeof window === "undefined") return;
412
+ handlerInstalled = true;
413
+ window.addEventListener("popstate", () => {
414
+ if (suppress > 0) {
415
+ suppress -= 1;
416
+ return;
417
+ }
418
+ let target = null;
419
+ for (let i = closers.length - 1; i >= 0; i -= 1) {
420
+ const c = closers[i];
421
+ if (!c.isOpen()) continue;
422
+ if (!target) target = c;
423
+ if (c.isTop()) {
424
+ target = c;
425
+ break;
426
+ }
427
+ }
428
+ if (!target) return;
429
+ target.consumed = true;
430
+ const idx = closers.indexOf(target);
431
+ if (idx !== -1) closers.splice(idx, 1);
432
+ target.close();
433
+ });
434
+ };
435
+ var programmaticBack = () => {
436
+ suppress += 1;
437
+ try {
438
+ history.back();
439
+ } catch {
440
+ suppress = Math.max(0, suppress - 1);
441
+ }
442
+ };
396
443
  function installRoute(deps) {
397
444
  if (typeof window === "undefined") {
398
445
  return () => {
@@ -402,82 +449,81 @@ function installRoute(deps) {
402
449
  return () => {
403
450
  };
404
451
  }
405
- let routePushed = false;
406
- let closeOnBackPushed = false;
407
- let detachPopstate = null;
408
- const popOurMarker = (key, expected) => {
409
- const state = history.state;
410
- if (state && state[key] === expected) {
411
- try {
412
- history.back();
413
- } catch {
414
- }
452
+ let pushed = false;
453
+ let closer = null;
454
+ const teardownEntry = () => {
455
+ if (closer) {
456
+ const idx = closers.indexOf(closer);
457
+ if (idx !== -1) closers.splice(idx, 1);
458
+ }
459
+ const wasConsumed = closer?.consumed ?? false;
460
+ closer = null;
461
+ if (pushed && !wasConsumed) {
462
+ pushed = false;
463
+ programmaticBack();
464
+ } else {
465
+ pushed = false;
415
466
  }
416
467
  };
417
468
  const onOpen = () => {
418
- if (deps.routedTo && !detachPopstate) {
419
- const target = deps.routedTo;
420
- try {
421
- history.pushState({ __bsRouted: target }, "", target);
422
- routePushed = true;
423
- } catch {
424
- }
425
- const onPop = (event) => {
426
- if (deps.isDestroyed()) return;
427
- const state = event.state;
428
- if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
429
- routePushed = false;
430
- void deps.close();
431
- }
432
- };
433
- window.addEventListener("popstate", onPop);
434
- detachPopstate = () => window.removeEventListener("popstate", onPop);
435
- } else if (deps.closeOnBack && !detachPopstate) {
436
- try {
469
+ if (closer) return;
470
+ closer = {
471
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
472
+ isTop: () => deps.isTopSheet(),
473
+ close: () => void deps.close(),
474
+ consumed: false
475
+ };
476
+ ensureHandler();
477
+ closers.push(closer);
478
+ try {
479
+ if (deps.routedTo !== void 0) {
480
+ history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
481
+ } else {
437
482
  history.pushState({ __bsSheet: deps.sheetId }, "");
438
- closeOnBackPushed = true;
439
- } catch {
440
483
  }
441
- const onPop = () => {
442
- if (deps.isDestroyed()) return;
443
- if (deps.getSize() > 0 && deps.isTopSheet()) {
444
- const state = history.state;
445
- if (state?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
446
- void deps.close();
447
- }
448
- };
449
- window.addEventListener("popstate", onPop);
450
- detachPopstate = () => window.removeEventListener("popstate", onPop);
451
- }
452
- };
453
- const onClose = () => {
454
- detachPopstate?.();
455
- detachPopstate = null;
456
- if (routePushed) {
457
- routePushed = false;
458
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
459
- }
460
- if (closeOnBackPushed) {
461
- closeOnBackPushed = false;
462
- popOurMarker("__bsSheet", deps.sheetId);
484
+ pushed = true;
485
+ } catch {
486
+ pushed = false;
463
487
  }
464
488
  };
465
489
  const unsubscribeOpen = deps.on("open", onOpen);
466
- const unsubscribeClose = deps.on("close", onClose);
490
+ const unsubscribeClose = deps.on("close", teardownEntry);
467
491
  if (deps.getSize() > 0) onOpen();
468
492
  return () => {
469
493
  unsubscribeOpen();
470
494
  unsubscribeClose();
471
- detachPopstate?.();
472
- detachPopstate = null;
473
- if (routePushed) {
474
- routePushed = false;
475
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
476
- }
477
- if (closeOnBackPushed) {
478
- closeOnBackPushed = false;
479
- popOurMarker("__bsSheet", deps.sheetId);
480
- }
495
+ teardownEntry();
496
+ };
497
+ }
498
+ function installRouteChange(deps) {
499
+ if (typeof window === "undefined" || typeof history === "undefined") {
500
+ return () => {
501
+ };
502
+ }
503
+ const onChange = () => {
504
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
505
+ void deps.close();
506
+ };
507
+ const wrap = (key) => {
508
+ const original = history[key];
509
+ const patched = function(...args) {
510
+ original.apply(this, args);
511
+ onChange();
512
+ };
513
+ history[key] = patched;
514
+ return () => {
515
+ if (history[key] === patched) {
516
+ history[key] = original;
517
+ }
518
+ };
519
+ };
520
+ window.addEventListener("popstate", onChange);
521
+ const restorePush = wrap("pushState");
522
+ const restoreReplace = wrap("replaceState");
523
+ return () => {
524
+ window.removeEventListener("popstate", onChange);
525
+ restorePush();
526
+ restoreReplace();
481
527
  };
482
528
  }
483
529
 
@@ -785,6 +831,7 @@ function applyOverlayPosition(ws, mode, position, inset) {
785
831
 
786
832
  // src/core/controllers/scrim-controller.ts
787
833
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
834
+ var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
788
835
  var ScrimController = class {
789
836
  constructor(deps, opts) {
790
837
  __publicField(this, "screenComponent");
@@ -823,9 +870,11 @@ var ScrimController = class {
823
870
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
824
871
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
825
872
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
873
+ const explicitColor = opts.scrimColor ?? preset?.color;
874
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
826
875
  if (this.screenComponent) {
827
876
  this.applyScrimStyles({
828
- color: opts.scrimColor ?? preset?.color,
877
+ color: effectiveColor,
829
878
  blur: opts.scrimBlur ?? preset?.blur,
830
879
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
831
880
  });
@@ -1599,7 +1648,7 @@ var LifecycleController = class {
1599
1648
  this.releaseScrollLock = null;
1600
1649
  this.inertSiblings.remove();
1601
1650
  if (this.returnFocus) {
1602
- const target = typeof this.returnFocus === "function" ? this.returnFocus() : this.returnFocus;
1651
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1603
1652
  target?.focus?.();
1604
1653
  }
1605
1654
  }
@@ -1766,6 +1815,7 @@ var GestureController = class {
1766
1815
  __publicField(this, "dragStartSize", 0);
1767
1816
  __publicField(this, "currentPointerKind", "touch");
1768
1817
  __publicField(this, "keyboardDismissed", false);
1818
+ __publicField(this, "dragSuppressed", false);
1769
1819
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1770
1820
  this.deps = deps;
1771
1821
  }
@@ -1776,6 +1826,11 @@ var GestureController = class {
1776
1826
  if (this.detach) return this.detach;
1777
1827
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1778
1828
  onStart: (_coord, kind) => {
1829
+ if (this.deps.getDisableDrag?.()) {
1830
+ this.dragSuppressed = true;
1831
+ return;
1832
+ }
1833
+ this.dragSuppressed = false;
1779
1834
  this.deps.cancelAnimation();
1780
1835
  this.isDragging_ = true;
1781
1836
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1786,6 +1841,7 @@ var GestureController = class {
1786
1841
  this.deps.emit("dragstart", { size: this.dragStartSize });
1787
1842
  },
1788
1843
  onMove: (delta) => {
1844
+ if (this.dragSuppressed) return;
1789
1845
  const ctx = this.deps.getDragContext();
1790
1846
  const { min, max } = ctx.range;
1791
1847
  const maxAxis = ctx.maxAxisSize;
@@ -1809,6 +1865,10 @@ var GestureController = class {
1809
1865
  }
1810
1866
  },
1811
1867
  onEnd: (delta, velocity, kind) => {
1868
+ if (this.dragSuppressed) {
1869
+ this.dragSuppressed = false;
1870
+ return;
1871
+ }
1812
1872
  this.isDragging_ = false;
1813
1873
  this.keyboardDismissed = false;
1814
1874
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1819,6 +1879,10 @@ var GestureController = class {
1819
1879
  this.deps.settleAfterDrag(delta, velocity, kind);
1820
1880
  },
1821
1881
  onCancel: () => {
1882
+ if (this.dragSuppressed) {
1883
+ this.dragSuppressed = false;
1884
+ return;
1885
+ }
1822
1886
  this.isDragging_ = false;
1823
1887
  this.keyboardDismissed = false;
1824
1888
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -2198,6 +2262,12 @@ function resolveEngineOptions(opts, extras) {
2198
2262
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2199
2263
  rubberBandEnabled: opts.rubberBand ?? true,
2200
2264
  closeOnBack: opts.closeOnBack ?? false,
2265
+ persistent: opts.persistent ?? false,
2266
+ disableClose: opts.disableClose ?? false,
2267
+ disableDrag: opts.disableDrag ?? false,
2268
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2269
+ radius: opts.radius,
2270
+ maxHeight: opts.maxHeight,
2201
2271
  initialAllowed,
2202
2272
  initialId,
2203
2273
  scrim: {
@@ -2224,7 +2294,8 @@ function resolveEngineOptions(opts, extras) {
2224
2294
  closeOnEscape: opts.closeOnEscape,
2225
2295
  lockBodyScroll: opts.lockBodyScroll,
2226
2296
  inertSiblings: opts.inertSiblings,
2227
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2297
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2298
+ returnFocus: opts.returnFocusTo
2228
2299
  }
2229
2300
  };
2230
2301
  }
@@ -2269,6 +2340,11 @@ var BottomSheetEngine = class {
2269
2340
  __publicField(this, "lifecycle");
2270
2341
  __publicField(this, "closeOnBack");
2271
2342
  __publicField(this, "routedTo");
2343
+ __publicField(this, "persistent");
2344
+ __publicField(this, "disableCloseFlag");
2345
+ __publicField(this, "disableDragFlag");
2346
+ __publicField(this, "closeOnRouteChange");
2347
+ __publicField(this, "maxHeightCap");
2272
2348
  __publicField(this, "snapPointsRaw");
2273
2349
  __publicField(this, "snaps");
2274
2350
  __publicField(this, "activeId");
@@ -2281,6 +2357,7 @@ var BottomSheetEngine = class {
2281
2357
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2282
2358
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2283
2359
  __publicField(this, "isTopSheet", true);
2360
+ __publicField(this, "opening", false);
2284
2361
  __publicField(this, "progressPayload", {
2285
2362
  value: 0,
2286
2363
  size: 0
@@ -2337,7 +2414,8 @@ var BottomSheetEngine = class {
2337
2414
  void this.snapTo(id);
2338
2415
  },
2339
2416
  close: () => {
2340
- void this.close();
2417
+ if (!this.canDismiss()) return;
2418
+ void this.close("backdrop");
2341
2419
  }
2342
2420
  },
2343
2421
  resolved.scrim
@@ -2361,6 +2439,10 @@ var BottomSheetEngine = class {
2361
2439
  );
2362
2440
  this.closeOnBack = resolved.closeOnBack;
2363
2441
  this.routedTo = opts.routedTo;
2442
+ this.persistent = resolved.persistent;
2443
+ this.disableCloseFlag = resolved.disableClose;
2444
+ this.disableDragFlag = resolved.disableDrag;
2445
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2364
2446
  this.stackEffectEnabled = opts.stackEffect ?? false;
2365
2447
  this.snapPointsRaw = opts.snapPoints;
2366
2448
  this.persistKey = opts.persistKey;
@@ -2374,7 +2456,7 @@ var BottomSheetEngine = class {
2374
2456
  this.snapPointsRaw,
2375
2457
  resolved.initialAllowed,
2376
2458
  this.mode,
2377
- () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2459
+ () => this.measureFitSize(),
2378
2460
  (maxAxisSize) => {
2379
2461
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2380
2462
  }
@@ -2408,6 +2490,19 @@ var BottomSheetEngine = class {
2408
2490
  io.observe(this.element);
2409
2491
  this.teardowns.add(() => io.disconnect());
2410
2492
  }
2493
+ this.installFitObserver();
2494
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2495
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2496
+ this.applyAutoAriaLabelledBy();
2497
+ if (this.closeOnRouteChange) {
2498
+ this.teardowns.add(
2499
+ installRouteChange({
2500
+ isDestroyed: () => this.destroyed,
2501
+ getSize: () => this.size,
2502
+ close: () => this.close()
2503
+ })
2504
+ );
2505
+ }
2411
2506
  if (this.persistKey) {
2412
2507
  this.teardowns.add(installPersist(this, this.persistKey));
2413
2508
  }
@@ -2432,7 +2527,7 @@ var BottomSheetEngine = class {
2432
2527
  isTopSheet: () => this.isTopSheet,
2433
2528
  getSize: () => this.size,
2434
2529
  isDestroyed: () => this.destroyed,
2435
- close: () => this.close(),
2530
+ close: () => this.close("back"),
2436
2531
  on: (event, fn) => this.on(event, fn),
2437
2532
  sheetId: this.id
2438
2533
  })
@@ -2526,6 +2621,10 @@ var BottomSheetEngine = class {
2526
2621
  const previousSize = this.size;
2527
2622
  this.scrollCache.cache(previousId, previousSize, target.size);
2528
2623
  this.activeId = id;
2624
+ if (wasClosed && target.size > 0) {
2625
+ this.opening = true;
2626
+ sheetStack.promote(this.id);
2627
+ }
2529
2628
  if (this.animation.viewTransitionsAvailable) {
2530
2629
  this.currentViewTransition?.skipTransition?.();
2531
2630
  const vt = document.startViewTransition(() => {
@@ -2540,19 +2639,26 @@ var BottomSheetEngine = class {
2540
2639
  } else {
2541
2640
  await this.animation.animateTo(target.size, velocityPxPerMs);
2542
2641
  }
2642
+ this.opening = false;
2543
2643
  externalSignal?.removeEventListener("abort", onExternalAbort);
2544
2644
  if (signal.aborted) return;
2545
2645
  this.scrollCache.restore(id, previousSize, target.size);
2546
2646
  this.updateAriaSlider();
2547
- this.emit("snap", { id, size: target.size });
2647
+ this.emit("snap", {
2648
+ id,
2649
+ size: target.size,
2650
+ progress: this.computeProgress(target.size)
2651
+ });
2548
2652
  if (wasClosed && target.size > 0) {
2549
2653
  this.emit("open", { id });
2550
2654
  this.handleOpen();
2551
2655
  notifyLinkedSheets(this.linkedSheets, this);
2656
+ this.emit("opened", { id });
2552
2657
  }
2553
2658
  if (target.size === 0) {
2554
2659
  this.emit("close", void 0);
2555
2660
  this.handleClose();
2661
+ this.emit("closed", void 0);
2556
2662
  }
2557
2663
  }
2558
2664
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2577,11 +2683,74 @@ var BottomSheetEngine = class {
2577
2683
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2578
2684
  return this.snapTo(target);
2579
2685
  }
2580
- close() {
2686
+ close(reason = "programmatic") {
2581
2687
  if (this.destroyed) return Promise.resolve();
2688
+ if (this.disableCloseFlag) return Promise.resolve();
2689
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2582
2690
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2583
2691
  return this.snapTo(closedId ?? this.activeId);
2584
2692
  }
2693
+ canDismiss() {
2694
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2695
+ }
2696
+ isTop() {
2697
+ return this.isTopSheet;
2698
+ }
2699
+ depth() {
2700
+ return sheetStack.depthOf(this.id);
2701
+ }
2702
+ expand() {
2703
+ if (this.destroyed) return Promise.resolve();
2704
+ const ids = this.allowedIdsBySize();
2705
+ const target = ids[ids.length - 1];
2706
+ if (!target) return Promise.resolve();
2707
+ return this.snapTo(target);
2708
+ }
2709
+ collapse() {
2710
+ if (this.destroyed) return Promise.resolve();
2711
+ const ids = this.allowedIdsBySize();
2712
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2713
+ const target = nonZero ?? ids[0];
2714
+ if (!target) return Promise.resolve();
2715
+ return this.snapTo(target);
2716
+ }
2717
+ setRadius(r) {
2718
+ if (this.destroyed) return;
2719
+ this.element.style.setProperty(
2720
+ "--bs-radius",
2721
+ typeof r === "number" ? `${r}px` : r
2722
+ );
2723
+ }
2724
+ setMaxHeight(h) {
2725
+ if (this.destroyed) return;
2726
+ const value = typeof h === "number" ? `${h}px` : h;
2727
+ const axis = layoutAxis(this.mode);
2728
+ this.element.style.setProperty(
2729
+ axis === "height" ? "maxHeight" : "maxWidth",
2730
+ value
2731
+ );
2732
+ if (typeof h === "number") {
2733
+ this.maxHeightCap = h;
2734
+ this.clampToMaxHeight();
2735
+ } else if (typeof window !== "undefined") {
2736
+ const probe2 = document.createElement("div");
2737
+ probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2738
+ document.body.appendChild(probe2);
2739
+ const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2740
+ probe2.remove();
2741
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2742
+ this.clampToMaxHeight();
2743
+ }
2744
+ }
2745
+ clampToMaxHeight() {
2746
+ if (this.maxHeightCap === void 0) return;
2747
+ if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2748
+ this.snaps.setMaxAxisSize(this.maxHeightCap);
2749
+ }
2750
+ if (this.size > this.maxHeightCap && !this.gesture?.isDragging) {
2751
+ this.applySize(this.maxHeightCap);
2752
+ }
2753
+ }
2585
2754
  setAllowed(ids, snap) {
2586
2755
  if (this.destroyed) return;
2587
2756
  this.snaps.setAllowedIds(ids);
@@ -2715,8 +2884,53 @@ var BottomSheetEngine = class {
2715
2884
  }
2716
2885
  this.updateAriaSlider();
2717
2886
  }
2887
+ recompute() {
2888
+ if (this.destroyed) return;
2889
+ this.snaps.recompute();
2890
+ this.scrim.invalidateOpacityCache();
2891
+ if (this.gesture?.isDragging) return;
2892
+ const current = this.snaps.findById(this.activeId);
2893
+ if (current) {
2894
+ this.size = current.size;
2895
+ this.applySize(this.size);
2896
+ this.updateAriaSlider();
2897
+ }
2898
+ }
2899
+ measureFitSize() {
2900
+ const vertical = layoutAxis(this.mode) === "height";
2901
+ const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2902
+ const content = this.scrollContainer;
2903
+ const natural = content ? handleSize + (vertical ? content.scrollHeight : content.scrollWidth) : handleSize;
2904
+ if (typeof window === "undefined") return natural;
2905
+ const viewport = vertical ? window.innerHeight : window.innerWidth;
2906
+ return viewport > 0 ? Math.min(natural, viewport) : natural;
2907
+ }
2908
+ installFitObserver() {
2909
+ if (typeof ResizeObserver === "undefined") return;
2910
+ if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
2911
+ return;
2912
+ const targets = [this.handle];
2913
+ if (this.scrollContainer && this.scrollContainer !== this.handle) {
2914
+ targets.push(this.scrollContainer);
2915
+ }
2916
+ let raf = 0;
2917
+ const ro = new ResizeObserver(() => {
2918
+ if (raf) return;
2919
+ raf = requestAnimationFrame(() => {
2920
+ raf = 0;
2921
+ if (this.destroyed || this.gesture?.isDragging) return;
2922
+ this.recompute();
2923
+ });
2924
+ });
2925
+ for (const t of targets) ro.observe(t);
2926
+ this.teardowns.add(() => {
2927
+ ro.disconnect();
2928
+ if (raf) cancelAnimationFrame(raf);
2929
+ });
2930
+ }
2718
2931
  destroy() {
2719
2932
  this.destroyed = true;
2933
+ this.opening = false;
2720
2934
  this.currentAbort.abort();
2721
2935
  this.currentViewTransition?.skipTransition?.();
2722
2936
  this.currentViewTransition = null;
@@ -2766,6 +2980,36 @@ var BottomSheetEngine = class {
2766
2980
  frozen = true;
2767
2981
  return cancelled;
2768
2982
  }
2983
+ emitBeforeClose(reason) {
2984
+ let cancelled = false;
2985
+ let frozen = false;
2986
+ this.emit("before-close", {
2987
+ reason,
2988
+ cancel: () => {
2989
+ if (frozen) {
2990
+ console.warn(
2991
+ "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
2992
+ );
2993
+ return;
2994
+ }
2995
+ cancelled = true;
2996
+ }
2997
+ });
2998
+ frozen = true;
2999
+ return cancelled;
3000
+ }
3001
+ applyAutoAriaLabelledBy() {
3002
+ if (typeof document === "undefined") return;
3003
+ if (!this.lifecycle.focusTrapEnabled) return;
3004
+ if (this.element.getAttribute("aria-labelledby")) return;
3005
+ const scope = this.handle ?? this.element;
3006
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3007
+ if (!titled) return;
3008
+ if (!titled.id) {
3009
+ titled.id = nextInstanceId("bs-title");
3010
+ }
3011
+ this.element.setAttribute("aria-labelledby", titled.id);
3012
+ }
2769
3013
  recomputeSnaps() {
2770
3014
  const seen = /* @__PURE__ */ new Set();
2771
3015
  for (const p of this.snapPointsRaw) {
@@ -2801,11 +3045,8 @@ var BottomSheetEngine = class {
2801
3045
  },
2802
3046
  setIsTop: (isTop) => {
2803
3047
  this.isTopSheet = isTop;
2804
- if (this.backdrop) {
2805
- this.backdrop.style.display = isTop ? "" : "none";
2806
- }
2807
3048
  },
2808
- isOpen: () => this.size > 0,
3049
+ isOpen: () => this.size > 0 || this.opening,
2809
3050
  setDepth: (depth) => this.applyStackDepth(depth)
2810
3051
  }));
2811
3052
  }
@@ -2837,6 +3078,7 @@ var BottomSheetEngine = class {
2837
3078
  buf.rubberBandEnabled = this.rubberBandEnabled;
2838
3079
  return buf;
2839
3080
  },
3081
+ getDisableDrag: () => this.disableDragFlag,
2840
3082
  cancelAnimation: () => this.animation.cancel(),
2841
3083
  applySize: (size) => this.applySize(size),
2842
3084
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -2916,8 +3158,9 @@ var BottomSheetEngine = class {
2916
3158
  if (this.lifecycle.closeOnEscape) {
2917
3159
  const onKey = (e) => {
2918
3160
  if (this.destroyed || e.defaultPrevented) return;
3161
+ if (!this.canDismiss()) return;
2919
3162
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2920
- void this.close();
3163
+ void this.close("escape");
2921
3164
  }
2922
3165
  };
2923
3166
  document.addEventListener("keydown", onKey);
@@ -2980,20 +3223,31 @@ var BottomSheetEngine = class {
2980
3223
  const previousSnapSize = this.snaps.findById(previousId)?.size;
2981
3224
  this.scrollCache.cache(previousId, previousSize, target.size);
2982
3225
  this.activeId = target.id;
3226
+ if (previousSnapSize === 0 && target.size > 0) {
3227
+ this.opening = true;
3228
+ sheetStack.promote(this.id);
3229
+ }
2983
3230
  void this.animation.animateTo(target.size, velocity).then(() => {
3231
+ this.opening = false;
2984
3232
  if (signal.aborted) return;
2985
3233
  this.scrollCache.restore(target.id, previousSize, target.size);
2986
3234
  this.updateAriaSlider();
2987
- this.emit("snap", { id: target.id, size: target.size });
3235
+ this.emit("snap", {
3236
+ id: target.id,
3237
+ size: target.size,
3238
+ progress: this.computeProgress(target.size)
3239
+ });
2988
3240
  this.haptic();
2989
3241
  if (previousSnapSize === 0 && target.size > 0) {
2990
3242
  this.emit("open", { id: target.id });
2991
3243
  this.handleOpen();
2992
3244
  notifyLinkedSheets(this.linkedSheets, this);
3245
+ this.emit("opened", { id: target.id });
2993
3246
  }
2994
3247
  if (target.size === 0) {
2995
3248
  this.emit("close", void 0);
2996
3249
  this.handleClose();
3250
+ this.emit("closed", void 0);
2997
3251
  }
2998
3252
  });
2999
3253
  }
@@ -3062,17 +3316,24 @@ var BottomSheetEngine = class {
3062
3316
  const target = this.snaps.findById(this.activeId);
3063
3317
  if (!target) return;
3064
3318
  this.updateAriaSlider();
3065
- this.emit("snap", { id: target.id, size: target.size });
3319
+ this.emit("snap", {
3320
+ id: target.id,
3321
+ size: target.size,
3322
+ progress: this.computeProgress(target.size)
3323
+ });
3066
3324
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3067
3325
  this.emit("open", { id: target.id });
3068
3326
  this.handleOpen();
3069
3327
  notifyLinkedSheets(this.linkedSheets, this);
3328
+ this.emit("opened", { id: target.id });
3070
3329
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3071
3330
  this.emit("close", void 0);
3072
3331
  this.handleClose();
3332
+ this.emit("closed", void 0);
3073
3333
  }
3074
3334
  }
3075
3335
  handleClose() {
3336
+ this.opening = false;
3076
3337
  this.lifecycle.release();
3077
3338
  sheetStack.update();
3078
3339
  }
@@ -3116,7 +3377,9 @@ var BottomSheet = component$((props) => {
3116
3377
  closeOnBack: props.closeOnBack,
3117
3378
  lockBodyScroll: props.lockBodyScroll,
3118
3379
  rubberBand: props.rubberBand,
3119
- backdropRange: props.backdropRange
3380
+ backdropRange: props.backdropRange,
3381
+ radius: props.radius,
3382
+ maxHeight: props.maxHeight
3120
3383
  };
3121
3384
  const engine = new BottomSheetEngine(engineOpts);
3122
3385
  engineStore.engine = noSerialize(engine);
@@ -3151,6 +3414,14 @@ var BottomSheet = component$((props) => {
3151
3414
  allowed
3152
3415
  );
3153
3416
  });
3417
+ useTask$(({ track }) => {
3418
+ const radius = track(() => props.radius);
3419
+ const maxHeight = track(() => props.maxHeight);
3420
+ const engine = engineStore.engine;
3421
+ if (!engine) return;
3422
+ if (radius !== void 0) engine.setRadius(radius);
3423
+ if (maxHeight !== void 0) engine.setMaxHeight(maxHeight);
3424
+ });
3154
3425
  const showBackdrop = props.backdrop !== false;
3155
3426
  const mode = props.mode ?? "bottom";
3156
3427
  const ariaLabel = props.ariaLabel ?? "Bottom sheet";