جدول داده
جدولهای قابل مرتبسازی، قابل فیلترسازی و صفحهبندیشده برای پنلهای مدیریت، داشبوردها و فهرست سفارشها — با جلوهی نئوبروتالیستی مرزها و سایهها.
جدول داده یک راهنماست نه کامپوننت بستهبندیشده: جدول خودتان را از کامپوننت <Table /> و TanStack Table — موتور headless برای sort، filter، pagination و انتخاب ردیف — میسازید، و دستور پخت نئوبروتالیستی حاشیههای ضخیم، سایههای سخت و تایپوگرافی پررنگ ظاهر را میسازد.
برای این موارد از آن استفاده کنید:
- پنل ادمین — سفارش، کاربر و فاکتور با ستونهای قابلمرتبسازی، فیلتر و منوی اقدام per-row.
- داشبورد — جدول پرداخت یا فعالیت با سلولهای قالببندیشده، ستون وضعیت و دادهٔ API صفحهبندیشده.
- گردشکار گروهی — انتخاب ردیف با چکباکس برای export، بایگانی یا حذف چند رکورد با هم.
مقدمه#
هر جدول داده یا datagrid که ساختهام منحصربهفرد بوده است. همهٔ آنها بهگونهای متفاوت رفتار میکنند، الزامات مرتبسازی و فیلتر کردن خاصی دارند و با منابع دادهٔ مختلف کار میکنند.
ترکیب همهٔ این تغییرات در یک کامپوننت واحد منطقی نیست. اگر این کار را انجام دهیم، انعطافپذیریای را که headless UI فراهم میکند از دست خواهیم داد.
بنابراین بهجای یک کامپوننت data-table، فکر کردم مفیدتر باشد که راهنمایی دربارهٔ نحوهٔ ساخت کامپوننت خودتان ارائه دهم.
ما با کامپوننت پایهٔ <Table /> شروع میکنیم و یک جدول دادهٔ پیچیده را از صفر میسازیم.
نکته: اگر میبینید که از یک جدول یکسان در چند جای برنامهٔ خود استفاده میکنید، همیشه میتوانید آن را در یک کامپوننت قابل استفادهٔ مجدد استخراج کنید.
فهرست مطالب#
این راهنما به شما نشان میدهد که چگونه از TanStack Table و کامپوننت <Table /> برای ساخت جدول دادهٔ سفارشی خودتان استفاده کنید. ما موضوعات زیر را پوشش خواهیم داد:
- جدول پایه
- اقدامات ردیف
- صفحهبندی
- مرتبسازی
- فیلتر کردن
- قابلیت مشاهده
- انتخاب ردیف
- کامپوننتهای قابل استفاده مجدد
نصب#
- کامپوننت
<Table />را به پروژهٔ خود اضافه کنید:
pnpm dlx shadcn@latest add tablenpx shadcn@latest add tableyarn dlx shadcn@latest add tablebunx --bun shadcn@latest add table
- وابستگی
tanstack/react-tableرا اضافه کنید:
pnpm add @tanstack/react-tablenpm install @tanstack/react-tableyarn add @tanstack/react-tablebun add @tanstack/react-table
پیشنیازها#
ما قصد داریم جدولی برای نمایش پرداختهای اخیر بسازیم. دادههای ما به این شکل است:
type Payment = {
id: string
amount: number
status: "pending" | "processing" | "success" | "failed"
email: string
}
export const payments: Payment[] = [
{
id: "728ed52f",
amount: 100,
status: "pending",
email: "[email protected]",
},
{
id: "489e1d42",
amount: 125,
status: "processing",
email: "[email protected]",
},
// ...
]ساختار پروژه#
با ایجاد ساختار فایل زیر شروع کنید:
app
└── payments
├── columns.tsx
├── data-table.tsx
└── page.tsxمن در اینجا از یک نمونهٔ Next.js استفاده میکنم، اما این با هر فریمورک React دیگری کار میکند.
columns.tsx(کامپوننت کلاینت) شامل تعاریف ستونهای ما خواهد بود.data-table.tsx(کامپوننت کلاینت) شامل کامپوننت<DataTable />ما خواهد بود.page.tsx(کامپوننت سرور) جایی است که در آن دادهها را دریافت کرده و جدول خود را رندر میکنیم.
جدول پایه#
بیایید با ساخت یک جدول پایه شروع کنیم.
تعاریف ستون#
ابتدا ستونهای خود را تعریف میکنیم.
"use client"
import { ColumnDef } from "@tanstack/react-table"
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type Payment = {
id: string
amount: number
status: "pending" | "processing" | "success" | "failed"
email: string
}
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "status",
header: "Status",
},
{
accessorKey: "email",
header: "Email",
},
{
accessorKey: "amount",
header: "Amount",
},
]توجه: ستونها جایی هستند که هستهٔ ظاهر جدول خود را در آن تعریف میکنید. آنها دادههایی را که نمایش داده میشوند و نحوهٔ قالببندی، مرتبسازی و فیلتر شدن آنها را تعریف میکنند.
کامپوننت <DataTable />#
سپس یک کامپوننت <DataTable /> برای رندر کردن جدول خود ایجاد میکنیم.
"use client"
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})
return (
<div className="overflow-hidden rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}نکته: اگر میبینید که از <DataTable /> در چند جا استفاده میکنید، این کامپوننتی است که میتوانید با استخراج آن به components/ui/data-table.tsx قابل استفادهٔ مجدد کنید.
<DataTable columns={columns} data={data} />
رندر کردن جدول#
در نهایت، جدول خود را در کامپوننت صفحهٔ خود رندر میکنیم.
import { columns, Payment } from "./columns"
import { DataTable } from "./data-table"
async function getData(): Promise<Payment[]> {
// Fetch data from your API here.
return [
{
id: "728ed52f",
amount: 100,
status: "pending",
email: "[email protected]",
},
// ...
]
}
export default async function DemoPage() {
const data = await getData()
return (
<div className="container mx-auto py-10">
<DataTable columns={columns} data={data} />
</div>
)
}قالببندی سلول#
بیایید سلول مبلغ را قالببندی کنیم تا مبلغ دلاری نمایش داده شود. همچنین سلول را به راست تراز میکنیم.
بهروزرسانی تعریف ستونها#
تعاریف header و cell را برای مبلغ به این صورت بهروزرسانی کنید:
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "amount",
header: () => <div className="text-right">Amount</div>,
cell: ({ row }) => {
const amount = parseFloat(row.getValue("amount"))
const formatted = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(amount)
return <div className="text-right font-medium">{formatted}</div>
},
},
]میتوانید از همین رویکرد برای قالببندی سلولها و سرصفحههای دیگر استفاده کنید.
اقدامات ردیف#
بیایید اقدامات ردیف را به جدول خود اضافه کنیم. برای این کار از یک کامپوننت <DropdownMenu /> استفاده میکنیم.
بهروزرسانی تعریف ستونها#
تعریف ستونهای خود را بهروزرسانی کنید تا یک ستون actions جدید اضافه شود. سلول actions یک کامپوننت <DropdownMenu /> را برمیگرداند.
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { MoreHorizontal } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export const columns: ColumnDef<Payment>[] = [
// ...
{
id: "actions",
cell: ({ row }) => {
const payment = row.original
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(payment.id)}
>
Copy payment ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View customer</DropdownMenuItem>
<DropdownMenuItem>View payment details</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
},
// ...
]میتوانید با استفاده از row.original در تابع cell به دادههای ردیف دسترسی داشته باشید. از این برای مدیریت اقدامات ردیف خود استفاده کنید، برای مثال از id برای انجام یک فراخوانی DELETE به API خود استفاده کنید.
صفحهبندی#
سپس، صفحهبندی را به جدول خود اضافه میکنیم.
بهروزرسانی <DataTable>#
import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
})
// ...
}این کار ردیفهای شما را بهصورت خودکار به صفحههای ۱۰تایی تقسیم میکند. برای اطلاعات بیشتر دربارهٔ سفارشیسازی اندازهٔ صفحه و پیادهسازی صفحهبندی دستی، مستندات صفحهبندی را ببینید.
افزودن کنترلهای صفحهبندی#
میتوانیم با استفاده از کامپوننت <Button /> و متدهای API table.previousPage()، table.nextPage() کنترلهای صفحهبندی را به جدول خود اضافه کنیم.
import { Button } from "@/components/ui/button"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
})
return (
<div>
<div className="overflow-hidden rounded-md border">
<Table>
{ // .... }
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
)
}برای یک کامپوننت صفحهبندی پیشرفتهتر، به بخش کامپوننتهای قابل استفاده مجدد نگاه کنید.
مرتبسازی#
بیایید ستون email را قابل مرتبسازی کنیم.
بهروزرسانی <DataTable>#
"use client"
import * as React from "react"
import {
ColumnDef,
SortingState,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
state: {
sorting,
},
})
return (
<div>
<div className="overflow-hidden rounded-md border">
<Table>{ ... }</Table>
</div>
</div>
)
}قابل مرتبسازی کردن سلول سرصفحه#
اکنون میتوانیم سلول سرصفحهٔ email را بهروزرسانی کنیم تا کنترلهای مرتبسازی اضافه شوند.
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { ArrowUpDown } from "lucide-react"
export const columns: ColumnDef<Payment>[] = [
{
accessorKey: "email",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
]این کار جدول را هنگامی که کاربر روی سلول سرصفحه کلیک میکند بهصورت خودکار مرتب میکند (صعودی و نزولی).
فیلتر کردن#
بیایید یک ورودی جستوجو اضافه کنیم تا ایمیلها را در جدول خود فیلتر کنیم.
بهروزرسانی <DataTable>#
"use client"
import * as React from "react"
import {
ColumnDef,
ColumnFiltersState,
SortingState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
state: {
sorting,
columnFilters,
},
})
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder="Filter emails..."
value={(table.getColumn("email")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("email")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
</div>
<div className="overflow-hidden rounded-md border">
<Table>{ ... }</Table>
</div>
</div>
)
}فیلتر کردن اکنون برای ستون email فعال است. میتوانید فیلترها را به ستونهای دیگر نیز اضافه کنید. برای اطلاعات بیشتر دربارهٔ سفارشیسازی فیلترها، مستندات فیلتر کردن را ببینید.
قابلیت مشاهده#
افزودن قابلیت مشاهدهٔ ستون با استفاده از API قابلیت مشاهدهٔ @tanstack/react-table نسبتاً ساده است.
بهروزرسانی <DataTable>#
"use client"
import * as React from "react"
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
state: {
sorting,
columnFilters,
columnVisibility,
},
})
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder="Filter emails..."
value={table.getColumn("email")?.getFilterValue() as string}
onChange={(event) =>
table.getColumn("email")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Columns
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter(
(column) => column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="overflow-hidden rounded-md border">
<Table>{ ... }</Table>
</div>
</div>
)
}این یک منوی کشویی اضافه میکند که میتوانید از آن برای تغییر قابلیت مشاهدهٔ ستون استفاده کنید.
انتخاب ردیف#
سپس، انتخاب ردیف را به جدول خود اضافه میکنیم.
بهروزرسانی تعاریف ستونها#
"use client"
import { ColumnDef } from "@tanstack/react-table"
import { Badge } from "@/components/ui/badge"
import { Checkbox } from "@/components/ui/checkbox"
export const columns: ColumnDef<Payment>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
]بهروزرسانی <DataTable>#
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div>
<div className="overflow-hidden rounded-md border">
<Table />
</div>
</div>
)
}این یک چکباکس به هر ردیف و یک چکباکس در سرصفحه برای انتخاب همهٔ ردیفها اضافه میکند.
نمایش ردیفهای انتخابشده#
میتوانید تعداد ردیفهای انتخابشده را با استفاده از API table.getFilteredSelectedRowModel() نمایش دهید.
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>کامپوننتهای قابل استفاده مجدد#
در اینجا چند کامپوننت آمده است که میتوانید برای ساخت جدولهای دادهٔ خود از آنها استفاده کنید. اینها از دموی Tasks هستند.
سرصفحهٔ ستون#
هر سرصفحهٔ ستونی را قابل مرتبسازی و قابل پنهان کردن کنید.
import { type Column } from "@tanstack/react-table"
import { ArrowDown, ArrowUp, ChevronsUpDown, EyeOff } from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/registry/base/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/base/ui/dropdown-menu"
interface DataTableColumnHeaderProps<
TData,
TValue,
> extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>
title: string
}
export function DataTableColumnHeader<TData, TValue>({
column,
title,
className,
}: DataTableColumnHeaderProps<TData, TValue>) {
if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div>
}
return (
<div className={cn("flex items-center gap-2", className)}>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="ghost"
size="sm"
className="-ml-3 h-8 data-popup-open:bg-accent"
/>
}
>
<span>{title}</span>
{column.getIsSorted() === "desc" ? (
<ArrowDown />
) : column.getIsSorted() === "asc" ? (
<ArrowUp />
) : (
<ChevronsUpDown />
)}
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUp />
Asc
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDown />
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeOff />
Hide
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}
export const columns = [
{
accessorKey: "email",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Email" />
),
},
]صفحهبندی#
کنترلهای صفحهبندی را به جدول خود اضافه کنید، از جمله اندازهٔ صفحه و تعداد انتخاب.
import { type Table } from "@tanstack/react-table"
import {
ChevronLeft,
ChevronRight,
ChevronsLeft,
ChevronsRight,
} from "lucide-react"
import { Button } from "@/registry/base/ui/button"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/base/ui/select"
const pageSizeItems = [10, 20, 25, 30, 40, 50].map((pageSize) => ({
label: `${pageSize}`,
value: `${pageSize}`,
}))
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Rows per page</p>
<Select
items={pageSizeItems}
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
if (value !== null) {
table.setPageSize(Number(value))
}
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
{pageSizeItems.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
size="icon"
className="hidden size-8 lg:flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<ChevronsLeft />
</Button>
<Button
variant="outline"
size="icon"
className="size-8"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeft />
</Button>
<Button
variant="outline"
size="icon"
className="size-8"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRight />
</Button>
<Button
variant="outline"
size="icon"
className="hidden size-8 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<ChevronsRight />
</Button>
</div>
</div>
</div>
)
}
<DataTablePagination table={table} />تغییر وضعیت ستون#
کامپوننتی برای تغییر قابلیت مشاهدهٔ ستون.
"use client"
import { type Table } from "@tanstack/react-table"
import { Settings2 } from "lucide-react"
import { Button } from "@/registry/base/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/registry/base/ui/dropdown-menu"
export function DataTableViewOptions<TData>({
table,
}: {
table: Table<TData>
}) {
return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
/>
}
>
<Settings2 />
View
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[150px]">
{/* GroupLabel (Base UI) needs a Group ancestor or it throws error #31. */}
<DropdownMenuGroup>
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
</DropdownMenuGroup>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== "undefined" && column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)
}
<DataTableViewOptions table={table} />RTL#
برای فعالسازی پشتیبانی RTL در Neobrutalism، راهنمای پیکربندی RTL را ببینید.
دسترسیپذیری#
TanStack Table سرِ headless است — markup رندر نمیکند، پس دسترسیپذیری از عناصر این راهنما میآید. کامپوننت <Table /> عناصر بومی <table>، <th> و <td> خروجی میدهد که به صفحهخوان زمینهٔ سطر و ستون را مطابق الگوی WAI-ARIA Table میدهند. دو نکته هنگام ساخت: aria-sort را روی header مرتبشده بگذارید، و aria-label روی چکباکسهای انتخاب و متن sr-only روی تریگرهای فقطآیکون را نگه دارید (snippetهای بالا هر دو را دارند).
هر کنترل این راهنما دکمه، چکباکس یا input بومی است، پس رفتار صفحهکلید پیشفرض مرورگر است:
| کلید | عمل |
|---|---|
Tab / Shift + Tab | حرکت فوکوس در کنترلهای جدول — input فیلتر، header مرتبسازی، منوی ردیف، pagination |
Space / Enter | فعالسازی دکمهٔ sort یا pagination فوکوسشده |
Space روی چکباکس | تغییر انتخاب ردیف یا select-all |