@surdeddd/bottom-sheet 0.12.0 → 0.14.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 (66) hide show
  1. package/README.md +85 -1
  2. package/dist/{BottomSheetEngine-D6oiEfZ4.d.cts → BottomSheetCore-Cp7dWy91.d.cts} +85 -5
  3. package/dist/{BottomSheetEngine-Dhy0X_e9.d.ts → BottomSheetCore-DJYwWOqt.d.ts} +85 -5
  4. package/dist/BottomSheetEngine-Lm7HIVHv.d.cts +7 -0
  5. package/dist/BottomSheetEngine-O5PbeKfe.d.ts +7 -0
  6. package/dist/core.cjs +3680 -0
  7. package/dist/core.d.cts +3 -0
  8. package/dist/core.d.ts +3 -0
  9. package/dist/core.js +3678 -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 +1314 -829
  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 +1314 -829
  19. package/dist/features.cjs +489 -0
  20. package/dist/features.d.cts +13 -0
  21. package/dist/features.d.ts +13 -0
  22. package/dist/features.js +482 -0
  23. package/dist/index-Bb5TRmyg.d.cts +9 -0
  24. package/dist/index-Cl9ebCP2.d.ts +9 -0
  25. package/dist/index.cjs +979 -1293
  26. package/dist/index.d.cts +43 -55
  27. package/dist/index.d.ts +43 -55
  28. package/dist/index.js +973 -1280
  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 +11 -10
  34. package/dist/overlay.js +11 -10
  35. package/dist/preact.d.cts +2 -1
  36. package/dist/preact.d.ts +2 -1
  37. package/dist/qwik-lib/index.qwik.cjs +4418 -0
  38. package/dist/qwik-lib/index.qwik.mjs +4418 -0
  39. package/dist/qwik.cjs +1089 -657
  40. package/dist/qwik.d.cts +4 -2
  41. package/dist/qwik.d.ts +4 -2
  42. package/dist/qwik.js +1089 -657
  43. package/dist/react.cjs +1100 -665
  44. package/dist/react.d.cts +3 -3
  45. package/dist/react.d.ts +3 -3
  46. package/dist/react.js +1100 -665
  47. package/dist/solid.cjs +1095 -657
  48. package/dist/solid.d.cts +90 -4
  49. package/dist/solid.d.ts +90 -4
  50. package/dist/solid.js +1095 -657
  51. package/dist/solid.source.jsx +1103 -657
  52. package/dist/styles.css +24 -4
  53. package/dist/svelte-core.cjs +981 -557
  54. package/dist/svelte-core.js +981 -557
  55. package/dist/svelte-src/BottomSheet.svelte +14 -0
  56. package/dist/svelte.cjs +1091 -657
  57. package/dist/svelte.d.cts +2 -0
  58. package/dist/svelte.d.ts +2 -0
  59. package/dist/svelte.js +1091 -657
  60. package/dist/vue.cjs +1105 -657
  61. package/dist/vue.js +1105 -657
  62. package/dist/webgl.cjs +583 -0
  63. package/dist/webgl.d.cts +17 -0
  64. package/dist/webgl.d.ts +17 -0
  65. package/dist/webgl.js +581 -0
  66. package/package.json +88 -12
package/dist/element.js CHANGED
@@ -10,10 +10,10 @@ var __export = (target, all) => {
10
10
  };
11
11
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
12
12
 
13
- // src/core/lifecycle/sheetStack.ts
13
+ // src/core/lifecycle/sheet-stack.ts
14
14
  var BASE_Z, STEP, SheetStack, sheetStack;
