@surdeddd/bottom-sheet 0.11.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 +68 -1
  2. package/dist/{BottomSheetEngine-DzsIwD28.d.cts → BottomSheetCore-CsIjyq7C.d.cts} +61 -5
  3. package/dist/{BottomSheetEngine-DRjCU73e.d.ts → BottomSheetCore-D16lsa6g.d.ts} +61 -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 +994 -697
  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 +994 -697
  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 +823 -542
  26. package/dist/index.d.cts +6 -15
  27. package/dist/index.d.ts +6 -15
  28. package/dist/index.js +817 -543
  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 +13 -8
  34. package/dist/overlay.js +13 -8
  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 +816 -542
  40. package/dist/qwik.d.cts +2 -2
  41. package/dist/qwik.d.ts +2 -2
  42. package/dist/qwik.js +816 -542
  43. package/dist/react.cjs +816 -542
  44. package/dist/react.d.cts +3 -2
  45. package/dist/react.d.ts +3 -2
  46. package/dist/react.js +816 -542
  47. package/dist/solid.cjs +816 -542
  48. package/dist/solid.d.cts +64 -4
  49. package/dist/solid.d.ts +64 -4
  50. package/dist/solid.js +816 -542
  51. package/dist/solid.source.jsx +816 -542
  52. package/dist/svelte-core.cjs +816 -542
  53. package/dist/svelte-core.js +816 -542
  54. package/dist/svelte.cjs +816 -542
  55. package/dist/svelte.js +816 -542
  56. package/dist/vue.cjs +927 -645
  57. package/dist/vue.js +927 -645
  58. package/package.json +63 -11
package/dist/vue.js CHANGED
@@ -348,74 +348,6 @@ var SnapResolver = class {
348
348
  }
349
349
  };
350
350
 
351
- // src/core/features/persist.ts
352
- function readPersistedId(key) {
353
- if (!key) return null;
354
- if (typeof window === "undefined") return null;
355
- if (typeof localStorage === "undefined") return null;
356
- try {
357
- return localStorage.getItem(key);
358
- } catch {
359
- return null;
360
- }
361
- }
362
- function installPersist(engine, key) {
363
- const unsubscribe = engine.on("snap", ({ id }) => {
364
- if (typeof window === "undefined") return;
365
- if (typeof localStorage === "undefined") return;
366
- try {
367
- localStorage.setItem(key, id);
368
- } catch {
369
- }
370
- });
371
- return unsubscribe;
372
- }
373
-
374
- // src/core/features/auto-collapse.ts
375
- function installAutoCollapse(deps) {
376
- const { ms } = deps;
377
- if (ms === void 0 || ms <= 0) {
378
- return () => {
379
- };
380
- }
381
- let timer = null;
382
- const clearTimer = () => {
383
- if (timer !== null) {
384
- clearTimeout(timer);
385
- timer = null;
386
- }
387
- };
388
- const fire = () => {
389
- if (deps.isDestroyed()) return;
390
- if (deps.isDragging()) return;
391
- const active = deps.resolveSnap(deps.getActiveId());
392
- if (!active || active.size === 0) return;
393
- let target = null;
394
- for (const id of deps.getAllowedIds()) {
395
- const snap = deps.resolveSnap(id);
396
- if (!snap || snap.size <= 0) continue;
397
- if (!target || snap.size < target.size) target = snap;
398
- }
399
- if (!target) return;
400
- if (target.id === active.id || target.size >= active.size) return;
401
- deps.snapTo(target.id);
402
- };
403
- const reset = () => {
404
- clearTimer();
405
- if (deps.isDestroyed()) return;
406
- timer = setTimeout(() => {
407
- timer = null;
408
- fire();
409
- }, ms);
410
- };
411
- const unsubscribe = deps.on("snap", () => reset());
412
- if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
413
- return () => {
414
- clearTimer();
415
- unsubscribe();
416
- };
417
- }
418
-
419
351
  // src/core/features/linked-sheets.ts
420
352
  function notifyLinkedSheets(sheets, self) {
421
353
  if (sheets.length === 0) return;
@@ -438,390 +370,65 @@ function notifyLinkedSheets(sheets, self) {
438
370
  }
439
371
  }
440
372
 
441
- // src/core/features/history-coordinator.ts
442
- var entries = [];
443
- var subscribers = /* @__PURE__ */ new Set();
444
- var suppress = 0;
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;
373
+ // src/core/features/resize-observer.ts
374
+ function installResizeObserver(deps) {
375
+ if (typeof window === "undefined") {
376
+ return () => {
377
+ };
521
378
  }
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;
379
+ const onResize = () => {
380
+ if (deps.isDestroyed()) return;
381
+ deps.recomputeSnaps();
382
+ const mode = deps.getMode();
383
+ const isVerticalAxis = mode === "bottom" || mode === "top";
384
+ const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
385
+ const max = deps.getMaxAxisSize();
386
+ if (viewportSize2 > 0 && viewportSize2 < max) {
387
+ deps.setMaxAxisSize(viewportSize2);
535
388
  }
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(entry.surface.markerState(), "", entry.surface.url);
557
- } catch {
558
- entry.live = false;
389
+ const wasAnimating = deps.isAnimating?.() ?? false;
390
+ deps.cancelInFlight();
391
+ deps.newCycle();
392
+ const current = deps.resolveActiveSnap();
393
+ const newMax = deps.getMaxAxisSize();
394
+ const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
395
+ deps.setSize(targetSize);
396
+ if (current && !deps.isDragging()) {
397
+ deps.applySize(targetSize);
398
+ if (wasAnimating) deps.resyncAfterCancel?.();
559
399
  }
560
- });
561
- if (entry.live) {
562
- entries.push(entry);
563
- ensureListener();
400
+ };
401
+ let resizeObserver = null;
402
+ if (typeof ResizeObserver !== "undefined") {
403
+ resizeObserver = new ResizeObserver(onResize);
404
+ resizeObserver.observe(document.documentElement);
405
+ } else {
406
+ window.addEventListener("resize", onResize);
564
407
  }
