@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.cjs CHANGED
@@ -8,7 +8,7 @@ var __defProp = Object.defineProperty;
8
8
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
9
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
10
10
 
11
- // src/core/lifecycle/sheetStack.ts
11
+ // src/core/lifecycle/sheet-stack.ts
12
12
  var BASE_Z = 100;
13
13
  var STEP = 10;
14
14
  var SheetStack = class {
@@ -139,7 +139,7 @@ function createEventBus() {
139
139
  };
140
140
  }
141
141
 
142
- // src/core/primitives/cssLength.ts
142
+ // src/core/primitives/css-length.ts
143
143
  var probe = null;
144
144
  var PROBE_ATTR = "data-bs-probe";
145
145
  var ensureProbe = (axis) => {
@@ -193,7 +193,7 @@ var resolveSnap = (point, mode, measureFit) => {
193
193
  return Math.max(0, el.getBoundingClientRect().width);
194
194
  };
195
195
 
196
- // src/core/primitives/devWarn.ts
196
+ // src/core/primitives/dev-warn.ts
197
197
  function devWarn(...args) {
198
198
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
199
199
  console.warn(...args);
@@ -1526,7 +1526,7 @@ var AnimationRunner = class {
1526
1526
  }
1527
1527
  };
1528
1528
 
1529
- // src/core/lifecycle/focusTrap.ts
1529
+ // src/core/lifecycle/focus-trap.ts
1530
1530
  var FOCUSABLE = [
1531
1531
  "a[href]",
1532
1532
  "area[href]",
@@ -1644,7 +1644,7 @@ var installFocusTrap = (container, options = {}) => {
1644
1644
  };
1645
1645
  };
1646
1646
 
1647
- // src/core/lifecycle/scrollLock.ts
1647
+ // src/core/lifecycle/scroll-lock.ts
1648
1648
  var lockCount = 0;
1649
1649
  var savedStyles = null;
1650
1650
  var lockBodyScroll = () => {
@@ -1826,30 +1826,44 @@ var SampleRing = class {
1826
1826
  this.size = 0;
1827
1827
  }
1828
1828
  };
1829
- var installGestures = (handle, mode, callbacks) => {
1829
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1830
1830
  let activePointerId = null;
1831
1831
  let activePointerType = "touch";
1832
1832
  let startCoord = 0;
1833
1833
  let lastCoord = 0;
1834
+ let pendingStart = false;
1834
1835
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1835
1836
  const MAX_SAMPLES = 32;
1836
1837
  const samples = new SampleRing(MAX_SAMPLES);
1837
1838
  const axis = isAxisVertical(mode) ? "Y" : "X";
1838
1839
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1839
1840
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1841
+ const capture = (pointerId) => {
1842
+ try {
1843
+ handle.setPointerCapture(pointerId);
1844
+ } catch {
1845
+ }
1846
+ };
1847
+ const releaseTracking = () => {
1848
+ activePointerId = null;
1849
+ pendingStart = false;
1850
+ samples.reset();
1851
+ };
1840
1852
  const onPointerDown = (e) => {
1841
1853
  if (activePointerId !== null) return;
1842
1854
  if (e.button !== void 0 && e.button !== 0) return;
1855
+ if (options.shouldStart && !options.shouldStart(e)) return;
1843
1856
  activePointerId = e.pointerId;
1844
1857
  activePointerType = e.pointerType || "touch";
1845
1858
  startCoord = coordOf(e);
1846
1859
  lastCoord = startCoord;
1847
1860
  samples.reset();
1848
1861
  samples.push(e.timeStamp, startCoord);
1849
- try {
1850
- handle.setPointerCapture(e.pointerId);
1851
- } catch {
1862
+ if (options.deferStart) {
1863
+ pendingStart = true;
1864
+ return;
1852
1865
  }
1866
+ capture(e.pointerId);
1853
1867
  callbacks.onStart(startCoord, activePointerType);
1854
1868
  };
1855
1869
  const onPointerMove = (e) => {
@@ -1857,6 +1871,22 @@ var installGestures = (handle, mode, callbacks) => {
1857
1871
  const coord = coordOf(e);
1858
1872
  const rawDelta = coord - startCoord;
1859
1873
  const delta = rawDelta * sign;
1874
+ if (pendingStart) {
1875
+ const verdict = options.deferStart(e, delta);
1876
+ if (verdict === false) {
1877
+ releaseTracking();
1878
+ return;
1879
+ }
1880
+ if (verdict !== true) return;
1881
+ pendingStart = false;
1882
+ startCoord = coord;
1883
+ lastCoord = coord;
1884
+ samples.reset();
1885
+ samples.push(e.timeStamp, coord);
1886
+ capture(e.pointerId);
1887
+ callbacks.onStart(startCoord, activePointerType);
1888
+ return;
1889
+ }
1860
1890
  lastCoord = coord;
1861
1891
  samples.push(e.timeStamp, coord);
1862
1892
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1865,6 +1895,10 @@ var installGestures = (handle, mode, callbacks) => {
1865
1895
  };
1866
1896
  const finishGesture = (e) => {
1867
1897
  if (e.pointerId !== activePointerId) return;
1898
+ if (pendingStart) {
1899
+ releaseTracking();
1900
+ return;
1901
+ }
1868
1902
  const rawDelta = lastCoord - startCoord;
1869
1903
  const delta = rawDelta * sign;
1870
1904
  let velocity = 0;
@@ -1887,22 +1921,26 @@ var installGestures = (handle, mode, callbacks) => {
1887
1921
  };
1888
1922
  const onPointerCancel = (e) => {
1889
1923
  if (e.pointerId !== activePointerId) return;
1890
- activePointerId = null;
1891
- samples.reset();
1924
+ const wasPending = pendingStart;
1925
+ releaseTracking();
1926
+ if (wasPending) return;
1892
1927
  callbacks.onCancel?.();
1893
1928
  };
1894
1929
  handle.addEventListener("pointerdown", onPointerDown);
1895
1930
  handle.addEventListener("pointermove", onPointerMove);
1896
1931
  handle.addEventListener("pointerup", finishGesture);
1897
1932
  handle.addEventListener("pointercancel", onPointerCancel);
1933
+ const managesTouchAction = options.manageTouchAction !== false;
1898
1934
  const prevTouchAction = handle.style.touchAction;
1899
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1935
+ if (managesTouchAction) {
1936
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1937
+ }
1900
1938
  return () => {
1901
1939
  handle.removeEventListener("pointerdown", onPointerDown);
1902
1940
  handle.removeEventListener("pointermove", onPointerMove);
1903
1941
  handle.removeEventListener("pointerup", finishGesture);
1904
1942
  handle.removeEventListener("pointercancel", onPointerCancel);
1905
- handle.style.touchAction = prevTouchAction;
1943
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1906
1944
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1907
1945
  handle.releasePointerCapture(activePointerId);
1908
1946
  }
@@ -2021,7 +2059,7 @@ var GestureController = class {
2021
2059
  const restoreTo = this.dragStartSize;
2022
2060
  void this.deps.animateTo(restoreTo, 0);
2023
2061
  }
2024
- });
2062
+ }, this.deps.gestureOptions);
2025
2063
  return this.detach;
2026
2064
  }
2027
2065
  forceClearDragState() {
@@ -2033,6 +2071,41 @@ var GestureController = class {
2033
2071
  }
2034
2072
  };
2035
2073
 
2074
+ // src/core/primitives/drag-zones.ts
2075
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2076
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2077
+ var asElement = (target) => {
2078
+ const candidate = target;
2079
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2080
+ };
2081
+ function isDragAllowedFrom(target, mode) {
2082
+ const el = asElement(target);
2083
+ if (!el) return mode !== "zones";
2084
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2085
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2086
+ return true;
2087
+ }
2088
+
2089
+ // src/core/primitives/content-gesture.ts
2090
+ var CONTENT_DRAG_SLOP = 6;
2091
+ function decideContentGesture(input) {
2092
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2093
+ if (Math.abs(input.delta) < slop) return "pending";
2094
+ if (input.scrollTop > 0) return "scroll";
2095
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2096
+ return "drag";
2097
+ }
2098
+
2099
+ // src/core/primitives/touch-scroll-guard.ts
2100
+ function installTouchScrollGuard(surface, isDragging) {
2101
+ const onTouchMove = (e) => {
2102
+ if (!isDragging()) return;
2103
+ if (e.cancelable) e.preventDefault();
2104
+ };
2105
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2106
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2107
+ }
2108
+
2036
2109
  // src/core/primitives/aria-slider-writer.ts
2037
2110
  var AriaSliderWriter = class {
2038
2111
  constructor(handle, mode) {
@@ -2388,6 +2461,8 @@ function resolveEngineOptions(opts, extras) {
2388
2461
  mode: opts.mode ?? "bottom",
2389
2462
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2390
2463
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2464
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2465
+ dragFromContent: opts.dragFromContent ?? true,
2391
2466
  rubberBandEnabled: opts.rubberBand ?? true,
2392
2467
  closeOnBack: opts.closeOnBack ?? false,
2393
2468
  persistent: opts.persistent ?? false,
@@ -2473,6 +2548,8 @@ var BottomSheetCore = class {
2473
2548
  __publicField(this, "mode");
2474
2549
  __publicField(this, "flickVelocity");
2475
2550
  __publicField(this, "dragThreshold");
2551
+ __publicField(this, "dragFromMode");
2552
+ __publicField(this, "dragFromContentDefault");
2476
2553
  __publicField(this, "rubberBandEnabled");
2477
2554
  __publicField(this, "scrim");
2478
2555
  __publicField(this, "aria");
@@ -2490,8 +2567,11 @@ var BottomSheetCore = class {
2490
2567
  __publicField(this, "activeId");
2491
2568
  __publicField(this, "size", 0);
2492
2569
  __publicField(this, "gesture");
2570
+ __publicField(this, "contentGesture");
2571
+ __publicField(this, "detachSheetGesture", null);
2493
2572
  __publicField(this, "rootEl", null);
2494
2573
  __publicField(this, "destroyed", false);
2574
+ __publicField(this, "restClosed", false);
2495
2575
  __publicField(this, "currentViewTransition", null);
2496
2576
  __publicField(this, "currentAbort", new AbortController());
2497
2577
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2544,6 +2624,8 @@ var BottomSheetCore = class {
2544
2624
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2545
2625
  this.flickVelocity = resolved.flickVelocity;
2546
2626
  this.dragThreshold = resolved.dragThreshold;
2627
+ this.dragFromMode = resolved.dragFrom;
2628
+ this.dragFromContentDefault = resolved.dragFromContent;
2547
2629
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2548
2630
  this.scrim = new ScrimController(
2549
2631
  {
@@ -2570,7 +2652,7 @@ var BottomSheetCore = class {
2570
2652
  getRootEl: () => this.rootEl,
2571
2653
  applySize: (size) => this.applySize(size),
2572
2654
  getSize: () => this.size,
2573
- isDragging: () => this.gesture?.isDragging ?? false,
2655
+ isDragging: () => this.isDraggingAny(),
2574
2656
  applyAux: (size) => this.applySize(size, true),
2575
2657
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2576
2658
  },
@@ -2631,6 +2713,7 @@ var BottomSheetCore = class {
2631
2713
  const initial = this.snaps.findById(this.activeId);
2632
2714
  if (initial) this.size = initial.size;
2633
2715
  this.applySize(this.size);
2716
+ this.setRestClosed(this.size === 0);
2634
2717
  this.updateAriaSlider();
2635
2718
  this.scrollCache = createScrollCache({
2636
2719
  scrollContainer: this.scrollContainer,
@@ -2661,7 +2744,7 @@ var BottomSheetCore = class {
2661
2744
  (p) => p.size === "fit" || p.size === "content"
2662
2745
  ),
2663
2746
  isDestroyed: () => this.destroyed,
2664
- isDragging: () => this.gesture?.isDragging ?? false,
2747
+ isDragging: () => this.isDraggingAny(),
2665
2748
  recompute: () => this.recompute()
2666
2749
  })
2667
2750
  );
@@ -2687,7 +2770,7 @@ var BottomSheetCore = class {
2687
2770
  autoCollapseAfter
2688
2771
  },
2689
2772
  isDestroyed: () => this.destroyed,
2690
- isDragging: () => this.gesture?.isDragging ?? false,
2773
+ isDragging: () => this.isDraggingAny(),
2691
2774
  isAnimating: () => this.animation.isAnimating,
2692
2775
  isTopSheet: () => this.isTopSheet,
2693
2776
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2717,6 +2800,7 @@ var BottomSheetCore = class {
2717
2800
  void this.snapTo(id);
2718
2801
  },
2719
2802
  close: (reason) => this.close(reason),
2803
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2720
2804
  on: (event, fn) => this.on(event, fn),
2721
2805
  addTeardown: (fn) => this.teardowns.add(fn)
2722
2806
  };
@@ -2745,7 +2829,7 @@ var BottomSheetCore = class {
2745
2829
  return {
2746
2830
  size: this.size,
2747
2831
  activeId: this.activeId,
2748
- isDragging: this.gesture?.isDragging ?? false,
2832
+ isDragging: this.isDraggingAny(),
2749
2833
  isAnimating: this.animation.isAnimating,
2750
2834
  progress: this.computeProgress(this.size)
2751
2835
  };
@@ -2865,9 +2949,16 @@ var BottomSheetCore = class {
2865
2949
  progress: this.computeProgress(this.size)
2866
2950
  });
2867
2951
  if (withHaptic) this.haptic();
2952
+ this.setRestClosed(targetSize === 0);
2868
2953
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2869
2954
  if (targetSize === 0) this.emitCloseSequence();
2870
2955
  }
2956
+ setRestClosed(closed) {
2957
+ if (closed === this.restClosed) return;
2958
+ this.restClosed = closed;
2959
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
2960
+ else this.element.removeAttribute("data-bs-rest");
2961
+ }
2871
2962
  emitOpenSequence(id) {
2872
2963
  this.emit("open", { id });
2873
2964
  this.handleOpen();
@@ -2923,6 +3014,20 @@ var BottomSheetCore = class {
2923
3014
  if (this.destroyed) return;
2924
3015
  this.disableDragFlag = value;
2925
3016
  }
3017
+ setDragFromContent(value) {
3018
+ if (this.destroyed) return;
3019
+ this.dragFromContentDefault = value;
3020
+ }
3021
+ setDragFrom(mode) {
3022
+ if (this.destroyed || mode === this.dragFromMode) return;
3023
+ this.gesture?.forceClearDragState();
3024
+ this.detachSheetGesture?.();
3025
+ this.dragFromMode = mode;
3026
+ this.mountSheetGesture();
3027
+ }
3028
+ getDragFrom() {
3029
+ return this.dragFromMode;
3030
+ }
2926
3031
  isTop() {
2927
3032
  return this.isTopSheet;
2928
3033
  }
@@ -3077,12 +3182,24 @@ var BottomSheetCore = class {
3077
3182
  auditVhUsage(points);
3078
3183
  this.auditDuplicateSnapIds(points);
3079
3184
  this.snaps.setRaw(points);
3080
- if (allowed) this.snaps.setAllowedIds(allowed);
3185
+ if (allowed) {
3186
+ this.snaps.setAllowedIds(allowed);
3187
+ } else {
3188
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3189
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3190
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3191
+ }
3081
3192
  this.scrollCache.clear();
3082
3193
  this.scrim.invalidateOpacityCache();
3083
3194
  const current = this.snaps.findById(this.activeId);
3084
3195
  if (current) {
3085
3196
  this.applySize(current.size);
3197
+ } else {
3198
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3199
+ if (fallback) {
3200
+ this.activeId = fallback.id;
3201
+ this.applySize(fallback.size);
3202
+ }
3086
3203
  }
3087
3204
  this.updateAriaSlider();
3088
3205
  }
@@ -3092,7 +3209,7 @@ var BottomSheetCore = class {
3092
3209
  this.snaps.recompute();
3093
3210
  this.maxHeight.clampTo();
3094
3211
  this.scrim.invalidateOpacityCache();
3095
- if (this.gesture?.isDragging) return;
3212
+ if (this.isDraggingAny()) return;
3096
3213
  this.healActiveSnap();
3097
3214
  }
3098
3215
  healActiveSnap() {
@@ -3114,6 +3231,8 @@ var BottomSheetCore = class {
3114
3231
  this.bus.clear();
3115
3232
  this.animation.cancel();
3116
3233
  this.gesture?.forceClearDragState();
3234
+ this.contentGesture?.forceClearDragState();
3235
+ this.setRestClosed(false);
3117
3236
  this.teardowns.drain();
3118
3237
  this.scrim.destroy();
3119
3238
  this.animation.destroy();
@@ -3226,11 +3345,12 @@ var BottomSheetCore = class {
3226
3345
  const scale = Math.max(1 - depth * 0.04, 0.86);
3227
3346
  this.element.style.scale = scale === 1 ? "" : String(scale);
3228
3347
  }
3229
- attach() {
3230
- this.gesture = new GestureController({
3231
- handle: this.handle,
3348
+ buildGestureDeps(surface, gestureOptions) {
3349
+ return {
3350
+ handle: surface,
3232
3351
  element: this.element,
3233
3352
  mode: this.mode,
3353
+ gestureOptions,
3234
3354
  getRoot: () => this.rootEl,
3235
3355
  getDragContext: () => {
3236
3356
  const buf = this.dragContextBuf;
@@ -3249,8 +3369,88 @@ var BottomSheetCore = class {
3249
3369
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3250
3370
  emit: (event, payload) => this.emit(event, payload),
3251
3371
  listenerCount: (event) => this.bus.listenerCount(event)
3372
+ };
3373
+ }
3374
+ isContentDragAllowed() {
3375
+ if (this.disableDragFlag) return false;
3376
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3377
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3378
+ }
3379
+ decideContentDrag(surface, e, delta) {
3380
+ if (this.destroyed) return false;
3381
+ if (e.pointerType === "mouse") return false;
3382
+ if (this.gesture?.isDragging) return false;
3383
+ if (!this.isContentDragAllowed()) return false;
3384
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3385
+ const decision = decideContentGesture({
3386
+ delta,
3387
+ scrollTop: surface.scrollTop,
3388
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3389
+ });
3390
+ if (decision === "pending") return null;
3391
+ return decision === "drag";
3392
+ }
3393
+ attachDragSurface(surface, kind) {
3394
+ if (this.destroyed || kind !== "content") return;
3395
+ if (this.contentGesture) return;
3396
+ const controller = new GestureController(
3397
+ this.buildGestureDeps(surface, {
3398
+ manageTouchAction: false,
3399
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3400
+ })
3401
+ );
3402
+ this.contentGesture = controller;
3403
+ const detachGestures = controller.install();
3404
+ const detachGuard = installTouchScrollGuard(
3405
+ surface,
3406
+ () => controller.isDragging
3407
+ );
3408
+ return () => {
3409
+ detachGuard();
3410
+ detachGestures();
3411
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3412
+ };
3413
+ }
3414
+ sheetDragSurface() {
3415
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3416
+ }
3417
+ canStartSheetDrag(e) {
3418
+ if (this.contentGesture?.isDragging) return false;
3419
+ const target = e.target;
3420
+ const scoped = typeof target?.closest === "function";
3421
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3422
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3423
+ return true;
3424
+ }
3425
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3426
+ if (this.dragFromMode === "handle") return true;
3427
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3428
+ return false;
3429
+ }
3430
+ return true;
3431
+ }
3432
+ mountSheetGesture() {
3433
+ const surface = this.sheetDragSurface();
3434
+ const controller = new GestureController(
3435
+ this.buildGestureDeps(surface, {
3436
+ shouldStart: (e) => this.canStartSheetDrag(e),
3437
+ manageTouchAction: this.dragFromMode === "handle"
3438
+ })
3439
+ );
3440
+ this.gesture = controller;
3441
+ const detachGestures = controller.install();
3442
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3443
+ this.detachSheetGesture = () => {
3444
+ detachGuard?.();
3445
+ detachGestures();
3446
+ };
3447
+ }
3448
+ attach() {
3449
+ this.mountSheetGesture();
3450
+ this.teardowns.add(() => {
3451
+ this.detachSheetGesture?.();
3452
+ this.detachSheetGesture = null;
3252
3453
  });
3253
- this.teardowns.add(this.gesture.install());
3254
3454
  try {
3255
3455
  this.attachFeatures();
3256
3456
  } catch (err) {
@@ -3287,7 +3487,7 @@ var BottomSheetCore = class {
3287
3487
  element: this.element,
3288
3488
  getMode: () => this.mode,
3289
3489
  isDestroyed: () => this.destroyed,
3290
- isDragging: () => this.gesture?.isDragging ?? false,
3490
+ isDragging: () => this.isDraggingAny(),
3291
3491
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3292
3492
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3293
3493
  getSize: () => this.size,
@@ -3385,8 +3585,11 @@ var BottomSheetCore = class {
3385
3585
  );
3386
3586
  });
3387
3587
  }
3588
+ isDraggingAny() {
3589
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3590
+ }
3388
3591
  clearWillChangeIfIdle() {
3389
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3592
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3390
3593
  this.element.style.willChange = "";
3391
3594
  }
3392
3595
  haptic() {
@@ -3399,8 +3602,9 @@ var BottomSheetCore = class {
3399
3602
  }
3400
3603
  applySize(size, skipTransform = false) {
3401
3604
  const cap = this.snaps.getMaxAxisSize();
3402
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3605
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3403
3606
  this.size = clamped;
3607
+ if (clamped > 0) this.setRestClosed(false);
3404
3608
  const offset = cap - clamped;
3405
3609
  const style = this.element.style;
3406
3610
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3780,50 +3984,10 @@ function installAutoCollapse(deps) {
3780
3984
  };
3781
3985
  }
3782
3986
 
3783
- // src/core/features/content-swipe.ts
3784
- var CONTENT_PULL_THRESHOLD = 60;
3785
- function installContentSwipe(deps) {
3786
- const { container } = deps;
3787
- let startY = 0;
3788
- let startScrollTop = 0;
3789
- let moved = false;
3790
- const onTouchStart = (e) => {
3791
- const t = e.touches[0];
3792
- if (!t) return;
3793
- startY = t.clientY;
3794
- startScrollTop = container.scrollTop;
3795
- moved = false;
3796
- };
3797
- const onTouchMove = (e) => {
3798
- const t = e.touches[0];
3799
- if (!t) return;
3800
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3801
- };
3802
- const onTouchEnd = (e) => {
3803
- if (!moved) return;
3804
- if (deps.isDragging() || deps.isAnimating()) return;
3805
- if (container.scrollTop !== startScrollTop) return;
3806
- const t = e.changedTouches[0];
3807
- if (!t) return;
3808
- const deltaY = t.clientY - startY;
3809
- const atTop = container.scrollTop <= 0;
3810
- const allowed = deps.getAllowedIds();
3811
- const idx = allowed.indexOf(deps.getActiveId());
3812
- if (idx === -1) return;
3813
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3814
- deps.snapTo(allowed[idx - 1]);
3815
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3816
- deps.snapTo(allowed[idx + 1]);
3817
- }
3818
- };
3819
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3820
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3821
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3822
- return () => {
3823
- container.removeEventListener("touchstart", onTouchStart);
3824
- container.removeEventListener("touchmove", onTouchMove);
3825
- container.removeEventListener("touchend", onTouchEnd);
3826
- };
3987
+ // src/core/features/content-drag.ts
3988
+ function installContentDrag(deps) {
3989
+ const detach = deps.attachDragSurface(deps.container, "content");
3990
+ return () => detach?.();
3827
3991
  }
3828
3992
 
3829
3993
  // src/core/features/visual-viewport.ts
@@ -3931,13 +4095,9 @@ function contentSwipeFeature() {
3931
4095
  stage: "attach",
3932
4096
  install: (ctx) => {
3933
4097
  if (!ctx.scrollContainer) return;
3934
- return installContentSwipe({
4098
+ return installContentDrag({
3935
4099
  container: ctx.scrollContainer,
3936
- isDragging: ctx.isDragging,
3937
- isAnimating: ctx.isAnimating,
3938
- getAllowedIds: ctx.allowedIdsBySize,
3939
- getActiveId: ctx.getActiveId,
3940
- snapTo: ctx.snapTo
4100
+ attachDragSurface: ctx.attachDragSurface
3941
4101
  });
3942
4102
  }
3943
4103
  };
@@ -4053,6 +4213,16 @@ function useEnginePropSync(engineRef, opts) {
4053
4213
  engineRef.current?.setDisableDrag(opts.disableDrag);
4054
4214
  }
4055
4215
  }, [opts.disableDrag]);
4216
+ react.useEffect(() => {
4217
+ if (opts.dragFrom !== void 0) {
4218
+ engineRef.current?.setDragFrom(opts.dragFrom);
4219
+ }
4220
+ }, [opts.dragFrom]);
4221
+ react.useEffect(() => {
4222
+ if (opts.dragFromContent !== void 0) {
4223
+ engineRef.current?.setDragFromContent(opts.dragFromContent);
4224
+ }
4225
+ }, [opts.disableDrag]);
4056
4226
  }
4057
4227
 
4058
4228
  // src/core/features/teleport.ts
@@ -4201,7 +4371,9 @@ function useBottomSheet(opts) {
4201
4371
  maxHeight: opts.maxHeight,
4202
4372
  persistent: opts.persistent,
4203
4373
  disableClose: opts.disableClose,
4204
- disableDrag: opts.disableDrag
4374
+ disableDrag: opts.disableDrag,
4375
+ dragFrom: opts.dragFrom,
4376
+ dragFromContent: opts.dragFromContent
4205
4377
  });
4206
4378
  const subscribe = react.useCallback((fn) => {
4207
4379
  subscribersRef.current.add(fn);
@@ -4269,7 +4441,6 @@ function useBottomSheet(opts) {
4269
4441
  addAnchor,
4270
4442
  setScrimStages,
4271
4443
  recompute,
4272
- engine: engineRef.current,
4273
4444
  getEngine
4274
4445
  }),
4275
4446
  [
package/dist/react.d.cts CHANGED
@@ -1,10 +1,10 @@
1
- import { a as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, t as ScrimStageDef, d as SheetEventMap, C as CloseReason, u as ScrimUpdate, q as ScrimOverlayOptions, b as SnapPointDef, c as SheetMode } from './BottomSheetCore-CsIjyq7C.cjs';
2
- export { e as SnapPoint } from './BottomSheetCore-CsIjyq7C.cjs';
1
+ import { a as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, t as ScrimStageDef, d as SheetEventMap, C as CloseReason, u as ScrimUpdate, q as ScrimOverlayOptions, b as SnapPointDef, c as SheetMode } from './BottomSheetCore-Cp7dWy91.cjs';
2
+ export { e as SnapPoint } from './BottomSheetCore-Cp7dWy91.cjs';
3
3
  import { OverlayOptions, OverlayState, Overlay as OverlayEngine } from './overlay.cjs';
4
4
  export { OverlayEdge } from './overlay.cjs';
5
5
  import * as react from 'react';
6
6
  import { CSSProperties, ReactNode } from 'react';
7
- import { B as BottomSheetEngine } from './BottomSheetEngine-BYjAVrmd.cjs';
7
+ import { B as BottomSheetEngine } from './BottomSheetEngine-Lm7HIVHv.cjs';
8
8
  import { T as TeleportTarget } from './teleport-7uYNiEAT.cjs';
9
9
  import './transform-jVfAikE9.cjs';
10
10
 
@@ -126,7 +126,6 @@ type UseBottomSheetReturn<TId extends string = string> = UseBottomSheetRefs & {
126
126
  addAnchor: (opts: AnchorOptions) => () => void;
127
127
  setScrimStages: (opts: ScrimStagesOptions | null) => () => void;
128
128
  recompute: () => void;
129
- engine: BottomSheetEngine | null;
130
129
  getEngine: () => BottomSheetEngine | null;
131
130
  };
132
131
  type HookOpts<TId extends string> = Omit<EngineOptions, "element" | "handle" | "scrollContainer" | "backdrop" | "scrim" | "snapPoints" | "allowed" | "initial"> & {
package/dist/react.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { a as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, t as ScrimStageDef, d as SheetEventMap, C as CloseReason, u as ScrimUpdate, q as ScrimOverlayOptions, b as SnapPointDef, c as SheetMode } from './BottomSheetCore-D16lsa6g.js';
2
- export { e as SnapPoint } from './BottomSheetCore-D16lsa6g.js';
1
+ import { a as EngineOptions, E as EngineState, A as AnchorOptions, S as ScrimStagesOptions, t as ScrimStageDef, d as SheetEventMap, C as CloseReason, u as ScrimUpdate, q as ScrimOverlayOptions, b as SnapPointDef, c as SheetMode } from './BottomSheetCore-DJYwWOqt.js';
2
+ export { e as SnapPoint } from './BottomSheetCore-DJYwWOqt.js';
3
3
  import { OverlayOptions, OverlayState, Overlay as OverlayEngine } from './overlay.js';
4
4
  export { OverlayEdge } from './overlay.js';
5
5
  import * as react from 'react';
6
6
  import { CSSProperties, ReactNode } from 'react';
7
- import { B as BottomSheetEngine } from './BottomSheetEngine-YuCNugZn.js';
7
+ import { B as BottomSheetEngine } from './BottomSheetEngine-O5PbeKfe.js';
8
8
  import { T as TeleportTarget } from './teleport-7uYNiEAT.js';
9
9
  import './transform-jVfAikE9.js';
10
10
 
@@ -126,7 +126,6 @@ type UseBottomSheetReturn<TId extends string = string> = UseBottomSheetRefs & {
126
126
  addAnchor: (opts: AnchorOptions) => () => void;
127
127
  setScrimStages: (opts: ScrimStagesOptions | null) => () => void;
128
128
  recompute: () => void;
129
- engine: BottomSheetEngine | null;
130
129
  getEngine: () => BottomSheetEngine | null;
131
130
  };
132
131
  type HookOpts<TId extends string> = Omit<EngineOptions, "element" | "handle" | "scrollContainer" | "backdrop" | "scrim" | "snapPoints" | "allowed" | "initial"> & {