@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
@@ -1,14 +1,25 @@
1
1
  // src/solid/index.tsx
2
2
  import {
3
+ children,
3
4
  createSignal,
4
5
  createMemo,
5
6
  createEffect,
6
7
  on,
7
8
  onCleanup,
8
9
  onMount,
9
- Show
10
+ Show,
11
+ untrack
10
12
  } from "solid-js";
11
13
 
14
+ // src/core/primitives/shared.ts
15
+ var STORE_KEY = /* @__PURE__ */ Symbol.for("@surdeddd/bottom-sheet/shared/1");
16
+ function shared(name, init) {
17
+ const host = globalThis;
18
+ const store = host[STORE_KEY] ??= {};
19
+ if (!(name in store)) store[name] = init();
20
+ return store[name];
21
+ }
22
+
12
23
  // src/core/lifecycle/sheet-stack.ts
13
24
  var BASE_Z = 100;
14
25
  var STEP = 10;
@@ -74,7 +85,7 @@ var SheetStack = class {
74
85
  });
75
86
  }
76
87
  };
77
- var sheetStack = new SheetStack();
88
+ var sheetStack = shared("stack", () => new SheetStack());
78
89
 
79
90
  // src/core/primitives/transform.ts
80
91
  function buildTransformTemplate(axis) {
@@ -93,7 +104,7 @@ var offsetFromMatrix = (axis, m) => axis === "bottom" ? m.m42 : axis === "top" ?
93
104
  var layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
94
105
 
95
106
  // src/core/primitives/instance-id.ts
96
- var counters = {};
107
+ var counters = shared("instanceIds", () => ({}));
97
108
  function nextInstanceId(prefix) {
98
109
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
99
110
  return `${prefix}-${crypto.randomUUID()}`;
@@ -121,7 +132,13 @@ function createEventBus() {
121
132
  const set = listeners.get(event);
122
133
  if (!set) return;
123
134
  for (const fn of [...set]) {
124
- fn(payload);
135
+ try {
136
+ fn(payload);
137
+ } catch (err) {
138
+ queueMicrotask(() => {
139
+ throw err;
140
+ });
141
+ }
125
142
  }
126
143
  },
127
144
  listenerCount(event) {
@@ -133,8 +150,23 @@ function createEventBus() {
133
150
  };
134
151
  }
135
152
 
153
+ // src/core/primitives/dev-warn.ts
154
+ function devWarn(...args) {
155
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
156
+ console.warn(...args);
157
+ }
158
+ }
159
+
136
160
  // src/core/primitives/css-length.ts
137
161
  var probe = null;
162
+ var rejected = /* @__PURE__ */ new Set();
163
+ var warnRejected = (value) => {
164
+ if (rejected.has(value)) return;
165
+ rejected.add(value);
166
+ devWarn(
167
+ `[BottomSheet] snap size "${value}" is not a CSS length the browser accepts \u2014 it resolves to 0.`
168
+ );
169
+ };
138
170
  var PROBE_ATTR = "data-bs-probe";
139
171
  var ensureProbe = (axis) => {
140
172
  if (typeof document === "undefined") return null;
@@ -181,19 +213,14 @@ var resolveSnap = (point, mode, measureFit) => {
181
213
  if (!el) return 0;
182
214
  if (axis === "vertical") {
183
215
  el.style.height = point;
216
+ if (el.style.height === "") warnRejected(point);
184
217
  return Math.max(0, el.getBoundingClientRect().height);
185
218
  }
186
219
  el.style.width = point;
220
+ if (el.style.width === "") warnRejected(point);
187
221
  return Math.max(0, el.getBoundingClientRect().width);
188
222
  };
189
223
 
190
- // src/core/primitives/dev-warn.ts
191
- function devWarn(...args) {
192
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
193
- console.warn(...args);
194
- }
195
- }
196
-
197
224
  // src/core/primitives/snap-points.ts
198
225
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
199
226
  const raw = resolveSnap(p.size, mode, measureFit);
@@ -205,6 +232,10 @@ var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
205
232
  }
206
233
  return { id: p.id, size: raw };
207
234
  }).sort((a, b) => a.size - b.size);
235
+ var startsClosed = (points, initial, allowed) => {
236
+ const id = initial ?? allowed?.[0] ?? points[0]?.id;
237
+ return points.find((p) => p.id === id)?.size === 0;
238
+ };
208
239
  var VH_NOT_DVH = /(^|[^a-z])vh\b/i;
