@surdeddd/bottom-sheet 0.13.0 → 0.20.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 +46 -0
  2. package/dist/{BottomSheetCore-D16lsa6g.d.ts → BottomSheetCore-Cgn8b_6A.d.ts} +36 -2
  3. package/dist/{BottomSheetCore-CsIjyq7C.d.cts → BottomSheetCore-VPJZcKcg.d.cts} +36 -2
  4. package/dist/{BottomSheetEngine-BYjAVrmd.d.cts → BottomSheetEngine-B8C2CwF9.d.cts} +1 -1
  5. package/dist/{BottomSheetEngine-YuCNugZn.d.ts → BottomSheetEngine-DwYR_ygJ.d.ts} +1 -1
  6. package/dist/core.cjs +328 -33
  7. package/dist/core.d.cts +2 -2
  8. package/dist/core.d.ts +2 -2
  9. package/dist/core.js +328 -33
  10. package/dist/debug.d.cts +1 -1
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/element.cjs +404 -108
  13. package/dist/element.d.cts +2 -2
  14. package/dist/element.d.ts +2 -2
  15. package/dist/element.iife.global.js +2 -2
  16. package/dist/element.js +404 -108
  17. package/dist/features.cjs +6 -50
  18. package/dist/features.d.cts +4 -4
  19. package/dist/features.d.ts +4 -4
  20. package/dist/features.js +6 -50
  21. package/dist/index-CnfbOKx0.d.cts +9 -0
  22. package/dist/index-DcMkhXAy.d.ts +9 -0
  23. package/dist/index.cjs +335 -829
  24. package/dist/index.d.cts +44 -47
  25. package/dist/index.d.ts +44 -47
  26. package/dist/index.js +336 -816
  27. package/dist/integrations/formik.d.cts +1 -1
  28. package/dist/integrations/formik.d.ts +1 -1
  29. package/dist/integrations/react-hook-form.d.cts +1 -1
  30. package/dist/integrations/react-hook-form.d.ts +1 -1
  31. package/dist/overlay.cjs +5 -5
  32. package/dist/overlay.js +5 -5
  33. package/dist/preact.d.cts +2 -2
  34. package/dist/preact.d.ts +2 -2
  35. package/dist/qwik-lib/index.qwik.cjs +323 -83
  36. package/dist/qwik-lib/index.qwik.mjs +323 -83
  37. package/dist/qwik.cjs +342 -83
  38. package/dist/qwik.d.cts +4 -2
  39. package/dist/qwik.d.ts +4 -2
  40. package/dist/qwik.js +342 -83
  41. package/dist/react.cjs +347 -85
  42. package/dist/react.d.cts +3 -4
  43. package/dist/react.d.ts +3 -4
  44. package/dist/react.js +347 -85
  45. package/dist/solid.cjs +348 -83
  46. package/dist/solid.d.cts +37 -1
  47. package/dist/solid.d.ts +37 -1
  48. package/dist/solid.js +348 -83
  49. package/dist/solid.source.jsx +356 -83
  50. package/dist/styles.css +24 -4
  51. package/dist/svelte-core.cjs +334 -83
  52. package/dist/svelte-core.js +334 -83
  53. package/dist/svelte-src/BottomSheet.svelte +14 -0
  54. package/dist/svelte.cjs +344 -83
  55. package/dist/svelte.d.cts +2 -0
  56. package/dist/svelte.d.ts +2 -0
  57. package/dist/svelte.js +344 -83
  58. package/dist/vue.cjs +350 -83
  59. package/dist/vue.js +350 -83
  60. package/dist/webgl.cjs +673 -0
  61. package/dist/webgl.d.cts +17 -0
  62. package/dist/webgl.d.ts +17 -0
  63. package/dist/webgl.js +671 -0
  64. package/package.json +64 -40
  65. package/dist/index-DdYLOIIT.d.ts +0 -15
  66. package/dist/index-dfrzw-lI.d.cts +0 -15
package/dist/svelte.cjs CHANGED
@@ -28,7 +28,7 @@ var __defProp = Object.defineProperty;
28
28
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
29
29
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
30
30
 
31
- // src/core/lifecycle/sheetStack.ts
31
+ // src/core/lifecycle/sheet-stack.ts
32
32
  var BASE_Z = 100;
33
33
  var STEP = 10;
34
34
  var SheetStack = class {
@@ -153,7 +153,7 @@ function createEventBus() {
153
153
  };
154
154
  }
155
155
 
156
- // src/core/primitives/cssLength.ts
156
+ // src/core/primitives/css-length.ts
157
157
  var probe = null;
158
158
  var PROBE_ATTR = "data-bs-probe";
