Skip to content

Commit

Permalink
feat: implement image gallery component for project deal page
Browse files Browse the repository at this point in the history
  • Loading branch information
GGWPXXXX committed Nov 10, 2024
1 parent 7e4a237 commit 493d280
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 32 deletions.
36 changes: 4 additions & 32 deletions src/app/(investment)/deals/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import ReactMarkdown from "react-markdown";

import * as Tabs from "@radix-ui/react-tabs";
import { Button } from "@/components/ui/button";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { Separator } from "@/components/ui/separator";
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
import FollowShareButtons from "./followShareButton";
import { DisplayFullImage } from "./displayImage";

import { getProjectData } from "@/lib/data/projectQuery";
import { getDealList } from "@/app/api/dealApi";
import { sumByKey, toPercentage } from "@/lib/utils";
import { redirect } from "next/navigation";
import { isOwnerOfProject } from "./query";
import remarkGfm from "remark-gfm";
import Gallery from "@/components/carousel";

const PHOTO_MATERIAL_ID = 2;

Expand Down Expand Up @@ -75,7 +75,6 @@ export default async function ProjectDealPage({ params }: { params: { id: number
? projectMaterial.flatMap((item) =>
(item.material_url || ["/boiler1.jpg"]).map((url: string) => ({
src: url,
alt: "Image",
}))
)
: [{ src: "/boiler1.jpg", alt: "Default Boiler Image" }];
Expand Down Expand Up @@ -106,35 +105,8 @@ export default async function ProjectDealPage({ params }: { params: { id: number
</div>
<div id="sub-content" className="flex flex-row mt-5">
{/* image carousel */}
<div id="image-carousel" className="shrink-0 w-[700px] flex flex-col">
{/* first carousel */}
<Carousel className="w-full h-[400px] ml-1 overflow-hidden">
<CarouselContent className="flex h-full">
{carouselData.map((item, index) => (
<CarouselItem key={index}>
<Image src={item.src} alt={item.alt} width={700} height={400} className="rounded-lg object-cover" />
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className="absolute left-2 top-1/2 transform -translate-y-1/2 z-10 text-white bg-black opacity-50 hover:opacity-100" />
<CarouselNext className="absolute right-2 top-1/2 transform -translate-y-1/2 z-10 text-white bg-black opacity-50 hover:opacity-100" />
</Carousel>
{/* second carousel */}
<Carousel className="w-full ml-1 h-[100px] mt-5 overflow-hidden">
<CarouselContent className="flex space-x-1 h-[100px]">
{carouselData.map((item, index) => (
<CarouselItem key={index} className="flex">
<DisplayFullImage
src={item.src}
alt={item.alt}
width={200}
height={100}
className="rounded-lg object-cover h-[100px] w-[200px]"
/>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div id="image-carousel" className="w-full">
<Gallery images={carouselData} />
</div>

<Card className="w-[80%] ml-10 shadow-sm">
Expand Down
88 changes: 88 additions & 0 deletions src/components/carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use client";
import { useEffect, useState, useMemo } from "react";
import { Carousel, CarouselContent, CarouselItem, type CarouselApi } from "./ui/carousel";
import Image from "next/image";

interface GalleryProps {
images: { src: string }[];
}

const Gallery = ({ images }: GalleryProps) => {
const [mainApi, setMainApi] = useState<CarouselApi>();
const [thumbnailApi, setThumbnailApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);

const mainImage = useMemo(
() =>
images.map((image, index) => (
<CarouselItem key={index} className="relative aspect-video w-full border-8 border-b">
<Image src={image.src} alt={`Carousel Main Image ${index + 1}`} fill style={{ objectFit: "contain" }} />
</CarouselItem>
)),
[images]
);

const thumbnailImages = useMemo(
() =>
images.map((image, index) => (
<CarouselItem key={index} className="relative aspect-square basis-1/4" onClick={() => handleClick(index)}>
<Image
className={`${index === current ? "border-2" : ""}`}
src={image.src}
fill
alt={`Carousel Thumbnail Image ${index + 1}`}
style={{ objectFit: "contain" }}
/>
</CarouselItem>
)),
[images, current]
);

useEffect(() => {
if (!mainApi || !thumbnailApi) {
return;
}

const handleTopSelect = () => {
const selected = mainApi.selectedScrollSnap();
setCurrent(selected);
thumbnailApi.scrollTo(selected);
};

const handleBottomSelect = () => {
const selected = thumbnailApi.selectedScrollSnap();
setCurrent(selected);
mainApi.scrollTo(selected);
};

mainApi.on("select", handleTopSelect);
thumbnailApi.on("select", handleBottomSelect);

return () => {
mainApi.off("select", handleTopSelect);
thumbnailApi.off("select", handleBottomSelect);
};
}, [mainApi, thumbnailApi]);

const handleClick = (index: number) => {
if (!mainApi || !thumbnailApi) {
return;
}
thumbnailApi.scrollTo(index);
mainApi.scrollTo(index);
setCurrent(index);
};

return (
<div className="w-full max-w-xl sm:w-auto">
<Carousel setApi={setMainApi}>
<CarouselContent className="m-1">{mainImage}</CarouselContent>
</Carousel>
<Carousel setApi={setThumbnailApi}>
<CarouselContent className="m-1 h-16">{thumbnailImages}</CarouselContent>
</Carousel>
</div>
);
};

export default Gallery;

0 comments on commit 493d280

Please sign in to comment.