بلاک‌ها · بخش‌های صفحه

تیم، قرص‌های اسم

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

بخشِ تیم روی سطحِ تیره: هر نفر قرصِ کوچکِ عکس و اسم است و با انتخاب به کارتِ کامل باز می‌شود (عکس، نقش، ایمیل، شهر، معرفی و دکمهٔ پیام).

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

این بلاک از ۴ آیتم دیگ استفاده می‌کند: buttonchipcontrol-sizeicons

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/chip.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/chip.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/chip.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/chip.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/icons.json

۲. کد صفحه

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

team-pills.tsx
"use client";

import * as React from "react";

import { EnvelopeIcon, GlobeIcon } from "@/components/icons";
import { Button } from "@/components/ui/button";
import { Chip } from "@/components/ui/chip";
import { ControlSizeProvider } from "@/components/ui/control-size";
import { cn } from "@/lib/utils";
import { stockImages, type StockImageName } from "@/lib/stock-images";

const PEOPLE: {
  name: string;
  role: string;
  team: string;
  email: string;
  city: string;
  bio: string;
  photo: StockImageName;
}[] = [
  {
    name: "مهدی افشار",
    role: "طراحِ ارشدِ تعامل",
    team: "تیمِ محصول",
    email: "mahdi@avand.ir",
    city: "تهران",
    bio: "حسِ حرکت در محصول را شکل می‌دهد؛ از ریزانیمیشن تا اولین تجربهٔ کاربر.",
    photo: "personMan1",
  },
  {
    name: "سارا نیک‌پور",
    role: "مدیرِ محصول",
    team: "تیمِ محصول",
    email: "sara@avand.ir",
    city: "اصفهان",
    bio: "هر هفته با ده مشتری حرف می‌زند و نقشهٔ راه را از همان گفت‌وگوها می‌سازد.",
    photo: "personWoman1",
  },
  {
    name: "نگار رحیمی",
    role: "مهندسِ زیرساخت",
    team: "تیمِ پلتفرم",
    email: "negar@avand.ir",
    city: "تبریز",
    bio: "مسئولِ اینکه سرویس ساعتِ سه صبح هم بیدار باشد و سریع جواب بدهد.",
    photo: "personWoman3",
  },
  {
    name: "آرش کریمی",
    role: "سرپرستِ پشتیبانی",
    team: "تیمِ مشتری",
    email: "arash@avand.ir",
    city: "مشهد",
    bio: "میانگینِ پاسخِ تیمش دوازده دقیقه است و هنوز راضی نیست.",
    photo: "personMan2",
  },
];

/**
 * تیم به‌شکلِ قرص‌های اسم: هر نفر یک قرصِ کوچک با عکس و اسم است و با انتخاب، همان قرص
 * نرم به کارتِ کامل باز می‌شود (عکسِ بزرگ، نقش، ایمیل، شهر، معرفی و دکمهٔ پیام)؛ بقیه
 * قرص می‌مانند.
 */
export default function TeamPillsBlock() {
  const [active, setActive] = React.useState(0);

  return (
    <ControlSizeProvider size="xl" asChild>
      <section
        aria-labelledby="tp-title"
        className="w-full bg-inverse py-24 text-[var(--dig-white)] [--muted-foreground:var(--dig-neutral-400)]"
      >
        <div className="mx-auto flex w-full max-w-6xl flex-col gap-12 px-4 sm:px-6">
          <div className="flex max-w-2xl flex-col gap-3">
            <span className="text-sm font-semibold text-[var(--dig-violet-300)]">
              آدم‌های آوند
            </span>
            <h2
              id="tp-title"
              className="text-3xl leading-snug font-black text-balance sm:text-4xl"
            >
              با کسی حرف بزنید که آن را ساخته
            </h2>
          </div>
          <ul className="flex flex-wrap items-center gap-3">
            {PEOPLE.map((person, index) => {
              const open = index === active;
              return (
                <li
                  key={person.name}
                  className={cn(
                    "overflow-hidden bg-[var(--dig-white)] text-[var(--dig-graphite-950)] shadow-lg transition-[border-radius,max-width,max-height] duration-500 ease-out",
                    open
                      ? "max-h-[40rem] w-full max-w-xl rounded-xl"
                      : "max-h-14 max-w-60 rounded-full",
                  )}
                >
                  {open ? (
                    <article className="animate-in fade-in grid grid-cols-1 gap-5 p-3 duration-500 sm:grid-cols-[10rem_1fr]">
                      {/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
                      <img
                        src={stockImages[person.photo].src}
                        alt=""
                        className="aspect-square w-full rounded-lg object-cover sm:aspect-[4/5]"
                      />
                      <div className="flex flex-col items-start gap-3 py-1 pe-2">
                        <div className="flex flex-col gap-0.5">
                          <h3 className="text-xl font-bold">{person.name}</h3>
                          <span className="text-sm text-[var(--dig-neutral-600)]">
                            {person.role}، {person.team}
                          </span>
                        </div>
                        <ul className="flex flex-col gap-1.5 text-sm">
                          <li className="flex items-center gap-2">
                            <EnvelopeIcon
                              aria-hidden
                              className="size-4 text-[var(--dig-neutral-500)]"
                            />
                            <bdi dir="ltr">{person.email}</bdi>
                          </li>
                          <li className="flex items-center gap-2">
                            <GlobeIcon
                              aria-hidden
                              className="size-4 text-[var(--dig-neutral-500)]"
                            />
                            {person.city}
                          </li>
                        </ul>
                        <p className="leading-7 text-[var(--dig-neutral-700)]">
                          {person.bio}
                        </p>
                        <Button
                          asChild
                          className="mt-auto [--primary-foreground:var(--dig-white)] [--primary:var(--dig-graphite-950)]"
                        >
                          <a href={`mailto:${person.email}`}>سلام کنید</a>
                        </Button>
                      </div>
                    </article>
                  ) : (
                    <Chip asChild variant="secondary">
                      <button
                        type="button"
                        onClick={() => setActive(index)}
                        aria-label={`${person.name}، ${person.role}`}
                        className="flex h-14 w-auto items-center gap-3 rounded-full border-0 bg-transparent p-2 pe-5 text-base font-semibold text-[var(--dig-graphite-950)] outline-none transition-colors hover:bg-[var(--dig-neutral-100)] focus-visible:ring-4 focus-visible:ring-[var(--dig-violet-300)]"
                      >
                        {/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
                        <img
                          src={stockImages[person.photo].src}
                          alt=""
                          className="size-10 rounded-full object-cover"
                        />
                        <span className="truncate">{person.name}</span>
                      </button>
                    </Chip>
                  )}
                </li>
              );
            })}
          </ul>
        </div>
      </section>
    </ControlSizeProvider>
  );
}