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