@simpleapps-com/augur-web 2.2.24 → 2.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,12 +8,29 @@ import {
8
8
  TableHeader,
9
9
  TableRow
10
10
  } from "./chunk-XIKSI7NY.js";
11
+ import {
12
+ ListPagination
13
+ } from "./chunk-3VN3FX2Q.js";
14
+ import {
15
+ DropdownMenu,
16
+ DropdownMenuCheckboxItem,
17
+ DropdownMenuContent,
18
+ DropdownMenuLabel,
19
+ DropdownMenuSeparator,
20
+ DropdownMenuTrigger
21
+ } from "./chunk-MOC5N2N4.js";
11
22
  import {
12
23
  Input
13
24
  } from "./chunk-HXQF6XTL.js";
14
25
  import {
15
26
  Spinner
16
27
  } from "./chunk-UMNTUD2P.js";
28
+ import {
29
+ Button
30
+ } from "./chunk-QICSGVX3.js";
31
+ import {
32
+ Checkbox
33
+ } from "./chunk-3UFEQSTN.js";
17
34
 
18
35
  // src/data-table.tsx
19
36
  import * as React from "react";
@@ -25,23 +42,257 @@ import {
25
42
  useReactTable
26
43
  } from "@tanstack/react-table";
27
44
  import { cn } from "@simpleapps-com/augur-utils/web";