408
+ window.addEventListener("orientationchange", onResize);
409
+ const onVisibility = () => {
410
+ if (deps.isDestroyed()) return;
411
+ if (document.hidden) deps.cancelInFlight();
412
+ };
413
+ document.addEventListener("visibilitychange", onVisibility);
414
+ return () => {
415
+ resizeObserver?.disconnect();
416
+ window.removeEventListener("resize", onResize);
417
+ window.removeEventListener("orientationchange", onResize);
418
+ document.removeEventListener("visibilitychange", onVisibility);
419
+ };
565
420
  }
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
633
- function installRoute(deps) {
634
- if (typeof window === "undefined") {
635
- return () => {
636
- };
637
- }
638
- if (!deps.routedTo && !deps.closeOnBack) {
639
- return () => {
640
- };
641
- }
642
- let handle = null;
643
- const onOpen = () => {
644
- if (handle) return;
645
- handle = pushBackMarker({
646
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
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;
657
- };
658
- const unsubscribeOpen = deps.on("open", onOpen);
659
- const unsubscribeClose = deps.on("close", onClose);
660
- if (deps.getSize() > 0) onOpen();
661
- return () => {
662
- unsubscribeOpen();
663
- unsubscribeClose();
664
- if (handle) {
665
- popBackMarker(handle);
666
- handle = null;
667
- }
668
- };
669
- }
670
- function installRouteChange(deps) {
671
- return subscribeRouteChange(() => {
672
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
673
- void deps.close();
674
- });
675
- }
676
-
677
- // src/core/features/content-swipe.ts
678
- var CONTENT_PULL_THRESHOLD = 60;
679
- function installContentSwipe(deps) {
680
- const { container } = deps;
681
- let startY = 0;
682
- let startScrollTop = 0;
683
- let moved = false;
684
- const onTouchStart = (e) => {
685
- const t = e.touches[0];
686
- if (!t) return;
687
- startY = t.clientY;
688
- startScrollTop = container.scrollTop;
689
- moved = false;
690
- };
691
- const onTouchMove = (e) => {
692
- const t = e.touches[0];
693
- if (!t) return;
694
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
695
- };
696
- const onTouchEnd = (e) => {
697
- if (!moved) return;
698
- if (deps.isDragging() || deps.isAnimating()) return;
699
- if (container.scrollTop !== startScrollTop) return;
700
- const t = e.changedTouches[0];
701
- if (!t) return;
702
- const deltaY = t.clientY - startY;
703
- const atTop = container.scrollTop <= 0;
704
- const allowed = deps.getAllowedIds();
705
- const idx = allowed.indexOf(deps.getActiveId());
706
- if (idx === -1) return;
707
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
708
- deps.snapTo(allowed[idx - 1]);
709
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
710
- deps.snapTo(allowed[idx + 1]);
711
- }
712
- };
713
- container.addEventListener("touchstart", onTouchStart, { passive: true });
714
- container.addEventListener("touchmove", onTouchMove, { passive: true });
715
- container.addEventListener("touchend", onTouchEnd, { passive: true });
716
- return () => {
717
- container.removeEventListener("touchstart", onTouchStart);
718
- container.removeEventListener("touchmove", onTouchMove);
719
- container.removeEventListener("touchend", onTouchEnd);
720
- };
721
- }
722
-
723
- // src/core/features/visual-viewport.ts
724
- var SAFETY_PAD = 8;
725
- function installVisualViewport(deps) {
726
- if (typeof window === "undefined" || !window.visualViewport) {
727
- return () => {
728
- };
729
- }
730
- const vv = window.visualViewport;
731
- let rafId = null;
732
- const onResize = () => {
733
- if (rafId !== null) return;
734
- rafId = requestAnimationFrame(() => {
735
- rafId = null;
736
- if (deps.isDestroyed()) return;
737
- deps.recomputeSnaps();
738
- const isVertical = deps.isVerticalAxis();
739
- const visible = isVertical ? vv.height : vv.width;
740
- const windowSize = isVertical ? window.innerHeight : window.innerWidth;
741
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
742
- if (clamp < deps.getMaxAxisSize()) {
743
- deps.setMaxAxisSize(clamp);
744
- }
745
- const wasAnimating = deps.isAnimating?.() ?? false;
746
- deps.cancelInFlight();
747
- deps.newCycle();
748
- const max = deps.getMaxAxisSize();
749
- deps.setSize(Math.min(deps.getSize(), max));
750
- const current = deps.resolveActiveSnap();
751
- if (current && !deps.isDragging()) {
752
- deps.applySize(Math.min(current.size, max));
753
- if (wasAnimating) deps.resyncAfterCancel?.();
754
- } else {
755
- deps.applySize(deps.getSize());
756
- }
757
- });
758
- };
759
- vv.addEventListener("resize", onResize);
760
- return () => {
761
- vv.removeEventListener("resize", onResize);
762
- if (rafId !== null) cancelAnimationFrame(rafId);
763
- };
764
- }
765
-
766
- // src/core/features/resize-observer.ts
767
- function installResizeObserver(deps) {
768
- if (typeof window === "undefined") {
769
- return () => {
770
- };
771
- }
772
- const onResize = () => {
773
- if (deps.isDestroyed()) return;
774
- deps.recomputeSnaps();
775
- const mode = deps.getMode();
776
- const isVerticalAxis = mode === "bottom" || mode === "top";
777
- const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
778
- const max = deps.getMaxAxisSize();
779
- if (viewportSize2 > 0 && viewportSize2 < max) {
780
- deps.setMaxAxisSize(viewportSize2);
781
- }
782
- const wasAnimating = deps.isAnimating?.() ?? false;
783
- deps.cancelInFlight();
784
- deps.newCycle();
785
- const current = deps.resolveActiveSnap();
786
- const newMax = deps.getMaxAxisSize();
787
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
788
- deps.setSize(targetSize);
789
- if (current && !deps.isDragging()) {
790
- deps.applySize(targetSize);
791
- if (wasAnimating) deps.resyncAfterCancel?.();
792
- }
793
- };
794
- let resizeObserver = null;
795
- if (typeof ResizeObserver !== "undefined") {
796
- resizeObserver = new ResizeObserver(onResize);
797
- resizeObserver.observe(document.documentElement);
798
- } else {
799
- window.addEventListener("resize", onResize);
800
- }
801
- window.addEventListener("orientationchange", onResize);
802
- const onVisibility = () => {
803
- if (deps.isDestroyed()) return;
804
- if (document.hidden) deps.cancelInFlight();
805
- };
806
- document.addEventListener("visibilitychange", onVisibility);
807
- return () => {
808
- resizeObserver?.disconnect();
809
- window.removeEventListener("resize", onResize);
810
- window.removeEventListener("orientationchange", onResize);
811
- document.removeEventListener("visibilitychange", onVisibility);
812
- };
813
- }
814
-
815
- // src/core/features/fit-observer.ts
816
- var contentTargets = (scroller) => {
817
- if (!scroller) return [];
818
- const hasSlot = typeof HTMLSlotElement !== "undefined";
819
- const out = [];
820
- for (const child of Array.from(scroller.children)) {
821
- if (hasSlot && child instanceof HTMLSlotElement) {
822
- for (const el of child.assignedElements()) out.push(el);
823
- } else {
824
- out.push(child);
421
+
422
+ // src/core/features/fit-observer.ts
423
+ var contentTargets = (scroller) => {
424
+ if (!scroller) return [];
425
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
426
+ const out = [];
427
+ for (const child of Array.from(scroller.children)) {
428
+ if (hasSlot && child instanceof HTMLSlotElement) {
429
+ for (const el of child.assignedElements()) out.push(el);
430
+ } else {
431
+ out.push(child);
825
432
  }
826
433
  }
827
434
  return out;
@@ -941,11 +548,15 @@ function createMaxHeightController(deps) {
941
548
 
942
549
  // src/core/features/fit-measurement.ts
943
550
  var measureSheetNatural = (element, content, vertical) => {
944
- if (!content) {
945
- return vertical ? element.offsetHeight : element.offsetWidth;
946
- }
947
551
  const axis = vertical ? "height" : "width";
948
552
  const sheetStyle = element.style;
553
+ if (!content) {
554
+ const prev = sheetStyle[axis];
555
+ sheetStyle[axis] = "auto";
556
+ const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
557
+ sheetStyle[axis] = prev;
558
+ return poked2;
559
+ }
949
560
  const cs = content.style;
950
561
  const prevSheet = sheetStyle[axis];
951
562
  const prevFlex = cs.flex;
@@ -1659,6 +1270,52 @@ function resolveAnimationPreset(animation) {
1659
1270
  }
1660
1271
  }
1661
1272
 
1273
+ // src/core/animation/waapi-settle.ts
1274
+ var SIM_HZ = 240;
1275
+ var SAMPLE_STRIDE = 4;
1276
+ var MAX_SIM_MS = 3e3;
1277
+ function sampleSpringSettle(from, to, velocityPxPerS, config) {
1278
+ const cfg = { ...DEFAULT_SPRING, ...config };
1279
+ const subDt = 1 / SIM_HZ;
1280
+ const stepMs = SAMPLE_STRIDE / SIM_HZ * 1e3;
1281
+ const maxSteps = Math.ceil(MAX_SIM_MS / 1e3 * SIM_HZ);
1282
+ const values = [from];
1283
+ let x = from;
1284
+ let v = velocityPxPerS;
1285
+ for (let i = 1; i <= maxSteps; i += 1) {
1286
+ const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1287
+ v += force / cfg.mass * subDt;
1288
+ x += v * subDt;
1289
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1290
+ values.push(to);
1291
+ break;
1292
+ }
1293
+ if (i % SAMPLE_STRIDE === 0) values.push(x);
1294
+ const atRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1295
+ if (atRest) break;
1296
+ }
1297
+ if (values[values.length - 1] !== to) values.push(to);
1298
+ return {
1299
+ values,
1300
+ stepMs,
1301
+ durationMs: (values.length - 1) * stepMs
1302
+ };
1303
+ }
1304
+ function sampleTweenSettle(from, to, durationMs, easing) {
1305
+ const count = Math.max(2, Math.min(60, Math.round(durationMs / 16)));
1306
+ const values = [];
1307
+ for (let i = 0; i <= count; i += 1) {
1308
+ const t = i / count;
1309
+ values.push(from + (to - from) * easing(t));
1310
+ }
1311
+ values[values.length - 1] = to;
1312
+ return {
1313
+ values,
1314
+ stepMs: durationMs / count,
1315
+ durationMs
1316
+ };
1317
+ }
1318
+
1662
1319
  // src/core/controllers/animation-runner.ts
1663
1320
  var DEFAULT_DURATION2 = 220;
1664
1321
  var VELOCITY_PX_PER_S = 1e3;
@@ -1669,6 +1326,10 @@ var AnimationRunner = class {
1669
1326
  __publicField(this, "applySizeFn");
1670
1327
  __publicField(this, "getSize");
1671
1328
  __publicField(this, "isDragging");
1329
+ __publicField(this, "applyAux");
1330
+ __publicField(this, "getTransformFor");
1331
+ __publicField(this, "settleWaapi");
1332
+ __publicField(this, "currentWaapi", null);
1672
1333
  __publicField(this, "animationKind");
1673
1334
  __publicField(this, "springConfig");
1674
1335
  __publicField(this, "duration");
@@ -1684,6 +1345,9 @@ var AnimationRunner = class {
1684
1345
  this.applySizeFn = deps.applySize;
1685
1346
  this.getSize = deps.getSize;
1686
1347
  this.isDragging = deps.isDragging;
1348
+ this.applyAux = deps.applyAux;
1349
+ this.getTransformFor = deps.getTransformFor;
1350
+ this.settleWaapi = opts.settleAnimation === "waapi";
1687
1351
  const preset = resolveAnimationPreset(opts.animation);
1688
1352
  this.animationKind = preset.kind;
1689
1353
  this.duration = opts.duration ?? preset.duration ?? DEFAULT_DURATION2;
@@ -1708,11 +1372,20 @@ var AnimationRunner = class {
1708
1372
  }
1709
1373
  }
1710
1374
  get isAnimating() {
1711
- return this.currentTween !== null || this.currentSpring !== null;
1375
+ return this.currentTween !== null || this.currentSpring !== null || this.currentWaapi !== null;
1712
1376
  }
1713
1377
  cancel() {
1714
1378
  this.currentTween?.cancel();
1715
1379
  this.currentSpring?.cancel();
1380
+ if (this.currentWaapi) {
1381
+ const entry = this.currentWaapi;
1382
+ this.currentWaapi = null;
1383
+ entry.stop();
1384
+ try {
1385
+ entry.anim.cancel();
1386
+ } catch {
1387
+ }
1388
+ }
1716
1389
  }
1717
1390
  async animateTo(target, velocityPxPerMs) {
1718
1391
  if (target === this.getSize()) {
@@ -1731,7 +1404,28 @@ var AnimationRunner = class {
1731
1404
  if (!this.isDragging()) this.element.style.willChange = "";
1732
1405
  return;
1733
1406
  }
1734
- if (this.animationKind === "spring") {
1407
+ if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1408
+ const samples = this.animationKind === "spring" ? sampleSpringSettle(
1409
+ this.getSize(),
1410
+ target,
1411
+ velocityPxPerMs * VELOCITY_PX_PER_S,
1412
+ this.springConfig
1413
+ ) : sampleTweenSettle(
1414
+ this.getSize(),
1415
+ target,
1416
+ this.duration,
1417
+ this.easing
1418
+ );
1419
+ if (samples.values.length >= 2 && samples.durationMs > 0) {
1420
+ await this.runWaapiSettle(samples, target);
1421
+ if (!this.isAnimating) {
1422
+ this.getRootEl()?.removeAttribute("data-animating");
1423
+ if (!this.isDragging()) this.element.style.willChange = "";
1424
+ }
1425
+ return;
1426
+ }
1427
+ }
1428
+ if (this.animationKind === "spring") {
1735
1429
  const spring = runSpring({
1736
1430
  from: this.getSize(),
1737
1431
  to: target,
@@ -1759,11 +1453,66 @@ var AnimationRunner = class {
1759
1453
  if (!this.isDragging()) this.element.style.willChange = "";
1760
1454
  }
1761
1455
  }
1456
+ async runWaapiSettle(samples, target) {
1457
+ const applyAux = this.applyAux;
1458
+ const getTransformFor = this.getTransformFor;
1459
+ const frames = samples.values.map((v) => ({
1460
+ transform: getTransformFor(v)
1461
+ }));
1462
+ const anim = this.element.animate(frames, {
1463
+ duration: samples.durationMs,
1464
+ easing: "linear",
1465
+ fill: "forwards"
1466
+ });
1467
+ let stopped = false;
1468
+ let auxRaf = 0;
1469
+ const start = performance.now();
1470
+ const lastIdx = samples.values.length - 1;
1471
+ const auxTick = () => {
1472
+ if (stopped) return;
1473
+ const elapsed = performance.now() - start;
1474
+ const idx = Math.min(Math.floor(elapsed / samples.stepMs), lastIdx);
1475
+ applyAux(samples.values[idx]);
1476
+ auxRaf = requestAnimationFrame(auxTick);
1477
+ };
1478
+ auxRaf = requestAnimationFrame(auxTick);
1479
+ const entry = {
1480
+ anim,
1481
+ stop: () => {
1482
+ stopped = true;
1483
+ cancelAnimationFrame(auxRaf);
1484
+ }
1485
+ };
1486
+ this.currentWaapi = entry;
1487
+ try {
1488
+ await anim.finished;
1489
+ } catch {
1490
+ }
1491
+ entry.stop();
1492
+ const wasCurrent = this.currentWaapi === entry;
1493
+ if (wasCurrent) this.currentWaapi = null;
1494
+ if (wasCurrent && anim.playState === "finished") {
1495
+ this.applySizeFn(target);
1496
+ try {
1497
+ anim.cancel();
1498
+ } catch {
1499
+ }
1500
+ }
1501
+ }
1762
1502
  destroy() {
1763
1503
  this.detachReducedMotion?.();
1764
1504
  this.detachReducedMotion = null;
1765
1505
  this.currentTween = null;
1766
1506
  this.currentSpring = null;
1507
+ if (this.currentWaapi) {
1508
+ const entry = this.currentWaapi;
1509
+ this.currentWaapi = null;
1510
+ entry.stop();
1511
+ try {
1512
+ entry.anim.cancel();
1513
+ } catch {
1514
+ }
1515
+ }
1767
1516
  }
1768
1517
  };
1769
1518
 
@@ -2620,7 +2369,7 @@ function resolveEngineOptions(opts, extras) {
2620
2369
  const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
2621
2370
  let initialId = opts.initial ?? initialAllowed[0] ?? opts.snapPoints[0]?.id ?? "default";
2622
2371
  if (opts.persistKey) {
2623
- const restored = readPersistedId(opts.persistKey);
2372
+ const restored = readPersistedIdSafe(opts.persistKey);
2624
2373
  if (restored && initialAllowed.includes(restored)) {
2625
2374
  initialId = restored;
2626
2375
  }
@@ -2651,6 +2400,7 @@ function resolveEngineOptions(opts, extras) {
2651
2400
  },
2652
2401
  animation: {
2653
2402
  animation: opts.animation,
2403
+ settleAnimation: opts.settleAnimation,
2654
2404
  duration: opts.duration,
2655
2405
  easing: opts.easing,
2656
2406
  spring: opts.spring,
@@ -2668,6 +2418,15 @@ function resolveEngineOptions(opts, extras) {
2668
2418
  }
2669
2419
  };
2670
2420
  }
2421
+ var readPersistedIdSafe = (key) => {
2422
+ if (typeof window === "undefined") return null;
2423
+ if (typeof localStorage === "undefined") return null;
2424
+ try {
2425
+ return localStorage.getItem(key);
2426
+ } catch {
2427
+ return null;
2428
+ }
2429
+ };
2671
2430
 
2672
2431
  // src/core/primitives/teardown-stack.ts
2673
2432
  var TeardownStack = class {
@@ -2690,9 +2449,9 @@ var TeardownStack = class {
2690
2449
  }
2691
2450
  };
2692
2451
 
2693
- // src/core/BottomSheetEngine.ts
2452
+ // src/core/BottomSheetCore.ts
2694
2453
  var HAPTIC_DURATION_MS = 8;
2695
- var BottomSheetEngine = class {
2454
+ var BottomSheetCore = class {
2696
2455
  constructor(opts) {
2697
2456
  __publicField(this, "id", nextInstanceId("bs"));
2698
2457
  __publicField(this, "element");
@@ -2753,6 +2512,8 @@ var BottomSheetEngine = class {
2753
2512
  __publicField(this, "stackEffectPrimed", false);
2754
2513
  __publicField(this, "fitDeps");
2755
2514
  __publicField(this, "transformTemplate");
2515
+ __publicField(this, "featureList", []);
2516
+ __publicField(this, "featureCtx");
2756
2517
  const resolved = resolveEngineOptions(opts, {
2757
2518
  shouldApplyInertSiblings: () => {
2758
2519
  if (typeof document === "undefined") return false;
@@ -2799,7 +2560,9 @@ var BottomSheetEngine = class {
2799
2560
  getRootEl: () => this.rootEl,
2800
2561
  applySize: (size) => this.applySize(size),
2801
2562
  getSize: () => this.size,
2802
- isDragging: () => this.gesture?.isDragging ?? false
2563
+ isDragging: () => this.gesture?.isDragging ?? false,
2564
+ applyAux: (size) => this.applySize(size, true),
2565
+ getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2803
2566
  },
2804
2567
  resolved.animation
2805
2568
  );
@@ -2822,6 +2585,11 @@ var BottomSheetEngine = class {
2822
2585
  if (opts.linkedSheets) {
2823
2586
  this.linkedSheets = opts.linkedSheets;
2824
2587
  }
2588
+ const byName = /* @__PURE__ */ new Map();
2589
+ for (const feature of opts.features ?? []) {
2590
+ byName.set(feature.name, feature);
2591
+ }
2592
+ this.featureList = Array.from(byName.values());
2825
2593
  this.activeId = resolved.initialId;
2826
2594
  auditVhUsage(this.snapPointsRaw);
2827
2595
  this.auditDuplicateSnapIds(this.snapPointsRaw);
@@ -2858,6 +2626,7 @@ var BottomSheetEngine = class {
2858
2626
  scrollContainer: this.scrollContainer,
2859
2627
  getMaxAxisSize: () => this.snaps.getMaxAxisSize()
2860
2628
  });
2629
+ this.featureCtx = this.buildFeatureContext(opts.autoCollapseAfter);
2861
2630
  this.attach();
2862
2631
  this.registerInStack();
2863
2632
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
@@ -2866,12 +2635,7 @@ var BottomSheetEngine = class {
2866
2635
  if (!entry.isIntersecting) continue;
2867
2636
  if (this.destroyed) return;
2868
2637
  this.snaps.recompute();
2869
- const current = this.snaps.findById(this.activeId);
2870
- if (current) {
2871
- this.size = current.size;
2872
- this.applySize(this.size);
2873
- this.updateAriaSlider();
2874
- }
2638
+ this.healActiveSnap();
2875
2639
  io.disconnect();
2876
2640
  return;
2877
2641
  }
@@ -2894,46 +2658,77 @@ var BottomSheetEngine = class {
2894
2658
  if (resolved.radius !== void 0) this.setRadius(resolved.radius);
2895
2659
  if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
2896
2660
  this.applyAutoAriaLabelledBy();
2897
- if (this.closeOnRouteChange) {
2898
- this.teardowns.add(
2899
- installRouteChange({
2900
- isDestroyed: () => this.destroyed,
2901
- getSize: () => this.size,
2902
- close: () => this.close()
2903
- })
2904
- );
2905
- }
2906
- if (this.persistKey) {
2907
- this.teardowns.add(installPersist(this, this.persistKey));
2661
+ this.runFeatureStage("post");
2662
+ this.warnOrphanFeatureOptions();
2663
+ if (this.size > 0) {
2664
+ this.handleOpen();
2908
2665
  }
2909
- this.teardowns.add(
2910
- installAutoCollapse({
2911
- ms: opts.autoCollapseAfter,
2912
- isDestroyed: () => this.destroyed,
2913
- isDragging: () => this.gesture?.isDragging ?? false,
2914
- getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2915
- getActiveId: () => this.activeId,
2916
- resolveSnap: (id) => this.snaps.findById(id),
2917
- snapTo: (id) => {
2918
- void this.snapTo(id);
2919
- },
2920
- on: (event, fn) => this.on(event, fn)
2921
- })
2922
- );
2923
- this.teardowns.add(
2924
- installRoute({
2666
+ }
2667
+ buildFeatureContext(autoCollapseAfter) {
2668
+ return {
2669
+ element: this.element,
2670
+ scrollContainer: this.scrollContainer,
2671
+ sheetId: this.id,
2672
+ options: {
2925
2673
  routedTo: this.routedTo,
2926
2674
  closeOnBack: this.closeOnBack,
2927
- isTopSheet: () => this.isTopSheet,
2928
- getSize: () => this.size,
2929
- isDestroyed: () => this.destroyed,
2930
- close: () => this.close("back"),
2931
- on: (event, fn) => this.on(event, fn),
2932
- sheetId: this.id
2933
- })
2934
- );
2935
- if (this.size > 0) {
2936
- this.handleOpen();
2675
+ closeOnRouteChange: this.closeOnRouteChange,
2676
+ persistKey: this.persistKey,
2677
+ autoCollapseAfter
2678
+ },
2679
+ isDestroyed: () => this.destroyed,
2680
+ isDragging: () => this.gesture?.isDragging ?? false,
2681
+ isAnimating: () => this.animation.isAnimating,
2682
+ isTopSheet: () => this.isTopSheet,
2683
+ isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
2684
+ getSize: () => this.size,
2685
+ getActiveId: () => this.activeId,
2686
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
2687
+ allowedIdsBySize: () => this.allowedIdsBySize(),
2688
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
2689
+ resolveSnap: (id) => this.snaps.findById(id),
2690
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
2691
+ setSize: (size) => {
2692
+ this.size = size;
2693
+ },
2694
+ setMaxAxisSize: (size) => {
2695
+ this.snaps.setMaxAxisSize(size);
2696
+ },
2697
+ applySize: (size) => this.applySize(size),
2698
+ recomputeSnaps: () => this.recomputeSnaps(),
2699
+ newCycle: () => {
2700
+ this.newCycle();
2701
+ },
2702
+ cancelInFlight: () => {
2703
+ this.animation.cancel();
2704
+ },
2705
+ resyncAfterResize: () => this.resyncAfterResize(),
2706
+ snapTo: (id) => {
2707
+ void this.snapTo(id);
2708
+ },
2709
+ close: (reason) => this.close(reason),
2710
+ on: (event, fn) => this.on(event, fn),
2711
+ addTeardown: (fn) => this.teardowns.add(fn)
2712
+ };
2713
+ }
2714
+ runFeatureStage(stage) {
2715
+ for (const feature of this.featureList) {
2716
+ if ((feature.stage ?? "post") !== stage) continue;
2717
+ const teardown = feature.install(this.featureCtx);
2718
+ if (teardown) this.teardowns.add(teardown);
2719
+ }
2720
+ }
2721
+ warnOrphanFeatureOptions() {
2722
+ const names = new Set(this.featureList.map((f) => f.name));
2723
+ if ((this.closeOnBack || this.routedTo !== void 0 || this.closeOnRouteChange) && !names.has("route")) {
2724
+ devWarn(
2725
+ "[BottomSheet] closeOnBack/routedTo/closeOnRouteChange need routeFeature() \u2014 option ignored"
2726
+ );
2727
+ }
2728
+ if (this.persistKey && !names.has("persist")) {
2729
+ devWarn(
2730
+ "[BottomSheet] persistKey needs persistFeature() \u2014 snaps will not be saved"
2731
+ );
2937
2732
  }
2938
2733
  }
2939
2734
  get state() {
@@ -3035,6 +2830,8 @@ var BottomSheetEngine = class {
3035
2830
  const vt = document.startViewTransition(() => {
3036
2831
  this.applySize(target.size);
3037
2832
  });
2833
+ vt.ready?.catch(() => {
2834
+ });
3038
2835
  this.currentViewTransition = vt;
3039
2836
  try {
3040
2837
  await vt.finished;
@@ -3286,11 +3083,16 @@ var BottomSheetEngine = class {
3286
3083
  this.maxHeight.clampTo();
3287
3084
  this.scrim.invalidateOpacityCache();
3288
3085
  if (this.gesture?.isDragging) return;
3086
+ this.healActiveSnap();
3087
+ }
3088
+ healActiveSnap() {
3289
3089
  const current = this.snaps.findById(this.activeId);
3290
- if (current) {
3291
- this.size = current.size;
3292
- this.applySize(this.size);
3293
- this.updateAriaSlider();
3090
+ if (!current) return;
3091
+ this.size = current.size;
3092
+ this.applySize(this.size);
3093
+ this.updateAriaSlider();
3094
+ if (this.size > 0 && !this.lifecycle.isInstalled && !this.opening && !this.animation.isAnimating) {
3095
+ this.emitOpenSequence(this.activeId);
3294
3096
  }
3295
3097
  }
3296
3098
  destroy() {
@@ -3469,18 +3271,7 @@ var BottomSheetEngine = class {
3469
3271
  }
3470
3272
  }
3471
3273
  attachFeatures() {
3472
- if (this.scrollContainer) {
3473
- this.teardowns.add(installContentSwipe({
3474
- container: this.scrollContainer,
3475
- isDragging: () => this.gesture?.isDragging ?? false,
3476
- isAnimating: () => this.animation.isAnimating,
3477
- getAllowedIds: () => this.allowedIdsBySize(),
3478
- getActiveId: () => this.activeId,
3479
- snapTo: (id) => {
3480
- void this.snapTo(id);
3481
- }
3482
- }));
3483
- }
3274
+ this.runFeatureStage("attach");
3484
3275
  if (typeof window !== "undefined") {
3485
3276
  this.teardowns.add(installResizeObserver({
3486
3277
  element: this.element,
@@ -3520,31 +3311,6 @@ var BottomSheetEngine = class {
3520
3311
  () => document.removeEventListener("keydown", onKey)
3521
3312
  );
3522
3313
  }
3523
- this.teardowns.add(installVisualViewport({
3524
- element: this.element,
3525
- isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
3526
- isDestroyed: () => this.destroyed,
3527
- isDragging: () => this.gesture?.isDragging ?? false,
3528
- recomputeSnaps: () => this.recomputeSnaps(),
3529
- resolveActiveSnap: () => this.snaps.findById(this.activeId),
3530
- getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3531
- getSize: () => this.size,
3532
- setMaxAxisSize: (size) => {
3533
- this.snaps.setMaxAxisSize(size);
3534
- },
3535
- setSize: (size) => {
3536
- this.size = size;
3537
- },
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()
3547
- }));
3548
3314
  }
3549
3315
  this.teardowns.add(installSliderKeyboard({
3550
3316
  handle: this.handle,
@@ -3585,123 +3351,631 @@ var BottomSheetEngine = class {
3585
3351
  }
3586
3352
  return;
3587
3353
  }
3588
- const previousSize = this.size;
3589
- const previousSnapSize = this.snaps.findById(previousId)?.size;
3590
- this.scrollCache.cache(previousId, previousSize, target.size);
3591
- this.activeId = target.id;
3592
- if (previousSnapSize === 0 && target.size > 0) {
3593
- this.opening = true;
3594
- sheetStack.promote(this.id);
3354
+ const previousSize = this.size;
3355
+ const previousSnapSize = this.snaps.findById(previousId)?.size;
3356
+ this.scrollCache.cache(previousId, previousSize, target.size);
3357
+ this.activeId = target.id;
3358
+ if (previousSnapSize === 0 && target.size > 0) {
3359
+ this.opening = true;
3360
+ sheetStack.promote(this.id);
3361
+ }
3362
+ const settleCap = this.snaps.getMaxAxisSize();
3363
+ const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3364
+ this.allowOvershoot = true;
3365
+ void this.animation.animateTo(settleSize, velocity).then(() => {
3366
+ if (signal.aborted) return;
3367
+ this.allowOvershoot = false;
3368
+ this.opening = false;
3369
+ this.completeSnap(
3370
+ target.id,
3371
+ target.size,
3372
+ previousSize,
3373
+ previousSnapSize === 0,
3374
+ true
3375
+ );
3376
+ });
3377
+ }
3378
+ clearWillChangeIfIdle() {
3379
+ if (this.animation.isAnimating || this.gesture?.isDragging) return;
3380
+ this.element.style.willChange = "";
3381
+ }
3382
+ haptic() {
3383
+ if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3384
+ try {
3385
+ navigator.vibrate(HAPTIC_DURATION_MS);
3386
+ } catch {
3387
+ }
3388
+ }
3389
+ }
3390
+ applySize(size, skipTransform = false) {
3391
+ const cap = this.snaps.getMaxAxisSize();
3392
+ const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3393
+ this.size = clamped;
3394
+ const offset = cap - clamped;
3395
+ const style = this.element.style;
3396
+ if (!skipTransform) style.transform = this.transformTemplate(offset);
3397
+ if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3398
+ style.setProperty("--bs-size", `${clamped}px`);
3399
+ if (this.scrimParent) {
3400
+ this.scrimParent.style.setProperty("--bs-size", `${clamped}px`);
3401
+ }
3402
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3403
+ this.anchorHost.style.setProperty("--bs-size", `${clamped}px`);
3404
+ }
3405
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3406
+ this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3407
+ }
3408
+ }
3409
+ const progress = this.computeProgress(clamped);
3410
+ const progressChanged = this.progressWriteSentinel.shouldWrite(
3411
+ progress,
3412
+ OPACITY_WRITE_EPSILON
3413
+ );
3414
+ if (progressChanged) {
3415
+ style.setProperty("--bs-progress", String(progress));
3416
+ if (this.anchorHost) {
3417
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3418
+ }
3419
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3420
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3421
+ }
3422
+ }
3423
+ this.scrim.applyOpacity(progress, progressChanged);
3424
+ if (progressChanged && this.bus.listenerCount("progress") > 0) {
3425
+ this.progressPayload.value = progress;
3426
+ this.progressPayload.size = clamped;
3427
+ this.emit("progress", this.progressPayload);
3428
+ }
3429
+ }
3430
+ computeProgress(size) {
3431
+ const { min, max } = this.getAllowedRange();
3432
+ const reachableMax = Math.min(max, this.snaps.getMaxAxisSize());
3433
+ if (reachableMax <= min) return 0;
3434
+ return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3435
+ }
3436
+ handleOpen() {
3437
+ sheetStack.promote(this.id);
3438
+ try {
3439
+ this.lifecycle.install();
3440
+ } catch (err) {
3441
+ if (!this.destroyed) this.newCycle();
3442
+ this.handleClose();
3443
+ this.applySize(0);
3444
+ this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3445
+ this.size = 0;
3446
+ queueMicrotask(() => {
3447
+ throw err;
3448
+ });
3449
+ }
3450
+ }
3451
+ resyncAfterResize() {
3452
+ if (this.destroyed) return;
3453
+ const target = this.snaps.findById(this.activeId);
3454
+ if (!target) return;
3455
+ this.updateAriaSlider();
3456
+ this.emit("snap", {
3457
+ id: target.id,
3458
+ size: this.size,
3459
+ progress: this.computeProgress(this.size)
3460
+ });
3461
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3462
+ this.emitOpenSequence(target.id);
3463
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3464
+ this.emitCloseSequence();
3465
+ }
3466
+ }
3467
+ handleClose() {
3468
+ this.opening = false;
3469
+ this.lifecycle.release();
3470
+ sheetStack.update();
3471
+ }
3472
+ };
3473
+
3474
+ // src/core/features/history-coordinator.ts
3475
+ var entries = [];
3476
+ var subscribers = /* @__PURE__ */ new Set();
3477
+ var suppress = 0;
3478
+ var internalDepth = 0;
3479
+ var pendingRebrands = [];
3480
+ var listenerInstalled = false;
3481
+ var patchInstalled = false;
3482
+ var origPushState = null;
3483
+ var origReplaceState = null;
3484
+ var patchedPushState = null;
3485
+ var patchedReplaceState = null;
3486
+ var hasWindow = () => typeof window !== "undefined";
3487
+ var hasHistory = () => typeof history !== "undefined";
3488
+ var brandMarker = (surface) => ({
3489
+ ...surface.markerState(),
3490
+ __bs: true
3491
+ });
3492
+ var runInternal = (fn) => {
3493
+ internalDepth += 1;
3494
+ try {
3495
+ fn();
3496
+ } finally {
3497
+ internalDepth -= 1;
3498
+ }
3499
+ };
3500
+ var notifyRouteChange = () => {
3501
+ const snapshot = Array.from(subscribers);
3502
+ for (const fn of snapshot) fn();
3503
+ };
3504
+ var ensureListener = () => {
3505
+ if (listenerInstalled || !hasWindow()) return;
3506
+ window.addEventListener("popstate", onPopState);
3507
+ listenerInstalled = true;
3508
+ };
3509
+ var maybeTeardownListener = () => {
3510
+ if (!listenerInstalled) return;
3511
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
3512
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
3513
+ listenerInstalled = false;
3514
+ };
3515
+ var installPatch = () => {
3516
+ if (patchInstalled || !hasHistory()) return;
3517
+ origPushState = history.pushState;
3518
+ origReplaceState = history.replaceState;
3519
+ patchedPushState = function(...args) {
3520
+ origPushState.apply(this, args);
3521
+ if (internalDepth === 0) notifyRouteChange();
3522
+ };
3523
+ patchedReplaceState = function(...args) {
3524
+ origReplaceState.apply(this, args);
3525
+ if (internalDepth === 0) notifyRouteChange();
3526
+ };
3527
+ history.pushState = patchedPushState;
3528
+ history.replaceState = patchedReplaceState;
3529
+ patchInstalled = true;
3530
+ };
3531
+ var removePatch = () => {
3532
+ if (!patchInstalled) return;
3533
+ if (history.pushState === patchedPushState && origPushState) {
3534
+ history.pushState = origPushState;
3535
+ }
3536
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
3537
+ history.replaceState = origReplaceState;
3538
+ }
3539
+ origPushState = null;
3540
+ origReplaceState = null;
3541
+ patchedPushState = null;
3542
+ patchedReplaceState = null;
3543
+ patchInstalled = false;
3544
+ };
3545
+ function onPopState() {
3546
+ if (suppress > 0) {
3547
+ suppress -= 1;
3548
+ const rebrand = pendingRebrands.shift();
3549
+ if (rebrand) rebrand();
3550
+ maybeTeardownListener();
3551
+ return;
3552
+ }
3553
+ if (entries.length === 0) {
3554
+ notifyRouteChange();
3555
+ maybeTeardownListener();
3556
+ return;
3557
+ }
3558
+ let target = null;
3559
+ let targetIdx = -1;
3560
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
3561
+ const entry = entries[i];
3562
+ if (!entry.surface.isOpen()) continue;
3563
+ if (!target) {
3564
+ target = entry;
3565
+ targetIdx = i;
3566
+ }
3567
+ if (entry.surface.isTop()) {
3568
+ target = entry;
3569
+ targetIdx = i;
3570
+ break;
3571
+ }
3572
+ }
3573
+ if (!target) {
3574
+ const removed = entries.pop();
3575
+ if (removed) removed.live = false;
3576
+ maybeTeardownListener();
3577
+ return;
3578
+ }
3579
+ target.live = false;
3580
+ entries.splice(targetIdx, 1);
3581
+ const settled = target;
3582
+ void Promise.resolve(settled.surface.close()).then(() => {
3583
+ if (settled.surface.isOpen()) restore(settled);
3584
+ });
3585
+ maybeTeardownListener();
3586
+ }
3587
+ function restore(entry) {
3588
+ if (!hasWindow() || !hasHistory()) return;
3589
+ entry.live = true;
3590
+ runInternal(() => {
3591
+ try {
3592
+ history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3593
+ } catch {
3594
+ entry.live = false;
3595
+ }
3596
+ });
3597
+ if (entry.live) {
3598
+ entries.push(entry);
3599
+ ensureListener();
3600
+ }
3601
+ }
3602
+ function pushBackMarker(surface) {
3603
+ const entry = { surface, priorUrl: void 0, live: false };
3604
+ if (!hasWindow() || !hasHistory()) {
3605
+ return entry;
3606
+ }
3607
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
3608
+ runInternal(() => {
3609
+ try {
3610
+ history.pushState(brandMarker(surface), "", surface.url);
3611
+ entry.live = true;
3612
+ } catch {
3613
+ entry.live = false;
3614
+ }
3615
+ });
3616
+ if (entry.live) {
3617
+ entries.push(entry);
3618
+ ensureListener();
3619
+ }
3620
+ return entry;
3621
+ }
3622
+ function popBackMarker(handle) {
3623
+ if (!hasWindow() || !hasHistory()) return;
3624
+ const entry = handle;
3625
+ const idx = entries.indexOf(entry);
3626
+ if (idx === -1 || !entry.live) return;
3627
+ const last = entries.length - 1;
3628
+ entry.live = false;
3629
+ entries.splice(idx, 1);
3630
+ let rebrand = null;
3631
+ if (idx !== last) {
3632
+ const rebrandSurface = entries[entries.length - 1].surface;
3633
+ const rebrandUrl = entry.priorUrl;
3634
+ rebrand = () => {
3635
+ runInternal(() => {
3636
+ history.replaceState(
3637
+ brandMarker(rebrandSurface),
3638
+ "",
3639
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
3640
+ );
3641
+ });
3642
+ };
3643
+ }
3644
+ pendingRebrands.push(rebrand);
3645
+ suppress += 1;
3646
+ runInternal(() => {
3647
+ try {
3648
+ history.back();
3649
+ } catch {
3650
+ suppress = Math.max(0, suppress - 1);
3651
+ pendingRebrands.pop();
3652
+ }
3653
+ });
3654
+ maybeTeardownListener();
3655
+ }
3656
+ function subscribeRouteChange(fn) {
3657
+ if (!hasWindow() || !hasHistory()) return () => {
3658
+ };
3659
+ subscribers.add(fn);
3660
+ if (subscribers.size === 1) installPatch();
3661
+ ensureListener();
3662
+ return () => {
3663
+ if (!subscribers.has(fn)) return;
3664
+ subscribers.delete(fn);
3665
+ if (subscribers.size === 0) removePatch();
3666
+ maybeTeardownListener();
3667
+ };
3668
+ }
3669
+
3670
+ // src/core/features/route.ts
3671
+ function installRoute(deps) {
3672
+ if (typeof window === "undefined") {
3673
+ return () => {
3674
+ };
3675
+ }
3676
+ if (!deps.routedTo && !deps.closeOnBack) {
3677
+ return () => {
3678
+ };
3679
+ }
3680
+ let handle = null;
3681
+ const onOpen = () => {
3682
+ if (handle) return;
3683
+ handle = pushBackMarker({
3684
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
3685
+ isTop: deps.isTopSheet,
3686
+ close: deps.close,
3687
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
3688
+ url: deps.routedTo
3689
+ });
3690
+ };
3691
+ const onClose = () => {
3692
+ if (!handle) return;
3693
+ popBackMarker(handle);
3694
+ handle = null;
3695
+ };
3696
+ const unsubscribeOpen = deps.on("open", onOpen);
3697
+ const unsubscribeClose = deps.on("close", onClose);
3698
+ if (deps.getSize() > 0) onOpen();
3699
+ return () => {
3700
+ unsubscribeOpen();
3701
+ unsubscribeClose();
3702
+ if (handle) {
3703
+ popBackMarker(handle);
3704
+ handle = null;
3705
+ }
3706
+ };
3707
+ }
3708
+ function installRouteChange(deps) {
3709
+ return subscribeRouteChange(() => {
3710
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
3711
+ void deps.close();
3712
+ });
3713
+ }
3714
+
3715
+ // src/core/features/persist.ts
3716
+ function installPersist(engine, key) {
3717
+ const unsubscribe = engine.on("snap", ({ id }) => {
3718
+ if (typeof window === "undefined") return;
3719
+ if (typeof localStorage === "undefined") return;
3720
+ try {
3721
+ localStorage.setItem(key, id);
3722
+ } catch {
3723
+ }
3724
+ });
3725
+ return unsubscribe;
3726
+ }
3727
+
3728
+ // src/core/features/auto-collapse.ts
3729
+ function installAutoCollapse(deps) {
3730
+ const { ms } = deps;
3731
+ if (ms === void 0 || ms <= 0) {
3732
+ return () => {
3733
+ };
3734
+ }
3735
+ let timer = null;
3736
+ const clearTimer = () => {
3737
+ if (timer !== null) {
3738
+ clearTimeout(timer);
3739
+ timer = null;
3740
+ }
3741
+ };
3742
+ const fire = () => {
3743
+ if (deps.isDestroyed()) return;
3744
+ if (deps.isDragging()) return;
3745
+ const active = deps.resolveSnap(deps.getActiveId());
3746
+ if (!active || active.size === 0) return;
3747
+ let target = null;
3748
+ for (const id of deps.getAllowedIds()) {
3749
+ const snap = deps.resolveSnap(id);
3750
+ if (!snap || snap.size <= 0) continue;
3751
+ if (!target || snap.size < target.size) target = snap;
3595
3752
  }
3596
- const settleCap = this.snaps.getMaxAxisSize();
3597
- const settleSize = settleCap > 0 ? Math.min(target.size, settleCap) : target.size;
3598
- this.allowOvershoot = true;
3599
- void this.animation.animateTo(settleSize, velocity).then(() => {
3600
- if (signal.aborted) return;
3601
- this.allowOvershoot = false;
3602
- this.opening = false;
3603
- this.completeSnap(
3604
- target.id,
3605
- target.size,
3606
- previousSize,
3607
- previousSnapSize === 0,
3608
- true
3609
- );
3610
- });
3611
- }
3612
- clearWillChangeIfIdle() {
3613
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3614
- this.element.style.willChange = "";
3615
- }
3616
- haptic() {
3617
- if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3618
- try {
3619
- navigator.vibrate(HAPTIC_DURATION_MS);
3620
- } catch {
3621
- }
3753
+ if (!target) return;
3754
+ if (target.id === active.id || target.size >= active.size) return;
3755
+ deps.snapTo(target.id);
3756
+ };
3757
+ const reset = () => {
3758
+ clearTimer();
3759
+ if (deps.isDestroyed()) return;
3760
+ timer = setTimeout(() => {
3761
+ timer = null;
3762
+ fire();
3763
+ }, ms);
3764
+ };
3765
+ const unsubscribe = deps.on("snap", () => reset());
3766
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
3767
+ return () => {
3768
+ clearTimer();
3769
+ unsubscribe();
3770
+ };
3771
+ }
3772
+
3773
+ // src/core/features/content-swipe.ts
3774
+ var CONTENT_PULL_THRESHOLD = 60;
3775
+ function installContentSwipe(deps) {
3776
+ const { container } = deps;
3777
+ let startY = 0;
3778
+ let startScrollTop = 0;
3779
+ let moved = false;
3780
+ const onTouchStart = (e) => {
3781
+ const t = e.touches[0];
3782
+ if (!t) return;
3783
+ startY = t.clientY;
3784
+ startScrollTop = container.scrollTop;
3785
+ moved = false;
3786
+ };
3787
+ const onTouchMove = (e) => {
3788
+ const t = e.touches[0];
3789
+ if (!t) return;
3790
+ if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3791
+ };
3792
+ const onTouchEnd = (e) => {
3793
+ if (!moved) return;
3794
+ if (deps.isDragging() || deps.isAnimating()) return;
3795
+ if (container.scrollTop !== startScrollTop) return;
3796
+ const t = e.changedTouches[0];
3797
+ if (!t) return;
3798
+ const deltaY = t.clientY - startY;
3799
+ const atTop = container.scrollTop <= 0;
3800
+ const allowed = deps.getAllowedIds();
3801
+ const idx = allowed.indexOf(deps.getActiveId());
3802
+ if (idx === -1) return;
3803
+ if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3804
+ deps.snapTo(allowed[idx - 1]);
3805
+ } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3806
+ deps.snapTo(allowed[idx + 1]);
3622
3807
  }
3808
+ };
3809
+ container.addEventListener("touchstart", onTouchStart, { passive: true });
3810
+ container.addEventListener("touchmove", onTouchMove, { passive: true });
3811
+ container.addEventListener("touchend", onTouchEnd, { passive: true });
3812
+ return () => {
3813
+ container.removeEventListener("touchstart", onTouchStart);
3814
+ container.removeEventListener("touchmove", onTouchMove);
3815
+ container.removeEventListener("touchend", onTouchEnd);
3816
+ };
3817
+ }
3818
+
3819
+ // src/core/features/visual-viewport.ts
3820
+ var SAFETY_PAD = 8;
3821
+ function installVisualViewport(deps) {
3822
+ if (typeof window === "undefined" || !window.visualViewport) {
3823
+ return () => {
3824
+ };
3623
3825
  }
3624
- applySize(size) {
3625
- const cap = this.snaps.getMaxAxisSize();
3626
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3627
- this.size = clamped;
3628
- const offset = cap - clamped;
3629
- const style = this.element.style;
3630
- style.transform = this.transformTemplate(offset);
3631
- if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3632
- style.setProperty("--bs-size", `${clamped}px`);
3633
- if (this.scrimParent) {
3634
- this.scrimParent.style.setProperty("--bs-size", `${clamped}px`);
3826
+ const vv = window.visualViewport;
3827
+ let rafId = null;
3828
+ const onResize = () => {
3829
+ if (rafId !== null) return;
3830
+ rafId = requestAnimationFrame(() => {
3831
+ rafId = null;
3832
+ if (deps.isDestroyed()) return;
3833
+ deps.recomputeSnaps();
3834
+ const isVertical = deps.isVerticalAxis();
3835
+ const visible = isVertical ? vv.height : vv.width;
3836
+ const windowSize = isVertical ? window.innerHeight : window.innerWidth;
3837
+ const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
3838
+ if (clamp < deps.getMaxAxisSize()) {
3839
+ deps.setMaxAxisSize(clamp);
3635
3840
  }
3636
- if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3637
- this.anchorHost.style.setProperty("--bs-size", `${clamped}px`);
3841
+ const wasAnimating = deps.isAnimating?.() ?? false;
3842
+ deps.cancelInFlight();
3843
+ deps.newCycle();
3844
+ const max = deps.getMaxAxisSize();
3845
+ deps.setSize(Math.min(deps.getSize(), max));
3846
+ const current = deps.resolveActiveSnap();
3847
+ if (current && !deps.isDragging()) {
3848
+ deps.applySize(Math.min(current.size, max));
3849
+ if (wasAnimating) deps.resyncAfterCancel?.();
3850
+ } else {
3851
+ deps.applySize(deps.getSize());
3638
3852
  }
3639
- if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3640
- this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3853
+ });
3854
+ };
3855
+ vv.addEventListener("resize", onResize);
3856
+ return () => {
3857
+ vv.removeEventListener("resize", onResize);
3858
+ if (rafId !== null) cancelAnimationFrame(rafId);
3859
+ };
3860
+ }
3861
+
3862
+ // src/core/features/engine-features.ts
3863
+ function routeFeature() {
3864
+ return {
3865
+ name: "route",
3866
+ install: (ctx) => {
3867
+ const teardowns = new TeardownStack();
3868
+ if (ctx.options.closeOnRouteChange) {
3869
+ teardowns.add(
3870
+ installRouteChange({
3871
+ isDestroyed: ctx.isDestroyed,
3872
+ getSize: ctx.getSize,
3873
+ close: () => ctx.close()
3874
+ })
3875
+ );
3641
3876
  }
3877
+ teardowns.add(
3878
+ installRoute({
3879
+ routedTo: ctx.options.routedTo,
3880
+ closeOnBack: ctx.options.closeOnBack,
3881
+ isTopSheet: ctx.isTopSheet,
3882
+ getSize: ctx.getSize,
3883
+ isDestroyed: ctx.isDestroyed,
3884
+ close: () => ctx.close("back"),
3885
+ on: ctx.on,
3886
+ sheetId: ctx.sheetId
3887
+ })
3888
+ );
3889
+ return () => teardowns.drain();
3642
3890
  }
3643
- const progress = this.computeProgress(clamped);
3644
- const progressChanged = this.progressWriteSentinel.shouldWrite(
3645
- progress,
3646
- OPACITY_WRITE_EPSILON
3647
- );
3648
- if (progressChanged) {
3649
- style.setProperty("--bs-progress", String(progress));
3650
- if (this.anchorHost) {
3651
- this.anchorHost.style.setProperty("--bs-progress", String(progress));
3652
- }
3653
- if (this.rootEl && this.rootEl !== this.anchorHost) {
3654
- this.rootEl.style.setProperty("--bs-progress", String(progress));
3655
- }
3891
+ };
3892
+ }
3893
+ function persistFeature() {
3894
+ return {
3895
+ name: "persist",
3896
+ install: (ctx) => {
3897
+ const key = ctx.options.persistKey;
3898
+ if (!key) return;
3899
+ return installPersist({ on: ctx.on }, key);
3656
3900
  }
3657
- this.scrim.applyOpacity(progress, progressChanged);
3658
- if (progressChanged && this.bus.listenerCount("progress") > 0) {
3659
- this.progressPayload.value = progress;
3660
- this.progressPayload.size = clamped;
3661
- this.emit("progress", this.progressPayload);
3901
+ };
3902
+ }
3903
+ function autoCollapseFeature() {
3904
+ return {
3905
+ name: "auto-collapse",
3906
+ install: (ctx) => installAutoCollapse({
3907
+ ms: ctx.options.autoCollapseAfter,
3908
+ isDestroyed: ctx.isDestroyed,
3909
+ isDragging: ctx.isDragging,
3910
+ getAllowedIds: ctx.getAllowedIds,
3911
+ getActiveId: ctx.getActiveId,
3912
+ resolveSnap: ctx.resolveSnap,
3913
+ snapTo: ctx.snapTo,
3914
+ on: ctx.on
3915
+ })
3916
+ };
3917
+ }
3918
+ function contentSwipeFeature() {
3919
+ return {
3920
+ name: "content-swipe",
3921
+ stage: "attach",
3922
+ install: (ctx) => {
3923
+ if (!ctx.scrollContainer) return;
3924
+ return installContentSwipe({
3925
+ container: ctx.scrollContainer,
3926
+ isDragging: ctx.isDragging,
3927
+ isAnimating: ctx.isAnimating,
3928
+ getAllowedIds: ctx.allowedIdsBySize,
3929
+ getActiveId: ctx.getActiveId,
3930
+ snapTo: ctx.snapTo
3931
+ });
3662
3932
  }
3663
- }
3664
- computeProgress(size) {
3665
- const { min, max } = this.getAllowedRange();
3666
- const reachableMax = Math.min(max, this.snaps.getMaxAxisSize());
3667
- if (reachableMax <= min) return 0;
3668
- return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3669
- }
3670
- handleOpen() {
3671
- sheetStack.promote(this.id);
3672
- try {
3673
- this.lifecycle.install();
3674
- } catch (err) {
3675
- if (!this.destroyed) this.newCycle();
3676
- this.handleClose();
3677
- this.applySize(0);
3678
- this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3679
- this.size = 0;
3680
- queueMicrotask(() => {
3681
- throw err;
3933
+ };
3934
+ }
3935
+ function visualViewportFeature() {
3936
+ return {
3937
+ name: "visual-viewport",
3938
+ install: (ctx) => {
3939
+ if (typeof window === "undefined") return;
3940
+ return installVisualViewport({
3941
+ element: ctx.element,
3942
+ isVerticalAxis: ctx.isVerticalAxis,
3943
+ isDestroyed: ctx.isDestroyed,
3944
+ isDragging: ctx.isDragging,
3945
+ recomputeSnaps: ctx.recomputeSnaps,
3946
+ resolveActiveSnap: ctx.resolveActiveSnap,
3947
+ getMaxAxisSize: ctx.getMaxAxisSize,
3948
+ getSize: ctx.getSize,
3949
+ setMaxAxisSize: ctx.setMaxAxisSize,
3950
+ setSize: ctx.setSize,
3951
+ applySize: ctx.applySize,
3952
+ cancelInFlight: ctx.cancelInFlight,
3953
+ newCycle: ctx.newCycle,
3954
+ isAnimating: ctx.isAnimating,
3955
+ resyncAfterCancel: ctx.resyncAfterResize
3682
3956
  });
3683
3957
  }
3684
- }
3685
- resyncAfterResize() {
3686
- if (this.destroyed) return;
3687
- const target = this.snaps.findById(this.activeId);
3688
- if (!target) return;
3689
- this.updateAriaSlider();
3690
- this.emit("snap", {
3691
- id: target.id,
3692
- size: this.size,
3693
- progress: this.computeProgress(this.size)
3958
+ };
3959
+ }
3960
+
3961
+ // src/core/default-features.ts
3962
+ function defaultEngineFeatures() {
3963
+ return [
3964
+ contentSwipeFeature(),
3965
+ visualViewportFeature(),
3966
+ persistFeature(),
3967
+ autoCollapseFeature(),
3968
+ routeFeature()
3969
+ ];
3970
+ }
3971
+
3972
+ // src/core/BottomSheetEngine.ts
3973
+ var BottomSheetEngine = class extends BottomSheetCore {
3974
+ constructor(opts) {
3975
+ super({
3976
+ ...opts,
3977
+ features: [...defaultEngineFeatures(), ...opts.features ?? []]
3694
3978
  });
3695
- if (target.size > 0 && !this.lifecycle.isInstalled) {
3696
- this.emitOpenSequence(target.id);
3697
- } else if (target.size === 0 && this.lifecycle.isInstalled) {
3698
- this.emitCloseSequence();
3699
- }
3700
- }
3701
- handleClose() {
3702
- this.opening = false;
3703
- this.lifecycle.release();
3704
- sheetStack.update();
3705
3979
  }
3706
3980
  };
3707
3981
 
@@ -3940,6 +4214,10 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
3940
4214
  type: String,
3941
4215
  required: false
3942
4216
  },
4217
+ settleAnimation: {
4218
+ type: String,
4219
+ required: false
4220
+ },
3943
4221
  spring: {
3944
4222
  type: Object,
3945
4223
  required: false
@@ -4022,6 +4300,10 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
4022
4300
  type: Boolean,
4023
4301
  required: false
4024
4302
  },
4303
+ features: {
4304
+ type: Array,
4305
+ required: false
4306
+ },
4025
4307
  snapPoints: {
4026
4308
  type: Array,
4027
4309
  required: true