@surdeddd/bottom-sheet 0.12.0 → 0.13.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 (58) hide show
  1. package/README.md +52 -1
  2. package/dist/{BottomSheetEngine-D6oiEfZ4.d.cts → BottomSheetCore-CsIjyq7C.d.cts} +60 -5
  3. package/dist/{BottomSheetEngine-Dhy0X_e9.d.ts → BottomSheetCore-D16lsa6g.d.ts} +60 -5
  4. package/dist/BottomSheetEngine-BYjAVrmd.d.cts +7 -0
  5. package/dist/BottomSheetEngine-YuCNugZn.d.ts +7 -0
  6. package/dist/core.cjs +3476 -0
  7. package/dist/core.d.cts +3 -0
  8. package/dist/core.d.ts +3 -0
  9. package/dist/core.js +3474 -0
  10. package/dist/debug.cjs +109 -0
  11. package/dist/debug.d.cts +22 -0
  12. package/dist/debug.d.ts +22 -0
  13. package/dist/debug.js +107 -0
  14. package/dist/element.cjs +972 -685
  15. package/dist/element.d.cts +2 -1
  16. package/dist/element.d.ts +2 -1
  17. package/dist/element.iife.global.js +2 -2
  18. package/dist/element.js +972 -685
  19. package/dist/features.cjs +533 -0
  20. package/dist/features.d.cts +13 -0
  21. package/dist/features.d.ts +13 -0
  22. package/dist/features.js +526 -0
  23. package/dist/index-DdYLOIIT.d.ts +15 -0
  24. package/dist/index-dfrzw-lI.d.cts +15 -0
  25. package/dist/index.cjs +805 -534
  26. package/dist/index.d.cts +6 -15
  27. package/dist/index.d.ts +6 -15
  28. package/dist/index.js +799 -535
  29. package/dist/integrations/formik.d.cts +1 -1
  30. package/dist/integrations/formik.d.ts +1 -1
  31. package/dist/integrations/react-hook-form.d.cts +1 -1
  32. package/dist/integrations/react-hook-form.d.ts +1 -1
  33. package/dist/overlay.cjs +6 -5
  34. package/dist/overlay.js +6 -5
  35. package/dist/preact.d.cts +2 -1
  36. package/dist/preact.d.ts +2 -1
  37. package/dist/qwik-lib/index.qwik.cjs +4267 -0
  38. package/dist/qwik-lib/index.qwik.mjs +4267 -0
  39. package/dist/qwik.cjs +798 -534
  40. package/dist/qwik.d.cts +2 -2
  41. package/dist/qwik.d.ts +2 -2
  42. package/dist/qwik.js +798 -534
  43. package/dist/react.cjs +798 -534
  44. package/dist/react.d.cts +3 -2
  45. package/dist/react.d.ts +3 -2
  46. package/dist/react.js +798 -534
  47. package/dist/solid.cjs +798 -534
  48. package/dist/solid.d.cts +63 -4
  49. package/dist/solid.d.ts +63 -4
  50. package/dist/solid.js +798 -534
  51. package/dist/solid.source.jsx +798 -534
  52. package/dist/svelte-core.cjs +798 -534
  53. package/dist/svelte-core.js +798 -534
  54. package/dist/svelte.cjs +798 -534
  55. package/dist/svelte.js +798 -534
  56. package/dist/vue.cjs +909 -637
  57. package/dist/vue.js +909 -637
  58. package/package.json +63 -11
package/dist/element.cjs CHANGED
@@ -402,82 +402,6 @@ var init_snap_resolver = __esm({
402
402
  }
403
403
  });
404
404
 
405
- // src/core/features/persist.ts
406
- function readPersistedId(key) {
407
- if (!key) return null;
408
- if (typeof window === "undefined") return null;
409
- if (typeof localStorage === "undefined") return null;
410
- try {
411
- return localStorage.getItem(key);
412
- } catch {
413
- return null;
414
- }
415
- }
416
- function installPersist(engine, key) {
417
- const unsubscribe = engine.on("snap", ({ id }) => {
418
- if (typeof window === "undefined") return;
419
- if (typeof localStorage === "undefined") return;
420
- try {
421
- localStorage.setItem(key, id);
422
- } catch {
423
- }
424
- });
425
- return unsubscribe;
426
- }
427
- var init_persist = __esm({
428
- "src/core/features/persist.ts"() {
429
- }
430
- });
431
-
432
- // src/core/features/auto-collapse.ts
433
- function installAutoCollapse(deps) {
434
- const { ms } = deps;
435
- if (ms === void 0 || ms <= 0) {
436
- return () => {
437
- };
438
- }
439
- let timer = null;
440
- const clearTimer = () => {
441
- if (timer !== null) {
442
- clearTimeout(timer);
443
- timer = null;
444
- }
445
- };
446
- const fire = () => {
447
- if (deps.isDestroyed()) return;
448
- if (deps.isDragging()) return;
449
- const active = deps.resolveSnap(deps.getActiveId());
450
- if (!active || active.size === 0) return;
451
- let target = null;
452
- for (const id of deps.getAllowedIds()) {
453
- const snap = deps.resolveSnap(id);
454
- if (!snap || snap.size <= 0) continue;
455
- if (!target || snap.size < target.size) target = snap;
456
- }
457
- if (!target) return;
458
- if (target.id === active.id || target.size >= active.size) return;
459
- deps.snapTo(target.id);
460
- };
461
- const reset = () => {
462
- clearTimer();
463
- if (deps.isDestroyed()) return;
464
- timer = setTimeout(() => {
465
- timer = null;
466
- fire();
467
- }, ms);
468
- };
469
- const unsubscribe = deps.on("snap", () => reset());
470
- if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
471
- return () => {
472
- clearTimer();
473
- unsubscribe();
474
- };
475
- }
476
- var init_auto_collapse = __esm({
477
- "src/core/features/auto-collapse.ts"() {
478
- }
479
- });
480
-
481
405
  // src/core/features/linked-sheets.ts
482
406
  function notifyLinkedSheets(sheets, self) {
483
407
  if (sheets.length === 0) return;
@@ -504,543 +428,194 @@ var init_linked_sheets = __esm({
504
428
  }
505
429
  });
506
430
 
