@surdeddd/bottom-sheet 0.23.3 → 0.24.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 (80) hide show
  1. package/README.md +513 -525
  2. package/README.ru.md +685 -0
  3. package/dist/{BottomSheetCore-COvILxmR.d.ts → BottomSheetCore-B_r4KCQL.d.cts} +48 -12
  4. package/dist/{BottomSheetCore-CaJwHnxY.d.cts → BottomSheetCore-B_r4KCQL.d.ts} +48 -12
  5. package/dist/{BottomSheetEngine-C_1aPJsU.d.cts → BottomSheetEngine-0F7mjyvF.d.cts} +1 -1
  6. package/dist/{BottomSheetEngine-CsgCcCSO.d.ts → BottomSheetEngine-CJV_DDrM.d.ts} +1 -1
  7. package/dist/core.cjs +810 -436
  8. package/dist/core.d.cts +2 -3
  9. package/dist/core.d.ts +2 -3
  10. package/dist/core.js +810 -436
  11. package/dist/debug.d.cts +3 -3
  12. package/dist/debug.d.ts +3 -3
  13. package/dist/{default-features-BJx7J0NH.d.ts → default-features-4Xwv6aVt.d.ts} +4 -2
  14. package/dist/{default-features-CTJb8Wdk.d.cts → default-features-DX2NOxP6.d.cts} +4 -2
  15. package/dist/element.cjs +1428 -699
  16. package/dist/element.d.cts +15 -5
  17. package/dist/element.d.ts +15 -5
  18. package/dist/element.iife.global.js +2 -2
  19. package/dist/element.js +1428 -699
  20. package/dist/features.cjs +334 -190
  21. package/dist/features.d.cts +3 -4
  22. package/dist/features.d.ts +3 -4
  23. package/dist/features.js +334 -191
  24. package/dist/{index-CXN7t3rr.d.ts → index-2shlxhcG.d.cts} +3 -2
  25. package/dist/{index-BBrJJjy5.d.cts → index-CNCRQnpc.d.ts} +3 -2
  26. package/dist/index.cjs +1129 -631
  27. package/dist/index.d.cts +13 -7
  28. package/dist/index.d.ts +13 -7
  29. package/dist/index.js +1129 -632
  30. package/dist/integrations/formik.cjs +21 -10
  31. package/dist/integrations/formik.d.cts +3 -3
  32. package/dist/integrations/formik.d.ts +3 -3
  33. package/dist/integrations/formik.js +22 -11
  34. package/dist/integrations/react-hook-form.cjs +21 -12
  35. package/dist/integrations/react-hook-form.d.cts +3 -3
  36. package/dist/integrations/react-hook-form.d.ts +3 -3
  37. package/dist/integrations/react-hook-form.js +21 -12
  38. package/dist/option-keys-DItuAjUw.d.cts +6 -0
  39. package/dist/option-keys-a5DYNwwO.d.ts +6 -0
  40. package/dist/overlay.cjs +386 -189
  41. package/dist/overlay.d.cts +3 -4
  42. package/dist/overlay.d.ts +3 -4
  43. package/dist/overlay.js +386 -189
  44. package/dist/preact.d.cts +3 -4
  45. package/dist/preact.d.ts +3 -4
  46. package/dist/qwik-lib/index.qwik.cjs +1306 -671
  47. package/dist/qwik-lib/index.qwik.mjs +1306 -671
  48. package/dist/qwik.cjs +1326 -701
  49. package/dist/qwik.d.cts +14 -30
  50. package/dist/qwik.d.ts +14 -30
  51. package/dist/qwik.js +1326 -701
  52. package/dist/react.cjs +1373 -750
  53. package/dist/react.d.cts +31 -14
  54. package/dist/react.d.ts +31 -14
  55. package/dist/react.js +1375 -752
  56. package/dist/solid.cjs +1332 -727
  57. package/dist/solid.d.cts +13 -437
  58. package/dist/solid.d.ts +13 -437
  59. package/dist/solid.js +1334 -729
  60. package/dist/solid.source.jsx +1374 -751
  61. package/dist/styles.css +20 -6
  62. package/dist/svelte-core.cjs +1211 -676
  63. package/dist/svelte-core.js +1207 -677
  64. package/dist/svelte-src/BottomSheet.svelte +102 -59
  65. package/dist/svelte.cjs +1324 -707
  66. package/dist/svelte.d.cts +45 -25
  67. package/dist/svelte.d.ts +45 -25
  68. package/dist/svelte.js +1324 -707
  69. package/dist/vue.cjs +1354 -773
  70. package/dist/vue.d.cts +20 -3
  71. package/dist/vue.d.ts +20 -3
  72. package/dist/vue.js +1354 -773
  73. package/dist/webgl.cjs +38 -7
  74. package/dist/webgl.d.cts +3 -3
  75. package/dist/webgl.d.ts +3 -3
  76. package/dist/webgl.js +38 -7
  77. package/llms.txt +330 -0
  78. package/package.json +62 -42
  79. package/dist/transform-jVfAikE9.d.cts +0 -3
  80. package/dist/transform-jVfAikE9.d.ts +0 -3
package/dist/index.js CHANGED
@@ -2,6 +2,15 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
 
5
+ // src/core/primitives/shared.ts
6
+ var STORE_KEY = /* @__PURE__ */ Symbol.for("@surdeddd/bottom-sheet/shared/1");
7
+ function shared(name, init) {
8
+ const host = globalThis;
9
+ const store = host[STORE_KEY] ?? (host[STORE_KEY] = {});
10
+ if (!(name in store)) store[name] = init();
11
+ return store[name];
12
+ }
13
+
5
14
  // src/core/lifecycle/sheet-stack.ts
6
15
  var BASE_Z = 100;
7
16
  var STEP = 10;
@@ -69,7 +78,7 @@ var SheetStack = class {
69
78
  });
70
79
  }
71
80
  };
72
- var sheetStack = new SheetStack();
81
+ var sheetStack = shared("stack", () => new SheetStack());
73
82
 
74
83
  // src/core/primitives/transform.ts
