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