'use client'
import { useState } from 'react'
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
import type { ColumnDef, ColumnFiltersState, SortingState, VisibilityState } from '@tanstack/react-table'
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable
} from '@tanstack/react-table'
import { Checkbox } from '@/registry/new-york/ui/checkbox'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/registry/new-york/ui/table'
import { cn } from '@/registry/new-york/lib/utils'
const data: Payment[] = [
{
id: '1',
name: 'Shang Chain',
amount: 699,
status: 'success',
email: 'shang07@yahoo.com'
},
{
id: '2',
name: 'Kevin Lincoln',
amount: 242,
status: 'success',
email: 'kevinli09@gmail.com'
},
{
id: '3',
name: 'Milton Rose',
amount: 655,
status: 'processing',
email: 'rose96@gmail.com'
},
{
id: '4',
name: 'Silas Ryan',
amount: 874,
status: 'success',
email: 'silas22@gmail.com'
},
{
id: '5',
name: 'Ben Tenison',
amount: 541,
status: 'failed',
email: 'bent@hotmail.com'
}
]
export type Payment = {
id: string
name: string
amount: number
status: 'pending' | 'processing' | 'success' | 'failed'
email: string
}
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
},
{
header: 'Name',
accessorKey: 'name',
cell: ({ row }) => <div className='font-medium'>{row.getValue('name')}</div>
},
{
accessorKey: 'status',
header: 'Status',
cell: ({ row }) => <div className='capitalize'>{row.getValue('status')}</div>
},
{
accessorKey: 'email',
header: 'Email',
cell: ({ row }) => <div className='lowercase'>{row.getValue('email')}</div>
},
{
accessorKey: 'amount',
header: 'Amount',
cell: ({ row }) => {
const amount = parseFloat(row.getValue('amount'))
const formatted = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount)
return <div className='font-medium'>{formatted}</div>
}
}
]
const DataTableWithSortableColumnDemo = () => {
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
const [rowSelection, setRowSelection] = 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 className='w-full'>
<div className='rounded-md border'>
<Table>
<TableHeader>
{table.getHeaderGroups().map(headerGroup => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<TableHead
key={header.id}
aria-sort={
header.column.getIsSorted() === 'asc'
? 'ascending'
: header.column.getIsSorted() === 'desc'
? 'descending'
: 'none'
}
>
{header.isPlaceholder ? null : (
<div
className={cn(
header.column.getCanSort() &&
'flex h-full cursor-pointer items-center justify-between gap-2 select-none'
)}
onClick={header.column.getToggleSortingHandler()}
onKeyDown={e => {
if (header.column.getCanSort() && (e.key === 'Enter' || e.key === ' ')) {
e.preventDefault()
header.column.getToggleSortingHandler()?.(e)
}
}}
tabIndex={header.column.getCanSort() ? 0 : undefined}
>
<span className='truncate'>
{flexRender(header.column.columnDef.header, header.getContext())}
</span>
{{
asc: <ChevronUpIcon className='shrink-0 opacity-60' size={16} aria-hidden='true' />,
desc: <ChevronDownIcon className='shrink-0 opacity-60' size={16} aria-hidden='true' />
}[header.column.getIsSorted() as string] ?? null}
</div>
)}
</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>
<p className='text-muted-foreground mt-4 text-center text-sm'>Data table with sortable column</p>
</div>
)
}
export default DataTableWithSortableColumnDemo