@simpleapps-com/augur-web 2.2.23 → 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.
Files changed (89) hide show
  1. package/dist/checkbox.cjs +4 -21
  2. package/dist/checkbox.cjs.map +1 -1
  3. package/dist/checkbox.js +3 -20
  4. package/dist/checkbox.js.map +1 -1
  5. package/dist/chunk-32XB3REY.cjs +34 -0
  6. package/dist/chunk-32XB3REY.cjs.map +1 -0
  7. package/dist/chunk-3UFEQSTN.js +26 -0
  8. package/dist/chunk-3UFEQSTN.js.map +1 -0
  9. package/dist/chunk-3VN3FX2Q.js +165 -0
  10. package/dist/chunk-3VN3FX2Q.js.map +1 -0
  11. package/dist/chunk-JXW4XE2P.cjs +143 -0
  12. package/dist/chunk-JXW4XE2P.cjs.map +1 -0
  13. package/dist/chunk-KHIHHL3A.js +52 -0
  14. package/dist/chunk-KHIHHL3A.js.map +1 -0
  15. package/dist/chunk-MOC5N2N4.js +143 -0
  16. package/dist/chunk-MOC5N2N4.js.map +1 -0
  17. package/dist/chunk-NEY26NNF.cjs +75 -0
  18. package/dist/chunk-NEY26NNF.cjs.map +1 -0
  19. package/dist/chunk-NSSX7USQ.cjs +165 -0
  20. package/dist/chunk-NSSX7USQ.cjs.map +1 -0
  21. package/dist/chunk-RGJT67PN.js +34 -0
  22. package/dist/chunk-RGJT67PN.js.map +1 -0
  23. package/dist/chunk-UB7HYAAC.js +34 -0
  24. package/dist/chunk-UB7HYAAC.js.map +1 -0
  25. package/dist/chunk-UFL6PVLI.cjs +52 -0
  26. package/dist/chunk-UFL6PVLI.cjs.map +1 -0
  27. package/dist/chunk-WFMP6TZN.js +51 -0
  28. package/dist/chunk-WFMP6TZN.js.map +1 -0
  29. package/dist/chunk-WJUDOEVB.cjs +51 -0
  30. package/dist/chunk-WJUDOEVB.cjs.map +1 -0
  31. package/dist/chunk-XGKBDPGD.cjs +26 -0
  32. package/dist/chunk-XGKBDPGD.cjs.map +1 -0
  33. package/dist/chunk-XIKSI7NY.js +75 -0
  34. package/dist/chunk-XIKSI7NY.js.map +1 -0
  35. package/dist/chunk-Z57QBVUC.cjs +34 -0
  36. package/dist/chunk-Z57QBVUC.cjs.map +1 -0
  37. package/dist/confirm-dialog.cjs +49 -0
  38. package/dist/confirm-dialog.cjs.map +1 -0
  39. package/dist/confirm-dialog.d.cts +35 -0
  40. package/dist/confirm-dialog.d.ts +35 -0
  41. package/dist/confirm-dialog.js +49 -0
  42. package/dist/confirm-dialog.js.map +1 -0
  43. package/dist/data-table.cjs +373 -0
  44. package/dist/data-table.cjs.map +1 -0
  45. package/dist/data-table.d.cts +49 -0
  46. package/dist/data-table.d.ts +49 -0
  47. package/dist/data-table.js +373 -0
  48. package/dist/data-table.js.map +1 -0
  49. package/dist/detail-panel.cjs +142 -0
  50. package/dist/detail-panel.cjs.map +1 -0
  51. package/dist/detail-panel.d.cts +59 -0
  52. package/dist/detail-panel.d.ts +59 -0
  53. package/dist/detail-panel.js +142 -0
  54. package/dist/detail-panel.js.map +1 -0
  55. package/dist/dropdown-menu.cjs +34 -140
  56. package/dist/dropdown-menu.cjs.map +1 -1
  57. package/dist/dropdown-menu.js +17 -123
  58. package/dist/dropdown-menu.js.map +1 -1
  59. package/dist/form-input.cjs +5 -28
  60. package/dist/form-input.cjs.map +1 -1
  61. package/dist/form-input.js +4 -27
  62. package/dist/form-input.js.map +1 -1
  63. package/dist/form-select.cjs +5 -45
  64. package/dist/form-select.cjs.map +1 -1
  65. package/dist/form-select.js +4 -44
  66. package/dist/form-select.js.map +1 -1
  67. package/dist/form-textarea.cjs +5 -28
  68. package/dist/form-textarea.cjs.map +1 -1
  69. package/dist/form-textarea.js +4 -27
  70. package/dist/form-textarea.js.map +1 -1
  71. package/dist/pagination.cjs +22 -161
  72. package/dist/pagination.cjs.map +1 -1
  73. package/dist/pagination.js +10 -149
  74. package/dist/pagination.js.map +1 -1
  75. package/dist/resource-tabs.cjs +25 -0
  76. package/dist/resource-tabs.cjs.map +1 -0
  77. package/dist/resource-tabs.d.cts +33 -0
  78. package/dist/resource-tabs.d.ts +33 -0
  79. package/dist/resource-tabs.js +25 -0
  80. package/dist/resource-tabs.js.map +1 -0
  81. package/dist/table.cjs +20 -72
  82. package/dist/table.cjs.map +1 -1
  83. package/dist/table.js +10 -62
  84. package/dist/table.js.map +1 -1
  85. package/dist/tabs.cjs +12 -49
  86. package/dist/tabs.cjs.map +1 -1
  87. package/dist/tabs.js +6 -43
  88. package/dist/tabs.js.map +1 -1
  89. package/package.json +29 -7
