@surdeddd/bottom-sheet 0.13.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 +33 -0
  2. package/dist/{BottomSheetCore-CsIjyq7C.d.cts → BottomSheetCore-Cp7dWy91.d.cts} +26 -1
  3. package/dist/{BottomSheetCore-D16lsa6g.d.ts → BottomSheetCore-DJYwWOqt.d.ts} +26 -1
  4. package/dist/{BottomSheetEngine-BYjAVrmd.d.cts → BottomSheetEngine-Lm7HIVHv.d.cts} +1 -1
  5. package/dist/{BottomSheetEngine-YuCNugZn.d.ts → BottomSheetEngine-O5PbeKfe.d.ts} +1 -1
  6. package/dist/core.cjs +231 -27
  7. package/dist/core.d.cts +2 -2
  8. package/dist/core.d.ts +2 -2
  9. package/dist/core.js +231 -27
  10. package/dist/debug.d.cts +1 -1
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/element.cjs +300 -102
  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 +300 -102
  17. package/dist/features.cjs +6 -50
  18. package/dist/features.d.cts +2 -2
  19. package/dist/features.d.ts +2 -2
  20. package/dist/features.js +6 -50
  21. package/dist/index-Bb5TRmyg.d.cts +9 -0
  22. package/dist/index-Cl9ebCP2.d.ts +9 -0
  23. package/dist/index.cjs +238 -823
  24. package/dist/index.d.cts +42 -45
  25. package/dist/index.d.ts +42 -45
  26. package/dist/index.js +239 -810
  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 +228 -77
  36. package/dist/qwik-lib/index.qwik.mjs +228 -77
  37. package/dist/qwik.cjs +245 -77
  38. package/dist/qwik.d.cts +4 -2
  39. package/dist/qwik.d.ts +4 -2
  40. package/dist/qwik.js +245 -77
  41. package/dist/react.cjs +250 -79
  42. package/dist/react.d.cts +3 -4
  43. package/dist/react.d.ts +3 -4
  44. package/dist/react.js +250 -79
  45. package/dist/solid.cjs +251 -77
  46. package/dist/solid.d.cts +27 -0
  47. package/dist/solid.d.ts +27 -0
  48. package/dist/solid.js +251 -77
  49. package/dist/solid.source.jsx +259 -77
  50. package/dist/styles.css +24 -4
  51. package/dist/svelte-core.cjs +237 -77
  52. package/dist/svelte-core.js +237 -77
  53. package/dist/svelte-src/BottomSheet.svelte +14 -0
  54. package/dist/svelte.cjs +247 -77
  55. package/dist/svelte.d.cts +2 -0
  56. package/dist/svelte.d.ts +2 -0
  57. package/dist/svelte.js +247 -77
  58. package/dist/vue.cjs +253 -77
  59. package/dist/vue.js +253 -77
  60. package/dist/webgl.cjs +583 -0
  61. package/dist/webgl.d.cts +17 -0
  62. package/dist/webgl.d.ts +17 -0
  63. package/dist/webgl.js +581 -0
  64. package/package.json +36 -12
  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);
@@ -1540,7 +1540,7 @@ var AnimationRunner = class {
1540
1540
  }
1541
1541
  };
1542
1542
 
1543
- // src/core/lifecycle/focusTrap.ts
1543
+ // src/core/lifecycle/focus-trap.ts
1544
1544
  var FOCUSABLE = [
1545
1545
  "a[href]",
1546
1546
  "area[href]",
@@ -1658,7 +1658,7 @@ var installFocusTrap = (container, options = {}) => {
1658
1658
  };
1659
1659
  };
1660
1660
 
1661
- // src/core/lifecycle/scrollLock.ts
1661
+ // src/core/lifecycle/scroll-lock.ts
1662
1662
  var lockCount = 0;
1663
1663
  var savedStyles = null;
1664
1664
  var lockBodyScroll = () => {
@@ -1840,30 +1840,44 @@ var SampleRing = class {
1840
1840
  this.size = 0;
1841
1841
  }
1842
1842
  };