28
- import { LuArrowDown, LuArrowUp, LuArrowUpDown } from "react-icons/lu";
29
- import { jsx, jsxs } from "react/jsx-runtime";
30
- function buildTanstackColumns(columns) {
31
- return columns.map((col) => ({
32
- id: col.key,
33
- accessorKey: col.key,
34
- header: col.header,
35
- enableSorting: col.sortable ?? false,
36
- cell: col.render ? (info) => col.render(info.row.original) : (info) => String(info.getValue() ?? ""),
37
- meta: { align: col.align }
38
- }));
45
+ import { LuArrowDown, LuArrowUp, LuArrowUpDown, LuDownload, LuSettings2 } from "react-icons/lu";
46
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
47
+ var ALIGN_CLASS = { left: "text-left", center: "text-center", right: "text-right" };
48
+ function getAlignCn(meta) {
49
+ const align = meta?.align ?? "left";
50
+ return ALIGN_CLASS[align];
51
+ }
52
+ function buildTanstackColumns(columns, selectable) {
53
+ const cols = [];
54
+ if (selectable) {
55
+ cols.push({
56
+ id: "_select",
57
+ header: ({ table }) => /* @__PURE__ */ jsx(
58
+ Checkbox,
59
+ {
60
+ checked: table.getIsAllPageRowsSelected(),
61
+ onCheckedChange: (v) => table.toggleAllPageRowsSelected(!!v),
62
+ "aria-label": "Select all"
63
+ }
64
+ ),
65
+ cell: ({ row }) => /* @__PURE__ */ jsx(
66
+ Checkbox,
67
+ {
68
+ checked: row.getIsSelected(),
69
+ onCheckedChange: (v) => row.toggleSelected(!!v),
70
+ "aria-label": "Select row",
71
+ onClick: (e) => e.stopPropagation()
72
+ }
73
+ ),
74
+ enableSorting: false,
75
+ enableHiding: false,
76
+ meta: { align: "center" }
77
+ });
78
+ }
79
+ for (const col of columns) {
80
+ cols.push({
81
+ id: col.key,
82
+ accessorKey: col.key,
83
+ header: col.header,
84
+ enableSorting: col.sortable ?? false,
85
+ enableHiding: col.hideable !== false,
86
+ cell: col.render ? (info) => col.render(info.row.original) : (info) => String(info.getValue() ?? ""),
87
+ meta: { align: col.align }
88
+ });
89
+ }
90
+ return cols;
91
+ }
92
+ function exportToCsv(columns, data, filename) {
93
+ const headers = columns.map((c) => c.header);
94
+ const rows = data.map(
95
+ (row) => columns.map((col) => {
96
+ const str = String(row[col.key] ?? "");
97
+ return str.includes(",") || str.includes('"') || str.includes("\n") ? `"${str.replace(/"/g, '""')}"` : str;
98
+ })
99
+ );
100
+ const csv = [headers.join(","), ...rows.map((r) => r.join(","))].join("\n");
101
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
102
+ const url = URL.createObjectURL(blob);
103
+ const link = document.createElement("a");
104
+ link.href = url;
105
+ link.download = `${filename}.csv`;
106
+ link.click();
107
+ URL.revokeObjectURL(url);
108
+ }
109
+ function SortIndicator({ direction }) {
110
+ if (direction === "asc") return /* @__PURE__ */ jsx(LuArrowUp, { className: "h-4 w-4" });
111
+ if (direction === "desc") return /* @__PURE__ */ jsx(LuArrowDown, { className: "h-4 w-4" });
112
+ return /* @__PURE__ */ jsx(LuArrowUpDown, { className: "text-muted-foreground/50 h-4 w-4" });
113
+ }
114
+ function ColumnToggle({ table }) {
115
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
116
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
117
+ /* @__PURE__ */ jsx(LuSettings2, { className: "mr-2 h-4 w-4" }),
118
+ "Columns"
119
+ ] }) }),
120
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
121
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: "Toggle columns" }),
122
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
123
+ table.getAllColumns().filter((col) => col.getCanHide()).map((col) => /* @__PURE__ */ jsx(
124
+ DropdownMenuCheckboxItem,
125
+ {
126
+ checked: col.getIsVisible(),
127
+ onCheckedChange: (v) => col.toggleVisibility(!!v),
128
+ children: col.columnDef.header
129
+ },
130
+ col.id
131
+ ))
132
+ ] })
133
+ ] });
134
+ }
135
+ function HeaderCell({ header }) {
136
+ return /* @__PURE__ */ jsx(
137
+ TableHead,
138
+ {
139
+ className: cn(
140
+ getAlignCn(header.column.columnDef.meta),
141
+ header.column.getCanSort() && "cursor-pointer select-none"
142
+ ),
143
+ onClick: header.column.getToggleSortingHandler(),
144
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
145
+ flexRender(header.column.columnDef.header, header.getContext()),
146
+ header.column.getCanSort() && /* @__PURE__ */ jsx(SortIndicator, { direction: header.column.getIsSorted() })
147
+ ] })
148
+ }
149
+ );
150
+ }
151
+ function DataCell({ cell }) {
152
+ return /* @__PURE__ */ jsx(TableCell, { className: getAlignCn(cell.column.columnDef.meta), children: flexRender(cell.column.columnDef.cell, cell.getContext()) });
153
+ }
154
+ function DataRow({ row, onRowClick }) {
155
+ return /* @__PURE__ */ jsx(
156
+ TableRow,
157
+ {
158
+ "data-state": row.getIsSelected() ? "selected" : void 0,
159
+ className: cn(onRowClick && "cursor-pointer"),
160
+ onClick: onRowClick ? () => onRowClick(row.original) : void 0,
161
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(DataCell, { cell }, cell.id))
162
+ }
163
+ );
164
+ }
165
+ function ExportButton({
166
+ columns,
167
+ data,
168
+ csvFilename
169
+ }) {
170
+ return /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => exportToCsv(columns, data, csvFilename), children: [
171
+ /* @__PURE__ */ jsx(LuDownload, { className: "mr-2 h-4 w-4" }),
172
+ "Export"
173
+ ] });
174
+ }
175
+ function BulkActionsBar({
176
+ selectedRows,
177
+ bulkActions
178
+ }) {
179
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
180
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-sm", children: [
181
+ selectedRows.length,
182
+ " selected"
183
+ ] }),
184
+ bulkActions(selectedRows)
185
+ ] });
186
+ }
187
+ function Toolbar({
188
+ searchable,
189
+ searchPlaceholder,
190
+ globalFilter,
191
+ onGlobalFilterChange,
192
+ selectedRows,
193
+ bulkActions,
194
+ toolbar,
195
+ exportCsv,
196
+ columns,
197
+ data,
198
+ csvFilename,
199
+ columnToggle,
200
+ table
201
+ }) {
202
+ const hasSelection = selectedRows.length > 0;
203
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
204
+ searchable && /* @__PURE__ */ jsx(
205
+ Input,
206
+ {
207
+ placeholder: searchPlaceholder,
208
+ value: globalFilter,
209
+ onChange: (e) => onGlobalFilterChange(e.target.value),
210
+ className: "max-w-sm"
211
+ }
212
+ ),
213
+ hasSelection && bulkActions && /* @__PURE__ */ jsx(BulkActionsBar, { selectedRows, bulkActions }),
214
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
215
+ toolbar,
216
+ exportCsv && /* @__PURE__ */ jsx(ExportButton, { columns, data, csvFilename }),
217
+ columnToggle && /* @__PURE__ */ jsx(ColumnToggle, { table })
218
+ ] })
219
+ ] });
220
+ }
221
+ function TableBodyContent({
222
+ loading,
223
+ colSpan,
224
+ emptyMessage,
225
+ rows,
226
+ onRowClick
227
+ }) {
228
+ if (loading) {
229
+ return /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan, className: "h-24 text-center", children: /* @__PURE__ */ jsx(Spinner, { size: "lg", className: "mx-auto" }) }) });
230
+ }
231
+ if (rows.length === 0) {
232
+ return /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan, className: "h-24 text-center", children: emptyMessage }) });
233
+ }
234
+ return /* @__PURE__ */ jsx(Fragment, { children: rows.map((row) => /* @__PURE__ */ jsx(DataRow, { row, onRowClick }, row.id)) });
235
+ }
236
+ function PaginationFooter({ pagination }) {
237
+ if (!pagination || pagination.totalPages <= 1) return null;
238
+ return /* @__PURE__ */ jsx(
239
+ ListPagination,
240
+ {
241
+ page: pagination.page,
242
+ totalPages: pagination.totalPages,
243
+ getHref: pagination.getHref,
244
+ onPageChange: pagination.onPageChange
245
+ }
246
+ );
247
+ }
248
+ function useDataTable(opts) {
249
+ const isManualFiltering = opts.externalGlobalFilter !== void 0;
250
+ const isManualSorting = opts.externalSorting !== void 0;
251
+ const [internalSorting, setInternalSorting] = React.useState([]);
252
+ const [internalGlobalFilter, setInternalGlobalFilter] = React.useState("");
253
+ const [internalRowSelection, setInternalRowSelection] = React.useState({});
254
+ const [columnVisibility, setColumnVisibility] = React.useState({});
255
+ const sorting = opts.externalSorting ?? internalSorting;
256
+ const globalFilter = opts.externalGlobalFilter ?? internalGlobalFilter;
257
+ const rowSelectionState = opts.externalRowSelection ?? internalRowSelection;
258
+ const handleSortingChange = opts.onSortingChange ? (updater) => {
259
+ opts.onSortingChange(updater(sorting));
260
+ } : setInternalSorting;
261
+ const handleGlobalFilterChange = opts.onGlobalFilterChange ?? setInternalGlobalFilter;
262
+ const handleRowSelectionChange = opts.onRowSelectionChange ? (updater) => {
263
+ opts.onRowSelectionChange(
264
+ updater(rowSelectionState)
265
+ );
266
+ } : setInternalRowSelection;
267
+ const tanstackColumns = React.useMemo(
268
+ () => buildTanstackColumns(opts.columns, opts.selectable),
269
+ [opts.columns, opts.selectable]
270
+ );
271
+ const table = useReactTable({
272
+ data: opts.data,
273
+ columns: tanstackColumns,
274
+ state: {
275
+ sorting,
276
+ globalFilter: isManualFiltering ? void 0 : globalFilter,
277
+ rowSelection: rowSelectionState,
278
+ columnVisibility
279
+ },
280
+ onSortingChange: handleSortingChange,
281
+ onGlobalFilterChange: isManualFiltering ? void 0 : setInternalGlobalFilter,
282
+ onRowSelectionChange: handleRowSelectionChange,
283
+ onColumnVisibilityChange: setColumnVisibility,
284
+ getCoreRowModel: getCoreRowModel(),
285
+ getSortedRowModel: isManualSorting ? void 0 : getSortedRowModel(),
286
+ getFilteredRowModel: isManualFiltering ? void 0 : getFilteredRowModel(),
287
+ manualSorting: isManualSorting,
288
+ manualFiltering: isManualFiltering,
289
+ manualPagination: opts.pagination !== void 0,
290
+ enableRowSelection: opts.selectable,
291
+ getRowId: opts.getRowId ? (row) => opts.getRowId(row) : void 0
292
+ });
293
+ const selectedRows = opts.selectable ? table.getSelectedRowModel().rows.map((r) => r.original) : [];
294
+ return { table, tanstackColumns, globalFilter, handleGlobalFilterChange, selectedRows };
39
295
  }
