From d468cbab43c4e0b673a01d34e40f4dd43278a9a5 Mon Sep 17 00:00:00 2001 From: Vishv Salvi Date: Sun, 6 Jul 2025 09:26:11 -0700 Subject: [PATCH] fix(modal): draggable modal, scrollable on mobile --- .changeset/clean-bulldogs-visit.md | 5 +++++ packages/hooks/use-draggable/src/index.ts | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .changeset/clean-bulldogs-visit.md diff --git a/.changeset/clean-bulldogs-visit.md b/.changeset/clean-bulldogs-visit.md new file mode 100644 index 0000000000..8f64b58f7c --- /dev/null +++ b/.changeset/clean-bulldogs-visit.md @@ -0,0 +1,5 @@ +--- +"@heroui/use-draggable": patch +--- + +Draggable modal will be scrollable in mobile devices (#5280) diff --git a/packages/hooks/use-draggable/src/index.ts b/packages/hooks/use-draggable/src/index.ts index 300ef53ee1..e5a2ea9710 100644 --- a/packages/hooks/use-draggable/src/index.ts +++ b/packages/hooks/use-draggable/src/index.ts @@ -28,9 +28,11 @@ export interface UseDraggableProps { export function useDraggable(props: UseDraggableProps): MoveResult { const {targetRef, isDisabled = false, canOverflow = false} = props; const boundary = useRef({minLeft: 0, minTop: 0, maxLeft: 0, maxTop: 0}); + const isDragging = useRef(false); let transform = {offsetX: 0, offsetY: 0}; const onMoveStart = useCallback(() => { + isDragging.current = true; const {offsetX, offsetY} = transform; const targetRect = targetRef?.current?.getBoundingClientRect(); @@ -82,27 +84,35 @@ export function useDraggable(props: UseDraggableProps): MoveResult { [isDisabled, transform, boundary.current, canOverflow, targetRef?.current], ); + const onMoveEnd = useCallback(() => { + isDragging.current = false; + }, []); + const {moveProps} = useMove({ onMoveStart, onMove, + onMoveEnd, }); const preventDefault = useCallback((e: TouchEvent) => { - e.preventDefault(); + // Only prevent touchmove events if we're actively dragging + if (isDragging.current) { + e.preventDefault(); + } }, []); // NOTE: This process is due to the modal being displayed at the bottom instead of the center when opened on mobile sizes. // It will become unnecessary once the modal is centered properly. useEffect(() => { if (!isDisabled) { - // Prevent body scroll when dragging at mobile. + // Prevent body scroll when dragging at mobile, but only during active dragging. document.body.addEventListener("touchmove", preventDefault, {passive: false}); } return () => { document.body.removeEventListener("touchmove", preventDefault); }; - }, [isDisabled]); + }, [isDisabled, preventDefault]); return { moveProps: {