پرتوپرتو

جدول داده (DataTable)

جدول داده ترکیبی با مرتب‌سازی، صفحه‌بندی، انتخاب ردیف، و حالت‌های بارگذاری و خالی

معرفی

DataTable یک کامپوننت ترکیبی است که جدول، مرتب‌سازی، صفحه‌بندی، انتخاب ردیف، و مدیریت حالت‌های خالی و بارگذاری را در یک API ساده ارائه می‌دهد. این کامپوننت رایج‌ترین الگوی نمایش داده در اپلیکیشن‌های SaaS را پوشش می‌دهد.

چه زمانی استفاده کنیم:

  • لیست داده‌ها با بیش از ۵ آیتم که نیاز به مرتب‌سازی یا صفحه‌بندی دارند
  • جداول با قابلیت انتخاب برای عملیات دسته‌ای
  • هر صفحه لیست در اپلیکیشن (اینفلوئنسرها، کمپین‌ها، گزارش‌ها)

چه زمانی استفاده نکنیم:

  • نمایش ساده ۳-۵ آیتم بدون تعامل — از Table مستقیم استفاده کنید
  • جداول با ساختار بسیار سفارشی — از کامپوننت‌های Table به صورت ترکیبی استفاده کنید
  • نمایش کارتی — از Card استفاده کنید
نامدنبال‌کنندگاننرخ تعامل
علی محمدی۱۲٬۵۰۰۴.۲٪
سارا احمدی۸۷٬۳۰۰۳.۱٪
رضا کریمی۲۳۴٬۰۰۰۲.۸٪
مریم حسینی۵٬۲۰۰۶.۷٪
امیر رضایی۴۵٬۸۰۰۳.۵٪
import { DataTable } from '@parto-system-design/ui'

زمین بازی

با تغییر تنظیمات زیر، پیش‌نمایش زنده را مشاهده کنید.

زمین بازی
احساس
پست الفاینستاگرام۱۲٬۳۴۵
۷۸٪
پست بتوییتر۸٬۹۰۰
۶۵٪
پست جتلگرام۵٬۴۰۰
۴۲٪
پست داینستاگرام۴٬۲۰۰
۸۸٪
پست هتوییتر۳٬۱۰۰
۳۵٪
تنظیمات
ظاهر
داده
5
چیدمان
حالت
محتوا
کد این نمونه به‌صورت خودکار قابل تولید نیست — برای کد آماده‌ی copy/paste به بخش «استفاده» در بالای صفحه مراجعه کنید.

استفاده پایه

import { DataTable, type DataTableColumn } from '@parto-system-design/ui'

interface Influencer {
  id: number
  name: string
  followers: number
  engagementRate: number
}

const columns: DataTableColumn<Influencer>[] = [
  { id: 'name', header: 'نام', cell: (row) => row.name, sortable: true },
  {
    id: 'followers',
    header: 'فالوورها',
    cell: (row) => row.followers.toLocaleString('fa-IR'),
    sortable: true,
    align: 'end',
  },
  {
    id: 'engagementRate',
    header: 'نرخ تعامل',
    cell: (row) => `${row.engagementRate}٪`,
    sortable: true,
    align: 'end',
  },
]

const data: Influencer[] = [
  { id: 1, name: 'محمد رضایی', followers: 125000, engagementRate: 4.2 },
  { id: 2, name: 'سارا احمدی', followers: 89000, engagementRate: 6.8 },
  { id: 3, name: 'علی محمدی', followers: 350000, engagementRate: 2.1 },
]

<DataTable columns={columns} data={data} />

با مرتب‌سازی

'use client'

import { useState } from 'react'
import { DataTable, type DataTableColumn, type SortDirection } from '@parto-system-design/ui'

