@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/qwik.js CHANGED
@@ -5,7 +5,7 @@ var __defProp = Object.defineProperty;
5
5
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
6
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
7
7
 
8
- // src/core/lifecycle/sheetStack.ts
8
+ // src/core/lifecycle/sheet-stack.ts
9
9
  var BASE_Z = 100;
10
10
  var STEP = 10;
11
11
  var SheetStack = class {
@@ -130,7 +130,7 @@ function createEventBus() {
130
130
  };
131
131
  }
132
132
 
133
- // src/core/primitives/cssLength.ts
133
+ // src/core/primitives/css-length.ts
134
134
  var probe = null;
135
135
  var PROBE_ATTR = "data-bs-probe";
136
136
  var ensureProbe = (axis) => {
@@ -184,7 +184,7 @@ var resolveSnap = (point, mode, measureFit) => {
184
184
  return Math.max(0, el.getBoundingClientRect().width);
185
185
  };
186
186
 
187
- // src/core/primitives/devWarn.ts
187
+ // src/core/primitives/dev-warn.ts
188
188
  function devWarn(...args) {
189
189
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" && typeof console !== "undefined") {
190
190
  console.warn(...args);
@@ -316,6 +316,11 @@ var SnapResolver = class {
316
316
  this.raw = raw;
317
317
  this.recompute();
318
318
  }
319
+ setMode(mode) {
320
+ if (mode === this.mode) return;
321
+ this.mode = mode;
322
+ this.recompute();
323
+ }
319
324
  setAllowedIds(ids) {
320
325
  this.allowedIds = ids;
321
326
  this.rangeCache = null;
@@ -899,6 +904,11 @@ var ScrimController = class {
899
904
  if (this.isDestroyed()) return;
900
905
  this.applyScrimStyles({ interactive });
901
906
  }
907
+ setSheetMode(mode) {
908
+ if (this.isDestroyed() || mode === this.mode) return;
909
+ this.mode = mode;
910
+ this.invalidateOpacityCache();
911
+ }
902
912
  setScrimMode(mode, applySize) {
903
913
  if (this.isDestroyed()) return;
904
914
  if (mode === this.scrimMode) return;
@@ -1517,7 +1527,7 @@ var AnimationRunner = class {
1517
1527
  }
1518
1528
  };
1519
1529
 
1520
- // src/core/lifecycle/focusTrap.ts
1530
+ // src/core/lifecycle/focus-trap.ts
1521
1531
  var FOCUSABLE = [
1522
1532
  "a[href]",
1523
1533
  "area[href]",
@@ -1635,7 +1645,7 @@ var installFocusTrap = (container, options = {}) => {
1635
1645
  };
1636
1646
  };
1637
1647
 
1638
- // src/core/lifecycle/scrollLock.ts
1648
+ // src/core/lifecycle/scroll-lock.ts
1639
1649
  var lockCount = 0;
1640
1650
  var savedStyles = null;
1641
1651
  var lockBodyScroll = () => {
@@ -1817,30 +1827,47 @@ var SampleRing = class {
1817
1827
  this.size = 0;
1818
1828
  }
1819
1829
  };
1820
- var installGestures = (handle, mode, callbacks) => {
1830
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1821
1831
  let activePointerId = null;
1822
1832
  let activePointerType = "touch";
1823
1833
  let startCoord = 0;
1834
+ let startCross = 0;
1824
1835
  let lastCoord = 0;
1836
+ let pendingStart = false;
1825
1837
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1826
1838
  const MAX_SAMPLES = 32;
1827
1839
  const samples = new SampleRing(MAX_SAMPLES);
1828
1840
  const axis = isAxisVertical(mode) ? "Y" : "X";
1829
1841
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1830
1842
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1843
+ const crossOf = (e) => axis === "Y" ? e.clientX : e.clientY;
1844
+ const capture = (pointerId) => {
1845
+ try {
1846
+ handle.setPointerCapture(pointerId);
1847
+ } catch {
1848
+ }
1849
+ };
1850
+ const releaseTracking = () => {
1851
+ activePointerId = null;
1852
+ pendingStart = false;
1853
+ samples.reset();
1854
+ };
1831
1855
  const onPointerDown = (e) => {
1832
1856
  if (activePointerId !== null) return;
1833
1857
  if (e.button !== void 0 && e.button !== 0) return;
1858
+ if (options.shouldStart && !options.shouldStart(e)) return;
1834
1859
  activePointerId = e.pointerId;
1835
1860
  activePointerType = e.pointerType || "touch";
1836
1861
  startCoord = coordOf(e);
1862
+ startCross = crossOf(e);
1837
1863
  lastCoord = startCoord;
1838
1864
  samples.reset();
1839
1865
  samples.push(e.timeStamp, startCoord);
1840
- try {
1841
- handle.setPointerCapture(e.pointerId);
1842
- } catch {
1866
+ if (options.deferStart) {
1867
+ pendingStart = true;
1868
+ return;
1843
1869
  }
1870
+ capture(e.pointerId);
1844
1871
  callbacks.onStart(startCoord, activePointerType);
1845
1872
  };
1846
1873
  const onPointerMove = (e) => {
@@ -1848,6 +1875,22 @@ var installGestures = (handle, mode, callbacks) => {
1848
1875
  const coord = coordOf(e);
1849
1876
  const rawDelta = coord - startCoord;
1850
1877
  const delta = rawDelta * sign;
1878
+ if (pendingStart) {
1879
+ const verdict = options.deferStart(e, delta, crossOf(e) - startCross);
1880
+ if (verdict === false) {
1881
+ releaseTracking();
1882
+ return;
1883
+ }
1884
+ if (verdict !== true) return;
1885
+ pendingStart = false;
1886
+ startCoord = coord;
1887
+ lastCoord = coord;
1888
+ samples.reset();
1889
+ samples.push(e.timeStamp, coord);
1890
+ capture(e.pointerId);
1891
+ callbacks.onStart(startCoord, activePointerType);
1892
+ return;
1893
+ }
1851
1894
  lastCoord = coord;
1852
1895
  samples.push(e.timeStamp, coord);
1853
1896
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1856,6 +1899,10 @@ var installGestures = (handle, mode, callbacks) => {
1856
1899
  };
1857
1900
  const finishGesture = (e) => {
1858
1901
  if (e.pointerId !== activePointerId) return;
1902
+ if (pendingStart) {
1903
+ releaseTracking();
1904
+ return;
1905
+ }
1859
1906
  const rawDelta = lastCoord - startCoord;
1860
1907
  const delta = rawDelta * sign;
1861
1908
  let velocity = 0;
@@ -1878,22 +1925,26 @@ var installGestures = (handle, mode, callbacks) => {
1878
1925
  };
1879
1926
  const onPointerCancel = (e) => {
1880
1927
  if (e.pointerId !== activePointerId) return;
1881
- activePointerId = null;
1882
- samples.reset();
1928
+ const wasPending = pendingStart;
1929
+ releaseTracking();
1930
+ if (wasPending) return;
1883
1931
  callbacks.onCancel?.();
1884
1932
  };
1885
1933
  handle.addEventListener("pointerdown", onPointerDown);
1886
1934
  handle.addEventListener("pointermove", onPointerMove);
1887
1935
  handle.addEventListener("pointerup", finishGesture);
1888
1936
  handle.addEventListener("pointercancel", onPointerCancel);
1937
+ const managesTouchAction = options.manageTouchAction !== false;
1889
1938
  const prevTouchAction = handle.style.touchAction;
1890
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1939
+ if (managesTouchAction) {
1940
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1941
+ }
1891
1942
  return () => {
1892
1943
  handle.removeEventListener("pointerdown", onPointerDown);
1893
1944
  handle.removeEventListener("pointermove", onPointerMove);
1894
1945
  handle.removeEventListener("pointerup", finishGesture);
1895
1946
  handle.removeEventListener("pointercancel", onPointerCancel);
1896
- handle.style.touchAction = prevTouchAction;
1947
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1897
1948
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1898
1949
  handle.releasePointerCapture(activePointerId);
1899
1950
  }
@@ -2012,7 +2063,7 @@ var GestureController = class {
2012
2063
  const restoreTo = this.dragStartSize;
2013
2064
  void this.deps.animateTo(restoreTo, 0);
2014
2065
  }
2015
- });
2066
+ }, this.deps.gestureOptions);
2016
2067
  return this.detach;
2017
2068
  }
2018
2069
  forceClearDragState() {
@@ -2024,6 +2075,74 @@ var GestureController = class {
2024
2075
  }
2025
2076
  };
2026
2077
 
2078
+ // src/core/primitives/drag-zones.ts
2079
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2080
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2081
+ var asElement = (target) => {
2082
+ const candidate = target;
2083
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2084
+ };
2085
+ function isDragAllowedFrom(target, mode) {
2086
+ const el = asElement(target);
2087
+ if (!el) return mode !== "zones";
2088
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2089
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2090
+ return true;
2091
+ }
2092
+
2093
+ // src/core/primitives/content-gesture.ts
2094
+ var CONTENT_DRAG_SLOP = 6;
2095
+ function decideContentGesture(input) {
2096
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2097
+ const cross = input.crossDelta ?? 0;
2098
+ const sharesScrollAxis = input.sharesScrollAxis ?? true;
2099
+ if (!sharesScrollAxis) {
2100
+ if (Math.abs(input.delta) < slop && Math.abs(cross) < slop) return "pending";
2101
+ if (Math.abs(input.delta) <= Math.abs(cross)) return "scroll";
2102
+ return "drag";
2103
+ }
2104
+ if (Math.abs(input.delta) < slop) return "pending";
2105
+ if (input.scrollTop > 0) return "scroll";
2106
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2107
+ return "drag";
2108
+ }
2109
+
2110
+ // src/core/primitives/logical-mode.ts
2111
+ var isLogicalMode = (mode) => mode === "start" || mode === "end";
2112
+ var readDirection = (el) => {
2113
+ if (!el) return "ltr";
2114
+ if (typeof getComputedStyle === "function") {
2115
+ try {
2116
+ if (getComputedStyle(el).direction === "rtl") return "rtl";
2117
+ } catch {
2118
+ }
2119
+ }
2120
+ try {
2121
+ const marked = el.closest?.("[dir]");
2122
+ if (marked) {
2123
+ return marked.getAttribute("dir")?.toLowerCase() === "rtl" ? "rtl" : "ltr";
2124
+ }
2125
+ } catch {
2126
+ }
2127
+ return "ltr";
2128
+ };
2129
+ var resolveMode = (mode, el) => {
2130
+ if (!isLogicalMode(mode)) return mode;
2131
+ const rtl = readDirection(el) === "rtl";
2132
+ if (mode === "start") return rtl ? "right" : "left";
2133
+ return rtl ? "left" : "right";
2134
+ };
2135
+
2136
+ // src/core/primitives/touch-scroll-guard.ts
2137
+ function installTouchScrollGuard(surface, isDragging) {
2138
+ const onTouchMove = (e) => {
2139
+ if (!isDragging()) return;
2140
+ if (e.cancelable) e.preventDefault();
2141
+ };
2142
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2143
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2144
+ }
2145
+
2027
2146
  // src/core/primitives/aria-slider-writer.ts
2028
2147
  var AriaSliderWriter = class {
2029
2148
  constructor(handle, mode) {
@@ -2376,9 +2495,11 @@ function resolveEngineOptions(opts, extras) {
2376
2495
  }
2377
2496
  }
2378
2497
  return {
2379
- mode: opts.mode ?? "bottom",
2498
+ mode: resolveMode(opts.mode ?? "bottom", opts.element),
2380
2499
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2381
2500
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2501
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2502
+ dragFromContent: opts.dragFromContent ?? true,
2382
2503
  rubberBandEnabled: opts.rubberBand ?? true,
2383
2504
  closeOnBack: opts.closeOnBack ?? false,
2384
2505
  persistent: opts.persistent ?? false,
@@ -2464,6 +2585,8 @@ var BottomSheetCore = class {
2464
2585
  __publicField(this, "mode");
2465
2586
  __publicField(this, "flickVelocity");
2466
2587
  __publicField(this, "dragThreshold");
2588
+ __publicField(this, "dragFromMode");
2589
+ __publicField(this, "dragFromContentDefault");
2467
2590
  __publicField(this, "rubberBandEnabled");
2468
2591
  __publicField(this, "scrim");
2469
2592
  __publicField(this, "aria");
@@ -2476,13 +2599,18 @@ var BottomSheetCore = class {
2476
2599
  __publicField(this, "disableDragFlag");
2477
2600
  __publicField(this, "closeOnRouteChange");
2478
2601
  __publicField(this, "maxHeight");
2602
+ __publicField(this, "maxHeightDeps");
2479
2603
  __publicField(this, "snapPointsRaw");
2480
2604
  __publicField(this, "snaps");
2481
2605
  __publicField(this, "activeId");
2482
2606
  __publicField(this, "size", 0);
2483
2607
  __publicField(this, "gesture");
2608
+ __publicField(this, "contentGesture");
2609
+ __publicField(this, "detachSheetGesture", null);
2610
+ __publicField(this, "detachSliderKeyboard", null);
2484
2611
  __publicField(this, "rootEl", null);
2485
2612
  __publicField(this, "destroyed", false);
2613
+ __publicField(this, "restClosed", false);
2486
2614
  __publicField(this, "currentViewTransition", null);
2487
2615
  __publicField(this, "currentAbort", new AbortController());
2488
2616
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2531,10 +2659,13 @@ var BottomSheetCore = class {
2531
2659
  this.screenComponent = opts.scrim ?? opts.screenComponent;
2532
2660
  this.scrimParent = this.screenComponent?.parentElement ?? null;
2533
2661
  this.mode = resolved.mode;
2662
+ this.element.dataset.mode = this.mode;
2534
2663
  this.transformTemplate = buildTransformTemplate(this.mode);
2535
2664
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2536
2665
  this.flickVelocity = resolved.flickVelocity;
2537
2666
  this.dragThreshold = resolved.dragThreshold;
2667
+ this.dragFromMode = resolved.dragFrom;
2668
+ this.dragFromContentDefault = resolved.dragFromContent;
2538
2669
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2539
2670
  this.scrim = new ScrimController(
2540
2671
  {
@@ -2561,7 +2692,7 @@ var BottomSheetCore = class {
2561
2692
  getRootEl: () => this.rootEl,
2562
2693
  applySize: (size) => this.applySize(size),
2563
2694
  getSize: () => this.size,
2564
- isDragging: () => this.gesture?.isDragging ?? false,
2695
+ isDragging: () => this.isDraggingAny(),
2565
2696
  applyAux: (size) => this.applySize(size, true),
2566
2697
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2567
2698
  },
@@ -2595,7 +2726,7 @@ var BottomSheetCore = class {
2595
2726
  auditVhUsage(this.snapPointsRaw);
2596
2727
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2597
2728
  this.rootEl = this.element.closest(".bs-root");
2598
- this.maxHeight = createMaxHeightController({
2729
+ this.maxHeightDeps = {
2599
2730
  element: this.element,
2600
2731
  mode: this.mode,
2601
2732
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
@@ -2603,7 +2734,8 @@ var BottomSheetCore = class {
2603
2734
  this.snaps.setMaxAxisSize(size);
2604
2735
  },
2605
2736
  recompute: () => this.recompute()
2606
- });
2737
+ };
2738
+ this.maxHeight = createMaxHeightController(this.maxHeightDeps);
2607
2739
  this.fitDeps = {
2608
2740
  element: this.element,
2609
2741
  scrollContainer: this.scrollContainer,
@@ -2622,6 +2754,7 @@ var BottomSheetCore = class {
2622
2754
  const initial = this.snaps.findById(this.activeId);
2623
2755
  if (initial) this.size = initial.size;
2624
2756
  this.applySize(this.size);
2757
+ this.setRestClosed(this.size === 0);
2625
2758
  this.updateAriaSlider();
2626
2759
  this.scrollCache = createScrollCache({
2627
2760
  scrollContainer: this.scrollContainer,
@@ -2652,7 +2785,7 @@ var BottomSheetCore = class {
2652
2785
  (p) => p.size === "fit" || p.size === "content"
2653
2786
  ),
2654
2787
  isDestroyed: () => this.destroyed,
2655
- isDragging: () => this.gesture?.isDragging ?? false,
2788
+ isDragging: () => this.isDraggingAny(),
2656
2789
  recompute: () => this.recompute()
2657
2790
  })
2658
2791
  );
@@ -2678,7 +2811,7 @@ var BottomSheetCore = class {
2678
2811
  autoCollapseAfter
2679
2812
  },
2680
2813
  isDestroyed: () => this.destroyed,
2681
- isDragging: () => this.gesture?.isDragging ?? false,
2814
+ isDragging: () => this.isDraggingAny(),
2682
2815
  isAnimating: () => this.animation.isAnimating,
2683
2816
  isTopSheet: () => this.isTopSheet,
2684
2817
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2708,6 +2841,7 @@ var BottomSheetCore = class {
2708
2841
  void this.snapTo(id);
2709
2842
  },
2710
2843
  close: (reason) => this.close(reason),
2844
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2711
2845
  on: (event, fn) => this.on(event, fn),
2712
2846
  addTeardown: (fn) => this.teardowns.add(fn)
2713
2847
  };
@@ -2736,7 +2870,7 @@ var BottomSheetCore = class {
2736
2870
  return {
2737
2871
  size: this.size,
2738
2872
  activeId: this.activeId,
2739
- isDragging: this.gesture?.isDragging ?? false,
2873
+ isDragging: this.isDraggingAny(),
2740
2874
  isAnimating: this.animation.isAnimating,
2741
2875
  progress: this.computeProgress(this.size)
2742
2876
  };
@@ -2856,9 +2990,16 @@ var BottomSheetCore = class {
2856
2990
  progress: this.computeProgress(this.size)
2857
2991
  });
2858
2992
  if (withHaptic) this.haptic();
2993
+ this.setRestClosed(targetSize === 0);
2859
2994
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2860
2995
  if (targetSize === 0) this.emitCloseSequence();
2861
2996
  }
2997
+ setRestClosed(closed) {
2998
+ if (closed === this.restClosed) return;
2999
+ this.restClosed = closed;
3000
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3001
+ else this.element.removeAttribute("data-bs-rest");
3002
+ }
2862
3003
  emitOpenSequence(id) {
2863
3004
  this.emit("open", { id });
2864
3005
  this.handleOpen();
@@ -2914,6 +3055,51 @@ var BottomSheetCore = class {
2914
3055
  if (this.destroyed) return;
2915
3056
  this.disableDragFlag = value;
2916
3057
  }
3058
+ setDragFromContent(value) {
3059
+ if (this.destroyed) return;
3060
+ this.dragFromContentDefault = value;
3061
+ }
3062
+ setDragFrom(mode) {
3063
+ if (this.destroyed || mode === this.dragFromMode) return;
3064
+ this.gesture?.forceClearDragState();
3065
+ this.detachSheetGesture?.();
3066
+ this.dragFromMode = mode;
3067
+ this.mountSheetGesture();
3068
+ }
3069
+ getDragFrom() {
3070
+ return this.dragFromMode;
3071
+ }
3072
+ setMode(mode) {
3073
+ if (this.destroyed) return;
3074
+ const next = resolveMode(mode, this.element);
3075
+ if (next === this.mode) return;
3076
+ const wasVertical = this.mode === "bottom" || this.mode === "top";
3077
+ const nowVertical = next === "bottom" || next === "top";
3078
+ this.gesture?.forceClearDragState();
3079
+ this.detachSheetGesture?.();
3080
+ this.animation.cancel();
3081
+ this.element.style.removeProperty(layoutAxis(this.mode));
3082
+ this.mode = next;
3083
+ this.element.dataset.mode = next;
3084
+ this.transformTemplate = buildTransformTemplate(next);
3085
+ this.maxHeightDeps.mode = next;
3086
+ this.fitDeps.mode = next;
3087
+ this.snaps.setMode(next);
3088
+ this.scrim.setSheetMode(next);
3089
+ if (wasVertical !== nowVertical) {
3090
+ this.aria = new AriaSliderWriter(this.handle, next);
3091
+ }
3092
+ this.detachSliderKeyboard?.();
3093
+ this.mountSliderKeyboard();
3094
+ this.mountSheetGesture();
3095
+ this.recompute();
3096
+ const active = this.snaps.findById(this.activeId);
3097
+ if (active) this.applySize(active.size);
3098
+ this.updateAriaSlider();
3099
+ }
3100
+ getMode() {
3101
+ return this.mode;
3102
+ }
2917
3103
  isTop() {
2918
3104
  return this.isTopSheet;
2919
3105
  }
@@ -3068,12 +3254,24 @@ var BottomSheetCore = class {
3068
3254
  auditVhUsage(points);
3069
3255
  this.auditDuplicateSnapIds(points);
3070
3256
  this.snaps.setRaw(points);
3071
- if (allowed) this.snaps.setAllowedIds(allowed);
3257
+ if (allowed) {
3258
+ this.snaps.setAllowedIds(allowed);
3259
+ } else {
3260
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3261
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3262
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3263
+ }
3072
3264
  this.scrollCache.clear();
3073
3265
  this.scrim.invalidateOpacityCache();
3074
3266
  const current = this.snaps.findById(this.activeId);
3075
3267
  if (current) {
3076
3268
  this.applySize(current.size);
3269
+ } else {
3270
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3271
+ if (fallback) {
3272
+ this.activeId = fallback.id;
3273
+ this.applySize(fallback.size);
3274
+ }
3077
3275
  }
3078
3276
  this.updateAriaSlider();
3079
3277
  }
@@ -3083,7 +3281,7 @@ var BottomSheetCore = class {
3083
3281
  this.snaps.recompute();
3084
3282
  this.maxHeight.clampTo();
3085
3283
  this.scrim.invalidateOpacityCache();
3086
- if (this.gesture?.isDragging) return;
3284
+ if (this.isDraggingAny()) return;
3087
3285
  this.healActiveSnap();
3088
3286
  }
3089
3287
  healActiveSnap() {
@@ -3105,6 +3303,8 @@ var BottomSheetCore = class {
3105
3303
  this.bus.clear();
3106
3304
  this.animation.cancel();
3107
3305
  this.gesture?.forceClearDragState();
3306
+ this.contentGesture?.forceClearDragState();
3307
+ this.setRestClosed(false);
3108
3308
  this.teardowns.drain();
3109
3309
  this.scrim.destroy();
3110
3310
  this.animation.destroy();
@@ -3217,11 +3417,12 @@ var BottomSheetCore = class {
3217
3417
  const scale = Math.max(1 - depth * 0.04, 0.86);
3218
3418
  this.element.style.scale = scale === 1 ? "" : String(scale);
3219
3419
  }
3220
- attach() {
3221
- this.gesture = new GestureController({
3222
- handle: this.handle,
3420
+ buildGestureDeps(surface, gestureOptions) {
3421
+ return {
3422
+ handle: surface,
3223
3423
  element: this.element,
3224
3424
  mode: this.mode,
3425
+ gestureOptions,
3225
3426
  getRoot: () => this.rootEl,
3226
3427
  getDragContext: () => {
3227
3428
  const buf = this.dragContextBuf;
@@ -3240,8 +3441,90 @@ var BottomSheetCore = class {
3240
3441
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3241
3442
  emit: (event, payload) => this.emit(event, payload),
3242
3443
  listenerCount: (event) => this.bus.listenerCount(event)
3444
+ };
3445
+ }
3446
+ isContentDragAllowed() {
3447
+ if (this.disableDragFlag) return false;
3448
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3449
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3450
+ }
3451
+ decideContentDrag(surface, e, delta, crossDelta) {
3452
+ if (this.destroyed) return false;
3453
+ if (e.pointerType === "mouse") return false;
3454
+ if (this.gesture?.isDragging) return false;
3455
+ if (!this.isContentDragAllowed()) return false;
3456
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3457
+ const decision = decideContentGesture({
3458
+ delta,
3459
+ crossDelta,
3460
+ sharesScrollAxis: this.mode === "bottom" || this.mode === "top",
3461
+ scrollTop: surface.scrollTop,
3462
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3463
+ });
3464
+ if (decision === "pending") return null;
3465
+ return decision === "drag";
3466
+ }
3467
+ attachDragSurface(surface, kind) {
3468
+ if (this.destroyed || kind !== "content") return;
3469
+ if (this.contentGesture) return;
3470
+ const controller = new GestureController(
3471
+ this.buildGestureDeps(surface, {
3472
+ manageTouchAction: false,
3473
+ deferStart: (e, delta, crossDelta) => this.decideContentDrag(surface, e, delta, crossDelta)
3474
+ })
3475
+ );
3476
+ this.contentGesture = controller;
3477
+ const detachGestures = controller.install();
3478
+ const detachGuard = installTouchScrollGuard(
3479
+ surface,
3480
+ () => controller.isDragging
3481
+ );
3482
+ return () => {
3483
+ detachGuard();
3484
+ detachGestures();
3485
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3486
+ };
3487
+ }
3488
+ sheetDragSurface() {
3489
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3490
+ }
3491
+ canStartSheetDrag(e) {
3492
+ if (this.contentGesture?.isDragging) return false;
3493
+ const target = e.target;
3494
+ const scoped = typeof target?.closest === "function";
3495
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3496
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3497
+ return true;
3498
+ }
3499
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3500
+ if (this.dragFromMode === "handle") return true;
3501
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3502
+ return false;
3503
+ }
3504
+ return true;
3505
+ }
3506
+ mountSheetGesture() {
3507
+ const surface = this.sheetDragSurface();
3508
+ const controller = new GestureController(
3509
+ this.buildGestureDeps(surface, {
3510
+ shouldStart: (e) => this.canStartSheetDrag(e),
3511
+ manageTouchAction: this.dragFromMode === "handle"
3512
+ })
3513
+ );
3514
+ this.gesture = controller;
3515
+ const detachGestures = controller.install();
3516
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3517
+ this.detachSheetGesture = () => {
3518
+ detachGuard?.();
3519
+ detachGestures();
3520
+ };
3521
+ }
3522
+ attach() {
3523
+ this.mountSheetGesture();
3524
+ this.teardowns.add(() => {
3525
+ this.detachSheetGesture?.();
3526
+ this.detachSheetGesture = null;
3243
3527
  });
3244
- this.teardowns.add(this.gesture.install());
3245
3528
  try {
3246
3529
  this.attachFeatures();
3247
3530
  } catch (err) {
@@ -3278,7 +3561,7 @@ var BottomSheetCore = class {
3278
3561
  element: this.element,
3279
3562
  getMode: () => this.mode,
3280
3563
  isDestroyed: () => this.destroyed,
3281
- isDragging: () => this.gesture?.isDragging ?? false,
3564
+ isDragging: () => this.isDraggingAny(),
3282
3565
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3283
3566
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3284
3567
  getSize: () => this.size,
@@ -3313,7 +3596,11 @@ var BottomSheetCore = class {
3313
3596
  );
3314
3597
  }
3315
3598
  }
3316
- this.teardowns.add(installSliderKeyboard({
3599
+ this.mountSliderKeyboard();
3600
+ this.scrim.attach();
3601
+ }
3602
+ mountSliderKeyboard() {
3603
+ const detach = installSliderKeyboard({
3317
3604
  handle: this.handle,
3318
3605
  mode: this.mode,
3319
3606
  isDestroyed: () => this.destroyed,
@@ -3322,8 +3609,12 @@ var BottomSheetCore = class {
3322
3609
  snapTo: (id) => {
3323
3610
  void this.snapTo(id);
3324
3611
  }
3325
- }));
3326
- this.scrim.attach();
3612
+ });
3613
+ this.detachSliderKeyboard = detach;
3614
+ this.teardowns.add(() => {
3615
+ this.detachSliderKeyboard?.();
3616
+ this.detachSliderKeyboard = null;
3617
+ });
3327
3618
  }
3328
3619
  settleAfterDrag(delta, velocity, kind = "touch") {
3329
3620
  const signal = this.newCycle();
@@ -3376,8 +3667,11 @@ var BottomSheetCore = class {
3376
3667
  );
3377
3668
  });
3378
3669
  }
3670
+ isDraggingAny() {
3671
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3672
+ }
3379
3673
  clearWillChangeIfIdle() {
3380
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3674
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3381
3675
  this.element.style.willChange = "";
3382
3676
  }
3383
3677
  haptic() {
@@ -3390,8 +3684,9 @@ var BottomSheetCore = class {
3390
3684
  }
3391
3685
  applySize(size, skipTransform = false) {
3392
3686
  const cap = this.snaps.getMaxAxisSize();
3393
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3687
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3394
3688
  this.size = clamped;
3689
+ if (clamped > 0) this.setRestClosed(false);
3395
3690
  const offset = cap - clamped;
3396
3691
  const style = this.element.style;
3397
3692
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3771,50 +4066,10 @@ function installAutoCollapse(deps) {
3771
4066
  };
3772
4067
  }
3773
4068
 
3774
- // src/core/features/content-swipe.ts
3775
- var CONTENT_PULL_THRESHOLD = 60;
3776
- function installContentSwipe(deps) {
3777
- const { container } = deps;
3778
- let startY = 0;
3779
- let startScrollTop = 0;
3780
- let moved = false;
3781
- const onTouchStart = (e) => {
3782
- const t = e.touches[0];
3783
- if (!t) return;
3784
- startY = t.clientY;
3785
- startScrollTop = container.scrollTop;
3786
- moved = false;
3787
- };
3788
- const onTouchMove = (e) => {
3789
- const t = e.touches[0];
3790
- if (!t) return;
3791
- if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
3792
- };
3793
- const onTouchEnd = (e) => {
3794
- if (!moved) return;
3795
- if (deps.isDragging() || deps.isAnimating()) return;
3796
- if (container.scrollTop !== startScrollTop) return;
3797
- const t = e.changedTouches[0];
3798
- if (!t) return;
3799
- const deltaY = t.clientY - startY;
3800
- const atTop = container.scrollTop <= 0;
3801
- const allowed = deps.getAllowedIds();
3802
- const idx = allowed.indexOf(deps.getActiveId());
3803
- if (idx === -1) return;
3804
- if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
3805
- deps.snapTo(allowed[idx - 1]);
3806
- } else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
3807
- deps.snapTo(allowed[idx + 1]);
3808
- }
3809
- };
3810
- container.addEventListener("touchstart", onTouchStart, { passive: true });
3811
- container.addEventListener("touchmove", onTouchMove, { passive: true });
3812
- container.addEventListener("touchend", onTouchEnd, { passive: true });
3813
- return () => {
3814
- container.removeEventListener("touchstart", onTouchStart);
3815
- container.removeEventListener("touchmove", onTouchMove);
3816
- container.removeEventListener("touchend", onTouchEnd);
3817
- };
4069
+ // src/core/features/content-drag.ts
4070
+ function installContentDrag(deps) {
4071
+ const detach = deps.attachDragSurface(deps.container, "content");
4072
+ return () => detach?.();
3818
4073
  }
3819
4074
 
3820
4075
  // src/core/features/visual-viewport.ts
@@ -3922,13 +4177,9 @@ function contentSwipeFeature() {
3922
4177
  stage: "attach",
3923
4178
  install: (ctx) => {
3924
4179
  if (!ctx.scrollContainer) return;
3925
- return installContentSwipe({
4180
+ return installContentDrag({
3926
4181
  container: ctx.scrollContainer,
3927
- isDragging: ctx.isDragging,
3928
- isAnimating: ctx.isAnimating,
3929
- getAllowedIds: ctx.allowedIdsBySize,
3930
- getActiveId: ctx.getActiveId,
3931
- snapTo: ctx.snapTo
4182
+ attachDragSurface: ctx.attachDragSurface
3932
4183
  });
3933
4184
  }
3934
4185
  };
@@ -4058,6 +4309,8 @@ var BottomSheet = component$((props) => {
4058
4309
  persistent: props.persistent,
4059
4310
  disableClose: props.disableClose,
4060
4311
  disableDrag: props.disableDrag,
4312
+ dragFrom: props.dragFrom,
4313
+ dragFromContent: props.dragFromContent,
4061
4314
  closeOnRouteChange: props.closeOnRouteChange,
4062
4315
  stackEffect: props.stackEffect,
4063
4316
  returnFocusTo: props.returnFocusTo,
@@ -4167,11 +4420,17 @@ var BottomSheet = component$((props) => {
4167
4420
  const persistent = track(() => props.persistent);
4168
4421
  const disableClose = track(() => props.disableClose);
4169
4422
  const disableDrag = track(() => props.disableDrag);
4423
+ const dragFromContent = track(() => props.dragFromContent);
4424
+ const dragFrom = track(() => props.dragFrom);
4170
4425
  const engine = engineStore.engine;
4171
4426
  if (!engine) return;
4172
4427
  if (persistent !== void 0) engine.setPersistent(persistent);
4173
4428
  if (disableClose !== void 0) engine.setDisableClose(disableClose);
4174
4429
  if (disableDrag !== void 0) engine.setDisableDrag(disableDrag);
4430
+ if (dragFromContent !== void 0) {
4431
+ engine.setDragFromContent(dragFromContent);
4432
+ }
4433
+ if (dragFrom !== void 0) engine.setDragFrom(dragFrom);
4175
4434
  });
4176
4435
  const showBackdrop = props.backdrop !== false;
4177
4436
  const mode = props.mode ?? "bottom";