تیم، قرصهای اسم
پیشنمایش تمامصفحهبخشِ تیم روی سطحِ تیره: هر نفر قرصِ کوچکِ عکس و اسم است و با انتخاب به کارتِ کامل باز میشود (عکس، نقش، ایمیل، شهر، معرفی و دکمهٔ پیام).
۱. نصب کامپوننتها
این بلاک از ۴ آیتم دیگ استفاده میکند: 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.jsonpnpm 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.jsonyarn 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.jsonbunx --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>
);
}