function SortableExample() {
  const [sortColumn, setSortColumn] = useState<string | null>(null)
  const [sortDirection, setSortDirection] = useState<SortDirection | null>(null)

  return (
    <DataTable
      columns={columns}
      data={data}
      sort={{
        column: sortColumn,
        direction: sortDirection,
        onSort: (col, dir) => {
          setSortColumn(col)
          setSortDirection(dir)
        },
      }}
    />
  )
}

با صفحه‌بندی

<DataTable
  columns={columns}
  data={pageData}
  pagination={{
    currentPage: 2,
    totalPages: 10,
    onPageChange: (page) => setCurrentPage(page),
  }}
/>

با انتخاب ردیف

'use client'

import { useState } from 'react'

function SelectableExample() {
  const [selected, setSelected] = useState<Set<number>>(new Set())

  return (
    <DataTable
      columns={columns}
      data={data}
      selection={{
        selectedRows: selected,
        onSelectionChange: setSelected,
        getRowKey: (row) => row.id,
      }}
    />
  )
}

حالت بارگذاری

<DataTable columns={columns} data={[]} isLoading loadingRows={5} />

حالت خالی

import { Empty, EmptyIcon, EmptyTitle, EmptyDescription, Button } from '@parto-system-design/ui'
import { SearchX } from 'lucide-react'
;<DataTable
  columns={columns}
  data={[]}
  emptyState={
    <Empty className="border-0">
      <EmptyIcon>
        <SearchX className="size-6" />
      </EmptyIcon>
      <EmptyTitle>اینفلوئنسری یافت نشد</EmptyTitle>
      <EmptyDescription>فیلترهای خود را تغییر دهید یا اینفلوئنسر جدید اضافه کنید</EmptyDescription>
    </Empty>
  }
/>

ترکیب کامل

'use client'

import { useState } from 'react'
import {
  DataTable,
  type DataTableColumn,
  type SortDirection,
  Avatar,
  Badge,
  SocialPlatformBadge,
} from '@parto-system-design/ui'

function FullExample() {
  const [page, setPage] = useState(1)
  const [sortCol, setSortCol] = useState<string | null>(null)
  const [sortDir, setSortDir] = useState<SortDirection | null>(null)
  const [selected, setSelected] = useState<Set<number>>(new Set())

  const columns: DataTableColumn<Influencer>[] = [
    {
      id: 'name',
      header: 'نام',
      sortable: true,
      cell: (row) => (
        <div className="flex items-center gap-2">
          <Avatar src={row.avatar} fallback={row.name[0]} size="sm" />
          <span className="font-medium">{row.name}</span>
        </div>
      ),
    },
    {
      id: 'platform',
      header: 'پلتفرم',
      cell: (row) => <SocialPlatformBadge platform={row.platform} size="sm" />,
    },
    {
      id: 'followers',
      header: 'فالوورها',
      sortable: true,
      align: 'end',
      cell: (row) => row.followers.toLocaleString('fa-IR'),
    },
    {
      id: 'status',
      header: 'وضعیت',
      cell: (row) => (
        <Badge variant={row.active ? 'success' : 'default'} size="sm">
          {row.active ? 'فعال' : 'غیرفعال'}
        </Badge>
      ),
    },
  ]

  return (
    <DataTable
      columns={columns}
      data={influencers}
      size="sm"
      striped
      sort={{
        column: sortCol,
        direction: sortDir,
        onSort: (col, dir) => {
          setSortCol(col)
          setSortDir(dir)
        },
      }}
      selection={{
        selectedRows: selected,
        onSelectionChange: setSelected,
        getRowKey: (row) => row.id,
      }}
      pagination={{
        currentPage: page,
        totalPages: 12,
        onPageChange: setPage,
      }}
      resultCount="۱۴۲ اینفلوئنسر"
    />
  )
}

جدول ویژگی‌ها

DataTable

Prop

Type

DataTableColumn

ویژگینوعتوضیح
idstringکلید یکتای ستون
headerReactNodeعنوان ستون
cell(row, index) => ReactNodeتابع رندر سلول
sortablebooleanقابلیت مرتب‌سازی
align"start" | "center" | "end"تراز ستون
classNamestringکلاس اضافی