1843
- var installGestures = (handle, mode, callbacks) => {
1843
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1844
1844
  let activePointerId = null;
1845
1845
  let activePointerType = "touch";
1846
1846
  let startCoord = 0;
1847
1847
  let lastCoord = 0;
1848
+ let pendingStart = false;
1848
1849
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1849
1850
  const MAX_SAMPLES = 32;
1850
1851
  const samples = new SampleRing(MAX_SAMPLES);
1851
1852
  const axis = isAxisVertical(mode) ? "Y" : "X";
1852
1853
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1853
1854
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1855
+ const capture = (pointerId) => {
1856
+ try {
1857
+ handle.setPointerCapture(pointerId);
1858
+ } catch {
1859
+ }
1860
+ };
1861
+ const releaseTracking = () => {
1862
+ activePointerId = null;
1863
+ pendingStart = false;
1864
+ samples.reset();
1865
+ };
1854
1866
  const onPointerDown = (e) => {
1855
1867
  if (activePointerId !== null) return;
1856
1868
  if (e.button !== void 0 && e.button !== 0) return;
1869
+ if (options.shouldStart && !options.shouldStart(e)) return;
1857
1870
  activePointerId = e.pointerId;
1858
1871
  activePointerType = e.pointerType || "touch";
1859
1872
  startCoord = coordOf(e);
1860
1873
  lastCoord = startCoord;
1861
1874
  samples.reset();
1862
1875
  samples.push(e.timeStamp, startCoord);
1863
- try {
1864
- handle.setPointerCapture(e.pointerId);
1865
- } catch {
1876
+ if (options.deferStart) {
1877
+ pendingStart = true;
1878
+ return;
1866
1879
  }
1880
+ capture(e.pointerId);
1867
1881
  callbacks.onStart(startCoord, activePointerType);
1868
1882
  };
1869
1883
  const onPointerMove = (e) => {
@@ -1871,6 +1885,22 @@ var installGestures = (handle, mode, callbacks) => {
1871
1885
  const coord = coordOf(e);
1872
1886
  const rawDelta = coord - startCoord;
1873
1887
  const delta = rawDelta * sign;
1888
+ if (pendingStart) {
1889
+ const verdict = options.deferStart(e, delta);
1890
+ if (verdict === false) {
1891
+ releaseTracking();
1892
+ return;
1893
+ }
1894
+ if (verdict !== true) return;
1895
+ pendingStart = false;
1896
+ startCoord = coord;
1897
+ lastCoord = coord;
1898
+ samples.reset();
1899
+ samples.push(e.timeStamp, coord);
1900
+ capture(e.pointerId);
1901
+ callbacks.onStart(startCoord, activePointerType);
1902
+ return;
1903
+ }
1874
1904
  lastCoord = coord;
1875
1905
  samples.push(e.timeStamp, coord);
1876
1906
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1879,6 +1909,10 @@ var installGestures = (handle, mode, callbacks) => {
1879
1909
  };
1880
1910
  const finishGesture = (e) => {
1881
1911
  if (e.pointerId !== activePointerId) return;
1912
+ if (pendingStart) {
1913
+ releaseTracking();
1914
+ return;
1915
+ }
1882
1916
  const rawDelta = lastCoord - startCoord;
1883
1917
  const delta = rawDelta * sign;
1884
1918
  let velocity = 0;
@@ -1901,22 +1935,26 @@ var installGestures = (handle, mode, callbacks) => {
1901
1935
  };
1902
1936
  const onPointerCancel = (e) => {
1903
1937
  if (e.pointerId !== activePointerId) return;
1904
- activePointerId = null;
1905
- samples.reset();
1938
+ const wasPending = pendingStart;
1939
+ releaseTracking();
1940
+ if (wasPending) return;
1906
1941
  callbacks.onCancel?.();
1907
1942
  };
1908
1943
  handle.addEventListener("pointerdown", onPointerDown);
1909
1944
  handle.addEventListener("pointermove", onPointerMove);
1910
1945
  handle.addEventListener("pointerup", finishGesture);
1911
1946
  handle.addEventListener("pointercancel", onPointerCancel);
1947
+ const managesTouchAction = options.manageTouchAction !== false;
1912
1948
  const prevTouchAction = handle.style.touchAction;
1913
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1949
+ if (managesTouchAction) {
1950
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1951
+ }
1914
1952
  return () => {
1915
1953
  handle.removeEventListener("pointerdown", onPointerDown);
1916
1954
  handle.removeEventListener("pointermove", onPointerMove);
1917
1955
  handle.removeEventListener("pointerup", finishGesture);
1918
1956
  handle.removeEventListener("pointercancel", onPointerCancel);
1919
- handle.style.touchAction = prevTouchAction;
1957
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1920
1958
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1921
1959
  handle.releasePointerCapture(activePointerId);
1922
1960
  }
@@ -2035,7 +2073,7 @@ var GestureController = class {
2035
2073
  const restoreTo = this.dragStartSize;
2036
2074
  void this.deps.animateTo(restoreTo, 0);
2037
2075
  }
2038
- });
2076
+ }, this.deps.gestureOptions);
2039
2077
  return this.detach;
2040
2078
  }
