Openstatus www.openstatus.dev
at 4c0f4c00a38753a5d0dfd7e7b7b7706dec6f1503 208 lines 7.1 kB view raw
1"use client"; 2 3import { 4 type ColumnDef, 5 type ColumnFiltersState, 6 type PaginationState, 7 type Row, 8 type SortingState, 9 type VisibilityState, 10 flexRender, 11 getCoreRowModel, 12 getExpandedRowModel, 13 getFacetedRowModel, 14 getFacetedUniqueValues, 15 getFilteredRowModel, 16 getPaginationRowModel, 17 getSortedRowModel, 18 useReactTable, 19} from "@tanstack/react-table"; 20import * as React from "react"; 21 22import { 23 Table, 24 TableBody, 25 TableCell, 26 TableHead, 27 TableHeader, 28 TableRow, 29} from "@/components/ui/table"; 30import { Fragment } from "react"; 31import type { DataTableActionBarProps } from "./data-table-action-bar"; 32import type { DataTablePaginationProps } from "./data-table-pagination"; 33import type { DataTableToolbarProps } from "./data-table-toobar"; 34 35export interface DataTableProps<TData, TValue> { 36 columns: ColumnDef<TData, TValue>[]; 37 data: TData[]; 38 rowComponent?: React.ComponentType<{ row: Row<TData> }>; 39 toolbarComponent?: React.ComponentType<DataTableToolbarProps<TData>>; 40 actionBar?: React.ComponentType<DataTableActionBarProps<TData>>; 41 paginationComponent?: React.ComponentType<DataTablePaginationProps<TData>>; 42 onRowClick?: (row: Row<TData>) => void; 43 defaultSorting?: SortingState; 44 defaultColumnVisibility?: VisibilityState; 45 defaultColumnFilters?: ColumnFiltersState; 46 defaultPagination?: PaginationState; 47 autoResetPageIndex?: boolean; 48 49 /** access the state from the parent component */ 50 columnFilters?: ColumnFiltersState; 51 setColumnFilters?: React.Dispatch<React.SetStateAction<ColumnFiltersState>>; 52 sorting?: SortingState; 53 setSorting?: React.Dispatch<React.SetStateAction<SortingState>>; 54 pagination?: PaginationState; 55 setPagination?: React.Dispatch<React.SetStateAction<PaginationState>>; 56} 57 58export function DataTable<TData, TValue>({ 59 columns, 60 data, 61 rowComponent, 62 toolbarComponent, 63 actionBar, 64 paginationComponent, 65 onRowClick, 66 defaultSorting = [], 67 defaultColumnVisibility = {}, 68 defaultColumnFilters = [], 69 defaultPagination = { pageIndex: 0, pageSize: 20 }, 70 autoResetPageIndex = true, 71 columnFilters, 72 setColumnFilters, 73 sorting, 74 setSorting, 75 pagination, 76 setPagination, 77}: DataTableProps<TData, TValue>) { 78 // biome-ignore lint/suspicious/noExplicitAny: <explanation> 79 const [globalFilter, setGlobalFilter] = React.useState<any>(); 80 const [rowSelection, setRowSelection] = React.useState({}); 81 const [columnVisibility, setColumnVisibility] = 82 React.useState<VisibilityState>(defaultColumnVisibility); 83 const [internalPagination, setInternalPagination] = 84 React.useState<PaginationState>(defaultPagination); 85 const [internalColumnFilters, setInternalColumnFilters] = 86 React.useState<ColumnFiltersState>(defaultColumnFilters); 87 const [internalSorting, setInternalSorting] = 88 React.useState<SortingState>(defaultSorting); 89 90 // Use controlled or uncontrolled column filters 91 const columnFiltersState = columnFilters ?? internalColumnFilters; 92 const setColumnFiltersState = setColumnFilters ?? setInternalColumnFilters; 93 const sortingState = sorting ?? internalSorting; 94 const setSortingState = setSorting ?? setInternalSorting; 95 const paginationState = pagination ?? internalPagination; 96 const setPaginationState = setPagination ?? setInternalPagination; 97 98 const table = useReactTable({ 99 data, 100 columns, 101 state: { 102 sorting: sortingState, 103 columnVisibility, 104 rowSelection, 105 pagination: paginationState, 106 columnFilters: columnFiltersState, 107 globalFilter, 108 }, 109 enableRowSelection: true, 110 onRowSelectionChange: setRowSelection, 111 onSortingChange: setSortingState, 112 onColumnFiltersChange: setColumnFiltersState, 113 onColumnVisibilityChange: setColumnVisibility, 114 onPaginationChange: setPaginationState, 115 onGlobalFilterChange: setGlobalFilter, 116 getCoreRowModel: getCoreRowModel(), 117 getFilteredRowModel: getFilteredRowModel(), 118 getPaginationRowModel: getPaginationRowModel(), 119 getSortedRowModel: getSortedRowModel(), 120 getFacetedRowModel: getFacetedRowModel(), 121 getFacetedUniqueValues: getFacetedUniqueValues(), 122 getExpandedRowModel: getExpandedRowModel(), 123 autoResetPageIndex, 124 // @ts-expect-error as we have an id in the data 125 getRowCanExpand: (row) => Boolean(row.original.id), 126 }); 127 128 return ( 129 <div className="grid gap-2"> 130 {toolbarComponent 131 ? React.createElement(toolbarComponent, { table }) 132 : null} 133 <Table> 134 <TableHeader> 135 {table.getHeaderGroups().map((headerGroup) => ( 136 <TableRow key={headerGroup.id}> 137 {headerGroup.headers.map((header) => { 138 return ( 139 <TableHead 140 key={header.id} 141 colSpan={header.colSpan} 142 className={header.column.columnDef.meta?.headerClassName} 143 > 144 {header.isPlaceholder 145 ? null 146 : flexRender( 147 header.column.columnDef.header, 148 header.getContext(), 149 )} 150 </TableHead> 151 ); 152 })} 153 </TableRow> 154 ))} 155 </TableHeader> 156 <TableBody> 157 {table.getRowModel().rows?.length ? ( 158 table.getRowModel().rows.map((row) => ( 159 <Fragment key={row.id}> 160 <TableRow 161 data-state={ 162 (row.getIsSelected() || row.getIsExpanded()) && "selected" 163 } 164 onClick={() => onRowClick?.(row)} 165 className="data-[state=selected]:bg-muted/50" 166 > 167 {row.getVisibleCells().map((cell) => ( 168 <TableCell 169 key={cell.id} 170 className={cell.column.columnDef.meta?.cellClassName} 171 > 172 {flexRender( 173 cell.column.columnDef.cell, 174 cell.getContext(), 175 )} 176 </TableCell> 177 ))} 178 </TableRow> 179 {row.getIsExpanded() && ( 180 <TableRow className="hover:bg-background"> 181 <TableCell 182 className="p-0" 183 colSpan={row.getVisibleCells().length} 184 > 185 {rowComponent 186 ? React.createElement(rowComponent, { row }) 187 : null} 188 </TableCell> 189 </TableRow> 190 )} 191 </Fragment> 192 )) 193 ) : ( 194 <TableRow> 195 <TableCell colSpan={columns.length} className="h-24 text-center"> 196 No results. 197 </TableCell> 198 </TableRow> 199 )} 200 </TableBody> 201 {actionBar ? React.createElement(actionBar, { table }) : null} 202 </Table> 203 {paginationComponent 204 ? React.createElement(paginationComponent, { table }) 205 : null} 206 </div> 207 ); 208}