بلاک‌ها · پنل مدیریت

کتابخانهٔ رسانه

پیش‌نمایش تمام‌صفحه

گریدِ عکس‌ها با انتخابِ چندتایی، حاشیهٔ برند برای انتخاب‌شده‌ها و نوارِ اقدامِ گروهی (دانلود، حذف).

۱. نصب کامپوننت‌ها

این بلاک از ۶ آیتم دیگ استفاده می‌کند: buttoncheckboxcontrol-sizeiconspersiantoast

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.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/toast.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.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/toast.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.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/toast.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/checkbox.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/toast.json

۲. کد صفحه

کد را در app/<route>/page.tsx بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

files-gallery.tsx
"use client"

import * as React from "react"

import { ArrowUpIcon, CheckIcon, DownloadIcon, TrashIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { toast } from "@/components/ui/toast"
import { toFaDigits } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"

const MEDIA: { id: string; image: StockImageName; name: string }[] = [
  { id: "1", image: "productVase", name: "گلدان-سفید.webp" },
  { id: "2", image: "studioSunlit", name: "استودیو.webp" },
  { id: "3", image: "coverMinimalRoom", name: "اتاق.webp" },
  { id: "4", image: "coverNotebook", name: "دفتر.webp" },
  { id: "5", image: "coverCalculator", name: "ماشین‌حساب.webp" },
  { id: "6", image: "coverOfficeSupplies", name: "لوازم.webp" },
  { id: "7", image: "projectCafeBrand", name: "کافه.webp" },
  { id: "8", image: "coverWorkspace", name: "میز-کار.webp" },
]

/**
 * کتابخانهٔ رسانه: گریدِ عکس‌ها با انتخابِ چندتایی (چک‌باکس روی هر عکس و حاشیهٔ برند برای
 * انتخاب‌شده‌ها)، و نوارِ اقدام که با انتخاب ظاهر می‌شود: دانلود و حذفِ گروهی.
 */
export default function FilesGalleryBlock() {
  const [media, setMedia] = React.useState(MEDIA)
  const [selected, setSelected] = React.useState<string[]>(["2"])

  // حذفِ گروهی برگشت‌پذیر است: اسنک‌بارِ «برگردان» به‌جای دیالوگِ تأیید.
  function removeSelected() {
    const before = media
    setMedia((list) => list.filter((item) => !selected.includes(item.id)))
    toast(`${toFaDigits(selected.length)} عکس حذف شد.`, { action: { label: "برگردان", onClick: () => setMedia(before) } })
    setSelected([])
  }
  const toggle = (id: string) => setSelected((list) => (list.includes(id) ? list.filter((item) => item !== id) : [...list, id]))

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-5xl 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(media.length)} عکس · ۱۸ مگابایت</p>
          </div>
          <Button>
            <ArrowUpIcon />
            بارگذاری
          </Button>
        </header>
        <div className={`flex min-h-14 flex-wrap items-center justify-between gap-3 rounded-xl px-4 py-2 ${selected.length ? "bg-primary-subtle" : "bg-subtle"}`} aria-live="polite">
          <span className="text-sm">{selected.length ? `${toFaDigits(selected.length)} مورد انتخاب شده` : "برای کارِ گروهی، عکس‌ها را انتخاب کن."}</span>
          {selected.length > 0 && (
            <div className="flex gap-2">
              <Button variant="outline" className="bg-card" onClick={() => toast.success("دانلود شروع شد.")}>
                <DownloadIcon />
                دانلود
              </Button>
              <Button variant="outline" color="destructive" className="bg-card" onClick={removeSelected}>
                <TrashIcon />
                حذف
              </Button>
            </div>
          )}
        </div>
        <ul className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
          {media.map((item) => {
            const checked = selected.includes(item.id)
            return (
              <li key={item.id} className={`group relative overflow-hidden rounded-xl border-2 ${checked ? "border-primary" : "border-card"}`}>
                {/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
                <img src={stockImages[item.image].src} alt={stockImages[item.image].alt} loading="lazy" className="aspect-square w-full object-cover" />
                <div className="absolute start-2 top-2 flex size-9 items-center justify-center rounded-lg bg-card shadow-control">
                  <Checkbox id={`fg-${item.id}`} checked={checked} onCheckedChange={() => toggle(item.id)} aria-label={`انتخابِ ${item.name}`} />
                </div>
                {checked && (
                  <span aria-hidden className="absolute end-2 top-2 flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground [&>svg]:size-3.5">
                    <CheckIcon />
                  </span>
                )}
                <span className="absolute inset-x-0 bottom-0 truncate bg-linear-to-t from-[var(--dig-graphite-950)] to-transparent px-3 pt-6 pb-2 text-xs text-[var(--dig-white)]">{item.name}</span>
              </li>
            )
          })}
        </ul>
      </main>
    </ControlSizeProvider>
  )
}