40
- var alignClass = {
41
- left: "text-left",
42
- center: "text-center",
43
- right: "text-right"
44
- };
45
296
  function DataTable({
46
297
  columns,
47
298
  data,
@@ -52,82 +303,70 @@ function DataTable({
52
303
  emptyMessage = "No results.",
53
304
  toolbar,
54
305
  className,
55
- getRowId
306
+ getRowId,
307
+ pagination,
308
+ globalFilter: externalGlobalFilter,
309
+ onGlobalFilterChange,
310
+ sortingState,
311
+ onSortingChange,
312
+ selectable = false,
313
+ rowSelection,
314
+ onRowSelectionChange,
315
+ bulkActions,
316
+ columnToggle = false,
317
+ exportCsv = false,
318
+ csvFilename = "export"
56
319
  }) {
57
- const [sorting, setSorting] = React.useState([]);
58
- const [globalFilter, setGlobalFilter] = React.useState("");
59
- const tanstackColumns = React.useMemo(() => buildTanstackColumns(columns), [columns]);
60
- const table = useReactTable({
320
+ const { table, tanstackColumns, globalFilter, handleGlobalFilterChange, selectedRows } = useDataTable({
321
+ columns,
61
322
  data,
62
- columns: tanstackColumns,
63
- state: { sorting, globalFilter },
64
- onSortingChange: setSorting,
65
- onGlobalFilterChange: setGlobalFilter,
66
- getCoreRowModel: getCoreRowModel(),
67
- getSortedRowModel: getSortedRowModel(),
68
- getFilteredRowModel: getFilteredRowModel(),
69
- getRowId: getRowId ? (row) => getRowId(row) : void 0
323
+ selectable,
324
+ getRowId,
325
+ pagination,
326
+ externalGlobalFilter,
327
+ onGlobalFilterChange,
328
+ externalSorting: sortingState,
329
+ onSortingChange,
330
+ externalRowSelection: rowSelection,
331
+ onRowSelectionChange
70
332
  });
333
+ const showToolbar = searchable || toolbar || columnToggle || exportCsv || selectedRows.length > 0;
71
334
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
72
- (searchable || toolbar) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
73
- searchable && /* @__PURE__ */ jsx(
74
- Input,
335
+ showToolbar && /* @__PURE__ */ jsx(
336
+ Toolbar,
337
+ {
338
+ searchable,
339
+ searchPlaceholder,
340
+ globalFilter,
341
+ onGlobalFilterChange: handleGlobalFilterChange,
342
+ selectedRows,
343
+ bulkActions,
344
+ toolbar,
345
+ exportCsv,
346
+ columns,
347
+ data,
348
+ csvFilename,
349
+ columnToggle,
350
+ table
351
+ }
352
+ ),
353
+ /* @__PURE__ */ jsxs(Table, { children: [
354
+ /* @__PURE__ */ jsx(TableHeader, { className: "bg-background sticky top-0 z-10", children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsx(TableRow, { children: hg.headers.map((h) => /* @__PURE__ */ jsx(HeaderCell, { header: h }, h.id)) }, hg.id)) }),
355
+ /* @__PURE__ */ jsx(TableBody, { children: /* @__PURE__ */ jsx(
356
+ TableBodyContent,
75
357
  {
76
- placeholder: searchPlaceholder,
77
- value: globalFilter,
78
- onChange: (e) => setGlobalFilter(e.target.value),
79
- className: "max-w-sm"
358
+ loading,
359
+ colSpan: tanstackColumns.length,
360
+ emptyMessage,
361
+ rows: table.getRowModel().rows,
362
+ onRowClick
80
363
  }
81
- ),
82
- toolbar
364
+ ) })
83
365
  ] }),
84
- /* @__PURE__ */ jsxs(Table, { children: [
85
- /* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
86
- const align = header.column.columnDef.meta?.align ?? "left";
87
- return /* @__PURE__ */ jsx(
88
- TableHead,
89
- {
90
- className: cn(
91
- alignClass[align],
92
- header.column.getCanSort() && "cursor-pointer select-none"
93
- ),
94
- onClick: header.column.getToggleSortingHandler(),
95
- children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
96
- flexRender(header.column.columnDef.header, header.getContext()),
97
- header.column.getCanSort() && /* @__PURE__ */ jsx(SortIndicator, { direction: header.column.getIsSorted() })
98
- ] })
99
- },
100
- header.id
101
- );
102
- }) }, headerGroup.id)) }),
103
- /* @__PURE__ */ jsx(TableBody, { children: loading ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: columns.length, className: "h-24 text-center", children: /* @__PURE__ */ jsx(Spinner, { size: "lg", className: "mx-auto" }) }) }) : table.getRowModel().rows.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: columns.length, className: "h-24 text-center", children: emptyMessage }) }) : table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(
104
- TableRow,
105
- {
106
- className: cn(onRowClick && "cursor-pointer"),
107
- onClick: onRowClick ? () => onRowClick(row.original) : void 0,
108
- children: row.getVisibleCells().map((cell) => {
109
- const align = cell.column.columnDef.meta?.align ?? "left";
110
- return /* @__PURE__ */ jsx(
111
- TableCell,
112
- {
113
- className: alignClass[align],
114
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
115
- },
116
- cell.id
117
- );
118
- })
119
- },
120
- row.id
121
- )) })
122
- ] })
366
+ /* @__PURE__ */ jsx(PaginationFooter, { pagination })
123
367
  ] });