2041
2079
  forceClearDragState() {
@@ -2047,6 +2085,41 @@ var GestureController = class {
2047
2085
  }
2048
2086
  };
2049
2087
 
2088
+ // src/core/primitives/drag-zones.ts
2089
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2090
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2091
+ var asElement = (target) => {
2092
+ const candidate = target;
2093
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2094
+ };
2095
+ function isDragAllowedFrom(target, mode) {
2096
+ const el = asElement(target);
2097
+ if (!el) return mode !== "zones";
2098
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2099
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2100
+ return true;
2101
+ }
2102
+
2103
+ // src/core/primitives/content-gesture.ts
2104
+ var CONTENT_DRAG_SLOP = 6;
2105
+ function decideContentGesture(input) {
2106
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2107
+ if (Math.abs(input.delta) < slop) return "pending";
2108
+ if (input.scrollTop > 0) return "scroll";
2109
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2110
+ return "drag";
2111
+ }
2112
+
2113
+ // src/core/primitives/touch-scroll-guard.ts
2114
+ function installTouchScrollGuard(surface, isDragging) {
2115
+ const onTouchMove = (e) => {
2116
+ if (!isDragging()) return;
2117
+ if (e.cancelable) e.preventDefault();
2118
+ };
2119
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2120
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2121
+ }
2122
+
2050
2123
  // src/core/primitives/aria-slider-writer.ts