159
159
  var ensureProbe = (axis) => {
@@ -207,7 +207,7 @@ var resolveSnap = (point, mode, measureFit) => {
207
207
  return Math.max(0, el.getBoundingClientRect().width);
208
208
  };
209
209
 
210
- // src/core/primitives/devWarn.ts
210
+ // src/core/primitives/dev-warn.ts
211
211
  function devWarn(...args) {
212
212
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
213
213
  console.warn(...args);
@@ -339,6 +339,11 @@ var SnapResolver = class {
339
339
  this.raw = raw;
340
340
  this.recompute();
341
341
  }
342
+ setMode(mode) {
343
+ if (mode === this.mode) return;
344
+ this.mode = mode;
345
+ this.recompute();
346
+ }
342
347
  setAllowedIds(ids) {
343
348
  this.allowedIds = ids;
344
349
  this.rangeCache = null;
@@ -922,6 +927,11 @@ var ScrimController = class {
922
927
  if (this.isDestroyed()) return;
923
928
  this.applyScrimStyles({ interactive });
924
929
  }
930
+ setSheetMode(mode) {
931
+ if (this.isDestroyed() || mode === this.mode) return;
932
+ this.mode = mode;
933
+ this.invalidateOpacityCache();
934
+ }
925
935
  setScrimMode(mode, applySize) {
926
936
  if (this.isDestroyed()) return;
927
937
  if (mode === this.scrimMode) return;
@@ -1540,7 +1550,7 @@ var AnimationRunner = class {
1540
1550
  }
1541
1551
  };
1542
1552
 
1543
- // src/core/lifecycle/focusTrap.ts
1553
+ // src/core/lifecycle/focus-trap.ts
1544
1554
  var FOCUSABLE = [
1545
1555
  "a[href]",
1546
1556
  "area[href]",
@@ -1658,7 +1668,7 @@ var installFocusTrap = (container, options = {}) => {
1658
1668
  };
1659
1669
  };
1660
1670
 
1661
- // src/core/lifecycle/scrollLock.ts
1671
+ // src/core/lifecycle/scroll-lock.ts
1662
1672
  var lockCount = 0;
1663
1673
  var savedStyles = null;
1664
1674
  var lockBodyScroll = () => {
@@ -1840,30 +1850,47 @@ var SampleRing = class {
1840
1850
  this.size = 0;
1841
1851
  }
1842
1852
  };
1843
- var installGestures = (handle, mode, callbacks) => {
1853
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1844
1854
  let activePointerId = null;
1845
1855
  let activePointerType = "touch";
1846
1856
  let startCoord = 0;
1857
+ let startCross = 0;
1847
1858
  let lastCoord = 0;
1859
+ let pendingStart = false;
1848
1860
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1849
1861
  const MAX_SAMPLES = 32;
1850
1862
  const samples = new SampleRing(MAX_SAMPLES);
1851
1863
  const axis = isAxisVertical(mode) ? "Y" : "X";
1852
1864
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1853
1865
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1866
+ const crossOf = (e) => axis === "Y" ? e.clientX : e.clientY;
1867
+ const capture = (pointerId) => {
1868
+ try {
1869
+ handle.setPointerCapture(pointerId);
1870
+ } catch {
1871
+ }
1872
+ };
1873
+ const releaseTracking = () => {
1874
+ activePointerId = null;
1875
+ pendingStart = false;
1876
+ samples.reset();
1877
+ };
1854
1878
  const onPointerDown = (e) => {
1855
1879
  if (activePointerId !== null) return;
1856
1880
  if (e.button !== void 0 && e.button !== 0) return;
1881
+ if (options.shouldStart && !options.shouldStart(e)) return;
1857
1882
  activePointerId = e.pointerId;
1858
1883
  activePointerType = e.pointerType || "touch";
1859
1884
  startCoord = coordOf(e);
1885
+ startCross = crossOf(e);
1860
1886
  lastCoord = startCoord;
1861
1887
  samples.reset();
1862
1888
  samples.push(e.timeStamp, startCoord);
1863
- try {
1864
- handle.setPointerCapture(e.pointerId);
1865
- } catch {
1889
+ if (options.deferStart) {
1890
+ pendingStart = true;
1891
+ return;
1866
1892
  }
1893
+ capture(e.pointerId);
1867
1894
  callbacks.onStart(startCoord, activePointerType);
1868
1895
  };
1869
1896
  const onPointerMove = (e) => {
@@ -1871,6 +1898,22 @@ var installGestures = (handle, mode, callbacks) => {
1871
1898
  const coord = coordOf(e);
1872
1899
  const rawDelta = coord - startCoord;
1873
1900
  const delta = rawDelta * sign;
1901
+ if (pendingStart) {
1902
+ const verdict = options.deferStart(e, delta, crossOf(e) - startCross);
1903
+ if (verdict === false) {
1904
+ releaseTracking();
1905
+ return;
1906
+ }
1907
+ if (verdict !== true) return;
1908
+ pendingStart = false;
1909
+ startCoord = coord;
1910
+ lastCoord = coord;
1911
+ samples.reset();
1912
+ samples.push(e.timeStamp, coord);
1913
+ capture(e.pointerId);
1914
+ callbacks.onStart(startCoord, activePointerType);
1915
+ return;
1916
+ }
1874
1917
  lastCoord = coord;
1875
1918
  samples.push(e.timeStamp, coord);
1876
1919
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1879,6 +1922,10 @@ var installGestures = (handle, mode, callbacks) => {
1879
1922
  };
1880
1923
  const finishGesture = (e) => {
1881
1924
  if (e.pointerId !== activePointerId) return;
1925
+ if (pendingStart) {
1926
+ releaseTracking();
1927
+ return;
1928
+ }
1882
1929
  const rawDelta = lastCoord - startCoord;
1883
1930
  const delta = rawDelta * sign;
1884
1931
  let velocity = 0;
@@ -1901,22 +1948,26 @@ var installGestures = (handle, mode, callbacks) => {
1901
1948
  };
1902
1949
  const onPointerCancel = (e) => {
1903
1950
  if (e.pointerId !== activePointerId) return;
1904
- activePointerId = null;
1905
- samples.reset();
1951
+ const wasPending = pendingStart;
1952
+ releaseTracking();
1953
+ if (wasPending) return;
1906
1954
  callbacks.onCancel?.();
1907
1955
  };
1908
1956
  handle.addEventListener("pointerdown", onPointerDown);
1909
1957
  handle.addEventListener("pointermove", onPointerMove);
1910
1958
  handle.addEventListener("pointerup", finishGesture);
1911
1959
  handle.addEventListener("pointercancel", onPointerCancel);
1960
+ const managesTouchAction = options.manageTouchAction !== false;
1912
1961
  const prevTouchAction = handle.style.touchAction;
1913
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1962
+ if (managesTouchAction) {
1963
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1964
+ }
1914
1965
  return () => {
1915
1966
  handle.removeEventListener("pointerdown", onPointerDown);
1916
1967
  handle.removeEventListener("pointermove", onPointerMove);
1917
1968
  handle.removeEventListener("pointerup", finishGesture);
1918
1969
  handle.removeEventListener("pointercancel", onPointerCancel);
1919
- handle.style.touchAction = prevTouchAction;
1970
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1920
1971
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1921
1972
  handle.releasePointerCapture(activePointerId);
1922
1973
  }
@@ -2035,7 +2086,7 @@ var GestureController = class {
2035
2086
  const restoreTo = this.dragStartSize;
2036
2087
  void this.deps.animateTo(restoreTo, 0);
2037
2088
  }
2038
- });
2089
+ }, this.deps.gestureOptions);
2039
2090
  return this.detach;
2040
2091
  }
2041
2092
  forceClearDragState() {
@@ -2047,6 +2098,74 @@ var GestureController = class {
2047
2098
  }
2048
2099
  };
2049
2100
 
2101
+ // src/core/primitives/drag-zones.ts
2102
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2103
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2104
+ var asElement = (target) => {
2105
+ const candidate = target;
2106
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2107
+ };
2108
+ function isDragAllowedFrom(target, mode) {
2109
+ const el = asElement(target);
2110
+ if (!el) return mode !== "zones";
2111
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2112
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2113
+ return true;
2114
+ }
2115
+
2116
+ // src/core/primitives/content-gesture.ts
2117
+ var CONTENT_DRAG_SLOP = 6;
2118
+ function decideContentGesture(input) {
2119
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2120
+ const cross = input.crossDelta ?? 0;
2121
+ const sharesScrollAxis = input.sharesScrollAxis ?? true;
2122
+ if (!sharesScrollAxis) {
2123
+ if (Math.abs(input.delta) < slop && Math.abs(cross) < slop) return "pending";
2124
+ if (Math.abs(input.delta) <= Math.abs(cross)) return "scroll";
2125
+ return "drag";
2126
+ }
2127
+ if (Math.abs(input.delta) < slop) return "pending";
2128
+ if (input.scrollTop > 0) return "scroll";
2129
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2130
+ return "drag";
2131
+ }
2132
+
2133
+ // src/core/primitives/logical-mode.ts
2134
+ var isLogicalMode = (mode) => mode === "start" || mode === "end";
2135
+ var readDirection = (el) => {
2136
+ if (!el) return "ltr";
2137
+ if (typeof getComputedStyle === "function") {
2138
+ try {
2139
+ if (getComputedStyle(el).direction === "rtl") return "rtl";
2140
+ } catch {
2141
+ }
2142
+ }
2143
+ try {
2144
+ const marked = el.closest?.("[dir]");
2145
+ if (marked) {
2146
+ return marked.getAttribute("dir")?.toLowerCase() === "rtl" ? "rtl" : "ltr";
2147
+ }
2148
+ } catch {
2149
+ }
2150
+ return "ltr";
2151
+ };
2152
+ var resolveMode = (mode, el) => {
2153
+ if (!isLogicalMode(mode)) return mode;
2154
+ const rtl = readDirection(el) === "rtl";
2155
+ if (mode === "start") return rtl ? "right" : "left";
2156
+ return rtl ? "left" : "right";
2157
+ };
2158
+
2159
+ // src/core/primitives/touch-scroll-guard.ts
2160
+ function installTouchScrollGuard(surface, isDragging) {
2161
+ const onTouchMove = (e) => {
2162
+ if (!isDragging()) return;
2163
+ if (e.cancelable) e.preventDefault();
2164
+ };
2165
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2166
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2167
+ }
2168
+
2050
2169
  // src/core/primitives/aria-slider-writer.ts
2051
2170
  var AriaSliderWriter = class {
2052
2171
  constructor(handle, mode) {
@@ -2399,9 +2518,11 @@ function resolveEngineOptions(opts, extras) {
2399
2518
  }
2400
2519
  }
2401
2520
  return {
2402
- mode: opts.mode ?? "bottom",
2521
+ mode: resolveMode(opts.mode ?? "bottom", opts.element),
2403
2522
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2404
2523
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2524
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2525
+ dragFromContent: opts.dragFromContent ?? true,
2405
2526
  rubberBandEnabled: opts.rubberBand ?? true,
2406
2527
  closeOnBack: opts.closeOnBack ?? false,
2407
2528
  persistent: opts.persistent ?? false,
@@ -2487,6 +2608,8 @@ var BottomSheetCore = class {
2487
2608
  __publicField(this, "mode");
2488
2609
  __publicField(this, "flickVelocity");
2489
2610
  __publicField(this, "dragThreshold");
2611
+ __publicField(this, "dragFromMode");
2612
+ __publicField(this, "dragFromContentDefault");
2490
2613
  __publicField(this, "rubberBandEnabled");
2491
2614
  __publicField(this, "scrim");
2492
2615
  __publicField(this, "aria");
@@ -2499,13 +2622,18 @@ var BottomSheetCore = class {
2499
2622
  __publicField(this, "disableDragFlag");
2500
2623
  __publicField(this, "closeOnRouteChange");
2501
2624
  __publicField(this, "maxHeight");
2625
+ __publicField(this, "maxHeightDeps");
2502
2626
  __publicField(this, "snapPointsRaw");
2503
2627
  __publicField(this, "snaps");
2504
2628
  __publicField(this, "activeId");
2505
2629
  __publicField(this, "size", 0);
2506
2630
  __publicField(this, "gesture");
2631
+ __publicField(this, "contentGesture");
2632
+ __publicField(this, "detachSheetGesture", null);
2633
+ __publicField(this, "detachSliderKeyboard", null);
2507
2634
  __publicField(this, "rootEl", null);
2508
2635
  __publicField(this, "destroyed", false);
2636
+ __publicField(this, "restClosed", false);
2509
2637
  __publicField(this, "currentViewTransition", null);
2510
2638
  __publicField(this, "currentAbort", new AbortController());
2511
2639
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2554,10 +2682,13 @@ var BottomSheetCore = class {
2554
2682
  this.screenComponent = opts.scrim ?? opts.screenComponent;
2555
2683
  this.scrimParent = this.screenComponent?.parentElement ?? null;
2556
2684
  this.mode = resolved.mode;
2685
+ this.element.dataset.mode = this.mode;
2557
2686
  this.transformTemplate = buildTransformTemplate(this.mode);
2558
2687
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2559
2688
  this.flickVelocity = resolved.flickVelocity;
2560
2689
  this.dragThreshold = resolved.dragThreshold;
2690
+ this.dragFromMode = resolved.dragFrom;
2691
+ this.dragFromContentDefault = resolved.dragFromContent;
2561
2692
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2562
2693
  this.scrim = new ScrimController(
2563
2694
  {
@@ -2584,7 +2715,7 @@ var BottomSheetCore = class {
2584
2715
  getRootEl: () => this.rootEl,
2585
2716
  applySize: (size) => this.applySize(size),
2586
2717
  getSize: () => this.size,
2587
- isDragging: () => this.gesture?.isDragging ?? false,
2718
+ isDragging: () => this.isDraggingAny(),
2588
2719
  applyAux: (size) => this.applySize(size, true),
2589
2720
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2590
2721
  },
@@ -2618,7 +2749,7 @@ var BottomSheetCore = class {
2618
2749
  auditVhUsage(this.snapPointsRaw);
2619
2750
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2620
2751
  this.rootEl = this.element.closest(".bs-root");
2621
- this.maxHeight = createMaxHeightController({
2752
+ this.maxHeightDeps = {
2622
2753
  element: this.element,
2623
2754
  mode: this.mode,
2624
2755
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
@@ -2626,7 +2757,8 @@ var BottomSheetCore = class {
2626
2757
  this.snaps.setMaxAxisSize(size);
2627
2758
  },
2628
2759
  recompute: () => this.recompute()
2629
- });
2760
+ };
2761
+ this.maxHeight = createMaxHeightController(this.maxHeightDeps);
2630
2762
  this.fitDeps = {
2631
2763
  element: this.element,
2632
2764
  scrollContainer: this.scrollContainer,
@@ -2645,6 +2777,7 @@ var BottomSheetCore = class {
2645
2777
  const initial = this.snaps.findById(this.activeId);
2646
2778
  if (initial) this.size = initial.size;
2647
2779
  this.applySize(this.size);
2780
+ this.setRestClosed(this.size === 0);
2648
2781
  this.updateAriaSlider();
2649
2782
  this.scrollCache = createScrollCache({
2650
2783
  scrollContainer: this.scrollContainer,
@@ -2675,7 +2808,7 @@ var BottomSheetCore = class {
2675
2808
  (p) => p.size === "fit" || p.size === "content"
2676
2809
  ),
2677
2810
  isDestroyed: () => this.destroyed,
2678
- isDragging: () => this.gesture?.isDragging ?? false,
2811
+ isDragging: () => this.isDraggingAny(),
2679
2812
  recompute: () => this.recompute()
2680
2813
  })
2681
2814
  );
@@ -2701,7 +2834,7 @@ var BottomSheetCore = class {
2701
2834
  autoCollapseAfter
2702
2835
  },
2703
2836
  isDestroyed: () => this.destroyed,
2704
- isDragging: () => this.gesture?.isDragging ?? false,
2837
+ isDragging: () => this.isDraggingAny(),
2705
2838
  isAnimating: () => this.animation.isAnimating,
2706
2839
  isTopSheet: () => this.isTopSheet,
2707
2840
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2731,6 +2864,7 @@ var BottomSheetCore = class {
2731
2864
  void this.snapTo(id);
2732
2865
  },
2733
2866
  close: (reason) => this.close(reason),
2867
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2734
2868
  on: (event, fn) => this.on(event, fn),
2735
2869
  addTeardown: (fn) => this.teardowns.add(fn)
2736
2870
  };
@@ -2759,7 +2893,7 @@ var BottomSheetCore = class {
2759
2893
  return {
2760
2894
  size: this.size,
2761
2895
  activeId: this.activeId,
2762
- isDragging: this.gesture?.isDragging ?? false,
2896
+ isDragging: this.isDraggingAny(),
2763
2897
  isAnimating: this.animation.isAnimating,
2764
2898
  progress: this.computeProgress(this.size)
2765
2899
  };
@@ -2879,9 +3013,16 @@ var BottomSheetCore = class {
2879
3013
  progress: this.computeProgress(this.size)
2880
3014
  });
2881
3015
  if (withHaptic) this.haptic();
3016
+ this.setRestClosed(targetSize === 0);
2882
3017
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2883
3018
  if (targetSize === 0) this.emitCloseSequence();
2884
3019
  }
3020
+ setRestClosed(closed) {
3021
+ if (closed === this.restClosed) return;
3022
+ this.restClosed = closed;
3023
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3024
+ else this.element.removeAttribute("data-bs-rest");
3025
+ }
2885
3026
  emitOpenSequence(id) {
2886
3027
  this.emit("open", { id });
2887
3028
  this.handleOpen();
@@ -2937,6 +3078,51 @@ var BottomSheetCore = class {
2937
3078
  if (this.destroyed) return;
2938
3079
  this.disableDragFlag = value;
2939
3080
  }
3081
+ setDragFromContent(value) {
3082
+ if (this.destroyed) return;
3083
+ this.dragFromContentDefault = value;
3084
+ }
3085
+ setDragFrom(mode) {
3086
+ if (this.destroyed || mode === this.dragFromMode) return;
3087
+ this.gesture?.forceClearDragState();
3088
+ this.detachSheetGesture?.();
3089
+ this.dragFromMode = mode;
3090
+ this.mountSheetGesture();
3091
+ }
3092
+ getDragFrom() {
3093
+ return this.dragFromMode;
3094
+ }
3095
+ setMode(mode) {
3096
+ if (this.destroyed) return;
3097
+ const next = resolveMode(mode, this.element);
3098
+ if (next === this.mode) return;
3099
+ const wasVertical = this.mode === "bottom" || this.mode === "top";
3100
+ const nowVertical = next === "bottom" || next === "top";
3101
+ this.gesture?.forceClearDragState();
3102
+ this.detachSheetGesture?.();
3103
+ this.animation.cancel();
3104
+ this.element.style.removeProperty(layoutAxis(this.mode));
3105
+ this.mode = next;
3106
+ this.element.dataset.mode = next;
3107
+ this.transformTemplate = buildTransformTemplate(next);
3108
+ this.maxHeightDeps.mode = next;
3109
+ this.fitDeps.mode = next;
3110
+ this.snaps.setMode(next);
3111
+ this.scrim.setSheetMode(next);
3112
+ if (wasVertical !== nowVertical) {
3113
+ this.aria = new AriaSliderWriter(this.handle, next);
3114
+ }
3115
+ this.detachSliderKeyboard?.();
3116
+ this.mountSliderKeyboard();
3117
+ this.mountSheetGesture();
3118
+ this.recompute();
3119
+ const active = this.snaps.findById(this.activeId);
3120
+ if (active) this.applySize(active.size);
3121
+ this.updateAriaSlider();
3122
+ }
3123
+ getMode() {
3124
+ return this.mode;
3125
+ }
2940
3126
  isTop() {
2941
3127
  return this.isTopSheet;
2942
3128
  }
@@ -3091,12 +3277,24 @@ var BottomSheetCore = class {
3091
3277
  auditVhUsage(points);
3092
3278
  this.auditDuplicateSnapIds(points);
3093
3279
  this.snaps.setRaw(points);
3094
- if (allowed) this.snaps.setAllowedIds(allowed);
3280
+ if (allowed) {
3281
+ this.snaps.setAllowedIds(allowed);
3282
+ } else {
3283
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3284
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3285
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3286
+ }
3095
3287
  this.scrollCache.clear();
3096
3288
  this.scrim.invalidateOpacityCache();
3097
3289
  const current = this.snaps.findById(this.activeId);
3098
3290
  if (current) {
3099
3291
  this.applySize(current.size);
3292
+ } else {
3293
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3294
+ if (fallback) {
3295
+ this.activeId = fallback.id;
3296
+ this.applySize(fallback.size);
3297
+ }
3100
3298
  }
3101
3299
  this.updateAriaSlider();
3102
3300
  }
@@ -3106,7 +3304,7 @@ var BottomSheetCore = class {
3106
3304
  this.snaps.recompute();
3107
3305
  this.maxHeight.clampTo();
3108
3306
  this.scrim.invalidateOpacityCache();
3109
- if (this.gesture?.isDragging) return;
3307
+ if (this.isDraggingAny()) return;
3110
3308
  this.healActiveSnap();
3111
3309
  }
3112
3310
  healActiveSnap() {
@@ -3128,6 +3326,8 @@ var BottomSheetCore = class {
3128
3326
  this.bus.clear();
3129
3327
  this.animation.cancel();
3130
3328
  this.gesture?.forceClearDragState();
3329
+ this.contentGesture?.forceClearDragState();
3330
+ this.setRestClosed(false);
3131
3331
  this.teardowns.drain();
3132
3332
  this.scrim.destroy();
3133
3333
  this.animation.destroy();
@@ -3240,11 +3440,12 @@ var BottomSheetCore = class {
3240
3440
  const scale = Math.max(1 - depth * 0.04, 0.86);
3241
3441
  this.element.style.scale = scale === 1 ? "" : String(scale);
3242
3442
  }
3243
- attach() {
3244
- this.gesture = new GestureController({
3245
- handle: this.handle,
3443
+ buildGestureDeps(surface, gestureOptions) {
3444
+ return {
3445
+ handle: surface,
3246
3446
  element: this.element,
3247
3447
  mode: this.mode,
3448
+ gestureOptions,
3248
3449
  getRoot: () => this.rootEl,
3249
3450
  getDragContext: () => {
3250
3451
  const buf = this.dragContextBuf;
@@ -3263,8 +3464,90 @@ var BottomSheetCore = class {
3263
3464
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3264
3465
  emit: (event, payload) => this.emit(event, payload),
3265
3466
  listenerCount: (event) => this.bus.listenerCount(event)
3467
+ };
3468
+ }
3469
+ isContentDragAllowed() {
3470
+ if (this.disableDragFlag) return false;
3471
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3472
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3473
+ }
3474
+ decideContentDrag(surface, e, delta, crossDelta) {
3475
+ if (this.destroyed) return false;
3476
+ if (e.pointerType === "mouse") return false;
3477
+ if (this.gesture?.isDragging) return false;
3478
+ if (!this.isContentDragAllowed()) return false;
3479
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3480
+ const decision = decideContentGesture({
3481
+ delta,
3482
+ crossDelta,
3483
+ sharesScrollAxis: this.mode === "bottom" || this.mode === "top",
3484
+ scrollTop: surface.scrollTop,
3485
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3486
+ });
3487
+ if (decision === "pending") return null;
3488
+ return decision === "drag";
3489
+ }
3490
+ attachDragSurface(surface, kind) {
3491
+ if (this.destroyed || kind !== "content") return;
3492
+ if (this.contentGesture) return;
3493
+ const controller = new GestureController(
3494
+ this.buildGestureDeps(surface, {
3495
+ manageTouchAction: false,
3496
+ deferStart: (e, delta, crossDelta) => this.decideContentDrag(surface, e, delta, crossDelta)
3497
+ })
3498
+ );
3499
+ this.contentGesture = controller;
3500
+ const detachGestures = controller.install();
3501
+ const detachGuard = installTouchScrollGuard(
3502
+ surface,
3503
+ () => controller.isDragging
3504
+ );
3505
+ return () => {
3506
+ detachGuard();
3507
+ detachGestures();
3508
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3509
+ };
3510
+ }
3511
+ sheetDragSurface() {
3512
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3513
+ }
3514
+ canStartSheetDrag(e) {
3515
+ if (this.contentGesture?.isDragging) return false;
3516
+ const target = e.target;
3517
+ const scoped = typeof target?.closest === "function";
3518
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3519
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3520
+ return true;
3521
+ }
3522
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3523
+ if (this.dragFromMode === "handle") return true;
3524
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3525
+ return false;
3526
+ }
3527
+ return true;
3528
+ }
3529
+ mountSheetGesture() {
3530
+ const surface = this.sheetDragSurface();
3531
+ const controller = new GestureController(
3532
+ this.buildGestureDeps(surface, {
3533
+ shouldStart: (e) => this.canStartSheetDrag(e),
3534
+ manageTouchAction: this.dragFromMode === "handle"
3535
+ })
3536
+ );
3537
+ this.gesture = controller;
3538
+ const detachGestures = controller.install();
3539
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3540
+ this.detachSheetGesture = () => {
3541
+ detachGuard?.();
3542
+ detachGestures();
3543
+ };
3544
+ }
3545
+ attach() {
3546
+ this.mountSheetGesture();
3547
+ this.teardowns.add(() => {
3548
+ this.detachSheetGesture?.();
3549
+ this.detachSheetGesture = null;
3266
3550
  });
3267
- this.teardowns.add(this.gesture.install());
3268
3551
  try {
3269
3552
  this.attachFeatures();
3270
3553
  } catch (err) {
@@ -3301,7 +3584,7 @@ var BottomSheetCore = class {
3301
3584
  element: this.element,
3302
3585
  getMode: () => this.mode,
3303
3586
  isDestroyed: () => this.destroyed,
3304
- isDragging: () => this.gesture?.isDragging ?? false,
3587
+ isDragging: () => this.isDraggingAny(),
3305
3588
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3306
3589
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3307
3590
  getSize: () => this.size,
@@ -3336,7 +3619,11 @@ var BottomSheetCore = class {
3336
3619
  );
3337
3620
  }
3338
3621
  }
3339
- this.teardowns.add(installSliderKeyboard({
3622
+ this.mountSliderKeyboard();
3623
+ this.scrim.attach();
3624
+ }
3625
+ mountSliderKeyboard() {
3626
+ const detach = installSliderKeyboard({
3340
3627
  handle: this.handle,
3341
3628
  mode: this.mode,
3342
3629
  isDestroyed: () => this.destroyed,
@@ -3345,8 +3632,12 @@ var BottomSheetCore = class {
3345
3632
  snapTo: (id) => {
3346
3633
  void this.snapTo(id);
3347
3634
  }
3348
- }));
3349
- this.scrim.attach();
3635
+ });
3636
+ this.detachSliderKeyboard = detach;
3637
+ this.teardowns.add(() => {
3638
+ this.detachSliderKeyboard?.();
3639
+ this.detachSliderKeyboard = null;
3640
+ });
3350
3641
  }
3351
3642
  settleAfterDrag(delta, velocity, kind = "touch") {
3352
3643
  const signal = this.newCycle();
@@ -3399,8 +3690,11 @@ var BottomSheetCore = class {
3399
3690
  );
3400
3691
  });
3401
3692
  }
3693
+ isDraggingAny() {
3694
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3695
+ }
3402
3696
  clearWillChangeIfIdle() {
3403
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3697
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3404
3698
  this.element.style.willChange = "";
3405
3699
  }
3406
3700
  haptic() {
@@ -3413,8 +3707,9 @@ var BottomSheetCore = class {
3413
3707
  }
3414
3708
  applySize(size, skipTransform = false) {
3415
3709
  const cap = this.snaps.getMaxAxisSize();
3416
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3710
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3417
3711
  this.size = clamped;
3712
+ if (clamped > 0) this.setRestClosed(false);
3418
3713
  const offset = cap - clamped;
3419
3714
  const style = this.element.style;
3420
3715
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3794,50 +4089,10 @@ function installAutoCollapse(deps) {
3794
4089
  };
3795
4090
  }
3796
4091
 
3797
- // src/core/features/content-swipe.ts
3798
- var CONTENT_PULL_THRESHOLD = 60;
3799
- function installContentSwipe(deps) {
3800
- const { container } = deps;
3801
- let startY = 0;
3802
- let startScrollTop = 0;
3803
- let moved = false;
3804
- const onTouchStart = (e) => {
3805
- const t = e.touches[0];
3806
- if (!t) return;
3807
- startY = t.clientY;
3808
- startScrollTop = container.scrollTop;
3809
- moved = false;
3810
- };
3811
- const onTouchMove = (e) => {
3812
- const t = e.touches[0];
3813
- if (!t) return;
3814
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3815
- };
3816
- const onTouchEnd = (e) => {
3817
- if (!moved) return;
3818
- if (deps.isDragging() || deps.isAnimating()) return;
3819
- if (container.scrollTop !== startScrollTop) return;
3820
- const t = e.changedTouches[0];
3821
- if (!t) return;
3822
- const deltaY = t.clientY - startY;
3823
- const atTop = container.scrollTop <= 0;
3824
- const allowed = deps.getAllowedIds();
3825
- const idx = allowed.indexOf(deps.getActiveId());
3826
- if (idx === -1) return;
3827
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3828
- deps.snapTo(allowed[idx - 1]);
3829
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3830
- deps.snapTo(allowed[idx + 1]);
3831
- }
3832
- };
3833
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3834
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3835
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3836
- return () => {
3837
- container.removeEventListener("touchstart", onTouchStart);
3838
- container.removeEventListener("touchmove", onTouchMove);
3839
- container.removeEventListener("touchend", onTouchEnd);
3840
- };
4092
+ // src/core/features/content-drag.ts
4093
+ function installContentDrag(deps) {
4094
+ const detach = deps.attachDragSurface(deps.container, "content");
4095
+ return () => detach?.();
3841
4096
  }
3842
4097
 
3843
4098
  // src/core/features/visual-viewport.ts
@@ -3945,13 +4200,9 @@ function contentSwipeFeature() {
3945
4200
  stage: "attach",
3946
4201
  install: (ctx) => {
3947
4202
  if (!ctx.scrollContainer) return;
3948
- return installContentSwipe({
4203
+ return installContentDrag({
3949
4204
  container: ctx.scrollContainer,
3950
- isDragging: ctx.isDragging,
3951
- isAnimating: ctx.isAnimating,
3952
- getAllowedIds: ctx.allowedIdsBySize,
3953
- getActiveId: ctx.getActiveId,
3954
- snapTo: ctx.snapTo
4205
+ attachDragSurface: ctx.attachDragSurface
3955
4206
  });
3956
4207
  }
3957
4208
  };
@@ -4163,6 +4414,8 @@ function BottomSheet($$anchor, $$props) {
4163
4414
  persistent: persistent(),
4164
4415
  disableClose: disableClose(),
4165
4416
  disableDrag: disableDrag(),
4417
+ dragFrom: $$props.dragFrom,
4418
+ dragFromContent: $$props.dragFromContent,
4166
4419
  closeOnRouteChange: closeOnRouteChange(),
4167
4420
  stackEffect: stackEffect(),
4168
4421
  radius: $$props.radius,
@@ -4259,6 +4512,14 @@ function BottomSheet($$anchor, $$props) {
4259
4512
  $2__namespace.user_effect(() => {
4260
4513
  engine?.setDisableDrag(disableDrag());
4261
4514
  });
4515
+ $2__namespace.user_effect(() => {
4516
+ if ($$props.dragFromContent !== void 0) {
4517
+ engine?.setDragFromContent($$props.dragFromContent);
4518
+ }
4519
+ });
4520
+ $2__namespace.user_effect(() => {
4521
+ if ($$props.dragFrom !== void 0) engine?.setDragFrom($$props.dragFrom);
4522
+ });
4262
4523
  $2__namespace.user_effect(() => {
4263
4524
  if ($$props.radius !== void 0) engine?.setRadius($$props.radius);
4264
4525
  });