טבלת נתונים
טבלאות הניתנות למיון, לסינון ולחלוקה לעמודים עבור לוחות בקרה, לוחות מחוונים ורשימות הזמנות — עם עיצוב בסגנון "ניאו-ברוטליסטי" הכולל מסגרות וצללים.
ה-data table הוא מדריך, לא רכיב ארוז: אתם מרכיבים טבלה משלכם מרכיב <Table /> ומ-TanStack Table, מנוע headless למיון, סינון, עימוד ובחירת שורות — כשהמתכון הנאו-ברוטליסטי של מסגרות עבות, צללים חדים וטיפוגרפיה מודגשת מטפל במראה.
השתמשו בו כש:
- פאנלי ניהול — הזמנות, משתמשים וחשבוניות עם עמודות ממוינות, מסננים ותפריטי פעולה לשורה.
- לוחות מחוונים — טבלאות תשלומים או פעילות עם תאים מעוצבים, עמודות סטטוס ונתוני API עם עימוד.
- זרימות מרובות — בחירת שורות ב-checkbox לייצוא, ארכוב או מחיקה של רשומות רבות בבת אחת.
מבוא#
כל טבלת נתונים או רשת נתונים שיצרתי הייתה ייחודית. כולן מתנהגות אחרת, יש להן דרישות מיון וסינון ספציפיות, והן עובדות עם מקורות נתונים שונים.
אין היגיון לשלב את כל הווריאציות האלה ברכיב יחיד. אם נעשה זאת, נאבד את הגמישות ש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(),
})
// ...
}פעולה זו תחלק אוטומטית את השורות שלכם לעמודים של 10. למידע נוסף על התאמת גודל העמוד ומימוש עימוד ידני, עיינו בתיעוד העימוד.
הוספת פקדי עימוד#
אפשר להוסיף פקדי עימוד לטבלה שלנו באמצעות הרכיב <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>
)
}עיינו במקטע רכיבים לשימוש חוזר לקבלת רכיב עימוד מתקדם יותר.
מיון#
בואו נהפוך את עמודת האימייל לניתנת למיון.
עדכון <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> מקוריים, שנותנים לקוראי מסך הקשר של שורה ועמודה לפי דפוס Table של WAI-ARIA. שני דברים לשמור כשבונים: הגדירו aria-sort על כותרת המיון הנוכחית, והשאירו את aria-label על checkboxes של בחירה ואת טקסט ה-sr-only על טריגרים עם אייקון בלבד (הקטעים למעלה עושים את שניהם).
כל בקרה במדריך הזה היא כפתור, checkbox או input מקורי, כך שהתנהגות המקלדת היא ברירת המחדל של הדפדפן:
| מקש | פעולה |
|---|---|
Tab / Shift + Tab | מעבירים מוקד דרך בקרות הטבלה — שדה מסנן, כותרות מיון, תפריטי שורה, עימוד |
Space / Enter | מפעילים את כפתור המיון או העימוד שבפוקוס |
Space | מחליפים את ה-checkbox של השורה שבפוקוס או select-all |