@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/solid.cjs CHANGED
@@ -7,7 +7,7 @@ var __defProp = Object.defineProperty;
7
7
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
8
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
9
9
 
10
- // src/core/lifecycle/sheetStack.ts
10
+ // src/core/lifecycle/sheet-stack.ts
11
11
  var BASE_Z = 100;
12
12
  var STEP = 10;
13
13
  var SheetStack = class {
@@ -132,7 +132,7 @@ function createEventBus() {
132
132
  };
133
133
  }
134
134
 
135
- // src/core/primitives/cssLength.ts
135
+ // src/core/primitives/css-length.ts
136
136
  var probe = null;
137
137
  var PROBE_ATTR = "data-bs-probe";
138
138
  var ensureProbe = (axis) => {
@@ -186,7 +186,7 @@ var resolveSnap = (point, mode, measureFit) => {
186
186
  return Math.max(0, el.getBoundingClientRect().width);
187
187
  };
188
188
 
189
- // src/core/primitives/devWarn.ts
189
+ // src/core/primitives/dev-warn.ts
190
190
  function devWarn(...args) {
191
191
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
192
192
  console.warn(...args);
@@ -1519,7 +1519,7 @@ var AnimationRunner = class {
1519
1519
  }
1520
1520
  };
1521
1521
 
1522
- // src/core/lifecycle/focusTrap.ts
1522
+ // src/core/lifecycle/focus-trap.ts
1523
1523
  var FOCUSABLE = [
1524
1524
  "a[href]",
1525
1525
  "area[href]",
@@ -1637,7 +1637,7 @@ var installFocusTrap = (container, options = {}) => {
1637
1637
  };
1638
1638
  };
1639
1639
 
1640
- // src/core/lifecycle/scrollLock.ts
1640
+ // src/core/lifecycle/scroll-lock.ts
1641
1641
  var lockCount = 0;
1642
1642
  var savedStyles = null;
1643
1643
  var lockBodyScroll = () => {
@@ -1819,30 +1819,44 @@ var SampleRing = class {
1819
1819
  this.size = 0;
1820
1820
  }
1821
1821
  };
1822
- var installGestures = (handle, mode, callbacks) => {
1822
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1823
1823
  let activePointerId = null;
1824
1824
  let activePointerType = "touch";
1825
1825
  let startCoord = 0;
1826
1826
  let lastCoord = 0;
1827
+ let pendingStart = false;
1827
1828
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1828
1829
  const MAX_SAMPLES = 32;
1829
1830
  const samples = new SampleRing(MAX_SAMPLES);
1830
1831
  const axis = isAxisVertical(mode) ? "Y" : "X";
1831
1832
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1832
1833
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1834
+ const capture = (pointerId) => {
1835
+ try {
1836
+ handle.setPointerCapture(pointerId);
1837
+ } catch {
1838
+ }
1839
+ };
1840
+ const releaseTracking = () => {
1841
+ activePointerId = null;
1842
+ pendingStart = false;
1843
+ samples.reset();
1844
+ };
1833
1845
  const onPointerDown = (e) => {
1834
1846
  if (activePointerId !== null) return;
1835
1847
  if (e.button !== void 0 && e.button !== 0) return;
1848
+ if (options.shouldStart && !options.shouldStart(e)) return;
1836
1849
  activePointerId = e.pointerId;
1837
1850
  activePointerType = e.pointerType || "touch";
1838
1851
  startCoord = coordOf(e);
1839
1852
  lastCoord = startCoord;
1840
1853
  samples.reset();
1841
1854
  samples.push(e.timeStamp, startCoord);
1842
- try {
1843
- handle.setPointerCapture(e.pointerId);
1844
- } catch {
1855
+ if (options.deferStart) {
1856
+ pendingStart = true;
1857
+ return;
1845
1858
  }
1859
+ capture(e.pointerId);
1846
1860
  callbacks.onStart(startCoord, activePointerType);
1847
1861
  };
1848
1862
  const onPointerMove = (e) => {
@@ -1850,6 +1864,22 @@ var installGestures = (handle, mode, callbacks) => {
1850
1864
  const coord = coordOf(e);
1851
1865
  const rawDelta = coord - startCoord;
1852
1866
  const delta = rawDelta * sign;
1867
+ if (pendingStart) {
1868
+ const verdict = options.deferStart(e, delta);
1869
+ if (verdict === false) {
1870
+ releaseTracking();
1871
+ return;
1872
+ }
1873
+ if (verdict !== true) return;
1874
+ pendingStart = false;
1875
+ startCoord = coord;
1876
+ lastCoord = coord;
1877
+ samples.reset();
1878
+ samples.push(e.timeStamp, coord);
1879
+ capture(e.pointerId);
1880
+ callbacks.onStart(startCoord, activePointerType);
1881
+ return;
1882
+ }
1853
1883
  lastCoord = coord;
1854
1884
  samples.push(e.timeStamp, coord);
1855
1885
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1858,6 +1888,10 @@ var installGestures = (handle, mode, callbacks) => {
1858
1888
  };
1859
1889
  const finishGesture = (e) => {
1860
1890
  if (e.pointerId !== activePointerId) return;
1891
+ if (pendingStart) {
1892
+ releaseTracking();
1893
+ return;
1894
+ }
1861
1895
  const rawDelta = lastCoord - startCoord;
1862
1896
  const delta = rawDelta * sign;
1863
1897
  let velocity = 0;
@@ -1880,22 +1914,26 @@ var installGestures = (handle, mode, callbacks) => {
1880
1914
  };
1881
1915
  const onPointerCancel = (e) => {
1882
1916
  if (e.pointerId !== activePointerId) return;
1883
- activePointerId = null;
1884
- samples.reset();
1917
+ const wasPending = pendingStart;
1918
+ releaseTracking();
1919
+ if (wasPending) return;
1885
1920
  callbacks.onCancel?.();
1886
1921
  };
1887
1922
  handle.addEventListener("pointerdown", onPointerDown);
1888
1923
  handle.addEventListener("pointermove", onPointerMove);
1889
1924
  handle.addEventListener("pointerup", finishGesture);
1890
1925
  handle.addEventListener("pointercancel", onPointerCancel);
1926
+ const managesTouchAction = options.manageTouchAction !== false;
1891
1927
  const prevTouchAction = handle.style.touchAction;
1892
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1928
+ if (managesTouchAction) {
1929
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1930
+ }
1893
1931
  return () => {
1894
1932
  handle.removeEventListener("pointerdown", onPointerDown);
1895
1933
  handle.removeEventListener("pointermove", onPointerMove);
1896
1934
  handle.removeEventListener("pointerup", finishGesture);
1897
1935
  handle.removeEventListener("pointercancel", onPointerCancel);
1898
- handle.style.touchAction = prevTouchAction;
1936
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1899
1937
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1900
1938
  handle.releasePointerCapture(activePointerId);
1901
1939
  }
@@ -2014,7 +2052,7 @@ var GestureController = class {
2014
2052
  const restoreTo = this.dragStartSize;
2015
2053
  void this.deps.animateTo(restoreTo, 0);
2016
2054
  }
2017
- });
2055
+ }, this.deps.gestureOptions);
2018
2056
  return this.detach;
2019
2057
  }
2020
2058
  forceClearDragState() {
@@ -2026,6 +2064,41 @@ var GestureController = class {
2026
2064
  }
2027
2065
  };
2028
2066
 
2067
+ // src/core/primitives/drag-zones.ts
2068
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2069
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2070
+ var asElement = (target) => {
2071
+ const candidate = target;
2072
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2073
+ };
2074
+ function isDragAllowedFrom(target, mode) {
2075
+ const el = asElement(target);
2076
+ if (!el) return mode !== "zones";
2077
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2078
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2079
+ return true;
2080
+ }
2081
+
2082
+ // src/core/primitives/content-gesture.ts
2083
+ var CONTENT_DRAG_SLOP = 6;
2084
+ function decideContentGesture(input) {
2085
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2086
+ if (Math.abs(input.delta) < slop) return "pending";
2087
+ if (input.scrollTop > 0) return "scroll";
2088
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2089
+ return "drag";
2090
+ }
2091
+
2092
+ // src/core/primitives/touch-scroll-guard.ts
2093
+ function installTouchScrollGuard(surface, isDragging) {
2094
+ const onTouchMove = (e) => {
2095
+ if (!isDragging()) return;
2096
+ if (e.cancelable) e.preventDefault();
2097
+ };
2098
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2099
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2100
+ }
2101
+
2029
2102
  // src/core/primitives/aria-slider-writer.ts
2030
2103
  var AriaSliderWriter = class {
2031
2104
  constructor(handle, mode) {
@@ -2381,6 +2454,8 @@ function resolveEngineOptions(opts, extras) {
2381
2454
  mode: opts.mode ?? "bottom",
2382
2455
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2383
2456
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2457
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2458
+ dragFromContent: opts.dragFromContent ?? true,
2384
2459
  rubberBandEnabled: opts.rubberBand ?? true,
2385
2460
  closeOnBack: opts.closeOnBack ?? false,
2386
2461
  persistent: opts.persistent ?? false,
@@ -2466,6 +2541,8 @@ var BottomSheetCore = class {
2466
2541
  __publicField(this, "mode");
2467
2542
  __publicField(this, "flickVelocity");
2468
2543
  __publicField(this, "dragThreshold");
2544
+ __publicField(this, "dragFromMode");
2545
+ __publicField(this, "dragFromContentDefault");
2469
2546
  __publicField(this, "rubberBandEnabled");
2470
2547
  __publicField(this, "scrim");
2471
2548
  __publicField(this, "aria");
@@ -2483,8 +2560,11 @@ var BottomSheetCore = class {
2483
2560
  __publicField(this, "activeId");
2484
2561
  __publicField(this, "size", 0);
2485
2562
  __publicField(this, "gesture");
2563
+ __publicField(this, "contentGesture");
2564
+ __publicField(this, "detachSheetGesture", null);
2486
2565
  __publicField(this, "rootEl", null);
2487
2566
  __publicField(this, "destroyed", false);
2567
+ __publicField(this, "restClosed", false);
2488
2568
  __publicField(this, "currentViewTransition", null);
2489
2569
  __publicField(this, "currentAbort", new AbortController());
2490
2570
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2537,6 +2617,8 @@ var BottomSheetCore = class {
2537
2617
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2538
2618
  this.flickVelocity = resolved.flickVelocity;
2539
2619
  this.dragThreshold = resolved.dragThreshold;
2620
+ this.dragFromMode = resolved.dragFrom;
2621
+ this.dragFromContentDefault = resolved.dragFromContent;
2540
2622
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2541
2623
  this.scrim = new ScrimController(
2542
2624
  {
@@ -2563,7 +2645,7 @@ var BottomSheetCore = class {
2563
2645
  getRootEl: () => this.rootEl,
2564
2646
  applySize: (size) => this.applySize(size),
2565
2647
  getSize: () => this.size,
2566
- isDragging: () => this.gesture?.isDragging ?? false,
2648
+ isDragging: () => this.isDraggingAny(),
2567
2649
  applyAux: (size) => this.applySize(size, true),
2568
2650
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2569
2651
  },
@@ -2624,6 +2706,7 @@ var BottomSheetCore = class {
2624
2706
  const initial = this.snaps.findById(this.activeId);
2625
2707
  if (initial) this.size = initial.size;
2626
2708
  this.applySize(this.size);
2709
+ this.setRestClosed(this.size === 0);
2627
2710
  this.updateAriaSlider();
2628
2711
  this.scrollCache = createScrollCache({
2629
2712
  scrollContainer: this.scrollContainer,
@@ -2654,7 +2737,7 @@ var BottomSheetCore = class {
2654
2737
  (p) => p.size === "fit" || p.size === "content"
2655
2738
  ),
2656
2739
  isDestroyed: () => this.destroyed,
2657
- isDragging: () => this.gesture?.isDragging ?? false,
2740
+ isDragging: () => this.isDraggingAny(),
2658
2741
  recompute: () => this.recompute()
2659
2742
  })
2660
2743
  );
@@ -2680,7 +2763,7 @@ var BottomSheetCore = class {
2680
2763
  autoCollapseAfter
2681
2764
  },
2682
2765
  isDestroyed: () => this.destroyed,
2683
- isDragging: () => this.gesture?.isDragging ?? false,
2766
+ isDragging: () => this.isDraggingAny(),
2684
2767
  isAnimating: () => this.animation.isAnimating,
2685
2768
  isTopSheet: () => this.isTopSheet,
2686
2769
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2710,6 +2793,7 @@ var BottomSheetCore = class {
2710
2793
  void this.snapTo(id);
2711
2794
  },
2712
2795
  close: (reason) => this.close(reason),
2796
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2713
2797
  on: (event, fn) => this.on(event, fn),
2714
2798
  addTeardown: (fn) => this.teardowns.add(fn)
2715
2799
  };
@@ -2738,7 +2822,7 @@ var BottomSheetCore = class {
2738
2822
  return {
2739
2823
  size: this.size,
2740
2824
  activeId: this.activeId,
2741
- isDragging: this.gesture?.isDragging ?? false,
2825
+ isDragging: this.isDraggingAny(),
2742
2826
  isAnimating: this.animation.isAnimating,
2743
2827
  progress: this.computeProgress(this.size)
2744
2828
  };
@@ -2858,9 +2942,16 @@ var BottomSheetCore = class {
2858
2942
  progress: this.computeProgress(this.size)
2859
2943
  });
2860
2944
  if (withHaptic) this.haptic();
2945
+ this.setRestClosed(targetSize === 0);
2861
2946
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2862
2947
  if (targetSize === 0) this.emitCloseSequence();
2863
2948
  }
2949
+ setRestClosed(closed) {
2950
+ if (closed === this.restClosed) return;
2951
+ this.restClosed = closed;
2952
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
2953
+ else this.element.removeAttribute("data-bs-rest");
2954
+ }
2864
2955
  emitOpenSequence(id) {
2865
2956
  this.emit("open", { id });
2866
2957
  this.handleOpen();
@@ -2916,6 +3007,20 @@ var BottomSheetCore = class {
2916
3007
  if (this.destroyed) return;
2917
3008
  this.disableDragFlag = value;
2918
3009
  }
3010
+ setDragFromContent(value) {
3011
+ if (this.destroyed) return;
3012
+ this.dragFromContentDefault = value;
3013
+ }
3014
+ setDragFrom(mode) {
3015
+ if (this.destroyed || mode === this.dragFromMode) return;
3016
+ this.gesture?.forceClearDragState();
3017
+ this.detachSheetGesture?.();
3018
+ this.dragFromMode = mode;
3019
+ this.mountSheetGesture();
3020
+ }
3021
+ getDragFrom() {
3022
+ return this.dragFromMode;
3023
+ }
2919
3024
  isTop() {
2920
3025
  return this.isTopSheet;
2921
3026
  }
@@ -3070,12 +3175,24 @@ var BottomSheetCore = class {
3070
3175
  auditVhUsage(points);
3071
3176
  this.auditDuplicateSnapIds(points);
3072
3177
  this.snaps.setRaw(points);
3073
- if (allowed) this.snaps.setAllowedIds(allowed);
3178
+ if (allowed) {
3179
+ this.snaps.setAllowedIds(allowed);
3180
+ } else {
3181
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3182
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3183
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3184
+ }
3074
3185
  this.scrollCache.clear();
3075
3186
  this.scrim.invalidateOpacityCache();
3076
3187
  const current = this.snaps.findById(this.activeId);
3077
3188
  if (current) {
3078
3189
  this.applySize(current.size);
3190
+ } else {
3191
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3192
+ if (fallback) {
3193
+ this.activeId = fallback.id;
3194
+ this.applySize(fallback.size);
3195
+ }
3079
3196
  }
3080
3197
  this.updateAriaSlider();
3081
3198
  }
@@ -3085,7 +3202,7 @@ var BottomSheetCore = class {
3085
3202
  this.snaps.recompute();
3086
3203
  this.maxHeight.clampTo();
3087
3204
  this.scrim.invalidateOpacityCache();
3088
- if (this.gesture?.isDragging) return;
3205
+ if (this.isDraggingAny()) return;
3089
3206
  this.healActiveSnap();
3090
3207
  }
3091
3208
  healActiveSnap() {
@@ -3107,6 +3224,8 @@ var BottomSheetCore = class {
3107
3224
  this.bus.clear();
3108
3225
  this.animation.cancel();
3109
3226
  this.gesture?.forceClearDragState();
3227
+ this.contentGesture?.forceClearDragState();
3228
+ this.setRestClosed(false);
3110
3229
  this.teardowns.drain();
3111
3230
  this.scrim.destroy();
3112
3231
  this.animation.destroy();
@@ -3219,11 +3338,12 @@ var BottomSheetCore = class {
3219
3338
  const scale = Math.max(1 - depth * 0.04, 0.86);
3220
3339
  this.element.style.scale = scale === 1 ? "" : String(scale);
3221
3340
  }
3222
- attach() {
3223
- this.gesture = new GestureController({
3224
- handle: this.handle,
3341
+ buildGestureDeps(surface, gestureOptions) {
3342
+ return {
3343
+ handle: surface,
3225
3344
  element: this.element,
3226
3345
  mode: this.mode,
3346
+ gestureOptions,
3227
3347
  getRoot: () => this.rootEl,
3228
3348
  getDragContext: () => {
3229
3349
  const buf = this.dragContextBuf;
@@ -3242,8 +3362,88 @@ var BottomSheetCore = class {
3242
3362
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3243
3363
  emit: (event, payload) => this.emit(event, payload),
3244
3364
  listenerCount: (event) => this.bus.listenerCount(event)
3365
+ };
3366
+ }
3367
+ isContentDragAllowed() {
3368
+ if (this.disableDragFlag) return false;
3369
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3370
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3371
+ }
3372
+ decideContentDrag(surface, e, delta) {
3373
+ if (this.destroyed) return false;
3374
+ if (e.pointerType === "mouse") return false;
3375
+ if (this.gesture?.isDragging) return false;
3376
+ if (!this.isContentDragAllowed()) return false;
3377
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3378
+ const decision = decideContentGesture({
3379
+ delta,
3380
+ scrollTop: surface.scrollTop,
3381
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3382
+ });
3383
+ if (decision === "pending") return null;
3384
+ return decision === "drag";
3385
+ }
3386
+ attachDragSurface(surface, kind) {
3387
+ if (this.destroyed || kind !== "content") return;
3388
+ if (this.contentGesture) return;
3389
+ const controller = new GestureController(
3390
+ this.buildGestureDeps(surface, {
3391
+ manageTouchAction: false,
3392
+ deferStart: (e, delta) => this.decideContentDrag(surface, e, delta)
3393
+ })
3394
+ );
3395
+ this.contentGesture = controller;
3396
+ const detachGestures = controller.install();
3397
+ const detachGuard = installTouchScrollGuard(
3398
+ surface,
3399
+ () => controller.isDragging
3400
+ );
3401
+ return () => {
3402
+ detachGuard();
3403
+ detachGestures();
3404
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3405
+ };
3406
+ }
3407
+ sheetDragSurface() {
3408
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3409
+ }
3410
+ canStartSheetDrag(e) {
3411
+ if (this.contentGesture?.isDragging) return false;
3412
+ const target = e.target;
3413
+ const scoped = typeof target?.closest === "function";
3414
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3415
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3416
+ return true;
3417
+ }
3418
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3419
+ if (this.dragFromMode === "handle") return true;
3420
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3421
+ return false;
3422
+ }
3423
+ return true;
3424
+ }
3425
+ mountSheetGesture() {
3426
+ const surface = this.sheetDragSurface();
3427
+ const controller = new GestureController(
3428
+ this.buildGestureDeps(surface, {
3429
+ shouldStart: (e) => this.canStartSheetDrag(e),
3430
+ manageTouchAction: this.dragFromMode === "handle"
3431
+ })
3432
+ );
3433
+ this.gesture = controller;
3434
+ const detachGestures = controller.install();
3435
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3436
+ this.detachSheetGesture = () => {
3437
+ detachGuard?.();
3438
+ detachGestures();
3439
+ };
3440
+ }
3441
+ attach() {
3442
+ this.mountSheetGesture();
3443
+ this.teardowns.add(() => {
3444
+ this.detachSheetGesture?.();
3445
+ this.detachSheetGesture = null;
3245
3446
  });
3246
- this.teardowns.add(this.gesture.install());
3247
3447
  try {
3248
3448
  this.attachFeatures();
3249
3449
  } catch (err) {
@@ -3280,7 +3480,7 @@ var BottomSheetCore = class {
3280
3480
  element: this.element,
3281
3481
  getMode: () => this.mode,
3282
3482
  isDestroyed: () => this.destroyed,
3283
- isDragging: () => this.gesture?.isDragging ?? false,
3483
+ isDragging: () => this.isDraggingAny(),
3284
3484
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3285
3485
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3286
3486
  getSize: () => this.size,
@@ -3378,8 +3578,11 @@ var BottomSheetCore = class {
3378
3578
  );
3379
3579
  });
3380
3580
  }
3581
+ isDraggingAny() {
3582
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3583
+ }
3381
3584
  clearWillChangeIfIdle() {
3382
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3585
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3383
3586
  this.element.style.willChange = "";
3384
3587
  }
3385
3588
  haptic() {
@@ -3392,8 +3595,9 @@ var BottomSheetCore = class {
3392
3595
  }
3393
3596
  applySize(size, skipTransform = false) {
3394
3597
  const cap = this.snaps.getMaxAxisSize();
3395
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3598
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3396
3599
  this.size = clamped;
3600
+ if (clamped > 0) this.setRestClosed(false);
3397
3601
  const offset = cap - clamped;
3398
3602
  const style = this.element.style;
3399
3603
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3773,50 +3977,10 @@ function installAutoCollapse(deps) {
3773
3977
  };
3774
3978
  }
3775
3979
 
3776
- // src/core/features/content-swipe.ts
3777
- var CONTENT_PULL_THRESHOLD = 60;
3778
- function installContentSwipe(deps) {
3779
- const { container } = deps;
3780
- let startY = 0;
3781
- let startScrollTop = 0;
3782
- let moved = false;
3783
- const onTouchStart = (e) => {
3784
- const t = e.touches[0];
3785
- if (!t) return;
3786
- startY = t.clientY;
3787
- startScrollTop = container.scrollTop;
3788
- moved = false;
3789
- };
3790
- const onTouchMove = (e) => {
3791
- const t = e.touches[0];
3792
- if (!t) return;
3793
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3794
- };
3795
- const onTouchEnd = (e) => {
3796
- if (!moved) return;
3797
- if (deps.isDragging() || deps.isAnimating()) return;
3798
- if (container.scrollTop !== startScrollTop) return;
3799
- const t = e.changedTouches[0];
3800
- if (!t) return;
3801
- const deltaY = t.clientY - startY;
3802
- const atTop = container.scrollTop <= 0;
3803
- const allowed = deps.getAllowedIds();
3804
- const idx = allowed.indexOf(deps.getActiveId());
3805
- if (idx === -1) return;
3806
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3807
- deps.snapTo(allowed[idx - 1]);
3808
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3809
- deps.snapTo(allowed[idx + 1]);
3810
- }
3811
- };
3812
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3813
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3814
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3815
- return () => {
3816
- container.removeEventListener("touchstart", onTouchStart);
3817
- container.removeEventListener("touchmove", onTouchMove);
3818
- container.removeEventListener("touchend", onTouchEnd);
3819
- };
3980
+ // src/core/features/content-drag.ts
3981
+ function installContentDrag(deps) {
3982
+ const detach = deps.attachDragSurface(deps.container, "content");
3983
+ return () => detach?.();
3820
3984
  }
3821
3985
 
3822
3986
  // src/core/features/visual-viewport.ts
@@ -3924,13 +4088,9 @@ function contentSwipeFeature() {
3924
4088
  stage: "attach",
3925
4089
  install: (ctx) => {
3926
4090
  if (!ctx.scrollContainer) return;
3927
- return installContentSwipe({
4091
+ return installContentDrag({
3928
4092
  container: ctx.scrollContainer,
3929
- isDragging: ctx.isDragging,
3930
- isAnimating: ctx.isAnimating,
3931
- getAllowedIds: ctx.allowedIdsBySize,
3932
- getActiveId: ctx.getActiveId,
3933
- snapTo: ctx.snapTo
4093
+ attachDragSurface: ctx.attachDragSurface
3934
4094
  });
3935
4095
  }
3936
4096
  };
@@ -4062,6 +4222,8 @@ var BottomSheet = (props) => {
4062
4222
  persistent: props.persistent,
4063
4223
  disableClose: props.disableClose,
4064
4224
  disableDrag: props.disableDrag,
4225
+ dragFrom: props.dragFrom,
4226
+ dragFromContent: props.dragFromContent,
4065
4227
  closeOnRouteChange: props.closeOnRouteChange,
4066
4228
  stackEffect: props.stackEffect,
4067
4229
  returnFocusTo: props.returnFocusTo
@@ -4150,6 +4312,18 @@ var BottomSheet = (props) => {
4150
4312
  }, {
4151
4313
  defer: true
4152
4314
  }));
4315
+ solidJs.createEffect(solidJs.on(() => props.dragFromContent, (value, prev) => {
4316
+ if (!engine || prev === void 0 || value === void 0) return;
4317
+ engine.setDragFromContent(value);
4318
+ }, {
4319
+ defer: true
4320
+ }));
4321
+ solidJs.createEffect(solidJs.on(() => props.dragFrom, (value, prev) => {
4322
+ if (!engine || prev === void 0 || value === void 0) return;
4323
+ engine.setDragFrom(value);
4324
+ }, {
4325
+ defer: true
4326
+ }));
4153
4327
  const showBackdrop = solidJs.createMemo(() => props.backdrop !== false);
4154
4328
  const mode = solidJs.createMemo(() => props.mode ?? "bottom");
4155
4329
  const ariaLabel = solidJs.createMemo(() => props.ariaLabel ?? "Bottom sheet");