@surdeddd/bottom-sheet 0.23.4 → 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 -527
  2. package/README.ru.md +533 -545
  3. package/dist/{BottomSheetCore-C5tJYVMY.d.cts → BottomSheetCore-B_r4KCQL.d.cts} +48 -13
  4. package/dist/{BottomSheetCore-DxSrC4QC.d.ts → BottomSheetCore-B_r4KCQL.d.ts} +48 -13
  5. package/dist/{BottomSheetEngine-B3F9qukX.d.cts → BottomSheetEngine-0F7mjyvF.d.cts} +1 -1
  6. package/dist/{BottomSheetEngine-BchzgqGG.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 +0 -1
  12. package/dist/debug.d.ts +0 -1
  13. package/dist/{default-features-DwVTzDwP.d.ts → default-features-4Xwv6aVt.d.ts} +4 -2
  14. package/dist/{default-features-BGiCSmV1.d.cts → default-features-DX2NOxP6.d.cts} +4 -2
  15. package/dist/element.cjs +1421 -697
  16. package/dist/element.d.cts +11 -3
  17. package/dist/element.d.ts +11 -3
  18. package/dist/element.iife.global.js +2 -2
  19. package/dist/element.js +1421 -697
  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-C7TSQkVK.d.cts → index-2shlxhcG.d.cts} +1 -1
  25. package/dist/{index-CdM1tke5.d.ts → index-CNCRQnpc.d.ts} +1 -1
  26. package/dist/index.cjs +1129 -631
  27. package/dist/index.d.cts +11 -6
  28. package/dist/index.d.ts +11 -6
  29. package/dist/index.js +1129 -632
  30. package/dist/integrations/formik.cjs +21 -10
  31. package/dist/integrations/formik.d.cts +0 -1
  32. package/dist/integrations/formik.d.ts +0 -1
  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 +0 -1
  36. package/dist/integrations/react-hook-form.d.ts +0 -1
  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 +1 -3
  42. package/dist/overlay.d.ts +1 -3
  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 +12 -30
  50. package/dist/qwik.d.ts +12 -30
  51. package/dist/qwik.js +1326 -701
  52. package/dist/react.cjs +1373 -750
  53. package/dist/react.d.cts +26 -12
  54. package/dist/react.d.ts +26 -12
  55. package/dist/react.js +1375 -752
  56. package/dist/solid.cjs +1332 -727
  57. package/dist/solid.d.cts +13 -438
  58. package/dist/solid.d.ts +13 -438
  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 +1209 -676
  63. package/dist/svelte-core.js +1207 -677
  64. package/dist/svelte-src/BottomSheet.svelte +101 -58
  65. package/dist/svelte.cjs +1323 -705
  66. package/dist/svelte.d.cts +45 -25
  67. package/dist/svelte.d.ts +45 -25
  68. package/dist/svelte.js +1323 -705
  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 +0 -1
  75. package/dist/webgl.d.ts +0 -1
  76. package/dist/webgl.js +38 -7
  77. package/llms.txt +330 -0
  78. package/package.json +41 -19
  79. package/dist/transform-jVfAikE9.d.cts +0 -3
  80. package/dist/transform-jVfAikE9.d.ts +0 -3
package/dist/react.cjs CHANGED
@@ -8,6 +8,15 @@ var __defProp = Object.defineProperty;
8
8
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
9
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
10
10
 
11
+ // src/core/primitives/shared.ts
12
+ var STORE_KEY = /* @__PURE__ */ Symbol.for("@surdeddd/bottom-sheet/shared/1");
13
+ function shared(name, init) {
14
+ const host = globalThis;
15
+ const store = host[STORE_KEY] ?? (host[STORE_KEY] = {});
16
+ if (!(name in store)) store[name] = init();
17
+ return store[name];
18
+ }
19
+
11
20
  // src/core/lifecycle/sheet-stack.ts
12
21
  var BASE_Z = 100;
13
22
  var STEP = 10;
@@ -75,7 +84,7 @@ var SheetStack = class {
75
84
  });
76
85
  }
77
86
  };
78
- var sheetStack = new SheetStack();
87
+ var sheetStack = shared("stack", () => new SheetStack());
79
88
 
80
89
  // src/core/primitives/transform.ts
81
90
  function buildTransformTemplate(axis) {
@@ -100,7 +109,7 @@ var CLOSED_TRANSFORM = {
100
109
  var layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
101
110
 
102
111
  // src/core/primitives/instance-id.ts
103
- var counters = {};
112
+ var counters = shared("instanceIds", () => ({}));
104
113
  function nextInstanceId(prefix) {
105
114
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
106
115
  return `${prefix}-${crypto.randomUUID()}`;
@@ -128,7 +137,13 @@ function createEventBus() {
128
137
  const set = listeners.get(event);
129
138
  if (!set) return;
130
139
  for (const fn of [...set]) {
131
- fn(payload);
140
+ try {
141
+ fn(payload);
142
+ } catch (err) {
143
+ queueMicrotask(() => {
144
+ throw err;
145
+ });
146
+ }
132
147
  }
133
148
  },
134
149
  listenerCount(event) {
@@ -140,8 +155,23 @@ function createEventBus() {
140
155
  };
141
156
  }
142
157
 
158
+ // src/core/primitives/dev-warn.ts
159
+ function devWarn(...args) {
160
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
161
+ console.warn(...args);
162
+ }
163
+ }
164
+
143
165
  // src/core/primitives/css-length.ts
144
166
  var probe = null;
167
+ var rejected = /* @__PURE__ */ new Set();
168
+ var warnRejected = (value) => {
169
+ if (rejected.has(value)) return;
170
+ rejected.add(value);
171
+ devWarn(
172
+ `[BottomSheet] snap size "${value}" is not a CSS length the browser accepts \u2014 it resolves to 0.`
173
+ );
174
+ };
145
175
  var PROBE_ATTR = "data-bs-probe";
146
176
  var ensureProbe = (axis) => {
147
177
  if (typeof document === "undefined") return null;
@@ -188,19 +218,14 @@ var resolveSnap = (point, mode, measureFit) => {
188
218
  if (!el) return 0;
189
219
  if (axis === "vertical") {
190
220
  el.style.height = point;
221
+ if (el.style.height === "") warnRejected(point);
191
222
  return Math.max(0, el.getBoundingClientRect().height);
192
223
  }
193
224
  el.style.width = point;
225
+ if (el.style.width === "") warnRejected(point);
194
226
  return Math.max(0, el.getBoundingClientRect().width);
195
227
  };
196
228
 
197
- // src/core/primitives/dev-warn.ts
198
- function devWarn(...args) {
199
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
200
- console.warn(...args);
201
- }
202
- }
203
-
204
229
  // src/core/primitives/snap-points.ts
205
230
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
206
231
  const raw = resolveSnap(p.size, mode, measureFit);
@@ -212,6 +237,10 @@ var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
212
237
  }
213
238
  return { id: p.id, size: raw };
214
239
  }).sort((a, b) => a.size - b.size);
240
+ var startsClosed = (points, initial, allowed) => {
241
+ const id = initial ?? allowed?.[0] ?? points[0]?.id;
242
+ return points.find((p) => p.id === id)?.size === 0;
243
+ };
215
244
  var VH_NOT_DVH = /(^|[^a-z])vh\b/i;
