feat(mobile): implement property photo swipe and refine lightbox UI
This commit is contained in:
@@ -33,6 +33,8 @@ const PropertyDetailDrawer = ({ isOpen, onClose, data, newLocation, onSave, load
|
|||||||
|
|
||||||
// Lightbox State
|
// Lightbox State
|
||||||
const [lightboxIndex, setLightboxIndex] = useState(null); // null = closed, number = open at index
|
const [lightboxIndex, setLightboxIndex] = useState(null); // null = closed, number = open at index
|
||||||
|
const [touchStart, setTouchStart] = useState(null);
|
||||||
|
const [touchEnd, setTouchEnd] = useState(null);
|
||||||
|
|
||||||
const panelRef = useRef(null);
|
const panelRef = useRef(null);
|
||||||
const closeButtonRef = useRef(null);
|
const closeButtonRef = useRef(null);
|
||||||
@@ -206,6 +208,29 @@ const PropertyDetailDrawer = ({ isOpen, onClose, data, newLocation, onSave, load
|
|||||||
if (data) setIsEditing(false);
|
if (data) setIsEditing(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Swipe Logic for Lightbox
|
||||||
|
const handleTouchStart = (e) => {
|
||||||
|
setTouchEnd(null);
|
||||||
|
setTouchStart(e.targetTouches[0].clientX);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchMove = (e) => {
|
||||||
|
setTouchEnd(e.targetTouches[0].clientX);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchEnd = () => {
|
||||||
|
if (!touchStart || !touchEnd) return;
|
||||||
|
const distance = touchStart - touchEnd;
|
||||||
|
const isLeftSwipe = distance > 50;
|
||||||
|
const isRightSwipe = distance < -50;
|
||||||
|
|
||||||
|
if (isLeftSwipe) {
|
||||||
|
setLightboxIndex((prev) => (prev + 1) % photos.length);
|
||||||
|
} else if (isRightSwipe) {
|
||||||
|
setLightboxIndex((prev) => (prev - 1 + photos.length) % photos.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Photo Logic
|
// Photo Logic
|
||||||
const handleUploadPhoto = (e) => {
|
const handleUploadPhoto = (e) => {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
@@ -624,11 +649,14 @@ const PropertyDetailDrawer = ({ isOpen, onClose, data, newLocation, onSave, load
|
|||||||
{/* LIGHTBOX OVERLAY */}
|
{/* LIGHTBOX OVERLAY */}
|
||||||
{lightboxIndex !== null && photos[lightboxIndex] && (
|
{lightboxIndex !== null && photos[lightboxIndex] && (
|
||||||
<div
|
<div
|
||||||
className="fixed inset-0 z-[2000] bg-black/95 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in duration-200 focus:outline-none"
|
className="fixed inset-0 z-[2000] bg-black/95 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in duration-200 focus:outline-none touch-none"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
ref={lightboxRef}
|
ref={lightboxRef}
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-label="Photo Lightbox"
|
aria-label="Photo Lightbox"
|
||||||
|
onTouchStart={handleTouchStart}
|
||||||
|
onTouchMove={handleTouchMove}
|
||||||
|
onTouchEnd={handleTouchEnd}
|
||||||
>
|
>
|
||||||
{/* Close Button */}
|
{/* Close Button */}
|
||||||
<button
|
<button
|
||||||
@@ -642,10 +670,10 @@ const PropertyDetailDrawer = ({ isOpen, onClose, data, newLocation, onSave, load
|
|||||||
{/* Navigation Left */}
|
{/* Navigation Left */}
|
||||||
<button
|
<button
|
||||||
onClick={(e) => { e.stopPropagation(); setLightboxIndex((prev) => (prev - 1 + photos.length) % photos.length); }}
|
onClick={(e) => { e.stopPropagation(); setLightboxIndex((prev) => (prev - 1 + photos.length) % photos.length); }}
|
||||||
className="absolute left-4 text-white/70 hover:text-white p-3 rounded-full hover:bg-white/10 transition-colors z-50 focus:ring-2 focus:ring-white hidden md:block"
|
className="absolute left-2 md:left-4 text-white/70 hover:text-white p-2 md:p-3 rounded-full hover:bg-white/10 transition-colors z-50 focus:ring-2 focus:ring-white"
|
||||||
aria-label="Previous photo"
|
aria-label="Previous photo"
|
||||||
>
|
>
|
||||||
<ChevronLeft size={32} />
|
<ChevronLeft size={28} className="md:w-8 md:h-8" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Main Image */}
|
{/* Main Image */}
|
||||||
@@ -664,17 +692,12 @@ const PropertyDetailDrawer = ({ isOpen, onClose, data, newLocation, onSave, load
|
|||||||
{/* Navigation Right */}
|
{/* Navigation Right */}
|
||||||
<button
|
<button
|
||||||
onClick={(e) => { e.stopPropagation(); setLightboxIndex((prev) => (prev + 1) % photos.length); }}
|
onClick={(e) => { e.stopPropagation(); setLightboxIndex((prev) => (prev + 1) % photos.length); }}
|
||||||
className="absolute right-4 text-white/70 hover:text-white p-3 rounded-full hover:bg-white/10 transition-colors z-50 focus:ring-2 focus:ring-white hidden md:block"
|
className="absolute right-2 md:right-4 text-white/70 hover:text-white p-2 md:p-3 rounded-full hover:bg-white/10 transition-colors z-50 focus:ring-2 focus:ring-white"
|
||||||
aria-label="Next photo"
|
aria-label="Next photo"
|
||||||
>
|
>
|
||||||
<ChevronRight size={32} />
|
<ChevronRight size={28} className="md:w-8 md:h-8" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Mobile Navigation Hints (Optional) */}
|
|
||||||
<div className="md:hidden absolute bottom-8 flex space-x-8 text-white/30 text-xs pointer-events-none">
|
|
||||||
<span>← Swipe Left</span>
|
|
||||||
<span>Swipe Right →</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user