15
- var init_sheetStack = __esm({
16
- "src/core/lifecycle/sheetStack.ts"() {
15
+ var init_sheet_stack = __esm({
16
+ "src/core/lifecycle/sheet-stack.ts"() {
17
17
  BASE_Z = 100;
18
18
  STEP = 10;
19
19
  SheetStack = class {
@@ -154,10 +154,10 @@ var init_event_bus = __esm({
154
154
  }
155
155
  });
156
156
 
157
- // src/core/primitives/cssLength.ts
157
+ // src/core/primitives/css-length.ts
158
158
  var probe, PROBE_ATTR, ensureProbe, isViewportAxisVertical, viewportSize, PERCENT, resolveSnap;
159
- var init_cssLength = __esm({
160
- "src/core/primitives/cssLength.ts"() {
159
+ var init_css_length = __esm({
160
+ "src/core/primitives/css-length.ts"() {
161
161
  probe = null;
162
162
  PROBE_ATTR = "data-bs-probe";
163
163
  ensureProbe = (axis) => {
@@ -213,14 +213,14 @@ var init_cssLength = __esm({
213
213
  }
214
214
  });
215
215
 
216
- // src/core/primitives/devWarn.ts
216
+ // src/core/primitives/dev-warn.ts
217
217
  function devWarn(...args) {
218
218
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
219
219
  console.warn(...args);
220
220
  }
221
221
  }
222
- var init_devWarn = __esm({
223
- "src/core/primitives/devWarn.ts"() {
222
+ var init_dev_warn = __esm({
223
+ "src/core/primitives/dev-warn.ts"() {
224
224
  }
225
225
  });
226
226
 
@@ -228,8 +228,8 @@ var init_devWarn = __esm({
228
228
  var resolveSnapList, VH_NOT_DVH, auditVhUsage, findNearest, allowedRange, findById, MOUSE_FLICK_VELOCITY, MOUSE_DRAG_THRESHOLD, DIRECTIONAL_INTENT_VELOCITY, findDragSettleTarget;
229
229
  var init_snap_points = __esm({
230
230
  "src/core/primitives/snap-points.ts"() {
231
- init_cssLength();
232
- init_devWarn();
231
+ init_css_length();
232
+ init_dev_warn();
233
233
  resolveSnapList = (points, mode, measureFit) => points.map((p) => {
234
234
  const raw = resolveSnap(p.size, mode, measureFit);
235
235
  if (!Number.isFinite(raw) || raw < 0) {
@@ -322,7 +322,7 @@ function emitCancelable(emit, base, warnLabel) {
322
322
  }
323
323
  var init_cancelable_emit = __esm({
324
324
  "src/core/primitives/cancelable-emit.ts"() {
325
- init_devWarn();
325
+ init_dev_warn();
326
326
  }
327
327
  });
328
328
 
@@ -400,82 +400,6 @@ var init_snap_resolver = __esm({
400
400
  }
401
401
  });
402
402
 
403
- // src/core/features/persist.ts
404
- function readPersistedId(key) {
405
- if (!key) return null;
406
- if (typeof window === "undefined") return null;
407
- if (typeof localStorage === "undefined") return null;
408
- try {
409
- return localStorage.getItem(key);
410
- } catch {
411
- return null;
412
- }
413
- }
414
- function installPersist(engine, key) {
415
- const unsubscribe = engine.on("snap", ({ id }) => {
416
- if (typeof window === "undefined") return;
417
- if (typeof localStorage === "undefined") return;
418
- try {
419
- localStorage.setItem(key, id);
420
- } catch {
421
- }
422
- });
423
- return unsubscribe;
424
- }
425
- var init_persist = __esm({
426
- "src/core/features/persist.ts"() {
427
- }
428
- });
429
-
430
- // src/core/features/auto-collapse.ts
431
- function installAutoCollapse(deps) {
432
- const { ms } = deps;
433
- if (ms === void 0 || ms <= 0) {
434
- return () => {
435
- };
436
- }
437
- let timer = null;
438
- const clearTimer = () => {
439
- if (timer !== null) {
440
- clearTimeout(timer);
441
- timer = null;
442
- }
443
- };
444
- const fire = () => {
445
- if (deps.isDestroyed()) return;
446
- if (deps.isDragging()) return;
447
- const active = deps.resolveSnap(deps.getActiveId());
448
- if (!active || active.size === 0) return;
449
- let target = null;
450
- for (const id of deps.getAllowedIds()) {
451
- const snap = deps.resolveSnap(id);
452
- if (!snap || snap.size <= 0) continue;
453
- if (!target || snap.size < target.size) target = snap;
454
- }
455
- if (!target) return;
456
- if (target.id === active.id || target.size >= active.size) return;
457
- deps.snapTo(target.id);
458
- };
459
- const reset = () => {
460
- clearTimer();
461
- if (deps.isDestroyed()) return;
462
- timer = setTimeout(() => {
463
- timer = null;
464
- fire();
465
- }, ms);
466
- };
467
- const unsubscribe = deps.on("snap", () => reset());
468
- if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
469
- return () => {
470
- clearTimer();
471
- unsubscribe();
472
- };
473
- }
474
- var init_auto_collapse = __esm({
475
- "src/core/features/auto-collapse.ts"() {
476
- }
477
- });
478
-
479
403
  // src/core/features/linked-sheets.ts
480
404
  function notifyLinkedSheets(sheets, self) {
481
405
  if (sheets.length === 0) return;
@@ -502,560 +426,211 @@ var init_linked_sheets = __esm({
502
426
  }
503
427
  });
504
428
 
505
- // src/core/features/history-coordinator.ts
506
- function onPopState() {
507
- if (suppress > 0) {
508
- suppress -= 1;
509
- const rebrand = pendingRebrand;
510
- pendingRebrand = null;
511
- if (rebrand) rebrand();
512
- maybeTeardownListener();
513
- return;
514
- }
515
- if (entries.length === 0) {
516
- notifyRouteChange();
517
- maybeTeardownListener();
518
- return;
429
+ // src/core/features/resize-observer.ts
430
+ function installResizeObserver(deps) {
431
+ if (typeof window === "undefined") {
432
+ return () => {
433
+ };
519
434
  }
520
- let target = null;
521
- let targetIdx = -1;
522
- for (let i = entries.length - 1; i >= 0; i -= 1) {
523
- const entry = entries[i];
524
- if (!entry.surface.isOpen()) continue;
525
- if (!target) {
526
- target = entry;
527
- targetIdx = i;
528
- }
529
- if (entry.surface.isTop()) {
530
- target = entry;
531
- targetIdx = i;
532
- break;
435
+ const onResize = () => {
436
+ if (deps.isDestroyed()) return;
437
+ deps.recomputeSnaps();
438
+ const mode = deps.getMode();
439
+ const isVerticalAxis = mode === "bottom" || mode === "top";
440
+ const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
441
+ const max = deps.getMaxAxisSize();
442
+ if (viewportSize2 > 0 && viewportSize2 < max) {
443
+ deps.setMaxAxisSize(viewportSize2);
533
444
  }
534
- }
535
- if (!target) {
536
- const removed = entries.pop();
537
- if (removed) removed.live = false;
538
- maybeTeardownListener();
539
- return;
540
- }
541
- target.live = false;
542
- entries.splice(targetIdx, 1);
543
- const settled = target;
544
- void Promise.resolve(settled.surface.close()).then(() => {
545
- if (settled.surface.isOpen()) restore(settled);
546
- });
547
- maybeTeardownListener();
548
- }
549
- function restore(entry) {
550
- if (!hasWindow() || !hasHistory()) return;
551
- entry.live = true;
552
- runInternal(() => {
553
- try {
554
- history.pushState(brandMarker(entry.surface), "", entry.surface.url);
555
- } catch {
556
- entry.live = false;
445
+ const wasAnimating = deps.isAnimating?.() ?? false;
446
+ deps.cancelInFlight();
447
+ deps.newCycle();
448
+ const current = deps.resolveActiveSnap();
449
+ const newMax = deps.getMaxAxisSize();
450
+ const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
451
+ deps.setSize(targetSize);
452
+ if (current && !deps.isDragging()) {
453
+ deps.applySize(targetSize);
454
+ if (wasAnimating) deps.resyncAfterCancel?.();
557
455
  }
558
- });
559
- if (entry.live) {
560
- entries.push(entry);
561
- ensureListener();
456
+ };
457
+ let resizeObserver = null;
458
+ if (typeof ResizeObserver !== "undefined") {
459
+ resizeObserver = new ResizeObserver(onResize);
460
+ resizeObserver.observe(document.documentElement);
461
+ } else {
462
+ window.addEventListener("resize", onResize);
562
463
  }
464
+ window.addEventListener("orientationchange", onResize);
465
+ const onVisibility = () => {
466
+ if (deps.isDestroyed()) return;
467
+ if (document.hidden) deps.cancelInFlight();
468
+ };
469
+ document.addEventListener("visibilitychange", onVisibility);
470
+ return () => {
471
+ resizeObserver?.disconnect();
472
+ window.removeEventListener("resize", onResize);
473
+ window.removeEventListener("orientationchange", onResize);
474
+ document.removeEventListener("visibilitychange", onVisibility);
475
+ };
563
476
  }
564
- function pushBackMarker(surface) {
565
- const entry = { surface, priorUrl: void 0, live: false };
566
- if (!hasWindow() || !hasHistory()) {
567
- return entry;
477
+ var init_resize_observer = __esm({
478
+ "src/core/features/resize-observer.ts"() {
568
479
  }
569
- entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
570
- runInternal(() => {
571
- try {
572
- history.pushState(brandMarker(surface), "", surface.url);
573
- entry.live = true;
574
- } catch {
575
- entry.live = false;
480
+ });
481
+
482
+ // src/core/features/fit-observer.ts
483
+ function installFitObserver(deps) {
484
+ if (typeof ResizeObserver === "undefined") return () => {
485
+ };
486
+ if (!deps.hasFitSnap()) return () => {
487
+ };
488
+ let raf = 0;
489
+ const schedule = () => {
490
+ if (raf) return;
491
+ raf = requestAnimationFrame(() => {
492
+ raf = 0;
493
+ if (deps.isDestroyed() || deps.isDragging()) return;
494
+ deps.recompute();
495
+ });
496
+ };
497
+ const ro = new ResizeObserver(schedule);
498
+ ro.observe(deps.handle);
499
+ const scroller = deps.scrollContainer;
500
+ const observed = /* @__PURE__ */ new Set();
501
+ const slots = /* @__PURE__ */ new Set();
502
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
503
+ const onSlotChange = () => resync();
504
+ const trackSlots = () => {
505
+ if (!scroller || !hasSlot) return;
506
+ for (const child of Array.from(scroller.children)) {
507
+ if (child instanceof HTMLSlotElement && !slots.has(child)) {
508
+ child.addEventListener("slotchange", onSlotChange);
509
+ slots.add(child);
510
+ }
511
+ }
512
+ };
513
+ const resync = () => {
514
+ trackSlots();
515
+ const targets = contentTargets(scroller);
516
+ for (const el of observed) {
517
+ if (!targets.includes(el)) {
518
+ ro.unobserve(el);
519
+ observed.delete(el);
520
+ }
521
+ }
522
+ for (const el of targets) {
523
+ if (!observed.has(el)) {
524
+ ro.observe(el);
525
+ observed.add(el);
526
+ }
527
+ }
528
+ schedule();
529
+ };
530
+ let mo;
531
+ if (scroller) {
532
+ resync();
533
+ if (typeof MutationObserver !== "undefined") {
534
+ mo = new MutationObserver(resync);
535
+ mo.observe(scroller, { childList: true });
576
536
  }
577
- });
578
- if (entry.live) {
579
- entries.push(entry);
580
- ensureListener();
581
537
  }
582
- return entry;
538
+ return () => {
539
+ ro.disconnect();
540
+ mo?.disconnect();
541
+ for (const slot of slots) {
542
+ slot.removeEventListener("slotchange", onSlotChange);
543
+ }
544
+ if (raf) cancelAnimationFrame(raf);
545
+ };
583
546
  }
584
- function popBackMarker(handle) {
585
- if (!hasWindow() || !hasHistory()) return;
586
- const entry = handle;
587
- const idx = entries.indexOf(entry);
588
- if (idx === -1 || !entry.live) return;
589
- const last = entries.length - 1;
590
- entry.live = false;
591
- entries.splice(idx, 1);
592
- if (idx !== last) {
593
- const rebrandSurface = entries[entries.length - 1].surface;
594
- const rebrandUrl = entry.priorUrl;
595
- pendingRebrand = () => {
596
- runInternal(() => {
597
- history.replaceState(
598
- brandMarker(rebrandSurface),
599
- "",
600
- rebrandUrl !== void 0 ? rebrandUrl : location.href
601
- );
602
- });
603
- };
604
- }
605
- suppress += 1;
606
- runInternal(() => {
607
- try {
608
- history.back();
609
- } catch {
610
- suppress = Math.max(0, suppress - 1);
611
- pendingRebrand = null;
612
- }
613
- });
614
- maybeTeardownListener();
615
- }
616
- function subscribeRouteChange(fn) {
617
- if (!hasWindow() || !hasHistory()) return () => {
618
- };
619
- subscribers.add(fn);
620
- if (subscribers.size === 1) installPatch();
621
- ensureListener();
622
- return () => {
623
- if (!subscribers.has(fn)) return;
624
- subscribers.delete(fn);
625
- if (subscribers.size === 0) removePatch();
626
- maybeTeardownListener();
627
- };
628
- }
629
- var entries, subscribers, suppress, internalDepth, pendingRebrand, listenerInstalled, patchInstalled, origPushState, origReplaceState, patchedPushState, patchedReplaceState, hasWindow, hasHistory, brandMarker, runInternal, notifyRouteChange, ensureListener, maybeTeardownListener, installPatch, removePatch;
630
- var init_history_coordinator = __esm({
631
- "src/core/features/history-coordinator.ts"() {
632
- entries = [];
633
- subscribers = /* @__PURE__ */ new Set();
634
- suppress = 0;
635
- internalDepth = 0;
636
- pendingRebrand = null;
637
- listenerInstalled = false;
638
- patchInstalled = false;
639
- origPushState = null;
640
- origReplaceState = null;
641
- patchedPushState = null;
642
- patchedReplaceState = null;
643
- hasWindow = () => typeof window !== "undefined";
644
- hasHistory = () => typeof history !== "undefined";
645
- brandMarker = (surface) => ({
646
- ...surface.markerState(),
647
- __bs: true
648
- });
649
- runInternal = (fn) => {
650
- internalDepth += 1;
651
- try {
652
- fn();
653
- } finally {
654
- internalDepth -= 1;
655
- }
656
- };
657
- notifyRouteChange = () => {
658
- const snapshot = Array.from(subscribers);
659
- for (const fn of snapshot) fn();
660
- };
661
- ensureListener = () => {
662
- if (listenerInstalled || !hasWindow()) return;
663
- window.addEventListener("popstate", onPopState);
664
- listenerInstalled = true;
665
- };
666
- maybeTeardownListener = () => {
667
- if (!listenerInstalled) return;
668
- if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
669
- if (hasWindow()) window.removeEventListener("popstate", onPopState);
670
- listenerInstalled = false;
671
- };
672
- installPatch = () => {
673
- if (patchInstalled || !hasHistory()) return;
674
- origPushState = history.pushState;
675
- origReplaceState = history.replaceState;
676
- patchedPushState = function(...args) {
677
- origPushState.apply(this, args);
678
- if (internalDepth === 0) notifyRouteChange();
679
- };
680
- patchedReplaceState = function(...args) {
681
- origReplaceState.apply(this, args);
682
- if (internalDepth === 0) notifyRouteChange();
683
- };
684
- history.pushState = patchedPushState;
685
- history.replaceState = patchedReplaceState;
686
- patchInstalled = true;
687
- };
688
- removePatch = () => {
689
- if (!patchInstalled) return;
690
- if (history.pushState === patchedPushState && origPushState) {
691
- history.pushState = origPushState;
692
- }
693
- if (history.replaceState === patchedReplaceState && origReplaceState) {
694
- history.replaceState = origReplaceState;
547
+ var contentTargets;
548
+ var init_fit_observer = __esm({
549
+ "src/core/features/fit-observer.ts"() {
550
+ contentTargets = (scroller) => {
551
+ if (!scroller) return [];
552
+ const hasSlot = typeof HTMLSlotElement !== "undefined";
553
+ const out = [];
554
+ for (const child of Array.from(scroller.children)) {
555
+ if (hasSlot && child instanceof HTMLSlotElement) {
556
+ for (const el of child.assignedElements()) out.push(el);
557
+ } else {
558
+ out.push(child);
559
+ }
695
560
  }
696
- origPushState = null;
697
- origReplaceState = null;
698
- patchedPushState = null;
699
- patchedReplaceState = null;
700
- patchInstalled = false;
561
+ return out;
701
562
  };
702
563
  }
703
564
  });
704
565
 
705
- // src/core/features/route.ts
706
- function installRoute(deps) {
707
- if (typeof window === "undefined") {
708
- return () => {
709
- };
710
- }
711
- if (!deps.routedTo && !deps.closeOnBack) {
712
- return () => {
713
- };
714
- }
715
- let handle = null;
716
- const onOpen = () => {
717
- if (handle) return;
718
- handle = pushBackMarker({
719
- isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
720
- isTop: deps.isTopSheet,
721
- close: deps.close,
722
- markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
723
- url: deps.routedTo
724
- });
725
- };
726
- const onClose = () => {
727
- if (!handle) return;
728
- popBackMarker(handle);
729
- handle = null;
730
- };
731
- const unsubscribeOpen = deps.on("open", onOpen);
732
- const unsubscribeClose = deps.on("close", onClose);
733
- if (deps.getSize() > 0) onOpen();
734
- return () => {
735
- unsubscribeOpen();
736
- unsubscribeClose();
737
- if (handle) {
738
- popBackMarker(handle);
739
- handle = null;
566
+ // src/core/features/max-height-controller.ts
567
+ function createMaxHeightController(deps) {
568
+ let maxHeightCap;
569
+ let maxHeightRaw;
570
+ const resolveCap = () => {
571
+ const raw = maxHeightRaw;
572
+ if (raw === void 0) {
573
+ maxHeightCap = void 0;
574
+ return;
740
575
  }
576
+ if (typeof raw === "number") {
577
+ maxHeightCap = raw;
578
+ return;
579
+ }
580
+ if (typeof window === "undefined") return;
581
+ const measured = resolveSnap(raw, deps.mode);
582
+ maxHeightCap = measured > 0 ? measured : void 0;
741
583
  };
742
- }
743
- function installRouteChange(deps) {
744
- return subscribeRouteChange(() => {
745
- if (deps.isDestroyed() || deps.getSize() <= 0) return;
746
- void deps.close();
747
- });
748
- }
749
- var init_route = __esm({
750
- "src/core/features/route.ts"() {
751
- init_history_coordinator();
752
- }
753
- });
754
-
755
- // src/core/features/content-swipe.ts
756
- function installContentSwipe(deps) {
757
- const { container } = deps;
758
- let startY = 0;
759
- let startScrollTop = 0;
760
- let moved = false;
761
- const onTouchStart = (e) => {
762
- const t = e.touches[0];
763
- if (!t) return;
764
- startY = t.clientY;
765
- startScrollTop = container.scrollTop;
766
- moved = false;
767
- };
768
- const onTouchMove = (e) => {
769
- const t = e.touches[0];
770
- if (!t) return;
771
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
772
- };
773
- const onTouchEnd = (e) => {
774
- if (!moved) return;
775
- if (deps.isDragging() || deps.isAnimating()) return;
776
- if (container.scrollTop !== startScrollTop) return;
777
- const t = e.changedTouches[0];
778
- if (!t) return;
779
- const deltaY = t.clientY - startY;
780
- const atTop = container.scrollTop <= 0;
781
- const allowed = deps.getAllowedIds();
782
- const idx = allowed.indexOf(deps.getActiveId());
783
- if (idx === -1) return;
784
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
785
- deps.snapTo(allowed[idx - 1]);
786
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
787
- deps.snapTo(allowed[idx + 1]);
584
+ const clampTo = () => {
585
+ if (maxHeightCap === void 0) return;
586
+ if (deps.getMaxAxisSize() > maxHeightCap) {
587
+ deps.setMaxAxisSize(maxHeightCap);
788
588
  }
789
589
  };
790
- container.addEventListener("touchstart", onTouchStart, { passive: true });
791
- container.addEventListener("touchmove", onTouchMove, { passive: true });
792
- container.addEventListener("touchend", onTouchEnd, { passive: true });
793
- return () => {
794
- container.removeEventListener("touchstart", onTouchStart);
795
- container.removeEventListener("touchmove", onTouchMove);
796
- container.removeEventListener("touchend", onTouchEnd);
590
+ return {
591
+ setRadius(r) {
592
+ deps.element.style.setProperty(
593
+ "--bs-radius",
594
+ typeof r === "number" ? `${r}px` : r
595
+ );
596
+ },
597
+ setMaxHeight(h) {
598
+ const value = typeof h === "number" ? `${h}px` : h;
599
+ const axis = layoutAxis(deps.mode);
600
+ deps.element.style.setProperty(
601
+ axis === "height" ? "max-height" : "max-width",
602
+ value
603
+ );
604
+ maxHeightRaw = h;
605
+ resolveCap();
606
+ deps.recompute();
607
+ },
608
+ resolveCap,
609
+ clampTo,
610
+ getCap: () => maxHeightCap
797
611
  };
798
612
  }
799
- var CONTENT_PULL_THRESHOLD;
800
- var init_content_swipe = __esm({
801
- "src/core/features/content-swipe.ts"() {
802
- CONTENT_PULL_THRESHOLD = 60;
613
+ var init_max_height_controller = __esm({
614
+ "src/core/features/max-height-controller.ts"() {
615
+ init_snap_points();
616
+ init_transform();
803
617
  }
804
618
  });
805
619
 
806
- // src/core/features/visual-viewport.ts
807
- function installVisualViewport(deps) {
808
- if (typeof window === "undefined" || !window.visualViewport) {
809
- return () => {
810
- };
811
- }
812
- const vv = window.visualViewport;
813
- let rafId = null;
814
- const onResize = () => {
815
- if (rafId !== null) return;
816
- rafId = requestAnimationFrame(() => {
817
- rafId = null;
818
- if (deps.isDestroyed()) return;
819
- deps.recomputeSnaps();
820
- const isVertical = deps.isVerticalAxis();
821
- const visible = isVertical ? vv.height : vv.width;
822
- const windowSize = isVertical ? window.innerHeight : window.innerWidth;
823
- const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
824
- if (clamp < deps.getMaxAxisSize()) {
825
- deps.setMaxAxisSize(clamp);
826
- }
827
- const wasAnimating = deps.isAnimating?.() ?? false;
828
- deps.cancelInFlight();
829
- deps.newCycle();
830
- const max = deps.getMaxAxisSize();
831
- deps.setSize(Math.min(deps.getSize(), max));
832
- const current = deps.resolveActiveSnap();
833
- if (current && !deps.isDragging()) {
834
- deps.applySize(Math.min(current.size, max));
835
- if (wasAnimating) deps.resyncAfterCancel?.();
836
- } else {
837
- deps.applySize(deps.getSize());
838
- }
839
- });
840
- };
841
- vv.addEventListener("resize", onResize);
842
- return () => {
843
- vv.removeEventListener("resize", onResize);
844
- if (rafId !== null) cancelAnimationFrame(rafId);
845
- };
846
- }
847
- var SAFETY_PAD;
848
- var init_visual_viewport = __esm({
849
- "src/core/features/visual-viewport.ts"() {
850
- SAFETY_PAD = 8;
851
- }
852
- });
853
-
854
- // src/core/features/resize-observer.ts
855
- function installResizeObserver(deps) {
856
- if (typeof window === "undefined") {
857
- return () => {
858
- };
859
- }
860
- const onResize = () => {
861
- if (deps.isDestroyed()) return;
862
- deps.recomputeSnaps();
863
- const mode = deps.getMode();
864
- const isVerticalAxis = mode === "bottom" || mode === "top";
865
- const viewportSize2 = isVerticalAxis ? window.innerHeight : window.innerWidth;
866
- const max = deps.getMaxAxisSize();
867
- if (viewportSize2 > 0 && viewportSize2 < max) {
868
- deps.setMaxAxisSize(viewportSize2);
869
- }
870
- const wasAnimating = deps.isAnimating?.() ?? false;
871
- deps.cancelInFlight();
872
- deps.newCycle();
873
- const current = deps.resolveActiveSnap();
874
- const newMax = deps.getMaxAxisSize();
875
- const targetSize = current ? Math.min(current.size, newMax) : Math.min(deps.getSize(), newMax);
876
- deps.setSize(targetSize);
877
- if (current && !deps.isDragging()) {
878
- deps.applySize(targetSize);
879
- if (wasAnimating) deps.resyncAfterCancel?.();
880
- }
881
- };
882
- let resizeObserver = null;
883
- if (typeof ResizeObserver !== "undefined") {
884
- resizeObserver = new ResizeObserver(onResize);
885
- resizeObserver.observe(document.documentElement);
886
- } else {
887
- window.addEventListener("resize", onResize);
888
- }
889
- window.addEventListener("orientationchange", onResize);
890
- const onVisibility = () => {
891
- if (deps.isDestroyed()) return;
892
- if (document.hidden) deps.cancelInFlight();
893
- };
894
- document.addEventListener("visibilitychange", onVisibility);
895
- return () => {
896
- resizeObserver?.disconnect();
897
- window.removeEventListener("resize", onResize);
898
- window.removeEventListener("orientationchange", onResize);
899
- document.removeEventListener("visibilitychange", onVisibility);
900
- };
901
- }
902
- var init_resize_observer = __esm({
903
- "src/core/features/resize-observer.ts"() {
904
- }
905
- });
906
-
907
- // src/core/features/fit-observer.ts
908
- function installFitObserver(deps) {
909
- if (typeof ResizeObserver === "undefined") return () => {
910
- };
911
- if (!deps.hasFitSnap()) return () => {
912
- };
913
- let raf = 0;
914
- const schedule = () => {
915
- if (raf) return;
916
- raf = requestAnimationFrame(() => {
917
- raf = 0;
918
- if (deps.isDestroyed() || deps.isDragging()) return;
919
- deps.recompute();
920
- });
921
- };
922
- const ro = new ResizeObserver(schedule);
923
- ro.observe(deps.handle);
924
- const scroller = deps.scrollContainer;
925
- const observed = /* @__PURE__ */ new Set();
926
- const slots = /* @__PURE__ */ new Set();
927
- const hasSlot = typeof HTMLSlotElement !== "undefined";
928
- const onSlotChange = () => resync();
929
- const trackSlots = () => {
930
- if (!scroller || !hasSlot) return;
931
- for (const child of Array.from(scroller.children)) {
932
- if (child instanceof HTMLSlotElement && !slots.has(child)) {
933
- child.addEventListener("slotchange", onSlotChange);
934
- slots.add(child);
935
- }
936
- }
937
- };
938
- const resync = () => {
939
- trackSlots();
940
- const targets = contentTargets(scroller);
941
- for (const el of observed) {
942
- if (!targets.includes(el)) {
943
- ro.unobserve(el);
944
- observed.delete(el);
945
- }
946
- }
947
- for (const el of targets) {
948
- if (!observed.has(el)) {
949
- ro.observe(el);
950
- observed.add(el);
951
- }
952
- }
953
- schedule();
954
- };
955
- let mo;
956
- if (scroller) {
957
- resync();
958
- if (typeof MutationObserver !== "undefined") {
959
- mo = new MutationObserver(resync);
960
- mo.observe(scroller, { childList: true });
961
- }
962
- }
963
- return () => {
964
- ro.disconnect();
965
- mo?.disconnect();
966
- for (const slot of slots) {
967
- slot.removeEventListener("slotchange", onSlotChange);
968
- }
969
- if (raf) cancelAnimationFrame(raf);
970
- };
971
- }
972
- var contentTargets;
973
- var init_fit_observer = __esm({
974
- "src/core/features/fit-observer.ts"() {
975
- contentTargets = (scroller) => {
976
- if (!scroller) return [];
977
- const hasSlot = typeof HTMLSlotElement !== "undefined";
978
- const out = [];
979
- for (const child of Array.from(scroller.children)) {
980
- if (hasSlot && child instanceof HTMLSlotElement) {
981
- for (const el of child.assignedElements()) out.push(el);
982
- } else {
983
- out.push(child);
984
- }
985
- }
986
- return out;
987
- };
988
- }
989
- });
990
-
991
- // src/core/features/max-height-controller.ts
992
- function createMaxHeightController(deps) {
993
- let maxHeightCap;
994
- let maxHeightRaw;
995
- const resolveCap = () => {
996
- const raw = maxHeightRaw;
997
- if (raw === void 0) {
998
- maxHeightCap = void 0;
999
- return;
1000
- }
1001
- if (typeof raw === "number") {
1002
- maxHeightCap = raw;
1003
- return;
1004
- }
1005
- if (typeof window === "undefined") return;
1006
- const measured = resolveSnap(raw, deps.mode);
1007
- maxHeightCap = measured > 0 ? measured : void 0;
1008
- };
1009
- const clampTo = () => {
1010
- if (maxHeightCap === void 0) return;
1011
- if (deps.getMaxAxisSize() > maxHeightCap) {
1012
- deps.setMaxAxisSize(maxHeightCap);
1013
- }
1014
- };
1015
- return {
1016
- setRadius(r) {
1017
- deps.element.style.setProperty(
1018
- "--bs-radius",
1019
- typeof r === "number" ? `${r}px` : r
1020
- );
1021
- },
1022
- setMaxHeight(h) {
1023
- const value = typeof h === "number" ? `${h}px` : h;
1024
- const axis = layoutAxis(deps.mode);
1025
- deps.element.style.setProperty(
1026
- axis === "height" ? "max-height" : "max-width",
1027
- value
1028
- );
1029
- maxHeightRaw = h;
1030
- resolveCap();
1031
- deps.recompute();
1032
- },
1033
- resolveCap,
1034
- clampTo,
1035
- getCap: () => maxHeightCap
1036
- };
1037
- }
1038
- var init_max_height_controller = __esm({
1039
- "src/core/features/max-height-controller.ts"() {
1040
- init_snap_points();
1041
- init_transform();
1042
- }
1043
- });
1044
-
1045
- // src/core/features/fit-measurement.ts
1046
- var measureSheetNatural, containingExtent, measureFitSize;
1047
- var init_fit_measurement = __esm({
1048
- "src/core/features/fit-measurement.ts"() {
1049
- init_transform();
1050
- measureSheetNatural = (element, content, vertical) => {
1051
- const axis = vertical ? "height" : "width";
1052
- const sheetStyle = element.style;
1053
- if (!content) {
1054
- const prev = sheetStyle[axis];
1055
- sheetStyle[axis] = "auto";
1056
- const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
1057
- sheetStyle[axis] = prev;
1058
- return poked2;
620
+ // src/core/features/fit-measurement.ts
621
+ var measureSheetNatural, containingExtent, measureFitSize;
622
+ var init_fit_measurement = __esm({
623
+ "src/core/features/fit-measurement.ts"() {
624
+ init_transform();
625
+ measureSheetNatural = (element, content, vertical) => {
626
+ const axis = vertical ? "height" : "width";
627
+ const sheetStyle = element.style;
628
+ if (!content) {
629
+ const prev = sheetStyle[axis];
630
+ sheetStyle[axis] = "auto";
631
+ const poked2 = vertical ? element.offsetHeight : element.offsetWidth;
632
+ sheetStyle[axis] = prev;
633
+ return poked2;
1059
634
  }
1060
635
  const cs = content.style;
1061
636
  const prevSheet = sheetStyle[axis];
@@ -1293,7 +868,7 @@ var init_scrim_controller = __esm({
1293
868
  init_snap_points();
1294
869
  init_hot_path_thresholds();
1295
870
  init_opacity_dedup();
1296
- init_devWarn();
871
+ init_dev_warn();
1297
872
  init_overlay_position();
1298
873
  VALID_CSS_LENGTH = /^-?\d+(?:\.\d+)?(?:px|em|rem|%)$/;
1299
874
  DEFAULT_SCRIM_DIM = "rgba(0, 0, 0, 0.4)";
@@ -1822,6 +1397,58 @@ var init_animation_presets = __esm({
1822
1397
  }
1823
1398
  });
1824
1399
 
1400
+ // src/core/animation/waapi-settle.ts
1401
+ function sampleSpringSettle(from, to, velocityPxPerS, config) {
1402
+ const cfg = { ...DEFAULT_SPRING, ...config };
1403
+ const subDt = 1 / SIM_HZ;
1404
+ const stepMs = SAMPLE_STRIDE / SIM_HZ * 1e3;
1405
+ const maxSteps = Math.ceil(MAX_SIM_MS / 1e3 * SIM_HZ);
1406
+ const values = [from];
1407
+ let x = from;
1408
+ let v = velocityPxPerS;
1409
+ for (let i = 1; i <= maxSteps; i += 1) {
1410
+ const force = -cfg.stiffness * (x - to) - cfg.damping * v;
1411
+ v += force / cfg.mass * subDt;
1412
+ x += v * subDt;
1413
+ if (!Number.isFinite(x) || !Number.isFinite(v)) {
1414
+ values.push(to);
1415
+ break;
1416
+ }
1417
+ if (i % SAMPLE_STRIDE === 0) values.push(x);
1418
+ const atRest = Math.abs(to - x) < (cfg.restDelta ?? 0.5) && Math.abs(v) < (cfg.restSpeed ?? 0.5);
1419
+ if (atRest) break;
1420
+ }
1421
+ if (values[values.length - 1] !== to) values.push(to);
1422
+ return {
1423
+ values,
1424
+ stepMs,
1425
+ durationMs: (values.length - 1) * stepMs
1426
+ };
1427
+ }
1428
+ function sampleTweenSettle(from, to, durationMs, easing) {
1429
+ const count = Math.max(2, Math.min(60, Math.round(durationMs / 16)));
1430
+ const values = [];
1431
+ for (let i = 0; i <= count; i += 1) {
1432
+ const t = i / count;
1433
+ values.push(from + (to - from) * easing(t));
1434
+ }
1435
+ values[values.length - 1] = to;
1436
+ return {
1437
+ values,
1438
+ stepMs: durationMs / count,
1439
+ durationMs
1440
+ };
1441
+ }
1442
+ var SIM_HZ, SAMPLE_STRIDE, MAX_SIM_MS;
1443
+ var init_waapi_settle = __esm({
1444
+ "src/core/animation/waapi-settle.ts"() {
1445
+ init_spring();
1446
+ SIM_HZ = 240;
1447
+ SAMPLE_STRIDE = 4;
1448
+ MAX_SIM_MS = 3e3;
1449
+ }
1450
+ });
1451
+
1825
1452
  // src/core/controllers/animation-runner.ts
1826
1453
  var DEFAULT_DURATION2, VELOCITY_PX_PER_S, AnimationRunner;
1827
1454
  var init_animation_runner = __esm({
@@ -1829,6 +1456,7 @@ var init_animation_runner = __esm({
1829
1456
  init_animation();
1830
1457
  init_spring();
1831
1458
  init_animation_presets();
1459
+ init_waapi_settle();
1832
1460
  DEFAULT_DURATION2 = 220;
1833
1461
  VELOCITY_PX_PER_S = 1e3;
1834
1462
  AnimationRunner = class {
@@ -1838,6 +1466,10 @@ var init_animation_runner = __esm({
1838
1466
  __publicField(this, "applySizeFn");
1839
1467
  __publicField(this, "getSize");
1840
1468
  __publicField(this, "isDragging");
1469
+ __publicField(this, "applyAux");
1470
+ __publicField(this, "getTransformFor");
1471
+ __publicField(this, "settleWaapi");
1472
+ __publicField(this, "currentWaapi", null);
1841
1473
  __publicField(this, "animationKind");
1842
1474
  __publicField(this, "springConfig");
1843
1475
  __publicField(this, "duration");
@@ -1853,6 +1485,9 @@ var init_animation_runner = __esm({
1853
1485
  this.applySizeFn = deps.applySize;
1854
1486
  this.getSize = deps.getSize;
1855
1487
  this.isDragging = deps.isDragging;
1488
+ this.applyAux = deps.applyAux;
1489
+ this.getTransformFor = deps.getTransformFor;
1490
+ this.settleWaapi = opts.settleAnimation === "waapi";
1856
1491
  const preset = resolveAnimationPreset(opts.animation);
1857
1492
  this.animationKind = preset.kind;
1858
1493
  this.duration = opts.duration ?? preset.duration ?? DEFAULT_DURATION2;
@@ -1877,11 +1512,20 @@ var init_animation_runner = __esm({
1877
1512
  }
1878
1513
  }
1879
1514
  get isAnimating() {
1880
- return this.currentTween !== null || this.currentSpring !== null;
1515
+ return this.currentTween !== null || this.currentSpring !== null || this.currentWaapi !== null;
1881
1516
  }
1882
1517
  cancel() {
1883
1518
  this.currentTween?.cancel();
1884
1519
  this.currentSpring?.cancel();
1520
+ if (this.currentWaapi) {
1521
+ const entry = this.currentWaapi;
1522
+ this.currentWaapi = null;
1523
+ entry.stop();
1524
+ try {
1525
+ entry.anim.cancel();
1526
+ } catch {
1527
+ }
1528
+ }
1885
1529
  }
1886
1530
  async animateTo(target, velocityPxPerMs) {
1887
1531
  if (target === this.getSize()) {
@@ -1900,6 +1544,27 @@ var init_animation_runner = __esm({
1900
1544
  if (!this.isDragging()) this.element.style.willChange = "";
1901
1545
  return;
1902
1546
  }
1547
+ if (this.settleWaapi && this.applyAux && this.getTransformFor && typeof this.element.animate === "function") {
1548
+ const samples = this.animationKind === "spring" ? sampleSpringSettle(
1549
+ this.getSize(),
1550
+ target,
1551
+ velocityPxPerMs * VELOCITY_PX_PER_S,
1552
+ this.springConfig
1553
+ ) : sampleTweenSettle(
1554
+ this.getSize(),
1555
+ target,
1556
+ this.duration,
1557
+ this.easing
1558
+ );
1559
+ if (samples.values.length >= 2 && samples.durationMs > 0) {
1560
+ await this.runWaapiSettle(samples, target);
1561
+ if (!this.isAnimating) {
1562
+ this.getRootEl()?.removeAttribute("data-animating");
1563
+ if (!this.isDragging()) this.element.style.willChange = "";
1564
+ }
1565
+ return;
1566
+ }
1567
+ }
1903
1568
  if (this.animationKind === "spring") {
1904
1569
  const spring = runSpring({
1905
1570
  from: this.getSize(),
@@ -1928,20 +1593,75 @@ var init_animation_runner = __esm({
1928
1593
  if (!this.isDragging()) this.element.style.willChange = "";
1929
1594
  }
1930
1595
  }
1931
- destroy() {
1932
- this.detachReducedMotion?.();
1933
- this.detachReducedMotion = null;
1596
+ async runWaapiSettle(samples, target) {
1597
+ const applyAux = this.applyAux;
1598
+ const getTransformFor = this.getTransformFor;
1599
+ const frames = samples.values.map((v) => ({
1600
+ transform: getTransformFor(v)
1601
+ }));
1602
+ const anim = this.element.animate(frames, {
1603
+ duration: samples.durationMs,
1604
+ easing: "linear",
1605
+ fill: "forwards"
1606
+ });
1607
+ let stopped = false;
1608
+ let auxRaf = 0;
1609
+ const start = performance.now();
1610
+ const lastIdx = samples.values.length - 1;
1611
+ const auxTick = () => {
1612
+ if (stopped) return;
1613
+ const elapsed = performance.now() - start;
1614
+ const idx = Math.min(Math.floor(elapsed / samples.stepMs), lastIdx);
1615
+ applyAux(samples.values[idx]);
1616
+ auxRaf = requestAnimationFrame(auxTick);
1617
+ };
1618
+ auxRaf = requestAnimationFrame(auxTick);
1619
+ const entry = {
1620
+ anim,
1621
+ stop: () => {
1622
+ stopped = true;
1623
+ cancelAnimationFrame(auxRaf);
1624
+ }
1625
+ };
1626
+ this.currentWaapi = entry;
1627
+ try {
1628
+ await anim.finished;
1629
+ } catch {
1630
+ }
1631
+ entry.stop();
1632
+ const wasCurrent = this.currentWaapi === entry;
1633
+ if (wasCurrent) this.currentWaapi = null;
1634
+ if (wasCurrent && anim.playState === "finished") {
1635
+ this.applySizeFn(target);
1636
+ try {
1637
+ anim.cancel();
1638
+ } catch {
1639
+ }
1640
+ }
1641
+ }
1642
+ destroy() {
1643
+ this.detachReducedMotion?.();
1644
+ this.detachReducedMotion = null;
1934
1645
  this.currentTween = null;
1935
1646
  this.currentSpring = null;
1647
+ if (this.currentWaapi) {
1648
+ const entry = this.currentWaapi;
1649
+ this.currentWaapi = null;
1650
+ entry.stop();
1651
+ try {
1652
+ entry.anim.cancel();
1653
+ } catch {
1654
+ }
1655
+ }
1936
1656
  }
1937
1657
  };
1938
1658
  }
1939
1659
  });
1940
1660
 
1941
- // src/core/lifecycle/focusTrap.ts
1661
+ // src/core/lifecycle/focus-trap.ts
1942
1662
  var FOCUSABLE, isVisible, collectFocusables, focusables, containsComposed, trapStack, installFocusTrap;
1943
- var init_focusTrap = __esm({
1944
- "src/core/lifecycle/focusTrap.ts"() {
1663
+ var init_focus_trap = __esm({
1664
+ "src/core/lifecycle/focus-trap.ts"() {
1945
1665
  FOCUSABLE = [
1946
1666
  "a[href]",
1947
1667
  "area[href]",
@@ -2061,10 +1781,10 @@ var init_focusTrap = __esm({
2061
1781
  }
2062
1782
  });
2063
1783
 
2064
- // src/core/lifecycle/scrollLock.ts
1784
+ // src/core/lifecycle/scroll-lock.ts
2065
1785
  var lockCount, savedStyles, lockBodyScroll;
2066
- var init_scrollLock = __esm({
2067
- "src/core/lifecycle/scrollLock.ts"() {
1786
+ var init_scroll_lock = __esm({
1787
+ "src/core/lifecycle/scroll-lock.ts"() {
2068
1788
  lockCount = 0;
2069
1789
  savedStyles = null;
2070
1790
  lockBodyScroll = () => {
@@ -2147,8 +1867,8 @@ var init_inert_siblings = __esm({
2147
1867
  var LifecycleController;
2148
1868
  var init_lifecycle_controller = __esm({
2149
1869
  "src/core/controllers/lifecycle-controller.ts"() {
2150
- init_focusTrap();
2151
- init_scrollLock();
1870
+ init_focus_trap();
1871
+ init_scroll_lock();
2152
1872
  init_inert_siblings();
2153
1873
  LifecycleController = class {
2154
1874
  constructor(deps, opts) {
@@ -2263,30 +1983,44 @@ var init_gestures = __esm({
2263
1983
  this.size = 0;
2264
1984
  }
2265
1985
  };
2266
- installGestures = (handle, mode, callbacks) => {
1986
+ installGestures = (handle, mode, callbacks, options = {}) => {
2267
1987
  let activePointerId = null;
2268
1988
  let activePointerType = "touch";
2269
1989
  let startCoord = 0;
2270
1990
  let lastCoord = 0;
1991
+ let pendingStart = false;
2271
1992
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
2272
1993
  const MAX_SAMPLES = 32;
2273
1994
  const samples = new SampleRing(MAX_SAMPLES);
2274
1995
  const axis = isAxisVertical(mode) ? "Y" : "X";
2275
1996
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
2276
1997
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1998
+ const capture = (pointerId) => {
1999
+ try {
2000
+ handle.setPointerCapture(pointerId);
2001
+ } catch {
2002
+ }
2003
+ };
2004
+ const releaseTracking = () => {
2005
+ activePointerId = null;
2006
+ pendingStart = false;
2007
+ samples.reset();
2008
+ };
2277
2009
  const onPointerDown = (e) => {
2278
2010
  if (activePointerId !== null) return;
2279
2011
  if (e.button !== void 0 && e.button !== 0) return;
2012
+ if (options.shouldStart && !options.shouldStart(e)) return;
2280
2013
  activePointerId = e.pointerId;
2281
2014
  activePointerType = e.pointerType || "touch";
2282
2015
  startCoord = coordOf(e);
2283
2016
  lastCoord = startCoord;
2284
2017
  samples.reset();
2285
2018
  samples.push(e.timeStamp, startCoord);
2286
- try {
2287
- handle.setPointerCapture(e.pointerId);
2288
- } catch {
2019
+ if (options.deferStart) {
2020
+ pendingStart = true;
2021
+ return;
2289
2022
  }
2023
+ capture(e.pointerId);
2290
2024
  callbacks.onStart(startCoord, activePointerType);
2291
2025
  };
2292
2026
  const onPointerMove = (e) => {
@@ -2294,6 +2028,22 @@ var init_gestures = __esm({
2294
2028
  const coord = coordOf(e);
2295
2029
  const rawDelta = coord - startCoord;
2296
2030
  const delta = rawDelta * sign;
2031
+ if (pendingStart) {
2032
+ const verdict = options.deferStart(e, delta);
2033
+ if (verdict === false) {
2034
+ releaseTracking();
2035
+ return;
2036
+ }
2037
+ if (verdict !== true) return;
2038
+ pendingStart = false;
2039
+ startCoord = coord;
2040
+ lastCoord = coord;
2041
+ samples.reset();
2042
+ samples.push(e.timeStamp, coord);
2043
+ capture(e.pointerId);
2044
+ callbacks.onStart(startCoord, activePointerType);
2045
+ return;
2046
+ }
2297
2047
  lastCoord = coord;
2298
2048
  samples.push(e.timeStamp, coord);
2299
2049
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -2302,6 +2052,10 @@ var init_gestures = __esm({
2302
2052
  };
2303
2053
  const finishGesture = (e) => {
2304
2054
  if (e.pointerId !== activePointerId) return;
2055
+ if (pendingStart) {
2056
+ releaseTracking();
2057
+ return;
2058
+ }
2305
2059
  const rawDelta = lastCoord - startCoord;
2306
2060
  const delta = rawDelta * sign;
2307
2061
  let velocity = 0;
@@ -2324,22 +2078,26 @@ var init_gestures = __esm({
2324
2078
  };
2325
2079
  const onPointerCancel = (e) => {
2326
2080
  if (e.pointerId !== activePointerId) return;
2327
- activePointerId = null;
2328
- samples.reset();
2081
+ const wasPending = pendingStart;
2082
+ releaseTracking();
2083
+ if (wasPending) return;
2329
2084
  callbacks.onCancel?.();
2330
2085
  };
2331
2086
  handle.addEventListener("pointerdown", onPointerDown);
2332
2087
  handle.addEventListener("pointermove", onPointerMove);
2333
2088
  handle.addEventListener("pointerup", finishGesture);
2334
2089
  handle.addEventListener("pointercancel", onPointerCancel);
2090
+ const managesTouchAction = options.manageTouchAction !== false;
2335
2091
  const prevTouchAction = handle.style.touchAction;
2336
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
2092
+ if (managesTouchAction) {
2093
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
2094
+ }
2337
2095
  return () => {
2338
2096
  handle.removeEventListener("pointerdown", onPointerDown);
2339
2097
  handle.removeEventListener("pointermove", onPointerMove);
2340
2098
  handle.removeEventListener("pointerup", finishGesture);
2341
2099
  handle.removeEventListener("pointercancel", onPointerCancel);
2342
- handle.style.touchAction = prevTouchAction;
2100
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
2343
2101
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
2344
2102
  handle.releasePointerCapture(activePointerId);
2345
2103
  }
@@ -2474,7 +2232,7 @@ var init_gesture_controller = __esm({
2474
2232
  const restoreTo = this.dragStartSize;
2475
2233
  void this.deps.animateTo(restoreTo, 0);
2476
2234
  }
2477
- });
2235
+ }, this.deps.gestureOptions);
2478
2236
  return this.detach;
2479
2237
  }
2480
2238
  forceClearDragState() {
@@ -2488,6 +2246,55 @@ var init_gesture_controller = __esm({
2488
2246
  }
2489
2247
  });
2490
2248
 
2249
+ // src/core/primitives/drag-zones.ts
2250
+ function isDragAllowedFrom(target, mode) {
2251
+ const el = asElement(target);
2252
+ if (!el) return mode !== "zones";
2253
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2254
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2255
+ return true;
2256
+ }
2257
+ var DRAG_ZONE_SELECTOR, NO_DRAG_SELECTOR, asElement;
2258
+ var init_drag_zones = __esm({
2259
+ "src/core/primitives/drag-zones.ts"() {
2260
+ DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2261
+ NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2262
+ asElement = (target) => {
2263
+ const candidate = target;
2264
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2265
+ };
2266
+ }
2267
+ });
2268
+
2269
+ // src/core/primitives/content-gesture.ts
2270
+ function decideContentGesture(input) {
2271
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2272
+ if (Math.abs(input.delta) < slop) return "pending";
2273
+ if (input.scrollTop > 0) return "scroll";
2274
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2275
+ return "drag";
2276
+ }
2277
+ var CONTENT_DRAG_SLOP;
2278
+ var init_content_gesture = __esm({
2279
+ "src/core/primitives/content-gesture.ts"() {
2280
+ CONTENT_DRAG_SLOP = 6;
2281
+ }
2282
+ });
2283
+
2284
+ // src/core/primitives/touch-scroll-guard.ts
2285
+ function installTouchScrollGuard(surface, isDragging) {
2286
+ const onTouchMove = (e) => {
2287
+ if (!isDragging()) return;
2288
+ if (e.cancelable) e.preventDefault();
2289
+ };
2290
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2291
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2292
+ }
2293
+ var init_touch_scroll_guard = __esm({
2294
+ "src/core/primitives/touch-scroll-guard.ts"() {
2295
+ }
2296
+ });
2297
+
2491
2298
  // src/core/primitives/aria-slider-writer.ts
2492
2299
  var AriaSliderWriter;
2493
2300
  var init_aria_slider_writer = __esm({
@@ -2860,7 +2667,7 @@ function resolveEngineOptions(opts, extras) {
2860
2667
  const initialAllowed = opts.allowed ?? opts.snapPoints.map((p) => p.id);
2861
2668
  let initialId = opts.initial ?? initialAllowed[0] ?? opts.snapPoints[0]?.id ?? "default";
2862
2669
  if (opts.persistKey) {
2863
- const restored = readPersistedId(opts.persistKey);
2670
+ const restored = readPersistedIdSafe(opts.persistKey);
2864
2671
  if (restored && initialAllowed.includes(restored)) {
2865
2672
  initialId = restored;
2866
2673
  }
@@ -2869,6 +2676,8 @@ function resolveEngineOptions(opts, extras) {
2869
2676
  mode: opts.mode ?? "bottom",
2870
2677
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2871
2678
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2679
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2680
+ dragFromContent: opts.dragFromContent ?? true,
2872
2681
  rubberBandEnabled: opts.rubberBand ?? true,
2873
2682
  closeOnBack: opts.closeOnBack ?? false,
2874
2683
  persistent: opts.persistent ?? false,
@@ -2891,6 +2700,7 @@ function resolveEngineOptions(opts, extras) {
2891
2700
  },
2892
2701
  animation: {
2893
2702
  animation: opts.animation,
2703
+ settleAnimation: opts.settleAnimation,
2894
2704
  duration: opts.duration,
2895
2705
  easing: opts.easing,
2896
2706
  spring: opts.spring,
@@ -2908,12 +2718,20 @@ function resolveEngineOptions(opts, extras) {
2908
2718
  }
2909
2719
  };
2910
2720
  }
2911
- var DEFAULT_FLICK_VELOCITY, DEFAULT_DRAG_THRESHOLD;
2721
+ var DEFAULT_FLICK_VELOCITY, DEFAULT_DRAG_THRESHOLD, readPersistedIdSafe;
2912
2722
  var init_engine_options = __esm({
2913
2723
  "src/core/primitives/engine-options.ts"() {
2914
- init_persist();
2915
2724
  DEFAULT_FLICK_VELOCITY = 0.65;
2916
2725
  DEFAULT_DRAG_THRESHOLD = 18;
2726
+ readPersistedIdSafe = (key) => {
2727
+ if (typeof window === "undefined") return null;
2728
+ if (typeof localStorage === "undefined") return null;
2729
+ try {
2730
+ return localStorage.getItem(key);
2731
+ } catch {
2732
+ return null;
2733
+ }
2734
+ };
2917
2735
  }
2918
2736
  });
2919
2737
 
@@ -2943,24 +2761,19 @@ var init_teardown_stack = __esm({
2943
2761
  }
2944
2762
  });
2945
2763
 
2946
- // src/core/BottomSheetEngine.ts
2947
- var HAPTIC_DURATION_MS, BottomSheetEngine;
2948
- var init_BottomSheetEngine = __esm({
2949
- "src/core/BottomSheetEngine.ts"() {
2950
- init_sheetStack();
2764
+ // src/core/BottomSheetCore.ts
2765
+ var HAPTIC_DURATION_MS, BottomSheetCore;
2766
+ var init_BottomSheetCore = __esm({
2767
+ "src/core/BottomSheetCore.ts"() {
2768
+ init_sheet_stack();
2951
2769
  init_transform();
2952
2770
  init_instance_id();
2953
2771
  init_event_bus();
2954
2772
  init_snap_points();
2955
2773
  init_cancelable_emit();
2956
- init_devWarn();
2774
+ init_dev_warn();
2957
2775
  init_snap_resolver();
2958
- init_persist();
2959
- init_auto_collapse();
2960
2776
  init_linked_sheets();
2961
- init_route();
2962
- init_content_swipe();
2963
- init_visual_viewport();
2964
2777
  init_resize_observer();
2965
2778
  init_fit_observer();
2966
2779
  init_max_height_controller();
@@ -2970,6 +2783,9 @@ var init_BottomSheetEngine = __esm({
2970
2783
  init_animation_runner();
2971
2784
  init_lifecycle_controller();
2972
2785
  init_gesture_controller();
2786
+ init_drag_zones();
2787
+ init_content_gesture();
2788
+ init_touch_scroll_guard();
2973
2789
  init_aria_slider_writer();
2974
2790
  init_hot_path_thresholds();
2975
2791
  init_scroll_cache();
@@ -2979,7 +2795,7 @@ var init_BottomSheetEngine = __esm({
2979
2795
  init_teardown_stack();
2980
2796
  init_opacity_dedup();
2981
2797
  HAPTIC_DURATION_MS = 8;
2982
- BottomSheetEngine = class {
2798
+ BottomSheetCore = class {
2983
2799
  constructor(opts) {
2984
2800
  __publicField(this, "id", nextInstanceId("bs"));
2985
2801
  __publicField(this, "element");
@@ -2991,6 +2807,8 @@ var init_BottomSheetEngine = __esm({
2991
2807
  __publicField(this, "mode");
2992
2808
  __publicField(this, "flickVelocity");
2993
2809
  __publicField(this, "dragThreshold");
2810
+ __publicField(this, "dragFromMode");
2811
+ __publicField(this, "dragFromContentDefault");
2994
2812
  __publicField(this, "rubberBandEnabled");
2995
2813
  __publicField(this, "scrim");
2996
2814
  __publicField(this, "aria");
@@ -3008,8 +2826,11 @@ var init_BottomSheetEngine = __esm({
3008
2826
  __publicField(this, "activeId");
3009
2827
  __publicField(this, "size", 0);
3010
2828
  __publicField(this, "gesture");
2829
+ __publicField(this, "contentGesture");
2830
+ __publicField(this, "detachSheetGesture", null);
3011
2831
  __publicField(this, "rootEl", null);
3012
2832
  __publicField(this, "destroyed", false);
2833
+ __publicField(this, "restClosed", false);
3013
2834
  __publicField(this, "currentViewTransition", null);
3014
2835
  __publicField(this, "currentAbort", new AbortController());
3015
2836
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -3040,6 +2861,8 @@ var init_BottomSheetEngine = __esm({
3040
2861
  __publicField(this, "stackEffectPrimed", false);
3041
2862
  __publicField(this, "fitDeps");
3042
2863
  __publicField(this, "transformTemplate");
2864
+ __publicField(this, "featureList", []);
2865
+ __publicField(this, "featureCtx");
3043
2866
  const resolved = resolveEngineOptions(opts, {
3044
2867
  shouldApplyInertSiblings: () => {
3045
2868
  if (typeof document === "undefined") return false;
@@ -3060,6 +2883,8 @@ var init_BottomSheetEngine = __esm({
3060
2883
  this.aria = new AriaSliderWriter(this.handle, this.mode);
3061
2884
  this.flickVelocity = resolved.flickVelocity;
3062
2885
  this.dragThreshold = resolved.dragThreshold;
2886
+ this.dragFromMode = resolved.dragFrom;
2887
+ this.dragFromContentDefault = resolved.dragFromContent;
3063
2888
  this.rubberBandEnabled = resolved.rubberBandEnabled;
3064
2889
  this.scrim = new ScrimController(
3065
2890
  {
@@ -3086,7 +2911,9 @@ var init_BottomSheetEngine = __esm({
3086
2911
  getRootEl: () => this.rootEl,
3087
2912
  applySize: (size) => this.applySize(size),
3088
2913
  getSize: () => this.size,
3089
- isDragging: () => this.gesture?.isDragging ?? false
2914
+ isDragging: () => this.isDraggingAny(),
2915
+ applyAux: (size) => this.applySize(size, true),
2916
+ getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
3090
2917
  },
3091
2918
  resolved.animation
3092
2919
  );
@@ -3109,6 +2936,11 @@ var init_BottomSheetEngine = __esm({
3109
2936
  if (opts.linkedSheets) {
3110
2937
  this.linkedSheets = opts.linkedSheets;
3111
2938
  }
2939
+ const byName = /* @__PURE__ */ new Map();
2940
+ for (const feature of opts.features ?? []) {
2941
+ byName.set(feature.name, feature);
2942
+ }
2943
+ this.featureList = Array.from(byName.values());
3112
2944
  this.activeId = resolved.initialId;
3113
2945
  auditVhUsage(this.snapPointsRaw);
3114
2946
  this.auditDuplicateSnapIds(this.snapPointsRaw);
@@ -3140,11 +2972,13 @@ var init_BottomSheetEngine = __esm({
3140
2972
  const initial = this.snaps.findById(this.activeId);
3141
2973
  if (initial) this.size = initial.size;
3142
2974
  this.applySize(this.size);
2975
+ this.setRestClosed(this.size === 0);
3143
2976
  this.updateAriaSlider();
3144
2977
  this.scrollCache = createScrollCache({
3145
2978
  scrollContainer: this.scrollContainer,
3146
2979
  getMaxAxisSize: () => this.snaps.getMaxAxisSize()
3147
2980
  });
2981
+ this.featureCtx = this.buildFeatureContext(opts.autoCollapseAfter);
3148
2982
  this.attach();
3149
2983
  this.registerInStack();
3150
2984
  if (this.snaps.getMaxAxisSize() === 0 && typeof IntersectionObserver !== "undefined" && typeof document !== "undefined") {
@@ -3169,60 +3003,92 @@ var init_BottomSheetEngine = __esm({
3169
3003
  (p) => p.size === "fit" || p.size === "content"
3170
3004
  ),
3171
3005
  isDestroyed: () => this.destroyed,
3172
- isDragging: () => this.gesture?.isDragging ?? false,
3006
+ isDragging: () => this.isDraggingAny(),
3173
3007
  recompute: () => this.recompute()
3174
3008
  })
3175
3009
  );
3176
3010
  if (resolved.radius !== void 0) this.setRadius(resolved.radius);
3177
3011
  if (resolved.maxHeight !== void 0) this.setMaxHeight(resolved.maxHeight);
3178
3012
  this.applyAutoAriaLabelledBy();
3179
- if (this.closeOnRouteChange) {
3180
- this.teardowns.add(
3181
- installRouteChange({
3182
- isDestroyed: () => this.destroyed,
3183
- getSize: () => this.size,
3184
- close: () => this.close()
3185
- })
3186
- );
3187
- }
3188
- if (this.persistKey) {
3189
- this.teardowns.add(installPersist(this, this.persistKey));
3013
+ this.runFeatureStage("post");
3014
+ this.warnOrphanFeatureOptions();
3015
+ if (this.size > 0) {
3016
+ this.handleOpen();
3190
3017
  }
3191
- this.teardowns.add(
3192
- installAutoCollapse({
3193
- ms: opts.autoCollapseAfter,
3194
- isDestroyed: () => this.destroyed,
3195
- isDragging: () => this.gesture?.isDragging ?? false,
3196
- getAllowedIds: () => this.snaps.getAllowedIds().slice(),
3197
- getActiveId: () => this.activeId,
3198
- resolveSnap: (id) => this.snaps.findById(id),
3199
- snapTo: (id) => {
3200
- void this.snapTo(id);
3201
- },
3202
- on: (event, fn) => this.on(event, fn)
3203
- })
3204
- );
3205
- this.teardowns.add(
3206
- installRoute({
3018
+ }
3019
+ buildFeatureContext(autoCollapseAfter) {
3020
+ return {
3021
+ element: this.element,
3022
+ scrollContainer: this.scrollContainer,
3023
+ sheetId: this.id,
3024
+ options: {
3207
3025
  routedTo: this.routedTo,
3208
3026
  closeOnBack: this.closeOnBack,
3209
- isTopSheet: () => this.isTopSheet,
3210
- getSize: () => this.size,
3211
- isDestroyed: () => this.destroyed,
3212
- close: () => this.close("back"),
3213
- on: (event, fn) => this.on(event, fn),
3214
- sheetId: this.id
3215
- })
3216
- );
3217
- if (this.size > 0) {
3218
- this.handleOpen();
3027
+ closeOnRouteChange: this.closeOnRouteChange,
3028
+ persistKey: this.persistKey,
3029
+ autoCollapseAfter
3030
+ },
3031
+ isDestroyed: () => this.destroyed,
3032
+ isDragging: () => this.isDraggingAny(),
3033
+ isAnimating: () => this.animation.isAnimating,
3034
+ isTopSheet: () => this.isTopSheet,
3035
+ isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
3036
+ getSize: () => this.size,
3037
+ getActiveId: () => this.activeId,
3038
+ getAllowedIds: () => this.snaps.getAllowedIds().slice(),
3039
+ allowedIdsBySize: () => this.allowedIdsBySize(),
3040
+ getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3041
+ resolveSnap: (id) => this.snaps.findById(id),
3042
+ resolveActiveSnap: () => this.snaps.findById(this.activeId),
3043
+ setSize: (size) => {
3044
+ this.size = size;
3045
+ },
3046
+ setMaxAxisSize: (size) => {
3047
+ this.snaps.setMaxAxisSize(size);
3048
+ },
3049
+ applySize: (size) => this.applySize(size),
3050
+ recomputeSnaps: () => this.recomputeSnaps(),
3051
+ newCycle: () => {
3052
+ this.newCycle();
3053
+ },
3054
+ cancelInFlight: () => {
3055
+ this.animation.cancel();
3056
+ },
3057
+ resyncAfterResize: () => this.resyncAfterResize(),
3058
+ snapTo: (id) => {
3059
+ void this.snapTo(id);
3060
+ },
3061
+ close: (reason) => this.close(reason),
3062
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
3063
+ on: (event, fn) => this.on(event, fn),
3064
+ addTeardown: (fn) => this.teardowns.add(fn)
3065
+ };
3066
+ }
3067
+ runFeatureStage(stage) {
3068
+ for (const feature of this.featureList) {
3069
+ if ((feature.stage ?? "post") !== stage) continue;
3070
+ const teardown = feature.install(this.featureCtx);
3071
+ if (teardown) this.teardowns.add(teardown);
3072
+ }
3073
+ }
3074
+ warnOrphanFeatureOptions() {
3075
+ const names = new Set(this.featureList.map((f) => f.name));
3076
+ if ((this.closeOnBack || this.routedTo !== void 0 || this.closeOnRouteChange) && !names.has("route")) {
3077
+ devWarn(
3078
+ "[BottomSheet] closeOnBack/routedTo/closeOnRouteChange need routeFeature() \u2014 option ignored"
3079
+ );
3080
+ }
3081
+ if (this.persistKey && !names.has("persist")) {
3082
+ devWarn(
3083
+ "[BottomSheet] persistKey needs persistFeature() \u2014 snaps will not be saved"
3084
+ );
3219
3085
  }
3220
3086
  }
3221
3087
  get state() {
3222
3088
  return {
3223
3089
  size: this.size,
3224
3090
  activeId: this.activeId,
3225
- isDragging: this.gesture?.isDragging ?? false,
3091
+ isDragging: this.isDraggingAny(),
3226
3092
  isAnimating: this.animation.isAnimating,
3227
3093
  progress: this.computeProgress(this.size)
3228
3094
  };
@@ -3342,9 +3208,16 @@ var init_BottomSheetEngine = __esm({
3342
3208
  progress: this.computeProgress(this.size)
3343
3209
  });
3344
3210
  if (withHaptic) this.haptic();
3211
+ this.setRestClosed(targetSize === 0);
3345
3212
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
3346
3213
  if (targetSize === 0) this.emitCloseSequence();
3347
3214
  }
3215
+ setRestClosed(closed) {
3216
+ if (closed === this.restClosed) return;
3217
+ this.restClosed = closed;
3218
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3219
+ else this.element.removeAttribute("data-bs-rest");
3220
+ }
3348
3221
  emitOpenSequence(id) {
3349
3222
  this.emit("open", { id });
3350
3223
  this.handleOpen();
@@ -3400,6 +3273,20 @@ var init_BottomSheetEngine = __esm({
3400
3273
  if (this.destroyed) return;
3401
3274
  this.disableDragFlag = value;
3402
3275
  }
3276
+ setDragFromContent(value) {
3277
+ if (this.destroyed) return;
3278
+ this.dragFromContentDefault = value;
3279
+ }
3280
+ setDragFrom(mode) {
3281
+ if (this.destroyed || mode === this.dragFromMode) return;
3282
+ this.gesture?.forceClearDragState();
3283
+ this.detachSheetGesture?.();
3284
+ this.dragFromMode = mode;
3285
+ this.mountSheetGesture();
3286
+ }
3287
+ getDragFrom() {
3288
+ return this.dragFromMode;
3289
+ }
3403
3290
  isTop() {
3404
3291
  return this.isTopSheet;
3405
3292
  }
@@ -3554,12 +3441,24 @@ var init_BottomSheetEngine = __esm({
3554
3441
  auditVhUsage(points);
3555
3442
  this.auditDuplicateSnapIds(points);
3556
3443
  this.snaps.setRaw(points);
3557
- if (allowed) this.snaps.setAllowedIds(allowed);
3444
+ if (allowed) {
3445
+ this.snaps.setAllowedIds(allowed);
3446
+ } else {
3447
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3448
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3449
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3450
+ }
3558
3451
  this.scrollCache.clear();
3559
3452
  this.scrim.invalidateOpacityCache();
3560
3453
  const current = this.snaps.findById(this.activeId);
3561
3454
  if (current) {
3562
3455
  this.applySize(current.size);
3456
+ } else {
3457
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3458
+ if (fallback) {
3459
+ this.activeId = fallback.id;
3460
+ this.applySize(fallback.size);
3461
+ }
3563
3462
  }
3564
3463
  this.updateAriaSlider();
3565
3464
  }
@@ -3569,7 +3468,7 @@ var init_BottomSheetEngine = __esm({
3569
3468
  this.snaps.recompute();
3570
3469
  this.maxHeight.clampTo();
3571
3470
  this.scrim.invalidateOpacityCache();
3572
- if (this.gesture?.isDragging) return;
3471
+ if (this.isDraggingAny()) return;
3573
3472
  this.healActiveSnap();
3574
3473
  }
3575
3474
  healActiveSnap() {
@@ -3591,6 +3490,8 @@ var init_BottomSheetEngine = __esm({
3591
3490
  this.bus.clear();
3592
3491
  this.animation.cancel();
3593
3492
  this.gesture?.forceClearDragState();
3493
+ this.contentGesture?.forceClearDragState();
3494
+ this.setRestClosed(false);
3594
3495
  this.teardowns.drain();
3595
3496
  this.scrim.destroy();
3596
3497
  this.animation.destroy();
@@ -3703,11 +3604,12 @@ var init_BottomSheetEngine = __esm({
3703
3604
  const scale = Math.max(1 - depth * 0.04, 0.86);
3704
3605
  this.element.style.scale = scale === 1 ? "" : String(scale);
3705
3606
  }
3706
- attach() {
3707
- this.gesture = new GestureController({
3708
- handle: this.handle,
3607
+ buildGestureDeps(surface, gestureOptions) {
3608
+ return {
3609
+ handle: surface,
3709
3610
  element: this.element,
3710
3611
  mode: this.mode,
3612
+ gestureOptions,
3711
3613
  getRoot: () => this.rootEl,
3712
3614
  getDragContext: () => {
3713
3615
  const buf = this.dragContextBuf;
@@ -3726,8 +3628,88 @@ var init_BottomSheetEngine = __esm({
3726
3628
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3727
3629
  emit: (event, payload) => this.emit(event, payload),
3728
3630
  listenerCount: (event) => this.bus.listenerCount(event)
3631
+ };
3632
+ }
3633
+ isContentDragAllowed() {
3634
+ if (this.disableDragFlag) return false;
3635
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3636
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3637
+ }
3638
+ decideContentDrag(surface, e, delta) {
3639
+ if (this.destroyed) return false;
3640
+ if (e.pointerType === "mouse") return false;
3641
+ if (this.gesture?.isDragging) return false;
3642
+ if (!this.isContentDragAllowed()) return false;
3643
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3644
+ const decision = decideContentGesture({
3645
+ delta,
3646
+ scrollTop: surface.scrollTop,
3647
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3648
+ });
3649
+ if (decision === "pending") return null;
3650
+ return decision === "drag";
3651
+ }
3652
+ attachDragSurface(surface, kind) {
3653
+ if (this.destroyed || kind !== "content") return;
3654
+ if (this.contentGesture) return;
3655
+ const controller = new GestureController(
3656
+ this.buildGestureDeps(surface, {
3657
+ manageTouchAction: false,
3658
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3659
+ })
3660
+ );
3661
+ this.contentGesture = controller;
3662
+ const detachGestures = controller.install();
3663
+ const detachGuard = installTouchScrollGuard(
3664
+ surface,
3665
+ () => controller.isDragging
3666
+ );
3667
+ return () => {
3668
+ detachGuard();
3669
+ detachGestures();
3670
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3671
+ };
3672
+ }
3673
+ sheetDragSurface() {
3674
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3675
+ }
3676
+ canStartSheetDrag(e) {
3677
+ if (this.contentGesture?.isDragging) return false;
3678
+ const target = e.target;
3679
+ const scoped = typeof target?.closest === "function";
3680
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3681
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3682
+ return true;
3683
+ }
3684
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3685
+ if (this.dragFromMode === "handle") return true;
3686
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3687
+ return false;
3688
+ }
3689
+ return true;
3690
+ }
3691
+ mountSheetGesture() {
3692
+ const surface = this.sheetDragSurface();
3693
+ const controller = new GestureController(
3694
+ this.buildGestureDeps(surface, {
3695
+ shouldStart: (e) => this.canStartSheetDrag(e),
3696
+ manageTouchAction: this.dragFromMode === "handle"
3697
+ })
3698
+ );
3699
+ this.gesture = controller;
3700
+ const detachGestures = controller.install();
3701
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3702
+ this.detachSheetGesture = () => {
3703
+ detachGuard?.();
3704
+ detachGestures();
3705
+ };
3706
+ }
3707
+ attach() {
3708
+ this.mountSheetGesture();
3709
+ this.teardowns.add(() => {
3710
+ this.detachSheetGesture?.();
3711
+ this.detachSheetGesture = null;
3729
3712
  });
3730
- this.teardowns.add(this.gesture.install());
3731
3713
  try {
3732
3714
  this.attachFeatures();
3733
3715
  } catch (err) {
@@ -3758,24 +3740,13 @@ var init_BottomSheetEngine = __esm({
3758
3740
  }
3759
3741
  }
3760
3742
  attachFeatures() {
3761
- if (this.scrollContainer) {
3762
- this.teardowns.add(installContentSwipe({
3763
- container: this.scrollContainer,
3764
- isDragging: () => this.gesture?.isDragging ?? false,
3765
- isAnimating: () => this.animation.isAnimating,
3766
- getAllowedIds: () => this.allowedIdsBySize(),
3767
- getActiveId: () => this.activeId,
3768
- snapTo: (id) => {
3769
- void this.snapTo(id);
3770
- }
3771
- }));
3772
- }
3743
+ this.runFeatureStage("attach");
3773
3744
  if (typeof window !== "undefined") {
3774
3745
  this.teardowns.add(installResizeObserver({
3775
3746
  element: this.element,
3776
3747
  getMode: () => this.mode,
3777
3748
  isDestroyed: () => this.destroyed,
3778
- isDragging: () => this.gesture?.isDragging ?? false,
3749
+ isDragging: () => this.isDraggingAny(),
3779
3750
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3780
3751
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3781
3752
  getSize: () => this.size,
@@ -3809,31 +3780,6 @@ var init_BottomSheetEngine = __esm({
3809
3780
  () => document.removeEventListener("keydown", onKey)
3810
3781
  );
3811
3782
  }
3812
- this.teardowns.add(installVisualViewport({
3813
- element: this.element,
3814
- isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
3815
- isDestroyed: () => this.destroyed,
3816
- isDragging: () => this.gesture?.isDragging ?? false,
3817
- recomputeSnaps: () => this.recomputeSnaps(),
3818
- resolveActiveSnap: () => this.snaps.findById(this.activeId),
3819
- getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3820
- getSize: () => this.size,
3821
- setMaxAxisSize: (size) => {
3822
- this.snaps.setMaxAxisSize(size);
3823
- },
3824
- setSize: (size) => {
3825
- this.size = size;
3826
- },
3827
- applySize: (size) => this.applySize(size),
3828
- cancelInFlight: () => {
3829
- this.animation.cancel();
3830
- },
3831
- newCycle: () => {
3832
- this.newCycle();
3833
- },
3834
- isAnimating: () => this.animation.isAnimating,
3835
- resyncAfterCancel: () => this.resyncAfterResize()
3836
- }));
3837
3783
  }
3838
3784
  this.teardowns.add(installSliderKeyboard({
3839
3785
  handle: this.handle,
@@ -3898,99 +3844,616 @@ var init_BottomSheetEngine = __esm({
3898
3844
  );
3899
3845
  });
3900
3846
  }
3847
+ isDraggingAny() {
3848
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3849
+ }
3901
3850
  clearWillChangeIfIdle() {
3902
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3851
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3903
3852
  this.element.style.willChange = "";
3904
3853
  }
3905
- haptic() {
3906
- if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3907
- try {
3908
- navigator.vibrate(HAPTIC_DURATION_MS);
3909
- } catch {
3910
- }
3911
- }
3854
+ haptic() {
3855
+ if (typeof navigator !== "undefined" && "vibrate" in navigator) {
3856
+ try {
3857
+ navigator.vibrate(HAPTIC_DURATION_MS);
3858
+ } catch {
3859
+ }
3860
+ }
3861
+ }
3862
+ applySize(size, skipTransform = false) {
3863
+ const cap = this.snaps.getMaxAxisSize();
3864
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3865
+ this.size = clamped;
3866
+ if (clamped > 0) this.setRestClosed(false);
3867
+ const offset = cap - clamped;
3868
+ const style = this.element.style;
3869
+ if (!skipTransform) style.transform = this.transformTemplate(offset);
3870
+ if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3871
+ style.setProperty("--bs-size", `${clamped}px`);
3872
+ if (this.scrimParent) {
3873
+ this.scrimParent.style.setProperty("--bs-size", `${clamped}px`);
3874
+ }
3875
+ if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3876
+ this.anchorHost.style.setProperty("--bs-size", `${clamped}px`);
3877
+ }
3878
+ if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3879
+ this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3880
+ }
3881
+ }
3882
+ const progress = this.computeProgress(clamped);
3883
+ const progressChanged = this.progressWriteSentinel.shouldWrite(
3884
+ progress,
3885
+ OPACITY_WRITE_EPSILON
3886
+ );
3887
+ if (progressChanged) {
3888
+ style.setProperty("--bs-progress", String(progress));
3889
+ if (this.anchorHost) {
3890
+ this.anchorHost.style.setProperty("--bs-progress", String(progress));
3891
+ }
3892
+ if (this.rootEl && this.rootEl !== this.anchorHost) {
3893
+ this.rootEl.style.setProperty("--bs-progress", String(progress));
3894
+ }
3895
+ }
3896
+ this.scrim.applyOpacity(progress, progressChanged);
3897
+ if (progressChanged && this.bus.listenerCount("progress") > 0) {
3898
+ this.progressPayload.value = progress;
3899
+ this.progressPayload.size = clamped;
3900
+ this.emit("progress", this.progressPayload);
3901
+ }
3902
+ }
3903
+ computeProgress(size) {
3904
+ const { min, max } = this.getAllowedRange();
3905
+ const reachableMax = Math.min(max, this.snaps.getMaxAxisSize());
3906
+ if (reachableMax <= min) return 0;
3907
+ return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3908
+ }
3909
+ handleOpen() {
3910
+ sheetStack.promote(this.id);
3911
+ try {
3912
+ this.lifecycle.install();
3913
+ } catch (err) {
3914
+ if (!this.destroyed) this.newCycle();
3915
+ this.handleClose();
3916
+ this.applySize(0);
3917
+ this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3918
+ this.size = 0;
3919
+ queueMicrotask(() => {
3920
+ throw err;
3921
+ });
3922
+ }
3923
+ }
3924
+ resyncAfterResize() {
3925
+ if (this.destroyed) return;
3926
+ const target = this.snaps.findById(this.activeId);
3927
+ if (!target) return;
3928
+ this.updateAriaSlider();
3929
+ this.emit("snap", {
3930
+ id: target.id,
3931
+ size: this.size,
3932
+ progress: this.computeProgress(this.size)
3933
+ });
3934
+ if (target.size > 0 && !this.lifecycle.isInstalled) {
3935
+ this.emitOpenSequence(target.id);
3936
+ } else if (target.size === 0 && this.lifecycle.isInstalled) {
3937
+ this.emitCloseSequence();
3938
+ }
3939
+ }
3940
+ handleClose() {
3941
+ this.opening = false;
3942
+ this.lifecycle.release();
3943
+ sheetStack.update();
3944
+ }
3945
+ };
3946
+ }
3947
+ });
3948
+
3949
+ // src/core/features/history-coordinator.ts
3950
+ function onPopState() {
3951
+ if (suppress > 0) {
3952
+ suppress -= 1;
3953
+ const rebrand = pendingRebrands.shift();
3954
+ if (rebrand) rebrand();
3955
+ maybeTeardownListener();
3956
+ return;
3957
+ }
3958
+ if (entries.length === 0) {
3959
+ notifyRouteChange();
3960
+ maybeTeardownListener();
3961
+ return;
3962
+ }
3963
+ let target = null;
3964
+ let targetIdx = -1;
3965
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
3966
+ const entry = entries[i];
3967
+ if (!entry.surface.isOpen()) continue;
3968
+ if (!target) {
3969
+ target = entry;
3970
+ targetIdx = i;
3971
+ }
3972
+ if (entry.surface.isTop()) {
3973
+ target = entry;
3974
+ targetIdx = i;
3975
+ break;
3976
+ }
3977
+ }
3978
+ if (!target) {
3979
+ const removed = entries.pop();
3980
+ if (removed) removed.live = false;
3981
+ maybeTeardownListener();
3982
+ return;
3983
+ }
3984
+ target.live = false;
3985
+ entries.splice(targetIdx, 1);
3986
+ const settled = target;
3987
+ void Promise.resolve(settled.surface.close()).then(() => {
3988
+ if (settled.surface.isOpen()) restore(settled);
3989
+ });
3990
+ maybeTeardownListener();
3991
+ }
3992
+ function restore(entry) {
3993
+ if (!hasWindow() || !hasHistory()) return;
3994
+ entry.live = true;
3995
+ runInternal(() => {
3996
+ try {
3997
+ history.pushState(brandMarker(entry.surface), "", entry.surface.url);
3998
+ } catch {
3999
+ entry.live = false;
4000
+ }
4001
+ });
4002
+ if (entry.live) {
4003
+ entries.push(entry);
4004
+ ensureListener();
4005
+ }
4006
+ }
4007
+ function pushBackMarker(surface) {
4008
+ const entry = { surface, priorUrl: void 0, live: false };
4009
+ if (!hasWindow() || !hasHistory()) {
4010
+ return entry;
4011
+ }
4012
+ entry.priorUrl = surface.url !== void 0 ? location.href : void 0;
4013
+ runInternal(() => {
4014
+ try {
4015
+ history.pushState(brandMarker(surface), "", surface.url);
4016
+ entry.live = true;
4017
+ } catch {
4018
+ entry.live = false;
4019
+ }
4020
+ });
4021
+ if (entry.live) {
4022
+ entries.push(entry);
4023
+ ensureListener();
4024
+ }
4025
+ return entry;
4026
+ }
4027
+ function popBackMarker(handle) {
4028
+ if (!hasWindow() || !hasHistory()) return;
4029
+ const entry = handle;
4030
+ const idx = entries.indexOf(entry);
4031
+ if (idx === -1 || !entry.live) return;
4032
+ const last = entries.length - 1;
4033
+ entry.live = false;
4034
+ entries.splice(idx, 1);
4035
+ let rebrand = null;
4036
+ if (idx !== last) {
4037
+ const rebrandSurface = entries[entries.length - 1].surface;
4038
+ const rebrandUrl = entry.priorUrl;
4039
+ rebrand = () => {
4040
+ runInternal(() => {
4041
+ history.replaceState(
4042
+ brandMarker(rebrandSurface),
4043
+ "",
4044
+ rebrandUrl !== void 0 ? rebrandUrl : location.href
4045
+ );
4046
+ });
4047
+ };
4048
+ }
4049
+ pendingRebrands.push(rebrand);
4050
+ suppress += 1;
4051
+ runInternal(() => {
4052
+ try {
4053
+ history.back();
4054
+ } catch {
4055
+ suppress = Math.max(0, suppress - 1);
4056
+ pendingRebrands.pop();
4057
+ }
4058
+ });
4059
+ maybeTeardownListener();
4060
+ }
4061
+ function subscribeRouteChange(fn) {
4062
+ if (!hasWindow() || !hasHistory()) return () => {
4063
+ };
4064
+ subscribers.add(fn);
4065
+ if (subscribers.size === 1) installPatch();
4066
+ ensureListener();
4067
+ return () => {
4068
+ if (!subscribers.has(fn)) return;
4069
+ subscribers.delete(fn);
4070
+ if (subscribers.size === 0) removePatch();
4071
+ maybeTeardownListener();
4072
+ };
4073
+ }
4074
+ var entries, subscribers, suppress, internalDepth, pendingRebrands, listenerInstalled, patchInstalled, origPushState, origReplaceState, patchedPushState, patchedReplaceState, hasWindow, hasHistory, brandMarker, runInternal, notifyRouteChange, ensureListener, maybeTeardownListener, installPatch, removePatch;
4075
+ var init_history_coordinator = __esm({
4076
+ "src/core/features/history-coordinator.ts"() {
4077
+ entries = [];
4078
+ subscribers = /* @__PURE__ */ new Set();
4079
+ suppress = 0;
4080
+ internalDepth = 0;
4081
+ pendingRebrands = [];
4082
+ listenerInstalled = false;
4083
+ patchInstalled = false;
4084
+ origPushState = null;
4085
+ origReplaceState = null;
4086
+ patchedPushState = null;
4087
+ patchedReplaceState = null;
4088
+ hasWindow = () => typeof window !== "undefined";
4089
+ hasHistory = () => typeof history !== "undefined";
4090
+ brandMarker = (surface) => ({
4091
+ ...surface.markerState(),
4092
+ __bs: true
4093
+ });
4094
+ runInternal = (fn) => {
4095
+ internalDepth += 1;
4096
+ try {
4097
+ fn();
4098
+ } finally {
4099
+ internalDepth -= 1;
4100
+ }
4101
+ };
4102
+ notifyRouteChange = () => {
4103
+ const snapshot = Array.from(subscribers);
4104
+ for (const fn of snapshot) fn();
4105
+ };
4106
+ ensureListener = () => {
4107
+ if (listenerInstalled || !hasWindow()) return;
4108
+ window.addEventListener("popstate", onPopState);
4109
+ listenerInstalled = true;
4110
+ };
4111
+ maybeTeardownListener = () => {
4112
+ if (!listenerInstalled) return;
4113
+ if (entries.length > 0 || subscribers.size > 0 || suppress > 0) return;
4114
+ if (hasWindow()) window.removeEventListener("popstate", onPopState);
4115
+ listenerInstalled = false;
4116
+ };
4117
+ installPatch = () => {
4118
+ if (patchInstalled || !hasHistory()) return;
4119
+ origPushState = history.pushState;
4120
+ origReplaceState = history.replaceState;
4121
+ patchedPushState = function(...args) {
4122
+ origPushState.apply(this, args);
4123
+ if (internalDepth === 0) notifyRouteChange();
4124
+ };
4125
+ patchedReplaceState = function(...args) {
4126
+ origReplaceState.apply(this, args);
4127
+ if (internalDepth === 0) notifyRouteChange();
4128
+ };
4129
+ history.pushState = patchedPushState;
4130
+ history.replaceState = patchedReplaceState;
4131
+ patchInstalled = true;
4132
+ };
4133
+ removePatch = () => {
4134
+ if (!patchInstalled) return;
4135
+ if (history.pushState === patchedPushState && origPushState) {
4136
+ history.pushState = origPushState;
4137
+ }
4138
+ if (history.replaceState === patchedReplaceState && origReplaceState) {
4139
+ history.replaceState = origReplaceState;
4140
+ }
4141
+ origPushState = null;
4142
+ origReplaceState = null;
4143
+ patchedPushState = null;
4144
+ patchedReplaceState = null;
4145
+ patchInstalled = false;
4146
+ };
4147
+ }
4148
+ });
4149
+
4150
+ // src/core/features/route.ts
4151
+ function installRoute(deps) {
4152
+ if (typeof window === "undefined") {
4153
+ return () => {
4154
+ };
4155
+ }
4156
+ if (!deps.routedTo && !deps.closeOnBack) {
4157
+ return () => {
4158
+ };
4159
+ }
4160
+ let handle = null;
4161
+ const onOpen = () => {
4162
+ if (handle) return;
4163
+ handle = pushBackMarker({
4164
+ isOpen: () => !deps.isDestroyed() && deps.getSize() > 0,
4165
+ isTop: deps.isTopSheet,
4166
+ close: deps.close,
4167
+ markerState: () => deps.routedTo !== void 0 ? { __bsRouted: deps.sheetId } : { __bsSheet: deps.sheetId },
4168
+ url: deps.routedTo
4169
+ });
4170
+ };
4171
+ const onClose = () => {
4172
+ if (!handle) return;
4173
+ popBackMarker(handle);
4174
+ handle = null;
4175
+ };
4176
+ const unsubscribeOpen = deps.on("open", onOpen);
4177
+ const unsubscribeClose = deps.on("close", onClose);
4178
+ if (deps.getSize() > 0) onOpen();
4179
+ return () => {
4180
+ unsubscribeOpen();
4181
+ unsubscribeClose();
4182
+ if (handle) {
4183
+ popBackMarker(handle);
4184
+ handle = null;
4185
+ }
4186
+ };
4187
+ }
4188
+ function installRouteChange(deps) {
4189
+ return subscribeRouteChange(() => {
4190
+ if (deps.isDestroyed() || deps.getSize() <= 0) return;
4191
+ void deps.close();
4192
+ });
4193
+ }
4194
+ var init_route = __esm({
4195
+ "src/core/features/route.ts"() {
4196
+ init_history_coordinator();
4197
+ }
4198
+ });
4199
+
4200
+ // src/core/features/persist.ts
4201
+ function installPersist(engine, key) {
4202
+ const unsubscribe = engine.on("snap", ({ id }) => {
4203
+ if (typeof window === "undefined") return;
4204
+ if (typeof localStorage === "undefined") return;
4205
+ try {
4206
+ localStorage.setItem(key, id);
4207
+ } catch {
4208
+ }
4209
+ });
4210
+ return unsubscribe;
4211
+ }
4212
+ var init_persist = __esm({
4213
+ "src/core/features/persist.ts"() {
4214
+ }
4215
+ });
4216
+
4217
+ // src/core/features/auto-collapse.ts
4218
+ function installAutoCollapse(deps) {
4219
+ const { ms } = deps;
4220
+ if (ms === void 0 || ms <= 0) {
4221
+ return () => {
4222
+ };
4223
+ }
4224
+ let timer = null;
4225
+ const clearTimer = () => {
4226
+ if (timer !== null) {
4227
+ clearTimeout(timer);
4228
+ timer = null;
4229
+ }
4230
+ };
4231
+ const fire = () => {
4232
+ if (deps.isDestroyed()) return;
4233
+ if (deps.isDragging()) return;
4234
+ const active = deps.resolveSnap(deps.getActiveId());
4235
+ if (!active || active.size === 0) return;
4236
+ let target = null;
4237
+ for (const id of deps.getAllowedIds()) {
4238
+ const snap = deps.resolveSnap(id);
4239
+ if (!snap || snap.size <= 0) continue;
4240
+ if (!target || snap.size < target.size) target = snap;
4241
+ }
4242
+ if (!target) return;
4243
+ if (target.id === active.id || target.size >= active.size) return;
4244
+ deps.snapTo(target.id);
4245
+ };
4246
+ const reset = () => {
4247
+ clearTimer();
4248
+ if (deps.isDestroyed()) return;
4249
+ timer = setTimeout(() => {
4250
+ timer = null;
4251
+ fire();
4252
+ }, ms);
4253
+ };
4254
+ const unsubscribe = deps.on("snap", () => reset());
4255
+ if ((deps.resolveSnap(deps.getActiveId())?.size ?? 0) > 0) reset();
4256
+ return () => {
4257
+ clearTimer();
4258
+ unsubscribe();
4259
+ };
4260
+ }
4261
+ var init_auto_collapse = __esm({
4262
+ "src/core/features/auto-collapse.ts"() {
4263
+ }
4264
+ });
4265
+
4266
+ // src/core/features/content-drag.ts
4267
+ function installContentDrag(deps) {
4268
+ const detach = deps.attachDragSurface(deps.container, "content");
4269
+ return () => detach?.();
4270
+ }
4271
+ var init_content_drag = __esm({
4272
+ "src/core/features/content-drag.ts"() {
4273
+ }
4274
+ });
4275
+
4276
+ // src/core/features/visual-viewport.ts
4277
+ function installVisualViewport(deps) {
4278
+ if (typeof window === "undefined" || !window.visualViewport) {
4279
+ return () => {
4280
+ };
4281
+ }
4282
+ const vv = window.visualViewport;
4283
+ let rafId = null;
4284
+ const onResize = () => {
4285
+ if (rafId !== null) return;
4286
+ rafId = requestAnimationFrame(() => {
4287
+ rafId = null;
4288
+ if (deps.isDestroyed()) return;
4289
+ deps.recomputeSnaps();
4290
+ const isVertical = deps.isVerticalAxis();
4291
+ const visible = isVertical ? vv.height : vv.width;
4292
+ const windowSize = isVertical ? window.innerHeight : window.innerWidth;
4293
+ const clamp = Math.max(0, Math.min(windowSize, visible) - SAFETY_PAD);
4294
+ if (clamp < deps.getMaxAxisSize()) {
4295
+ deps.setMaxAxisSize(clamp);
4296
+ }
4297
+ const wasAnimating = deps.isAnimating?.() ?? false;
4298
+ deps.cancelInFlight();
4299
+ deps.newCycle();
4300
+ const max = deps.getMaxAxisSize();
4301
+ deps.setSize(Math.min(deps.getSize(), max));
4302
+ const current = deps.resolveActiveSnap();
4303
+ if (current && !deps.isDragging()) {
4304
+ deps.applySize(Math.min(current.size, max));
4305
+ if (wasAnimating) deps.resyncAfterCancel?.();
4306
+ } else {
4307
+ deps.applySize(deps.getSize());
3912
4308
  }
3913
- applySize(size) {
3914
- const cap = this.snaps.getMaxAxisSize();
3915
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3916
- this.size = clamped;
3917
- const offset = cap - clamped;
3918
- const style = this.element.style;
3919
- style.transform = this.transformTemplate(offset);
3920
- if (this.sizeWriteSentinel.shouldWrite(clamped, SIZE_WRITE_EPSILON)) {
3921
- style.setProperty("--bs-size", `${clamped}px`);
3922
- if (this.scrimParent) {
3923
- this.scrimParent.style.setProperty("--bs-size", `${clamped}px`);
3924
- }
3925
- if (this.anchorHost && this.anchorHost !== this.scrimParent) {
3926
- this.anchorHost.style.setProperty("--bs-size", `${clamped}px`);
3927
- }
3928
- if (this.rootEl && this.rootEl !== this.scrimParent && this.rootEl !== this.anchorHost) {
3929
- this.rootEl.style.setProperty("--bs-size", `${clamped}px`);
3930
- }
3931
- }
3932
- const progress = this.computeProgress(clamped);
3933
- const progressChanged = this.progressWriteSentinel.shouldWrite(
3934
- progress,
3935
- OPACITY_WRITE_EPSILON
4309
+ });
4310
+ };
4311
+ vv.addEventListener("resize", onResize);
4312
+ return () => {
4313
+ vv.removeEventListener("resize", onResize);
4314
+ if (rafId !== null) cancelAnimationFrame(rafId);
4315
+ };
4316
+ }
4317
+ var SAFETY_PAD;
4318
+ var init_visual_viewport = __esm({
4319
+ "src/core/features/visual-viewport.ts"() {
4320
+ SAFETY_PAD = 8;
4321
+ }
4322
+ });
4323
+
4324
+ // src/core/features/engine-features.ts
4325
+ function routeFeature() {
4326
+ return {
4327
+ name: "route",
4328
+ install: (ctx) => {
4329
+ const teardowns = new TeardownStack();
4330
+ if (ctx.options.closeOnRouteChange) {
4331
+ teardowns.add(
4332
+ installRouteChange({
4333
+ isDestroyed: ctx.isDestroyed,
4334
+ getSize: ctx.getSize,
4335
+ close: () => ctx.close()
4336
+ })
3936
4337
  );
3937
- if (progressChanged) {
3938
- style.setProperty("--bs-progress", String(progress));
3939
- if (this.anchorHost) {
3940
- this.anchorHost.style.setProperty("--bs-progress", String(progress));
3941
- }
3942
- if (this.rootEl && this.rootEl !== this.anchorHost) {
3943
- this.rootEl.style.setProperty("--bs-progress", String(progress));
3944
- }
3945
- }
3946
- this.scrim.applyOpacity(progress, progressChanged);
3947
- if (progressChanged && this.bus.listenerCount("progress") > 0) {
3948
- this.progressPayload.value = progress;
3949
- this.progressPayload.size = clamped;
3950
- this.emit("progress", this.progressPayload);
3951
- }
3952
- }
3953
- computeProgress(size) {
3954
- const { min, max } = this.getAllowedRange();
3955
- const reachableMax = Math.min(max, this.snaps.getMaxAxisSize());
3956
- if (reachableMax <= min) return 0;
3957
- return Math.min(Math.max((size - min) / (reachableMax - min), 0), 1);
3958
- }
3959
- handleOpen() {
3960
- sheetStack.promote(this.id);
3961
- try {
3962
- this.lifecycle.install();
3963
- } catch (err) {
3964
- if (!this.destroyed) this.newCycle();
3965
- this.handleClose();
3966
- this.applySize(0);
3967
- this.activeId = this.snapPointsRaw.find((p) => p.id === "closed")?.id ?? this.activeId;
3968
- this.size = 0;
3969
- queueMicrotask(() => {
3970
- throw err;
3971
- });
3972
- }
3973
4338
  }
3974
- resyncAfterResize() {
3975
- if (this.destroyed) return;
3976
- const target = this.snaps.findById(this.activeId);
3977
- if (!target) return;
3978
- this.updateAriaSlider();
3979
- this.emit("snap", {
3980
- id: target.id,
3981
- size: this.size,
3982
- progress: this.computeProgress(this.size)
4339
+ teardowns.add(
4340
+ installRoute({
4341
+ routedTo: ctx.options.routedTo,
4342
+ closeOnBack: ctx.options.closeOnBack,
4343
+ isTopSheet: ctx.isTopSheet,
4344
+ getSize: ctx.getSize,
4345
+ isDestroyed: ctx.isDestroyed,
4346
+ close: () => ctx.close("back"),
4347
+ on: ctx.on,
4348
+ sheetId: ctx.sheetId
4349
+ })
4350
+ );
4351
+ return () => teardowns.drain();
4352
+ }
4353
+ };
4354
+ }
4355
+ function persistFeature() {
4356
+ return {
4357
+ name: "persist",
4358
+ install: (ctx) => {
4359
+ const key = ctx.options.persistKey;
4360
+ if (!key) return;
4361
+ return installPersist({ on: ctx.on }, key);
4362
+ }
4363
+ };
4364
+ }
4365
+ function autoCollapseFeature() {
4366
+ return {
4367
+ name: "auto-collapse",
4368
+ install: (ctx) => installAutoCollapse({
4369
+ ms: ctx.options.autoCollapseAfter,
4370
+ isDestroyed: ctx.isDestroyed,
4371
+ isDragging: ctx.isDragging,
4372
+ getAllowedIds: ctx.getAllowedIds,
4373
+ getActiveId: ctx.getActiveId,
4374
+ resolveSnap: ctx.resolveSnap,
4375
+ snapTo: ctx.snapTo,
4376
+ on: ctx.on
4377
+ })
4378
+ };
4379
+ }
4380
+ function contentSwipeFeature() {
4381
+ return {
4382
+ name: "content-swipe",
4383
+ stage: "attach",
4384
+ install: (ctx) => {
4385
+ if (!ctx.scrollContainer) return;
4386
+ return installContentDrag({
4387
+ container: ctx.scrollContainer,
4388
+ attachDragSurface: ctx.attachDragSurface
4389
+ });
4390
+ }
4391
+ };
4392
+ }
4393
+ function visualViewportFeature() {
4394
+ return {
4395
+ name: "visual-viewport",
4396
+ install: (ctx) => {
4397
+ if (typeof window === "undefined") return;
4398
+ return installVisualViewport({
4399
+ element: ctx.element,
4400
+ isVerticalAxis: ctx.isVerticalAxis,
4401
+ isDestroyed: ctx.isDestroyed,
4402
+ isDragging: ctx.isDragging,
4403
+ recomputeSnaps: ctx.recomputeSnaps,
4404
+ resolveActiveSnap: ctx.resolveActiveSnap,
4405
+ getMaxAxisSize: ctx.getMaxAxisSize,
4406
+ getSize: ctx.getSize,
4407
+ setMaxAxisSize: ctx.setMaxAxisSize,
4408
+ setSize: ctx.setSize,
4409
+ applySize: ctx.applySize,
4410
+ cancelInFlight: ctx.cancelInFlight,
4411
+ newCycle: ctx.newCycle,
4412
+ isAnimating: ctx.isAnimating,
4413
+ resyncAfterCancel: ctx.resyncAfterResize
4414
+ });
4415
+ }
4416
+ };
4417
+ }
4418
+ var init_engine_features = __esm({
4419
+ "src/core/features/engine-features.ts"() {
4420
+ init_route();
4421
+ init_persist();
4422
+ init_auto_collapse();
4423
+ init_content_drag();
4424
+ init_visual_viewport();
4425
+ init_teardown_stack();
4426
+ }
4427
+ });
4428
+
4429
+ // src/core/default-features.ts
4430
+ function defaultEngineFeatures() {
4431
+ return [
4432
+ contentSwipeFeature(),
4433
+ visualViewportFeature(),
4434
+ persistFeature(),
4435
+ autoCollapseFeature(),
4436
+ routeFeature()
4437
+ ];
4438
+ }
4439
+ var init_default_features = __esm({
4440
+ "src/core/default-features.ts"() {
4441
+ init_engine_features();
4442
+ }
4443
+ });
4444
+
4445
+ // src/core/BottomSheetEngine.ts
4446
+ var BottomSheetEngine;
4447
+ var init_BottomSheetEngine = __esm({
4448
+ "src/core/BottomSheetEngine.ts"() {
4449
+ init_BottomSheetCore();
4450
+ init_default_features();
4451
+ BottomSheetEngine = class extends BottomSheetCore {
4452
+ constructor(opts) {
4453
+ super({
4454
+ ...opts,
4455
+ features: [...defaultEngineFeatures(), ...opts.features ?? []]
3983
4456
  });
3984
- if (target.size > 0 && !this.lifecycle.isInstalled) {
3985
- this.emitOpenSequence(target.id);
3986
- } else if (target.size === 0 && this.lifecycle.isInstalled) {
3987
- this.emitCloseSequence();
3988
- }
3989
- }
3990
- handleClose() {
3991
- this.opening = false;
3992
- this.lifecycle.release();
3993
- sheetStack.update();
3994
4457
  }
3995
4458
  };
3996
4459
  }
@@ -4000,7 +4463,7 @@ var init_BottomSheetEngine = __esm({
4000
4463
  var baseStyles;
4001
4464
  var init_baseStyles = __esm({
4002
4465
  "src/web-component/baseStyles.ts"() {
4003
- baseStyles = `:where(.bs-root){--bs-surface-d:#ffffff;--bs-surface-border-d:rgba(225,227,237,1);--bs-radius-d:20px;--bs-handle-color-d:rgba(26,22,20,0.3);--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-backdrop-color-d:rgba(0,0,0,0.5);--bs-scrim-color-d:transparent;--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color,var(--bs-backdrop-color-d));opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.24s cubic-bezier(0.22,1,0.36,1);will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface,var(--bs-surface-d));box-shadow:var(--bs-shadow,var(--bs-shadow-auto,none));z-index:var(--bs-z-sheet);pointer-events:auto;overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius,var(--bs-radius-d));border-top-right-radius:var(--bs-radius,var(--bs-radius-d));border-top:1px solid var(--bs-surface-border,var(--bs-surface-border-d));padding-bottom:env(safe-area-inset-bottom);--bs-shadow-auto:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba(0,0,0,calc(var(--bs-progress,0) * 0.15 + 0.12))}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-right-radius:var(--bs-radius,var(--bs-radius-d));border-bottom:1px solid var(--bs-surface-border,var(--bs-surface-border-d));flex-direction:column-reverse;--bs-shadow-auto:0 3px 12px rgba(0,0,0,0.12);padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-right-radius:var(--bs-radius,var(--bs-radius-d));border-right:1px solid var(--bs-surface-border,var(--bs-surface-border-d));--bs-shadow-auto:3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-left-radius:var(--bs-radius,var(--bs-radius-d));border-left:1px solid var(--bs-surface-border,var(--bs-surface-border-d));--bs-shadow-auto:-3px 0 12px rgba(0,0,0,0.12)}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:var(--bs-handle-padding,8px 16px 4px);cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface,var(--bs-surface-d));-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius,var(--bs-radius-d))}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color,var(--bs-handle-color-d));border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface,var(--bs-surface-d));z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border,var(--bs-surface-border-d))}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border,var(--bs-surface-border-d))}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color,var(--bs-handle-color-d)),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color,var(--bs-handle-color-d)),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color,var(--bs-handle-color-d)) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-header{flex:0 0 auto;min-height:var(--bs-header-min-height,0px);padding:var(--bs-header-padding,0 16px 8px);background:var(--bs-surface,var(--bs-surface-d))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;user-select:text;padding:var(--bs-content-padding,0 16px 16px)}.bs-footer{flex:0 0 auto;padding:var(--bs-footer-padding,12px 16px calc(12px + env(safe-area-inset-bottom)));border-top:1px solid var(--bs-surface-border,var(--bs-surface-border-d));background:var(--bs-surface,var(--bs-surface-d))}.bs-sheet[data-mode="bottom"]:has(.bs-footer:not([hidden]):has(*)){padding-bottom:0}.bs-header:not(:has(*)),.bs-footer:not(:has(*)),.bs-header[hidden],.bs-footer[hidden]{display:none}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim);background:var(--bs-scrim-color,var(--bs-scrim-color-d));backdrop-filter:blur(var(--bs-scrim-blur,0px));-webkit-backdrop-filter:blur(var(--bs-scrim-blur,0px));transition:opacity 0.24s cubic-bezier(0.22,1,0.36,1);will-change:opacity}.bs-root[data-dragging="true"] .bs-screen,.bs-root[data-animating="true"] .bs-screen{transition:none}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-button-slot{position:fixed;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-mode="top"]{bottom:auto;top:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-mode="left"]{left:calc(var(--bs-size,0px) + 12px);right:auto;bottom:auto;top:16px}.bs-button-slot[data-mode="right"]{right:calc(var(--bs-size,0px) + 12px);left:auto;bottom:auto;top:16px}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface-d:#1a1a1d;--bs-surface-border-d:rgba(255,255,255,0.08);--bs-handle-color-d:rgba(255,255,255,0.45);--bs-shadow-auto:0 -3px 16px rgba(0,0,0,0.5)}}@media (forced-colors:active){.bs-sheet{border:1px solid CanvasText}.bs-handle::before{background:CanvasText;forced-color-adjust:none}.bs-backdrop,.bs-screen{forced-color-adjust:none}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,var(--bs-surface-d,#ffffff));--ovl-surface-border:var(--bs-surface-border,var(--bs-surface-border-d,rgba(0,0,0,0.08)));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,var(--bs-radius-d,20px));--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
4466
+ baseStyles = `:where(.bs-root){--bs-surface-d:#ffffff;--bs-surface-border-d:rgba(225,227,237,1);--bs-radius-d:20px;--bs-handle-color-d:rgba(26,22,20,0.3);--bs-handle-width:40px;--bs-handle-height:5px;--bs-z-backdrop:90;--bs-z-sheet:100;--bs-backdrop-color-d:rgba(0,0,0,0.5);--bs-scrim-color-d:transparent;--bs-scrim-blur:0px;--bs-z-scrim:calc(var(--bs-z-sheet) - 1);--bs-size:0px;--bs-progress:0}.bs-backdrop{position:fixed;inset:0;background:var(--bs-backdrop-color,var(--bs-backdrop-color-d));opacity:0;pointer-events:none;z-index:var(--bs-z-backdrop);transition:opacity 0.24s cubic-bezier(0.22,1,0.36,1);will-change:opacity}.bs-root[data-dragging="true"] .bs-backdrop,.bs-root[data-animating="true"] .bs-backdrop{transition:none}.bs-sheet{position:fixed;background:var(--bs-surface,var(--bs-surface-d));box-shadow:var(--bs-shadow,var(--bs-shadow-auto,none));--bs-shadow-fade:clamp(0,calc(var(--bs-progress,0) * 8),1);z-index:var(--bs-z-sheet);pointer-events:auto;overflow:hidden;display:flex;flex-direction:column;-webkit-tap-highlight-color:transparent;user-select:none;overscroll-behavior:contain}.bs-sheet[data-mode="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--bs-radius,var(--bs-radius-d));border-top-right-radius:var(--bs-radius,var(--bs-radius-d));border-top:1px solid var(--bs-surface-border,var(--bs-surface-border-d));padding-bottom:env(safe-area-inset-bottom);--bs-shadow-auto:0 calc(var(--bs-progress,0) * -2px - 3px) calc(var(--bs-progress,0) * 18px + 12px) rgba( 0,0,0,calc(var(--bs-shadow-fade) * (var(--bs-progress,0) * 0.15 + 0.12)) )}.bs-sheet[data-mode="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-right-radius:var(--bs-radius,var(--bs-radius-d));border-bottom:1px solid var(--bs-surface-border,var(--bs-surface-border-d));flex-direction:column-reverse;--bs-shadow-auto:0 3px 12px rgba(0,0,0,calc(var(--bs-shadow-fade) * 0.12));padding-top:env(safe-area-inset-top)}.bs-sheet[data-mode="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-right-radius:var(--bs-radius,var(--bs-radius-d));border-right:1px solid var(--bs-surface-border,var(--bs-surface-border-d));--bs-shadow-auto:3px 0 12px rgba(0,0,0,calc(var(--bs-shadow-fade) * 0.12))}.bs-sheet[data-mode="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--bs-radius,var(--bs-radius-d));border-bottom-left-radius:var(--bs-radius,var(--bs-radius-d));border-left:1px solid var(--bs-surface-border,var(--bs-surface-border-d));--bs-shadow-auto:-3px 0 12px rgba(0,0,0,calc(var(--bs-shadow-fade) * 0.12))}.bs-sheet[data-bs-rest="closed"]{box-shadow:none;visibility:hidden}.bs-sheet[data-bs-webgl="on"],.bs-sheet[data-bs-webgl="on"] .bs-handle,.bs-sheet[data-bs-webgl="on"] .bs-header,.bs-sheet[data-bs-webgl="on"] .bs-footer{background:transparent;box-shadow:none}.bs-sheet[data-mode="left"] .bs-content{padding-right:36px}.bs-sheet[data-mode="right"] .bs-content{padding-left:36px}.bs-handle{flex-shrink:0;min-height:44px;display:flex;flex-direction:column;justify-content:center;padding:var(--bs-handle-padding,8px 16px 4px);cursor:grab;touch-action:none;user-select:none;background:var(--bs-surface,var(--bs-surface-d));-webkit-tap-highlight-color:transparent;outline:none;position:relative}.bs-handle:active{cursor:grabbing}.bs-handle:focus-visible{outline:2px solid currentColor;outline-offset:-2px;border-radius:var(--bs-radius,var(--bs-radius-d))}.bs-handle::before{content:"";display:block;width:var(--bs-handle-width);height:var(--bs-handle-height);background:var(--bs-handle-color,var(--bs-handle-color-d));border-radius:calc(var(--bs-handle-height) / 2);margin:0 auto 8px;transition:width 0.15s ease,background 0.15s ease,transform 0.15s ease;pointer-events:none}.bs-sheet[data-mode="left"] .bs-handle,.bs-sheet[data-mode="right"] .bs-handle{position:absolute;top:0;bottom:0;width:28px;min-width:0;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:0;background:var(--bs-surface,var(--bs-surface-d));z-index:1;overflow:hidden}.bs-sheet[data-mode="left"] .bs-handle>*,.bs-sheet[data-mode="right"] .bs-handle>*{display:none}.bs-sheet[data-mode="left"] .bs-handle{right:0;border-left:1px solid var(--bs-surface-border,var(--bs-surface-border-d))}.bs-sheet[data-mode="right"] .bs-handle{left:0;border-right:1px solid var(--bs-surface-border,var(--bs-surface-border-d))}.bs-sheet[data-mode="left"] .bs-handle::before,.bs-sheet[data-mode="right"] .bs-handle::before{width:var(--bs-handle-height);height:var(--bs-handle-width);margin:0}.bs-sheet:hover .bs-handle::before{background:color-mix(in oklab,var(--bs-handle-color,var(--bs-handle-color-d)),#000 15%)}.bs-handle:active::before{background:color-mix(in oklab,var(--bs-handle-color,var(--bs-handle-color-d)),#000 35%);transform:scaleX(1.15)}.bs-handle.bs-handle--ticks::after{content:"";display:block;position:absolute;left:50%;bottom:6px;transform:translateX(-50%);width:calc(var(--bs-handle-width) * 2);height:3px;background-image:radial-gradient( circle,var(--bs-handle-color,var(--bs-handle-color-d)) 0 1.5px,transparent 1.6px );background-size:8px 3px;background-repeat:repeat-x;background-position:center;opacity:0;transition:opacity 0.18s ease-out;pointer-events:none}.bs-root[data-dragging="true"] .bs-handle.bs-handle--ticks::after{opacity:0.4}.bs-sheet[data-mode="top"] .bs-handle.bs-handle--ticks::after{bottom:auto;top:6px}.bs-sheet[data-mode="left"] .bs-handle.bs-handle--ticks::after,.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{left:50%;top:50%;bottom:auto;transform:translate(-50%,-50%);width:3px;height:calc(var(--bs-handle-width) * 2);background-size:3px 8px;background-repeat:repeat-y;margin-left:calc(var(--bs-handle-height) + 6px)}.bs-sheet[data-mode="right"] .bs-handle.bs-handle--ticks::after{margin-left:calc(-1 * (var(--bs-handle-height) + 6px))}.bs-header{flex:0 0 auto;min-height:var(--bs-header-min-height,0px);padding:var(--bs-header-padding,0 16px 8px);background:var(--bs-surface,var(--bs-surface-d))}.bs-content{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;user-select:text;padding:var(--bs-content-padding,0 16px 16px)}.bs-footer{flex:0 0 auto;padding:var(--bs-footer-padding,12px 16px calc(12px + env(safe-area-inset-bottom)));border-top:1px solid var(--bs-surface-border,var(--bs-surface-border-d));background:var(--bs-surface,var(--bs-surface-d))}.bs-sheet[data-mode="bottom"]:has(.bs-footer:not([hidden]):has(*)){padding-bottom:0}.bs-header:not(:has(*)),.bs-footer:not(:has(*)),.bs-header[hidden],.bs-footer[hidden]{display:none}.bs-screen{position:absolute;inset:0;pointer-events:none;z-index:var(--bs-z-scrim);background:var(--bs-scrim-color,var(--bs-scrim-color-d));backdrop-filter:blur(var(--bs-scrim-blur,0px));-webkit-backdrop-filter:blur(var(--bs-scrim-blur,0px));transition:opacity 0.24s cubic-bezier(0.22,1,0.36,1);will-change:opacity}.bs-root[data-dragging="true"] .bs-screen,.bs-root[data-animating="true"] .bs-screen{transition:none}.bs-scrim-overlay{z-index:calc(var(--bs-z-sheet) + 1);pointer-events:none}.bs-button-slot{position:fixed;z-index:calc(var(--bs-z-sheet) + 1);pointer-events:auto}.bs-button-slot[data-side="left"]{left:16px;bottom:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-side="right"]{right:16px;bottom:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-mode="top"]{bottom:auto;top:calc(var(--bs-size,0px) + 12px)}.bs-button-slot[data-mode="left"]{left:calc(var(--bs-size,0px) + 12px);right:auto;bottom:auto;top:16px}.bs-button-slot[data-mode="right"]{right:calc(var(--bs-size,0px) + 12px);left:auto;bottom:auto;top:16px}@media (prefers-reduced-motion:reduce){.bs-backdrop,.bs-handle::before,.bs-button-slot[data-side="left"],.bs-button-slot[data-side="right"]{transition:none !important}}@media (prefers-color-scheme:dark){:where(.bs-root){--bs-surface-d:#1a1a1d;--bs-surface-border-d:rgba(255,255,255,0.08);--bs-handle-color-d:rgba(255,255,255,0.45);--bs-shadow-auto:0 -3px 16px rgba(0,0,0,0.5)}}@media (forced-colors:active){.bs-sheet{border:1px solid CanvasText}.bs-handle::before{background:CanvasText;forced-color-adjust:none}.bs-backdrop,.bs-screen{forced-color-adjust:none}}.bs-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}:where(.ovl-root){--ovl-surface:var(--bs-surface,var(--bs-surface-d,#ffffff));--ovl-surface-border:var(--bs-surface-border,var(--bs-surface-border-d,rgba(0,0,0,0.08)));--ovl-shadow:0 -8px 40px rgba(0,0,0,0.35);--ovl-radius:var(--bs-radius,var(--bs-radius-d,20px));--ovl-backdrop-color:rgba(0,0,0,0.5);--ovl-z-backdrop:200;--ovl-z-panel:201}.ovl-backdrop{position:fixed;inset:0;background:var(--ovl-backdrop-color);opacity:0;pointer-events:none;z-index:var(--ovl-z-backdrop)}.ovl-panel{position:fixed;background:var(--ovl-surface);box-shadow:var(--ovl-shadow);z-index:var(--ovl-z-panel);-webkit-tap-highlight-color:transparent;outline:none;transition:transform 0.32s cubic-bezier(0.29,1.24,0.84,0.99),opacity 0.32s ease-out}.ovl-panel[data-state="open"]{transform:translate3d(0,0,0);opacity:1}@starting-style{.ovl-panel[data-edge="bottom"][data-state="open"]{transform:translate3d(0,100%,0);opacity:0}.ovl-panel[data-edge="top"][data-state="open"]{transform:translate3d(0,-100%,0);opacity:0}.ovl-panel[data-edge="left"][data-state="open"]{transform:translate3d(-100%,0,0);opacity:0}.ovl-panel[data-edge="right"][data-state="open"]{transform:translate3d(100%,0,0);opacity:0}}.ovl-panel[data-edge="bottom"]{left:0;right:0;bottom:0;width:100%;border-top-left-radius:var(--ovl-radius);border-top-right-radius:var(--ovl-radius);border-top:1px solid var(--ovl-surface-border);padding-bottom:env(safe-area-inset-bottom)}.ovl-panel[data-edge="top"]{left:0;right:0;top:0;width:100%;border-bottom-left-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-bottom:1px solid var(--ovl-surface-border);padding-top:env(safe-area-inset-top)}.ovl-panel[data-edge="left"]{top:0;bottom:0;left:0;height:100%;border-top-right-radius:var(--ovl-radius);border-bottom-right-radius:var(--ovl-radius);border-right:1px solid var(--ovl-surface-border)}.ovl-panel[data-edge="right"]{top:0;bottom:0;right:0;height:100%;border-top-left-radius:var(--ovl-radius);border-bottom-left-radius:var(--ovl-radius);border-left:1px solid var(--ovl-surface-border)}@media (prefers-reduced-motion:reduce){.ovl-panel,.ovl-backdrop{transition:none !important}}`;
4004
4467
  }
4005
4468
  });
4006
4469
 
@@ -4097,7 +4560,7 @@ var init_shadow_tree = __esm({
4097
4560
  });
4098
4561
 
4099
4562
  // src/web-component/attributes.ts
4100
- var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, ATTR_PERSISTENT, ATTR_DISABLE_CLOSE, ATTR_DISABLE_DRAG, ATTR_CLOSE_ON_ROUTE_CHANGE, ATTR_RADIUS, ATTR_MAX_HEIGHT, ATTR_RETURN_FOCUS_TO, ATTR_BACKDROP_COLOR, ATTR_SCRIM_COLOR, ATTR_SNAP, LIVE_ATTRS, OBSERVED_ATTRS, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, parseList, parseDimension, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions;
4563
+ var ATTR_SNAP_POINTS, ATTR_ALLOWED, ATTR_INITIAL, ATTR_MODE, ATTR_BACKDROP, ATTR_STYLESHEET, ATTR_ANIMATION, ATTR_FOCUS_TRAP, ATTR_CLOSE_ON_ESCAPE, ATTR_LOCK_BODY_SCROLL, ATTR_SHEET_LABEL, ATTR_STACK_EFFECT, ATTR_PERSISTENT, ATTR_DISABLE_CLOSE, ATTR_DISABLE_DRAG, ATTR_DRAG_FROM, ATTR_DRAG_FROM_CONTENT, ATTR_CLOSE_ON_ROUTE_CHANGE, ATTR_RADIUS, ATTR_MAX_HEIGHT, ATTR_RETURN_FOCUS_TO, ATTR_BACKDROP_COLOR, ATTR_SCRIM_COLOR, ATTR_SNAP, LIVE_ATTRS, OBSERVED_ATTRS, isValidSnapPoint, DEFAULT_SNAP_POINTS, parseSnapPoints, VALID_MODES, VALID_ANIMATIONS, parseMode, parseAnimation, VALID_DRAG_FROM, parseDragFrom, parseList, parseDimension, ANCHOR_POSITIONS, ANCHOR_ANIMATIONS, parseAnchorOptions;
4101
4564
  var init_attributes = __esm({
4102
4565
  "src/web-component/attributes.ts"() {
4103
4566
  ATTR_SNAP_POINTS = "snap-points";
@@ -4115,6 +4578,8 @@ var init_attributes = __esm({
4115
4578
  ATTR_PERSISTENT = "persistent";
4116
4579
  ATTR_DISABLE_CLOSE = "disable-close";
4117
4580
  ATTR_DISABLE_DRAG = "disable-drag";
4581
+ ATTR_DRAG_FROM = "drag-from";
4582
+ ATTR_DRAG_FROM_CONTENT = "drag-from-content";
4118
4583
  ATTR_CLOSE_ON_ROUTE_CHANGE = "close-on-route-change";
4119
4584
  ATTR_RADIUS = "radius";
4120
4585
  ATTR_MAX_HEIGHT = "max-height";
@@ -4130,7 +4595,9 @@ var init_attributes = __esm({
4130
4595
  ATTR_SNAP,
4131
4596
  ATTR_PERSISTENT,
4132
4597
  ATTR_DISABLE_CLOSE,
4133
- ATTR_DISABLE_DRAG
4598
+ ATTR_DISABLE_DRAG,
4599
+ ATTR_DRAG_FROM,
4600
+ ATTR_DRAG_FROM_CONTENT
4134
4601
  ]);
4135
4602
  OBSERVED_ATTRS = [
4136
4603
  ATTR_SNAP_POINTS,
@@ -4147,6 +4614,8 @@ var init_attributes = __esm({
4147
4614
  ATTR_PERSISTENT,
4148
4615
  ATTR_DISABLE_CLOSE,
4149
4616
  ATTR_DISABLE_DRAG,
4617
+ ATTR_DRAG_FROM,
4618
+ ATTR_DRAG_FROM_CONTENT,
4150
4619
  ATTR_CLOSE_ON_ROUTE_CHANGE,
4151
4620
  ATTR_RADIUS,
4152
4621
  ATTR_MAX_HEIGHT,
@@ -4183,6 +4652,12 @@ var init_attributes = __esm({
4183
4652
  VALID_ANIMATIONS = ["spring", "tween", "ios-spring", "material-bounce", "linear", "snappy"];
4184
4653
  parseMode = (raw) => raw && VALID_MODES.includes(raw) ? raw : "bottom";
4185
4654
  parseAnimation = (raw) => raw && VALID_ANIMATIONS.includes(raw) ? raw : void 0;
4655
+ VALID_DRAG_FROM = [
4656
+ "handle",
4657
+ "sheet",
4658
+ "zones"
4659
+ ];
4660
+ parseDragFrom = (raw) => raw && VALID_DRAG_FROM.includes(raw) ? raw : void 0;
4186
4661
  parseList = (raw) => raw ? raw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
4187
4662
  parseDimension = (raw) => {
4188
4663
  if (raw === null) return void 0;
@@ -4331,6 +4806,14 @@ var init_BottomSheetElement = __esm({
4331
4806
  case ATTR_DISABLE_DRAG:
4332
4807
  this.engine.setDisableDrag(value === "true");
4333
4808
  break;
4809
+ case ATTR_DRAG_FROM_CONTENT:
4810
+ this.engine.setDragFromContent(value !== "false");
4811
+ break;
4812
+ case ATTR_DRAG_FROM: {
4813
+ const mode = parseDragFrom(value);
4814
+ if (mode) this.engine.setDragFrom(mode);
4815
+ break;
4816
+ }
4334
4817
  case ATTR_SNAP:
4335
4818
  if (value && value !== this.engine.state.activeId) {
4336
4819
  void this.engine.snapTo(value);
@@ -4401,6 +4884,8 @@ var init_BottomSheetElement = __esm({
4401
4884
  persistent: this.getAttribute(ATTR_PERSISTENT) === "true",
4402
4885
  disableClose: this.getAttribute(ATTR_DISABLE_CLOSE) === "true",
4403
4886
  disableDrag: this.getAttribute(ATTR_DISABLE_DRAG) === "true",
4887
+ dragFrom: parseDragFrom(this.getAttribute(ATTR_DRAG_FROM)),
4888
+ dragFromContent: this.getAttribute(ATTR_DRAG_FROM_CONTENT) === null ? void 0 : this.getAttribute(ATTR_DRAG_FROM_CONTENT) !== "false",
4404
4889
  closeOnRouteChange: this.getAttribute(ATTR_CLOSE_ON_ROUTE_CHANGE) === "true",
4405
4890
  radius: parseDimension(this.getAttribute(ATTR_RADIUS)),
4406
4891
  maxHeight: parseDimension(this.getAttribute(ATTR_MAX_HEIGHT)),