209
240
  var auditVhUsage = (points) => {
210
241
  for (const p of points) {
@@ -325,6 +356,7 @@ var SnapResolver = class {
325
356
  setMode(mode) {
326
357
  if (mode === this.mode) return;
327
358
  this.mode = mode;
359
+ this.notified = false;
328
360
  this.recompute();
329
361
  }
330
362
  setAllowedIds(ids) {
@@ -334,14 +366,20 @@ var SnapResolver = class {
334
366
  findById(id) {
335
367
  return findById(id, this.resolved);
336
368
  }
337
- findDragSettleTarget(args) {
369
+ findDragSettleTarget(args, openOnly = false) {
338
370
  return findDragSettleTarget({
339
371
  ...args,
340
372
  resolved: this.resolved,
341
- allowed: this.allowedIds,
373
+ allowed: this.pool(openOnly),
342
374
  maxAxisSize: this.maxAxisSize
343
375
  });
344
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
+ }
345
383
  getAllowedRange() {
346
384
  return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
347
385
  }
@@ -389,19 +427,20 @@ var POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
389
427
  var RANGE_DIVISION_EPSILON = 1e-4;
390
428
 
391
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;
392
436
  function installResizeObserver(deps) {
393
437
  if (typeof window === "undefined") {
394
438
  return () => {
395
439
  };
396
440
  }
397
- const seatFor = () => {
398
- const snap = deps.resolveActiveSnap();
399
- const max = deps.getMaxAxisSize();
400
- return { target: snap ? Math.min(snap.size, max) : null, max };
401
- };
402
441
  const onResize = () => {
403
442
  if (deps.isDestroyed()) return;
404
- const before = seatFor();
443
+ const before = seatOf(deps);
405
444
  deps.recomputeSnaps();
406
445
  const mode = deps.getMode();
407
446
  const isVerticalAxis = mode === "bottom" || mode === "top";
@@ -410,21 +449,8 @@ function installResizeObserver(deps) {
410
449
  if (viewportSize2 > 0 && viewportSize2 < max) {
411
450
  deps.setMaxAxisSize(viewportSize2);
412
451
  }
413
- const after = seatFor();
414
- 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) {
415
- return;
416
- }
417
- const wasAnimating = deps.isAnimating?.() ?? false;
418
- deps.cancelInFlight();
419
- deps.newCycle();
420
- const current = deps.resolveActiveSnap();
421
- const newMax = deps.getMaxAxisSize();
422
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
423
- deps.setSize(targetSize);
424
- if (current && !deps.isDragging()) {
425
- deps.applySize(targetSize);
426
- if (wasAnimating) deps.resyncAfterCancel?.();
427
- }
452
+ if (sameSeat(before, seatOf(deps))) return;
453
+ deps.reseat();
428
454
  };
429
455
  let resizeObserver = null;
430
456
  if (typeof ResizeObserver !== "undefined") {
@@ -436,7 +462,7 @@ function installResizeObserver(deps) {
436
462
  window.addEventListener("orientationchange", onResize);
437
463
  const onVisibility = () => {
438
464
  if (deps.isDestroyed()) return;
439
- if (document.hidden) deps.cancelInFlight();
465
+ if (document.hidden) deps.landNow();
440
466
  };
441
467
  document.addEventListener("visibilitychange", onVisibility);
442
468
  return () => {
@@ -461,6 +487,16 @@ var contentTargets = (scroller) => {
461
487
  }
462
488
  return out;
463
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
+ };
464
500
  function installFitObserver(deps) {
465
501
  if (typeof ResizeObserver === "undefined") return () => {
466
502
  };
@@ -476,7 +512,6 @@ function installFitObserver(deps) {
476
512
  });
477
513
  };
478
514
  const ro = new ResizeObserver(schedule);
479
- ro.observe(deps.handle);
480
515
  const scroller = deps.scrollContainer;
481
516
  const observed = /* @__PURE__ */ new Set();
482
517
  const slots = /* @__PURE__ */ new Set();
@@ -493,7 +528,7 @@ function installFitObserver(deps) {
493
528
  };
494
529
  const resync = () => {
495
530
  trackSlots();
496
- const targets = contentTargets(scroller);
531
+ const targets = [...chromeTargets(deps), ...contentTargets(scroller)];
497
532
  for (const el of observed) {
498
533
  if (!targets.includes(el)) {
499
534
  ro.unobserve(el);
@@ -508,13 +543,12 @@ function installFitObserver(deps) {
508
543
  }
509
544
  schedule();
510
545
  };
546
+ resync();
511
547
  let mo;
512
- if (scroller) {
513
- resync();
514
- if (typeof MutationObserver !== "undefined") {
515
- mo = new MutationObserver(resync);
516
- mo.observe(scroller, { childList: true });
517
- }
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 });
518
552
  }
519
553
  return () => {
520
554
  ro.disconnect();
@@ -526,6 +560,72 @@ function installFitObserver(deps) {
526
560
  };
527
561
  }
528
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
+
529
629
  // src/core/features/max-height-controller.ts
530
630
  function createMaxHeightController(deps) {
531
631
  let maxHeightCap;
@@ -629,8 +729,9 @@ function installSliderKeyboard(deps) {
629
729
  const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
630
730
  const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
631
731
  const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
632
- const sheetExpandsKey = deps.mode === "top" ? stepDown : stepUp;
633
- 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;
634
735
  const onHandleKey = (e) => {
635
736
  if (deps.isDestroyed()) return;
636
737
  const allowed = deps.getAllowedIds();
@@ -704,11 +805,12 @@ var WriteSentinel = class {
704
805
  // src/core/primitives/overlay-position.ts
705
806
  function resolveSheetAnchoredStyle(mode, position, inset) {
706
807
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
808
+ const lifted = `calc(var(--bs-size, 0px) + var(--bs-keyboard-inset, 0px) + ${inset})`;
707
809
  switch (mode) {
708
810
  case "bottom":
709
- if (position === "sheet-top-left") return { bottom: sizeOffset, left: inset };
710
- if (position === "sheet-top-right") return { bottom: sizeOffset, right: inset };
711
- return { bottom: sizeOffset, left: "50%", transform: "translateX(-50%)" };
811
+ if (position === "sheet-top-left") return { bottom: lifted, left: inset };
812
+ if (position === "sheet-top-right") return { bottom: lifted, right: inset };
813
+ return { bottom: lifted, left: "50%", transform: "translateX(-50%)" };
712
814
  case "top":
713
815
  if (position === "sheet-top-left") return { top: sizeOffset, left: inset };
714
816
  if (position === "sheet-top-right") return { top: sizeOffset, right: inset };
@@ -723,6 +825,13 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
723
825
  return { right: sizeOffset, top: "50%", transform: "translateY(-50%)" };
724
826
  }
725
827
  }
828
+ function resetOverlayPosition(ws) {
829
+ ws.top = "";
830
+ ws.bottom = "";
831
+ ws.left = "";
832
+ ws.right = "";
833
+ ws.transform = "";
834
+ }
726
835
  function applyOverlayPosition(ws, mode, position, inset) {
727
836
  switch (position) {
728
837
  case "top-left":
@@ -783,6 +892,8 @@ function applyOverlayPosition(ws, mode, position, inset) {
783
892
  // src/core/controllers/scrim-controller.ts
784
893
  var VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
785
894
  var DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
895
+ var BACKDROP_COLOR_VAR = "--bs-backdrop-color";
896
+ var unit = (value) => Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : 1;
786
897
  var ScrimController = class {
787
898
  screenComponent;
788
899
  backdrop;
@@ -796,11 +907,13 @@ var ScrimController = class {
796
907
  scrimMode;
797
908
  backdropRange;
798
909
  screenRange;
910
+ backdropPeak;
799
911
  scrimEnabled = true;
800
912
  savedScrimRanges = null;
801
913
  scrimTapToCloseEnabled = false;
802
914
  detachScrimTap = null;
803
915
  scrimOverlayTeardown = null;
916
+ placeOverlay = null;
804
917
  backdropOpacitySentinel = new WriteSentinel();
805
918
  screenOpacitySentinel = new WriteSentinel();
806
919
  lastBackdropPointer = "";
@@ -822,6 +935,8 @@ var ScrimController = class {
822
935
  const preset = opts.scrimPreset ? SCRIM_PRESETS[opts.scrimPreset] : null;
823
936
  this.backdropRange = opts.backdropRange ?? preset?.range ?? [0, 1];
824
937
  this.screenRange = opts.screenRange ?? preset?.range ?? [0, 1];
938
+ this.backdropPeak = unit(opts.backdropOpacity ?? 1);
939
+ this.setBackdropColor(opts.backdropColor);
825
940
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
826
941
  const explicitColor = opts.scrimColor ?? preset?.color;
827
942
  const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
@@ -847,24 +962,19 @@ var ScrimController = class {
847
962
  this.setScrimTapToClose(true);
848
963
  }
849
964
  }
850
- applyOpacity(progress, progressChanged) {
851
- if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
965
+ applyOpacity(progress, progressChanged, epsilon = OPACITY_WRITE_EPSILON) {
966
+ if (epsilon > 0 && !progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
852
967
  return;
853
968
  }
854
969
  if (this.backdrop) {
855
970
  const [s, e] = this.backdropRange;
856
971
  const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
857
- const backdropOpacity = Math.min(
858
- Math.max((progress - s) / range, 0),
859
- 1
860
- );
861
- if (this.backdropOpacitySentinel.shouldWrite(
862
- backdropOpacity,
863
- OPACITY_WRITE_EPSILON
864
- )) {
972
+ const along = Math.min(Math.max((progress - s) / range, 0), 1);
973
+ const backdropOpacity = along * this.backdropPeak;
974
+ if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, epsilon)) {
865
975
  this.backdrop.style.opacity = String(backdropOpacity);
866
976
  }
867
- const nextPointer = backdropOpacity > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
977
+ const nextPointer = along > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
868
978
  if (nextPointer !== this.lastBackdropPointer) {
869
979
  this.backdrop.style.pointerEvents = nextPointer;
870
980
  this.lastBackdropPointer = nextPointer;
@@ -879,10 +989,7 @@ var ScrimController = class {
879
989
  (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)
880
990
  )
881
991
  );
882
- if (this.screenOpacitySentinel.shouldWrite(
883
- screenOpacity,
884
- OPACITY_WRITE_EPSILON
885
- )) {
992
+ if (this.screenOpacitySentinel.shouldWrite(screenOpacity, epsilon)) {
886
993
  this.screenComponent.style.opacity = String(screenOpacity);
887
994
  }
888
995
  const nextDisplay = screenOpacity > 0 ? "" : "none";
@@ -898,6 +1005,17 @@ var ScrimController = class {
898
1005
  this.invalidateOpacityCache();
899
1006
  applySize();
900
1007
  }
1008
+ setBackdropColor(color) {
1009
+ if (this.isDestroyed() || !this.backdrop) return;
1010
+ if (color) this.backdrop.style.setProperty(BACKDROP_COLOR_VAR, color);
1011
+ else this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1012
+ }
1013
+ setBackdropOpacity(value, applySize) {
1014
+ if (this.isDestroyed()) return;
1015
+ this.backdropPeak = unit(value);
1016
+ this.invalidateOpacityCache();
1017
+ applySize();
1018
+ }
901
1019
  setScreenRange(range, applySize) {
902
1020
  if (this.isDestroyed()) return;
903
1021
  this.screenRange = range;
@@ -920,6 +1038,8 @@ var ScrimController = class {
920
1038
  if (this.isDestroyed() || mode === this.mode) return;
921
1039
  this.mode = mode;
922
1040
  this.invalidateOpacityCache();
1041
+ if (this.scrimMode === "above-sheet") this.applyAboveSheetInset();
1042
+ this.placeOverlay?.();
923
1043
  }
924
1044
  setScrimMode(mode, applySize) {
925
1045
  if (this.isDestroyed()) return;
@@ -932,7 +1052,7 @@ var ScrimController = class {
932
1052
  if (previous === "above-sheet" && mode !== "above-sheet") {
933
1053
  s.position = "";
934
1054
  s.inset = "";
935
- s.pointerEvents = "";
1055
+ this.syncPointer();
936
1056
  }
937
1057
  if (mode === "above-sheet") {
938
1058
  this.applyAboveSheetInset();
@@ -957,9 +1077,6 @@ var ScrimController = class {
957
1077
  if (enabled === installed) return;
958
1078
  if (enabled) {
959
1079
  const target = this.screenComponent;
960
- if (target.style.pointerEvents === "none") {
961
- target.style.pointerEvents = "auto";
962
- }
963
1080
  const onClick = () => {
964
1081
  if (this.isDestroyed()) return;
965
1082
  if (this.closeFn) {
@@ -982,6 +1099,11 @@ var ScrimController = class {
982
1099
  this.detachScrimTap = null;
983
1100
  this.scrimTapToCloseEnabled = false;
984
1101
  }
1102
+ this.syncPointer();
1103
+ }
1104
+ syncPointer() {
1105
+ if (!this.screenComponent) return;
1106
+ this.screenComponent.style.pointerEvents = this.scrimInteractive || this.detachScrimTap ? "auto" : "none";
985
1107
  }
986
1108
  setScrimEnabled(enabled, applySize) {
987
1109
  if (this.isDestroyed()) return;
@@ -1065,8 +1187,12 @@ var ScrimController = class {
1065
1187
  const wrapper = screen.ownerDocument.createElement("div");
1066
1188
  wrapper.className = "bs-scrim-overlay";
1067
1189
  const ws = wrapper.style;
1068
- ws.position = "absolute";
1069
- applyOverlayPosition(ws, this.mode, position, inset);
1190
+ ws.position = "fixed";
1191
+ const place = () => {
1192
+ resetOverlayPosition(ws);
1193
+ applyOverlayPosition(ws, this.mode, position, inset);
1194
+ };
1195
+ place();
1070
1196
  if (interactive) {
1071
1197
  ws.pointerEvents = "auto";
1072
1198
  }
@@ -1080,8 +1206,10 @@ var ScrimController = class {
1080
1206
  };
1081
1207
  }
1082
1208
  host.appendChild(wrapper);
1209
+ this.placeOverlay = place;
1083
1210
  const teardown = () => {
1084
1211
  if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1212
+ if (this.placeOverlay === place) this.placeOverlay = null;
1085
1213
  if (this.scrimOverlayTeardown === teardown) {
1086
1214
  this.scrimOverlayTeardown = null;
1087
1215
  }
@@ -1105,6 +1233,7 @@ var ScrimController = class {
1105
1233
  if (this.backdrop) {
1106
1234
  this.backdrop.style.opacity = "";
1107
1235
  this.backdrop.style.pointerEvents = "";
1236
+ this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1108
1237
  }
1109
1238
  this.invalidateOpacityCache();
1110
1239
  this.lastBackdropPointer = "";
@@ -1129,13 +1258,13 @@ var ScrimController = class {
1129
1258
  }
1130
1259
  if (opts.interactive !== void 0) {
1131
1260
  this.scrimInteractive = opts.interactive;
1132
- s.pointerEvents = opts.interactive ? "auto" : "none";
1261
+ this.syncPointer();
1133
1262
  }
1134
1263
  }
1135
1264
  applyAboveSheetInset() {
1136
1265
  if (!this.screenComponent) return;
1137
1266
  const insetByMode = {
1138
- bottom: "0 0 var(--bs-size) 0",
1267
+ bottom: "0 0 calc(var(--bs-size) + var(--bs-keyboard-inset, 0px)) 0",
1139
1268
  top: "var(--bs-size) 0 0 0",
1140
1269
  left: "0 0 0 var(--bs-size)",
1141
1270
  right: "0 var(--bs-size) 0 0"
@@ -1143,7 +1272,7 @@ var ScrimController = class {
1143
1272
  const s = this.screenComponent.style;
1144
1273
  s.position = "fixed";
1145
1274
  s.inset = insetByMode[this.mode];
1146
- s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1275
+ this.syncPointer();
1147
1276
  }
1148
1277
  };
1149
1278
 
@@ -1156,6 +1285,7 @@ var easeOutBack = (t) => {
1156
1285
  var easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1157
1286
  var easeLinear = (t) => t;
1158
1287
  var easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
1288
+ var MIN_RETARGET_MS = 120;
1159
1289
  var tween = ({
1160
1290
  from,
1161
1291
  to,
@@ -1164,12 +1294,18 @@ var tween = ({
1164
1294
  onUpdate
1165
1295
  }) => {
1166
1296
  let cancelled = false;
1297
+ let finished = false;
1167
1298
  let rafId = 0;
1168
1299
  let resolveFn = null;
1169
- const start = performance.now();
1300
+ let start = performance.now();
1301
+ let origin = from;
1302
+ let target = to;
1303
+ let span = duration;
1304
+ let last = from;
1170
1305
  const promise = new Promise((resolve) => {
1171
1306
  resolveFn = resolve;
1172
1307
  if (duration <= 0) {
1308
+ finished = true;
1173
1309
  onUpdate(to);
1174
1310
  resolve();
1175
1311
  return;
@@ -1177,12 +1313,13 @@ var tween = ({
1177
1313
  const step = (now) => {
1178
1314
  if (cancelled) return;
1179
1315
  const elapsed = Math.max(0, now - start);
1180
- const t = Math.min(elapsed / duration, 1);
1181
- const value = from + (to - from) * easing(t);
1182
- onUpdate(value);
1316
+ const t = Math.min(elapsed / span, 1);
1317
+ last = origin + (target - origin) * easing(t);
1318
+ onUpdate(last);
1183
1319
  if (t < 1) {
1184
1320
  rafId = requestAnimationFrame(step);
1185
1321
  } else {
1322
+ finished = true;
1186
1323
  resolve();
1187
1324
  }
1188
1325
  };
@@ -1194,6 +1331,15 @@ var tween = ({
1194
1331
  cancelAnimationFrame(rafId);
1195
1332
  resolveFn?.();
1196
1333
  },
1334
+ retarget: (next) => {
1335
+ if (cancelled || finished || next === target) return;
1336
+ const now = performance.now();
1337
+ const remaining = span - Math.max(0, now - start);
1338
+ origin = last;
1339
+ target = next;
1340
+ start = now;
1341
+ span = Math.max(remaining, MIN_RETARGET_MS);
1342
+ },
1197
1343
  promise
1198
1344
  };
1199
1345
  };
@@ -1211,22 +1357,32 @@ var DEFAULT_SPRING = {
1211
1357
  restSpeed: 0.5
1212
1358
  };
1213
1359
  var MAX_DT = 1 / 30;
1360
+ var MAX_DURATION_MS = 3e3;
1214
1361
  var runSpring = ({
1215
1362
  from,
1216
1363
  to,
1217
1364
  velocity = 0,
1218
1365
  config,
1366
+ maxDuration = MAX_DURATION_MS,
1219
1367
  onUpdate
1220
1368
  }) => {
1221
1369
  const cfg = { ...DEFAULT_SPRING, ...config };
1222
1370
  let cancelled = false;
1371
+ let finished = false;
1223
1372
  let rafId = 0;
1224
1373
  let resolveFn = null;
1225
1374
  let x = from;
1226
1375
  let v = velocity;
1376
+ let target = to;
1227
1377
  let lastTime = performance.now();
1378
+ let deadline = lastTime + maxDuration;
1228
1379
  const promise = new Promise((resolve) => {
1229
1380
  resolveFn = resolve;
1381
+ const land = () => {
1382
+ finished = true;
1383
+ onUpdate(target, 0);
1384
+ resolve();
1385
+ };
1230
1386
  const step = (now) => {
1231
1387
  if (cancelled) return;
1232
1388
  const dt = Math.min(Math.max(0, now - lastTime) / 1e3, MAX_DT);
@@ -1235,21 +1391,19 @@ var runSpring = ({
1235
1391
  const subDt = dt / steps;
1236
1392
  let stepsLeft = steps;
1237
1393
  while (stepsLeft-- > 0) {
1238
- const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1394
+ const force = -cfg.stiffness * (x - target) - cfg.damping * v;
1239
1395
  const accel = force / cfg.mass;
1240
1396
  v += accel * subDt;
1241
1397
  x += v * subDt;
1242
1398
  }
1243
- if (!Number.isFinite(x) || !Number.isFinite(v)) {
1244
- onUpdate(to, 0);
1245
- resolve();
1399
+ if (!Number.isFinite(x) || !Number.isFinite(v) || lastTime >= deadline) {
1400
+ land();
1246
1401
  return;
1247
1402
  }
1248
1403
  onUpdate(x, v);
1249
- const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1404
+ const isAtRest = Math.abs(target - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1250
1405
  if (isAtRest) {
1251
- onUpdate(to, 0);
1252
- resolve();
1406
+ land();
1253
1407
  } else {
1254
1408
  rafId = requestAnimationFrame(step);
1255
1409
  }
@@ -1262,6 +1416,11 @@ var runSpring = ({
1262
1416
  cancelAnimationFrame(rafId);
1263
1417
  resolveFn?.();
1264
1418
  },
1419
+ retarget: (next) => {
1420
+ if (cancelled || finished) return;
1421
+ target = next;
1422
+ deadline = performance.now() + maxDuration;
1423
+ },
1265
1424
  promise
1266
1425
  };
1267
1426
  };
@@ -1413,21 +1572,40 @@ var AnimationRunner = class {
1413
1572
  this.applySizeFn(size);
1414
1573
  }
1415
1574
  cancel() {
1416
- this.currentTween?.cancel();
1417
- this.currentSpring?.cancel();
1575
+ const tween2 = this.currentTween;
1576
+ const spring = this.currentSpring;
1577
+ this.currentTween = null;
1578
+ this.currentSpring = null;
1579
+ tween2?.cancel();
1580
+ spring?.cancel();
1418
1581
  this.cancelWaapi();
1419
1582
  }
1583
+ retarget(target) {
1584
+ if (this.currentSpring) {
1585
+ this.currentSpring.retarget(target);
1586
+ return true;
1587
+ }
1588
+ if (this.currentTween) {
1589
+ this.currentTween.retarget(target);
1590
+ return true;
1591
+ }
1592
+ if (this.currentWaapi) {
1593
+ this.currentWaapi.retarget(target);
1594
+ return true;
1595
+ }
1596
+ return false;
1597
+ }
1420
1598
  async animateTo(target, velocityPxPerMs) {
1421
1599
  if (target === this.getSize()) {
1422
- if (!this.isAnimating && !this.isDragging()) {
1600
+ if (this.isAnimating) this.cancel();
1601
+ if (!this.isDragging()) {
1423
1602
  this.applySizeFn(target);
1603
+ this.getRootEl()?.removeAttribute("data-animating");
1424
1604
  this.element.style.willChange = "";
1425
1605
  }
1426
1606
  return;
1427
1607
  }
1428
- this.currentTween?.cancel();
1429
- this.currentSpring?.cancel();
1430
- this.cancelWaapi();
1608
+ this.cancel();
1431
1609
  this.getRootEl()?.setAttribute("data-animating", "true");
1432
1610
  this.element.style.willChange = "transform";
1433
1611
  if (this.reducedMotion) {
@@ -1437,19 +1615,13 @@ var AnimationRunner = class {
1437
1615
  return;
1438
1616
  }
1439
1617
  if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1440
- const samples = this.animationKind === "spring" ? sampleSpringSettle(
1441
- this.getSize(),
1442
- target,
1443
- velocityPxPerMs * VELOCITY_PX_PER_S,
1444
- this.springConfig
1445
- ) : sampleTweenSettle(
1618
+ const samples = this.sampleSettle(
1446
1619
  this.getSize(),
1447
1620
  target,
1448
- this.duration,
1449
- this.easing
1621
+ velocityPxPerMs * VELOCITY_PX_PER_S
1450
1622
  );
1451
1623
  if (samples.values.length >= 2 && samples.durationMs > 0) {
1452
- await this.runWaapiSettle(samples, target);
1624
+ await this.runWaapiSettle({ samples, target });
1453
1625
  if (!this.isAnimating) {
1454
1626
  this.getRootEl()?.removeAttribute("data-animating");
1455
1627
  if (!this.isDragging()) this.element.style.willChange = "";
@@ -1491,7 +1663,10 @@ var AnimationRunner = class {
1491
1663
  }
1492
1664
  }
1493
1665
  }
1494
- async runWaapiSettle(samples, target) {
1666
+ sampleSettle(from, to, velocityPxPerS) {
1667
+ return this.animationKind === "spring" ? sampleSpringSettle(from, to, velocityPxPerS, this.springConfig) : sampleTweenSettle(from, to, this.duration, this.easing);
1668
+ }
1669
+ startWaapiLeg(samples, onRetarget) {
1495
1670
  const applyAux = this.applyAux;
1496
1671
  const getTransformFor = this.getTransformFor;
1497
1672
  const frames = samples.values.map((v) => ({
@@ -1506,16 +1681,20 @@ var AnimationRunner = class {
1506
1681
  let stopped = false;
1507
1682
  let auxRaf = 0;
1508
1683
  const lastIdx = samples.values.length - 1;
1684
+ const cursor = () => Math.min((Number(anim.currentTime) || 0) / samples.stepMs, lastIdx);
1509
1685
  const sizeNow = () => {
1510
- const at = Math.min(
1511
- (Number(anim.currentTime) || 0) / samples.stepMs,
1512
- lastIdx
1513
- );
1686
+ const at = cursor();
1514
1687
  const idx = Math.floor(at);
1515
1688
  const from = samples.values[idx];
1516
1689
  const to = samples.values[Math.min(idx + 1, lastIdx)];
1517
1690
  return from + (to - from) * (at - idx);
1518
1691
  };
1692
+ const velocityNow = () => {
1693
+ const idx = Math.min(Math.floor(cursor()), Math.max(lastIdx - 1, 0));
1694
+ const from = samples.values[idx];
1695
+ const to = samples.values[Math.min(idx + 1, lastIdx)];
1696
+ return (to - from) / samples.stepMs * 1e3;
1697
+ };
1519
1698
  const auxTick = () => {
1520
1699
  if (stopped) return;
1521
1700
  applyAux(sizeNow());
@@ -1528,22 +1707,55 @@ var AnimationRunner = class {
1528
1707
  stopped = true;
1529
1708
  cancelAnimationFrame(auxRaf);
1530
1709
  },
1531
- sizeNow
1710
+ sizeNow,
1711
+ velocityNow,
1712
+ retarget: (to) => onRetarget(entry, to)
1532
1713
  };
1533
- this.currentWaapi = entry;
1534
- try {
1535
- await anim.finished;
1536
- } catch {
1537
- }
1538
- entry.stop();
1539
- const wasCurrent = this.currentWaapi === entry;
1540
- if (wasCurrent) this.currentWaapi = null;
1541
- if (wasCurrent && anim.playState === "finished") {
1542
- this.applySizeFn(target);
1714
+ return entry;
1715
+ }
1716
+ async runWaapiSettle(first) {
1717
+ let leg = first;
1718
+ for (; ; ) {
1719
+ let queued = null;
1720
+ const entry = this.startWaapiLeg(leg.samples, (running, to) => {
1721
+ const size = this.readShownSize?.() ?? running.sizeNow();
1722
+ queued = {
1723
+ samples: this.sampleSettle(size, to, running.velocityNow()),
1724
+ target: to
1725
+ };
1726
+ running.stop();
1727
+ try {
1728
+ running.anim.cancel();
1729
+ } catch {
1730
+ }
1731
+ this.applySizeFn(size);
1732
+ });
1733
+ this.currentWaapi = entry;
1543
1734
  try {
1544
- anim.cancel();
1735
+ await entry.anim.finished;
1545
1736
  } catch {
1546
1737
  }
1738
+ entry.stop();
1739
+ const wasCurrent = this.currentWaapi === entry;
1740
+ const next = queued;
1741
+ if (wasCurrent && next) {
1742
+ if (next.samples.values.length >= 2 && next.samples.durationMs > 0) {
1743
+ leg = next;
1744
+ continue;
1745
+ }
1746
+ this.currentWaapi = null;
1747
+ this.applySizeFn(next.target);
1748
+ return;
1749
+ }
1750
+ if (wasCurrent) this.currentWaapi = null;
1751
+ if (wasCurrent && entry.anim.playState === "finished") {
1752
+ this.applySizeFn(leg.target);
1753
+ try {
1754
+ entry.anim.cancel();
1755
+ } catch {
1756
+ }
1757
+ }
1758
+ return;
1547
1759
  }
1548
1760
  }
1549
1761
  destroy() {
@@ -1598,27 +1810,51 @@ var collectFocusables = (root) => {
1598
1810
  var focusables = (root) => collectFocusables(root).filter(
1599
1811
  (el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
1600
1812
  );
1813
+ var findDeep = (root, selector) => {
1814
+ const direct = root.querySelector(selector);
1815
+ if (direct || typeof HTMLSlotElement === "undefined") return direct;
1816
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1817
+ for (const assigned of slot.assignedElements()) {
1818
+ if (assigned.matches(selector)) return assigned;
1819
+ const inner = findDeep(assigned, selector);
1820
+ if (inner) return inner;
1821
+ }
1822
+ }
1823
+ return null;
1824
+ };
1825
+ var FOCUS_ALLOW_ATTR = "data-bs-focus-allow";
1826
+ var composedParent = (node) => {
1827
+ const slot = node.assignedSlot;
1828
+ if (slot) return slot;
1829
+ const parent = node.parentNode;
1830
+ return parent instanceof ShadowRoot ? parent.host : parent;
1831
+ };
1601
1832
  var containsComposed = (container, node) => {
1602
- let n = node;
1603
- while (n) {
1833
+ for (let n = node; n; n = composedParent(n)) {
1604
1834
  if (n === container) return true;
1605
- const slot = n.assignedSlot;
1606
- if (slot) {
1607
- n = slot;
1608
- continue;
1609
- }
1610
- const parent = n.parentNode;
1611
- n = parent instanceof ShadowRoot ? parent.host : parent;
1612
1835
  }
1613
1836
  return false;
1614
1837
  };
1615
- var trapStack = [];
1838
+ var isAllowedOutside = (node) => {
1839
+ for (let n = node; n; n = composedParent(n)) {
1840
+ if (n instanceof Element && n.hasAttribute(FOCUS_ALLOW_ATTR)) return true;
1841
+ }
1842
+ return false;
1843
+ };
1844
+ var deepActiveElement = () => {
1845
+ let active = document.activeElement;
1846
+ while (active?.shadowRoot?.activeElement) {
1847
+ active = active.shadowRoot.activeElement;
1848
+ }
1849
+ return active;
1850
+ };
1851
+ var trapStack = shared("focusTraps", () => []);
1616
1852
  var installFocusTrap = (container, options = {}) => {
1617
1853
  if (typeof document === "undefined") return () => {
1618
1854
  };
1619
1855
  const token = {};
1620
- trapStack.push(token);
1621
- const isActive = () => trapStack[trapStack.length - 1] === token;
1856
+ if (!options.isActive) trapStack.push(token);
1857
+ const isActive = options.isActive ?? (() => trapStack[trapStack.length - 1] === token);
1622
1858
  const previouslyFocused = document.activeElement;
1623
1859
  const focusInitial = () => {
1624
1860
  const spec = options.initialFocus;
@@ -1627,10 +1863,11 @@ var installFocusTrap = (container, options = {}) => {
1627
1863
  container.focus({ preventScroll: true });
1628
1864
  return;
1629
1865
  }
1630
- const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1866
+ const initial = typeof spec === "string" ? findDeep(container, spec) : spec ?? focusables(container)[0] ?? container;
1631
1867
  initial?.focus({ preventScroll: true });
1632
1868
  };
1633
- focusInitial();
1869
+ if (!containsComposed(container, deepActiveElement())) focusInitial();
1870
+ let backwards = false;
1634
1871
  const handleKey = (e) => {
1635
1872
  if (!isActive()) return;
1636
1873
  if (e.key === "Escape" && options.onEscape) {
@@ -1638,6 +1875,10 @@ var installFocusTrap = (container, options = {}) => {
1638
1875
  return;
1639
1876
  }
1640
1877
  if (e.key !== "Tab") return;
1878
+ backwards = e.shiftKey;
1879
+ setTimeout(() => {
1880
+ backwards = false;
1881
+ }, 0);
1641
1882
  const list = focusables(container);
1642
1883
  if (list.length === 0) {
1643
1884
  e.preventDefault();
@@ -1645,7 +1886,7 @@ var installFocusTrap = (container, options = {}) => {
1645
1886
  }
1646
1887
  const first = list[0];
1647
1888
  const last = list[list.length - 1];
1648
- const active = document.activeElement;
1889
+ const active = deepActiveElement();
1649
1890
  if (e.shiftKey && active === first) {
1650
1891
  e.preventDefault();
1651
1892
  last.focus();
@@ -1656,11 +1897,11 @@ var installFocusTrap = (container, options = {}) => {
1656
1897
  };
1657
1898
  const handleFocusIn = (e) => {
1658
1899
  if (!isActive()) return;
1659
- const target = e.target;
1660
- if (target && !containsComposed(container, target)) {
1661
- const list = focusables(container);
1662
- list[0]?.focus({ preventScroll: true });
1663
- }
1900
+ const target = e.composedPath()[0] ?? e.target;
1901
+ if (!target || containsComposed(container, target)) return;
1902
+ if (isAllowedOutside(target)) return;
1903
+ const list = focusables(container);
1904
+ list[backwards ? list.length - 1 : 0]?.focus({ preventScroll: true });
1664
1905
  };
1665
1906
  document.addEventListener("keydown", handleKey, true);
1666
1907
  document.addEventListener("focusin", handleFocusIn, true);
@@ -1682,21 +1923,25 @@ var installFocusTrap = (container, options = {}) => {
1682
1923
  };
1683
1924
 
1684
1925
  // src/core/lifecycle/scroll-lock.ts
1685
- var lockCount = 0;
1686
- var savedStyles = null;
1926
+ var state = shared("scrollLock", () => ({
1927
+ count: 0,
1928
+ saved: null
1929
+ }));
1687
1930
  var lockBodyScroll = () => {
1688
1931
  if (typeof document === "undefined") return () => {
1689
1932
  };
1690
- if (lockCount === 0) {
1933
+ if (state.count === 0) {
1691
1934
  const body = document.body;
1692
- savedStyles = {
1935
+ const saved = {
1693
1936
  overflow: body.style.overflow,
1694
1937
  position: body.style.position,
1695
1938
  top: body.style.top,
1696
1939
  width: body.style.width,
1697
1940
  paddingRight: body.style.paddingRight,
1941
+ scrollX: window.scrollX,
1698
1942
  scrollY: window.scrollY
1699
1943
  };
1944
+ state.saved = saved;
1700
1945
  const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1701
1946
  if (scrollbarGap > 0) {
1702
1947
  const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
@@ -1704,59 +1949,79 @@ var lockBodyScroll = () => {
1704
1949
  }
1705
1950
  body.style.overflow = "hidden";
1706
1951
  body.style.position = "fixed";
1707
- body.style.top = `-${savedStyles.scrollY}px`;
1952
+ body.style.top = `-${saved.scrollY}px`;
1708
1953
  body.style.width = "100%";
1709
1954
  }
1710
- lockCount++;
1955
+ state.count++;
1711
1956
  let released = false;
1712
1957
  return () => {
1713
1958
  if (released) return;
1714
1959
  released = true;
1715
- lockCount = Math.max(0, lockCount - 1);
1716
- if (lockCount === 0 && savedStyles) {
1960
+ state.count = Math.max(0, state.count - 1);
1961
+ if (state.count === 0 && state.saved) {
1717
1962
  const body = document.body;
1718
- body.style.overflow = savedStyles.overflow;
1719
- body.style.position = savedStyles.position;
1720
- body.style.top = savedStyles.top;
1721
- body.style.width = savedStyles.width;
1722
- body.style.paddingRight = savedStyles.paddingRight;
1723
- window.scrollTo(0, savedStyles.scrollY);
1724
- savedStyles = null;
1963
+ const saved = state.saved;
1964
+ body.style.overflow = saved.overflow;
1965
+ body.style.position = saved.position;
1966
+ body.style.top = saved.top;
1967
+ body.style.width = saved.width;
1968
+ body.style.paddingRight = saved.paddingRight;
1969
+ window.scrollTo({
1970
+ top: saved.scrollY,
1971
+ left: saved.scrollX,
1972
+ behavior: "instant"
1973
+ });
1974
+ state.saved = null;
1725
1975
  }
1726
1976
  };
1727
1977
  };
1728
1978
 
1729
1979
  // src/core/features/inert-siblings.ts
1980
+ var OWN_ROOTS = ".bs-root, .ovl-root";
1981
+ var LAYER_CLASSES = [
1982
+ "bs-backdrop",
1983
+ "bs-screen",
1984
+ "bs-root",
1985
+ "ovl-backdrop",
1986
+ "ovl-root"
1987
+ ];
1988
+ var isLayer = (el) => LAYER_CLASSES.some((name) => el.classList.contains(name));
1730
1989
  function createInertSiblings(rootProvider) {
1731
1990
  const tracked = [];
1991
+ const remove = () => {
1992
+ for (const el of tracked) el.removeAttribute("inert");
1993
+ tracked.length = 0;
1994
+ };
1732
1995
  return {
1733
1996
  apply() {
1734
1997
  if (typeof document === "undefined") return;
1735
- let top = rootProvider();
1736
- while (top.parentElement && top.parentElement !== document.body) {
1737
- top = top.parentElement;
1738
- }
1739
- const parent = top.parentElement ?? document.body;
1740
- for (const child of Array.from(parent.children)) {
1741
- if (child === top) continue;
1742
- if (child.hasAttribute("inert")) continue;
1743
- if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
1998
+ remove();
1999
+ const surface = rootProvider();
2000
+ let node = surface.closest(OWN_ROOTS) ?? surface;
2001
+ while (node && node !== document.body) {
2002
+ const parent = node.parentElement;
2003
+ if (!parent) {
2004
+ const root = node.getRootNode();
2005
+ node = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
1744
2006
  continue;
1745
2007
  }
1746
- child.setAttribute("inert", "");
1747
- tracked.push(child);
2008
+ for (const sibling of Array.from(parent.children)) {
2009
+ if (sibling === node) continue;
2010
+ if (sibling.hasAttribute("inert") || isLayer(sibling)) continue;
2011
+ sibling.setAttribute("inert", "");
2012
+ tracked.push(sibling);
2013
+ }
2014
+ node = parent;
1748
2015
  }
1749
2016
  },
1750
- remove() {
1751
- for (const el of tracked) el.removeAttribute("inert");
1752
- tracked.length = 0;
1753
- }
2017
+ remove
1754
2018
  };
1755
2019
  }
1756
2020
 
1757
2021
  // src/core/controllers/lifecycle-controller.ts
1758
2022
  var LifecycleController = class {
1759
2023
  element;
2024
+ isTop;
1760
2025
  focusTrapEnabled;
1761
2026
  initialFocus;
1762
2027
  closeOnEscape;
@@ -1774,6 +2039,7 @@ var LifecycleController = class {
1774
2039
  }
1775
2040
  constructor(deps, opts) {
1776
2041
  this.element = deps.element;
2042
+ this.isTop = deps.isTop;
1777
2043
  this.focusTrapEnabled = opts.focusTrap ?? false;
1778
2044
  this.initialFocus = opts.initialFocus;
1779
2045
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1791,12 +2057,17 @@ var LifecycleController = class {
1791
2057
  }
1792
2058
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1793
2059
  this.releaseFocusTrap = installFocusTrap(this.element, {
1794
- initialFocus: this.initialFocus
2060
+ initialFocus: this.initialFocus,
2061
+ isActive: this.isTop
1795
2062
  });
1796
2063
  }
1797
- if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
1798
- this.inertSiblings.apply();
1799
- }
2064
+ this.syncTop();
2065
+ }
2066
+ syncTop() {
2067
+ if (!this.installed || !this.inertSiblingsEnabled) return;
2068
+ if (!this.shouldApplyInertSiblings()) return;
2069
+ if (this.isTop?.() ?? true) this.inertSiblings.apply();
2070
+ else this.inertSiblings.remove();
1800
2071
  }
1801
2072
  setReturnFocus(target) {
1802
2073
  if (this.destroyed) return;
@@ -1966,10 +2237,14 @@ var installGestures = (handle, mode, callbacks, options = {}) => {
1966
2237
  if (wasPending) return;
1967
2238
  callbacks.onCancel?.();
1968
2239
  };
2240
+ const onLostCapture = (e) => {
2241
+ if (e.target === handle) onPointerCancel(e);
2242
+ };
1969
2243
  handle.addEventListener("pointerdown", onPointerDown);
1970
2244
  handle.addEventListener("pointermove", onPointerMove);
1971
2245
  handle.addEventListener("pointerup", finishGesture);
1972
2246
  handle.addEventListener("pointercancel", onPointerCancel);
2247
+ handle.addEventListener("lostpointercapture", onLostCapture);
1973
2248
  const managesTouchAction = options.manageTouchAction !== false;
1974
2249
  const prevTouchAction = handle.style.touchAction;
1975
2250
  if (managesTouchAction) {
@@ -1980,6 +2255,7 @@ var installGestures = (handle, mode, callbacks, options = {}) => {
1980
2255
  handle.removeEventListener("pointermove", onPointerMove);
1981
2256
  handle.removeEventListener("pointerup", finishGesture);
1982
2257
  handle.removeEventListener("pointercancel", onPointerCancel);
2258
+ handle.removeEventListener("lostpointercapture", onLostCapture);
1983
2259
  if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1984
2260
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1985
2261
  handle.releasePointerCapture(activePointerId);
@@ -2096,8 +2372,7 @@ var GestureController = class {
2096
2372
  size: this.deps.getDragContext().size,
2097
2373
  velocity: 0
2098
2374
  });
2099
- const restoreTo = this.dragStartSize;
2100
- void this.deps.animateTo(restoreTo, 0);
2375
+ this.deps.restoreAfterDrag();
2101
2376
  }
2102
2377
  }, this.deps.gestureOptions);
2103
2378
  return this.detach;
@@ -2162,6 +2437,11 @@ var readDirection = (el) => {
2162
2437
  }
2163
2438
  return "ltr";
2164
2439
  };
2440
+ var guessPhysicalMode = (mode) => {
2441
+ if (mode === "start") return "left";
2442
+ if (mode === "end") return "right";
2443
+ return mode ?? "bottom";
2444
+ };
2165
2445
  var resolveMode = (mode, el) => {
2166
2446
  if (!isLogicalMode(mode)) return mode;
2167
2447
  const rtl = readDirection(el) === "rtl";
@@ -2215,22 +2495,24 @@ function createScrollCache(deps) {
2215
2495
  return size < max / 2;
2216
2496
  };
2217
2497
  return {
2218
- cache(fromId, prevSize, nextSize) {
2498
+ leave(fromId, prevSize, nextSize) {
2219
2499
  const container = deps.scrollContainer;
2220
2500
  if (!container) return;
2221
- if (isSmallSize(prevSize)) return;
2222
- if (!isSmallSize(nextSize)) return;
2501
+ if (nextSize <= 0) {
2502
+ positions.clear();
2503
+ return;
2504
+ }
2505
+ if (isSmallSize(prevSize) || !isSmallSize(nextSize)) return;
2223
2506
  positions.set(fromId, container.scrollTop);
2224
2507
  },
2225
- restore(toId, prevSize, nextSize) {
2508
+ enter(toId, prevSize, nextSize) {
2226
2509
  const container = deps.scrollContainer;
2227
2510
  if (!container) return;
2228
- if (isSmallSize(nextSize)) return;
2229
- if (!isSmallSize(prevSize)) return;
2511
+ if (isSmallSize(nextSize) || !isSmallSize(prevSize)) return;
2230
2512
  const cached = positions.get(toId);
2231
2513
  if (cached === void 0) return;
2232
- container.scrollTop = cached;
2233
2514
  positions.delete(toId);
2515
+ container.scrollTop = cached;
2234
2516
  },
2235
2517
  clear() {
2236
2518
  positions.clear();
@@ -2344,17 +2626,18 @@ function attachAnchor(deps, opts) {
2344
2626
  const isDock = position === "dock-bottom" || position === "dock-top";
2345
2627
  const ws = wrapper.style;
2346
2628
  ws.position = "fixed";
2347
- if (isDock) {
2629
+ const place = () => {
2630
+ resetOverlayPosition(ws);
2631
+ if (!isDock) {
2632
+ applyOverlayPosition(ws, deps.getMode(), position, opts.inset ?? "16px");
2633
+ return;
2634
+ }
2348
2635
  ws.left = "0";
2349
2636
  ws.right = "0";
2350
- if (position === "dock-bottom") {
2351
- ws.bottom = "0";
2352
- } else {
2353
- ws.top = "0";
2354
- }
2355
- } else {
2356
- applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2357
- }
2637
+ if (position === "dock-bottom") ws.bottom = "0";
2638
+ else ws.top = "0";
2639
+ };
2640
+ place();
2358
2641
  if (opts.fadeRange) {
2359
2642
  const [r0, r1] = opts.fadeRange;
2360
2643
  const span = Math.max(r1 - r0, 1e-4);
@@ -2364,10 +2647,10 @@ function attachAnchor(deps, opts) {
2364
2647
  deps.host.appendChild(wrapper);
2365
2648
  let visible = null;
2366
2649
  let inFlight = null;
2367
- const shouldShow = (state) => {
2368
- if (typeof opts.showOn === "function") return opts.showOn(state);
2369
- if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2370
- return isDock || state.size > 0;
2650
+ const shouldShow = (state3) => {
2651
+ if (typeof opts.showOn === "function") return opts.showOn(state3);
2652
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state3.activeId);
2653
+ return isDock || state3.size > 0;
2371
2654
  };
2372
2655
  const applyVisibility = (next, animate) => {
2373
2656
  if (next === visible) return;
@@ -2414,7 +2697,8 @@ function attachAnchor(deps, opts) {
2414
2697
  },
2415
2698
  syncZ: (z) => {
2416
2699
  wrapper.style.zIndex = String(z);
2417
- }
2700
+ },
2701
+ reposition: place
2418
2702
  };
2419
2703
  }
2420
2704
 
@@ -2430,30 +2714,37 @@ var matchesRange = (def, progress) => {
2430
2714
  };
2431
2715
  function installScrimStages(deps, opts) {
2432
2716
  const doc = deps.host.ownerDocument;
2433
- const entries2 = opts.stages.map((def) => {
2717
+ const entries = opts.stages.map((def) => {
2434
2718
  const wrapper = doc.createElement("div");
2435
2719
  wrapper.className = "bs-scrim-stage";
2436
2720
  const ws = wrapper.style;
2437
- ws.position = "absolute";
2438
- applyOverlayPosition(
2439
- ws,
2440
- deps.mode,
2441
- def.position ?? opts.position ?? "center",
2442
- def.inset ?? opts.inset ?? "16px"
2443
- );
2721
+ ws.position = "fixed";
2444
2722
  ws.visibility = "hidden";
2445
2723
  ws.pointerEvents = "none";
2446
2724
  wrapper.appendChild(def.element);
2447
2725
  deps.host.appendChild(wrapper);
2448
2726
  return { def, wrapper, inFlight: null };
2449
2727
  });
2728
+ const place = () => {
2729
+ for (const entry of entries) {
2730
+ const ws = entry.wrapper.style;
2731
+ resetOverlayPosition(ws);
2732
+ applyOverlayPosition(
2733
+ ws,
2734
+ deps.getMode(),
2735
+ entry.def.position ?? opts.position ?? "center",
2736
+ entry.def.inset ?? opts.inset ?? "16px"
2737
+ );
2738
+ }
2739
+ };
2740
+ place();
2450
2741
  let active = null;
2451
2742
  const resolveActive = () => {
2452
- const state = deps.getState();
2453
- if (state.size === 0) {
2454
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2743
+ const state3 = deps.getState();
2744
+ if (state3.size === 0) {
2745
+ return entries.find((e) => matchesId(e.def, state3.activeId)) ?? null;
2455
2746
  }
2456
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2747
+ return entries.find((e) => matchesId(e.def, state3.activeId)) ?? entries.find((e) => matchesRange(e.def, state3.progress)) ?? null;
2457
2748
  };
2458
2749
  const hide = (entry, animate) => {
2459
2750
  entry.inFlight?.cancel();
@@ -2502,20 +2793,23 @@ function installScrimStages(deps, opts) {
2502
2793
  deps.on("open", () => update(true)),
2503
2794
  deps.on("close", () => update(true))
2504
2795
  ];
2505
- if (entries2.some((e) => e.def.forRange)) {
2796
+ if (entries.some((e) => e.def.forRange)) {
2506
2797
  offs.push(deps.on("progress", () => update(true)));
2507
2798
  }
2508
2799
  update(false);
2509
- return () => {
2510
- offs.forEach((off) => off());
2511
- for (const entry of entries2) {
2512
- entry.inFlight?.cancel();
2513
- entry.inFlight = null;
2514
- if (entry.wrapper.parentNode) {
2515
- entry.wrapper.parentNode.removeChild(entry.wrapper);
2800
+ return {
2801
+ reposition: place,
2802
+ detach: () => {
2803
+ offs.forEach((off) => off());
2804
+ for (const entry of entries) {
2805
+ entry.inFlight?.cancel();
2806
+ entry.inFlight = null;
2807
+ if (entry.wrapper.parentNode) {
2808
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2809
+ }
2516
2810
  }
2811
+ active = null;
2517
2812
  }
2518
- active = null;
2519
2813
  };
2520
2814
  }
2521
2815
 
@@ -2551,11 +2845,13 @@ function resolveEngineOptions(opts, extras) {
2551
2845
  scrimMode: opts.scrimMode,
2552
2846
  scrimColor: opts.scrimColor,
2553
2847
  scrimBlur: opts.scrimBlur,
2554
- scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2848
+ scrimInteractive: opts.scrimInteractive,
2555
2849
  scrimTapToClose: opts.scrimTapToClose,
2556
2850
  scrimPreset: opts.scrimPreset,
2557
2851
  screenRange: opts.screenRange,
2558
- backdropRange: opts.backdropRange
2852
+ backdropRange: opts.backdropRange,
2853
+ backdropOpacity: opts.backdropOpacity,
2854
+ backdropColor: opts.backdropColor
2559
2855
  },
2560
2856
  animation: {
2561
2857
  animation: opts.animation,
@@ -2631,6 +2927,7 @@ var BottomSheetCore = class {
2631
2927
  fitContentToSnap;
2632
2928
  settleAnimation;
2633
2929
  persistent;
2930
+ keyboardInset = 0;
2634
2931
  disableCloseFlag;
2635
2932
  disableDragFlag;
2636
2933
  closeOnRouteChange;
@@ -2645,6 +2942,7 @@ var BottomSheetCore = class {
2645
2942
  detachSheetGesture = null;
2646
2943
  detachSliderKeyboard = null;
2647
2944
  rootEl = null;
2945
+ homeRoot = null;
2648
2946
  destroyed = false;
2649
2947
  restClosed = false;
2650
2948
  currentViewTransition = null;
@@ -2652,8 +2950,13 @@ var BottomSheetCore = class {
2652
2950
  sizeWriteSentinel = new WriteSentinel();
2653
2951
  progressWriteSentinel = new WriteSentinel();
2654
2952
  isTopSheet = true;
2655
- opening = false;
2656
- promotedForOpen = false;
2953
+ phase = "closed";
2954
+ transition = null;
2955
+ inFlight = null;
2956
+ outbox = [];
2957
+ flushing = false;
2958
+ openPending = false;
2959
+ settleHolds = [];
2657
2960
  allowOvershoot = false;
2658
2961
  progressPayload = {
2659
2962
  value: 0,
@@ -2674,6 +2977,7 @@ var BottomSheetCore = class {
2674
2977
  anchorHost = null;
2675
2978
  stackZ = 100;
2676
2979
  detachScrimStages = null;
2980
+ placeScrimStages = null;
2677
2981
  stackEffectEnabled = false;
2678
2982
  stackEffectPrimed = false;
2679
2983
  fitDeps;
@@ -2682,13 +2986,7 @@ var BottomSheetCore = class {
2682
2986
  featureCtx;
2683
2987
  constructor(opts) {
2684
2988
  const resolved = resolveEngineOptions(opts, {
2685
- shouldApplyInertSiblings: () => {
2686
- if (typeof document === "undefined") return false;
2687
- const bodyChildren = Array.from(document.body.children);
2688
- return bodyChildren.some(
2689
- (c) => c === opts.element || c.contains(opts.element)
2690
- );
2691
- }
2989
+ shouldApplyInertSiblings: () => opts.element.isConnected
2692
2990
  });
2693
2991
  this.element = opts.element;
2694
2992
  this.handle = opts.handle ?? opts.element;
@@ -2717,7 +3015,6 @@ var BottomSheetCore = class {
2717
3015
  void this.snapTo(id);
2718
3016
  },
2719
3017
  close: () => {
2720
- if (!this.canDismiss()) return;
2721
3018
  void this.close("backdrop");
2722
3019
  }
2723
3020
  },
@@ -2747,7 +3044,8 @@ var BottomSheetCore = class {
2747
3044
  this.lifecycle = new LifecycleController(
2748
3045
  {
2749
3046
  element: this.element,
2750
- close: () => this.close()
3047
+ close: () => this.close(),
3048
+ isTop: () => this.isTopSheet
2751
3049
  },
2752
3050
  resolved.lifecycle
2753
3051
  );
@@ -2774,6 +3072,7 @@ var BottomSheetCore = class {
2774
3072
  auditVhUsage(this.snapPointsRaw);
2775
3073
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2776
3074
  this.rootEl = this.element.closest(".bs-root");
3075
+ this.homeRoot = homeRootOf(this.rootEl);
2777
3076
  this.maxHeightDeps = {
2778
3077
  element: this.element,
2779
3078
  mode: this.mode,
@@ -2801,7 +3100,9 @@ var BottomSheetCore = class {
2801
3100
  );
2802
3101
  const initial = this.snaps.findById(this.activeId);
2803
3102
  if (initial) this.size = initial.size;
3103
+ this.phase = this.size > 0 ? "open" : "closed";
2804
3104
  this.applySize(this.size);
3105
+ this.restClosed = this.element.getAttribute("data-bs-rest") === "closed";
2805
3106
  this.setRestClosed(this.size === 0);
2806
3107
  this.updateAriaSlider();
2807
3108
  this.scrollCache = createScrollCache({
@@ -2812,12 +3113,11 @@ var BottomSheetCore = class {
2812
3113
  this.attach();
2813
3114
  this.registerInStack();
2814
3115
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2815
- const io = new IntersectionObserver((entries2) => {
2816
- for (const entry of entries2) {
3116
+ const io = new IntersectionObserver((entries) => {
3117
+ for (const entry of entries) {
2817
3118
  if (!entry.isIntersecting) continue;
2818
3119
  if (this.destroyed) return;
2819
- this.snaps.recompute();
2820
- this.healActiveSnap();
3120
+ this.recompute();
2821
3121
  io.disconnect();
2822
3122
  return;
2823
3123
  }
@@ -2827,6 +3127,7 @@ var BottomSheetCore = class {
2827
3127
  }
2828
3128
  this.teardowns.add(
2829
3129
  installFitObserver({
3130
+ element: this.element,
2830
3131
  handle: this.handle,
2831
3132
  scrollContainer: this.scrollContainer,
2832
3133
  hasFitSnap: () => this.snapPointsRaw.some(
@@ -2842,9 +3143,7 @@ var BottomSheetCore = class {
2842
3143
  this.applyAutoAriaLabelledBy();
2843
3144
  this.runFeatureStage("post");
2844
3145
  this.warnOrphanFeatureOptions();
2845
- if (this.size > 0) {
2846
- this.handleOpen();
2847
- }
3146
+ if (this.phase === "open") this.installLifecycle(true);
2848
3147
  }
2849
3148
  buildFeatureContext(autoCollapseAfter) {
2850
3149
  return {
@@ -2872,21 +3171,27 @@ var BottomSheetCore = class {
2872
3171
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2873
3172
  resolveSnap: (id) => this.snaps.findById(id),
2874
3173
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3174
+ getPhase: () => this.phase,
2875
3175
  setSize: (size) => {
2876
3176
  this.size = size;
2877
3177
  },
2878
3178
  setMaxAxisSize: (size) => {
2879
3179
  this.snaps.setMaxAxisSize(size);
2880
3180
  },
3181
+ setKeyboardInset: (px) => this.setKeyboardInset(px),
2881
3182
  applySize: (size) => this.applySize(size),
2882
3183
  recomputeSnaps: () => this.recomputeSnaps(),
3184
+ reseat: () => this.reseat(),
3185
+ holdSettle: (settled) => {
3186
+ this.settleHolds.push(settled);
3187
+ },
2883
3188
  newCycle: () => {
2884
3189
  this.newCycle();
2885
3190
  },
2886
- cancelInFlight: () => {
2887
- this.animation.cancel();
3191
+ cancelInFlight: () => this.suspend(),
3192
+ resyncAfterResize: () => {
3193
+ if (!this.destroyed) this.land(false);
2888
3194
  },
2889
- resyncAfterResize: () => this.resyncAfterResize(),
2890
3195
  snapTo: (id) => {
2891
3196
  void this.snapTo(id);
2892
3197
  },
@@ -2925,8 +3230,9 @@ var BottomSheetCore = class {
2925
3230
  return {
2926
3231
  size: this.size,
2927
3232
  activeId: this.activeId,
3233
+ phase: this.phase,
2928
3234
  isDragging: this.isDraggingAny(),
2929
- isAnimating: this.animation.isAnimating,
3235
+ isAnimating: this.animation.isAnimating || this.currentViewTransition !== null,
2930
3236
  progress: this.computeProgress(this.size)
2931
3237
  };
2932
3238
  }
@@ -2980,49 +3286,53 @@ var BottomSheetCore = class {
2980
3286
  getResolvedSnaps() {
2981
3287
  return this.snaps.getResolvedSnaps();
2982
3288
  }
2983
- async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2984
- if (this.destroyed) return;
3289
+ snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2985
3290
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
2986
- const velocityPxPerMs = opts.velocity ?? 0;
2987
- const externalSignal = opts.signal;
2988
- if (externalSignal?.aborted) return;
3291
+ return this.transitionTo(
3292
+ id,
3293
+ opts.velocity ?? 0,
3294
+ _skipBeforeSnap,
3295
+ opts.signal
3296
+ );
3297
+ }
3298
+ transitionTo(id, velocityPxPerMs = 0, skipBeforeSnap = false, externalSignal) {
3299
+ if (this.destroyed || externalSignal?.aborted) return Promise.resolve();
2989
3300
  const target = this.snaps.findById(id);
2990
3301
  if (!target) {
2991
3302
  devWarn(`[BottomSheet] unknown snap id: ${id}`);
2992
- return;
3303
+ return Promise.resolve();
2993
3304
  }
2994
3305
  if (!this.snaps.getAllowedIds().includes(id)) {
2995
3306
  devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2996
- return;
2997
- }
2998
- if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
2999
- return;
3000
- }
3001
- const resumingOpen = this.opening;
3002
- const signal = this.newCycle();
3003
- const onExternalAbort = () => {
3004
- this.animation.cancel();
3005
- this.newCycle();
3006
- };
3307
+ return Promise.resolve();
3308
+ }
3309
+ if (target.size === 0 && this.closingBlocked()) return Promise.resolve();
3310
+ if (!externalSignal && this.inFlight?.id === id) return this.inFlight.done;
3311
+ if (!skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
3312
+ return Promise.resolve();
3313
+ }
3314
+ const flight = { id, done: Promise.resolve() };
3315
+ flight.done = this.runTransition(
3316
+ target,
3317
+ velocityPxPerMs,
3318
+ false,
3319
+ externalSignal,
3320
+ flight
3321
+ );
3322
+ return flight.done;
3323
+ }
3324
+ async runTransition(target, velocityPxPerMs, fromDrag, externalSignal, flight = null) {
3325
+ const signal = this.beginTransition(target);
3326
+ const transition = this.transition;
3327
+ if (signal.aborted || !transition) return;
3328
+ this.inFlight = flight;
3329
+ const onExternalAbort = () => this.suspend();
3007
3330
  externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3008
- const wasClosed = this.size === 0 || resumingOpen;
3009
- const previousId = this.activeId;
3010
- const previousSize = this.size;
3011
- this.scrollCache.cache(previousId, previousSize, target.size);
3012
- this.activeId = id;
3013
- const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
3014
- const opensToRest = id === "closed" || rawTargetSize === 0;
3015
- if (wasClosed && !opensToRest) {
3016
- this.opening = true;
3017
- if (!resumingOpen) {
3018
- this.promotedForOpen = true;
3019
- sheetStack.promote(this.id);
3020
- }
3021
- }
3022
- if (this.animation.viewTransitionsAvailable) {
3331
+ this.allowOvershoot = fromDrag;
3332
+ if (!fromDrag && this.animation.viewTransitionsAvailable) {
3023
3333
  this.currentViewTransition?.skipTransition?.();
3024
3334
  const vt = document.startViewTransition(() => {
3025
- this.applySize(target.size);
3335
+ if (!signal.aborted) this.applySize(transition.size);
3026
3336
  });
3027
3337
  vt.ready?.catch(() => {
3028
3338
  });
@@ -3033,71 +3343,241 @@ var BottomSheetCore = class {
3033
3343
  }
3034
3344
  if (this.currentViewTransition === vt) this.currentViewTransition = null;
3035
3345
  } else {
3036
- await this.animation.animateTo(target.size, velocityPxPerMs);
3346
+ await this.animation.animateTo(transition.size, velocityPxPerMs);
3037
3347
  }
3038
3348
  externalSignal?.removeEventListener("abort", onExternalAbort);
3039
3349
  if (signal.aborted) return;
3040
- this.opening = false;
3041
- this.completeSnap(id, target.size, previousSize, wasClosed, false);
3350
+ if (this.size !== transition.size) this.applySize(transition.size);
3351
+ this.land(fromDrag);
3352
+ if (this.settleHolds.length > 0) {
3353
+ await Promise.allSettled(this.settleHolds.splice(0));
3354
+ }
3355
+ }
3356
+ beginTransition(target) {
3357
+ const signal = this.newCycle();
3358
+ const cap = this.snaps.getMaxAxisSize();
3359
+ const fromId = this.activeId;
3360
+ const fromSize = this.snaps.findById(fromId)?.size ?? this.size;
3361
+ this.scrollCache.leave(fromId, fromSize, target.size);
3362
+ this.scrollCache.enter(target.id, fromSize, target.size);
3363
+ this.activeId = target.id;
3364
+ this.transition = {
3365
+ size: cap > 0 ? Math.min(target.size, cap) : target.size,
3366
+ cap
3367
+ };
3368
+ if (target.size > 0) {
3369
+ this.enterOpening(target.id);
3370
+ } else {
3371
+ if (this.phase === "closed" && !this.isClosedSnap(target.id)) {
3372
+ sheetStack.promote(this.id);
3373
+ }
3374
+ this.enterClosing();
3375
+ }
3376
+ this.flush();
3377
+ return signal;
3378
+ }
3379
+ enterOpening(id) {
3380
+ if (this.phase === "open" || this.phase === "opening") return;
3381
+ const fresh = this.phase === "closed";
3382
+ this.phase = "opening";
3383
+ this.setRestClosed(false);
3384
+ if (!this.installLifecycle(fresh)) return;
3385
+ this.post("open", { id });
3386
+ this.outbox.push(() => {
3387
+ if (this.phase !== "closed") {
3388
+ notifyLinkedSheets(this.linkedSheets, this);
3389
+ }
3390
+ });
3391
+ }
3392
+ enterClosing() {
3393
+ if (this.phase === "closed" || this.phase === "closing") return;
3394
+ this.phase = "closing";
3395
+ this.post("close", void 0);
3396
+ }
3397
+ settlePhase(heal) {
3398
+ if (this.size > 0) {
3399
+ this.enterOpening(this.activeId);
3400
+ if (this.phase === "opening") {
3401
+ this.phase = "open";
3402
+ this.post("opened", { id: this.activeId });
3403
+ }
3404
+ return;
3405
+ }
3406
+ if (heal && this.phase !== "closing") return;
3407
+ this.enterClosing();
3408
+ if (this.phase === "closing") {
3409
+ this.phase = "closed";
3410
+ this.setRestClosed(true);
3411
+ this.lifecycle.release();
3412
+ sheetStack.update();
3413
+ this.post("closed", void 0);
3414
+ }
3042
3415
  }
3043
- completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
3044
- this.scrollCache.restore(id, previousSize, targetSize);
3416
+ land(haptic) {
3417
+ this.transition = null;
3418
+ this.inFlight = null;
3419
+ this.allowOvershoot = false;
3420
+ this.openPending = this.size === 0 && !this.isClosedSnap(this.activeId);
3045
3421
  this.updateAriaSlider();
3046
- this.emit("snap", {
3047
- id,
3422
+ this.setRestClosed(this.size === 0);
3423
+ this.post("snap", {
3424
+ id: this.activeId,
3048
3425
  size: this.size,
3049
3426
  progress: this.computeProgress(this.size)
3050
3427
  });
3051
- if (withHaptic) this.haptic();
3052
- this.setRestClosed(targetSize === 0);
3053
- if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
3054
- if (targetSize === 0) this.emitCloseSequence();
3055
- }
3056
- setRestClosed(closed) {
3057
- if (closed === this.restClosed) return;
3058
- this.restClosed = closed;
3059
- if (closed) this.element.setAttribute("data-bs-rest", "closed");
3060
- else this.element.removeAttribute("data-bs-rest");
3061
- }
3062
- emitOpenSequence(id) {
3063
- this.emit("open", { id });
3064
- this.handleOpen();
3065
- notifyLinkedSheets(this.linkedSheets, this);
3066
- this.emit("opened", { id });
3428
+ this.settlePhase(false);
3429
+ if (haptic) this.haptic();
3430
+ this.flush();
3067
3431
  }
3068
- emitCloseSequence() {
3069
- this.emit("close", void 0);
3070
- this.handleClose();
3071
- this.emit("closed", void 0);
3432
+ suspend() {
3433
+ this.animation.cancel();
3434
+ this.currentViewTransition?.skipTransition?.();
3435
+ this.newCycle();
3072
3436
  }
3073
- async dragTo(targetSize, velocityOrOpts = 0) {
3074
- if (this.destroyed) return;
3075
- const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
3076
- const velocityPxPerMs = opts.velocity ?? 0;
3077
- const externalSignal = opts.signal;
3078
- if (externalSignal?.aborted) return;
3437
+ landNow() {
3438
+ const transition = this.transition;
3439
+ if (this.destroyed || !transition || this.isDraggingAny()) return;
3440
+ this.animation.cancel();
3441
+ this.currentViewTransition?.skipTransition?.();
3079
3442
  this.newCycle();
3080
- const onExternalAbort = () => {
3081
- this.animation.cancel();
3082
- this.newCycle();
3083
- };
3084
- externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3085
- const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3086
- await this.animation.animateTo(clamped, velocityPxPerMs);
3087
- externalSignal?.removeEventListener("abort", onExternalAbort);
3088
- if (this.destroyed || externalSignal?.aborted) return;
3443
+ this.applySize(transition.size);
3444
+ this.land(false);
3089
3445
  }
3090
- open(id) {
3091
- if (this.destroyed) return Promise.resolve();
3092
- const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
3093
- return this.snapTo(target);
3446
+ reseat() {
3447
+ if (this.destroyed || this.isDraggingAny()) return;
3448
+ const active = this.snaps.findById(this.activeId);
3449
+ if (!active) return;
3450
+ const cap = this.snaps.getMaxAxisSize();
3451
+ const target = cap > 0 ? Math.min(active.size, cap) : active.size;
3452
+ this.updateAriaSlider();
3453
+ const transition = this.transition;
3454
+ if (transition) {
3455
+ if (Math.abs(target - transition.size) < SIZE_WRITE_EPSILON && Math.abs(cap - transition.cap) < SIZE_WRITE_EPSILON) {
3456
+ return;
3457
+ }
3458
+ transition.size = target;
3459
+ transition.cap = cap;
3460
+ this.applySize(this.animation.retarget(target) ? this.size : target);
3461
+ return;
3462
+ }
3463
+ if (this.openPending && target > 0 && this.phase === "closed") {
3464
+ void this.runTransition(active, 0, false);
3465
+ return;
3466
+ }
3467
+ this.applySize(target);
3468
+ this.settlePhase(true);
3469
+ this.flush();
3094
3470
  }
3095
- close(reason = "programmatic") {
3471
+ installLifecycle(promote) {
3472
+ if (promote) sheetStack.promote(this.id);
3473
+ if (this.lifecycle.isInstalled) return true;
3474
+ try {
3475
+ this.lifecycle.install();
3476
+ return true;
3477
+ } catch (err) {
3478
+ this.abortOpen();
3479
+ queueMicrotask(() => {
3480
+ throw err;
3481
+ });
3482
+ return false;
3483
+ }
3484
+ }
3485
+ abortOpen() {
3486
+ this.animation.cancel();
3487
+ if (!this.destroyed) this.newCycle();
3488
+ this.phase = "closed";
3489
+ this.openPending = false;
3490
+ this.lifecycle.release();
3491
+ sheetStack.update();
3492
+ this.activeId = this.zeroSnapId() ?? this.activeId;
3493
+ this.applySize(0);
3494
+ this.setRestClosed(true);
3495
+ this.updateAriaSlider();
3496
+ }
3497
+ zeroSnapId() {
3498
+ return this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0);
3499
+ }
3500
+ isClosedSnap(id) {
3501
+ if (id === "closed") return true;
3502
+ return this.snapPointsRaw.find((p) => p.id === id)?.size === 0;
3503
+ }
3504
+ closingBlocked() {
3505
+ return this.disableCloseFlag && this.phase !== "closed";
3506
+ }
3507
+ setKeyboardInset(px) {
3508
+ if (px === this.keyboardInset) return;
3509
+ this.keyboardInset = px;
3510
+ for (const host of this.varHosts()) {
3511
+ host.style.setProperty("--bs-keyboard-inset", `${px}px`);
3512
+ }
3513
+ }
3514
+ varHosts() {
3515
+ const hosts = /* @__PURE__ */ new Set([this.element]);
3516
+ for (const host of [
3517
+ this.anchorHost,
3518
+ this.scrimParent,
3519
+ this.rootEl,
3520
+ this.homeRoot
3521
+ ]) {
3522
+ if (host) hosts.add(host);
3523
+ }
3524
+ return hosts;
3525
+ }
3526
+ dismissBlocked() {
3527
+ return (this.disableCloseFlag || this.persistent) && this.phase !== "closed";
3528
+ }
3529
+ post(event, payload) {
3530
+ this.outbox.push(() => this.bus.emit(event, payload));
3531
+ }
3532
+ flush() {
3533
+ if (this.flushing) return;
3534
+ this.flushing = true;
3535
+ try {
3536
+ for (let next = this.outbox.shift(); next; next = this.outbox.shift()) {
3537
+ next();
3538
+ }
3539
+ } finally {
3540
+ this.flushing = false;
3541
+ }
3542
+ }
3543
+ setRestClosed(closed) {
3544
+ if (closed === this.restClosed) return;
3545
+ this.restClosed = closed;
3546
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3547
+ else this.element.removeAttribute("data-bs-rest");
3548
+ }
3549
+ async dragTo(targetSize, velocityOrOpts = 0) {
3550
+ if (this.destroyed) return;
3551
+ const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
3552
+ const velocityPxPerMs = opts.velocity ?? 0;
3553
+ const externalSignal = opts.signal;
3554
+ if (externalSignal?.aborted) return;
3555
+ this.newCycle();
3556
+ const onExternalAbort = () => this.suspend();
3557
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
3558
+ const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3559
+ await this.animation.animateTo(clamped, velocityPxPerMs);
3560
+ externalSignal?.removeEventListener("abort", onExternalAbort);
3561
+ }
3562
+ open(id) {
3096
3563
  if (this.destroyed) return Promise.resolve();
3097
- if (this.disableCloseFlag) return Promise.resolve();
3098
- if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
3099
- 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];
3100
- return this.snapTo(closedId ?? this.activeId);
3564
+ if (!id && (this.phase === "open" || this.phase === "opening")) {
3565
+ return Promise.resolve();
3566
+ }
3567
+ const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
3568
+ return this.snapTo(target);
3569
+ }
3570
+ close(reason = "programmatic") {
3571
+ if (this.destroyed || this.disableCloseFlag) return Promise.resolve();
3572
+ if (this.persistent && reason !== "programmatic") return Promise.resolve();
3573
+ const target = this.zeroSnapId() ?? this.allowedIdsBySize()[0];
3574
+ if (target === void 0) return Promise.resolve();
3575
+ if (this.phase === "closed" && this.activeId === target) {
3576
+ return Promise.resolve();
3577
+ }
3578
+ const visible = this.phase === "open" || this.phase === "opening";
3579
+ if (visible && this.emitBeforeClose(reason)) return Promise.resolve();
3580
+ return this.snapTo(target);
3101
3581
  }
3102
3582
  canDismiss() {
3103
3583
  return !this.persistent && !this.disableCloseFlag && !this.destroyed;
@@ -3120,10 +3600,12 @@ var BottomSheetCore = class {
3120
3600
  }
3121
3601
  setDragFrom(mode) {
3122
3602
  if (this.destroyed || mode === this.dragFromMode) return;
3603
+ const wasDragging = this.gesture?.isDragging ?? false;
3123
3604
  this.gesture?.forceClearDragState();
3124
3605
  this.detachSheetGesture?.();
3125
3606
  this.dragFromMode = mode;
3126
3607
  this.mountSheetGesture();
3608
+ if (wasDragging) this.restoreAfterDrag();
3127
3609
  }
3128
3610
  getDragFrom() {
3129
3611
  return this.dragFromMode;
@@ -3136,6 +3618,7 @@ var BottomSheetCore = class {
3136
3618
  const nowVertical = next === "bottom" || next === "top";
3137
3619
  this.gesture?.forceClearDragState();
3138
3620
  this.detachSheetGesture?.();
3621
+ this.landNow();
3139
3622
  this.animation.cancel();
3140
3623
  this.element.style.removeProperty(layoutAxis(this.mode));
3141
3624
  this.mode = next;
@@ -3151,9 +3634,12 @@ var BottomSheetCore = class {
3151
3634
  this.detachSliderKeyboard?.();
3152
3635
  this.mountSliderKeyboard();
3153
3636
  this.mountSheetGesture();
3637
+ for (const anchor of this.anchors) anchor.reposition();
3638
+ this.placeScrimStages?.();
3639
+ if (this.stackEffectPrimed) {
3640
+ this.element.style.transformOrigin = nowVertical ? "50% 100%" : "0% 50%";
3641
+ }
3154
3642
  this.recompute();
3155
- const active = this.snaps.findById(this.activeId);
3156
- if (active) this.applySize(active.size);
3157
3643
  this.updateAriaSlider();
3158
3644
  }
3159
3645
  getMode() {
@@ -3195,13 +3681,24 @@ var BottomSheetCore = class {
3195
3681
  if (snap && ids.includes(snap)) {
3196
3682
  void this.snapTo(snap, 0, true);
3197
3683
  } else if (!ids.includes(this.activeId)) {
3198
- const fallback = ids[0];
3199
- if (fallback) void this.snapTo(fallback, 0, true);
3684
+ const fallback = this.fallbackSnap(
3685
+ this.snaps.findById(this.activeId)?.size ?? this.size
3686
+ );
3687
+ if (fallback) void this.snapTo(fallback.id, 0, true);
3200
3688
  }
3201
3689
  }
3690
+ fallbackSnap(from) {
3691
+ return (from > 0 ? this.snaps.nearest(from, true) : null) ?? this.snaps.nearest(from, false);
3692
+ }
3202
3693
  setBackdropRange(range) {
3203
3694
  this.scrim.setBackdropRange(range, () => this.applySize(this.size));
3204
3695
  }
3696
+ setBackdropOpacity(value) {
3697
+ this.scrim.setBackdropOpacity(value, () => this.applySize(this.size));
3698
+ }
3699
+ setBackdropColor(color) {
3700
+ this.scrim.setBackdropColor(color);
3701
+ }
3205
3702
  setScreenRange(range) {
3206
3703
  this.scrim.setScreenRange(range, () => this.applySize(this.size));
3207
3704
  }
@@ -3242,7 +3739,7 @@ var BottomSheetCore = class {
3242
3739
  }
3243
3740
  const handle = attachAnchor(
3244
3741
  {
3245
- mode: this.mode,
3742
+ getMode: () => this.mode,
3246
3743
  host: this.anchorHost,
3247
3744
  getState: () => ({
3248
3745
  activeId: this.activeId,
@@ -3280,9 +3777,9 @@ var BottomSheetCore = class {
3280
3777
  return () => {
3281
3778
  };
3282
3779
  }
3283
- const detachInstalled = installScrimStages(
3780
+ const stages = installScrimStages(
3284
3781
  {
3285
- mode: this.mode,
3782
+ getMode: () => this.mode,
3286
3783
  host,
3287
3784
  getState: () => ({
3288
3785
  activeId: this.activeId,
@@ -3295,10 +3792,14 @@ var BottomSheetCore = class {
3295
3792
  opts
3296
3793
  );
3297
3794
  const detach = () => {
3298
- if (this.detachScrimStages === detach) this.detachScrimStages = null;
3299
- detachInstalled();
3795
+ if (this.detachScrimStages === detach) {
3796
+ this.detachScrimStages = null;
3797
+ this.placeScrimStages = null;
3798
+ }
3799
+ stages.detach();
3300
3800
  };
3301
3801
  this.detachScrimStages = detach;
3802
+ this.placeScrimStages = stages.reposition;
3302
3803
  this.teardowns.add(() => this.detachScrimStages?.());
3303
3804
  return detach;
3304
3805
  }
@@ -3307,32 +3808,29 @@ var BottomSheetCore = class {
3307
3808
  }
3308
3809
  setSnapPoints(points, allowed) {
3309
3810
  if (this.destroyed) return;
3310
- this.animation.cancel();
3311
- this.newCycle();
3811
+ const from = this.snaps.findById(this.activeId)?.size ?? this.size;
3812
+ const restricted = this.snaps.getResolvedSnaps().some((s) => !this.snaps.getAllowedIds().includes(s.id));
3312
3813
  this.snapPointsRaw = points;
3313
3814
  auditVhUsage(points);
3314
3815
  this.auditDuplicateSnapIds(points);
3816
+ this.maxHeight.resolveCap();
3315
3817
  this.snaps.setRaw(points);
3818
+ this.maxHeight.clampTo();
3316
3819
  if (allowed) {
3317
3820
  this.snaps.setAllowedIds(allowed);
3318
3821
  } else {
3319
3822
  const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3320
- const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3823
+ const kept = restricted ? this.snaps.getAllowedIds().filter((id) => present.includes(id)) : [];
3321
3824
  this.snaps.setAllowedIds(kept.length ? kept : present);
3322
3825
  }
3323
3826
  this.scrollCache.clear();
3324
3827
  this.scrim.invalidateOpacityCache();
3325
- const current = this.snaps.findById(this.activeId);
3326
- if (current) {
3327
- this.applySize(current.size);
3328
- } else {
3329
- const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3330
- if (fallback) {
3331
- this.activeId = fallback.id;
3332
- this.applySize(fallback.size);
3333
- }
3828
+ if (this.snaps.findById(this.activeId)) {
3829
+ this.reseat();
3830
+ return;
3334
3831
  }
3335
- this.updateAriaSlider();
3832
+ const fallback = this.fallbackSnap(from) ?? this.snaps.getResolvedSnaps()[0];
3833
+ if (fallback) void this.transitionTo(fallback.id, 0, true);
3336
3834
  }
3337
3835
  recompute() {
3338
3836
  if (this.destroyed) return;
@@ -3340,22 +3838,11 @@ var BottomSheetCore = class {
3340
3838
  this.snaps.recompute();
3341
3839
  this.maxHeight.clampTo();
3342
3840
  this.scrim.invalidateOpacityCache();
3343
- if (this.isDraggingAny()) return;
3344
- this.healActiveSnap();
3345
- }
3346
- healActiveSnap() {
3347
- const current = this.snaps.findById(this.activeId);
3348
- if (!current) return;
3349
- this.size = current.size;
3350
- this.applySize(this.size);
3351
- this.updateAriaSlider();
3352
- if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3353
- this.emitOpenSequence(this.activeId);
3354
- }
3841
+ this.reseat();
3355
3842
  }
3356
3843
  destroy() {
3357
3844
  this.destroyed = true;
3358
- this.opening = false;
3845
+ this.outbox.length = 0;
3359
3846
  this.currentAbort.abort();
3360
3847
  this.currentViewTransition?.skipTransition?.();
3361
3848
  this.currentViewTransition = null;
@@ -3372,11 +3859,19 @@ var BottomSheetCore = class {
3372
3859
  this.backdrop.style.opacity = "";
3373
3860
  this.backdrop.style.pointerEvents = "";
3374
3861
  }
3375
- for (const host of [this.anchorHost, this.scrimParent, this.rootEl]) {
3862
+ for (const host of [
3863
+ this.anchorHost,
3864
+ this.scrimParent,
3865
+ this.rootEl,
3866
+ this.homeRoot
3867
+ ]) {
3376
3868
  if (!host) continue;
3377
3869
  host.style.removeProperty("--bs-size");
3378
3870
  host.style.removeProperty("--bs-progress");
3871
+ host.style.removeProperty("--bs-z-sheet");
3872
+ host.style.removeProperty("--bs-keyboard-inset");
3379
3873
  }
3874
+ this.element.style.removeProperty("--bs-keyboard-inset");
3380
3875
  if (this.screenComponent) {
3381
3876
  this.screenComponent.style.opacity = "";
3382
3877
  this.screenComponent.style.display = "";
@@ -3389,7 +3884,8 @@ var BottomSheetCore = class {
3389
3884
  }
3390
3885
  newCycle() {
3391
3886
  this.allowOvershoot = false;
3392
- this.opening = false;
3887
+ this.transition = null;
3888
+ this.inFlight = null;
3393
3889
  this.currentAbort.abort();
3394
3890
  this.currentAbort = new AbortController();
3395
3891
  return this.currentAbort.signal;
@@ -3412,8 +3908,9 @@ var BottomSheetCore = class {
3412
3908
  if (typeof document === "undefined") return;
3413
3909
  if (!this.lifecycle.focusTrapEnabled) return;
3414
3910
  if (this.element.getAttribute("aria-labelledby")) return;
3415
- const scope = this.handle ?? this.element;
3416
- const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3911
+ const HEADING = "h1,h2,h3,h4,h5,h6";
3912
+ const header = this.element.querySelector(".bs-header");
3913
+ const titled = this.element.querySelector("[data-bs-title]") ?? this.handle.querySelector(HEADING) ?? header?.querySelector(HEADING);
3417
3914
  if (!titled) return;
3418
3915
  if (!titled.id) {
3419
3916
  titled.id = nextInstanceId("bs-title");
@@ -3455,12 +3952,16 @@ var BottomSheetCore = class {
3455
3952
  this.stackZ = z;
3456
3953
  this.element.style.zIndex = String(z);
3457
3954
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3955
+ this.rootEl?.style.setProperty("--bs-z-sheet", String(z));
3956
+ this.homeRoot?.style.setProperty("--bs-z-sheet", String(z));
3458
3957
  for (const anchor of this.anchors) anchor.syncZ(z + 1);
3459
3958
  },
3460
3959
  setIsTop: (isTop) => {
3960
+ if (isTop === this.isTopSheet) return;
3461
3961
  this.isTopSheet = isTop;
3962
+ this.lifecycle.syncTop();
3462
3963
  },
3463
- isOpen: () => this.size > 0 || this.opening,
3964
+ isOpen: () => this.phase !== "closed",
3464
3965
  setDepth: (depth) => this.applyStackDepth(depth)
3465
3966
  }));
3466
3967
  }
@@ -3488,15 +3989,15 @@ var BottomSheetCore = class {
3488
3989
  const r = this.getAllowedRange();
3489
3990
  buf.size = this.size;
3490
3991
  buf.maxAxisSize = this.snaps.getMaxAxisSize();
3491
- buf.range.min = r.min;
3992
+ buf.range.min = this.dismissBlocked() ? Math.max(r.min, this.smallestOpenSize()) : r.min;
3492
3993
  buf.range.max = r.max;
3493
3994
  buf.rubberBandEnabled = this.rubberBandEnabled;
3494
3995
  return buf;
3495
3996
  },
3496
3997
  getDisableDrag: () => this.disableDragFlag,
3497
- cancelAnimation: () => this.animation.cancel(),
3998
+ cancelAnimation: () => this.suspend(),
3498
3999
  applySize: (size) => this.applySize(size),
3499
- animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
4000
+ restoreAfterDrag: () => this.restoreAfterDrag(),
3500
4001
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3501
4002
  emit: (event, payload) => this.emit(event, payload),
3502
4003
  listenerCount: (event) => this.bus.listenerCount(event)
@@ -3620,32 +4121,19 @@ var BottomSheetCore = class {
3620
4121
  element: this.element,
3621
4122
  getMode: () => this.mode,
3622
4123
  isDestroyed: () => this.destroyed,
3623
- isDragging: () => this.isDraggingAny(),
3624
4124
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3625
4125
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3626
- getSize: () => this.size,
3627
4126
  setMaxAxisSize: (size) => {
3628
4127
  this.snaps.setMaxAxisSize(size);
3629
4128
  },
3630
- setSize: (size) => {
3631
- this.size = size;
3632
- },
3633
4129
  recomputeSnaps: () => this.recomputeSnaps(),
3634
- applySize: (size) => this.applySize(size),
3635
- cancelInFlight: () => {
3636
- this.animation.cancel();
3637
- },
3638
- newCycle: () => {
3639
- this.newCycle();
3640
- },
3641
- isAnimating: () => this.animation.isAnimating,
3642
- resyncAfterCancel: () => this.resyncAfterResize()
4130
+ reseat: () => this.reseat(),
4131
+ landNow: () => this.landNow()
3643
4132
  }));
3644
4133
  if (this.lifecycle.closeOnEscape) {
3645
4134
  const onKey = (e) => {
3646
4135
  if (this.destroyed || e.defaultPrevented) return;
3647
- if (!this.canDismiss()) return;
3648
- if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
4136
+ if (e.key === "Escape" && this.isTopSheet) {
3649
4137
  void this.close("escape");
3650
4138
  }
3651
4139
  };
@@ -3663,7 +4151,10 @@ var BottomSheetCore = class {
3663
4151
  handle: this.handle,
3664
4152
  mode: this.mode,
3665
4153
  isDestroyed: () => this.destroyed,
3666
- getAllowedIds: () => this.allowedIdsBySize(),
4154
+ getAllowedIds: () => {
4155
+ const ids = this.allowedIdsBySize();
4156
+ return this.dismissBlocked() ? ids.filter((id) => (this.snaps.findById(id)?.size ?? 0) > 0) : ids;
4157
+ },
3667
4158
  getActiveId: () => this.activeId,
3668
4159
  snapTo: (id) => {
3669
4160
  void this.snapTo(id);
@@ -3676,56 +4167,46 @@ var BottomSheetCore = class {
3676
4167
  });
3677
4168
  }
3678
4169
  settleAfterDrag(delta, velocity, kind = "touch") {
3679
- const signal = this.newCycle();
3680
- const target = this.snaps.findDragSettleTarget({
3681
- delta,
3682
- velocity,
3683
- pointerKind: kind,
3684
- size: this.size,
3685
- activeId: this.activeId,
3686
- flickVelocity: this.flickVelocity,
3687
- dragThreshold: this.dragThreshold
3688
- });
4170
+ const target = this.snaps.findDragSettleTarget(
4171
+ {
4172
+ delta,
4173
+ velocity,
4174
+ pointerKind: kind,
4175
+ size: this.size,
4176
+ activeId: this.activeId,
4177
+ flickVelocity: this.flickVelocity,
4178
+ dragThreshold: this.dragThreshold
4179
+ },
4180
+ this.dismissBlocked()
4181
+ );
3689
4182
  if (!target) {
3690
4183
  this.clearWillChangeIfIdle();
3691
4184
  return;
3692
4185
  }
3693
4186
  const previousId = this.activeId;
3694
4187
  if (target.id === previousId && target.size === this.size) {
4188
+ if (this.phase === "opening" || this.phase === "closing") {
4189
+ this.land(false);
4190
+ }
3695
4191
  this.clearWillChangeIfIdle();
3696
4192
  return;
3697
4193
  }
3698
4194
  if (this.emitBeforeSnap(target, previousId)) {
3699
- const restore2 = this.snaps.findById(previousId);
3700
- if (restore2) {
3701
- void this.animation.animateTo(restore2.size, 0);
3702
- }
4195
+ this.restoreAfterDrag();
3703
4196
  return;
3704
4197
  }
3705
- const previousSize = this.size;
3706
- const previousSnapSize = this.snaps.findById(previousId)?.size;
3707
- this.scrollCache.cache(previousId, previousSize, target.size);
3708
- this.activeId = target.id;
3709
- if (previousSnapSize === 0 && target.size > 0) {
3710
- this.opening = true;
3711
- this.promotedForOpen = true;
3712
- sheetStack.promote(this.id);
3713
- }
3714
- const settleCap = this.snaps.getMaxAxisSize();
3715
- const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3716
- this.allowOvershoot = true;
3717
- void this.animation.animateTo(settleSize, velocity).then(() => {
3718
- if (signal.aborted) return;
3719
- this.allowOvershoot = false;
3720
- this.opening = false;
3721
- this.completeSnap(
3722
- target.id,
3723
- target.size,
3724
- previousSize,
3725
- previousSnapSize === 0,
3726
- true
3727
- );
3728
- });
4198
+ void this.runTransition(target, velocity, true);
4199
+ }
4200
+ restoreAfterDrag() {
4201
+ const active = this.snaps.findById(this.activeId);
4202
+ if (active) void this.runTransition(active, 0, false);
4203
+ }
4204
+ smallestOpenSize() {
4205
+ const allowed = this.snaps.getAllowedIds();
4206
+ for (const snap of this.snaps.getResolvedSnaps()) {
4207
+ if (snap.size > 0 && allowed.includes(snap.id)) return snap.size;
4208
+ }
4209
+ return 0;
3729
4210
  }
3730
4211
  isDraggingAny() {
3731
4212
  return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
@@ -3750,9 +4231,10 @@ var BottomSheetCore = class {
3750
4231
  const offset = cap - clamped;
3751
4232
  const style = this.element.style;
3752
4233
  if (!skipTransform) style.transform = this.transformTemplate(offset);
4234
+ const moving = this.animation.isAnimating || this.isDraggingAny();
3753
4235
  const sizeChanged = this.sizeWriteSentinel.shouldWrite(
3754
4236
  clamped,
3755
- this.animation.isAnimating || this.isDraggingAny() ? SIZE_WRITE_EPSILON : 0
4237
+ moving ? SIZE_WRITE_EPSILON : 0
3756
4238
  );
3757
4239
  if (sizeChanged) {
3758
4240
  style.setProperty("--bs-size", `${clamped}px`);
@@ -3765,11 +4247,13 @@ var BottomSheetCore = class {
3765
4247
  if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3766
4248
  this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3767
4249
  }
4250
+ this.homeRoot?.style.setProperty("--bs-size", `${clamped}px`);
3768
4251
  }
3769
4252
  const progress = this.computeProgress(clamped);
4253
+ const opacityEpsilon = moving ? OPACITY_WRITE_EPSILON : 0;
3770
4254
  const progressChanged = this.progressWriteSentinel.shouldWrite(
3771
4255
  progress,
3772
- OPACITY_WRITE_EPSILON
4256
+ opacityEpsilon
3773
4257
  );
3774
4258
  if (progressChanged) {
3775
4259
  style.setProperty("--bs-progress", String(progress));
@@ -3779,8 +4263,9 @@ var BottomSheetCore = class {
3779
4263
  if (this.rootEl && this.rootEl !== this.anchorHost) {
3780
4264
  this.rootEl.style.setProperty("--bs-progress", String(progress));
3781
4265
  }
4266
+ this.homeRoot?.style.setProperty("--bs-progress", String(progress));
3782
4267
  }
3783
- this.scrim.applyOpacity(progress, progressChanged);
4268
+ this.scrim.applyOpacity(progress, progressChanged, opacityEpsilon);
3784
4269
  if ((progressChanged || sizeChanged) && this.bus.listenerCount("progress") > 0) {
3785
4270
  this.progressPayload.value = progress;
3786
4271
  this.progressPayload.size = clamped;
@@ -3793,238 +4278,322 @@ var BottomSheetCore = class {
3793
4278
  if (reachableMax <= min) return 0;
3794
4279
  return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3795
4280
  }
3796
- handleOpen() {
3797
- if (this.promotedForOpen) this.promotedForOpen = false;
3798
- else sheetStack.promote(this.id);
3799
- try {
3800
- this.lifecycle.install();
3801
- } catch (err) {
3802
- if (!this.destroyed) this.newCycle();
3803
- this.handleClose();
3804
- this.applySize(0);
3805
- this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3806
- this.size = 0;
3807
- queueMicrotask(() => {
3808
- throw err;
3809
- });
3810
- }
3811
- }
3812
- resyncAfterResize() {
3813
- if (this.destroyed) return;
3814
- const target = this.snaps.findById(this.activeId);
3815
- if (!target) return;
3816
- this.updateAriaSlider();
3817
- this.emit("snap", {
3818
- id: target.id,
3819
- size: this.size,
3820
- progress: this.computeProgress(this.size)
3821
- });
3822
- if (target.size > 0 && !this.lifecycle.isInstalled) {
3823
- this.emitOpenSequence(target.id);
3824
- } else if (target.size === 0 && this.lifecycle.isInstalled) {
3825
- this.emitCloseSequence();
3826
- }
3827
- }
3828
- handleClose() {
3829
- this.opening = false;
3830
- this.promotedForOpen = false;
3831
- this.lifecycle.release();
3832
- sheetStack.update();
3833
- }
3834
4281
  };
3835
4282
 
3836
4283
  // src/core/features/history-coordinator.ts
3837
- var entries = [];
3838
- var subscribers = /* @__PURE__ */ new Set();
3839
- var suppress = 0;
3840
- var internalDepth = 0;
3841
- var pendingRebrands = [];
3842
- var listenerInstalled = false;
3843
- var patchInstalled = false;
3844
- var origPushState = null;
3845
- var origReplaceState = null;
3846
- var patchedPushState = null;
3847
- var patchedReplaceState = null;
3848
- var hasWindow = () => typeof window !== "undefined";
3849
- var hasHistory = () => typeof history !== "undefined";
3850
- var brandMarker = (surface) => ({
4284
+ var MARKER = "__bs";
4285
+ var STACK = "__bsStack";
4286
+ var DEPTH = "__bsDepth";
4287
+ var ROUTED = "__bsRouted";
4288
+ var SETTLE_TIMEOUT_MS = 1e3;
4289
+ var state2 = shared(
4290
+ "history",
4291
+ () => ({
4292
+ live: [],
4293
+ stackId: void 0,
4294
+ baseUrl: void 0,
4295
+ position: null,
4296
+ knownLength: 0,
4297
+ href: "",
4298
+ subscribers: /* @__PURE__ */ new Set(),
4299
+ internal: 0,
4300
+ pending: [],
4301
+ deferred: [],
4302
+ waiters: [],
4303
+ timer: null,
4304
+ listening: false,
4305
+ leftovers: false,
4306
+ orphanChecked: false,
4307
+ push: null,
4308
+ replace: null
4309
+ })
4310
+ );
4311
+ var hasHistory = () => typeof window !== "undefined" && typeof history !== "undefined";
4312
+ var plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
4313
+ var depthOf = (value) => {
4314
+ const depth = value?.[DEPTH];
4315
+ return typeof depth === "number" ? depth : null;
4316
+ };
4317
+ var isMarker = (value) => value?.[MARKER] === true;
4318
+ var inStack = (value) => value !== null && state2.stackId !== void 0 && value[STACK] === state2.stackId && depthOf(value) !== null;
4319
+ var foreignKeys = (value) => {
4320
+ const out = {};
4321
+ const source = plain(value);
4322
+ if (!source) return out;
4323
+ for (const key of Object.keys(source)) {
4324
+ if (!key.startsWith(MARKER)) out[key] = source[key];
4325
+ }
4326
+ return out;
4327
+ };
4328
+ var markerFor = (surface, depth) => ({
4329
+ ...foreignKeys(history.state),
3851
4330
  ...surface.markerState(),
3852
- __bs: true
4331
+ [MARKER]: true,
4332
+ [STACK]: state2.stackId,
4333
+ [DEPTH]: depth
3853
4334
  });
4335
+ var urlForDepth = (depth) => {
4336
+ for (let i = depth - 1; i >= 0; i -= 1) {
4337
+ const url = state2.live[i]?.surface.url;
4338
+ if (url !== void 0) return url;
4339
+ }
4340
+ return state2.baseUrl;
4341
+ };
3854
4342
  var runInternal = (fn) => {
3855
- internalDepth += 1;
4343
+ state2.internal += 1;
3856
4344
  try {
3857
4345
  fn();
4346
+ return true;
4347
+ } catch {
4348
+ return false;
3858
4349
  } finally {
3859
- internalDepth -= 1;
4350
+ state2.internal -= 1;
3860
4351
  }
3861
4352
  };
3862
4353
  var notifyRouteChange = () => {
3863
- const snapshot = Array.from(subscribers);
3864
- for (const fn of snapshot) fn();
4354
+ for (const fn of Array.from(state2.subscribers)) {
4355
+ try {
4356
+ fn();
4357
+ } catch (err) {
4358
+ queueMicrotask(() => {
4359
+ throw err;
4360
+ });
4361
+ }
4362
+ }
3865
4363
  };
3866
4364
  var ensureListener = () => {
3867
- if (listenerInstalled || !hasWindow()) return;
4365
+ if (state2.listening || !hasHistory()) return;
3868
4366
  window.addEventListener("popstate", onPopState);
3869
- listenerInstalled = true;
4367
+ state2.listening = true;
3870
4368
  };
3871
4369
  var maybeTeardownListener = () => {
3872
- if (!listenerInstalled) return;
3873
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3874
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
3875
- listenerInstalled = false;
3876
- };
3877
- var installPatch = () => {
3878
- if (patchInstalled || !hasHistory()) return;
3879
- origPushState = history.pushState;
3880
- origReplaceState = history.replaceState;
3881
- patchedPushState = function(...args) {
3882
- origPushState.apply(this, args);
3883
- if (internalDepth === 0) notifyRouteChange();
3884
- };
3885
- patchedReplaceState = function(...args) {
3886
- origReplaceState.apply(this, args);
3887
- if (internalDepth === 0) notifyRouteChange();
3888
- };
3889
- history.pushState = patchedPushState;
3890
- history.replaceState = patchedReplaceState;
3891
- patchInstalled = true;
3892
- };
3893
- var removePatch = () => {
3894
- if (!patchInstalled) return;
3895
- if (history.pushState === patchedPushState && origPushState) {
3896
- history.pushState = origPushState;
3897
- }
3898
- if (history.replaceState === patchedReplaceState && origReplaceState) {
3899
- history.replaceState = origReplaceState;
3900
- }
3901
- origPushState = null;
3902
- origReplaceState = null;
3903
- patchedPushState = null;
3904
- patchedReplaceState = null;
3905
- patchInstalled = false;
3906
- };
3907
- function onPopState() {
3908
- if (suppress > 0) {
3909
- suppress -= 1;
3910
- const rebrand = pendingRebrands.shift();
3911
- if (rebrand) rebrand();
3912
- maybeTeardownListener();
3913
- return;
3914
- }
3915
- if (entries.length === 0) {
3916
- notifyRouteChange();
3917
- maybeTeardownListener();
4370
+ if (!state2.listening) return;
4371
+ if (state2.live.length > 0 || state2.subscribers.size > 0 || state2.pending.length > 0 || state2.leftovers) {
3918
4372
  return;
3919
4373
  }
3920
- let target = null;
3921
- let targetIdx = -1;
3922
- for (let i = entries.length - 1; i >= 0; i -= 1) {
3923
- const entry = entries[i];
3924
- if (!entry.surface.isOpen()) continue;
3925
- if (!target) {
3926
- target = entry;
3927
- targetIdx = i;
3928
- }
3929
- if (entry.surface.isTop()) {
3930
- target = entry;
3931
- targetIdx = i;
3932
- break;
3933
- }
3934
- }
3935
- if (!target) {
3936
- const removed = entries.pop();
3937
- if (removed) removed.live = false;
4374
+ if (hasHistory()) window.removeEventListener("popstate", onPopState);
4375
+ state2.listening = false;
4376
+ };
4377
+ var settleIfIdle = () => {
4378
+ if (state2.pending.length > 0) return;
4379
+ if (state2.timer !== null) {
4380
+ clearTimeout(state2.timer);
4381
+ state2.timer = null;
4382
+ }
4383
+ for (const entry of state2.deferred.splice(0)) pushEntry(entry);
4384
+ for (const resolve of state2.waiters.splice(0)) resolve();
4385
+ };
4386
+ var traverse = (delta, kind) => {
4387
+ state2.pending.push(kind);
4388
+ if (state2.timer !== null) clearTimeout(state2.timer);
4389
+ state2.timer = setTimeout(() => {
4390
+ state2.timer = null;
4391
+ state2.pending.length = 0;
4392
+ settleIfIdle();
3938
4393
  maybeTeardownListener();
3939
- return;
3940
- }
3941
- target.live = false;
3942
- entries.splice(targetIdx, 1);
3943
- const settled = target;
3944
- void Promise.resolve(settled.surface.close()).then(() => {
3945
- if (settled.surface.isOpen()) restore(settled);
4394
+ }, SETTLE_TIMEOUT_MS);
4395
+ ensureListener();
4396
+ const moved = runInternal(() => {
4397
+ if (delta < 0) history.back();
4398
+ else history.forward();
3946
4399
  });
3947
- maybeTeardownListener();
3948
- }
3949
- function restore(entry) {
3950
- if (!hasWindow() || !hasHistory()) return;
3951
- entry.live = true;
4400
+ if (!moved) {
4401
+ state2.pending.pop();
4402
+ settleIfIdle();
4403
+ }
4404
+ };
4405
+ var newStackId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
4406
+ var startStack = () => {
4407
+ state2.stackId = newStackId();
4408
+ const current = history.state;
4409
+ const leftover = plain(current);
4410
+ state2.baseUrl = isMarker(leftover) && leftover?.[ROUTED] !== void 0 ? void 0 : location.href;
4411
+ if (isMarker(leftover)) return;
4412
+ if (current !== null && current !== void 0 && !leftover) return;
3952
4413
  runInternal(() => {
3953
- try {
3954
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3955
- } catch {
3956
- entry.live = false;
3957
- }
4414
+ history.replaceState(
4415
+ { ...foreignKeys(current), [STACK]: state2.stackId, [DEPTH]: 0 },
4416
+ "",
4417
+ location.href
4418
+ );
3958
4419
  });
3959
- if (entry.live) {
3960
- entries.push(entry);
3961
- ensureListener();
3962
- }
4420
+ };
4421
+ function pushEntry(entry) {
4422
+ const depth = state2.live.length + 1;
4423
+ if (state2.live.length === 0) startStack();
4424
+ const written = runInternal(() => {
4425
+ history.pushState(markerFor(entry.surface, depth), "", entry.surface.url);
4426
+ });
4427
+ if (!written) return false;
4428
+ state2.live.push(entry);
4429
+ state2.position = depth;
4430
+ state2.knownLength = history.length;
4431
+ state2.href = location.href;
4432
+ ensureListener();
4433
+ return true;
3963
4434
  }
3964
- function pushBackMarker(surface) {
3965
- const entry = { surface, priorUrl: void 0, live: false };
3966
- if (!hasWindow() || !hasHistory()) {
3967
- return entry;
4435
+ var enlist = (entry) => {
4436
+ if (state2.pending.length > 0) state2.deferred.push(entry);
4437
+ else pushEntry(entry);
4438
+ };
4439
+ var sameBrand = (a, b) => {
4440
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])) {
4441
+ if (key.startsWith(MARKER) && a[key] !== b[key]) return false;
3968
4442
  }
3969
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
4443
+ return true;
4444
+ };
4445
+ var rebrand = (depth) => {
4446
+ const entry = state2.live[depth - 1];
4447
+ if (!entry) return;
4448
+ const url = urlForDepth(depth);
4449
+ const marker = markerFor(entry.surface, depth);
4450
+ const atUrl = url === void 0 || new URL(url, location.href).href === location.href;
4451
+ if (atUrl && sameBrand(plain(history.state) ?? {}, marker)) return;
3970
4452
  runInternal(() => {
4453
+ history.replaceState(marker, "", url);
4454
+ });
4455
+ state2.href = location.href;
4456
+ };
4457
+ var closeAbove = (depth) => {
4458
+ const dropped = state2.live.splice(depth);
4459
+ for (let i = dropped.length - 1; i >= 0; i -= 1) {
4460
+ const entry = dropped[i];
4461
+ let closing = void 0;
3971
4462
  try {
3972
- history.pushState(brandMarker(surface), "", surface.url);
3973
- entry.live = true;
3974
- } catch {
3975
- entry.live = false;
4463
+ closing = entry.surface.close();
4464
+ } catch (err) {
4465
+ queueMicrotask(() => {
4466
+ throw err;
4467
+ });
3976
4468
  }
3977
- });
3978
- if (entry.live) {
3979
- entries.push(entry);
3980
- ensureListener();
4469
+ void Promise.resolve(closing).then(() => {
4470
+ if (!entry.surface.isOpen()) return;
4471
+ const known = (e) => e.surface === entry.surface;
4472
+ if (state2.live.some(known) || state2.deferred.some(known)) return;
4473
+ enlist(entry);
4474
+ });
3981
4475
  }
4476
+ };
4477
+ function onPopState(event) {
4478
+ const kind = state2.pending.shift();
4479
+ const byUser = kind !== "pop";
4480
+ const landed = plain(event.state);
4481
+ const depth = depthOf(landed);
4482
+ const from = state2.position;
4483
+ const liveBefore = state2.live.length;
4484
+ const grew = history.length > state2.knownLength;
4485
+ state2.knownLength = history.length;
4486
+ const within = inStack(landed);
4487
+ if (within && depth !== null) {
4488
+ state2.position = depth;
4489
+ if (depth > state2.live.length) {
4490
+ const forward = from !== null && from < depth;
4491
+ traverse(forward ? 1 : -1, byUser ? "skip" : "pop");
4492
+ } else if (depth < state2.live.length) {
4493
+ if (byUser) closeAbove(depth);
4494
+ } else {
4495
+ rebrand(depth);
4496
+ }
4497
+ } else if (isMarker(landed)) {
4498
+ state2.position = null;
4499
+ traverse(-1, byUser ? "skip" : "pop");
4500
+ } else {
4501
+ state2.position = null;
4502
+ if (byUser && !grew && state2.live.length > 0) {
4503
+ closeAbove(state2.live.length - 1);
4504
+ }
4505
+ }
4506
+ const moved = location.href !== state2.href;
4507
+ state2.href = location.href;
4508
+ if (kind === void 0 && moved && (liveBefore === 0 || !within)) {
4509
+ notifyRouteChange();
4510
+ }
4511
+ settleIfIdle();
4512
+ maybeTeardownListener();
4513
+ }
4514
+ function pushBackMarker(surface) {
4515
+ const entry = { surface };
4516
+ if (hasHistory()) enlist(entry);
3982
4517
  return entry;
3983
4518
  }
3984
4519
  function popBackMarker(handle) {
3985
- if (!hasWindow() || !hasHistory()) return;
4520
+ if (!hasHistory()) return Promise.resolve();
3986
4521
  const entry = handle;
3987
- const idx = entries.indexOf(entry);
3988
- if (idx === -1 || !entry.live) return;
3989
- const last = entries.length - 1;
3990
- entry.live = false;
3991
- entries.splice(idx, 1);
3992
- let rebrand = null;
3993
- if (idx !== last) {
3994
- const rebrandSurface = entries[entries.length - 1].surface;
3995
- const rebrandUrl = entry.priorUrl;
3996
- rebrand = () => {
3997
- runInternal(() => {
3998
- history.replaceState(
3999
- brandMarker(rebrandSurface),
4000
- "",
4001
- rebrandUrl !== void 0 ? rebrandUrl : location.href
4002
- );
4003
- });
4004
- };
4522
+ const waiting = state2.deferred.indexOf(entry);
4523
+ if (waiting !== -1) state2.deferred.splice(waiting, 1);
4524
+ const idx = state2.live.indexOf(entry);
4525
+ if (idx === -1) return historySettled();
4526
+ const top = state2.live.length;
4527
+ state2.live.splice(idx, 1);
4528
+ const current = plain(history.state);
4529
+ const onTop = isMarker(current) && inStack(current) && depthOf(current) === top;
4530
+ if (onTop && state2.pending.length === 0) {
4531
+ traverse(-1, "pop");
4532
+ } else {
4533
+ state2.leftovers = true;
4534
+ ensureListener();
4005
4535
  }
4006
- pendingRebrands.push(rebrand);
4007
- suppress += 1;
4008
- runInternal(() => {
4009
- try {
4010
- history.back();
4011
- } catch {
4012
- suppress = Math.max(0, suppress - 1);
4013
- pendingRebrands.pop();
4014
- }
4015
- });
4016
4536
  maybeTeardownListener();
4537
+ return historySettled();
4538
+ }
4539
+ function releaseOrphanMarker() {
4540
+ if (!hasHistory() || state2.orphanChecked) return;
4541
+ state2.orphanChecked = true;
4542
+ if (state2.live.length > 0 || state2.pending.length > 0) return;
4543
+ if (!isMarker(plain(history.state))) return;
4544
+ state2.leftovers = true;
4545
+ traverse(-1, "pop");
4546
+ }
4547
+ function historySettled() {
4548
+ if (state2.pending.length === 0) return Promise.resolve();
4549
+ return new Promise((resolve) => {
4550
+ state2.waiters.push(resolve);
4551
+ });
4017
4552
  }
4553
+ var wrap = (original) => function(...args) {
4554
+ original.apply(this, args);
4555
+ state2.knownLength = history.length;
4556
+ if (location.href === state2.href) return;
4557
+ state2.href = location.href;
4558
+ if (state2.internal === 0 && state2.subscribers.size > 0) {
4559
+ notifyRouteChange();
4560
+ }
4561
+ };
4562
+ var installPatch = () => {
4563
+ if (!state2.push || history.pushState === state2.push.original) {
4564
+ const original = history.pushState;
4565
+ const wrapper = wrap(original);
4566
+ history.pushState = wrapper;
4567
+ state2.push = { wrapper, original };
4568
+ }
4569
+ if (!state2.replace || history.replaceState === state2.replace.original) {
4570
+ const original = history.replaceState;
4571
+ const wrapper = wrap(original);
4572
+ history.replaceState = wrapper;
4573
+ state2.replace = { wrapper, original };
4574
+ }
4575
+ };
4576
+ var removePatch = () => {
4577
+ if (state2.push && history.pushState === state2.push.wrapper) {
4578
+ history.pushState = state2.push.original;
4579
+ state2.push = null;
4580
+ }
4581
+ if (state2.replace && history.replaceState === state2.replace.wrapper) {
4582
+ history.replaceState = state2.replace.original;
4583
+ state2.replace = null;
4584
+ }
4585
+ };
4018
4586
  function subscribeRouteChange(fn) {
4019
- if (!hasWindow() || !hasHistory()) return () => {
4587
+ if (!hasHistory()) return () => {
4020
4588
  };
4021
- subscribers.add(fn);
4022
- if (subscribers.size === 1) installPatch();
4589
+ state2.subscribers.add(fn);
4590
+ state2.href = location.href;
4591
+ state2.knownLength = history.length;
4592
+ installPatch();
4023
4593
  ensureListener();
4024
4594
  return () => {
4025
- if (!subscribers.has(fn)) return;
4026
- subscribers.delete(fn);
4027
- if (subscribers.size === 0) removePatch();
4595
+ if (!state2.subscribers.delete(fn)) return;
4596
+ if (state2.subscribers.size === 0) removePatch();
4028
4597
  maybeTeardownListener();
4029
4598
  };
4030
4599
  }
@@ -4040,36 +4609,37 @@ function installRoute(deps) {
4040
4609
  };
4041
4610
  }
4042
4611
  let handle = null;
4612
+ const surface = {
4613
+ isOpen: () => !deps.isDestroyed() && deps.isOpen(),
4614
+ close: deps.close,
4615
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4616
+ url: deps.routedTo
4617
+ };
4043
4618
  const onOpen = () => {
4044
4619
  if (handle) return;
4045
- handle = pushBackMarker({
4046
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
4047
- isTop: deps.isTopSheet,
4048
- close: deps.close,
4049
- markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4050
- url: deps.routedTo
4051
- });
4620
+ handle = pushBackMarker(surface);
4052
4621
  };
4053
4622
  const onClose = () => {
4054
4623
  if (!handle) return;
4055
- popBackMarker(handle);
4624
+ deps.hold(popBackMarker(handle));
4056
4625
  handle = null;
4057
4626
  };
4058
4627
  const unsubscribeOpen = deps.on("open", onOpen);
4059
4628
  const unsubscribeClose = deps.on("close", onClose);
4060
- if (deps.getSize() > 0) onOpen();
4629
+ if (deps.isOpen()) onOpen();
4630
+ else releaseOrphanMarker();
4061
4631
  return () => {
4062
4632
  unsubscribeOpen();
4063
4633
  unsubscribeClose();
4064
4634
  if (handle) {
4065
- popBackMarker(handle);
4635
+ void popBackMarker(handle);
4066
4636
  handle = null;
4067
4637
  }
4068
4638
  };
4069
4639
  }
4070
4640
  function installRouteChange(deps) {
4071
4641
  return subscribeRouteChange(() => {
4072
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
4642
+ if (deps.isDestroyed() || !deps.isOpen()) return;
4073
4643
  void deps.close();
4074
4644
  });
4075
4645
  }
@@ -4103,7 +4673,10 @@ function installAutoCollapse(deps) {
4103
4673
  };
4104
4674
  const fire = () => {
4105
4675
  if (deps.isDestroyed()) return;
4106
- if (deps.isDragging()) return;
4676
+ if (deps.isDragging() || deps.isAnimating()) {
4677
+ reset();
4678
+ return;
4679
+ }
4107
4680
  const active = deps.resolveSnap(deps.getActiveId());
4108
4681
  if (!active || active.size === 0) return;
4109
4682
  let target = null;
@@ -4140,6 +4713,9 @@ function installContentDrag(deps) {
4140
4713
 
4141
4714
  // src/core/features/visual-viewport.ts
4142
4715
  var SAFETY_PAD = 8;
4716
+ var KEYBOARD_MIN_INSET = 1;
4717
+ var KEYBOARD_MIN_COVER = 60;
4718
+ var ZOOMED = 1.01;
4143
4719
  function installVisualViewport(deps) {
4144
4720
  if (typeof window === "undefined" || !window.visualViewport) {
4145
4721
  return () => {
@@ -4147,36 +4723,36 @@ function installVisualViewport(deps) {
4147
4723
  }
4148
4724
  const vv = window.visualViewport;
4149
4725
  let rafId = null;
4726
+ const coveredByKeyboard = () => {
4727
+ if ((vv.scale || 1) > ZOOMED) return 0;
4728
+ const covered = window.innerHeight - ((vv.offsetTop || 0) + vv.height);
4729
+ return covered >= KEYBOARD_MIN_COVER ? Math.round(covered) : 0;
4730
+ };
4150
4731
  const onResize = () => {
4151
4732
  if (rafId !== null) return;
4152
4733
  rafId = requestAnimationFrame(() => {
4153
4734
  rafId = null;
4154
4735
  if (deps.isDestroyed()) return;
4736
+ deps.setKeyboardInset(coveredByKeyboard());
4737
+ const before = seatOf(deps);
4155
4738
  deps.recomputeSnaps();
4156
4739
  const isVertical = deps.isVerticalAxis();
4157
4740
  const visible = isVertical ? vv.height : vv.width;
4158
4741
  const windowSize = isVertical ? window.innerHeight : window.innerWidth;
4159
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
4742
+ const pad = windowSize - visible > KEYBOARD_MIN_INSET ? SAFETY_PAD : 0;
4743
+ const clamp = Math.max(0, Math.min(windowSize, visible) - pad);
4160
4744
  if (clamp < deps.getMaxAxisSize()) {
4161
4745
  deps.setMaxAxisSize(clamp);
4162
4746
  }
4163
- const wasAnimating = deps.isAnimating?.() ?? false;
4164
- deps.cancelInFlight();
4165
- deps.newCycle();
4166
- const max = deps.getMaxAxisSize();
4167
- deps.setSize(Math.min(deps.getSize(), max));
4168
- const current = deps.resolveActiveSnap();
4169
- if (current && !deps.isDragging()) {
4170
- deps.applySize(Math.min(current.size, max));
4171
- if (wasAnimating) deps.resyncAfterCancel?.();
4172
- } else {
4173
- deps.applySize(deps.getSize());
4174
- }
4747
+ if (sameSeat(before, seatOf(deps))) return;
4748
+ deps.reseat();
4175
4749
  });
4176
4750
  };
4177
4751
  vv.addEventListener("resize", onResize);
4752
+ vv.addEventListener("scroll", onResize);
4178
4753
  return () => {
4179
4754
  vv.removeEventListener("resize", onResize);
4755
+ vv.removeEventListener("scroll", onResize);
4180
4756
  if (rafId !== null) cancelAnimationFrame(rafId);
4181
4757
  };
4182
4758
  }
@@ -4191,7 +4767,7 @@ function routeFeature() {
4191
4767
  teardowns.add(
4192
4768
  installRouteChange({
4193
4769
  isDestroyed: ctx.isDestroyed,
4194
- getSize: ctx.getSize,
4770
+ isOpen: () => ctx.getPhase() !== "closed",
4195
4771
  close: () => ctx.close()
4196
4772
  })
4197
4773
  );
@@ -4200,10 +4776,10 @@ function routeFeature() {
4200
4776
  installRoute({
4201
4777
  routedTo: ctx.options.routedTo,
4202
4778
  closeOnBack: ctx.options.closeOnBack,
4203
- isTopSheet: ctx.isTopSheet,
4204
- getSize: ctx.getSize,
4779
+ isOpen: () => ctx.getPhase() !== "closed",
4205
4780
  isDestroyed: ctx.isDestroyed,
4206
4781
  close: () => ctx.close("back"),
4782
+ hold: ctx.holdSettle,
4207
4783
  on: ctx.on,
4208
4784
  sheetId: ctx.sheetId
4209
4785
  })
@@ -4229,6 +4805,7 @@ function autoCollapseFeature() {
4229
4805
  ms: ctx.options.autoCollapseAfter,
4230
4806
  isDestroyed: ctx.isDestroyed,
4231
4807
  isDragging: ctx.isDragging,
4808
+ isAnimating: ctx.isAnimating,
4232
4809
  getAllowedIds: ctx.getAllowedIds,
4233
4810
  getActiveId: ctx.getActiveId,
4234
4811
  resolveSnap: ctx.resolveSnap,
@@ -4256,21 +4833,14 @@ function visualViewportFeature() {
4256
4833
  install: (ctx) => {
4257
4834
  if (typeof window === "undefined") return;
4258
4835
  return installVisualViewport({
4259
- element: ctx.element,
4260
4836
  isVerticalAxis: ctx.isVerticalAxis,
4261
4837
  isDestroyed: ctx.isDestroyed,
4262
- isDragging: ctx.isDragging,
4263
4838
  recomputeSnaps: ctx.recomputeSnaps,
4264
4839
  resolveActiveSnap: ctx.resolveActiveSnap,
4265
4840
  getMaxAxisSize: ctx.getMaxAxisSize,
4266
- getSize: ctx.getSize,
4267
4841
  setMaxAxisSize: ctx.setMaxAxisSize,
4268
- setSize: ctx.setSize,
4269
- applySize: ctx.applySize,
4270
- cancelInFlight: ctx.cancelInFlight,
4271
- newCycle: ctx.newCycle,
4272
- isAnimating: ctx.isAnimating,
4273
- resyncAfterCancel: ctx.resyncAfterResize
4842
+ setKeyboardInset: ctx.setKeyboardInset,
4843
+ reseat: ctx.reseat
4274
4844
  });
4275
4845
  }
4276
4846
  };
@@ -4434,36 +5004,71 @@ var BottomSheetEngine = class extends BottomSheetCore {
4434
5004
  }
4435
5005
  };
4436
5006
 
4437
- // src/core/features/teleport.ts
4438
- var resolveTeleportTarget = (target) => {
4439
- if (!target) return null;
4440
- if (typeof target !== "string") return target;
4441
- if (typeof document === "undefined") return null;
4442
- if (target === "body") return document.body;
4443
- return document.querySelector(target);
4444
- };
4445
- var teleportElements = (els, target) => {
4446
- const records = [];
4447
- for (const el of els) {
4448
- if (!el || el.parentNode === target) continue;
4449
- records.push({ el, parent: el.parentNode, next: el.nextSibling });
4450
- target.appendChild(el);
5007
+ // src/core/primitives/option-keys.ts
5008
+ var ENGINE_OPTION_KEYS = [
5009
+ "mode",
5010
+ "snapPoints",
5011
+ "allowed",
5012
+ "initial",
5013
+ "duration",
5014
+ "easing",
5015
+ "respectReducedMotion",
5016
+ "flickVelocity",
5017
+ "dragThreshold",
5018
+ "dragFrom",
5019
+ "dragFromContent",
5020
+ "fitContentToSnap",
5021
+ "rubberBand",
5022
+ "backdropRange",
5023
+ "backdropOpacity",
5024
+ "backdropColor",
5025
+ "screenRange",
5026
+ "scrimMode",
5027
+ "scrimColor",
5028
+ "scrimBlur",
5029
+ "scrimInteractive",
5030
+ "scrimTapToClose",
5031
+ "scrimPreset",
5032
+ "animation",
5033
+ "settleAnimation",
5034
+ "spring",
5035
+ "focusTrap",
5036
+ "initialFocus",
5037
+ "closeOnEscape",
5038
+ "lockBodyScroll",
5039
+ "closeOnBack",
5040
+ "routedTo",
5041
+ "inertSiblings",
5042
+ "persistent",
5043
+ "disableClose",
5044
+ "disableDrag",
5045
+ "closeOnRouteChange",
5046
+ "radius",
5047
+ "maxHeight",
5048
+ "returnFocusTo",
5049
+ "persistKey",
5050
+ "autoCollapseAfter",
5051
+ "linkedSheets",
5052
+ "viewTransitions",
5053
+ "stackEffect",
5054
+ "features"
5055
+ ];
5056
+ var pickEngineOptions = (source) => {
5057
+ const out = {};
5058
+ for (const key of ENGINE_OPTION_KEYS) {
5059
+ const value = source[key];
5060
+ if (value !== void 0) out[key] = value;
4451
5061
  }
4452
- return () => {
4453
- for (let i = records.length - 1; i >= 0; i--) {
4454
- const { el, parent, next } = records[i];
4455
- if (parent && parent.isConnected) {
4456
- const before = next && next.parentNode === parent ? next : null;
4457
- try {
4458
- parent.insertBefore(el, before);
4459
- } catch {
4460
- el.parentNode?.removeChild(el);
4461
- }
4462
- } else {
4463
- el.parentNode?.removeChild(el);
4464
- }
4465
- }
4466
- };
5062
+ return out;
5063
+ };
5064
+
5065
+ // src/core/primitives/open-state.ts
5066
+ var closedSnapId = (points) => points.find((point) => point.size === 0)?.id;
5067
+ var syncOpen = (engine, open) => {
5068
+ const phase = engine.state.phase;
5069
+ const showing = phase === "open" || phase === "opening";
5070
+ if (open && !showing) void engine.open();
5071
+ else if (!open && showing) void engine.close();
4467
5072
  };
4468
5073
 
4469
5074
  // src/solid/index.tsx
@@ -4473,19 +5078,28 @@ var BottomSheet = (props) => {
4473
5078
  let contentEl;
4474
5079
  let backdropEl;
4475
5080
  let scrimEl;
4476
- const initialActive = () => props.initial ?? props.snapPoints[0]?.id ?? "default";
4477
- const [state, setState] = createSignal({
5081
+ const initialSnap = () => props.initial ?? (props.open === false ? closedSnapId(props.snapPoints) : void 0);
5082
+ const initialActive = () => initialSnap() ?? props.snapPoints[0]?.id ?? "default";
5083
+ const [state3, setState] = createSignal({
4478
5084
  size: 0,
4479
5085
  activeId: initialActive(),
5086
+ phase: "closed",
4480
5087
  isDragging: false,
4481
5088
  isAnimating: false,
4482
5089
  progress: 0
4483
5090
  });
4484
5091
  let engine = null;
4485
5092
  let restoreTeleport = null;
5093
+ const [physicalMode, setPhysicalMode] = createSignal(
5094
+ guessPhysicalMode(untrack(() => props.mode))
5095
+ );
5096
+ const restAtStart = untrack(
5097
+ () => startsClosed(props.snapPoints, initialSnap(), props.allowed) ? "closed" : void 0
5098
+ );
4486
5099
  onMount(() => {
4487
5100
  if (!sheetEl) return;
4488
5101
  const engineOpts = {
5102
+ ...pickEngineOptions(props),
4489
5103
  element: sheetEl,
4490
5104
  handle: handleEl,
4491
5105
  scrollContainer: contentEl,
@@ -4493,30 +5107,8 @@ var BottomSheet = (props) => {
4493
5107
  scrim: scrimEl,
4494
5108
  mode: props.mode ?? "bottom",
4495
5109
  snapPoints: props.snapPoints,
4496
- allowed: props.allowed,
4497
- initial: props.initial,
4498
- animation: props.animation,
4499
- spring: props.spring,
4500
- focusTrap: props.focusTrap,
4501
- closeOnEscape: props.closeOnEscape,
4502
- closeOnBack: props.closeOnBack,
4503
- lockBodyScroll: props.lockBodyScroll,
4504
- rubberBand: props.rubberBand,
4505
- backdropRange: props.backdropRange,
4506
- radius: props.radius,
4507
- maxHeight: props.maxHeight,
4508
- persistent: props.persistent,
4509
- disableClose: props.disableClose,
4510
- disableDrag: props.disableDrag,
4511
- dragFrom: props.dragFrom,
4512
- dragFromContent: props.dragFromContent,
4513
- fitContentToSnap: props.fitContentToSnap,
4514
- closeOnRouteChange: props.closeOnRouteChange,
4515
- stackEffect: props.stackEffect,
4516
- returnFocusTo: props.returnFocusTo
5110
+ initial: initialSnap()
4517
5111
  };
4518
- engine = new BottomSheetEngine(engineOpts);
4519
- props.engineRef?.(engine);
4520
5112
  const teleportTarget = props.teleport === false ? null : resolveTeleportTarget(props.teleportTo);
4521
5113
  if (teleportTarget) {
4522
5114
  restoreTeleport = teleportElements(
@@ -4524,6 +5116,9 @@ var BottomSheet = (props) => {
4524
5116
  teleportTarget
4525
5117
  );
4526
5118
  }
5119
+ engine = new BottomSheetEngine(engineOpts);
5120
+ setPhysicalMode(engine.getMode());
5121
+ props.engineRef?.(engine);
4527
5122
  const sync = () => {
4528
5123
  if (!engine) return;
4529
5124
  const s = engine.state;
@@ -4537,8 +5132,15 @@ var BottomSheet = (props) => {
4537
5132
  });
4538
5133
  engine.on("before-snap", (payload) => props.onBeforeSnap?.(payload));
4539
5134
  engine.on("before-close", (payload) => props.onBeforeClose?.(payload));
4540
- engine.on("open", (payload) => props.onOpen?.(payload.id));
4541
- engine.on("close", () => props.onClose?.());
5135
+ engine.on("open", (payload) => {
5136
+ props.onOpen?.(payload.id);
5137
+ props.onOpenChange?.(true);
5138
+ });
5139
+ engine.on("close", () => {
5140
+ props.onClose?.();
5141
+ props.onOpenChange?.(false);
5142
+ });
5143
+ if (props.open !== void 0) syncOpen(engine, props.open);
4542
5144
  engine.on("opened", (payload) => props.onOpened?.(payload.id));
4543
5145
  engine.on("closed", () => props.onClosed?.());
4544
5146
  engine.on("dragstart", (payload) => {
@@ -4573,9 +5175,8 @@ var BottomSheet = (props) => {
4573
5175
  createEffect(
4574
5176
  on(
4575
5177
  () => props.snapPoints,
4576
- (points, prev) => {
4577
- if (!engine || prev === void 0) return;
4578
- engine.setSnapPoints(
5178
+ (points) => {
5179
+ engine?.setSnapPoints(
4579
5180
  points,
4580
5181
  props.allowed
4581
5182
  );
@@ -4586,59 +5187,72 @@ var BottomSheet = (props) => {
4586
5187
  createEffect(
4587
5188
  on(
4588
5189
  () => props.allowed,
4589
- (allowed, prev) => {
4590
- if (!engine || prev === void 0) return;
4591
- if (allowed) engine.setAllowed(allowed);
5190
+ (allowed) => {
5191
+ if (allowed) engine?.setAllowed(allowed);
4592
5192
  },
4593
5193
  { defer: true }
4594
5194
  )
4595
5195
  );
4596
5196
  createEffect(
4597
5197
  on(
4598
- () => props.persistent,
4599
- (value, prev) => {
4600
- if (!engine || prev === void 0 || value === void 0) return;
4601
- engine.setPersistent(value);
5198
+ () => props.open,
5199
+ (value) => {
5200
+ if (value !== void 0 && engine) syncOpen(engine, value);
4602
5201
  },
4603
5202
  { defer: true }
4604
5203
  )
4605
5204
  );
5205
+ createEffect(
5206
+ on(
5207
+ () => props.persistent,
5208
+ (value) => engine?.setPersistent(value ?? false),
5209
+ { defer: true }
5210
+ )
5211
+ );
4606
5212
  createEffect(
4607
5213
  on(
4608
5214
  () => props.disableClose,
4609
- (value, prev) => {
4610
- if (!engine || prev === void 0 || value === void 0) return;
4611
- engine.setDisableClose(value);
4612
- },
5215
+ (value) => engine?.setDisableClose(value ?? false),
4613
5216
  { defer: true }
4614
5217
  )
4615
5218
  );
4616
5219
  createEffect(
4617
5220
  on(
4618
5221
  () => props.disableDrag,
4619
- (value, prev) => {
4620
- if (!engine || prev === void 0 || value === void 0) return;
4621
- engine.setDisableDrag(value);
4622
- },
5222
+ (value) => engine?.setDisableDrag(value ?? false),
4623
5223
  { defer: true }
4624
5224
  )
4625
5225
  );
4626
5226
  createEffect(
4627
5227
  on(
4628
5228
  () => props.dragFromContent,
4629
- (value, prev) => {
4630
- if (!engine || prev === void 0 || value === void 0) return;
4631
- engine.setDragFromContent(value);
4632
- },
5229
+ (value) => engine?.setDragFromContent(value ?? true),
4633
5230
  { defer: true }
4634
5231
  )
4635
5232
  );
4636
5233
  createEffect(
4637
5234
  on(
4638
5235
  () => props.dragFrom,
4639
- (value, prev) => {
4640
- if (!engine || prev === void 0 || value === void 0) return;
4641
- engine.setDragFrom(value);
5236
+ (value) => {
5237
+ if (value !== void 0) engine?.setDragFrom(value);
5238
+ },
5239
+ { defer: true }
5240
+ )
5241
+ );
5242
+ createEffect(
5243
+ on(
5244
+ () => props.radius,
5245
+ (value) => {
5246
+ if (value !== void 0) engine?.setRadius(value);
5247
+ },
5248
+ { defer: true }
5249
+ )
5250
+ );
5251
+ createEffect(
5252
+ on(
5253
+ () => props.maxHeight,
5254
+ (value) => {
5255
+ if (value !== void 0) engine?.setMaxHeight(value);
4642
5256
  },
4643
5257
  { defer: true }
4644
5258
  )
@@ -4646,16 +5260,20 @@ var BottomSheet = (props) => {
4646
5260
  createEffect(
4647
5261
  on(
4648
5262
  () => props.mode,
4649
- (value, prev) => {
4650
- if (!engine || prev === void 0 || value === void 0) return;
4651
- engine.setMode(value);
5263
+ (value) => {
5264
+ const next = value ?? "bottom";
5265
+ engine?.setMode(next);
5266
+ setPhysicalMode(engine ? engine.getMode() : guessPhysicalMode(next));
4652
5267
  },
4653
5268
  { defer: true }
4654
5269
  )
4655
5270
  );
4656
5271
  const showBackdrop = createMemo(() => props.backdrop !== false);
4657
- const mode = createMemo(() => props.mode ?? "bottom");
4658
5272
  const ariaLabel = createMemo(() => props.ariaLabel ?? "Bottom sheet");
5273
+ const header = children(() => props.header);
5274
+ const footer = children(() => props.footer);
5275
+ const leftButton = children(() => props.leftButton);
5276
+ const rightButton = children(() => props.rightButton);
4659
5277
  return <div class="bs-root">
4660
5278
  {showBackdrop() ? <div class="bs-backdrop" ref={(el) => backdropEl = el} /> : null}
4661
5279
  <div class="bs-screen" ref={(el) => scrimEl = el}>
@@ -4664,8 +5282,9 @@ var BottomSheet = (props) => {
4664
5282
  <section
4665
5283
  class="bs-sheet"
4666
5284
  ref={(el) => sheetEl = el}
4667
- data-mode={mode()}
4668
- data-active={state().activeId}
5285
+ data-mode={physicalMode()}
5286
+ data-bs-rest={restAtStart}
5287
+ data-active={state3().activeId}
4669
5288
  role="dialog"
4670
5289
  aria-modal={props.focusTrap ? "true" : void 0}
4671
5290
  aria-label={ariaLabel()}
@@ -4675,35 +5294,39 @@ var BottomSheet = (props) => {
4675
5294
  ref={(el) => handleEl = el}
4676
5295
  role="slider"
4677
5296
  tabIndex={0}
4678
- aria-label="Resize sheet"
5297
+ aria-label={props.handleLabel ?? "Resize sheet"}
4679
5298
  />
4680
- <Show when={props.header}>
4681
- <div class="bs-header">{props.header}</div>
5299
+ <Show when={header()}>
5300
+ <div class="bs-header">{header()}</div>
4682
5301
  </Show>
4683
5302
  <div
4684
5303
  class="bs-content"
4685
5304
  ref={(el) => contentEl = el}
4686
5305
  tabIndex={0}
4687
5306
  role="region"
4688
- aria-label="Sheet content"
5307
+ aria-label={props.contentLabel ?? "Sheet content"}
4689
5308
  >
4690
5309
  {props.children}
4691
5310
  </div>
4692
- <Show when={props.footer}>
4693
- <div class="bs-footer">{props.footer}</div>
5311
+ <Show when={footer()}>
5312
+ <div class="bs-footer">{footer()}</div>
4694
5313
  </Show>
4695
5314
  <span class="bs-sr-only" role="status" aria-live="polite">
4696
- {state().activeId}
5315
+ {state3().activeId}
4697
5316
  </span>
4698
5317
  </section>
4699
- <Show when={props.leftButton}>
4700
- <div class="bs-button-slot" data-side="left" data-mode={mode()}>
4701
- {props.leftButton}
5318
+ <Show when={leftButton()}>
5319
+ <div class="bs-button-slot" data-side="left" data-mode={physicalMode()}>
5320
+ {leftButton()}
4702
5321
  </div>
4703
5322
  </Show>
4704
- <Show when={props.rightButton}>
4705
- <div class="bs-button-slot" data-side="right" data-mode={mode()}>
4706
- {props.rightButton}
5323
+ <Show when={rightButton()}>
5324
+ <div
5325
+ class="bs-button-slot"
5326
+ data-side="right"
5327
+ data-mode={physicalMode()}
5328
+ >
5329
+ {rightButton()}
4707
5330
  </div>
4708
5331
  </Show>
4709
5332
  </div>;