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