@@ -0,0 +1,373 @@
1
+ "use client";
2
+ "use client";
3
+ import {
4
+ Table,
5
+ TableBody,
6
+ TableCell,
7
+ TableHead,
8
+ TableHeader,
9
+ TableRow
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";
22
+ import {
23
+ Input
24
+ } from "./chunk-HXQF6XTL.js";
25
+ import {
26
+ Spinner
27
+ } from "./chunk-UMNTUD2P.js";
28
+ import {
29
+ Button
30
+ } from "./chunk-QICSGVX3.js";
31
+ import {
32
+ Checkbox
33
+ } from "./chunk-3UFEQSTN.js";
34
+
35
+ // src/data-table.tsx
36
+ import * as React from "react";
37
+ import {
38
+ flexRender,
39
+ getCoreRowModel,
40
+ getSortedRowModel,
41
+ getFilteredRowModel,
42
+ useReactTable
43
+ } from "@tanstack/react-table";
44
+ import { cn } from "@simpleapps-com/augur-utils/web";
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 };
295
+ }
296
+ function DataTable({
297
+ columns,
298
+ data,
299
+ searchable = false,
300
+ searchPlaceholder = "Search...",
301
+ onRowClick,
302
+ loading = false,
303
+ emptyMessage = "No results.",
304
+ toolbar,
305
+ className,
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"
319
+ }) {
320
+ const { table, tanstackColumns, globalFilter, handleGlobalFilterChange, selectedRows } = useDataTable({
321
+ columns,
322
+ data,
323
+ selectable,
324
+ getRowId,
325
+ pagination,
326
+ externalGlobalFilter,
327
+ onGlobalFilterChange,
328
+ externalSorting: sortingState,
329
+ onSortingChange,
330
+ externalRowSelection: rowSelection,
331
+ onRowSelectionChange
332
+ });
333
+ const showToolbar = searchable || toolbar || columnToggle || exportCsv || selectedRows.length > 0;
334
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
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,
357
+ {
358
+ loading,
359
+ colSpan: tanstackColumns.length,
360
+ emptyMessage,
361
+ rows: table.getRowModel().rows,
362
+ onRowClick
363
+ }
364
+ ) })
365
+ ] }),
366
+ /* @__PURE__ */ jsx(PaginationFooter, { pagination })
367
+ ] });
368
+ }
369
+ DataTable.displayName = "DataTable";
370
+ export {
371
+ DataTable
372
+ };
373
+ //# sourceMappingURL=data-table.js.map
@@ -0,0 +1 @@
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":[]}
@@ -0,0 +1,142 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }"use client";
2
+ "use client";
3
+
4
+
5
+ var _chunkWJUDOEVBcjs = require('./chunk-WJUDOEVB.cjs');
6
+
7
+
8
+ var _chunkZ57QBVUCcjs = require('./chunk-Z57QBVUC.cjs');
9
+ require('./chunk-5ZITWMV4.cjs');
10
+
11
+
12
+ var _chunk32XB3REYcjs = require('./chunk-32XB3REY.cjs');
13
+ require('./chunk-BXMTQGKV.cjs');
14
+ require('./chunk-76RSVRGB.cjs');
15
+ require('./chunk-7EXXNELX.cjs');
16
+ require('./chunk-MAENILZP.cjs');
17
+
18
+
19
+ var _chunk5VMEEKZ5cjs = require('./chunk-5VMEEKZ5.cjs');
20
+
21
+
22
+ var _chunkVL6L4GDAcjs = require('./chunk-VL6L4GDA.cjs');
23
+
24
+ // src/detail-panel.tsx
25
+ var _react = require('react'); var React = _interopRequireWildcard(_react);
26
+ var _web = require('@simpleapps-com/augur-utils/web');
27
+ var _jsxruntime = require('react/jsx-runtime');
28
+ function DetailPanel({
29
+ fields,
30
+ data,
31
+ onSave,
32
+ onCancel,
33
+ loading = false,
34
+ title,
35
+ className,
36
+ defaultEditing = false
37
+ }) {
38
+ const [editing, setEditing] = React.useState(defaultEditing);
39
+ const [draft, setDraft] = React.useState(data);
40
+ React.useEffect(() => {
41
+ setDraft(data);
42
+ }, [data]);
43
+ const handleEdit = () => {
44
+ setDraft(data);
45
+ setEditing(true);
46
+ };
47
+ const handleCancel = () => {
48
+ setDraft(data);
49
+ setEditing(false);
50
+ _optionalChain([onCancel, 'optionalCall', _ => _()]);
51
+ };
52
+ const handleSave = () => {
53
+ onSave(draft);
54
+ };
55
+ const updateField = (key, value) => {
56
+ setDraft((prev) => ({ ...prev, [key]: value }));
57
+ };
58
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: _web.cn.call(void 0, "space-y-6", className), children: [
59
+ (title || !editing) && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-between", children: [
60
+ title && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "text-lg font-semibold", children: title }),
61
+ !editing && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVL6L4GDAcjs.Button, { variant: "outline", size: "sm", onClick: handleEdit, children: "Edit" })
62
+ ] }),
63
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "grid gap-4 sm:grid-cols-2", children: fields.map(
64
+ (field) => editing ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
65
+ EditField,
66
+ {
67
+ field,
68
+ value: draft[field.key],
69
+ onChange: updateField
70
+ },
71
+ field.key
72
+ ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ReadField, { field, value: data[field.key] }, field.key)
73
+ ) }),
74
+ editing && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex gap-2", children: [
75
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkVL6L4GDAcjs.Button, { onClick: handleSave, disabled: loading, children: [
76
+ loading && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk5VMEEKZ5cjs.Spinner, { size: "sm", className: "mr-2" }),
77
+ "Save"
78
+ ] }),
79
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVL6L4GDAcjs.Button, { variant: "outline", onClick: handleCancel, disabled: loading, children: "Cancel" })
80
+ ] })
81
+ ] });
82
+ }
83
+ DetailPanel.displayName = "DetailPanel";
84
+ function ReadField({ field, value }) {
85
+ const display = field.render ? field.render(value) : String(_nullishCoalesce(value, () => ( "")));
86
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-1", children: [
87
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-muted-foreground text-sm font-medium", children: field.label }),
88
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-sm", children: display })
89
+ ] });
90
+ }
91
+ function EditField({
92
+ field,
93
+ value,
94
+ onChange
95
+ }) {
96
+ const strValue = String(_nullishCoalesce(value, () => ( "")));
97
+ if (field.type === "readonly") {
98
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "space-y-1", children: [
99
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-muted-foreground text-sm font-medium", children: field.label }),
100
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-sm", children: field.render ? field.render(value) : strValue })
101
+ ] });
102
+ }
103
+ if (field.type === "select") {
104
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
105
+ _chunkWJUDOEVBcjs.FormSelect,
106
+ {
107
+ label: field.label,
108
+ name: field.key,
109
+ required: field.required,
110
+ options: _nullishCoalesce(field.options, () => ( [])),
111
+ value: strValue,
112
+ onValueChange: (v) => onChange(field.key, v)
113
+ }
114
+ );
115
+ }
116
+ if (field.type === "textarea") {
117
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
118
+ _chunkZ57QBVUCcjs.FormTextarea,
119
+ {
120
+ label: field.label,
121
+ name: field.key,
122
+ required: field.required,
123
+ value: strValue,
124
+ onChange: (e) => onChange(field.key, e.target.value)
125
+ }
126
+ );
127
+ }
128
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
129
+ _chunk32XB3REYcjs.FormInput,
130
+ {
131
+ label: field.label,
132
+ name: field.key,
133
+ required: field.required,
134
+ value: strValue,
135
+ onChange: (e) => onChange(field.key, e.target.value)
136
+ }
137
+ );
138
+ }
139
+
140
+
141
+ exports.DetailPanel = DetailPanel;
142
+ //# sourceMappingURL=detail-panel.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/augur-packages/augur-packages/packages/augur-web/dist/detail-panel.cjs","../src/detail-panel.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/augur-packages/augur-packages/packages/augur-web/dist/detail-panel.cjs","sourcesContent":[null,"/** Read/edit toggle panel for a single record. */\n\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@simpleapps-com/augur-utils/web\";\nimport { Button } from \"./button\";\nimport { FormInput } from \"./form-input\";\nimport { FormSelect } from \"./form-select\";\nimport { FormTextarea } from \"./form-textarea\";\nimport { Spinner } from \"./spinner\";\n\nexport interface FieldDef<T> {\n /** Property key on the data object. */\n key: keyof T & string;\n /** Display label. */\n label: string;\n /** Field type determines the input component used in edit mode. */\n type: \"text\" | \"textarea\" | \"select\" | \"readonly\";\n /** Options for select fields. */\n options?: { label: string; value: string }[];\n /** Mark the field as required in edit mode. */\n required?: boolean;\n /** Custom read-mode renderer. Receives the field value. */\n render?: (value: T[keyof T]) => React.ReactNode;\n}\n\nexport interface DetailPanelProps<T extends object> {\n /** Field definitions describing each field. */\n fields: FieldDef<T>[];\n /** The record to display/edit. */\n data: T;\n /** Called with the updated record when the user saves. */\n onSave: (data: T) => void;\n /** Called when the user cancels editing. */\n onCancel?: () => void;\n /** Whether a save operation is in progress. */\n loading?: boolean;\n /** Optional title displayed above the fields. */\n title?: string;\n /** Additional class name for the root container. */\n className?: string;\n /** Start in edit mode. */\n defaultEditing?: boolean;\n}\n\n/**\n * Read/edit toggle panel for a single record. Renders field values in read\n * mode, and form inputs in edit mode based on field definitions.\n *\n * @example\n * ```tsx\n * const fields: FieldDef<Brand>[] = [\n * { key: \"brandsName\", label: \"Name\", type: \"text\", required: true },\n * { key: \"brandsDesc\", label: \"Description\", type: \"textarea\" },\n * { key: \"brandsUid\", label: \"UID\", type: \"readonly\" },\n * ];\n *\n * <DetailPanel fields={fields} data={brand} onSave={handleSave} />\n * ```\n */\nfunction DetailPanel<T extends object>({\n fields,\n data,\n onSave,\n onCancel,\n loading = false,\n title,\n className,\n defaultEditing = false,\n}: DetailPanelProps<T>) {\n const [editing, setEditing] = React.useState(defaultEditing);\n const [draft, setDraft] = React.useState<T>(data);\n\n React.useEffect(() => {\n setDraft(data);\n }, [data]);\n\n const handleEdit = () => {\n setDraft(data);\n setEditing(true);\n };\n\n const handleCancel = () => {\n setDraft(data);\n setEditing(false);\n onCancel?.();\n };\n\n const handleSave = () => {\n onSave(draft);\n };\n\n const updateField = (key: keyof T & string, value: unknown) => {\n setDraft((prev) => ({ ...prev, [key]: value }));\n };\n\n return (\n <div className={cn(\"space-y-6\", className)}>\n {(title || !editing) && (\n <div className=\"flex items-center justify-between\">\n {title && <h3 className=\"text-lg font-semibold\">{title}</h3>}\n {!editing && (\n <Button variant=\"outline\" size=\"sm\" onClick={handleEdit}>\n Edit\n </Button>\n )}\n </div>\n )}\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {fields.map((field) =>\n editing ? (\n <EditField\n key={field.key}\n field={field}\n value={draft[field.key]}\n onChange={updateField}\n />\n ) : (\n <ReadField key={field.key} field={field} value={data[field.key]} />\n ),\n )}\n </div>\n {editing && (\n <div className=\"flex gap-2\">\n <Button onClick={handleSave} disabled={loading}>\n {loading && <Spinner size=\"sm\" className=\"mr-2\" />}\n Save\n </Button>\n <Button variant=\"outline\" onClick={handleCancel} disabled={loading}>\n Cancel\n </Button>\n </div>\n )}\n </div>\n );\n}\nDetailPanel.displayName = \"DetailPanel\";\n\nfunction ReadField<T>({ field, value }: { field: FieldDef<T>; value: T[keyof T] }) {\n const display = field.render ? field.render(value) : String(value ?? \"\");\n return (\n <div className=\"space-y-1\">\n <p className=\"text-muted-foreground text-sm font-medium\">{field.label}</p>\n <p className=\"text-sm\">{display}</p>\n </div>\n );\n}\n\nfunction EditField<T>({\n field,\n value,\n onChange,\n}: {\n field: FieldDef<T>;\n value: unknown;\n onChange: (key: keyof T & string, value: unknown) => void;\n}) {\n const strValue = String(value ?? \"\");\n\n if (field.type === \"readonly\") {\n return (\n <div className=\"space-y-1\">\n <p className=\"text-muted-foreground text-sm font-medium\">{field.label}</p>\n <p className=\"text-sm\">{field.render ? field.render(value as T[keyof T]) : strValue}</p>\n </div>\n );\n }\n\n if (field.type === \"select\") {\n return (\n <FormSelect\n label={field.label}\n name={field.key}\n required={field.required}\n options={field.options ?? []}\n value={strValue}\n onValueChange={(v) => onChange(field.key, v)}\n />\n );\n }\n\n if (field.type === \"textarea\") {\n return (\n <FormTextarea\n label={field.label}\n name={field.key}\n required={field.required}\n value={strValue}\n onChange={(e) => onChange(field.key, e.target.value)}\n />\n );\n }\n\n return (\n <FormInput\n label={field.label}\n name={field.key}\n required={field.required}\n value={strValue}\n onChange={(e) => onChange(field.key, e.target.value)}\n />\n );\n}\n\nexport { DetailPanel };\n"]}
@@ -0,0 +1,59 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ interface FieldDef<T> {
5
+ /** Property key on the data object. */
6
+ key: keyof T & string;
7
+ /** Display label. */
8
+ label: string;
9
+ /** Field type determines the input component used in edit mode. */
10
+ type: "text" | "textarea" | "select" | "readonly";
11
+ /** Options for select fields. */
12
+ options?: {
13
+ label: string;
14
+ value: string;
15
+ }[];
16
+ /** Mark the field as required in edit mode. */
17
+ required?: boolean;
18
+ /** Custom read-mode renderer. Receives the field value. */
19
+ render?: (value: T[keyof T]) => React.ReactNode;
20
+ }
21
+ interface DetailPanelProps<T extends object> {
22
+ /** Field definitions describing each field. */
23
+ fields: FieldDef<T>[];
24
+ /** The record to display/edit. */
25
+ data: T;
26
+ /** Called with the updated record when the user saves. */
27
+ onSave: (data: T) => void;
28
+ /** Called when the user cancels editing. */
29
+ onCancel?: () => void;
30
+ /** Whether a save operation is in progress. */
31
+ loading?: boolean;
32
+ /** Optional title displayed above the fields. */
33
+ title?: string;
34
+ /** Additional class name for the root container. */
35
+ className?: string;
36
+ /** Start in edit mode. */
37
+ defaultEditing?: boolean;
38
+ }
39
+ /**
40
+ * Read/edit toggle panel for a single record. Renders field values in read
41
+ * mode, and form inputs in edit mode based on field definitions.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * const fields: FieldDef<Brand>[] = [
46
+ * { key: "brandsName", label: "Name", type: "text", required: true },
47
+ * { key: "brandsDesc", label: "Description", type: "textarea" },
48
+ * { key: "brandsUid", label: "UID", type: "readonly" },
49
+ * ];
50
+ *
51
+ * <DetailPanel fields={fields} data={brand} onSave={handleSave} />
52
+ * ```
53
+ */
54
+ declare function DetailPanel<T extends object>({ fields, data, onSave, onCancel, loading, title, className, defaultEditing, }: DetailPanelProps<T>): react_jsx_runtime.JSX.Element;
55
+ declare namespace DetailPanel {
56
+ var displayName: string;
57
+ }
58
+
59
+ export { DetailPanel, type DetailPanelProps, type FieldDef };