@surdeddd/bottom-sheet 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +126 -83
  2. package/dist/{BottomSheetEngine-CBRcBwV-.d.ts → BottomSheetEngine-DRjCU73e.d.ts} +6 -5
  3. package/dist/{BottomSheetEngine-BcyruNP1.d.cts → BottomSheetEngine-DzsIwD28.d.cts} +6 -5
  4. package/dist/element.cjs +388 -222
  5. package/dist/element.d.cts +1 -1
  6. package/dist/element.d.ts +1 -1
  7. package/dist/element.iife.global.js +2 -2
  8. package/dist/element.js +388 -222
  9. package/dist/index.cjs +389 -266
  10. package/dist/index.d.cts +2 -2
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +389 -266
  13. package/dist/integrations/formik.d.cts +1 -1
  14. package/dist/integrations/formik.d.ts +1 -1
  15. package/dist/integrations/react-hook-form.d.cts +1 -1
  16. package/dist/integrations/react-hook-form.d.ts +1 -1
  17. package/dist/overlay.cjs +191 -58
  18. package/dist/overlay.d.cts +4 -3
  19. package/dist/overlay.d.ts +4 -3
  20. package/dist/overlay.js +191 -58
  21. package/dist/preact.d.cts +2 -1
  22. package/dist/preact.d.ts +2 -1
  23. package/dist/qwik.cjs +396 -211
  24. package/dist/qwik.d.cts +11 -2
  25. package/dist/qwik.d.ts +11 -2
  26. package/dist/qwik.js +396 -211
  27. package/dist/react.cjs +500 -332
  28. package/dist/react.d.cts +26 -4
  29. package/dist/react.d.ts +26 -4
  30. package/dist/react.js +500 -332
  31. package/dist/solid.cjs +362 -210
  32. package/dist/solid.d.cts +14 -4
  33. package/dist/solid.d.ts +14 -4
  34. package/dist/solid.js +362 -210
  35. package/dist/solid.source.jsx +362 -210
  36. package/dist/styles.css +17 -1
  37. package/dist/svelte-core.cjs +345 -208
  38. package/dist/svelte-core.js +345 -208
  39. package/dist/svelte-src/BottomSheet.svelte +6 -0
  40. package/dist/svelte.cjs +359 -220
  41. package/dist/svelte.d.cts +2 -0
  42. package/dist/svelte.d.ts +2 -0
  43. package/dist/svelte.js +359 -220
  44. package/dist/teleport-7uYNiEAT.d.cts +3 -0
  45. package/dist/teleport-7uYNiEAT.d.ts +3 -0
  46. package/dist/vue.cjs +353 -209
  47. package/dist/vue.d.cts +3 -1
  48. package/dist/vue.d.ts +3 -1
  49. package/dist/vue.js +353 -209
  50. package/package.json +11 -9
package/dist/react.js CHANGED
@@ -191,11 +191,18 @@ var resolveSnap = (point, mode, measureFit) => {
191
191
  return Math.max(0, el.getBoundingClientRect().width);
192
192
  };
193
193
 
194
+ // src/core/primitives/devWarn.ts
195
+ function devWarn(...args) {
196
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
197
+ console.warn(...args);
198
+ }
199
+ }
200
+
194
201
  // src/core/primitives/snap-points.ts
195
202
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
196
203
  const raw = resolveSnap(p.size, mode, measureFit);
