@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/react.js CHANGED
@@ -6,7 +6,7 @@ var __defProp = Object.defineProperty;
6
6
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
7
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
8
 
9
- // src/core/lifecycle/sheetStack.ts
9
+ // src/core/lifecycle/sheet-stack.ts
10
10
  var BASE_Z = 100;
11
11
  var STEP = 10;
12
12
  var SheetStack = class {
@@ -137,7 +137,7 @@ function createEventBus() {
137
137
  };
138
138
  }
139
139
 
140
- // src/core/primitives/cssLength.ts
140
+ // src/core/primitives/css-length.ts
141
141
  var probe = null;
142
142
  var PROBE_ATTR = "data-bs-probe";
143
143
  var ensureProbe = (axis) => {
@@ -191,7 +191,7 @@ var resolveSnap = (point, mode, measureFit) => {
191
191
  return Math.max(0, el.getBoundingClientRect().width);
192
192
  };
193
193
 
194
- // src/core/primitives/devWarn.ts
194
+ // src/core/primitives/dev-warn.ts
195
195
  function devWarn(...args) {
196
196
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
197
197
  console.warn(...args);
@@ -1524,7 +1524,7 @@ var AnimationRunner = class {
1524
1524
  }
1525
1525
  };
1526
1526
 
1527
- // src/core/lifecycle/focusTrap.ts
1527
+ // src/core/lifecycle/focus-trap.ts
1528
1528
  var FOCUSABLE = [
1529
1529
  "a[href]",
1530
1530
  "area[href]",
@@ -1642,7 +1642,7 @@ var installFocusTrap = (container, options = {}) => {
1642
1642
  };
1643
1643
  };
1644
1644
 
1645
- // src/core/lifecycle/scrollLock.ts
1645
+ // src/core/lifecycle/scroll-lock.ts
1646
1646
  var lockCount = 0;
1647
1647
  var savedStyles = null;
1648
1648
  var lockBodyScroll = () => {
@@ -1824,30 +1824,44 @@ var SampleRing = class {
1824
1824
  this.size = 0;
1825
1825
  }
1826
1826
  };
1827
- var installGestures = (handle, mode, callbacks) => {
1827
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1828
1828
  let activePointerId = null;
1829
1829
  let activePointerType = "touch";
1830
1830
  let startCoord = 0;
1831
1831
  let lastCoord = 0;
1832
+ let pendingStart = false;
1832
1833
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1833
1834
  const MAX_SAMPLES = 32;
1834
1835
  const samples = new SampleRing(MAX_SAMPLES);
1835
1836
  const axis = isAxisVertical(mode) ? "Y" : "X";
1836
1837
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1837
1838
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1839
+ const capture = (pointerId) => {
1840
+ try {
1841
+ handle.setPointerCapture(pointerId);
1842
+ } catch {
1843
+ }
1844
+ };
1845
+ const releaseTracking = () => {
1846
+ activePointerId = null;
1847
+ pendingStart = false;
1848
+ samples.reset();
1849
+ };
1838
1850
  const onPointerDown = (e) => {
1839
1851
  if (activePointerId !== null) return;
1840
1852
  if (e.button !== void 0 && e.button !== 0) return;
1853
+ if (options.shouldStart && !options.shouldStart(e)) return;
1841
1854
  activePointerId = e.pointerId;
1842
1855
  activePointerType = e.pointerType || "touch";
1843
1856
  startCoord = coordOf(e);
1844
1857
  lastCoord = startCoord;
1845
1858
  samples.reset();
1846
1859
  samples.push(e.timeStamp, startCoord);
1847
- try {
1848
- handle.setPointerCapture(e.pointerId);
1849
- } catch {
1860
+ if (options.deferStart) {
1861
+ pendingStart = true;
1862
+ return;
1850
1863
  }
1864
+ capture(e.pointerId);
1851
1865
  callbacks.onStart(startCoord, activePointerType);
1852
1866
  };
1853
1867
  const onPointerMove = (e) => {
@@ -1855,6 +1869,22 @@ var installGestures = (handle, mode, callbacks) => {
1855
1869
  const coord = coordOf(e);
1856
1870
  const rawDelta = coord - startCoord;
1857
1871
  const delta = rawDelta * sign;
1872
+ if (pendingStart) {
1873
+ const verdict = options.deferStart(e, delta);
1874
+ if (verdict === false) {
1875
+ releaseTracking();
1876
+ return;
1877
+ }
1878
+ if (verdict !== true) return;
1879
+ pendingStart = false;
1880
+ startCoord = coord;
1881
+ lastCoord = coord;
1882
+ samples.reset();
1883
+ samples.push(e.timeStamp, coord);
1884
+ capture(e.pointerId);
1885
+ callbacks.onStart(startCoord, activePointerType);
1886
+ return;
1887
+ }
1858
1888
  lastCoord = coord;
1859
1889
  samples.push(e.timeStamp, coord);
1860
1890
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1863,6 +1893,10 @@ var installGestures = (handle, mode, callbacks) => {
1863
1893
  };
1864
1894
  const finishGesture = (e) => {
1865
1895
  if (e.pointerId !== activePointerId) return;
1896
+ if (pendingStart) {
1897
+ releaseTracking();
1898
+ return;
1899
+ }
1866
1900
  const rawDelta = lastCoord - startCoord;
1867
1901
  const delta = rawDelta * sign;
1868
1902
  let velocity = 0;
@@ -1885,22 +1919,26 @@ var installGestures = (handle, mode, callbacks) => {
1885
1919
  };
1886
1920
  const onPointerCancel = (e) => {
1887
1921
  if (e.pointerId !== activePointerId) return;
1888
- activePointerId = null;
1889
- samples.reset();
1922
+ const wasPending = pendingStart;
1923
+ releaseTracking();
1924
+ if (wasPending) return;
1890
1925
  callbacks.onCancel?.();
1891
1926
  };
1892
1927
  handle.addEventListener("pointerdown", onPointerDown);
1893
1928
  handle.addEventListener("pointermove", onPointerMove);
1894
1929
  handle.addEventListener("pointerup", finishGesture);
1895
1930
  handle.addEventListener("pointercancel", onPointerCancel);
1931
+ const managesTouchAction = options.manageTouchAction !== false;
1896
1932
  const prevTouchAction = handle.style.touchAction;
1897
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1933
+ if (managesTouchAction) {
1934
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1935
+ }
1898
1936
  return () => {
1899
1937
  handle.removeEventListener("pointerdown", onPointerDown);
1900
1938
  handle.removeEventListener("pointermove", onPointerMove);
1901
1939
  handle.removeEventListener("pointerup", finishGesture);
1902
1940
  handle.removeEventListener("pointercancel", onPointerCancel);
1903
- handle.style.touchAction = prevTouchAction;
1941
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1904
1942
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1905
1943
  handle.releasePointerCapture(activePointerId);
1906
1944
  }
@@ -2019,7 +2057,7 @@ var GestureController = class {
2019
2057
  const restoreTo = this.dragStartSize;
2020
2058
  void this.deps.animateTo(restoreTo, 0);
2021
2059
  }
2022
- });
2060
+ }, this.deps.gestureOptions);
2023
2061
  return this.detach;
2024
2062
  }
2025
2063
  forceClearDragState() {
@@ -2031,6 +2069,41 @@ var GestureController = class {
2031
2069
  }
2032
2070
  };
2033
2071
 
2072
+ // src/core/primitives/drag-zones.ts
2073
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2074
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2075
+ var asElement = (target) => {
2076
+ const candidate = target;
2077
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2078
+ };
2079
+ function isDragAllowedFrom(target, mode) {
2080
+ const el = asElement(target);
2081
+ if (!el) return mode !== "zones";
2082
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2083
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2084
+ return true;
2085
+ }
2086
+
2087
+ // src/core/primitives/content-gesture.ts
2088
+ var CONTENT_DRAG_SLOP = 6;
2089
+ function decideContentGesture(input) {
2090
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2091
+ if (Math.abs(input.delta) < slop) return "pending";
2092
+ if (input.scrollTop > 0) return "scroll";
2093
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2094
+ return "drag";
2095
+ }
2096
+
2097
+ // src/core/primitives/touch-scroll-guard.ts
2098
+ function installTouchScrollGuard(surface, isDragging) {
2099
+ const onTouchMove = (e) => {
2100
+ if (!isDragging()) return;
2101
+ if (e.cancelable) e.preventDefault();
2102
+ };
2103
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2104
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2105
+ }
2106
+
2034
2107
  // src/core/primitives/aria-slider-writer.ts
2035
2108
  var AriaSliderWriter = class {
2036
2109
  constructor(handle, mode) {
@@ -2386,6 +2459,8 @@ function resolveEngineOptions(opts, extras) {
2386
2459
  mode: opts.mode ?? "bottom",
2387
2460
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2388
2461
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2462
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2463
+ dragFromContent: opts.dragFromContent ?? true,
2389
2464
  rubberBandEnabled: opts.rubberBand ?? true,
2390
2465
  closeOnBack: opts.closeOnBack ?? false,
2391
2466
  persistent: opts.persistent ?? false,
@@ -2471,6 +2546,8 @@ var BottomSheetCore = class {
2471
2546
  __publicField(this, "mode");
2472
2547
  __publicField(this, "flickVelocity");
2473
2548
  __publicField(this, "dragThreshold");
2549
+ __publicField(this, "dragFromMode");
2550
+ __publicField(this, "dragFromContentDefault");
2474
2551
  __publicField(this, "rubberBandEnabled");
2475
2552
  __publicField(this, "scrim");
2476
2553
  __publicField(this, "aria");
@@ -2488,8 +2565,11 @@ var BottomSheetCore = class {
2488
2565
  __publicField(this, "activeId");
2489
2566
  __publicField(this, "size", 0);
2490
2567
  __publicField(this, "gesture");
2568
+ __publicField(this, "contentGesture");
2569
+ __publicField(this, "detachSheetGesture", null);
2491
2570
  __publicField(this, "rootEl", null);
2492
2571
  __publicField(this, "destroyed", false);
2572
+ __publicField(this, "restClosed", false);
2493
2573
  __publicField(this, "currentViewTransition", null);
2494
2574
  __publicField(this, "currentAbort", new AbortController());
2495
2575
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2542,6 +2622,8 @@ var BottomSheetCore = class {
2542
2622
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2543
2623
  this.flickVelocity = resolved.flickVelocity;
2544
2624
  this.dragThreshold = resolved.dragThreshold;
2625
+ this.dragFromMode = resolved.dragFrom;
2626
+ this.dragFromContentDefault = resolved.dragFromContent;
2545
2627
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2546
2628
  this.scrim = new ScrimController(
2547
2629
  {
@@ -2568,7 +2650,7 @@ var BottomSheetCore = class {
2568
2650
  getRootEl: () => this.rootEl,
2569
2651
  applySize: (size) => this.applySize(size),
2570
2652
  getSize: () => this.size,
2571
- isDragging: () => this.gesture?.isDragging ?? false,
2653
+ isDragging: () => this.isDraggingAny(),
2572
2654
  applyAux: (size) => this.applySize(size, true),
2573
2655
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2574
2656
  },
@@ -2629,6 +2711,7 @@ var BottomSheetCore = class {
2629
2711
  const initial = this.snaps.findById(this.activeId);
2630
2712
  if (initial) this.size = initial.size;
2631
2713
  this.applySize(this.size);
2714
+ this.setRestClosed(this.size === 0);
2632
2715
  this.updateAriaSlider();
2633
2716
  this.scrollCache = createScrollCache({
2634
2717
  scrollContainer: this.scrollContainer,
@@ -2659,7 +2742,7 @@ var BottomSheetCore = class {
2659
2742
  (p) => p.size === "fit" || p.size === "content"
2660
2743
  ),
2661
2744
  isDestroyed: () => this.destroyed,
2662
- isDragging: () => this.gesture?.isDragging ?? false,
2745
+ isDragging: () => this.isDraggingAny(),
2663
2746
  recompute: () => this.recompute()
2664
2747
  })
2665
2748
  );
@@ -2685,7 +2768,7 @@ var BottomSheetCore = class {
2685
2768
  autoCollapseAfter
2686
2769
  },
2687
2770
  isDestroyed: () => this.destroyed,
2688
- isDragging: () => this.gesture?.isDragging ?? false,
2771
+ isDragging: () => this.isDraggingAny(),
2689
2772
  isAnimating: () => this.animation.isAnimating,
2690
2773
  isTopSheet: () => this.isTopSheet,
2691
2774
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2715,6 +2798,7 @@ var BottomSheetCore = class {
2715
2798
  void this.snapTo(id);
2716
2799
  },
2717
2800
  close: (reason) => this.close(reason),
2801
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2718
2802
  on: (event, fn) => this.on(event, fn),
2719
2803
  addTeardown: (fn) => this.teardowns.add(fn)
2720
2804
  };
@@ -2743,7 +2827,7 @@ var BottomSheetCore = class {
2743
2827
  return {
2744
2828
  size: this.size,
2745
2829
  activeId: this.activeId,
2746
- isDragging: this.gesture?.isDragging ?? false,
2830
+ isDragging: this.isDraggingAny(),
2747
2831
  isAnimating: this.animation.isAnimating,
2748
2832
  progress: this.computeProgress(this.size)
2749
2833
  };
@@ -2863,9 +2947,16 @@ var BottomSheetCore = class {
2863
2947
  progress: this.computeProgress(this.size)
2864
2948
  });
2865
2949
  if (withHaptic) this.haptic();
2950
+ this.setRestClosed(targetSize === 0);
2866
2951
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2867
2952
  if (targetSize === 0) this.emitCloseSequence();
2868
2953
  }
2954
+ setRestClosed(closed) {
2955
+ if (closed === this.restClosed) return;
2956
+ this.restClosed = closed;
2957
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
2958
+ else this.element.removeAttribute("data-bs-rest");
2959
+ }
2869
2960
  emitOpenSequence(id) {
2870
2961
  this.emit("open", { id });
2871
2962
  this.handleOpen();
@@ -2921,6 +3012,20 @@ var BottomSheetCore = class {
2921
3012
  if (this.destroyed) return;
2922
3013
  this.disableDragFlag = value;
2923
3014
  }
3015
+ setDragFromContent(value) {
3016
+ if (this.destroyed) return;
3017
+ this.dragFromContentDefault = value;
3018
+ }
3019
+ setDragFrom(mode) {
3020
+ if (this.destroyed || mode === this.dragFromMode) return;
3021
+ this.gesture?.forceClearDragState();
3022
+ this.detachSheetGesture?.();
3023
+ this.dragFromMode = mode;
3024
+ this.mountSheetGesture();
3025
+ }
3026
+ getDragFrom() {
3027
+ return this.dragFromMode;
3028
+ }
2924
3029
  isTop() {
2925
3030
  return this.isTopSheet;
2926
3031
  }
@@ -3075,12 +3180,24 @@ var BottomSheetCore = class {
3075
3180
  auditVhUsage(points);
3076
3181
  this.auditDuplicateSnapIds(points);
3077
3182
  this.snaps.setRaw(points);
3078
- if (allowed) this.snaps.setAllowedIds(allowed);
3183
+ if (allowed) {
3184
+ this.snaps.setAllowedIds(allowed);
3185
+ } else {
3186
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3187
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3188
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3189
+ }
3079
3190
  this.scrollCache.clear();
3080
3191
  this.scrim.invalidateOpacityCache();
3081
3192
  const current = this.snaps.findById(this.activeId);
3082
3193
  if (current) {
3083
3194
  this.applySize(current.size);
3195
+ } else {
3196
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3197
+ if (fallback) {
3198
+ this.activeId = fallback.id;
3199
+ this.applySize(fallback.size);
3200
+ }
3084
3201
  }
3085
3202
  this.updateAriaSlider();
3086
3203
  }
@@ -3090,7 +3207,7 @@ var BottomSheetCore = class {
3090
3207
  this.snaps.recompute();
3091
3208
  this.maxHeight.clampTo();
3092
3209
  this.scrim.invalidateOpacityCache();
3093
- if (this.gesture?.isDragging) return;
3210
+ if (this.isDraggingAny()) return;
3094
3211
  this.healActiveSnap();
3095
3212
  }
3096
3213
  healActiveSnap() {
@@ -3112,6 +3229,8 @@ var BottomSheetCore = class {
3112
3229
  this.bus.clear();
3113
3230
  this.animation.cancel();
3114
3231
  this.gesture?.forceClearDragState();
3232
+ this.contentGesture?.forceClearDragState();
3233
+ this.setRestClosed(false);
3115
3234
  this.teardowns.drain();
3116
3235
  this.scrim.destroy();
3117
3236
  this.animation.destroy();
@@ -3224,11 +3343,12 @@ var BottomSheetCore = class {
3224
3343
  const scale = Math.max(1 - depth * 0.04, 0.86);
3225
3344
  this.element.style.scale = scale === 1 ? "" : String(scale);
3226
3345
  }
3227
- attach() {
3228
- this.gesture = new GestureController({
3229
- handle: this.handle,
3346
+ buildGestureDeps(surface, gestureOptions) {
3347
+ return {
3348
+ handle: surface,
3230
3349
  element: this.element,
3231
3350
  mode: this.mode,
3351
+ gestureOptions,
3232
3352
  getRoot: () => this.rootEl,
3233
3353
  getDragContext: () => {
3234
3354
  const buf = this.dragContextBuf;
@@ -3247,8 +3367,88 @@ var BottomSheetCore = class {
3247
3367
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3248
3368
  emit: (event, payload) => this.emit(event, payload),
3249
3369
  listenerCount: (event) => this.bus.listenerCount(event)
3370
+ };
3371
+ }
3372
+ isContentDragAllowed() {
3373
+ if (this.disableDragFlag) return false;
3374
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3375
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3376
+ }
3377
+ decideContentDrag(surface, e, delta) {
3378
+ if (this.destroyed) return false;
3379
+ if (e.pointerType === "mouse") return false;
3380
+ if (this.gesture?.isDragging) return false;
3381
+ if (!this.isContentDragAllowed()) return false;
3382
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3383
+ const decision = decideContentGesture({
3384
+ delta,
3385
+ scrollTop: surface.scrollTop,
3386
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3387
+ });
3388
+ if (decision === "pending") return null;
3389
+ return decision === "drag";
3390
+ }
3391
+ attachDragSurface(surface, kind) {
3392
+ if (this.destroyed || kind !== "content") return;
3393
+ if (this.contentGesture) return;
3394
+ const controller = new GestureController(
3395
+ this.buildGestureDeps(surface, {
3396
+ manageTouchAction: false,
3397
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3398
+ })
3399
+ );
3400
+ this.contentGesture = controller;
3401
+ const detachGestures = controller.install();
3402
+ const detachGuard = installTouchScrollGuard(
3403
+ surface,
3404
+ () => controller.isDragging
3405
+ );
3406
+ return () => {
3407
+ detachGuard();
3408
+ detachGestures();
3409
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3410
+ };
3411
+ }
3412
+ sheetDragSurface() {
3413
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3414
+ }
3415
+ canStartSheetDrag(e) {
3416
+ if (this.contentGesture?.isDragging) return false;
3417
+ const target = e.target;
3418
+ const scoped = typeof target?.closest === "function";
3419
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3420
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3421
+ return true;
3422
+ }
3423
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3424
+ if (this.dragFromMode === "handle") return true;
3425
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3426
+ return false;
3427
+ }
3428
+ return true;
3429
+ }
3430
+ mountSheetGesture() {
3431
+ const surface = this.sheetDragSurface();
3432
+ const controller = new GestureController(
3433
+ this.buildGestureDeps(surface, {
3434
+ shouldStart: (e) => this.canStartSheetDrag(e),
3435
+ manageTouchAction: this.dragFromMode === "handle"
3436
+ })
3437
+ );
3438
+ this.gesture = controller;
3439
+ const detachGestures = controller.install();
3440
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3441
+ this.detachSheetGesture = () => {
3442
+ detachGuard?.();
3443
+ detachGestures();
3444
+ };
3445
+ }
3446
+ attach() {
3447
+ this.mountSheetGesture();
3448
+ this.teardowns.add(() => {
3449
+ this.detachSheetGesture?.();
3450
+ this.detachSheetGesture = null;
3250
3451
  });
3251
- this.teardowns.add(this.gesture.install());
3252
3452
  try {
3253
3453
  this.attachFeatures();
3254
3454
  } catch (err) {
@@ -3285,7 +3485,7 @@ var BottomSheetCore = class {
3285
3485
  element: this.element,
3286
3486
  getMode: () => this.mode,
3287
3487
  isDestroyed: () => this.destroyed,
3288
- isDragging: () => this.gesture?.isDragging ?? false,
3488
+ isDragging: () => this.isDraggingAny(),
3289
3489
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3290
3490
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3291
3491
  getSize: () => this.size,
@@ -3383,8 +3583,11 @@ var BottomSheetCore = class {
3383
3583
  );
3384
3584
  });
3385
3585
  }
3586
+ isDraggingAny() {
3587
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3588
+ }
3386
3589
  clearWillChangeIfIdle() {
3387
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3590
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3388
3591
  this.element.style.willChange = "";
3389
3592
  }
3390
3593
  haptic() {
@@ -3397,8 +3600,9 @@ var BottomSheetCore = class {
3397
3600
  }
3398
3601
  applySize(size, skipTransform = false) {
3399
3602
  const cap = this.snaps.getMaxAxisSize();
3400
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3603
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3401
3604
  this.size = clamped;
3605
+ if (clamped > 0) this.setRestClosed(false);
3402
3606
  const offset = cap - clamped;
3403
3607
  const style = this.element.style;
3404
3608
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3778,50 +3982,10 @@ function installAutoCollapse(deps) {
3778
3982
  };
3779
3983
  }
3780
3984
 
3781
- // src/core/features/content-swipe.ts
3782
- var CONTENT_PULL_THRESHOLD = 60;
3783
- function installContentSwipe(deps) {
3784
- const { container } = deps;
3785
- let startY = 0;
3786
- let startScrollTop = 0;
3787
- let moved = false;
3788
- const onTouchStart = (e) => {
3789
- const t = e.touches[0];
3790
- if (!t) return;
3791
- startY = t.clientY;
3792
- startScrollTop = container.scrollTop;
3793
- moved = false;
3794
- };
3795
- const onTouchMove = (e) => {
3796
- const t = e.touches[0];
3797
- if (!t) return;
3798
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3799
- };
3800
- const onTouchEnd = (e) => {
3801
- if (!moved) return;
3802
- if (deps.isDragging() || deps.isAnimating()) return;
3803
- if (container.scrollTop !== startScrollTop) return;
3804
- const t = e.changedTouches[0];
3805
- if (!t) return;
3806
- const deltaY = t.clientY - startY;
3807
- const atTop = container.scrollTop <= 0;
3808
- const allowed = deps.getAllowedIds();
3809
- const idx = allowed.indexOf(deps.getActiveId());
3810
- if (idx === -1) return;
3811
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3812
- deps.snapTo(allowed[idx - 1]);
3813
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3814
- deps.snapTo(allowed[idx + 1]);
3815
- }
3816
- };
3817
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3818
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3819
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3820
- return () => {
3821
- container.removeEventListener("touchstart", onTouchStart);
3822
- container.removeEventListener("touchmove", onTouchMove);
3823
- container.removeEventListener("touchend", onTouchEnd);
3824
- };
3985
+ // src/core/features/content-drag.ts
3986
+ function installContentDrag(deps) {
3987
+ const detach = deps.attachDragSurface(deps.container, "content");
3988
+ return () => detach?.();
3825
3989
  }
3826
3990
 
3827
3991
  // src/core/features/visual-viewport.ts
@@ -3929,13 +4093,9 @@ function contentSwipeFeature() {
3929
4093
  stage: "attach",
3930
4094
  install: (ctx) => {
3931
4095
  if (!ctx.scrollContainer) return;
3932
- return installContentSwipe({
4096
+ return installContentDrag({
3933
4097
  container: ctx.scrollContainer,
3934
- isDragging: ctx.isDragging,
3935
- isAnimating: ctx.isAnimating,
3936
- getAllowedIds: ctx.allowedIdsBySize,
3937
- getActiveId: ctx.getActiveId,
3938
- snapTo: ctx.snapTo
4098
+ attachDragSurface: ctx.attachDragSurface
3939
4099
  });
3940
4100
  }
3941
4101
  };
@@ -4051,6 +4211,16 @@ function useEnginePropSync(engineRef, opts) {
4051
4211
  engineRef.current?.setDisableDrag(opts.disableDrag);
4052
4212
  }
4053
4213
  }, [opts.disableDrag]);
4214
+ useEffect(() => {
4215
+ if (opts.dragFrom !== void 0) {
4216
+ engineRef.current?.setDragFrom(opts.dragFrom);
4217
+ }
4218
+ }, [opts.dragFrom]);
4219
+ useEffect(() => {
4220
+ if (opts.dragFromContent !== void 0) {
4221
+ engineRef.current?.setDragFromContent(opts.dragFromContent);
4222
+ }
4223
+ }, [opts.disableDrag]);
4054
4224
  }
4055
4225
 
4056
4226
  // src/core/features/teleport.ts
@@ -4199,7 +4369,9 @@ function useBottomSheet(opts) {
4199
4369
  maxHeight: opts.maxHeight,
4200
4370
  persistent: opts.persistent,
4201
4371
  disableClose: opts.disableClose,
4202
- disableDrag: opts.disableDrag
4372
+ disableDrag: opts.disableDrag,
4373
+ dragFrom: opts.dragFrom,
4374
+ dragFromContent: opts.dragFromContent
4203
4375
  });
4204
4376
  const subscribe = useCallback((fn) => {
4205
4377
  subscribersRef.current.add(fn);
@@ -4267,7 +4439,6 @@ function useBottomSheet(opts) {
4267
4439
  addAnchor,
4268
4440
  setScrimStages,
4269
4441
  recompute,
4270
- engine: engineRef.current,
4271
4442
  getEngine
4272
4443
  }),
4273
4444
  [