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