2051
2124
  var AriaSliderWriter = class {
2052
2125
  constructor(handle, mode) {
@@ -2402,6 +2475,8 @@ function resolveEngineOptions(opts, extras) {
2402
2475
  mode: opts.mode ?? "bottom",
2403
2476
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2404
2477
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2478
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2479
+ dragFromContent: opts.dragFromContent ?? true,
2405
2480
  rubberBandEnabled: opts.rubberBand ?? true,
2406
2481
  closeOnBack: opts.closeOnBack ?? false,
2407
2482
  persistent: opts.persistent ?? false,
@@ -2487,6 +2562,8 @@ var BottomSheetCore = class {
2487
2562
  __publicField(this, "mode");
2488
2563
  __publicField(this, "flickVelocity");
2489
2564
  __publicField(this, "dragThreshold");
2565
+ __publicField(this, "dragFromMode");
2566
+ __publicField(this, "dragFromContentDefault");
2490
2567
  __publicField(this, "rubberBandEnabled");
2491
2568
  __publicField(this, "scrim");
2492
2569
  __publicField(this, "aria");
@@ -2504,8 +2581,11 @@ var BottomSheetCore = class {
2504
2581
  __publicField(this, "activeId");
2505
2582
  __publicField(this, "size", 0);
2506
2583
  __publicField(this, "gesture");
2584
+ __publicField(this, "contentGesture");
2585
+ __publicField(this, "detachSheetGesture", null);
2507
2586
  __publicField(this, "rootEl", null);
2508
2587
  __publicField(this, "destroyed", false);
2588
+ __publicField(this, "restClosed", false);
2509
2589
  __publicField(this, "currentViewTransition", null);
2510
2590
  __publicField(this, "currentAbort", new AbortController());
2511
2591
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2558,6 +2638,8 @@ var BottomSheetCore = class {
2558
2638
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2559
2639
  this.flickVelocity = resolved.flickVelocity;
2560
2640
  this.dragThreshold = resolved.dragThreshold;
2641
+ this.dragFromMode = resolved.dragFrom;
2642
+ this.dragFromContentDefault = resolved.dragFromContent;
2561
2643
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2562
2644
  this.scrim = new ScrimController(
2563
2645
  {
@@ -2584,7 +2666,7 @@ var BottomSheetCore = class {
2584
2666
  getRootEl: () => this.rootEl,
2585
2667
  applySize: (size) => this.applySize(size),
2586
2668
  getSize: () => this.size,
2587
- isDragging: () => this.gesture?.isDragging ?? false,
2669
+ isDragging: () => this.isDraggingAny(),
2588
2670
  applyAux: (size) => this.applySize(size, true),
2589
2671
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2590
2672
  },
@@ -2645,6 +2727,7 @@ var BottomSheetCore = class {
2645
2727
  const initial = this.snaps.findById(this.activeId);
2646
2728
  if (initial) this.size = initial.size;
2647
2729
  this.applySize(this.size);
2730
+ this.setRestClosed(this.size === 0);
2648
2731
  this.updateAriaSlider();
2649
2732
  this.scrollCache = createScrollCache({
2650
2733
  scrollContainer: this.scrollContainer,
@@ -2675,7 +2758,7 @@ var BottomSheetCore = class {
2675
2758
  (p) => p.size === "fit" || p.size === "content"
2676
2759
  ),
2677
2760
  isDestroyed: () => this.destroyed,
2678
- isDragging: () => this.gesture?.isDragging ?? false,
2761
+ isDragging: () => this.isDraggingAny(),
2679
2762
  recompute: () => this.recompute()
2680
2763
  })
2681
2764
  );
@@ -2701,7 +2784,7 @@ var BottomSheetCore = class {
2701
2784
  autoCollapseAfter
2702
2785
  },
2703
2786
  isDestroyed: () => this.destroyed,
2704
- isDragging: () => this.gesture?.isDragging ?? false,
2787
+ isDragging: () => this.isDraggingAny(),
2705
2788
  isAnimating: () => this.animation.isAnimating,
2706
2789
  isTopSheet: () => this.isTopSheet,
2707
2790
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2731,6 +2814,7 @@ var BottomSheetCore = class {
2731
2814
  void this.snapTo(id);
2732
2815
  },
2733
2816
  close: (reason) => this.close(reason),
2817
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2734
2818
  on: (event, fn) => this.on(event, fn),
2735
2819
  addTeardown: (fn) => this.teardowns.add(fn)
2736
2820
  };
@@ -2759,7 +2843,7 @@ var BottomSheetCore = class {
2759
2843
  return {
2760
2844
  size: this.size,
2761
2845
  activeId: this.activeId,
2762
- isDragging: this.gesture?.isDragging ?? false,
2846
+ isDragging: this.isDraggingAny(),
2763
2847
  isAnimating: this.animation.isAnimating,
2764
2848
  progress: this.computeProgress(this.size)
2765
2849
  };
@@ -2879,9 +2963,16 @@ var BottomSheetCore = class {
2879
2963
  progress: this.computeProgress(this.size)
2880
2964
  });
2881
2965
  if (withHaptic) this.haptic();
2966
+ this.setRestClosed(targetSize === 0);
2882
2967
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2883
2968
  if (targetSize === 0) this.emitCloseSequence();
2884
2969
  }
2970
+ setRestClosed(closed) {
2971
+ if (closed === this.restClosed) return;
2972
+ this.restClosed = closed;
2973
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
2974
+ else this.element.removeAttribute("data-bs-rest");
2975
+ }
2885
2976
  emitOpenSequence(id) {
2886
2977
  this.emit("open", { id });
2887
2978
  this.handleOpen();
@@ -2937,6 +3028,20 @@ var BottomSheetCore = class {
2937
3028
  if (this.destroyed) return;
2938
3029
  this.disableDragFlag = value;
2939
3030
  }
3031
+ setDragFromContent(value) {
3032
+ if (this.destroyed) return;
3033
+ this.dragFromContentDefault = value;
3034
+ }
3035
+ setDragFrom(mode) {
3036
+ if (this.destroyed || mode === this.dragFromMode) return;
3037
+ this.gesture?.forceClearDragState();
3038
+ this.detachSheetGesture?.();
3039
+ this.dragFromMode = mode;
3040
+ this.mountSheetGesture();
3041
+ }
3042
+ getDragFrom() {
3043
+ return this.dragFromMode;
3044
+ }
2940
3045
  isTop() {
2941
3046
  return this.isTopSheet;
2942
3047
  }
@@ -3091,12 +3196,24 @@ var BottomSheetCore = class {
3091
3196
  auditVhUsage(points);
3092
3197
  this.auditDuplicateSnapIds(points);
3093
3198
  this.snaps.setRaw(points);
3094
- if (allowed) this.snaps.setAllowedIds(allowed);
3199
+ if (allowed) {
3200
+ this.snaps.setAllowedIds(allowed);
3201
+ } else {
3202
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3203
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3204
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3205
+ }
3095
3206
  this.scrollCache.clear();
3096
3207
  this.scrim.invalidateOpacityCache();
3097
3208
  const current = this.snaps.findById(this.activeId);
3098
3209
  if (current) {
3099
3210
  this.applySize(current.size);
3211
+ } else {
3212
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3213
+ if (fallback) {
3214
+ this.activeId = fallback.id;
3215
+ this.applySize(fallback.size);
3216
+ }
3100
3217
  }
3101
3218
  this.updateAriaSlider();
3102
3219
  }
@@ -3106,7 +3223,7 @@ var BottomSheetCore = class {
3106
3223
  this.snaps.recompute();
3107
3224
  this.maxHeight.clampTo();
3108
3225
  this.scrim.invalidateOpacityCache();
3109
- if (this.gesture?.isDragging) return;
3226
+ if (this.isDraggingAny()) return;
3110
3227
  this.healActiveSnap();
3111
3228
  }
3112
3229
  healActiveSnap() {
@@ -3128,6 +3245,8 @@ var BottomSheetCore = class {
3128
3245
  this.bus.clear();
3129
3246
  this.animation.cancel();
3130
3247
  this.gesture?.forceClearDragState();
3248
+ this.contentGesture?.forceClearDragState();
3249
+ this.setRestClosed(false);
3131
3250
  this.teardowns.drain();
3132
3251
  this.scrim.destroy();
3133
3252
  this.animation.destroy();
@@ -3240,11 +3359,12 @@ var BottomSheetCore = class {
3240
3359
  const scale = Math.max(1 - depth * 0.04, 0.86);
3241
3360
  this.element.style.scale = scale === 1 ? "" : String(scale);
3242
3361
  }
3243
- attach() {
3244
- this.gesture = new GestureController({
3245
- handle: this.handle,
3362
+ buildGestureDeps(surface, gestureOptions) {
3363
+ return {
3364
+ handle: surface,
3246
3365
  element: this.element,
3247
3366
  mode: this.mode,
3367
+ gestureOptions,
3248
3368
  getRoot: () => this.rootEl,
3249
3369
  getDragContext: () => {
3250
3370
  const buf = this.dragContextBuf;
@@ -3263,8 +3383,88 @@ var BottomSheetCore = class {
3263
3383
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3264
3384
  emit: (event, payload) => this.emit(event, payload),
3265
3385
  listenerCount: (event) => this.bus.listenerCount(event)
3386
+ };
3387
+ }
3388
+ isContentDragAllowed() {
3389
+ if (this.disableDragFlag) return false;
3390
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3391
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3392
+ }
3393
+ decideContentDrag(surface, e, delta) {
3394
+ if (this.destroyed) return false;
3395
+ if (e.pointerType === "mouse") return false;
3396
+ if (this.gesture?.isDragging) return false;
3397
+ if (!this.isContentDragAllowed()) return false;
3398
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3399
+ const decision = decideContentGesture({
3400
+ delta,
3401
+ scrollTop: surface.scrollTop,
3402
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3403
+ });
3404
+ if (decision === "pending") return null;
3405
+ return decision === "drag";
3406
+ }
3407
+ attachDragSurface(surface, kind) {
3408
+ if (this.destroyed || kind !== "content") return;
3409
+ if (this.contentGesture) return;
3410
+ const controller = new GestureController(
3411
+ this.buildGestureDeps(surface, {
3412
+ manageTouchAction: false,
3413
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3414
+ })
3415
+ );
3416
+ this.contentGesture = controller;
3417
+ const detachGestures = controller.install();
3418
+ const detachGuard = installTouchScrollGuard(
3419
+ surface,
3420
+ () => controller.isDragging
3421
+ );
3422
+ return () => {
3423
+ detachGuard();
3424
+ detachGestures();
3425
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3426
+ };
3427
+ }
3428
+ sheetDragSurface() {
3429
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3430
+ }
3431
+ canStartSheetDrag(e) {
3432
+ if (this.contentGesture?.isDragging) return false;
3433
+ const target = e.target;
3434
+ const scoped = typeof target?.closest === "function";
3435
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3436
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3437
+ return true;
3438
+ }
3439
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3440
+ if (this.dragFromMode === "handle") return true;
3441
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3442
+ return false;
3443
+ }
3444
+ return true;
3445
+ }
3446
+ mountSheetGesture() {
3447
+ const surface = this.sheetDragSurface();
3448
+ const controller = new GestureController(
3449
+ this.buildGestureDeps(surface, {
3450
+ shouldStart: (e) => this.canStartSheetDrag(e),
3451
+ manageTouchAction: this.dragFromMode === "handle"
3452
+ })
3453
+ );
3454
+ this.gesture = controller;
3455
+ const detachGestures = controller.install();
3456
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3457
+ this.detachSheetGesture = () => {
3458
+ detachGuard?.();
3459
+ detachGestures();
3460
+ };
3461
+ }
3462
+ attach() {
3463
+ this.mountSheetGesture();
3464
+ this.teardowns.add(() => {
3465
+ this.detachSheetGesture?.();
3466
+ this.detachSheetGesture = null;
3266
3467
  });
3267
- this.teardowns.add(this.gesture.install());
3268
3468
  try {
3269
3469
  this.attachFeatures();
3270
3470
  } catch (err) {
@@ -3301,7 +3501,7 @@ var BottomSheetCore = class {
3301
3501
  element: this.element,
3302
3502
  getMode: () => this.mode,
3303
3503
  isDestroyed: () => this.destroyed,
3304
- isDragging: () => this.gesture?.isDragging ?? false,
3504
+ isDragging: () => this.isDraggingAny(),
3305
3505
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3306
3506
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3307
3507
  getSize: () => this.size,
@@ -3399,8 +3599,11 @@ var BottomSheetCore = class {
3399
3599
  );
3400
3600
  });
3401
3601
  }
3602
+ isDraggingAny() {
3603
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3604
+ }
3402
3605
  clearWillChangeIfIdle() {
3403
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3606
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3404
3607
  this.element.style.willChange = "";
3405
3608
  }
3406
3609
  haptic() {
@@ -3413,8 +3616,9 @@ var BottomSheetCore = class {
3413
3616
  }
3414
3617
  applySize(size, skipTransform = false) {
3415
3618
  const cap = this.snaps.getMaxAxisSize();
3416
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3619
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3417
3620
  this.size = clamped;
3621
+ if (clamped > 0) this.setRestClosed(false);
3418
3622
  const offset = cap - clamped;
3419
3623
  const style = this.element.style;
3420
3624
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3794,50 +3998,10 @@ function installAutoCollapse(deps) {
3794
3998
  };
3795
3999
  }
3796
4000
 
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
- };
4001
+ // src/core/features/content-drag.ts
4002
+ function installContentDrag(deps) {
4003
+ const detach = deps.attachDragSurface(deps.container, "content");
4004
+ return () => detach?.();
3841
4005
  }
3842
4006
 
3843
4007
  // src/core/features/visual-viewport.ts
@@ -3945,13 +4109,9 @@ function contentSwipeFeature() {
3945
4109
  stage: "attach",
3946
4110
  install: (ctx) => {
3947
4111
  if (!ctx.scrollContainer) return;
3948
- return installContentSwipe({
4112
+ return installContentDrag({
3949
4113
  container: ctx.scrollContainer,
3950
- isDragging: ctx.isDragging,
3951
- isAnimating: ctx.isAnimating,
3952
- getAllowedIds: ctx.allowedIdsBySize,
3953
- getActiveId: ctx.getActiveId,
3954
- snapTo: ctx.snapTo
4114
+ attachDragSurface: ctx.attachDragSurface
3955
4115
  });
3956
4116
  }
3957
4117
  };
@@ -4163,6 +4323,8 @@ function BottomSheet($$anchor, $$props) {
4163
4323
  persistent: persistent(),
4164
4324
  disableClose: disableClose(),
4165
4325
  disableDrag: disableDrag(),
4326
+ dragFrom: $$props.dragFrom,
4327
+ dragFromContent: $$props.dragFromContent,
4166
4328
  closeOnRouteChange: closeOnRouteChange(),
4167
4329
  stackEffect: stackEffect(),
4168
4330
  radius: $$props.radius,
@@ -4259,6 +4421,14 @@ function BottomSheet($$anchor, $$props) {
4259
4421
  $2__namespace.user_effect(() => {
4260
4422
  engine?.setDisableDrag(disableDrag());
4261
4423
  });
4424
+ $2__namespace.user_effect(() => {
4425
+ if ($$props.dragFromContent !== void 0) {
4426
+ engine?.setDragFromContent($$props.dragFromContent);
4427
+ }
4428
+ });
4429
+ $2__namespace.user_effect(() => {
4430
+ if ($$props.dragFrom !== void 0) engine?.setDragFrom($$props.dragFrom);
4431
+ });
4262
4432
  $2__namespace.user_effect(() => {
4263
4433
  if ($$props.radius !== void 0) engine?.setRadius($$props.radius);
4264
4434
  });
package/dist/svelte.d.cts CHANGED
@@ -88,6 +88,8 @@ export type BottomSheetProps<TId extends string = string> = {
88
88
  persistent?: boolean;
89
89
  disableClose?: boolean;
90
90
  disableDrag?: boolean;
91
+ dragFrom?: "handle" | "sheet" | "zones";
92
+ dragFromContent?: boolean;
91
93
  closeOnRouteChange?: boolean;
92
94
  stackEffect?: boolean;
93
95
  teleport?: boolean;
package/dist/svelte.d.ts CHANGED
@@ -88,6 +88,8 @@ export type BottomSheetProps<TId extends string = string> = {
88
88
  persistent?: boolean;
89
89
  disableClose?: boolean;
90
90
  disableDrag?: boolean;
91
+ dragFrom?: "handle" | "sheet" | "zones";
92
+ dragFromContent?: boolean;
91
93
  closeOnRouteChange?: boolean;
92
94
  stackEffect?: boolean;
93
95
  teleport?: boolean;