Data Table 6

PreviousNext

Data table with row expansion functionality for detailed content display

Docs
shadcn-studiocomponent

Preview

Loading preview…
registry/new-york/components/data-table/data-table-06.tsx
'use client'

import { useState } from 'react'

import type { ColumnDef, ColumnFiltersState, SortingState } from '@tanstack/react-table'
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'

import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/registry/new-york/ui/table'

const data: Payment[] = [
  {
    id: '1',
    name: 'Shang Chain',
    amount: 699,
    status: 'success',
    email: 'shang07@yahoo.com',
    dueDate: '2023-10-01'
  },
  {
    id: '2',
    name: 'Kevin Lincoln',
    amount: 242,
    status: 'success',
    email: 'kevinli09@gmail.com',
    dueDate: '2023-10-01'
  },
  {
    id: '3',
    name: 'Milton Rose',
    amount: 655,
    status: 'processing',
    email: 'rose96@gmail.com',
    dueDate: '2023-10-01'
  },
  {
    id: '4',
    name: 'Silas Ryan',
    amount: 874,
    status: 'success',
    email: 'silas22@gmail.com',
    dueDate: '2023-10-01'
  },
  {
    id: '5',
    name: 'Ben Tenison',
    amount: 541,
    status: 'failed',
    email: 'bent@hotmail.com',
    dueDate: '2023-10-01'
  }
]

export type Payment = {
  id: string
  name: string
  amount: number
  status: 'pending' | 'processing' | 'success' | 'failed'
  email: string
  dueDate: string
}

export const columns: ColumnDef<Payment>[] = [
  {
    accessorKey: 'name',
    header: () => <div className='truncate'>Name</div>,
    cell: ({ row }) => <div className='truncate font-medium'>{row.getValue('name')}</div>
  },
  {
    accessorKey: 'status',
    header: () => <div className='truncate'>Status</div>,
    cell: ({ row }) => <div className='truncate capitalize'>{row.getValue('status')}</div>
  },
  {
    accessorKey: 'email',
    header: () => <div className='truncate'>Email</div>,
    cell: ({ row }) => <div className='truncate lowercase'>{row.getValue('email')}</div>
  },
  {
    accessorKey: 'amount',
    header: () => <div className='truncate'>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='font-medium'>{formatted}</div>
    }
  },
  {
    accessorKey: 'dueDate',
    header: () => <div className='truncate'>Due Date</div>,
    cell: ({ row }) => <div className='truncate lowercase'>{row.getValue('dueDate')}</div>
  }
]

const DataTableWithResizableColumnsDemo = () => {
  const [sorting, setSorting] = useState<SortingState>([])
  const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
  const [rowSelection, setRowSelection] = useState({})

  const table = useReactTable({
    data,
    columns,
    columnResizeMode: 'onChange',
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    getCoreRowModel: getCoreRowModel(),
    onRowSelectionChange: setRowSelection,
    state: {
      sorting,
      columnFilters,
      rowSelection
    }
  })

  return (
    <div className='max-w-3xl max-md:max-w-full'>
      <div className='rounded-md border'>
        <Table
          className='table-fixed'
          style={{
            width: table.getCenterTotalSize()
          }}
        >
          <TableHeader>
            {table.getHeaderGroups().map(headerGroup => (
              <TableRow key={headerGroup.id}>
                {headerGroup.headers.map(header => {
                  return (
                    <TableHead
                      key={header.id}
                      className='group/head relative h-10 select-none last:[&>.cursor-col-resize]:opacity-0'
                      {...{
                        colSpan: header.colSpan,
                        style: {
                          width: header.getSize()
                        }
                      }}
                    >
                      {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
                      {header.column.getCanResize() && (
                        <div
                          {...{
                            onDoubleClick: () => header.column.resetSize(),
                            onMouseDown: header.getResizeHandler(),
                            onTouchStart: header.getResizeHandler(),
                            className:
                              'group-last/head:hidden absolute top-0 h-full w-4 cursor-col-resize user-select-none touch-none -right-2 z-10 flex justify-center before:absolute before:w-px before:inset-y-0 before:bg-border before:translate-x-px'
                          }}
                        />
                      )}
                    </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} className='truncate'>
                      {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 resizable columns</p>
    </div>
  )
}

export default DataTableWithResizableColumnsDemo

Installation

npx shadcn@latest add @shadcn-studio/data-table-06

Usage

import { DataTable06 } from "@/components/data-table-06"
<DataTable06 />