آیتم
یک ردیف انعطافپذیر با اسلاتهای رسانه، عنوان، توضیحات و اقدام برای فیدهای اعلان، فهرستهای تنظیمات و ردیفهای فایل — به سبک نئوبروتالیست.
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
import { Button } from "@/components/ui/button"Item یک ردیف flex با اسلاتهای نامدار — media، content، actions — برای الگوی «فهرست چیزها» است که هر اپی به آن میرسد. primitiveِ headless زیرین ندارد؛ فقط عناصر استایلشده (بهعلاوهٔ useRender در Base UI برای propِ render)، با دستور پخت نئوبروتالیستی: حاشیهٔ 2px — در variantِ outline نمایان — و حلقهٔ فوکوس offset سخت.
برای این موارد از آن استفاده کنید:
- فید اعلان و فعالیت — آواتار، پیام، timestamp و دکمهٔ dismiss یا اقدام در هر ردیف.
- فهرست تنظیمات و یکپارچهسازی — آیکون، نام، خط وضعیت، با switch یا دکمه در لبهٔ trailing.
- مرورگر فایل و نتیجهٔ جستجو — thumbnail یا آیکون بههمراه metadata، کل ردیف لینک از طریق
render.
کامپوننت Item یک کانتینر flex ساده است که میتواند تقریباً هر نوع محتوایی را در خود جای دهد. از آن برای نمایش عنوان، توضیح و کنشها استفاده کنید. آن را با کامپوننت ItemGroup گروهبندی کنید تا فهرستی از آیتمها بسازید.
نصب#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/item.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/item.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/item.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/item.json
کد زیر را کپی کرده و در پروژهٔ خود جایگذاری کنید.
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@/lib/utils"
import { Separator } from "@/components/ui/separator"
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
role="list"
data-slot="item-group"
className={cn(
"group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2",
className
)}
{...props}
/>
)
}
function ItemSeparator({
className,
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot="item-separator"
orientation="horizontal"
className={cn("my-2", className)}
{...props}
/>
)
}
const itemVariants = cva(
"group/item flex w-full flex-wrap items-center rounded border-2 text-sm transition-colors duration-100 outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary [a]:transition-colors [a]:hover:bg-accent",
{
variants: {
variant: {
default: "border-transparent",
outline: "border-border",
muted: "border-transparent bg-muted/50",
},
size: {
default: "gap-2.5 px-3 py-2.5",
sm: "gap-2.5 px-3 py-2.5",
xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Item({
className,
variant = "default",
size = "default",
asChild = false,
...props
}: React.ComponentProps<"div"> &
VariantProps<typeof itemVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot.Root : "div"
return (
<Comp
data-slot="item"
data-variant={variant}
data-size={size}
className={cn(itemVariants({ variant, size, className }))}
{...props}
/>
)
}
const itemMediaVariants = cva(
"flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none",
{
variants: {
variant: {
default: "bg-transparent",
icon: "[&_svg:not([class*='size-'])]:size-4",
image:
"size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover",
},
},
defaultVariants: {
variant: "default",
},
}
)
function ItemMedia({
className,
variant = "default",
...props
}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
return (
<div
data-slot="item-media"
data-variant={variant}
className={cn(itemMediaVariants({ variant, className }))}
{...props}
/>
)
}
function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-content"
className={cn(
"flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none",
className
)}
{...props}
/>
)
}
function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-title"
className={cn(
"line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4",
className
)}
{...props}
/>
)
}
function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
data-slot="item-description"
className={cn(
"line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
className
)}
{...props}
/>
)
}
function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-actions"
className={cn("flex items-center gap-2", className)}
{...props}
/>
)
}
function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-header"
className={cn(
"flex basis-full items-center justify-between gap-2",
className
)}
{...props}
/>
)
}
function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-footer"
className={cn(
"flex basis-full items-center justify-between gap-2",
className
)}
{...props}
/>
)
}
export {
Item,
ItemMedia,
ItemContent,
ItemActions,
ItemGroup,
ItemSeparator,
ItemTitle,
ItemDescription,
ItemHeader,
ItemFooter,
}
مسیرهای import را مطابق با پیکربندی پروژهٔ خود بهروزرسانی کنید.
استفاده#
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"<Item>
<ItemMedia variant="icon">
<Icon />
</ItemMedia>
<ItemContent>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</ItemContent>
<ItemActions>
<Button>Action</Button>
</ItemActions>
</Item>ترکیب#
برای ساخت یک Item، از ترکیب زیر استفاده کنید:
ItemGroup
└── Item
├── ItemHeader
├── ItemMedia
├── ItemContent
│ ├── ItemTitle
│ └── ItemDescription
├── ItemActions
└── ItemFooterItem vs Field#
اگر نیاز دارید یک ورودی فرم مانند چکباکس، ورودی، رادیو یا select نمایش دهید، از Field استفاده کنید.
اگر فقط نیاز دارید محتوایی مانند عنوان، توضیح و کنشها نمایش دهید، از Item استفاده کنید.
Variant#
برای تغییر استایل بصری آیتم، از ویژگی variant استفاده کنید.
import { InboxIcon } from "lucide-react"
import {Size#
برای تغییر اندازهٔ آیتم، از ویژگی size استفاده کنید. اندازههای در دسترس عبارتاند از default، sm و xs.
import { InboxIcon } from "lucide-react"
import {نمونهها#
آیکون#
برای نمایش یک آیکون، از ItemMedia با variant="icon" استفاده کنید.
import { ShieldAlertIcon } from "lucide-react"
import { Button } from "@/components/ui/button"Avatar#
میتوانید برای نمایش یک آواتار، از ItemMedia با variant="avatar" استفاده کنید.
import { Plus } from "lucide-react"
import {تصویر#
برای نمایش یک تصویر، از ItemMedia با variant="image" استفاده کنید.
import Image from "next/image"
import {گروه#
برای گروهبندی آیتمهای مرتبط، از ItemGroup استفاده کنید.
import * as React from "react"
import { PlusIcon } from "lucide-react"
سرصفحه#
برای افزودن یک سرصفحه بالای محتوای آیتم، از ItemHeader استفاده کنید.
import Image from "next/image"
import {پیوند#
برای رندر آیتم بهصورت یک پیوند، از ویژگی render استفاده کنید. حالتهای hover و focus به عنصر anchor اعمال میشوند.
import { ChevronRightIcon, ExternalLinkIcon } from "lucide-react"
import {<Item render={<a href="/dashboard" />}>
<ItemMedia variant="icon">
<HomeIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Dashboard</ItemTitle>
<ItemDescription>Overview of your account and activity.</ItemDescription>
</ItemContent>
</Item>Dropdown#
"use client"
import { ChevronDownIcon } from "lucide-react"RTL#
برای فعالسازی پشتیبانی RTL در Neobrutalism، راهنمای پیکربندی RTL را ببینید.
"use client"
import * as React from "react"دسترسیپذیری#
Item presentational است — عناصر استایلشده بدون سیمکشی ARIA خودشان، هرچند ItemGroup مقدار role="list" رندر میکند (اگر ساختار فهرست باید اعلام شود به هر Item مقدار role="listitem" بدهید). برای ردیفهای تعاملی، handler کلیک را روی div نگذارید — از طریق propِ render یک <a> یا <button> واقعی رندر کنید تا فوکوس و رفتار صفحهکلید بومی بگیرد؛ حلقهٔ فوکوس قابلمشاهده از قبل استایل شده. media آیکون تزئینی را aria-hidden کنید و به media تصویری alt معنادار بدهید.
مرجع API#
Item#
کامپوننت اصلی برای نمایش محتوا همراه با رسانه، عنوان، توضیح و کنشها.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "outline" | "muted" | "default" |
size | "default" | "sm" | "xs" | "default" |
render | React.ReactElement |
ItemGroup#
کانتینری که آیتمهای مرتبط را با استایل یکسان گروهبندی میکند.
<ItemGroup>
<Item />
<Item />
</ItemGroup>ItemSeparator#
یک جداکننده میان آیتمها در یک گروه.
<ItemGroup>
<Item />
<ItemSeparator />
<Item />
</ItemGroup>ItemMedia#
برای نمایش محتوای رسانهای مانند آیکون، تصویر یا آواتار، از ItemMedia استفاده کنید.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "icon" | "image" | "default" |
<ItemMedia variant="icon">
<Icon />
</ItemMedia><ItemMedia variant="image">
<img src="..." alt="..." />
</ItemMedia>ItemContent#
عنوان و توضیح آیتم را در بر میگیرد.
<ItemContent>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</ItemContent>ItemTitle#
عنوان آیتم را نمایش میدهد.
<ItemTitle>Item Title</ItemTitle>ItemDescription#
توضیح آیتم را نمایش میدهد.
<ItemDescription>Item description</ItemDescription>ItemActions#
کانتینری برای دکمههای کنش یا دیگر عناصر تعاملی.
<ItemActions>
<Button>Action</Button>
</ItemActions>ItemHeader#
یک سرصفحه بالای محتوای آیتم نمایش میدهد.
<Item>
<ItemHeader>Header</ItemHeader>
<ItemContent>...</ItemContent>
</Item>ItemFooter#
یک پاصفحه پایین محتوای آیتم نمایش میدهد.
<Item>
<ItemContent>...</ItemContent>
<ItemFooter>Footer</ItemFooter>
</Item>