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