فهرستِ ردیفیِ محصول
پیشنمایش تمامصفحهفهرستِ ردیفی برای مقایسهٔ سریع: عکس، مشخصاتِ کوتاه، امتیاز با تعدادِ نظر، ارسالِ سریع، قیمت و سبد؛ فیلترهای فعال بهصورتِ دکمهٔ حذفشدنی.
۱. نصب کامپوننتها
این بلاک از ۶ آیتم دیگ استفاده میکند: add-to-cartbuttoncontrol-sizeiconspersiantoggle-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/add-to-cart.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/add-to-cart.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/add-to-cart.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/add-to-cart.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/toggle-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
product-list-rows.tsx
"use client"
import * as React from "react"
import { StarIcon, TruckIcon, XIcon } from "@/components/icons"
import { AddToCart } from "@/components/ui/add-to-cart"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatCurrency, toFaDigits } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"
const PRODUCTS: { id: string; title: string; image: StockImageName; price: number; rating: number; reviews: number; specs: string; fast?: boolean; brand: string }[] = [
{ id: "1", title: "گلدانِ سرامیکی دوقلو", image: "productVase", price: 1_290_000, rating: 4.8, reviews: 212, specs: "سرامیک · ۳۲ سانتیمتر · دستساز", fast: true, brand: "خانهٔ سفال" },
{ id: "2", title: "ظرفِ سفالی لعابمات", image: "coverMinimalRoom", price: 890_000, rating: 4.5, reviews: 96, specs: "سفال · ۲۴ سانتیمتر", brand: "خانهٔ سفال" },
{ id: "3", title: "ست فنجانِ دستساز", image: "studioSunlit", price: 1_640_000, rating: 4.7, reviews: 141, specs: "شش عدد · لعابِ براق", fast: true, brand: "کارگاهِ میبد" },
{ id: "4", title: "جاشمعی سنگی", image: "coverNotebook", price: 420_000, rating: 4.2, reviews: 38, specs: "سنگِ مصنوعی · ۱۲ سانتیمتر", brand: "کارگاهِ میبد" },
]
const SORTS = [
{ value: "popular", label: "محبوب" },
{ value: "cheap", label: "ارزان" },
{ value: "rating", label: "امتیاز" },
]
/**
* فهرستِ ردیفی محصول (برای مقایسهٔ سریع): هر ردیف عکس، مشخصاتِ کوتاه، امتیاز با تعدادِ نظر،
* نشانِ ارسالِ سریع و قیمت با دکمهٔ سبد؛ فیلترهای فعال بهصورتِ چیپِ حذفشدنی بالای فهرست.
*/
export default function ProductListRowsBlock() {
const [sort, setSort] = React.useState("popular")
const [filters, setFilters] = React.useState(["ارسالِ سریع", "خانهٔ سفال"])
const visible = PRODUCTS.filter((product) => (!filters.includes("ارسالِ سریع") || product.fast) && (!filters.includes("خانهٔ سفال") || product.brand === "خانهٔ سفال")).sort((a, b) =>
sort === "cheap" ? a.price - b.price : sort === "rating" ? b.rating - a.rating : b.reviews - a.reviews
)
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto flex w-full max-w-4xl flex-col gap-6 p-4 sm:p-6">
<header className="flex flex-wrap items-end justify-between gap-x-4 gap-y-6">
<div className="flex flex-col gap-1">
<h1 className="text-2xl font-bold">سفال و سرامیک</h1>
<p className="text-muted-foreground">{toFaDigits(visible.length)} کالا</p>
</div>
<ToggleGroup type="single" variant="segmented" value={sort} onValueChange={(value) => value && setSort(value as string)} aria-label="مرتبسازی">
{SORTS.map((item) => (
<ToggleGroupItem key={item.value} value={item.value}>
{item.label}
</ToggleGroupItem>
))}
</ToggleGroup>
</header>
{filters.length > 0 && (
<div className="flex flex-wrap items-center gap-2" aria-label="فیلترهای فعال">
{filters.map((filter) => (
<Button key={filter} variant="outline" onClick={() => setFilters((list) => list.filter((item) => item !== filter))} aria-label={`برداشتنِ فیلترِ ${filter}`}>
{filter}
<XIcon />
</Button>
))}
<Button variant="ghost" onClick={() => setFilters([])}>
پاک کردنِ همه
</Button>
</div>
)}
<ul className="flex flex-col divide-y divide-border">
{visible.map((product) => (
<li key={product.id} className="grid grid-cols-[6rem_minmax(0,1fr)] gap-4 py-5 sm:grid-cols-[8rem_minmax(0,1fr)_auto] sm:items-center">
{/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
<img src={stockImages[product.image].src} alt={stockImages[product.image].alt} loading="lazy" className="aspect-square w-full rounded-lg object-cover" />
<div className="flex flex-col gap-1.5">
<span className="text-xs text-muted-foreground">{product.brand}</span>
<a href="#" className="font-bold hover:underline">
{product.title}
</a>
<span className="text-sm text-muted-foreground">{product.specs}</span>
<span className="flex flex-wrap items-center gap-3 text-sm">
<span className="flex items-center gap-1">
<StarIcon aria-hidden className="size-4 fill-[var(--dig-orange-400)] text-[var(--dig-orange-400)]" />
{toFaDigits(String(product.rating).replace(".", "٫"))}
<span className="text-muted-foreground">({toFaDigits(product.reviews)})</span>
</span>
{product.fast && (
<span className="flex items-center gap-1 text-success">
<TruckIcon aria-hidden className="size-4" />
ارسالِ فردا
</span>
)}
</span>
</div>
<div className="col-span-2 flex items-center justify-between gap-3 sm:col-span-1 sm:flex-col sm:items-end">
<span className="text-lg font-black tabular-nums">{formatCurrency(product.price)}</span>
<AddToCart variant="default" label="افزودن" itemName={product.title} />
</div>
</li>
))}
{visible.length === 0 && <li className="py-10 text-center text-muted-foreground">با این فیلترها کالایی نیست؛ یکی را بردار.</li>}
</ul>
</main>
</ControlSizeProvider>
)
}