124
368
  }
125
369
  DataTable.displayName = "DataTable";
126
- function SortIndicator({ direction }) {
127
- if (direction === "asc") return /* @__PURE__ */ jsx(LuArrowUp, { className: "h-4 w-4" });
128
- if (direction === "desc") return /* @__PURE__ */ jsx(LuArrowDown, { className: "h-4 w-4" });
129
- return /* @__PURE__ */ jsx(LuArrowUpDown, { className: "text-muted-foreground/50 h-4 w-4" });
130
- }
131
370
  export {
132
371
  DataTable
133
372
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/data-table.tsx"],"sourcesContent":["/** Sortable, searchable data table built on TanStack Table. */\n\"use client\";\n\nimport * as React from \"react\";\nimport {\n flexRender,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n useReactTable,\n type ColumnDef as TanstackColumnDef,\n type SortingState,\n} from \"@tanstack/react-table\";\nimport { cn } from \"@simpleapps-com/augur-utils/web\";\nimport { LuArrowDown, LuArrowUp, LuArrowUpDown } from \"react-icons/lu\";\nimport { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from \"./table\";\nimport { Input } from \"./input\";\nimport { Spinner } from \"./spinner\";\n\nexport interface ColumnDef<T> {\n /** Property key on the data object. */\n key: keyof T & string;\n /** Column header label. */\n header: string;\n /** Enable sorting on this column. */\n sortable?: boolean;\n /** Custom cell renderer. Receives the row data object. */\n render?: (row: T) => React.ReactNode;\n /** Text alignment. */\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport interface DataTableProps<T> {\n /** Column definitions describing each table column. */\n columns: ColumnDef<T>[];\n /** Data rows to display. */\n data: T[];\n /** Show a search input above the table. */\n searchable?: boolean;\n /** Placeholder text for the search input. */\n searchPlaceholder?: string;\n /** Callback when a row is clicked. Receives the row data. */\n onRowClick?: (row: T) => void;\n /** Show a loading spinner instead of data. */\n loading?: boolean;\n /** Message shown when data is empty. */\n emptyMessage?: string;\n /** Additional toolbar content rendered between search and table. */\n toolbar?: React.ReactNode;\n /** Additional class name for the root container. */\n className?: string;\n /** Row key extractor. Defaults to the row index. */\n getRowId?: (row: T) => string;\n}\n\nfunction buildTanstackColumns<T>(columns: ColumnDef<T>[]): TanstackColumnDef<T, unknown>[] {\n return columns.map((col) => ({\n id: col.key,\n accessorKey: col.key,\n header: col.header,\n enableSorting: col.sortable ?? false,\n cell: col.render\n ? (info) => col.render!(info.row.original)\n : (info) => String(info.getValue() ?? \"\"),\n meta: { align: col.align },\n }));\n}\n\nconst alignClass = {\n left: \"text-left\",\n center: \"text-center\",\n right: \"text-right\",\n} as const;\n\n/**\n * Data-driven table with sorting and search. Wraps TanStack Table with\n * the augur-web Table primitives.\n *\n * @example\n * ```tsx\n * const columns: ColumnDef<Brand>[] = [\n * { key: \"brandsUid\", header: \"UID\", sortable: true },\n * { key: \"brandsName\", header: \"Name\", sortable: true },\n * { key: \"brandsDesc\", header: \"Description\" },\n * ];\n *\n * <DataTable columns={columns} data={brands} searchable onRowClick={selectBrand} />\n * ```\n */\nfunction DataTable<T>({\n columns,\n data,\n searchable = false,\n searchPlaceholder = \"Search...\",\n onRowClick,\n loading = false,\n emptyMessage = \"No results.\",\n toolbar,\n className,\n getRowId,\n}: DataTableProps<T>) {\n const [sorting, setSorting] = React.useState<SortingState>([]);\n const [globalFilter, setGlobalFilter] = React.useState(\"\");\n\n const tanstackColumns = React.useMemo(() => buildTanstackColumns(columns), [columns]);\n\n const table = useReactTable({\n data,\n columns: tanstackColumns,\n state: { sorting, globalFilter },\n onSortingChange: setSorting,\n onGlobalFilterChange: setGlobalFilter,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: getSortedRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n getRowId: getRowId ? (row) => getRowId(row) : undefined,\n });\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n {(searchable || toolbar) && (\n <div className=\"flex items-center gap-4\">\n {searchable && (\n <Input\n placeholder={searchPlaceholder}\n value={globalFilter}\n onChange={(e) => setGlobalFilter(e.target.value)}\n className=\"max-w-sm\"\n />\n )}\n {toolbar}\n </div>\n )}\n <Table>\n <TableHeader>\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers.map((header) => {\n const align =\n (header.column.columnDef.meta as { align?: string } | undefined)?.align ?? \"left\";\n return (\n <TableHead\n key={header.id}\n className={cn(\n alignClass[align as keyof typeof alignClass],\n header.column.getCanSort() && \"cursor-pointer select-none\",\n )}\n onClick={header.column.getToggleSortingHandler()}\n >\n <span className=\"inline-flex items-center gap-1\">\n {flexRender(header.column.columnDef.header, header.getContext())}\n {header.column.getCanSort() && (\n <SortIndicator direction={header.column.getIsSorted()} />\n )}\n </span>\n </TableHead>\n );\n })}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n {loading ? (\n <TableRow>\n <TableCell colSpan={columns.length} className=\"h-24 text-center\">\n <Spinner size=\"lg\" className=\"mx-auto\" />\n </TableCell>\n </TableRow>\n ) : table.getRowModel().rows.length === 0 ? (\n <TableRow>\n <TableCell colSpan={columns.length} className=\"h-24 text-center\">\n {emptyMessage}\n </TableCell>\n </TableRow>\n ) : (\n table.getRowModel().rows.map((row) => (\n <TableRow\n key={row.id}\n className={cn(onRowClick && \"cursor-pointer\")}\n onClick={onRowClick ? () => onRowClick(row.original) : undefined}\n >\n {row.getVisibleCells().map((cell) => {\n const align =\n (cell.column.columnDef.meta as { align?: string } | undefined)?.align ?? \"left\";\n return (\n <TableCell\n key={cell.id}\n className={alignClass[align as keyof typeof alignClass]}\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </TableCell>\n );\n })}\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n );\n}\nDataTable.displayName = \"DataTable\";\n\nfunction SortIndicator({ direction }: { direction: false | \"asc\" | \"desc\" }) {\n if (direction === \"asc\") return <LuArrowUp className=\"h-4 w-4\" />;\n if (direction === \"desc\") return <LuArrowDown className=\"h-4 w-4\" />;\n return <LuArrowUpDown className=\"text-muted-foreground/50 h-4 w-4\" />;\n}\n\nexport { DataTable };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAGA,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,UAAU;AACnB,SAAS,aAAa,WAAW,qBAAqB;AA2G9C,SAEI,KAFJ;AAlER,SAAS,qBAAwB,SAA0D;AACzF,SAAO,QAAQ,IAAI,CAAC,SAAS;AAAA,IAC3B,IAAI,IAAI;AAAA,IACR,aAAa,IAAI;AAAA,IACjB,QAAQ,IAAI;AAAA,IACZ,eAAe,IAAI,YAAY;AAAA,IAC/B,MAAM,IAAI,SACN,CAAC,SAAS,IAAI,OAAQ,KAAK,IAAI,QAAQ,IACvC,CAAC,SAAS,OAAO,KAAK,SAAS,KAAK,EAAE;AAAA,IAC1C,MAAM,EAAE,OAAO,IAAI,MAAM;AAAA,EAC3B,EAAE;AACJ;AAEA,IAAM,aAAa;AAAA,EACjB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AACT;AAiBA,SAAS,UAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB;AAAA,EACA,UAAU;AAAA,EACV,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,CAAC,SAAS,UAAU,IAAU,eAAuB,CAAC,CAAC;AAC7D,QAAM,CAAC,cAAc,eAAe,IAAU,eAAS,EAAE;AAEzD,QAAM,kBAAwB,cAAQ,MAAM,qBAAqB,OAAO,GAAG,CAAC,OAAO,CAAC;AAEpF,QAAM,QAAQ,cAAc;AAAA,IAC1B;AAAA,IACA,SAAS;AAAA,IACT,OAAO,EAAE,SAAS,aAAa;AAAA,IAC/B,iBAAiB;AAAA,IACjB,sBAAsB;AAAA,IACtB,iBAAiB,gBAAgB;AAAA,IACjC,mBAAmB,kBAAkB;AAAA,IACrC,qBAAqB,oBAAoB;AAAA,IACzC,UAAU,WAAW,CAAC,QAAQ,SAAS,GAAG,IAAI;AAAA,EAChD,CAAC;AAED,SACE,qBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GACrC;AAAA,mBAAc,YACd,qBAAC,SAAI,WAAU,2BACZ;AAAA,oBACC;AAAA,QAAC;AAAA;AAAA,UACC,aAAa;AAAA,UACb,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAK;AAAA,UAC/C,WAAU;AAAA;AAAA,MACZ;AAAA,MAED;AAAA,OACH;AAAA,IAEF,qBAAC,SACC;AAAA,0BAAC,eACE,gBAAM,gBAAgB,EAAE,IAAI,CAAC,gBAC5B,oBAAC,YACE,sBAAY,QAAQ,IAAI,CAAC,WAAW;AACnC,cAAM,QACH,OAAO,OAAO,UAAU,MAAyC,SAAS;AAC7E,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW;AAAA,cACT,WAAW,KAAgC;AAAA,cAC3C,OAAO,OAAO,WAAW,KAAK;AAAA,YAChC;AAAA,YACA,SAAS,OAAO,OAAO,wBAAwB;AAAA,YAE/C,+BAAC,UAAK,WAAU,kCACb;AAAA,yBAAW,OAAO,OAAO,UAAU,QAAQ,OAAO,WAAW,CAAC;AAAA,cAC9D,OAAO,OAAO,WAAW,KACxB,oBAAC,iBAAc,WAAW,OAAO,OAAO,YAAY,GAAG;AAAA,eAE3D;AAAA;AAAA,UAZK,OAAO;AAAA,QAad;AAAA,MAEJ,CAAC,KArBY,YAAY,EAsB3B,CACD,GACH;AAAA,MACA,oBAAC,aACE,oBACC,oBAAC,YACC,8BAAC,aAAU,SAAS,QAAQ,QAAQ,WAAU,oBAC5C,8BAAC,WAAQ,MAAK,MAAK,WAAU,WAAU,GACzC,GACF,IACE,MAAM,YAAY,EAAE,KAAK,WAAW,IACtC,oBAAC,YACC,8BAAC,aAAU,SAAS,QAAQ,QAAQ,WAAU,oBAC3C,wBACH,GACF,IAEA,MAAM,YAAY,EAAE,KAAK,IAAI,CAAC,QAC5B;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,GAAG,cAAc,gBAAgB;AAAA,UAC5C,SAAS,aAAa,MAAM,WAAW,IAAI,QAAQ,IAAI;AAAA,UAEtD,cAAI,gBAAgB,EAAE,IAAI,CAAC,SAAS;AACnC,kBAAM,QACH,KAAK,OAAO,UAAU,MAAyC,SAAS;AAC3E,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,WAAW,WAAW,KAAgC;AAAA,gBAErD,qBAAW,KAAK,OAAO,UAAU,MAAM,KAAK,WAAW,CAAC;AAAA;AAAA,cAHpD,KAAK;AAAA,YAIZ;AAAA,UAEJ,CAAC;AAAA;AAAA,QAfI,IAAI;AAAA,MAgBX,CACD,GAEL;AAAA,OACF;AAAA,KACF;AAEJ;AACA,UAAU,cAAc;AAExB,SAAS,cAAc,EAAE,UAAU,GAA0C;AAC3E,MAAI,cAAc,MAAO,QAAO,oBAAC,aAAU,WAAU,WAAU;AAC/D,MAAI,cAAc,OAAQ,QAAO,oBAAC,eAAY,WAAU,WAAU;AAClE,SAAO,oBAAC,iBAAc,WAAU,oCAAmC;AACrE;","names":[]}
1
+ {"version":3,"sources":["../src/data-table.tsx"],"sourcesContent":["/** Feature-complete admin data table built on TanStack Table. */\n\"use client\";\n\nimport * as React from \"react\";\nimport {\n flexRender,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n useReactTable,\n type ColumnDef as TanstackColumnDef,\n type SortingState,\n type RowSelectionState,\n type VisibilityState,\n type Table as TanstackTable,\n type Header,\n type Row,\n type Cell,\n} from \"@tanstack/react-table\";\nimport { cn } from \"@simpleapps-com/augur-utils/web\";\nimport { LuArrowDown, LuArrowUp, LuArrowUpDown, LuDownload, LuSettings2 } from \"react-icons/lu\";\nimport { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from \"./table\";\nimport { Input } from \"./input\";\nimport { Spinner } from \"./spinner\";\nimport { Checkbox } from \"./checkbox\";\nimport { Button } from \"./button\";\nimport { ListPagination } from \"./pagination\";\nimport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuCheckboxItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"./dropdown-menu\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface ColumnDef<T> {\n key: keyof T & string;\n header: string;\n sortable?: boolean;\n render?: (row: T) => React.ReactNode;\n align?: \"left\" | \"center\" | \"right\";\n hideable?: boolean;\n}\n\nexport interface DataTablePagination {\n page: number;\n totalPages: number;\n getHref: (page: number) => string;\n onPageChange?: (page: number) => void;\n}\n\nexport interface DataTableProps<T> {\n columns: ColumnDef<T>[];\n data: T[];\n searchable?: boolean;\n searchPlaceholder?: string;\n onRowClick?: (row: T) => void;\n loading?: boolean;\n emptyMessage?: string;\n toolbar?: React.ReactNode;\n className?: string;\n getRowId?: (row: T) => string;\n pagination?: DataTablePagination;\n globalFilter?: string;\n onGlobalFilterChange?: (value: string) => void;\n sortingState?: SortingState;\n onSortingChange?: (state: SortingState) => void;\n selectable?: boolean;\n rowSelection?: RowSelectionState;\n onRowSelectionChange?: (state: RowSelectionState) => void;\n bulkActions?: (selectedRows: T[]) => React.ReactNode;\n columnToggle?: boolean;\n exportCsv?: boolean;\n csvFilename?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst ALIGN_CLASS = { left: \"text-left\", center: \"text-center\", right: \"text-right\" } as const;\n\nfunction getAlignCn(meta: unknown): string {\n const align = (meta as { align?: string } | undefined)?.align ?? \"left\";\n return ALIGN_CLASS[align as keyof typeof ALIGN_CLASS];\n}\n\nfunction buildTanstackColumns<T>(\n columns: ColumnDef<T>[],\n selectable: boolean,\n): TanstackColumnDef<T, unknown>[] {\n const cols: TanstackColumnDef<T, unknown>[] = [];\n if (selectable) {\n cols.push({\n id: \"_select\",\n header: ({ table }) => (\n <Checkbox\n checked={table.getIsAllPageRowsSelected()}\n onCheckedChange={(v) => table.toggleAllPageRowsSelected(!!v)}\n aria-label=\"Select all\"\n />\n ),\n cell: ({ row }) => (\n <Checkbox\n checked={row.getIsSelected()}\n onCheckedChange={(v) => row.toggleSelected(!!v)}\n aria-label=\"Select row\"\n onClick={(e) => e.stopPropagation()}\n />\n ),\n enableSorting: false,\n enableHiding: false,\n meta: { align: \"center\" },\n });\n }\n for (const col of columns) {\n cols.push({\n id: col.key,\n accessorKey: col.key,\n header: col.header,\n enableSorting: col.sortable ?? false,\n enableHiding: col.hideable !== false,\n cell: col.render\n ? (info) => col.render!(info.row.original)\n : (info) => String(info.getValue() ?? \"\"),\n meta: { align: col.align },\n });\n }\n return cols;\n}\n\nfunction exportToCsv<T>(columns: ColumnDef<T>[], data: T[], filename: string) {\n const headers = columns.map((c) => c.header);\n const rows = data.map((row) =>\n columns.map((col) => {\n const str = String(row[col.key] ?? \"\");\n return str.includes(\",\") || str.includes('\"') || str.includes(\"\\n\")\n ? `\"${str.replace(/\"/g, '\"\"')}\"`\n : str;\n }),\n );\n const csv = [headers.join(\",\"), ...rows.map((r) => r.join(\",\"))].join(\"\\n\");\n const blob = new Blob([csv], { type: \"text/csv;charset=utf-8;\" });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = `${filename}.csv`;\n link.click();\n URL.revokeObjectURL(url);\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction SortIndicator({ direction }: { direction: false | \"asc\" | \"desc\" }) {\n if (direction === \"asc\") return <LuArrowUp className=\"h-4 w-4\" />;\n if (direction === \"desc\") return <LuArrowDown className=\"h-4 w-4\" />;\n return <LuArrowUpDown className=\"text-muted-foreground/50 h-4 w-4\" />;\n}\n\nfunction ColumnToggle<T>({ table }: { table: TanstackTable<T> }) {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\">\n <LuSettings2 className=\"mr-2 h-4 w-4\" />\n Columns\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>\n <DropdownMenuSeparator />\n {table\n .getAllColumns()\n .filter((col) => col.getCanHide())\n .map((col) => (\n <DropdownMenuCheckboxItem\n key={col.id}\n checked={col.getIsVisible()}\n onCheckedChange={(v) => col.toggleVisibility(!!v)}\n >\n {col.columnDef.header as string}\n </DropdownMenuCheckboxItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction HeaderCell<T>({ header }: { header: Header<T, unknown> }) {\n return (\n <TableHead\n className={cn(\n getAlignCn(header.column.columnDef.meta),\n header.column.getCanSort() && \"cursor-pointer select-none\",\n )}\n onClick={header.column.getToggleSortingHandler()}\n >\n <span className=\"inline-flex items-center gap-1\">\n {flexRender(header.column.columnDef.header, header.getContext())}\n {header.column.getCanSort() && <SortIndicator direction={header.column.getIsSorted()} />}\n </span>\n </TableHead>\n );\n}\n\nfunction DataCell<T>({ cell }: { cell: Cell<T, unknown> }) {\n return (\n <TableCell className={getAlignCn(cell.column.columnDef.meta)}>\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </TableCell>\n );\n}\n\nfunction DataRow<T>({ row, onRowClick }: { row: Row<T>; onRowClick?: (r: T) => void }) {\n return (\n <TableRow\n data-state={row.getIsSelected() ? \"selected\" : undefined}\n className={cn(onRowClick && \"cursor-pointer\")}\n onClick={onRowClick ? () => onRowClick(row.original) : undefined}\n >\n {row.getVisibleCells().map((cell) => (\n <DataCell key={cell.id} cell={cell} />\n ))}\n </TableRow>\n );\n}\n\nfunction ExportButton<T>({\n columns,\n data,\n csvFilename,\n}: {\n columns: ColumnDef<T>[];\n data: T[];\n csvFilename: string;\n}) {\n return (\n <Button variant=\"outline\" size=\"sm\" onClick={() => exportToCsv(columns, data, csvFilename)}>\n <LuDownload className=\"mr-2 h-4 w-4\" />\n Export\n </Button>\n );\n}\n\nfunction BulkActionsBar<T>({\n selectedRows,\n bulkActions,\n}: {\n selectedRows: T[];\n bulkActions: (rows: T[]) => React.ReactNode;\n}) {\n return (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground text-sm\">{selectedRows.length} selected</span>\n {bulkActions(selectedRows)}\n </div>\n );\n}\n\nfunction Toolbar<T>({\n searchable,\n searchPlaceholder,\n globalFilter,\n onGlobalFilterChange,\n selectedRows,\n bulkActions,\n toolbar,\n exportCsv,\n columns,\n data,\n csvFilename,\n columnToggle,\n table,\n}: {\n searchable: boolean;\n searchPlaceholder: string;\n globalFilter: string;\n onGlobalFilterChange: (value: string) => void;\n selectedRows: T[];\n bulkActions?: (rows: T[]) => React.ReactNode;\n toolbar?: React.ReactNode;\n exportCsv: boolean;\n columns: ColumnDef<T>[];\n data: T[];\n csvFilename: string;\n columnToggle: boolean;\n table: TanstackTable<T>;\n}) {\n const hasSelection = selectedRows.length > 0;\n return (\n <div className=\"flex items-center gap-4\">\n {searchable && (\n <Input\n placeholder={searchPlaceholder}\n value={globalFilter}\n onChange={(e) => onGlobalFilterChange(e.target.value)}\n className=\"max-w-sm\"\n />\n )}\n {hasSelection && bulkActions && (\n <BulkActionsBar selectedRows={selectedRows} bulkActions={bulkActions} />\n )}\n <div className=\"ml-auto flex items-center gap-2\">\n {toolbar}\n {exportCsv && <ExportButton columns={columns} data={data} csvFilename={csvFilename} />}\n {columnToggle && <ColumnToggle table={table} />}\n </div>\n </div>\n );\n}\n\nfunction TableBodyContent<T>({\n loading,\n colSpan,\n emptyMessage,\n rows,\n onRowClick,\n}: {\n loading: boolean;\n colSpan: number;\n emptyMessage: string;\n rows: Row<T>[];\n onRowClick?: (r: T) => void;\n}) {\n if (loading) {\n return (\n <TableRow>\n <TableCell colSpan={colSpan} className=\"h-24 text-center\">\n <Spinner size=\"lg\" className=\"mx-auto\" />\n </TableCell>\n </TableRow>\n );\n }\n if (rows.length === 0) {\n return (\n <TableRow>\n <TableCell colSpan={colSpan} className=\"h-24 text-center\">\n {emptyMessage}\n </TableCell>\n </TableRow>\n );\n }\n return (\n <>\n {rows.map((row) => (\n <DataRow key={row.id} row={row} onRowClick={onRowClick} />\n ))}\n </>\n );\n}\n\nfunction PaginationFooter({ pagination }: { pagination?: DataTablePagination }) {\n if (!pagination || pagination.totalPages <= 1) return null;\n return (\n <ListPagination\n page={pagination.page}\n totalPages={pagination.totalPages}\n getHref={pagination.getHref}\n onPageChange={pagination.onPageChange}\n />\n );\n}\n\n// ---------------------------------------------------------------------------\n// Hook: encapsulates all state management and TanStack Table configuration\n// ---------------------------------------------------------------------------\n\ninterface UseDataTableOptions<T> {\n columns: ColumnDef<T>[];\n data: T[];\n selectable: boolean;\n getRowId?: (row: T) => string;\n pagination?: DataTablePagination;\n externalGlobalFilter?: string;\n onGlobalFilterChange?: (value: string) => void;\n externalSorting?: SortingState;\n onSortingChange?: (state: SortingState) => void;\n externalRowSelection?: RowSelectionState;\n onRowSelectionChange?: (state: RowSelectionState) => void;\n}\n\nfunction useDataTable<T>(opts: UseDataTableOptions<T>) {\n const isManualFiltering = opts.externalGlobalFilter !== undefined;\n const isManualSorting = opts.externalSorting !== undefined;\n\n const [internalSorting, setInternalSorting] = React.useState<SortingState>([]);\n const [internalGlobalFilter, setInternalGlobalFilter] = React.useState(\"\");\n const [internalRowSelection, setInternalRowSelection] = React.useState<RowSelectionState>({});\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});\n\n const sorting = opts.externalSorting ?? internalSorting;\n const globalFilter = opts.externalGlobalFilter ?? internalGlobalFilter;\n const rowSelectionState = opts.externalRowSelection ?? internalRowSelection;\n\n const handleSortingChange = opts.onSortingChange\n ? (updater: SortingState | ((prev: SortingState) => SortingState)) => {\n opts.onSortingChange!((updater as (prev: SortingState) => SortingState)(sorting));\n }\n : setInternalSorting;\n\n const handleGlobalFilterChange = opts.onGlobalFilterChange ?? setInternalGlobalFilter;\n\n const handleRowSelectionChange = opts.onRowSelectionChange\n ? (updater: RowSelectionState | ((prev: RowSelectionState) => RowSelectionState)) => {\n opts.onRowSelectionChange!(\n (updater as (prev: RowSelectionState) => RowSelectionState)(rowSelectionState),\n );\n }\n : setInternalRowSelection;\n\n const tanstackColumns = React.useMemo(\n () => buildTanstackColumns(opts.columns, opts.selectable),\n [opts.columns, opts.selectable],\n );\n\n const table = useReactTable({\n data: opts.data,\n columns: tanstackColumns,\n state: {\n sorting,\n globalFilter: isManualFiltering ? undefined : globalFilter,\n rowSelection: rowSelectionState,\n columnVisibility,\n },\n onSortingChange: handleSortingChange,\n onGlobalFilterChange: isManualFiltering ? undefined : setInternalGlobalFilter,\n onRowSelectionChange: handleRowSelectionChange,\n onColumnVisibilityChange: setColumnVisibility,\n getCoreRowModel: getCoreRowModel(),\n getSortedRowModel: isManualSorting ? undefined : getSortedRowModel(),\n getFilteredRowModel: isManualFiltering ? undefined : getFilteredRowModel(),\n manualSorting: isManualSorting,\n manualFiltering: isManualFiltering,\n manualPagination: opts.pagination !== undefined,\n enableRowSelection: opts.selectable,\n getRowId: opts.getRowId ? (row) => opts.getRowId!(row) : undefined,\n });\n\n const selectedRows = opts.selectable\n ? table.getSelectedRowModel().rows.map((r) => r.original)\n : [];\n\n return { table, tanstackColumns, globalFilter, handleGlobalFilterChange, selectedRows };\n}\n\n// ---------------------------------------------------------------------------\n// Main component\n// ---------------------------------------------------------------------------\n\nfunction DataTable<T>({\n columns,\n data,\n searchable = false,\n searchPlaceholder = \"Search...\",\n onRowClick,\n loading = false,\n emptyMessage = \"No results.\",\n toolbar,\n className,\n getRowId,\n pagination,\n globalFilter: externalGlobalFilter,\n onGlobalFilterChange,\n sortingState,\n onSortingChange,\n selectable = false,\n rowSelection,\n onRowSelectionChange,\n bulkActions,\n columnToggle = false,\n exportCsv = false,\n csvFilename = \"export\",\n}: DataTableProps<T>) {\n const { table, tanstackColumns, globalFilter, handleGlobalFilterChange, selectedRows } =\n useDataTable({\n columns,\n data,\n selectable,\n getRowId,\n pagination,\n externalGlobalFilter,\n onGlobalFilterChange,\n externalSorting: sortingState,\n onSortingChange,\n externalRowSelection: rowSelection,\n onRowSelectionChange,\n });\n\n const showToolbar = searchable || toolbar || columnToggle || exportCsv || selectedRows.length > 0;\n\n return (\n <div className={cn(\"space-y-4\", className)}>\n {showToolbar && (\n <Toolbar\n searchable={searchable}\n searchPlaceholder={searchPlaceholder}\n globalFilter={globalFilter}\n onGlobalFilterChange={handleGlobalFilterChange}\n selectedRows={selectedRows}\n bulkActions={bulkActions}\n toolbar={toolbar}\n exportCsv={exportCsv}\n columns={columns}\n data={data}\n csvFilename={csvFilename}\n columnToggle={columnToggle}\n table={table}\n />\n )}\n <Table>\n <TableHeader className=\"bg-background sticky top-0 z-10\">\n {table.getHeaderGroups().map((hg) => (\n <TableRow key={hg.id}>\n {hg.headers.map((h) => (\n <HeaderCell key={h.id} header={h} />\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n <TableBodyContent\n loading={loading}\n colSpan={tanstackColumns.length}\n emptyMessage={emptyMessage}\n rows={table.getRowModel().rows}\n onRowClick={onRowClick}\n />\n </TableBody>\n </Table>\n <PaginationFooter pagination={pagination} />\n </div>\n );\n}\nDataTable.displayName = \"DataTable\";\n\nexport { DataTable };\nexport type { SortingState, RowSelectionState, VisibilityState };\n"],"mappings":"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","names":[]}