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