@surdeddd/bottom-sheet 0.13.0 → 0.20.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 +46 -0
  2. package/dist/{BottomSheetCore-D16lsa6g.d.ts → BottomSheetCore-Cgn8b_6A.d.ts} +36 -2
  3. package/dist/{BottomSheetCore-CsIjyq7C.d.cts → BottomSheetCore-VPJZcKcg.d.cts} +36 -2
  4. package/dist/{BottomSheetEngine-BYjAVrmd.d.cts → BottomSheetEngine-B8C2CwF9.d.cts} +1 -1
  5. package/dist/{BottomSheetEngine-YuCNugZn.d.ts → BottomSheetEngine-DwYR_ygJ.d.ts} +1 -1
  6. package/dist/core.cjs +328 -33
  7. package/dist/core.d.cts +2 -2
  8. package/dist/core.d.ts +2 -2
  9. package/dist/core.js +328 -33
  10. package/dist/debug.d.cts +1 -1
  11. package/dist/debug.d.ts +1 -1
  12. package/dist/element.cjs +404 -108
  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 +404 -108
  17. package/dist/features.cjs +6 -50
  18. package/dist/features.d.cts +4 -4
  19. package/dist/features.d.ts +4 -4
  20. package/dist/features.js +6 -50
  21. package/dist/index-CnfbOKx0.d.cts +9 -0
  22. package/dist/index-DcMkhXAy.d.ts +9 -0
  23. package/dist/index.cjs +335 -829
  24. package/dist/index.d.cts +44 -47
  25. package/dist/index.d.ts +44 -47
  26. package/dist/index.js +336 -816
  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 +323 -83
  36. package/dist/qwik-lib/index.qwik.mjs +323 -83
  37. package/dist/qwik.cjs +342 -83
  38. package/dist/qwik.d.cts +4 -2
  39. package/dist/qwik.d.ts +4 -2
  40. package/dist/qwik.js +342 -83
  41. package/dist/react.cjs +347 -85
  42. package/dist/react.d.cts +3 -4
  43. package/dist/react.d.ts +3 -4
  44. package/dist/react.js +347 -85
  45. package/dist/solid.cjs +348 -83
  46. package/dist/solid.d.cts +37 -1
  47. package/dist/solid.d.ts +37 -1
  48. package/dist/solid.js +348 -83
  49. package/dist/solid.source.jsx +356 -83
  50. package/dist/styles.css +24 -4
  51. package/dist/svelte-core.cjs +334 -83
  52. package/dist/svelte-core.js +334 -83
  53. package/dist/svelte-src/BottomSheet.svelte +14 -0
  54. package/dist/svelte.cjs +344 -83
  55. package/dist/svelte.d.cts +2 -0
  56. package/dist/svelte.d.ts +2 -0
  57. package/dist/svelte.js +344 -83
  58. package/dist/vue.cjs +350 -83
  59. package/dist/vue.js +350 -83
  60. package/dist/webgl.cjs +673 -0
  61. package/dist/webgl.d.cts +17 -0
  62. package/dist/webgl.d.ts +17 -0
  63. package/dist/webgl.js +671 -0
  64. package/package.json +64 -40
  65. package/dist/index-DdYLOIIT.d.ts +0 -15
  66. package/dist/index-dfrzw-lI.d.cts +0 -15
package/dist/react.cjs CHANGED
@@ -8,7 +8,7 @@ var __defProp = Object.defineProperty;
8
8
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
9
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
10
10
 
11
- // src/core/lifecycle/sheetStack.ts
11
+ // src/core/lifecycle/sheet-stack.ts
12
12
  var BASE_Z = 100;
13
13
  var STEP = 10;
14
14
  var SheetStack = class {
@@ -139,7 +139,7 @@ function createEventBus() {
139
139
  };
140
140
  }
141
141
 
142
- // src/core/primitives/cssLength.ts
142
+ // src/core/primitives/css-length.ts
143
143
  var probe = null;
144
144
  var PROBE_ATTR = "data-bs-probe";
