گالریِ اسلایدی
پیشنمایش تمامصفحهبخشِ گالری با اسلایدهای پهن و توضیحِ هر پروژه، دکمههای قبلی/بعدی کنارِ تیتر، اسلایدِ بعدی از لبه پیدا و نوارِ پیشرفت؛ عکسهای غیرفعال سیاهوسفید.
۱. نصب کامپوننتها
این بلاک از ۳ آیتم دیگ استفاده میکند: carouselcontrol-sizepersian
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/carousel.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/persian.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/carousel.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/persian.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/carousel.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/persian.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/carousel.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/persian.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
gallery-carousel.tsx
"use client"
import * as React from "react"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, type CarouselApi } from "@/components/ui/carousel"
import { toFaDigits } from "@/lib/persian"
import { cn } from "@/lib/utils"
import { stockImages, type StockImageName } from "@/lib/stock-images"
const SLIDES: { photo: StockImageName; title: string; caption: string }[] = [
{ photo: "studioSunlit", title: "استودیوی نور", caption: "بازسازیِ یک انبارِ قدیمی به فضای کارِ اشتراکی، ۱۴۰۴" },
{ photo: "restaurant", title: "رستورانِ حیاط", caption: "طراحیِ داخلی و نورپردازیِ سالنِ ۸۰ نفره" },
{ photo: "coverMinimalRoom", title: "خانهٔ سپید", caption: "آپارتمانِ ۹۰ متری با مبلمانِ سفارشی" },
{ photo: "clinicRoom", title: "کلینیکِ مهر", caption: "فضای انتظارِ آرام با نورِ طبیعی" },
{ photo: "coverWorkspace", title: "دفترِ آوند", caption: "دفترِ ۴۰ نفره با اتاقهای تمرکز" },
]
/**
* گالریِ اسلایدی: عکسهای پهن با اسم و توضیحِ پروژه، دکمههای قبلی/بعدی کنارِ تیتر،
* نوارِ پیشرفتِ اسلایدها و شمارهٔ اسلاید؛ اسلایدِ بعدی از لبه پیداست تا کشیدن را القا کند.
*/
export default function GalleryCarouselBlock() {
const [api, setApi] = React.useState<CarouselApi>()
const [index, setIndex] = React.useState(0)
React.useEffect(() => {
if (!api) return
const update = () => setIndex(api.selectedScrollSnap())
update()
api.on("select", update)
return () => {
api.off("select", update)
}
}, [api])
return (
<ControlSizeProvider size="xl" asChild>
<section aria-labelledby="gc-title" className="flex w-full flex-col gap-10 overflow-hidden bg-subtle py-24">
<Carousel setApi={setApi} opts={{ align: "start" }} className="mx-auto flex w-full max-w-6xl flex-col gap-10 px-4 sm:px-6">
<div className="flex items-end justify-between gap-6">
<div className="flex flex-col gap-3">
<span className="text-sm font-semibold text-brand-secondary">نمونهکار</span>
<h2 id="gc-title" className="text-3xl font-black sm:text-4xl">
فضاهایی که ساختیم
</h2>
</div>
<div className="flex shrink-0 gap-2">
<CarouselPrevious className="static size-11 translate-y-0" />
<CarouselNext className="static size-11 translate-y-0" />
</div>
</div>
<CarouselContent className="[&>*]:basis-[88%] sm:[&>*]:basis-[70%] lg:[&>*]:basis-[58%]">
{SLIDES.map((slide, slideIndex) => (
<CarouselItem key={slide.title}>
<figure className="flex flex-col gap-4">
{/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
<img
src={stockImages[slide.photo].src}
alt={stockImages[slide.photo].alt}
className={cn("aspect-[16/10] w-full rounded-xl object-cover transition-[filter] duration-500", slideIndex !== index && "grayscale")}
/>
<figcaption className="flex items-baseline justify-between gap-4">
<span className="flex flex-col gap-1">
<span className="text-lg font-bold">{slide.title}</span>
<span className="text-sm text-muted-foreground">{slide.caption}</span>
</span>
<span className="text-sm text-muted-foreground tabular-nums">{toFaDigits(String(slideIndex + 1).padStart(2, "0"))}</span>
</figcaption>
</figure>
</CarouselItem>
))}
</CarouselContent>
<div aria-hidden className="flex gap-1.5">
{SLIDES.map((slide, slideIndex) => (
<span key={slide.title} className={cn("h-1 rounded-full transition-all duration-300", slideIndex === index ? "w-10 bg-foreground" : "w-4 bg-border")} />
))}
</div>
</Carousel>
</section>
</ControlSizeProvider>
)
}