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