507
- // src/core/features/history-coordinator.ts
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;
431
+ // src/core/features/resize-observer.ts
432
+ function installResizeObserver(deps) {
433
+ if (typeof window === "undefined") {
434
+ return () => {
435
+ };
521
436
  }
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;
530
- }
531
- if (entry.surface.isTop()) {
532
- target = entry;
533
- targetIdx = i;
534
- break;
437
+ const onResize = () => {
438
+ if (deps.isDestroyed()) return;
439
+ deps.recomputeSnaps();
440
+ const mode = deps.getMode();
441
+ const isVerticalAxis = mode === "bottom" || mode === "top";
442
+ const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
443
+ const max = deps.getMaxAxisSize();
444
+ if (viewportSize2 > 0 && viewportSize2 < max) {
445
+ deps.setMaxAxisSize(viewportSize2);
535
446
  }
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);
548
- });
549
- maybeTeardownListener();
550
- }
551
- function restore(entry) {
552
- if (!hasWindow() || !hasHistory()) return;
553
- entry.live = true;
554
- runInternal(() => {
555
- try {
556
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
557
- } catch {
558
- entry.live = false;
447
+ const wasAnimating = deps.isAnimating?.() ?? false;
448
+ deps.cancelInFlight();
449
+ deps.newCycle();
450
+ const current = deps.resolveActiveSnap();
451
+ const newMax = deps.getMaxAxisSize();
452
+ const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
453
+ deps.setSize(targetSize);
454
+ if (current && !deps.isDragging()) {
455
+ deps.applySize(targetSize);
456
+ if (wasAnimating) deps.resyncAfterCancel?.();
559
457
  }
560
- });
561
- if (entry.live) {
562
- entries.push(entry);
563
- ensureListener();
458
+ };
459
+ let resizeObserver = null;
460
+ if (typeof ResizeObserver !== "undefined") {
461
+ resizeObserver = new ResizeObserver(onResize);
462
+ resizeObserver.observe(document.documentElement);
463
+ } else {
464
+ window.addEventListener("resize", onResize);
564
465
  }
466
+ window.addEventListener("orientationchange", onResize);
467
+ const onVisibility = () => {
468
+ if (deps.isDestroyed()) return;
469
+ if (document.hidden) deps.cancelInFlight();
470
+ };
471
+ document.addEventListener("visibilitychange", onVisibility);
472
+ return () => {
473
+ resizeObserver?.disconnect();
474
+ window.removeEventListener("resize", onResize);
475
+ window.removeEventListener("orientationchange", onResize);
476
+ document.removeEventListener("visibilitychange", onVisibility);
477
+ };
565
478
  }
566
- function pushBackMarker(surface) {
567
- const entry = { surface, priorUrl: void 0, live: false };
568
- if (!hasWindow() || !hasHistory()) {
569
- return entry;
479
+ var init_resize_observer = __esm({
480
+ "src/core/features/resize-observer.ts"() {
570
481
  }
571
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
572
- runInternal(() => {
573
- try {
574
- history.pushState(brandMarker(surface), "", surface.url);
575
- entry.live = true;
576
- } catch {
577
- entry.live = false;
482
+ });
483
+
484
+ // src/core/features/fit-observer.ts
485
+ function installFitObserver(deps) {
486
+ if (typeof ResizeObserver === "undefined") return () => {
487
+ };
488
+ if (!deps.hasFitSnap()) return () => {
489
+ };
490
+ let raf = 0;
491
+ const schedule = () => {
492
+ if (raf) return;
493
+ raf = requestAnimationFrame(() => {
494
+ raf = 0;
495
+ if (deps.isDestroyed() || deps.isDragging()) return;
496
+ deps.recompute();
497
+ });
498
+ };
499
+ const ro = new ResizeObserver(schedule);
500
+ ro.observe(deps.handle);
501
+ const scroller = deps.scrollContainer;
502
+ const observed = /* @__PURE__ */ new Set();
503
+ const slots = /* @__PURE__ */ new Set();
504
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
505
+ const onSlotChange = () => resync();
506
+ const trackSlots = () => {
507
+ if (!scroller || !hasSlot) return;
508
+ for (const child of Array.from(scroller.children)) {
509
+ if (child instanceof HTMLSlotElement && !slots.has(child)) {
510
+ child.addEventListener("slotchange", onSlotChange);
511
+ slots.add(child);
512
+ }
513
+ }
514
+ };
515
+ const resync = () => {
516
+ trackSlots();
517
+ const targets = contentTargets(scroller);
518
+ for (const el of observed) {
519
+ if (!targets.includes(el)) {
520
+ ro.unobserve(el);
521
+ observed.delete(el);
522
+ }
523
+ }
524
+ for (const el of targets) {
525
+ if (!observed.has(el)) {
526
+ ro.observe(el);
527
+ observed.add(el);
528
+ }
529
+ }
530
+ schedule();
531
+ };
532
+ let mo;
533
+ if (scroller) {
534
+ resync();
535
+ if (typeof MutationObserver !== "undefined") {
536
+ mo = new MutationObserver(resync);
537
+ mo.observe(scroller, { childList: true });
578
538
  }
579
- });
580
- if (entry.live) {
581
- entries.push(entry);
582
- ensureListener();
583
539
  }
584
- return entry;
540
+ return () => {
541
+ ro.disconnect();
542
+ mo?.disconnect();
543
+ for (const slot of slots) {
544
+ slot.removeEventListener("slotchange", onSlotChange);
545
+ }
546
+ if (raf) cancelAnimationFrame(raf);
547
+ };
585
548
  }
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
- brandMarker(rebrandSurface),
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
- var entries, subscribers, suppress, internalDepth, pendingRebrand, listenerInstalled, patchInstalled, origPushState, origReplaceState, patchedPushState, patchedReplaceState, hasWindow, hasHistory, brandMarker, runInternal, notifyRouteChange, ensureListener, maybeTeardownListener, installPatch, removePatch;
632
- var init_history_coordinator = __esm({
633
- "src/core/features/history-coordinator.ts"() {
634
- entries = [];
635
- subscribers = /* @__PURE__ */ new Set();
636
- suppress = 0;
637
- internalDepth = 0;
638
- pendingRebrand = null;
639
- listenerInstalled = false;
640
- patchInstalled = false;
641
- origPushState = null;
642
- origReplaceState = null;
643
- patchedPushState = null;
644
- patchedReplaceState = null;
645
- hasWindow = () => typeof window !== "undefined";
646
- hasHistory = () => typeof history !== "undefined";
647
- brandMarker = (surface) => ({
648
- ...surface.markerState(),
649
- __bs: true
650
- });
651
- runInternal = (fn) => {
652
- internalDepth += 1;
653
- try {
654
- fn();
655
- } finally {
656
- internalDepth -= 1;
657
- }
658
- };
659
- notifyRouteChange = () => {
660
- const snapshot = Array.from(subscribers);
661
- for (const fn of snapshot) fn();
662
- };
663
- ensureListener = () => {
664
- if (listenerInstalled || !hasWindow()) return;
665
- window.addEventListener("popstate", onPopState);
666
- listenerInstalled = true;
667
- };
668
- maybeTeardownListener = () => {
669
- if (!listenerInstalled) return;
670
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
671
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
672
- listenerInstalled = false;
673
- };
674
- installPatch = () => {
675
- if (patchInstalled || !hasHistory()) return;
676
- origPushState = history.pushState;
677
- origReplaceState = history.replaceState;
678
- patchedPushState = function(...args) {
679
- origPushState.apply(this, args);
680
- if (internalDepth === 0) notifyRouteChange();
681
- };
682
- patchedReplaceState = function(...args) {
683
- origReplaceState.apply(this, args);
684
- if (internalDepth === 0) notifyRouteChange();
685
- };
686
- history.pushState = patchedPushState;
687
- history.replaceState = patchedReplaceState;
688
- patchInstalled = true;
689
- };
690
- removePatch = () => {
691
- if (!patchInstalled) return;
692
- if (history.pushState === patchedPushState && origPushState) {
693
- history.pushState = origPushState;
694
- }
695
- if (history.replaceState === patchedReplaceState && origReplaceState) {
696
- history.replaceState = origReplaceState;
549
+ var contentTargets;
550
+ var init_fit_observer = __esm({
551
+ "src/core/features/fit-observer.ts"() {
552
+ contentTargets = (scroller) => {
553
+ if (!scroller) return [];
554
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
555
+ const out = [];
556
+ for (const child of Array.from(scroller.children)) {
557
+ if (hasSlot && child instanceof HTMLSlotElement) {
558
+ for (const el of child.assignedElements()) out.push(el);
559
+ } else {
560
+ out.push(child);
561
+ }
697
562
  }
698
- origPushState = null;
699
- origReplaceState = null;
700
- patchedPushState = null;
701
- patchedReplaceState = null;
702
- patchInstalled = false;
563
+ return out;
703
564
  };
704
565
  }
705
566
  });
706
567
 
707
- // src/core/features/route.ts
708
- function installRoute(deps) {
709
- if (typeof window === "undefined") {
710
- return () => {
711
- };
712
- }
713
- if (!deps.routedTo && !deps.closeOnBack) {
714
- return () => {
715
- };
716
- }
717
- let handle = null;
718
- const onOpen = () => {
719
- if (handle) return;
720
- handle = pushBackMarker({
721
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
722
- isTop: deps.isTopSheet,
723
- close: deps.close,
724
- markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
725
- url: deps.routedTo
726
- });
727
- };
728
- const onClose = () => {
729
- if (!handle) return;
730
- popBackMarker(handle);
731
- handle = null;
732
- };
733
- const unsubscribeOpen = deps.on("open", onOpen);
734
- const unsubscribeClose = deps.on("close", onClose);
735
- if (deps.getSize() > 0) onOpen();
736
- return () => {
737
- unsubscribeOpen();
738
- unsubscribeClose();
739
- if (handle) {
740
- popBackMarker(handle);
741
- handle = null;
568
+ // src/core/features/max-height-controller.ts
569
+ function createMaxHeightController(deps) {
570
+ let maxHeightCap;
571
+ let maxHeightRaw;
572
+ const resolveCap = () => {
573
+ const raw = maxHeightRaw;
574
+ if (raw === void 0) {
575
+ maxHeightCap = void 0;
576
+ return;
742
577
  }
578
+ if (typeof raw === "number") {
579
+ maxHeightCap = raw;
580
+ return;
581
+ }
582
+ if (typeof window === "undefined") return;
583
+ const measured = resolveSnap(raw, deps.mode);
584
+ maxHeightCap = measured > 0 ? measured : void 0;
743
585
  };
744
- }
745
- function installRouteChange(deps) {
746
- return subscribeRouteChange(() => {
747
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
748
- void deps.close();
749
- });
750
- }
751
- var init_route = __esm({
752
- "src/core/features/route.ts"() {
753
- init_history_coordinator();
754
- }
755
- });
756
-
757
- // src/core/features/content-swipe.ts
758
- function installContentSwipe(deps) {
759
- const { container } = deps;
760
- let startY = 0;
761
- let startScrollTop = 0;
762
- let moved = false;
763
- const onTouchStart = (e) => {
764
- const t = e.touches[0];
765
- if (!t) return;
766
- startY = t.clientY;
767
- startScrollTop = container.scrollTop;
768
- moved = false;
769
- };
770
- const onTouchMove = (e) => {
771
- const t = e.touches[0];
772
- if (!t) return;
773
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
774
- };
775
- const onTouchEnd = (e) => {
776
- if (!moved) return;
777
- if (deps.isDragging() || deps.isAnimating()) return;
778
- if (container.scrollTop !== startScrollTop) return;
779
- const t = e.changedTouches[0];
780
- if (!t) return;
781
- const deltaY = t.clientY - startY;
782
- const atTop = container.scrollTop <= 0;
783
- const allowed = deps.getAllowedIds();
784
- const idx = allowed.indexOf(deps.getActiveId());
785
- if (idx === -1) return;
786
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
787
- deps.snapTo(allowed[idx - 1]);
788
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
789
- deps.snapTo(allowed[idx + 1]);
586
+ const clampTo = () => {
587
+ if (maxHeightCap === void 0) return;
588
+ if (deps.getMaxAxisSize() > maxHeightCap) {
589
+ deps.setMaxAxisSize(maxHeightCap);
790
590
  }
791
591
  };
792
- container.addEventListener("touchstart", onTouchStart, { passive: true });
793
- container.addEventListener("touchmove", onTouchMove, { passive: true });
794
- container.addEventListener("touchend", onTouchEnd, { passive: true });
795
- return () => {
796
- container.removeEventListener("touchstart", onTouchStart);
797
- container.removeEventListener("touchmove", onTouchMove);
798
- container.removeEventListener("touchend", onTouchEnd);
592
+ return {
593
+ setRadius(r) {
594
+ deps.element.style.setProperty(
595
+ "--bs-radius",
596
+ typeof r === "number" ? `${r}px` : r
597
+ );
598
+ },
599
+ setMaxHeight(h) {
600
+ const value = typeof h === "number" ? `${h}px` : h;
601
+ const axis = layoutAxis(deps.mode);
602
+ deps.element.style.setProperty(
603
+ axis === "height" ? "max-height" : "max-width",
604
+ value
605
+ );
606
+ maxHeightRaw = h;
607
+ resolveCap();
608
+ deps.recompute();
609
+ },
610
+ resolveCap,
611
+ clampTo,
612
+ getCap: () => maxHeightCap
799
613
  };
800
614
  }
801
- var CONTENT_PULL_THRESHOLD;
802
- var init_content_swipe = __esm({
803
- "src/core/features/content-swipe.ts"() {
804
- CONTENT_PULL_THRESHOLD = 60;
805
- }
806
- });
807
-
808
- // src/core/features/visual-viewport.ts
809
- function installVisualViewport(deps) {
810
- if (typeof window === "undefined" || !window.visualViewport) {
811
- return () => {
812
- };
813
- }
814
- const vv = window.visualViewport;
815
- let rafId = null;
816
- const onResize = () => {
817
- if (rafId !== null) return;
818
- rafId = requestAnimationFrame(() => {
819
- rafId = null;
820
- if (deps.isDestroyed()) return;
821
- deps.recomputeSnaps();
822
- const isVertical = deps.isVerticalAxis();
823
- const visible = isVertical ? vv.height : vv.width;
824
- const windowSize = isVertical ? window.innerHeight : window.innerWidth;
825
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
826
- if (clamp < deps.getMaxAxisSize()) {
827
- deps.setMaxAxisSize(clamp);
828
- }
829
- const wasAnimating = deps.isAnimating?.() ?? false;
830
- deps.cancelInFlight();
831
- deps.newCycle();
832
- const max = deps.getMaxAxisSize();
833
- deps.setSize(Math.min(deps.getSize(), max));
834
- const current = deps.resolveActiveSnap();
835
- if (current && !deps.isDragging()) {
836
- deps.applySize(Math.min(current.size, max));
837
- if (wasAnimating) deps.resyncAfterCancel?.();
838
- } else {
839
- deps.applySize(deps.getSize());
840
- }
841
- });
842
- };
843
- vv.addEventListener("resize", onResize);
844
- return () => {
845
- vv.removeEventListener("resize", onResize);
846
- if (rafId !== null) cancelAnimationFrame(rafId);
847
- };
848
- }
849
- var SAFETY_PAD;
850
- var init_visual_viewport = __esm({
851
- "src/core/features/visual-viewport.ts"() {
852
- SAFETY_PAD = 8;
853
- }
854
- });
855
-
856
- // src/core/features/resize-observer.ts
857
- function installResizeObserver(deps) {
858
- if (typeof window === "undefined") {
859
- return () => {
860
- };
861
- }
862
- const onResize = () => {
863
- if (deps.isDestroyed()) return;
864
- deps.recomputeSnaps();
865
- const mode = deps.getMode();
866
- const isVerticalAxis = mode === "bottom" || mode === "top";
867
- const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
868
- const max = deps.getMaxAxisSize();
869
- if (viewportSize2 > 0 && viewportSize2 < max) {
870
- deps.setMaxAxisSize(viewportSize2);
871
- }
872
- const wasAnimating = deps.isAnimating?.() ?? false;
873
- deps.cancelInFlight();
874
- deps.newCycle();
875
- const current = deps.resolveActiveSnap();
876
- const newMax = deps.getMaxAxisSize();
877
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
878
- deps.setSize(targetSize);
879
- if (current && !deps.isDragging()) {
880
- deps.applySize(targetSize);
881
- if (wasAnimating) deps.resyncAfterCancel?.();
882
- }
883
- };
884
- let resizeObserver = null;
885
- if (typeof ResizeObserver !== "undefined") {
886
- resizeObserver = new ResizeObserver(onResize);
887
- resizeObserver.observe(document.documentElement);
888
- } else {
889
- window.addEventListener("resize", onResize);
890
- }
891
- window.addEventListener("orientationchange", onResize);
892
- const onVisibility = () => {
893
- if (deps.isDestroyed()) return;
894
- if (document.hidden) deps.cancelInFlight();
895
- };
896
- document.addEventListener("visibilitychange", onVisibility);
897
- return () => {
898
- resizeObserver?.disconnect();
899
- window.removeEventListener("resize", onResize);
900
- window.removeEventListener("orientationchange", onResize);
901
- document.removeEventListener("visibilitychange", onVisibility);
902
- };
903
- }
904
- var init_resize_observer = __esm({
905
- "src/core/features/resize-observer.ts"() {
906
- }
907
- });
908
-
909
- // src/core/features/fit-observer.ts
910
- function installFitObserver(deps) {
911
- if (typeof ResizeObserver === "undefined") return () => {
912
- };
913
- if (!deps.hasFitSnap()) return () => {
914
- };
915
- let raf = 0;
916
- const schedule = () => {
917
- if (raf) return;
918
- raf = requestAnimationFrame(() => {
919
- raf = 0;
920
- if (deps.isDestroyed() || deps.isDragging()) return;
921
- deps.recompute();
922
- });
923
- };
924
- const ro = new ResizeObserver(schedule);
925
- ro.observe(deps.handle);
926
- const scroller = deps.scrollContainer;
927
- const observed = /* @__PURE__ */ new Set();
928
- const slots = /* @__PURE__ */ new Set();
929
- const hasSlot = typeof HTMLSlotElement !== "undefined";
930
- const onSlotChange = () => resync();
931
- const trackSlots = () => {
932
- if (!scroller || !hasSlot) return;
933
- for (const child of Array.from(scroller.children)) {
934
- if (child instanceof HTMLSlotElement && !slots.has(child)) {
935
- child.addEventListener("slotchange", onSlotChange);
936
- slots.add(child);
937
- }
938
- }
939
- };
940
- const resync = () => {
941
- trackSlots();
942
- const targets = contentTargets(scroller);
943
- for (const el of observed) {
944
- if (!targets.includes(el)) {
945
- ro.unobserve(el);
946
- observed.delete(el);
947
- }
948
- }
949
- for (const el of targets) {
950
- if (!observed.has(el)) {
951
- ro.observe(el);
952
- observed.add(el);
953
- }
954
- }
955
- schedule();
956
- };
957
- let mo;
958
- if (scroller) {
959
- resync();
960
- if (typeof MutationObserver !== "undefined") {
961
- mo = new MutationObserver(resync);
962
- mo.observe(scroller, { childList: true });
963
- }
964
- }
965
- return () => {
966
- ro.disconnect();
967
- mo?.disconnect();
968
- for (const slot of slots) {
969
- slot.removeEventListener("slotchange", onSlotChange);
970
- }
971
- if (raf) cancelAnimationFrame(raf);
972
- };
973
- }
974
- var contentTargets;
975
- var init_fit_observer = __esm({
976
- "src/core/features/fit-observer.ts"() {
977
- contentTargets = (scroller) => {
978
- if (!scroller) return [];
979
- const hasSlot = typeof HTMLSlotElement !== "undefined";
980
- const out = [];
981
- for (const child of Array.from(scroller.children)) {
982
- if (hasSlot && child instanceof HTMLSlotElement) {
983
- for (const el of child.assignedElements()) out.push(el);
984
- } else {
985
- out.push(child);
986
- }
987
- }
988
- return out;
989
- };
990
- }
991
- });
992
-
993
- // src/core/features/max-height-controller.ts
994
- function createMaxHeightController(deps) {
995
- let maxHeightCap;
996
- let maxHeightRaw;
997
- const resolveCap = () => {
998
- const raw = maxHeightRaw;
999
- if (raw === void 0) {
1000
- maxHeightCap = void 0;
1001
- return;
1002
- }
1003
- if (typeof raw === "number") {
1004
- maxHeightCap = raw;
1005
- return;
1006
- }
1007
- if (typeof window === "undefined") return;
1008
- const measured = resolveSnap(raw, deps.mode);
1009
- maxHeightCap = measured > 0 ? measured : void 0;
1010
- };
1011
- const clampTo = () => {
1012
- if (maxHeightCap === void 0) return;
1013
- if (deps.getMaxAxisSize() > maxHeightCap) {
1014
- deps.setMaxAxisSize(maxHeightCap);
1015
- }
1016
- };
1017
- return {
1018
- setRadius(r) {
1019
- deps.element.style.setProperty(
1020
- "--bs-radius",
1021
- typeof r === "number" ? `${r}px` : r
1022
- );
1023
- },
1024
- setMaxHeight(h) {
1025
- const value = typeof h === "number" ? `${h}px` : h;
1026
- const axis = layoutAxis(deps.mode);
1027
- deps.element.style.setProperty(
1028
- axis === "height" ? "max-height" : "max-width",
1029
- value
1030
- );
1031
- maxHeightRaw = h;
1032
- resolveCap();
1033
- deps.recompute();
1034
- },
1035
- resolveCap,
1036
- clampTo,
1037
- getCap: () => maxHeightCap
1038
- };
1039
- }
1040
- var init_max_height_controller = __esm({
1041
- "src/core/features/max-height-controller.ts"() {
1042
- init_snap_points();
1043
- init_transform();
615
+ var init_max_height_controller = __esm({
616
+ "src/core/features/max-height-controller.ts"() {
617
+ init_snap_points();
618
+ init_transform();
1044
619
  }
1045
620
  });
1046
621
 
@@ -1824,6 +1399,58 @@ var init_animation_presets = __esm({
1824
1399
  }
1825
1400
  });
1826
1401
 
1402
+ // src/core/animation/waapi-settle.ts
1403
+ function sampleSpringSettle(from, to, velocityPxPerS, config) {
1404
+ const cfg = { ...DEFAULT_SPRING, ...config };
1405
+ const subDt = 1 / SIM_HZ;
1406
+ const stepMs = SAMPLE_STRIDE / SIM_HZ * 1e3;
1407
+ const maxSteps = Math.ceil(MAX_SIM_MS / 1e3 * SIM_HZ);
1408
+ const values = [from];
1409
+ let x = from;
1410
+ let v = velocityPxPerS;
1411
+ for (let i = 1; i <= maxSteps; i += 1) {
1412
+ const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1413
+ v += force / cfg.mass * subDt;
1414
+ x += v * subDt;
1415
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1416
+ values.push(to);
1417
+ break;
1418
+ }
1419
+ if (i % SAMPLE_STRIDE === 0) values.push(x);
1420
+ const atRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1421
+ if (atRest) break;
1422
+ }
1423
+ if (values[values.length - 1] !== to) values.push(to);
1424
+ return {
1425
+ values,
1426
+ stepMs,
1427
+ durationMs: (values.length - 1) * stepMs
1428
+ };
1429
+ }
1430
+ function sampleTweenSettle(from, to, durationMs, easing) {
1431
+ const count = Math.max(2, Math.min(60, Math.round(durationMs / 16)));
1432
+ const values = [];
1433
+ for (let i = 0; i <= count; i += 1) {
1434
+ const t = i / count;
1435
+ values.push(from + (to - from) * easing(t));
1436
+ }
1437
+ values[values.length - 1] = to;
1438
+ return {
1439
+ values,
1440
+ stepMs: durationMs / count,
1441
+ durationMs
1442
+ };
1443
+ }
1444
+ var SIM_HZ, SAMPLE_STRIDE, MAX_SIM_MS;
1445
+ var init_waapi_settle = __esm({
1446
+ "src/core/animation/waapi-settle.ts"() {
1447
+ init_spring();
1448
+ SIM_HZ = 240;
1449
+ SAMPLE_STRIDE = 4;
1450
+ MAX_SIM_MS = 3e3;
1451
+ }
1452
+ });
1453
+
1827
1454
  // src/core/controllers/animation-runner.ts
1828
1455
  var DEFAULT_DURATION2, VELOCITY_PX_PER_S, AnimationRunner;
1829
1456
  var init_animation_runner = __esm({
@@ -1831,6 +1458,7 @@ var init_animation_runner = __esm({
1831
1458
  init_animation();
1832
1459
  init_spring();
1833
1460
  init_animation_presets();
1461
+ init_waapi_settle();
1834
1462
  DEFAULT_DURATION2 = 220;
1835
1463
  VELOCITY_PX_PER_S = 1e3;
1836
1464
  AnimationRunner = class {
@@ -1840,6 +1468,10 @@ var init_animation_runner = __esm({
1840
1468
  __publicField(this, "applySizeFn");
1841
1469
  __publicField(this, "getSize");
1842
1470
  __publicField(this, "isDragging");
1471
+ __publicField(this, "applyAux");
1472
+ __publicField(this, "getTransformFor");
1473
+ __publicField(this, "settleWaapi");
1474
+ __publicField(this, "currentWaapi", null);
1843
1475
  __publicField(this, "animationKind");
1844
1476
  __publicField(this, "springConfig");
1845
1477
  __publicField(this, "duration");
@@ -1855,6 +1487,9 @@ var init_animation_runner = __esm({
1855
1487
  this.applySizeFn = deps.applySize;
1856
1488
  this.getSize = deps.getSize;
1857
1489
  this.isDragging = deps.isDragging;
1490
+ this.applyAux = deps.applyAux;
1491
+ this.getTransformFor = deps.getTransformFor;
1492
+ this.settleWaapi = opts.settleAnimation === "waapi";
1858
1493
  const preset = resolveAnimationPreset(opts.animation);
1859
1494
  this.animationKind = preset.kind;
1860
1495
  this.duration = opts.duration ?? preset.duration ?? DEFAULT_DURATION2;
@@ -1879,11 +1514,20 @@ var init_animation_runner = __esm({
1879
1514
  }
1880
1515
  }
1881
1516
  get isAnimating() {
1882
- return this.currentTween !== null || this.currentSpring !== null;
1517
+ return this.currentTween !== null || this.currentSpring !== null || this.currentWaapi !== null;
1883
1518
  }
1884
1519
  cancel() {
1885
1520
  this.currentTween?.cancel();
1886
1521
  this.currentSpring?.cancel();
1522
+ if (this.currentWaapi) {
1523
+ const entry = this.currentWaapi;
1524
+ this.currentWaapi = null;
1525
+ entry.stop();
1526
+ try {
1527
+ entry.anim.cancel();
1528
+ } catch {
1529
+ }
1530
+ }
1887
1531
  }
1888
1532
  async animateTo(target, velocityPxPerMs) {
1889
1533
  if (target === this.getSize()) {
@@ -1902,7 +1546,28 @@ var init_animation_runner = __esm({
1902
1546
  if (!this.isDragging()) this.element.style.willChange = "";
1903
1547
  return;
1904
1548
  }
1905
- if (this.animationKind === "spring") {
1549
+ if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1550
+ const samples = this.animationKind === "spring" ? sampleSpringSettle(
1551
+ this.getSize(),
1552
+ target,
1553
+ velocityPxPerMs * VELOCITY_PX_PER_S,
1554
+ this.springConfig
1555
+ ) : sampleTweenSettle(
1556
+ this.getSize(),
1557
+ target,
1558
+ this.duration,
1559
+ this.easing
1560
+ );
1561
+ if (samples.values.length >= 2 && samples.durationMs > 0) {
1562
+ await this.runWaapiSettle(samples, target);
1563
+ if (!this.isAnimating) {
1564
+ this.getRootEl()?.removeAttribute("data-animating");
1565
+ if (!this.isDragging()) this.element.style.willChange = "";
1566
+ }
1567
+ return;
1568
+ }
1569
+ }
1570
+ if (this.animationKind === "spring") {
1906
1571
  const spring = runSpring({
1907
1572
  from: this.getSize(),
1908
1573
  to: target,
@@ -1930,11 +1595,66 @@ var init_animation_runner = __esm({
1930
1595
  if (!this.isDragging()) this.element.style.willChange = "";
1931
1596
  }
1932
1597
  }
1598
+ async runWaapiSettle(samples, target) {
1599
+ const applyAux = this.applyAux;
1600
+ const getTransformFor = this.getTransformFor;
1601
+ const frames = samples.values.map((v) => ({
1602
+ transform: getTransformFor(v)
1603
+ }));
1604
+ const anim = this.element.animate(frames, {
1605
+ duration: samples.durationMs,
1606
+ easing: "linear",
1607
+ fill: "forwards"
1608
+ });
1609
+ let stopped = false;
1610
+ let auxRaf = 0;
1611
+ const start = performance.now();
1612
+ const lastIdx = samples.values.length - 1;
1613
+ const auxTick = () => {
1614
+ if (stopped) return;
1615
+ const elapsed = performance.now() - start;
1616
+ const idx = Math.min(Math.floor(elapsed / samples.stepMs), lastIdx);
1617
+ applyAux(samples.values[idx]);
1618
+ auxRaf = requestAnimationFrame(auxTick);
1619
+ };
1620
+ auxRaf = requestAnimationFrame(auxTick);
1621
+ const entry = {
1622
+ anim,
1623
+ stop: () => {
1624
+ stopped = true;
1625
+ cancelAnimationFrame(auxRaf);
1626
+ }
1627
+ };
1628
+ this.currentWaapi = entry;
1629
+ try {
1630
+ await anim.finished;
1631
+ } catch {
1632
+ }
1633
+ entry.stop();
1634
+ const wasCurrent = this.currentWaapi === entry;
1635
+ if (wasCurrent) this.currentWaapi = null;
1636
+ if (wasCurrent && anim.playState === "finished") {
1637
+ this.applySizeFn(target);
1638
+ try {
1639
+ anim.cancel();
1640
+ } catch {
1641
+ }
1642
+ }
1643
+ }
1933
1644
  destroy() {
1934
1645
  this.detachReducedMotion?.();
1935
1646
  this.detachReducedMotion = null;
1936
1647
  this.currentTween = null;
1937
1648
  this.currentSpring = null;
1649
+ if (this.currentWaapi) {
1650
+ const entry = this.currentWaapi;
1651
+ this.currentWaapi = null;
1652
+ entry.stop();
1653
+ try {
1654
+ entry.anim.cancel();
1655
+ } catch {
1656
+ }
1657
+ }
1938
1658
  }
1939
1659
  };
1940
1660
  }
@@ -2862,7 +2582,7 @@ function resolveEngineOptions(opts, extras) {
2862
2582
  const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
2863
2583
  let initialId = opts.initial ?? initialAllowed[0] ?? opts.snapPoints[0]?.id ?? "default";
2864
2584
  if (opts.persistKey) {
2865
- const restored = readPersistedId(opts.persistKey);
2585
+ const restored = readPersistedIdSafe(opts.persistKey);
2866
2586
  if (restored && initialAllowed.includes(restored)) {
2867
2587
  initialId = restored;
2868
2588
  }
@@ -2893,6 +2613,7 @@ function resolveEngineOptions(opts, extras) {
2893
2613
  },
2894
2614
  animation: {
2895
2615
  animation: opts.animation,
2616
+ settleAnimation: opts.settleAnimation,
2896
2617
  duration: opts.duration,
2897
2618
  easing: opts.easing,
2898
2619
  spring: opts.spring,
@@ -2910,12 +2631,20 @@ function resolveEngineOptions(opts, extras) {
2910
2631
  }
2911
2632
  };
2912
2633
  }
2913
- var DEFAULT_FLICK_VELOCITY, DEFAULT_DRAG_THRESHOLD;
2634
+ var DEFAULT_FLICK_VELOCITY, DEFAULT_DRAG_THRESHOLD, readPersistedIdSafe;
2914
2635
  var init_engine_options = __esm({
2915
2636
  "src/core/primitives/engine-options.ts"() {
2916
- init_persist();
2917
2637
  DEFAULT_FLICK_VELOCITY = 0.65;
2918
2638
  DEFAULT_DRAG_THRESHOLD = 18;
2639
+ readPersistedIdSafe = (key) => {
2640
+ if (typeof window === "undefined") return null;
2641
+ if (typeof localStorage === "undefined") return null;
2642
+ try {
2643
+ return localStorage.getItem(key);
2644
+ } catch {
2645
+ return null;
2646
+ }
2647
+ };
2919
2648
  }
2920
2649
  });
2921
2650
 
@@ -2945,10 +2674,10 @@ var init_teardown_stack = __esm({
2945
2674
  }
2946
2675
  });
2947
2676
 
2948
- // src/core/BottomSheetEngine.ts
2949
- var HAPTIC_DURATION_MS, BottomSheetEngine;
2950
- var init_BottomSheetEngine = __esm({
2951
- "src/core/BottomSheetEngine.ts"() {
2677
+ // src/core/BottomSheetCore.ts
2678
+ var HAPTIC_DURATION_MS, BottomSheetCore;
2679
+ var init_BottomSheetCore = __esm({
2680
+ "src/core/BottomSheetCore.ts"() {
2952
2681
  init_sheetStack();
2953
2682
  init_transform();
2954
2683
  init_instance_id();
@@ -2957,12 +2686,7 @@ var init_BottomSheetEngine = __esm({
2957
2686
  init_cancelable_emit();
2958
2687
  init_devWarn();
2959
2688
  init_snap_resolver();
2960
- init_persist();
2961
- init_auto_collapse();
2962
2689
  init_linked_sheets();
2963
- init_route();
2964
- init_content_swipe();
2965
- init_visual_viewport();
2966
2690
  init_resize_observer();
2967
2691
  init_fit_observer();
2968
2692
  init_max_height_controller();
@@ -2981,7 +2705,7 @@ var init_BottomSheetEngine = __esm({
2981
2705
  init_teardown_stack();
2982
2706
  init_opacity_dedup();
2983
2707
  HAPTIC_DURATION_MS = 8;
2984
- BottomSheetEngine = class {
2708
+ BottomSheetCore = class {
2985
2709
  constructor(opts) {
2986
2710
  __publicField(this, "id", nextInstanceId("bs"));
2987
2711
  __publicField(this, "element");
@@ -3042,6 +2766,8 @@ var init_BottomSheetEngine = __esm({
3042
2766
  __publicField(this, "stackEffectPrimed", false);
3043
2767
  __publicField(this, "fitDeps");
3044
2768
  __publicField(this, "transformTemplate");
2769
+ __publicField(this, "featureList", []);
2770
+ __publicField(this, "featureCtx");
3045
2771
  const resolved = resolveEngineOptions(opts, {
3046
2772
  shouldApplyInertSiblings: () => {
3047
2773
  if (typeof document === "undefined") return false;
@@ -3088,7 +2814,9 @@ var init_BottomSheetEngine = __esm({
3088
2814
  getRootEl: () => this.rootEl,
3089
2815
  applySize: (size) => this.applySize(size),
3090
2816
  getSize: () => this.size,
3091
- isDragging: () => this.gesture?.isDragging ?? false
2817
+ isDragging: () => this.gesture?.isDragging ?? false,
2818
+ applyAux: (size) => this.applySize(size, true),
2819
+ getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
3092
2820
  },
3093
2821
  resolved.animation
3094
2822
  );
@@ -3111,6 +2839,11 @@ var init_BottomSheetEngine = __esm({
3111
2839
  if (opts.linkedSheets) {
3112
2840
  this.linkedSheets = opts.linkedSheets;
3113
2841
  }
2842
+ const byName = /* @__PURE__ */ new Map();
2843
+ for (const feature of opts.features ?? []) {
2844
+ byName.set(feature.name, feature);
2845
+ }
2846
+ this.featureList = Array.from(byName.values());
3114
2847
  this.activeId = resolved.initialId;
3115
2848
  auditVhUsage(this.snapPointsRaw);
3116
2849
  this.auditDuplicateSnapIds(this.snapPointsRaw);
@@ -3147,6 +2880,7 @@ var init_BottomSheetEngine = __esm({
3147
2880
  scrollContainer: this.scrollContainer,
3148
2881
  getMaxAxisSize: () => this.snaps.getMaxAxisSize()
3149
2882
  });
2883
+ this.featureCtx = this.buildFeatureContext(opts.autoCollapseAfter);
3150
2884
  this.attach();
3151
2885
  this.registerInStack();
3152
2886
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
@@ -3178,46 +2912,77 @@ var init_BottomSheetEngine = __esm({
3178
2912
  if (resolved.radius !== void 0) this.setRadius(resolved.radius);
3179
2913
  if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
3180
2914
  this.applyAutoAriaLabelledBy();
3181
- if (this.closeOnRouteChange) {
3182
- this.teardowns.add(
3183
- installRouteChange({
3184
- isDestroyed: () => this.destroyed,
3185
- getSize: () => this.size,
3186
- close: () => this.close()
3187
- })
3188
- );
3189
- }
3190
- if (this.persistKey) {
3191
- this.teardowns.add(installPersist(this, this.persistKey));
2915
+ this.runFeatureStage("post");
2916
+ this.warnOrphanFeatureOptions();
2917
+ if (this.size > 0) {
2918
+ this.handleOpen();
3192
2919
  }
3193
- this.teardowns.add(
3194
- installAutoCollapse({
3195
- ms: opts.autoCollapseAfter,
3196
- isDestroyed: () => this.destroyed,
3197
- isDragging: () => this.gesture?.isDragging ?? false,
3198
- getAllowedIds: () => this.snaps.getAllowedIds().slice(),
3199
- getActiveId: () => this.activeId,
3200
- resolveSnap: (id) => this.snaps.findById(id),
3201
- snapTo: (id) => {
3202
- void this.snapTo(id);
3203
- },
3204
- on: (event, fn) => this.on(event, fn)
3205
- })
3206
- );
3207
- this.teardowns.add(
3208
- installRoute({
2920
+ }
2921
+ buildFeatureContext(autoCollapseAfter) {
2922
+ return {
2923
+ element: this.element,
2924
+ scrollContainer: this.scrollContainer,
2925
+ sheetId: this.id,
2926
+ options: {
3209
2927
  routedTo: this.routedTo,
3210
2928
  closeOnBack: this.closeOnBack,
3211
- isTopSheet: () => this.isTopSheet,
3212
- getSize: () => this.size,
3213
- isDestroyed: () => this.destroyed,
3214
- close: () => this.close("back"),
3215
- on: (event, fn) => this.on(event, fn),
3216
- sheetId: this.id
3217
- })
3218
- );
3219
- if (this.size > 0) {
3220
- this.handleOpen();
2929
+ closeOnRouteChange: this.closeOnRouteChange,
2930
+ persistKey: this.persistKey,
2931
+ autoCollapseAfter
2932
+ },
2933
+ isDestroyed: () => this.destroyed,
2934
+ isDragging: () => this.gesture?.isDragging ?? false,
2935
+ isAnimating: () => this.animation.isAnimating,
2936
+ isTopSheet: () => this.isTopSheet,
2937
+ isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
2938
+ getSize: () => this.size,
2939
+ getActiveId: () => this.activeId,
2940
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2941
+ allowedIdsBySize: () => this.allowedIdsBySize(),
2942
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2943
+ resolveSnap: (id) => this.snaps.findById(id),
2944
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
2945
+ setSize: (size) => {
2946
+ this.size = size;
2947
+ },
2948
+ setMaxAxisSize: (size) => {
2949
+ this.snaps.setMaxAxisSize(size);
2950
+ },
2951
+ applySize: (size) => this.applySize(size),
2952
+ recomputeSnaps: () => this.recomputeSnaps(),
2953
+ newCycle: () => {
2954
+ this.newCycle();
2955
+ },
2956
+ cancelInFlight: () => {
2957
+ this.animation.cancel();
2958
+ },
2959
+ resyncAfterResize: () => this.resyncAfterResize(),
2960
+ snapTo: (id) => {
2961
+ void this.snapTo(id);
2962
+ },
2963
+ close: (reason) => this.close(reason),
2964
+ on: (event, fn) => this.on(event, fn),
2965
+ addTeardown: (fn) => this.teardowns.add(fn)
2966
+ };
2967
+ }
2968
+ runFeatureStage(stage) {
2969
+ for (const feature of this.featureList) {
2970
+ if ((feature.stage ?? "post") !== stage) continue;
2971
+ const teardown = feature.install(this.featureCtx);
2972
+ if (teardown) this.teardowns.add(teardown);
2973
+ }
2974
+ }
2975
+ warnOrphanFeatureOptions() {
2976
+ const names = new Set(this.featureList.map((f) => f.name));
2977
+ if ((this.closeOnBack || this.routedTo !== void 0 || this.closeOnRouteChange) && !names.has("route")) {
2978
+ devWarn(
2979
+ "[BottomSheet] closeOnBack/routedTo/closeOnRouteChange need routeFeature() \u2014 option ignored"
2980
+ );
2981
+ }
2982
+ if (this.persistKey && !names.has("persist")) {
2983
+ devWarn(
2984
+ "[BottomSheet] persistKey needs persistFeature() \u2014 snaps will not be saved"
2985
+ );
3221
2986
  }
3222
2987
  }
3223
2988
  get state() {
@@ -3760,18 +3525,7 @@ var init_BottomSheetEngine = __esm({
3760
3525
  }
3761
3526
  }
3762
3527
  attachFeatures() {
3763
- if (this.scrollContainer) {
3764
- this.teardowns.add(installContentSwipe({
3765
- container: this.scrollContainer,
3766
- isDragging: () => this.gesture?.isDragging ?? false,
3767
- isAnimating: () => this.animation.isAnimating,
3768
- getAllowedIds: () => this.allowedIdsBySize(),
3769
- getActiveId: () => this.activeId,
3770
- snapTo: (id) => {
3771
- void this.snapTo(id);
3772
- }
3773
- }));
3774
- }
3528
+ this.runFeatureStage("attach");
3775
3529
  if (typeof window !== "undefined") {
3776
3530
  this.teardowns.add(installResizeObserver({
3777
3531
  element: this.element,
@@ -3811,31 +3565,6 @@ var init_BottomSheetEngine = __esm({
3811
3565
  () => document.removeEventListener("keydown", onKey)
3812
3566
  );
3813
3567
  }
3814
- this.teardowns.add(installVisualViewport({
3815
- element: this.element,
3816
- isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
3817
- isDestroyed: () => this.destroyed,
3818
- isDragging: () => this.gesture?.isDragging ?? false,
3819
- recomputeSnaps: () => this.recomputeSnaps(),
3820
- resolveActiveSnap: () => this.snaps.findById(this.activeId),
3821
- getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3822
- getSize: () => this.size,
3823
- setMaxAxisSize: (size) => {
3824
- this.snaps.setMaxAxisSize(size);
3825
- },
3826
- setSize: (size) => {
3827
- this.size = size;
3828
- },
3829
- applySize: (size) => this.applySize(size),
3830
- cancelInFlight: () => {
3831
- this.animation.cancel();
3832
- },
3833
- newCycle: () => {
3834
- this.newCycle();
3835
- },
3836
- isAnimating: () => this.animation.isAnimating,
3837
- resyncAfterCancel: () => this.resyncAfterResize()
3838
- }));
3839
3568
  }
3840
3569
  this.teardowns.add(installSliderKeyboard({
3841
3570
  handle: this.handle,
@@ -3912,13 +3641,13 @@ var init_BottomSheetEngine = __esm({
3912
3641
  }
3913
3642
  }
3914
3643
  }
3915
- applySize(size) {
3644
+ applySize(size, skipTransform = false) {
3916
3645
  const cap = this.snaps.getMaxAxisSize();
3917
3646
  const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3918
3647
  this.size = clamped;
3919
3648
  const offset = cap - clamped;
3920
3649
  const style = this.element.style;
3921
- style.transform = this.transformTemplate(offset);
3650
+ if (!skipTransform) style.transform = this.transformTemplate(offset);
3922
3651
  if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3923
3652
  style.setProperty("--bs-size", `${clamped}px`);
3924
3653
  if (this.scrimParent) {
@@ -3998,6 +3727,564 @@ var init_BottomSheetEngine = __esm({
3998
3727
  }
3999
3728
  });
4000
3729
 
3730
+ // src/core/features/history-coordinator.ts
3731
+ function onPopState() {
3732
+ if (suppress > 0) {
3733
+ suppress -= 1;
3734
+ const rebrand = pendingRebrands.shift();
3735
+ if (rebrand) rebrand();
3736
+ maybeTeardownListener();
3737
+ return;
3738
+ }
3739
+ if (entries.length === 0) {
3740
+ notifyRouteChange();
3741
+ maybeTeardownListener();
3742
+ return;
3743
+ }
3744
+ let target = null;
3745
+ let targetIdx = -1;
3746
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
3747
+ const entry = entries[i];
3748
+ if (!entry.surface.isOpen()) continue;
3749
+ if (!target) {
3750
+ target = entry;
3751
+ targetIdx = i;
3752
+ }
3753
+ if (entry.surface.isTop()) {
3754
+ target = entry;
3755
+ targetIdx = i;
3756
+ break;
3757
+ }
3758
+ }
3759
+ if (!target) {
3760
+ const removed = entries.pop();
3761
+ if (removed) removed.live = false;
3762
+ maybeTeardownListener();
3763
+ return;
3764
+ }
3765
+ target.live = false;
3766
+ entries.splice(targetIdx, 1);
3767
+ const settled = target;
3768
+ void Promise.resolve(settled.surface.close()).then(() => {
3769
+ if (settled.surface.isOpen()) restore(settled);
3770
+ });
3771
+ maybeTeardownListener();
3772
+ }
3773
+ function restore(entry) {
3774
+ if (!hasWindow() || !hasHistory()) return;
3775
+ entry.live = true;
3776
+ runInternal(() => {
3777
+ try {
3778
+ history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3779
+ } catch {
3780
+ entry.live = false;
3781
+ }
3782
+ });
3783
+ if (entry.live) {
3784
+ entries.push(entry);
3785
+ ensureListener();
3786
+ }
3787
+ }
3788
+ function pushBackMarker(surface) {
3789
+ const entry = { surface, priorUrl: void 0, live: false };
3790
+ if (!hasWindow() || !hasHistory()) {
3791
+ return entry;
3792
+ }
3793
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
3794
+ runInternal(() => {
3795
+ try {
3796
+ history.pushState(brandMarker(surface), "", surface.url);
3797
+ entry.live = true;
3798
+ } catch {
3799
+ entry.live = false;
3800
+ }
3801
+ });
3802
+ if (entry.live) {
3803
+ entries.push(entry);
3804
+ ensureListener();
3805
+ }
3806
+ return entry;
3807
+ }
3808
+ function popBackMarker(handle) {
3809
+ if (!hasWindow() || !hasHistory()) return;
3810
+ const entry = handle;
3811
+ const idx = entries.indexOf(entry);
3812
+ if (idx === -1 || !entry.live) return;
3813
+ const last = entries.length - 1;
3814
+ entry.live = false;
3815
+ entries.splice(idx, 1);
3816
+ let rebrand = null;
3817
+ if (idx !== last) {
3818
+ const rebrandSurface = entries[entries.length - 1].surface;
3819
+ const rebrandUrl = entry.priorUrl;
3820
+ rebrand = () => {
3821
+ runInternal(() => {
3822
+ history.replaceState(
3823
+ brandMarker(rebrandSurface),
3824
+ "",
3825
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
3826
+ );
3827
+ });
3828
+ };
3829
+ }
3830
+ pendingRebrands.push(rebrand);
3831
+ suppress += 1;
3832
+ runInternal(() => {
3833
+ try {
3834
+ history.back();
3835
+ } catch {
3836
+ suppress = Math.max(0, suppress - 1);
3837
+ pendingRebrands.pop();
3838
+ }
3839
+ });
3840
+ maybeTeardownListener();
3841
+ }
3842
+ function subscribeRouteChange(fn) {
3843
+ if (!hasWindow() || !hasHistory()) return () => {
3844
+ };
3845
+ subscribers.add(fn);
3846
+ if (subscribers.size === 1) installPatch();
3847
+ ensureListener();
3848
+ return () => {
3849
+ if (!subscribers.has(fn)) return;
3850
+ subscribers.delete(fn);
3851
+ if (subscribers.size === 0) removePatch();
3852
+ maybeTeardownListener();
3853
+ };
3854
+ }
3855
+ var entries, subscribers, suppress, internalDepth, pendingRebrands, listenerInstalled, patchInstalled, origPushState, origReplaceState, patchedPushState, patchedReplaceState, hasWindow, hasHistory, brandMarker, runInternal, notifyRouteChange, ensureListener, maybeTeardownListener, installPatch, removePatch;
3856
+ var init_history_coordinator = __esm({
3857
+ "src/core/features/history-coordinator.ts"() {
3858
+ entries = [];
3859
+ subscribers = /* @__PURE__ */ new Set();
3860
+ suppress = 0;
3861
+ internalDepth = 0;
3862
+ pendingRebrands = [];
3863
+ listenerInstalled = false;
3864
+ patchInstalled = false;
3865
+ origPushState = null;
3866
+ origReplaceState = null;
3867
+ patchedPushState = null;
3868
+ patchedReplaceState = null;
3869
+ hasWindow = () => typeof window !== "undefined";
3870
+ hasHistory = () => typeof history !== "undefined";
3871
+ brandMarker = (surface) => ({
3872
+ ...surface.markerState(),
3873
+ __bs: true
3874
+ });
3875
+ runInternal = (fn) => {
3876
+ internalDepth += 1;
3877
+ try {
3878
+ fn();
3879
+ } finally {
3880
+ internalDepth -= 1;
3881
+ }
3882
+ };
3883
+ notifyRouteChange = () => {
3884
+ const snapshot = Array.from(subscribers);
3885
+ for (const fn of snapshot) fn();
3886
+ };
3887
+ ensureListener = () => {
3888
+ if (listenerInstalled || !hasWindow()) return;
3889
+ window.addEventListener("popstate", onPopState);
3890
+ listenerInstalled = true;
3891
+ };
3892
+ maybeTeardownListener = () => {
3893
+ if (!listenerInstalled) return;
3894
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3895
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
3896
+ listenerInstalled = false;
3897
+ };
3898
+ installPatch = () => {
3899
+ if (patchInstalled || !hasHistory()) return;
3900
+ origPushState = history.pushState;
3901
+ origReplaceState = history.replaceState;
3902
+ patchedPushState = function(...args) {
3903
+ origPushState.apply(this, args);
3904
+ if (internalDepth === 0) notifyRouteChange();
3905
+ };
3906
+ patchedReplaceState = function(...args) {
3907
+ origReplaceState.apply(this, args);
3908
+ if (internalDepth === 0) notifyRouteChange();
3909
+ };
3910
+ history.pushState = patchedPushState;
3911
+ history.replaceState = patchedReplaceState;
3912
+ patchInstalled = true;
3913
+ };
3914
+ removePatch = () => {
3915
+ if (!patchInstalled) return;
3916
+ if (history.pushState === patchedPushState && origPushState) {
3917
+ history.pushState = origPushState;
3918
+ }
3919
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
3920
+ history.replaceState = origReplaceState;
3921
+ }
3922
+ origPushState = null;
3923
+ origReplaceState = null;
3924
+ patchedPushState = null;
3925
+ patchedReplaceState = null;
3926
+ patchInstalled = false;
3927
+ };
3928
+ }
3929
+ });
3930
+
3931
+ // src/core/features/route.ts
3932
+ function installRoute(deps) {
3933
+ if (typeof window === "undefined") {
3934
+ return () => {
3935
+ };
3936
+ }
3937
+ if (!deps.routedTo && !deps.closeOnBack) {
3938
+ return () => {
3939
+ };
3940
+ }
3941
+ let handle = null;
3942
+ const onOpen = () => {
3943
+ if (handle) return;
3944
+ handle = pushBackMarker({
3945
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
3946
+ isTop: deps.isTopSheet,
3947
+ close: deps.close,
3948
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
3949
+ url: deps.routedTo
3950
+ });
3951
+ };
3952
+ const onClose = () => {
3953
+ if (!handle) return;
3954
+ popBackMarker(handle);
3955
+ handle = null;
3956
+ };
3957
+ const unsubscribeOpen = deps.on("open", onOpen);
3958
+ const unsubscribeClose = deps.on("close", onClose);
3959
+ if (deps.getSize() > 0) onOpen();
3960
+ return () => {
3961
+ unsubscribeOpen();
3962
+ unsubscribeClose();
3963
+ if (handle) {
3964
+ popBackMarker(handle);
3965
+ handle = null;
3966
+ }
3967
+ };
3968
+ }
3969
+ function installRouteChange(deps) {
3970
+ return subscribeRouteChange(() => {
3971
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
3972
+ void deps.close();
3973
+ });
3974
+ }
3975
+ var init_route = __esm({
3976
+ "src/core/features/route.ts"() {
3977
+ init_history_coordinator();
3978
+ }
3979
+ });
3980
+
3981
+ // src/core/features/persist.ts
3982
+ function installPersist(engine, key) {
3983
+ const unsubscribe = engine.on("snap", ({ id }) => {
3984
+ if (typeof window === "undefined") return;
3985
+ if (typeof localStorage === "undefined") return;
3986
+ try {
3987
+ localStorage.setItem(key, id);
3988
+ } catch {
3989
+ }
3990
+ });
3991
+ return unsubscribe;
3992
+ }
3993
+ var init_persist = __esm({
3994
+ "src/core/features/persist.ts"() {
3995
+ }
3996
+ });
3997
+
3998
+ // src/core/features/auto-collapse.ts
3999
+ function installAutoCollapse(deps) {
4000
+ const { ms } = deps;
4001
+ if (ms === void 0 || ms <= 0) {
4002
+ return () => {
4003
+ };
4004
+ }
4005
+ let timer = null;
4006
+ const clearTimer = () => {
4007
+ if (timer !== null) {
4008
+ clearTimeout(timer);
4009
+ timer = null;
4010
+ }
4011
+ };
4012
+ const fire = () => {
4013
+ if (deps.isDestroyed()) return;
4014
+ if (deps.isDragging()) return;
4015
+ const active = deps.resolveSnap(deps.getActiveId());
4016
+ if (!active || active.size === 0) return;
4017
+ let target = null;
4018
+ for (const id of deps.getAllowedIds()) {
4019
+ const snap = deps.resolveSnap(id);
4020
+ if (!snap || snap.size <= 0) continue;
4021
+ if (!target || snap.size < target.size) target = snap;
4022
+ }
4023
+ if (!target) return;
4024
+ if (target.id === active.id || target.size >= active.size) return;
4025
+ deps.snapTo(target.id);
4026
+ };
4027
+ const reset = () => {
4028
+ clearTimer();
4029
+ if (deps.isDestroyed()) return;
4030
+ timer = setTimeout(() => {
4031
+ timer = null;
4032
+ fire();
4033
+ }, ms);
4034
+ };
4035
+ const unsubscribe = deps.on("snap", () => reset());
4036
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
4037
+ return () => {
4038
+ clearTimer();
4039
+ unsubscribe();
4040
+ };
4041
+ }
4042
+ var init_auto_collapse = __esm({
4043
+ "src/core/features/auto-collapse.ts"() {
4044
+ }
4045
+ });
4046
+
4047
+ // src/core/features/content-swipe.ts
4048
+ function installContentSwipe(deps) {
4049
+ const { container } = deps;
4050
+ let startY = 0;
4051
+ let startScrollTop = 0;
4052
+ let moved = false;
4053
+ const onTouchStart = (e) => {
4054
+ const t = e.touches[0];
4055
+ if (!t) return;
4056
+ startY = t.clientY;
4057
+ startScrollTop = container.scrollTop;
4058
+ moved = false;
4059
+ };
4060
+ const onTouchMove = (e) => {
4061
+ const t = e.touches[0];
4062
+ if (!t) return;
4063
+ if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
4064
+ };
4065
+ const onTouchEnd = (e) => {
4066
+ if (!moved) return;
4067
+ if (deps.isDragging() || deps.isAnimating()) return;
4068
+ if (container.scrollTop !== startScrollTop) return;
4069
+ const t = e.changedTouches[0];
4070
+ if (!t) return;
4071
+ const deltaY = t.clientY - startY;
4072
+ const atTop = container.scrollTop <= 0;
4073
+ const allowed = deps.getAllowedIds();
4074
+ const idx = allowed.indexOf(deps.getActiveId());
4075
+ if (idx === -1) return;
4076
+ if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
4077
+ deps.snapTo(allowed[idx - 1]);
4078
+ } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
4079
+ deps.snapTo(allowed[idx + 1]);
4080
+ }
4081
+ };
4082
+ container.addEventListener("touchstart", onTouchStart, { passive: true });
4083
+ container.addEventListener("touchmove", onTouchMove, { passive: true });
4084
+ container.addEventListener("touchend", onTouchEnd, { passive: true });
4085
+ return () => {
4086
+ container.removeEventListener("touchstart", onTouchStart);
4087
+ container.removeEventListener("touchmove", onTouchMove);
4088
+ container.removeEventListener("touchend", onTouchEnd);
4089
+ };
4090
+ }
4091
+ var CONTENT_PULL_THRESHOLD;
4092
+ var init_content_swipe = __esm({
4093
+ "src/core/features/content-swipe.ts"() {
4094
+ CONTENT_PULL_THRESHOLD = 60;
4095
+ }
4096
+ });
4097
+
4098
+ // src/core/features/visual-viewport.ts
4099
+ function installVisualViewport(deps) {
4100
+ if (typeof window === "undefined" || !window.visualViewport) {
4101
+ return () => {
4102
+ };
4103
+ }
4104
+ const vv = window.visualViewport;
4105
+ let rafId = null;
4106
+ const onResize = () => {
4107
+ if (rafId !== null) return;
4108
+ rafId = requestAnimationFrame(() => {
4109
+ rafId = null;
4110
+ if (deps.isDestroyed()) return;
4111
+ deps.recomputeSnaps();
4112
+ const isVertical = deps.isVerticalAxis();
4113
+ const visible = isVertical ? vv.height : vv.width;
4114
+ const windowSize = isVertical ? window.innerHeight : window.innerWidth;
4115
+ const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
4116
+ if (clamp < deps.getMaxAxisSize()) {
4117
+ deps.setMaxAxisSize(clamp);
4118
+ }
4119
+ const wasAnimating = deps.isAnimating?.() ?? false;
4120
+ deps.cancelInFlight();
4121
+ deps.newCycle();
4122
+ const max = deps.getMaxAxisSize();
4123
+ deps.setSize(Math.min(deps.getSize(), max));
4124
+ const current = deps.resolveActiveSnap();
4125
+ if (current && !deps.isDragging()) {
4126
+ deps.applySize(Math.min(current.size, max));
4127
+ if (wasAnimating) deps.resyncAfterCancel?.();
4128
+ } else {
4129
+ deps.applySize(deps.getSize());
4130
+ }
4131
+ });
4132
+ };
4133
+ vv.addEventListener("resize", onResize);
4134
+ return () => {
4135
+ vv.removeEventListener("resize", onResize);
4136
+ if (rafId !== null) cancelAnimationFrame(rafId);
4137
+ };
4138
+ }
4139
+ var SAFETY_PAD;
4140
+ var init_visual_viewport = __esm({
4141
+ "src/core/features/visual-viewport.ts"() {
4142
+ SAFETY_PAD = 8;
4143
+ }
4144
+ });
4145
+
4146
+ // src/core/features/engine-features.ts
4147
+ function routeFeature() {
4148
+ return {
4149
+ name: "route",
4150
+ install: (ctx) => {
4151
+ const teardowns = new TeardownStack();
4152
+ if (ctx.options.closeOnRouteChange) {
4153
+ teardowns.add(
4154
+ installRouteChange({
4155
+ isDestroyed: ctx.isDestroyed,
4156
+ getSize: ctx.getSize,
4157
+ close: () => ctx.close()
4158
+ })
4159
+ );
4160
+ }
4161
+ teardowns.add(
4162
+ installRoute({
4163
+ routedTo: ctx.options.routedTo,
4164
+ closeOnBack: ctx.options.closeOnBack,
4165
+ isTopSheet: ctx.isTopSheet,
4166
+ getSize: ctx.getSize,
4167
+ isDestroyed: ctx.isDestroyed,
4168
+ close: () => ctx.close("back"),
4169
+ on: ctx.on,
4170
+ sheetId: ctx.sheetId
4171
+ })
4172
+ );
4173
+ return () => teardowns.drain();
4174
+ }
4175
+ };
4176
+ }
4177
+ function persistFeature() {
4178
+ return {
4179
+ name: "persist",
4180
+ install: (ctx) => {
4181
+ const key = ctx.options.persistKey;
4182
+ if (!key) return;
4183
+ return installPersist({ on: ctx.on }, key);
4184
+ }
4185
+ };
4186
+ }
4187
+ function autoCollapseFeature() {
4188
+ return {
4189
+ name: "auto-collapse",
4190
+ install: (ctx) => installAutoCollapse({
4191
+ ms: ctx.options.autoCollapseAfter,
4192
+ isDestroyed: ctx.isDestroyed,
4193
+ isDragging: ctx.isDragging,
4194
+ getAllowedIds: ctx.getAllowedIds,
4195
+ getActiveId: ctx.getActiveId,
4196
+ resolveSnap: ctx.resolveSnap,
4197
+ snapTo: ctx.snapTo,
4198
+ on: ctx.on
4199
+ })
4200
+ };
4201
+ }
4202
+ function contentSwipeFeature() {
4203
+ return {
4204
+ name: "content-swipe",
4205
+ stage: "attach",
4206
+ install: (ctx) => {
4207
+ if (!ctx.scrollContainer) return;
4208
+ return installContentSwipe({
4209
+ container: ctx.scrollContainer,
4210
+ isDragging: ctx.isDragging,
4211
+ isAnimating: ctx.isAnimating,
4212
+ getAllowedIds: ctx.allowedIdsBySize,
4213
+ getActiveId: ctx.getActiveId,
4214
+ snapTo: ctx.snapTo
4215
+ });
4216
+ }
4217
+ };
4218
+ }
4219
+ function visualViewportFeature() {
4220
+ return {
4221
+ name: "visual-viewport",
4222
+ install: (ctx) => {
4223
+ if (typeof window === "undefined") return;
4224
+ return installVisualViewport({
4225
+ element: ctx.element,
4226
+ isVerticalAxis: ctx.isVerticalAxis,
4227
+ isDestroyed: ctx.isDestroyed,
4228
+ isDragging: ctx.isDragging,
4229
+ recomputeSnaps: ctx.recomputeSnaps,
4230
+ resolveActiveSnap: ctx.resolveActiveSnap,
4231
+ getMaxAxisSize: ctx.getMaxAxisSize,
4232
+ getSize: ctx.getSize,
4233
+ setMaxAxisSize: ctx.setMaxAxisSize,
4234
+ setSize: ctx.setSize,
4235
+ applySize: ctx.applySize,
4236
+ cancelInFlight: ctx.cancelInFlight,
4237
+ newCycle: ctx.newCycle,
4238
+ isAnimating: ctx.isAnimating,
4239
+ resyncAfterCancel: ctx.resyncAfterResize
4240
+ });
4241
+ }
4242
+ };
4243
+ }
4244
+ var init_engine_features = __esm({
4245
+ "src/core/features/engine-features.ts"() {
4246
+ init_route();
4247
+ init_persist();
4248
+ init_auto_collapse();
4249
+ init_content_swipe();
4250
+ init_visual_viewport();
4251
+ init_teardown_stack();
4252
+ }
4253
+ });
4254
+
4255
+ // src/core/default-features.ts
4256
+ function defaultEngineFeatures() {
4257
+ return [
4258
+ contentSwipeFeature(),
4259
+ visualViewportFeature(),
4260
+ persistFeature(),
4261
+ autoCollapseFeature(),
4262
+ routeFeature()
4263
+ ];
4264
+ }
4265
+ var init_default_features = __esm({
4266
+ "src/core/default-features.ts"() {
4267
+ init_engine_features();
4268
+ }
4269
+ });
4270
+
4271
+ // src/core/BottomSheetEngine.ts
4272
+ var BottomSheetEngine;
4273
+ var init_BottomSheetEngine = __esm({
4274
+ "src/core/BottomSheetEngine.ts"() {
4275
+ init_BottomSheetCore();
4276
+ init_default_features();
4277
+ BottomSheetEngine = class extends BottomSheetCore {
4278
+ constructor(opts) {
4279
+ super({
4280
+ ...opts,
4281
+ features: [...defaultEngineFeatures(), ...opts.features ?? []]
4282
+ });
4283
+ }
4284
+ };
4285
+ }
4286
+ });
4287
+
4001
4288
  // src/web-component/baseStyles.ts
4002
4289
  var baseStyles;
4003
4290
  var init_baseStyles = __esm({