216
245
  var auditVhUsage = (points) => {
217
246
  for (const p of points) {
@@ -329,6 +358,7 @@ var SnapResolver = class {
329
358
  setMode(mode) {
330
359
  if (mode === this.mode) return;
331
360
  this.mode = mode;
361
+ this.notified = false;
332
362
  this.recompute();
333
363
  }
334
364
  setAllowedIds(ids) {
@@ -338,14 +368,20 @@ var SnapResolver = class {
338
368
  findById(id) {
339
369
  return findById(id, this.resolved);
340
370
  }
341
- findDragSettleTarget(args) {
371
+ findDragSettleTarget(args, openOnly = false) {
342
372
  return findDragSettleTarget({
343
373
  ...args,
344
374
  resolved: this.resolved,
345
- allowed: this.allowedIds,
375
+ allowed: this.pool(openOnly),
346
376
  maxAxisSize: this.maxAxisSize
347
377
  });
348
378
  }
379
+ nearest(size, openOnly) {
380
+ return findNearest(size, this.resolved, this.pool(openOnly));
381
+ }
382
+ pool(openOnly) {
383
+ return openOnly ? this.allowedIds.filter((id) => (this.findById(id)?.size ?? 0) > 0) : this.allowedIds;
384
+ }
349
385
  getAllowedRange() {
350
386
  return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
351
387
  }
@@ -393,19 +429,20 @@ var POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
393
429
  var RANGE_DIVISION_EPSILON = 1e-4;
394
430
 
395
431
  // src/core/features/resize-observer.ts
432
+ var seatOf = (deps) => {
433
+ const snap = deps.resolveActiveSnap();
434
+ const max = deps.getMaxAxisSize();
435
+ return { target: snap ? Math.min(snap.size, max) : null, max };
436
+ };
437
+ 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;
396
438
  function installResizeObserver(deps) {
397
439
  if (typeof window === "undefined") {
398
440
  return () => {
399
441
  };
400
442
  }
401
- const seatFor = () => {
402
- const snap = deps.resolveActiveSnap();
403
- const max = deps.getMaxAxisSize();
404
- return { target: snap ? Math.min(snap.size, max) : null, max };
405
- };
406
443
  const onResize = () => {
407
444
  if (deps.isDestroyed()) return;
408
- const before = seatFor();
445
+ const before = seatOf(deps);
409
446
  deps.recomputeSnaps();
410
447
  const mode = deps.getMode();
411
448
  const isVerticalAxis = mode === "bottom" || mode === "top";
@@ -414,21 +451,8 @@ function installResizeObserver(deps) {
414
451
  if (viewportSize2 > 0 && viewportSize2 < max) {
415
452
  deps.setMaxAxisSize(viewportSize2);
416
453
  }
417
- const after = seatFor();
418
- 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) {
419
- return;
420
- }
421
- const wasAnimating = deps.isAnimating?.() ?? false;
422
- deps.cancelInFlight();
423
- deps.newCycle();
424
- const current = deps.resolveActiveSnap();
425
- const newMax = deps.getMaxAxisSize();
426
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
427
- deps.setSize(targetSize);
428
- if (current && !deps.isDragging()) {
429
- deps.applySize(targetSize);
430
- if (wasAnimating) deps.resyncAfterCancel?.();
431
- }
454
+ if (sameSeat(before, seatOf(deps))) return;
455
+ deps.reseat();
432
456
  };
433
457
  let resizeObserver = null;
434
458
  if (typeof ResizeObserver !== "undefined") {
@@ -440,7 +464,7 @@ function installResizeObserver(deps) {
440
464
  window.addEventListener("orientationchange", onResize);
441
465
  const onVisibility = () => {
442
466
  if (deps.isDestroyed()) return;
443
- if (document.hidden) deps.cancelInFlight();
467
+ if (document.hidden) deps.landNow();
444
468
  };
445
469
  document.addEventListener("visibilitychange", onVisibility);
446
470
  return () => {
@@ -465,6 +489,16 @@ var contentTargets = (scroller) => {
465
489
  }
466
490
  return out;
467
491
  };
492
+ var chromeTargets = (deps) => {
493
+ const out = [];
494
+ for (const child of Array.from(deps.element.children)) {
495
+ if (child !== deps.scrollContainer) out.push(child);
496
+ }
497
+ if (deps.handle !== deps.element && !out.includes(deps.handle)) {
498
+ out.push(deps.handle);
499
+ }
500
+ return out;
501
+ };
468
502
  function installFitObserver(deps) {
469
503
  if (typeof ResizeObserver === "undefined") return () => {
470
504
  };
@@ -480,7 +514,6 @@ function installFitObserver(deps) {
480
514
  });
481
515
  };
482
516
  const ro = new ResizeObserver(schedule);
483
- ro.observe(deps.handle);
484
517
  const scroller = deps.scrollContainer;
485
518
  const observed = /* @__PURE__ */ new Set();
486
519
  const slots = /* @__PURE__ */ new Set();
@@ -497,7 +530,7 @@ function installFitObserver(deps) {
497
530
  };
498
531
  const resync = () => {
499
532
  trackSlots();
500
- const targets = contentTargets(scroller);
533
+ const targets = [...chromeTargets(deps), ...contentTargets(scroller)];
501
534
  for (const el of observed) {
502
535
  if (!targets.includes(el)) {
503
536
  ro.unobserve(el);
@@ -512,13 +545,12 @@ function installFitObserver(deps) {
512
545
  }
513
546
  schedule();
514
547
  };
548
+ resync();
515
549
  let mo;
516
- if (scroller) {
517
- resync();
518
- if (typeof MutationObserver !== "undefined") {
519
- mo = new MutationObserver(resync);
520
- mo.observe(scroller, { childList: true });
521
- }
550
+ if (typeof MutationObserver !== "undefined") {
551
+ mo = new MutationObserver(resync);
552
+ mo.observe(deps.element, { childList: true });
553
+ if (scroller) mo.observe(scroller, { childList: true });
522
554
  }
523
555
  return () => {
524
556
  ro.disconnect();
@@ -530,6 +562,72 @@ function installFitObserver(deps) {
530
562
  };
531
563
  }
532
564
 
565
+ // src/core/features/teleport.ts
566
+ var resolveTeleportTarget = (target) => {
567
+ if (!target) return null;
568
+ if (typeof target !== "string") return target;
569
+ if (typeof document === "undefined") return null;
570
+ if (target === "body") return document.body;
571
+ return document.querySelector(target);
572
+ };
573
+ var ROOT_CLASS = "bs-root";
574
+ var homes = /* @__PURE__ */ new WeakMap();
575
+ var homeRootOf = (root) => (root && homes.get(root)) ?? null;
576
+ var mirrorClasses = (home, wrapper) => {
577
+ wrapper.className = home?.className ?? "";
578
+ wrapper.classList.add(ROOT_CLASS);
579
+ };
580
+ var teleportElements = (els, target) => {
581
+ const nodes = els.filter(
582
+ (el) => !!el && el.parentNode !== target
583
+ );
584
+ if (nodes.length === 0) return () => {
585
+ };
586
+ const home = nodes[0].closest(`.${ROOT_CLASS}`);
587
+ if (home && home.parentNode === target) return () => {
588
+ };
589
+ const wrapper = target.ownerDocument.createElement("div");
590
+ wrapper.setAttribute("data-bs-teleported", "");
591
+ mirrorClasses(home, wrapper);
592
+ let watcher = null;
593
+ if (home) {
594
+ for (const name of Array.from(home.style)) {
595
+ if (name.startsWith("--")) {
596
+ wrapper.style.setProperty(name, home.style.getPropertyValue(name));
597
+ }
598
+ }
599
+ homes.set(wrapper, home);
600
+ if (typeof MutationObserver !== "undefined") {
601
+ watcher = new MutationObserver(() => mirrorClasses(home, wrapper));
602
+ watcher.observe(home, { attributes: true, attributeFilter: ["class"] });
603
+ }
604
+ }
605
+ target.appendChild(wrapper);
606
+ const records = [];
607
+ for (const el of nodes) {
608
+ records.push({ el, parent: el.parentNode, next: el.nextSibling });
609
+ wrapper.appendChild(el);
610
+ }
611
+ return () => {
612
+ watcher?.disconnect();
613
+ for (let i = records.length - 1; i >= 0; i--) {
614
+ const { el, parent, next } = records[i];
615
+ if (parent && parent.isConnected) {
616
+ const before = next && next.parentNode === parent ? next : null;
617
+ try {
618
+ parent.insertBefore(el, before);
619
+ } catch {
620
+ el.parentNode?.removeChild(el);
621
+ }
622
+ } else {
623
+ el.parentNode?.removeChild(el);
624
+ }
625
+ }
626
+ homes.delete(wrapper);
627
+ wrapper.parentNode?.removeChild(wrapper);
628
+ };
629
+ };
630
+
533
631
  // src/core/features/max-height-controller.ts
534
632
  function createMaxHeightController(deps) {
535
633
  let maxHeightCap;
@@ -633,8 +731,9 @@ function installSliderKeyboard(deps) {
633
731
  const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
634
732
  const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
635
733
  const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
636
- const sheetExpandsKey = deps.mode === "top" ? stepDown : stepUp;
637
- const sheetCollapsesKey = deps.mode === "top" ? stepUp : stepDown;
734
+ const flipped = deps.mode === "top" || deps.mode === "right";
735
+ const sheetExpandsKey = flipped ? stepDown : stepUp;
736
+ const sheetCollapsesKey = flipped ? stepUp : stepDown;
638
737
  const onHandleKey = (e) => {
639
738
  if (deps.isDestroyed()) return;
640
739
  const allowed = deps.getAllowedIds();
@@ -710,11 +809,12 @@ var WriteSentinel = class {
710
809
  // src/core/primitives/overlay-position.ts
711
810
  function resolveSheetAnchoredStyle(mode, position, inset) {
712
811
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
812
+ const lifted = `calc(var(--bs-size, 0px) + var(--bs-keyboard-inset, 0px) + ${inset})`;
713
813
  switch (mode) {
714
814
  case "bottom":
715
- if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
716
- if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
717
- return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
815
+ if (position === "sheet-top-left") return { bottom: lifted, left: inset };
816
+ if (position === "sheet-top-right") return { bottom: lifted, right: inset };
817
+ return { bottom: lifted, left: "50%", transform: "translateX(-50%)" };
718
818
  case "top":
719
819
  if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
720
820
  if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
@@ -729,6 +829,13 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
729
829
  return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
730
830
  }
731
831
  }
832
+ function resetOverlayPosition(ws) {
833
+ ws.top = "";
834
+ ws.bottom = "";
835
+ ws.left = "";
836
+ ws.right = "";
837
+ ws.transform = "";
838
+ }
732
839
  function applyOverlayPosition(ws, mode, position, inset) {
733
840
  switch (position) {
734
841
  case "top-left":
@@ -789,6 +896,8 @@ function applyOverlayPosition(ws, mode, position, inset) {
789
896
  // src/core/controllers/scrim-controller.ts
790
897
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
791
898
  var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
899
+ var BACKDROP_COLOR_VAR = "--bs-backdrop-color";
900
+ var unit = (value) => Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : 1;
792
901
  var ScrimController = class {
793
902
  constructor(deps, opts) {
794
903
  __publicField(this, "screenComponent");
@@ -803,11 +912,13 @@ var ScrimController = class {
803
912
  __publicField(this, "scrimMode");
804
913
  __publicField(this, "backdropRange");
805
914
  __publicField(this, "screenRange");
915
+ __publicField(this, "backdropPeak");
806
916
  __publicField(this, "scrimEnabled", true);
807
917
  __publicField(this, "savedScrimRanges", null);
808
918
  __publicField(this, "scrimTapToCloseEnabled", false);
809
919
  __publicField(this, "detachScrimTap", null);
810
920
  __publicField(this, "scrimOverlayTeardown", null);
921
+ __publicField(this, "placeOverlay", null);
811
922
  __publicField(this, "backdropOpacitySentinel", new WriteSentinel());
812
923
  __publicField(this, "screenOpacitySentinel", new WriteSentinel());
813
924
  __publicField(this, "lastBackdropPointer", "");
@@ -824,6 +935,8 @@ var ScrimController = class {
824
935
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
825
936
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
826
937
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
938
+ this.backdropPeak = unit(opts.backdropOpacity ?? 1);
939
+ this.setBackdropColor(opts.backdropColor);
827
940
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
828
941
  const explicitColor = opts.scrimColor ?? preset?.color;
829
942
  const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
@@ -853,24 +966,19 @@ var ScrimController = class {
853
966
  this.setScrimTapToClose(true);
854
967
  }
855
968
  }
856
- applyOpacity(progress, progressChanged) {
857
- if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
969
+ applyOpacity(progress, progressChanged, epsilon = OPACITY_WRITE_EPSILON) {
970
+ if (epsilon > 0 && !progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
858
971
  return;
859
972
  }
860
973
  if (this.backdrop) {
861
974
  const [s, e] = this.backdropRange;
862
975
  const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
863
- const backdropOpacity = Math.min(
864
- Math.max((progress - s) / range, 0),
865
- 1
866
- );
867
- if (this.backdropOpacitySentinel.shouldWrite(
868
- backdropOpacity,
869
- OPACITY_WRITE_EPSILON
870
- )) {
976
+ const along = Math.min(Math.max((progress - s) / range, 0), 1);
977
+ const backdropOpacity = along * this.backdropPeak;
978
+ if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, epsilon)) {
871
979
  this.backdrop.style.opacity = String(backdropOpacity);
872
980
  }
873
- const nextPointer = backdropOpacity > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
981
+ const nextPointer = along > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
874
982
  if (nextPointer !== this.lastBackdropPointer) {
875
983
  this.backdrop.style.pointerEvents = nextPointer;
876
984
  this.lastBackdropPointer = nextPointer;
@@ -885,10 +993,7 @@ var ScrimController = class {
885
993
  (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)
886
994
  )
887
995
  );
888
- if (this.screenOpacitySentinel.shouldWrite(
889
- screenOpacity,
890
- OPACITY_WRITE_EPSILON
891
- )) {
996
+ if (this.screenOpacitySentinel.shouldWrite(screenOpacity, epsilon)) {
892
997
  this.screenComponent.style.opacity = String(screenOpacity);
893
998
  }
894
999
  const nextDisplay = screenOpacity > 0 ? "" : "none";
@@ -904,6 +1009,17 @@ var ScrimController = class {
904
1009
  this.invalidateOpacityCache();
905
1010
  applySize();
906
1011
  }
1012
+ setBackdropColor(color) {
1013
+ if (this.isDestroyed() || !this.backdrop) return;
1014
+ if (color) this.backdrop.style.setProperty(BACKDROP_COLOR_VAR, color);
1015
+ else this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1016
+ }
1017
+ setBackdropOpacity(value, applySize) {
1018
+ if (this.isDestroyed()) return;
1019
+ this.backdropPeak = unit(value);
1020
+ this.invalidateOpacityCache();
1021
+ applySize();
1022
+ }
907
1023
  setScreenRange(range, applySize) {
908
1024
  if (this.isDestroyed()) return;
909
1025
  this.screenRange = range;
@@ -926,6 +1042,8 @@ var ScrimController = class {
926
1042
  if (this.isDestroyed() || mode === this.mode) return;
927
1043
  this.mode = mode;
928
1044
  this.invalidateOpacityCache();
1045
+ if (this.scrimMode === "above-sheet") this.applyAboveSheetInset();
1046
+ this.placeOverlay?.();
929
1047
  }
930
1048
  setScrimMode(mode, applySize) {
931
1049
  if (this.isDestroyed()) return;
@@ -938,7 +1056,7 @@ var ScrimController = class {
938
1056
  if (previous === "above-sheet" && mode !== "above-sheet") {
939
1057
  s.position = "";
940
1058
  s.inset = "";
941
- s.pointerEvents = "";
1059
+ this.syncPointer();
942
1060
  }
943
1061
  if (mode === "above-sheet") {
944
1062
  this.applyAboveSheetInset();
@@ -963,9 +1081,6 @@ var ScrimController = class {
963
1081
  if (enabled === installed) return;
964
1082
  if (enabled) {
965
1083
  const target = this.screenComponent;
966
- if (target.style.pointerEvents === "none") {
967
- target.style.pointerEvents = "auto";
968
- }
969
1084
  const onClick = () => {
970
1085
  if (this.isDestroyed()) return;
971
1086
  if (this.closeFn) {
@@ -988,6 +1103,11 @@ var ScrimController = class {
988
1103
  this.detachScrimTap = null;
989
1104
  this.scrimTapToCloseEnabled = false;
990
1105
  }
1106
+ this.syncPointer();
1107
+ }
1108
+ syncPointer() {
1109
+ if (!this.screenComponent) return;
1110
+ this.screenComponent.style.pointerEvents = this.scrimInteractive || this.detachScrimTap ? "auto" : "none";
991
1111
  }
992
1112
  setScrimEnabled(enabled, applySize) {
993
1113
  if (this.isDestroyed()) return;
@@ -1071,8 +1191,12 @@ var ScrimController = class {
1071
1191
  const wrapper = screen.ownerDocument.createElement("div");
1072
1192
  wrapper.className = "bs-scrim-overlay";
1073
1193
  const ws = wrapper.style;
1074
- ws.position = "absolute";
1075
- applyOverlayPosition(ws, this.mode, position, inset);
1194
+ ws.position = "fixed";
1195
+ const place = () => {
1196
+ resetOverlayPosition(ws);
1197
+ applyOverlayPosition(ws, this.mode, position, inset);
1198
+ };
1199
+ place();
1076
1200
  if (interactive) {
1077
1201
  ws.pointerEvents = "auto";
1078
1202
  }
@@ -1086,8 +1210,10 @@ var ScrimController = class {
1086
1210
  };
1087
1211
  }
1088
1212
  host.appendChild(wrapper);
1213
+ this.placeOverlay = place;
1089
1214
  const teardown = () => {
1090
1215
  if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1216
+ if (this.placeOverlay === place) this.placeOverlay = null;
1091
1217
  if (this.scrimOverlayTeardown === teardown) {
1092
1218
  this.scrimOverlayTeardown = null;
1093
1219
  }
@@ -1111,6 +1237,7 @@ var ScrimController = class {
1111
1237
  if (this.backdrop) {
1112
1238
  this.backdrop.style.opacity = "";
1113
1239
  this.backdrop.style.pointerEvents = "";
1240
+ this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1114
1241
  }
1115
1242
  this.invalidateOpacityCache();
1116
1243
  this.lastBackdropPointer = "";
@@ -1135,13 +1262,13 @@ var ScrimController = class {
1135
1262
  }
1136
1263
  if (opts.interactive !== void 0) {
1137
1264
  this.scrimInteractive = opts.interactive;
1138
- s.pointerEvents = opts.interactive ? "auto" : "none";
1265
+ this.syncPointer();
1139
1266
  }
1140
1267
  }
1141
1268
  applyAboveSheetInset() {
1142
1269
  if (!this.screenComponent) return;
1143
1270
  const insetByMode = {
1144
- bottom: "0 0 var(--bs-size) 0",
1271
+ bottom: "0 0 calc(var(--bs-size) + var(--bs-keyboard-inset, 0px)) 0",
1145
1272
  top: "var(--bs-size) 0 0 0",
1146
1273
  left: "0 0 0 var(--bs-size)",
1147
1274
  right: "0 var(--bs-size) 0 0"
@@ -1149,7 +1276,7 @@ var ScrimController = class {
1149
1276
  const s = this.screenComponent.style;
1150
1277
  s.position = "fixed";
1151
1278
  s.inset = insetByMode[this.mode];
1152
- s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1279
+ this.syncPointer();
1153
1280
  }
1154
1281
  };
1155
1282
 
@@ -1162,6 +1289,7 @@ var easeOutBack = (t) => {
1162
1289
  var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1163
1290
  var easeLinear = (t) => t;
1164
1291
  var easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
1292
+ var MIN_RETARGET_MS = 120;
1165
1293
  var tween = ({
1166
1294
  from,
1167
1295
  to,
@@ -1170,12 +1298,18 @@ var tween = ({
1170
1298
  onUpdate
1171
1299
  }) => {
1172
1300
  let cancelled = false;
1301
+ let finished = false;
1173
1302
  let rafId = 0;
1174
1303
  let resolveFn = null;
1175
- const start = performance.now();
1304
+ let start = performance.now();
1305
+ let origin = from;
1306
+ let target = to;
1307
+ let span = duration;
1308
+ let last = from;
1176
1309
  const promise = new Promise((resolve) => {
1177
1310
  resolveFn = resolve;
1178
1311
  if (duration <= 0) {
1312
+ finished = true;
1179
1313
  onUpdate(to);
1180
1314
  resolve();
1181
1315
  return;
@@ -1183,12 +1317,13 @@ var tween = ({
1183
1317
  const step = (now) => {
1184
1318
  if (cancelled) return;
1185
1319
  const elapsed = Math.max(0, now - start);
1186
- const t = Math.min(elapsed / duration, 1);
1187
- const value = from + (to - from) * easing(t);
1188
- onUpdate(value);
1320
+ const t = Math.min(elapsed / span, 1);
1321
+ last = origin + (target - origin) * easing(t);
1322
+ onUpdate(last);
1189
1323
  if (t < 1) {
1190
1324
  rafId = requestAnimationFrame(step);
1191
1325
  } else {
1326
+ finished = true;
1192
1327
  resolve();
1193
1328
  }
1194
1329
  };
@@ -1200,6 +1335,15 @@ var tween = ({
1200
1335
  cancelAnimationFrame(rafId);
1201
1336
  resolveFn?.();
1202
1337
  },
1338
+ retarget: (next) => {
1339
+ if (cancelled || finished || next === target) return;
1340
+ const now = performance.now();
1341
+ const remaining = span - Math.max(0, now - start);
1342
+ origin = last;
1343
+ target = next;
1344
+ start = now;
1345
+ span = Math.max(remaining, MIN_RETARGET_MS);
1346
+ },
1203
1347
  promise
1204
1348
  };
1205
1349
  };
@@ -1217,22 +1361,32 @@ var DEFAULT_SPRING = {
1217
1361
  restSpeed: 0.5
1218
1362
  };
1219
1363
  var MAX_DT = 1 / 30;
1364
+ var MAX_DURATION_MS = 3e3;
1220
1365
  var runSpring = ({
1221
1366
  from,
1222
1367
  to,
1223
1368
  velocity = 0,
1224
1369
  config,
1370
+ maxDuration = MAX_DURATION_MS,
1225
1371
  onUpdate
1226
1372
  }) => {
1227
1373
  const cfg = { ...DEFAULT_SPRING, ...config };
1228
1374
  let cancelled = false;
1375
+ let finished = false;
1229
1376
  let rafId = 0;
1230
1377
  let resolveFn = null;
1231
1378
  let x = from;
1232
1379
  let v = velocity;
1380
+ let target = to;
1233
1381
  let lastTime = performance.now();
1382
+ let deadline = lastTime + maxDuration;
1234
1383
  const promise = new Promise((resolve) => {
1235
1384
  resolveFn = resolve;
1385
+ const land = () => {
1386
+ finished = true;
1387
+ onUpdate(target, 0);
1388
+ resolve();
1389
+ };
1236
1390
  const step = (now) => {
1237
1391
  if (cancelled) return;
1238
1392
  const dt = Math.min(Math.max(0, now - lastTime) / 1e3, MAX_DT);
@@ -1241,21 +1395,19 @@ var runSpring = ({
1241
1395
  const subDt = dt / steps;
1242
1396
  let stepsLeft = steps;
1243
1397
  while (stepsLeft-- > 0) {
1244
- const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1398
+ const force = -cfg.stiffness * (x - target) - cfg.damping * v;
1245
1399
  const accel = force / cfg.mass;
1246
1400
  v += accel * subDt;
1247
1401
  x += v * subDt;
1248
1402
  }
1249
- if (!Number.isFinite(x) || !Number.isFinite(v)) {
1250
- onUpdate(to, 0);
1251
- resolve();
1403
+ if (!Number.isFinite(x) || !Number.isFinite(v) || lastTime >= deadline) {
1404
+ land();
1252
1405
  return;
1253
1406
  }
1254
1407
  onUpdate(x, v);
1255
- const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1408
+ const isAtRest = Math.abs(target - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1256
1409
  if (isAtRest) {
1257
- onUpdate(to, 0);
1258
- resolve();
1410
+ land();
1259
1411
  } else {
1260
1412
  rafId = requestAnimationFrame(step);
1261
1413
  }
@@ -1268,6 +1420,11 @@ var runSpring = ({
1268
1420
  cancelAnimationFrame(rafId);
1269
1421
  resolveFn?.();
1270
1422
  },
1423
+ retarget: (next) => {
1424
+ if (cancelled || finished) return;
1425
+ target = next;
1426
+ deadline = performance.now() + maxDuration;
1427
+ },
1271
1428
  promise
1272
1429
  };
1273
1430
  };
@@ -1419,21 +1576,40 @@ var AnimationRunner = class {
1419
1576
  this.applySizeFn(size);
1420
1577
  }
1421
1578
  cancel() {
1422
- this.currentTween?.cancel();
1423
- this.currentSpring?.cancel();
1579
+ const tween2 = this.currentTween;
1580
+ const spring = this.currentSpring;
1581
+ this.currentTween = null;
1582
+ this.currentSpring = null;
1583
+ tween2?.cancel();
1584
+ spring?.cancel();
1424
1585
  this.cancelWaapi();
1425
1586
  }
1587
+ retarget(target) {
1588
+ if (this.currentSpring) {
1589
+ this.currentSpring.retarget(target);
1590
+ return true;
1591
+ }
1592
+ if (this.currentTween) {
1593
+ this.currentTween.retarget(target);
1594
+ return true;
1595
+ }
1596
+ if (this.currentWaapi) {
1597
+ this.currentWaapi.retarget(target);
1598
+ return true;
1599
+ }
1600
+ return false;
1601
+ }
1426
1602
  async animateTo(target, velocityPxPerMs) {
1427
1603
  if (target === this.getSize()) {
1428
- if (!this.isAnimating && !this.isDragging()) {
1604
+ if (this.isAnimating) this.cancel();
1605
+ if (!this.isDragging()) {
1429
1606
  this.applySizeFn(target);
1607
+ this.getRootEl()?.removeAttribute("data-animating");
1430
1608
  this.element.style.willChange = "";
1431
1609
  }
1432
1610
  return;
1433
1611
  }
1434
- this.currentTween?.cancel();
1435
- this.currentSpring?.cancel();
1436
- this.cancelWaapi();
1612
+ this.cancel();
1437
1613
  this.getRootEl()?.setAttribute("data-animating", "true");
1438
1614
  this.element.style.willChange = "transform";
1439
1615
  if (this.reducedMotion) {
@@ -1443,19 +1619,13 @@ var AnimationRunner = class {
1443
1619
  return;
1444
1620
  }
1445
1621
  if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1446
- const samples = this.animationKind === "spring" ? sampleSpringSettle(
1447
- this.getSize(),
1448
- target,
1449
- velocityPxPerMs * VELOCITY_PX_PER_S,
1450
- this.springConfig
1451
- ) : sampleTweenSettle(
1622
+ const samples = this.sampleSettle(
1452
1623
  this.getSize(),
1453
1624
  target,
1454
- this.duration,
1455
- this.easing
1625
+ velocityPxPerMs * VELOCITY_PX_PER_S
1456
1626
  );
1457
1627
  if (samples.values.length >= 2 && samples.durationMs > 0) {
1458
- await this.runWaapiSettle(samples, target);
1628
+ await this.runWaapiSettle({ samples, target });
1459
1629
  if (!this.isAnimating) {
1460
1630
  this.getRootEl()?.removeAttribute("data-animating");
1461
1631
  if (!this.isDragging()) this.element.style.willChange = "";
@@ -1497,7 +1667,10 @@ var AnimationRunner = class {
1497
1667
  }
1498
1668
  }
1499
1669
  }
1500
- async runWaapiSettle(samples, target) {
1670
+ sampleSettle(from, to, velocityPxPerS) {
1671
+ return this.animationKind === "spring" ? sampleSpringSettle(from, to, velocityPxPerS, this.springConfig) : sampleTweenSettle(from, to, this.duration, this.easing);
1672
+ }
1673
+ startWaapiLeg(samples, onRetarget) {
1501
1674
  const applyAux = this.applyAux;
1502
1675
  const getTransformFor = this.getTransformFor;
1503
1676
  const frames = samples.values.map((v) => ({
@@ -1512,16 +1685,20 @@ var AnimationRunner = class {
1512
1685
  let stopped = false;
1513
1686
  let auxRaf = 0;
1514
1687
  const lastIdx = samples.values.length - 1;
1688
+ const cursor = () => Math.min((Number(anim.currentTime) || 0) / samples.stepMs, lastIdx);
1515
1689
  const sizeNow = () => {
1516
- const at = Math.min(
1517
- (Number(anim.currentTime) || 0) / samples.stepMs,
1518
- lastIdx
1519
- );
1690
+ const at = cursor();
1520
1691
  const idx = Math.floor(at);
1521
1692
  const from = samples.values[idx];
1522
1693
  const to = samples.values[Math.min(idx + 1, lastIdx)];
1523
1694
  return from + (to - from) * (at - idx);
1524
1695
  };
1696
+ const velocityNow = () => {
1697
+ const idx = Math.min(Math.floor(cursor()), Math.max(lastIdx - 1, 0));
1698
+ const from = samples.values[idx];
1699
+ const to = samples.values[Math.min(idx + 1, lastIdx)];
1700
+ return (to - from) / samples.stepMs * 1e3;
1701
+ };
1525
1702
  const auxTick = () => {
1526
1703
  if (stopped) return;
1527
1704
  applyAux(sizeNow());
@@ -1534,22 +1711,55 @@ var AnimationRunner = class {
1534
1711
  stopped = true;
1535
1712
  cancelAnimationFrame(auxRaf);
1536
1713
  },
1537
- sizeNow
1714
+ sizeNow,
1715
+ velocityNow,
1716
+ retarget: (to) => onRetarget(entry, to)
1538
1717
  };
1539
- this.currentWaapi = entry;
1540
- try {
1541
- await anim.finished;
1542
- } catch {
1543
- }
1544
- entry.stop();
1545
- const wasCurrent = this.currentWaapi === entry;
1546
- if (wasCurrent) this.currentWaapi = null;
1547
- if (wasCurrent && anim.playState === "finished") {
1548
- this.applySizeFn(target);
1718
+ return entry;
1719
+ }
1720
+ async runWaapiSettle(first) {
1721
+ let leg = first;
1722
+ for (; ; ) {
1723
+ let queued = null;
1724
+ const entry = this.startWaapiLeg(leg.samples, (running, to) => {
1725
+ const size = this.readShownSize?.() ?? running.sizeNow();
1726
+ queued = {
1727
+ samples: this.sampleSettle(size, to, running.velocityNow()),
1728
+ target: to
1729
+ };
1730
+ running.stop();
1731
+ try {
1732
+ running.anim.cancel();
1733
+ } catch {
1734
+ }
1735
+ this.applySizeFn(size);
1736
+ });
1737
+ this.currentWaapi = entry;
1549
1738
  try {
1550
- anim.cancel();
1739
+ await entry.anim.finished;
1551
1740
  } catch {
1552
1741
  }
1742
+ entry.stop();
1743
+ const wasCurrent = this.currentWaapi === entry;
1744
+ const next = queued;
1745
+ if (wasCurrent && next) {
1746
+ if (next.samples.values.length >= 2 && next.samples.durationMs > 0) {
1747
+ leg = next;
1748
+ continue;
1749
+ }
1750
+ this.currentWaapi = null;
1751
+ this.applySizeFn(next.target);
1752
+ return;
1753
+ }
1754
+ if (wasCurrent) this.currentWaapi = null;
1755
+ if (wasCurrent && entry.anim.playState === "finished") {
1756
+ this.applySizeFn(leg.target);
1757
+ try {
1758
+ entry.anim.cancel();
1759
+ } catch {
1760
+ }
1761
+ }
1762
+ return;
1553
1763
  }
1554
1764
  }
1555
1765
  destroy() {
@@ -1604,27 +1814,51 @@ var collectFocusables = (root) => {
1604
1814
  var focusables = (root) => collectFocusables(root).filter(
1605
1815
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1606
1816
  );
1817
+ var findDeep = (root, selector) => {
1818
+ const direct = root.querySelector(selector);
1819
+ if (direct || typeof HTMLSlotElement === "undefined") return direct;
1820
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1821
+ for (const assigned of slot.assignedElements()) {
1822
+ if (assigned.matches(selector)) return assigned;
1823
+ const inner = findDeep(assigned, selector);
1824
+ if (inner) return inner;
1825
+ }
1826
+ }
1827
+ return null;
1828
+ };
1829
+ var FOCUS_ALLOW_ATTR = "data-bs-focus-allow";
1830
+ var composedParent = (node) => {
1831
+ const slot = node.assignedSlot;
1832
+ if (slot) return slot;
1833
+ const parent = node.parentNode;
1834
+ return parent instanceof ShadowRoot ? parent.host : parent;
1835
+ };
1607
1836
  var containsComposed = (container, node) => {
1608
- let n = node;
1609
- while (n) {
1837
+ for (let n = node; n; n = composedParent(n)) {
1610
1838
  if (n === container) return true;
1611
- const slot = n.assignedSlot;
1612
- if (slot) {
1613
- n = slot;
1614
- continue;
1615
- }
1616
- const parent = n.parentNode;
1617
- n = parent instanceof ShadowRoot ? parent.host : parent;
1618
1839
  }
1619
1840
  return false;
1620
1841
  };
1621
- var trapStack = [];
1842
+ var isAllowedOutside = (node) => {
1843
+ for (let n = node; n; n = composedParent(n)) {
1844
+ if (n instanceof Element && n.hasAttribute(FOCUS_ALLOW_ATTR)) return true;
1845
+ }
1846
+ return false;
1847
+ };
1848
+ var deepActiveElement = () => {
1849
+ let active = document.activeElement;
1850
+ while (active?.shadowRoot?.activeElement) {
1851
+ active = active.shadowRoot.activeElement;
1852
+ }
1853
+ return active;
1854
+ };
1855
+ var trapStack = shared("focusTraps", () => []);
1622
1856
  var installFocusTrap = (container, options = {}) => {
1623
1857
  if (typeof document === "undefined") return () => {
1624
1858
  };
1625
1859
  const token = {};
1626
- trapStack.push(token);
1627
- const isActive = () => trapStack[trapStack.length - 1] === token;
1860
+ if (!options.isActive) trapStack.push(token);
1861
+ const isActive = options.isActive ?? (() => trapStack[trapStack.length - 1] === token);
1628
1862
  const previouslyFocused = document.activeElement;
1629
1863
  const focusInitial = () => {
1630
1864
  const spec = options.initialFocus;
@@ -1633,10 +1867,11 @@ var installFocusTrap = (container, options = {}) => {
1633
1867
  container.focus({ preventScroll: true });
1634
1868
  return;
1635
1869
  }
1636
- const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1870
+ const initial = typeof spec === "string" ? findDeep(container, spec) : spec ?? focusables(container)[0] ?? container;
1637
1871
  initial?.focus({ preventScroll: true });
1638
1872
  };
1639
- focusInitial();
1873
+ if (!containsComposed(container, deepActiveElement())) focusInitial();
1874
+ let backwards = false;
1640
1875
  const handleKey = (e) => {
1641
1876
  if (!isActive()) return;
1642
1877
  if (e.key === "Escape" && options.onEscape) {
@@ -1644,6 +1879,10 @@ var installFocusTrap = (container, options = {}) => {
1644
1879
  return;
1645
1880
  }
1646
1881
  if (e.key !== "Tab") return;
1882
+ backwards = e.shiftKey;
1883
+ setTimeout(() => {
1884
+ backwards = false;
1885
+ }, 0);
1647
1886
  const list = focusables(container);
1648
1887
  if (list.length === 0) {
1649
1888
  e.preventDefault();
@@ -1651,7 +1890,7 @@ var installFocusTrap = (container, options = {}) => {
1651
1890
  }
1652
1891
  const first = list[0];
1653
1892
  const last = list[list.length - 1];
1654
- const active = document.activeElement;
1893
+ const active = deepActiveElement();
1655
1894
  if (e.shiftKey && active === first) {
1656
1895
  e.preventDefault();
1657
1896
  last.focus();
@@ -1662,11 +1901,11 @@ var installFocusTrap = (container, options = {}) => {
1662
1901
  };
1663
1902
  const handleFocusIn = (e) => {
1664
1903
  if (!isActive()) return;
1665
- const target = e.target;
1666
- if (target && !containsComposed(container, target)) {
1667
- const list = focusables(container);
1668
- list[0]?.focus({ preventScroll: true });
1669
- }
1904
+ const target = e.composedPath()[0] ?? e.target;
1905
+ if (!target || containsComposed(container, target)) return;
1906
+ if (isAllowedOutside(target)) return;
1907
+ const list = focusables(container);
1908
+ list[backwards ? list.length - 1 : 0]?.focus({ preventScroll: true });
1670
1909
  };
1671
1910
  document.addEventListener("keydown", handleKey, true);
1672
1911
  document.addEventListener("focusin", handleFocusIn, true);
@@ -1688,21 +1927,25 @@ var installFocusTrap = (container, options = {}) => {
1688
1927
  };
1689
1928
 
1690
1929
  // src/core/lifecycle/scroll-lock.ts
1691
- var lockCount = 0;
1692
- var savedStyles = null;
1930
+ var state = shared("scrollLock", () => ({
1931
+ count: 0,
1932
+ saved: null
1933
+ }));
1693
1934
  var lockBodyScroll = () => {
1694
1935
  if (typeof document === "undefined") return () => {
1695
1936
  };
1696
- if (lockCount === 0) {
1937
+ if (state.count === 0) {
1697
1938
  const body = document.body;
1698
- savedStyles = {
1939
+ const saved = {
1699
1940
  overflow: body.style.overflow,
1700
1941
  position: body.style.position,
1701
1942
  top: body.style.top,
1702
1943
  width: body.style.width,
1703
1944
  paddingRight: body.style.paddingRight,
1945
+ scrollX: window.scrollX,
1704
1946
  scrollY: window.scrollY
1705
1947
  };
1948
+ state.saved = saved;
1706
1949
  const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1707
1950
  if (scrollbarGap > 0) {
1708
1951
  const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
@@ -1710,53 +1953,72 @@ var lockBodyScroll = () => {
1710
1953
  }
1711
1954
  body.style.overflow = "hidden";
1712
1955
  body.style.position = "fixed";
1713
- body.style.top = `-${savedStyles.scrollY}px`;
1956
+ body.style.top = `-${saved.scrollY}px`;
1714
1957
  body.style.width = "100%";
1715
1958
  }
1716
- lockCount++;
1959
+ state.count++;
1717
1960
  let released = false;
1718
1961
  return () => {
1719
1962
  if (released) return;
1720
1963
  released = true;
1721
- lockCount = Math.max(0, lockCount - 1);
1722
- if (lockCount === 0 && savedStyles) {
1964
+ state.count = Math.max(0, state.count - 1);
1965
+ if (state.count === 0 && state.saved) {
1723
1966
  const body = document.body;
1724
- body.style.overflow = savedStyles.overflow;
1725
- body.style.position = savedStyles.position;
1726
- body.style.top = savedStyles.top;
1727
- body.style.width = savedStyles.width;
1728
- body.style.paddingRight = savedStyles.paddingRight;
1729
- window.scrollTo(0, savedStyles.scrollY);
1730
- savedStyles = null;
1967
+ const saved = state.saved;
1968
+ body.style.overflow = saved.overflow;
1969
+ body.style.position = saved.position;
1970
+ body.style.top = saved.top;
1971
+ body.style.width = saved.width;
1972
+ body.style.paddingRight = saved.paddingRight;
1973
+ window.scrollTo({
1974
+ top: saved.scrollY,
1975
+ left: saved.scrollX,
1976
+ behavior: "instant"
1977
+ });
1978
+ state.saved = null;
1731
1979
  }
1732
1980
  };
1733
1981
  };
1734
1982
 
1735
1983
  // src/core/features/inert-siblings.ts
1984
+ var OWN_ROOTS = ".bs-root, .ovl-root";
1985
+ var LAYER_CLASSES = [
1986
+ "bs-backdrop",
1987
+ "bs-screen",
1988
+ "bs-root",
1989
+ "ovl-backdrop",
1990
+ "ovl-root"
1991
+ ];
1992
+ var isLayer = (el) => LAYER_CLASSES.some((name) => el.classList.contains(name));
1736
1993
  function createInertSiblings(rootProvider) {
1737
1994
  const tracked = [];
1995
+ const remove = () => {
1996
+ for (const el of tracked) el.removeAttribute("inert");
1997
+ tracked.length = 0;
1998
+ };
1738
1999
  return {
1739
2000
  apply() {
1740
2001
  if (typeof document === "undefined") return;
1741
- let top = rootProvider();
1742
- while (top.parentElement && top.parentElement !== document.body) {
1743
- top = top.parentElement;
1744
- }
1745
- const parent = top.parentElement ?? document.body;
1746
- for (const child of Array.from(parent.children)) {
1747
- if (child === top) continue;
1748
- if (child.hasAttribute("inert")) continue;
1749
- if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
2002
+ remove();
2003
+ const surface = rootProvider();
2004
+ let node = surface.closest(OWN_ROOTS) ?? surface;
2005
+ while (node && node !== document.body) {
2006
+ const parent = node.parentElement;
2007
+ if (!parent) {
2008
+ const root = node.getRootNode();
2009
+ node = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
1750
2010
  continue;
1751
2011
  }
1752
- child.setAttribute("inert", "");
1753
- tracked.push(child);
2012
+ for (const sibling of Array.from(parent.children)) {
2013
+ if (sibling === node) continue;
2014
+ if (sibling.hasAttribute("inert") || isLayer(sibling)) continue;
2015
+ sibling.setAttribute("inert", "");
2016
+ tracked.push(sibling);
2017
+ }
2018
+ node = parent;
1754
2019
  }
1755
2020
  },
1756
- remove() {
1757
- for (const el of tracked) el.removeAttribute("inert");
1758
- tracked.length = 0;
1759
- }
2021
+ remove
1760
2022
  };
1761
2023
  }
1762
2024
 
@@ -1764,6 +2026,7 @@ function createInertSiblings(rootProvider) {
1764
2026
  var LifecycleController = class {
1765
2027
  constructor(deps, opts) {
1766
2028
  __publicField(this, "element");
2029
+ __publicField(this, "isTop");
1767
2030
  __publicField(this, "focusTrapEnabled");
1768
2031
  __publicField(this, "initialFocus");
1769
2032
  __publicField(this, "closeOnEscape");
@@ -1777,6 +2040,7 @@ var LifecycleController = class {
1777
2040
  __publicField(this, "installed", false);
1778
2041
  __publicField(this, "destroyed", false);
1779
2042
  this.element = deps.element;
2043
+ this.isTop = deps.isTop;
1780
2044
  this.focusTrapEnabled = opts.focusTrap ?? false;
1781
2045
  this.initialFocus = opts.initialFocus;
1782
2046
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1797,12 +2061,17 @@ var LifecycleController = class {
1797
2061
  }
1798
2062
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1799
2063
  this.releaseFocusTrap = installFocusTrap(this.element, {
1800
- initialFocus: this.initialFocus
2064
+ initialFocus: this.initialFocus,
2065
+ isActive: this.isTop
1801
2066
  });
1802
2067
  }
1803
- if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
1804
- this.inertSiblings.apply();
1805
- }
2068
+ this.syncTop();
2069
+ }
2070
+ syncTop() {
2071
+ if (!this.installed || !this.inertSiblingsEnabled) return;
2072
+ if (!this.shouldApplyInertSiblings()) return;
2073
+ if (this.isTop?.() ?? true) this.inertSiblings.apply();
2074
+ else this.inertSiblings.remove();
1806
2075
  }
1807
2076
  setReturnFocus(target) {
1808
2077
  if (this.destroyed) return;
@@ -1972,10 +2241,14 @@ var installGestures = (handle, mode, callbacks, options = {}) => {
1972
2241
  if (wasPending) return;
1973
2242
  callbacks.onCancel?.();
1974
2243
  };
2244
+ const onLostCapture = (e) => {
2245
+ if (e.target === handle) onPointerCancel(e);
2246
+ };
1975
2247
  handle.addEventListener("pointerdown", onPointerDown);
1976
2248
  handle.addEventListener("pointermove", onPointerMove);
1977
2249
  handle.addEventListener("pointerup", finishGesture);
1978
2250
  handle.addEventListener("pointercancel", onPointerCancel);
2251
+ handle.addEventListener("lostpointercapture", onLostCapture);
1979
2252
  const managesTouchAction = options.manageTouchAction !== false;
1980
2253
  const prevTouchAction = handle.style.touchAction;
1981
2254
  if (managesTouchAction) {
@@ -1986,6 +2259,7 @@ var installGestures = (handle, mode, callbacks, options = {}) => {
1986
2259
  handle.removeEventListener("pointermove", onPointerMove);
1987
2260
  handle.removeEventListener("pointerup", finishGesture);
1988
2261
  handle.removeEventListener("pointercancel", onPointerCancel);
2262
+ handle.removeEventListener("lostpointercapture", onLostCapture);
1989
2263
  if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1990
2264
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1991
2265
  handle.releasePointerCapture(activePointerId);
@@ -2102,8 +2376,7 @@ var GestureController = class {
2102
2376
  size: this.deps.getDragContext().size,
2103
2377
  velocity: 0
2104
2378
  });
2105
- const restoreTo = this.dragStartSize;
2106
- void this.deps.animateTo(restoreTo, 0);
2379
+ this.deps.restoreAfterDrag();
2107
2380
  }
2108
2381
  }, this.deps.gestureOptions);
2109
2382
  return this.detach;
@@ -2168,6 +2441,11 @@ var readDirection = (el) => {
2168
2441
  }
2169
2442
  return "ltr";
2170
2443
  };
2444
+ var guessPhysicalMode = (mode) => {
2445
+ if (mode === "start") return "left";
2446
+ if (mode === "end") return "right";
2447
+ return mode ?? "bottom";
2448
+ };
2171
2449
  var resolveMode = (mode, el) => {
2172
2450
  if (!isLogicalMode(mode)) return mode;
2173
2451
  const rtl = readDirection(el) === "rtl";
@@ -2220,22 +2498,24 @@ function createScrollCache(deps) {
2220
2498
  return size < max / 2;
2221
2499
  };
2222
2500
  return {
2223
- cache(fromId, prevSize, nextSize) {
2501
+ leave(fromId, prevSize, nextSize) {
2224
2502
  const container = deps.scrollContainer;
2225
2503
  if (!container) return;
2226
- if (isSmallSize(prevSize)) return;
2227
- if (!isSmallSize(nextSize)) return;
2504
+ if (nextSize <= 0) {
2505
+ positions.clear();
2506
+ return;
2507
+ }
2508
+ if (isSmallSize(prevSize) || !isSmallSize(nextSize)) return;
2228
2509
  positions.set(fromId, container.scrollTop);
2229
2510
  },
2230
- restore(toId, prevSize, nextSize) {
2511
+ enter(toId, prevSize, nextSize) {
2231
2512
  const container = deps.scrollContainer;
2232
2513
  if (!container) return;
2233
- if (isSmallSize(nextSize)) return;
2234
- if (!isSmallSize(prevSize)) return;
2514
+ if (isSmallSize(nextSize) || !isSmallSize(prevSize)) return;
2235
2515
  const cached = positions.get(toId);
2236
2516
  if (cached === void 0) return;
2237
- container.scrollTop = cached;
2238
2517
  positions.delete(toId);
2518
+ container.scrollTop = cached;
2239
2519
  },
2240
2520
  clear() {
2241
2521
  positions.clear();
@@ -2349,17 +2629,18 @@ function attachAnchor(deps, opts) {
2349
2629
  const isDock = position === "dock-bottom" || position === "dock-top";
2350
2630
  const ws = wrapper.style;
2351
2631
  ws.position = "fixed";
2352
- if (isDock) {
2632
+ const place = () => {
2633
+ resetOverlayPosition(ws);
2634
+ if (!isDock) {
2635
+ applyOverlayPosition(ws, deps.getMode(), position, opts.inset ?? "16px");
2636
+ return;
2637
+ }
2353
2638
  ws.left = "0";
2354
2639
  ws.right = "0";
2355
- if (position === "dock-bottom") {
2356
- ws.bottom = "0";
2357
- } else {
2358
- ws.top = "0";
2359
- }
2360
- } else {
2361
- applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2362
- }
2640
+ if (position === "dock-bottom") ws.bottom = "0";
2641
+ else ws.top = "0";
2642
+ };
2643
+ place();
2363
2644
  if (opts.fadeRange) {
2364
2645
  const [r0, r1] = opts.fadeRange;
2365
2646
  const span = Math.max(r1 - r0, 1e-4);
@@ -2369,10 +2650,10 @@ function attachAnchor(deps, opts) {
2369
2650
  deps.host.appendChild(wrapper);
2370
2651
  let visible = null;
2371
2652
  let inFlight = null;
2372
- const shouldShow = (state) => {
2373
- if (typeof opts.showOn === "function") return opts.showOn(state);
2374
- if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2375
- return isDock || state.size > 0;
2653
+ const shouldShow = (state3) => {
2654
+ if (typeof opts.showOn === "function") return opts.showOn(state3);
2655
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state3.activeId);
2656
+ return isDock || state3.size > 0;
2376
2657
  };
2377
2658
  const applyVisibility = (next, animate) => {
2378
2659
  if (next === visible) return;
@@ -2419,7 +2700,8 @@ function attachAnchor(deps, opts) {
2419
2700
  },
2420
2701
  syncZ: (z) => {
2421
2702
  wrapper.style.zIndex = String(z);
2422
- }
2703
+ },
2704
+ reposition: place
2423
2705
  };
2424
2706
  }
2425
2707
 
@@ -2435,30 +2717,37 @@ var matchesRange = (def, progress) => {
2435
2717
  };
2436
2718
  function installScrimStages(deps, opts) {
2437
2719
  const doc = deps.host.ownerDocument;
2438
- const entries2 = opts.stages.map((def) => {
2720
+ const entries = opts.stages.map((def) => {
2439
2721
  const wrapper = doc.createElement("div");
2440
2722
  wrapper.className = "bs-scrim-stage";
2441
2723
  const ws = wrapper.style;
2442
- ws.position = "absolute";
2443
- applyOverlayPosition(
2444
- ws,
2445
- deps.mode,
2446
- def.position ?? opts.position ?? "center",
2447
- def.inset ?? opts.inset ?? "16px"
2448
- );
2724
+ ws.position = "fixed";
2449
2725
  ws.visibility = "hidden";
2450
2726
  ws.pointerEvents = "none";
2451
2727
  wrapper.appendChild(def.element);
2452
2728
  deps.host.appendChild(wrapper);
2453
2729
  return { def, wrapper, inFlight: null };
2454
2730
  });
2731
+ const place = () => {
2732
+ for (const entry of entries) {
2733
+ const ws = entry.wrapper.style;
2734
+ resetOverlayPosition(ws);
2735
+ applyOverlayPosition(
2736
+ ws,
2737
+ deps.getMode(),
2738
+ entry.def.position ?? opts.position ?? "center",
2739
+ entry.def.inset ?? opts.inset ?? "16px"
2740
+ );
2741
+ }
2742
+ };
2743
+ place();
2455
2744
  let active = null;
2456
2745
  const resolveActive = () => {
2457
- const state = deps.getState();
2458
- if (state.size === 0) {
2459
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2746
+ const state3 = deps.getState();
2747
+ if (state3.size === 0) {
2748
+ return entries.find((e) => matchesId(e.def, state3.activeId)) ?? null;
2460
2749
  }
2461
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2750
+ return entries.find((e) => matchesId(e.def, state3.activeId)) ?? entries.find((e) => matchesRange(e.def, state3.progress)) ?? null;
2462
2751
  };
2463
2752
  const hide = (entry, animate) => {
2464
2753
  entry.inFlight?.cancel();
@@ -2507,20 +2796,23 @@ function installScrimStages(deps, opts) {
2507
2796
  deps.on("open", () => update(true)),
2508
2797
  deps.on("close", () => update(true))
2509
2798
  ];
2510
- if (entries2.some((e) => e.def.forRange)) {
2799
+ if (entries.some((e) => e.def.forRange)) {
2511
2800
  offs.push(deps.on("progress", () => update(true)));
2512
2801
  }
2513
2802
  update(false);
2514
- return () => {
2515
- offs.forEach((off) => off());
2516
- for (const entry of entries2) {
2517
- entry.inFlight?.cancel();
2518
- entry.inFlight = null;
2519
- if (entry.wrapper.parentNode) {
2520
- entry.wrapper.parentNode.removeChild(entry.wrapper);
2803
+ return {
2804
+ reposition: place,
2805
+ detach: () => {
2806
+ offs.forEach((off) => off());
2807
+ for (const entry of entries) {
2808
+ entry.inFlight?.cancel();
2809
+ entry.inFlight = null;
2810
+ if (entry.wrapper.parentNode) {
2811
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2812
+ }
2521
2813
  }
2814
+ active = null;
2522
2815
  }
2523
- active = null;
2524
2816
  };
2525
2817
  }
2526
2818
 
@@ -2556,11 +2848,13 @@ function resolveEngineOptions(opts, extras) {
2556
2848
  scrimMode: opts.scrimMode,
2557
2849
  scrimColor: opts.scrimColor,
2558
2850
  scrimBlur: opts.scrimBlur,
2559
- scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2851
+ scrimInteractive: opts.scrimInteractive,
2560
2852
  scrimTapToClose: opts.scrimTapToClose,
2561
2853
  scrimPreset: opts.scrimPreset,
2562
2854
  screenRange: opts.screenRange,
2563
- backdropRange: opts.backdropRange
2855
+ backdropRange: opts.backdropRange,
2856
+ backdropOpacity: opts.backdropOpacity,
2857
+ backdropColor: opts.backdropColor
2564
2858
  },
2565
2859
  animation: {
2566
2860
  animation: opts.animation,
@@ -2639,6 +2933,7 @@ var BottomSheetCore = class {
2639
2933
  __publicField(this, "fitContentToSnap");
2640
2934
  __publicField(this, "settleAnimation");
2641
2935
  __publicField(this, "persistent");
2936
+ __publicField(this, "keyboardInset", 0);
2642
2937
  __publicField(this, "disableCloseFlag");
2643
2938
  __publicField(this, "disableDragFlag");
2644
2939
  __publicField(this, "closeOnRouteChange");
@@ -2653,6 +2948,7 @@ var BottomSheetCore = class {
2653
2948
  __publicField(this, "detachSheetGesture", null);
2654
2949
  __publicField(this, "detachSliderKeyboard", null);
2655
2950
  __publicField(this, "rootEl", null);
2951
+ __publicField(this, "homeRoot", null);
2656
2952
  __publicField(this, "destroyed", false);
2657
2953
  __publicField(this, "restClosed", false);
2658
2954
  __publicField(this, "currentViewTransition", null);
@@ -2660,8 +2956,13 @@ var BottomSheetCore = class {
2660
2956
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2661
2957
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2662
2958
  __publicField(this, "isTopSheet", true);
2663
- __publicField(this, "opening", false);
2664
- __publicField(this, "promotedForOpen", false);
2959
+ __publicField(this, "phase", "closed");
2960
+ __publicField(this, "transition", null);
2961
+ __publicField(this, "inFlight", null);
2962
+ __publicField(this, "outbox", []);
2963
+ __publicField(this, "flushing", false);
2964
+ __publicField(this, "openPending", false);
2965
+ __publicField(this, "settleHolds", []);
2665
2966
  __publicField(this, "allowOvershoot", false);
2666
2967
  __publicField(this, "progressPayload", {
2667
2968
  value: 0,
@@ -2682,6 +2983,7 @@ var BottomSheetCore = class {
2682
2983
  __publicField(this, "anchorHost", null);
2683
2984
  __publicField(this, "stackZ", 100);
2684
2985
  __publicField(this, "detachScrimStages", null);
2986
+ __publicField(this, "placeScrimStages", null);
2685
2987
  __publicField(this, "stackEffectEnabled", false);
2686
2988
  __publicField(this, "stackEffectPrimed", false);
2687
2989
  __publicField(this, "fitDeps");
@@ -2689,13 +2991,7 @@ var BottomSheetCore = class {
2689
2991
  __publicField(this, "featureList", []);
2690
2992
  __publicField(this, "featureCtx");
2691
2993
  const resolved = resolveEngineOptions(opts, {
2692
- shouldApplyInertSiblings: () => {
2693
- if (typeof document === "undefined") return false;
2694
- const bodyChildren = Array.from(document.body.children);
2695
- return bodyChildren.some(
2696
- (c) => c === opts.element || c.contains(opts.element)
2697
- );
2698
- }
2994
+ shouldApplyInertSiblings: () => opts.element.isConnected
2699
2995
  });
2700
2996
  this.element = opts.element;
2701
2997
  this.handle = opts.handle ?? opts.element;
@@ -2724,7 +3020,6 @@ var BottomSheetCore = class {
2724
3020
  void this.snapTo(id);
2725
3021
  },
2726
3022
  close: () => {
2727
- if (!this.canDismiss()) return;
2728
3023
  void this.close("backdrop");
2729
3024
  }
2730
3025
  },
@@ -2754,7 +3049,8 @@ var BottomSheetCore = class {
2754
3049
  this.lifecycle = new LifecycleController(
2755
3050
  {
2756
3051
  element: this.element,
2757
- close: () => this.close()
3052
+ close: () => this.close(),
3053
+ isTop: () => this.isTopSheet
2758
3054
  },
2759
3055
  resolved.lifecycle
2760
3056
  );
@@ -2781,6 +3077,7 @@ var BottomSheetCore = class {
2781
3077
  auditVhUsage(this.snapPointsRaw);
2782
3078
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2783
3079
  this.rootEl = this.element.closest(".bs-root");
3080
+ this.homeRoot = homeRootOf(this.rootEl);
2784
3081
  this.maxHeightDeps = {
2785
3082
  element: this.element,
2786
3083
  mode: this.mode,
@@ -2808,7 +3105,9 @@ var BottomSheetCore = class {
2808
3105
  );
2809
3106
  const initial = this.snaps.findById(this.activeId);
2810
3107
  if (initial) this.size = initial.size;
3108
+ this.phase = this.size > 0 ? "open" : "closed";
2811
3109
  this.applySize(this.size);
3110
+ this.restClosed = this.element.getAttribute("data-bs-rest") === "closed";
2812
3111
  this.setRestClosed(this.size === 0);
2813
3112
  this.updateAriaSlider();
2814
3113
  this.scrollCache = createScrollCache({
@@ -2819,12 +3118,11 @@ var BottomSheetCore = class {
2819
3118
  this.attach();
2820
3119
  this.registerInStack();
2821
3120
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2822
- const io = new IntersectionObserver((entries2) => {
2823
- for (const entry of entries2) {
3121
+ const io = new IntersectionObserver((entries) => {
3122
+ for (const entry of entries) {
2824
3123
  if (!entry.isIntersecting) continue;
2825
3124
  if (this.destroyed) return;
2826
- this.snaps.recompute();
2827
- this.healActiveSnap();
3125
+ this.recompute();
2828
3126
  io.disconnect();
2829
3127
  return;
2830
3128
  }
@@ -2834,6 +3132,7 @@ var BottomSheetCore = class {
2834
3132
  }
2835
3133
  this.teardowns.add(
2836
3134
  installFitObserver({
3135
+ element: this.element,
2837
3136
  handle: this.handle,
2838
3137
  scrollContainer: this.scrollContainer,
2839
3138
  hasFitSnap: () => this.snapPointsRaw.some(
@@ -2849,9 +3148,7 @@ var BottomSheetCore = class {
2849
3148
  this.applyAutoAriaLabelledBy();
2850
3149
  this.runFeatureStage("post");
2851
3150
  this.warnOrphanFeatureOptions();
2852
- if (this.size > 0) {
2853
- this.handleOpen();
2854
- }
3151
+ if (this.phase === "open") this.installLifecycle(true);
2855
3152
  }
2856
3153
  buildFeatureContext(autoCollapseAfter) {
2857
3154
  return {
@@ -2879,21 +3176,27 @@ var BottomSheetCore = class {
2879
3176
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2880
3177
  resolveSnap: (id) => this.snaps.findById(id),
2881
3178
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3179
+ getPhase: () => this.phase,
2882
3180
  setSize: (size) => {
2883
3181
  this.size = size;
2884
3182
  },
2885
3183
  setMaxAxisSize: (size) => {
2886
3184
  this.snaps.setMaxAxisSize(size);
2887
3185
  },
3186
+ setKeyboardInset: (px) => this.setKeyboardInset(px),
2888
3187
  applySize: (size) => this.applySize(size),
2889
3188
  recomputeSnaps: () => this.recomputeSnaps(),
3189
+ reseat: () => this.reseat(),
3190
+ holdSettle: (settled) => {
3191
+ this.settleHolds.push(settled);
3192
+ },
2890
3193
  newCycle: () => {
2891
3194
  this.newCycle();
2892
3195
  },
2893
- cancelInFlight: () => {
2894
- this.animation.cancel();
3196
+ cancelInFlight: () => this.suspend(),
3197
+ resyncAfterResize: () => {
3198
+ if (!this.destroyed) this.land(false);
2895
3199
  },
2896
- resyncAfterResize: () => this.resyncAfterResize(),
2897
3200
  snapTo: (id) => {
2898
3201
  void this.snapTo(id);
2899
3202
  },
@@ -2932,8 +3235,9 @@ var BottomSheetCore = class {
2932
3235
  return {
2933
3236
  size: this.size,
2934
3237
  activeId: this.activeId,
3238
+ phase: this.phase,
2935
3239
  isDragging: this.isDraggingAny(),
2936
- isAnimating: this.animation.isAnimating,
3240
+ isAnimating: this.animation.isAnimating || this.currentViewTransition !== null,
2937
3241
  progress: this.computeProgress(this.size)
2938
3242
  };
2939
3243
  }
@@ -2987,49 +3291,53 @@ var BottomSheetCore = class {
2987
3291
  getResolvedSnaps() {
2988
3292
  return this.snaps.getResolvedSnaps();
2989
3293
  }
2990
- async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2991
- if (this.destroyed) return;
3294
+ snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2992
3295
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
2993
- const velocityPxPerMs = opts.velocity ?? 0;
2994
- const externalSignal = opts.signal;
2995
- if (externalSignal?.aborted) return;
3296
+ return this.transitionTo(
3297
+ id,
3298
+ opts.velocity ?? 0,
3299
+ _skipBeforeSnap,
3300
+ opts.signal
3301
+ );
3302
+ }
3303
+ transitionTo(id, velocityPxPerMs = 0, skipBeforeSnap = false, externalSignal) {
3304
+ if (this.destroyed || externalSignal?.aborted) return Promise.resolve();
2996
3305
  const target = this.snaps.findById(id);
2997
3306
  if (!target) {
2998
3307
  devWarn(`[BottomSheet] unknown snap id: ${id}`);
2999
- return;
3308
+ return Promise.resolve();
3000
3309
  }
3001
3310
  if (!this.snaps.getAllowedIds().includes(id)) {
3002
3311
  devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
3003
- return;
3004
- }
3005
- if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
3006
- return;
3007
- }
3008
- const resumingOpen = this.opening;
3009
- const signal = this.newCycle();
3010
- const onExternalAbort = () => {
3011
- this.animation.cancel();
3012
- this.newCycle();
3013
- };
3312
+ return Promise.resolve();
3313
+ }
3314
+ if (target.size === 0 && this.closingBlocked()) return Promise.resolve();
3315
+ if (!externalSignal && this.inFlight?.id === id) return this.inFlight.done;
3316
+ if (!skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
3317
+ return Promise.resolve();
3318
+ }
3319
+ const flight = { id, done: Promise.resolve() };
3320
+ flight.done = this.runTransition(
3321
+ target,
3322
+ velocityPxPerMs,
3323
+ false,
3324
+ externalSignal,
3325
+ flight
3326
+ );
3327
+ return flight.done;
3328
+ }
3329
+ async runTransition(target, velocityPxPerMs, fromDrag, externalSignal, flight = null) {
3330
+ const signal = this.beginTransition(target);
3331
+ const transition = this.transition;
3332
+ if (signal.aborted || !transition) return;
3333
+ this.inFlight = flight;
3334
+ const onExternalAbort = () => this.suspend();
3014
3335
  externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3015
- const wasClosed = this.size === 0 || resumingOpen;
3016
- const previousId = this.activeId;
3017
- const previousSize = this.size;
3018
- this.scrollCache.cache(previousId, previousSize, target.size);
3019
- this.activeId = id;
3020
- const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
3021
- const opensToRest = id === "closed" || rawTargetSize === 0;
3022
- if (wasClosed && !opensToRest) {
3023
- this.opening = true;
3024
- if (!resumingOpen) {
3025
- this.promotedForOpen = true;
3026
- sheetStack.promote(this.id);
3027
- }
3028
- }
3029
- if (this.animation.viewTransitionsAvailable) {
3336
+ this.allowOvershoot = fromDrag;
3337
+ if (!fromDrag && this.animation.viewTransitionsAvailable) {
3030
3338
  this.currentViewTransition?.skipTransition?.();
3031
3339
  const vt = document.startViewTransition(() => {
3032
- this.applySize(target.size);
3340
+ if (!signal.aborted) this.applySize(transition.size);
3033
3341
  });
3034
3342
  vt.ready?.catch(() => {
3035
3343
  });
@@ -3040,71 +3348,241 @@ var BottomSheetCore = class {
3040
3348
  }
3041
3349
  if (this.currentViewTransition === vt) this.currentViewTransition = null;
3042
3350
  } else {
3043
- await this.animation.animateTo(target.size, velocityPxPerMs);
3351
+ await this.animation.animateTo(transition.size, velocityPxPerMs);
3044
3352
  }
3045
3353
  externalSignal?.removeEventListener("abort", onExternalAbort);
3046
3354
  if (signal.aborted) return;
3047
- this.opening = false;
3048
- this.completeSnap(id, target.size, previousSize, wasClosed, false);
3355
+ if (this.size !== transition.size) this.applySize(transition.size);
3356
+ this.land(fromDrag);
3357
+ if (this.settleHolds.length > 0) {
3358
+ await Promise.allSettled(this.settleHolds.splice(0));
3359
+ }
3360
+ }
3361
+ beginTransition(target) {
3362
+ const signal = this.newCycle();
3363
+ const cap = this.snaps.getMaxAxisSize();
3364
+ const fromId = this.activeId;
3365
+ const fromSize = this.snaps.findById(fromId)?.size ?? this.size;
3366
+ this.scrollCache.leave(fromId, fromSize, target.size);
3367
+ this.scrollCache.enter(target.id, fromSize, target.size);
3368
+ this.activeId = target.id;
3369
+ this.transition = {
3370
+ size: cap > 0 ? Math.min(target.size, cap) : target.size,
3371
+ cap
3372
+ };
3373
+ if (target.size > 0) {
3374
+ this.enterOpening(target.id);
3375
+ } else {
3376
+ if (this.phase === "closed" && !this.isClosedSnap(target.id)) {
3377
+ sheetStack.promote(this.id);
3378
+ }
3379
+ this.enterClosing();
3380
+ }
3381
+ this.flush();
3382
+ return signal;
3383
+ }
3384
+ enterOpening(id) {
3385
+ if (this.phase === "open" || this.phase === "opening") return;
3386
+ const fresh = this.phase === "closed";
3387
+ this.phase = "opening";
3388
+ this.setRestClosed(false);
3389
+ if (!this.installLifecycle(fresh)) return;
3390
+ this.post("open", { id });
3391
+ this.outbox.push(() => {
3392
+ if (this.phase !== "closed") {
3393
+ notifyLinkedSheets(this.linkedSheets, this);
3394
+ }
3395
+ });
3396
+ }
3397
+ enterClosing() {
3398
+ if (this.phase === "closed" || this.phase === "closing") return;
3399
+ this.phase = "closing";
3400
+ this.post("close", void 0);
3401
+ }
3402
+ settlePhase(heal) {
3403
+ if (this.size > 0) {
3404
+ this.enterOpening(this.activeId);
3405
+ if (this.phase === "opening") {
3406
+ this.phase = "open";
3407
+ this.post("opened", { id: this.activeId });
3408
+ }
3409
+ return;
3410
+ }
3411
+ if (heal && this.phase !== "closing") return;
3412
+ this.enterClosing();
3413
+ if (this.phase === "closing") {
3414
+ this.phase = "closed";
3415
+ this.setRestClosed(true);
3416
+ this.lifecycle.release();
3417
+ sheetStack.update();
3418
+ this.post("closed", void 0);
3419
+ }
3049
3420
  }
3050
- completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
3051
- this.scrollCache.restore(id, previousSize, targetSize);
3421
+ land(haptic) {
3422
+ this.transition = null;
3423
+ this.inFlight = null;
3424
+ this.allowOvershoot = false;
3425
+ this.openPending = this.size === 0 && !this.isClosedSnap(this.activeId);
3052
3426
  this.updateAriaSlider();
3053
- this.emit("snap", {
3054
- id,
3427
+ this.setRestClosed(this.size === 0);
3428
+ this.post("snap", {
3429
+ id: this.activeId,
3055
3430
  size: this.size,
3056
3431
  progress: this.computeProgress(this.size)
3057
3432
  });
3058
- if (withHaptic) this.haptic();
3059
- this.setRestClosed(targetSize === 0);
3060
- if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
3061
- if (targetSize === 0) this.emitCloseSequence();
3433
+ this.settlePhase(false);
3434
+ if (haptic) this.haptic();
3435
+ this.flush();
3062
3436
  }
3063
- setRestClosed(closed) {
3064
- if (closed === this.restClosed) return;
3065
- this.restClosed = closed;
3066
- if (closed) this.element.setAttribute("data-bs-rest", "closed");
3067
- else this.element.removeAttribute("data-bs-rest");
3068
- }
3069
- emitOpenSequence(id) {
3070
- this.emit("open", { id });
3071
- this.handleOpen();
3072
- notifyLinkedSheets(this.linkedSheets, this);
3073
- this.emit("opened", { id });
3074
- }
3075
- emitCloseSequence() {
3076
- this.emit("close", void 0);
3077
- this.handleClose();
3078
- this.emit("closed", void 0);
3437
+ suspend() {
3438
+ this.animation.cancel();
3439
+ this.currentViewTransition?.skipTransition?.();
3440
+ this.newCycle();
3079
3441
  }
3080
- async dragTo(targetSize, velocityOrOpts = 0) {
3081
- if (this.destroyed) return;
3082
- const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
3083
- const velocityPxPerMs = opts.velocity ?? 0;
3084
- const externalSignal = opts.signal;
3085
- if (externalSignal?.aborted) return;
3442
+ landNow() {
3443
+ const transition = this.transition;
3444
+ if (this.destroyed || !transition || this.isDraggingAny()) return;
3445
+ this.animation.cancel();
3446
+ this.currentViewTransition?.skipTransition?.();
3086
3447
  this.newCycle();
3087
- const onExternalAbort = () => {
3088
- this.animation.cancel();
3089
- this.newCycle();
3090
- };
3091
- externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3092
- const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3093
- await this.animation.animateTo(clamped, velocityPxPerMs);
3094
- externalSignal?.removeEventListener("abort", onExternalAbort);
3095
- if (this.destroyed || externalSignal?.aborted) return;
3448
+ this.applySize(transition.size);
3449
+ this.land(false);
3096
3450
  }
3097
- open(id) {
3451
+ reseat() {
3452
+ if (this.destroyed || this.isDraggingAny()) return;
3453
+ const active = this.snaps.findById(this.activeId);
3454
+ if (!active) return;
3455
+ const cap = this.snaps.getMaxAxisSize();
3456
+ const target = cap > 0 ? Math.min(active.size, cap) : active.size;
3457
+ this.updateAriaSlider();
3458
+ const transition = this.transition;
3459
+ if (transition) {
3460
+ if (Math.abs(target - transition.size) < SIZE_WRITE_EPSILON && Math.abs(cap - transition.cap) < SIZE_WRITE_EPSILON) {
3461
+ return;
3462
+ }
3463
+ transition.size = target;
3464
+ transition.cap = cap;
3465
+ this.applySize(this.animation.retarget(target) ? this.size : target);
3466
+ return;
3467
+ }
3468
+ if (this.openPending && target > 0 && this.phase === "closed") {
3469
+ void this.runTransition(active, 0, false);
3470
+ return;
3471
+ }
3472
+ this.applySize(target);
3473
+ this.settlePhase(true);
3474
+ this.flush();
3475
+ }
3476
+ installLifecycle(promote) {
3477
+ if (promote) sheetStack.promote(this.id);
3478
+ if (this.lifecycle.isInstalled) return true;
3479
+ try {
3480
+ this.lifecycle.install();
3481
+ return true;
3482
+ } catch (err) {
3483
+ this.abortOpen();
3484
+ queueMicrotask(() => {
3485
+ throw err;
3486
+ });
3487
+ return false;
3488
+ }
3489
+ }
3490
+ abortOpen() {
3491
+ this.animation.cancel();
3492
+ if (!this.destroyed) this.newCycle();
3493
+ this.phase = "closed";
3494
+ this.openPending = false;
3495
+ this.lifecycle.release();
3496
+ sheetStack.update();
3497
+ this.activeId = this.zeroSnapId() ?? this.activeId;
3498
+ this.applySize(0);
3499
+ this.setRestClosed(true);
3500
+ this.updateAriaSlider();
3501
+ }
3502
+ zeroSnapId() {
3503
+ return this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0);
3504
+ }
3505
+ isClosedSnap(id) {
3506
+ if (id === "closed") return true;
3507
+ return this.snapPointsRaw.find((p) => p.id === id)?.size === 0;
3508
+ }
3509
+ closingBlocked() {
3510
+ return this.disableCloseFlag && this.phase !== "closed";
3511
+ }
3512
+ setKeyboardInset(px) {
3513
+ if (px === this.keyboardInset) return;
3514
+ this.keyboardInset = px;
3515
+ for (const host of this.varHosts()) {
3516
+ host.style.setProperty("--bs-keyboard-inset", `${px}px`);
3517
+ }
3518
+ }
3519
+ varHosts() {
3520
+ const hosts = /* @__PURE__ */ new Set([this.element]);
3521
+ for (const host of [
3522
+ this.anchorHost,
3523
+ this.scrimParent,
3524
+ this.rootEl,
3525
+ this.homeRoot
3526
+ ]) {
3527
+ if (host) hosts.add(host);
3528
+ }
3529
+ return hosts;
3530
+ }
3531
+ dismissBlocked() {
3532
+ return (this.disableCloseFlag || this.persistent) && this.phase !== "closed";
3533
+ }
3534
+ post(event, payload) {
3535
+ this.outbox.push(() => this.bus.emit(event, payload));
3536
+ }
3537
+ flush() {
3538
+ if (this.flushing) return;
3539
+ this.flushing = true;
3540
+ try {
3541
+ for (let next = this.outbox.shift(); next; next = this.outbox.shift()) {
3542
+ next();
3543
+ }
3544
+ } finally {
3545
+ this.flushing = false;
3546
+ }
3547
+ }
3548
+ setRestClosed(closed) {
3549
+ if (closed === this.restClosed) return;
3550
+ this.restClosed = closed;
3551
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3552
+ else this.element.removeAttribute("data-bs-rest");
3553
+ }
3554
+ async dragTo(targetSize, velocityOrOpts = 0) {
3555
+ if (this.destroyed) return;
3556
+ const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
3557
+ const velocityPxPerMs = opts.velocity ?? 0;
3558
+ const externalSignal = opts.signal;
3559
+ if (externalSignal?.aborted) return;
3560
+ this.newCycle();
3561
+ const onExternalAbort = () => this.suspend();
3562
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3563
+ const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3564
+ await this.animation.animateTo(clamped, velocityPxPerMs);
3565
+ externalSignal?.removeEventListener("abort", onExternalAbort);
3566
+ }
3567
+ open(id) {
3098
3568
  if (this.destroyed) return Promise.resolve();
3569
+ if (!id && (this.phase === "open" || this.phase === "opening")) {
3570
+ return Promise.resolve();
3571
+ }
3099
3572
  const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
3100
3573
  return this.snapTo(target);
3101
3574
  }
3102
3575
  close(reason = "programmatic") {
3103
- if (this.destroyed) return Promise.resolve();
3104
- if (this.disableCloseFlag) return Promise.resolve();
3105
- if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
3106
- 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];
3107
- return this.snapTo(closedId ?? this.activeId);
3576
+ if (this.destroyed || this.disableCloseFlag) return Promise.resolve();
3577
+ if (this.persistent && reason !== "programmatic") return Promise.resolve();
3578
+ const target = this.zeroSnapId() ?? this.allowedIdsBySize()[0];
3579
+ if (target === void 0) return Promise.resolve();
3580
+ if (this.phase === "closed" && this.activeId === target) {
3581
+ return Promise.resolve();
3582
+ }
3583
+ const visible = this.phase === "open" || this.phase === "opening";
3584
+ if (visible && this.emitBeforeClose(reason)) return Promise.resolve();
3585
+ return this.snapTo(target);
3108
3586
  }
3109
3587
  canDismiss() {
3110
3588
  return !this.persistent && !this.disableCloseFlag && !this.destroyed;
@@ -3127,10 +3605,12 @@ var BottomSheetCore = class {
3127
3605
  }
3128
3606
  setDragFrom(mode) {
3129
3607
  if (this.destroyed || mode === this.dragFromMode) return;
3608
+ const wasDragging = this.gesture?.isDragging ?? false;
3130
3609
  this.gesture?.forceClearDragState();
3131
3610
  this.detachSheetGesture?.();
3132
3611
  this.dragFromMode = mode;
3133
3612
  this.mountSheetGesture();
3613
+ if (wasDragging) this.restoreAfterDrag();
3134
3614
  }
3135
3615
  getDragFrom() {
3136
3616
  return this.dragFromMode;
@@ -3143,6 +3623,7 @@ var BottomSheetCore = class {
3143
3623
  const nowVertical = next === "bottom" || next === "top";
3144
3624
  this.gesture?.forceClearDragState();
3145
3625
  this.detachSheetGesture?.();
3626
+ this.landNow();
3146
3627
  this.animation.cancel();
3147
3628
  this.element.style.removeProperty(layoutAxis(this.mode));
3148
3629
  this.mode = next;
@@ -3158,9 +3639,12 @@ var BottomSheetCore = class {
3158
3639
  this.detachSliderKeyboard?.();
3159
3640
  this.mountSliderKeyboard();
3160
3641
  this.mountSheetGesture();
3642
+ for (const anchor of this.anchors) anchor.reposition();
3643
+ this.placeScrimStages?.();
3644
+ if (this.stackEffectPrimed) {
3645
+ this.element.style.transformOrigin = nowVertical ? "50% 100%" : "0% 50%";
3646
+ }
3161
3647
  this.recompute();
3162
- const active = this.snaps.findById(this.activeId);
3163
- if (active) this.applySize(active.size);
3164
3648
  this.updateAriaSlider();
3165
3649
  }
3166
3650
  getMode() {
@@ -3202,13 +3686,24 @@ var BottomSheetCore = class {
3202
3686
  if (snap && ids.includes(snap)) {
3203
3687
  void this.snapTo(snap, 0, true);
3204
3688
  } else if (!ids.includes(this.activeId)) {
3205
- const fallback = ids[0];
3206
- if (fallback) void this.snapTo(fallback, 0, true);
3689
+ const fallback = this.fallbackSnap(
3690
+ this.snaps.findById(this.activeId)?.size ?? this.size
3691
+ );
3692
+ if (fallback) void this.snapTo(fallback.id, 0, true);
3207
3693
  }
3208
3694
  }
3695
+ fallbackSnap(from) {
3696
+ return (from > 0 ? this.snaps.nearest(from, true) : null) ?? this.snaps.nearest(from, false);
3697
+ }
3209
3698
  setBackdropRange(range) {
3210
3699
  this.scrim.setBackdropRange(range, () => this.applySize(this.size));
3211
3700
  }
3701
+ setBackdropOpacity(value) {
3702
+ this.scrim.setBackdropOpacity(value, () => this.applySize(this.size));
3703
+ }
3704
+ setBackdropColor(color) {
3705
+ this.scrim.setBackdropColor(color);
3706
+ }
3212
3707
  setScreenRange(range) {
3213
3708
  this.scrim.setScreenRange(range, () => this.applySize(this.size));
3214
3709
  }
@@ -3249,7 +3744,7 @@ var BottomSheetCore = class {
3249
3744
  }
3250
3745
  const handle = attachAnchor(
3251
3746
  {
3252
- mode: this.mode,
3747
+ getMode: () => this.mode,
3253
3748
  host: this.anchorHost,
3254
3749
  getState: () => ({
3255
3750
  activeId: this.activeId,
@@ -3287,9 +3782,9 @@ var BottomSheetCore = class {
3287
3782
  return () => {
3288
3783
  };
3289
3784
  }
3290
- const detachInstalled = installScrimStages(
3785
+ const stages = installScrimStages(
3291
3786
  {
3292
- mode: this.mode,
3787
+ getMode: () => this.mode,
3293
3788
  host,
3294
3789
  getState: () => ({
3295
3790
  activeId: this.activeId,
@@ -3302,10 +3797,14 @@ var BottomSheetCore = class {
3302
3797
  opts
3303
3798
  );
3304
3799
  const detach = () => {
3305
- if (this.detachScrimStages === detach) this.detachScrimStages = null;
3306
- detachInstalled();
3800
+ if (this.detachScrimStages === detach) {
3801
+ this.detachScrimStages = null;
3802
+ this.placeScrimStages = null;
3803
+ }
3804
+ stages.detach();
3307
3805
  };
3308
3806
  this.detachScrimStages = detach;
3807
+ this.placeScrimStages = stages.reposition;
3309
3808
  this.teardowns.add(() => this.detachScrimStages?.());
3310
3809
  return detach;
3311
3810
  }
@@ -3314,32 +3813,29 @@ var BottomSheetCore = class {
3314
3813
  }
3315
3814
  setSnapPoints(points, allowed) {
3316
3815
  if (this.destroyed) return;
3317
- this.animation.cancel();
3318
- this.newCycle();
3816
+ const from = this.snaps.findById(this.activeId)?.size ?? this.size;
3817
+ const restricted = this.snaps.getResolvedSnaps().some((s) => !this.snaps.getAllowedIds().includes(s.id));
3319
3818
  this.snapPointsRaw = points;
3320
3819
  auditVhUsage(points);
3321
3820
  this.auditDuplicateSnapIds(points);
3821
+ this.maxHeight.resolveCap();
3322
3822
  this.snaps.setRaw(points);
3823
+ this.maxHeight.clampTo();
3323
3824
  if (allowed) {
3324
3825
  this.snaps.setAllowedIds(allowed);
3325
3826
  } else {
3326
3827
  const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3327
- const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3828
+ const kept = restricted ? this.snaps.getAllowedIds().filter((id) => present.includes(id)) : [];
3328
3829
  this.snaps.setAllowedIds(kept.length ? kept : present);
3329
3830
  }
3330
3831
  this.scrollCache.clear();
3331
3832
  this.scrim.invalidateOpacityCache();
3332
- const current = this.snaps.findById(this.activeId);
3333
- if (current) {
3334
- this.applySize(current.size);
3335
- } else {
3336
- const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3337
- if (fallback) {
3338
- this.activeId = fallback.id;
3339
- this.applySize(fallback.size);
3340
- }
3833
+ if (this.snaps.findById(this.activeId)) {
3834
+ this.reseat();
3835
+ return;
3341
3836
  }
3342
- this.updateAriaSlider();
3837
+ const fallback = this.fallbackSnap(from) ?? this.snaps.getResolvedSnaps()[0];
3838
+ if (fallback) void this.transitionTo(fallback.id, 0, true);
3343
3839
  }
3344
3840
  recompute() {
3345
3841
  if (this.destroyed) return;
@@ -3347,22 +3843,11 @@ var BottomSheetCore = class {
3347
3843
  this.snaps.recompute();
3348
3844
  this.maxHeight.clampTo();
3349
3845
  this.scrim.invalidateOpacityCache();
3350
- if (this.isDraggingAny()) return;
3351
- this.healActiveSnap();
3352
- }
3353
- healActiveSnap() {
3354
- const current = this.snaps.findById(this.activeId);
3355
- if (!current) return;
3356
- this.size = current.size;
3357
- this.applySize(this.size);
3358
- this.updateAriaSlider();
3359
- if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3360
- this.emitOpenSequence(this.activeId);
3361
- }
3846
+ this.reseat();
3362
3847
  }
3363
3848
  destroy() {
3364
3849
  this.destroyed = true;
3365
- this.opening = false;
3850
+ this.outbox.length = 0;
3366
3851
  this.currentAbort.abort();
3367
3852
  this.currentViewTransition?.skipTransition?.();
3368
3853
  this.currentViewTransition = null;
@@ -3379,11 +3864,19 @@ var BottomSheetCore = class {
3379
3864
  this.backdrop.style.opacity = "";
3380
3865
  this.backdrop.style.pointerEvents = "";
3381
3866
  }
3382
- for (const host of [this.anchorHost, this.scrimParent, this.rootEl]) {
3867
+ for (const host of [
3868
+ this.anchorHost,
3869
+ this.scrimParent,
3870
+ this.rootEl,
3871
+ this.homeRoot
3872
+ ]) {
3383
3873
  if (!host) continue;
3384
3874
  host.style.removeProperty("--bs-size");
3385
3875
  host.style.removeProperty("--bs-progress");
3876
+ host.style.removeProperty("--bs-z-sheet");
3877
+ host.style.removeProperty("--bs-keyboard-inset");
3386
3878
  }
3879
+ this.element.style.removeProperty("--bs-keyboard-inset");
3387
3880
  if (this.screenComponent) {
3388
3881
  this.screenComponent.style.opacity = "";
3389
3882
  this.screenComponent.style.display = "";
@@ -3396,7 +3889,8 @@ var BottomSheetCore = class {
3396
3889
  }
3397
3890
  newCycle() {
3398
3891
  this.allowOvershoot = false;
3399
- this.opening = false;
3892
+ this.transition = null;
3893
+ this.inFlight = null;
3400
3894
  this.currentAbort.abort();
3401
3895
  this.currentAbort = new AbortController();
3402
3896
  return this.currentAbort.signal;
@@ -3419,8 +3913,9 @@ var BottomSheetCore = class {
3419
3913
  if (typeof document === "undefined") return;
3420
3914
  if (!this.lifecycle.focusTrapEnabled) return;
3421
3915
  if (this.element.getAttribute("aria-labelledby")) return;
3422
- const scope = this.handle ?? this.element;
3423
- const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3916
+ const HEADING = "h1,h2,h3,h4,h5,h6";
3917
+ const header = this.element.querySelector(".bs-header");
3918
+ const titled = this.element.querySelector("[data-bs-title]") ?? this.handle.querySelector(HEADING) ?? header?.querySelector(HEADING);
3424
3919
  if (!titled) return;
3425
3920
  if (!titled.id) {
3426
3921
  titled.id = nextInstanceId("bs-title");
@@ -3462,12 +3957,16 @@ var BottomSheetCore = class {
3462
3957
  this.stackZ = z;
3463
3958
  this.element.style.zIndex = String(z);
3464
3959
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3960
+ this.rootEl?.style.setProperty("--bs-z-sheet", String(z));
3961
+ this.homeRoot?.style.setProperty("--bs-z-sheet", String(z));
3465
3962
  for (const anchor of this.anchors) anchor.syncZ(z + 1);
3466
3963
  },
3467
3964
  setIsTop: (isTop) => {
3965
+ if (isTop === this.isTopSheet) return;
3468
3966
  this.isTopSheet = isTop;
3967
+ this.lifecycle.syncTop();
3469
3968
  },
3470
- isOpen: () => this.size > 0 || this.opening,
3969
+ isOpen: () => this.phase !== "closed",
3471
3970
  setDepth: (depth) => this.applyStackDepth(depth)
3472
3971
  }));
3473
3972
  }
@@ -3495,15 +3994,15 @@ var BottomSheetCore = class {
3495
3994
  const r = this.getAllowedRange();
3496
3995
  buf.size = this.size;
3497
3996
  buf.maxAxisSize = this.snaps.getMaxAxisSize();
3498
- buf.range.min = r.min;
3997
+ buf.range.min = this.dismissBlocked() ? Math.max(r.min, this.smallestOpenSize()) : r.min;
3499
3998
  buf.range.max = r.max;
3500
3999
  buf.rubberBandEnabled = this.rubberBandEnabled;
3501
4000
  return buf;
3502
4001
  },
3503
4002
  getDisableDrag: () => this.disableDragFlag,
3504
- cancelAnimation: () => this.animation.cancel(),
4003
+ cancelAnimation: () => this.suspend(),
3505
4004
  applySize: (size) => this.applySize(size),
3506
- animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
4005
+ restoreAfterDrag: () => this.restoreAfterDrag(),
3507
4006
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3508
4007
  emit: (event, payload) => this.emit(event, payload),
3509
4008
  listenerCount: (event) => this.bus.listenerCount(event)
@@ -3627,32 +4126,19 @@ var BottomSheetCore = class {
3627
4126
  element: this.element,
3628
4127
  getMode: () => this.mode,
3629
4128
  isDestroyed: () => this.destroyed,
3630
- isDragging: () => this.isDraggingAny(),
3631
4129
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3632
4130
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3633
- getSize: () => this.size,
3634
4131
  setMaxAxisSize: (size) => {
3635
4132
  this.snaps.setMaxAxisSize(size);
3636
4133
  },
3637
- setSize: (size) => {
3638
- this.size = size;
3639
- },
3640
4134
  recomputeSnaps: () => this.recomputeSnaps(),
3641
- applySize: (size) => this.applySize(size),
3642
- cancelInFlight: () => {
3643
- this.animation.cancel();
3644
- },
3645
- newCycle: () => {
3646
- this.newCycle();
3647
- },
3648
- isAnimating: () => this.animation.isAnimating,
3649
- resyncAfterCancel: () => this.resyncAfterResize()
4135
+ reseat: () => this.reseat(),
4136
+ landNow: () => this.landNow()
3650
4137
  }));
3651
4138
  if (this.lifecycle.closeOnEscape) {
3652
4139
  const onKey = (e) => {
3653
4140
  if (this.destroyed || e.defaultPrevented) return;
3654
- if (!this.canDismiss()) return;
3655
- if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
4141
+ if (e.key === "Escape" && this.isTopSheet) {
3656
4142
  void this.close("escape");
3657
4143
  }
3658
4144
  };
@@ -3670,7 +4156,10 @@ var BottomSheetCore = class {
3670
4156
  handle: this.handle,
3671
4157
  mode: this.mode,
3672
4158
  isDestroyed: () => this.destroyed,
3673
- getAllowedIds: () => this.allowedIdsBySize(),
4159
+ getAllowedIds: () => {
4160
+ const ids = this.allowedIdsBySize();
4161
+ return this.dismissBlocked() ? ids.filter((id) => (this.snaps.findById(id)?.size ?? 0) > 0) : ids;
4162
+ },
3674
4163
  getActiveId: () => this.activeId,
3675
4164
  snapTo: (id) => {
3676
4165
  void this.snapTo(id);
@@ -3683,56 +4172,46 @@ var BottomSheetCore = class {
3683
4172
  });
3684
4173
  }
3685
4174
  settleAfterDrag(delta, velocity, kind = "touch") {
3686
- const signal = this.newCycle();
3687
- const target = this.snaps.findDragSettleTarget({
3688
- delta,
3689
- velocity,
3690
- pointerKind: kind,
3691
- size: this.size,
3692
- activeId: this.activeId,
3693
- flickVelocity: this.flickVelocity,
3694
- dragThreshold: this.dragThreshold
3695
- });
4175
+ const target = this.snaps.findDragSettleTarget(
4176
+ {
4177
+ delta,
4178
+ velocity,
4179
+ pointerKind: kind,
4180
+ size: this.size,
4181
+ activeId: this.activeId,
4182
+ flickVelocity: this.flickVelocity,
4183
+ dragThreshold: this.dragThreshold
4184
+ },
4185
+ this.dismissBlocked()
4186
+ );
3696
4187
  if (!target) {
3697
4188
  this.clearWillChangeIfIdle();
3698
4189
  return;
3699
4190
  }
3700
4191
  const previousId = this.activeId;
3701
4192
  if (target.id === previousId && target.size === this.size) {
4193
+ if (this.phase === "opening" || this.phase === "closing") {
4194
+ this.land(false);
4195
+ }
3702
4196
  this.clearWillChangeIfIdle();
3703
4197
  return;
3704
4198
  }
3705
4199
  if (this.emitBeforeSnap(target, previousId)) {
3706
- const restore2 = this.snaps.findById(previousId);
3707
- if (restore2) {
3708
- void this.animation.animateTo(restore2.size, 0);
3709
- }
4200
+ this.restoreAfterDrag();
3710
4201
  return;
3711
4202
  }
3712
- const previousSize = this.size;
3713
- const previousSnapSize = this.snaps.findById(previousId)?.size;
3714
- this.scrollCache.cache(previousId, previousSize, target.size);
3715
- this.activeId = target.id;
3716
- if (previousSnapSize === 0 && target.size > 0) {
3717
- this.opening = true;
3718
- this.promotedForOpen = true;
3719
- sheetStack.promote(this.id);
3720
- }
3721
- const settleCap = this.snaps.getMaxAxisSize();
3722
- const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3723
- this.allowOvershoot = true;
3724
- void this.animation.animateTo(settleSize, velocity).then(() => {
3725
- if (signal.aborted) return;
3726
- this.allowOvershoot = false;
3727
- this.opening = false;
3728
- this.completeSnap(
3729
- target.id,
3730
- target.size,
3731
- previousSize,
3732
- previousSnapSize === 0,
3733
- true
3734
- );
3735
- });
4203
+ void this.runTransition(target, velocity, true);
4204
+ }
4205
+ restoreAfterDrag() {
4206
+ const active = this.snaps.findById(this.activeId);
4207
+ if (active) void this.runTransition(active, 0, false);
4208
+ }
4209
+ smallestOpenSize() {
4210
+ const allowed = this.snaps.getAllowedIds();
4211
+ for (const snap of this.snaps.getResolvedSnaps()) {
4212
+ if (snap.size > 0 && allowed.includes(snap.id)) return snap.size;
4213
+ }
4214
+ return 0;
3736
4215
  }
3737
4216
  isDraggingAny() {
3738
4217
  return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
@@ -3757,9 +4236,10 @@ var BottomSheetCore = class {
3757
4236
  const offset = cap - clamped;
3758
4237
  const style = this.element.style;
3759
4238
  if (!skipTransform) style.transform = this.transformTemplate(offset);
4239
+ const moving = this.animation.isAnimating || this.isDraggingAny();
3760
4240
  const sizeChanged = this.sizeWriteSentinel.shouldWrite(
3761
4241
  clamped,
3762
- this.animation.isAnimating || this.isDraggingAny() ? SIZE_WRITE_EPSILON : 0
4242
+ moving ? SIZE_WRITE_EPSILON : 0
3763
4243
  );
3764
4244
  if (sizeChanged) {
3765
4245
  style.setProperty("--bs-size", `${clamped}px`);
@@ -3772,11 +4252,13 @@ var BottomSheetCore = class {
3772
4252
  if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3773
4253
  this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3774
4254
  }
4255
+ this.homeRoot?.style.setProperty("--bs-size", `${clamped}px`);
3775
4256
  }
3776
4257
  const progress = this.computeProgress(clamped);
4258
+ const opacityEpsilon = moving ? OPACITY_WRITE_EPSILON : 0;
3777
4259
  const progressChanged = this.progressWriteSentinel.shouldWrite(
3778
4260
  progress,
3779
- OPACITY_WRITE_EPSILON
4261
+ opacityEpsilon
3780
4262
  );
3781
4263
  if (progressChanged) {
3782
4264
  style.setProperty("--bs-progress", String(progress));
@@ -3786,8 +4268,9 @@ var BottomSheetCore = class {
3786
4268
  if (this.rootEl && this.rootEl !== this.anchorHost) {
3787
4269
  this.rootEl.style.setProperty("--bs-progress", String(progress));
3788
4270
  }
4271
+ this.homeRoot?.style.setProperty("--bs-progress", String(progress));
3789
4272
  }
3790
- this.scrim.applyOpacity(progress, progressChanged);
4273
+ this.scrim.applyOpacity(progress, progressChanged, opacityEpsilon);
3791
4274
  if ((progressChanged || sizeChanged) && this.bus.listenerCount("progress") > 0) {
3792
4275
  this.progressPayload.value = progress;
3793
4276
  this.progressPayload.size = clamped;
@@ -3800,238 +4283,322 @@ var BottomSheetCore = class {
3800
4283
  if (reachableMax <= min) return 0;
3801
4284
  return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3802
4285
  }
3803
- handleOpen() {
3804
- if (this.promotedForOpen) this.promotedForOpen = false;
3805
- else sheetStack.promote(this.id);
3806
- try {
3807
- this.lifecycle.install();
3808
- } catch (err) {
3809
- if (!this.destroyed) this.newCycle();
3810
- this.handleClose();
3811
- this.applySize(0);
3812
- this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3813
- this.size = 0;
3814
- queueMicrotask(() => {
3815
- throw err;
3816
- });
3817
- }
3818
- }
3819
- resyncAfterResize() {
3820
- if (this.destroyed) return;
3821
- const target = this.snaps.findById(this.activeId);
3822
- if (!target) return;
3823
- this.updateAriaSlider();
3824
- this.emit("snap", {
3825
- id: target.id,
3826
- size: this.size,
3827
- progress: this.computeProgress(this.size)
3828
- });
3829
- if (target.size > 0 && !this.lifecycle.isInstalled) {
3830
- this.emitOpenSequence(target.id);
3831
- } else if (target.size === 0 && this.lifecycle.isInstalled) {
3832
- this.emitCloseSequence();
3833
- }
3834
- }
3835
- handleClose() {
3836
- this.opening = false;
3837
- this.promotedForOpen = false;
3838
- this.lifecycle.release();
3839
- sheetStack.update();
3840
- }
3841
4286
  };
3842
4287
 
3843
4288
  // src/core/features/history-coordinator.ts
3844
- var entries = [];
3845
- var subscribers = /* @__PURE__ */ new Set();
3846
- var suppress = 0;
3847
- var internalDepth = 0;
3848
- var pendingRebrands = [];
3849
- var listenerInstalled = false;
3850
- var patchInstalled = false;
3851
- var origPushState = null;
3852
- var origReplaceState = null;
3853
- var patchedPushState = null;
3854
- var patchedReplaceState = null;
3855
- var hasWindow = () => typeof window !== "undefined";
3856
- var hasHistory = () => typeof history !== "undefined";
3857
- var brandMarker = (surface) => ({
4289
+ var MARKER = "__bs";
4290
+ var STACK = "__bsStack";
4291
+ var DEPTH = "__bsDepth";
4292
+ var ROUTED = "__bsRouted";
4293
+ var SETTLE_TIMEOUT_MS = 1e3;
4294
+ var state2 = shared(
4295
+ "history",
4296
+ () => ({
4297
+ live: [],
4298
+ stackId: void 0,
4299
+ baseUrl: void 0,
4300
+ position: null,
4301
+ knownLength: 0,
4302
+ href: "",
4303
+ subscribers: /* @__PURE__ */ new Set(),
4304
+ internal: 0,
4305
+ pending: [],
4306
+ deferred: [],
4307
+ waiters: [],
4308
+ timer: null,
4309
+ listening: false,
4310
+ leftovers: false,
4311
+ orphanChecked: false,
4312
+ push: null,
4313
+ replace: null
4314
+ })
4315
+ );
4316
+ var hasHistory = () => typeof window !== "undefined" && typeof history !== "undefined";
4317
+ var plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
4318
+ var depthOf = (value) => {
4319
+ const depth = value?.[DEPTH];
4320
+ return typeof depth === "number" ? depth : null;
4321
+ };
4322
+ var isMarker = (value) => value?.[MARKER] === true;
4323
+ var inStack = (value) => value !== null && state2.stackId !== void 0 && value[STACK] === state2.stackId && depthOf(value) !== null;
4324
+ var foreignKeys = (value) => {
4325
+ const out = {};
4326
+ const source = plain(value);
4327
+ if (!source) return out;
4328
+ for (const key of Object.keys(source)) {
4329
+ if (!key.startsWith(MARKER)) out[key] = source[key];
4330
+ }
4331
+ return out;
4332
+ };
4333
+ var markerFor = (surface, depth) => ({
4334
+ ...foreignKeys(history.state),
3858
4335
  ...surface.markerState(),
3859
- __bs: true
4336
+ [MARKER]: true,
4337
+ [STACK]: state2.stackId,
4338
+ [DEPTH]: depth
3860
4339
  });
4340
+ var urlForDepth = (depth) => {
4341
+ for (let i = depth - 1; i >= 0; i -= 1) {
4342
+ const url = state2.live[i]?.surface.url;
4343
+ if (url !== void 0) return url;
4344
+ }
4345
+ return state2.baseUrl;
4346
+ };
3861
4347
  var runInternal = (fn) => {
3862
- internalDepth += 1;
4348
+ state2.internal += 1;
3863
4349
  try {
3864
4350
  fn();
4351
+ return true;
4352
+ } catch {
4353
+ return false;
3865
4354
  } finally {
3866
- internalDepth -= 1;
4355
+ state2.internal -= 1;
3867
4356
  }
3868
4357
  };
3869
4358
  var notifyRouteChange = () => {
3870
- const snapshot = Array.from(subscribers);
3871
- for (const fn of snapshot) fn();
4359
+ for (const fn of Array.from(state2.subscribers)) {
4360
+ try {
4361
+ fn();
4362
+ } catch (err) {
4363
+ queueMicrotask(() => {
4364
+ throw err;
4365
+ });
4366
+ }
4367
+ }
3872
4368
  };
3873
4369
  var ensureListener = () => {
3874
- if (listenerInstalled || !hasWindow()) return;
4370
+ if (state2.listening || !hasHistory()) return;
3875
4371
  window.addEventListener("popstate", onPopState);
3876
- listenerInstalled = true;
4372
+ state2.listening = true;
3877
4373
  };
3878
4374
  var maybeTeardownListener = () => {
3879
- if (!listenerInstalled) return;
3880
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3881
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
3882
- listenerInstalled = false;
3883
- };
3884
- var installPatch = () => {
3885
- if (patchInstalled || !hasHistory()) return;
3886
- origPushState = history.pushState;
3887
- origReplaceState = history.replaceState;
3888
- patchedPushState = function(...args) {
3889
- origPushState.apply(this, args);
3890
- if (internalDepth === 0) notifyRouteChange();
3891
- };
3892
- patchedReplaceState = function(...args) {
3893
- origReplaceState.apply(this, args);
3894
- if (internalDepth === 0) notifyRouteChange();
3895
- };
3896
- history.pushState = patchedPushState;
3897
- history.replaceState = patchedReplaceState;
3898
- patchInstalled = true;
3899
- };
3900
- var removePatch = () => {
3901
- if (!patchInstalled) return;
3902
- if (history.pushState === patchedPushState && origPushState) {
3903
- history.pushState = origPushState;
3904
- }
3905
- if (history.replaceState === patchedReplaceState && origReplaceState) {
3906
- history.replaceState = origReplaceState;
3907
- }
3908
- origPushState = null;
3909
- origReplaceState = null;
3910
- patchedPushState = null;
3911
- patchedReplaceState = null;
3912
- patchInstalled = false;
3913
- };
3914
- function onPopState() {
3915
- if (suppress > 0) {
3916
- suppress -= 1;
3917
- const rebrand = pendingRebrands.shift();
3918
- if (rebrand) rebrand();
3919
- maybeTeardownListener();
4375
+ if (!state2.listening) return;
4376
+ if (state2.live.length > 0 || state2.subscribers.size > 0 || state2.pending.length > 0 || state2.leftovers) {
3920
4377
  return;
3921
4378
  }
3922
- if (entries.length === 0) {
3923
- notifyRouteChange();
3924
- maybeTeardownListener();
3925
- return;
3926
- }
3927
- let target = null;
3928
- let targetIdx = -1;
3929
- for (let i = entries.length - 1; i >= 0; i -= 1) {
3930
- const entry = entries[i];
3931
- if (!entry.surface.isOpen()) continue;
3932
- if (!target) {
3933
- target = entry;
3934
- targetIdx = i;
3935
- }
3936
- if (entry.surface.isTop()) {
3937
- target = entry;
3938
- targetIdx = i;
3939
- break;
3940
- }
3941
- }
3942
- if (!target) {
3943
- const removed = entries.pop();
3944
- if (removed) removed.live = false;
4379
+ if (hasHistory()) window.removeEventListener("popstate", onPopState);
4380
+ state2.listening = false;
4381
+ };
4382
+ var settleIfIdle = () => {
4383
+ if (state2.pending.length > 0) return;
4384
+ if (state2.timer !== null) {
4385
+ clearTimeout(state2.timer);
4386
+ state2.timer = null;
4387
+ }
4388
+ for (const entry of state2.deferred.splice(0)) pushEntry(entry);
4389
+ for (const resolve of state2.waiters.splice(0)) resolve();
4390
+ };
4391
+ var traverse = (delta, kind) => {
4392
+ state2.pending.push(kind);
4393
+ if (state2.timer !== null) clearTimeout(state2.timer);
4394
+ state2.timer = setTimeout(() => {
4395
+ state2.timer = null;
4396
+ state2.pending.length = 0;
4397
+ settleIfIdle();
3945
4398
  maybeTeardownListener();
3946
- return;
3947
- }
3948
- target.live = false;
3949
- entries.splice(targetIdx, 1);
3950
- const settled = target;
3951
- void Promise.resolve(settled.surface.close()).then(() => {
3952
- if (settled.surface.isOpen()) restore(settled);
4399
+ }, SETTLE_TIMEOUT_MS);
4400
+ ensureListener();
4401
+ const moved = runInternal(() => {
4402
+ if (delta < 0) history.back();
4403
+ else history.forward();
3953
4404
  });
3954
- maybeTeardownListener();
3955
- }
3956
- function restore(entry) {
3957
- if (!hasWindow() || !hasHistory()) return;
3958
- entry.live = true;
4405
+ if (!moved) {
4406
+ state2.pending.pop();
4407
+ settleIfIdle();
4408
+ }
4409
+ };
4410
+ var newStackId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
4411
+ var startStack = () => {
4412
+ state2.stackId = newStackId();
4413
+ const current = history.state;
4414
+ const leftover = plain(current);
4415
+ state2.baseUrl = isMarker(leftover) && leftover?.[ROUTED] !== void 0 ? void 0 : location.href;
4416
+ if (isMarker(leftover)) return;
4417
+ if (current !== null && current !== void 0 && !leftover) return;
3959
4418
  runInternal(() => {
3960
- try {
3961
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3962
- } catch {
3963
- entry.live = false;
3964
- }
4419
+ history.replaceState(
4420
+ { ...foreignKeys(current), [STACK]: state2.stackId, [DEPTH]: 0 },
4421
+ "",
4422
+ location.href
4423
+ );
3965
4424
  });
3966
- if (entry.live) {
3967
- entries.push(entry);
3968
- ensureListener();
3969
- }
4425
+ };
4426
+ function pushEntry(entry) {
4427
+ const depth = state2.live.length + 1;
4428
+ if (state2.live.length === 0) startStack();
4429
+ const written = runInternal(() => {
4430
+ history.pushState(markerFor(entry.surface, depth), "", entry.surface.url);
4431
+ });
4432
+ if (!written) return false;
4433
+ state2.live.push(entry);
4434
+ state2.position = depth;
4435
+ state2.knownLength = history.length;
4436
+ state2.href = location.href;
4437
+ ensureListener();
4438
+ return true;
3970
4439
  }
3971
- function pushBackMarker(surface) {
3972
- const entry = { surface, priorUrl: void 0, live: false };
3973
- if (!hasWindow() || !hasHistory()) {
3974
- return entry;
4440
+ var enlist = (entry) => {
4441
+ if (state2.pending.length > 0) state2.deferred.push(entry);
4442
+ else pushEntry(entry);
4443
+ };
4444
+ var sameBrand = (a, b) => {
4445
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])) {
4446
+ if (key.startsWith(MARKER) && a[key] !== b[key]) return false;
3975
4447
  }
3976
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
4448
+ return true;
4449
+ };
4450
+ var rebrand = (depth) => {
4451
+ const entry = state2.live[depth - 1];
4452
+ if (!entry) return;
4453
+ const url = urlForDepth(depth);
4454
+ const marker = markerFor(entry.surface, depth);
4455
+ const atUrl = url === void 0 || new URL(url, location.href).href === location.href;
4456
+ if (atUrl && sameBrand(plain(history.state) ?? {}, marker)) return;
3977
4457
  runInternal(() => {
4458
+ history.replaceState(marker, "", url);
4459
+ });
4460
+ state2.href = location.href;
4461
+ };
4462
+ var closeAbove = (depth) => {
4463
+ const dropped = state2.live.splice(depth);
4464
+ for (let i = dropped.length - 1; i >= 0; i -= 1) {
4465
+ const entry = dropped[i];
4466
+ let closing = void 0;
3978
4467
  try {
3979
- history.pushState(brandMarker(surface), "", surface.url);
3980
- entry.live = true;
3981
- } catch {
3982
- entry.live = false;
4468
+ closing = entry.surface.close();
4469
+ } catch (err) {
4470
+ queueMicrotask(() => {
4471
+ throw err;
4472
+ });
4473
+ }
4474
+ void Promise.resolve(closing).then(() => {
4475
+ if (!entry.surface.isOpen()) return;
4476
+ const known = (e) => e.surface === entry.surface;
4477
+ if (state2.live.some(known) || state2.deferred.some(known)) return;
4478
+ enlist(entry);
4479
+ });
4480
+ }
4481
+ };
4482
+ function onPopState(event) {
4483
+ const kind = state2.pending.shift();
4484
+ const byUser = kind !== "pop";
4485
+ const landed = plain(event.state);
4486
+ const depth = depthOf(landed);
4487
+ const from = state2.position;
4488
+ const liveBefore = state2.live.length;
4489
+ const grew = history.length > state2.knownLength;
4490
+ state2.knownLength = history.length;
4491
+ const within = inStack(landed);
4492
+ if (within && depth !== null) {
4493
+ state2.position = depth;
4494
+ if (depth > state2.live.length) {
4495
+ const forward = from !== null && from < depth;
4496
+ traverse(forward ? 1 : -1, byUser ? "skip" : "pop");
4497
+ } else if (depth < state2.live.length) {
4498
+ if (byUser) closeAbove(depth);
4499
+ } else {
4500
+ rebrand(depth);
4501
+ }
4502
+ } else if (isMarker(landed)) {
4503
+ state2.position = null;
4504
+ traverse(-1, byUser ? "skip" : "pop");
4505
+ } else {
4506
+ state2.position = null;
4507
+ if (byUser && !grew && state2.live.length > 0) {
4508
+ closeAbove(state2.live.length - 1);
3983
4509
  }
3984
- });
3985
- if (entry.live) {
3986
- entries.push(entry);
3987
- ensureListener();
3988
4510
  }
4511
+ const moved = location.href !== state2.href;
4512
+ state2.href = location.href;
4513
+ if (kind === void 0 && moved && (liveBefore === 0 || !within)) {
4514
+ notifyRouteChange();
4515
+ }
4516
+ settleIfIdle();
4517
+ maybeTeardownListener();
4518
+ }
4519
+ function pushBackMarker(surface) {
4520
+ const entry = { surface };
4521
+ if (hasHistory()) enlist(entry);
3989
4522
  return entry;
3990
4523
  }
3991
4524
  function popBackMarker(handle) {
3992
- if (!hasWindow() || !hasHistory()) return;
4525
+ if (!hasHistory()) return Promise.resolve();
3993
4526
  const entry = handle;
3994
- const idx = entries.indexOf(entry);
3995
- if (idx === -1 || !entry.live) return;
3996
- const last = entries.length - 1;
3997
- entry.live = false;
3998
- entries.splice(idx, 1);
3999
- let rebrand = null;
4000
- if (idx !== last) {
4001
- const rebrandSurface = entries[entries.length - 1].surface;
4002
- const rebrandUrl = entry.priorUrl;
4003
- rebrand = () => {
4004
- runInternal(() => {
4005
- history.replaceState(
4006
- brandMarker(rebrandSurface),
4007
- "",
4008
- rebrandUrl !== void 0 ? rebrandUrl : location.href
4009
- );
4010
- });
4011
- };
4527
+ const waiting = state2.deferred.indexOf(entry);
4528
+ if (waiting !== -1) state2.deferred.splice(waiting, 1);
4529
+ const idx = state2.live.indexOf(entry);
4530
+ if (idx === -1) return historySettled();
4531
+ const top = state2.live.length;
4532
+ state2.live.splice(idx, 1);
4533
+ const current = plain(history.state);
4534
+ const onTop = isMarker(current) && inStack(current) && depthOf(current) === top;
4535
+ if (onTop && state2.pending.length === 0) {
4536
+ traverse(-1, "pop");
4537
+ } else {
4538
+ state2.leftovers = true;
4539
+ ensureListener();
4012
4540
  }
4013
- pendingRebrands.push(rebrand);
4014
- suppress += 1;
4015
- runInternal(() => {
4016
- try {
4017
- history.back();
4018
- } catch {
4019
- suppress = Math.max(0, suppress - 1);
4020
- pendingRebrands.pop();
4021
- }
4022
- });
4023
4541
  maybeTeardownListener();
4542
+ return historySettled();
4024
4543
  }
4544
+ function releaseOrphanMarker() {
4545
+ if (!hasHistory() || state2.orphanChecked) return;
4546
+ state2.orphanChecked = true;
4547
+ if (state2.live.length > 0 || state2.pending.length > 0) return;
4548
+ if (!isMarker(plain(history.state))) return;
4549
+ state2.leftovers = true;
4550
+ traverse(-1, "pop");
4551
+ }
4552
+ function historySettled() {
4553
+ if (state2.pending.length === 0) return Promise.resolve();
4554
+ return new Promise((resolve) => {
4555
+ state2.waiters.push(resolve);
4556
+ });
4557
+ }
4558
+ var wrap = (original) => function(...args) {
4559
+ original.apply(this, args);
4560
+ state2.knownLength = history.length;
4561
+ if (location.href === state2.href) return;
4562
+ state2.href = location.href;
4563
+ if (state2.internal === 0 && state2.subscribers.size > 0) {
4564
+ notifyRouteChange();
4565
+ }
4566
+ };
4567
+ var installPatch = () => {
4568
+ if (!state2.push || history.pushState === state2.push.original) {
4569
+ const original = history.pushState;
4570
+ const wrapper = wrap(original);
4571
+ history.pushState = wrapper;
4572
+ state2.push = { wrapper, original };
4573
+ }
4574
+ if (!state2.replace || history.replaceState === state2.replace.original) {
4575
+ const original = history.replaceState;
4576
+ const wrapper = wrap(original);
4577
+ history.replaceState = wrapper;
4578
+ state2.replace = { wrapper, original };
4579
+ }
4580
+ };
4581
+ var removePatch = () => {
4582
+ if (state2.push && history.pushState === state2.push.wrapper) {
4583
+ history.pushState = state2.push.original;
4584
+ state2.push = null;
4585
+ }
4586
+ if (state2.replace && history.replaceState === state2.replace.wrapper) {
4587
+ history.replaceState = state2.replace.original;
4588
+ state2.replace = null;
4589
+ }
4590
+ };
4025
4591
  function subscribeRouteChange(fn) {
4026
- if (!hasWindow() || !hasHistory()) return () => {
4592
+ if (!hasHistory()) return () => {
4027
4593
  };
4028
- subscribers.add(fn);
4029
- if (subscribers.size === 1) installPatch();
4594
+ state2.subscribers.add(fn);
4595
+ state2.href = location.href;
4596
+ state2.knownLength = history.length;
4597
+ installPatch();
4030
4598
  ensureListener();
4031
4599
  return () => {
4032
- if (!subscribers.has(fn)) return;
4033
- subscribers.delete(fn);
4034
- if (subscribers.size === 0) removePatch();
4600
+ if (!state2.subscribers.delete(fn)) return;
4601
+ if (state2.subscribers.size === 0) removePatch();
4035
4602
  maybeTeardownListener();
4036
4603
  };
4037
4604
  }
@@ -4047,36 +4614,37 @@ function installRoute(deps) {
4047
4614
  };
4048
4615
  }
4049
4616
  let handle = null;
4617
+ const surface = {
4618
+ isOpen: () => !deps.isDestroyed() && deps.isOpen(),
4619
+ close: deps.close,
4620
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4621
+ url: deps.routedTo
4622
+ };
4050
4623
  const onOpen = () => {
4051
4624
  if (handle) return;
4052
- handle = pushBackMarker({
4053
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
4054
- isTop: deps.isTopSheet,
4055
- close: deps.close,
4056
- markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4057
- url: deps.routedTo
4058
- });
4625
+ handle = pushBackMarker(surface);
4059
4626
  };
4060
4627
  const onClose = () => {
4061
4628
  if (!handle) return;
4062
- popBackMarker(handle);
4629
+ deps.hold(popBackMarker(handle));
4063
4630
  handle = null;
4064
4631
  };
4065
4632
  const unsubscribeOpen = deps.on("open", onOpen);
4066
4633
  const unsubscribeClose = deps.on("close", onClose);
4067
- if (deps.getSize() > 0) onOpen();
4634
+ if (deps.isOpen()) onOpen();
4635
+ else releaseOrphanMarker();
4068
4636
  return () => {
4069
4637
  unsubscribeOpen();
4070
4638
  unsubscribeClose();
4071
4639
  if (handle) {
4072
- popBackMarker(handle);
4640
+ void popBackMarker(handle);
4073
4641
  handle = null;
4074
4642
  }
4075
4643
  };
4076
4644
  }
4077
4645
  function installRouteChange(deps) {
4078
4646
  return subscribeRouteChange(() => {
4079
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
4647
+ if (deps.isDestroyed() || !deps.isOpen()) return;
4080
4648
  void deps.close();
4081
4649
  });
4082
4650
  }
@@ -4110,7 +4678,10 @@ function installAutoCollapse(deps) {
4110
4678
  };
4111
4679
  const fire = () => {
4112
4680
  if (deps.isDestroyed()) return;
4113
- if (deps.isDragging()) return;
4681
+ if (deps.isDragging() || deps.isAnimating()) {
4682
+ reset();
4683
+ return;
4684
+ }
4114
4685
  const active = deps.resolveSnap(deps.getActiveId());
4115
4686
  if (!active || active.size === 0) return;
4116
4687
  let target = null;
@@ -4147,6 +4718,9 @@ function installContentDrag(deps) {
4147
4718
 
4148
4719
  // src/core/features/visual-viewport.ts
4149
4720
  var SAFETY_PAD = 8;
4721
+ var KEYBOARD_MIN_INSET = 1;
4722
+ var KEYBOARD_MIN_COVER = 60;
4723
+ var ZOOMED = 1.01;
4150
4724
  function installVisualViewport(deps) {
4151
4725
  if (typeof window === "undefined" || !window.visualViewport) {
4152
4726
  return () => {
@@ -4154,36 +4728,36 @@ function installVisualViewport(deps) {
4154
4728
  }
4155
4729
  const vv = window.visualViewport;
4156
4730
  let rafId = null;
4731
+ const coveredByKeyboard = () => {
4732
+ if ((vv.scale || 1) > ZOOMED) return 0;
4733
+ const covered = window.innerHeight - ((vv.offsetTop || 0) + vv.height);
4734
+ return covered >= KEYBOARD_MIN_COVER ? Math.round(covered) : 0;
4735
+ };
4157
4736
  const onResize = () => {
4158
4737
  if (rafId !== null) return;
4159
4738
  rafId = requestAnimationFrame(() => {
4160
4739
  rafId = null;
4161
4740
  if (deps.isDestroyed()) return;
4741
+ deps.setKeyboardInset(coveredByKeyboard());
4742
+ const before = seatOf(deps);
4162
4743
  deps.recomputeSnaps();
4163
4744
  const isVertical = deps.isVerticalAxis();
4164
4745
  const visible = isVertical ? vv.height : vv.width;
4165
4746
  const windowSize = isVertical ? window.innerHeight : window.innerWidth;
4166
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
4747
+ const pad = windowSize - visible > KEYBOARD_MIN_INSET ? SAFETY_PAD : 0;
4748
+ const clamp = Math.max(0, Math.min(windowSize, visible) - pad);
4167
4749
  if (clamp < deps.getMaxAxisSize()) {
4168
4750
  deps.setMaxAxisSize(clamp);
4169
4751
  }
4170
- const wasAnimating = deps.isAnimating?.() ?? false;
4171
- deps.cancelInFlight();
4172
- deps.newCycle();
4173
- const max = deps.getMaxAxisSize();
4174
- deps.setSize(Math.min(deps.getSize(), max));
4175
- const current = deps.resolveActiveSnap();
4176
- if (current && !deps.isDragging()) {
4177
- deps.applySize(Math.min(current.size, max));
4178
- if (wasAnimating) deps.resyncAfterCancel?.();
4179
- } else {
4180
- deps.applySize(deps.getSize());
4181
- }
4752
+ if (sameSeat(before, seatOf(deps))) return;
4753
+ deps.reseat();
4182
4754
  });
4183
4755
  };
4184
4756
  vv.addEventListener("resize", onResize);
4757
+ vv.addEventListener("scroll", onResize);
4185
4758
  return () => {
4186
4759
  vv.removeEventListener("resize", onResize);
4760
+ vv.removeEventListener("scroll", onResize);
4187
4761
  if (rafId !== null) cancelAnimationFrame(rafId);
4188
4762
  };
4189
4763
  }
@@ -4198,7 +4772,7 @@ function routeFeature() {
4198
4772
  teardowns.add(
4199
4773
  installRouteChange({
4200
4774
  isDestroyed: ctx.isDestroyed,
4201
- getSize: ctx.getSize,
4775
+ isOpen: () => ctx.getPhase() !== "closed",
4202
4776
  close: () => ctx.close()
4203
4777
  })
4204
4778
  );
@@ -4207,10 +4781,10 @@ function routeFeature() {
4207
4781
  installRoute({
4208
4782
  routedTo: ctx.options.routedTo,
4209
4783
  closeOnBack: ctx.options.closeOnBack,
4210
- isTopSheet: ctx.isTopSheet,
4211
- getSize: ctx.getSize,
4784
+ isOpen: () => ctx.getPhase() !== "closed",
4212
4785
  isDestroyed: ctx.isDestroyed,
4213
4786
  close: () => ctx.close("back"),
4787
+ hold: ctx.holdSettle,
4214
4788
  on: ctx.on,
4215
4789
  sheetId: ctx.sheetId
4216
4790
  })
@@ -4236,6 +4810,7 @@ function autoCollapseFeature() {
4236
4810
  ms: ctx.options.autoCollapseAfter,
4237
4811
  isDestroyed: ctx.isDestroyed,
4238
4812
  isDragging: ctx.isDragging,
4813
+ isAnimating: ctx.isAnimating,
4239
4814
  getAllowedIds: ctx.getAllowedIds,
4240
4815
  getActiveId: ctx.getActiveId,
4241
4816
  resolveSnap: ctx.resolveSnap,
@@ -4263,21 +4838,14 @@ function visualViewportFeature() {
4263
4838
  install: (ctx) => {
4264
4839
  if (typeof window === "undefined") return;
4265
4840
  return installVisualViewport({
4266
- element: ctx.element,
4267
4841
  isVerticalAxis: ctx.isVerticalAxis,
4268
4842
  isDestroyed: ctx.isDestroyed,
4269
- isDragging: ctx.isDragging,
4270
4843
  recomputeSnaps: ctx.recomputeSnaps,
4271
4844
  resolveActiveSnap: ctx.resolveActiveSnap,
4272
4845
  getMaxAxisSize: ctx.getMaxAxisSize,
4273
- getSize: ctx.getSize,
4274
4846
  setMaxAxisSize: ctx.setMaxAxisSize,
4275
- setSize: ctx.setSize,
4276
- applySize: ctx.applySize,
4277
- cancelInFlight: ctx.cancelInFlight,
4278
- newCycle: ctx.newCycle,
4279
- isAnimating: ctx.isAnimating,
4280
- resyncAfterCancel: ctx.resyncAfterResize
4847
+ setKeyboardInset: ctx.setKeyboardInset,
4848
+ reseat: ctx.reseat
4281
4849
  });
4282
4850
  }
4283
4851
  };
@@ -4469,13 +5037,13 @@ function useEnginePropSync(engineRef, opts) {
4469
5037
  react.useEffect(() => {
4470
5038
  if (!engineRef.current) return;
4471
5039
  if (opts.backdropColor !== void 0) {
4472
- engineRef.current.setScrimColor(opts.backdropColor);
5040
+ engineRef.current.setBackdropColor(opts.backdropColor);
4473
5041
  }
4474
5042
  }, [opts.backdropColor]);
4475
5043
  react.useEffect(() => {
4476
5044
  if (!engineRef.current) return;
4477
5045
  if (opts.backdropOpacity !== void 0) {
4478
- engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
5046
+ engineRef.current.setBackdropOpacity(opts.backdropOpacity);
4479
5047
  }
4480
5048
  }, [opts.backdropOpacity]);
4481
5049
  react.useEffect(() => {
@@ -4514,39 +5082,18 @@ function useEnginePropSync(engineRef, opts) {
4514
5082
  if (opts.dragFromContent !== void 0) {
4515
5083
  engineRef.current?.setDragFromContent(opts.dragFromContent);
4516
5084
  }
4517
- }, [opts.disableDrag]);
5085
+ }, [opts.dragFromContent]);
4518
5086
  }
4519
-
4520
- // src/core/features/teleport.ts
4521
- var resolveTeleportTarget = (target) => {
4522
- if (!target) return null;
4523
- if (typeof target !== "string") return target;
4524
- if (typeof document === "undefined") return null;
4525
- if (target === "body") return document.body;
4526
- return document.querySelector(target);
4527
- };
4528
- var teleportElements = (els, target) => {
4529
- const records = [];
4530
- for (const el of els) {
4531
- if (!el || el.parentNode === target) continue;
4532
- records.push({ el, parent: el.parentNode, next: el.nextSibling });
4533
- target.appendChild(el);
5087
+ var useElementRef = () => {
5088
+ const holder = react.useRef(null);
5089
+ if (!holder.current) {
5090
+ const assign = ((node) => {
5091
+ assign.current = node;
5092
+ });
5093
+ assign.current = null;
5094
+ holder.current = assign;
4534
5095
  }
4535
- return () => {
4536
- for (let i = records.length - 1; i >= 0; i--) {
4537
- const { el, parent, next } = records[i];
4538
- if (parent && parent.isConnected) {
4539
- const before = next && next.parentNode === parent ? next : null;
4540
- try {
4541
- parent.insertBefore(el, before);
4542
- } catch {
4543
- el.parentNode?.removeChild(el);
4544
- }
4545
- } else {
4546
- el.parentNode?.removeChild(el);
4547
- }
4548
- }
4549
- };
5096
+ return holder.current;
4550
5097
  };
4551
5098
 
4552
5099
  // src/react/useBottomSheet.ts
@@ -4554,25 +5101,32 @@ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutE
4554
5101
  var SSR_STATE = Object.freeze({
4555
5102
  size: 0,
4556
5103
  activeId: "",
5104
+ phase: "closed",
4557
5105
  isDragging: false,
4558
5106
  isAnimating: false,
4559
5107
  progress: 0
4560
5108
  });
4561
5109
  function useBottomSheet(opts) {
4562
- const sheetRef = react.useRef(null);
4563
- const handleRef = react.useRef(null);
4564
- const contentRef = react.useRef(null);
4565
- const backdropRef = react.useRef(null);
4566
- const screenRef = react.useRef(null);
5110
+ const sheetRef = useElementRef();
5111
+ const handleRef = useElementRef();
5112
+ const contentRef = useElementRef();
5113
+ const backdropRef = useElementRef();
5114
+ const screenRef = useElementRef();
4567
5115
  const engineRef = react.useRef(null);
4568
5116
  const subscribersRef = react.useRef(/* @__PURE__ */ new Set());
4569
5117
  const cachedSnapshotRef = react.useRef(SSR_STATE);
4570
5118
  const optsRef = react.useRef(opts);
4571
5119
  optsRef.current = opts;
5120
+ const subscriptionsRef = react.useRef([]);
4572
5121
  const [, setMounted] = react.useState(0);
4573
5122
  useIsomorphicLayoutEffect(() => {
4574
5123
  if (!sheetRef.current) return;
4575
5124
  const current = optsRef.current;
5125
+ const target = resolveTeleportTarget(optsRef.current.teleportTo);
5126
+ const restoreTeleport = target ? teleportElements(
5127
+ [backdropRef.current, screenRef.current, sheetRef.current],
5128
+ target
5129
+ ) : null;
4576
5130
  const engine = new BottomSheetEngine({
4577
5131
  ...current,
4578
5132
  element: sheetRef.current,
@@ -4582,16 +5136,11 @@ function useBottomSheet(opts) {
4582
5136
  scrim: screenRef.current ?? void 0
4583
5137
  });
4584
5138
  engineRef.current = engine;
4585
- const target = resolveTeleportTarget(optsRef.current.teleportTo);
4586
- const restoreTeleport = target ? teleportElements(
4587
- [backdropRef.current, screenRef.current, sheetRef.current],
4588
- target
4589
- ) : null;
4590
- if (optsRef.current.backdropColor !== void 0) {
4591
- engine.setScrimColor(optsRef.current.backdropColor);
4592
- }
4593
- if (optsRef.current.backdropOpacity !== void 0) {
4594
- engine.setBackdropRange([0, optsRef.current.backdropOpacity]);
5139
+ for (const entry of subscriptionsRef.current) {
5140
+ entry.off = engine.on(
5141
+ entry.event,
5142
+ entry.fn
5143
+ );
4595
5144
  }
4596
5145
  cachedSnapshotRef.current = { ...engine.state };
4597
5146
  setMounted((n) => n + 1);
@@ -4649,6 +5198,7 @@ function useBottomSheet(opts) {
4649
5198
  offs.forEach((off) => off());
4650
5199
  restoreTeleport?.();
4651
5200
  engine.destroy();
5201
+ for (const entry of subscriptionsRef.current) entry.off = null;
4652
5202
  engineRef.current = null;
4653
5203
  cachedSnapshotRef.current = SSR_STATE;
4654
5204
  setMounted((n) => n + 1);
@@ -4676,7 +5226,7 @@ function useBottomSheet(opts) {
4676
5226
  []
4677
5227
  );
4678
5228
  const getServerSnapshot = react.useCallback(() => SSR_STATE, []);
4679
- const state = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
5229
+ const state3 = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
4680
5230
  const snapTo = react.useCallback(
4681
5231
  (id) => engineRef.current?.snapTo(id) ?? Promise.resolve(),
4682
5232
  []
@@ -4714,6 +5264,23 @@ function useBottomSheet(opts) {
4714
5264
  []
4715
5265
  );
4716
5266
  const recompute = react.useCallback(() => engineRef.current?.recompute(), []);
5267
+ const on = react.useCallback(
5268
+ (event, fn) => {
5269
+ const entry = {
5270
+ event,
5271
+ fn,
5272
+ off: engineRef.current ? engineRef.current.on(event, fn) : null
5273
+ };
5274
+ subscriptionsRef.current.push(entry);
5275
+ return () => {
5276
+ entry.off?.();
5277
+ subscriptionsRef.current = subscriptionsRef.current.filter(
5278
+ (e) => e !== entry
5279
+ );
5280
+ };
5281
+ },
5282
+ []
5283
+ );
4717
5284
  return react.useMemo(
4718
5285
  () => ({
4719
5286
  sheetRef,
@@ -4721,7 +5288,7 @@ function useBottomSheet(opts) {
4721
5288
  contentRef,
4722
5289
  backdropRef,
4723
5290
  screenRef,
4724
- state,
5291
+ state: state3,
4725
5292
  snapTo,
4726
5293
  open,
4727
5294
  close,
@@ -4733,10 +5300,11 @@ function useBottomSheet(opts) {
4733
5300
  addAnchor,
4734
5301
  setScrimStages,
4735
5302
  recompute,
5303
+ on,
4736
5304
  getEngine
4737
5305
  }),
4738
5306
  [
4739
- state,
5307
+ state3,
4740
5308
  snapTo,
4741
5309
  open,
4742
5310
  close,
@@ -4752,6 +5320,15 @@ function useBottomSheet(opts) {
4752
5320
  ]
4753
5321
  );
4754
5322
  }
5323
+
5324
+ // src/core/primitives/open-state.ts
5325
+ var closedSnapId = (points) => points.find((point) => point.size === 0)?.id;
5326
+ var syncOpen = (engine, open) => {
5327
+ const phase = engine.state.phase;
5328
+ const showing = phase === "open" || phase === "opening";
5329
+ if (open && !showing) void engine.open();
5330
+ else if (!open && showing) void engine.close();
5331
+ };
4755
5332
  var useHasMounted = () => {
4756
5333
  const [mounted, setMounted] = react.useState(false);
4757
5334
  react.useEffect(() => setMounted(true), []);
@@ -4759,6 +5336,13 @@ var useHasMounted = () => {
4759
5336
  };
4760
5337
  var BottomSheet = react.forwardRef(
4761
5338
  function BottomSheet2(props, ref) {
5339
+ const mounted = useHasMounted();
5340
+ if (props.noSSR && !mounted) return null;
5341
+ return /* @__PURE__ */ jsxRuntime.jsx(MountedBottomSheet, { ...props, ref });
5342
+ }
5343
+ );
5344
+ var MountedBottomSheet = react.forwardRef(
5345
+ function MountedBottomSheet2(props, ref) {
4762
5346
  const {
4763
5347
  backdrop = true,
4764
5348
  closeOnBackdrop = true,
@@ -4784,17 +5368,21 @@ var BottomSheet = react.forwardRef(
4784
5368
  onProgress,
4785
5369
  ariaLabel = "Bottom sheet",
4786
5370
  ariaLabelledBy,
4787
- noSSR = false,
5371
+ handleLabel = "Resize sheet",
5372
+ contentLabel = "Sheet content",
5373
+ open: openProp,
5374
+ onOpenChange,
5375
+ noSSR: _noSSR,
4788
5376
  ...engineOpts
4789
5377
  } = props;
4790
- const mounted = useHasMounted();
5378
+ const initial = engineOpts.initial ?? (openProp === false ? closedSnapId(engineOpts.snapPoints) : void 0);
4791
5379
  const {
4792
5380
  sheetRef,
4793
5381
  handleRef,
4794
5382
  contentRef,
4795
5383
  backdropRef,
4796
5384
  screenRef,
4797
- state,
5385
+ state: state3,
4798
5386
  snapTo,
4799
5387
  open,
4800
5388
  close,
@@ -4803,12 +5391,20 @@ var BottomSheet = react.forwardRef(
4803
5391
  isTop,
4804
5392
  depth,
4805
5393
  setAllowed,
5394
+ on,
4806
5395
  getEngine
4807
5396
  } = useBottomSheet({
4808
5397
  ...engineOpts,
5398
+ initial,
4809
5399
  onBeforeClose,
4810
- onOpen,
4811
- onClose,
5400
+ onOpen: (id) => {
5401
+ onOpen?.(id);
5402
+ onOpenChange?.(true);
5403
+ },
5404
+ onClose: () => {
5405
+ onClose?.();
5406
+ onOpenChange?.(false);
5407
+ },
4812
5408
  onOpened,
4813
5409
  onClosed,
4814
5410
  onDragStart,
@@ -4817,14 +5413,18 @@ var BottomSheet = react.forwardRef(
4817
5413
  onProgress
4818
5414
  });
4819
5415
  const ariaModal = engineOpts.focusTrap === true;
5416
+ react.useEffect(() => {
5417
+ const engine = getEngine();
5418
+ if (openProp !== void 0 && engine) syncOpen(engine, openProp);
5419
+ }, [openProp, getEngine]);
4820
5420
  const lastAnnouncedRef = react.useRef("");
4821
5421
  const [announce, setAnnounce] = react.useState("");
4822
5422
  react.useEffect(() => {
4823
- if (state.activeId !== lastAnnouncedRef.current) {
4824
- lastAnnouncedRef.current = state.activeId;
4825
- setAnnounce(state.activeId);
5423
+ if (state3.activeId !== lastAnnouncedRef.current) {
5424
+ lastAnnouncedRef.current = state3.activeId;
5425
+ setAnnounce(state3.activeId);
4826
5426
  }
4827
- }, [state.activeId]);
5427
+ }, [state3.activeId]);
4828
5428
  react.useImperativeHandle(
4829
5429
  ref,
4830
5430
  () => ({
@@ -4845,9 +5445,10 @@ var BottomSheet = react.forwardRef(
4845
5445
  setScrimStages: (opts) => getEngine()?.setScrimStages(opts) ?? (() => {
4846
5446
  }),
4847
5447
  recompute: () => getEngine()?.recompute(),
5448
+ on,
4848
5449
  getEngine,
4849
5450
  get state() {
4850
- return getEngine()?.state ?? state;
5451
+ return getEngine()?.state ?? state3;
4851
5452
  }
4852
5453
  }),
4853
5454
  [
@@ -4860,17 +5461,30 @@ var BottomSheet = react.forwardRef(
4860
5461
  depth,
4861
5462
  setAllowed,
4862
5463
  getEngine,
4863
- state
5464
+ state3
4864
5465
  ]
4865
5466
  );
5467
+ const onChangeRef = react.useRef(onChange);
5468
+ onChangeRef.current = onChange;
4866
5469
  react.useEffect(() => {
4867
- onChange?.(state);
4868
- }, [state, onChange]);
5470
+ onChangeRef.current?.(state3);
5471
+ }, [state3]);
4869
5472
  const mode = engineOpts.mode;
5473
+ const [physicalMode, setPhysicalMode] = react.useState(
5474
+ () => guessPhysicalMode(mode)
5475
+ );
4870
5476
  react.useEffect(() => {
4871
- if (mode === void 0) return;
4872
- getEngine()?.setMode(mode);
5477
+ const engine = getEngine();
5478
+ if (!engine) {
5479
+ setPhysicalMode(guessPhysicalMode(mode));
5480
+ return;
5481
+ }
5482
+ if (mode !== void 0) engine.setMode(mode);
5483
+ setPhysicalMode(engine.getMode());
4873
5484
  }, [getEngine, mode]);
5485
+ const [restAtStart] = react.useState(
5486
+ () => startsClosed(engineOpts.snapPoints, initial, engineOpts.allowed) ? "closed" : void 0
5487
+ );
4874
5488
  const [anchorHosts, setAnchorHosts] = react.useState([]);
4875
5489
  const anchorsKey = anchors ? JSON.stringify(
4876
5490
  anchors.map((a) => [
@@ -4926,7 +5540,6 @@ var BottomSheet = react.forwardRef(
4926
5540
  setStageHosts([]);
4927
5541
  };
4928
5542
  }, [getEngine, stagesKey]);
4929
- if (noSSR && !mounted) return null;
4930
5543
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bs-root", children: [
4931
5544
  backdrop && /* @__PURE__ */ jsxRuntime.jsx(
4932
5545
  "div",
@@ -4952,8 +5565,9 @@ var BottomSheet = react.forwardRef(
4952
5565
  ref: sheetRef,
4953
5566
  className: `bs-sheet ${className}`.trim(),
4954
5567
  style,
4955
- "data-mode": engineOpts.mode ?? "bottom",
4956
- "data-active": state.activeId,
5568
+ "data-mode": physicalMode,
5569
+ "data-bs-rest": restAtStart,
5570
+ "data-active": state3.activeId,
4957
5571
  role: ariaModal ? "dialog" : "region",
4958
5572
  "aria-modal": ariaModal ? "true" : void 0,
4959
5573
  "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
@@ -4966,10 +5580,10 @@ var BottomSheet = react.forwardRef(
4966
5580
  className: "bs-handle",
4967
5581
  role: "slider",
4968
5582
  tabIndex: 0,
4969
- "aria-label": "Resize sheet"
5583
+ "aria-label": handleLabel
4970
5584
  }
4971
5585
  ),
4972
- header != null && header !== false && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bs-header", children: typeof header === "function" ? header(state) : header }),
5586
+ header != null && header !== false && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bs-header", children: typeof header === "function" ? header(state3) : header }),
4973
5587
  /* @__PURE__ */ jsxRuntime.jsx(
4974
5588
  "div",
4975
5589
  {
@@ -4977,11 +5591,11 @@ var BottomSheet = react.forwardRef(
4977
5591
  className: "bs-content",
4978
5592
  tabIndex: 0,
4979
5593
  role: "region",
4980
- "aria-label": "Sheet content",
5594
+ "aria-label": contentLabel,
4981
5595
  children
4982
5596
  }
4983
5597
  ),
4984
- footer != null && footer !== false && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bs-footer", children: typeof footer === "function" ? footer(state) : footer }),
5598
+ footer != null && footer !== false && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bs-footer", children: typeof footer === "function" ? footer(state3) : footer }),
4985
5599
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bs-sr-only", role: "status", "aria-live": "polite", children: announce })
4986
5600
  ]
4987
5601
  }
@@ -4991,7 +5605,7 @@ var BottomSheet = react.forwardRef(
4991
5605
  {
4992
5606
  className: "bs-button-slot",
4993
5607
  "data-side": "left",
4994
- "data-mode": engineOpts.mode ?? "bottom",
5608
+ "data-mode": physicalMode,
4995
5609
  children: leftButton
4996
5610
  }
4997
5611
  ),
@@ -5000,7 +5614,7 @@ var BottomSheet = react.forwardRef(
5000
5614
  {
5001
5615
  className: "bs-button-slot",
5002
5616
  "data-side": "right",
5003
- "data-mode": engineOpts.mode ?? "bottom",
5617
+ "data-mode": physicalMode,
5004
5618
  children: rightButton
5005
5619
  }
5006
5620
  ),
@@ -5222,7 +5836,8 @@ var OverlayEngine = class {
5222
5836
  this.lifecycle = new LifecycleController(
5223
5837
  {
5224
5838
  element: this.element,
5225
- close: () => this.close("escape")
5839
+ close: () => this.close("escape"),
5840
+ isTop: () => this.isTop
5226
5841
  },
5227
5842
  {
5228
5843
  focusTrap: this.focusTrapEnabled,
@@ -5230,12 +5845,7 @@ var OverlayEngine = class {
5230
5845
  closeOnEscape: this.closeOnEscape,
5231
5846
  lockBodyScroll: this.bodyScrollLockEnabled,
5232
5847
  inertSiblings: this.inertSiblingsEnabled,
5233
- shouldApplyInertSiblings: () => {
5234
- const bodyChildren = Array.from(document.body.children);
5235
- return bodyChildren.some(
5236
- (c) => c === this.element || c.contains(this.element)
5237
- );
5238
- },
5848
+ shouldApplyInertSiblings: () => this.element.isConnected,
5239
5849
  returnFocus: opts.returnFocus
5240
5850
  }
5241
5851
  );
@@ -5252,9 +5862,9 @@ var OverlayEngine = class {
5252
5862
  open() {
5253
5863
  if (this.destroyed || this.isOpen_) return Promise.resolve();
5254
5864
  this.maybeMount();
5255
- this.emit("before-open", void 0);
5256
5865
  this.isOpen_ = true;
5257
5866
  this.isAnimating_ = true;
5867
+ this.emit("before-open", void 0);
5258
5868
  sheetStack.promote(this.id);
5259
5869
  this.element.removeAttribute("hidden");
5260
5870
  this.element.setAttribute("data-state", "entering");
@@ -5331,13 +5941,11 @@ var OverlayEngine = class {
5331
5941
  }
5332
5942
  close(reason = "programmatic") {
5333
5943
  if (this.destroyed || !this.isOpen_) return Promise.resolve();
5334
- this.emit("before-close", void 0);
5335
5944
  this.isOpen_ = false;
5336
5945
  this.isAnimating_ = true;
5946
+ this.emit("before-close", void 0);
5337
5947
  sheetStack.update();
5338
- if (reason !== "back" && this.backMarker) {
5339
- popBackMarker(this.backMarker);
5340
- }
5948
+ const historyDone = reason !== "back" && this.backMarker ? popBackMarker(this.backMarker) : Promise.resolve();
5341
5949
  this.backMarker = null;
5342
5950
  this.element.setAttribute("data-state", "exiting");
5343
5951
  const dur = this.effectiveDuration();
@@ -5348,7 +5956,7 @@ var OverlayEngine = class {
5348
5956
  }
5349
5957
  this.setWillChange(true);
5350
5958
  const cycle = ++this.cycleNonce;
5351
- return new Promise((resolve) => {
5959
+ const exited = new Promise((resolve) => {
5352
5960
  requestAnimationFrame(() => {
5353
5961
  if (this.destroyed || cycle !== this.cycleNonce) return resolve();
5354
5962
  this.element.style.transform = closedTransformFor(
@@ -5385,6 +5993,7 @@ var OverlayEngine = class {
5385
5993
  });
5386
5994
  });
5387
5995
  });
5996
+ return Promise.all([exited, historyDone]).then(() => void 0);
5388
5997
  }
5389
5998
  toggle() {
5390
5999
  if (this.destroyed) return Promise.resolve();
@@ -5397,7 +6006,7 @@ var OverlayEngine = class {
5397
6006
  this.setWillChange(false);
5398
6007
  this.releaseInteractiveListeners();
5399
6008
  if (this.backMarker) {
5400
- popBackMarker(this.backMarker);
6009
+ void popBackMarker(this.backMarker);
5401
6010
  this.backMarker = null;
5402
6011
  }
5403
6012
  this.releaseStackEntry?.();
@@ -5691,7 +6300,6 @@ var OverlayEngine = class {
5691
6300
  if (this.closeOnBackEnabled && this.backMarker === null && typeof window !== "undefined") {
5692
6301
  this.backMarker = pushBackMarker({
5693
6302
  isOpen: () => this.isOpen_,
5694
- isTop: () => this.isTop,
5695
6303
  close: () => this.close("back"),
5696
6304
  markerState: () => ({ __bsOverlay: this.id })
5697
6305
  });
@@ -5727,7 +6335,9 @@ var OverlayEngine = class {
5727
6335
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
5728
6336
  },
5729
6337
  setIsTop: (isTop) => {
6338
+ const changed = this.isTop !== isTop;
5730
6339
  this.isTop = isTop;
6340
+ if (changed) this.lifecycle?.syncTop();
5731
6341
  if (this.backdrop) {
5732
6342
  this.backdrop.style.display = isTop ? "" : "none";
5733
6343
  }
@@ -5740,12 +6350,12 @@ var OverlayEngine = class {
5740
6350
  // src/react/useOverlay.ts
5741
6351
  var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
5742
6352
  var useOverlay = (opts) => {
5743
- const panelRef = react.useRef(null);
5744
- const backdropRef = react.useRef(null);
6353
+ const panelRef = useElementRef();
6354
+ const backdropRef = useElementRef();
5745
6355
  const engineRef = react.useRef(null);
5746
6356
  const optsRef = react.useRef(opts);
5747
6357
  optsRef.current = opts;
5748
- const [state, setState] = react.useState({
6358
+ const [state3, setState] = react.useState({
5749
6359
  isOpen: opts.initialOpen ?? false,
5750
6360
  isAnimating: false
5751
6361
  });
@@ -5788,13 +6398,13 @@ var useOverlay = (opts) => {
5788
6398
  () => ({
5789
6399
  panelRef,
5790
6400
  backdropRef,
5791
- state,
6401
+ state: state3,
5792
6402
  open,
5793
6403
  close,
5794
6404
  toggle,
5795
6405
  getEngine
5796
6406
  }),
5797
- [state, open, close, toggle, getEngine]
6407
+ [state3, open, close, toggle, getEngine]
5798
6408
  );
5799
6409
  };
5800
6410
  var Overlay = react.forwardRef(function Overlay2(props, ref) {
@@ -5810,22 +6420,35 @@ var Overlay = react.forwardRef(function Overlay2(props, ref) {
5810
6420
  edge = "bottom",
5811
6421
  ...engineOpts
5812
6422
  } = props;
5813
- const { panelRef, backdropRef, state, open, close, toggle } = useOverlay({
6423
+ const { panelRef, backdropRef, state: state3, open, close, toggle, getEngine } = useOverlay({
5814
6424
  ...engineOpts,
5815
6425
  edge
5816
6426
  });
5817
6427
  react.useEffect(() => {
5818
6428
  if (openProp === void 0) return;
5819
- if (openProp && !state.isOpen) void open();
5820
- else if (!openProp && state.isOpen) void close();
5821
- }, [openProp, state.isOpen]);
6429
+ const live = getEngine()?.state.isOpen ?? false;
6430
+ if (openProp && !live) void open();
6431
+ else if (!openProp && live) void close();
6432
+ }, [openProp]);
6433
+ const onChangeRef = react.useRef(onChange);
6434
+ onChangeRef.current = onChange;
5822
6435
  react.useEffect(() => {
5823
- onChange?.(state);
5824
- }, [state, onChange]);
6436
+ const engine = getEngine();
6437
+ if (!engine) return;
6438
+ const report = () => onChangeRef.current?.(engine.state);
6439
+ const offs = [
6440
+ engine.on("before-open", report),
6441
+ engine.on("open", report),
6442
+ engine.on("before-close", report),
6443
+ engine.on("close", report)
6444
+ ];
6445
+ report();
6446
+ return () => offs.forEach((off) => off());
6447
+ }, [getEngine]);
5825
6448
  react.useImperativeHandle(
5826
6449
  ref,
5827
- () => ({ open, close, toggle, state }),
5828
- [open, close, toggle, state]
6450
+ () => ({ open, close, toggle, state: state3 }),
6451
+ [open, close, toggle, state3]
5829
6452
  );
5830
6453
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ovl-root", children: [
5831
6454
  backdrop && /* @__PURE__ */ jsxRuntime.jsx(