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