From 611449d32802cd6d9d27891163c6c2fa8bfbcf89 Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 21 Feb 2026 23:57:26 +0000 Subject: [PATCH] Add photo carousel to swipe cards SwipeCard now shows all available photos (up to 5) using embla-carousel instead of just the thumbnail. Includes prev/next arrow buttons and dot indicators. The photo area uses touch-action: pan-x so carousel swipes don't trigger card swipes. --- frontend/src/components/SwipeCard.tsx | 61 ++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/SwipeCard.tsx b/frontend/src/components/SwipeCard.tsx index 74c4850..e7add7a 100644 --- a/frontend/src/components/SwipeCard.tsx +++ b/frontend/src/components/SwipeCard.tsx @@ -1,7 +1,8 @@ -import { useRef } from 'react'; +import { useRef, useState, useCallback, useEffect } from 'react'; import { animated, useSpring } from '@react-spring/web'; import { useDrag } from '@use-gesture/react'; -import { Bed, Maximize2, ExternalLink } from 'lucide-react'; +import useEmblaCarousel from 'embla-carousel-react'; +import { Bed, Maximize2, ExternalLink, ChevronLeft, ChevronRight } from 'lucide-react'; import type { PropertyFeature } from '@/types'; interface SwipeCardProps { @@ -17,6 +18,21 @@ const SWIPE_THRESHOLD = 100; export function SwipeCard({ feature, onSwipe, onTap, isTop, stackIndex }: SwipeCardProps) { const hasSwiped = useRef(false); const p = feature.properties; + const photos = p.photos?.length ? p.photos : p.photo_thumbnail ? [p.photo_thumbnail] : []; + + const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }); + const [selectedPhoto, setSelectedPhoto] = useState(0); + + const onPhotoSelect = useCallback(() => { + if (!emblaApi) return; + setSelectedPhoto(emblaApi.selectedScrollSnap()); + }, [emblaApi]); + + useEffect(() => { + if (!emblaApi) return; + emblaApi.on('select', onPhotoSelect); + return () => { emblaApi.off('select', onPhotoSelect); }; + }, [emblaApi, onPhotoSelect]); const [style, api] = useSpring(() => ({ x: 0, @@ -96,11 +112,42 @@ export function SwipeCard({ feature, onSwipe, onTap, isTop, stackIndex }: SwipeC /> )} - {/* Photo */} -
- {p.photo_thumbnail && ( - Property - )} + {/* Photo carousel */} +
+ {photos.length > 1 ? ( + <> +
+
+ {photos.map((url, i) => ( +
+ {`Photo +
+ ))} +
+
+ + +
+ {photos.map((_, i) => ( +
+ ))} +
+ + ) : photos.length === 1 ? ( + Property + ) : null}