fix(ui): attach the bottom sheet's touch listeners when the sheet mounts

The non-passive touchmove listeners on the sheet and its backdrop were bound in an effect that ran while the portaled sheet was still unmounted, and never ran again, so they were never attached. The mounted element is now state, and the listeners follow it.
This commit is contained in:
Marc-André Bombeck committed 2026-10-22 15:35:41 +02:00
1 parent 996cc15b50
commit a0e97a4e54
2 files changed
+17 -5

No files matched your search

+3
View File
@@ -148,6 +148,7 @@ export function BottomSheetContent({
const openerRef = React.useRef<HTMLElement | null>(null);
const initial: SheetSnap = preview ? "preview" : "full";
const [snap, setSnap] = React.useState<SheetSnap>(initial);
const [node, setNode] = React.useState<HTMLDivElement | null>(null);
const drag = useSheetDrag({
enabled: draggable,
@@ -158,11 +159,13 @@ export function BottomSheetContent({
contentRef,
overlayRef,
probeRef,
node,
});
const setContent = React.useCallback(
(node: HTMLDivElement | null) => {
contentRef.current = node;
setNode(node);
if (typeof ref === "function") ref(node);
else if (ref) ref.current = node;
},
+14 -5
View File
@@ -422,6 +422,14 @@ export interface UseSheetDragArgs {
contentRef: React.RefObject<HTMLElement | null>;
overlayRef: React.RefObject<HTMLElement | null>;
probeRef: React.RefObject<HTMLElement | null>;
/**
* The mounted sheet element, as state rather than a ref. The sheet is
* portaled and mounts only while open, often long after the component
* that owns it (the More sheet's content is rendered, closed, from the
* first paint); the touch listeners below must be attached when the
* element appears, which a ref cannot announce.
*/
node: HTMLElement | null;
}
export interface SheetDragHandlers {
@@ -441,6 +449,7 @@ export function useSheetDrag({
contentRef,
overlayRef,
probeRef,
node,
}: UseSheetDragArgs): SheetDragHandlers {
const latest = useRef({ hasPreview, snap, onSnap, onClose });
useEffect(() => {
@@ -463,8 +472,8 @@ export function useSheetDrag({
}, [contentRef, overlayRef, probeRef]);
useEffect(() => {
if (!enabled) return;
const content = contentRef.current;
if (!enabled || !node) return;
const content = node;
const overlay = overlayRef.current;
const drag = controller();
const onContentTouch = (event: TouchEvent) => drag.touchMove(event);
@@ -472,14 +481,14 @@ export function useSheetDrag({
const onOverlayTouch = (event: TouchEvent) => {
if (event.cancelable) event.preventDefault();
};
content?.addEventListener("touchmove", onContentTouch, { passive: false });
content.addEventListener("touchmove", onContentTouch, { passive: false });
overlay?.addEventListener("touchmove", onOverlayTouch, { passive: false });
return () => {
content?.removeEventListener("touchmove", onContentTouch);
content.removeEventListener("touchmove", onContentTouch);
overlay?.removeEventListener("touchmove", onOverlayTouch);
drag.dispose();
};
}, [enabled, controller, contentRef, overlayRef]);
}, [enabled, node, controller, overlayRef]);
if (!enabled) return {};
return {