راهنمای استفاده

بکنید

  • از DataTable برای لیست داده‌ها با بیش از ۵ آیتم که نیاز به مرتب‌سازی یا صفحه‌بندی دارند استفاده کنید - همیشه emptyState سفارشی با پیام فارسی مناسب ارائه دهید - برای جداول با انتخاب ردیف، getRowKey یکتا تعریف کنید

نکنید

  • برای نمایش ساده ۳-۵ آیتم بدون تعامل از DataTable استفاده نکنید — از Table استفاده کنید - ستون‌های غیرضروری اضافه نکنید — اطلاعات باید قابل اسکن باشند - برای نمایش کارتی از جدول استفاده نکنید — از Card در grid layout استفاده کنید

دسترسی‌پذیری

  • ستون‌های قابل مرتب‌سازی از aria-sort استفاده می‌کنند
  • چک‌باکس‌ها دارای aria-label فارسی هستند
  • حالت بارگذاری از اسکلتون بصری استفاده می‌کند
  • ساختار جدول از تگ‌های معنایی HTML (table, thead, tbody, th, td) پیروی می‌کند

تعامل با کیبورد

  • Tab: حرکت بین عناصر تعاملی (چک‌باکس‌ها، دکمه‌های مرتب‌سازی، صفحه‌بندی) - Space: انتخاب ردیف (چک‌باکس) - Enter: فعال‌سازی مرتب‌سازی ستون

v2 features (نمایان‌سازی ستون، expansion، pinning، export)

'use client'
import {
  DataTable,
  DataTableColumnVisibilityToggle,
  DataTableExportButton,
} from '@parto-system-design/ui'

const [visibility, setVisibility] = useState({})
const [expandedRows, setExpandedRows] = useState(new Set<number>())

<div className="flex items-center justify-end gap-2">
  <DataTableColumnVisibilityToggle
    columns={columns}
    visibility={{ visible: visibility, onVisibilityChange: setVisibility }}
  />
  <DataTableExportButton columns={columns} data={rows} filename="pages.csv" />
</div>

<DataTable
  columns={[
    { id: 'name', header: 'نام', cell: (r) => r.name, pinned: 'start', width: 200 },
    { id: 'engagement', header: 'تعامل', cell: (r) => r.engagement },
    { id: 'notes', header: 'یادداشت', cell: (r) => r.notes, defaultVisible: false },
  ]}
  data={rows}
  columnVisibility={{ visible: visibility, onVisibilityChange: setVisibility }}
  expansion={{
    expandedRows,
    onExpandedRowsChange: setExpandedRows,
    renderExpandedRow: (r) => <div className="p-4 text-sm">{r.detailsHtml}</div>,
  }}
/>
  • columnVisibility (4.2) — show/hide columns via a checkbox dropdown. defaultVisible: false on a column hides it until toggled on.
  • expansion (4.5) — adds a chevron column; clicking expands a full-width detail row below.
  • pinned: 'start' | 'end' (4.6) — sticky columns که در زمان horizontal scroll قابل دیدن می‌مانند. RTL-aware via CSS logical inset.
  • width: number — عرض ثابت پیکسل برای layout header (همراه با pinning مفید است).
  • <DataTableExportButton> (4.9) — dropdown با CSV download + TSV-clipboard. exportValue در ستون برای cellهای React-node serialize را override می‌کند.

کامپوننت‌های مرتبط

  • Table — اگر داده‌های شما ساده هستند و نیاز به مرتب‌سازی یا صفحه‌بندی ندارند، از Table استفاده کنید
  • Card — اگر نمایش کارتی مناسب‌تر از جدول است، از Card در grid layout استفاده کنید
  • DataTableCells — renderer های مخصوص cell جدول (sparkline، trend، status، …)