@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
@@ -183,11 +183,18 @@ var resolveSnap = (point, mode, measureFit) => {
183
183
  return Math.max(0, el.getBoundingClientRect().width);
184
184
  };
185
185
 
186
+ // src/core/primitives/devWarn.ts
187
+ function devWarn(...args) {
188
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
189
+ console.warn(...args);
190
+ }
191
+ }
192
+
186
193
  // src/core/primitives/snap-points.ts
187
194
  var resolveSnapList = (points, mode, measureFit) => points.map((p) => {
188
195
  const raw = resolveSnap(p.size, mode, measureFit);
189
196
  if (!Number.isFinite(raw) || raw < 0) {
190
- console.warn(
197
+ devWarn(
191
198
  `[BottomSheet] snap "${p.id}" resolved to invalid size (${String(p.size)} \u2192 ${raw}); clamped to 0.`
192
199
  );
193
200
  return { id: p.id, size: 0 };
@@ -199,7 +206,7 @@ var auditVhUsage = (points) => {
199
206
  for (const p of points) {
200
207
  if (typeof p.size !== "string") continue;
201
208
  if (!VH_NOT_DVH.test(p.size)) continue;
202
- console.warn(
209
+ devWarn(
203
210
  `[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`
204
211
  );
205
212
  }
@@ -253,6 +260,26 @@ var findDragSettleTarget = (input) => {
253
260
  );
254
261
  };
255
262
 
263
+ // src/core/primitives/cancelable-emit.ts
264
+ function emitCancelable(emit, base, warnLabel) {
265
+ let cancelled = false;
266
+ let frozen = false;
267
+ emit({
268
+ ...base,
269
+ cancel: () => {
270
+ if (frozen) {
271
+ devWarn(
272
+ `[BottomSheet] ${warnLabel}.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener.`
273
+ );
274
+ return;
275
+ }
276
+ cancelled = true;
277
+ }
278
+ });
279
+ frozen = true;
280
+ return cancelled;
281
+ }
282
+
256
283
  // src/core/primitives/snap-resolver.ts
257
284
  var SnapResolver = class {
258
285
  constructor(raw, allowed, mode, measureFit, onMaxAxisSizeChange) {
@@ -397,7 +424,9 @@ function notifyLinkedSheets(sheets, self) {
397
424
  try {
398
425
  if (linked.state.size === 0) continue;
399
426
  const ids = linked.getAllowedIds();
400
- const target = ids.find((id) => id !== "closed");
427
+ const resolved = linked.getResolvedSnaps();
428
+ const sizeOf = (id) => resolved.find((s) => s.id === id)?.size ?? 0;
429
+ const target = ids.find((id) => sizeOf(id) > 0);
401
430
  if (target && target !== linked.state.activeId) {
402
431
  void linked.snapTo(target);
403
432
  }
@@ -409,46 +438,198 @@ function notifyLinkedSheets(sheets, self) {
409
438
  }
410
439
  }
411
440
 
412
- // src/core/features/route.ts
413
- var closers = [];
414
- var handlerInstalled = false;
441
+ // src/core/features/history-coordinator.ts
442
+ var entries = [];
443
+ var subscribers = /* @__PURE__ */ new Set();
415
444
  var suppress = 0;
416
- var ensureHandler = () => {
417
- if (handlerInstalled || typeof window === "undefined") return;
418
- handlerInstalled = true;
419
- window.addEventListener("popstate", () => {
420
- if (suppress > 0) {
421
- suppress -= 1;
422
- return;
445
+ var internalDepth = 0;
446
+ var pendingRebrand = null;
447
+ var listenerInstalled = false;
448
+ var patchInstalled = false;
449
+ var origPushState = null;
450
+ var origReplaceState = null;
451
+ var patchedPushState = null;
452
+ var patchedReplaceState = null;
453
+ var hasWindow = () => typeof window !== "undefined";
454
+ var hasHistory = () => typeof history !== "undefined";
455
+ var runInternal = (fn) => {
456
+ internalDepth += 1;
457
+ try {
458
+ fn();
459
+ } finally {
460
+ internalDepth -= 1;
461
+ }
462
+ };
463
+ var notifyRouteChange = () => {
464
+ const snapshot = Array.from(subscribers);
465
+ for (const fn of snapshot) fn();
466
+ };
467
+ var ensureListener = () => {
468
+ if (listenerInstalled || !hasWindow()) return;
469
+ window.addEventListener("popstate", onPopState);
470
+ listenerInstalled = true;
471
+ };
472
+ var maybeTeardownListener = () => {
473
+ if (!listenerInstalled) return;
474
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
475
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
476
+ listenerInstalled = false;
477
+ };
478
+ var installPatch = () => {
479
+ if (patchInstalled || !hasHistory()) return;
480
+ origPushState = history.pushState;
481
+ origReplaceState = history.replaceState;
482
+ patchedPushState = function(...args) {
483
+ origPushState.apply(this, args);
484
+ if (internalDepth === 0) notifyRouteChange();
485
+ };
486
+ patchedReplaceState = function(...args) {
487
+ origReplaceState.apply(this, args);
488
+ if (internalDepth === 0) notifyRouteChange();
489
+ };
490
+ history.pushState = patchedPushState;
491
+ history.replaceState = patchedReplaceState;
492
+ patchInstalled = true;
493
+ };
494
+ var removePatch = () => {
495
+ if (!patchInstalled) return;
496
+ if (history.pushState === patchedPushState && origPushState) {
497
+ history.pushState = origPushState;
498
+ }
499
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
500
+ history.replaceState = origReplaceState;
501
+ }
502
+ origPushState = null;
503
+ origReplaceState = null;
504
+ patchedPushState = null;
505
+ patchedReplaceState = null;
506
+ patchInstalled = false;
507
+ };
508
+ function onPopState() {
509
+ if (suppress > 0) {
510
+ suppress -= 1;
511
+ const rebrand = pendingRebrand;
512
+ pendingRebrand = null;
513
+ if (rebrand) rebrand();
514
+ maybeTeardownListener();
515
+ return;
516
+ }
517
+ if (entries.length === 0) {
518
+ notifyRouteChange();
519
+ maybeTeardownListener();
520
+ return;
521
+ }
522
+ let target = null;
523
+ let targetIdx = -1;
524
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
525
+ const entry = entries[i];
526
+ if (!entry.surface.isOpen()) continue;
527
+ if (!target) {
528
+ target = entry;
529
+ targetIdx = i;
423
530
  }
424
- let target = null;
425
- for (let i = closers.length - 1; i >= 0; i -= 1) {
426
- const c = closers[i];
427
- if (!c.isOpen()) continue;
428
- if (!target) target = c;
429
- if (c.isTop()) {
430
- target = c;
431
- break;
432
- }
531
+ if (entry.surface.isTop()) {
532
+ target = entry;
533
+ targetIdx = i;
534
+ break;
433
535
  }
434
- if (!target) return;
435
- target.consumed = true;
436
- const idx = closers.indexOf(target);
437
- if (idx !== -1) closers.splice(idx, 1);
438
- const settled = target;
439
- void Promise.resolve(settled.close()).then(() => {
440
- if (settled.isOpen()) settled.restore();
441
- });
536
+ }
537
+ if (!target) {
538
+ const removed = entries.pop();
539
+ if (removed) removed.live = false;
540
+ maybeTeardownListener();
541
+ return;
542
+ }
543
+ target.live = false;
544
+ entries.splice(targetIdx, 1);
545
+ const settled = target;
546
+ void Promise.resolve(settled.surface.close()).then(() => {
547
+ if (settled.surface.isOpen()) restore(settled);
442
548
  });
443
- };
444
- var programmaticBack = () => {
445
- suppress += 1;
446
- try {
447
- history.back();
448
- } catch {
449
- suppress = Math.max(0, suppress - 1);
549
+ maybeTeardownListener();
550
+ }
551
+ function restore(entry) {
552
+ if (!hasWindow() || !hasHistory()) return;
553
+ entry.live = true;
554
+ runInternal(() => {
555
+ try {
556
+ history.pushState(entry.surface.markerState(), "", entry.surface.url);
557
+ } catch {
558
+ entry.live = false;
559
+ }
560
+ });
561
+ if (entry.live) {
562
+ entries.push(entry);
563
+ ensureListener();
450
564
  }
451
- };
565
+ }
566
+ function pushBackMarker(surface) {
567
+ const entry = { surface, priorUrl: void 0, live: false };
568
+ if (!hasWindow() || !hasHistory()) {
569
+ return entry;
570
+ }
571
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
572
+ runInternal(() => {
573
+ try {
574
+ history.pushState(surface.markerState(), "", surface.url);
575
+ entry.live = true;
576
+ } catch {
577
+ entry.live = false;
578
+ }
579
+ });
580
+ if (entry.live) {
581
+ entries.push(entry);
582
+ ensureListener();
583
+ }
584
+ return entry;
585
+ }
586
+ function popBackMarker(handle) {
587
+ if (!hasWindow() || !hasHistory()) return;
588
+ const entry = handle;
589
+ const idx = entries.indexOf(entry);
590
+ if (idx === -1 || !entry.live) return;
591
+ const last = entries.length - 1;
592
+ entry.live = false;
593
+ entries.splice(idx, 1);
594
+ if (idx !== last) {
595
+ const rebrandSurface = entries[entries.length - 1].surface;
596
+ const rebrandUrl = entry.priorUrl;
597
+ pendingRebrand = () => {
598
+ runInternal(() => {
599
+ history.replaceState(
600
+ rebrandSurface.markerState(),
601
+ "",
602
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
603
+ );
604
+ });
605
+ };
606
+ }
607
+ suppress += 1;
608
+ runInternal(() => {
609
+ try {
610
+ history.back();
611
+ } catch {
612
+ suppress = Math.max(0, suppress - 1);
613
+ pendingRebrand = null;
614
+ }
615
+ });
616
+ maybeTeardownListener();
617
+ }
618
+ function subscribeRouteChange(fn) {
619
+ if (!hasWindow() || !hasHistory()) return () => {
620
+ };
621
+ subscribers.add(fn);
622
+ if (subscribers.size === 1) installPatch();
623
+ ensureListener();
624
+ return () => {
625
+ if (!subscribers.has(fn)) return;
626
+ subscribers.delete(fn);
627
+ if (subscribers.size === 0) removePatch();
628
+ maybeTeardownListener();
629
+ };
630
+ }
631
+
632
+ // src/core/features/route.ts
452
633
  function installRoute(deps) {
453
634
  if (typeof window === "undefined") {
454
635
  return () => {
@@ -458,92 +639,39 @@ function installRoute(deps) {
458
639
  return () => {
459
640
  };
460
641
  }
461
- let pushed = false;
462
- let closer = null;
463
- const teardownEntry = () => {
464
- if (closer) {
465
- const idx = closers.indexOf(closer);
466
- if (idx !== -1) closers.splice(idx, 1);
467
- }
468
- const wasConsumed = closer?.consumed ?? false;
469
- closer = null;
470
- if (pushed && !wasConsumed) {
471
- pushed = false;
472
- programmaticBack();
473
- } else {
474
- pushed = false;
475
- }
476
- };
477
- const pushEntry = () => {
478
- try {
479
- if (deps.routedTo !== void 0) {
480
- history.pushState({ __bsRouted: deps.sheetId }, "", deps.routedTo);
481
- } else {
482
- history.pushState({ __bsSheet: deps.sheetId }, "");
483
- }
484
- pushed = true;
485
- } catch {
486
- pushed = false;
487
- }
488
- };
642
+ let handle = null;
489
643
  const onOpen = () => {
490
- if (closer) return;
491
- const self = {
644
+ if (handle) return;
645
+ handle = pushBackMarker({
492
646
  isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
493
- isTop: () => deps.isTopSheet(),
494
- close: () => deps.close(),
495
- restore: () => {
496
- if (deps.isDestroyed() || closer !== self) return;
497
- self.consumed = false;
498
- if (!closers.includes(self)) closers.push(self);
499
- pushEntry();
500
- },
501
- consumed: false
502
- };
503
- closer = self;
504
- ensureHandler();
505
- closers.push(self);
506
- pushEntry();
647
+ isTop: deps.isTopSheet,
648
+ close: deps.close,
649
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
650
+ url: deps.routedTo
651
+ });
652
+ };
653
+ const onClose = () => {
654
+ if (!handle) return;
655
+ popBackMarker(handle);
656
+ handle = null;
507
657
  };
508
658
  const unsubscribeOpen = deps.on("open", onOpen);
509
- const unsubscribeClose = deps.on("close", teardownEntry);
659
+ const unsubscribeClose = deps.on("close", onClose);
510
660
  if (deps.getSize() > 0) onOpen();
511
661
  return () => {
512
662
  unsubscribeOpen();
513
663
  unsubscribeClose();
514
- teardownEntry();
664
+ if (handle) {
665
+ popBackMarker(handle);
666
+ handle = null;
667
+ }
515
668
  };
516
669
  }
517
670
  function installRouteChange(deps) {
518
- if (typeof window === "undefined" || typeof history === "undefined") {
519
- return () => {
520
- };
521
- }
522
- const onChange = () => {
671
+ return subscribeRouteChange(() => {
523
672
  if (deps.isDestroyed() || deps.getSize() <= 0) return;
524
673
  void deps.close();
525
- };
526
- const wrap = (key) => {
527
- const original = history[key];
528
- const patched = function(...args) {
529
- original.apply(this, args);
530
- onChange();
531
- };
532
- history[key] = patched;
533
- return () => {
534
- if (history[key] === patched) {
535
- history[key] = original;
536
- }
537
- };
538
- };
539
- window.addEventListener("popstate", onChange);
540
- const restorePush = wrap("pushState");
541
- const restoreReplace = wrap("replaceState");
542
- return () => {
543
- window.removeEventListener("popstate", onChange);
544
- restorePush();
545
- restoreReplace();
546
- };
674
+ });
547
675
  }
548
676
 
549
677
  // src/core/features/content-swipe.ts
@@ -614,11 +742,15 @@ function installVisualViewport(deps) {
614
742
  if (clamp < deps.getMaxAxisSize()) {
615
743
  deps.setMaxAxisSize(clamp);
616
744
  }
745
+ const wasAnimating = deps.isAnimating?.() ?? false;
746
+ deps.cancelInFlight();
747
+ deps.newCycle();
617
748
  const max = deps.getMaxAxisSize();
618
749
  deps.setSize(Math.min(deps.getSize(), max));
619
750
  const current = deps.resolveActiveSnap();
620
751
  if (current && !deps.isDragging()) {
621
752
  deps.applySize(Math.min(current.size, max));
753
+ if (wasAnimating) deps.resyncAfterCancel?.();
622
754
  } else {
623
755
  deps.applySize(deps.getSize());
624
756
  }
@@ -759,6 +891,54 @@ function installFitObserver(deps) {
759
891
  };
760
892
  }
761
893
 
894
+ // src/core/features/max-height-controller.ts
895
+ function createMaxHeightController(deps) {
896
+ let maxHeightCap;
897
+ let maxHeightRaw;
898
+ const resolveCap = () => {
899
+ const raw = maxHeightRaw;
900
+ if (raw === void 0) {
901
+ maxHeightCap = void 0;
902
+ return;
903
+ }
904
+ if (typeof raw === "number") {
905
+ maxHeightCap = raw;
906
+ return;
907
+ }
908
+ if (typeof window === "undefined") return;
909
+ const measured = resolveSnap(raw, deps.mode);
910
+ maxHeightCap = measured > 0 ? measured : void 0;
911
+ };
912
+ const clampTo = () => {
913
+ if (maxHeightCap === void 0) return;
914
+ if (deps.getMaxAxisSize() > maxHeightCap) {
915
+ deps.setMaxAxisSize(maxHeightCap);
916
+ }
917
+ };
918
+ return {
919
+ setRadius(r) {
920
+ deps.element.style.setProperty(
921
+ "--bs-radius",
922
+ typeof r === "number" ? `${r}px` : r
923
+ );
924
+ },
925
+ setMaxHeight(h) {
926
+ const value = typeof h === "number" ? `${h}px` : h;
927
+ const axis = layoutAxis(deps.mode);
928
+ deps.element.style.setProperty(
929
+ axis === "height" ? "max-height" : "max-width",
930
+ value
931
+ );
932
+ maxHeightRaw = h;
933
+ resolveCap();
934
+ deps.recompute();
935
+ },
936
+ resolveCap,
937
+ clampTo,
938
+ getCap: () => maxHeightCap
939
+ };
940
+ }
941
+
762
942
  // src/core/features/fit-measurement.ts
763
943
  var measureSheetNatural = (element, content, vertical) => {
764
944
  if (!content) {
@@ -890,13 +1070,6 @@ var WriteSentinel = class {
890
1070
  }
891
1071
  };
892
1072
 
893
- // src/core/primitives/devWarn.ts
894
- function devWarn(...args) {
895
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
896
- console.warn(...args);
897
- }
898
- }
899
-
900
1073
  // src/core/primitives/overlay-position.ts
901
1074
  function resolveSheetAnchoredStyle(mode, position, inset) {
902
1075
  const sizeOffset = `calc(var(--bs-size, 0px) + ${inset})`;
@@ -2351,7 +2524,7 @@ var matchesRange = (def, progress) => {
2351
2524
  };
2352
2525
  function installScrimStages(deps, opts) {
2353
2526
  const doc = deps.host.ownerDocument;
2354
- const entries = opts.stages.map((def) => {
2527
+ const entries2 = opts.stages.map((def) => {
2355
2528
  const wrapper = doc.createElement("div");
2356
2529
  wrapper.className = "bs-scrim-stage";
2357
2530
  const ws = wrapper.style;
@@ -2372,9 +2545,9 @@ function installScrimStages(deps, opts) {
2372
2545
  const resolveActive = () => {
2373
2546
  const state = deps.getState();
2374
2547
  if (state.size === 0) {
2375
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? null;
2548
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? null;
2376
2549
  }
2377
- return entries.find((e) => matchesId(e.def, state.activeId)) ?? entries.find((e) => matchesRange(e.def, state.progress)) ?? null;
2550
+ return entries2.find((e) => matchesId(e.def, state.activeId)) ?? entries2.find((e) => matchesRange(e.def, state.progress)) ?? null;
2378
2551
  };
2379
2552
  const hide = (entry, animate) => {
2380
2553
  entry.inFlight?.cancel();
@@ -2423,13 +2596,13 @@ function installScrimStages(deps, opts) {
2423
2596
  deps.on("open", () => update(true)),
2424
2597
  deps.on("close", () => update(true))
2425
2598
  ];
2426
- if (entries.some((e) => e.def.forRange)) {
2599
+ if (entries2.some((e) => e.def.forRange)) {
2427
2600
  offs.push(deps.on("progress", () => update(true)));
2428
2601
  }
2429
2602
  update(false);
2430
2603
  return () => {
2431
2604
  offs.forEach((off) => off());
2432
- for (const entry of entries) {
2605
+ for (const entry of entries2) {
2433
2606
  entry.inFlight?.cancel();
2434
2607
  entry.inFlight = null;
2435
2608
  if (entry.wrapper.parentNode) {
@@ -2542,8 +2715,7 @@ var BottomSheetEngine = class {
2542
2715
  __publicField(this, "disableCloseFlag");
2543
2716
  __publicField(this, "disableDragFlag");
2544
2717
  __publicField(this, "closeOnRouteChange");
2545
- __publicField(this, "maxHeightCap");
2546
- __publicField(this, "maxHeightRaw");
2718
+ __publicField(this, "maxHeight");
2547
2719
  __publicField(this, "snapPointsRaw");
2548
2720
  __publicField(this, "snaps");
2549
2721
  __publicField(this, "activeId");
@@ -2654,11 +2826,20 @@ var BottomSheetEngine = class {
2654
2826
  auditVhUsage(this.snapPointsRaw);
2655
2827
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2656
2828
  this.rootEl = this.element.closest(".bs-root");
2829
+ this.maxHeight = createMaxHeightController({
2830
+ element: this.element,
2831
+ mode: this.mode,
2832
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2833
+ setMaxAxisSize: (size) => {
2834
+ this.snaps.setMaxAxisSize(size);
2835
+ },
2836
+ recompute: () => this.recompute()
2837
+ });
2657
2838
  this.fitDeps = {
2658
2839
  element: this.element,
2659
2840
  scrollContainer: this.scrollContainer,
2660
2841
  mode: this.mode,
2661
- getMaxHeightCap: () => this.maxHeightCap
2842
+ getMaxHeightCap: () => this.maxHeight.getCap()
2662
2843
  };
2663
2844
  this.snaps = new SnapResolver(
2664
2845
  this.snapPointsRaw,
@@ -2680,8 +2861,8 @@ var BottomSheetEngine = class {
2680
2861
  this.attach();
2681
2862
  this.registerInStack();
2682
2863
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
2683
- const io = new IntersectionObserver((entries) => {
2684
- for (const entry of entries) {
2864
+ const io = new IntersectionObserver((entries2) => {
2865
+ for (const entry of entries2) {
2685
2866
  if (!entry.isIntersecting) continue;
2686
2867
  if (this.destroyed) return;
2687
2868
  this.snaps.recompute();
@@ -2769,7 +2950,7 @@ var BottomSheetEngine = class {
2769
2950
  }
2770
2951
  use(plugin) {
2771
2952
  if (this.destroyed) {
2772
- console.warn(
2953
+ devWarn(
2773
2954
  `[BottomSheet] use("${plugin.name}") called on destroyed engine \u2014 ignored.`
2774
2955
  );
2775
2956
  return this;
@@ -2811,6 +2992,9 @@ var BottomSheetEngine = class {
2811
2992
  getAllowedIds() {
2812
2993
  return this.snaps.getAllowedIds().slice();
2813
2994
  }
2995
+ getResolvedSnaps() {
2996
+ return this.snaps.getResolvedSnaps();
2997
+ }
2814
2998
  async snapTo(id, velocityOrOpts = 0, _skipBeforeSnap = false) {
2815
2999
  if (this.destroyed) return;
2816
3000
  const opts = typeof velocityOrOpts === "object" ? velocityOrOpts : { velocity: velocityOrOpts };
@@ -2819,11 +3003,11 @@ var BottomSheetEngine = class {
2819
3003
  if (externalSignal?.aborted) return;
2820
3004
  const target = this.snaps.findById(id);
2821
3005
  if (!target) {
2822
- console.warn(`[BottomSheet] unknown snap id: ${id}`);
3006
+ devWarn(`[BottomSheet] unknown snap id: ${id}`);
2823
3007
  return;
2824
3008
  }
2825
3009
  if (!this.snaps.getAllowedIds().includes(id)) {
2826
- console.warn(`[BottomSheet] snap "${id}" is not in allowed list`);
3010
+ devWarn(`[BottomSheet] snap "${id}" is not in allowed list`);
2827
3011
  return;
2828
3012
  }
2829
3013
  if (!_skipBeforeSnap && this.emitBeforeSnap(target, this.activeId)) {
@@ -2955,42 +3139,11 @@ var BottomSheetEngine = class {
2955
3139
  }
2956
3140
  setRadius(r) {
2957
3141
  if (this.destroyed) return;
2958
- this.element.style.setProperty(
2959
- "--bs-radius",
2960
- typeof r === "number" ? `${r}px` : r
2961
- );
3142
+ this.maxHeight.setRadius(r);
2962
3143
  }
2963
3144
  setMaxHeight(h) {
2964
3145
  if (this.destroyed) return;
2965
- const value = typeof h === "number" ? `${h}px` : h;
2966
- const axis = layoutAxis(this.mode);
2967
- this.element.style.setProperty(
2968
- axis === "height" ? "max-height" : "max-width",
2969
- value
2970
- );
2971
- this.maxHeightRaw = h;
2972
- this.resolveMaxHeightCap();
2973
- this.recompute();
2974
- }
2975
- resolveMaxHeightCap() {
2976
- const raw = this.maxHeightRaw;
2977
- if (raw === void 0) {
2978
- this.maxHeightCap = void 0;
2979
- return;
2980
- }
2981
- if (typeof raw === "number") {
2982
- this.maxHeightCap = raw;
2983
- return;
2984
- }
2985
- if (typeof window === "undefined") return;
2986
- const measured = resolveSnap(raw, this.mode);
2987
- this.maxHeightCap = measured > 0 ? measured : void 0;
2988
- }
2989
- clampToMaxHeight() {
2990
- if (this.maxHeightCap === void 0) return;
2991
- if (this.snaps.getMaxAxisSize() > this.maxHeightCap) {
2992
- this.snaps.setMaxAxisSize(this.maxHeightCap);
2993
- }
3146
+ this.maxHeight.setMaxHeight(h);
2994
3147
  }
2995
3148
  setAllowed(ids, snap) {
2996
3149
  if (this.destroyed) return;
@@ -3078,7 +3231,7 @@ var BottomSheetEngine = class {
3078
3231
  };
3079
3232
  const host = this.screenComponent?.parentElement;
3080
3233
  if (!host) {
3081
- console.warn(
3234
+ devWarn(
3082
3235
  "[BottomSheet] setScrimStages: no scrim element mounted \u2014 pass `scrim` to the engine first."
3083
3236
  );
3084
3237
  return () => {
@@ -3128,9 +3281,9 @@ var BottomSheetEngine = class {
3128
3281
  }
3129
3282
  recompute() {
3130
3283
  if (this.destroyed) return;
3131
- this.resolveMaxHeightCap();
3284
+ this.maxHeight.resolveCap();
3132
3285
  this.snaps.recompute();
3133
- this.clampToMaxHeight();
3286
+ this.maxHeight.clampTo();
3134
3287
  this.scrim.invalidateOpacityCache();
3135
3288
  if (this.gesture?.isDragging) return;
3136
3289
  const current = this.snaps.findById(this.activeId);
@@ -3180,42 +3333,18 @@ var BottomSheetEngine = class {
3180
3333
  return this.currentAbort.signal;
3181
3334
  }
3182
3335
  emitBeforeSnap(target, previousId) {
3183
- let cancelled = false;
3184
- let frozen = false;
3185
- this.emit("before-snap", {
3186
- id: target.id,
3187
- size: target.size,
3188
- previousId,
3189
- cancel: () => {
3190
- if (frozen) {
3191
- console.warn(
3192
- "[BottomSheet] before-snap.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3193
- );
3194
- return;
3195
- }
3196
- cancelled = true;
3197
- }
3198
- });
3199
- frozen = true;
3200
- return cancelled;
3336
+ return emitCancelable(
3337
+ (payload) => this.emit("before-snap", payload),
3338
+ { id: target.id, size: target.size, previousId },
3339
+ "before-snap"
3340
+ );
3201
3341
  }
3202
3342
  emitBeforeClose(reason) {
3203
- let cancelled = false;
3204
- let frozen = false;
3205
- this.emit("before-close", {
3206
- reason,
3207
- cancel: () => {
3208
- if (frozen) {
3209
- console.warn(
3210
- "[BottomSheet] before-close.cancel() called asynchronously \u2014 ignored. cancel() must be invoked synchronously inside the listener."
3211
- );
3212
- return;
3213
- }
3214
- cancelled = true;
3215
- }
3216
- });
3217
- frozen = true;
3218
- return cancelled;
3343
+ return emitCancelable(
3344
+ (payload) => this.emit("before-close", payload),
3345
+ { reason },
3346
+ "before-close"
3347
+ );
3219
3348
  }
3220
3349
  applyAutoAriaLabelledBy() {
3221
3350
  if (typeof document === "undefined") return;
@@ -3230,16 +3359,16 @@ var BottomSheetEngine = class {
3230
3359
  this.element.setAttribute("aria-labelledby", titled.id);
3231
3360
  }
3232
3361
  recomputeSnaps() {
3233
- this.resolveMaxHeightCap();
3362
+ this.maxHeight.resolveCap();
3234
3363
  this.snaps.setRaw(this.snapPointsRaw);
3235
- this.clampToMaxHeight();
3364
+ this.maxHeight.clampTo();
3236
3365
  this.scrim.invalidateOpacityCache();
3237
3366
  }
3238
3367
  auditDuplicateSnapIds(points) {
3239
3368
  const seen = /* @__PURE__ */ new Set();
3240
3369
  for (const p of points) {
3241
3370
  if (seen.has(p.id)) {
3242
- console.warn(
3371
+ devWarn(
3243
3372
  `[BottomSheet] duplicate snap id "${p.id}" \u2014 only the first occurrence is reachable.`
3244
3373
  );
3245
3374
  }
@@ -3406,7 +3535,15 @@ var BottomSheetEngine = class {
3406
3535
  setSize: (size) => {
3407
3536
  this.size = size;
3408
3537
  },
3409
- applySize: (size) => this.applySize(size)
3538
+ applySize: (size) => this.applySize(size),
3539
+ cancelInFlight: () => {
3540
+ this.animation.cancel();
3541
+ },
3542
+ newCycle: () => {
3543
+ this.newCycle();
3544
+ },
3545
+ isAnimating: () => this.animation.isAnimating,
3546
+ resyncAfterCancel: () => this.resyncAfterResize()
3410
3547
  }));
3411
3548
  }
3412
3549
  this.teardowns.add(installSliderKeyboard({
@@ -3442,9 +3579,9 @@ var BottomSheetEngine = class {
3442
3579
  return;
3443
3580
  }
3444
3581
  if (this.emitBeforeSnap(target, previousId)) {
3445
- const restore = this.snaps.findById(previousId);
3446
- if (restore) {
3447
- void this.animation.animateTo(restore.size, 0);
3582
+ const restore2 = this.snaps.findById(previousId);
3583
+ if (restore2) {
3584
+ void this.animation.animateTo(restore2.size, 0);
3448
3585
  }
3449
3586
  return;
3450
3587
  }