@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
@@ -4,7 +4,7 @@ var __defProp = Object.defineProperty;
4
4
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
5
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
6
6
 
7
- // src/core/lifecycle/sheetStack.ts
7
+ // src/core/lifecycle/sheet-stack.ts
8
8
  var BASE_Z = 100;
9
9
  var STEP = 10;
10
10
  var SheetStack = class {
@@ -129,7 +129,7 @@ function createEventBus() {
129
129
  };
130
130
  }
131
131
 
132
- // src/core/primitives/cssLength.ts
132
+ // src/core/primitives/css-length.ts
133
133
  var probe = null;
134
134
  var PROBE_ATTR = "data-bs-probe";
135
135
  var ensureProbe = (axis) => {
@@ -183,7 +183,7 @@ var resolveSnap = (point, mode, measureFit) => {
183
183
  return Math.max(0, el.getBoundingClientRect().width);
184
184
  };
185
185
 
186
- // src/core/primitives/devWarn.ts
186
+ // src/core/primitives/dev-warn.ts
187
187
  function devWarn(...args) {
188
188
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
189
189
  console.warn(...args);
@@ -1516,7 +1516,7 @@ var AnimationRunner = class {
1516
1516
  }
1517
1517
  };
1518
1518
 
1519
- // src/core/lifecycle/focusTrap.ts
1519
+ // src/core/lifecycle/focus-trap.ts
1520
1520
  var FOCUSABLE = [
1521
1521
  "a[href]",
1522
1522
  "area[href]",
@@ -1634,7 +1634,7 @@ var installFocusTrap = (container, options = {}) => {
1634
1634
  };
1635
1635
  };
1636
1636
 
1637
- // src/core/lifecycle/scrollLock.ts
1637
+ // src/core/lifecycle/scroll-lock.ts
1638
1638
  var lockCount = 0;
1639
1639
  var savedStyles = null;
1640
1640
  var lockBodyScroll = () => {
@@ -1816,30 +1816,44 @@ var SampleRing = class {
1816
1816
  this.size = 0;
1817
1817
  }
1818
1818
  };
1819
- var installGestures = (handle, mode, callbacks) => {
1819
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1820
1820
  let activePointerId = null;
1821
1821
  let activePointerType = "touch";
1822
1822
  let startCoord = 0;
1823
1823
  let lastCoord = 0;
1824
+ let pendingStart = false;
1824
1825
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1825
1826
  const MAX_SAMPLES = 32;
1826
1827
  const samples = new SampleRing(MAX_SAMPLES);
1827
1828
  const axis = isAxisVertical(mode) ? "Y" : "X";
1828
1829
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1829
1830
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1831
+ const capture = (pointerId) => {
1832
+ try {
1833
+ handle.setPointerCapture(pointerId);
1834
+ } catch {
1835
+ }
1836
+ };
1837
+ const releaseTracking = () => {
1838
+ activePointerId = null;
1839
+ pendingStart = false;
1840
+ samples.reset();
1841
+ };
1830
1842
  const onPointerDown = (e) => {
1831
1843
  if (activePointerId !== null) return;
1832
1844
  if (e.button !== void 0 && e.button !== 0) return;
1845
+ if (options.shouldStart && !options.shouldStart(e)) return;
1833
1846
  activePointerId = e.pointerId;
1834
1847
  activePointerType = e.pointerType || "touch";
1835
1848
  startCoord = coordOf(e);
1836
1849
  lastCoord = startCoord;
1837
1850
  samples.reset();
1838
1851
  samples.push(e.timeStamp, startCoord);
1839
- try {
1840
- handle.setPointerCapture(e.pointerId);
1841
- } catch {
1852
+ if (options.deferStart) {
1853
+ pendingStart = true;
1854
+ return;
1842
1855
  }
1856
+ capture(e.pointerId);
1843
1857
  callbacks.onStart(startCoord, activePointerType);
1844
1858
  };
1845
1859
  const onPointerMove = (e) => {
@@ -1847,6 +1861,22 @@ var installGestures = (handle, mode, callbacks) => {
1847
1861
  const coord = coordOf(e);
1848
1862
  const rawDelta = coord - startCoord;
1849
1863
  const delta = rawDelta * sign;
1864
+ if (pendingStart) {
1865
+ const verdict = options.deferStart(e, delta);
1866
+ if (verdict === false) {
1867
+ releaseTracking();
1868
+ return;
1869
+ }
1870
+ if (verdict !== true) return;
1871
+ pendingStart = false;
1872
+ startCoord = coord;
1873
+ lastCoord = coord;
1874
+ samples.reset();
1875
+ samples.push(e.timeStamp, coord);
1876
+ capture(e.pointerId);
1877
+ callbacks.onStart(startCoord, activePointerType);
1878
+ return;
1879
+ }
1850
1880
  lastCoord = coord;
1851
1881
  samples.push(e.timeStamp, coord);
1852
1882
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1855,6 +1885,10 @@ var installGestures = (handle, mode, callbacks) => {
1855
1885
  };
1856
1886
  const finishGesture = (e) => {
1857
1887
  if (e.pointerId !== activePointerId) return;
1888
+ if (pendingStart) {
1889
+ releaseTracking();
1890
+ return;
1891
+ }
1858
1892
  const rawDelta = lastCoord - startCoord;
1859
1893
  const delta = rawDelta * sign;
1860
1894
  let velocity = 0;
@@ -1877,22 +1911,26 @@ var installGestures = (handle, mode, callbacks) => {
1877
1911
  };
1878
1912
  const onPointerCancel = (e) => {
1879
1913
  if (e.pointerId !== activePointerId) return;
1880
- activePointerId = null;
1881
- samples.reset();
1914
+ const wasPending = pendingStart;
1915
+ releaseTracking();
1916
+ if (wasPending) return;
1882
1917
  callbacks.onCancel?.();
1883
1918
  };
1884
1919
  handle.addEventListener("pointerdown", onPointerDown);
1885
1920
  handle.addEventListener("pointermove", onPointerMove);
1886
1921
  handle.addEventListener("pointerup", finishGesture);
1887
1922
  handle.addEventListener("pointercancel", onPointerCancel);
1923
+ const managesTouchAction = options.manageTouchAction !== false;
1888
1924
  const prevTouchAction = handle.style.touchAction;
1889
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1925
+ if (managesTouchAction) {
1926
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1927
+ }
1890
1928
  return () => {
1891
1929
  handle.removeEventListener("pointerdown", onPointerDown);
1892
1930
  handle.removeEventListener("pointermove", onPointerMove);
1893
1931
  handle.removeEventListener("pointerup", finishGesture);
1894
1932
  handle.removeEventListener("pointercancel", onPointerCancel);
1895
- handle.style.touchAction = prevTouchAction;
1933
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1896
1934
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1897
1935
  handle.releasePointerCapture(activePointerId);
1898
1936
  }
@@ -2011,7 +2049,7 @@ var GestureController = class {
2011
2049
  const restoreTo = this.dragStartSize;
2012
2050
  void this.deps.animateTo(restoreTo, 0);
2013
2051
  }
2014
- });
2052
+ }, this.deps.gestureOptions);
2015
2053
  return this.detach;
2016
2054
  }
2017
2055
  forceClearDragState() {
@@ -2023,6 +2061,41 @@ var GestureController = class {
2023
2061
  }
2024
2062
  };
2025
2063
 
2064
+ // src/core/primitives/drag-zones.ts
2065
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2066
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2067
+ var asElement = (target) => {
2068
+ const candidate = target;
2069
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2070
+ };
2071
+ function isDragAllowedFrom(target, mode) {
2072
+ const el = asElement(target);
2073
+ if (!el) return mode !== "zones";
2074
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2075
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2076
+ return true;
2077
+ }
2078
+
2079
+ // src/core/primitives/content-gesture.ts
2080
+ var CONTENT_DRAG_SLOP = 6;
2081
+ function decideContentGesture(input) {
2082
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2083
+ if (Math.abs(input.delta) < slop) return "pending";
2084
+ if (input.scrollTop > 0) return "scroll";
2085
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2086
+ return "drag";
2087
+ }
2088
+
2089
+ // src/core/primitives/touch-scroll-guard.ts
2090
+ function installTouchScrollGuard(surface, isDragging) {
2091
+ const onTouchMove = (e) => {
2092
+ if (!isDragging()) return;
2093
+ if (e.cancelable) e.preventDefault();
2094
+ };
2095
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2096
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2097
+ }
2098
+
2026
2099
  // src/core/primitives/aria-slider-writer.ts
2027
2100
  var AriaSliderWriter = class {
2028
2101
  constructor(handle, mode) {
@@ -2378,6 +2451,8 @@ function resolveEngineOptions(opts, extras) {
2378
2451
  mode: opts.mode ?? "bottom",
2379
2452
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2380
2453
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2454
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2455
+ dragFromContent: opts.dragFromContent ?? true,
2381
2456
  rubberBandEnabled: opts.rubberBand ?? true,
2382
2457
  closeOnBack: opts.closeOnBack ?? false,
2383
2458
  persistent: opts.persistent ?? false,
@@ -2463,6 +2538,8 @@ var BottomSheetCore = class {
2463
2538
  __publicField(this, "mode");
2464
2539
  __publicField(this, "flickVelocity");
2465
2540
  __publicField(this, "dragThreshold");
2541
+ __publicField(this, "dragFromMode");
2542
+ __publicField(this, "dragFromContentDefault");
2466
2543
  __publicField(this, "rubberBandEnabled");
2467
2544
  __publicField(this, "scrim");
2468
2545
  __publicField(this, "aria");
@@ -2480,8 +2557,11 @@ var BottomSheetCore = class {
2480
2557
  __publicField(this, "activeId");
2481
2558
  __publicField(this, "size", 0);
2482
2559
  __publicField(this, "gesture");
2560
+ __publicField(this, "contentGesture");
2561
+ __publicField(this, "detachSheetGesture", null);
2483
2562
  __publicField(this, "rootEl", null);
2484
2563
  __publicField(this, "destroyed", false);
2564
+ __publicField(this, "restClosed", false);
2485
2565
  __publicField(this, "currentViewTransition", null);
2486
2566
  __publicField(this, "currentAbort", new AbortController());
2487
2567
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2534,6 +2614,8 @@ var BottomSheetCore = class {
2534
2614
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2535
2615
  this.flickVelocity = resolved.flickVelocity;
2536
2616
  this.dragThreshold = resolved.dragThreshold;
2617
+ this.dragFromMode = resolved.dragFrom;
2618
+ this.dragFromContentDefault = resolved.dragFromContent;
2537
2619
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2538
2620
  this.scrim = new ScrimController(
2539
2621
  {
@@ -2560,7 +2642,7 @@ var BottomSheetCore = class {
2560
2642
  getRootEl: () => this.rootEl,
2561
2643
  applySize: (size) => this.applySize(size),
2562
2644
  getSize: () => this.size,
2563
- isDragging: () => this.gesture?.isDragging ?? false,
2645
+ isDragging: () => this.isDraggingAny(),
2564
2646
  applyAux: (size) => this.applySize(size, true),
2565
2647
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2566
2648
  },
@@ -2621,6 +2703,7 @@ var BottomSheetCore = class {
2621
2703
  const initial = this.snaps.findById(this.activeId);
2622
2704
  if (initial) this.size = initial.size;
2623
2705
  this.applySize(this.size);
2706
+ this.setRestClosed(this.size === 0);
2624
2707
  this.updateAriaSlider();
2625
2708
  this.scrollCache = createScrollCache({
2626
2709
  scrollContainer: this.scrollContainer,
@@ -2651,7 +2734,7 @@ var BottomSheetCore = class {
2651
2734
  (p) => p.size === "fit" || p.size === "content"
2652
2735
  ),
2653
2736
  isDestroyed: () => this.destroyed,
2654
- isDragging: () => this.gesture?.isDragging ?? false,
2737
+ isDragging: () => this.isDraggingAny(),
2655
2738
  recompute: () => this.recompute()
2656
2739
  })
2657
2740
  );
@@ -2677,7 +2760,7 @@ var BottomSheetCore = class {
2677
2760
  autoCollapseAfter
2678
2761
  },
2679
2762
  isDestroyed: () => this.destroyed,
2680
- isDragging: () => this.gesture?.isDragging ?? false,
2763
+ isDragging: () => this.isDraggingAny(),
2681
2764
  isAnimating: () => this.animation.isAnimating,
2682
2765
  isTopSheet: () => this.isTopSheet,
2683
2766
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2707,6 +2790,7 @@ var BottomSheetCore = class {
2707
2790
  void this.snapTo(id);
2708
2791
  },
2709
2792
  close: (reason) => this.close(reason),
2793
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2710
2794
  on: (event, fn) => this.on(event, fn),
2711
2795
  addTeardown: (fn) => this.teardowns.add(fn)
2712
2796
  };
@@ -2735,7 +2819,7 @@ var BottomSheetCore = class {
2735
2819
  return {
2736
2820
  size: this.size,
2737
2821
  activeId: this.activeId,
2738
- isDragging: this.gesture?.isDragging ?? false,
2822
+ isDragging: this.isDraggingAny(),
2739
2823
  isAnimating: this.animation.isAnimating,
2740
2824
  progress: this.computeProgress(this.size)
2741
2825
  };
@@ -2855,9 +2939,16 @@ var BottomSheetCore = class {
2855
2939
  progress: this.computeProgress(this.size)
2856
2940
  });
2857
2941
  if (withHaptic) this.haptic();
2942
+ this.setRestClosed(targetSize === 0);
2858
2943
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2859
2944
  if (targetSize === 0) this.emitCloseSequence();
2860
2945
  }
2946
+ setRestClosed(closed) {
2947
+ if (closed === this.restClosed) return;
2948
+ this.restClosed = closed;
2949
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
2950
+ else this.element.removeAttribute("data-bs-rest");
2951
+ }
2861
2952
  emitOpenSequence(id) {
2862
2953
  this.emit("open", { id });
2863
2954
  this.handleOpen();
@@ -2913,6 +3004,20 @@ var BottomSheetCore = class {
2913
3004
  if (this.destroyed) return;
2914
3005
  this.disableDragFlag = value;
2915
3006
  }
3007
+ setDragFromContent(value) {
3008
+ if (this.destroyed) return;
3009
+ this.dragFromContentDefault = value;
3010
+ }
3011
+ setDragFrom(mode) {
3012
+ if (this.destroyed || mode === this.dragFromMode) return;
3013
+ this.gesture?.forceClearDragState();
3014
+ this.detachSheetGesture?.();
3015
+ this.dragFromMode = mode;
3016
+ this.mountSheetGesture();
3017
+ }
3018
+ getDragFrom() {
3019
+ return this.dragFromMode;
3020
+ }
2916
3021
  isTop() {
2917
3022
  return this.isTopSheet;
2918
3023
  }
@@ -3067,12 +3172,24 @@ var BottomSheetCore = class {
3067
3172
  auditVhUsage(points);
3068
3173
  this.auditDuplicateSnapIds(points);
3069
3174
  this.snaps.setRaw(points);
3070
- if (allowed) this.snaps.setAllowedIds(allowed);
3175
+ if (allowed) {
3176
+ this.snaps.setAllowedIds(allowed);
3177
+ } else {
3178
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3179
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3180
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3181
+ }
3071
3182
  this.scrollCache.clear();
3072
3183
  this.scrim.invalidateOpacityCache();
3073
3184
  const current = this.snaps.findById(this.activeId);
3074
3185
  if (current) {
3075
3186
  this.applySize(current.size);
3187
+ } else {
3188
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3189
+ if (fallback) {
3190
+ this.activeId = fallback.id;
3191
+ this.applySize(fallback.size);
3192
+ }
3076
3193
  }
3077
3194
  this.updateAriaSlider();
3078
3195
  }
@@ -3082,7 +3199,7 @@ var BottomSheetCore = class {
3082
3199
  this.snaps.recompute();
3083
3200
  this.maxHeight.clampTo();
3084
3201
  this.scrim.invalidateOpacityCache();
3085
- if (this.gesture?.isDragging) return;
3202
+ if (this.isDraggingAny()) return;
3086
3203
  this.healActiveSnap();
3087
3204
  }
3088
3205
  healActiveSnap() {
@@ -3104,6 +3221,8 @@ var BottomSheetCore = class {
3104
3221
  this.bus.clear();
3105
3222
  this.animation.cancel();
3106
3223
  this.gesture?.forceClearDragState();
3224
+ this.contentGesture?.forceClearDragState();
3225
+ this.setRestClosed(false);
3107
3226
  this.teardowns.drain();
3108
3227
  this.scrim.destroy();
3109
3228
  this.animation.destroy();
@@ -3216,11 +3335,12 @@ var BottomSheetCore = class {
3216
3335
  const scale = Math.max(1 - depth * 0.04, 0.86);
3217
3336
  this.element.style.scale = scale === 1 ? "" : String(scale);
3218
3337
  }
3219
- attach() {
3220
- this.gesture = new GestureController({
3221
- handle: this.handle,
3338
+ buildGestureDeps(surface, gestureOptions) {
3339
+ return {
3340
+ handle: surface,
3222
3341
  element: this.element,
3223
3342
  mode: this.mode,
3343
+ gestureOptions,
3224
3344
  getRoot: () => this.rootEl,
3225
3345
  getDragContext: () => {
3226
3346
  const buf = this.dragContextBuf;
@@ -3239,8 +3359,88 @@ var BottomSheetCore = class {
3239
3359
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3240
3360
  emit: (event, payload) => this.emit(event, payload),
3241
3361
  listenerCount: (event) => this.bus.listenerCount(event)
3362
+ };
3363
+ }
3364
+ isContentDragAllowed() {
3365
+ if (this.disableDragFlag) return false;
3366
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3367
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3368
+ }
3369
+ decideContentDrag(surface, e, delta) {
3370
+ if (this.destroyed) return false;
3371
+ if (e.pointerType === "mouse") return false;
3372
+ if (this.gesture?.isDragging) return false;
3373
+ if (!this.isContentDragAllowed()) return false;
3374
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3375
+ const decision = decideContentGesture({
3376
+ delta,
3377
+ scrollTop: surface.scrollTop,
3378
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3379
+ });
3380
+ if (decision === "pending") return null;
3381
+ return decision === "drag";
3382
+ }
3383
+ attachDragSurface(surface, kind) {
3384
+ if (this.destroyed || kind !== "content") return;
3385
+ if (this.contentGesture) return;
3386
+ const controller = new GestureController(
3387
+ this.buildGestureDeps(surface, {
3388
+ manageTouchAction: false,
3389
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3390
+ })
3391
+ );
3392
+ this.contentGesture = controller;
3393
+ const detachGestures = controller.install();
3394
+ const detachGuard = installTouchScrollGuard(
3395
+ surface,
3396
+ () => controller.isDragging
3397
+ );
3398
+ return () => {
3399
+ detachGuard();
3400
+ detachGestures();
3401
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3402
+ };
3403
+ }
3404
+ sheetDragSurface() {
3405
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3406
+ }
3407
+ canStartSheetDrag(e) {
3408
+ if (this.contentGesture?.isDragging) return false;
3409
+ const target = e.target;
3410
+ const scoped = typeof target?.closest === "function";
3411
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3412
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3413
+ return true;
3414
+ }
3415
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3416
+ if (this.dragFromMode === "handle") return true;
3417
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3418
+ return false;
3419
+ }
3420
+ return true;
3421
+ }
3422
+ mountSheetGesture() {
3423
+ const surface = this.sheetDragSurface();
3424
+ const controller = new GestureController(
3425
+ this.buildGestureDeps(surface, {
3426
+ shouldStart: (e) => this.canStartSheetDrag(e),
3427
+ manageTouchAction: this.dragFromMode === "handle"
3428
+ })
3429
+ );
3430
+ this.gesture = controller;
3431
+ const detachGestures = controller.install();
3432
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3433
+ this.detachSheetGesture = () => {
3434
+ detachGuard?.();
3435
+ detachGestures();
3436
+ };
3437
+ }
3438
+ attach() {
3439
+ this.mountSheetGesture();
3440
+ this.teardowns.add(() => {
3441
+ this.detachSheetGesture?.();
3442
+ this.detachSheetGesture = null;
3242
3443
  });
3243
- this.teardowns.add(this.gesture.install());
3244
3444
  try {
3245
3445
  this.attachFeatures();
3246
3446
  } catch (err) {
@@ -3277,7 +3477,7 @@ var BottomSheetCore = class {
3277
3477
  element: this.element,
3278
3478
  getMode: () => this.mode,
3279
3479
  isDestroyed: () => this.destroyed,
3280
- isDragging: () => this.gesture?.isDragging ?? false,
3480
+ isDragging: () => this.isDraggingAny(),
3281
3481
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3282
3482
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3283
3483
  getSize: () => this.size,
@@ -3375,8 +3575,11 @@ var BottomSheetCore = class {
3375
3575
  );
3376
3576
  });
3377
3577
  }
3578
+ isDraggingAny() {
3579
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3580
+ }
3378
3581
  clearWillChangeIfIdle() {
3379
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3582
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3380
3583
  this.element.style.willChange = "";
3381
3584
  }
3382
3585
  haptic() {
@@ -3389,8 +3592,9 @@ var BottomSheetCore = class {
3389
3592
  }
3390
3593
  applySize(size, skipTransform = false) {
3391
3594
  const cap = this.snaps.getMaxAxisSize();
3392
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3595
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3393
3596
  this.size = clamped;
3597
+ if (clamped > 0) this.setRestClosed(false);
3394
3598
  const offset = cap - clamped;
3395
3599
  const style = this.element.style;
3396
3600
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3770,50 +3974,10 @@ function installAutoCollapse(deps) {
3770
3974
  };
3771
3975
  }
3772
3976
 
3773
- // src/core/features/content-swipe.ts
3774
- var CONTENT_PULL_THRESHOLD = 60;
3775
- function installContentSwipe(deps) {
3776
- const { container } = deps;
3777
- let startY = 0;
3778
- let startScrollTop = 0;
3779
- let moved = false;
3780
- const onTouchStart = (e) => {
3781
- const t = e.touches[0];
3782
- if (!t) return;
3783
- startY = t.clientY;
3784
- startScrollTop = container.scrollTop;
3785
- moved = false;
3786
- };
3787
- const onTouchMove = (e) => {
3788
- const t = e.touches[0];
3789
- if (!t) return;
3790
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3791
- };
3792
- const onTouchEnd = (e) => {
3793
- if (!moved) return;
3794
- if (deps.isDragging() || deps.isAnimating()) return;
3795
- if (container.scrollTop !== startScrollTop) return;
3796
- const t = e.changedTouches[0];
3797
- if (!t) return;
3798
- const deltaY = t.clientY - startY;
3799
- const atTop = container.scrollTop <= 0;
3800
- const allowed = deps.getAllowedIds();
3801
- const idx = allowed.indexOf(deps.getActiveId());
3802
- if (idx === -1) return;
3803
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3804
- deps.snapTo(allowed[idx - 1]);
3805
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3806
- deps.snapTo(allowed[idx + 1]);
3807
- }
3808
- };
3809
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3810
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3811
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3812
- return () => {
3813
- container.removeEventListener("touchstart", onTouchStart);
3814
- container.removeEventListener("touchmove", onTouchMove);
3815
- container.removeEventListener("touchend", onTouchEnd);
3816
- };
3977
+ // src/core/features/content-drag.ts
3978
+ function installContentDrag(deps) {
3979
+ const detach = deps.attachDragSurface(deps.container, "content");
3980
+ return () => detach?.();
3817
3981
  }
3818
3982
 
3819
3983
  // src/core/features/visual-viewport.ts
@@ -3921,13 +4085,9 @@ function contentSwipeFeature() {
3921
4085
  stage: "attach",
3922
4086
  install: (ctx) => {
3923
4087
  if (!ctx.scrollContainer) return;
3924
- return installContentSwipe({
4088
+ return installContentDrag({
3925
4089
  container: ctx.scrollContainer,
3926
- isDragging: ctx.isDragging,
3927
- isAnimating: ctx.isAnimating,
3928
- getAllowedIds: ctx.allowedIdsBySize,
3929
- getActiveId: ctx.getActiveId,
3930
- snapTo: ctx.snapTo
4090
+ attachDragSurface: ctx.attachDragSurface
3931
4091
  });
3932
4092
  }
3933
4093
  };
@@ -34,6 +34,8 @@
34
34
  persistent?: boolean;
35
35
  disableClose?: boolean;
36
36
  disableDrag?: boolean;
37
+ dragFrom?: EngineOptions["dragFrom"];
38
+ dragFromContent?: boolean;
37
39
  closeOnRouteChange?: boolean;
38
40
  stackEffect?: boolean;
39
41
  teleport?: boolean;
@@ -84,6 +86,8 @@
84
86
  persistent = false,
85
87
  disableClose = false,
86
88
  disableDrag = false,
89
+ dragFrom,
90
+ dragFromContent,
87
91
  closeOnRouteChange = false,
88
92
  stackEffect = false,
89
93
  teleport = false,
@@ -158,6 +162,8 @@
158
162
  persistent,
159
163
  disableClose,
160
164
  disableDrag,
165
+ dragFrom,
166
+ dragFromContent,
161
167
  closeOnRouteChange,
162
168
  stackEffect,
163
169
  radius,
@@ -272,6 +278,14 @@
272
278
  $effect(() => {
273
279
  engine?.setDisableDrag(disableDrag);
274
280
  });
281
+ $effect(() => {
282
+ if (dragFromContent !== undefined) {
283
+ engine?.setDragFromContent(dragFromContent);
284
+ }
285
+ });
286
+ $effect(() => {
287
+ if (dragFrom !== undefined) engine?.setDragFrom(dragFrom);
288
+ });
275
289
  $effect(() => {
276
290
  if (radius !== undefined) engine?.setRadius(radius);
277
291
  });