@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.
- package/README.md +46 -0
- package/dist/{BottomSheetCore-D16lsa6g.d.ts → BottomSheetCore-Cgn8b_6A.d.ts} +36 -2
- package/dist/{BottomSheetCore-CsIjyq7C.d.cts → BottomSheetCore-VPJZcKcg.d.cts} +36 -2
- package/dist/{BottomSheetEngine-BYjAVrmd.d.cts → BottomSheetEngine-B8C2CwF9.d.cts} +1 -1
- package/dist/{BottomSheetEngine-YuCNugZn.d.ts → BottomSheetEngine-DwYR_ygJ.d.ts} +1 -1
- package/dist/core.cjs +328 -33
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +328 -33
- package/dist/debug.d.cts +1 -1
- package/dist/debug.d.ts +1 -1
- package/dist/element.cjs +404 -108
- package/dist/element.d.cts +2 -2
- package/dist/element.d.ts +2 -2
- package/dist/element.iife.global.js +2 -2
- package/dist/element.js +404 -108
- package/dist/features.cjs +6 -50
- package/dist/features.d.cts +4 -4
- package/dist/features.d.ts +4 -4
- package/dist/features.js +6 -50
- package/dist/index-CnfbOKx0.d.cts +9 -0
- package/dist/index-DcMkhXAy.d.ts +9 -0
- package/dist/index.cjs +335 -829
- package/dist/index.d.cts +44 -47
- package/dist/index.d.ts +44 -47
- package/dist/index.js +336 -816
- package/dist/integrations/formik.d.cts +1 -1
- package/dist/integrations/formik.d.ts +1 -1
- package/dist/integrations/react-hook-form.d.cts +1 -1
- package/dist/integrations/react-hook-form.d.ts +1 -1
- package/dist/overlay.cjs +5 -5
- package/dist/overlay.js +5 -5
- package/dist/preact.d.cts +2 -2
- package/dist/preact.d.ts +2 -2
- package/dist/qwik-lib/index.qwik.cjs +323 -83
- package/dist/qwik-lib/index.qwik.mjs +323 -83
- package/dist/qwik.cjs +342 -83
- package/dist/qwik.d.cts +4 -2
- package/dist/qwik.d.ts +4 -2
- package/dist/qwik.js +342 -83
- package/dist/react.cjs +347 -85
- package/dist/react.d.cts +3 -4
- package/dist/react.d.ts +3 -4
- package/dist/react.js +347 -85
- package/dist/solid.cjs +348 -83
- package/dist/solid.d.cts +37 -1
- package/dist/solid.d.ts +37 -1
- package/dist/solid.js +348 -83
- package/dist/solid.source.jsx +356 -83
- package/dist/styles.css +24 -4
- package/dist/svelte-core.cjs +334 -83
- package/dist/svelte-core.js +334 -83
- package/dist/svelte-src/BottomSheet.svelte +14 -0
- package/dist/svelte.cjs +344 -83
- package/dist/svelte.d.cts +2 -0
- package/dist/svelte.d.ts +2 -0
- package/dist/svelte.js +344 -83
- package/dist/vue.cjs +350 -83
- package/dist/vue.js +350 -83
- package/dist/webgl.cjs +673 -0
- package/dist/webgl.d.cts +17 -0
- package/dist/webgl.d.ts +17 -0
- package/dist/webgl.js +671 -0
- package/package.json +64 -40
- package/dist/index-DdYLOIIT.d.ts +0 -15
- package/dist/index-dfrzw-lI.d.cts +0 -15
|
@@ -300,6 +300,11 @@ class SnapResolver {
|
|
|
300
300
|
this.raw = raw;
|
|
301
301
|
this.recompute();
|
|
302
302
|
}
|
|
303
|
+
setMode(mode) {
|
|
304
|
+
if (mode === this.mode) return;
|
|
305
|
+
this.mode = mode;
|
|
306
|
+
this.recompute();
|
|
307
|
+
}
|
|
303
308
|
setAllowedIds(ids) {
|
|
304
309
|
this.allowedIds = ids;
|
|
305
310
|
this.rangeCache = null;
|
|
@@ -902,6 +907,11 @@ class ScrimController {
|
|
|
902
907
|
interactive
|
|
903
908
|
});
|
|
904
909
|
}
|
|
910
|
+
setSheetMode(mode) {
|
|
911
|
+
if (this.isDestroyed() || mode === this.mode) return;
|
|
912
|
+
this.mode = mode;
|
|
913
|
+
this.invalidateOpacityCache();
|
|
914
|
+
}
|
|
905
915
|
setScrimMode(mode, applySize) {
|
|
906
916
|
if (this.isDestroyed()) return;
|
|
907
917
|
if (mode === this.scrimMode) return;
|
|
@@ -1832,30 +1842,47 @@ class SampleRing {
|
|
|
1832
1842
|
this.size = 0;
|
|
1833
1843
|
}
|
|
1834
1844
|
}
|
|
1835
|
-
const installGestures = (handle, mode, callbacks) => {
|
|
1845
|
+
const installGestures = (handle, mode, callbacks, options = {}) => {
|
|
1836
1846
|
let activePointerId = null;
|
|
1837
1847
|
let activePointerType = "touch";
|
|
1838
1848
|
let startCoord = 0;
|
|
1849
|
+
let startCross = 0;
|
|
1839
1850
|
let lastCoord = 0;
|
|
1851
|
+
let pendingStart = false;
|
|
1840
1852
|
const VELOCITY_WINDOW_MS = (kind) => kind === "mouse" ? 160 : 120;
|
|
1841
1853
|
const MAX_SAMPLES = 32;
|
|
1842
1854
|
const samples = new SampleRing(MAX_SAMPLES);
|
|
1843
1855
|
const axis = isAxisVertical(mode) ? "Y" : "X";
|
|
1844
1856
|
const sign = mode === "bottom" || mode === "right" ? -1 : 1;
|
|
1845
1857
|
const coordOf = (e) => axis === "Y" ? e.clientY : e.clientX;
|
|
1858
|
+
const crossOf = (e) => axis === "Y" ? e.clientX : e.clientY;
|
|
1859
|
+
const capture = (pointerId) => {
|
|
1860
|
+
try {
|
|
1861
|
+
handle.setPointerCapture(pointerId);
|
|
1862
|
+
} catch {
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1865
|
+
const releaseTracking = () => {
|
|
1866
|
+
activePointerId = null;
|
|
1867
|
+
pendingStart = false;
|
|
1868
|
+
samples.reset();
|
|
1869
|
+
};
|
|
1846
1870
|
const onPointerDown = (e) => {
|
|
1847
1871
|
if (activePointerId !== null) return;
|
|
1848
1872
|
if (e.button !== void 0 && e.button !== 0) return;
|
|
1873
|
+
if (options.shouldStart && !options.shouldStart(e)) return;
|
|
1849
1874
|
activePointerId = e.pointerId;
|
|
1850
1875
|
activePointerType = e.pointerType || "touch";
|
|
1851
1876
|
startCoord = coordOf(e);
|
|
1877
|
+
startCross = crossOf(e);
|
|
1852
1878
|
lastCoord = startCoord;
|
|
1853
1879
|
samples.reset();
|
|
1854
1880
|
samples.push(e.timeStamp, startCoord);
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1881
|
+
if (options.deferStart) {
|
|
1882
|
+
pendingStart = true;
|
|
1883
|
+
return;
|
|
1858
1884
|
}
|
|
1885
|
+
capture(e.pointerId);
|
|
1859
1886
|
callbacks.onStart(startCoord, activePointerType);
|
|
1860
1887
|
};
|
|
1861
1888
|
const onPointerMove = (e) => {
|
|
@@ -1863,6 +1890,22 @@ const installGestures = (handle, mode, callbacks) => {
|
|
|
1863
1890
|
const coord = coordOf(e);
|
|
1864
1891
|
const rawDelta = coord - startCoord;
|
|
1865
1892
|
const delta = rawDelta * sign;
|
|
1893
|
+
if (pendingStart) {
|
|
1894
|
+
const verdict = options.deferStart(e, delta, crossOf(e) - startCross);
|
|
1895
|
+
if (verdict === false) {
|
|
1896
|
+
releaseTracking();
|
|
1897
|
+
return;
|
|
1898
|
+
}
|
|
1899
|
+
if (verdict !== true) return;
|
|
1900
|
+
pendingStart = false;
|
|
1901
|
+
startCoord = coord;
|
|
1902
|
+
lastCoord = coord;
|
|
1903
|
+
samples.reset();
|
|
1904
|
+
samples.push(e.timeStamp, coord);
|
|
1905
|
+
capture(e.pointerId);
|
|
1906
|
+
callbacks.onStart(startCoord, activePointerType);
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1866
1909
|
lastCoord = coord;
|
|
1867
1910
|
samples.push(e.timeStamp, coord);
|
|
1868
1911
|
const cutoff = e.timeStamp - VELOCITY_WINDOW_MS(activePointerType);
|
|
@@ -1871,6 +1914,10 @@ const installGestures = (handle, mode, callbacks) => {
|
|
|
1871
1914
|
};
|
|
1872
1915
|
const finishGesture = (e) => {
|
|
1873
1916
|
if (e.pointerId !== activePointerId) return;
|
|
1917
|
+
if (pendingStart) {
|
|
1918
|
+
releaseTracking();
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1874
1921
|
const rawDelta = lastCoord - startCoord;
|
|
1875
1922
|
const delta = rawDelta * sign;
|
|
1876
1923
|
let velocity = 0;
|
|
@@ -1893,22 +1940,26 @@ const installGestures = (handle, mode, callbacks) => {
|
|
|
1893
1940
|
};
|
|
1894
1941
|
const onPointerCancel = (e) => {
|
|
1895
1942
|
if (e.pointerId !== activePointerId) return;
|
|
1896
|
-
|
|
1897
|
-
|
|
1943
|
+
const wasPending = pendingStart;
|
|
1944
|
+
releaseTracking();
|
|
1945
|
+
if (wasPending) return;
|
|
1898
1946
|
callbacks.onCancel?.();
|
|
1899
1947
|
};
|
|
1900
1948
|
handle.addEventListener("pointerdown", onPointerDown);
|
|
1901
1949
|
handle.addEventListener("pointermove", onPointerMove);
|
|
1902
1950
|
handle.addEventListener("pointerup", finishGesture);
|
|
1903
1951
|
handle.addEventListener("pointercancel", onPointerCancel);
|
|
1952
|
+
const managesTouchAction = options.manageTouchAction !== false;
|
|
1904
1953
|
const prevTouchAction = handle.style.touchAction;
|
|
1905
|
-
|
|
1954
|
+
if (managesTouchAction) {
|
|
1955
|
+
handle.style.touchAction = isAxisVertical(mode) ? "pan-x" : "pan-y";
|
|
1956
|
+
}
|
|
1906
1957
|
return () => {
|
|
1907
1958
|
handle.removeEventListener("pointerdown", onPointerDown);
|
|
1908
1959
|
handle.removeEventListener("pointermove", onPointerMove);
|
|
1909
1960
|
handle.removeEventListener("pointerup", finishGesture);
|
|
1910
1961
|
handle.removeEventListener("pointercancel", onPointerCancel);
|
|
1911
|
-
handle.style.touchAction = prevTouchAction;
|
|
1962
|
+
if (managesTouchAction) handle.style.touchAction = prevTouchAction;
|
|
1912
1963
|
if (activePointerId !== null && handle.hasPointerCapture(activePointerId)) {
|
|
1913
1964
|
handle.releasePointerCapture(activePointerId);
|
|
1914
1965
|
}
|
|
@@ -2026,7 +2077,7 @@ class GestureController {
|
|
|
2026
2077
|
const restoreTo = this.dragStartSize;
|
|
2027
2078
|
void this.deps.animateTo(restoreTo, 0);
|
|
2028
2079
|
}
|
|
2029
|
-
});
|
|
2080
|
+
}, this.deps.gestureOptions);
|
|
2030
2081
|
return this.detach;
|
|
2031
2082
|
}
|
|
2032
2083
|
forceClearDragState() {
|
|
@@ -2037,6 +2088,68 @@ class GestureController {
|
|
|
2037
2088
|
this.deps.element.style.willChange = "";
|
|
2038
2089
|
}
|
|
2039
2090
|
}
|
|
2091
|
+
const DRAG_ZONE_SELECTOR = "[data-bs-drag]";
|
|
2092
|
+
const NO_DRAG_SELECTOR = "[data-bs-no-drag]";
|
|
2093
|
+
const asElement = (target) => {
|
|
2094
|
+
const candidate = target;
|
|
2095
|
+
return candidate && typeof candidate.closest === "function" ? candidate : null;
|
|
2096
|
+
};
|
|
2097
|
+
function isDragAllowedFrom(target, mode) {
|
|
2098
|
+
const el = asElement(target);
|
|
2099
|
+
if (!el) return mode !== "zones";
|
|
2100
|
+
if (el.closest(NO_DRAG_SELECTOR)) return false;
|
|
2101
|
+
if (mode === "zones") return el.closest(DRAG_ZONE_SELECTOR) !== null;
|
|
2102
|
+
return true;
|
|
2103
|
+
}
|
|
2104
|
+
const CONTENT_DRAG_SLOP = 6;
|
|
2105
|
+
function decideContentGesture(input) {
|
|
2106
|
+
const slop = input.slop ?? CONTENT_DRAG_SLOP;
|
|
2107
|
+
const cross = input.crossDelta ?? 0;
|
|
2108
|
+
const sharesScrollAxis = input.sharesScrollAxis ?? true;
|
|
2109
|
+
if (!sharesScrollAxis) {
|
|
2110
|
+
if (Math.abs(input.delta) < slop && Math.abs(cross) < slop) return "pending";
|
|
2111
|
+
if (Math.abs(input.delta) <= Math.abs(cross)) return "scroll";
|
|
2112
|
+
return "drag";
|
|
2113
|
+
}
|
|
2114
|
+
if (Math.abs(input.delta) < slop) return "pending";
|
|
2115
|
+
if (input.scrollTop > 0) return "scroll";
|
|
2116
|
+
if (input.delta > 0 && input.atMaxSnap) return "scroll";
|
|
2117
|
+
return "drag";
|
|
2118
|
+
}
|
|
2119
|
+
const isLogicalMode = (mode) => mode === "start" || mode === "end";
|
|
2120
|
+
const readDirection = (el) => {
|
|
2121
|
+
if (!el) return "ltr";
|
|
2122
|
+
if (typeof getComputedStyle === "function") {
|
|
2123
|
+
try {
|
|
2124
|
+
if (getComputedStyle(el).direction === "rtl") return "rtl";
|
|
2125
|
+
} catch {
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
try {
|
|
2129
|
+
const marked = el.closest?.("[dir]");
|
|
2130
|
+
if (marked) {
|
|
2131
|
+
return marked.getAttribute("dir")?.toLowerCase() === "rtl" ? "rtl" : "ltr";
|
|
2132
|
+
}
|
|
2133
|
+
} catch {
|
|
2134
|
+
}
|
|
2135
|
+
return "ltr";
|
|
2136
|
+
};
|
|
2137
|
+
const resolveMode = (mode, el) => {
|
|
2138
|
+
if (!isLogicalMode(mode)) return mode;
|
|
2139
|
+
const rtl = readDirection(el) === "rtl";
|
|
2140
|
+
if (mode === "start") return rtl ? "right" : "left";
|
|
2141
|
+
return rtl ? "left" : "right";
|
|
2142
|
+
};
|
|
2143
|
+
function installTouchScrollGuard(surface, isDragging) {
|
|
2144
|
+
const onTouchMove = (e) => {
|
|
2145
|
+
if (!isDragging()) return;
|
|
2146
|
+
if (e.cancelable) e.preventDefault();
|
|
2147
|
+
};
|
|
2148
|
+
surface.addEventListener("touchmove", onTouchMove, {
|
|
2149
|
+
passive: false
|
|
2150
|
+
});
|
|
2151
|
+
return () => surface.removeEventListener("touchmove", onTouchMove);
|
|
2152
|
+
}
|
|
2040
2153
|
class AriaSliderWriter {
|
|
2041
2154
|
constructor(handle, mode) {
|
|
2042
2155
|
__publicField(this, "handle");
|
|
@@ -2427,9 +2540,11 @@ function resolveEngineOptions(opts, extras) {
|
|
|
2427
2540
|
}
|
|
2428
2541
|
}
|
|
2429
2542
|
return {
|
|
2430
|
-
mode: opts.mode ?? "bottom",
|
|
2543
|
+
mode: resolveMode(opts.mode ?? "bottom", opts.element),
|
|
2431
2544
|
flickVelocity: opts.flickVelocity ?? DEFAULT_FLICK_VELOCITY,
|
|
2432
2545
|
dragThreshold: opts.dragThreshold ?? DEFAULT_DRAG_THRESHOLD,
|
|
2546
|
+
dragFrom: opts.dragFrom ?? (opts.handle ? "handle" : "sheet"),
|
|
2547
|
+
dragFromContent: opts.dragFromContent ?? true,
|
|
2433
2548
|
rubberBandEnabled: opts.rubberBand ?? true,
|
|
2434
2549
|
closeOnBack: opts.closeOnBack ?? false,
|
|
2435
2550
|
persistent: opts.persistent ?? false,
|
|
@@ -2511,6 +2626,8 @@ class BottomSheetCore {
|
|
|
2511
2626
|
__publicField(this, "mode");
|
|
2512
2627
|
__publicField(this, "flickVelocity");
|
|
2513
2628
|
__publicField(this, "dragThreshold");
|
|
2629
|
+
__publicField(this, "dragFromMode");
|
|
2630
|
+
__publicField(this, "dragFromContentDefault");
|
|
2514
2631
|
__publicField(this, "rubberBandEnabled");
|
|
2515
2632
|
__publicField(this, "scrim");
|
|
2516
2633
|
__publicField(this, "aria");
|
|
@@ -2523,13 +2640,18 @@ class BottomSheetCore {
|
|
|
2523
2640
|
__publicField(this, "disableDragFlag");
|
|
2524
2641
|
__publicField(this, "closeOnRouteChange");
|
|
2525
2642
|
__publicField(this, "maxHeight");
|
|
2643
|
+
__publicField(this, "maxHeightDeps");
|
|
2526
2644
|
__publicField(this, "snapPointsRaw");
|
|
2527
2645
|
__publicField(this, "snaps");
|
|
2528
2646
|
__publicField(this, "activeId");
|
|
2529
2647
|
__publicField(this, "size", 0);
|
|
2530
2648
|
__publicField(this, "gesture");
|
|
2649
|
+
__publicField(this, "contentGesture");
|
|
2650
|
+
__publicField(this, "detachSheetGesture", null);
|
|
2651
|
+
__publicField(this, "detachSliderKeyboard", null);
|
|
2531
2652
|
__publicField(this, "rootEl", null);
|
|
2532
2653
|
__publicField(this, "destroyed", false);
|
|
2654
|
+
__publicField(this, "restClosed", false);
|
|
2533
2655
|
__publicField(this, "currentViewTransition", null);
|
|
2534
2656
|
__publicField(this, "currentAbort", new AbortController());
|
|
2535
2657
|
__publicField(this, "sizeWriteSentinel", new WriteSentinel());
|
|
@@ -2579,10 +2701,13 @@ class BottomSheetCore {
|
|
|
2579
2701
|
this.screenComponent = opts.scrim ?? opts.screenComponent;
|
|
2580
2702
|
this.scrimParent = this.screenComponent?.parentElement ?? null;
|
|
2581
2703
|
this.mode = resolved.mode;
|
|
2704
|
+
this.element.dataset.mode = this.mode;
|
|
2582
2705
|
this.transformTemplate = buildTransformTemplate(this.mode);
|
|
2583
2706
|
this.aria = new AriaSliderWriter(this.handle, this.mode);
|
|
2584
2707
|
this.flickVelocity = resolved.flickVelocity;
|
|
2585
2708
|
this.dragThreshold = resolved.dragThreshold;
|
|
2709
|
+
this.dragFromMode = resolved.dragFrom;
|
|
2710
|
+
this.dragFromContentDefault = resolved.dragFromContent;
|
|
2586
2711
|
this.rubberBandEnabled = resolved.rubberBandEnabled;
|
|
2587
2712
|
this.scrim = new ScrimController({
|
|
2588
2713
|
mode: this.mode,
|
|
@@ -2605,7 +2730,7 @@ class BottomSheetCore {
|
|
|
2605
2730
|
getRootEl: () => this.rootEl,
|
|
2606
2731
|
applySize: (size) => this.applySize(size),
|
|
2607
2732
|
getSize: () => this.size,
|
|
2608
|
-
isDragging: () => this.
|
|
2733
|
+
isDragging: () => this.isDraggingAny(),
|
|
2609
2734
|
applyAux: (size) => this.applySize(size, true),
|
|
2610
2735
|
getTransformFor: (size) => this.transformTemplate(this.snaps.getMaxAxisSize() - size)
|
|
2611
2736
|
}, resolved.animation);
|
|
@@ -2634,7 +2759,7 @@ class BottomSheetCore {
|
|
|
2634
2759
|
auditVhUsage(this.snapPointsRaw);
|
|
2635
2760
|
this.auditDuplicateSnapIds(this.snapPointsRaw);
|
|
2636
2761
|
this.rootEl = this.element.closest(".bs-root");
|
|
2637
|
-
this.
|
|
2762
|
+
this.maxHeightDeps = {
|
|
2638
2763
|
element: this.element,
|
|
2639
2764
|
mode: this.mode,
|
|
2640
2765
|
getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
|
|
@@ -2642,7 +2767,8 @@ class BottomSheetCore {
|
|
|
2642
2767
|
this.snaps.setMaxAxisSize(size);
|
|
2643
2768
|
},
|
|
2644
2769
|
recompute: () => this.recompute()
|
|
2645
|
-
}
|
|
2770
|
+
};
|
|
2771
|
+
this.maxHeight = createMaxHeightController(this.maxHeightDeps);
|
|
2646
2772
|
this.fitDeps = {
|
|
2647
2773
|
element: this.element,
|
|
2648
2774
|
scrollContainer: this.scrollContainer,
|
|
@@ -2655,6 +2781,7 @@ class BottomSheetCore {
|
|
|
2655
2781
|
const initial = this.snaps.findById(this.activeId);
|
|
2656
2782
|
if (initial) this.size = initial.size;
|
|
2657
2783
|
this.applySize(this.size);
|
|
2784
|
+
this.setRestClosed(this.size === 0);
|
|
2658
2785
|
this.updateAriaSlider();
|
|
2659
2786
|
this.scrollCache = createScrollCache({
|
|
2660
2787
|
scrollContainer: this.scrollContainer,
|
|
@@ -2682,7 +2809,7 @@ class BottomSheetCore {
|
|
|
2682
2809
|
scrollContainer: this.scrollContainer,
|
|
2683
2810
|
hasFitSnap: () => this.snapPointsRaw.some((p) => p.size === "fit" || p.size === "content"),
|
|
2684
2811
|
isDestroyed: () => this.destroyed,
|
|
2685
|
-
isDragging: () => this.
|
|
2812
|
+
isDragging: () => this.isDraggingAny(),
|
|
2686
2813
|
recompute: () => this.recompute()
|
|
2687
2814
|
}));
|
|
2688
2815
|
if (resolved.radius !== void 0) this.setRadius(resolved.radius);
|
|
@@ -2707,7 +2834,7 @@ class BottomSheetCore {
|
|
|
2707
2834
|
autoCollapseAfter
|
|
2708
2835
|
},
|
|
2709
2836
|
isDestroyed: () => this.destroyed,
|
|
2710
|
-
isDragging: () => this.
|
|
2837
|
+
isDragging: () => this.isDraggingAny(),
|
|
2711
2838
|
isAnimating: () => this.animation.isAnimating,
|
|
2712
2839
|
isTopSheet: () => this.isTopSheet,
|
|
2713
2840
|
isVerticalAxis: () => this.mode === "bottom" || this.mode === "top",
|
|
@@ -2737,6 +2864,7 @@ class BottomSheetCore {
|
|
|
2737
2864
|
void this.snapTo(id);
|
|
2738
2865
|
},
|
|
2739
2866
|
close: (reason) => this.close(reason),
|
|
2867
|
+
attachDragSurface: (surface, kind) => this.attachDragSurface(surface, kind),
|
|
2740
2868
|
on: (event, fn) => this.on(event, fn),
|
|
2741
2869
|
addTeardown: (fn) => this.teardowns.add(fn)
|
|
2742
2870
|
};
|
|
@@ -2761,7 +2889,7 @@ class BottomSheetCore {
|
|
|
2761
2889
|
return {
|
|
2762
2890
|
size: this.size,
|
|
2763
2891
|
activeId: this.activeId,
|
|
2764
|
-
isDragging: this.
|
|
2892
|
+
isDragging: this.isDraggingAny(),
|
|
2765
2893
|
isAnimating: this.animation.isAnimating,
|
|
2766
2894
|
progress: this.computeProgress(this.size)
|
|
2767
2895
|
};
|
|
@@ -2883,9 +3011,16 @@ class BottomSheetCore {
|
|
|
2883
3011
|
progress: this.computeProgress(this.size)
|
|
2884
3012
|
});
|
|
2885
3013
|
if (withHaptic) this.haptic();
|
|
3014
|
+
this.setRestClosed(targetSize === 0);
|
|
2886
3015
|
if (wasClosed && targetSize > 0) this.emitOpenSequence(id);
|
|
2887
3016
|
if (targetSize === 0) this.emitCloseSequence();
|
|
2888
3017
|
}
|
|
3018
|
+
setRestClosed(closed) {
|
|
3019
|
+
if (closed === this.restClosed) return;
|
|
3020
|
+
this.restClosed = closed;
|
|
3021
|
+
if (closed) this.element.setAttribute("data-bs-rest", "closed");
|
|
3022
|
+
else this.element.removeAttribute("data-bs-rest");
|
|
3023
|
+
}
|
|
2889
3024
|
emitOpenSequence(id) {
|
|
2890
3025
|
this.emit("open", {
|
|
2891
3026
|
id
|
|
@@ -2949,6 +3084,51 @@ class BottomSheetCore {
|
|
|
2949
3084
|
if (this.destroyed) return;
|
|
2950
3085
|
this.disableDragFlag = value;
|
|
2951
3086
|
}
|
|
3087
|
+
setDragFromContent(value) {
|
|
3088
|
+
if (this.destroyed) return;
|
|
3089
|
+
this.dragFromContentDefault = value;
|
|
3090
|
+
}
|
|
3091
|
+
setDragFrom(mode) {
|
|
3092
|
+
if (this.destroyed || mode === this.dragFromMode) return;
|
|
3093
|
+
this.gesture?.forceClearDragState();
|
|
3094
|
+
this.detachSheetGesture?.();
|
|
3095
|
+
this.dragFromMode = mode;
|
|
3096
|
+
this.mountSheetGesture();
|
|
3097
|
+
}
|
|
3098
|
+
getDragFrom() {
|
|
3099
|
+
return this.dragFromMode;
|
|
3100
|
+
}
|
|
3101
|
+
setMode(mode) {
|
|
3102
|
+
if (this.destroyed) return;
|
|
3103
|
+
const next = resolveMode(mode, this.element);
|
|
3104
|
+
if (next === this.mode) return;
|
|
3105
|
+
const wasVertical = this.mode === "bottom" || this.mode === "top";
|
|
3106
|
+
const nowVertical = next === "bottom" || next === "top";
|
|
3107
|
+
this.gesture?.forceClearDragState();
|
|
3108
|
+
this.detachSheetGesture?.();
|
|
3109
|
+
this.animation.cancel();
|
|
3110
|
+
this.element.style.removeProperty(layoutAxis(this.mode));
|
|
3111
|
+
this.mode = next;
|
|
3112
|
+
this.element.dataset.mode = next;
|
|
3113
|
+
this.transformTemplate = buildTransformTemplate(next);
|
|
3114
|
+
this.maxHeightDeps.mode = next;
|
|
3115
|
+
this.fitDeps.mode = next;
|
|
3116
|
+
this.snaps.setMode(next);
|
|
3117
|
+
this.scrim.setSheetMode(next);
|
|
3118
|
+
if (wasVertical !== nowVertical) {
|
|
3119
|
+
this.aria = new AriaSliderWriter(this.handle, next);
|
|
3120
|
+
}
|
|
3121
|
+
this.detachSliderKeyboard?.();
|
|
3122
|
+
this.mountSliderKeyboard();
|
|
3123
|
+
this.mountSheetGesture();
|
|
3124
|
+
this.recompute();
|
|
3125
|
+
const active = this.snaps.findById(this.activeId);
|
|
3126
|
+
if (active) this.applySize(active.size);
|
|
3127
|
+
this.updateAriaSlider();
|
|
3128
|
+
}
|
|
3129
|
+
getMode() {
|
|
3130
|
+
return this.mode;
|
|
3131
|
+
}
|
|
2952
3132
|
isTop() {
|
|
2953
3133
|
return this.isTopSheet;
|
|
2954
3134
|
}
|
|
@@ -3095,12 +3275,24 @@ class BottomSheetCore {
|
|
|
3095
3275
|
auditVhUsage(points);
|
|
3096
3276
|
this.auditDuplicateSnapIds(points);
|
|
3097
3277
|
this.snaps.setRaw(points);
|
|
3098
|
-
if (allowed)
|
|
3278
|
+
if (allowed) {
|
|
3279
|
+
this.snaps.setAllowedIds(allowed);
|
|
3280
|
+
} else {
|
|
3281
|
+
const present = this.snaps.getResolvedSnaps().map((s) => s.id);
|
|
3282
|
+
const kept = this.snaps.getAllowedIds().filter((id) => present.includes(id));
|
|
3283
|
+
this.snaps.setAllowedIds(kept.length ? kept : present);
|
|
3284
|
+
}
|
|
3099
3285
|
this.scrollCache.clear();
|
|
3100
3286
|
this.scrim.invalidateOpacityCache();
|
|
3101
3287
|
const current = this.snaps.findById(this.activeId);
|
|
3102
3288
|
if (current) {
|
|
3103
3289
|
this.applySize(current.size);
|
|
3290
|
+
} else {
|
|
3291
|
+
const fallback = this.snaps.getResolvedSnaps().find((s) => this.snaps.getAllowedIds().includes(s.id)) ?? this.snaps.getResolvedSnaps()[0];
|
|
3292
|
+
if (fallback) {
|
|
3293
|
+
this.activeId = fallback.id;
|
|
3294
|
+
this.applySize(fallback.size);
|
|
3295
|
+
}
|
|
3104
3296
|
}
|
|
3105
3297
|
this.updateAriaSlider();
|
|
3106
3298
|
}
|
|
@@ -3110,7 +3302,7 @@ class BottomSheetCore {
|
|
|
3110
3302
|
this.snaps.recompute();
|
|
3111
3303
|
this.maxHeight.clampTo();
|
|
3112
3304
|
this.scrim.invalidateOpacityCache();
|
|
3113
|
-
if (this.
|
|
3305
|
+
if (this.isDraggingAny()) return;
|
|
3114
3306
|
this.healActiveSnap();
|
|
3115
3307
|
}
|
|
3116
3308
|
healActiveSnap() {
|
|
@@ -3132,6 +3324,8 @@ class BottomSheetCore {
|
|
|
3132
3324
|
this.bus.clear();
|
|
3133
3325
|
this.animation.cancel();
|
|
3134
3326
|
this.gesture?.forceClearDragState();
|
|
3327
|
+
this.contentGesture?.forceClearDragState();
|
|
3328
|
+
this.setRestClosed(false);
|
|
3135
3329
|
this.teardowns.drain();
|
|
3136
3330
|
this.scrim.destroy();
|
|
3137
3331
|
this.animation.destroy();
|
|
@@ -3242,11 +3436,12 @@ class BottomSheetCore {
|
|
|
3242
3436
|
const scale = Math.max(1 - depth * 0.04, 0.86);
|
|
3243
3437
|
this.element.style.scale = scale === 1 ? "" : String(scale);
|
|
3244
3438
|
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
handle:
|
|
3439
|
+
buildGestureDeps(surface, gestureOptions) {
|
|
3440
|
+
return {
|
|
3441
|
+
handle: surface,
|
|
3248
3442
|
element: this.element,
|
|
3249
3443
|
mode: this.mode,
|
|
3444
|
+
gestureOptions,
|
|
3250
3445
|
getRoot: () => this.rootEl,
|
|
3251
3446
|
getDragContext: () => {
|
|
3252
3447
|
const buf = this.dragContextBuf;
|
|
@@ -3265,8 +3460,83 @@ class BottomSheetCore {
|
|
|
3265
3460
|
settleAfterDrag: (delta, velocity, kind) => this.settleAfterDrag(delta, velocity, kind),
|
|
3266
3461
|
emit: (event, payload) => this.emit(event, payload),
|
|
3267
3462
|
listenerCount: (event) => this.bus.listenerCount(event)
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
isContentDragAllowed() {
|
|
3466
|
+
if (this.disableDragFlag) return false;
|
|
3467
|
+
const point = this.snapPointsRaw.find((p) => p.id === this.activeId);
|
|
3468
|
+
return point?.dragFromContent ?? this.dragFromContentDefault;
|
|
3469
|
+
}
|
|
3470
|
+
decideContentDrag(surface, e, delta, crossDelta) {
|
|
3471
|
+
if (this.destroyed) return false;
|
|
3472
|
+
if (e.pointerType === "mouse") return false;
|
|
3473
|
+
if (this.gesture?.isDragging) return false;
|
|
3474
|
+
if (!this.isContentDragAllowed()) return false;
|
|
3475
|
+
if (!isDragAllowedFrom(e.target, "sheet")) return false;
|
|
3476
|
+
const decision = decideContentGesture({
|
|
3477
|
+
delta,
|
|
3478
|
+
crossDelta,
|
|
3479
|
+
sharesScrollAxis: this.mode === "bottom" || this.mode === "top",
|
|
3480
|
+
scrollTop: surface.scrollTop,
|
|
3481
|
+
atMaxSnap: this.size >= this.getAllowedRange().max - 0.5
|
|
3482
|
+
});
|
|
3483
|
+
if (decision === "pending") return null;
|
|
3484
|
+
return decision === "drag";
|
|
3485
|
+
}
|
|
3486
|
+
attachDragSurface(surface, kind) {
|
|
3487
|
+
if (this.destroyed || kind !== "content") return;
|
|
3488
|
+
if (this.contentGesture) return;
|
|
3489
|
+
const controller = new GestureController(this.buildGestureDeps(surface, {
|
|
3490
|
+
manageTouchAction: false,
|
|
3491
|
+
deferStart: (e, delta, crossDelta) => this.decideContentDrag(surface, e, delta, crossDelta)
|
|
3492
|
+
}));
|
|
3493
|
+
this.contentGesture = controller;
|
|
3494
|
+
const detachGestures = controller.install();
|
|
3495
|
+
const detachGuard = installTouchScrollGuard(surface, () => controller.isDragging);
|
|
3496
|
+
return () => {
|
|
3497
|
+
detachGuard();
|
|
3498
|
+
detachGestures();
|
|
3499
|
+
if (this.contentGesture === controller) this.contentGesture = void 0;
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
sheetDragSurface() {
|
|
3503
|
+
return this.dragFromMode === "handle" ? this.handle : this.element;
|
|
3504
|
+
}
|
|
3505
|
+
canStartSheetDrag(e) {
|
|
3506
|
+
if (this.contentGesture?.isDragging) return false;
|
|
3507
|
+
const target = e.target;
|
|
3508
|
+
const scoped = typeof target?.closest === "function";
|
|
3509
|
+
if (scoped && target.closest(NO_DRAG_SELECTOR)) return false;
|
|
3510
|
+
if (this.handle !== this.element && scoped && this.handle.contains(target)) {
|
|
3511
|
+
return true;
|
|
3512
|
+
}
|
|
3513
|
+
if (!isDragAllowedFrom(e.target, this.dragFromMode)) return false;
|
|
3514
|
+
if (this.dragFromMode === "handle") return true;
|
|
3515
|
+
if (this.scrollContainer && scoped && this.scrollContainer.contains(target) && !target.closest(DRAG_ZONE_SELECTOR)) {
|
|
3516
|
+
return false;
|
|
3517
|
+
}
|
|
3518
|
+
return true;
|
|
3519
|
+
}
|
|
3520
|
+
mountSheetGesture() {
|
|
3521
|
+
const surface = this.sheetDragSurface();
|
|
3522
|
+
const controller = new GestureController(this.buildGestureDeps(surface, {
|
|
3523
|
+
shouldStart: (e) => this.canStartSheetDrag(e),
|
|
3524
|
+
manageTouchAction: this.dragFromMode === "handle"
|
|
3525
|
+
}));
|
|
3526
|
+
this.gesture = controller;
|
|
3527
|
+
const detachGestures = controller.install();
|
|
3528
|
+
const detachGuard = this.dragFromMode === "handle" ? null : installTouchScrollGuard(surface, () => controller.isDragging);
|
|
3529
|
+
this.detachSheetGesture = () => {
|
|
3530
|
+
detachGuard?.();
|
|
3531
|
+
detachGestures();
|
|
3532
|
+
};
|
|
3533
|
+
}
|
|
3534
|
+
attach() {
|
|
3535
|
+
this.mountSheetGesture();
|
|
3536
|
+
this.teardowns.add(() => {
|
|
3537
|
+
this.detachSheetGesture?.();
|
|
3538
|
+
this.detachSheetGesture = null;
|
|
3268
3539
|
});
|
|
3269
|
-
this.teardowns.add(this.gesture.install());
|
|
3270
3540
|
try {
|
|
3271
3541
|
this.attachFeatures();
|
|
3272
3542
|
} catch (err) {
|
|
@@ -3303,7 +3573,7 @@ class BottomSheetCore {
|
|
|
3303
3573
|
element: this.element,
|
|
3304
3574
|
getMode: () => this.mode,
|
|
3305
3575
|
isDestroyed: () => this.destroyed,
|
|
3306
|
-
isDragging: () => this.
|
|
3576
|
+
isDragging: () => this.isDraggingAny(),
|
|
3307
3577
|
resolveActiveSnap: () => this.snaps.findById(this.activeId),
|
|
3308
3578
|
getMaxAxisSize: () => this.snaps.getMaxAxisSize(),
|
|
3309
3579
|
getSize: () => this.size,
|
|
@@ -3336,7 +3606,11 @@ class BottomSheetCore {
|
|
|
3336
3606
|
this.teardowns.add(() => document.removeEventListener("keydown", onKey));
|
|
3337
3607
|
}
|
|
3338
3608
|
}
|
|
3339
|
-
this.
|
|
3609
|
+
this.mountSliderKeyboard();
|
|
3610
|
+
this.scrim.attach();
|
|
3611
|
+
}
|
|
3612
|
+
mountSliderKeyboard() {
|
|
3613
|
+
const detach = installSliderKeyboard({
|
|
3340
3614
|
handle: this.handle,
|
|
3341
3615
|
mode: this.mode,
|
|
3342
3616
|
isDestroyed: () => this.destroyed,
|
|
@@ -3345,8 +3619,12 @@ class BottomSheetCore {
|
|
|
3345
3619
|
snapTo: (id) => {
|
|
3346
3620
|
void this.snapTo(id);
|
|
3347
3621
|
}
|
|
3348
|
-
})
|
|
3349
|
-
this.
|
|
3622
|
+
});
|
|
3623
|
+
this.detachSliderKeyboard = detach;
|
|
3624
|
+
this.teardowns.add(() => {
|
|
3625
|
+
this.detachSliderKeyboard?.();
|
|
3626
|
+
this.detachSliderKeyboard = null;
|
|
3627
|
+
});
|
|
3350
3628
|
}
|
|
3351
3629
|
settleAfterDrag(delta, velocity, kind = "touch") {
|
|
3352
3630
|
const signal = this.newCycle();
|
|
@@ -3393,8 +3671,11 @@ class BottomSheetCore {
|
|
|
3393
3671
|
this.completeSnap(target.id, target.size, previousSize, previousSnapSize === 0, true);
|
|
3394
3672
|
});
|
|
3395
3673
|
}
|
|
3674
|
+
isDraggingAny() {
|
|
3675
|
+
return (this.gesture?.isDragging ?? false) || (this.contentGesture?.isDragging ?? false);
|
|
3676
|
+
}
|
|
3396
3677
|
clearWillChangeIfIdle() {
|
|
3397
|
-
if (this.animation.isAnimating || this.
|
|
3678
|
+
if (this.animation.isAnimating || this.isDraggingAny()) return;
|
|
3398
3679
|
this.element.style.willChange = "";
|
|
3399
3680
|
}
|
|
3400
3681
|
haptic() {
|
|
@@ -3407,8 +3688,9 @@ class BottomSheetCore {
|
|
|
3407
3688
|
}
|
|
3408
3689
|
applySize(size, skipTransform = false) {
|
|
3409
3690
|
const cap = this.snaps.getMaxAxisSize();
|
|
3410
|
-
const clamped = cap > 0 && size > cap && !this.
|
|
3691
|
+
const clamped = cap > 0 && size > cap && !this.isDraggingAny() && !this.allowOvershoot ? cap : size;
|
|
3411
3692
|
this.size = clamped;
|
|
3693
|
+
if (clamped > 0) this.setRestClosed(false);
|
|
3412
3694
|
const offset = cap - clamped;
|
|
3413
3695
|
const style = this.element.style;
|
|
3414
3696
|
if (!skipTransform) style.transform = this.transformTemplate(offset);
|
|
@@ -3780,55 +4062,9 @@ function installAutoCollapse(deps) {
|
|
|
3780
4062
|
unsubscribe();
|
|
3781
4063
|
};
|
|
3782
4064
|
}
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
let startY = 0;
|
|
3787
|
-
let startScrollTop = 0;
|
|
3788
|
-
let moved = false;
|
|
3789
|
-
const onTouchStart = (e) => {
|
|
3790
|
-
const t = e.touches[0];
|
|
3791
|
-
if (!t) return;
|
|
3792
|
-
startY = t.clientY;
|
|
3793
|
-
startScrollTop = container.scrollTop;
|
|
3794
|
-
moved = false;
|
|
3795
|
-
};
|
|
3796
|
-
const onTouchMove = (e) => {
|
|
3797
|
-
const t = e.touches[0];
|
|
3798
|
-
if (!t) return;
|
|
3799
|
-
if (Math.abs(t.clientY - startY) > CONTENT_PULL_THRESHOLD) moved = true;
|
|
3800
|
-
};
|
|
3801
|
-
const onTouchEnd = (e) => {
|
|
3802
|
-
if (!moved) return;
|
|
3803
|
-
if (deps.isDragging() || deps.isAnimating()) return;
|
|
3804
|
-
if (container.scrollTop !== startScrollTop) return;
|
|
3805
|
-
const t = e.changedTouches[0];
|
|
3806
|
-
if (!t) return;
|
|
3807
|
-
const deltaY = t.clientY - startY;
|
|
3808
|
-
const atTop = container.scrollTop <= 0;
|
|
3809
|
-
const allowed = deps.getAllowedIds();
|
|
3810
|
-
const idx = allowed.indexOf(deps.getActiveId());
|
|
3811
|
-
if (idx === -1) return;
|
|
3812
|
-
if (atTop && deltaY > CONTENT_PULL_THRESHOLD && idx > 0) {
|
|
3813
|
-
deps.snapTo(allowed[idx - 1]);
|
|
3814
|
-
} else if (deltaY < -CONTENT_PULL_THRESHOLD && idx < allowed.length - 1) {
|
|
3815
|
-
deps.snapTo(allowed[idx + 1]);
|
|
3816
|
-
}
|
|
3817
|
-
};
|
|
3818
|
-
container.addEventListener("touchstart", onTouchStart, {
|
|
3819
|
-
passive: true
|
|
3820
|
-
});
|
|
3821
|
-
container.addEventListener("touchmove", onTouchMove, {
|
|
3822
|
-
passive: true
|
|
3823
|
-
});
|
|
3824
|
-
container.addEventListener("touchend", onTouchEnd, {
|
|
3825
|
-
passive: true
|
|
3826
|
-
});
|
|
3827
|
-
return () => {
|
|
3828
|
-
container.removeEventListener("touchstart", onTouchStart);
|
|
3829
|
-
container.removeEventListener("touchmove", onTouchMove);
|
|
3830
|
-
container.removeEventListener("touchend", onTouchEnd);
|
|
3831
|
-
};
|
|
4065
|
+
function installContentDrag(deps) {
|
|
4066
|
+
const detach = deps.attachDragSurface(deps.container, "content");
|
|
4067
|
+
return () => detach?.();
|
|
3832
4068
|
}
|
|
3833
4069
|
const SAFETY_PAD = 8;
|
|
3834
4070
|
function installVisualViewport(deps) {
|
|
@@ -3930,13 +4166,9 @@ function contentSwipeFeature() {
|
|
|
3930
4166
|
stage: "attach",
|
|
3931
4167
|
install: (ctx) => {
|
|
3932
4168
|
if (!ctx.scrollContainer) return;
|
|
3933
|
-
return
|
|
4169
|
+
return installContentDrag({
|
|
3934
4170
|
container: ctx.scrollContainer,
|
|
3935
|
-
|
|
3936
|
-
isAnimating: ctx.isAnimating,
|
|
3937
|
-
getAllowedIds: ctx.allowedIdsBySize,
|
|
3938
|
-
getActiveId: ctx.getActiveId,
|
|
3939
|
-
snapTo: ctx.snapTo
|
|
4171
|
+
attachDragSurface: ctx.attachDragSurface
|
|
3940
4172
|
});
|
|
3941
4173
|
}
|
|
3942
4174
|
};
|
|
@@ -4067,6 +4299,8 @@ const BottomSheet = component$((props) => {
|
|
|
4067
4299
|
persistent: props.persistent,
|
|
4068
4300
|
disableClose: props.disableClose,
|
|
4069
4301
|
disableDrag: props.disableDrag,
|
|
4302
|
+
dragFrom: props.dragFrom,
|
|
4303
|
+
dragFromContent: props.dragFromContent,
|
|
4070
4304
|
closeOnRouteChange: props.closeOnRouteChange,
|
|
4071
4305
|
stackEffect: props.stackEffect,
|
|
4072
4306
|
returnFocusTo: props.returnFocusTo,
|
|
@@ -4174,11 +4408,17 @@ const BottomSheet = component$((props) => {
|
|
|
4174
4408
|
const persistent = track(() => props.persistent);
|
|
4175
4409
|
const disableClose = track(() => props.disableClose);
|
|
4176
4410
|
const disableDrag = track(() => props.disableDrag);
|
|
4411
|
+
const dragFromContent = track(() => props.dragFromContent);
|
|
4412
|
+
const dragFrom = track(() => props.dragFrom);
|
|
4177
4413
|
const engine = engineStore.engine;
|
|
4178
4414
|
if (!engine) return;
|
|
4179
4415
|
if (persistent !== void 0) engine.setPersistent(persistent);
|
|
4180
4416
|
if (disableClose !== void 0) engine.setDisableClose(disableClose);
|
|
4181
4417
|
if (disableDrag !== void 0) engine.setDisableDrag(disableDrag);
|
|
4418
|
+
if (dragFromContent !== void 0) {
|
|
4419
|
+
engine.setDragFromContent(dragFromContent);
|
|
4420
|
+
}
|
|
4421
|
+
if (dragFrom !== void 0) engine.setDragFrom(dragFrom);
|
|
4182
4422
|
});
|
|
4183
4423
|
const showBackdrop = props.backdrop !== false;
|
|
4184
4424
|
const mode = props.mode ?? "bottom";
|