@surdeddd/bottom-sheet 0.3.0 → 0.4.1

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 +490 -91
  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 +490 -91
  8. package/dist/index.cjs +347 -80
  9. package/dist/index.d.cts +3 -2
  10. package/dist/index.d.ts +3 -2
  11. package/dist/index.js +347 -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 +358 -81
  21. package/dist/qwik.d.cts +4 -2
  22. package/dist/qwik.d.ts +4 -2
  23. package/dist/qwik.js +358 -81
  24. package/dist/react.cjs +451 -89
  25. package/dist/react.d.cts +29 -4
  26. package/dist/react.d.ts +29 -4
  27. package/dist/react.js +451 -89
  28. package/dist/solid.cjs +347 -80
  29. package/dist/solid.d.cts +38 -2
  30. package/dist/solid.d.ts +38 -2
  31. package/dist/solid.js +347 -80
  32. package/dist/solid.source.jsx +347 -80
  33. package/dist/styles.css +68 -55
  34. package/dist/svelte-core.cjs +352 -80
  35. package/dist/svelte-core.js +352 -80
  36. package/dist/svelte-src/BottomSheet.svelte +113 -15
  37. package/dist/svelte.cjs +474 -133
  38. package/dist/svelte.d.cts +21 -0
  39. package/dist/svelte.d.ts +21 -0
  40. package/dist/svelte.js +474 -133
  41. package/dist/vue.cjs +610 -109
  42. package/dist/vue.d.cts +51 -2
  43. package/dist/vue.d.ts +51 -2
  44. package/dist/vue.js +611 -110
  45. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -35,6 +35,15 @@ var SheetStack = class {
35
35
  size() {
36
36
  return this.entries.length;
37
37
  }
38
+ depthOf(id) {
39
+ const open = [];
40
+ for (const entry of this.entries) {
41
+ if (!entry.isOpen || entry.isOpen()) open.push(entry);
42
+ }
43
+ const idx = open.findIndex((e) => e.id === id);
44
+ if (idx === -1) return 0;
45
+ return open.length - 1 - idx;
46
+ }
38
47
  clear() {
39
48
  this.entries = [];
40
49
  }
@@ -159,7 +168,9 @@ var PERCENT = /^(-?\d+(?:\.\d+)?)%$/;
159
168
  var resolveSnap = (point, mode, measureFit) => {
160
169
  if (typeof point === "number") return Math.max(0, point);
161
170
  if (point === "full") return viewportSize(mode);
162
- if (point === "fit") return Math.max(0, measureFit?.() ?? 0);
171
+ if (point === "fit" || point === "content") {
172
+ return Math.max(0, measureFit?.() ?? 0);
173
+ }
163
174
  const pctMatch = point.match(PERCENT);
164
175
  if (pctMatch) {
165
176
  const pct = parseFloat(pctMatch[1]) / 100;
@@ -396,6 +407,42 @@ function notifyLinkedSheets(sheets, self) {
396
407
  }
397
408
 
398
409
  // src/core/features/route.ts
410
+ var closers = [];
411
+ var handlerInstalled = false;
412
+ var suppress = 0;
413
+ var ensureHandler = () => {
414
+ if (handlerInstalled || typeof window === "undefined") return;
415
+ handlerInstalled = true;
416
+ window.addEventListener("popstate", () => {
417
+ if (suppress > 0) {
418
+ suppress -= 1;
419
+ return;
420
+ }
421
+ let target = null;
422
+ for (let i = closers.length - 1; i >= 0; i -= 1) {
423
+ const c = closers[i];
424
+ if (!c.isOpen()) continue;
425
+ if (!target) target = c;
426
+ if (c.isTop()) {
427
+ target = c;
428
+ break;
429
+ }
430
+ }
431
+ if (!target) return;
432
+ target.consumed = true;
433
+ const idx = closers.indexOf(target);
434
+ if (idx !== -1) closers.splice(idx, 1);
435
+ target.close();
436
+ });
437
+ };
438
+ var programmaticBack = () => {
439
+ suppress += 1;
440
+ try {
441
+ history.back();
442
+ } catch {
443
+ suppress = Math.max(0, suppress - 1);
444
+ }
445
+ };
399
446
  function installRoute(deps) {
400
447
  if (typeof window === "undefined") {
401
448
  return () => {
@@ -405,82 +452,81 @@ function installRoute(deps) {
405
452
  return () => {
406
453
  };
407
454
  }
408
- let routePushed = false;
409
- let closeOnBackPushed = false;
410
- let detachPopstate = null;
411
- const popOurMarker = (key, expected) => {
412
- const state = history.state;
413
- if (state && state[key] === expected) {
414
- try {
415
- history.back();
416
- } catch {
417
- }
455
+ let pushed = false;
456
+ let closer = null;
457
+ const teardownEntry = () => {
458
+ if (closer) {
459
+ const idx = closers.indexOf(closer);
460
+ if (idx !== -1) closers.splice(idx, 1);
461
+ }
462
+ const wasConsumed = closer?.consumed ?? false;
463
+ closer = null;
464
+ if (pushed && !wasConsumed) {
465
+ pushed = false;
466
+ programmaticBack();
467
+ } else {
468
+ pushed = false;
418
469
  }
419
470
  };
420
471
  const onOpen = () => {
421
- if (deps.routedTo && !detachPopstate) {
422
- const target = deps.routedTo;
423
- try {
424
- history.pushState({ __bsRouted: target }, "", target);
425
- routePushed = true;
426
- } catch {
427
- }
428
- const onPop = (event) => {
429
- if (deps.isDestroyed()) return;
430
- const state = event.state;
431
- if ((!state || state.__bsRouted !== target) && deps.getSize() > 0) {
432
- routePushed = false;
433
- void deps.close();
434
- }
435
- };
436
- window.addEventListener("popstate", onPop);
437
- detachPopstate = () => window.removeEventListener("popstate", onPop);
438
- } else if (deps.closeOnBack && !detachPopstate) {
439
- try {
472
+ if (closer) return;
473
+ closer = {
474
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
475
+ isTop: () => deps.isTopSheet(),
476
+ close: () => void deps.close(),
477
+ consumed: false
478
+ };
479
+ ensureHandler();
480
+ closers.push(closer);
481
+ try {
482
+ if (deps.routedTo !== void 0) {
483
+ history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
484
+ } else {
440
485
  history.pushState({ __bsSheet: deps.sheetId }, "");
441
- closeOnBackPushed = true;
442
- } catch {
443
486
  }
444
- const onPop = () => {
445
- if (deps.isDestroyed()) return;
446
- if (deps.getSize() > 0 && deps.isTopSheet()) {
447
- const state = history.state;
448
- if (state?.__bsSheet !== deps.sheetId) closeOnBackPushed = false;
449
- void deps.close();
450
- }
451
- };
452
- window.addEventListener("popstate", onPop);
453
- detachPopstate = () => window.removeEventListener("popstate", onPop);
454
- }
455
- };
456
- const onClose = () => {
457
- detachPopstate?.();
458
- detachPopstate = null;
459
- if (routePushed) {
460
- routePushed = false;
461
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
462
- }
463
- if (closeOnBackPushed) {
464
- closeOnBackPushed = false;
465
- popOurMarker("__bsSheet", deps.sheetId);
487
+ pushed = true;
488
+ } catch {
489
+ pushed = false;
466
490
  }
467
491
  };
468
492
  const unsubscribeOpen = deps.on("open", onOpen);
469
- const unsubscribeClose = deps.on("close", onClose);
493
+ const unsubscribeClose = deps.on("close", teardownEntry);
470
494
  if (deps.getSize() > 0) onOpen();
471
495
  return () => {
472
496
  unsubscribeOpen();
473
497
  unsubscribeClose();
474
- detachPopstate?.();
475
- detachPopstate = null;
476
- if (routePushed) {
477
- routePushed = false;
478
- if (deps.routedTo !== void 0) popOurMarker("__bsRouted", deps.routedTo);
479
- }
480
- if (closeOnBackPushed) {
481
- closeOnBackPushed = false;
482
- popOurMarker("__bsSheet", deps.sheetId);
483
- }
498
+ teardownEntry();
499
+ };
500
+ }
501
+ function installRouteChange(deps) {
502
+ if (typeof window === "undefined" || typeof history === "undefined") {
503
+ return () => {
504
+ };
505
+ }
506
+ const onChange = () => {
507
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
508
+ void deps.close();
509
+ };
510
+ const wrap = (key) => {
511
+ const original = history[key];
512
+ const patched = function(...args) {
513
+ original.apply(this, args);
514
+ onChange();
515
+ };
516
+ history[key] = patched;
517
+ return () => {
518
+ if (history[key] === patched) {
519
+ history[key] = original;
520
+ }
521
+ };
522
+ };
523
+ window.addEventListener("popstate", onChange);
524
+ const restorePush = wrap("pushState");
525
+ const restoreReplace = wrap("replaceState");
526
+ return () => {
527
+ window.removeEventListener("popstate", onChange);
528
+ restorePush();
529
+ restoreReplace();
484
530
  };
485
531
  }
486
532
 
@@ -788,6 +834,7 @@ function applyOverlayPosition(ws, mode, position, inset) {
788
834
 
789
835
  // src/core/controllers/scrim-controller.ts
790
836
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
837
+ var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
791
838
  var ScrimController = class {
792
839
  constructor(deps, opts) {
793
840
  __publicField(this, "screenComponent");
@@ -826,9 +873,11 @@ var ScrimController = class {
826
873
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
827
874
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
828
875
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
876
+ const explicitColor = opts.scrimColor ?? preset?.color;
877
+ const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
829
878
  if (this.screenComponent) {
830
879
  this.applyScrimStyles({
831
- color: opts.scrimColor ?? preset?.color,
880
+ color: effectiveColor,
832
881
  blur: opts.scrimBlur ?? preset?.blur,
833
882
  interactive: opts.scrimInteractive ?? preset?.interactive ?? false
834
883
  });
@@ -1602,7 +1651,7 @@ var LifecycleController = class {
1602
1651
  this.releaseScrollLock = null;
1603
1652
  this.inertSiblings.remove();
1604
1653
  if (this.returnFocus) {
1605
- const target = typeof this.returnFocus === "function" ? this.returnFocus() : this.returnFocus;
1654
+ const target = typeof this.returnFocus === "function" ? this.returnFocus() : typeof this.returnFocus === "string" ? typeof document !== "undefined" ? document.querySelector(this.returnFocus) : null : this.returnFocus;
1606
1655
  target?.focus?.();
1607
1656
  }
1608
1657
  }
@@ -1770,6 +1819,7 @@ var GestureController = class {
1770
1819
  __publicField(this, "dragStartSize", 0);
1771
1820
  __publicField(this, "currentPointerKind", "touch");
1772
1821
  __publicField(this, "keyboardDismissed", false);
1822
+ __publicField(this, "dragSuppressed", false);
1773
1823
  __publicField(this, "dragPayload", { size: 0, delta: 0 });
1774
1824
  this.deps = deps;
1775
1825
  }
@@ -1780,6 +1830,11 @@ var GestureController = class {
1780
1830
  if (this.detach) return this.detach;
1781
1831
  this.detach = installGestures(this.deps.handle, this.deps.mode, {
1782
1832
  onStart: (_coord, kind) => {
1833
+ if (this.deps.getDisableDrag?.()) {
1834
+ this.dragSuppressed = true;
1835
+ return;
1836
+ }
1837
+ this.dragSuppressed = false;
1783
1838
  this.deps.cancelAnimation();
1784
1839
  this.isDragging_ = true;
1785
1840
  this.dragStartSize = this.deps.getDragContext().size;
@@ -1790,6 +1845,7 @@ var GestureController = class {
1790
1845
  this.deps.emit("dragstart", { size: this.dragStartSize });
1791
1846
  },
1792
1847
  onMove: (delta) => {
1848
+ if (this.dragSuppressed) return;
1793
1849
  const ctx = this.deps.getDragContext();
1794
1850
  const { min, max } = ctx.range;
1795
1851
  const maxAxis = ctx.maxAxisSize;
@@ -1813,6 +1869,10 @@ var GestureController = class {
1813
1869
  }
1814
1870
  },
1815
1871
  onEnd: (delta, velocity, kind) => {
1872
+ if (this.dragSuppressed) {
1873
+ this.dragSuppressed = false;
1874
+ return;
1875
+ }
1816
1876
  this.isDragging_ = false;
1817
1877
  this.keyboardDismissed = false;
1818
1878
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -1823,6 +1883,10 @@ var GestureController = class {
1823
1883
  this.deps.settleAfterDrag(delta, velocity, kind);
1824
1884
  },
1825
1885
  onCancel: () => {
1886
+ if (this.dragSuppressed) {
1887
+ this.dragSuppressed = false;
1888
+ return;
1889
+ }
1826
1890
  this.isDragging_ = false;
1827
1891
  this.keyboardDismissed = false;
1828
1892
  this.deps.getRoot()?.removeAttribute("data-dragging");
@@ -2202,6 +2266,12 @@ function resolveEngineOptions(opts, extras) {
2202
2266
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2203
2267
  rubberBandEnabled: opts.rubberBand ?? true,
2204
2268
  closeOnBack: opts.closeOnBack ?? false,
2269
+ persistent: opts.persistent ?? false,
2270
+ disableClose: opts.disableClose ?? false,
2271
+ disableDrag: opts.disableDrag ?? false,
2272
+ closeOnRouteChange: opts.closeOnRouteChange ?? false,
2273
+ radius: opts.radius,
2274
+ maxHeight: opts.maxHeight,
2205
2275
  initialAllowed,
2206
2276
  initialId,
2207
2277
  scrim: {
@@ -2228,7 +2298,8 @@ function resolveEngineOptions(opts, extras) {
2228
2298
  closeOnEscape: opts.closeOnEscape,
2229
2299
  lockBodyScroll: opts.lockBodyScroll,
2230
2300
  inertSiblings: opts.inertSiblings,
2231
- shouldApplyInertSiblings: extras?.shouldApplyInertSiblings
2301
+ shouldApplyInertSiblings: extras?.shouldApplyInertSiblings,
2302
+ returnFocus: opts.returnFocusTo
2232
2303
  }
2233
2304
  };
2234
2305
  }
@@ -2273,6 +2344,11 @@ var BottomSheetEngine = class {
2273
2344
  __publicField(this, "lifecycle");
2274
2345
  __publicField(this, "closeOnBack");
2275
2346
  __publicField(this, "routedTo");
2347
+ __publicField(this, "persistent");
2348
+ __publicField(this, "disableCloseFlag");
2349
+ __publicField(this, "disableDragFlag");
2350
+ __publicField(this, "closeOnRouteChange");
2351
+ __publicField(this, "maxHeightCap");
2276
2352
  __publicField(this, "snapPointsRaw");
2277
2353
  __publicField(this, "snaps");
2278
2354
  __publicField(this, "activeId");
@@ -2285,6 +2361,7 @@ var BottomSheetEngine = class {
2285
2361
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2286
2362
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2287
2363
  __publicField(this, "isTopSheet", true);
2364
+ __publicField(this, "opening", false);
2288
2365
  __publicField(this, "progressPayload", {
2289
2366
  value: 0,
2290
2367
  size: 0
@@ -2341,7 +2418,8 @@ var BottomSheetEngine = class {
2341
2418
  void this.snapTo(id);
2342
2419
  },
2343
2420
  close: () => {
2344
- void this.close();
2421
+ if (!this.canDismiss()) return;
2422
+ void this.close("backdrop");
2345
2423
  }
2346
2424
  },
2347
2425
  resolved.scrim
@@ -2365,6 +2443,10 @@ var BottomSheetEngine = class {
2365
2443
  );
2366
2444
  this.closeOnBack = resolved.closeOnBack;
2367
2445
  this.routedTo = opts.routedTo;
2446
+ this.persistent = resolved.persistent;
2447
+ this.disableCloseFlag = resolved.disableClose;
2448
+ this.disableDragFlag = resolved.disableDrag;
2449
+ this.closeOnRouteChange = resolved.closeOnRouteChange;
2368
2450
  this.stackEffectEnabled = opts.stackEffect ?? false;
2369
2451
  this.snapPointsRaw = opts.snapPoints;
2370
2452
  this.persistKey = opts.persistKey;
@@ -2378,7 +2460,7 @@ var BottomSheetEngine = class {
2378
2460
  this.snapPointsRaw,
2379
2461
  resolved.initialAllowed,
2380
2462
  this.mode,
2381
- () => layoutAxis(this.mode) === "width" ? this.handle.offsetWidth : this.handle.offsetHeight,
2463
+ () => this.measureFitSize(),
2382
2464
  (maxAxisSize) => {
2383
2465
  this.element.style[layoutAxis(this.mode)] = `${maxAxisSize}px`;
2384
2466
  }
@@ -2412,6 +2494,19 @@ var BottomSheetEngine = class {
2412
2494
  io.observe(this.element);
2413
2495
  this.teardowns.add(() => io.disconnect());
2414
2496
  }
2497
+ this.installFitObserver();
2498
+ if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2499
+ if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2500
+ this.applyAutoAriaLabelledBy();
2501
+ if (this.closeOnRouteChange) {
2502
+ this.teardowns.add(
2503
+ installRouteChange({
2504
+ isDestroyed: () => this.destroyed,
2505
+ getSize: () => this.size,
2506
+ close: () => this.close()
2507
+ })
2508
+ );
2509
+ }
2415
2510
  if (this.persistKey) {
2416
2511
  this.teardowns.add(installPersist(this, this.persistKey));
2417
2512
  }
@@ -2436,7 +2531,7 @@ var BottomSheetEngine = class {
2436
2531
  isTopSheet: () => this.isTopSheet,
2437
2532
  getSize: () => this.size,
2438
2533
  isDestroyed: () => this.destroyed,
2439
- close: () => this.close(),
2534
+ close: () => this.close("back"),
2440
2535
  on: (event, fn) => this.on(event, fn),
2441
2536
  sheetId: this.id
2442
2537
  })
@@ -2530,6 +2625,10 @@ var BottomSheetEngine = class {
2530
2625
  const previousSize = this.size;
2531
2626
  this.scrollCache.cache(previousId, previousSize, target.size);
2532
2627
  this.activeId = id;
2628
+ if (wasClosed && target.size > 0) {
2629
+ this.opening = true;
2630
+ sheetStack.promote(this.id);
2631
+ }
2533
2632
  if (this.animation.viewTransitionsAvailable) {
2534
2633
  this.currentViewTransition?.skipTransition?.();
2535
2634
  const vt = document.startViewTransition(() => {
@@ -2544,19 +2643,26 @@ var BottomSheetEngine = class {
2544
2643
  } else {
2545
2644
  await this.animation.animateTo(target.size, velocityPxPerMs);
2546
2645
  }
2646
+ this.opening = false;
2547
2647
  externalSignal?.removeEventListener("abort", onExternalAbort);
2548
2648
  if (signal.aborted) return;
2549
2649
  this.scrollCache.restore(id, previousSize, target.size);
2550
2650
  this.updateAriaSlider();
2551
- this.emit("snap", { id, size: target.size });
2651
+ this.emit("snap", {
2652
+ id,
2653
+ size: target.size,
2654
+ progress: this.computeProgress(target.size)
2655
+ });
2552
2656
  if (wasClosed && target.size > 0) {
2553
2657
  this.emit("open", { id });
2554
2658
  this.handleOpen();
2555
2659
  notifyLinkedSheets(this.linkedSheets, this);
2660
+ this.emit("opened", { id });
2556
2661
  }
2557
2662
  if (target.size === 0) {
2558
2663
  this.emit("close", void 0);
2559
2664
  this.handleClose();
2665
+ this.emit("closed", void 0);
2560
2666
  }
2561
2667
  }
2562
2668
  async dragTo(targetSize, velocityOrOpts = 0) {
@@ -2581,11 +2687,74 @@ var BottomSheetEngine = class {
2581
2687
  const target = id ?? this.snaps.getAllowedIds().find((a) => a !== "closed") ?? this.activeId;
2582
2688
  return this.snapTo(target);
2583
2689
  }
2584
- close() {
2690
+ close(reason = "programmatic") {
2585
2691
  if (this.destroyed) return Promise.resolve();
2692
+ if (this.disableCloseFlag) return Promise.resolve();
2693
+ if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
2586
2694
  const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds()[0];
2587
2695
  return this.snapTo(closedId ?? this.activeId);
2588
2696
  }
2697
+ canDismiss() {
2698
+ return !this.persistent && !this.disableCloseFlag && !this.destroyed;
2699
+ }
2700
+ isTop() {
2701
+ return this.isTopSheet;
2702
+ }
2703
+ depth() {
2704
+ return sheetStack.depthOf(this.id);
2705
+ }
2706
+ expand() {
2707
+ if (this.destroyed) return Promise.resolve();
2708
+ const ids = this.allowedIdsBySize();
2709
+ const target = ids[ids.length - 1];
2710
+ if (!target) return Promise.resolve();
2711
+ return this.snapTo(target);
2712
+ }
2713
+ collapse() {
2714
+ if (this.destroyed) return Promise.resolve();
2715
+ const ids = this.allowedIdsBySize();
2716
+ const nonZero = ids.find((id) => (this.snaps.findById(id)?.size ?? 0) > 0);
2717
+ const target = nonZero ?? ids[0];
2718
+ if (!target) return Promise.resolve();
2719
+ return this.snapTo(target);
2720
+ }
2721
+ setRadius(r) {
2722
+ if (this.destroyed) return;
2723
+ this.element.style.setProperty(
2724
+ "--bs-radius",
2725
+ typeof r === "number" ? `${r}px` : r
2726
+ );
2727
+ }
2728
+ setMaxHeight(h) {
2729
+ if (this.destroyed) return;
2730
+ const value = typeof h === "number" ? `${h}px` : h;
2731
+ const axis = layoutAxis(this.mode);
2732
+ this.element.style.setProperty(
2733
+ axis === "height" ? "maxHeight" : "maxWidth",
2734
+ value
2735
+ );
2736
+ if (typeof h === "number") {
2737
+ this.maxHeightCap = h;
2738
+ this.clampToMaxHeight();
2739
+ } else if (typeof window !== "undefined") {
2740
+ const probe2 = document.createElement("div");
2741
+ probe2.style.cssText = `position:absolute;visibility:hidden;pointer-events:none;${axis === "height" ? "height" : "width"}:${value};`;
2742
+ document.body.appendChild(probe2);
2743
+ const measured = axis === "height" ? probe2.offsetHeight : probe2.offsetWidth;
2744
+ probe2.remove();
2745
+ this.maxHeightCap = measured > 0 ? measured : void 0;
2746
+ this.clampToMaxHeight();
2747
+ }
2748
+ }
2749
+ clampToMaxHeight() {
2750
+ if (this.maxHeightCap === void 0) return;
2751
+ if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2752
+ this.snaps.setMaxAxisSize(this.maxHeightCap);
2753
+ }
2754
+ if (this.size > this.maxHeightCap && !this.gesture?.isDragging) {
2755
+ this.applySize(this.maxHeightCap);
2756
+ }
2757
+ }
2589
2758
  setAllowed(ids, snap) {
2590
2759
  if (this.destroyed) return;
2591
2760
  this.snaps.setAllowedIds(ids);
@@ -2719,8 +2888,53 @@ var BottomSheetEngine = class {
2719
2888
  }
2720
2889
  this.updateAriaSlider();
2721
2890
  }
2891
+ recompute() {
2892
+ if (this.destroyed) return;
2893
+ this.snaps.recompute();
2894
+ this.scrim.invalidateOpacityCache();
2895
+ if (this.gesture?.isDragging) return;
2896
+ const current = this.snaps.findById(this.activeId);
2897
+ if (current) {
2898
+ this.size = current.size;
2899
+ this.applySize(this.size);
2900
+ this.updateAriaSlider();
2901
+ }
2902
+ }
2903
+ measureFitSize() {
2904
+ const vertical = layoutAxis(this.mode) === "height";
2905
+ const handleSize = vertical ? this.handle.offsetHeight : this.handle.offsetWidth;
2906
+ const content = this.scrollContainer;
2907
+ const natural = content ? handleSize + (vertical ? content.scrollHeight : content.scrollWidth) : handleSize;
2908
+ if (typeof window === "undefined") return natural;
2909
+ const viewport = vertical ? window.innerHeight : window.innerWidth;
2910
+ return viewport > 0 ? Math.min(natural, viewport) : natural;
2911
+ }
2912
+ installFitObserver() {
2913
+ if (typeof ResizeObserver === "undefined") return;
2914
+ if (!this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"))
2915
+ return;
2916
+ const targets = [this.handle];
2917
+ if (this.scrollContainer && this.scrollContainer !== this.handle) {
2918
+ targets.push(this.scrollContainer);
2919
+ }
2920
+ let raf = 0;
2921
+ const ro = new ResizeObserver(() => {
2922
+ if (raf) return;
2923
+ raf = requestAnimationFrame(() => {
2924
+ raf = 0;
2925
+ if (this.destroyed || this.gesture?.isDragging) return;
2926
+ this.recompute();
2927
+ });
2928
+ });
2929
+ for (const t of targets) ro.observe(t);
2930
+ this.teardowns.add(() => {
2931
+ ro.disconnect();
2932
+ if (raf) cancelAnimationFrame(raf);
2933
+ });
2934
+ }
2722
2935
  destroy() {
2723
2936
  this.destroyed = true;
2937
+ this.opening = false;
2724
2938
  this.currentAbort.abort();
2725
2939
  this.currentViewTransition?.skipTransition?.();
2726
2940
  this.currentViewTransition = null;
@@ -2770,6 +2984,36 @@ var BottomSheetEngine = class {
2770
2984
  frozen = true;
2771
2985
  return cancelled;
2772
2986
  }
2987
+ emitBeforeClose(reason) {
2988
+ let cancelled = false;
2989
+ let frozen = false;
2990
+ this.emit("before-close", {
2991
+ reason,
2992
+ cancel: () => {
2993
+ if (frozen) {
2994
+ console.warn(
2995
+ "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
2996
+ );
2997
+ return;
2998
+ }
2999
+ cancelled = true;
3000
+ }
3001
+ });
3002
+ frozen = true;
3003
+ return cancelled;
3004
+ }
3005
+ applyAutoAriaLabelledBy() {
3006
+ if (typeof document === "undefined") return;
3007
+ if (!this.lifecycle.focusTrapEnabled) return;
3008
+ if (this.element.getAttribute("aria-labelledby")) return;
3009
+ const scope = this.handle ?? this.element;
3010
+ const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3011
+ if (!titled) return;
3012
+ if (!titled.id) {
3013
+ titled.id = nextInstanceId("bs-title");
3014
+ }
3015
+ this.element.setAttribute("aria-labelledby", titled.id);
3016
+ }
2773
3017
  recomputeSnaps() {
2774
3018
  const seen = /* @__PURE__ */ new Set();
2775
3019
  for (const p of this.snapPointsRaw) {
@@ -2805,11 +3049,8 @@ var BottomSheetEngine = class {
2805
3049
  },
2806
3050
  setIsTop: (isTop) => {
2807
3051
  this.isTopSheet = isTop;
2808
- if (this.backdrop) {
2809
- this.backdrop.style.display = isTop ? "" : "none";
2810
- }
2811
3052
  },
2812
- isOpen: () => this.size > 0,
3053
+ isOpen: () => this.size > 0 || this.opening,
2813
3054
  setDepth: (depth) => this.applyStackDepth(depth)
2814
3055
  }));
2815
3056
  }
@@ -2841,6 +3082,7 @@ var BottomSheetEngine = class {
2841
3082
  buf.rubberBandEnabled = this.rubberBandEnabled;
2842
3083
  return buf;
2843
3084
  },
3085
+ getDisableDrag: () => this.disableDragFlag,
2844
3086
  cancelAnimation: () => this.animation.cancel(),
2845
3087
  applySize: (size) => this.applySize(size),
2846
3088
  animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
@@ -2920,8 +3162,9 @@ var BottomSheetEngine = class {
2920
3162
  if (this.lifecycle.closeOnEscape) {
2921
3163
  const onKey = (e) => {
2922
3164
  if (this.destroyed || e.defaultPrevented) return;
3165
+ if (!this.canDismiss()) return;
2923
3166
  if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
2924
- void this.close();
3167
+ void this.close("escape");
2925
3168
  }
2926
3169
  };
2927
3170
  document.addEventListener("keydown", onKey);
@@ -2984,20 +3227,31 @@ var BottomSheetEngine = class {
2984
3227
  const previousSnapSize = this.snaps.findById(previousId)?.size;
2985
3228
  this.scrollCache.cache(previousId, previousSize, target.size);
2986
3229
  this.activeId = target.id;
3230
+ if (previousSnapSize === 0 && target.size > 0) {
3231
+ this.opening = true;
3232
+ sheetStack.promote(this.id);
3233
+ }
2987
3234
  void this.animation.animateTo(target.size, velocity).then(() => {
3235
+ this.opening = false;
2988
3236
  if (signal.aborted) return;
2989
3237
  this.scrollCache.restore(target.id, previousSize, target.size);
2990
3238
  this.updateAriaSlider();
2991
- this.emit("snap", { id: target.id, size: target.size });
3239
+ this.emit("snap", {
3240
+ id: target.id,
3241
+ size: target.size,
3242
+ progress: this.computeProgress(target.size)
3243
+ });
2992
3244
  this.haptic();
2993
3245
  if (previousSnapSize === 0 && target.size > 0) {
2994
3246
  this.emit("open", { id: target.id });
2995
3247
  this.handleOpen();
2996
3248
  notifyLinkedSheets(this.linkedSheets, this);
3249
+ this.emit("opened", { id: target.id });
2997
3250
  }
2998
3251
  if (target.size === 0) {
2999
3252
  this.emit("close", void 0);
3000
3253
  this.handleClose();
3254
+ this.emit("closed", void 0);
3001
3255
  }
3002
3256
  });
3003
3257
  }
@@ -3022,6 +3276,9 @@ var BottomSheetEngine = class {
3022
3276
  if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3023
3277
  this.anchorHost.style.setProperty("--bs-size", `${size}px`);
3024
3278
  }
3279
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3280
+ this.rootEl.style.setProperty("--bs-size", `${size}px`);
3281
+ }
3025
3282
  }
3026
3283
  const progress = this.computeProgress(size);
3027
3284
  const progressChanged = this.progressWriteSentinel.shouldWrite(
@@ -3033,6 +3290,9 @@ var BottomSheetEngine = class {
3033
3290
  if (this.anchorHost) {
3034
3291
  this.anchorHost.style.setProperty("--bs-progress", String(progress));
3035
3292
  }
3293
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3294
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3295
+ }
3036
3296
  }
3037
3297
  this.scrim.applyOpacity(progress, progressChanged);
3038
3298
  if (progressChanged && this.bus.listenerCount("progress") > 0) {
@@ -3066,17 +3326,24 @@ var BottomSheetEngine = class {
3066
3326
  const target = this.snaps.findById(this.activeId);
3067
3327
  if (!target) return;
3068
3328
  this.updateAriaSlider();
3069
- this.emit("snap", { id: target.id, size: target.size });
3329
+ this.emit("snap", {
3330
+ id: target.id,
3331
+ size: target.size,
3332
+ progress: this.computeProgress(target.size)
3333
+ });
3070
3334
  if (target.size > 0 && !this.lifecycle.isInstalled) {
3071
3335
  this.emit("open", { id: target.id });
3072
3336
  this.handleOpen();
3073
3337
  notifyLinkedSheets(this.linkedSheets, this);
3338
+ this.emit("opened", { id: target.id });
3074
3339
  } else if (target.size === 0 && this.lifecycle.isInstalled) {
3075
3340
  this.emit("close", void 0);
3076
3341
  this.handleClose();
3342
+ this.emit("closed", void 0);
3077
3343
  }
3078
3344
  }
3079
3345
  handleClose() {
3346
+ this.opening = false;
3080
3347
  this.lifecycle.release();
3081
3348
  sheetStack.update();
3082
3349
  }