Skip to content

Commit 4611ae9

Browse files
committed
maybe fix mobile items
1 parent d53a2d4 commit 4611ae9

2 files changed

Lines changed: 41 additions & 49 deletions

File tree

src/components/PurchaseDetailModal.jsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@ export default function PurchaseDetailModal({ purchase, user, validation, onClos
2626

2727
const handleClose = () => {
2828
setIsClosing(true);
29-
// Wait for animation to complete before actually closing
3029
setTimeout(() => {
3130
onClose();
32-
}, 300); // Match animation duration
31+
}, 300); // matches animation duration
3332
};
3433

3534
// Custom hooks for drag functionality - pass handleClose instead of onClose
36-
const { translateY, onTouchStart, onTouchMove, onTouchEnd } = useModalDrag(sheetRef, scrollRef, headerRef, handleClose);
37-
35+
const { translateY, onTouchStart, onTouchMove, onTouchEnd } = useModalDrag(sheetRef, scrollRef, handleClose);
3836
// Custom hook for approval logic
3937
const {
4038
canApproveRequest,
@@ -210,17 +208,20 @@ export default function PurchaseDetailModal({ purchase, user, validation, onClos
210208
onTouchEnd={onTouchEnd}
211209
style={{
212210
transform: isClosing
213-
? (window.innerWidth < 768 ? 'translateY(100%)' : `scale(0.95) translateY(${translateY}px)`)
211+
? (window.innerWidth < 768
212+
? "translateY(100%)"
213+
: `scale(0.95) translateY(${translateY}px)`)
214214
: `translateY(${translateY}px) scale(1)`,
215215
opacity: isClosing ? 0 : 1,
216+
paddingTop: "env(safe-area-inset-top)", // ⬅ keeps header below Chrome bar
216217
transition: isClosing
217-
? 'all 300ms cubic-bezier(.22,.9,.32,1)'
218-
: 'transform 180ms cubic-bezier(.22,.9,.32,1), opacity 300ms ease-out',
219-
touchAction: 'pan-y',
218+
? "all 300ms cubic-bezier(.22,.9,.32,1)"
219+
: "transform 180ms cubic-bezier(.22,.9,.32,1), opacity 300ms ease-out",
220+
touchAction: "pan-y",
220221
}}
221-
className={`bg-white shadow-2xl w-full rounded-t-2xl md:rounded-2xl md:max-w-3xl md:h-auto md:max-h-[90vh] max-h-[95vh] overflow-hidden flex flex-col ${
222-
!isClosing && 'md:animate-slideUp animate-slideInFromBottom'
223-
}`}
222+
className="bg-white shadow-2xl w-full rounded-t-2xl md:rounded-2xl
223+
md:max-w-3xl md:h-auto md:max-h-[90vh]
224+
max-h-[95vh] overflow-hidden flex flex-col"
224225
>
225226
<ModalHeader
226227
ref={headerRef}

src/hooks/useModalDrag.jsx

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,83 @@
11
import { useState, useEffect, useRef } from 'react';
22

3-
export const useModalDrag = (sheetRef, scrollRef, headerRef, onClose) => {
3+
export const useModalDrag = (sheetRef, scrollRef, onClose) => {
44
const [translateY, setTranslateY] = useState(0);
55
const [isDragging, setIsDragging] = useState(false);
6+
67
const startYRef = useRef(0);
78
const lastYRef = useRef(0);
8-
const dragStartedOnHeaderRef = useRef(false);
99

1010
const onTouchStart = (e) => {
11-
if (!sheetRef.current || !headerRef.current) return;
11+
if (!sheetRef.current) return;
1212

13-
// Check if touch started on the header
1413
const touch = e.touches ? e.touches[0] : e;
15-
const headerRect = headerRef.current.getBoundingClientRect();
16-
const touchedHeader = touch.clientY >= headerRect.top && touch.clientY <= headerRect.bottom;
14+
const scrollEl = scrollRef.current;
1715

18-
if (!touchedHeader) {
19-
dragStartedOnHeaderRef.current = false;
20-
return;
21-
}
16+
// Only allow drag when content is fully scrolled to top
17+
const scrollAtTop = !scrollEl || scrollEl.scrollTop <= 0;
18+
if (!scrollAtTop) return;
2219

23-
dragStartedOnHeaderRef.current = true;
2420
startYRef.current = touch.clientY;
2521
lastYRef.current = touch.clientY;
2622
setIsDragging(true);
2723
};
2824

2925
const onTouchMove = (e) => {
30-
if (!isDragging || !sheetRef.current || !dragStartedOnHeaderRef.current) return;
26+
if (!isDragging || !sheetRef.current) return;
3127

3228
const touch = e.touches ? e.touches[0] : e;
3329
const deltaY = touch.clientY - startYRef.current;
34-
lastYRef.current = touch.clientY;
3530

36-
const scrollEl = scrollRef.current;
37-
const scrollAtTop = !scrollEl || scrollEl.scrollTop <= 0;
38-
39-
if (deltaY > 0 && scrollAtTop) {
40-
setTranslateY(deltaY);
41-
if (e.cancelable) e.preventDefault();
42-
} else if (deltaY < 0 && translateY > 0) {
43-
setTranslateY(Math.max(0, deltaY));
31+
// Only drag downward, and damp movement to reduce jitter
32+
if (deltaY > 0) {
33+
setTranslateY(deltaY * 0.9);
4434
if (e.cancelable) e.preventDefault();
4535
}
4636
};
4737

4838
const onTouchEnd = () => {
49-
if (!isDragging || !dragStartedOnHeaderRef.current) return;
39+
if (!isDragging) return;
40+
5041
setIsDragging(false);
51-
dragStartedOnHeaderRef.current = false;
5242

5343
const delta = lastYRef.current - startYRef.current;
54-
const threshold = Math.min(140, window.innerHeight * 0.18);
44+
const threshold = window.innerHeight * 0.50; // must drag half screen to close
45+
5546
if (delta > threshold) {
56-
// Let the parent handle the close with animation
47+
// Trigger animated close via parent, not instant close
5748
onClose();
5849
} else {
5950
setTranslateY(0);
6051
}
6152
};
6253

54+
// Use pointer events for stability
6355
useEffect(() => {
6456
const el = sheetRef.current;
6557
if (!el) return;
6658

67-
const onPointerDown = (e) => {
59+
const handlePointerDown = (e) => {
6860
if (window.innerWidth >= 768) return;
69-
el.setPointerCapture?.(e.pointerId);
7061
onTouchStart(e);
7162
};
72-
const onPointerMove = (e) => {
63+
const handlePointerMove = (e) => {
7364
if (!isDragging) return;
65+
lastYRef.current = e.clientY;
7466
onTouchMove(e);
7567
};
76-
const onPointerUp = (e) => {
68+
const handlePointerUp = () => {
7769
if (!isDragging) return;
78-
onTouchEnd(e);
79-
el.releasePointerCapture?.(e.pointerId);
70+
onTouchEnd();
8071
};
8172

82-
el.addEventListener('pointerdown', onPointerDown);
83-
window.addEventListener('pointermove', onPointerMove);
84-
window.addEventListener('pointerup', onPointerUp);
73+
el.addEventListener("pointerdown", handlePointerDown);
74+
window.addEventListener("pointermove", handlePointerMove);
75+
window.addEventListener("pointerup", handlePointerUp);
8576

8677
return () => {
87-
el.removeEventListener('pointerdown', onPointerDown);
88-
window.removeEventListener('pointermove', onPointerMove);
89-
window.removeEventListener('pointerup', onPointerUp);
78+
el.removeEventListener("pointerdown", handlePointerDown);
79+
window.removeEventListener("pointermove", handlePointerMove);
80+
window.removeEventListener("pointerup", handlePointerUp);
9081
};
9182
}, [isDragging]);
9283

@@ -96,4 +87,4 @@ export const useModalDrag = (sheetRef, scrollRef, headerRef, onClose) => {
9687
onTouchMove,
9788
onTouchEnd
9889
};
99-
};
90+
};

0 commit comments

Comments
 (0)