@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/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);
@@ -317,6 +317,11 @@ var SnapResolver = class {
317
317
  this.raw = raw;
318
318
  this.recompute();
319
319
  }
320
+ setMode(mode) {
321
+ if (mode === this.mode) return;
322
+ this.mode = mode;
323
+ this.recompute();
324
+ }
320
325
  setAllowedIds(ids) {
321
326
  this.allowedIds = ids;
322
327
  this.rangeCache = null;
@@ -900,6 +905,11 @@ var ScrimController = class {
900
905
  if (this.isDestroyed()) return;
901
906
  this.applyScrimStyles({ interactive });
902
907
  }
908
+ setSheetMode(mode) {
909
+ if (this.isDestroyed() || mode === this.mode) return;
910
+ this.mode = mode;
911
+ this.invalidateOpacityCache();
912
+ }
903
913
  setScrimMode(mode, applySize) {
904
914
  if (this.isDestroyed()) return;
905
915
  if (mode === this.scrimMode) return;
@@ -1518,7 +1528,7 @@ var AnimationRunner = class {
1518
1528
  }
1519
1529
  };
1520
1530
 
1521
- // src/core/lifecycle/focusTrap.ts
1531
+ // src/core/lifecycle/focus-trap.ts
1522
1532
  var FOCUSABLE = [
1523
1533
  "a[href]",
1524
1534
  "area[href]",
@@ -1636,7 +1646,7 @@ var installFocusTrap = (container, options = {}) => {
1636
1646
  };
1637
1647
  };
1638
1648
 
1639
- // src/core/lifecycle/scrollLock.ts
1649
+ // src/core/lifecycle/scroll-lock.ts
1640
1650
  var lockCount = 0;
1641
1651
  var savedStyles = null;
1642
1652
  var lockBodyScroll = () => {
@@ -1818,30 +1828,47 @@ var SampleRing = class {
1818
1828
  this.size = 0;
1819
1829
  }
1820
1830
  };
1821
- var installGestures = (handle, mode, callbacks) => {
1831
+ var installGestures = (handle, mode, callbacks, options = {}) => {
1822
1832
  let activePointerId = null;
1823
1833
  let activePointerType = "touch";
1824
1834
  let startCoord = 0;
1835
+ let startCross = 0;
1825
1836
  let lastCoord = 0;
1837
+ let pendingStart = false;
1826
1838
  const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
1827
1839
  const MAX_SAMPLES = 32;
1828
1840
  const samples = new SampleRing(MAX_SAMPLES);
1829
1841
  const axis = isAxisVertical(mode) ? "Y" : "X";
1830
1842
  const sign = mode === "bottom" || mode === "right" ? -1 : 1;
1831
1843
  const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
1844
+ const crossOf = (e) => axis === "Y" ? e.clientX : e.clientY;
1845
+ const capture = (pointerId) => {
1846
+ try {
1847
+ handle.setPointerCapture(pointerId);
1848
+ } catch {
1849
+ }
1850
+ };
1851
+ const releaseTracking = () => {
1852
+ activePointerId = null;
1853
+ pendingStart = false;
1854
+ samples.reset();
1855
+ };
1832
1856
  const onPointerDown = (e) => {
1833
1857
  if (activePointerId !== null) return;
1834
1858
  if (e.button !== void 0 && e.button !== 0) return;
1859
+ if (options.shouldStart && !options.shouldStart(e)) return;
1835
1860
  activePointerId = e.pointerId;
1836
1861
  activePointerType = e.pointerType || "touch";
1837
1862
  startCoord = coordOf(e);
1863
+ startCross = crossOf(e);
1838
1864
  lastCoord = startCoord;
1839
1865
  samples.reset();
1840
1866
  samples.push(e.timeStamp, startCoord);
1841
- try {
1842
- handle.setPointerCapture(e.pointerId);
1843
- } catch {
1867
+ if (options.deferStart) {
1868
+ pendingStart = true;
1869
+ return;
1844
1870
  }
1871
+ capture(e.pointerId);
1845
1872
  callbacks.onStart(startCoord, activePointerType);
1846
1873
  };
1847
1874
  const onPointerMove = (e) => {
@@ -1849,6 +1876,22 @@ var installGestures = (handle, mode, callbacks) => {
1849
1876
  const coord = coordOf(e);
1850
1877
  const rawDelta = coord - startCoord;
1851
1878
  const delta = rawDelta * sign;
1879
+ if (pendingStart) {
1880
+ const verdict = options.deferStart(e, delta, crossOf(e) - startCross);
1881
+ if (verdict === false) {
1882
+ releaseTracking();
1883
+ return;
1884
+ }
1885
+ if (verdict !== true) return;
1886
+ pendingStart = false;
1887
+ startCoord = coord;
1888
+ lastCoord = coord;
1889
+ samples.reset();
1890
+ samples.push(e.timeStamp, coord);
1891
+ capture(e.pointerId);
1892
+ callbacks.onStart(startCoord, activePointerType);
1893
+ return;
1894
+ }
1852
1895
  lastCoord = coord;
1853
1896
  samples.push(e.timeStamp, coord);
1854
1897
  const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
@@ -1857,6 +1900,10 @@ var installGestures = (handle, mode, callbacks) => {
1857
1900
  };
1858
1901
  const finishGesture = (e) => {
1859
1902
  if (e.pointerId !== activePointerId) return;
1903
+ if (pendingStart) {
1904
+ releaseTracking();
1905
+ return;
1906
+ }
1860
1907
  const rawDelta = lastCoord - startCoord;
1861
1908
  const delta = rawDelta * sign;
1862
1909
  let velocity = 0;
@@ -1879,22 +1926,26 @@ var installGestures = (handle, mode, callbacks) => {
1879
1926
  };
1880
1927
  const onPointerCancel = (e) => {
1881
1928
  if (e.pointerId !== activePointerId) return;
1882
- activePointerId = null;
1883
- samples.reset();
1929
+ const wasPending = pendingStart;
1930
+ releaseTracking();
1931
+ if (wasPending) return;
1884
1932
  callbacks.onCancel?.();
1885
1933
  };
1886
1934
  handle.addEventListener("pointerdown", onPointerDown);
1887
1935
  handle.addEventListener("pointermove", onPointerMove);
1888
1936
  handle.addEventListener("pointerup", finishGesture);
1889
1937
  handle.addEventListener("pointercancel", onPointerCancel);
1938
+ const managesTouchAction = options.manageTouchAction !== false;
1890
1939
  const prevTouchAction = handle.style.touchAction;
1891
- handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1940
+ if (managesTouchAction) {
1941
+ handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
1942
+ }
1892
1943
  return () => {
1893
1944
  handle.removeEventListener("pointerdown", onPointerDown);
1894
1945
  handle.removeEventListener("pointermove", onPointerMove);
1895
1946
  handle.removeEventListener("pointerup", finishGesture);
1896
1947
  handle.removeEventListener("pointercancel", onPointerCancel);
1897
- handle.style.touchAction = prevTouchAction;
1948
+ if (managesTouchAction) handle.style.touchAction = prevTouchAction;
1898
1949
  if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
1899
1950
  handle.releasePointerCapture(activePointerId);
1900
1951
  }
@@ -2013,7 +2064,7 @@ var GestureController = class {
2013
2064
  const restoreTo = this.dragStartSize;
2014
2065
  void this.deps.animateTo(restoreTo, 0);
2015
2066
  }
2016
- });
2067
+ }, this.deps.gestureOptions);
2017
2068
  return this.detach;
2018
2069
  }
2019
2070
  forceClearDragState() {
@@ -2025,6 +2076,74 @@ var GestureController = class {
2025
2076
  }
2026
2077
  };
2027
2078
 
2079
+ // src/core/primitives/drag-zones.ts
2080
+ var DRAG_ZONE_SELECTOR = "[data-bs-drag]";
2081
+ var NO_DRAG_SELECTOR = "[data-bs-no-drag]";
2082
+ var asElement = (target) => {
2083
+ const candidate = target;
2084
+ return candidate && typeof candidate.closest === "function" ? candidate : null;
2085
+ };
2086
+ function isDragAllowedFrom(target, mode) {
2087
+ const el = asElement(target);
2088
+ if (!el) return mode !== "zones";
2089
+ if (el.closest(NO_DRAG_SELECTOR)) return false;
2090
+ if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
2091
+ return true;
2092
+ }
2093
+
2094
+ // src/core/primitives/content-gesture.ts
2095
+ var CONTENT_DRAG_SLOP = 6;
2096
+ function decideContentGesture(input) {
2097
+ const slop = input.slop ?? CONTENT_DRAG_SLOP;
2098
+ const cross = input.crossDelta ?? 0;
2099
+ const sharesScrollAxis = input.sharesScrollAxis ?? true;
2100
+ if (!sharesScrollAxis) {
2101
+ if (Math.abs(input.delta) < slop && Math.abs(cross) < slop) return "pending";
2102
+ if (Math.abs(input.delta) <= Math.abs(cross)) return "scroll";
2103
+ return "drag";
2104
+ }
2105
+ if (Math.abs(input.delta) < slop) return "pending";
2106
+ if (input.scrollTop > 0) return "scroll";
2107
+ if (input.delta > 0 && input.atMaxSnap) return "scroll";
2108
+ return "drag";
2109
+ }
2110
+
2111
+ // src/core/primitives/logical-mode.ts
2112
+ var isLogicalMode = (mode) => mode === "start" || mode === "end";
2113
+ var readDirection = (el) => {
2114
+ if (!el) return "ltr";
2115
+ if (typeof getComputedStyle === "function") {
2116
+ try {
2117
+ if (getComputedStyle(el).direction === "rtl") return "rtl";
2118
+ } catch {
2119
+ }
2120
+ }
2121
+ try {
2122
+ const marked = el.closest?.("[dir]");
2123
+ if (marked) {
2124
+ return marked.getAttribute("dir")?.toLowerCase() === "rtl" ? "rtl" : "ltr";
2125
+ }
2126
+ } catch {
2127
+ }
2128
+ return "ltr";
2129
+ };
2130
+ var resolveMode = (mode, el) => {
2131
+ if (!isLogicalMode(mode)) return mode;
2132
+ const rtl = readDirection(el) === "rtl";
2133
+ if (mode === "start") return rtl ? "right" : "left";
2134
+ return rtl ? "left" : "right";
2135
+ };
2136
+
2137
+ // src/core/primitives/touch-scroll-guard.ts
2138
+ function installTouchScrollGuard(surface, isDragging) {
2139
+ const onTouchMove = (e) => {
2140
+ if (!isDragging()) return;
2141
+ if (e.cancelable) e.preventDefault();
2142
+ };
2143
+ surface.addEventListener("touchmove", onTouchMove, { passive: false });
2144
+ return () => surface.removeEventListener("touchmove", onTouchMove);
2145
+ }
2146
+
2028
2147
  // src/core/primitives/aria-slider-writer.ts
2029
2148
  var AriaSliderWriter = class {
2030
2149
  constructor(handle, mode) {
@@ -2377,9 +2496,11 @@ function resolveEngineOptions(opts, extras) {
2377
2496
  }
2378
2497
  }
2379
2498
  return {
2380
- mode: opts.mode ?? "bottom",
2499
+ mode: resolveMode(opts.mode ?? "bottom", opts.element),
2381
2500
  flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
2382
2501
  dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
2502
+ dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
2503
+ dragFromContent: opts.dragFromContent ?? true,
2383
2504
  rubberBandEnabled: opts.rubberBand ?? true,
2384
2505
  closeOnBack: opts.closeOnBack ?? false,
2385
2506
  persistent: opts.persistent ?? false,
@@ -2465,6 +2586,8 @@ var BottomSheetCore = class {
2465
2586
  __publicField(this, "mode");
2466
2587
  __publicField(this, "flickVelocity");
2467
2588
  __publicField(this, "dragThreshold");
2589
+ __publicField(this, "dragFromMode");
2590
+ __publicField(this, "dragFromContentDefault");
2468
2591
  __publicField(this, "rubberBandEnabled");
2469
2592
  __publicField(this, "scrim");
2470
2593
  __publicField(this, "aria");
@@ -2477,13 +2600,18 @@ var BottomSheetCore = class {
2477
2600
  __publicField(this, "disableDragFlag");
2478
2601
  __publicField(this, "closeOnRouteChange");
2479
2602
  __publicField(this, "maxHeight");
2603
+ __publicField(this, "maxHeightDeps");
2480
2604
  __publicField(this, "snapPointsRaw");
2481
2605
  __publicField(this, "snaps");
2482
2606
  __publicField(this, "activeId");
2483
2607
  __publicField(this, "size", 0);
2484
2608
  __publicField(this, "gesture");
2609
+ __publicField(this, "contentGesture");
2610
+ __publicField(this, "detachSheetGesture", null);
2611
+ __publicField(this, "detachSliderKeyboard", null);
2485
2612
  __publicField(this, "rootEl", null);
2486
2613
  __publicField(this, "destroyed", false);
2614
+ __publicField(this, "restClosed", false);
2487
2615
  __publicField(this, "currentViewTransition", null);
2488
2616
  __publicField(this, "currentAbort", new AbortController());
2489
2617
  __publicField(this, "sizeWriteSentinel", new WriteSentinel());
@@ -2532,10 +2660,13 @@ var BottomSheetCore = class {
2532
2660
  this.screenComponent = opts.scrim ?? opts.screenComponent;
2533
2661
  this.scrimParent = this.screenComponent?.parentElement ?? null;
2534
2662
  this.mode = resolved.mode;
2663
+ this.element.dataset.mode = this.mode;
2535
2664
  this.transformTemplate = buildTransformTemplate(this.mode);
2536
2665
  this.aria = new AriaSliderWriter(this.handle, this.mode);
2537
2666
  this.flickVelocity = resolved.flickVelocity;
2538
2667
  this.dragThreshold = resolved.dragThreshold;
2668
+ this.dragFromMode = resolved.dragFrom;
2669
+ this.dragFromContentDefault = resolved.dragFromContent;
2539
2670
  this.rubberBandEnabled = resolved.rubberBandEnabled;
2540
2671
  this.scrim = new ScrimController(
2541
2672
  {
@@ -2562,7 +2693,7 @@ var BottomSheetCore = class {
2562
2693
  getRootEl: () => this.rootEl,
2563
2694
  applySize: (size) => this.applySize(size),
2564
2695
  getSize: () => this.size,
2565
- isDragging: () => this.gesture?.isDragging ?? false,
2696
+ isDragging: () => this.isDraggingAny(),
2566
2697
  applyAux: (size) => this.applySize(size, true),
2567
2698
  getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
2568
2699
  },
@@ -2596,7 +2727,7 @@ var BottomSheetCore = class {
2596
2727
  auditVhUsage(this.snapPointsRaw);
2597
2728
  this.auditDuplicateSnapIds(this.snapPointsRaw);
2598
2729
  this.rootEl = this.element.closest(".bs-root");
2599
- this.maxHeight = createMaxHeightController({
2730
+ this.maxHeightDeps = {
2600
2731
  element: this.element,
2601
2732
  mode: this.mode,
2602
2733
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
@@ -2604,7 +2735,8 @@ var BottomSheetCore = class {
2604
2735
  this.snaps.setMaxAxisSize(size);
2605
2736
  },
2606
2737
  recompute: () => this.recompute()
2607
- });
2738
+ };
2739
+ this.maxHeight = createMaxHeightController(this.maxHeightDeps);
2608
2740
  this.fitDeps = {
2609
2741
  element: this.element,
2610
2742
  scrollContainer: this.scrollContainer,
@@ -2623,6 +2755,7 @@ var BottomSheetCore = class {
2623
2755
  const initial = this.snaps.findById(this.activeId);
2624
2756
  if (initial) this.size = initial.size;
2625
2757
  this.applySize(this.size);
2758
+ this.setRestClosed(this.size === 0);
2626
2759
  this.updateAriaSlider();
2627
2760
  this.scrollCache = createScrollCache({
2628
2761
  scrollContainer: this.scrollContainer,
@@ -2653,7 +2786,7 @@ var BottomSheetCore = class {
2653
2786
  (p) => p.size === "fit" || p.size === "content"
2654
2787
  ),
2655
2788
  isDestroyed: () => this.destroyed,
2656
- isDragging: () => this.gesture?.isDragging ?? false,
2789
+ isDragging: () => this.isDraggingAny(),
2657
2790
  recompute: () => this.recompute()
2658
2791
  })
2659
2792
  );
@@ -2679,7 +2812,7 @@ var BottomSheetCore = class {
2679
2812
  autoCollapseAfter
2680
2813
  },
2681
2814
  isDestroyed: () => this.destroyed,
2682
- isDragging: () => this.gesture?.isDragging ?? false,
2815
+ isDragging: () => this.isDraggingAny(),
2683
2816
  isAnimating: () => this.animation.isAnimating,
2684
2817
  isTopSheet: () => this.isTopSheet,
2685
2818
  isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
@@ -2709,6 +2842,7 @@ var BottomSheetCore = class {
2709
2842
  void this.snapTo(id);
2710
2843
  },
2711
2844
  close: (reason) => this.close(reason),
2845
+ attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
2712
2846
  on: (event, fn) => this.on(event, fn),
2713
2847
  addTeardown: (fn) => this.teardowns.add(fn)
2714
2848
  };
@@ -2737,7 +2871,7 @@ var BottomSheetCore = class {
2737
2871
  return {
2738
2872
  size: this.size,
2739
2873
  activeId: this.activeId,
2740
- isDragging: this.gesture?.isDragging ?? false,
2874
+ isDragging: this.isDraggingAny(),
2741
2875
  isAnimating: this.animation.isAnimating,
2742
2876
  progress: this.computeProgress(this.size)
2743
2877
  };
@@ -2857,9 +2991,16 @@ var BottomSheetCore = class {
2857
2991
  progress: this.computeProgress(this.size)
2858
2992
  });
2859
2993
  if (withHaptic) this.haptic();
2994
+ this.setRestClosed(targetSize === 0);
2860
2995
  if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
2861
2996
  if (targetSize === 0) this.emitCloseSequence();
2862
2997
  }
2998
+ setRestClosed(closed) {
2999
+ if (closed === this.restClosed) return;
3000
+ this.restClosed = closed;
3001
+ if (closed) this.element.setAttribute("data-bs-rest", "closed");
3002
+ else this.element.removeAttribute("data-bs-rest");
3003
+ }
2863
3004
  emitOpenSequence(id) {
2864
3005
  this.emit("open", { id });
2865
3006
  this.handleOpen();
@@ -2915,6 +3056,51 @@ var BottomSheetCore = class {
2915
3056
  if (this.destroyed) return;
2916
3057
  this.disableDragFlag = value;
2917
3058
  }
3059
+ setDragFromContent(value) {
3060
+ if (this.destroyed) return;
3061
+ this.dragFromContentDefault = value;
3062
+ }
3063
+ setDragFrom(mode) {
3064
+ if (this.destroyed || mode === this.dragFromMode) return;
3065
+ this.gesture?.forceClearDragState();
3066
+ this.detachSheetGesture?.();
3067
+ this.dragFromMode = mode;
3068
+ this.mountSheetGesture();
3069
+ }
3070
+ getDragFrom() {
3071
+ return this.dragFromMode;
3072
+ }
3073
+ setMode(mode) {
3074
+ if (this.destroyed) return;
3075
+ const next = resolveMode(mode, this.element);
3076
+ if (next === this.mode) return;
3077
+ const wasVertical = this.mode === "bottom" || this.mode === "top";
3078
+ const nowVertical = next === "bottom" || next === "top";
3079
+ this.gesture?.forceClearDragState();
3080
+ this.detachSheetGesture?.();
3081
+ this.animation.cancel();
3082
+ this.element.style.removeProperty(layoutAxis(this.mode));
3083
+ this.mode = next;
3084
+ this.element.dataset.mode = next;
3085
+ this.transformTemplate = buildTransformTemplate(next);
3086
+ this.maxHeightDeps.mode = next;
3087
+ this.fitDeps.mode = next;
3088
+ this.snaps.setMode(next);
3089
+ this.scrim.setSheetMode(next);
3090
+ if (wasVertical !== nowVertical) {
3091
+ this.aria = new AriaSliderWriter(this.handle, next);
3092
+ }
3093
+ this.detachSliderKeyboard?.();
3094
+ this.mountSliderKeyboard();
3095
+ this.mountSheetGesture();
3096
+ this.recompute();
3097
+ const active = this.snaps.findById(this.activeId);
3098
+ if (active) this.applySize(active.size);
3099
+ this.updateAriaSlider();
3100
+ }
3101
+ getMode() {
3102
+ return this.mode;
3103
+ }
2918
3104
  isTop() {
2919
3105
  return this.isTopSheet;
2920
3106
  }
@@ -3069,12 +3255,24 @@ var BottomSheetCore = class {
3069
3255
  auditVhUsage(points);
3070
3256
  this.auditDuplicateSnapIds(points);
3071
3257
  this.snaps.setRaw(points);
3072
- if (allowed) this.snaps.setAllowedIds(allowed);
3258
+ if (allowed) {
3259
+ this.snaps.setAllowedIds(allowed);
3260
+ } else {
3261
+ const present = this.snaps.getResolvedSnaps().map((s) => s.id);
3262
+ const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
3263
+ this.snaps.setAllowedIds(kept.length ? kept : present);
3264
+ }
3073
3265
  this.scrollCache.clear();
3074
3266
  this.scrim.invalidateOpacityCache();
3075
3267
  const current = this.snaps.findById(this.activeId);
3076
3268
  if (current) {
3077
3269
  this.applySize(current.size);
3270
+ } else {
3271
+ const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
3272
+ if (fallback) {
3273
+ this.activeId = fallback.id;
3274
+ this.applySize(fallback.size);
3275
+ }
3078
3276
  }
3079
3277
  this.updateAriaSlider();
3080
3278
  }
@@ -3084,7 +3282,7 @@ var BottomSheetCore = class {
3084
3282
  this.snaps.recompute();
3085
3283
  this.maxHeight.clampTo();
3086
3284
  this.scrim.invalidateOpacityCache();
3087
- if (this.gesture?.isDragging) return;
3285
+ if (this.isDraggingAny()) return;
3088
3286
  this.healActiveSnap();
3089
3287
  }
3090
3288
  healActiveSnap() {
@@ -3106,6 +3304,8 @@ var BottomSheetCore = class {
3106
3304
  this.bus.clear();
3107
3305
  this.animation.cancel();
3108
3306
  this.gesture?.forceClearDragState();
3307
+ this.contentGesture?.forceClearDragState();
3308
+ this.setRestClosed(false);
3109
3309
  this.teardowns.drain();
3110
3310
  this.scrim.destroy();
3111
3311
  this.animation.destroy();
@@ -3218,11 +3418,12 @@ var BottomSheetCore = class {
3218
3418
  const scale = Math.max(1 - depth * 0.04, 0.86);
3219
3419
  this.element.style.scale = scale === 1 ? "" : String(scale);
3220
3420
  }
3221
- attach() {
3222
- this.gesture = new GestureController({
3223
- handle: this.handle,
3421
+ buildGestureDeps(surface, gestureOptions) {
3422
+ return {
3423
+ handle: surface,
3224
3424
  element: this.element,
3225
3425
  mode: this.mode,
3426
+ gestureOptions,
3226
3427
  getRoot: () => this.rootEl,
3227
3428
  getDragContext: () => {
3228
3429
  const buf = this.dragContextBuf;
@@ -3241,8 +3442,90 @@ var BottomSheetCore = class {
3241
3442
  settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
3242
3443
  emit: (event, payload) => this.emit(event, payload),
3243
3444
  listenerCount: (event) => this.bus.listenerCount(event)
3445
+ };
3446
+ }
3447
+ isContentDragAllowed() {
3448
+ if (this.disableDragFlag) return false;
3449
+ const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
3450
+ return point?.dragFromContent ?? this.dragFromContentDefault;
3451
+ }
3452
+ decideContentDrag(surface, e, delta, crossDelta) {
3453
+ if (this.destroyed) return false;
3454
+ if (e.pointerType === "mouse") return false;
3455
+ if (this.gesture?.isDragging) return false;
3456
+ if (!this.isContentDragAllowed()) return false;
3457
+ if (!isDragAllowedFrom(e.target, "sheet")) return false;
3458
+ const decision = decideContentGesture({
3459
+ delta,
3460
+ crossDelta,
3461
+ sharesScrollAxis: this.mode === "bottom" || this.mode === "top",
3462
+ scrollTop: surface.scrollTop,
3463
+ atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
3464
+ });
3465
+ if (decision === "pending") return null;
3466
+ return decision === "drag";
3467
+ }
3468
+ attachDragSurface(surface, kind) {
3469
+ if (this.destroyed || kind !== "content") return;
3470
+ if (this.contentGesture) return;
3471
+ const controller = new GestureController(
3472
+ this.buildGestureDeps(surface, {
3473
+ manageTouchAction: false,
3474
+ deferStart: (e, delta, crossDelta) => this.decideContentDrag(surface, e, delta, crossDelta)
3475
+ })
3476
+ );
3477
+ this.contentGesture = controller;
3478
+ const detachGestures = controller.install();
3479
+ const detachGuard = installTouchScrollGuard(
3480
+ surface,
3481
+ () => controller.isDragging
3482
+ );
3483
+ return () => {
3484
+ detachGuard();
3485
+ detachGestures();
3486
+ if (this.contentGesture === controller) this.contentGesture = void 0;
3487
+ };
3488
+ }
3489
+ sheetDragSurface() {
3490
+ return this.dragFromMode === "handle" ? this.handle : this.element;
3491
+ }
3492
+ canStartSheetDrag(e) {
3493
+ if (this.contentGesture?.isDragging) return false;
3494
+ const target = e.target;
3495
+ const scoped = typeof target?.closest === "function";
3496
+ if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
3497
+ if (this.handle !== this.element && scoped && this.handle.contains(target)) {
3498
+ return true;
3499
+ }
3500
+ if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
3501
+ if (this.dragFromMode === "handle") return true;
3502
+ if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
3503
+ return false;
3504
+ }
3505
+ return true;
3506
+ }
3507
+ mountSheetGesture() {
3508
+ const surface = this.sheetDragSurface();
3509
+ const controller = new GestureController(
3510
+ this.buildGestureDeps(surface, {
3511
+ shouldStart: (e) => this.canStartSheetDrag(e),
3512
+ manageTouchAction: this.dragFromMode === "handle"
3513
+ })
3514
+ );
3515
+ this.gesture = controller;
3516
+ const detachGestures = controller.install();
3517
+ const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
3518
+ this.detachSheetGesture = () => {
3519
+ detachGuard?.();
3520
+ detachGestures();
3521
+ };
3522
+ }
3523
+ attach() {
3524
+ this.mountSheetGesture();
3525
+ this.teardowns.add(() => {
3526
+ this.detachSheetGesture?.();
3527
+ this.detachSheetGesture = null;
3244
3528
  });
3245
- this.teardowns.add(this.gesture.install());
3246
3529
  try {
3247
3530
  this.attachFeatures();
3248
3531
  } catch (err) {
@@ -3279,7 +3562,7 @@ var BottomSheetCore = class {
3279
3562
  element: this.element,
3280
3563
  getMode: () => this.mode,
3281
3564
  isDestroyed: () => this.destroyed,
3282
- isDragging: () => this.gesture?.isDragging ?? false,
3565
+ isDragging: () => this.isDraggingAny(),
3283
3566
  resolveActiveSnap: () => this.snaps.findById(this.activeId),
3284
3567
  getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
3285
3568
  getSize: () => this.size,
@@ -3314,7 +3597,11 @@ var BottomSheetCore = class {
3314
3597
  );
3315
3598
  }
3316
3599
  }
3317
- this.teardowns.add(installSliderKeyboard({
3600
+ this.mountSliderKeyboard();
3601
+ this.scrim.attach();
3602
+ }
3603
+ mountSliderKeyboard() {
3604
+ const detach = installSliderKeyboard({
3318
3605
  handle: this.handle,
3319
3606
  mode: this.mode,
3320
3607
  isDestroyed: () => this.destroyed,
@@ -3323,8 +3610,12 @@ var BottomSheetCore = class {
3323
3610
  snapTo: (id) => {
3324
3611
  void this.snapTo(id);
3325
3612
  }
3326
- }));
3327
- this.scrim.attach();
3613
+ });
3614
+ this.detachSliderKeyboard = detach;
3615
+ this.teardowns.add(() => {
3616
+ this.detachSliderKeyboard?.();
3617
+ this.detachSliderKeyboard = null;
3618
+ });
3328
3619
  }
3329
3620
  settleAfterDrag(delta, velocity, kind = "touch") {
3330
3621
  const signal = this.newCycle();
@@ -3377,8 +3668,11 @@ var BottomSheetCore = class {
3377
3668
  );
3378
3669
  });
3379
3670
  }
3671
+ isDraggingAny() {
3672
+ return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
3673
+ }
3380
3674
  clearWillChangeIfIdle() {
3381
- if (this.animation.isAnimating || this.gesture?.isDragging) return;
3675
+ if (this.animation.isAnimating || this.isDraggingAny()) return;
3382
3676
  this.element.style.willChange = "";
3383
3677
  }
3384
3678
  haptic() {
@@ -3391,8 +3685,9 @@ var BottomSheetCore = class {
3391
3685
  }
3392
3686
  applySize(size, skipTransform = false) {
3393
3687
  const cap = this.snaps.getMaxAxisSize();
3394
- const clamped = cap > 0 && size > cap && !this.gesture?.isDragging && !this.allowOvershoot ? cap : size;
3688
+ const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
3395
3689
  this.size = clamped;
3690
+ if (clamped > 0) this.setRestClosed(false);
3396
3691
  const offset = cap - clamped;
3397
3692
  const style = this.element.style;
3398
3693
  if (!skipTransform) style.transform = this.transformTemplate(offset);
@@ -3772,50 +4067,10 @@ function installAutoCollapse(deps) {
3772
4067
  };
3773
4068
  }
3774
4069
 
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
- };
4070
+ // src/core/features/content-drag.ts
4071
+ function installContentDrag(deps) {
4072
+ const detach = deps.attachDragSurface(deps.container, "content");
4073
+ return () => detach?.();
3819
4074
  }
3820
4075
 
3821
4076
  // src/core/features/visual-viewport.ts
@@ -3923,13 +4178,9 @@ function contentSwipeFeature() {
3923
4178
  stage: "attach",
3924
4179
  install: (ctx) => {
3925
4180
  if (!ctx.scrollContainer) return;
3926
- return installContentSwipe({
4181
+ return installContentDrag({
3927
4182
  container: ctx.scrollContainer,
3928
- isDragging: ctx.isDragging,
3929
- isAnimating: ctx.isAnimating,
3930
- getAllowedIds: ctx.allowedIdsBySize,
3931
- getActiveId: ctx.getActiveId,
3932
- snapTo: ctx.snapTo
4183
+ attachDragSurface: ctx.attachDragSurface
3933
4184
  });
3934
4185
  }
3935
4186
  };
@@ -4176,6 +4427,14 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
4176
4427
  type: Number,
4177
4428
  required: false
4178
4429
  },
4430
+ dragFrom: {
4431
+ type: String,
4432
+ required: false
4433
+ },
4434
+ dragFromContent: {
4435
+ type: Boolean,
4436
+ required: false
4437
+ },
4179
4438
  rubberBand: {
4180
4439
  type: Boolean,
4181
4440
  required: false
@@ -4471,6 +4730,14 @@ var _sfc_main = /* @__PURE__ */ vue.defineComponent({
4471
4730
  if (next === void 0) return;
4472
4731
  getEngine()?.setDisableDrag(next);
4473
4732
  });
4733
+ vue.watch(() => props.dragFromContent, (next) => {
4734
+ if (next === void 0) return;
4735
+ getEngine()?.setDragFromContent(next);
4736
+ });
4737
+ vue.watch(() => props.dragFrom, (next) => {
4738
+ if (next === void 0) return;
4739
+ getEngine()?.setDragFrom(next);
4740
+ });
4474
4741
  const mounted = vue.ref(false);
4475
4742
  const anchorHosts = vue.shallowRef({});
4476
4743
  const anchorDetachers = vue.ref([]);