@surdeddd/bottom-sheet 0.23.4 → 0.24.1

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 +159 -53
  74. package/dist/webgl.d.cts +2 -2
  75. package/dist/webgl.d.ts +2 -2
  76. package/dist/webgl.js +159 -53
  77. package/llms.txt +330 -0
  78. package/package.json +42 -20
  79. package/dist/transform-jVfAikE9.d.cts +0 -3
  80. package/dist/transform-jVfAikE9.d.ts +0 -3
@@ -5,6 +5,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
6
  const jsxRuntime = require("@builder.io/qwik/jsx-runtime");
7
7
  const qwik = require("@builder.io/qwik");
8
+ const STORE_KEY = /* @__PURE__ */ Symbol.for("@surdeddd/bottom-sheet/shared/1");
9
+ function shared(name, init) {
10
+ const host = globalThis;
11
+ const store = host[STORE_KEY] ?? (host[STORE_KEY] = {});
12
+ if (!(name in store)) store[name] = init();
13
+ return store[name];
14
+ }
8
15
  const BASE_Z = 100;
9
16
  const STEP = 10;
10
17
  class SheetStack {
@@ -71,7 +78,7 @@ class SheetStack {
71
78
  });
72
79
  }
73
80
  }
74
- const sheetStack = new SheetStack();
81
+ const sheetStack = shared("stack", () => new SheetStack());
75
82
  function buildTransformTemplate(axis) {
76
83
  switch (axis) {
77
84
  case "bottom":
@@ -86,7 +93,7 @@ function buildTransformTemplate(axis) {
86
93
  }
87
94
  const offsetFromMatrix = (axis, m) => axis === "bottom" ? m.m42 : axis === "top" ? -m.m42 : axis === "left" ? -m.m41 : m.m41;
88
95
  const layoutAxis = (axis) => axis === "left" || axis === "right" ? "width" : "height";
89
- const counters = {};
96
+ const counters = shared("instanceIds", () => ({}));
90
97
  function nextInstanceId(prefix) {
91
98
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
92
99
  return `${prefix}-${crypto.randomUUID()}`;
@@ -114,7 +121,13 @@ function createEventBus() {
114
121
  for (const fn of [
115
122
  ...set
116
123
  ]) {
117
- fn(payload);
124
+ try {
125
+ fn(payload);
126
+ } catch (err) {
127
+ queueMicrotask(() => {
128
+ throw err;
129
+ });
130
+ }
118
131
  }
119
132
  },
120
133
  listenerCount(event) {
@@ -125,7 +138,18 @@ function createEventBus() {
125
138
  }
126
139
  };
127
140
  }
141
+ function devWarn(...args) {
142
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
143
+ console.warn(...args);
144
+ }
145
+ }
128
146
  let probe = null;
147
+ const rejected = /* @__PURE__ */ new Set();
148
+ const warnRejected = (value) => {
149
+ if (rejected.has(value)) return;
150
+ rejected.add(value);
151
+ devWarn(`[BottomSheet] snap size "${value}" is not a CSS length the browser accepts — it resolves to 0.`);
152
+ };
129
153
  const PROBE_ATTR = "data-bs-probe";
130
154
  const ensureProbe = (axis) => {
131
155
  if (typeof document === "undefined") return null;
@@ -172,16 +196,13 @@ const resolveSnap = (point, mode, measureFit) => {
172
196
  if (!el) return 0;
173
197
  if (axis === "vertical") {
174
198
  el.style.height = point;
199
+ if (el.style.height === "") warnRejected(point);
175
200
  return Math.max(0, el.getBoundingClientRect().height);
176
201
  }
177
202
  el.style.width = point;
203
+ if (el.style.width === "") warnRejected(point);
178
204
  return Math.max(0, el.getBoundingClientRect().width);
179
205
  };
180
- function devWarn(...args) {
181
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
182
- console.warn(...args);
183
- }
184
- }
185
206
  const resolveSnapList = (points, mode, measureFit) => points.map((p) => {
186
207
  const raw = resolveSnap(p.size, mode, measureFit);
187
208
  if (!Number.isFinite(raw) || raw < 0) {
@@ -196,6 +217,10 @@ const resolveSnapList = (points, mode, measureFit) => points.map((p) => {
196
217
  size: raw
197
218
  };
198
219
  }).sort((a, b) => a.size - b.size);
220
+ const startsClosed = (points, initial, allowed) => {
221
+ const id = initial ?? allowed?.[0] ?? points[0]?.id;
222
+ return points.find((p) => p.id === id)?.size === 0;
223
+ };
199
224
  const VH_NOT_DVH = /(^|[^a-z])vh\b/i;
200
225
  const auditVhUsage = (points) => {
201
226
  for (const p of points) {
@@ -306,6 +331,7 @@ class SnapResolver {
306
331
  setMode(mode) {
307
332
  if (mode === this.mode) return;
308
333
  this.mode = mode;
334
+ this.notified = false;
309
335
  this.recompute();
310
336
  }
311
337
  setAllowedIds(ids) {
@@ -315,14 +341,20 @@ class SnapResolver {
315
341
  findById(id) {
316
342
  return findById(id, this.resolved);
317
343
  }
318
- findDragSettleTarget(args) {
344
+ findDragSettleTarget(args, openOnly = false) {
319
345
  return findDragSettleTarget({
320
346
  ...args,
321
347
  resolved: this.resolved,
322
- allowed: this.allowedIds,
348
+ allowed: this.pool(openOnly),
323
349
  maxAxisSize: this.maxAxisSize
324
350
  });
325
351
  }
352
+ nearest(size, openOnly) {
353
+ return findNearest(size, this.resolved, this.pool(openOnly));
354
+ }
355
+ pool(openOnly) {
356
+ return openOnly ? this.allowedIds.filter((id) => (this.findById(id)?.size ?? 0) > 0) : this.allowedIds;
357
+ }
326
358
  getAllowedRange() {
327
359
  return this.rangeCache ?? (this.rangeCache = allowedRange(this.resolved, this.allowedIds));
328
360
  }
@@ -364,22 +396,23 @@ const SIZE_WRITE_EPSILON = 0.5;
364
396
  const OPACITY_WRITE_EPSILON = 5e-3;
365
397
  const POINTER_EVENTS_OPACITY_THRESHOLD = 0.05;
366
398
  const RANGE_DIVISION_EPSILON = 1e-4;
399
+ const seatOf = (deps) => {
400
+ const snap = deps.resolveActiveSnap();
401
+ const max = deps.getMaxAxisSize();
402
+ return {
403
+ target: snap ? Math.min(snap.size, max) : null,
404
+ max
405
+ };
406
+ };
407
+ const 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;
367
408
  function installResizeObserver(deps) {
368
409
  if (typeof window === "undefined") {
369
410
  return () => {
370
411
  };
371
412
  }
372
- const seatFor = () => {
373
- const snap = deps.resolveActiveSnap();
374
- const max = deps.getMaxAxisSize();
375
- return {
376
- target: snap ? Math.min(snap.size, max) : null,
377
- max
378
- };
379
- };
380
413
  const onResize = () => {
381
414
  if (deps.isDestroyed()) return;
382
- const before = seatFor();
415
+ const before = seatOf(deps);
383
416
  deps.recomputeSnaps();
384
417
  const mode = deps.getMode();
385
418
  const isVerticalAxis = mode === "bottom" || mode === "top";
@@ -388,21 +421,8 @@ function installResizeObserver(deps) {
388
421
  if (viewportSize2 > 0 && viewportSize2 < max) {
389
422
  deps.setMaxAxisSize(viewportSize2);
390
423
  }
391
- const after = seatFor();
392
- 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) {
393
- return;
394
- }
395
- const wasAnimating = deps.isAnimating?.() ?? false;
396
- deps.cancelInFlight();
397
- deps.newCycle();
398
- const current = deps.resolveActiveSnap();
399
- const newMax = deps.getMaxAxisSize();
400
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
401
- deps.setSize(targetSize);
402
- if (current && !deps.isDragging()) {
403
- deps.applySize(targetSize);
404
- if (wasAnimating) deps.resyncAfterCancel?.();
405
- }
424
+ if (sameSeat(before, seatOf(deps))) return;
425
+ deps.reseat();
406
426
  };
407
427
  let resizeObserver = null;
408
428
  if (typeof ResizeObserver !== "undefined") {
@@ -414,7 +434,7 @@ function installResizeObserver(deps) {
414
434
  window.addEventListener("orientationchange", onResize);
415
435
  const onVisibility = () => {
416
436
  if (deps.isDestroyed()) return;
417
- if (document.hidden) deps.cancelInFlight();
437
+ if (document.hidden) deps.landNow();
418
438
  };
419
439
  document.addEventListener("visibilitychange", onVisibility);
420
440
  return () => {
@@ -437,6 +457,16 @@ const contentTargets = (scroller) => {
437
457
  }
438
458
  return out;
439
459
  };
460
+ const chromeTargets = (deps) => {
461
+ const out = [];
462
+ for (const child of Array.from(deps.element.children)) {
463
+ if (child !== deps.scrollContainer) out.push(child);
464
+ }
465
+ if (deps.handle !== deps.element && !out.includes(deps.handle)) {
466
+ out.push(deps.handle);
467
+ }
468
+ return out;
469
+ };
440
470
  function installFitObserver(deps) {
441
471
  if (typeof ResizeObserver === "undefined") return () => {
442
472
  };
@@ -452,7 +482,6 @@ function installFitObserver(deps) {
452
482
  });
453
483
  };
454
484
  const ro = new ResizeObserver(schedule);
455
- ro.observe(deps.handle);
456
485
  const scroller = deps.scrollContainer;
457
486
  const observed = /* @__PURE__ */ new Set();
458
487
  const slots = /* @__PURE__ */ new Set();
@@ -469,7 +498,10 @@ function installFitObserver(deps) {
469
498
  };
470
499
  const resync = () => {
471
500
  trackSlots();
472
- const targets = contentTargets(scroller);
501
+ const targets = [
502
+ ...chromeTargets(deps),
503
+ ...contentTargets(scroller)
504
+ ];
473
505
  for (const el of observed) {
474
506
  if (!targets.includes(el)) {
475
507
  ro.unobserve(el);
@@ -484,15 +516,16 @@ function installFitObserver(deps) {
484
516
  }
485
517
  schedule();
486
518
  };
519
+ resync();
487
520
  let mo;
488
- if (scroller) {
489
- resync();
490
- if (typeof MutationObserver !== "undefined") {
491
- mo = new MutationObserver(resync);
492
- mo.observe(scroller, {
493
- childList: true
494
- });
495
- }
521
+ if (typeof MutationObserver !== "undefined") {
522
+ mo = new MutationObserver(resync);
523
+ mo.observe(deps.element, {
524
+ childList: true
525
+ });
526
+ if (scroller) mo.observe(scroller, {
527
+ childList: true
528
+ });
496
529
  }
497
530
  return () => {
498
531
  ro.disconnect();
@@ -503,6 +536,77 @@ function installFitObserver(deps) {
503
536
  if (raf) cancelAnimationFrame(raf);
504
537
  };
505
538
  }
539
+ const resolveTeleportTarget = (target) => {
540
+ if (!target) return null;
541
+ if (typeof target !== "string") return target;
542
+ if (typeof document === "undefined") return null;
543
+ if (target === "body") return document.body;
544
+ return document.querySelector(target);
545
+ };
546
+ const ROOT_CLASS = "bs-root";
547
+ const homes = /* @__PURE__ */ new WeakMap();
548
+ const homeRootOf = (root) => (root && homes.get(root)) ?? null;
549
+ const mirrorClasses = (home, wrapper) => {
550
+ wrapper.className = home?.className ?? "";
551
+ wrapper.classList.add(ROOT_CLASS);
552
+ };
553
+ const teleportElements = (els, target) => {
554
+ const nodes = els.filter((el) => !!el && el.parentNode !== target);
555
+ if (nodes.length === 0) return () => {
556
+ };
557
+ const home = nodes[0].closest(`.${ROOT_CLASS}`);
558
+ if (home && home.parentNode === target) return () => {
559
+ };
560
+ const wrapper = target.ownerDocument.createElement("div");
561
+ wrapper.setAttribute("data-bs-teleported", "");
562
+ mirrorClasses(home, wrapper);
563
+ let watcher = null;
564
+ if (home) {
565
+ for (const name of Array.from(home.style)) {
566
+ if (name.startsWith("--")) {
567
+ wrapper.style.setProperty(name, home.style.getPropertyValue(name));
568
+ }
569
+ }
570
+ homes.set(wrapper, home);
571
+ if (typeof MutationObserver !== "undefined") {
572
+ watcher = new MutationObserver(() => mirrorClasses(home, wrapper));
573
+ watcher.observe(home, {
574
+ attributes: true,
575
+ attributeFilter: [
576
+ "class"
577
+ ]
578
+ });
579
+ }
580
+ }
581
+ target.appendChild(wrapper);
582
+ const records = [];
583
+ for (const el of nodes) {
584
+ records.push({
585
+ el,
586
+ parent: el.parentNode,
587
+ next: el.nextSibling
588
+ });
589
+ wrapper.appendChild(el);
590
+ }
591
+ return () => {
592
+ watcher?.disconnect();
593
+ for (let i = records.length - 1; i >= 0; i--) {
594
+ const { el, parent, next } = records[i];
595
+ if (parent && parent.isConnected) {
596
+ const before = next && next.parentNode === parent ? next : null;
597
+ try {
598
+ parent.insertBefore(el, before);
599
+ } catch {
600
+ el.parentNode?.removeChild(el);
601
+ }
602
+ } else {
603
+ el.parentNode?.removeChild(el);
604
+ }
605
+ }
606
+ homes.delete(wrapper);
607
+ wrapper.parentNode?.removeChild(wrapper);
608
+ };
609
+ };
506
610
  function createMaxHeightController(deps) {
507
611
  let maxHeightCap;
508
612
  let maxHeightRaw;
@@ -591,8 +695,9 @@ function installSliderKeyboard(deps) {
591
695
  const isVerticalAxis = deps.mode === "bottom" || deps.mode === "top";
592
696
  const stepUp = isVerticalAxis ? "ArrowUp" : "ArrowRight";
593
697
  const stepDown = isVerticalAxis ? "ArrowDown" : "ArrowLeft";
594
- const sheetExpandsKey = deps.mode === "top" ? stepDown : stepUp;
595
- const sheetCollapsesKey = deps.mode === "top" ? stepUp : stepDown;
698
+ const flipped = deps.mode === "top" || deps.mode === "right";
699
+ const sheetExpandsKey = flipped ? stepDown : stepUp;
700
+ const sheetCollapsesKey = flipped ? stepUp : stepDown;
596
701
  const onHandleKey = (e) => {
597
702
  if (deps.isDestroyed()) return;
598
703
  const allowed = deps.getAllowedIds();
@@ -674,18 +779,19 @@ class WriteSentinel {
674
779
  }
675
780
  function resolveSheetAnchoredStyle(mode, position, inset) {
676
781
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
782
+ const lifted = `calc(var(--bs-size, 0px) + var(--bs-keyboard-inset, 0px) + ${inset})`;
677
783
  switch (mode) {
678
784
  case "bottom":
679
785
  if (position === "sheet-top-left") return {
680
- bottom: sizeOffset,
786
+ bottom: lifted,
681
787
  left: inset
682
788
  };
683
789
  if (position === "sheet-top-right") return {
684
- bottom: sizeOffset,
790
+ bottom: lifted,
685
791
  right: inset
686
792
  };
687
793
  return {
688
- bottom: sizeOffset,
794
+ bottom: lifted,
689
795
  left: "50%",
690
796
  transform: "translateX(-50%)"
691
797
  };
@@ -733,6 +839,13 @@ function resolveSheetAnchoredStyle(mode, position, inset) {
733
839
  };
734
840
  }
735
841
  }
842
+ function resetOverlayPosition(ws) {
843
+ ws.top = "";
844
+ ws.bottom = "";
845
+ ws.left = "";
846
+ ws.right = "";
847
+ ws.transform = "";
848
+ }
736
849
  function applyOverlayPosition(ws, mode, position, inset) {
737
850
  switch (position) {
738
851
  case "top-left":
@@ -791,6 +904,8 @@ function applyOverlayPosition(ws, mode, position, inset) {
791
904
  }
792
905
  const VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
793
906
  const DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
907
+ const BACKDROP_COLOR_VAR = "--bs-backdrop-color";
908
+ const unit = (value) => Number.isFinite(value) ? Math.min(Math.max(value, 0), 1) : 1;
794
909
  class ScrimController {
795
910
  constructor(deps, opts) {
796
911
  __publicField(this, "screenComponent");
@@ -805,11 +920,13 @@ class ScrimController {
805
920
  __publicField(this, "scrimMode");
806
921
  __publicField(this, "backdropRange");
807
922
  __publicField(this, "screenRange");
923
+ __publicField(this, "backdropPeak");
808
924
  __publicField(this, "scrimEnabled", true);
809
925
  __publicField(this, "savedScrimRanges", null);
810
926
  __publicField(this, "scrimTapToCloseEnabled", false);
811
927
  __publicField(this, "detachScrimTap", null);
812
928
  __publicField(this, "scrimOverlayTeardown", null);
929
+ __publicField(this, "placeOverlay", null);
813
930
  __publicField(this, "backdropOpacitySentinel", new WriteSentinel());
814
931
  __publicField(this, "screenOpacitySentinel", new WriteSentinel());
815
932
  __publicField(this, "lastBackdropPointer", "");
@@ -832,6 +949,8 @@ class ScrimController {
832
949
  0,
833
950
  1
834
951
  ];
952
+ this.backdropPeak = unit(opts.backdropOpacity ?? 1);
953
+ this.setBackdropColor(opts.backdropColor);
835
954
  this.scrimTapToCloseEnabled = opts.scrimTapToClose ?? false;
836
955
  const explicitColor = opts.scrimColor ?? preset?.color;
837
956
  const effectiveColor = explicitColor ?? (this.scrimMode !== "off" && !this.backdrop ? DEFAULT_SCRIM_DIM : void 0);
@@ -861,18 +980,19 @@ class ScrimController {
861
980
  this.setScrimTapToClose(true);
862
981
  }
863
982
  }
864
- applyOpacity(progress, progressChanged) {
865
- if (!progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
983
+ applyOpacity(progress, progressChanged, epsilon = OPACITY_WRITE_EPSILON) {
984
+ if (epsilon > 0 && !progressChanged && this.backdropOpacitySentinel.value !== -1 && this.screenOpacitySentinel.value !== -1) {
866
985
  return;
867
986
  }
868
987
  if (this.backdrop) {
869
988
  const [s, e] = this.backdropRange;
870
989
  const range = Math.max(e - s, RANGE_DIVISION_EPSILON);
871
- const backdropOpacity = Math.min(Math.max((progress - s) / range, 0), 1);
872
- if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, OPACITY_WRITE_EPSILON)) {
990
+ const along = Math.min(Math.max((progress - s) / range, 0), 1);
991
+ const backdropOpacity = along * this.backdropPeak;
992
+ if (this.backdropOpacitySentinel.shouldWrite(backdropOpacity, epsilon)) {
873
993
  this.backdrop.style.opacity = String(backdropOpacity);
874
994
  }
875
- const nextPointer = backdropOpacity > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
995
+ const nextPointer = along > POINTER_EVENTS_OPACITY_THRESHOLD ? "auto" : "none";
876
996
  if (nextPointer !== this.lastBackdropPointer) {
877
997
  this.backdrop.style.pointerEvents = nextPointer;
878
998
  this.lastBackdropPointer = nextPointer;
@@ -881,7 +1001,7 @@ class ScrimController {
881
1001
  if (this.screenComponent && this.scrimMode !== "off") {
882
1002
  const [ss, se] = this.screenRange;
883
1003
  const screenOpacity = Math.min(1, Math.max(0, (progress - ss) / Math.max(RANGE_DIVISION_EPSILON, se - ss)));
884
- if (this.screenOpacitySentinel.shouldWrite(screenOpacity, OPACITY_WRITE_EPSILON)) {
1004
+ if (this.screenOpacitySentinel.shouldWrite(screenOpacity, epsilon)) {
885
1005
  this.screenComponent.style.opacity = String(screenOpacity);
886
1006
  }
887
1007
  const nextDisplay = screenOpacity > 0 ? "" : "none";
@@ -897,6 +1017,17 @@ class ScrimController {
897
1017
  this.invalidateOpacityCache();
898
1018
  applySize();
899
1019
  }
1020
+ setBackdropColor(color) {
1021
+ if (this.isDestroyed() || !this.backdrop) return;
1022
+ if (color) this.backdrop.style.setProperty(BACKDROP_COLOR_VAR, color);
1023
+ else this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1024
+ }
1025
+ setBackdropOpacity(value, applySize) {
1026
+ if (this.isDestroyed()) return;
1027
+ this.backdropPeak = unit(value);
1028
+ this.invalidateOpacityCache();
1029
+ applySize();
1030
+ }
900
1031
  setScreenRange(range, applySize) {
901
1032
  if (this.isDestroyed()) return;
902
1033
  this.screenRange = range;
@@ -925,6 +1056,8 @@ class ScrimController {
925
1056
  if (this.isDestroyed() || mode === this.mode) return;
926
1057
  this.mode = mode;
927
1058
  this.invalidateOpacityCache();
1059
+ if (this.scrimMode === "above-sheet") this.applyAboveSheetInset();
1060
+ this.placeOverlay?.();
928
1061
  }
929
1062
  setScrimMode(mode, applySize) {
930
1063
  if (this.isDestroyed()) return;
@@ -937,7 +1070,7 @@ class ScrimController {
937
1070
  if (previous === "above-sheet" && mode !== "above-sheet") {
938
1071
  s.position = "";
939
1072
  s.inset = "";
940
- s.pointerEvents = "";
1073
+ this.syncPointer();
941
1074
  }
942
1075
  if (mode === "above-sheet") {
943
1076
  this.applyAboveSheetInset();
@@ -962,9 +1095,6 @@ class ScrimController {
962
1095
  if (enabled === installed) return;
963
1096
  if (enabled) {
964
1097
  const target = this.screenComponent;
965
- if (target.style.pointerEvents === "none") {
966
- target.style.pointerEvents = "auto";
967
- }
968
1098
  const onClick = () => {
969
1099
  if (this.isDestroyed()) return;
970
1100
  if (this.closeFn) {
@@ -987,6 +1117,11 @@ class ScrimController {
987
1117
  this.detachScrimTap = null;
988
1118
  this.scrimTapToCloseEnabled = false;
989
1119
  }
1120
+ this.syncPointer();
1121
+ }
1122
+ syncPointer() {
1123
+ if (!this.screenComponent) return;
1124
+ this.screenComponent.style.pointerEvents = this.scrimInteractive || this.detachScrimTap ? "auto" : "none";
990
1125
  }
991
1126
  setScrimEnabled(enabled, applySize) {
992
1127
  if (this.isDestroyed()) return;
@@ -1081,8 +1216,12 @@ class ScrimController {
1081
1216
  const wrapper = screen.ownerDocument.createElement("div");
1082
1217
  wrapper.className = "bs-scrim-overlay";
1083
1218
  const ws = wrapper.style;
1084
- ws.position = "absolute";
1085
- applyOverlayPosition(ws, this.mode, position, inset);
1219
+ ws.position = "fixed";
1220
+ const place = () => {
1221
+ resetOverlayPosition(ws);
1222
+ applyOverlayPosition(ws, this.mode, position, inset);
1223
+ };
1224
+ place();
1086
1225
  if (interactive) {
1087
1226
  ws.pointerEvents = "auto";
1088
1227
  }
@@ -1094,8 +1233,10 @@ class ScrimController {
1094
1233
  };
1095
1234
  }
1096
1235
  host.appendChild(wrapper);
1236
+ this.placeOverlay = place;
1097
1237
  const teardown = () => {
1098
1238
  if (wrapper.parentNode) wrapper.parentNode.removeChild(wrapper);
1239
+ if (this.placeOverlay === place) this.placeOverlay = null;
1099
1240
  if (this.scrimOverlayTeardown === teardown) {
1100
1241
  this.scrimOverlayTeardown = null;
1101
1242
  }
@@ -1119,6 +1260,7 @@ class ScrimController {
1119
1260
  if (this.backdrop) {
1120
1261
  this.backdrop.style.opacity = "";
1121
1262
  this.backdrop.style.pointerEvents = "";
1263
+ this.backdrop.style.removeProperty(BACKDROP_COLOR_VAR);
1122
1264
  }
1123
1265
  this.invalidateOpacityCache();
1124
1266
  this.lastBackdropPointer = "";
@@ -1143,13 +1285,13 @@ class ScrimController {
1143
1285
  }
1144
1286
  if (opts.interactive !== void 0) {
1145
1287
  this.scrimInteractive = opts.interactive;
1146
- s.pointerEvents = opts.interactive ? "auto" : "none";
1288
+ this.syncPointer();
1147
1289
  }
1148
1290
  }
1149
1291
  applyAboveSheetInset() {
1150
1292
  if (!this.screenComponent) return;
1151
1293
  const insetByMode = {
1152
- bottom: "0 0 var(--bs-size) 0",
1294
+ bottom: "0 0 calc(var(--bs-size) + var(--bs-keyboard-inset, 0px)) 0",
1153
1295
  top: "var(--bs-size) 0 0 0",
1154
1296
  left: "0 0 0 var(--bs-size)",
1155
1297
  right: "0 var(--bs-size) 0 0"
@@ -1157,7 +1299,7 @@ class ScrimController {
1157
1299
  const s = this.screenComponent.style;
1158
1300
  s.position = "fixed";
1159
1301
  s.inset = insetByMode[this.mode];
1160
- s.pointerEvents = this.scrimInteractive || this.scrimTapToCloseEnabled ? "auto" : "none";
1302
+ this.syncPointer();
1161
1303
  }
1162
1304
  }
1163
1305
  const easeOutBack = (t) => {
@@ -1168,14 +1310,21 @@ const easeOutBack = (t) => {
1168
1310
  const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
1169
1311
  const easeLinear = (t) => t;
1170
1312
  const easeOutQuint = (t) => 1 - Math.pow(1 - t, 5);
1313
+ const MIN_RETARGET_MS = 120;
1171
1314
  const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
1172
1315
  let cancelled = false;
1316
+ let finished = false;
1173
1317
  let rafId = 0;
1174
1318
  let resolveFn = null;
1175
- const start = performance.now();
1319
+ let start = performance.now();
1320
+ let origin = from;
1321
+ let target = to;
1322
+ let span = duration;
1323
+ let last = from;
1176
1324
  const promise = new Promise((resolve) => {
1177
1325
  resolveFn = resolve;
1178
1326
  if (duration <= 0) {
1327
+ finished = true;
1179
1328
  onUpdate(to);
1180
1329
  resolve();
1181
1330
  return;
@@ -1183,12 +1332,13 @@ const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
1183
1332
  const step = (now) => {
1184
1333
  if (cancelled) return;
1185
1334
  const elapsed = Math.max(0, now - start);
1186
- const t = Math.min(elapsed / duration, 1);
1187
- const value = from + (to - from) * easing(t);
1188
- onUpdate(value);
1335
+ const t = Math.min(elapsed / span, 1);
1336
+ last = origin + (target - origin) * easing(t);
1337
+ onUpdate(last);
1189
1338
  if (t < 1) {
1190
1339
  rafId = requestAnimationFrame(step);
1191
1340
  } else {
1341
+ finished = true;
1192
1342
  resolve();
1193
1343
  }
1194
1344
  };
@@ -1200,6 +1350,15 @@ const tween = ({ from, to, duration, easing = easeOutCubic, onUpdate }) => {
1200
1350
  cancelAnimationFrame(rafId);
1201
1351
  resolveFn?.();
1202
1352
  },
1353
+ retarget: (next) => {
1354
+ if (cancelled || finished || next === target) return;
1355
+ const now = performance.now();
1356
+ const remaining = span - Math.max(0, now - start);
1357
+ origin = last;
1358
+ target = next;
1359
+ start = now;
1360
+ span = Math.max(remaining, MIN_RETARGET_MS);
1361
+ },
1203
1362
  promise
1204
1363
  };
1205
1364
  };
@@ -1215,19 +1374,28 @@ const DEFAULT_SPRING = {
1215
1374
  restSpeed: 0.5
1216
1375
  };
1217
1376
  const MAX_DT = 1 / 30;
1218
- const runSpring = ({ from, to, velocity = 0, config, onUpdate }) => {
1377
+ const MAX_DURATION_MS = 3e3;
1378
+ const runSpring = ({ from, to, velocity = 0, config, maxDuration = MAX_DURATION_MS, onUpdate }) => {
1219
1379
  const cfg = {
1220
1380
  ...DEFAULT_SPRING,
1221
1381
  ...config
1222
1382
  };
1223
1383
  let cancelled = false;
1384
+ let finished = false;
1224
1385
  let rafId = 0;
1225
1386
  let resolveFn = null;
1226
1387
  let x = from;
1227
1388
  let v = velocity;
1389
+ let target = to;
1228
1390
  let lastTime = performance.now();
1391
+ let deadline = lastTime + maxDuration;
1229
1392
  const promise = new Promise((resolve) => {
1230
1393
  resolveFn = resolve;
1394
+ const land = () => {
1395
+ finished = true;
1396
+ onUpdate(target, 0);
1397
+ resolve();
1398
+ };
1231
1399
  const step = (now) => {
1232
1400
  if (cancelled) return;
1233
1401
  const dt = Math.min(Math.max(0, now - lastTime) / 1e3, MAX_DT);
@@ -1236,21 +1404,19 @@ const runSpring = ({ from, to, velocity = 0, config, onUpdate }) => {
1236
1404
  const subDt = dt / steps;
1237
1405
  let stepsLeft = steps;
1238
1406
  while (stepsLeft-- > 0) {
1239
- const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1407
+ const force = -cfg.stiffness * (x - target) - cfg.damping * v;
1240
1408
  const accel = force / cfg.mass;
1241
1409
  v += accel * subDt;
1242
1410
  x += v * subDt;
1243
1411
  }
1244
- if (!Number.isFinite(x) || !Number.isFinite(v)) {
1245
- onUpdate(to, 0);
1246
- resolve();
1412
+ if (!Number.isFinite(x) || !Number.isFinite(v) || lastTime >= deadline) {
1413
+ land();
1247
1414
  return;
1248
1415
  }
1249
1416
  onUpdate(x, v);
1250
- const isAtRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1417
+ const isAtRest = Math.abs(target - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1251
1418
  if (isAtRest) {
1252
- onUpdate(to, 0);
1253
- resolve();
1419
+ land();
1254
1420
  } else {
1255
1421
  rafId = requestAnimationFrame(step);
1256
1422
  }
@@ -1263,6 +1429,11 @@ const runSpring = ({ from, to, velocity = 0, config, onUpdate }) => {
1263
1429
  cancelAnimationFrame(rafId);
1264
1430
  resolveFn?.();
1265
1431
  },
1432
+ retarget: (next) => {
1433
+ if (cancelled || finished) return;
1434
+ target = next;
1435
+ deadline = performance.now() + maxDuration;
1436
+ },
1266
1437
  promise
1267
1438
  };
1268
1439
  };
@@ -1436,21 +1607,40 @@ class AnimationRunner {
1436
1607
  this.applySizeFn(size);
1437
1608
  }
1438
1609
  cancel() {
1439
- this.currentTween?.cancel();
1440
- this.currentSpring?.cancel();
1610
+ const tween2 = this.currentTween;
1611
+ const spring = this.currentSpring;
1612
+ this.currentTween = null;
1613
+ this.currentSpring = null;
1614
+ tween2?.cancel();
1615
+ spring?.cancel();
1441
1616
  this.cancelWaapi();
1442
1617
  }
1618
+ retarget(target) {
1619
+ if (this.currentSpring) {
1620
+ this.currentSpring.retarget(target);
1621
+ return true;
1622
+ }
1623
+ if (this.currentTween) {
1624
+ this.currentTween.retarget(target);
1625
+ return true;
1626
+ }
1627
+ if (this.currentWaapi) {
1628
+ this.currentWaapi.retarget(target);
1629
+ return true;
1630
+ }
1631
+ return false;
1632
+ }
1443
1633
  async animateTo(target, velocityPxPerMs) {
1444
1634
  if (target === this.getSize()) {
1445
- if (!this.isAnimating && !this.isDragging()) {
1635
+ if (this.isAnimating) this.cancel();
1636
+ if (!this.isDragging()) {
1446
1637
  this.applySizeFn(target);
1638
+ this.getRootEl()?.removeAttribute("data-animating");
1447
1639
  this.element.style.willChange = "";
1448
1640
  }
1449
1641
  return;
1450
1642
  }
1451
- this.currentTween?.cancel();
1452
- this.currentSpring?.cancel();
1453
- this.cancelWaapi();
1643
+ this.cancel();
1454
1644
  this.getRootEl()?.setAttribute("data-animating", "true");
1455
1645
  this.element.style.willChange = "transform";
1456
1646
  if (this.reducedMotion) {
@@ -1460,9 +1650,12 @@ class AnimationRunner {
1460
1650
  return;
1461
1651
  }
1462
1652
  if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1463
- const samples = this.animationKind === "spring" ? sampleSpringSettle(this.getSize(), target, velocityPxPerMs * VELOCITY_PX_PER_S, this.springConfig) : sampleTweenSettle(this.getSize(), target, this.duration, this.easing);
1653
+ const samples = this.sampleSettle(this.getSize(), target, velocityPxPerMs * VELOCITY_PX_PER_S);
1464
1654
  if (samples.values.length >= 2 && samples.durationMs > 0) {
1465
- await this.runWaapiSettle(samples, target);
1655
+ await this.runWaapiSettle({
1656
+ samples,
1657
+ target
1658
+ });
1466
1659
  if (!this.isAnimating) {
1467
1660
  this.getRootEl()?.removeAttribute("data-animating");
1468
1661
  if (!this.isDragging()) this.element.style.willChange = "";
@@ -1504,7 +1697,10 @@ class AnimationRunner {
1504
1697
  }
1505
1698
  }
1506
1699
  }
1507
- async runWaapiSettle(samples, target) {
1700
+ sampleSettle(from, to, velocityPxPerS) {
1701
+ return this.animationKind === "spring" ? sampleSpringSettle(from, to, velocityPxPerS, this.springConfig) : sampleTweenSettle(from, to, this.duration, this.easing);
1702
+ }
1703
+ startWaapiLeg(samples, onRetarget) {
1508
1704
  const applyAux = this.applyAux;
1509
1705
  const getTransformFor = this.getTransformFor;
1510
1706
  const frames = samples.values.map((v) => ({
@@ -1519,13 +1715,20 @@ class AnimationRunner {
1519
1715
  let stopped = false;
1520
1716
  let auxRaf = 0;
1521
1717
  const lastIdx = samples.values.length - 1;
1718
+ const cursor = () => Math.min((Number(anim.currentTime) || 0) / samples.stepMs, lastIdx);
1522
1719
  const sizeNow = () => {
1523
- const at = Math.min((Number(anim.currentTime) || 0) / samples.stepMs, lastIdx);
1720
+ const at = cursor();
1524
1721
  const idx = Math.floor(at);
1525
1722
  const from = samples.values[idx];
1526
1723
  const to = samples.values[Math.min(idx + 1, lastIdx)];
1527
1724
  return from + (to - from) * (at - idx);
1528
1725
  };
1726
+ const velocityNow = () => {
1727
+ const idx = Math.min(Math.floor(cursor()), Math.max(lastIdx - 1, 0));
1728
+ const from = samples.values[idx];
1729
+ const to = samples.values[Math.min(idx + 1, lastIdx)];
1730
+ return (to - from) / samples.stepMs * 1e3;
1731
+ };
1529
1732
  const auxTick = () => {
1530
1733
  if (stopped) return;
1531
1734
  applyAux(sizeNow());
@@ -1538,22 +1741,55 @@ class AnimationRunner {
1538
1741
  stopped = true;
1539
1742
  cancelAnimationFrame(auxRaf);
1540
1743
  },
1541
- sizeNow
1744
+ sizeNow,
1745
+ velocityNow,
1746
+ retarget: (to) => onRetarget(entry, to)
1542
1747
  };
1543
- this.currentWaapi = entry;
1544
- try {
1545
- await anim.finished;
1546
- } catch {
1547
- }
1548
- entry.stop();
1549
- const wasCurrent = this.currentWaapi === entry;
1550
- if (wasCurrent) this.currentWaapi = null;
1551
- if (wasCurrent && anim.playState === "finished") {
1552
- this.applySizeFn(target);
1748
+ return entry;
1749
+ }
1750
+ async runWaapiSettle(first) {
1751
+ let leg = first;
1752
+ for (; ; ) {
1753
+ let queued = null;
1754
+ const entry = this.startWaapiLeg(leg.samples, (running, to) => {
1755
+ const size = this.readShownSize?.() ?? running.sizeNow();
1756
+ queued = {
1757
+ samples: this.sampleSettle(size, to, running.velocityNow()),
1758
+ target: to
1759
+ };
1760
+ running.stop();
1761
+ try {
1762
+ running.anim.cancel();
1763
+ } catch {
1764
+ }
1765
+ this.applySizeFn(size);
1766
+ });
1767
+ this.currentWaapi = entry;
1553
1768
  try {
1554
- anim.cancel();
1769
+ await entry.anim.finished;
1555
1770
  } catch {
1556
1771
  }
1772
+ entry.stop();
1773
+ const wasCurrent = this.currentWaapi === entry;
1774
+ const next = queued;
1775
+ if (wasCurrent && next) {
1776
+ if (next.samples.values.length >= 2 && next.samples.durationMs > 0) {
1777
+ leg = next;
1778
+ continue;
1779
+ }
1780
+ this.currentWaapi = null;
1781
+ this.applySizeFn(next.target);
1782
+ return;
1783
+ }
1784
+ if (wasCurrent) this.currentWaapi = null;
1785
+ if (wasCurrent && entry.anim.playState === "finished") {
1786
+ this.applySizeFn(leg.target);
1787
+ try {
1788
+ entry.anim.cancel();
1789
+ } catch {
1790
+ }
1791
+ }
1792
+ return;
1557
1793
  }
1558
1794
  }
1559
1795
  destroy() {
@@ -1607,27 +1843,51 @@ const collectFocusables = (root) => {
1607
1843
  return out;
1608
1844
  };
1609
1845
  const focusables = (root) => collectFocusables(root).filter((el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']"));
1846
+ const findDeep = (root, selector) => {
1847
+ const direct = root.querySelector(selector);
1848
+ if (direct || typeof HTMLSlotElement === "undefined") return direct;
1849
+ for (const slot of Array.from(root.querySelectorAll("slot"))) {
1850
+ for (const assigned of slot.assignedElements()) {
1851
+ if (assigned.matches(selector)) return assigned;
1852
+ const inner = findDeep(assigned, selector);
1853
+ if (inner) return inner;
1854
+ }
1855
+ }
1856
+ return null;
1857
+ };
1858
+ const FOCUS_ALLOW_ATTR = "data-bs-focus-allow";
1859
+ const composedParent = (node) => {
1860
+ const slot = node.assignedSlot;
1861
+ if (slot) return slot;
1862
+ const parent = node.parentNode;
1863
+ return parent instanceof ShadowRoot ? parent.host : parent;
1864
+ };
1610
1865
  const containsComposed = (container, node) => {
1611
- let n = node;
1612
- while (n) {
1866
+ for (let n = node; n; n = composedParent(n)) {
1613
1867
  if (n === container) return true;
1614
- const slot = n.assignedSlot;
1615
- if (slot) {
1616
- n = slot;
1617
- continue;
1618
- }
1619
- const parent = n.parentNode;
1620
- n = parent instanceof ShadowRoot ? parent.host : parent;
1621
1868
  }
1622
1869
  return false;
1623
1870
  };
1624
- const trapStack = [];
1871
+ const isAllowedOutside = (node) => {
1872
+ for (let n = node; n; n = composedParent(n)) {
1873
+ if (n instanceof Element && n.hasAttribute(FOCUS_ALLOW_ATTR)) return true;
1874
+ }
1875
+ return false;
1876
+ };
1877
+ const deepActiveElement = () => {
1878
+ let active = document.activeElement;
1879
+ while (active?.shadowRoot?.activeElement) {
1880
+ active = active.shadowRoot.activeElement;
1881
+ }
1882
+ return active;
1883
+ };
1884
+ const trapStack = shared("focusTraps", () => []);
1625
1885
  const installFocusTrap = (container, options = {}) => {
1626
1886
  if (typeof document === "undefined") return () => {
1627
1887
  };
1628
1888
  const token = {};
1629
- trapStack.push(token);
1630
- const isActive = () => trapStack[trapStack.length - 1] === token;
1889
+ if (!options.isActive) trapStack.push(token);
1890
+ const isActive = options.isActive ?? (() => trapStack[trapStack.length - 1] === token);
1631
1891
  const previouslyFocused = document.activeElement;
1632
1892
  const focusInitial = () => {
1633
1893
  const spec = options.initialFocus;
@@ -1638,12 +1898,13 @@ const installFocusTrap = (container, options = {}) => {
1638
1898
  });
1639
1899
  return;
1640
1900
  }
1641
- const initial = typeof spec === "string" ? container.querySelector(spec) : spec ?? focusables(container)[0] ?? container;
1901
+ const initial = typeof spec === "string" ? findDeep(container, spec) : spec ?? focusables(container)[0] ?? container;
1642
1902
  initial?.focus({
1643
1903
  preventScroll: true
1644
1904
  });
1645
1905
  };
1646
- focusInitial();
1906
+ if (!containsComposed(container, deepActiveElement())) focusInitial();
1907
+ let backwards = false;
1647
1908
  const handleKey = (e) => {
1648
1909
  if (!isActive()) return;
1649
1910
  if (e.key === "Escape" && options.onEscape) {
@@ -1651,6 +1912,10 @@ const installFocusTrap = (container, options = {}) => {
1651
1912
  return;
1652
1913
  }
1653
1914
  if (e.key !== "Tab") return;
1915
+ backwards = e.shiftKey;
1916
+ setTimeout(() => {
1917
+ backwards = false;
1918
+ }, 0);
1654
1919
  const list = focusables(container);
1655
1920
  if (list.length === 0) {
1656
1921
  e.preventDefault();
@@ -1658,7 +1923,7 @@ const installFocusTrap = (container, options = {}) => {
1658
1923
  }
1659
1924
  const first = list[0];
1660
1925
  const last = list[list.length - 1];
1661
- const active = document.activeElement;
1926
+ const active = deepActiveElement();
1662
1927
  if (e.shiftKey && active === first) {
1663
1928
  e.preventDefault();
1664
1929
  last.focus();
@@ -1669,13 +1934,13 @@ const installFocusTrap = (container, options = {}) => {
1669
1934
  };
1670
1935
  const handleFocusIn = (e) => {
1671
1936
  if (!isActive()) return;
1672
- const target = e.target;
1673
- if (target && !containsComposed(container, target)) {
1674
- const list = focusables(container);
1675
- list[0]?.focus({
1676
- preventScroll: true
1677
- });
1678
- }
1937
+ const target = e.composedPath()[0] ?? e.target;
1938
+ if (!target || containsComposed(container, target)) return;
1939
+ if (isAllowedOutside(target)) return;
1940
+ const list = focusables(container);
1941
+ list[backwards ? list.length - 1 : 0]?.focus({
1942
+ preventScroll: true
1943
+ });
1679
1944
  };
1680
1945
  document.addEventListener("keydown", handleKey, true);
1681
1946
  document.addEventListener("focusin", handleFocusIn, true);
@@ -1699,21 +1964,25 @@ const installFocusTrap = (container, options = {}) => {
1699
1964
  }
1700
1965
  };
1701
1966
  };
1702
- let lockCount = 0;
1703
- let savedStyles = null;
1967
+ const state$1 = shared("scrollLock", () => ({
1968
+ count: 0,
1969
+ saved: null
1970
+ }));
1704
1971
  const lockBodyScroll = () => {
1705
1972
  if (typeof document === "undefined") return () => {
1706
1973
  };
1707
- if (lockCount === 0) {
1974
+ if (state$1.count === 0) {
1708
1975
  const body = document.body;
1709
- savedStyles = {
1976
+ const saved = {
1710
1977
  overflow: body.style.overflow,
1711
1978
  position: body.style.position,
1712
1979
  top: body.style.top,
1713
1980
  width: body.style.width,
1714
1981
  paddingRight: body.style.paddingRight,
1982
+ scrollX: window.scrollX,
1715
1983
  scrollY: window.scrollY
1716
1984
  };
1985
+ state$1.saved = saved;
1717
1986
  const scrollbarGap = window.innerWidth - document.documentElement.clientWidth;
1718
1987
  if (scrollbarGap > 0) {
1719
1988
  const current = parseFloat(getComputedStyle(body).paddingRight) || 0;
@@ -1721,56 +1990,76 @@ const lockBodyScroll = () => {
1721
1990
  }
1722
1991
  body.style.overflow = "hidden";
1723
1992
  body.style.position = "fixed";
1724
- body.style.top = `-${savedStyles.scrollY}px`;
1993
+ body.style.top = `-${saved.scrollY}px`;
1725
1994
  body.style.width = "100%";
1726
1995
  }
1727
- lockCount++;
1996
+ state$1.count++;
1728
1997
  let released = false;
1729
1998
  return () => {
1730
1999
  if (released) return;
1731
2000
  released = true;
1732
- lockCount = Math.max(0, lockCount - 1);
1733
- if (lockCount === 0 && savedStyles) {
2001
+ state$1.count = Math.max(0, state$1.count - 1);
2002
+ if (state$1.count === 0 && state$1.saved) {
1734
2003
  const body = document.body;
1735
- body.style.overflow = savedStyles.overflow;
1736
- body.style.position = savedStyles.position;
1737
- body.style.top = savedStyles.top;
1738
- body.style.width = savedStyles.width;
1739
- body.style.paddingRight = savedStyles.paddingRight;
1740
- window.scrollTo(0, savedStyles.scrollY);
1741
- savedStyles = null;
2004
+ const saved = state$1.saved;
2005
+ body.style.overflow = saved.overflow;
2006
+ body.style.position = saved.position;
2007
+ body.style.top = saved.top;
2008
+ body.style.width = saved.width;
2009
+ body.style.paddingRight = saved.paddingRight;
2010
+ window.scrollTo({
2011
+ top: saved.scrollY,
2012
+ left: saved.scrollX,
2013
+ behavior: "instant"
2014
+ });
2015
+ state$1.saved = null;
1742
2016
  }
1743
2017
  };
1744
2018
  };
2019
+ const OWN_ROOTS = ".bs-root, .ovl-root";
2020
+ const LAYER_CLASSES = [
2021
+ "bs-backdrop",
2022
+ "bs-screen",
2023
+ "bs-root",
2024
+ "ovl-backdrop",
2025
+ "ovl-root"
2026
+ ];
2027
+ const isLayer = (el) => LAYER_CLASSES.some((name) => el.classList.contains(name));
1745
2028
  function createInertSiblings(rootProvider) {
1746
2029
  const tracked = [];
2030
+ const remove = () => {
2031
+ for (const el of tracked) el.removeAttribute("inert");
2032
+ tracked.length = 0;
2033
+ };
1747
2034
  return {
1748
2035
  apply() {
1749
2036
  if (typeof document === "undefined") return;
1750
- let top = rootProvider();
1751
- while (top.parentElement && top.parentElement !== document.body) {
1752
- top = top.parentElement;
1753
- }
1754
- const parent = top.parentElement ?? document.body;
1755
- for (const child of Array.from(parent.children)) {
1756
- if (child === top) continue;
1757
- if (child.hasAttribute("inert")) continue;
1758
- if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
2037
+ remove();
2038
+ const surface = rootProvider();
2039
+ let node = surface.closest(OWN_ROOTS) ?? surface;
2040
+ while (node && node !== document.body) {
2041
+ const parent = node.parentElement;
2042
+ if (!parent) {
2043
+ const root = node.getRootNode();
2044
+ node = typeof ShadowRoot !== "undefined" && root instanceof ShadowRoot ? root.host : null;
1759
2045
  continue;
1760
2046
  }
1761
- child.setAttribute("inert", "");
1762
- tracked.push(child);
2047
+ for (const sibling of Array.from(parent.children)) {
2048
+ if (sibling === node) continue;
2049
+ if (sibling.hasAttribute("inert") || isLayer(sibling)) continue;
2050
+ sibling.setAttribute("inert", "");
2051
+ tracked.push(sibling);
2052
+ }
2053
+ node = parent;
1763
2054
  }
1764
2055
  },
1765
- remove() {
1766
- for (const el of tracked) el.removeAttribute("inert");
1767
- tracked.length = 0;
1768
- }
2056
+ remove
1769
2057
  };
1770
2058
  }
1771
2059
  class LifecycleController {
1772
2060
  constructor(deps, opts) {
1773
2061
  __publicField(this, "element");
2062
+ __publicField(this, "isTop");
1774
2063
  __publicField(this, "focusTrapEnabled");
1775
2064
  __publicField(this, "initialFocus");
1776
2065
  __publicField(this, "closeOnEscape");
@@ -1784,6 +2073,7 @@ class LifecycleController {
1784
2073
  __publicField(this, "installed", false);
1785
2074
  __publicField(this, "destroyed", false);
1786
2075
  this.element = deps.element;
2076
+ this.isTop = deps.isTop;
1787
2077
  this.focusTrapEnabled = opts.focusTrap ?? false;
1788
2078
  this.initialFocus = opts.initialFocus;
1789
2079
  this.closeOnEscape = opts.closeOnEscape ?? true;
@@ -1804,12 +2094,17 @@ class LifecycleController {
1804
2094
  }
1805
2095
  if (this.focusTrapEnabled && !this.releaseFocusTrap) {
1806
2096
  this.releaseFocusTrap = installFocusTrap(this.element, {
1807
- initialFocus: this.initialFocus
2097
+ initialFocus: this.initialFocus,
2098
+ isActive: this.isTop
1808
2099
  });
1809
2100
  }
1810
- if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
1811
- this.inertSiblings.apply();
1812
- }
2101
+ this.syncTop();
2102
+ }
2103
+ syncTop() {
2104
+ if (!this.installed || !this.inertSiblingsEnabled) return;
2105
+ if (!this.shouldApplyInertSiblings()) return;
2106
+ if (this.isTop?.() ?? true) this.inertSiblings.apply();
2107
+ else this.inertSiblings.remove();
1813
2108
  }
1814
2109
  setReturnFocus(target) {
1815
2110
  if (this.destroyed) return;
@@ -1980,10 +2275,14 @@ const installGestures = (handle, mode, callbacks, options = {}) => {
1980
2275
  if (wasPending) return;
1981
2276
  callbacks.onCancel?.();
1982
2277
  };
2278
+ const onLostCapture = (e) => {
2279
+ if (e.target === handle) onPointerCancel(e);
2280
+ };
1983
2281
  handle.addEventListener("pointerdown", onPointerDown);
1984
2282
  handle.addEventListener("pointermove", onPointerMove);
1985
2283
  handle.addEventListener("pointerup", finishGesture);
1986
2284
  handle.addEventListener("pointercancel", onPointerCancel);
2285
+ handle.addEventListener("lostpointercapture", onLostCapture);
1987
2286
  const managesTouchAction = options.manageTouchAction !== false;
1988
2287
  const prevTouchAction = handle.style.touchAction;
1989
2288
  if (managesTouchAction) {
@@ -1994,6 +2293,7 @@ const installGestures = (handle, mode, callbacks, options = {}) => {
1994
2293
  handle.removeEventListener("pointermove", onPointerMove);
1995
2294
  handle.removeEventListener("pointerup", finishGesture);
1996
2295
  handle.removeEventListener("pointercancel", onPointerCancel);
2296
+ handle.removeEventListener("lostpointercapture", onLostCapture);
1997
2297
  if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1998
2298
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1999
2299
  handle.releasePointerCapture(activePointerId);
@@ -2109,8 +2409,7 @@ class GestureController {
2109
2409
  size: this.deps.getDragContext().size,
2110
2410
  velocity: 0
2111
2411
  });
2112
- const restoreTo = this.dragStartSize;
2113
- void this.deps.animateTo(restoreTo, 0);
2412
+ this.deps.restoreAfterDrag();
2114
2413
  }
2115
2414
  }, this.deps.gestureOptions);
2116
2415
  return this.detach;
@@ -2169,6 +2468,11 @@ const readDirection = (el) => {
2169
2468
  }
2170
2469
  return "ltr";
2171
2470
  };
2471
+ const guessPhysicalMode = (mode) => {
2472
+ if (mode === "start") return "left";
2473
+ if (mode === "end") return "right";
2474
+ return mode ?? "bottom";
2475
+ };
2172
2476
  const resolveMode = (mode, el) => {
2173
2477
  if (!isLogicalMode(mode)) return mode;
2174
2478
  const rtl = readDirection(el) === "rtl";
@@ -2215,22 +2519,24 @@ function createScrollCache(deps) {
2215
2519
  return size < max / 2;
2216
2520
  };
2217
2521
  return {
2218
- cache(fromId, prevSize, nextSize) {
2522
+ leave(fromId, prevSize, nextSize) {
2219
2523
  const container = deps.scrollContainer;
2220
2524
  if (!container) return;
2221
- if (isSmallSize(prevSize)) return;
2222
- if (!isSmallSize(nextSize)) return;
2525
+ if (nextSize <= 0) {
2526
+ positions.clear();
2527
+ return;
2528
+ }
2529
+ if (isSmallSize(prevSize) || !isSmallSize(nextSize)) return;
2223
2530
  positions.set(fromId, container.scrollTop);
2224
2531
  },
2225
- restore(toId, prevSize, nextSize) {
2532
+ enter(toId, prevSize, nextSize) {
2226
2533
  const container = deps.scrollContainer;
2227
2534
  if (!container) return;
2228
- if (isSmallSize(nextSize)) return;
2229
- if (!isSmallSize(prevSize)) return;
2535
+ if (isSmallSize(nextSize) || !isSmallSize(prevSize)) return;
2230
2536
  const cached = positions.get(toId);
2231
2537
  if (cached === void 0) return;
2232
- container.scrollTop = cached;
2233
2538
  positions.delete(toId);
2539
+ container.scrollTop = cached;
2234
2540
  },
2235
2541
  clear() {
2236
2542
  positions.clear();
@@ -2400,17 +2706,18 @@ function attachAnchor(deps, opts) {
2400
2706
  const isDock = position === "dock-bottom" || position === "dock-top";
2401
2707
  const ws = wrapper.style;
2402
2708
  ws.position = "fixed";
2403
- if (isDock) {
2709
+ const place = () => {
2710
+ resetOverlayPosition(ws);
2711
+ if (!isDock) {
2712
+ applyOverlayPosition(ws, deps.getMode(), position, opts.inset ?? "16px");
2713
+ return;
2714
+ }
2404
2715
  ws.left = "0";
2405
2716
  ws.right = "0";
2406
- if (position === "dock-bottom") {
2407
- ws.bottom = "0";
2408
- } else {
2409
- ws.top = "0";
2410
- }
2411
- } else {
2412
- applyOverlayPosition(ws, deps.mode, position, opts.inset ?? "16px");
2413
- }
2717
+ if (position === "dock-bottom") ws.bottom = "0";
2718
+ else ws.top = "0";
2719
+ };
2720
+ place();
2414
2721
  if (opts.fadeRange) {
2415
2722
  const [r0, r1] = opts.fadeRange;
2416
2723
  const span = Math.max(r1 - r0, 1e-4);
@@ -2420,10 +2727,10 @@ function attachAnchor(deps, opts) {
2420
2727
  deps.host.appendChild(wrapper);
2421
2728
  let visible = null;
2422
2729
  let inFlight = null;
2423
- const shouldShow = (state) => {
2424
- if (typeof opts.showOn === "function") return opts.showOn(state);
2425
- if (Array.isArray(opts.showOn)) return opts.showOn.includes(state.activeId);
2426
- return isDock || state.size > 0;
2730
+ const shouldShow = (state2) => {
2731
+ if (typeof opts.showOn === "function") return opts.showOn(state2);
2732
+ if (Array.isArray(opts.showOn)) return opts.showOn.includes(state2.activeId);
2733
+ return isDock || state2.size > 0;
2427
2734
  };
2428
2735
  const applyVisibility = (next, animate) => {
2429
2736
  if (next === visible) return;
@@ -2470,7 +2777,8 @@ function attachAnchor(deps, opts) {
2470
2777
  },
2471
2778
  syncZ: (z) => {
2472
2779
  wrapper.style.zIndex = String(z);
2473
- }
2780
+ },
2781
+ reposition: place
2474
2782
  };
2475
2783
  }
2476
2784
  const matchesId = (def, activeId) => {
@@ -2484,12 +2792,11 @@ const matchesRange = (def, progress) => {
2484
2792
  };
2485
2793
  function installScrimStages(deps, opts) {
2486
2794
  const doc = deps.host.ownerDocument;
2487
- const entries2 = opts.stages.map((def) => {
2795
+ const entries = opts.stages.map((def) => {
2488
2796
  const wrapper = doc.createElement("div");
2489
2797
  wrapper.className = "bs-scrim-stage";
2490
2798
  const ws = wrapper.style;
2491
- ws.position = "absolute";
2492
- applyOverlayPosition(ws, deps.mode, def.position ?? opts.position ?? "center", def.inset ?? opts.inset ?? "16px");
2799
+ ws.position = "fixed";
2493
2800
  ws.visibility = "hidden";
2494
2801
  ws.pointerEvents = "none";
2495
2802
  wrapper.appendChild(def.element);
@@ -2500,13 +2807,21 @@ function installScrimStages(deps, opts) {
2500
2807
  inFlight: null
2501
2808
  };
2502
2809
  });
2810
+ const place = () => {
2811
+ for (const entry of entries) {
2812
+ const ws = entry.wrapper.style;
2813
+ resetOverlayPosition(ws);
2814
+ applyOverlayPosition(ws, deps.getMode(), entry.def.position ?? opts.position ?? "center", entry.def.inset ?? opts.inset ?? "16px");
2815
+ }
2816
+ };
2817
+ place();
2503
2818
  let active = null;
2504
2819
  const resolveActive = () => {
2505
- const state = deps.getState();
2506
- if (state.size === 0) {
2507
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2820
+ const state2 = deps.getState();
2821
+ if (state2.size === 0) {
2822
+ return entries.find((e) => matchesId(e.def, state2.activeId)) ?? null;
2508
2823
  }
2509
- return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2824
+ return entries.find((e) => matchesId(e.def, state2.activeId)) ?? entries.find((e) => matchesRange(e.def, state2.progress)) ?? null;
2510
2825
  };
2511
2826
  const hide = (entry, animate) => {
2512
2827
  entry.inFlight?.cancel();
@@ -2547,20 +2862,23 @@ function installScrimStages(deps, opts) {
2547
2862
  deps.on("open", () => update(true)),
2548
2863
  deps.on("close", () => update(true))
2549
2864
  ];
2550
- if (entries2.some((e) => e.def.forRange)) {
2865
+ if (entries.some((e) => e.def.forRange)) {
2551
2866
  offs.push(deps.on("progress", () => update(true)));
2552
2867
  }
2553
2868
  update(false);
2554
- return () => {
2555
- offs.forEach((off) => off());
2556
- for (const entry of entries2) {
2557
- entry.inFlight?.cancel();
2558
- entry.inFlight = null;
2559
- if (entry.wrapper.parentNode) {
2560
- entry.wrapper.parentNode.removeChild(entry.wrapper);
2869
+ return {
2870
+ reposition: place,
2871
+ detach: () => {
2872
+ offs.forEach((off) => off());
2873
+ for (const entry of entries) {
2874
+ entry.inFlight?.cancel();
2875
+ entry.inFlight = null;
2876
+ if (entry.wrapper.parentNode) {
2877
+ entry.wrapper.parentNode.removeChild(entry.wrapper);
2878
+ }
2561
2879
  }
2880
+ active = null;
2562
2881
  }
2563
- active = null;
2564
2882
  };
2565
2883
  }
2566
2884
  const DEFAULT_FLICK_VELOCITY = 0.65;
@@ -2594,11 +2912,13 @@ function resolveEngineOptions(opts, extras) {
2594
2912
  scrimMode: opts.scrimMode,
2595
2913
  scrimColor: opts.scrimColor,
2596
2914
  scrimBlur: opts.scrimBlur,
2597
- scrimInteractive: opts.scrimInteractive ?? (opts.scrimTapToClose ? true : void 0),
2915
+ scrimInteractive: opts.scrimInteractive,
2598
2916
  scrimTapToClose: opts.scrimTapToClose,
2599
2917
  scrimPreset: opts.scrimPreset,
2600
2918
  screenRange: opts.screenRange,
2601
- backdropRange: opts.backdropRange
2919
+ backdropRange: opts.backdropRange,
2920
+ backdropOpacity: opts.backdropOpacity,
2921
+ backdropColor: opts.backdropColor
2602
2922
  },
2603
2923
  animation: {
2604
2924
  animation: opts.animation,
@@ -2673,6 +2993,7 @@ class BottomSheetCore {
2673
2993
  __publicField(this, "fitContentToSnap");
2674
2994
  __publicField(this, "settleAnimation");
2675
2995
  __publicField(this, "persistent");
2996
+ __publicField(this, "keyboardInset", 0);
2676
2997
  __publicField(this, "disableCloseFlag");
2677
2998
  __publicField(this, "disableDragFlag");
2678
2999
  __publicField(this, "closeOnRouteChange");
@@ -2687,6 +3008,7 @@ class BottomSheetCore {
2687
3008
  __publicField(this, "detachSheetGesture", null);
2688
3009
  __publicField(this, "detachSliderKeyboard", null);
2689
3010
  __publicField(this, "rootEl", null);
3011
+ __publicField(this, "homeRoot", null);
2690
3012
  __publicField(this, "destroyed", false);
2691
3013
  __publicField(this, "restClosed", false);
2692
3014
  __publicField(this, "currentViewTransition", null);
@@ -2694,8 +3016,13 @@ class BottomSheetCore {
2694
3016
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
2695
3017
  __publicField(this, "progressWriteSentinel", new WriteSentinel());
2696
3018
  __publicField(this, "isTopSheet", true);
2697
- __publicField(this, "opening", false);
2698
- __publicField(this, "promotedForOpen", false);
3019
+ __publicField(this, "phase", "closed");
3020
+ __publicField(this, "transition", null);
3021
+ __publicField(this, "inFlight", null);
3022
+ __publicField(this, "outbox", []);
3023
+ __publicField(this, "flushing", false);
3024
+ __publicField(this, "openPending", false);
3025
+ __publicField(this, "settleHolds", []);
2699
3026
  __publicField(this, "allowOvershoot", false);
2700
3027
  __publicField(this, "progressPayload", {
2701
3028
  value: 0,
@@ -2719,6 +3046,7 @@ class BottomSheetCore {
2719
3046
  __publicField(this, "anchorHost", null);
2720
3047
  __publicField(this, "stackZ", 100);
2721
3048
  __publicField(this, "detachScrimStages", null);
3049
+ __publicField(this, "placeScrimStages", null);
2722
3050
  __publicField(this, "stackEffectEnabled", false);
2723
3051
  __publicField(this, "stackEffectPrimed", false);
2724
3052
  __publicField(this, "fitDeps");
@@ -2726,11 +3054,7 @@ class BottomSheetCore {
2726
3054
  __publicField(this, "featureList", []);
2727
3055
  __publicField(this, "featureCtx");
2728
3056
  const resolved = resolveEngineOptions(opts, {
2729
- shouldApplyInertSiblings: () => {
2730
- if (typeof document === "undefined") return false;
2731
- const bodyChildren = Array.from(document.body.children);
2732
- return bodyChildren.some((c) => c === opts.element || c.contains(opts.element));
2733
- }
3057
+ shouldApplyInertSiblings: () => opts.element.isConnected
2734
3058
  });
2735
3059
  this.element = opts.element;
2736
3060
  this.handle = opts.handle ?? opts.element;
@@ -2758,7 +3082,6 @@ class BottomSheetCore {
2758
3082
  void this.snapTo(id);
2759
3083
  },
2760
3084
  close: () => {
2761
- if (!this.canDismiss()) return;
2762
3085
  void this.close("backdrop");
2763
3086
  }
2764
3087
  }, resolved.scrim);
@@ -2782,7 +3105,8 @@ class BottomSheetCore {
2782
3105
  }, resolved.animation);
2783
3106
  this.lifecycle = new LifecycleController({
2784
3107
  element: this.element,
2785
- close: () => this.close()
3108
+ close: () => this.close(),
3109
+ isTop: () => this.isTopSheet
2786
3110
  }, resolved.lifecycle);
2787
3111
  this.closeOnBack = resolved.closeOnBack;
2788
3112
  this.routedTo = opts.routedTo;
@@ -2807,6 +3131,7 @@ class BottomSheetCore {
2807
3131
  auditVhUsage(this.snapPointsRaw);
2808
3132
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2809
3133
  this.rootEl = this.element.closest(".bs-root");
3134
+ this.homeRoot = homeRootOf(this.rootEl);
2810
3135
  this.maxHeightDeps = {
2811
3136
  element: this.element,
2812
3137
  mode: this.mode,
@@ -2828,7 +3153,9 @@ class BottomSheetCore {
2828
3153
  });
2829
3154
  const initial = this.snaps.findById(this.activeId);
2830
3155
  if (initial) this.size = initial.size;
3156
+ this.phase = this.size > 0 ? "open" : "closed";
2831
3157
  this.applySize(this.size);
3158
+ this.restClosed = this.element.getAttribute("data-bs-rest") === "closed";
2832
3159
  this.setRestClosed(this.size === 0);
2833
3160
  this.updateAriaSlider();
2834
3161
  this.scrollCache = createScrollCache({
@@ -2839,12 +3166,11 @@ class BottomSheetCore {
2839
3166
  this.attach();
2840
3167
  this.registerInStack();
2841
3168
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2842
- const io = new IntersectionObserver((entries2) => {
2843
- for (const entry of entries2) {
3169
+ const io = new IntersectionObserver((entries) => {
3170
+ for (const entry of entries) {
2844
3171
  if (!entry.isIntersecting) continue;
2845
3172
  if (this.destroyed) return;
2846
- this.snaps.recompute();
2847
- this.healActiveSnap();
3173
+ this.recompute();
2848
3174
  io.disconnect();
2849
3175
  return;
2850
3176
  }
@@ -2853,6 +3179,7 @@ class BottomSheetCore {
2853
3179
  this.teardowns.add(() => io.disconnect());
2854
3180
  }
2855
3181
  this.teardowns.add(installFitObserver({
3182
+ element: this.element,
2856
3183
  handle: this.handle,
2857
3184
  scrollContainer: this.scrollContainer,
2858
3185
  hasFitSnap: () => this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"),
@@ -2865,9 +3192,7 @@ class BottomSheetCore {
2865
3192
  this.applyAutoAriaLabelledBy();
2866
3193
  this.runFeatureStage("post");
2867
3194
  this.warnOrphanFeatureOptions();
2868
- if (this.size > 0) {
2869
- this.handleOpen();
2870
- }
3195
+ if (this.phase === "open") this.installLifecycle(true);
2871
3196
  }
2872
3197
  buildFeatureContext(autoCollapseAfter) {
2873
3198
  return {
@@ -2895,21 +3220,27 @@ class BottomSheetCore {
2895
3220
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2896
3221
  resolveSnap: (id) => this.snaps.findById(id),
2897
3222
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3223
+ getPhase: () => this.phase,
2898
3224
  setSize: (size) => {
2899
3225
  this.size = size;
2900
3226
  },
2901
3227
  setMaxAxisSize: (size) => {
2902
3228
  this.snaps.setMaxAxisSize(size);
2903
3229
  },
3230
+ setKeyboardInset: (px) => this.setKeyboardInset(px),
2904
3231
  applySize: (size) => this.applySize(size),
2905
3232
  recomputeSnaps: () => this.recomputeSnaps(),
3233
+ reseat: () => this.reseat(),
3234
+ holdSettle: (settled) => {
3235
+ this.settleHolds.push(settled);
3236
+ },
2906
3237
  newCycle: () => {
2907
3238
  this.newCycle();
2908
3239
  },
2909
- cancelInFlight: () => {
2910
- this.animation.cancel();
3240
+ cancelInFlight: () => this.suspend(),
3241
+ resyncAfterResize: () => {
3242
+ if (!this.destroyed) this.land(false);
2911
3243
  },
2912
- resyncAfterResize: () => this.resyncAfterResize(),
2913
3244
  snapTo: (id) => {
2914
3245
  void this.snapTo(id);
2915
3246
  },
@@ -2942,8 +3273,9 @@ class BottomSheetCore {
2942
3273
  return {
2943
3274
  size: this.size,
2944
3275
  activeId: this.activeId,
3276
+ phase: this.phase,
2945
3277
  isDragging: this.isDraggingAny(),
2946
- isAnimating: this.animation.isAnimating,
3278
+ isAnimating: this.animation.isAnimating || this.currentViewTransition !== null,
2947
3279
  progress: this.computeProgress(this.size)
2948
3280
  };
2949
3281
  }
@@ -2995,53 +3327,49 @@ class BottomSheetCore {
2995
3327
  getResolvedSnaps() {
2996
3328
  return this.snaps.getResolvedSnaps();
2997
3329
  }
2998
- async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2999
- if (this.destroyed) return;
3330
+ snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
3000
3331
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : {
3001
3332
  velocity: velocityOrOpts
3002
3333
  };
3003
- const velocityPxPerMs = opts.velocity ?? 0;
3004
- const externalSignal = opts.signal;
3005
- if (externalSignal?.aborted) return;
3334
+ return this.transitionTo(id, opts.velocity ?? 0, _skipBeforeSnap, opts.signal);
3335
+ }
3336
+ transitionTo(id, velocityPxPerMs = 0, skipBeforeSnap = false, externalSignal) {
3337
+ if (this.destroyed || externalSignal?.aborted) return Promise.resolve();
3006
3338
  const target = this.snaps.findById(id);
3007
3339
  if (!target) {
3008
3340
  devWarn(`[BottomSheet] unknown snap id: ${id}`);
3009
- return;
3341
+ return Promise.resolve();
3010
3342
  }
3011
3343
  if (!this.snaps.getAllowedIds().includes(id)) {
3012
3344
  devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
3013
- return;
3345
+ return Promise.resolve();
3014
3346
  }
3015
- if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
3016
- return;
3347
+ if (target.size === 0 && this.closingBlocked()) return Promise.resolve();
3348
+ if (!externalSignal && this.inFlight?.id === id) return this.inFlight.done;
3349
+ if (!skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
3350
+ return Promise.resolve();
3017
3351
  }
3018
- const resumingOpen = this.opening;
3019
- const signal = this.newCycle();
3020
- const onExternalAbort = () => {
3021
- this.animation.cancel();
3022
- this.newCycle();
3352
+ const flight = {
3353
+ id,
3354
+ done: Promise.resolve()
3023
3355
  };
3356
+ flight.done = this.runTransition(target, velocityPxPerMs, false, externalSignal, flight);
3357
+ return flight.done;
3358
+ }
3359
+ async runTransition(target, velocityPxPerMs, fromDrag, externalSignal, flight = null) {
3360
+ const signal = this.beginTransition(target);
3361
+ const transition = this.transition;
3362
+ if (signal.aborted || !transition) return;
3363
+ this.inFlight = flight;
3364
+ const onExternalAbort = () => this.suspend();
3024
3365
  externalSignal?.addEventListener("abort", onExternalAbort, {
3025
3366
  once: true
3026
3367
  });
3027
- const wasClosed = this.size === 0 || resumingOpen;
3028
- const previousId = this.activeId;
3029
- const previousSize = this.size;
3030
- this.scrollCache.cache(previousId, previousSize, target.size);
3031
- this.activeId = id;
3032
- const rawTargetSize = this.snapPointsRaw.find((p) => p.id === id)?.size;
3033
- const opensToRest = id === "closed" || rawTargetSize === 0;
3034
- if (wasClosed && !opensToRest) {
3035
- this.opening = true;
3036
- if (!resumingOpen) {
3037
- this.promotedForOpen = true;
3038
- sheetStack.promote(this.id);
3039
- }
3040
- }
3041
- if (this.animation.viewTransitionsAvailable) {
3368
+ this.allowOvershoot = fromDrag;
3369
+ if (!fromDrag && this.animation.viewTransitionsAvailable) {
3042
3370
  this.currentViewTransition?.skipTransition?.();
3043
3371
  const vt = document.startViewTransition(() => {
3044
- this.applySize(target.size);
3372
+ if (!signal.aborted) this.applySize(transition.size);
3045
3373
  });
3046
3374
  vt.ready?.catch(() => {
3047
3375
  });
@@ -3052,25 +3380,208 @@ class BottomSheetCore {
3052
3380
  }
3053
3381
  if (this.currentViewTransition === vt) this.currentViewTransition = null;
3054
3382
  } else {
3055
- await this.animation.animateTo(target.size, velocityPxPerMs);
3383
+ await this.animation.animateTo(transition.size, velocityPxPerMs);
3056
3384
  }
3057
3385
  externalSignal?.removeEventListener("abort", onExternalAbort);
3058
3386
  if (signal.aborted) return;
3059
- this.opening = false;
3060
- this.completeSnap(id, target.size, previousSize, wasClosed, false);
3387
+ if (this.size !== transition.size) this.applySize(transition.size);
3388
+ this.land(fromDrag);
3389
+ if (this.settleHolds.length > 0) {
3390
+ await Promise.allSettled(this.settleHolds.splice(0));
3391
+ }
3392
+ }
3393
+ beginTransition(target) {
3394
+ const signal = this.newCycle();
3395
+ const cap = this.snaps.getMaxAxisSize();
3396
+ const fromId = this.activeId;
3397
+ const fromSize = this.snaps.findById(fromId)?.size ?? this.size;
3398
+ this.scrollCache.leave(fromId, fromSize, target.size);
3399
+ this.scrollCache.enter(target.id, fromSize, target.size);
3400
+ this.activeId = target.id;
3401
+ this.transition = {
3402
+ size: cap > 0 ? Math.min(target.size, cap) : target.size,
3403
+ cap
3404
+ };
3405
+ if (target.size > 0) {
3406
+ this.enterOpening(target.id);
3407
+ } else {
3408
+ if (this.phase === "closed" && !this.isClosedSnap(target.id)) {
3409
+ sheetStack.promote(this.id);
3410
+ }
3411
+ this.enterClosing();
3412
+ }
3413
+ this.flush();
3414
+ return signal;
3415
+ }
3416
+ enterOpening(id) {
3417
+ if (this.phase === "open" || this.phase === "opening") return;
3418
+ const fresh = this.phase === "closed";
3419
+ this.phase = "opening";
3420
+ this.setRestClosed(false);
3421
+ if (!this.installLifecycle(fresh)) return;
3422
+ this.post("open", {
3423
+ id
3424
+ });
3425
+ this.outbox.push(() => {
3426
+ if (this.phase !== "closed") {
3427
+ notifyLinkedSheets(this.linkedSheets, this);
3428
+ }
3429
+ });
3061
3430
  }
3062
- completeSnap(id, targetSize, previousSize, wasClosed, withHaptic) {
3063
- this.scrollCache.restore(id, previousSize, targetSize);
3431
+ enterClosing() {
3432
+ if (this.phase === "closed" || this.phase === "closing") return;
3433
+ this.phase = "closing";
3434
+ this.post("close", void 0);
3435
+ }
3436
+ settlePhase(heal) {
3437
+ if (this.size > 0) {
3438
+ this.enterOpening(this.activeId);
3439
+ if (this.phase === "opening") {
3440
+ this.phase = "open";
3441
+ this.post("opened", {
3442
+ id: this.activeId
3443
+ });
3444
+ }
3445
+ return;
3446
+ }
3447
+ if (heal && this.phase !== "closing") return;
3448
+ this.enterClosing();
3449
+ if (this.phase === "closing") {
3450
+ this.phase = "closed";
3451
+ this.setRestClosed(true);
3452
+ this.lifecycle.release();
3453
+ sheetStack.update();
3454
+ this.post("closed", void 0);
3455
+ }
3456
+ }
3457
+ land(haptic) {
3458
+ this.transition = null;
3459
+ this.inFlight = null;
3460
+ this.allowOvershoot = false;
3461
+ this.openPending = this.size === 0 && !this.isClosedSnap(this.activeId);
3064
3462
  this.updateAriaSlider();
3065
- this.emit("snap", {
3066
- id,
3463
+ this.setRestClosed(this.size === 0);
3464
+ this.post("snap", {
3465
+ id: this.activeId,
3067
3466
  size: this.size,
3068
3467
  progress: this.computeProgress(this.size)
3069
3468
  });
3070
- if (withHaptic) this.haptic();
3071
- this.setRestClosed(targetSize === 0);
3072
- if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
3073
- if (targetSize === 0) this.emitCloseSequence();
3469
+ this.settlePhase(false);
3470
+ if (haptic) this.haptic();
3471
+ this.flush();
3472
+ }
3473
+ suspend() {
3474
+ this.animation.cancel();
3475
+ this.currentViewTransition?.skipTransition?.();
3476
+ this.newCycle();
3477
+ }
3478
+ landNow() {
3479
+ const transition = this.transition;
3480
+ if (this.destroyed || !transition || this.isDraggingAny()) return;
3481
+ this.animation.cancel();
3482
+ this.currentViewTransition?.skipTransition?.();
3483
+ this.newCycle();
3484
+ this.applySize(transition.size);
3485
+ this.land(false);
3486
+ }
3487
+ reseat() {
3488
+ if (this.destroyed || this.isDraggingAny()) return;
3489
+ const active = this.snaps.findById(this.activeId);
3490
+ if (!active) return;
3491
+ const cap = this.snaps.getMaxAxisSize();
3492
+ const target = cap > 0 ? Math.min(active.size, cap) : active.size;
3493
+ this.updateAriaSlider();
3494
+ const transition = this.transition;
3495
+ if (transition) {
3496
+ if (Math.abs(target - transition.size) < SIZE_WRITE_EPSILON && Math.abs(cap - transition.cap) < SIZE_WRITE_EPSILON) {
3497
+ return;
3498
+ }
3499
+ transition.size = target;
3500
+ transition.cap = cap;
3501
+ this.applySize(this.animation.retarget(target) ? this.size : target);
3502
+ return;
3503
+ }
3504
+ if (this.openPending && target > 0 && this.phase === "closed") {
3505
+ void this.runTransition(active, 0, false);
3506
+ return;
3507
+ }
3508
+ this.applySize(target);
3509
+ this.settlePhase(true);
3510
+ this.flush();
3511
+ }
3512
+ installLifecycle(promote) {
3513
+ if (promote) sheetStack.promote(this.id);
3514
+ if (this.lifecycle.isInstalled) return true;
3515
+ try {
3516
+ this.lifecycle.install();
3517
+ return true;
3518
+ } catch (err) {
3519
+ this.abortOpen();
3520
+ queueMicrotask(() => {
3521
+ throw err;
3522
+ });
3523
+ return false;
3524
+ }
3525
+ }
3526
+ abortOpen() {
3527
+ this.animation.cancel();
3528
+ if (!this.destroyed) this.newCycle();
3529
+ this.phase = "closed";
3530
+ this.openPending = false;
3531
+ this.lifecycle.release();
3532
+ sheetStack.update();
3533
+ this.activeId = this.zeroSnapId() ?? this.activeId;
3534
+ this.applySize(0);
3535
+ this.setRestClosed(true);
3536
+ this.updateAriaSlider();
3537
+ }
3538
+ zeroSnapId() {
3539
+ return this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.snaps.getAllowedIds().find((a) => this.snaps.findById(a)?.size === 0);
3540
+ }
3541
+ isClosedSnap(id) {
3542
+ if (id === "closed") return true;
3543
+ return this.snapPointsRaw.find((p) => p.id === id)?.size === 0;
3544
+ }
3545
+ closingBlocked() {
3546
+ return this.disableCloseFlag && this.phase !== "closed";
3547
+ }
3548
+ setKeyboardInset(px) {
3549
+ if (px === this.keyboardInset) return;
3550
+ this.keyboardInset = px;
3551
+ for (const host of this.varHosts()) {
3552
+ host.style.setProperty("--bs-keyboard-inset", `${px}px`);
3553
+ }
3554
+ }
3555
+ varHosts() {
3556
+ const hosts = /* @__PURE__ */ new Set([
3557
+ this.element
3558
+ ]);
3559
+ for (const host of [
3560
+ this.anchorHost,
3561
+ this.scrimParent,
3562
+ this.rootEl,
3563
+ this.homeRoot
3564
+ ]) {
3565
+ if (host) hosts.add(host);
3566
+ }
3567
+ return hosts;
3568
+ }
3569
+ dismissBlocked() {
3570
+ return (this.disableCloseFlag || this.persistent) && this.phase !== "closed";
3571
+ }
3572
+ post(event, payload) {
3573
+ this.outbox.push(() => this.bus.emit(event, payload));
3574
+ }
3575
+ flush() {
3576
+ if (this.flushing) return;
3577
+ this.flushing = true;
3578
+ try {
3579
+ for (let next = this.outbox.shift(); next; next = this.outbox.shift()) {
3580
+ next();
3581
+ }
3582
+ } finally {
3583
+ this.flushing = false;
3584
+ }
3074
3585
  }
3075
3586
  setRestClosed(closed) {
3076
3587
  if (closed === this.restClosed) return;
@@ -3078,21 +3589,6 @@ class BottomSheetCore {
3078
3589
  if (closed) this.element.setAttribute("data-bs-rest", "closed");
3079
3590
  else this.element.removeAttribute("data-bs-rest");
3080
3591
  }
3081
- emitOpenSequence(id) {
3082
- this.emit("open", {
3083
- id
3084
- });
3085
- this.handleOpen();
3086
- notifyLinkedSheets(this.linkedSheets, this);
3087
- this.emit("opened", {
3088
- id
3089
- });
3090
- }
3091
- emitCloseSequence() {
3092
- this.emit("close", void 0);
3093
- this.handleClose();
3094
- this.emit("closed", void 0);
3095
- }
3096
3592
  async dragTo(targetSize, velocityOrOpts = 0) {
3097
3593
  if (this.destroyed) return;
3098
3594
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : {
@@ -3102,29 +3598,33 @@ class BottomSheetCore {
3102
3598
  const externalSignal = opts.signal;
3103
3599
  if (externalSignal?.aborted) return;
3104
3600
  this.newCycle();
3105
- const onExternalAbort = () => {
3106
- this.animation.cancel();
3107
- this.newCycle();
3108
- };
3601
+ const onExternalAbort = () => this.suspend();
3109
3602
  externalSignal?.addEventListener("abort", onExternalAbort, {
3110
3603
  once: true
3111
3604
  });
3112
3605
  const clamped = Math.max(0, Math.min(targetSize, this.snaps.getMaxAxisSize()));
3113
3606
  await this.animation.animateTo(clamped, velocityPxPerMs);
3114
3607
  externalSignal?.removeEventListener("abort", onExternalAbort);
3115
- if (this.destroyed || externalSignal?.aborted) return;
3116
3608
  }
3117
3609
  open(id) {
3118
3610
  if (this.destroyed) return Promise.resolve();
3611
+ if (!id && (this.phase === "open" || this.phase === "opening")) {
3612
+ return Promise.resolve();
3613
+ }
3119
3614
  const target = id ?? this.snaps.getAllowedIds().find((a) => (this.snaps.findById(a)?.size ?? 0) > 0) ?? this.activeId;
3120
3615
  return this.snapTo(target);
3121
3616
  }
3122
3617
  close(reason = "programmatic") {
3123
- if (this.destroyed) return Promise.resolve();
3124
- if (this.disableCloseFlag) return Promise.resolve();
3125
- if (this.size > 0 && this.emitBeforeClose(reason)) return Promise.resolve();
3126
- 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];
3127
- return this.snapTo(closedId ?? this.activeId);
3618
+ if (this.destroyed || this.disableCloseFlag) return Promise.resolve();
3619
+ if (this.persistent && reason !== "programmatic") return Promise.resolve();
3620
+ const target = this.zeroSnapId() ?? this.allowedIdsBySize()[0];
3621
+ if (target === void 0) return Promise.resolve();
3622
+ if (this.phase === "closed" && this.activeId === target) {
3623
+ return Promise.resolve();
3624
+ }
3625
+ const visible = this.phase === "open" || this.phase === "opening";
3626
+ if (visible && this.emitBeforeClose(reason)) return Promise.resolve();
3627
+ return this.snapTo(target);
3128
3628
  }
3129
3629
  canDismiss() {
3130
3630
  return !this.persistent && !this.disableCloseFlag && !this.destroyed;
@@ -3147,10 +3647,12 @@ class BottomSheetCore {
3147
3647
  }
3148
3648
  setDragFrom(mode) {
3149
3649
  if (this.destroyed || mode === this.dragFromMode) return;
3650
+ const wasDragging = this.gesture?.isDragging ?? false;
3150
3651
  this.gesture?.forceClearDragState();
3151
3652
  this.detachSheetGesture?.();
3152
3653
  this.dragFromMode = mode;
3153
3654
  this.mountSheetGesture();
3655
+ if (wasDragging) this.restoreAfterDrag();
3154
3656
  }
3155
3657
  getDragFrom() {
3156
3658
  return this.dragFromMode;
@@ -3163,6 +3665,7 @@ class BottomSheetCore {
3163
3665
  const nowVertical = next === "bottom" || next === "top";
3164
3666
  this.gesture?.forceClearDragState();
3165
3667
  this.detachSheetGesture?.();
3668
+ this.landNow();
3166
3669
  this.animation.cancel();
3167
3670
  this.element.style.removeProperty(layoutAxis(this.mode));
3168
3671
  this.mode = next;
@@ -3178,9 +3681,12 @@ class BottomSheetCore {
3178
3681
  this.detachSliderKeyboard?.();
3179
3682
  this.mountSliderKeyboard();
3180
3683
  this.mountSheetGesture();
3684
+ for (const anchor of this.anchors) anchor.reposition();
3685
+ this.placeScrimStages?.();
3686
+ if (this.stackEffectPrimed) {
3687
+ this.element.style.transformOrigin = nowVertical ? "50% 100%" : "0% 50%";
3688
+ }
3181
3689
  this.recompute();
3182
- const active = this.snaps.findById(this.activeId);
3183
- if (active) this.applySize(active.size);
3184
3690
  this.updateAriaSlider();
3185
3691
  }
3186
3692
  getMode() {
@@ -3222,13 +3728,22 @@ class BottomSheetCore {
3222
3728
  if (snap && ids.includes(snap)) {
3223
3729
  void this.snapTo(snap, 0, true);
3224
3730
  } else if (!ids.includes(this.activeId)) {
3225
- const fallback = ids[0];
3226
- if (fallback) void this.snapTo(fallback, 0, true);
3731
+ const fallback = this.fallbackSnap(this.snaps.findById(this.activeId)?.size ?? this.size);
3732
+ if (fallback) void this.snapTo(fallback.id, 0, true);
3227
3733
  }
3228
3734
  }
3735
+ fallbackSnap(from) {
3736
+ return (from > 0 ? this.snaps.nearest(from, true) : null) ?? this.snaps.nearest(from, false);
3737
+ }
3229
3738
  setBackdropRange(range) {
3230
3739
  this.scrim.setBackdropRange(range, () => this.applySize(this.size));
3231
3740
  }
3741
+ setBackdropOpacity(value) {
3742
+ this.scrim.setBackdropOpacity(value, () => this.applySize(this.size));
3743
+ }
3744
+ setBackdropColor(color) {
3745
+ this.scrim.setBackdropColor(color);
3746
+ }
3232
3747
  setScreenRange(range) {
3233
3748
  this.scrim.setScreenRange(range, () => this.applySize(this.size));
3234
3749
  }
@@ -3265,7 +3780,7 @@ class BottomSheetCore {
3265
3780
  this.anchorHost.style.setProperty("--bs-progress", String(this.computeProgress(this.size)));
3266
3781
  }
3267
3782
  const handle = attachAnchor({
3268
- mode: this.mode,
3783
+ getMode: () => this.mode,
3269
3784
  host: this.anchorHost,
3270
3785
  getState: () => ({
3271
3786
  activeId: this.activeId,
@@ -3299,8 +3814,8 @@ class BottomSheetCore {
3299
3814
  return () => {
3300
3815
  };
3301
3816
  }
3302
- const detachInstalled = installScrimStages({
3303
- mode: this.mode,
3817
+ const stages = installScrimStages({
3818
+ getMode: () => this.mode,
3304
3819
  host,
3305
3820
  getState: () => ({
3306
3821
  activeId: this.activeId,
@@ -3311,10 +3826,14 @@ class BottomSheetCore {
3311
3826
  isDestroyed: () => this.destroyed
3312
3827
  }, opts);
3313
3828
  const detach = () => {
3314
- if (this.detachScrimStages === detach) this.detachScrimStages = null;
3315
- detachInstalled();
3829
+ if (this.detachScrimStages === detach) {
3830
+ this.detachScrimStages = null;
3831
+ this.placeScrimStages = null;
3832
+ }
3833
+ stages.detach();
3316
3834
  };
3317
3835
  this.detachScrimStages = detach;
3836
+ this.placeScrimStages = stages.reposition;
3318
3837
  this.teardowns.add(() => this.detachScrimStages?.());
3319
3838
  return detach;
3320
3839
  }
@@ -3326,32 +3845,29 @@ class BottomSheetCore {
3326
3845
  }
3327
3846
  setSnapPoints(points, allowed) {
3328
3847
  if (this.destroyed) return;
3329
- this.animation.cancel();
3330
- this.newCycle();
3848
+ const from = this.snaps.findById(this.activeId)?.size ?? this.size;
3849
+ const restricted = this.snaps.getResolvedSnaps().some((s) => !this.snaps.getAllowedIds().includes(s.id));
3331
3850
  this.snapPointsRaw = points;
3332
3851
  auditVhUsage(points);
3333
3852
  this.auditDuplicateSnapIds(points);
3853
+ this.maxHeight.resolveCap();
3334
3854
  this.snaps.setRaw(points);
3855
+ this.maxHeight.clampTo();
3335
3856
  if (allowed) {
3336
3857
  this.snaps.setAllowedIds(allowed);
3337
3858
  } else {
3338
3859
  const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3339
- const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3860
+ const kept = restricted ? this.snaps.getAllowedIds().filter((id) => present.includes(id)) : [];
3340
3861
  this.snaps.setAllowedIds(kept.length ? kept : present);
3341
3862
  }
3342
3863
  this.scrollCache.clear();
3343
3864
  this.scrim.invalidateOpacityCache();
3344
- const current = this.snaps.findById(this.activeId);
3345
- if (current) {
3346
- this.applySize(current.size);
3347
- } else {
3348
- const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3349
- if (fallback) {
3350
- this.activeId = fallback.id;
3351
- this.applySize(fallback.size);
3352
- }
3865
+ if (this.snaps.findById(this.activeId)) {
3866
+ this.reseat();
3867
+ return;
3353
3868
  }
3354
- this.updateAriaSlider();
3869
+ const fallback = this.fallbackSnap(from) ?? this.snaps.getResolvedSnaps()[0];
3870
+ if (fallback) void this.transitionTo(fallback.id, 0, true);
3355
3871
  }
3356
3872
  recompute() {
3357
3873
  if (this.destroyed) return;
@@ -3359,22 +3875,11 @@ class BottomSheetCore {
3359
3875
  this.snaps.recompute();
3360
3876
  this.maxHeight.clampTo();
3361
3877
  this.scrim.invalidateOpacityCache();
3362
- if (this.isDraggingAny()) return;
3363
- this.healActiveSnap();
3364
- }
3365
- healActiveSnap() {
3366
- const current = this.snaps.findById(this.activeId);
3367
- if (!current) return;
3368
- this.size = current.size;
3369
- this.applySize(this.size);
3370
- this.updateAriaSlider();
3371
- if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3372
- this.emitOpenSequence(this.activeId);
3373
- }
3878
+ this.reseat();
3374
3879
  }
3375
3880
  destroy() {
3376
3881
  this.destroyed = true;
3377
- this.opening = false;
3882
+ this.outbox.length = 0;
3378
3883
  this.currentAbort.abort();
3379
3884
  this.currentViewTransition?.skipTransition?.();
3380
3885
  this.currentViewTransition = null;
@@ -3394,12 +3899,16 @@ class BottomSheetCore {
3394
3899
  for (const host of [
3395
3900
  this.anchorHost,
3396
3901
  this.scrimParent,
3397
- this.rootEl
3902
+ this.rootEl,
3903
+ this.homeRoot
3398
3904
  ]) {
3399
3905
  if (!host) continue;
3400
3906
  host.style.removeProperty("--bs-size");
3401
3907
  host.style.removeProperty("--bs-progress");
3908
+ host.style.removeProperty("--bs-z-sheet");
3909
+ host.style.removeProperty("--bs-keyboard-inset");
3402
3910
  }
3911
+ this.element.style.removeProperty("--bs-keyboard-inset");
3403
3912
  if (this.screenComponent) {
3404
3913
  this.screenComponent.style.opacity = "";
3405
3914
  this.screenComponent.style.display = "";
@@ -3412,7 +3921,8 @@ class BottomSheetCore {
3412
3921
  }
3413
3922
  newCycle() {
3414
3923
  this.allowOvershoot = false;
3415
- this.opening = false;
3924
+ this.transition = null;
3925
+ this.inFlight = null;
3416
3926
  this.currentAbort.abort();
3417
3927
  this.currentAbort = new AbortController();
3418
3928
  return this.currentAbort.signal;
@@ -3433,8 +3943,9 @@ class BottomSheetCore {
3433
3943
  if (typeof document === "undefined") return;
3434
3944
  if (!this.lifecycle.focusTrapEnabled) return;
3435
3945
  if (this.element.getAttribute("aria-labelledby")) return;
3436
- const scope = this.handle ?? this.element;
3437
- const titled = scope.querySelector("[data-bs-title]") ?? scope.querySelector("h1,h2,h3,h4,h5,h6");
3946
+ const HEADING = "h1,h2,h3,h4,h5,h6";
3947
+ const header = this.element.querySelector(".bs-header");
3948
+ const titled = this.element.querySelector("[data-bs-title]") ?? this.handle.querySelector(HEADING) ?? header?.querySelector(HEADING);
3438
3949
  if (!titled) return;
3439
3950
  if (!titled.id) {
3440
3951
  titled.id = nextInstanceId("bs-title");
@@ -3472,12 +3983,16 @@ class BottomSheetCore {
3472
3983
  this.stackZ = z;
3473
3984
  this.element.style.zIndex = String(z);
3474
3985
  if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
3986
+ this.rootEl?.style.setProperty("--bs-z-sheet", String(z));
3987
+ this.homeRoot?.style.setProperty("--bs-z-sheet", String(z));
3475
3988
  for (const anchor of this.anchors) anchor.syncZ(z + 1);
3476
3989
  },
3477
3990
  setIsTop: (isTop) => {
3991
+ if (isTop === this.isTopSheet) return;
3478
3992
  this.isTopSheet = isTop;
3993
+ this.lifecycle.syncTop();
3479
3994
  },
3480
- isOpen: () => this.size > 0 || this.opening,
3995
+ isOpen: () => this.phase !== "closed",
3481
3996
  setDepth: (depth) => this.applyStackDepth(depth)
3482
3997
  }));
3483
3998
  }
@@ -3505,15 +4020,15 @@ class BottomSheetCore {
3505
4020
  const r = this.getAllowedRange();
3506
4021
  buf.size = this.size;
3507
4022
  buf.maxAxisSize = this.snaps.getMaxAxisSize();
3508
- buf.range.min = r.min;
4023
+ buf.range.min = this.dismissBlocked() ? Math.max(r.min, this.smallestOpenSize()) : r.min;
3509
4024
  buf.range.max = r.max;
3510
4025
  buf.rubberBandEnabled = this.rubberBandEnabled;
3511
4026
  return buf;
3512
4027
  },
3513
4028
  getDisableDrag: () => this.disableDragFlag,
3514
- cancelAnimation: () => this.animation.cancel(),
4029
+ cancelAnimation: () => this.suspend(),
3515
4030
  applySize: (size) => this.applySize(size),
3516
- animateTo: (size, velocity) => this.animation.animateTo(size, velocity),
4031
+ restoreAfterDrag: () => this.restoreAfterDrag(),
3517
4032
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3518
4033
  emit: (event, payload) => this.emit(event, payload),
3519
4034
  listenerCount: (event) => this.bus.listenerCount(event)
@@ -3630,32 +4145,19 @@ class BottomSheetCore {
3630
4145
  element: this.element,
3631
4146
  getMode: () => this.mode,
3632
4147
  isDestroyed: () => this.destroyed,
3633
- isDragging: () => this.isDraggingAny(),
3634
4148
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3635
4149
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3636
- getSize: () => this.size,
3637
4150
  setMaxAxisSize: (size) => {
3638
4151
  this.snaps.setMaxAxisSize(size);
3639
4152
  },
3640
- setSize: (size) => {
3641
- this.size = size;
3642
- },
3643
4153
  recomputeSnaps: () => this.recomputeSnaps(),
3644
- applySize: (size) => this.applySize(size),
3645
- cancelInFlight: () => {
3646
- this.animation.cancel();
3647
- },
3648
- newCycle: () => {
3649
- this.newCycle();
3650
- },
3651
- isAnimating: () => this.animation.isAnimating,
3652
- resyncAfterCancel: () => this.resyncAfterResize()
4154
+ reseat: () => this.reseat(),
4155
+ landNow: () => this.landNow()
3653
4156
  }));
3654
4157
  if (this.lifecycle.closeOnEscape) {
3655
4158
  const onKey = (e) => {
3656
4159
  if (this.destroyed || e.defaultPrevented) return;
3657
- if (!this.canDismiss()) return;
3658
- if (e.key === "Escape" && this.isTopSheet && this.size > 0) {
4160
+ if (e.key === "Escape" && this.isTopSheet) {
3659
4161
  void this.close("escape");
3660
4162
  }
3661
4163
  };
@@ -3671,7 +4173,10 @@ class BottomSheetCore {
3671
4173
  handle: this.handle,
3672
4174
  mode: this.mode,
3673
4175
  isDestroyed: () => this.destroyed,
3674
- getAllowedIds: () => this.allowedIdsBySize(),
4176
+ getAllowedIds: () => {
4177
+ const ids = this.allowedIdsBySize();
4178
+ return this.dismissBlocked() ? ids.filter((id) => (this.snaps.findById(id)?.size ?? 0) > 0) : ids;
4179
+ },
3675
4180
  getActiveId: () => this.activeId,
3676
4181
  snapTo: (id) => {
3677
4182
  void this.snapTo(id);
@@ -3684,7 +4189,6 @@ class BottomSheetCore {
3684
4189
  });
3685
4190
  }
3686
4191
  settleAfterDrag(delta, velocity, kind = "touch") {
3687
- const signal = this.newCycle();
3688
4192
  const target = this.snaps.findDragSettleTarget({
3689
4193
  delta,
3690
4194
  velocity,
@@ -3693,41 +4197,35 @@ class BottomSheetCore {
3693
4197
  activeId: this.activeId,
3694
4198
  flickVelocity: this.flickVelocity,
3695
4199
  dragThreshold: this.dragThreshold
3696
- });
4200
+ }, this.dismissBlocked());
3697
4201
  if (!target) {
3698
4202
  this.clearWillChangeIfIdle();
3699
4203
  return;
3700
4204
  }
3701
4205
  const previousId = this.activeId;
3702
4206
  if (target.id === previousId && target.size === this.size) {
4207
+ if (this.phase === "opening" || this.phase === "closing") {
4208
+ this.land(false);
4209
+ }
3703
4210
  this.clearWillChangeIfIdle();
3704
4211
  return;
3705
4212
  }
3706
4213
  if (this.emitBeforeSnap(target, previousId)) {
3707
- const restore2 = this.snaps.findById(previousId);
3708
- if (restore2) {
3709
- void this.animation.animateTo(restore2.size, 0);
3710
- }
4214
+ this.restoreAfterDrag();
3711
4215
  return;
3712
4216
  }
3713
- const previousSize = this.size;
3714
- const previousSnapSize = this.snaps.findById(previousId)?.size;
3715
- this.scrollCache.cache(previousId, previousSize, target.size);
3716
- this.activeId = target.id;
3717
- if (previousSnapSize === 0 && target.size > 0) {
3718
- this.opening = true;
3719
- this.promotedForOpen = true;
3720
- sheetStack.promote(this.id);
3721
- }
3722
- const settleCap = this.snaps.getMaxAxisSize();
3723
- const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3724
- this.allowOvershoot = true;
3725
- void this.animation.animateTo(settleSize, velocity).then(() => {
3726
- if (signal.aborted) return;
3727
- this.allowOvershoot = false;
3728
- this.opening = false;
3729
- this.completeSnap(target.id, target.size, previousSize, previousSnapSize === 0, true);
3730
- });
4217
+ void this.runTransition(target, velocity, true);
4218
+ }
4219
+ restoreAfterDrag() {
4220
+ const active = this.snaps.findById(this.activeId);
4221
+ if (active) void this.runTransition(active, 0, false);
4222
+ }
4223
+ smallestOpenSize() {
4224
+ const allowed = this.snaps.getAllowedIds();
4225
+ for (const snap of this.snaps.getResolvedSnaps()) {
4226
+ if (snap.size > 0 && allowed.includes(snap.id)) return snap.size;
4227
+ }
4228
+ return 0;
3731
4229
  }
3732
4230
  isDraggingAny() {
3733
4231
  return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
@@ -3752,7 +4250,8 @@ class BottomSheetCore {
3752
4250
  const offset = cap - clamped;
3753
4251
  const style = this.element.style;
3754
4252
  if (!skipTransform) style.transform = this.transformTemplate(offset);
3755
- const sizeChanged = this.sizeWriteSentinel.shouldWrite(clamped, this.animation.isAnimating || this.isDraggingAny() ? SIZE_WRITE_EPSILON : 0);
4253
+ const moving = this.animation.isAnimating || this.isDraggingAny();
4254
+ const sizeChanged = this.sizeWriteSentinel.shouldWrite(clamped, moving ? SIZE_WRITE_EPSILON : 0);
3756
4255
  if (sizeChanged) {
3757
4256
  style.setProperty("--bs-size", `${clamped}px`);
3758
4257
  if (this.scrimParent) {
@@ -3764,9 +4263,11 @@ class BottomSheetCore {
3764
4263
  if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3765
4264
  this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3766
4265
  }
4266
+ this.homeRoot?.style.setProperty("--bs-size", `${clamped}px`);
3767
4267
  }
3768
4268
  const progress = this.computeProgress(clamped);
3769
- const progressChanged = this.progressWriteSentinel.shouldWrite(progress, OPACITY_WRITE_EPSILON);
4269
+ const opacityEpsilon = moving ? OPACITY_WRITE_EPSILON : 0;
4270
+ const progressChanged = this.progressWriteSentinel.shouldWrite(progress, opacityEpsilon);
3770
4271
  if (progressChanged) {
3771
4272
  style.setProperty("--bs-progress", String(progress));
3772
4273
  if (this.anchorHost) {
@@ -3775,8 +4276,9 @@ class BottomSheetCore {
3775
4276
  if (this.rootEl && this.rootEl !== this.anchorHost) {
3776
4277
  this.rootEl.style.setProperty("--bs-progress", String(progress));
3777
4278
  }
4279
+ this.homeRoot?.style.setProperty("--bs-progress", String(progress));
3778
4280
  }
3779
- this.scrim.applyOpacity(progress, progressChanged);
4281
+ this.scrim.applyOpacity(progress, progressChanged, opacityEpsilon);
3780
4282
  if ((progressChanged || sizeChanged) && this.bus.listenerCount("progress") > 0) {
3781
4283
  this.progressPayload.value = progress;
3782
4284
  this.progressPayload.size = clamped;
@@ -3789,236 +4291,328 @@ class BottomSheetCore {
3789
4291
  if (reachableMax <= min) return 0;
3790
4292
  return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3791
4293
  }
3792
- handleOpen() {
3793
- if (this.promotedForOpen) this.promotedForOpen = false;
3794
- else sheetStack.promote(this.id);
3795
- try {
3796
- this.lifecycle.install();
3797
- } catch (err) {
3798
- if (!this.destroyed) this.newCycle();
3799
- this.handleClose();
3800
- this.applySize(0);
3801
- this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3802
- this.size = 0;
3803
- queueMicrotask(() => {
3804
- throw err;
3805
- });
3806
- }
3807
- }
3808
- resyncAfterResize() {
3809
- if (this.destroyed) return;
3810
- const target = this.snaps.findById(this.activeId);
3811
- if (!target) return;
3812
- this.updateAriaSlider();
3813
- this.emit("snap", {
3814
- id: target.id,
3815
- size: this.size,
3816
- progress: this.computeProgress(this.size)
3817
- });
3818
- if (target.size > 0 && !this.lifecycle.isInstalled) {
3819
- this.emitOpenSequence(target.id);
3820
- } else if (target.size === 0 && this.lifecycle.isInstalled) {
3821
- this.emitCloseSequence();
3822
- }
3823
- }
3824
- handleClose() {
3825
- this.opening = false;
3826
- this.promotedForOpen = false;
3827
- this.lifecycle.release();
3828
- sheetStack.update();
3829
- }
3830
4294
  }
3831
- const entries = [];
3832
- const subscribers = /* @__PURE__ */ new Set();
3833
- let suppress = 0;
3834
- let internalDepth = 0;
3835
- const pendingRebrands = [];
3836
- let listenerInstalled = false;
3837
- let patchInstalled = false;
3838
- let origPushState = null;
3839
- let origReplaceState = null;
3840
- let patchedPushState = null;
3841
- let patchedReplaceState = null;
3842
- const hasWindow = () => typeof window !== "undefined";
3843
- const hasHistory = () => typeof history !== "undefined";
3844
- const brandMarker = (surface) => ({
4295
+ const MARKER = "__bs";
4296
+ const STACK = "__bsStack";
4297
+ const DEPTH = "__bsDepth";
4298
+ const ROUTED = "__bsRouted";
4299
+ const SETTLE_TIMEOUT_MS = 1e3;
4300
+ const state = shared("history", () => ({
4301
+ live: [],
4302
+ stackId: void 0,
4303
+ baseUrl: void 0,
4304
+ position: null,
4305
+ knownLength: 0,
4306
+ href: "",
4307
+ subscribers: /* @__PURE__ */ new Set(),
4308
+ internal: 0,
4309
+ pending: [],
4310
+ deferred: [],
4311
+ waiters: [],
4312
+ timer: null,
4313
+ listening: false,
4314
+ leftovers: false,
4315
+ orphanChecked: false,
4316
+ push: null,
4317
+ replace: null
4318
+ }));
4319
+ const hasHistory = () => typeof window !== "undefined" && typeof history !== "undefined";
4320
+ const plain = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
4321
+ const depthOf = (value) => {
4322
+ const depth = value?.[DEPTH];
4323
+ return typeof depth === "number" ? depth : null;
4324
+ };
4325
+ const isMarker = (value) => value?.[MARKER] === true;
4326
+ const inStack = (value) => value !== null && state.stackId !== void 0 && value[STACK] === state.stackId && depthOf(value) !== null;
4327
+ const foreignKeys = (value) => {
4328
+ const out = {};
4329
+ const source = plain(value);
4330
+ if (!source) return out;
4331
+ for (const key of Object.keys(source)) {
4332
+ if (!key.startsWith(MARKER)) out[key] = source[key];
4333
+ }
4334
+ return out;
4335
+ };
4336
+ const markerFor = (surface, depth) => ({
4337
+ ...foreignKeys(history.state),
3845
4338
  ...surface.markerState(),
3846
- __bs: true
4339
+ [MARKER]: true,
4340
+ [STACK]: state.stackId,
4341
+ [DEPTH]: depth
3847
4342
  });
4343
+ const urlForDepth = (depth) => {
4344
+ for (let i = depth - 1; i >= 0; i -= 1) {
4345
+ const url = state.live[i]?.surface.url;
4346
+ if (url !== void 0) return url;
4347
+ }
4348
+ return state.baseUrl;
4349
+ };
3848
4350
  const runInternal = (fn) => {
3849
- internalDepth += 1;
4351
+ state.internal += 1;
3850
4352
  try {
3851
4353
  fn();
4354
+ return true;
4355
+ } catch {
4356
+ return false;
3852
4357
  } finally {
3853
- internalDepth -= 1;
4358
+ state.internal -= 1;
3854
4359
  }
3855
4360
  };
3856
4361
  const notifyRouteChange = () => {
3857
- const snapshot = Array.from(subscribers);
3858
- for (const fn of snapshot) fn();
4362
+ for (const fn of Array.from(state.subscribers)) {
4363
+ try {
4364
+ fn();
4365
+ } catch (err) {
4366
+ queueMicrotask(() => {
4367
+ throw err;
4368
+ });
4369
+ }
4370
+ }
3859
4371
  };
3860
4372
  const ensureListener = () => {
3861
- if (listenerInstalled || !hasWindow()) return;
4373
+ if (state.listening || !hasHistory()) return;
3862
4374
  window.addEventListener("popstate", onPopState);
3863
- listenerInstalled = true;
4375
+ state.listening = true;
3864
4376
  };
3865
4377
  const maybeTeardownListener = () => {
3866
- if (!listenerInstalled) return;
3867
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3868
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
3869
- listenerInstalled = false;
3870
- };
3871
- const installPatch = () => {
3872
- if (patchInstalled || !hasHistory()) return;
3873
- origPushState = history.pushState;
3874
- origReplaceState = history.replaceState;
3875
- patchedPushState = function(...args) {
3876
- origPushState.apply(this, args);
3877
- if (internalDepth === 0) notifyRouteChange();
3878
- };
3879
- patchedReplaceState = function(...args) {
3880
- origReplaceState.apply(this, args);
3881
- if (internalDepth === 0) notifyRouteChange();
3882
- };
3883
- history.pushState = patchedPushState;
3884
- history.replaceState = patchedReplaceState;
3885
- patchInstalled = true;
3886
- };
3887
- const removePatch = () => {
3888
- if (!patchInstalled) return;
3889
- if (history.pushState === patchedPushState && origPushState) {
3890
- history.pushState = origPushState;
3891
- }
3892
- if (history.replaceState === patchedReplaceState && origReplaceState) {
3893
- history.replaceState = origReplaceState;
3894
- }
3895
- origPushState = null;
3896
- origReplaceState = null;
3897
- patchedPushState = null;
3898
- patchedReplaceState = null;
3899
- patchInstalled = false;
3900
- };
3901
- function onPopState() {
3902
- if (suppress > 0) {
3903
- suppress -= 1;
3904
- const rebrand = pendingRebrands.shift();
3905
- if (rebrand) rebrand();
3906
- maybeTeardownListener();
4378
+ if (!state.listening) return;
4379
+ if (state.live.length > 0 || state.subscribers.size > 0 || state.pending.length > 0 || state.leftovers) {
3907
4380
  return;
3908
4381
  }
3909
- if (entries.length === 0) {
3910
- notifyRouteChange();
3911
- maybeTeardownListener();
3912
- return;
3913
- }
3914
- let target = null;
3915
- let targetIdx = -1;
3916
- for (let i = entries.length - 1; i >= 0; i -= 1) {
3917
- const entry = entries[i];
3918
- if (!entry.surface.isOpen()) continue;
3919
- if (!target) {
3920
- target = entry;
3921
- targetIdx = i;
3922
- }
3923
- if (entry.surface.isTop()) {
3924
- target = entry;
3925
- targetIdx = i;
3926
- break;
3927
- }
3928
- }
3929
- if (!target) {
3930
- const removed = entries.pop();
3931
- if (removed) removed.live = false;
4382
+ if (hasHistory()) window.removeEventListener("popstate", onPopState);
4383
+ state.listening = false;
4384
+ };
4385
+ const settleIfIdle = () => {
4386
+ if (state.pending.length > 0) return;
4387
+ if (state.timer !== null) {
4388
+ clearTimeout(state.timer);
4389
+ state.timer = null;
4390
+ }
4391
+ for (const entry of state.deferred.splice(0)) pushEntry(entry);
4392
+ for (const resolve of state.waiters.splice(0)) resolve();
4393
+ };
4394
+ const traverse = (delta, kind) => {
4395
+ state.pending.push(kind);
4396
+ if (state.timer !== null) clearTimeout(state.timer);
4397
+ state.timer = setTimeout(() => {
4398
+ state.timer = null;
4399
+ state.pending.length = 0;
4400
+ settleIfIdle();
3932
4401
  maybeTeardownListener();
3933
- return;
4402
+ }, SETTLE_TIMEOUT_MS);
4403
+ ensureListener();
4404
+ const moved = runInternal(() => {
4405
+ if (delta < 0) history.back();
4406
+ else history.forward();
4407
+ });
4408
+ if (!moved) {
4409
+ state.pending.pop();
4410
+ settleIfIdle();
3934
4411
  }
3935
- target.live = false;
3936
- entries.splice(targetIdx, 1);
3937
- const settled = target;
3938
- void Promise.resolve(settled.surface.close()).then(() => {
3939
- if (settled.surface.isOpen()) restore(settled);
4412
+ };
4413
+ const newStackId = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
4414
+ const startStack = () => {
4415
+ state.stackId = newStackId();
4416
+ const current = history.state;
4417
+ const leftover = plain(current);
4418
+ state.baseUrl = isMarker(leftover) && leftover?.[ROUTED] !== void 0 ? void 0 : location.href;
4419
+ if (isMarker(leftover)) return;
4420
+ if (current !== null && current !== void 0 && !leftover) return;
4421
+ runInternal(() => {
4422
+ history.replaceState({
4423
+ ...foreignKeys(current),
4424
+ [STACK]: state.stackId,
4425
+ [DEPTH]: 0
4426
+ }, "", location.href);
3940
4427
  });
3941
- maybeTeardownListener();
4428
+ };
4429
+ function pushEntry(entry) {
4430
+ const depth = state.live.length + 1;
4431
+ if (state.live.length === 0) startStack();
4432
+ const written = runInternal(() => {
4433
+ history.pushState(markerFor(entry.surface, depth), "", entry.surface.url);
4434
+ });
4435
+ if (!written) return false;
4436
+ state.live.push(entry);
4437
+ state.position = depth;
4438
+ state.knownLength = history.length;
4439
+ state.href = location.href;
4440
+ ensureListener();
4441
+ return true;
3942
4442
  }
3943
- function restore(entry) {
3944
- if (!hasWindow() || !hasHistory()) return;
3945
- entry.live = true;
4443
+ const enlist = (entry) => {
4444
+ if (state.pending.length > 0) state.deferred.push(entry);
4445
+ else pushEntry(entry);
4446
+ };
4447
+ const sameBrand = (a, b) => {
4448
+ for (const key of /* @__PURE__ */ new Set([
4449
+ ...Object.keys(a),
4450
+ ...Object.keys(b)
4451
+ ])) {
4452
+ if (key.startsWith(MARKER) && a[key] !== b[key]) return false;
4453
+ }
4454
+ return true;
4455
+ };
4456
+ const rebrand = (depth) => {
4457
+ const entry = state.live[depth - 1];
4458
+ if (!entry) return;
4459
+ const url = urlForDepth(depth);
4460
+ const marker = markerFor(entry.surface, depth);
4461
+ const atUrl = url === void 0 || new URL(url, location.href).href === location.href;
4462
+ if (atUrl && sameBrand(plain(history.state) ?? {}, marker)) return;
3946
4463
  runInternal(() => {
4464
+ history.replaceState(marker, "", url);
4465
+ });
4466
+ state.href = location.href;
4467
+ };
4468
+ const closeAbove = (depth) => {
4469
+ const dropped = state.live.splice(depth);
4470
+ for (let i = dropped.length - 1; i >= 0; i -= 1) {
4471
+ const entry = dropped[i];
4472
+ let closing = void 0;
3947
4473
  try {
3948
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3949
- } catch {
3950
- entry.live = false;
4474
+ closing = entry.surface.close();
4475
+ } catch (err) {
4476
+ queueMicrotask(() => {
4477
+ throw err;
4478
+ });
3951
4479
  }
3952
- });
3953
- if (entry.live) {
3954
- entries.push(entry);
3955
- ensureListener();
4480
+ void Promise.resolve(closing).then(() => {
4481
+ if (!entry.surface.isOpen()) return;
4482
+ const known = (e) => e.surface === entry.surface;
4483
+ if (state.live.some(known) || state.deferred.some(known)) return;
4484
+ enlist(entry);
4485
+ });
3956
4486
  }
4487
+ };
4488
+ function onPopState(event) {
4489
+ const kind = state.pending.shift();
4490
+ const byUser = kind !== "pop";
4491
+ const landed = plain(event.state);
4492
+ const depth = depthOf(landed);
4493
+ const from = state.position;
4494
+ const liveBefore = state.live.length;
4495
+ const grew = history.length > state.knownLength;
4496
+ state.knownLength = history.length;
4497
+ const within = inStack(landed);
4498
+ if (within && depth !== null) {
4499
+ state.position = depth;
4500
+ if (depth > state.live.length) {
4501
+ const forward = from !== null && from < depth;
4502
+ traverse(forward ? 1 : -1, byUser ? "skip" : "pop");
4503
+ } else if (depth < state.live.length) {
4504
+ if (byUser) closeAbove(depth);
4505
+ } else {
4506
+ rebrand(depth);
4507
+ }
4508
+ } else if (isMarker(landed)) {
4509
+ state.position = null;
4510
+ traverse(-1, byUser ? "skip" : "pop");
4511
+ } else {
4512
+ state.position = null;
4513
+ if (byUser && !grew && state.live.length > 0) {
4514
+ closeAbove(state.live.length - 1);
4515
+ }
4516
+ }
4517
+ const moved = location.href !== state.href;
4518
+ state.href = location.href;
4519
+ if (kind === void 0 && moved && (liveBefore === 0 || !within)) {
4520
+ notifyRouteChange();
4521
+ }
4522
+ settleIfIdle();
4523
+ maybeTeardownListener();
3957
4524
  }
3958
4525
  function pushBackMarker(surface) {
3959
4526
  const entry = {
3960
- surface,
3961
- priorUrl: void 0,
3962
- live: false
4527
+ surface
3963
4528
  };
3964
- if (!hasWindow() || !hasHistory()) {
3965
- return entry;
3966
- }
3967
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
3968
- runInternal(() => {
3969
- try {
3970
- history.pushState(brandMarker(surface), "", surface.url);
3971
- entry.live = true;
3972
- } catch {
3973
- entry.live = false;
3974
- }
3975
- });
3976
- if (entry.live) {
3977
- entries.push(entry);
3978
- ensureListener();
3979
- }
4529
+ if (hasHistory()) enlist(entry);
3980
4530
  return entry;
3981
4531
  }
3982
4532
  function popBackMarker(handle) {
3983
- if (!hasWindow() || !hasHistory()) return;
4533
+ if (!hasHistory()) return Promise.resolve();
3984
4534
  const entry = handle;
3985
- const idx = entries.indexOf(entry);
3986
- if (idx === -1 || !entry.live) return;
3987
- const last = entries.length - 1;
3988
- entry.live = false;
3989
- entries.splice(idx, 1);
3990
- let rebrand = null;
3991
- if (idx !== last) {
3992
- const rebrandSurface = entries[entries.length - 1].surface;
3993
- const rebrandUrl = entry.priorUrl;
3994
- rebrand = () => {
3995
- runInternal(() => {
3996
- history.replaceState(brandMarker(rebrandSurface), "", rebrandUrl !== void 0 ? rebrandUrl : location.href);
3997
- });
3998
- };
4535
+ const waiting = state.deferred.indexOf(entry);
4536
+ if (waiting !== -1) state.deferred.splice(waiting, 1);
4537
+ const idx = state.live.indexOf(entry);
4538
+ if (idx === -1) return historySettled();
4539
+ const top = state.live.length;
4540
+ state.live.splice(idx, 1);
4541
+ const current = plain(history.state);
4542
+ const onTop = isMarker(current) && inStack(current) && depthOf(current) === top;
4543
+ if (onTop && state.pending.length === 0) {
4544
+ traverse(-1, "pop");
4545
+ } else {
4546
+ state.leftovers = true;
4547
+ ensureListener();
3999
4548
  }
4000
- pendingRebrands.push(rebrand);
4001
- suppress += 1;
4002
- runInternal(() => {
4003
- try {
4004
- history.back();
4005
- } catch {
4006
- suppress = Math.max(0, suppress - 1);
4007
- pendingRebrands.pop();
4008
- }
4009
- });
4010
4549
  maybeTeardownListener();
4550
+ return historySettled();
4011
4551
  }
4552
+ function releaseOrphanMarker() {
4553
+ if (!hasHistory() || state.orphanChecked) return;
4554
+ state.orphanChecked = true;
4555
+ if (state.live.length > 0 || state.pending.length > 0) return;
4556
+ if (!isMarker(plain(history.state))) return;
4557
+ state.leftovers = true;
4558
+ traverse(-1, "pop");
4559
+ }
4560
+ function historySettled() {
4561
+ if (state.pending.length === 0) return Promise.resolve();
4562
+ return new Promise((resolve) => {
4563
+ state.waiters.push(resolve);
4564
+ });
4565
+ }
4566
+ const wrap = (original) => function(...args) {
4567
+ original.apply(this, args);
4568
+ state.knownLength = history.length;
4569
+ if (location.href === state.href) return;
4570
+ state.href = location.href;
4571
+ if (state.internal === 0 && state.subscribers.size > 0) {
4572
+ notifyRouteChange();
4573
+ }
4574
+ };
4575
+ const installPatch = () => {
4576
+ if (!state.push || history.pushState === state.push.original) {
4577
+ const original = history.pushState;
4578
+ const wrapper = wrap(original);
4579
+ history.pushState = wrapper;
4580
+ state.push = {
4581
+ wrapper,
4582
+ original
4583
+ };
4584
+ }
4585
+ if (!state.replace || history.replaceState === state.replace.original) {
4586
+ const original = history.replaceState;
4587
+ const wrapper = wrap(original);
4588
+ history.replaceState = wrapper;
4589
+ state.replace = {
4590
+ wrapper,
4591
+ original
4592
+ };
4593
+ }
4594
+ };
4595
+ const removePatch = () => {
4596
+ if (state.push && history.pushState === state.push.wrapper) {
4597
+ history.pushState = state.push.original;
4598
+ state.push = null;
4599
+ }
4600
+ if (state.replace && history.replaceState === state.replace.wrapper) {
4601
+ history.replaceState = state.replace.original;
4602
+ state.replace = null;
4603
+ }
4604
+ };
4012
4605
  function subscribeRouteChange(fn) {
4013
- if (!hasWindow() || !hasHistory()) return () => {
4606
+ if (!hasHistory()) return () => {
4014
4607
  };
4015
- subscribers.add(fn);
4016
- if (subscribers.size === 1) installPatch();
4608
+ state.subscribers.add(fn);
4609
+ state.href = location.href;
4610
+ state.knownLength = history.length;
4611
+ installPatch();
4017
4612
  ensureListener();
4018
4613
  return () => {
4019
- if (!subscribers.has(fn)) return;
4020
- subscribers.delete(fn);
4021
- if (subscribers.size === 0) removePatch();
4614
+ if (!state.subscribers.delete(fn)) return;
4615
+ if (state.subscribers.size === 0) removePatch();
4022
4616
  maybeTeardownListener();
4023
4617
  };
4024
4618
  }
@@ -4032,40 +4626,41 @@ function installRoute(deps) {
4032
4626
  };
4033
4627
  }
4034
4628
  let handle = null;
4629
+ const surface = {
4630
+ isOpen: () => !deps.isDestroyed() && deps.isOpen(),
4631
+ close: deps.close,
4632
+ markerState: () => deps.routedTo !== void 0 ? {
4633
+ __bsRouted: deps.sheetId
4634
+ } : {
4635
+ __bsSheet: deps.sheetId
4636
+ },
4637
+ url: deps.routedTo
4638
+ };
4035
4639
  const onOpen = () => {
4036
4640
  if (handle) return;
4037
- handle = pushBackMarker({
4038
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
4039
- isTop: deps.isTopSheet,
4040
- close: deps.close,
4041
- markerState: () => deps.routedTo !== void 0 ? {
4042
- __bsRouted: deps.sheetId
4043
- } : {
4044
- __bsSheet: deps.sheetId
4045
- },
4046
- url: deps.routedTo
4047
- });
4641
+ handle = pushBackMarker(surface);
4048
4642
  };
4049
4643
  const onClose = () => {
4050
4644
  if (!handle) return;
4051
- popBackMarker(handle);
4645
+ deps.hold(popBackMarker(handle));
4052
4646
  handle = null;
4053
4647
  };
4054
4648
  const unsubscribeOpen = deps.on("open", onOpen);
4055
4649
  const unsubscribeClose = deps.on("close", onClose);
4056
- if (deps.getSize() > 0) onOpen();
4650
+ if (deps.isOpen()) onOpen();
4651
+ else releaseOrphanMarker();
4057
4652
  return () => {
4058
4653
  unsubscribeOpen();
4059
4654
  unsubscribeClose();
4060
4655
  if (handle) {
4061
- popBackMarker(handle);
4656
+ void popBackMarker(handle);
4062
4657
  handle = null;
4063
4658
  }
4064
4659
  };
4065
4660
  }
4066
4661
  function installRouteChange(deps) {
4067
4662
  return subscribeRouteChange(() => {
4068
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
4663
+ if (deps.isDestroyed() || !deps.isOpen()) return;
4069
4664
  void deps.close();
4070
4665
  });
4071
4666
  }
@@ -4095,7 +4690,10 @@ function installAutoCollapse(deps) {
4095
4690
  };
4096
4691
  const fire = () => {
4097
4692
  if (deps.isDestroyed()) return;
4098
- if (deps.isDragging()) return;
4693
+ if (deps.isDragging() || deps.isAnimating()) {
4694
+ reset();
4695
+ return;
4696
+ }
4099
4697
  const active = deps.resolveSnap(deps.getActiveId());
4100
4698
  if (!active || active.size === 0) return;
4101
4699
  let target = null;
@@ -4128,6 +4726,9 @@ function installContentDrag(deps) {
4128
4726
  return () => detach?.();
4129
4727
  }
4130
4728
  const SAFETY_PAD = 8;
4729
+ const KEYBOARD_MIN_INSET = 1;
4730
+ const KEYBOARD_MIN_COVER = 60;
4731
+ const ZOOMED = 1.01;
4131
4732
  function installVisualViewport(deps) {
4132
4733
  if (typeof window === "undefined" || !window.visualViewport) {
4133
4734
  return () => {
@@ -4135,36 +4736,36 @@ function installVisualViewport(deps) {
4135
4736
  }
4136
4737
  const vv = window.visualViewport;
4137
4738
  let rafId = null;
4739
+ const coveredByKeyboard = () => {
4740
+ if ((vv.scale || 1) > ZOOMED) return 0;
4741
+ const covered = window.innerHeight - ((vv.offsetTop || 0) + vv.height);
4742
+ return covered >= KEYBOARD_MIN_COVER ? Math.round(covered) : 0;
4743
+ };
4138
4744
  const onResize = () => {
4139
4745
  if (rafId !== null) return;
4140
4746
  rafId = requestAnimationFrame(() => {
4141
4747
  rafId = null;
4142
4748
  if (deps.isDestroyed()) return;
4749
+ deps.setKeyboardInset(coveredByKeyboard());
4750
+ const before = seatOf(deps);
4143
4751
  deps.recomputeSnaps();
4144
4752
  const isVertical = deps.isVerticalAxis();
4145
4753
  const visible = isVertical ? vv.height : vv.width;
4146
4754
  const windowSize = isVertical ? window.innerHeight : window.innerWidth;
4147
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
4755
+ const pad = windowSize - visible > KEYBOARD_MIN_INSET ? SAFETY_PAD : 0;
4756
+ const clamp = Math.max(0, Math.min(windowSize, visible) - pad);
4148
4757
  if (clamp < deps.getMaxAxisSize()) {
4149
4758
  deps.setMaxAxisSize(clamp);
4150
4759
  }
4151
- const wasAnimating = deps.isAnimating?.() ?? false;
4152
- deps.cancelInFlight();
4153
- deps.newCycle();
4154
- const max = deps.getMaxAxisSize();
4155
- deps.setSize(Math.min(deps.getSize(), max));
4156
- const current = deps.resolveActiveSnap();
4157
- if (current && !deps.isDragging()) {
4158
- deps.applySize(Math.min(current.size, max));
4159
- if (wasAnimating) deps.resyncAfterCancel?.();
4160
- } else {
4161
- deps.applySize(deps.getSize());
4162
- }
4760
+ if (sameSeat(before, seatOf(deps))) return;
4761
+ deps.reseat();
4163
4762
  });
4164
4763
  };
4165
4764
  vv.addEventListener("resize", onResize);
4765
+ vv.addEventListener("scroll", onResize);
4166
4766
  return () => {
4167
4767
  vv.removeEventListener("resize", onResize);
4768
+ vv.removeEventListener("scroll", onResize);
4168
4769
  if (rafId !== null) cancelAnimationFrame(rafId);
4169
4770
  };
4170
4771
  }
@@ -4176,17 +4777,17 @@ function routeFeature() {
4176
4777
  if (ctx.options.closeOnRouteChange) {
4177
4778
  teardowns.add(installRouteChange({
4178
4779
  isDestroyed: ctx.isDestroyed,
4179
- getSize: ctx.getSize,
4780
+ isOpen: () => ctx.getPhase() !== "closed",
4180
4781
  close: () => ctx.close()
4181
4782
  }));
4182
4783
  }
4183
4784
  teardowns.add(installRoute({
4184
4785
  routedTo: ctx.options.routedTo,
4185
4786
  closeOnBack: ctx.options.closeOnBack,
4186
- isTopSheet: ctx.isTopSheet,
4187
- getSize: ctx.getSize,
4787
+ isOpen: () => ctx.getPhase() !== "closed",
4188
4788
  isDestroyed: ctx.isDestroyed,
4189
4789
  close: () => ctx.close("back"),
4790
+ hold: ctx.holdSettle,
4190
4791
  on: ctx.on,
4191
4792
  sheetId: ctx.sheetId
4192
4793
  }));
@@ -4213,6 +4814,7 @@ function autoCollapseFeature() {
4213
4814
  ms: ctx.options.autoCollapseAfter,
4214
4815
  isDestroyed: ctx.isDestroyed,
4215
4816
  isDragging: ctx.isDragging,
4817
+ isAnimating: ctx.isAnimating,
4216
4818
  getAllowedIds: ctx.getAllowedIds,
4217
4819
  getActiveId: ctx.getActiveId,
4218
4820
  resolveSnap: ctx.resolveSnap,
@@ -4240,21 +4842,14 @@ function visualViewportFeature() {
4240
4842
  install: (ctx) => {
4241
4843
  if (typeof window === "undefined") return;
4242
4844
  return installVisualViewport({
4243
- element: ctx.element,
4244
4845
  isVerticalAxis: ctx.isVerticalAxis,
4245
4846
  isDestroyed: ctx.isDestroyed,
4246
- isDragging: ctx.isDragging,
4247
4847
  recomputeSnaps: ctx.recomputeSnaps,
4248
4848
  resolveActiveSnap: ctx.resolveActiveSnap,
4249
4849
  getMaxAxisSize: ctx.getMaxAxisSize,
4250
- getSize: ctx.getSize,
4251
4850
  setMaxAxisSize: ctx.setMaxAxisSize,
4252
- setSize: ctx.setSize,
4253
- applySize: ctx.applySize,
4254
- cancelInFlight: ctx.cancelInFlight,
4255
- newCycle: ctx.newCycle,
4256
- isAnimating: ctx.isAnimating,
4257
- resyncAfterCancel: ctx.resyncAfterResize
4851
+ setKeyboardInset: ctx.setKeyboardInset,
4852
+ reseat: ctx.reseat
4258
4853
  });
4259
4854
  }
4260
4855
  };
@@ -4409,39 +5004,68 @@ class BottomSheetEngine extends BottomSheetCore {
4409
5004
  });
4410
5005
  }
4411
5006
  }
4412
- const resolveTeleportTarget = (target) => {
4413
- if (!target) return null;
4414
- if (typeof target !== "string") return target;
4415
- if (typeof document === "undefined") return null;
4416
- if (target === "body") return document.body;
4417
- return document.querySelector(target);
4418
- };
4419
- const teleportElements = (els, target) => {
4420
- const records = [];
4421
- for (const el of els) {
4422
- if (!el || el.parentNode === target) continue;
4423
- records.push({
4424
- el,
4425
- parent: el.parentNode,
4426
- next: el.nextSibling
4427
- });
4428
- target.appendChild(el);
5007
+ const ENGINE_OPTION_KEYS = [
5008
+ "mode",
5009
+ "snapPoints",
5010
+ "allowed",
5011
+ "initial",
5012
+ "duration",
5013
+ "easing",
5014
+ "respectReducedMotion",
5015
+ "flickVelocity",
5016
+ "dragThreshold",
5017
+ "dragFrom",
5018
+ "dragFromContent",
5019
+ "fitContentToSnap",
5020
+ "rubberBand",
5021
+ "backdropRange",
5022
+ "backdropOpacity",
5023
+ "backdropColor",
5024
+ "screenRange",
5025
+ "scrimMode",
5026
+ "scrimColor",
5027
+ "scrimBlur",
5028
+ "scrimInteractive",
5029
+ "scrimTapToClose",
5030
+ "scrimPreset",
5031
+ "animation",
5032
+ "settleAnimation",
5033
+ "spring",
5034
+ "focusTrap",
5035
+ "initialFocus",
5036
+ "closeOnEscape",
5037
+ "lockBodyScroll",
5038
+ "closeOnBack",
5039
+ "routedTo",
5040
+ "inertSiblings",
5041
+ "persistent",
5042
+ "disableClose",
5043
+ "disableDrag",
5044
+ "closeOnRouteChange",
5045
+ "radius",
5046
+ "maxHeight",
5047
+ "returnFocusTo",
5048
+ "persistKey",
5049
+ "autoCollapseAfter",
5050
+ "linkedSheets",
5051
+ "viewTransitions",
5052
+ "stackEffect",
5053
+ "features"
5054
+ ];
5055
+ const pickEngineOptions = (source) => {
5056
+ const out = {};
5057
+ for (const key of ENGINE_OPTION_KEYS) {
5058
+ const value = source[key];
5059
+ if (value !== void 0) out[key] = value;
4429
5060
  }
4430
- return () => {
4431
- for (let i = records.length - 1; i >= 0; i--) {
4432
- const { el, parent, next } = records[i];
4433
- if (parent && parent.isConnected) {
4434
- const before = next && next.parentNode === parent ? next : null;
4435
- try {
4436
- parent.insertBefore(el, before);
4437
- } catch {
4438
- el.parentNode?.removeChild(el);
4439
- }
4440
- } else {
4441
- el.parentNode?.removeChild(el);
4442
- }
4443
- }
4444
- };
5061
+ return out;
5062
+ };
5063
+ const closedSnapId = (points) => points.find((point) => point.size === 0)?.id;
5064
+ const syncOpen = (engine, open) => {
5065
+ const phase = engine.state.phase;
5066
+ const showing = phase === "open" || phase === "opening";
5067
+ if (open && !showing) void engine.open();
5068
+ else if (!open && showing) void engine.close();
4445
5069
  };
4446
5070
  const BottomSheet = qwik.component$((props) => {
4447
5071
  const sheetRef = qwik.useSignal();
@@ -4453,10 +5077,12 @@ const BottomSheet = qwik.component$((props) => {
4453
5077
  const footerRef = qwik.useSignal();
4454
5078
  const leftBtnRef = qwik.useSignal();
4455
5079
  const rightBtnRef = qwik.useSignal();
4456
- const initialActive = props.initial ?? props.snapPoints[0]?.id ?? "default";
4457
- const state = qwik.useStore({
5080
+ const initialSnap = props.initial ?? (props.open === false ? closedSnapId(props.snapPoints) : void 0) ?? props.snap;
5081
+ const initialActive = initialSnap ?? props.snapPoints[0]?.id ?? "default";
5082
+ const state2 = qwik.useStore({
4458
5083
  size: 0,
4459
5084
  activeId: initialActive,
5085
+ phase: "closed",
4460
5086
  isDragging: false,
4461
5087
  isAnimating: false,
4462
5088
  progress: 0
@@ -4464,12 +5090,15 @@ const BottomSheet = qwik.component$((props) => {
4464
5090
  const engineStore = qwik.useStore({
4465
5091
  engine: void 0
4466
5092
  });
5093
+ const physicalMode = qwik.useSignal(guessPhysicalMode(props.mode));
5094
+ const restAtStart = startsClosed(props.snapPoints, initialSnap, props.allowed) ? "closed" : void 0;
4467
5095
  qwik.useVisibleTask$(({ cleanup }) => {
4468
5096
  if (!sheetRef.value) return;
4469
5097
  if (!handleRef.value || !contentRef.value) {
4470
5098
  devWarn("[BottomSheet/qwik] handle or content ref missing at attach time — drag and content-swipe disabled. Make sure ref bindings render before useVisibleTask$ fires.");
4471
5099
  }
4472
5100
  const engineOpts = {
5101
+ ...pickEngineOptions(props),
4473
5102
  element: sheetRef.value,
4474
5103
  handle: handleRef.value,
4475
5104
  scrollContainer: contentRef.value,
@@ -4477,43 +5106,25 @@ const BottomSheet = qwik.component$((props) => {
4477
5106
  scrim: scrimRef.value,
4478
5107
  mode: props.mode ?? "bottom",
4479
5108
  snapPoints: props.snapPoints,
4480
- allowed: props.allowed,
4481
- initial: props.initial,
4482
- animation: props.animation,
4483
- spring: props.spring,
4484
- focusTrap: props.focusTrap,
4485
- closeOnEscape: props.closeOnEscape,
4486
- closeOnBack: props.closeOnBack,
4487
- lockBodyScroll: props.lockBodyScroll,
4488
- rubberBand: props.rubberBand,
4489
- backdropRange: props.backdropRange,
4490
- persistent: props.persistent,
4491
- disableClose: props.disableClose,
4492
- disableDrag: props.disableDrag,
4493
- dragFrom: props.dragFrom,
4494
- dragFromContent: props.dragFromContent,
4495
- fitContentToSnap: props.fitContentToSnap,
4496
- closeOnRouteChange: props.closeOnRouteChange,
4497
- stackEffect: props.stackEffect,
4498
- returnFocusTo: props.returnFocusTo,
4499
- radius: props.radius,
4500
- maxHeight: props.maxHeight
5109
+ initial: initialSnap
4501
5110
  };
4502
- const engine = new BottomSheetEngine(engineOpts);
4503
- engineStore.engine = qwik.noSerialize(engine);
4504
5111
  const teleportTarget = props.teleport === false ? null : resolveTeleportTarget(props.teleportTo);
4505
5112
  const restoreTeleport = teleportTarget ? teleportElements([
4506
5113
  backdropRef.value,
4507
5114
  scrimRef.value,
4508
5115
  sheetRef.value
4509
5116
  ], teleportTarget) : null;
5117
+ const engine = new BottomSheetEngine(engineOpts);
5118
+ engineStore.engine = qwik.noSerialize(engine);
5119
+ physicalMode.value = engine.getMode();
4510
5120
  const sync = () => {
4511
5121
  const s = engine.state;
4512
- state.size = s.size;
4513
- state.activeId = s.activeId;
4514
- state.isDragging = s.isDragging;
4515
- state.isAnimating = s.isAnimating;
4516
- state.progress = s.progress;
5122
+ state2.size = s.size;
5123
+ state2.activeId = s.activeId;
5124
+ state2.isDragging = s.isDragging;
5125
+ state2.phase = s.phase;
5126
+ state2.isAnimating = s.isAnimating;
5127
+ state2.progress = s.progress;
4517
5128
  props.onChange$?.(s);
4518
5129
  };
4519
5130
  sync();
@@ -4521,8 +5132,15 @@ const BottomSheet = qwik.component$((props) => {
4521
5132
  sync();
4522
5133
  props.onSnap$?.(payload.id);
4523
5134
  });
4524
- engine.on("open", (payload) => props.onOpen$?.(payload.id));
4525
- 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);
4526
5144
  engine.on("opened", (payload) => props.onOpened$?.(payload.id));
4527
5145
  engine.on("closed", () => props.onClosed$?.());
4528
5146
  engine.on("dragstart", (payload) => {
@@ -4580,6 +5198,8 @@ const BottomSheet = qwik.component$((props) => {
4580
5198
  engineStore.engine = void 0;
4581
5199
  engine.destroy();
4582
5200
  });
5201
+ }, {
5202
+ strategy: "document-ready"
4583
5203
  });
4584
5204
  qwik.useTask$(({ track }) => {
4585
5205
  const points = track(() => props.snapPoints);
@@ -4612,14 +5232,25 @@ const BottomSheet = qwik.component$((props) => {
4612
5232
  }
4613
5233
  if (dragFrom !== void 0) engine.setDragFrom(dragFrom);
4614
5234
  });
5235
+ qwik.useTask$(({ track }) => {
5236
+ const open = track(() => props.open);
5237
+ const engine = engineStore.engine;
5238
+ if (engine && open !== void 0) syncOpen(engine, open);
5239
+ });
5240
+ qwik.useTask$(({ track }) => {
5241
+ const snap = track(() => props.snap);
5242
+ const engine = engineStore.engine;
5243
+ if (!engine || snap === void 0 || snap === engine.state.activeId) return;
5244
+ void engine.snapTo(snap);
5245
+ });
4615
5246
  qwik.useTask$(({ track }) => {
4616
5247
  const nextMode = track(() => props.mode);
4617
5248
  const engine = engineStore.engine;
4618
5249
  if (!engine || nextMode === void 0) return;
4619
5250
  engine.setMode(nextMode);
5251
+ physicalMode.value = engine.getMode();
4620
5252
  });
4621
5253
  const showBackdrop = props.backdrop !== false;
4622
- const mode = props.mode ?? "bottom";
4623
5254
  const ariaLabel = props.ariaLabel ?? "Bottom sheet";
4624
5255
  return /* @__PURE__ */ jsxRuntime.jsxs("div", {
4625
5256
  class: "bs-root",
@@ -4638,8 +5269,9 @@ const BottomSheet = qwik.component$((props) => {
4638
5269
  /* @__PURE__ */ jsxRuntime.jsxs("section", {
4639
5270
  class: "bs-sheet",
4640
5271
  ref: sheetRef,
4641
- "data-mode": mode,
4642
- "data-active": state.activeId,
5272
+ "data-mode": physicalMode.value,
5273
+ "data-bs-rest": restAtStart,
5274
+ "data-active": state2.activeId,
4643
5275
  role: "dialog",
4644
5276
  "aria-modal": props.focusTrap ? "true" : void 0,
4645
5277
  "aria-label": ariaLabel,
@@ -4649,7 +5281,7 @@ const BottomSheet = qwik.component$((props) => {
4649
5281
  ref: handleRef,
4650
5282
  role: "slider",
4651
5283
  tabIndex: 0,
4652
- "aria-label": "Resize sheet"
5284
+ "aria-label": props.handleLabel ?? "Resize sheet"
4653
5285
  }),
4654
5286
  /* @__PURE__ */ jsxRuntime.jsx("div", {
4655
5287
  class: "bs-header",
@@ -4661,6 +5293,9 @@ const BottomSheet = qwik.component$((props) => {
4661
5293
  /* @__PURE__ */ jsxRuntime.jsx("div", {
4662
5294
  class: "bs-content",
4663
5295
  ref: contentRef,
5296
+ tabIndex: 0,
5297
+ role: "region",
5298
+ "aria-label": props.contentLabel ?? "Sheet content",
4664
5299
  children: /* @__PURE__ */ jsxRuntime.jsx(qwik.Slot, {})
4665
5300
  }),
4666
5301
  /* @__PURE__ */ jsxRuntime.jsx("div", {
@@ -4674,14 +5309,14 @@ const BottomSheet = qwik.component$((props) => {
4674
5309
  class: "bs-sr-only",
4675
5310
  role: "status",
4676
5311
  "aria-live": "polite",
4677
- children: state.activeId
5312
+ children: state2.activeId
4678
5313
  })
4679
5314
  ]
4680
5315
  }),
4681
5316
  /* @__PURE__ */ jsxRuntime.jsx("div", {
4682
5317
  class: "bs-button-slot",
4683
5318
  "data-side": "left",
4684
- "data-mode": mode,
5319
+ "data-mode": physicalMode.value,
4685
5320
  ref: leftBtnRef,
4686
5321
  children: /* @__PURE__ */ jsxRuntime.jsx(qwik.Slot, {
4687
5322
  name: "leftButton"
@@ -4690,7 +5325,7 @@ const BottomSheet = qwik.component$((props) => {
4690
5325
  /* @__PURE__ */ jsxRuntime.jsx("div", {
4691
5326
  class: "bs-button-slot",
4692
5327
  "data-side": "right",
4693
- "data-mode": mode,
5328
+ "data-mode": physicalMode.value,
4694
5329
  ref: rightBtnRef,
4695
5330
  children: /* @__PURE__ */ jsxRuntime.jsx(qwik.Slot, {
4696
5331
  name: "rightButton"