145
145
  var ensureProbe = (axis) => {
@@ -193,7 +193,7 @@ var resolveSnap = (point, mode, measureFit) => {
193
193
  return Math.max(0, el.getBoundingClientRect().width);
194
194
  };
195
195
 
196
- // src/core/primitives/devWarn.ts
196
+ // src/core/primitives/dev-warn.ts
197
197
  function devWarn(...args) {
198
198
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
199
199
  console.warn(...args);
@@ -325,6 +325,11 @@ var SnapResolver = class {
325
325
  this.raw = raw;
326
326
  this.recompute();
327
327
  }
328
+ setMode(mode) {
329
+ if (mode === this.mode) return;
330
+ this.mode = mode;
331
+ this.recompute();
332
+ }
328
333
  setAllowedIds(ids) {
329
334
  this.allowedIds = ids;
330
335
  this.rangeCache = null;
@@ -908,6 +913,11 @@ var ScrimController = class {
908
913
  if (this.isDestroyed()) return;
909
914
  this.applyScrimStyles({ interactive });
910
915
  }
916
+ setSheetMode(mode) {
917
+ if (this.isDestroyed() || mode === this.mode) return;
918
+ this.mode = mode;
919
+ this.invalidateOpacityCache();
920
+ }
911
921
  setScrimMode(mode, applySize) {
912
922
  if (this.isDestroyed()) return;
913
923
  if (mode === this.scrimMode) return;
@@ -1526,7 +1536,7 @@ var AnimationRunner = class {
1526
1536
  }
1527
1537
  };
1528
1538
 
1529
- // src/core/lifecycle/focusTrap.ts
1539
+ // src/core/lifecycle/focus-trap.ts
1530
1540
  var FOCUSABLE = [
1531
1541
  "a[href]",
1532
1542
  "area[href]",
@@ -1644,7 +1654,7 @@ var installFocusTrap = (container, options = {}) => {
1644
1654
  };
1645
1655
  };
1646
1656
 
1647
- // src/core/lifecycle/scrollLock.ts
1657
+ // src/core/lifecycle/scroll-lock.ts
1648
1658
  var lockCount = 0;
1649
1659
  var savedStyles = null;
1650
1660
  var lockBodyScroll = () => {
@@ -1826,30 +1836,47 @@ var SampleRing = class {
1826
1836
  this.size = 0;
1827
1837
  }
1828
1838
  };
1829
- var installGestures = (handle, mode, callbacks) => {
1839
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1830
1840
  let activePointerId = null;
1831
1841
  let activePointerType = "touch";
1832
1842
  let startCoord = 0;
1843
+ let startCross = 0;
1833
1844
  let lastCoord = 0;
1845
+ let pendingStart = false;
1834
1846
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1835
1847
  const MAX_SAMPLES = 32;
1836
1848
  const samples = new SampleRing(MAX_SAMPLES);
1837
1849
  const axis = isAxisVertical(mode) ? "Y" : "X";
1838
1850
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1839
1851
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1852
+ const crossOf = (e) => axis === "Y" ? e.clientX : e.clientY;
1853
+ const capture = (pointerId) => {
1854
+ try {
1855
+ handle.setPointerCapture(pointerId);
1856
+ } catch {
1857
+ }
1858
+ };
1859
+ const releaseTracking = () => {
1860
+ activePointerId = null;
1861
+ pendingStart = false;
1862
+ samples.reset();
1863
+ };
1840
1864
  const onPointerDown = (e) => {
1841
1865
  if (activePointerId !== null) return;
1842
1866
  if (e.button !== void 0 && e.button !== 0) return;
1867
+ if (options.shouldStart && !options.shouldStart(e)) return;
1843
1868
  activePointerId = e.pointerId;
1844
1869
  activePointerType = e.pointerType || "touch";
1845
1870
  startCoord = coordOf(e);
1871
+ startCross = crossOf(e);
1846
1872
  lastCoord = startCoord;
1847
1873
  samples.reset();
1848
1874
  samples.push(e.timeStamp, startCoord);
1849
- try {
1850
- handle.setPointerCapture(e.pointerId);
1851
- } catch {
1875
+ if (options.deferStart) {
1876
+ pendingStart = true;
1877
+ return;
1852
1878
  }
1879
+ capture(e.pointerId);
1853
1880
  callbacks.onStart(startCoord, activePointerType);
1854
1881
  };
1855
1882
  const onPointerMove = (e) => {
@@ -1857,6 +1884,22 @@ var installGestures = (handle, mode, callbacks) => {
1857
1884
  const coord = coordOf(e);
1858
1885
  const rawDelta = coord - startCoord;
1859
1886
  const delta = rawDelta * sign;
1887
+ if (pendingStart) {
1888
+ const verdict = options.deferStart(e, delta, crossOf(e) - startCross);
1889
+ if (verdict === false) {
1890
+ releaseTracking();
1891
+ return;
1892
+ }
1893
+ if (verdict !== true) return;
1894
+ pendingStart = false;
1895
+ startCoord = coord;
1896
+ lastCoord = coord;
1897
+ samples.reset();
1898
+ samples.push(e.timeStamp, coord);
1899
+ capture(e.pointerId);
1900
+ callbacks.onStart(startCoord, activePointerType);
1901
+ return;
1902
+ }
1860
1903
  lastCoord = coord;
1861
1904
  samples.push(e.timeStamp, coord);
1862
1905
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1865,6 +1908,10 @@ var installGestures = (handle, mode, callbacks) => {
1865
1908
  };
1866
1909
  const finishGesture = (e) => {
1867
1910
  if (e.pointerId !== activePointerId) return;
1911
+ if (pendingStart) {
1912
+ releaseTracking();
1913
+ return;
1914
+ }
1868
1915
  const rawDelta = lastCoord - startCoord;
1869
1916
  const delta = rawDelta * sign;
1870
1917
  let velocity = 0;
@@ -1887,22 +1934,26 @@ var installGestures = (handle, mode, callbacks) => {
1887
1934
  };
1888
1935
  const onPointerCancel = (e) => {
1889
1936
  if (e.pointerId !== activePointerId) return;
1890
- activePointerId = null;
1891
- samples.reset();
1937
+ const wasPending = pendingStart;
1938
+ releaseTracking();
1939
+ if (wasPending) return;
1892
1940
  callbacks.onCancel?.();
1893
1941
  };
1894
1942
  handle.addEventListener("pointerdown", onPointerDown);
1895
1943
  handle.addEventListener("pointermove", onPointerMove);
1896
1944
  handle.addEventListener("pointerup", finishGesture);
1897
1945
  handle.addEventListener("pointercancel", onPointerCancel);
1946
+ const managesTouchAction = options.manageTouchAction !== false;
1898
1947
  const prevTouchAction = handle.style.touchAction;
1899
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1948
+ if (managesTouchAction) {
1949
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1950
+ }
1900
1951
  return () => {
1901
1952
  handle.removeEventListener("pointerdown", onPointerDown);
1902
1953
  handle.removeEventListener("pointermove", onPointerMove);
1903
1954
  handle.removeEventListener("pointerup", finishGesture);
1904
1955
  handle.removeEventListener("pointercancel", onPointerCancel);
1905
- handle.style.touchAction = prevTouchAction;
1956
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1906
1957
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1907
1958
  handle.releasePointerCapture(activePointerId);
1908
1959
  }
@@ -2021,7 +2072,7 @@ var GestureController = class {
2021
2072
  const restoreTo = this.dragStartSize;
2022
2073
  void this.deps.animateTo(restoreTo, 0);
2023
2074
  }
2024
- });
2075
+ }, this.deps.gestureOptions);
2025
2076
  return this.detach;
2026
2077
  }
2027
2078
  forceClearDragState() {
@@ -2033,6 +2084,74 @@ var GestureController = class {
2033
2084
  }
2034
2085
  };
2035
2086
 
2087
+ // src/core/primitives/drag-zones.ts
2088
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2089
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2090
+ var asElement = (target) => {
2091
+ const candidate = target;
2092
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2093
+ };
2094
+ function isDragAllowedFrom(target, mode) {
2095
+ const el = asElement(target);
2096
+ if (!el) return mode !== "zones";
2097
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2098
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2099
+ return true;
2100
+ }
2101
+
2102
+ // src/core/primitives/content-gesture.ts
2103
+ var CONTENT_DRAG_SLOP = 6;
2104
+ function decideContentGesture(input) {
2105
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2106
+ const cross = input.crossDelta ?? 0;
2107
+ const sharesScrollAxis = input.sharesScrollAxis ?? true;
2108
+ if (!sharesScrollAxis) {
2109
+ if (Math.abs(input.delta) < slop && Math.abs(cross) < slop) return "pending";
2110
+ if (Math.abs(input.delta) <= Math.abs(cross)) return "scroll";
2111
+ return "drag";
2112
+ }
2113
+ if (Math.abs(input.delta) < slop) return "pending";
2114
+ if (input.scrollTop > 0) return "scroll";
2115
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2116
+ return "drag";
2117
+ }
2118
+
2119
+ // src/core/primitives/logical-mode.ts
2120
+ var isLogicalMode = (mode) => mode === "start" || mode === "end";
2121
+ var readDirection = (el) => {
2122
+ if (!el) return "ltr";
2123
+ if (typeof getComputedStyle === "function") {
2124
+ try {
2125
+ if (getComputedStyle(el).direction === "rtl") return "rtl";
2126
+ } catch {
2127
+ }
2128
+ }
2129
+ try {
2130
+ const marked = el.closest?.("[dir]");
2131
+ if (marked) {
2132
+ return marked.getAttribute("dir")?.toLowerCase() === "rtl" ? "rtl" : "ltr";
2133
+ }
2134
+ } catch {
2135
+ }
2136
+ return "ltr";
2137
+ };
2138
+ var resolveMode = (mode, el) => {
2139
+ if (!isLogicalMode(mode)) return mode;
2140
+ const rtl = readDirection(el) === "rtl";
2141
+ if (mode === "start") return rtl ? "right" : "left";
2142
+ return rtl ? "left" : "right";
2143
+ };
2144
+
2145
+ // src/core/primitives/touch-scroll-guard.ts
2146
+ function installTouchScrollGuard(surface, isDragging) {
2147
+ const onTouchMove = (e) => {
2148
+ if (!isDragging()) return;
2149
+ if (e.cancelable) e.preventDefault();
2150
+ };
2151
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2152
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2153
+ }
2154
+
2036
2155
  // src/core/primitives/aria-slider-writer.ts
2037
2156
  var AriaSliderWriter = class {
2038
2157
  constructor(handle, mode) {
@@ -2385,9 +2504,11 @@ function resolveEngineOptions(opts, extras) {
2385
2504
  }
2386
2505
  }
2387
2506
  return {
2388
- mode: opts.mode ?? "bottom",
2507
+ mode: resolveMode(opts.mode ?? "bottom", opts.element),
2389
2508
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2390
2509
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2510
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2511
+ dragFromContent: opts.dragFromContent ?? true,
2391
2512
  rubberBandEnabled: opts.rubberBand ?? true,
2392
2513
  closeOnBack: opts.closeOnBack ?? false,
2393
2514
  persistent: opts.persistent ?? false,
@@ -2473,6 +2594,8 @@ var BottomSheetCore = class {
2473
2594
  __publicField(this, "mode");
2474
2595
  __publicField(this, "flickVelocity");
2475
2596
  __publicField(this, "dragThreshold");
2597
+ __publicField(this, "dragFromMode");
2598
+ __publicField(this, "dragFromContentDefault");
2476
2599
  __publicField(this, "rubberBandEnabled");
2477
2600
  __publicField(this, "scrim");
2478
2601
  __publicField(this, "aria");
@@ -2485,13 +2608,18 @@ var BottomSheetCore = class {
2485
2608
  __publicField(this, "disableDragFlag");
2486
2609
  __publicField(this, "closeOnRouteChange");
2487
2610
  __publicField(this, "maxHeight");
2611
+ __publicField(this, "maxHeightDeps");
2488
2612
  __publicField(this, "snapPointsRaw");
2489
2613
  __publicField(this, "snaps");
2490
2614
  __publicField(this, "activeId");
2491
2615
  __publicField(this, "size", 0);
2492
2616
  __publicField(this, "gesture");
2617
+ __publicField(this, "contentGesture");
2618
+ __publicField(this, "detachSheetGesture", null);
2619
+ __publicField(this, "detachSliderKeyboard", null);
2493
2620
  __publicField(this, "rootEl", null);
2494
2621
  __publicField(this, "destroyed", false);
2622
+ __publicField(this, "restClosed", false);
2495
2623
  __publicField(this, "currentViewTransition", null);
2496
2624
  __publicField(this, "currentAbort", new AbortController());
2497
2625
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2540,10 +2668,13 @@ var BottomSheetCore = class {
2540
2668
  this.screenComponent = opts.scrim ?? opts.screenComponent;
2541
2669
  this.scrimParent = this.screenComponent?.parentElement ?? null;
2542
2670
  this.mode = resolved.mode;
2671
+ this.element.dataset.mode = this.mode;
2543
2672
  this.transformTemplate = buildTransformTemplate(this.mode);
2544
2673
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2545
2674
  this.flickVelocity = resolved.flickVelocity;
2546
2675
  this.dragThreshold = resolved.dragThreshold;
2676
+ this.dragFromMode = resolved.dragFrom;
2677
+ this.dragFromContentDefault = resolved.dragFromContent;
2547
2678
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2548
2679
  this.scrim = new ScrimController(
2549
2680
  {
@@ -2570,7 +2701,7 @@ var BottomSheetCore = class {
2570
2701
  getRootEl: () => this.rootEl,
2571
2702
  applySize: (size) => this.applySize(size),
2572
2703
  getSize: () => this.size,
2573
- isDragging: () => this.gesture?.isDragging ?? false,
2704
+ isDragging: () => this.isDraggingAny(),
2574
2705
  applyAux: (size) => this.applySize(size, true),
2575
2706
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2576
2707
  },
@@ -2604,7 +2735,7 @@ var BottomSheetCore = class {
2604
2735
  auditVhUsage(this.snapPointsRaw);
2605
2736
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2606
2737
  this.rootEl = this.element.closest(".bs-root");
2607
- this.maxHeight = createMaxHeightController({
2738
+ this.maxHeightDeps = {
2608
2739
  element: this.element,
2609
2740
  mode: this.mode,
2610
2741
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
@@ -2612,7 +2743,8 @@ var BottomSheetCore = class {
2612
2743
  this.snaps.setMaxAxisSize(size);
2613
2744
  },
2614
2745
  recompute: () => this.recompute()
2615
- });
2746
+ };
2747
+ this.maxHeight = createMaxHeightController(this.maxHeightDeps);
2616
2748
  this.fitDeps = {
2617
2749
  element: this.element,
2618
2750
  scrollContainer: this.scrollContainer,
@@ -2631,6 +2763,7 @@ var BottomSheetCore = class {
2631
2763
  const initial = this.snaps.findById(this.activeId);
2632
2764
  if (initial) this.size = initial.size;
2633
2765
  this.applySize(this.size);
2766
+ this.setRestClosed(this.size === 0);
2634
2767
  this.updateAriaSlider();
2635
2768
  this.scrollCache = createScrollCache({
2636
2769
  scrollContainer: this.scrollContainer,
@@ -2661,7 +2794,7 @@ var BottomSheetCore = class {
2661
2794
  (p) => p.size === "fit" || p.size === "content"
2662
2795
  ),
2663
2796
  isDestroyed: () => this.destroyed,
2664
- isDragging: () => this.gesture?.isDragging ?? false,
2797
+ isDragging: () => this.isDraggingAny(),
2665
2798
  recompute: () => this.recompute()
2666
2799
  })
2667
2800
  );
@@ -2687,7 +2820,7 @@ var BottomSheetCore = class {
2687
2820
  autoCollapseAfter
2688
2821
  },
2689
2822
  isDestroyed: () => this.destroyed,
2690
- isDragging: () => this.gesture?.isDragging ?? false,
2823
+ isDragging: () => this.isDraggingAny(),
2691
2824
  isAnimating: () => this.animation.isAnimating,
2692
2825
  isTopSheet: () => this.isTopSheet,
2693
2826
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2717,6 +2850,7 @@ var BottomSheetCore = class {
2717
2850
  void this.snapTo(id);
2718
2851
  },
2719
2852
  close: (reason) => this.close(reason),
2853
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2720
2854
  on: (event, fn) => this.on(event, fn),
2721
2855
  addTeardown: (fn) => this.teardowns.add(fn)
2722
2856
  };
@@ -2745,7 +2879,7 @@ var BottomSheetCore = class {
2745
2879
  return {
2746
2880
  size: this.size,
2747
2881
  activeId: this.activeId,
2748
- isDragging: this.gesture?.isDragging ?? false,
2882
+ isDragging: this.isDraggingAny(),
2749
2883
  isAnimating: this.animation.isAnimating,
2750
2884
  progress: this.computeProgress(this.size)
2751
2885
  };
@@ -2865,9 +2999,16 @@ var BottomSheetCore = class {
2865
2999
  progress: this.computeProgress(this.size)
2866
3000
  });
2867
3001
  if (withHaptic) this.haptic();
3002
+ this.setRestClosed(targetSize === 0);
2868
3003
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2869
3004
  if (targetSize === 0) this.emitCloseSequence();
2870
3005
  }
3006
+ setRestClosed(closed) {
3007
+ if (closed === this.restClosed) return;
3008
+ this.restClosed = closed;
3009
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3010
+ else this.element.removeAttribute("data-bs-rest");
3011
+ }
2871
3012
  emitOpenSequence(id) {
2872
3013
  this.emit("open", { id });
2873
3014
  this.handleOpen();
@@ -2923,6 +3064,51 @@ var BottomSheetCore = class {
2923
3064
  if (this.destroyed) return;
2924
3065
  this.disableDragFlag = value;
2925
3066
  }
3067
+ setDragFromContent(value) {
3068
+ if (this.destroyed) return;
3069
+ this.dragFromContentDefault = value;
3070
+ }
3071
+ setDragFrom(mode) {
3072
+ if (this.destroyed || mode === this.dragFromMode) return;
3073
+ this.gesture?.forceClearDragState();
3074
+ this.detachSheetGesture?.();
3075
+ this.dragFromMode = mode;
3076
+ this.mountSheetGesture();
3077
+ }
3078
+ getDragFrom() {
3079
+ return this.dragFromMode;
3080
+ }
3081
+ setMode(mode) {
3082
+ if (this.destroyed) return;
3083
+ const next = resolveMode(mode, this.element);
3084
+ if (next === this.mode) return;
3085
+ const wasVertical = this.mode === "bottom" || this.mode === "top";
3086
+ const nowVertical = next === "bottom" || next === "top";
3087
+ this.gesture?.forceClearDragState();
3088
+ this.detachSheetGesture?.();
3089
+ this.animation.cancel();
3090
+ this.element.style.removeProperty(layoutAxis(this.mode));
3091
+ this.mode = next;
3092
+ this.element.dataset.mode = next;
3093
+ this.transformTemplate = buildTransformTemplate(next);
3094
+ this.maxHeightDeps.mode = next;
3095
+ this.fitDeps.mode = next;
3096
+ this.snaps.setMode(next);
3097
+ this.scrim.setSheetMode(next);
3098
+ if (wasVertical !== nowVertical) {
3099
+ this.aria = new AriaSliderWriter(this.handle, next);
3100
+ }
3101
+ this.detachSliderKeyboard?.();
3102
+ this.mountSliderKeyboard();
3103
+ this.mountSheetGesture();
3104
+ this.recompute();
3105
+ const active = this.snaps.findById(this.activeId);
3106
+ if (active) this.applySize(active.size);
3107
+ this.updateAriaSlider();
3108
+ }
3109
+ getMode() {
3110
+ return this.mode;
3111
+ }
2926
3112
  isTop() {
2927
3113
  return this.isTopSheet;
2928
3114
  }
@@ -3077,12 +3263,24 @@ var BottomSheetCore = class {
3077
3263
  auditVhUsage(points);
3078
3264
  this.auditDuplicateSnapIds(points);
3079
3265
  this.snaps.setRaw(points);
3080
- if (allowed) this.snaps.setAllowedIds(allowed);
3266
+ if (allowed) {
3267
+ this.snaps.setAllowedIds(allowed);
3268
+ } else {
3269
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3270
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3271
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3272
+ }
3081
3273
  this.scrollCache.clear();
3082
3274
  this.scrim.invalidateOpacityCache();
3083
3275
  const current = this.snaps.findById(this.activeId);
3084
3276
  if (current) {
3085
3277
  this.applySize(current.size);
3278
+ } else {
3279
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3280
+ if (fallback) {
3281
+ this.activeId = fallback.id;
3282
+ this.applySize(fallback.size);
3283
+ }
3086
3284
  }
3087
3285
  this.updateAriaSlider();
3088
3286
  }
@@ -3092,7 +3290,7 @@ var BottomSheetCore = class {
3092
3290
  this.snaps.recompute();
3093
3291
  this.maxHeight.clampTo();
3094
3292
  this.scrim.invalidateOpacityCache();
3095
- if (this.gesture?.isDragging) return;
3293
+ if (this.isDraggingAny()) return;
3096
3294
  this.healActiveSnap();
3097
3295
  }
3098
3296
  healActiveSnap() {
@@ -3114,6 +3312,8 @@ var BottomSheetCore = class {
3114
3312
  this.bus.clear();
3115
3313
  this.animation.cancel();
3116
3314
  this.gesture?.forceClearDragState();
3315
+ this.contentGesture?.forceClearDragState();
3316
+ this.setRestClosed(false);
3117
3317
  this.teardowns.drain();
3118
3318
  this.scrim.destroy();
3119
3319
  this.animation.destroy();
@@ -3226,11 +3426,12 @@ var BottomSheetCore = class {
3226
3426
  const scale = Math.max(1 - depth * 0.04, 0.86);
3227
3427
  this.element.style.scale = scale === 1 ? "" : String(scale);
3228
3428
  }
3229
- attach() {
3230
- this.gesture = new GestureController({
3231
- handle: this.handle,
3429
+ buildGestureDeps(surface, gestureOptions) {
3430
+ return {
3431
+ handle: surface,
3232
3432
  element: this.element,
3233
3433
  mode: this.mode,
3434
+ gestureOptions,
3234
3435
  getRoot: () => this.rootEl,
3235
3436
  getDragContext: () => {
3236
3437
  const buf = this.dragContextBuf;
@@ -3249,8 +3450,90 @@ var BottomSheetCore = class {
3249
3450
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3250
3451
  emit: (event, payload) => this.emit(event, payload),
3251
3452
  listenerCount: (event) => this.bus.listenerCount(event)
3453
+ };
3454
+ }
3455
+ isContentDragAllowed() {
3456
+ if (this.disableDragFlag) return false;
3457
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3458
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3459
+ }
3460
+ decideContentDrag(surface, e, delta, crossDelta) {
3461
+ if (this.destroyed) return false;
3462
+ if (e.pointerType === "mouse") return false;
3463
+ if (this.gesture?.isDragging) return false;
3464
+ if (!this.isContentDragAllowed()) return false;
3465
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3466
+ const decision = decideContentGesture({
3467
+ delta,
3468
+ crossDelta,
3469
+ sharesScrollAxis: this.mode === "bottom" || this.mode === "top",
3470
+ scrollTop: surface.scrollTop,
3471
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3472
+ });
3473
+ if (decision === "pending") return null;
3474
+ return decision === "drag";
3475
+ }
3476
+ attachDragSurface(surface, kind) {
3477
+ if (this.destroyed || kind !== "content") return;
3478
+ if (this.contentGesture) return;
3479
+ const controller = new GestureController(
3480
+ this.buildGestureDeps(surface, {
3481
+ manageTouchAction: false,
3482
+ deferStart: (e, delta, crossDelta) => this.decideContentDrag(surface, e, delta, crossDelta)
3483
+ })
3484
+ );
3485
+ this.contentGesture = controller;
3486
+ const detachGestures = controller.install();
3487
+ const detachGuard = installTouchScrollGuard(
3488
+ surface,
3489
+ () => controller.isDragging
3490
+ );
3491
+ return () => {
3492
+ detachGuard();
3493
+ detachGestures();
3494
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3495
+ };
3496
+ }
3497
+ sheetDragSurface() {
3498
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3499
+ }
3500
+ canStartSheetDrag(e) {
3501
+ if (this.contentGesture?.isDragging) return false;
3502
+ const target = e.target;
3503
+ const scoped = typeof target?.closest === "function";
3504
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3505
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3506
+ return true;
3507
+ }
3508
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3509
+ if (this.dragFromMode === "handle") return true;
3510
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3511
+ return false;
3512
+ }
3513
+ return true;
3514
+ }
3515
+ mountSheetGesture() {
3516
+ const surface = this.sheetDragSurface();
3517
+ const controller = new GestureController(
3518
+ this.buildGestureDeps(surface, {
3519
+ shouldStart: (e) => this.canStartSheetDrag(e),
3520
+ manageTouchAction: this.dragFromMode === "handle"
3521
+ })
3522
+ );
3523
+ this.gesture = controller;
3524
+ const detachGestures = controller.install();
3525
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3526
+ this.detachSheetGesture = () => {
3527
+ detachGuard?.();
3528
+ detachGestures();
3529
+ };
3530
+ }
3531
+ attach() {
3532
+ this.mountSheetGesture();
3533
+ this.teardowns.add(() => {
3534
+ this.detachSheetGesture?.();
3535
+ this.detachSheetGesture = null;
3252
3536
  });
3253
- this.teardowns.add(this.gesture.install());
3254
3537
  try {
3255
3538
  this.attachFeatures();
3256
3539
  } catch (err) {
@@ -3287,7 +3570,7 @@ var BottomSheetCore = class {
3287
3570
  element: this.element,
3288
3571
  getMode: () => this.mode,
3289
3572
  isDestroyed: () => this.destroyed,
3290
- isDragging: () => this.gesture?.isDragging ?? false,
3573
+ isDragging: () => this.isDraggingAny(),
3291
3574
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3292
3575
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3293
3576
  getSize: () => this.size,
@@ -3322,7 +3605,11 @@ var BottomSheetCore = class {
3322
3605
  );
3323
3606
  }
3324
3607
  }
3325
- this.teardowns.add(installSliderKeyboard({
3608
+ this.mountSliderKeyboard();
3609
+ this.scrim.attach();
3610
+ }
3611
+ mountSliderKeyboard() {
3612
+ const detach = installSliderKeyboard({
3326
3613
  handle: this.handle,
3327
3614
  mode: this.mode,
3328
3615
  isDestroyed: () => this.destroyed,
@@ -3331,8 +3618,12 @@ var BottomSheetCore = class {
3331
3618
  snapTo: (id) => {
3332
3619
  void this.snapTo(id);
3333
3620
  }
3334
- }));
3335
- this.scrim.attach();
3621
+ });
3622
+ this.detachSliderKeyboard = detach;
3623
+ this.teardowns.add(() => {
3624
+ this.detachSliderKeyboard?.();
3625
+ this.detachSliderKeyboard = null;
3626
+ });
3336
3627
  }
3337
3628
  settleAfterDrag(delta, velocity, kind = "touch") {
3338
3629
  const signal = this.newCycle();
@@ -3385,8 +3676,11 @@ var BottomSheetCore = class {
3385
3676
  );
3386
3677
  });
3387
3678
  }
3679
+ isDraggingAny() {
3680
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3681
+ }
3388
3682
  clearWillChangeIfIdle() {
3389
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3683
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3390
3684
  this.element.style.willChange = "";
3391
3685
  }
3392
3686
  haptic() {
@@ -3399,8 +3693,9 @@ var BottomSheetCore = class {
3399
3693
  }
3400
3694
  applySize(size, skipTransform = false) {
3401
3695
  const cap = this.snaps.getMaxAxisSize();
3402
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3696
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3403
3697
  this.size = clamped;
3698
+ if (clamped > 0) this.setRestClosed(false);
3404
3699
  const offset = cap - clamped;
3405
3700
  const style = this.element.style;
3406
3701
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3780,50 +4075,10 @@ function installAutoCollapse(deps) {
3780
4075
  };
3781
4076
  }
3782
4077
 
3783
- // src/core/features/content-swipe.ts
3784
- var CONTENT_PULL_THRESHOLD = 60;
3785
- function installContentSwipe(deps) {
3786
- const { container } = deps;
3787
- let startY = 0;
3788
- let startScrollTop = 0;
3789
- let moved = false;
3790
- const onTouchStart = (e) => {
3791
- const t = e.touches[0];
3792
- if (!t) return;
3793
- startY = t.clientY;
3794
- startScrollTop = container.scrollTop;
3795
- moved = false;
3796
- };
3797
- const onTouchMove = (e) => {
3798
- const t = e.touches[0];
3799
- if (!t) return;
3800
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3801
- };
3802
- const onTouchEnd = (e) => {
3803
- if (!moved) return;
3804
- if (deps.isDragging() || deps.isAnimating()) return;
3805
- if (container.scrollTop !== startScrollTop) return;
3806
- const t = e.changedTouches[0];
3807
- if (!t) return;
3808
- const deltaY = t.clientY - startY;
3809
- const atTop = container.scrollTop <= 0;
3810
- const allowed = deps.getAllowedIds();
3811
- const idx = allowed.indexOf(deps.getActiveId());
3812
- if (idx === -1) return;
3813
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3814
- deps.snapTo(allowed[idx - 1]);
3815
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3816
- deps.snapTo(allowed[idx + 1]);
3817
- }
3818
- };
3819
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3820
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3821
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3822
- return () => {
3823
- container.removeEventListener("touchstart", onTouchStart);
3824
- container.removeEventListener("touchmove", onTouchMove);
3825
- container.removeEventListener("touchend", onTouchEnd);
3826
- };
4078
+ // src/core/features/content-drag.ts
4079
+ function installContentDrag(deps) {
4080
+ const detach = deps.attachDragSurface(deps.container, "content");
4081
+ return () => detach?.();
3827
4082
  }
3828
4083
 
3829
4084
  // src/core/features/visual-viewport.ts
@@ -3931,13 +4186,9 @@ function contentSwipeFeature() {
3931
4186
  stage: "attach",
3932
4187
  install: (ctx) => {
3933
4188
  if (!ctx.scrollContainer) return;
3934
- return installContentSwipe({
4189
+ return installContentDrag({
3935
4190
  container: ctx.scrollContainer,
3936
- isDragging: ctx.isDragging,
3937
- isAnimating: ctx.isAnimating,
3938
- getAllowedIds: ctx.allowedIdsBySize,
3939
- getActiveId: ctx.getActiveId,
3940
- snapTo: ctx.snapTo
4191
+ attachDragSurface: ctx.attachDragSurface
3941
4192
  });
3942
4193
  }
3943
4194
  };
@@ -4053,6 +4304,16 @@ function useEnginePropSync(engineRef, opts) {
4053
4304
  engineRef.current?.setDisableDrag(opts.disableDrag);
4054
4305
  }
4055
4306
  }, [opts.disableDrag]);
4307
+ react.useEffect(() => {
4308
+ if (opts.dragFrom !== void 0) {
4309
+ engineRef.current?.setDragFrom(opts.dragFrom);
4310
+ }
4311
+ }, [opts.dragFrom]);
4312
+ react.useEffect(() => {
4313
+ if (opts.dragFromContent !== void 0) {
4314
+ engineRef.current?.setDragFromContent(opts.dragFromContent);
4315
+ }
4316
+ }, [opts.disableDrag]);
4056
4317
  }
4057
4318
 
4058
4319
  // src/core/features/teleport.ts
@@ -4201,7 +4462,9 @@ function useBottomSheet(opts) {
4201
4462
  maxHeight: opts.maxHeight,
4202
4463
  persistent: opts.persistent,
4203
4464
  disableClose: opts.disableClose,
4204
- disableDrag: opts.disableDrag
4465
+ disableDrag: opts.disableDrag,
4466
+ dragFrom: opts.dragFrom,
4467
+ dragFromContent: opts.dragFromContent
4205
4468
  });
4206
4469
  const subscribe = react.useCallback((fn) => {
4207
4470
  subscribersRef.current.add(fn);
@@ -4269,7 +4532,6 @@ function useBottomSheet(opts) {
4269
4532
  addAnchor,
4270
4533
  setScrimStages,
4271
4534
  recompute,
4272
- engine: engineRef.current,
4273
4535
  getEngine
4274
4536
  }),
4275
4537
  [