پرش به محتوا

آیتم

یک ردیف انعطاف‌پذیر با اسلات‌های رسانه، عنوان، توضیحات و اقدام برای فیدهای اعلان، فهرست‌های تنظیمات و ردیف‌های فایل — به سبک نئوبروتالیست.

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.json
npx shadcn@latest add https://neobrutalism.com/r/base/item.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/item.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/item.json

کد زیر را کپی کرده و در پروژهٔ خود جای‌گذاری کنید.

components/ui/item.tsx
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
    └── ItemFooter

Item 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>
"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

کامپوننت اصلی برای نمایش محتوا همراه با رسانه، عنوان، توضیح و کنش‌ها.

PropTypeDefault
variant"default" | "outline" | "muted""default"
size"default" | "sm" | "xs""default"
renderReact.ReactElement

ItemGroup

کانتینری که آیتم‌های مرتبط را با استایل یکسان گروه‌بندی می‌کند.

<ItemGroup>
  <Item />
  <Item />
</ItemGroup>

ItemSeparator

یک جداکننده میان آیتم‌ها در یک گروه.

<ItemGroup>
  <Item />
  <ItemSeparator />
  <Item />
</ItemGroup>

ItemMedia

برای نمایش محتوای رسانه‌ای مانند آیکون، تصویر یا آواتار، از ItemMedia استفاده کنید.

PropTypeDefault
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>