75
84
  function buildTransformTemplate(axis) {
@@ -88,7 +97,7 @@ var offsetFromMatrix = (axis, m) => axis === "bottom" ? m.m42 : axis === "top" ?
88
97
  var layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
89
98
 
90
99
  // src/core/primitives/instance-id.ts
91
- var counters = {};
100
+ var counters = shared("instanceIds", () => ({}));
92
101
  function nextInstanceId(prefix) {
93
102
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
94
103
  return `${prefix}-${crypto.randomUUID()}`;
@@ -116,7 +125,13 @@ function createEventBus() {
116
125
  const set = listeners.get(event);
117
126
  if (!set) return;
118
127
  for (const fn of [...set]) {
119
- fn(payload);
128
+ try {
129
+ fn(payload);
130
+ } catch (err) {
131
+ queueMicrotask(() => {
132
+ throw err;
133
+ });
134
+ }
120
135
  }
121
136
  },
122
137
  listenerCount(event) {
@@ -128,8 +143,23 @@ function createEventBus() {
128
143
  };
129
144
  }
130
145
 
146
+ // src/core/primitives/dev-warn.ts
147
+ function devWarn(...args) {
148
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
149
+ console.warn(...args);
150
+ }
151
+ }
152
+
131
153
  // src/core/primitives/css-length.ts
132
154
  var probe = null;
155
+ var rejected = /* @__PURE__ */ new Set();
156
+ var warnRejected = (value) => {
157
+ if (rejected.has(value)) return;
158
+ rejected.add(value);
159
+ devWarn(
160
+ `[BottomSheet] snap size "${value}" is not a CSS length the browser accepts \u2014 it resolves to 0.`
161
+ );
162
+ };
133
163
  var PROBE_ATTR = "data-bs-probe";
134
164
  var ensureProbe = (axis) => {
135
165
  if (typeof document === "undefined") return null;
@@ -176,19 +206,14 @@ var resolveSnap = (point, mode, measureFit) => {
176
206
  if (!el) return 0;
177
207
  if (axis === "vertical") {
178
208
  el.style.height = point;
209
+ if (el.style.height === "") warnRejected(point);
179
210
  return Math.max(0, el.getBoundingClientRect().height);
180
211
  }
181
212
  el.style.width = point;
213
+ if (el.style.width === "") warnRejected(point);
182
214
  return Math.max(0, el.getBoundingClientRect().width);
183
215
  };
184
216
 
185
- // src/core/primitives/dev-warn.ts
186
- function devWarn(...args) {
187
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
188
- console.warn(...args);
189
- }
190
- }
191
-
192
217
  // src/core/primitives/snap-points.ts
193
218
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
194
219
  const raw = resolveSnap(p.size, mode, measureFit);
@@ -317,6 +342,7 @@ var SnapResolver = class {
317
342
  setMode(mode) {
318
343
  if (mode === this.mode) return;
319
344
  this.mode = mode;
345
+ this.notified = false;
320
346
  this.recompute();
321
347
  }
322
348
  setAllowedIds(ids) {
@@ -326,14 +352,20 @@ var SnapResolver = class {
326
352
  findById(id) {
327
353
  return findById(id, this.resolved);
328
354
  }
329
- findDragSettleTarget(args) {
355
+ findDragSettleTarget(args, openOnly = false) {
330
356
  return findDragSettleTarget({
331
357
  ...args,
332
358
  resolved: this.resolved,
333
- allowed: this.allowedIds,
359
+ allowed: this.pool(openOnly),
334
360
  maxAxisSize: this.maxAxisSize
335
361
  });
336
362
  }
363
+ nearest(size, openOnly) {
364
+ return findNearest(size, this.resolved, this.pool(openOnly));
365
+ }
366
+ pool(openOnly) {
367
+ return openOnly ? this.allowedIds.filter((id) => (this.findById(id)?.size ?? 0) > 0) : this.allowedIds;
368
+ }
337
369
  getAllowedRange() {
338
370
  return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
339
371
  }
@@ -381,19 +413,20 @@ var POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
381
413
  var RANGE_DIVISION_EPSILON = 1e-4;
382
414
 
383
415
  // src/core/features/resize-observer.ts
416
+ var seatOf = (deps) => {
417
+ const snap = deps.resolveActiveSnap();
418
+ const max = deps.getMaxAxisSize();
419
+ return { target: snap ? Math.min(snap.size, max) : null, max };
420
+ };
421
+ var sameSeat = (before, after) => before.target !== null && after.target !== null && Math.abs(after.target - before.target) < SIZE_WRITE_EPSILON && Math.abs(after.max - before.max) < SIZE_WRITE_EPSILON;
384
422
  function installResizeObserver(deps) {
385
423
  if (typeof window === "undefined") {
386
424
  return () => {
387
425
  };
388
426
  }
389
- const seatFor = () => {
390
- const snap = deps.resolveActiveSnap();
391
- const max = deps.getMaxAxisSize();
392
- return { target: snap ? Math.min(snap.size, max) : null, max };
393
- };
394
427
  const onResize = () => {
395
428
  if (deps.isDestroyed()) return;
396
- const before = seatFor();
429
+ const before = seatOf(deps);
397
430
  deps.recomputeSnaps();
398
431
  const mode = deps.getMode();
399
432
  const isVerticalAxis = mode === "bottom" || mode === "top";
@@ -402,21 +435,8 @@ function installResizeObserver(deps) {
402
435
  if (viewportSize2 > 0 && viewportSize2 < max) {
403
436
  deps.setMaxAxisSize(viewportSize2);
404
437
  }
405
- const after = seatFor();
406
- if (before.target !== null && after.target !== null && Math.abs(after.target - before.target) < SIZE_WRITE_EPSILON && Math.abs(after.max - before.max) < SIZE_WRITE_EPSILON) {
407
- return;
408
- }
409
- const wasAnimating = deps.isAnimating?.() ?? false;
410
- deps.cancelInFlight();
411
- deps.newCycle();
412
- const current = deps.resolveActiveSnap();
413
- const newMax = deps.getMaxAxisSize();
414
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
415
- deps.setSize(targetSize);
416
- if (current && !deps.isDragging()) {
417
- deps.applySize(targetSize);
418
- if (wasAnimating) deps.resyncAfterCancel?.();
419
- }
438
+ if (sameSeat(before, seatOf(deps))) return;
439
+ deps.reseat();
420
440
  };
421
441
  let resizeObserver = null;
422
442
  if (typeof ResizeObserver !== "undefined") {
@@ -428,7 +448,7 @@ function installResizeObserver(deps) {
428
448
  window.addEventListener("orientationchange", onResize);
429
449
  const onVisibility = () => {
430
450
  if (deps.isDestroyed()) return;
431
- if (document.hidden) deps.cancelInFlight();
451
+ if (document.hidden) deps.landNow();
432
452
  };
433
453
  document.addEventListener("visibilitychange", onVisibility);
434
454
  return () => {
@@ -453,6 +473,16 @@ var contentTargets = (scroller) => {
453
473
  }
454
474
  return out;
455
475
  };
476
+ var chromeTargets = (deps) => {
477
+ const out = [];
478
+ for (const child of Array.from(deps.element.children)) {
479
+ if (child !== deps.scrollContainer) out.push(child);
480
+ }
481
+ if (deps.handle !== deps.element && !out.includes(deps.handle)) {
482
+ out.push(deps.handle);
483
+ }
484
+ return out;
485
+ };
456
486
  function installFitObserver(deps) {
457
487
  if (typeof ResizeObserver === "undefined") return () => {
458
488
  };
@@ -468,7 +498,6 @@ function installFitObserver(deps) {
468
498
  });
469
499
  };
470
500
  const ro = new ResizeObserver(schedule);
471
- ro.observe(deps.handle);
472
501
  const scroller = deps.scrollContainer;
473
502
  const observed = /* @__PURE__ */ new Set();
474
503
  const slots = /* @__PURE__ */ new Set();
@@ -485,7 +514,7 @@ function installFitObserver(deps) {
485
514
  };
486
515
  const resync = () => {
487
516
  trackSlots();
488
- const targets = contentTargets(scroller);
517
+ const targets = [...chromeTargets(deps), ...contentTargets(scroller)];
489
518
  for (const el of observed) {
490
519
  if (!targets.includes(el)) {
491
520
  ro.unobserve(el);
@@ -500,13 +529,12 @@ function installFitObserver(deps) {
500
529
  }
501
530
  schedule();
502
531
  };
532
+ resync();
503
533
  let mo;
504
- if (scroller) {
505
- resync();
506
- if (typeof MutationObserver !== "undefined") {
507
- mo = new MutationObserver(resync);
508
- mo.observe(scroller, { childList: true });
509
- }
534
+ if (typeof MutationObserver !== "undefined") {
535
+ mo = new MutationObserver(resync);
536
+ mo.observe(deps.element, { childList: true });
537
+ if (scroller) mo.observe(scroller, { childList: true });
510
538
  }
511
539
  return () => {
512
540
  ro.disconnect();
@@ -518,6 +546,10 @@ function installFitObserver(deps) {
518
546
  };
519
547
  }
520
548
 
549
+ // src/core/features/teleport.ts
550
+ var homes = /* @__PURE__ */ new WeakMap();
551
+ var homeRootOf = (root) => (root && homes.get(root)) ?? null;
552
+
521
553
  // src/core/features/max-height-controller.ts
522
554
  function createMaxHeightController(deps) {
523
555
  let maxHeightCap;
@@ -621,8 +653,9 @@ function installSliderKeyboard(deps) {
621
653
  const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
622
654
  const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
623
655
  const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
624
- const sheetExpandsKey = deps.mode === "top" ? stepDown : stepUp;
625
- const sheetCollapsesKey = deps.mode === "top" ? stepUp : stepDown;
656
+ const flipped = deps.mode === "top" || deps.mode === "right";
657
+ const sheetExpandsKey = flipped ? stepDown : stepUp;
658
+ const sheetCollapsesKey = flipped ? stepUp : stepDown;
626
659
  const onHandleKey = (e) => {
627
660
  if (deps.isDestroyed()) return;
628
661
  const allowed = deps.getAllowedIds();
@@ -698,11 +731,12 @@ var WriteSentinel = class {
698
731
  // src/core/primitives/overlay-position.ts
699
732
  function resolveSheetAnchoredStyle(mode, position, inset) {
700
733
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
734
+ const lifted = `calc(var(--bs-size, 0px) + var(--bs-keyboard-inset, 0px) + ${inset})`;
701
735
  switch (mode) {
702
736
  case "bottom":
703
- if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
704
- if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
705
- return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
737
+ if (position === "sheet-top-left") return { bottom: lifted, left: inset };
738
+ if (position === "sheet-top-right") return { bottom: lifted, right: inset };
739
+ return { bottom: lifted, left: "50%", transform: "translateX(-50%)" };
706
740
  case "top":
707
741
  if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
708
742
  if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
@@ -717,6 +751,13 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
717
751
  return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
718
752
  }
719
753
  }
754
+ function resetOverlayPosition(ws) {
755
+ ws.top = "";
756
+ ws.bottom = "";
757
+ ws.left = "";
758
+ ws.right = "";
759
+ ws.transform = "";
760
+ }
720
761
  function applyOverlayPosition(ws, mode, position, inset) {
721
762
  switch (position) {
722
763
  case "top-left":
@@ -777,6 +818,8 @@ function applyOverlayPosition(ws, mode, position, inset) {
777
818
  // src/core/controllers/scrim-controller.ts
778
819
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
779
820
  var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
821
+ var BACKDROP_COLOR_VAR = "--bs-backdrop-color";
822
+ var unit = (value) => Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : 1;
780
823
  var ScrimController = class {
781
824
  constructor(deps, opts) {
782
825
  __publicField(this, "screenComponent");
@@ -791,11 +834,13 @@ var ScrimController = class {
791
834
  __publicField(this, "scrimMode");
792
835
  __publicField(this, "backdropRange");
793
836
  __publicField(this, "screenRange");
837
+ __publicField(this, "backdropPeak");
794
838
  __publicField(this, "scrimEnabled", true);
795
839
  __publicField(this, "savedScrimRanges", null);
796
840
  __publicField(this, "scrimTapToCloseEnabled", false);
797
841
  __publicField(this, "detachScrimTap", null);
798
842
  __publicField(this, "scrimOverlayTeardown", null);
843
+ __publicField(this, "placeOverlay", null);
799
844
  __publicField(this, "backdropOpacitySentinel", new WriteSentinel());
800
845
  __publicField(this, "screenOpacitySentinel", new WriteSentinel());
801
846
  __publicField(this, "lastBackdropPointer", "");
@@ -812,6 +857,8 @@ var ScrimController = class {
812
857
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
813
858
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
814
859
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
860
+ this.backdropPeak = unit(opts.backdropOpacity ?? 1);
861
+ this.setBackdropColor(opts.backdropColor);
815
862
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
816
863
  const explicitColor = opts.scrimColor ?? preset?.color;
817
864
  const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
@@ -841,24 +888,19 @@ var ScrimController = class {
841
888
  this.setScrimTapToClose(true);
842
889
  }
843
890
  }
844
- applyOpacity(progress, progressChanged) {
845
- if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
891
+ applyOpacity(progress, progressChanged, epsilon = OPACITY_WRITE_EPSILON) {
892
+ if (epsilon > 0 && !progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
846
893
  return;
847
894
  }
848
895
  if (this.backdrop) {
849
896
  const [s, e] = this.backdropRange;
850
897
  const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
851
- const backdropOpacity = Math.min(
852
- Math.max((progress - s) / range, 0),
853
- 1
854
- );
855
- if (this.backdropOpacitySentinel.shouldWrite(
856
- backdropOpacity,
857
- OPACITY_WRITE_EPSILON
858
- )) {
898
+ const along = Math.min(Math.max((progress - s) / range, 0), 1);
899
+ const backdropOpacity = along * this.backdropPeak;
900
+ if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, epsilon)) {
859
901
  this.backdrop.style.opacity = String(backdropOpacity);
860
902
  }
861
- const nextPointer = backdropOpacity > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
903
+ const nextPointer = along > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
862
904
  if (nextPointer !== this.lastBackdropPointer) {
863
905
  this.backdrop.style.pointerEvents = nextPointer;
864
906
  this.lastBackdropPointer = nextPointer;
@@ -873,10 +915,7 @@ var ScrimController = class {
873
915
  (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)
874
916
  )
875
917
  );
876
- if (this.screenOpacitySentinel.shouldWrite(
877
- screenOpacity,
878
- OPACITY_WRITE_EPSILON
879
- )) {
918
+ if (this.screenOpacitySentinel.shouldWrite(screenOpacity, epsilon)) {
880
919
  this.screenComponent.style.opacity = String(screenOpacity);
881
920
  }
882
921
  const nextDisplay = screenOpacity > 0 ? "" : "none";
@@ -892,6 +931,17 @@ var ScrimController = class {
892
931
  this.invalidateOpacityCache();
893
932
  applySize();
894
933
  }
934
+ setBackdropColor(color) {
935
+ if (this.isDestroyed() || !this.backdrop) return;
936
+ if (color) this.backdrop.style.setProperty(BACKDROP_COLOR_VAR, color);
937
+ else this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
938
+ }
939
+ setBackdropOpacity(value, applySize) {
940
+ if (this.isDestroyed()) return;
941
+ this.backdropPeak = unit(value);
942
+ this.invalidateOpacityCache();
943
+ applySize();
944
+ }
895
945
  setScreenRange(range, applySize) {
896
946
  if (this.isDestroyed()) return;
897
947
  this.screenRange = range;
@@ -914,6 +964,8 @@ var ScrimController = class {
914
964
  if (this.isDestroyed() || mode === this.mode) return;
915
965
  this.mode = mode;
916
966
  this.invalidateOpacityCache();
967
+ if (this.scrimMode === "above-sheet") this.applyAboveSheetInset();
968
+ this.placeOverlay?.();
917
969
  }
918
970
  setScrimMode(mode, applySize) {
919
971
  if (this.isDestroyed()) return;
@@ -926,7 +978,7 @@ var ScrimController = class {
926
978
  if (previous === "above-sheet" && mode !== "above-sheet") {
927
979
  s.position = "";
928
980
  s.inset = "";
929
- s.pointerEvents = "";
981
+ this.syncPointer();
930
982
  }
931
983
  if (mode === "above-sheet") {
932
984
  this.applyAboveSheetInset();
@@ -951,9 +1003,6 @@ var ScrimController = class {
951
1003
  if (enabled === installed) return;
952
1004
  if (enabled) {
953
1005
  const target = this.screenComponent;
954
- if (target.style.pointerEvents === "none") {
955
- target.style.pointerEvents = "auto";
956
- }
957
1006
  const onClick = () => {
958
1007
  if (this.isDestroyed()) return;
959
1008
  if (this.closeFn) {
@@ -976,6 +1025,11 @@ var ScrimController = class {
976
1025
  this.detachScrimTap = null;
977
1026
  this.scrimTapToCloseEnabled = false;
978
1027
  }
1028
+ this.syncPointer();
1029
+ }
1030
+ syncPointer() {
1031
+ if (!this.screenComponent) return;
1032
+ this.screenComponent.style.pointerEvents = this.scrimInteractive || this.detachScrimTap ? "auto" : "none";
979
1033
  }
980
1034
  setScrimEnabled(enabled, applySize) {
981
1035
  if (this.isDestroyed()) return;
@@ -1059,8 +1113,12 @@ var ScrimController = class {
1059
1113
  const wrapper = screen.ownerDocument.createElement("div");
1060
1114
  wrapper.className = "bs-scrim-overlay";
1061
1115
  const ws = wrapper.style;
1062
- ws.position = "absolute";
1063
- applyOverlayPosition(ws, this.mode, position, inset);
1116
+ ws.position = "fixed";
1117
+ const place = () => {
1118
+ resetOverlayPosition(ws);
1119
+ applyOverlayPosition(ws, this.mode, position, inset);
1120
+ };
1121
+ place();
1064
1122
  if (interactive) {
1065
1123
  ws.pointerEvents = "auto";
1066
1124
  }
@@ -1074,8 +1132,10 @@ var ScrimController = class {
1074
1132
  };
1075
1133
  }
1076
1134
  host.appendChild(wrapper);
1135
+ this.placeOverlay = place;
1077
1136
  const teardown = () => {
1078
1137
  if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1138
+ if (this.placeOverlay === place) this.placeOverlay = null;
1079
1139
  if (this.scrimOverlayTeardown === teardown) {
1080
1140
  this.scrimOverlayTeardown = null;
1081
1141
  }
@@ -1099,6 +1159,7 @@ var ScrimController = class {
1099
1159
  if (this.backdrop) {
1100
1160
  this.backdrop.style.opacity = "";
1101
1161
  this.backdrop.style.pointerEvents = "";
1162
+ this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1102
1163
  }
1103
1164
  this.invalidateOpacityCache();
1104
1165
  this.lastBackdropPointer = "";
@@ -1123,13 +1184,13 @@ var ScrimController = class {
1123
1184
  }
1124
1185
  if (opts.interactive !== void 0) {
1125
1186
  this.scrimInteractive = opts.interactive;
1126
- s.pointerEvents = opts.interactive ? "auto" : "none";
1187
+ this.syncPointer();
1127
1188
  }
1128
1189
  }
1129
1190
  applyAboveSheetInset() {
1130
1191
  if (!this.screenComponent) return;
1131
1192
  const insetByMode = {
1132
- bottom: "0 0 var(--bs-size) 0",
1193
+ bottom: "0 0 calc(var(--bs-size) + var(--bs-keyboard-inset, 0px)) 0",
1133
1194
  top: "var(--bs-size) 0 0 0",
1134
1195
  left: "0 0 0 var(--bs-size)",
1135
1196
  right: "0 var(--bs-size) 0 0"
@@ -1137,7 +1198,7 @@ var ScrimController = class {
1137
1198
  const s = this.screenComponent.style;
1138
1199
  s.position = "fixed";
1139
1200
  s.inset = insetByMode[this.mode];
1140
- s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1201
+ this.syncPointer();
1141
1202
  }
1142
1203
  };
1143
1204
 
@@ -1150,6 +1211,7 @@ var easeOutBack = (t) => {
1150
1211
  var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1151
1212
  var easeLinear = (t) => t;
1152
1213
  var easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
1214
+ var MIN_RETARGET_MS = 120;
1153
1215
  var tween = ({
1154
1216
  from,
1155
1217
  to,
@@ -1158,12 +1220,18 @@ var tween = ({
1158
1220
  onUpdate
1159
1221
  }) => {
1160
1222
  let cancelled = false;
1223
+ let finished = false;
1161
1224
  let rafId = 0;
1162
1225
  let resolveFn = null;
1163
- const start = performance.now();
1226
+ let start = performance.now();
1227
+ let origin = from;
1228
+ let target = to;
1229
+ let span = duration;
1230
+ let last = from;
1164
1231
  const promise = new Promise((resolve) => {
1165
1232
  resolveFn = resolve;
1166
1233
  if (duration <= 0) {
1234
+ finished = true;
1167
1235
  onUpdate(to);
1168
1236
  resolve();
1169
1237
  return;
@@ -1171,12 +1239,13 @@ var tween = ({
1171
1239
  const step = (now) => {
1172
1240
  if (cancelled) return;
1173
1241
  const elapsed = Math.max(0, now - start);
1174
- const t = Math.min(elapsed / duration, 1);
1175
- const value = from + (to - from) * easing(t);
1176
- onUpdate(value);
1242
+ const t = Math.min(elapsed / span, 1);
1243
+ last = origin + (target - origin) * easing(t);
1244
+ onUpdate(last);
1177
1245
  if (t < 1) {
1178
1246
  rafId = requestAnimationFrame(step);
1179
1247
  } else {
1248
+ finished = true;
1180
1249
  resolve();
1181
1250
  }
1182
1251
  };
@@ -1188,6 +1257,15 @@ var tween = ({
1188
1257
  cancelAnimationFrame(rafId);
1189
1258
  resolveFn?.();
1190
1259
  },
1260
+ retarget: (next) => {
1261
+ if (cancelled || finished || next === target) return;
1262
+ const now = performance.now();
1263
+ const remaining = span - Math.max(0, now - start);
1264
+ origin = last;
1265
+ target = next;
1266
+ start = now;
1267
+ span = Math.max(remaining, MIN_RETARGET_MS);
1268
+ },
1191
1269
  promise
1192
1270
  };
1193
1271
  };
@@ -1205,22 +1283,32 @@ var DEFAULT_SPRING = {
1205
1283
  restSpeed: 0.5
1206
1284
  };
1207
1285
  var MAX_DT = 1 / 30;
1286
+ var MAX_DURATION_MS = 3e3;
1208
1287
  var runSpring = ({
1209
1288
  from,
1210
1289
  to,
1211
1290
  velocity = 0,
1212
1291
  config,
1292
+ maxDuration = MAX_DURATION_MS,
1213
1293
  onUpdate
1214
1294
  }) => {
1215
1295
  const cfg = { ...DEFAULT_SPRING, ...config };
1216
1296
  let cancelled = false;
1297
+ let finished = false;
1217
1298
  let rafId = 0;
1218
1299
  let resolveFn = null;
1219
1300
  let x = from;
1220
1301
  let v = velocity;
1302
+ let target = to;
1221
1303
  let lastTime = performance.now();
1304
+ let deadline = lastTime + maxDuration;
1222
1305
  const promise = new Promise((resolve) => {
1223
1306
  resolveFn = resolve;
1307
+ const land = () => {
1308
+ finished = true;
1309
+ onUpdate(target, 0);
1310
+ resolve();
1311
+ };
1224
1312
  const step = (now) => {
1225
1313
  if (cancelled) return;
1226
1314
  const dt = Math.min(Math.max(0, now - lastTime) / 1e3, MAX_DT);
@@ -1229,21 +1317,19 @@ var runSpring = ({
1229
1317
  const subDt = dt / steps;
1230
1318
  let stepsLeft = steps;
1231
1319
  while (stepsLeft-- > 0) {
1232
- const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1320
+ const force = -cfg.stiffness * (x - target) - cfg.damping * v;
1233
1321
  const accel = force / cfg.mass;
1234
1322
  v += accel * subDt;
1235
1323
  x += v * subDt;
1236
1324
  }
1237
- if (!Number.isFinite(x) || !Number.isFinite(v)) {
1238
- onUpdate(to, 0);
1239
- resolve();
1325
+ if (!Number.isFinite(x) || !Number.isFinite(v) || lastTime >= deadline) {
1326
+ land();
1240
1327
  return;
1241
1328
  }
1242
1329
  onUpdate(x, v);
1243
- const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1330
+ const isAtRest = Math.abs(target - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1244
1331
  if (isAtRest) {
1245
- onUpdate(to, 0);
1246
- resolve();
1332
+ land();
1247
1333
  } else {
1248
1334
  rafId = requestAnimationFrame(step);
1249
1335
  }
@@ -1256,6 +1342,11 @@ var runSpring = ({
1256
1342
  cancelAnimationFrame(rafId);
1257
1343
  resolveFn?.();
1258
1344
  },
1345
+ retarget: (next) => {
1346
+ if (cancelled || finished) return;
1347
+ target = next;
1348
+ deadline = performance.now() + maxDuration;
1349
+ },
1259
1350
  promise
1260
1351
  };
1261
1352
  };
@@ -1407,21 +1498,40 @@ var AnimationRunner = class {
1407
1498
  this.applySizeFn(size);
1408
1499
  }
1409
1500
  cancel() {
1410
- this.currentTween?.cancel();
1411
- this.currentSpring?.cancel();
1501
+ const tween2 = this.currentTween;
1502
+ const spring = this.currentSpring;
1503
+ this.currentTween = null;
1504
+ this.currentSpring = null;
1505
+ tween2?.cancel();
1506
+ spring?.cancel();
1412
1507
  this.cancelWaapi();
1413
1508
  }
1509
+ retarget(target) {
1510
+ if (this.currentSpring) {
1511
+ this.currentSpring.retarget(target);
1512
+ return true;
1513
+ }
1514
+ if (this.currentTween) {
1515
+ this.currentTween.retarget(target);
1516
+ return true;
1517
+ }
1518
+ if (this.currentWaapi) {
1519
+ this.currentWaapi.retarget(target);
1520
+ return true;
1521
+ }
1522
+ return false;
1523
+ }
1414
1524
  async animateTo(target, velocityPxPerMs) {
1415
1525
  if (target === this.getSize()) {
1416
- if (!this.isAnimating && !this.isDragging()) {
1526
+ if (this.isAnimating) this.cancel();
1527
+ if (!this.isDragging()) {
1417
1528
  this.applySizeFn(target);
1529
+ this.getRootEl()?.removeAttribute("data-animating");
1418
1530
  this.element.style.willChange = "";
1419
1531
  }
1420
1532
  return;
1421
1533
  }
1422
- this.currentTween?.cancel();
1423
- this.currentSpring?.cancel();
1424
- this.cancelWaapi();
1534
+ this.cancel();
1425
1535
  this.getRootEl()?.setAttribute("data-animating", "true");
1426
1536
  this.element.style.willChange = "transform";
1427
1537
  if (this.reducedMotion) {
@@ -1431,19 +1541,13 @@ var AnimationRunner = class {
1431
1541
  return;
1432
1542
  }
1433
1543
  if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1434
- const samples = this.animationKind === "spring" ? sampleSpringSettle(
1435
- this.getSize(),
1436
- target,
1437
- velocityPxPerMs * VELOCITY_PX_PER_S,
1438
- this.springConfig
1439
- ) : sampleTweenSettle(
1544
+ const samples = this.sampleSettle(
1440
1545
  this.getSize(),
1441
1546
  target,
1442
- this.duration,
1443
- this.easing
1547
+ velocityPxPerMs * VELOCITY_PX_PER_S
1444
1548
  );
1445
1549
  if (samples.values.length >= 2 && samples.durationMs > 0) {
1446
- await this.runWaapiSettle(samples, target);
1550
+ await this.runWaapiSettle({ samples, target });
1447
1551
  if (!this.isAnimating) {
1448
1552
  this.getRootEl()?.removeAttribute("data-animating");
1449
1553
  if (!this.isDragging()) this.element.style.willChange = "";
@@ -1485,7 +1589,10 @@ var AnimationRunner = class {
1485
1589
  }
1486
1590
  }
1487
1591
  }
1488
- async runWaapiSettle(samples, target) {
1592
+ sampleSettle(from, to, velocityPxPerS) {
1593
+ return this.animationKind === "spring" ? sampleSpringSettle(from, to, velocityPxPerS, this.springConfig) : sampleTweenSettle(from, to, this.duration, this.easing);
1594
+ }
1595
+ startWaapiLeg(samples, onRetarget) {
1489
1596
  const applyAux = this.applyAux;
1490
1597
  const getTransformFor = this.getTransformFor;
1491
1598
  const frames = samples.values.map((v) => ({
@@ -1500,16 +1607,20 @@ var AnimationRunner = class {
1500
1607
  let stopped = false;
1501
1608
  let auxRaf = 0;
1502
1609
  const lastIdx = samples.values.length - 1;
1610
+ const cursor = () => Math.min((Number(anim.currentTime) || 0) / samples.stepMs, lastIdx);
1503
1611
  const sizeNow = () => {
1504
- const at = Math.min(
1505
- (Number(anim.currentTime) || 0) / samples.stepMs,
1506
- lastIdx
1507
- );
1612
+ const at = cursor();
1508
1613
  const idx = Math.floor(at);
1509
1614
  const from = samples.values[idx];
1510
1615
  const to = samples.values[Math.min(idx + 1, lastIdx)];
1511
1616
  return from + (to - from) * (at - idx);
1512
1617
  };
1618
+ const velocityNow = () => {
1619
+ const idx = Math.min(Math.floor(cursor()), Math.max(lastIdx - 1, 0));
1620
+ const from = samples.values[idx];
1621
+ const to = samples.values[Math.min(idx + 1, lastIdx)];
1622
+ return (to - from) / samples.stepMs * 1e3;
1623
+ };
1513
1624
  const auxTick = () => {
1514
1625
  if (stopped) return;
1515
1626
  applyAux(sizeNow());
@@ -1522,22 +1633,55 @@ var AnimationRunner = class {
1522
1633
  stopped = true;
1523
1634
  cancelAnimationFrame(auxRaf);
1524
1635
  },
1525
- sizeNow
1636
+ sizeNow,
1637
+ velocityNow,
1638
+ retarget: (to) => onRetarget(entry, to)
1526
1639
  };
1527
- this.currentWaapi = entry;
1528
- try {
1529
- await anim.finished;
1530
- } catch {
1531
- }
1532
- entry.stop();
1533
- const wasCurrent = this.currentWaapi === entry;
1534
- if (wasCurrent) this.currentWaapi = null;
1535
- if (wasCurrent && anim.playState === "finished") {
1536
- this.applySizeFn(target);
1640
+ return entry;
1641
+ }
1642
+ async runWaapiSettle(first) {
1643
+ let leg = first;
1644
+ for (; ; ) {
1645
+ let queued = null;
1646
+ const entry = this.startWaapiLeg(leg.samples, (running, to) => {
1647
+ const size = this.readShownSize?.() ?? running.sizeNow();
1648
+ queued = {
1649
+ samples: this.sampleSettle(size, to, running.velocityNow()),
1650
+ target: to
1651
+ };
1652
+ running.stop();
1653
+ try {
1654
+ running.anim.cancel();
1655
+ } catch {
1656
+ }
1657
+ this.applySizeFn(size);
1658
+ });
1659
+ this.currentWaapi = entry;
1537
1660
  try {
1538
- anim.cancel();
1661
+ await entry.anim.finished;
1539
1662
  } catch {
1540
1663
  }
1664
+ entry.stop();
1665
+ const wasCurrent = this.currentWaapi === entry;
1666
+ const next = queued;
1667
+ if (wasCurrent && next) {
1668
+ if (next.samples.values.length >= 2 && next.samples.durationMs > 0) {
1669
+ leg = next;
1670
+ continue;
1671
+ }
1672
+ this.currentWaapi = null;
1673
+ this.applySizeFn(next.target);
1674
+ return;
1675
+ }
1676
+ if (wasCurrent) this.currentWaapi = null;
1677
+ if (wasCurrent && entry.anim.playState === "finished") {
1678
+ this.applySizeFn(leg.target);
1679
+ try {
1680
+ entry.anim.cancel();
1681
+ } catch {
1682
+ }
1683
+ }
1684
+ return;
1541
1685
  }
1542
1686
  }
1543
1687
  destroy() {
@@ -1592,27 +1736,51 @@ var collectFocusables = (root) => {
1592
1736
  var focusables = (root) => collectFocusables(root).filter(
1593
1737
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1594
1738
  );
1739
+ var findDeep = (root, selector) => {
1740
+ const direct = root.querySelector(selector);
1741
+ if (direct || typeof HTMLSlotElement === "undefined") return direct;
1742
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1743
+ for (const assigned of slot.assignedElements()) {
1744
+ if (assigned.matches(selector)) return assigned;
1745
+ const inner = findDeep(assigned, selector);
1746
+ if (inner) return inner;
1747
+ }
1748
+ }
1749
+ return null;
1750
+ };
1751
+ var FOCUS_ALLOW_ATTR = "data-bs-focus-allow";
1752
+ var composedParent = (node) => {
1753
+ const slot = node.assignedSlot;
1754
+ if (slot) return slot;
1755
+ const parent = node.parentNode;
1756
+ return parent instanceof ShadowRoot ? parent.host : parent;
1757
+ };
1595
1758
  var containsComposed = (container, node) => {
1596
- let n = node;
1597
- while (n) {
1759
+ for (let n = node; n; n = composedParent(n)) {
1598
1760
  if (n === container) return true;
1599
- const slot = n.assignedSlot;
1600
- if (slot) {
1601
- n = slot;
1602
- continue;
1603
- }
1604
- const parent = n.parentNode;
1605
- n = parent instanceof ShadowRoot ? parent.host : parent;
1606
1761
  }
1607
1762
  return false;
1608
1763
  };
1609
- var trapStack = [];
1764
+ var isAllowedOutside = (node) => {
1765
+ for (let n = node; n; n = composedParent(n)) {
1766
+ if (n instanceof Element && n.hasAttribute(FOCUS_ALLOW_ATTR)) return true;
1767
+ }
1768
+ return false;
1769
+ };
1770
+ var deepActiveElement = () => {
1771
+ let active = document.activeElement;
1772
+ while (active?.shadowRoot?.activeElement) {
1773
+ active = active.shadowRoot.activeElement;
1774
+ }
1775
+ return active;
1776
+ };
1777
+ var trapStack = shared("focusTraps", () => []);
1610
1778
  var installFocusTrap = (container, options = {}) => {
1611
1779
  if (typeof document === "undefined") return () => {
1612
1780
  };
1613
1781
  const token = {};
1614
- trapStack.push(token);
1615
- const isActive = () => trapStack[trapStack.length - 1] === token;
1782
+ if (!options.isActive) trapStack.push(token);
1783
+ const isActive = options.isActive ?? (() => trapStack[trapStack.length - 1] === token);
1616
1784
  const previouslyFocused = document.activeElement;
1617
1785
  const focusInitial = () => {
1618
1786
  const spec = options.initialFocus;
@@ -1621,10 +1789,11 @@ var installFocusTrap = (container, options = {}) => {
1621
1789
  container.focus({ preventScroll: true });
1622
1790
  return;
1623
1791
  }
1624
- const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1792
+ const initial = typeof spec === "string" ? findDeep(container, spec) : spec ?? focusables(container)[0] ?? container;
1625
1793
  initial?.focus({ preventScroll: true });
1626
1794
  };
1627
- focusInitial();
1795
+ if (!containsComposed(container, deepActiveElement())) focusInitial();
1796
+ let backwards = false;
1628
1797
  const handleKey = (e) => {
1629
1798
  if (!isActive()) return;
1630
1799
  if (e.key === "Escape" && options.onEscape) {
@@ -1632,6 +1801,10 @@ var installFocusTrap = (container, options = {}) => {
1632
1801
  return;
1633
1802
  }
1634
1803
  if (e.key !== "Tab") return;
1804
+ backwards = e.shiftKey;
1805
+ setTimeout(() => {
1806
+ backwards = false;
1807
+ }, 0);
1635
1808
  const list = focusables(container);
1636
1809
  if (list.length === 0) {
1637
1810
  e.preventDefault();
@@ -1639,7 +1812,7 @@ var installFocusTrap = (container, options = {}) => {
1639
1812
  }
1640
1813
  const first = list[0];
1641
1814
  const last = list[list.length - 1];
1642
- const active = document.activeElement;
1815
+ const active = deepActiveElement();
1643
1816
  if (e.shiftKey && active === first) {
1644
1817
  e.preventDefault();
1645
1818
  last.focus();
@@ -1650,11 +1823,11 @@ var installFocusTrap = (container, options = {}) => {
1650
1823
  };
1651
1824
  const handleFocusIn = (e) => {
1652
1825
  if (!isActive()) return;
1653
- const target = e.target;
1654
- if (target && !containsComposed(container, target)) {
1655
- const list = focusables(container);
1656
- list[0]?.focus({ preventScroll: true });
1657
- }
1826
+ const target = e.composedPath()[0] ?? e.target;
1827
+ if (!target || containsComposed(container, target)) return;
1828
+ if (isAllowedOutside(target)) return;
1829
+ const list = focusables(container);
1830
+ list[backwards ? list.length - 1 : 0]?.focus({ preventScroll: true });
1658
1831
  };
1659
1832
  document.addEventListener("keydown", handleKey, true);
1660
1833
  document.addEventListener("focusin", handleFocusIn, true);
@@ -1676,21 +1849,25 @@ var installFocusTrap = (container, options = {}) => {
1676
1849
  };
1677
1850
 
1678
1851
  // src/core/lifecycle/scroll-lock.ts
1679
- var lockCount = 0;
1680
- var savedStyles = null;
1852
+ var state = shared("scrollLock", () => ({
1853
+ count: 0,
1854
+ saved: null
1855
+ }));
1681
1856
  var lockBodyScroll = () => {
1682
1857
  if (typeof document === "undefined") return () => {
1683
1858
  };
1684
- if (lockCount === 0) {
1859
+ if (state.count === 0) {
1685
1860
  const body = document.body;
1686
- savedStyles = {
1861
+ const saved = {
1687
1862
  overflow: body.style.overflow,
1688
1863
  position: body.style.position,
1689
1864
  top: body.style.top,
1690
1865
  width: body.style.width,
1691
1866
  paddingRight: body.style.paddingRight,
1867
+ scrollX: window.scrollX,
1692
1868
  scrollY: window.scrollY
1693
1869
  };
1870
+ state.saved = saved;
1694
1871
  const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1695
1872
  if (scrollbarGap > 0) {
1696
1873
  const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
@@ -1698,53 +1875,72 @@ var lockBodyScroll = () => {
1698
1875
  }
1699
1876
  body.style.overflow = "hidden";
1700
1877
  body.style.position = "fixed";
1701
- body.style.top = `-${savedStyles.scrollY}px`;
1878
+ body.style.top = `-${saved.scrollY}px`;
1702
1879
  body.style.width = "100%";
1703
1880
  }
1704
- lockCount++;
1881
+ state.count++;
1705
1882
  let released = false;
1706
1883
  return () => {
1707
1884
  if (released) return;
1708
1885
  released = true;
1709
- lockCount = Math.max(0, lockCount - 1);
1710
- if (lockCount === 0 && savedStyles) {
1886
+ state.count = Math.max(0, state.count - 1);
1887
+ if (state.count === 0 && state.saved) {
1711
1888
  const body = document.body;
1712
- body.style.overflow = savedStyles.overflow;
1713
- body.style.position = savedStyles.position;
1714
- body.style.top = savedStyles.top;
1715
- body.style.width = savedStyles.width;
1716
- body.style.paddingRight = savedStyles.paddingRight;
1717
- window.scrollTo(0, savedStyles.scrollY);
1718
- savedStyles = null;
1889
+ const saved = state.saved;
1890
+ body.style.overflow = saved.overflow;
1891
+ body.style.position = saved.position;
1892
+ body.style.top = saved.top;
1893
+ body.style.width = saved.width;
1894
+ body.style.paddingRight = saved.paddingRight;
1895
+ window.scrollTo({
1896
+ top: saved.scrollY,
1897
+ left: saved.scrollX,
1898
+ behavior: "instant"
1899
+ });
1900
+ state.saved = null;
1719
1901
  }
1720
1902
  };
1721
1903
  };
1722
1904
 
1723
1905
  // src/core/features/inert-siblings.ts
1906
+ var OWN_ROOTS = ".bs-root, .ovl-root";
1907
+ var LAYER_CLASSES = [
1908
+ "bs-backdrop",
1909
+ "bs-screen",
1910
+ "bs-root",
1911
+ "ovl-backdrop",
1912
+ "ovl-root"
1913
+ ];
1914
+ var isLayer = (el) => LAYER_CLASSES.some((name) => el.classList.contains(name));
1724
1915
  function createInertSiblings(rootProvider) {
1725
1916
  const tracked = [];
1917
+ const remove = () => {
1918
+ for (const el of tracked) el.removeAttribute("inert");
1919
+ tracked.length = 0;
1920
+ };
1726
1921
  return {
1727
1922
  apply() {
1728
1923
  if (typeof document === "undefined") return;
1729
- let top = rootProvider();
1730
- while (top.parentElement && top.parentElement !== document.body) {
1731
- top = top.parentElement;
1732
- }
1733
- const parent = top.parentElement ?? document.body;
1734
- for (const child of Array.from(parent.children)) {
1735
- if (child === top) continue;
1736
- if (child.hasAttribute("inert")) continue;
1737
- if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1924
+ remove();
1925
+ const surface = rootProvider();
1926
+ let node = surface.closest(OWN_ROOTS) ?? surface;
1927
+ while (node && node !== document.body) {
1928
+ const parent = node.parentElement;
1929
+ if (!parent) {
1930
+ const root = node.getRootNode();
1931
+ node = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
1738
1932
  continue;
1739
1933
  }
1740
- child.setAttribute("inert", "");
1741
- tracked.push(child);
1934
+ for (const sibling of Array.from(parent.children)) {
1935
+ if (sibling === node) continue;
1936
+ if (sibling.hasAttribute("inert") || isLayer(sibling)) continue;
1937
+ sibling.setAttribute("inert", "");
1938
+ tracked.push(sibling);
1939
+ }
1940
+ node = parent;
1742
1941
  }
1743
1942
  },
1744
- remove() {
1745
- for (const el of tracked) el.removeAttribute("inert");
1746
- tracked.length = 0;
1747
- }
1943
+ remove
1748
1944
  };
1749
1945
  }
1750
1946
 
@@ -1752,6 +1948,7 @@ function createInertSiblings(rootProvider) {
1752
1948
  var LifecycleController = class {
1753
1949
  constructor(deps, opts) {
1754
1950
  __publicField(this, "element");
1951
+ __publicField(this, "isTop");
1755
1952
  __publicField(this, "focusTrapEnabled");
1756
1953
  __publicField(this, "initialFocus");
1757
1954
  __publicField(this, "closeOnEscape");
@@ -1765,6 +1962,7 @@ var LifecycleController = class {
1765
1962
  __publicField(this, "installed", false);
1766
1963
  __publicField(this, "destroyed", false);
1767
1964
  this.element = deps.element;
1965
+ this.isTop = deps.isTop;
1768
1966
  this.focusTrapEnabled = opts.focusTrap ?? false;
1769
1967
  this.initialFocus = opts.initialFocus;
1770
1968
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1785,12 +1983,17 @@ var LifecycleController = class {
1785
1983
  }
1786
1984
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1787
1985
  this.releaseFocusTrap = installFocusTrap(this.element, {
1788
- initialFocus: this.initialFocus
1986
+ initialFocus: this.initialFocus,
1987
+ isActive: this.isTop
1789
1988
  });
1790
1989
  }
1791
- if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
1792
- this.inertSiblings.apply();
1793
- }
1990
+ this.syncTop();
1991
+ }
1992
+ syncTop() {
1993
+ if (!this.installed || !this.inertSiblingsEnabled) return;
1994
+ if (!this.shouldApplyInertSiblings()) return;
1995
+ if (this.isTop?.() ?? true) this.inertSiblings.apply();
1996
+ else this.inertSiblings.remove();
1794
1997
  }
1795
1998
  setReturnFocus(target) {
1796
1999
  if (this.destroyed) return;
@@ -1960,10 +2163,14 @@ var installGestures = (handle, mode, callbacks, options = {}) => {
1960
2163
  if (wasPending) return;
1961
2164
  callbacks.onCancel?.();
1962
2165
  };
2166
+ const onLostCapture = (e) => {
2167
+ if (e.target === handle) onPointerCancel(e);
2168
+ };
1963
2169
  handle.addEventListener("pointerdown", onPointerDown);
1964
2170
  handle.addEventListener("pointermove", onPointerMove);
1965
2171
  handle.addEventListener("pointerup", finishGesture);
1966
2172
  handle.addEventListener("pointercancel", onPointerCancel);
2173
+ handle.addEventListener("lostpointercapture", onLostCapture);
1967
2174
  const managesTouchAction = options.manageTouchAction !== false;
1968
2175
  const prevTouchAction = handle.style.touchAction;
1969
2176
  if (managesTouchAction) {
@@ -1974,6 +2181,7 @@ var installGestures = (handle, mode, callbacks, options = {}) => {
1974
2181
  handle.removeEventListener("pointermove", onPointerMove);
1975
2182
  handle.removeEventListener("pointerup", finishGesture);
1976
2183
  handle.removeEventListener("pointercancel", onPointerCancel);
2184
+ handle.removeEventListener("lostpointercapture", onLostCapture);
1977
2185
  if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1978
2186
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1979
2187
  handle.releasePointerCapture(activePointerId);
@@ -2090,8 +2298,7 @@ var GestureController = class {
2090
2298
  size: this.deps.getDragContext().size,
2091
2299
  velocity: 0
2092
2300
  });
2093
- const restoreTo = this.dragStartSize;
2094
- void this.deps.animateTo(restoreTo, 0);
2301
+ this.deps.restoreAfterDrag();
2095
2302
  }
2096
2303
  }, this.deps.gestureOptions);
2097
2304
  return this.detach;
@@ -2208,22 +2415,24 @@ function createScrollCache(deps) {
2208
2415
  return size < max / 2;
2209
2416
  };
2210
2417
  return {
2211
- cache(fromId, prevSize, nextSize) {
2418
+ leave(fromId, prevSize, nextSize) {
2212
2419
  const container = deps.scrollContainer;
2213
2420
  if (!container) return;
2214
- if (isSmallSize(prevSize)) return;
2215
- if (!isSmallSize(nextSize)) return;
2421
+ if (nextSize <= 0) {
2422
+ positions.clear();
2423
+ return;
2424
+ }
2425
+ if (isSmallSize(prevSize) || !isSmallSize(nextSize)) return;
2216
2426
  positions.set(fromId, container.scrollTop);
2217
2427
  },
2218
- restore(toId, prevSize, nextSize) {
2428
+ enter(toId, prevSize, nextSize) {
2219
2429
  const container = deps.scrollContainer;
2220
2430
  if (!container) return;
2221
- if (isSmallSize(nextSize)) return;
2222
- if (!isSmallSize(prevSize)) return;
2431
+ if (isSmallSize(nextSize) || !isSmallSize(prevSize)) return;
2223
2432
  const cached = positions.get(toId);
2224
2433
  if (cached === void 0) return;
2225
- container.scrollTop = cached;
2226
2434
  positions.delete(toId);
2435
+ container.scrollTop = cached;
2227
2436
  },
2228
2437
  clear() {
2229
2438
  positions.clear();
@@ -2337,17 +2546,18 @@ function attachAnchor(deps, opts) {
2337
2546
  const isDock = position === "dock-bottom" || position === "dock-top";
2338
2547
  const ws = wrapper.style;
2339
2548
  ws.position = "fixed";
2340
- if (isDock) {
2549
+ const place = () => {
2550
+ resetOverlayPosition(ws);
2551
+ if (!isDock) {
2552
+ applyOverlayPosition(ws, deps.getMode(), position, opts.inset ?? "16px");
2553
+ return;
2554
+ }
2341
2555
  ws.left = "0";
2342
2556
  ws.right = "0";
2343
- if (position === "dock-bottom") {
2344
- ws.bottom = "0";
2345
- } else {
2346
- ws.top = "0";
2347
- }
2348
- } else {
2349
- applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2350
- }
2557
+ if (position === "dock-bottom") ws.bottom = "0";
2558
+ else ws.top = "0";
2559
+ };
2560
+ place();
2351
2561
  if (opts.fadeRange) {
2352
2562
  const [r0, r1] = opts.fadeRange;
2353
2563
  const span = Math.max(r1 - r0, 1e-4);
@@ -2357,10 +2567,10 @@ function attachAnchor(deps, opts) {
2357
2567
  deps.host.appendChild(wrapper);
2358
2568
  let visible = null;
2359
2569
  let inFlight = null;
2360
- const shouldShow = (state) => {
2361
- if (typeof opts.showOn === "function") return opts.showOn(state);
2362
- if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2363
- return isDock || state.size > 0;
2570
+ const shouldShow = (state3) => {
2571
+ if (typeof opts.showOn === "function") return opts.showOn(state3);
2572
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state3.activeId);
2573
+ return isDock || state3.size > 0;
2364
2574
  };
2365
2575
  const applyVisibility = (next, animate) => {
2366
2576
  if (next === visible) return;
@@ -2407,7 +2617,8 @@ function attachAnchor(deps, opts) {
2407
2617
  },
2408
2618
  syncZ: (z) => {
2409
2619
  wrapper.style.zIndex = String(z);
2410
- }
2620
+ },
2621
+ reposition: place
2411
2622
  };
2412
2623
  }
2413
2624
 
@@ -2423,30 +2634,37 @@ var matchesRange = (def, progress) => {
2423
2634
  };
2424
2635
  function installScrimStages(deps, opts) {
2425
2636
  const doc = deps.host.ownerDocument;
2426
- const entries2 = opts.stages.map((def) => {
2637
+ const entries = opts.stages.map((def) => {
2427
2638
  const wrapper = doc.createElement("div");
2428
2639
  wrapper.className = "bs-scrim-stage";
2429
2640
  const ws = wrapper.style;
2430
- ws.position = "absolute";
2431
- applyOverlayPosition(
2432
- ws,
2433
- deps.mode,
2434
- def.position ?? opts.position ?? "center",
2435
- def.inset ?? opts.inset ?? "16px"
2436
- );
2641
+ ws.position = "fixed";
2437
2642
  ws.visibility = "hidden";
2438
2643
  ws.pointerEvents = "none";
2439
2644
  wrapper.appendChild(def.element);
2440
2645
  deps.host.appendChild(wrapper);
2441
2646
  return { def, wrapper, inFlight: null };
2442
2647
  });
2648
+ const place = () => {
2649
+ for (const entry of entries) {
2650
+ const ws = entry.wrapper.style;
2651
+ resetOverlayPosition(ws);
2652
+ applyOverlayPosition(
2653
+ ws,
2654
+ deps.getMode(),
2655
+ entry.def.position ?? opts.position ?? "center",
2656
+ entry.def.inset ?? opts.inset ?? "16px"
2657
+ );
2658
+ }
2659
+ };
2660
+ place();
2443
2661
  let active = null;
2444
2662
  const resolveActive = () => {
2445
- const state = deps.getState();
2446
- if (state.size === 0) {
2447
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2663
+ const state3 = deps.getState();
2664
+ if (state3.size === 0) {
2665
+ return entries.find((e) => matchesId(e.def, state3.activeId)) ?? null;
2448
2666
  }
2449
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2667
+ return entries.find((e) => matchesId(e.def, state3.activeId)) ?? entries.find((e) => matchesRange(e.def, state3.progress)) ?? null;
2450
2668
  };
2451
2669
  const hide = (entry, animate) => {
2452
2670
  entry.inFlight?.cancel();
@@ -2495,20 +2713,23 @@ function installScrimStages(deps, opts) {
2495
2713
  deps.on("open", () => update(true)),
2496
2714
  deps.on("close", () => update(true))
2497
2715
  ];
2498
- if (entries2.some((e) => e.def.forRange)) {
2716
+ if (entries.some((e) => e.def.forRange)) {
2499
2717
  offs.push(deps.on("progress", () => update(true)));
2500
2718
  }
2501
2719
  update(false);
2502
- return () => {
2503
- offs.forEach((off) => off());
2504
- for (const entry of entries2) {
2505
- entry.inFlight?.cancel();
2506
- entry.inFlight = null;
2507
- if (entry.wrapper.parentNode) {
2508
- entry.wrapper.parentNode.removeChild(entry.wrapper);
2720
+ return {
2721
+ reposition: place,
2722
+ detach: () => {
2723
+ offs.forEach((off) => off());
2724
+ for (const entry of entries) {
2725
+ entry.inFlight?.cancel();
2726
+ entry.inFlight = null;
2727
+ if (entry.wrapper.parentNode) {
2728
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2729
+ }
2509
2730
  }
2731
+ active = null;
2510
2732
  }
2511
- active = null;
2512
2733
  };
2513
2734
  }
2514
2735
 
@@ -2544,11 +2765,13 @@ function resolveEngineOptions(opts, extras) {
2544
2765
  scrimMode: opts.scrimMode,
2545
2766
  scrimColor: opts.scrimColor,
2546
2767
  scrimBlur: opts.scrimBlur,
2547
- scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2768
+ scrimInteractive: opts.scrimInteractive,
2548
2769
  scrimTapToClose: opts.scrimTapToClose,
2549
2770
  scrimPreset: opts.scrimPreset,
2550
2771
  screenRange: opts.screenRange,
2551
- backdropRange: opts.backdropRange
2772
+ backdropRange: opts.backdropRange,
2773
+ backdropOpacity: opts.backdropOpacity,
2774
+ backdropColor: opts.backdropColor
2552
2775
  },
2553
2776
  animation: {
2554
2777
  animation: opts.animation,
@@ -2627,6 +2850,7 @@ var BottomSheetCore = class {
2627
2850
  __publicField(this, "fitContentToSnap");
2628
2851
  __publicField(this, "settleAnimation");
2629
2852
  __publicField(this, "persistent");
2853
+ __publicField(this, "keyboardInset", 0);
2630
2854
  __publicField(this, "disableCloseFlag");
2631
2855
  __publicField(this, "disableDragFlag");
2632
2856
  __publicField(this, "closeOnRouteChange");
@@ -2641,6 +2865,7 @@ var BottomSheetCore = class {
2641
2865
  __publicField(this, "detachSheetGesture", null);
2642
2866
  __publicField(this, "detachSliderKeyboard", null);
2643
2867
  __publicField(this, "rootEl", null);
2868
+ __publicField(this, "homeRoot", null);
2644
2869
  __publicField(this, "destroyed", false);
2645
2870
  __publicField(this, "restClosed", false);
2646
2871
  __publicField(this, "currentViewTransition", null);
@@ -2648,8 +2873,13 @@ var BottomSheetCore = class {
2648
2873
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2649
2874
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2650
2875
  __publicField(this, "isTopSheet", true);
2651
- __publicField(this, "opening", false);
2652
- __publicField(this, "promotedForOpen", false);
2876
+ __publicField(this, "phase", "closed");
2877
+ __publicField(this, "transition", null);
2878
+ __publicField(this, "inFlight", null);
2879
+ __publicField(this, "outbox", []);
2880
+ __publicField(this, "flushing", false);
2881
+ __publicField(this, "openPending", false);
2882
+ __publicField(this, "settleHolds", []);
2653
2883
  __publicField(this, "allowOvershoot", false);
2654
2884
  __publicField(this, "progressPayload", {
2655
2885
  value: 0,
@@ -2670,6 +2900,7 @@ var BottomSheetCore = class {
2670
2900
  __publicField(this, "anchorHost", null);
2671
2901
  __publicField(this, "stackZ", 100);
2672
2902
  __publicField(this, "detachScrimStages", null);
2903
+ __publicField(this, "placeScrimStages", null);
2673
2904
  __publicField(this, "stackEffectEnabled", false);
2674
2905
  __publicField(this, "stackEffectPrimed", false);
2675
2906
  __publicField(this, "fitDeps");
@@ -2677,13 +2908,7 @@ var BottomSheetCore = class {
2677
2908
  __publicField(this, "featureList", []);
2678
2909
  __publicField(this, "featureCtx");
2679
2910
  const resolved = resolveEngineOptions(opts, {
2680
- shouldApplyInertSiblings: () => {
2681
- if (typeof document === "undefined") return false;
2682
- const bodyChildren = Array.from(document.body.children);
2683
- return bodyChildren.some(
2684
- (c) => c === opts.element || c.contains(opts.element)
2685
- );
2686
- }
2911
+ shouldApplyInertSiblings: () => opts.element.isConnected
2687
2912
  });
2688
2913
  this.element = opts.element;
2689
2914
  this.handle = opts.handle ?? opts.element;
@@ -2712,7 +2937,6 @@ var BottomSheetCore = class {
2712
2937
  void this.snapTo(id);
2713
2938
  },
2714
2939
  close: () => {
2715
- if (!this.canDismiss()) return;
2716
2940
  void this.close("backdrop");
2717
2941
  }
2718
2942
  },
@@ -2742,7 +2966,8 @@ var BottomSheetCore = class {
2742
2966
  this.lifecycle = new LifecycleController(
2743
2967
  {
2744
2968
  element: this.element,
2745
- close: () => this.close()
2969
+ close: () => this.close(),
2970
+ isTop: () => this.isTopSheet
2746
2971
  },
2747
2972
  resolved.lifecycle
2748
2973
  );
@@ -2769,6 +2994,7 @@ var BottomSheetCore = class {
2769
2994
  auditVhUsage(this.snapPointsRaw);
2770
2995
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2771
2996
  this.rootEl = this.element.closest(".bs-root");
2997
+ this.homeRoot = homeRootOf(this.rootEl);
2772
2998
  this.maxHeightDeps = {
2773
2999
  element: this.element,
2774
3000
  mode: this.mode,
@@ -2796,7 +3022,9 @@ var BottomSheetCore = class {
2796
3022
  );
2797
3023
  const initial = this.snaps.findById(this.activeId);
2798
3024
  if (initial) this.size = initial.size;
3025
+ this.phase = this.size > 0 ? "open" : "closed";
2799
3026
  this.applySize(this.size);
3027
+ this.restClosed = this.element.getAttribute("data-bs-rest") === "closed";
2800
3028
  this.setRestClosed(this.size === 0);
2801
3029
  this.updateAriaSlider();
2802
3030
  this.scrollCache = createScrollCache({
@@ -2807,12 +3035,11 @@ var BottomSheetCore = class {
2807
3035
  this.attach();
2808
3036
  this.registerInStack();
2809
3037
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2810
- const io = new IntersectionObserver((entries2) => {
2811
- for (const entry of entries2) {
3038
+ const io = new IntersectionObserver((entries) => {
3039
+ for (const entry of entries) {
2812
3040
  if (!entry.isIntersecting) continue;
2813
3041
  if (this.destroyed) return;
2814
- this.snaps.recompute();
2815
- this.healActiveSnap();
3042
+ this.recompute();
2816
3043
  io.disconnect();
2817
3044
  return;
2818
3045
  }
@@ -2822,6 +3049,7 @@ var BottomSheetCore = class {
2822
3049
  }
2823
3050
  this.teardowns.add(
2824
3051
  installFitObserver({
3052
+ element: this.element,
2825
3053
  handle: this.handle,
2826
3054
  scrollContainer: this.scrollContainer,
2827
3055
  hasFitSnap: () => this.snapPointsRaw.some(
@@ -2837,9 +3065,7 @@ var BottomSheetCore = class {
2837
3065
  this.applyAutoAriaLabelledBy();
2838
3066
  this.runFeatureStage("post");
2839
3067
  this.warnOrphanFeatureOptions();
2840
- if (this.size > 0) {
2841
- this.handleOpen();
2842
- }
3068
+ if (this.phase === "open") this.installLifecycle(true);
2843
3069
  }
2844
3070
  buildFeatureContext(autoCollapseAfter) {
2845
3071
  return {
@@ -2867,21 +3093,27 @@ var BottomSheetCore = class {
2867
3093
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2868
3094
  resolveSnap: (id) => this.snaps.findById(id),
2869
3095
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3096
+ getPhase: () => this.phase,
2870
3097
  setSize: (size) => {
2871
3098
  this.size = size;
2872
3099
  },
2873
3100
  setMaxAxisSize: (size) => {
2874
3101
  this.snaps.setMaxAxisSize(size);
2875
3102
  },
3103
+ setKeyboardInset: (px) => this.setKeyboardInset(px),
2876
3104
  applySize: (size) => this.applySize(size),
2877
3105
  recomputeSnaps: () => this.recomputeSnaps(),
3106
+ reseat: () => this.reseat(),
3107
+ holdSettle: (settled) => {
3108
+ this.settleHolds.push(settled);
3109
+ },
2878
3110
  newCycle: () => {
2879
3111
  this.newCycle();
2880
3112
  },
2881
- cancelInFlight: () => {
2882
- this.animation.cancel();
3113
+ cancelInFlight: () => this.suspend(),
3114
+ resyncAfterResize: () => {
3115
+ if (!this.destroyed) this.land(false);
2883
3116
  },
2884
- resyncAfterResize: () => this.resyncAfterResize(),
2885
3117
  snapTo: (id) => {
2886
3118
  void this.snapTo(id);
2887
3119
  },
@@ -2920,8 +3152,9 @@ var BottomSheetCore = class {
2920
3152
  return {
2921
3153
  size: this.size,
2922
3154
  activeId: this.activeId,
3155
+ phase: this.phase,
2923
3156
  isDragging: this.isDraggingAny(),
2924
- isAnimating: this.animation.isAnimating,
3157
+ isAnimating: this.animation.isAnimating || this.currentViewTransition !== null,
2925
3158
  progress: this.computeProgress(this.size)
2926
3159
  };
2927
3160
  }
@@ -2975,49 +3208,53 @@ var BottomSheetCore = class {
2975
3208
  getResolvedSnaps() {
2976
3209
  return this.snaps.getResolvedSnaps();
2977
3210
  }
2978
- async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2979
- if (this.destroyed) return;
3211
+ snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2980
3212
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
2981
- const velocityPxPerMs = opts.velocity ?? 0;
2982
- const externalSignal = opts.signal;
2983
- if (externalSignal?.aborted) return;
3213
+ return this.transitionTo(
3214
+ id,
3215
+ opts.velocity ?? 0,
3216
+ _skipBeforeSnap,
3217
+ opts.signal
3218
+ );
3219
+ }
3220
+ transitionTo(id, velocityPxPerMs = 0, skipBeforeSnap = false, externalSignal) {
3221
+ if (this.destroyed || externalSignal?.aborted) return Promise.resolve();
2984
3222
  const target = this.snaps.findById(id);
2985
3223
  if (!target) {
2986
3224
  devWarn(`[BottomSheet] unknown snap id: ${id}`);
2987
- return;
3225
+ return Promise.resolve();
2988
3226
  }
2989
3227
  if (!this.snaps.getAllowedIds().includes(id)) {
2990
3228
  devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2991
- return;
2992
- }
2993
- if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
2994
- return;
2995
- }
2996
- const resumingOpen = this.opening;
2997
- const signal = this.newCycle();
2998
- const onExternalAbort = () => {
2999
- this.animation.cancel();
3000
- this.newCycle();
3001
- };
3229
+ return Promise.resolve();
3230
+ }
3231
+ if (target.size === 0 && this.closingBlocked()) return Promise.resolve();
3232
+ if (!externalSignal && this.inFlight?.id === id) return this.inFlight.done;
3233
+ if (!skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
3234
+ return Promise.resolve();
3235
+ }
3236
+ const flight = { id, done: Promise.resolve() };
3237
+ flight.done = this.runTransition(
3238
+ target,
3239
+ velocityPxPerMs,
3240
+ false,
3241
+ externalSignal,
3242
+ flight
3243
+ );
3244
+ return flight.done;
3245
+ }
3246
+ async runTransition(target, velocityPxPerMs, fromDrag, externalSignal, flight = null) {
3247
+ const signal = this.beginTransition(target);
3248
+ const transition = this.transition;
3249
+ if (signal.aborted || !transition) return;
3250
+ this.inFlight = flight;
3251
+ const onExternalAbort = () => this.suspend();
3002
3252
  externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3003
- const wasClosed = this.size === 0 || resumingOpen;
3004
- const previousId = this.activeId;
3005
- const previousSize = this.size;
3006
- this.scrollCache.cache(previousId, previousSize, target.size);
3007
- this.activeId = id;
3008
- const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
3009
- const opensToRest = id === "closed" || rawTargetSize === 0;
3010
- if (wasClosed && !opensToRest) {
3011
- this.opening = true;
3012
- if (!resumingOpen) {
3013
- this.promotedForOpen = true;
3014
- sheetStack.promote(this.id);
3015
- }
3016
- }
3017
- if (this.animation.viewTransitionsAvailable) {
3253
+ this.allowOvershoot = fromDrag;
3254
+ if (!fromDrag && this.animation.viewTransitionsAvailable) {
3018
3255
  this.currentViewTransition?.skipTransition?.();
3019
3256
  const vt = document.startViewTransition(() => {
3020
- this.applySize(target.size);
3257
+ if (!signal.aborted) this.applySize(transition.size);
3021
3258
  });
3022
3259
  vt.ready?.catch(() => {
3023
3260
  });
@@ -3028,25 +3265,202 @@ var BottomSheetCore = class {
3028
3265
  }
3029
3266
  if (this.currentViewTransition === vt) this.currentViewTransition = null;
3030
3267
  } else {
3031
- await this.animation.animateTo(target.size, velocityPxPerMs);
3268
+ await this.animation.animateTo(transition.size, velocityPxPerMs);
3032
3269
  }
3033
3270
  externalSignal?.removeEventListener("abort", onExternalAbort);
3034
3271
  if (signal.aborted) return;
3035
- this.opening = false;
3036
- this.completeSnap(id, target.size, previousSize, wasClosed, false);
3272
+ if (this.size !== transition.size) this.applySize(transition.size);
3273
+ this.land(fromDrag);
3274
+ if (this.settleHolds.length > 0) {
3275
+ await Promise.allSettled(this.settleHolds.splice(0));
3276
+ }
3277
+ }
3278
+ beginTransition(target) {
3279
+ const signal = this.newCycle();
3280
+ const cap = this.snaps.getMaxAxisSize();
3281
+ const fromId = this.activeId;
3282
+ const fromSize = this.snaps.findById(fromId)?.size ?? this.size;
3283
+ this.scrollCache.leave(fromId, fromSize, target.size);
3284
+ this.scrollCache.enter(target.id, fromSize, target.size);
3285
+ this.activeId = target.id;
3286
+ this.transition = {
3287
+ size: cap > 0 ? Math.min(target.size, cap) : target.size,
3288
+ cap
3289
+ };
3290
+ if (target.size > 0) {
3291
+ this.enterOpening(target.id);
3292
+ } else {
3293
+ if (this.phase === "closed" && !this.isClosedSnap(target.id)) {
3294
+ sheetStack.promote(this.id);
3295
+ }
3296
+ this.enterClosing();
3297
+ }
3298
+ this.flush();
3299
+ return signal;
3037
3300
  }
3038
- completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
3039
- this.scrollCache.restore(id, previousSize, targetSize);
3301
+ enterOpening(id) {
3302
+ if (this.phase === "open" || this.phase === "opening") return;
3303
+ const fresh = this.phase === "closed";
3304
+ this.phase = "opening";
3305
+ this.setRestClosed(false);
3306
+ if (!this.installLifecycle(fresh)) return;
3307
+ this.post("open", { id });
3308
+ this.outbox.push(() => {
3309
+ if (this.phase !== "closed") {
3310
+ notifyLinkedSheets(this.linkedSheets, this);
3311
+ }
3312
+ });
3313
+ }
3314
+ enterClosing() {
3315
+ if (this.phase === "closed" || this.phase === "closing") return;
3316
+ this.phase = "closing";
3317
+ this.post("close", void 0);
3318
+ }
3319
+ settlePhase(heal) {
3320
+ if (this.size > 0) {
3321
+ this.enterOpening(this.activeId);
3322
+ if (this.phase === "opening") {
3323
+ this.phase = "open";
3324
+ this.post("opened", { id: this.activeId });
3325
+ }
3326
+ return;
3327
+ }
3328
+ if (heal && this.phase !== "closing") return;
3329
+ this.enterClosing();
3330
+ if (this.phase === "closing") {
3331
+ this.phase = "closed";
3332
+ this.setRestClosed(true);
3333
+ this.lifecycle.release();
3334
+ sheetStack.update();
3335
+ this.post("closed", void 0);
3336
+ }
3337
+ }
3338
+ land(haptic) {
3339
+ this.transition = null;
3340
+ this.inFlight = null;
3341
+ this.allowOvershoot = false;
3342
+ this.openPending = this.size === 0 && !this.isClosedSnap(this.activeId);
3040
3343
  this.updateAriaSlider();
3041
- this.emit("snap", {
3042
- id,
3344
+ this.setRestClosed(this.size === 0);
3345
+ this.post("snap", {
3346
+ id: this.activeId,
3043
3347
  size: this.size,
3044
3348
  progress: this.computeProgress(this.size)
3045
3349
  });
3046
- if (withHaptic) this.haptic();
3047
- this.setRestClosed(targetSize === 0);
3048
- if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
3049
- if (targetSize === 0) this.emitCloseSequence();
3350
+ this.settlePhase(false);
3351
+ if (haptic) this.haptic();
3352
+ this.flush();
3353
+ }
3354
+ suspend() {
3355
+ this.animation.cancel();
3356
+ this.currentViewTransition?.skipTransition?.();
3357
+ this.newCycle();
3358
+ }
3359
+ landNow() {
3360
+ const transition = this.transition;
3361
+ if (this.destroyed || !transition || this.isDraggingAny()) return;
3362
+ this.animation.cancel();
3363
+ this.currentViewTransition?.skipTransition?.();
3364
+ this.newCycle();
3365
+ this.applySize(transition.size);
3366
+ this.land(false);
3367
+ }
3368
+ reseat() {
3369
+ if (this.destroyed || this.isDraggingAny()) return;
3370
+ const active = this.snaps.findById(this.activeId);
3371
+ if (!active) return;
3372
+ const cap = this.snaps.getMaxAxisSize();
3373
+ const target = cap > 0 ? Math.min(active.size, cap) : active.size;
3374
+ this.updateAriaSlider();
3375
+ const transition = this.transition;
3376
+ if (transition) {
3377
+ if (Math.abs(target - transition.size) < SIZE_WRITE_EPSILON && Math.abs(cap - transition.cap) < SIZE_WRITE_EPSILON) {
3378
+ return;
3379
+ }
3380
+ transition.size = target;
3381
+ transition.cap = cap;
3382
+ this.applySize(this.animation.retarget(target) ? this.size : target);
3383
+ return;
3384
+ }
3385
+ if (this.openPending && target > 0 && this.phase === "closed") {
3386
+ void this.runTransition(active, 0, false);
3387
+ return;
3388
+ }
3389
+ this.applySize(target);
3390
+ this.settlePhase(true);
3391
+ this.flush();
3392
+ }
3393
+ installLifecycle(promote) {
3394
+ if (promote) sheetStack.promote(this.id);
3395
+ if (this.lifecycle.isInstalled) return true;
3396
+ try {
3397
+ this.lifecycle.install();
3398
+ return true;
3399
+ } catch (err) {
3400
+ this.abortOpen();
3401
+ queueMicrotask(() => {
3402
+ throw err;
3403
+ });
3404
+ return false;
3405
+ }
3406
+ }
3407
+ abortOpen() {
3408
+ this.animation.cancel();
3409
+ if (!this.destroyed) this.newCycle();
3410
+ this.phase = "closed";
3411
+ this.openPending = false;
3412
+ this.lifecycle.release();
3413
+ sheetStack.update();
3414
+ this.activeId = this.zeroSnapId() ?? this.activeId;
3415
+ this.applySize(0);
3416
+ this.setRestClosed(true);
3417
+ this.updateAriaSlider();
3418
+ }
3419
+ zeroSnapId() {
3420
+ return this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0);
3421
+ }
3422
+ isClosedSnap(id) {
3423
+ if (id === "closed") return true;
3424
+ return this.snapPointsRaw.find((p) => p.id === id)?.size === 0;
3425
+ }
3426
+ closingBlocked() {
3427
+ return this.disableCloseFlag && this.phase !== "closed";
3428
+ }
3429
+ setKeyboardInset(px) {
3430
+ if (px === this.keyboardInset) return;
3431
+ this.keyboardInset = px;
3432
+ for (const host of this.varHosts()) {
3433
+ host.style.setProperty("--bs-keyboard-inset", `${px}px`);
3434
+ }
3435
+ }
3436
+ varHosts() {
3437
+ const hosts = /* @__PURE__ */ new Set([this.element]);
3438
+ for (const host of [
3439
+ this.anchorHost,
3440
+ this.scrimParent,
3441
+ this.rootEl,
3442
+ this.homeRoot
3443
+ ]) {
3444
+ if (host) hosts.add(host);
3445
+ }
3446
+ return hosts;
3447
+ }
3448
+ dismissBlocked() {
3449
+ return (this.disableCloseFlag || this.persistent) && this.phase !== "closed";
3450
+ }
3451
+ post(event, payload) {
3452
+ this.outbox.push(() => this.bus.emit(event, payload));
3453
+ }
3454
+ flush() {
3455
+ if (this.flushing) return;
3456
+ this.flushing = true;
3457
+ try {
3458
+ for (let next = this.outbox.shift(); next; next = this.outbox.shift()) {
3459
+ next();
3460
+ }
3461
+ } finally {
3462
+ this.flushing = false;
3463
+ }
3050
3464
  }
3051
3465
  setRestClosed(closed) {
3052
3466
  if (closed === this.restClosed) return;
@@ -3054,17 +3468,6 @@ var BottomSheetCore = class {
3054
3468
  if (closed) this.element.setAttribute("data-bs-rest", "closed");
3055
3469
  else this.element.removeAttribute("data-bs-rest");
3056
3470
  }
3057
- emitOpenSequence(id) {
3058
- this.emit("open", { id });
3059
- this.handleOpen();
3060
- notifyLinkedSheets(this.linkedSheets, this);
3061
- this.emit("opened", { id });
3062
- }
3063
- emitCloseSequence() {
3064
- this.emit("close", void 0);
3065
- this.handleClose();
3066
- this.emit("closed", void 0);
3067
- }
3068
3471
  async dragTo(targetSize, velocityOrOpts = 0) {
3069
3472
  if (this.destroyed) return;
3070
3473
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -3072,27 +3475,31 @@ var BottomSheetCore = class {
3072
3475
  const externalSignal = opts.signal;
3073
3476
  if (externalSignal?.aborted) return;
3074
3477
  this.newCycle();
3075
- const onExternalAbort = () => {
3076
- this.animation.cancel();
3077
- this.newCycle();
3078
- };
3478
+ const onExternalAbort = () => this.suspend();
3079
3479
  externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3080
3480
  const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3081
3481
  await this.animation.animateTo(clamped, velocityPxPerMs);
3082
3482
  externalSignal?.removeEventListener("abort", onExternalAbort);
3083
- if (this.destroyed || externalSignal?.aborted) return;
3084
3483
  }
3085
3484
  open(id) {
3086
3485
  if (this.destroyed) return Promise.resolve();
3486
+ if (!id && (this.phase === "open" || this.phase === "opening")) {
3487
+ return Promise.resolve();
3488
+ }
3087
3489
  const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
3088
3490
  return this.snapTo(target);
3089
3491
  }
3090
3492
  close(reason = "programmatic") {
3091
- if (this.destroyed) return Promise.resolve();
3092
- if (this.disableCloseFlag) return Promise.resolve();
3093
- if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
3094
- const closedId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0) ?? this.snaps.getAllowedIds()[0];
3095
- return this.snapTo(closedId ?? this.activeId);
3493
+ if (this.destroyed || this.disableCloseFlag) return Promise.resolve();
3494
+ if (this.persistent && reason !== "programmatic") return Promise.resolve();
3495
+ const target = this.zeroSnapId() ?? this.allowedIdsBySize()[0];
3496
+ if (target === void 0) return Promise.resolve();
3497
+ if (this.phase === "closed" && this.activeId === target) {
3498
+ return Promise.resolve();
3499
+ }
3500
+ const visible = this.phase === "open" || this.phase === "opening";
3501
+ if (visible && this.emitBeforeClose(reason)) return Promise.resolve();
3502
+ return this.snapTo(target);
3096
3503
  }
3097
3504
  canDismiss() {
3098
3505
  return !this.persistent && !this.disableCloseFlag && !this.destroyed;
@@ -3115,10 +3522,12 @@ var BottomSheetCore = class {
3115
3522
  }
3116
3523
  setDragFrom(mode) {
3117
3524
  if (this.destroyed || mode === this.dragFromMode) return;
3525
+ const wasDragging = this.gesture?.isDragging ?? false;
3118
3526
  this.gesture?.forceClearDragState();
3119
3527
  this.detachSheetGesture?.();
3120
3528
  this.dragFromMode = mode;
3121
3529
  this.mountSheetGesture();
3530
+ if (wasDragging) this.restoreAfterDrag();
3122
3531
  }
3123
3532
  getDragFrom() {
3124
3533
  return this.dragFromMode;
@@ -3131,6 +3540,7 @@ var BottomSheetCore = class {
3131
3540
  const nowVertical = next === "bottom" || next === "top";
3132
3541
  this.gesture?.forceClearDragState();
3133
3542
  this.detachSheetGesture?.();
3543
+ this.landNow();
3134
3544
  this.animation.cancel();
3135
3545
  this.element.style.removeProperty(layoutAxis(this.mode));
3136
3546
  this.mode = next;
@@ -3146,9 +3556,12 @@ var BottomSheetCore = class {
3146
3556
  this.detachSliderKeyboard?.();
3147
3557
  this.mountSliderKeyboard();
3148
3558
  this.mountSheetGesture();
3559
+ for (const anchor of this.anchors) anchor.reposition();
3560
+ this.placeScrimStages?.();
3561
+ if (this.stackEffectPrimed) {
3562
+ this.element.style.transformOrigin = nowVertical ? "50% 100%" : "0% 50%";
3563
+ }
3149
3564
  this.recompute();
3150
- const active = this.snaps.findById(this.activeId);
3151
- if (active) this.applySize(active.size);
3152
3565
  this.updateAriaSlider();
3153
3566
  }
3154
3567
  getMode() {
@@ -3190,13 +3603,24 @@ var BottomSheetCore = class {
3190
3603
  if (snap && ids.includes(snap)) {
3191
3604
  void this.snapTo(snap, 0, true);
3192
3605
  } else if (!ids.includes(this.activeId)) {
3193
- const fallback = ids[0];
3194
- if (fallback) void this.snapTo(fallback, 0, true);
3606
+ const fallback = this.fallbackSnap(
3607
+ this.snaps.findById(this.activeId)?.size ?? this.size
3608
+ );
3609
+ if (fallback) void this.snapTo(fallback.id, 0, true);
3195
3610
  }
3196
3611
  }
3612
+ fallbackSnap(from) {
3613
+ return (from > 0 ? this.snaps.nearest(from, true) : null) ?? this.snaps.nearest(from, false);
3614
+ }
3197
3615
  setBackdropRange(range) {
3198
3616
  this.scrim.setBackdropRange(range, () => this.applySize(this.size));
3199
3617
  }
3618
+ setBackdropOpacity(value) {
3619
+ this.scrim.setBackdropOpacity(value, () => this.applySize(this.size));
3620
+ }
3621
+ setBackdropColor(color) {
3622
+ this.scrim.setBackdropColor(color);
3623
+ }
3200
3624
  setScreenRange(range) {
3201
3625
  this.scrim.setScreenRange(range, () => this.applySize(this.size));
3202
3626
  }
@@ -3237,7 +3661,7 @@ var BottomSheetCore = class {
3237
3661
  }
3238
3662
  const handle = attachAnchor(
3239
3663
  {
3240
- mode: this.mode,
3664
+ getMode: () => this.mode,
3241
3665
  host: this.anchorHost,
3242
3666
  getState: () => ({
3243
3667
  activeId: this.activeId,
@@ -3275,9 +3699,9 @@ var BottomSheetCore = class {
3275
3699
  return () => {
3276
3700
  };
3277
3701
  }
3278
- const detachInstalled = installScrimStages(
3702
+ const stages = installScrimStages(
3279
3703
  {
3280
- mode: this.mode,
3704
+ getMode: () => this.mode,
3281
3705
  host,
3282
3706
  getState: () => ({
3283
3707
  activeId: this.activeId,
@@ -3290,10 +3714,14 @@ var BottomSheetCore = class {
3290
3714
  opts
3291
3715
  );
3292
3716
  const detach = () => {
3293
- if (this.detachScrimStages === detach) this.detachScrimStages = null;
3294
- detachInstalled();
3717
+ if (this.detachScrimStages === detach) {
3718
+ this.detachScrimStages = null;
3719
+ this.placeScrimStages = null;
3720
+ }
3721
+ stages.detach();
3295
3722
  };
3296
3723
  this.detachScrimStages = detach;
3724
+ this.placeScrimStages = stages.reposition;
3297
3725
  this.teardowns.add(() => this.detachScrimStages?.());
3298
3726
  return detach;
3299
3727
  }
@@ -3302,32 +3730,29 @@ var BottomSheetCore = class {
3302
3730
  }
3303
3731
  setSnapPoints(points, allowed) {
3304
3732
  if (this.destroyed) return;
3305
- this.animation.cancel();
3306
- this.newCycle();
3733
+ const from = this.snaps.findById(this.activeId)?.size ?? this.size;
3734
+ const restricted = this.snaps.getResolvedSnaps().some((s) => !this.snaps.getAllowedIds().includes(s.id));
3307
3735
  this.snapPointsRaw = points;
3308
3736
  auditVhUsage(points);
3309
3737
  this.auditDuplicateSnapIds(points);
3738
+ this.maxHeight.resolveCap();
3310
3739
  this.snaps.setRaw(points);
3740
+ this.maxHeight.clampTo();
3311
3741
  if (allowed) {
3312
3742
  this.snaps.setAllowedIds(allowed);
3313
3743
  } else {
3314
3744
  const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3315
- const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3745
+ const kept = restricted ? this.snaps.getAllowedIds().filter((id) => present.includes(id)) : [];
3316
3746
  this.snaps.setAllowedIds(kept.length ? kept : present);
3317
3747
  }
3318
3748
  this.scrollCache.clear();
3319
3749
  this.scrim.invalidateOpacityCache();
3320
- const current = this.snaps.findById(this.activeId);
3321
- if (current) {
3322
- this.applySize(current.size);
3323
- } else {
3324
- const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3325
- if (fallback) {
3326
- this.activeId = fallback.id;
3327
- this.applySize(fallback.size);
3328
- }
3750
+ if (this.snaps.findById(this.activeId)) {
3751
+ this.reseat();
3752
+ return;
3329
3753
  }
3330
- this.updateAriaSlider();
3754
+ const fallback = this.fallbackSnap(from) ?? this.snaps.getResolvedSnaps()[0];
3755
+ if (fallback) void this.transitionTo(fallback.id, 0, true);
3331
3756
  }
3332
3757
  recompute() {
3333
3758
  if (this.destroyed) return;
@@ -3335,22 +3760,11 @@ var BottomSheetCore = class {
3335
3760
  this.snaps.recompute();
3336
3761
  this.maxHeight.clampTo();
3337
3762
  this.scrim.invalidateOpacityCache();
3338
- if (this.isDraggingAny()) return;
3339
- this.healActiveSnap();
3340
- }
3341
- healActiveSnap() {
3342
- const current = this.snaps.findById(this.activeId);
3343
- if (!current) return;
3344
- this.size = current.size;
3345
- this.applySize(this.size);
3346
- this.updateAriaSlider();
3347
- if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3348
- this.emitOpenSequence(this.activeId);
3349
- }
3763
+ this.reseat();
3350
3764
  }
3351
3765
  destroy() {
3352
3766
  this.destroyed = true;
3353
- this.opening = false;
3767
+ this.outbox.length = 0;
3354
3768
  this.currentAbort.abort();
3355
3769
  this.currentViewTransition?.skipTransition?.();
3356
3770
  this.currentViewTransition = null;
@@ -3367,11 +3781,19 @@ var BottomSheetCore = class {
3367
3781
  this.backdrop.style.opacity = "";
3368
3782
  this.backdrop.style.pointerEvents = "";
3369
3783
  }
3370
- for (const host of [this.anchorHost, this.scrimParent, this.rootEl]) {
3784
+ for (const host of [
3785
+ this.anchorHost,
3786
+ this.scrimParent,
3787
+ this.rootEl,
3788
+ this.homeRoot
3789
+ ]) {
3371
3790
  if (!host) continue;
3372
3791
  host.style.removeProperty("--bs-size");
3373
3792
  host.style.removeProperty("--bs-progress");
3793
+ host.style.removeProperty("--bs-z-sheet");
3794
+ host.style.removeProperty("--bs-keyboard-inset");
3374
3795
  }
3796
+ this.element.style.removeProperty("--bs-keyboard-inset");
3375
3797
  if (this.screenComponent) {
3376
3798
  this.screenComponent.style.opacity = "";
3377
3799
  this.screenComponent.style.display = "";
@@ -3384,7 +3806,8 @@ var BottomSheetCore = class {
3384
3806
  }
3385
3807
  newCycle() {
3386
3808
  this.allowOvershoot = false;
3387
- this.opening = false;
3809
+ this.transition = null;
3810
+ this.inFlight = null;
3388
3811
  this.currentAbort.abort();
3389
3812
  this.currentAbort = new AbortController();
3390
3813
  return this.currentAbort.signal;
@@ -3407,8 +3830,9 @@ var BottomSheetCore = class {
3407
3830
  if (typeof document === "undefined") return;
3408
3831
  if (!this.lifecycle.focusTrapEnabled) return;
3409
3832
  if (this.element.getAttribute("aria-labelledby")) return;
3410
- const scope = this.handle ?? this.element;
3411
- const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3833
+ const HEADING = "h1,h2,h3,h4,h5,h6";
3834
+ const header = this.element.querySelector(".bs-header");
3835
+ const titled = this.element.querySelector("[data-bs-title]") ?? this.handle.querySelector(HEADING) ?? header?.querySelector(HEADING);
3412
3836
  if (!titled) return;
3413
3837
  if (!titled.id) {
3414
3838
  titled.id = nextInstanceId("bs-title");
@@ -3450,12 +3874,16 @@ var BottomSheetCore = class {
3450
3874
  this.stackZ = z;
3451
3875
  this.element.style.zIndex = String(z);
3452
3876
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3877
+ this.rootEl?.style.setProperty("--bs-z-sheet", String(z));
3878
+ this.homeRoot?.style.setProperty("--bs-z-sheet", String(z));
3453
3879
  for (const anchor of this.anchors) anchor.syncZ(z + 1);
3454
3880
  },
3455
3881
  setIsTop: (isTop) => {
3882
+ if (isTop === this.isTopSheet) return;
3456
3883
  this.isTopSheet = isTop;
3884
+ this.lifecycle.syncTop();
3457
3885
  },
3458
- isOpen: () => this.size > 0 || this.opening,
3886
+ isOpen: () => this.phase !== "closed",
3459
3887
  setDepth: (depth) => this.applyStackDepth(depth)
3460
3888
  }));
3461
3889
  }
@@ -3483,15 +3911,15 @@ var BottomSheetCore = class {
3483
3911
  const r = this.getAllowedRange();
3484
3912
  buf.size = this.size;
3485
3913
  buf.maxAxisSize = this.snaps.getMaxAxisSize();
3486
- buf.range.min = r.min;
3914
+ buf.range.min = this.dismissBlocked() ? Math.max(r.min, this.smallestOpenSize()) : r.min;
3487
3915
  buf.range.max = r.max;
3488
3916
  buf.rubberBandEnabled = this.rubberBandEnabled;
3489
3917
  return buf;
3490
3918
  },
3491
3919
  getDisableDrag: () => this.disableDragFlag,
3492
- cancelAnimation: () => this.animation.cancel(),
3920
+ cancelAnimation: () => this.suspend(),
3493
3921
  applySize: (size) => this.applySize(size),
3494
- animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
3922
+ restoreAfterDrag: () => this.restoreAfterDrag(),
3495
3923
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3496
3924
  emit: (event, payload) => this.emit(event, payload),
3497
3925
  listenerCount: (event) => this.bus.listenerCount(event)
@@ -3615,32 +4043,19 @@ var BottomSheetCore = class {
3615
4043
  element: this.element,
3616
4044
  getMode: () => this.mode,
3617
4045
  isDestroyed: () => this.destroyed,
3618
- isDragging: () => this.isDraggingAny(),
3619
4046
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3620
4047
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3621
- getSize: () => this.size,
3622
4048
  setMaxAxisSize: (size) => {
3623
4049
  this.snaps.setMaxAxisSize(size);
3624
4050
  },
3625
- setSize: (size) => {
3626
- this.size = size;
3627
- },
3628
4051
  recomputeSnaps: () => this.recomputeSnaps(),
3629
- applySize: (size) => this.applySize(size),
3630
- cancelInFlight: () => {
3631
- this.animation.cancel();
3632
- },
3633
- newCycle: () => {
3634
- this.newCycle();
3635
- },
3636
- isAnimating: () => this.animation.isAnimating,
3637
- resyncAfterCancel: () => this.resyncAfterResize()
4052
+ reseat: () => this.reseat(),
4053
+ landNow: () => this.landNow()
3638
4054
  }));
3639
4055
  if (this.lifecycle.closeOnEscape) {
3640
4056
  const onKey = (e) => {
3641
4057
  if (this.destroyed || e.defaultPrevented) return;
3642
- if (!this.canDismiss()) return;
3643
- if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
4058
+ if (e.key === "Escape" && this.isTopSheet) {
3644
4059
  void this.close("escape");
3645
4060
  }
3646
4061
  };
@@ -3658,7 +4073,10 @@ var BottomSheetCore = class {
3658
4073
  handle: this.handle,
3659
4074
  mode: this.mode,
3660
4075
  isDestroyed: () => this.destroyed,
3661
- getAllowedIds: () => this.allowedIdsBySize(),
4076
+ getAllowedIds: () => {
4077
+ const ids = this.allowedIdsBySize();
4078
+ return this.dismissBlocked() ? ids.filter((id) => (this.snaps.findById(id)?.size ?? 0) > 0) : ids;
4079
+ },
3662
4080
  getActiveId: () => this.activeId,
3663
4081
  snapTo: (id) => {
3664
4082
  void this.snapTo(id);
@@ -3671,56 +4089,46 @@ var BottomSheetCore = class {
3671
4089
  });
3672
4090
  }
3673
4091
  settleAfterDrag(delta, velocity, kind = "touch") {
3674
- const signal = this.newCycle();
3675
- const target = this.snaps.findDragSettleTarget({
3676
- delta,
3677
- velocity,
3678
- pointerKind: kind,
3679
- size: this.size,
3680
- activeId: this.activeId,
3681
- flickVelocity: this.flickVelocity,
3682
- dragThreshold: this.dragThreshold
3683
- });
4092
+ const target = this.snaps.findDragSettleTarget(
4093
+ {
4094
+ delta,
4095
+ velocity,
4096
+ pointerKind: kind,
4097
+ size: this.size,
4098
+ activeId: this.activeId,
4099
+ flickVelocity: this.flickVelocity,
4100
+ dragThreshold: this.dragThreshold
4101
+ },
4102
+ this.dismissBlocked()
4103
+ );
3684
4104
  if (!target) {
3685
4105
  this.clearWillChangeIfIdle();
3686
4106
  return;
3687
4107
  }
3688
4108
  const previousId = this.activeId;
3689
4109
  if (target.id === previousId && target.size === this.size) {
4110
+ if (this.phase === "opening" || this.phase === "closing") {
4111
+ this.land(false);
4112
+ }
3690
4113
  this.clearWillChangeIfIdle();
3691
4114
  return;
3692
4115
  }
3693
4116
  if (this.emitBeforeSnap(target, previousId)) {
3694
- const restore2 = this.snaps.findById(previousId);
3695
- if (restore2) {
3696
- void this.animation.animateTo(restore2.size, 0);
3697
- }
4117
+ this.restoreAfterDrag();
3698
4118
  return;
3699
4119
  }
3700
- const previousSize = this.size;
3701
- const previousSnapSize = this.snaps.findById(previousId)?.size;
3702
- this.scrollCache.cache(previousId, previousSize, target.size);
3703
- this.activeId = target.id;
3704
- if (previousSnapSize === 0 && target.size > 0) {
3705
- this.opening = true;
3706
- this.promotedForOpen = true;
3707
- sheetStack.promote(this.id);
3708
- }
3709
- const settleCap = this.snaps.getMaxAxisSize();
3710
- const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3711
- this.allowOvershoot = true;
3712
- void this.animation.animateTo(settleSize, velocity).then(() => {
3713
- if (signal.aborted) return;
3714
- this.allowOvershoot = false;
3715
- this.opening = false;
3716
- this.completeSnap(
3717
- target.id,
3718
- target.size,
3719
- previousSize,
3720
- previousSnapSize === 0,
3721
- true
3722
- );
3723
- });
4120
+ void this.runTransition(target, velocity, true);
4121
+ }
4122
+ restoreAfterDrag() {
4123
+ const active = this.snaps.findById(this.activeId);
4124
+ if (active) void this.runTransition(active, 0, false);
4125
+ }
4126
+ smallestOpenSize() {
4127
+ const allowed = this.snaps.getAllowedIds();
4128
+ for (const snap of this.snaps.getResolvedSnaps()) {
4129
+ if (snap.size > 0 && allowed.includes(snap.id)) return snap.size;
4130
+ }
4131
+ return 0;
3724
4132
  }
3725
4133
  isDraggingAny() {
3726
4134
  return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
@@ -3745,9 +4153,10 @@ var BottomSheetCore = class {
3745
4153
  const offset = cap - clamped;
3746
4154
  const style = this.element.style;
3747
4155
  if (!skipTransform) style.transform = this.transformTemplate(offset);
4156
+ const moving = this.animation.isAnimating || this.isDraggingAny();
3748
4157
  const sizeChanged = this.sizeWriteSentinel.shouldWrite(
3749
4158
  clamped,
3750
- this.animation.isAnimating || this.isDraggingAny() ? SIZE_WRITE_EPSILON : 0
4159
+ moving ? SIZE_WRITE_EPSILON : 0
3751
4160
  );
3752
4161
  if (sizeChanged) {
3753
4162
  style.setProperty("--bs-size", `${clamped}px`);
@@ -3760,11 +4169,13 @@ var BottomSheetCore = class {
3760
4169
  if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3761
4170
  this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3762
4171
  }
4172
+ this.homeRoot?.style.setProperty("--bs-size", `${clamped}px`);
3763
4173
  }
3764
4174
  const progress = this.computeProgress(clamped);
4175
+ const opacityEpsilon = moving ? OPACITY_WRITE_EPSILON : 0;
3765
4176
  const progressChanged = this.progressWriteSentinel.shouldWrite(
3766
4177
  progress,
3767
- OPACITY_WRITE_EPSILON
4178
+ opacityEpsilon
3768
4179
  );
3769
4180
  if (progressChanged) {
3770
4181
  style.setProperty("--bs-progress", String(progress));
@@ -3774,8 +4185,9 @@ var BottomSheetCore = class {
3774
4185
  if (this.rootEl && this.rootEl !== this.anchorHost) {
3775
4186
  this.rootEl.style.setProperty("--bs-progress", String(progress));
3776
4187
  }
4188
+ this.homeRoot?.style.setProperty("--bs-progress", String(progress));
3777
4189
  }
3778
- this.scrim.applyOpacity(progress, progressChanged);
4190
+ this.scrim.applyOpacity(progress, progressChanged, opacityEpsilon);
3779
4191
  if ((progressChanged || sizeChanged) && this.bus.listenerCount("progress") > 0) {
3780
4192
  this.progressPayload.value = progress;
3781
4193
  this.progressPayload.size = clamped;
@@ -3788,238 +4200,322 @@ var BottomSheetCore = class {
3788
4200
  if (reachableMax <= min) return 0;
3789
4201
  return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3790
4202
  }
3791
- handleOpen() {
3792
- if (this.promotedForOpen) this.promotedForOpen = false;
3793
- else sheetStack.promote(this.id);
3794
- try {
3795
- this.lifecycle.install();
3796
- } catch (err) {
3797
- if (!this.destroyed) this.newCycle();
3798
- this.handleClose();
3799
- this.applySize(0);
3800
- this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3801
- this.size = 0;
3802
- queueMicrotask(() => {
3803
- throw err;
3804
- });
3805
- }
3806
- }
3807
- resyncAfterResize() {
3808
- if (this.destroyed) return;
3809
- const target = this.snaps.findById(this.activeId);
3810
- if (!target) return;
3811
- this.updateAriaSlider();
3812
- this.emit("snap", {
3813
- id: target.id,
3814
- size: this.size,
3815
- progress: this.computeProgress(this.size)
3816
- });
3817
- if (target.size > 0 && !this.lifecycle.isInstalled) {
3818
- this.emitOpenSequence(target.id);
3819
- } else if (target.size === 0 && this.lifecycle.isInstalled) {
3820
- this.emitCloseSequence();
3821
- }
3822
- }
3823
- handleClose() {
3824
- this.opening = false;
3825
- this.promotedForOpen = false;
3826
- this.lifecycle.release();
3827
- sheetStack.update();
3828
- }
3829
4203
  };
3830
4204
 
3831
4205
  // src/core/features/history-coordinator.ts
3832
- var entries = [];
3833
- var subscribers = /* @__PURE__ */ new Set();
3834
- var suppress = 0;
3835
- var internalDepth = 0;
3836
- var pendingRebrands = [];
3837
- var listenerInstalled = false;
3838
- var patchInstalled = false;
3839
- var origPushState = null;
3840
- var origReplaceState = null;
3841
- var patchedPushState = null;
3842
- var patchedReplaceState = null;
3843
- var hasWindow = () => typeof window !== "undefined";
3844
- var hasHistory = () => typeof history !== "undefined";
3845
- var brandMarker = (surface) => ({
4206
+ var MARKER = "__bs";
4207
+ var STACK = "__bsStack";
4208
+ var DEPTH = "__bsDepth";
4209
+ var ROUTED = "__bsRouted";
4210
+ var SETTLE_TIMEOUT_MS = 1e3;
4211
+ var state2 = shared(
4212
+ "history",
4213
+ () => ({
4214
+ live: [],
4215
+ stackId: void 0,
4216
+ baseUrl: void 0,
4217
+ position: null,
4218
+ knownLength: 0,
4219
+ href: "",
4220
+ subscribers: /* @__PURE__ */ new Set(),
4221
+ internal: 0,
4222
+ pending: [],
4223
+ deferred: [],
4224
+ waiters: [],
4225
+ timer: null,
4226
+ listening: false,
4227
+ leftovers: false,
4228
+ orphanChecked: false,
4229
+ push: null,
4230
+ replace: null
4231
+ })
4232
+ );
4233
+ var hasHistory = () => typeof window !== "undefined" && typeof history !== "undefined";
4234
+ var plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
4235
+ var depthOf = (value) => {
4236
+ const depth = value?.[DEPTH];
4237
+ return typeof depth === "number" ? depth : null;
4238
+ };
4239
+ var isMarker = (value) => value?.[MARKER] === true;
4240
+ var inStack = (value) => value !== null && state2.stackId !== void 0 && value[STACK] === state2.stackId && depthOf(value) !== null;
4241
+ var foreignKeys = (value) => {
4242
+ const out = {};
4243
+ const source = plain(value);
4244
+ if (!source) return out;
4245
+ for (const key of Object.keys(source)) {
4246
+ if (!key.startsWith(MARKER)) out[key] = source[key];
4247
+ }
4248
+ return out;
4249
+ };
4250
+ var markerFor = (surface, depth) => ({
4251
+ ...foreignKeys(history.state),
3846
4252
  ...surface.markerState(),
3847
- __bs: true
4253
+ [MARKER]: true,
4254
+ [STACK]: state2.stackId,
4255
+ [DEPTH]: depth
3848
4256
  });
4257
+ var urlForDepth = (depth) => {
4258
+ for (let i = depth - 1; i >= 0; i -= 1) {
4259
+ const url = state2.live[i]?.surface.url;
4260
+ if (url !== void 0) return url;
4261
+ }
4262
+ return state2.baseUrl;
4263
+ };
3849
4264
  var runInternal = (fn) => {
3850
- internalDepth += 1;
4265
+ state2.internal += 1;
3851
4266
  try {
3852
4267
  fn();
4268
+ return true;
4269
+ } catch {
4270
+ return false;
3853
4271
  } finally {
3854
- internalDepth -= 1;
4272
+ state2.internal -= 1;
3855
4273
  }
3856
4274
  };
3857
4275
  var notifyRouteChange = () => {
3858
- const snapshot = Array.from(subscribers);
3859
- for (const fn of snapshot) fn();
4276
+ for (const fn of Array.from(state2.subscribers)) {
4277
+ try {
4278
+ fn();
4279
+ } catch (err) {
4280
+ queueMicrotask(() => {
4281
+ throw err;
4282
+ });
4283
+ }
4284
+ }
3860
4285
  };
3861
4286
  var ensureListener = () => {
3862
- if (listenerInstalled || !hasWindow()) return;
4287
+ if (state2.listening || !hasHistory()) return;
3863
4288
  window.addEventListener("popstate", onPopState);
3864
- listenerInstalled = true;
4289
+ state2.listening = true;
3865
4290
  };
3866
4291
  var maybeTeardownListener = () => {
3867
- if (!listenerInstalled) return;
3868
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3869
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
3870
- listenerInstalled = false;
3871
- };
3872
- var installPatch = () => {
3873
- if (patchInstalled || !hasHistory()) return;
3874
- origPushState = history.pushState;
3875
- origReplaceState = history.replaceState;
3876
- patchedPushState = function(...args) {
3877
- origPushState.apply(this, args);
3878
- if (internalDepth === 0) notifyRouteChange();
3879
- };
3880
- patchedReplaceState = function(...args) {
3881
- origReplaceState.apply(this, args);
3882
- if (internalDepth === 0) notifyRouteChange();
3883
- };
3884
- history.pushState = patchedPushState;
3885
- history.replaceState = patchedReplaceState;
3886
- patchInstalled = true;
3887
- };
3888
- var removePatch = () => {
3889
- if (!patchInstalled) return;
3890
- if (history.pushState === patchedPushState && origPushState) {
3891
- history.pushState = origPushState;
3892
- }
3893
- if (history.replaceState === patchedReplaceState && origReplaceState) {
3894
- history.replaceState = origReplaceState;
3895
- }
3896
- origPushState = null;
3897
- origReplaceState = null;
3898
- patchedPushState = null;
3899
- patchedReplaceState = null;
3900
- patchInstalled = false;
3901
- };
3902
- function onPopState() {
3903
- if (suppress > 0) {
3904
- suppress -= 1;
3905
- const rebrand = pendingRebrands.shift();
3906
- if (rebrand) rebrand();
3907
- maybeTeardownListener();
4292
+ if (!state2.listening) return;
4293
+ if (state2.live.length > 0 || state2.subscribers.size > 0 || state2.pending.length > 0 || state2.leftovers) {
3908
4294
  return;
3909
4295
  }
3910
- if (entries.length === 0) {
3911
- notifyRouteChange();
3912
- maybeTeardownListener();
3913
- return;
3914
- }
3915
- let target = null;
3916
- let targetIdx = -1;
3917
- for (let i = entries.length - 1; i >= 0; i -= 1) {
3918
- const entry = entries[i];
3919
- if (!entry.surface.isOpen()) continue;
3920
- if (!target) {
3921
- target = entry;
3922
- targetIdx = i;
3923
- }
3924
- if (entry.surface.isTop()) {
3925
- target = entry;
3926
- targetIdx = i;
3927
- break;
3928
- }
3929
- }
3930
- if (!target) {
3931
- const removed = entries.pop();
3932
- if (removed) removed.live = false;
4296
+ if (hasHistory()) window.removeEventListener("popstate", onPopState);
4297
+ state2.listening = false;
4298
+ };
4299
+ var settleIfIdle = () => {
4300
+ if (state2.pending.length > 0) return;
4301
+ if (state2.timer !== null) {
4302
+ clearTimeout(state2.timer);
4303
+ state2.timer = null;
4304
+ }
4305
+ for (const entry of state2.deferred.splice(0)) pushEntry(entry);
4306
+ for (const resolve of state2.waiters.splice(0)) resolve();
4307
+ };
4308
+ var traverse = (delta, kind) => {
4309
+ state2.pending.push(kind);
4310
+ if (state2.timer !== null) clearTimeout(state2.timer);
4311
+ state2.timer = setTimeout(() => {
4312
+ state2.timer = null;
4313
+ state2.pending.length = 0;
4314
+ settleIfIdle();
3933
4315
  maybeTeardownListener();
3934
- return;
3935
- }
3936
- target.live = false;
3937
- entries.splice(targetIdx, 1);
3938
- const settled = target;
3939
- void Promise.resolve(settled.surface.close()).then(() => {
3940
- if (settled.surface.isOpen()) restore(settled);
4316
+ }, SETTLE_TIMEOUT_MS);
4317
+ ensureListener();
4318
+ const moved = runInternal(() => {
4319
+ if (delta < 0) history.back();
4320
+ else history.forward();
3941
4321
  });
3942
- maybeTeardownListener();
3943
- }
3944
- function restore(entry) {
3945
- if (!hasWindow() || !hasHistory()) return;
3946
- entry.live = true;
4322
+ if (!moved) {
4323
+ state2.pending.pop();
4324
+ settleIfIdle();
4325
+ }
4326
+ };
4327
+ var newStackId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
4328
+ var startStack = () => {
4329
+ state2.stackId = newStackId();
4330
+ const current = history.state;
4331
+ const leftover = plain(current);
4332
+ state2.baseUrl = isMarker(leftover) && leftover?.[ROUTED] !== void 0 ? void 0 : location.href;
4333
+ if (isMarker(leftover)) return;
4334
+ if (current !== null && current !== void 0 && !leftover) return;
3947
4335
  runInternal(() => {
3948
- try {
3949
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3950
- } catch {
3951
- entry.live = false;
3952
- }
4336
+ history.replaceState(
4337
+ { ...foreignKeys(current), [STACK]: state2.stackId, [DEPTH]: 0 },
4338
+ "",
4339
+ location.href
4340
+ );
3953
4341
  });
3954
- if (entry.live) {
3955
- entries.push(entry);
3956
- ensureListener();
3957
- }
4342
+ };
4343
+ function pushEntry(entry) {
4344
+ const depth = state2.live.length + 1;
4345
+ if (state2.live.length === 0) startStack();
4346
+ const written = runInternal(() => {
4347
+ history.pushState(markerFor(entry.surface, depth), "", entry.surface.url);
4348
+ });
4349
+ if (!written) return false;
4350
+ state2.live.push(entry);
4351
+ state2.position = depth;
4352
+ state2.knownLength = history.length;
4353
+ state2.href = location.href;
4354
+ ensureListener();
4355
+ return true;
3958
4356
  }
3959
- function pushBackMarker(surface) {
3960
- const entry = { surface, priorUrl: void 0, live: false };
3961
- if (!hasWindow() || !hasHistory()) {
3962
- return entry;
4357
+ var enlist = (entry) => {
4358
+ if (state2.pending.length > 0) state2.deferred.push(entry);
4359
+ else pushEntry(entry);
4360
+ };
4361
+ var sameBrand = (a, b) => {
4362
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])) {
4363
+ if (key.startsWith(MARKER) && a[key] !== b[key]) return false;
3963
4364
  }
3964
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
4365
+ return true;
4366
+ };
4367
+ var rebrand = (depth) => {
4368
+ const entry = state2.live[depth - 1];
4369
+ if (!entry) return;
4370
+ const url = urlForDepth(depth);
4371
+ const marker = markerFor(entry.surface, depth);
4372
+ const atUrl = url === void 0 || new URL(url, location.href).href === location.href;
4373
+ if (atUrl && sameBrand(plain(history.state) ?? {}, marker)) return;
3965
4374
  runInternal(() => {
4375
+ history.replaceState(marker, "", url);
4376
+ });
4377
+ state2.href = location.href;
4378
+ };
4379
+ var closeAbove = (depth) => {
4380
+ const dropped = state2.live.splice(depth);
4381
+ for (let i = dropped.length - 1; i >= 0; i -= 1) {
4382
+ const entry = dropped[i];
4383
+ let closing = void 0;
3966
4384
  try {
3967
- history.pushState(brandMarker(surface), "", surface.url);
3968
- entry.live = true;
3969
- } catch {
3970
- entry.live = false;
4385
+ closing = entry.surface.close();
4386
+ } catch (err) {
4387
+ queueMicrotask(() => {
4388
+ throw err;
4389
+ });
4390
+ }
4391
+ void Promise.resolve(closing).then(() => {
4392
+ if (!entry.surface.isOpen()) return;
4393
+ const known = (e) => e.surface === entry.surface;
4394
+ if (state2.live.some(known) || state2.deferred.some(known)) return;
4395
+ enlist(entry);
4396
+ });
4397
+ }
4398
+ };
4399
+ function onPopState(event) {
4400
+ const kind = state2.pending.shift();
4401
+ const byUser = kind !== "pop";
4402
+ const landed = plain(event.state);
4403
+ const depth = depthOf(landed);
4404
+ const from = state2.position;
4405
+ const liveBefore = state2.live.length;
4406
+ const grew = history.length > state2.knownLength;
4407
+ state2.knownLength = history.length;
4408
+ const within = inStack(landed);
4409
+ if (within && depth !== null) {
4410
+ state2.position = depth;
4411
+ if (depth > state2.live.length) {
4412
+ const forward = from !== null && from < depth;
4413
+ traverse(forward ? 1 : -1, byUser ? "skip" : "pop");
4414
+ } else if (depth < state2.live.length) {
4415
+ if (byUser) closeAbove(depth);
4416
+ } else {
4417
+ rebrand(depth);
4418
+ }
4419
+ } else if (isMarker(landed)) {
4420
+ state2.position = null;
4421
+ traverse(-1, byUser ? "skip" : "pop");
4422
+ } else {
4423
+ state2.position = null;
4424
+ if (byUser && !grew && state2.live.length > 0) {
4425
+ closeAbove(state2.live.length - 1);
3971
4426
  }
3972
- });
3973
- if (entry.live) {
3974
- entries.push(entry);
3975
- ensureListener();
3976
4427
  }
4428
+ const moved = location.href !== state2.href;
4429
+ state2.href = location.href;
4430
+ if (kind === void 0 && moved && (liveBefore === 0 || !within)) {
4431
+ notifyRouteChange();
4432
+ }
4433
+ settleIfIdle();
4434
+ maybeTeardownListener();
4435
+ }
4436
+ function pushBackMarker(surface) {
4437
+ const entry = { surface };
4438
+ if (hasHistory()) enlist(entry);
3977
4439
  return entry;
3978
4440
  }
3979
4441
  function popBackMarker(handle) {
3980
- if (!hasWindow() || !hasHistory()) return;
4442
+ if (!hasHistory()) return Promise.resolve();
3981
4443
  const entry = handle;
3982
- const idx = entries.indexOf(entry);
3983
- if (idx === -1 || !entry.live) return;
3984
- const last = entries.length - 1;
3985
- entry.live = false;
3986
- entries.splice(idx, 1);
3987
- let rebrand = null;
3988
- if (idx !== last) {
3989
- const rebrandSurface = entries[entries.length - 1].surface;
3990
- const rebrandUrl = entry.priorUrl;
3991
- rebrand = () => {
3992
- runInternal(() => {
3993
- history.replaceState(
3994
- brandMarker(rebrandSurface),
3995
- "",
3996
- rebrandUrl !== void 0 ? rebrandUrl : location.href
3997
- );
3998
- });
3999
- };
4444
+ const waiting = state2.deferred.indexOf(entry);
4445
+ if (waiting !== -1) state2.deferred.splice(waiting, 1);
4446
+ const idx = state2.live.indexOf(entry);
4447
+ if (idx === -1) return historySettled();
4448
+ const top = state2.live.length;
4449
+ state2.live.splice(idx, 1);
4450
+ const current = plain(history.state);
4451
+ const onTop = isMarker(current) && inStack(current) && depthOf(current) === top;
4452
+ if (onTop && state2.pending.length === 0) {
4453
+ traverse(-1, "pop");
4454
+ } else {
4455
+ state2.leftovers = true;
4456
+ ensureListener();
4000
4457
  }
4001
- pendingRebrands.push(rebrand);
4002
- suppress += 1;
4003
- runInternal(() => {
4004
- try {
4005
- history.back();
4006
- } catch {
4007
- suppress = Math.max(0, suppress - 1);
4008
- pendingRebrands.pop();
4009
- }
4010
- });
4011
4458
  maybeTeardownListener();
4459
+ return historySettled();
4460
+ }
4461
+ function releaseOrphanMarker() {
4462
+ if (!hasHistory() || state2.orphanChecked) return;
4463
+ state2.orphanChecked = true;
4464
+ if (state2.live.length > 0 || state2.pending.length > 0) return;
4465
+ if (!isMarker(plain(history.state))) return;
4466
+ state2.leftovers = true;
4467
+ traverse(-1, "pop");
4012
4468
  }
4469
+ function historySettled() {
4470
+ if (state2.pending.length === 0) return Promise.resolve();
4471
+ return new Promise((resolve) => {
4472
+ state2.waiters.push(resolve);
4473
+ });
4474
+ }
4475
+ var wrap = (original) => function(...args) {
4476
+ original.apply(this, args);
4477
+ state2.knownLength = history.length;
4478
+ if (location.href === state2.href) return;
4479
+ state2.href = location.href;
4480
+ if (state2.internal === 0 && state2.subscribers.size > 0) {
4481
+ notifyRouteChange();
4482
+ }
4483
+ };
4484
+ var installPatch = () => {
4485
+ if (!state2.push || history.pushState === state2.push.original) {
4486
+ const original = history.pushState;
4487
+ const wrapper = wrap(original);
4488
+ history.pushState = wrapper;
4489
+ state2.push = { wrapper, original };
4490
+ }
4491
+ if (!state2.replace || history.replaceState === state2.replace.original) {
4492
+ const original = history.replaceState;
4493
+ const wrapper = wrap(original);
4494
+ history.replaceState = wrapper;
4495
+ state2.replace = { wrapper, original };
4496
+ }
4497
+ };
4498
+ var removePatch = () => {
4499
+ if (state2.push && history.pushState === state2.push.wrapper) {
4500
+ history.pushState = state2.push.original;
4501
+ state2.push = null;
4502
+ }
4503
+ if (state2.replace && history.replaceState === state2.replace.wrapper) {
4504
+ history.replaceState = state2.replace.original;
4505
+ state2.replace = null;
4506
+ }
4507
+ };
4013
4508
  function subscribeRouteChange(fn) {
4014
- if (!hasWindow() || !hasHistory()) return () => {
4509
+ if (!hasHistory()) return () => {
4015
4510
  };
4016
- subscribers.add(fn);
4017
- if (subscribers.size === 1) installPatch();
4511
+ state2.subscribers.add(fn);
4512
+ state2.href = location.href;
4513
+ state2.knownLength = history.length;
4514
+ installPatch();
4018
4515
  ensureListener();
4019
4516
  return () => {
4020
- if (!subscribers.has(fn)) return;
4021
- subscribers.delete(fn);
4022
- if (subscribers.size === 0) removePatch();
4517
+ if (!state2.subscribers.delete(fn)) return;
4518
+ if (state2.subscribers.size === 0) removePatch();
4023
4519
  maybeTeardownListener();
4024
4520
  };
4025
4521
  }
@@ -4035,36 +4531,37 @@ function installRoute(deps) {
4035
4531
  };
4036
4532
  }
4037
4533
  let handle = null;
4534
+ const surface = {
4535
+ isOpen: () => !deps.isDestroyed() && deps.isOpen(),
4536
+ close: deps.close,
4537
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4538
+ url: deps.routedTo
4539
+ };
4038
4540
  const onOpen = () => {
4039
4541
  if (handle) return;
4040
- handle = pushBackMarker({
4041
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
4042
- isTop: deps.isTopSheet,
4043
- close: deps.close,
4044
- markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4045
- url: deps.routedTo
4046
- });
4542
+ handle = pushBackMarker(surface);
4047
4543
  };
4048
4544
  const onClose = () => {
4049
4545
  if (!handle) return;
4050
- popBackMarker(handle);
4546
+ deps.hold(popBackMarker(handle));
4051
4547
  handle = null;
4052
4548
  };
4053
4549
  const unsubscribeOpen = deps.on("open", onOpen);
4054
4550
  const unsubscribeClose = deps.on("close", onClose);
4055
- if (deps.getSize() > 0) onOpen();
4551
+ if (deps.isOpen()) onOpen();
4552
+ else releaseOrphanMarker();
4056
4553
  return () => {
4057
4554
  unsubscribeOpen();
4058
4555
  unsubscribeClose();
4059
4556
  if (handle) {
4060
- popBackMarker(handle);
4557
+ void popBackMarker(handle);
4061
4558
  handle = null;
4062
4559
  }
4063
4560
  };
4064
4561
  }
4065
4562
  function installRouteChange(deps) {
4066
4563
  return subscribeRouteChange(() => {
4067
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
4564
+ if (deps.isDestroyed() || !deps.isOpen()) return;
4068
4565
  void deps.close();
4069
4566
  });
4070
4567
  }
@@ -4098,7 +4595,10 @@ function installAutoCollapse(deps) {
4098
4595
  };
4099
4596
  const fire = () => {
4100
4597
  if (deps.isDestroyed()) return;
4101
- if (deps.isDragging()) return;
4598
+ if (deps.isDragging() || deps.isAnimating()) {
4599
+ reset();
4600
+ return;
4601
+ }
4102
4602
  const active = deps.resolveSnap(deps.getActiveId());
4103
4603
  if (!active || active.size === 0) return;
4104
4604
  let target = null;
@@ -4135,6 +4635,9 @@ function installContentDrag(deps) {
4135
4635
 
4136
4636
  // src/core/features/visual-viewport.ts
4137
4637
  var SAFETY_PAD = 8;
4638
+ var KEYBOARD_MIN_INSET = 1;
4639
+ var KEYBOARD_MIN_COVER = 60;
4640
+ var ZOOMED = 1.01;
4138
4641
  function installVisualViewport(deps) {
4139
4642
  if (typeof window === "undefined" || !window.visualViewport) {
4140
4643
  return () => {
@@ -4142,36 +4645,36 @@ function installVisualViewport(deps) {
4142
4645
  }
4143
4646
  const vv = window.visualViewport;
4144
4647
  let rafId = null;
4648
+ const coveredByKeyboard = () => {
4649
+ if ((vv.scale || 1) > ZOOMED) return 0;
4650
+ const covered = window.innerHeight - ((vv.offsetTop || 0) + vv.height);
4651
+ return covered >= KEYBOARD_MIN_COVER ? Math.round(covered) : 0;
4652
+ };
4145
4653
  const onResize = () => {
4146
4654
  if (rafId !== null) return;
4147
4655
  rafId = requestAnimationFrame(() => {
4148
4656
  rafId = null;
4149
4657
  if (deps.isDestroyed()) return;
4658
+ deps.setKeyboardInset(coveredByKeyboard());
4659
+ const before = seatOf(deps);
4150
4660
  deps.recomputeSnaps();
4151
4661
  const isVertical = deps.isVerticalAxis();
4152
4662
  const visible = isVertical ? vv.height : vv.width;
4153
4663
  const windowSize = isVertical ? window.innerHeight : window.innerWidth;
4154
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
4664
+ const pad = windowSize - visible > KEYBOARD_MIN_INSET ? SAFETY_PAD : 0;
4665
+ const clamp = Math.max(0, Math.min(windowSize, visible) - pad);
4155
4666
  if (clamp < deps.getMaxAxisSize()) {
4156
4667
  deps.setMaxAxisSize(clamp);
4157
4668
  }
4158
- const wasAnimating = deps.isAnimating?.() ?? false;
4159
- deps.cancelInFlight();
4160
- deps.newCycle();
4161
- const max = deps.getMaxAxisSize();
4162
- deps.setSize(Math.min(deps.getSize(), max));
4163
- const current = deps.resolveActiveSnap();
4164
- if (current && !deps.isDragging()) {
4165
- deps.applySize(Math.min(current.size, max));
4166
- if (wasAnimating) deps.resyncAfterCancel?.();
4167
- } else {
4168
- deps.applySize(deps.getSize());
4169
- }
4669
+ if (sameSeat(before, seatOf(deps))) return;
4670
+ deps.reseat();
4170
4671
  });
4171
4672
  };
4172
4673
  vv.addEventListener("resize", onResize);
4674
+ vv.addEventListener("scroll", onResize);
4173
4675
  return () => {
4174
4676
  vv.removeEventListener("resize", onResize);
4677
+ vv.removeEventListener("scroll", onResize);
4175
4678
  if (rafId !== null) cancelAnimationFrame(rafId);
4176
4679
  };
4177
4680
  }
@@ -4186,7 +4689,7 @@ function routeFeature() {
4186
4689
  teardowns.add(
4187
4690
  installRouteChange({
4188
4691
  isDestroyed: ctx.isDestroyed,
4189
- getSize: ctx.getSize,
4692
+ isOpen: () => ctx.getPhase() !== "closed",
4190
4693
  close: () => ctx.close()
4191
4694
  })
4192
4695
  );
@@ -4195,10 +4698,10 @@ function routeFeature() {
4195
4698
  installRoute({
4196
4699
  routedTo: ctx.options.routedTo,
4197
4700
  closeOnBack: ctx.options.closeOnBack,
4198
- isTopSheet: ctx.isTopSheet,
4199
- getSize: ctx.getSize,
4701
+ isOpen: () => ctx.getPhase() !== "closed",
4200
4702
  isDestroyed: ctx.isDestroyed,
4201
4703
  close: () => ctx.close("back"),
4704
+ hold: ctx.holdSettle,
4202
4705
  on: ctx.on,
4203
4706
  sheetId: ctx.sheetId
4204
4707
  })
@@ -4224,6 +4727,7 @@ function autoCollapseFeature() {
4224
4727
  ms: ctx.options.autoCollapseAfter,
4225
4728
  isDestroyed: ctx.isDestroyed,
4226
4729
  isDragging: ctx.isDragging,
4730
+ isAnimating: ctx.isAnimating,
4227
4731
  getAllowedIds: ctx.getAllowedIds,
4228
4732
  getActiveId: ctx.getActiveId,
4229
4733
  resolveSnap: ctx.resolveSnap,
@@ -4251,21 +4755,14 @@ function visualViewportFeature() {
4251
4755
  install: (ctx) => {
4252
4756
  if (typeof window === "undefined") return;
4253
4757
  return installVisualViewport({
4254
- element: ctx.element,
4255
4758
  isVerticalAxis: ctx.isVerticalAxis,
4256
4759
  isDestroyed: ctx.isDestroyed,
4257
- isDragging: ctx.isDragging,
4258
4760
  recomputeSnaps: ctx.recomputeSnaps,
4259
4761
  resolveActiveSnap: ctx.resolveActiveSnap,
4260
4762
  getMaxAxisSize: ctx.getMaxAxisSize,
4261
- getSize: ctx.getSize,
4262
4763
  setMaxAxisSize: ctx.setMaxAxisSize,
4263
- setSize: ctx.setSize,
4264
- applySize: ctx.applySize,
4265
- cancelInFlight: ctx.cancelInFlight,
4266
- newCycle: ctx.newCycle,
4267
- isAnimating: ctx.isAnimating,
4268
- resyncAfterCancel: ctx.resyncAfterResize
4764
+ setKeyboardInset: ctx.setKeyboardInset,
4765
+ reseat: ctx.reseat
4269
4766
  });
4270
4767
  }
4271
4768
  };
@@ -4455,6 +4952,6 @@ var createSheetManager = (initial = {}) => {
4455
4952
  };
4456
4953
  };
4457
4954
 
4458
- export { BottomSheetCore, BottomSheetEngine, SCRIM_PRESETS, applyOverlayPosition, autoCollapseFeature, contentSwipeFeature, createSheetManager, defaultEngineFeatures, installFocusTrap, installGestures, lockBodyScroll, persistFeature, resolveSheetAnchoredStyle, resolveSnap, resolveSnapList, routeFeature, runAnchorTransition, sheetStack, visualViewportFeature };
4955
+ export { BottomSheetCore, BottomSheetEngine, SCRIM_PRESETS, applyOverlayPosition, autoCollapseFeature, contentSwipeFeature, createSheetManager, defaultEngineFeatures, historySettled, installFocusTrap, installGestures, lockBodyScroll, persistFeature, resolveSheetAnchoredStyle, resolveSnap, resolveSnapList, routeFeature, runAnchorTransition, sheetStack, visualViewportFeature };
4459
4956
  //# sourceMappingURL=index.js.map
4460
4957
  //# sourceMappingURL=index.js.map