mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-18 21:44:06 +01:00
feat: implement image gallery component for project deal page
This commit is contained in:
parent
7e4a237075
commit
493d2805e6
@ -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;
|
||||
|
||||
@ -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" }];
|
||||
@ -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">
|
||||
|
||||
88
src/components/carousel.tsx
Normal file
88
src/components/carousel.tsx
Normal file
@ -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;
|
||||
Loading…
Reference in New Issue
Block a user