Openstatus
www.openstatus.dev
1"use client";
2
3import type {
4 ColumnDef,
5 ColumnFiltersState,
6 ExpandedState,
7 PaginationState,
8 Row,
9 SortingState,
10 VisibilityState,
11} from "@tanstack/react-table";
12import {
13 flexRender,
14 getCoreRowModel,
15 getExpandedRowModel,
16 getFilteredRowModel,
17 getPaginationRowModel,
18 getSortedRowModel,
19 useReactTable,
20} from "@tanstack/react-table";
21import * as React from "react";
22
23import {
24 Table,
25 TableBody,
26 TableCell,
27 TableHead,
28 TableHeader,
29 TableRow,
30} from "@openstatus/ui";
31
32import { cn } from "@/lib/utils";
33import { DataTablePagination } from "./data-table-pagination";
34import { DataTableToolbar } from "./data-table-toolbar";
35
36interface DataTableProps<TData, TValue> {
37 columns: ColumnDef<TData, TValue>[];
38 data: TData[];
39 renderSubComponent(props: { row: Row<TData> }): React.ReactElement<unknown>;
40 getRowCanExpand(row: Row<TData>): boolean;
41 autoResetExpanded?: boolean;
42 defaultColumnFilters?: ColumnFiltersState;
43 defaultPagination?: PaginationState;
44 defaultVisibility?: VisibilityState;
45 // allowedPeriods?: Period[]; REMIDNER: disabled unallowed periods
46}
47
48export function DataTable<TData, TValue>({
49 columns,
50 data,
51 renderSubComponent,
52 getRowCanExpand,
53 autoResetExpanded,
54 defaultColumnFilters = [],
55 defaultPagination = { pageIndex: 0, pageSize: 10 },
56 defaultVisibility = {},
57}: DataTableProps<TData, TValue>) {
58 const [sorting, setSorting] = React.useState<SortingState>([]);
59 const [columnFilters, setColumnFilters] =
60 React.useState<ColumnFiltersState>(defaultColumnFilters);
61 const [expanded, setExpanded] = React.useState<ExpandedState>({});
62 const [pagination, setPagination] =
63 React.useState<PaginationState>(defaultPagination);
64 const [columnVisibility, setColumnVisibility] =
65 React.useState<VisibilityState>(defaultVisibility);
66
67 const table = useReactTable({
68 data,
69 columns,
70 getCoreRowModel: getCoreRowModel(),
71 onPaginationChange: setPagination,
72 getPaginationRowModel: getPaginationRowModel(),
73 onSortingChange: setSorting,
74 getSortedRowModel: getSortedRowModel(),
75 onColumnFiltersChange: setColumnFilters,
76 getFilteredRowModel: getFilteredRowModel(),
77 onExpandedChange: setExpanded,
78 getExpandedRowModel: getExpandedRowModel(),
79 onColumnVisibilityChange: setColumnVisibility,
80 getRowCanExpand,
81 autoResetExpanded,
82 state: {
83 sorting,
84 columnFilters,
85 expanded,
86 pagination,
87 columnVisibility,
88 },
89 });
90
91 return (
92 <div className="space-y-3">
93 <DataTableToolbar table={table} />
94 <div className="rounded-md border">
95 <Table>
96 <TableHeader className="bg-muted/50">
97 {table.getHeaderGroups().map((headerGroup) => (
98 <TableRow key={headerGroup.id}>
99 {headerGroup.headers.map((header) => {
100 return (
101 <TableHead key={header.id}>
102 {header.isPlaceholder
103 ? null
104 : flexRender(
105 header.column.columnDef.header,
106 header.getContext(),
107 )}
108 </TableHead>
109 );
110 })}
111 </TableRow>
112 ))}
113 </TableHeader>
114 <TableBody>
115 {table.getRowModel().rows?.length ? (
116 table.getRowModel().rows.map((row) => (
117 <React.Fragment key={row.id}>
118 <TableRow
119 data-state={
120 (row.getIsSelected() || row.getIsExpanded()) && "selected"
121 }
122 onClick={() => {
123 if (!row.getCanExpand()) return;
124 // REMINDER: this is a workaround for single row expansion
125 if (!row.getIsExpanded()) table.resetExpanded();
126 row.toggleExpanded();
127 }}
128 className={cn(row.getCanExpand() && "cursor-pointer")}
129 >
130 {row.getVisibleCells().map((cell) => (
131 <TableCell key={cell.id}>
132 {flexRender(
133 cell.column.columnDef.cell,
134 cell.getContext(),
135 )}
136 </TableCell>
137 ))}
138 </TableRow>
139 {row.getIsExpanded() && (
140 <TableRow
141 data-state="expanded"
142 className="hover:bg-muted/10 data-[state=expanded]:bg-muted/10"
143 >
144 {/* 2nd row is a custom 1 cell row */}
145 <TableCell colSpan={row.getVisibleCells().length}>
146 {renderSubComponent({ row })}
147 </TableCell>
148 </TableRow>
149 )}
150 </React.Fragment>
151 ))
152 ) : (
153 <TableRow>
154 <TableCell
155 colSpan={columns.length}
156 className="h-24 text-center"
157 >
158 No results.
159 </TableCell>
160 </TableRow>
161 )}
162 </TableBody>
163 </Table>
164 </div>
165 <DataTablePagination table={table} />
166 </div>
167 );
168}