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