197
204
  if (!Number.isFinite(raw) || raw < 0) {
198
- console.warn(
205
+ devWarn(
199
206
  `[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} \u2192 ${raw}); clamped to 0.`
200
207
  );
201
208
  return { id: p.id, size: 0 };
@@ -207,7 +214,7 @@ var auditVhUsage = (points) => {
207
214
  for (const p of points) {
208
215
  if (typeof p.size !== "string") continue;
209
216
  if (!VH_NOT_DVH.test(p.size)) continue;
210
- console.warn(
217
+ devWarn(
211
218
  `[BottomSheet] snap "${p.id}" uses "vh" \u2014 prefer "dvh" on mobile (iOS Safari URL bar makes vh unstable). See https://web.dev/blog/viewport-units`
212
219
  );
213
220
  }
@@ -261,6 +268,26 @@ var findDragSettleTarget = (input) => {
261
268
  );
262
269
  };
263
270
 
271
+ // src/core/primitives/cancelable-emit.ts
272
+ function emitCancelable(emit, base, warnLabel) {
273
+ let cancelled = false;
274
+ let frozen = false;
275
+ emit({
276
+ ...base,
277
+ cancel: () => {
278
+ if (frozen) {
279
+ devWarn(
280
+ `[BottomSheet] ${warnLabel}.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener.`
281
+ );
282
+ return;
283
+ }
284
+ cancelled = true;
285
+ }
286
+ });
287
+ frozen = true;
288
+ return cancelled;
289
+ }
290
+
264
291
  // src/core/primitives/snap-resolver.ts
265
292
  var SnapResolver = class {
266
293
  constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
@@ -405,7 +432,9 @@ function notifyLinkedSheets(sheets, self) {
405
432
  try {
406
433
  if (linked.state.size === 0) continue;
407
434
  const ids = linked.getAllowedIds();
408
- const target = ids.find((id) => id !== "closed");
435
+ const resolved = linked.getResolvedSnaps();
436
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
437
+ const target = ids.find((id) => sizeOf(id) > 0);
409
438
  if (target && target !== linked.state.activeId) {
410
439
  void linked.snapTo(target);
411
440
  }
@@ -417,46 +446,198 @@ function notifyLinkedSheets(sheets, self) {
417
446
  }
418
447
  }
419
448
 
420
- // src/core/features/route.ts
421
- var closers = [];
422
- var handlerInstalled = false;
449
+ // src/core/features/history-coordinator.ts
450
+ var entries = [];
451
+ var subscribers = /* @__PURE__ */ new Set();
423
452
  var suppress = 0;
424
- var ensureHandler = () => {
425
- if (handlerInstalled || typeof window === "undefined") return;
426
- handlerInstalled = true;
427
- window.addEventListener("popstate", () => {
428
- if (suppress > 0) {
429
- suppress -= 1;
430
- return;
453
+ var internalDepth = 0;
454
+ var pendingRebrand = null;
455
+ var listenerInstalled = false;
456
+ var patchInstalled = false;
457
+ var origPushState = null;
458
+ var origReplaceState = null;
459
+ var patchedPushState = null;
460
+ var patchedReplaceState = null;
461
+ var hasWindow = () => typeof window !== "undefined";
462
+ var hasHistory = () => typeof history !== "undefined";
463
+ var runInternal = (fn) => {
464
+ internalDepth += 1;
465
+ try {
466
+ fn();
467
+ } finally {
468
+ internalDepth -= 1;
469
+ }
470
+ };
471
+ var notifyRouteChange = () => {
472
+ const snapshot = Array.from(subscribers);
473
+ for (const fn of snapshot) fn();
474
+ };
475
+ var ensureListener = () => {
476
+ if (listenerInstalled || !hasWindow()) return;
477
+ window.addEventListener("popstate", onPopState);
478
+ listenerInstalled = true;
479
+ };
480
+ var maybeTeardownListener = () => {
481
+ if (!listenerInstalled) return;
482
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
483
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
484
+ listenerInstalled = false;
485
+ };
486
+ var installPatch = () => {
487
+ if (patchInstalled || !hasHistory()) return;
488
+ origPushState = history.pushState;
489
+ origReplaceState = history.replaceState;
490
+ patchedPushState = function(...args) {
491
+ origPushState.apply(this, args);
492
+ if (internalDepth === 0) notifyRouteChange();
493
+ };
494
+ patchedReplaceState = function(...args) {
495
+ origReplaceState.apply(this, args);
496
+ if (internalDepth === 0) notifyRouteChange();
497
+ };
498
+ history.pushState = patchedPushState;
499
+ history.replaceState = patchedReplaceState;
500
+ patchInstalled = true;
501
+ };
502
+ var removePatch = () => {
503
+ if (!patchInstalled) return;
504
+ if (history.pushState === patchedPushState && origPushState) {
505
+ history.pushState = origPushState;
506
+ }
507
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
508
+ history.replaceState = origReplaceState;
509
+ }
510
+ origPushState = null;
511
+ origReplaceState = null;
512
+ patchedPushState = null;
513
+ patchedReplaceState = null;
514
+ patchInstalled = false;
515
+ };
516
+ function onPopState() {
517
+ if (suppress > 0) {
518
+ suppress -= 1;
519
+ const rebrand = pendingRebrand;
520
+ pendingRebrand = null;
521
+ if (rebrand) rebrand();
522
+ maybeTeardownListener();
523
+ return;
524
+ }
525
+ if (entries.length === 0) {
526
+ notifyRouteChange();
527
+ maybeTeardownListener();
528
+ return;
529
+ }
530
+ let target = null;
531
+ let targetIdx = -1;
532
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
533
+ const entry = entries[i];
534
+ if (!entry.surface.isOpen()) continue;
535
+ if (!target) {
536
+ target = entry;
537
+ targetIdx = i;
431
538
  }
432
- let target = null;
433
- for (let i = closers.length - 1; i >= 0; i -= 1) {
434
- const c = closers[i];
435
- if (!c.isOpen()) continue;
436
- if (!target) target = c;
437
- if (c.isTop()) {
438
- target = c;
439
- break;
440
- }
539
+ if (entry.surface.isTop()) {
540
+ target = entry;
541
+ targetIdx = i;
542
+ break;
441
543
  }
442
- if (!target) return;
443
- target.consumed = true;
444
- const idx = closers.indexOf(target);
445
- if (idx !== -1) closers.splice(idx, 1);
446
- const settled = target;
447
- void Promise.resolve(settled.close()).then(() => {
448
- if (settled.isOpen()) settled.restore();
449
- });
544
+ }
545
+ if (!target) {
546
+ const removed = entries.pop();
547
+ if (removed) removed.live = false;
548
+ maybeTeardownListener();
549
+ return;
550
+ }
551
+ target.live = false;
552
+ entries.splice(targetIdx, 1);
553
+ const settled = target;
554
+ void Promise.resolve(settled.surface.close()).then(() => {
555
+ if (settled.surface.isOpen()) restore(settled);
450
556
  });
451
- };
452
- var programmaticBack = () => {
453
- suppress += 1;
454
- try {
455
- history.back();
456
- } catch {
457
- suppress = Math.max(0, suppress - 1);
557
+ maybeTeardownListener();
558
+ }
559
+ function restore(entry) {
560
+ if (!hasWindow() || !hasHistory()) return;
561
+ entry.live = true;
562
+ runInternal(() => {
563
+ try {
564
+ history.pushState(entry.surface.markerState(), "", entry.surface.url);
565
+ } catch {
566
+ entry.live = false;
567
+ }
568
+ });
569
+ if (entry.live) {
570
+ entries.push(entry);
571
+ ensureListener();
458
572
  }
459
- };
573
+ }
574
+ function pushBackMarker(surface) {
575
+ const entry = { surface, priorUrl: void 0, live: false };
576
+ if (!hasWindow() || !hasHistory()) {
577
+ return entry;
578
+ }
579
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
580
+ runInternal(() => {
581
+ try {
582
+ history.pushState(surface.markerState(), "", surface.url);
583
+ entry.live = true;
584
+ } catch {
585
+ entry.live = false;
586
+ }
587
+ });
588
+ if (entry.live) {
589
+ entries.push(entry);
590
+ ensureListener();
591
+ }
592
+ return entry;
593
+ }
594
+ function popBackMarker(handle) {
595
+ if (!hasWindow() || !hasHistory()) return;
596
+ const entry = handle;
597
+ const idx = entries.indexOf(entry);
598
+ if (idx === -1 || !entry.live) return;
599
+ const last = entries.length - 1;
600
+ entry.live = false;
601
+ entries.splice(idx, 1);
602
+ if (idx !== last) {
603
+ const rebrandSurface = entries[entries.length - 1].surface;
604
+ const rebrandUrl = entry.priorUrl;
605
+ pendingRebrand = () => {
606
+ runInternal(() => {
607
+ history.replaceState(
608
+ rebrandSurface.markerState(),
609
+ "",
610
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
611
+ );
612
+ });
613
+ };
614
+ }
615
+ suppress += 1;
616
+ runInternal(() => {
617
+ try {
618
+ history.back();
619
+ } catch {
620
+ suppress = Math.max(0, suppress - 1);
621
+ pendingRebrand = null;
622
+ }
623
+ });
624
+ maybeTeardownListener();
625
+ }
626
+ function subscribeRouteChange(fn) {
627
+ if (!hasWindow() || !hasHistory()) return () => {
628
+ };
629
+ subscribers.add(fn);
630
+ if (subscribers.size === 1) installPatch();
631
+ ensureListener();
632
+ return () => {
633
+ if (!subscribers.has(fn)) return;
634
+ subscribers.delete(fn);
635
+ if (subscribers.size === 0) removePatch();
636
+ maybeTeardownListener();
637
+ };
638
+ }
639
+
640
+ // src/core/features/route.ts
460
641
  function installRoute(deps) {
461
642
  if (typeof window === "undefined") {
462
643
  return () => {
@@ -466,92 +647,39 @@ function installRoute(deps) {
466
647
  return () => {
467
648
  };
468
649
  }
469
- let pushed = false;
470
- let closer = null;
471
- const teardownEntry = () => {
472
- if (closer) {
473
- const idx = closers.indexOf(closer);
474
- if (idx !== -1) closers.splice(idx, 1);
475
- }
476
- const wasConsumed = closer?.consumed ?? false;
477
- closer = null;
478
- if (pushed && !wasConsumed) {
479
- pushed = false;
480
- programmaticBack();
481
- } else {
482
- pushed = false;
483
- }
484
- };
485
- const pushEntry = () => {
486
- try {
487
- if (deps.routedTo !== void 0) {
488
- history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
489
- } else {
490
- history.pushState({ __bsSheet: deps.sheetId }, "");
491
- }
492
- pushed = true;
493
- } catch {
494
- pushed = false;
495
- }
496
- };
650
+ let handle = null;
497
651
  const onOpen = () => {
498
- if (closer) return;
499
- const self = {
652
+ if (handle) return;
653
+ handle = pushBackMarker({
500
654
  isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
501
- isTop: () => deps.isTopSheet(),
502
- close: () => deps.close(),
503
- restore: () => {
504
- if (deps.isDestroyed() || closer !== self) return;
505
- self.consumed = false;
506
- if (!closers.includes(self)) closers.push(self);
507
- pushEntry();
508
- },
509
- consumed: false
510
- };
511
- closer = self;
512
- ensureHandler();
513
- closers.push(self);
514
- pushEntry();
655
+ isTop: deps.isTopSheet,
656
+ close: deps.close,
657
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
658
+ url: deps.routedTo
659
+ });
660
+ };
661
+ const onClose = () => {
662
+ if (!handle) return;
663
+ popBackMarker(handle);
664
+ handle = null;
515
665
  };
516
666
  const unsubscribeOpen = deps.on("open", onOpen);
517
- const unsubscribeClose = deps.on("close", teardownEntry);
667
+ const unsubscribeClose = deps.on("close", onClose);
518
668
  if (deps.getSize() > 0) onOpen();
519
669
  return () => {
520
670
  unsubscribeOpen();
521
671
  unsubscribeClose();
522
- teardownEntry();
672
+ if (handle) {
673
+ popBackMarker(handle);
674
+ handle = null;
675
+ }
523
676
  };
524
677
  }
525
678
  function installRouteChange(deps) {
526
- if (typeof window === "undefined" || typeof history === "undefined") {
527
- return () => {
528
- };
529
- }
530
- const onChange = () => {
679
+ return subscribeRouteChange(() => {
531
680
  if (deps.isDestroyed() || deps.getSize() <= 0) return;
532
681
  void deps.close();
533
- };
534
- const wrap = (key) => {
535
- const original = history[key];
536
- const patched = function(...args) {
537
- original.apply(this, args);
538
- onChange();
539
- };
540
- history[key] = patched;
541
- return () => {
542
- if (history[key] === patched) {
543
- history[key] = original;
544
- }
545
- };
546
- };
547
- window.addEventListener("popstate", onChange);
548
- const restorePush = wrap("pushState");
549
- const restoreReplace = wrap("replaceState");
550
- return () => {
551
- window.removeEventListener("popstate", onChange);
552
- restorePush();
553
- restoreReplace();
554
- };
682
+ });
555
683
  }
556
684
 
557
685
  // src/core/features/content-swipe.ts
@@ -622,11 +750,15 @@ function installVisualViewport(deps) {
622
750
  if (clamp < deps.getMaxAxisSize()) {
623
751
  deps.setMaxAxisSize(clamp);
624
752
  }
753
+ const wasAnimating = deps.isAnimating?.() ?? false;
754
+ deps.cancelInFlight();
755
+ deps.newCycle();
625
756
  const max = deps.getMaxAxisSize();
626
757
  deps.setSize(Math.min(deps.getSize(), max));
627
758
  const current = deps.resolveActiveSnap();
628
759
  if (current && !deps.isDragging()) {
629
760
  deps.applySize(Math.min(current.size, max));
761
+ if (wasAnimating) deps.resyncAfterCancel?.();
630
762
  } else {
631
763
  deps.applySize(deps.getSize());
632
764
  }
@@ -767,6 +899,54 @@ function installFitObserver(deps) {
767
899
  };
768
900
  }
769
901
 
902
+ // src/core/features/max-height-controller.ts
903
+ function createMaxHeightController(deps) {
904
+ let maxHeightCap;
905
+ let maxHeightRaw;
906
+ const resolveCap = () => {
907
+ const raw = maxHeightRaw;
908
+ if (raw === void 0) {
909
+ maxHeightCap = void 0;
910
+ return;
911
+ }
912
+ if (typeof raw === "number") {
913
+ maxHeightCap = raw;
914
+ return;
915
+ }
916
+ if (typeof window === "undefined") return;
917
+ const measured = resolveSnap(raw, deps.mode);
918
+ maxHeightCap = measured > 0 ? measured : void 0;
919
+ };
920
+ const clampTo = () => {
921
+ if (maxHeightCap === void 0) return;
922
+ if (deps.getMaxAxisSize() > maxHeightCap) {
923
+ deps.setMaxAxisSize(maxHeightCap);
924
+ }
925
+ };
926
+ return {
927
+ setRadius(r) {
928
+ deps.element.style.setProperty(
929
+ "--bs-radius",
930
+ typeof r === "number" ? `${r}px` : r
931
+ );
932
+ },
933
+ setMaxHeight(h) {
934
+ const value = typeof h === "number" ? `${h}px` : h;
935
+ const axis = layoutAxis(deps.mode);
936
+ deps.element.style.setProperty(
937
+ axis === "height" ? "max-height" : "max-width",
938
+ value
939
+ );
940
+ maxHeightRaw = h;
941
+ resolveCap();
942
+ deps.recompute();
943
+ },
944
+ resolveCap,
945
+ clampTo,
946
+ getCap: () => maxHeightCap
947
+ };
948
+ }
949
+
770
950
  // src/core/features/fit-measurement.ts
771
951
  var measureSheetNatural = (element, content, vertical) => {
772
952
  if (!content) {
@@ -898,13 +1078,6 @@ var WriteSentinel = class {
898
1078
  }
899
1079
  };
900
1080
 
901
- // src/core/primitives/devWarn.ts
902
- function devWarn(...args) {
903
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
904
- console.warn(...args);
905
- }
906
- }
907
-
908
1081
  // src/core/primitives/overlay-position.ts
909
1082
  function resolveSheetAnchoredStyle(mode, position, inset) {
910
1083
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
@@ -2359,7 +2532,7 @@ var matchesRange = (def, progress) => {
2359
2532
  };
2360
2533
  function installScrimStages(deps, opts) {
2361
2534
  const doc = deps.host.ownerDocument;
2362
- const entries = opts.stages.map((def) => {
2535
+ const entries2 = opts.stages.map((def) => {
2363
2536
  const wrapper = doc.createElement("div");
2364
2537
  wrapper.className = "bs-scrim-stage";
2365
2538
  const ws = wrapper.style;
@@ -2380,9 +2553,9 @@ function installScrimStages(deps, opts) {
2380
2553
  const resolveActive = () => {
2381
2554
  const state = deps.getState();
2382
2555
  if (state.size === 0) {
2383
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2556
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2384
2557
  }
2385
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2558
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2386
2559
  };
2387
2560
  const hide = (entry, animate) => {
2388
2561
  entry.inFlight?.cancel();
@@ -2431,13 +2604,13 @@ function installScrimStages(deps, opts) {
2431
2604
  deps.on("open", () => update(true)),
2432
2605
  deps.on("close", () => update(true))
2433
2606
  ];
2434
- if (entries.some((e) => e.def.forRange)) {
2607
+ if (entries2.some((e) => e.def.forRange)) {
2435
2608
  offs.push(deps.on("progress", () => update(true)));
2436
2609
  }
2437
2610
  update(false);
2438
2611
  return () => {
2439
2612
  offs.forEach((off) => off());
2440
- for (const entry of entries) {
2613
+ for (const entry of entries2) {
2441
2614
  entry.inFlight?.cancel();
2442
2615
  entry.inFlight = null;
2443
2616
  if (entry.wrapper.parentNode) {
@@ -2550,8 +2723,7 @@ var BottomSheetEngine = class {
2550
2723
  __publicField(this, "disableCloseFlag");
2551
2724
  __publicField(this, "disableDragFlag");
2552
2725
  __publicField(this, "closeOnRouteChange");
2553
- __publicField(this, "maxHeightCap");
2554
- __publicField(this, "maxHeightRaw");
2726
+ __publicField(this, "maxHeight");
2555
2727
  __publicField(this, "snapPointsRaw");
2556
2728
  __publicField(this, "snaps");
2557
2729
  __publicField(this, "activeId");
@@ -2662,11 +2834,20 @@ var BottomSheetEngine = class {
2662
2834
  auditVhUsage(this.snapPointsRaw);
2663
2835
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2664
2836
  this.rootEl = this.element.closest(".bs-root");
2837
+ this.maxHeight = createMaxHeightController({
2838
+ element: this.element,
2839
+ mode: this.mode,
2840
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2841
+ setMaxAxisSize: (size) => {
2842
+ this.snaps.setMaxAxisSize(size);
2843
+ },
2844
+ recompute: () => this.recompute()
2845
+ });
2665
2846
  this.fitDeps = {
2666
2847
  element: this.element,
2667
2848
  scrollContainer: this.scrollContainer,
2668
2849
  mode: this.mode,
2669
- getMaxHeightCap: () => this.maxHeightCap
2850
+ getMaxHeightCap: () => this.maxHeight.getCap()
2670
2851
  };
2671
2852
  this.snaps = new SnapResolver(
2672
2853
  this.snapPointsRaw,
@@ -2688,8 +2869,8 @@ var BottomSheetEngine = class {
2688
2869
  this.attach();
2689
2870
  this.registerInStack();
2690
2871
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2691
- const io = new IntersectionObserver((entries) => {
2692
- for (const entry of entries) {
2872
+ const io = new IntersectionObserver((entries2) => {
2873
+ for (const entry of entries2) {
2693
2874
  if (!entry.isIntersecting) continue;
2694
2875
  if (this.destroyed) return;
2695
2876
  this.snaps.recompute();
@@ -2777,7 +2958,7 @@ var BottomSheetEngine = class {
2777
2958
  }
2778
2959
  use(plugin) {
2779
2960
  if (this.destroyed) {
2780
- console.warn(
2961
+ devWarn(
2781
2962
  `[BottomSheet] use("${plugin.name}") called on destroyed engine \u2014 ignored.`
2782
2963
  );
2783
2964
  return this;
@@ -2819,6 +3000,9 @@ var BottomSheetEngine = class {
2819
3000
  getAllowedIds() {
2820
3001
  return this.snaps.getAllowedIds().slice();
2821
3002
  }
3003
+ getResolvedSnaps() {
3004
+ return this.snaps.getResolvedSnaps();
3005
+ }
2822
3006
  async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2823
3007
  if (this.destroyed) return;
2824
3008
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2827,11 +3011,11 @@ var BottomSheetEngine = class {
2827
3011
  if (externalSignal?.aborted) return;
2828
3012
  const target = this.snaps.findById(id);
2829
3013
  if (!target) {
2830
- console.warn(`[BottomSheet] unknown snap id: ${id}`);
3014
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2831
3015
  return;
2832
3016
  }
2833
3017
  if (!this.snaps.getAllowedIds().includes(id)) {
2834
- console.warn(`[BottomSheet] snap "${id}" is not in allowed list`);
3018
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2835
3019
  return;
2836
3020
  }
2837
3021
  if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
@@ -2963,42 +3147,11 @@ var BottomSheetEngine = class {
2963
3147
  }
2964
3148
  setRadius(r) {
2965
3149
  if (this.destroyed) return;
2966
- this.element.style.setProperty(
2967
- "--bs-radius",
2968
- typeof r === "number" ? `${r}px` : r
2969
- );
3150
+ this.maxHeight.setRadius(r);
2970
3151
  }
2971
3152
  setMaxHeight(h) {
2972
3153
  if (this.destroyed) return;
2973
- const value = typeof h === "number" ? `${h}px` : h;
2974
- const axis = layoutAxis(this.mode);
2975
- this.element.style.setProperty(
2976
- axis === "height" ? "max-height" : "max-width",
2977
- value
2978
- );
2979
- this.maxHeightRaw = h;
2980
- this.resolveMaxHeightCap();
2981
- this.recompute();
2982
- }
2983
- resolveMaxHeightCap() {
2984
- const raw = this.maxHeightRaw;
2985
- if (raw === void 0) {
2986
- this.maxHeightCap = void 0;
2987
- return;
2988
- }
2989
- if (typeof raw === "number") {
2990
- this.maxHeightCap = raw;
2991
- return;
2992
- }
2993
- if (typeof window === "undefined") return;
2994
- const measured = resolveSnap(raw, this.mode);
2995
- this.maxHeightCap = measured > 0 ? measured : void 0;
2996
- }
2997
- clampToMaxHeight() {
2998
- if (this.maxHeightCap === void 0) return;
2999
- if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
3000
- this.snaps.setMaxAxisSize(this.maxHeightCap);
3001
- }
3154
+ this.maxHeight.setMaxHeight(h);
3002
3155
  }
3003
3156
  setAllowed(ids, snap) {
3004
3157
  if (this.destroyed) return;
@@ -3086,7 +3239,7 @@ var BottomSheetEngine = class {
3086
3239
  };
3087
3240
  const host = this.screenComponent?.parentElement;
3088
3241
  if (!host) {
3089
- console.warn(
3242
+ devWarn(
3090
3243
  "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3091
3244
  );
3092
3245
  return () => {
@@ -3136,9 +3289,9 @@ var BottomSheetEngine = class {
3136
3289
  }
3137
3290
  recompute() {
3138
3291
  if (this.destroyed) return;
3139
- this.resolveMaxHeightCap();
3292
+ this.maxHeight.resolveCap();
3140
3293
  this.snaps.recompute();
3141
- this.clampToMaxHeight();
3294
+ this.maxHeight.clampTo();
3142
3295
  this.scrim.invalidateOpacityCache();
3143
3296
  if (this.gesture?.isDragging) return;
3144
3297
  const current = this.snaps.findById(this.activeId);
@@ -3188,42 +3341,18 @@ var BottomSheetEngine = class {
3188
3341
  return this.currentAbort.signal;
3189
3342
  }
3190
3343
  emitBeforeSnap(target, previousId) {
3191
- let cancelled = false;
3192
- let frozen = false;
3193
- this.emit("before-snap", {
3194
- id: target.id,
3195
- size: target.size,
3196
- previousId,
3197
- cancel: () => {
3198
- if (frozen) {
3199
- console.warn(
3200
- "[BottomSheet] before-snap.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3201
- );
3202
- return;
3203
- }
3204
- cancelled = true;
3205
- }
3206
- });
3207
- frozen = true;
3208
- return cancelled;
3344
+ return emitCancelable(
3345
+ (payload) => this.emit("before-snap", payload),
3346
+ { id: target.id, size: target.size, previousId },
3347
+ "before-snap"
3348
+ );
3209
3349
  }
3210
3350
  emitBeforeClose(reason) {
3211
- let cancelled = false;
3212
- let frozen = false;
3213
- this.emit("before-close", {
3214
- reason,
3215
- cancel: () => {
3216
- if (frozen) {
3217
- console.warn(
3218
- "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3219
- );
3220
- return;
3221
- }
3222
- cancelled = true;
3223
- }
3224
- });
3225
- frozen = true;
3226
- return cancelled;
3351
+ return emitCancelable(
3352
+ (payload) => this.emit("before-close", payload),
3353
+ { reason },
3354
+ "before-close"
3355
+ );
3227
3356
  }
3228
3357
  applyAutoAriaLabelledBy() {
3229
3358
  if (typeof document === "undefined") return;
@@ -3238,16 +3367,16 @@ var BottomSheetEngine = class {
3238
3367
  this.element.setAttribute("aria-labelledby", titled.id);
3239
3368
  }
3240
3369
  recomputeSnaps() {
3241
- this.resolveMaxHeightCap();
3370
+ this.maxHeight.resolveCap();
3242
3371
  this.snaps.setRaw(this.snapPointsRaw);
3243
- this.clampToMaxHeight();
3372
+ this.maxHeight.clampTo();
3244
3373
  this.scrim.invalidateOpacityCache();
3245
3374
  }
3246
3375
  auditDuplicateSnapIds(points) {
3247
3376
  const seen = /* @__PURE__ */ new Set();
3248
3377
  for (const p of points) {
3249
3378
  if (seen.has(p.id)) {
3250
- console.warn(
3379
+ devWarn(
3251
3380
  `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
3252
3381
  );
3253
3382
  }
@@ -3414,7 +3543,15 @@ var BottomSheetEngine = class {
3414
3543
  setSize: (size) => {
3415
3544
  this.size = size;
3416
3545
  },
3417
- applySize: (size) => this.applySize(size)
3546
+ applySize: (size) => this.applySize(size),
3547
+ cancelInFlight: () => {
3548
+ this.animation.cancel();
3549
+ },
3550
+ newCycle: () => {
3551
+ this.newCycle();
3552
+ },
3553
+ isAnimating: () => this.animation.isAnimating,
3554
+ resyncAfterCancel: () => this.resyncAfterResize()
3418
3555
  }));
3419
3556
  }
3420
3557
  this.teardowns.add(installSliderKeyboard({
@@ -3450,9 +3587,9 @@ var BottomSheetEngine = class {
3450
3587
  return;
3451
3588
  }
3452
3589
  if (this.emitBeforeSnap(target, previousId)) {
3453
- const restore = this.snaps.findById(previousId);
3454
- if (restore) {
3455
- void this.animation.animateTo(restore.size, 0);
3590
+ const restore2 = this.snaps.findById(previousId);
3591
+ if (restore2) {
3592
+ void this.animation.animateTo(restore2.size, 0);
3456
3593
  }
3457
3594
  return;
3458
3595
  }
@@ -3575,6 +3712,72 @@ var BottomSheetEngine = class {
3575
3712
  sheetStack.update();
3576
3713
  }
3577
3714
  };
3715
+ function useEnginePropSync(engineRef, opts) {
3716
+ const optsRef = useRef(opts);
3717
+ optsRef.current = opts;
3718
+ const allowedKey = (opts.allowed ?? opts.snapPoints.map((p) => p.id)).join(
3719
+ "\0"
3720
+ );
3721
+ const defKey = opts.snapPoints.map((p) => `${p.id}${String(p.size)}`).join("\0");
3722
+ const defApplied = useRef(false);
3723
+ useEffect(() => {
3724
+ if (!defApplied.current) {
3725
+ defApplied.current = true;
3726
+ return;
3727
+ }
3728
+ const engine = engineRef.current;
3729
+ if (!engine) return;
3730
+ const current = optsRef.current;
3731
+ engine.setSnapPoints(
3732
+ current.snapPoints,
3733
+ current.allowed ? Array.from(current.allowed) : void 0
3734
+ );
3735
+ }, [defKey]);
3736
+ useEffect(() => {
3737
+ if (!engineRef.current) return;
3738
+ const ids = opts.allowed ? Array.from(opts.allowed) : opts.snapPoints.map((p) => p.id);
3739
+ engineRef.current.setAllowed(ids);
3740
+ }, [allowedKey]);
3741
+ useEffect(() => {
3742
+ if (!engineRef.current) return;
3743
+ if (opts.backdropColor !== void 0) {
3744
+ engineRef.current.setScrimColor(opts.backdropColor);
3745
+ }
3746
+ }, [opts.backdropColor]);
3747
+ useEffect(() => {
3748
+ if (!engineRef.current) return;
3749
+ if (opts.backdropOpacity !== void 0) {
3750
+ engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3751
+ }
3752
+ }, [opts.backdropOpacity]);
3753
+ useEffect(() => {
3754
+ if (!engineRef.current) return;
3755
+ if (opts.radius !== void 0) {
3756
+ engineRef.current.setRadius(opts.radius);
3757
+ }
3758
+ }, [opts.radius]);
3759
+ useEffect(() => {
3760
+ if (!engineRef.current) return;
3761
+ if (opts.maxHeight !== void 0) {
3762
+ engineRef.current.setMaxHeight(opts.maxHeight);
3763
+ }
3764
+ }, [opts.maxHeight]);
3765
+ useEffect(() => {
3766
+ if (opts.persistent !== void 0) {
3767
+ engineRef.current?.setPersistent(opts.persistent);
3768
+ }
3769
+ }, [opts.persistent]);
3770
+ useEffect(() => {
3771
+ if (opts.disableClose !== void 0) {
3772
+ engineRef.current?.setDisableClose(opts.disableClose);
3773
+ }
3774
+ }, [opts.disableClose]);
3775
+ useEffect(() => {
3776
+ if (opts.disableDrag !== void 0) {
3777
+ engineRef.current?.setDisableDrag(opts.disableDrag);
3778
+ }
3779
+ }, [opts.disableDrag]);
3780
+ }
3578
3781
 
3579
3782
  // src/core/features/teleport.ts
3580
3783
  var resolveTeleportTarget = (target) => {
@@ -3669,6 +3872,12 @@ function useBottomSheet(opts) {
3669
3872
  engine.on("before-close", (payload) => {
3670
3873
  optsRef.current.onBeforeClose?.(payload);
3671
3874
  }),
3875
+ engine.on("open", (payload) => {
3876
+ optsRef.current.onOpen?.(payload.id);
3877
+ }),
3878
+ engine.on("close", () => {
3879
+ optsRef.current.onClose?.();
3880
+ }),
3672
3881
  engine.on("opened", (payload) => {
3673
3882
  optsRef.current.onOpened?.(payload.id);
3674
3883
  }),
@@ -3684,6 +3893,20 @@ function useBottomSheet(opts) {
3684
3893
  optsRef.current.onDragEnd?.(payload);
3685
3894
  })
3686
3895
  ];
3896
+ if (optsRef.current.onDrag) {
3897
+ offs.push(
3898
+ engine.on("drag", (payload) => {
3899
+ optsRef.current.onDrag?.(payload);
3900
+ })
3901
+ );
3902
+ }
3903
+ if (optsRef.current.onProgress) {
3904
+ offs.push(
3905
+ engine.on("progress", (payload) => {
3906
+ optsRef.current.onProgress?.(payload);
3907
+ })
3908
+ );
3909
+ }
3687
3910
  return () => {
3688
3911
  offs.forEach((off) => off());
3689
3912
  restoreTeleport?.();
@@ -3693,68 +3916,17 @@ function useBottomSheet(opts) {
3693
3916
  setMounted((n) => n + 1);
3694
3917
  };
3695
3918
  }, []);
3696
- const allowedKey = (opts.allowed ?? opts.snapPoints.map((p) => p.id)).join(
3697
- "\0"
3698
- );
3699
- const defKey = opts.snapPoints.map((p) => `${p.id}${String(p.size)}`).join("\0");
3700
- const defApplied = useRef(false);
3701
- useEffect(() => {
3702
- if (!defApplied.current) {
3703
- defApplied.current = true;
3704
- return;
3705
- }
3706
- const engine = engineRef.current;
3707
- if (!engine) return;
3708
- const current = optsRef.current;
3709
- engine.setSnapPoints(
3710
- current.snapPoints,
3711
- current.allowed ? Array.from(current.allowed) : void 0
3712
- );
3713
- }, [defKey]);
3714
- useEffect(() => {
3715
- if (!engineRef.current) return;
3716
- const ids = opts.allowed ? Array.from(opts.allowed) : opts.snapPoints.map((p) => p.id);
3717
- engineRef.current.setAllowed(ids);
3718
- }, [allowedKey]);
3719
- useEffect(() => {
3720
- if (!engineRef.current) return;
3721
- if (opts.backdropColor !== void 0) {
3722
- engineRef.current.setScrimColor(opts.backdropColor);
3723
- }
3724
- }, [opts.backdropColor]);
3725
- useEffect(() => {
3726
- if (!engineRef.current) return;
3727
- if (opts.backdropOpacity !== void 0) {
3728
- engineRef.current.setBackdropRange([0, opts.backdropOpacity]);
3729
- }
3730
- }, [opts.backdropOpacity]);
3731
- useEffect(() => {
3732
- if (!engineRef.current) return;
3733
- if (opts.radius !== void 0) {
3734
- engineRef.current.setRadius(opts.radius);
3735
- }
3736
- }, [opts.radius]);
3737
- useEffect(() => {
3738
- if (!engineRef.current) return;
3739
- if (opts.maxHeight !== void 0) {
3740
- engineRef.current.setMaxHeight(opts.maxHeight);
3741
- }
3742
- }, [opts.maxHeight]);
3743
- useEffect(() => {
3744
- if (opts.persistent !== void 0) {
3745
- engineRef.current?.setPersistent(opts.persistent);
3746
- }
3747
- }, [opts.persistent]);
3748
- useEffect(() => {
3749
- if (opts.disableClose !== void 0) {
3750
- engineRef.current?.setDisableClose(opts.disableClose);
3751
- }
3752
- }, [opts.disableClose]);
3753
- useEffect(() => {
3754
- if (opts.disableDrag !== void 0) {
3755
- engineRef.current?.setDisableDrag(opts.disableDrag);
3756
- }
3757
- }, [opts.disableDrag]);
3919
+ useEnginePropSync(engineRef, {
3920
+ snapPoints: opts.snapPoints,
3921
+ allowed: opts.allowed,
3922
+ backdropColor: opts.backdropColor,
3923
+ backdropOpacity: opts.backdropOpacity,
3924
+ radius: opts.radius,
3925
+ maxHeight: opts.maxHeight,
3926
+ persistent: opts.persistent,
3927
+ disableClose: opts.disableClose,
3928
+ disableDrag: opts.disableDrag
3929
+ });
3758
3930
  const subscribe = useCallback((fn) => {
3759
3931
  subscribersRef.current.add(fn);
3760
3932
  return () => subscribersRef.current.delete(fn);
@@ -3863,10 +4035,14 @@ var BottomSheet = forwardRef(
3863
4035
  scrimStages,
3864
4036
  onChange,
3865
4037
  onBeforeClose,
4038
+ onOpen,
4039
+ onClose,
3866
4040
  onOpened,
3867
4041
  onClosed,
3868
4042
  onDragStart,
3869
4043
  onDragEnd,
4044
+ onDrag,
4045
+ onProgress,
3870
4046
  ariaLabel = "Bottom sheet",
3871
4047
  ariaLabelledBy,
3872
4048
  noSSR = false,
@@ -3892,10 +4068,14 @@ var BottomSheet = forwardRef(
3892
4068
  } = useBottomSheet({
3893
4069
  ...engineOpts,
3894
4070
  onBeforeClose,
4071
+ onOpen,
4072
+ onClose,
3895
4073
  onOpened,
3896
4074
  onClosed,
3897
4075
  onDragStart,
3898
- onDragEnd
4076
+ onDragEnd,
4077
+ onDrag,
4078
+ onProgress
3899
4079
  });
3900
4080
  const ariaModal = engineOpts.focusTrap === true;
3901
4081
  const lastAnnouncedRef = useRef("");
@@ -4179,7 +4359,7 @@ function installOverlaySwipe(deps) {
4179
4359
  };
4180
4360
  }
4181
4361
 
4182
- // src/core/overlay.ts
4362
+ // src/core/overlay-options.ts
4183
4363
  var OVERLAY_PRESETS = Object.freeze({
4184
4364
  sheet: Object.freeze({
4185
4365
  enterAnimation: "slide",
@@ -4210,6 +4390,8 @@ var OVERLAY_PRESETS = Object.freeze({
4210
4390
  swipeToClose: false
4211
4391
  })
4212
4392
  });
4393
+
4394
+ // src/core/overlay.ts
4213
4395
  var OverlayEngine = class {
4214
4396
  constructor(opts) {
4215
4397
  __publicField(this, "id", nextInstanceId("ovl"));
@@ -4244,7 +4426,7 @@ var OverlayEngine = class {
4244
4426
  __publicField(this, "detachSwipe", null);
4245
4427
  __publicField(this, "respectReducedMotionEnabled");
4246
4428
  __publicField(this, "isTop", true);
4247
- __publicField(this, "backPushed", false);
4429
+ __publicField(this, "backMarker", null);
4248
4430
  __publicField(this, "movedToTarget", false);
4249
4431
  __publicField(this, "onOpenCb");
4250
4432
  __publicField(this, "onCloseCb");
@@ -4256,7 +4438,6 @@ var OverlayEngine = class {
4256
4438
  __publicField(this, "bus", createEventBus());
4257
4439
  __publicField(this, "detachEscape", null);
4258
4440
  __publicField(this, "detachBackdrop", null);
4259
- __publicField(this, "detachHardwareBack", null);
4260
4441
  __publicField(this, "releaseStackEntry", null);
4261
4442
  __publicField(this, "lifecycle");
4262
4443
  this.element = opts.element;
@@ -4286,7 +4467,7 @@ var OverlayEngine = class {
4286
4467
  this.onOpenCb = opts.onOpen;
4287
4468
  this.onCloseCb = opts.onClose;
4288
4469
  if (this.peekRaw !== void 0 && this.edge !== "bottom") {
4289
- console.warn(
4470
+ devWarn(
4290
4471
  "[OverlayEngine] `peek` is only supported on edge: 'bottom' \u2014 ignoring."
4291
4472
  );
4292
4473
  this.peekRaw = void 0;
@@ -4353,10 +4534,9 @@ var OverlayEngine = class {
4353
4534
  this.element.style.opacity = "1";
4354
4535
  if (this.backdrop) {
4355
4536
  this.backdrop.style.opacity = String(this.backdropOpacity);
4356
- this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
4537
+ this.syncBackdropPointerEvents();
4357
4538
  if (this.backdropFilter) {
4358
- this.backdrop.style.backdropFilter = this.backdropFilter;
4359
- this.backdrop.style.webkitBackdropFilter = this.backdropFilter;
4539
+ this.applyBackdropFilter(this.backdropFilter);
4360
4540
  }
4361
4541
  }
4362
4542
  try {
@@ -4376,8 +4556,7 @@ var OverlayEngine = class {
4376
4556
  this.backdrop.style.opacity = "0";
4377
4557
  this.backdrop.style.pointerEvents = "none";
4378
4558
  if (this.backdropFilter) {
4379
- this.backdrop.style.backdropFilter = "";
4380
- this.backdrop.style.webkitBackdropFilter = "";
4559
+ this.applyBackdropFilter(void 0);
4381
4560
  }
4382
4561
  }
4383
4562
  this.element.setAttribute("data-state", "closed");
@@ -4412,7 +4591,10 @@ var OverlayEngine = class {
4412
4591
  this.isOpen_ = false;
4413
4592
  this.isAnimating_ = true;
4414
4593
  sheetStack.update();
4415
- this.popBackMarker(reason);
4594
+ if (reason !== "back" && this.backMarker) {
4595
+ popBackMarker(this.backMarker);
4596
+ }
4597
+ this.backMarker = null;
4416
4598
  this.element.setAttribute("data-state", "exiting");
4417
4599
  const dur = this.effectiveDuration();
4418
4600
  const transitionStr = `transform ${dur}ms ${this.exitEasing}, opacity ${dur}ms ease-in`;
@@ -4435,8 +4617,7 @@ var OverlayEngine = class {
4435
4617
  this.backdrop.style.opacity = "0";
4436
4618
  this.backdrop.style.pointerEvents = "none";
4437
4619
  if (this.backdropFilter) {
4438
- this.backdrop.style.backdropFilter = "";
4439
- this.backdrop.style.webkitBackdropFilter = "";
4620
+ this.applyBackdropFilter(void 0);
4440
4621
  }
4441
4622
  }
4442
4623
  this.releaseInteractiveListeners();
@@ -4471,6 +4652,10 @@ var OverlayEngine = class {
4471
4652
  this.bus.clear();
4472
4653
  this.setWillChange(false);
4473
4654
  this.releaseInteractiveListeners();
4655
+ if (this.backMarker) {
4656
+ popBackMarker(this.backMarker);
4657
+ this.backMarker = null;
4658
+ }
4474
4659
  this.releaseStackEntry?.();
4475
4660
  this.releaseStackEntry = null;
4476
4661
  const hadOriginalParent = this.originalParent !== null;
@@ -4484,20 +4669,14 @@ var OverlayEngine = class {
4484
4669
  this.backdropOpacity = opacity;
4485
4670
  if (this.isOpen_ && this.backdrop) {
4486
4671
  this.backdrop.style.opacity = String(opacity);
4672
+ this.syncBackdropPointerEvents();
4487
4673
  }
4488
4674
  }
4489
4675
  setBackdropFilter(filter) {
4490
4676
  if (this.destroyed) return;
4491
4677
  this.backdropFilter = filter;
4492
4678
  if (!this.isOpen_ || !this.backdrop) return;
4493
- const style = this.backdrop.style;
4494
- if (filter) {
4495
- this.backdrop.style.backdropFilter = filter;
4496
- style.webkitBackdropFilter = filter;
4497
- } else {
4498
- this.backdrop.style.backdropFilter = "";
4499
- style.webkitBackdropFilter = "";
4500
- }
4679
+ this.applyBackdropFilter(filter);
4501
4680
  }
4502
4681
  setSwipeToClose(config) {
4503
4682
  if (this.destroyed) return;
@@ -4566,16 +4745,10 @@ var OverlayEngine = class {
4566
4745
  if (this.isOpen_ && this.backdrop) {
4567
4746
  if (merged.backdropOpacity !== void 0) {
4568
4747
  this.backdrop.style.opacity = String(this.backdropOpacity);
4748
+ this.syncBackdropPointerEvents();
4569
4749
  }
4570
4750
  if (merged.backdropFilter !== void 0) {
4571
- const style = this.backdrop.style;
4572
- if (this.backdropFilter) {
4573
- this.backdrop.style.backdropFilter = this.backdropFilter;
4574
- style.webkitBackdropFilter = this.backdropFilter;
4575
- } else {
4576
- this.backdrop.style.backdropFilter = "";
4577
- style.webkitBackdropFilter = "";
4578
- }
4751
+ this.applyBackdropFilter(this.backdropFilter);
4579
4752
  }
4580
4753
  }
4581
4754
  if (opts.duration !== void 0) this.duration = opts.duration;
@@ -4644,19 +4817,6 @@ var OverlayEngine = class {
4644
4817
  effectiveDuration() {
4645
4818
  return this.respectReducedMotionEnabled && prefersReducedMotion() ? 0 : this.duration;
4646
4819
  }
4647
- popBackMarker(reason) {
4648
- if (!this.backPushed) return;
4649
- this.backPushed = false;
4650
- if (reason === "back") return;
4651
- const state = history.state;
4652
- if (state && state.__bsOverlay === this.id) {
4653
- try {
4654
- history.back();
4655
- } catch {
4656
- return;
4657
- }
4658
- }
4659
- }
4660
4820
  resolvePeekPx(raw) {
4661
4821
  if (typeof raw === "number") return Math.max(0, raw);
4662
4822
  try {
@@ -4710,6 +4870,21 @@ var OverlayEngine = class {
4710
4870
  this.backdrop.style.pointerEvents = "none";
4711
4871
  }
4712
4872
  }
4873
+ syncBackdropPointerEvents() {
4874
+ if (!this.backdrop) return;
4875
+ this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
4876
+ }
4877
+ applyBackdropFilter(filter) {
4878
+ if (!this.backdrop) return;
4879
+ const style = this.backdrop.style;
4880
+ if (filter) {
4881
+ this.backdrop.style.backdropFilter = filter;
4882
+ style.webkitBackdropFilter = filter;
4883
+ } else {
4884
+ this.backdrop.style.backdropFilter = "";
4885
+ style.webkitBackdropFilter = "";
4886
+ }
4887
+ }
4713
4888
  setWillChange(on) {
4714
4889
  this.element.style.willChange = on ? "transform, opacity" : "";
4715
4890
  if (this.backdrop) {
@@ -4757,21 +4932,6 @@ var OverlayEngine = class {
4757
4932
  this.backdrop.addEventListener("click", handler);
4758
4933
  this.detachBackdrop = () => this.backdrop?.removeEventListener("click", handler);
4759
4934
  }
4760
- if (this.closeOnBackEnabled && typeof window !== "undefined" && !this.detachHardwareBack) {
4761
- try {
4762
- history.pushState({ __bsOverlay: this.id }, "");
4763
- this.backPushed = true;
4764
- } catch {
4765
- }
4766
- const onPop = () => {
4767
- if (this.isOpen_ && this.isTop) {
4768
- this.backPushed = false;
4769
- void this.close("back");
4770
- }
4771
- };
4772
- window.addEventListener("popstate", onPop);
4773
- this.detachHardwareBack = () => window.removeEventListener("popstate", onPop);
4774
- }
4775
4935
  if (this.closeOnOutsidePointerEnabled && !this.detachOutsidePointer) {
4776
4936
  const handler = (e) => {
4777
4937
  if (!this.isOpen_) return;
@@ -4784,14 +4944,20 @@ var OverlayEngine = class {
4784
4944
  document.addEventListener("pointerdown", handler, true);
4785
4945
  this.detachOutsidePointer = () => document.removeEventListener("pointerdown", handler, true);
4786
4946
  }
4947
+ if (this.closeOnBackEnabled && this.backMarker === null && typeof window !== "undefined") {
4948
+ this.backMarker = pushBackMarker({
4949
+ isOpen: () => this.isOpen_,
4950
+ isTop: () => this.isTop,
4951
+ close: () => this.close("back"),
4952
+ markerState: () => ({ __bsOverlay: this.id })
4953
+ });
4954
+ }
4787
4955
  }
4788
4956
  releaseInteractiveListeners() {
4789
4957
  this.detachEscape?.();
4790
4958
  this.detachEscape = null;
4791
4959
  this.detachBackdrop?.();
4792
4960
  this.detachBackdrop = null;
4793
- this.detachHardwareBack?.();
4794
- this.detachHardwareBack = null;
4795
4961
  this.detachSwipe?.();
4796
4962
  this.detachSwipe = null;
4797
4963
  this.detachOutsidePointer?.();
@@ -4942,6 +5108,8 @@ var Overlay = forwardRef(function Overlay2(props, ref) {
4942
5108
  )
4943
5109
  ] });
4944
5110
  });
5111
+
5112
+ // src/react/config-validation.ts
4945
5113
  var SUPPORTED_VERSION = 1;
4946
5114
  var TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
4947
5115
  "version",
@@ -4966,7 +5134,7 @@ var warnUnknownKeys = (obj, allowed, context) => {
4966
5134
  if (!obj || typeof obj !== "object") return;
4967
5135
  const unknown = Object.keys(obj).filter((k) => !allowed.has(k));
4968
5136
  if (unknown.length === 0) return;
4969
- console.warn(
5137
+ devWarn(
4970
5138
  `[BottomSheetFromConfig] Unknown ${unknown.length === 1 ? "key" : "keys"} in ${context}: ${unknown.map((k) => `"${k}"`).join(", ")}. Allowed: ${[...allowed].join(", ")}. Likely a typo \u2014 values ignored.`
4971
5139
  );
4972
5140
  };
@@ -4976,7 +5144,7 @@ function validateConfig(config) {
4976
5144
  validatedConfigs.add(config);
4977
5145
  let ok = true;
4978
5146
  if (config.version !== SUPPORTED_VERSION) {
4979
- console.warn(
5147
+ devWarn(
4980
5148
  `[BottomSheetFromConfig] Unsupported config version ${String(
4981
5149
  config.version
4982
5150
  )}; expected ${SUPPORTED_VERSION}. Falling back to defaults.`
@@ -4984,7 +5152,7 @@ function validateConfig(config) {
4984
5152
  return false;
4985
5153
  }
4986
5154
  if (!Array.isArray(config.snapPoints) || config.snapPoints.length === 0) {
4987
- console.warn(
5155
+ devWarn(
4988
5156
  "[BottomSheetFromConfig] config.snapPoints is missing or empty; using a single full-height fallback snap."
4989
5157
  );
4990
5158
  ok = false;