@simpleapps-com/augur-web 2026.11.2 → 2026.11.3

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 (167) hide show
  1. package/EXPORTS.md +1 -1
  2. package/README.md +7 -2
  3. package/dist/add-to-cart-button.cjs +3 -2
  4. package/dist/add-to-cart-button.cjs.map +1 -1
  5. package/dist/add-to-cart-button.js +2 -1
  6. package/dist/add-to-cart-button.js.map +1 -1
  7. package/dist/bin-scan.d.ts +23 -0
  8. package/dist/bin-scan.d.ts.map +1 -0
  9. package/dist/button.d.ts +1 -1
  10. package/dist/{chunk-32XB3REY.cjs → chunk-6SSR5Z6X.cjs} +8 -5
  11. package/dist/chunk-6SSR5Z6X.cjs.map +1 -0
  12. package/dist/{chunk-RCUKSVMF.js → chunk-6USFUHWW.js} +9 -14
  13. package/dist/chunk-6USFUHWW.js.map +1 -0
  14. package/dist/{chunk-WJUDOEVB.cjs → chunk-7RDA26CL.cjs} +7 -7
  15. package/dist/{chunk-WJUDOEVB.cjs.map → chunk-7RDA26CL.cjs.map} +1 -1
  16. package/dist/{chunk-UMNTUD2P.js → chunk-AC3OCGDU.js} +6 -8
  17. package/dist/chunk-AC3OCGDU.js.map +1 -0
  18. package/dist/{chunk-MOC5N2N4.js → chunk-BSRLAXZ2.js} +9 -6
  19. package/dist/chunk-BSRLAXZ2.js.map +1 -0
  20. package/dist/{chunk-UB7HYAAC.js → chunk-EBCKH6JE.js} +6 -3
  21. package/dist/chunk-EBCKH6JE.js.map +1 -0
  22. package/dist/{chunk-WFMP6TZN.js → chunk-I6LMIYVF.js} +3 -3
  23. package/dist/{chunk-RGJT67PN.js → chunk-IDCDOE2L.js} +6 -3
  24. package/dist/chunk-IDCDOE2L.js.map +1 -0
  25. package/dist/{chunk-MXSQYVCK.js → chunk-IXQFZJSN.js} +7 -3
  26. package/dist/chunk-IXQFZJSN.js.map +1 -0
  27. package/dist/{chunk-76RSVRGB.cjs → chunk-KVJQIZSM.cjs} +10 -15
  28. package/dist/chunk-KVJQIZSM.cjs.map +1 -0
  29. package/dist/{chunk-TFP5RICB.cjs → chunk-L7JN7ED7.cjs} +37 -37
  30. package/dist/chunk-L7JN7ED7.cjs.map +1 -0
  31. package/dist/{chunk-JXW4XE2P.cjs → chunk-N57EZBPE.cjs} +10 -7
  32. package/dist/chunk-N57EZBPE.cjs.map +1 -0
  33. package/dist/chunk-NBXA42AW.cjs +534 -0
  34. package/dist/chunk-NBXA42AW.cjs.map +1 -0
  35. package/dist/chunk-NSMOT7GD.js +534 -0
  36. package/dist/chunk-NSMOT7GD.js.map +1 -0
  37. package/dist/{chunk-BXMTQGKV.cjs → chunk-OJS2B6U2.cjs} +7 -3
  38. package/dist/chunk-OJS2B6U2.cjs.map +1 -0
  39. package/dist/{chunk-5VMEEKZ5.cjs → chunk-SJGSBMMN.cjs} +7 -9
  40. package/dist/chunk-SJGSBMMN.cjs.map +1 -0
  41. package/dist/{chunk-C6K7FZNX.js → chunk-TEDFINZP.js} +2 -2
  42. package/dist/{chunk-DMWSWL4B.cjs → chunk-VCFKYOVH.cjs} +3 -3
  43. package/dist/{chunk-DMWSWL4B.cjs.map → chunk-VCFKYOVH.cjs.map} +1 -1
  44. package/dist/{chunk-Z57QBVUC.cjs → chunk-VXVOYZBN.cjs} +8 -5
  45. package/dist/chunk-VXVOYZBN.cjs.map +1 -0
  46. package/dist/{chunk-KVHOMD4T.js → chunk-YJDVFOVR.js} +40 -40
  47. package/dist/chunk-YJDVFOVR.js.map +1 -0
  48. package/dist/command.cjs +3 -3
  49. package/dist/command.cjs.map +1 -1
  50. package/dist/command.d.ts.map +1 -1
  51. package/dist/command.js +2 -2
  52. package/dist/command.js.map +1 -1
  53. package/dist/confirm-dialog.cjs +3 -2
  54. package/dist/confirm-dialog.cjs.map +1 -1
  55. package/dist/confirm-dialog.js +2 -1
  56. package/dist/confirm-dialog.js.map +1 -1
  57. package/dist/data-table.cjs +65 -46
  58. package/dist/data-table.cjs.map +1 -1
  59. package/dist/data-table.d.ts.map +1 -1
  60. package/dist/data-table.js +59 -40
  61. package/dist/data-table.js.map +1 -1
  62. package/dist/detail-panel.cjs +71 -63
  63. package/dist/detail-panel.cjs.map +1 -1
  64. package/dist/detail-panel.d.ts.map +1 -1
  65. package/dist/detail-panel.js +70 -62
  66. package/dist/detail-panel.js.map +1 -1
  67. package/dist/dropdown-menu.cjs +3 -2
  68. package/dist/dropdown-menu.cjs.map +1 -1
  69. package/dist/dropdown-menu.d.ts.map +1 -1
  70. package/dist/dropdown-menu.js +2 -1
  71. package/dist/financing-widget.cjs +4 -1
  72. package/dist/financing-widget.cjs.map +1 -1
  73. package/dist/financing-widget.d.ts.map +1 -1
  74. package/dist/financing-widget.js +4 -1
  75. package/dist/financing-widget.js.map +1 -1
  76. package/dist/form-field.cjs +3 -2
  77. package/dist/form-field.cjs.map +1 -1
  78. package/dist/form-field.d.ts.map +1 -1
  79. package/dist/form-field.js +2 -1
  80. package/dist/form-input.cjs +4 -3
  81. package/dist/form-input.cjs.map +1 -1
  82. package/dist/form-input.d.ts.map +1 -1
  83. package/dist/form-input.js +3 -2
  84. package/dist/form-select.cjs +5 -4
  85. package/dist/form-select.cjs.map +1 -1
  86. package/dist/form-select.js +4 -3
  87. package/dist/form-textarea.cjs +4 -3
  88. package/dist/form-textarea.cjs.map +1 -1
  89. package/dist/form-textarea.d.ts.map +1 -1
  90. package/dist/form-textarea.js +3 -2
  91. package/dist/gtm.cjs +3 -3
  92. package/dist/gtm.js +2 -2
  93. package/dist/pagination.cjs +3 -3
  94. package/dist/pagination.d.ts +1 -1
  95. package/dist/pagination.d.ts.map +1 -1
  96. package/dist/pagination.js +2 -2
  97. package/dist/payment-modal.cjs +52 -34
  98. package/dist/payment-modal.cjs.map +1 -1
  99. package/dist/payment-modal.d.ts +4 -4
  100. package/dist/payment-modal.d.ts.map +1 -1
  101. package/dist/payment-modal.js +52 -34
  102. package/dist/payment-modal.js.map +1 -1
  103. package/dist/popover.cjs +4 -4
  104. package/dist/popover.cjs.map +1 -1
  105. package/dist/popover.js +3 -3
  106. package/dist/popover.js.map +1 -1
  107. package/dist/price.cjs +2 -2
  108. package/dist/price.js +1 -1
  109. package/dist/provider.cjs +3 -3
  110. package/dist/provider.js +2 -2
  111. package/dist/quantity-input.cjs +125 -111
  112. package/dist/quantity-input.cjs.map +1 -1
  113. package/dist/quantity-input.d.ts +16 -5
  114. package/dist/quantity-input.d.ts.map +1 -1
  115. package/dist/quantity-input.js +127 -113
  116. package/dist/quantity-input.js.map +1 -1
  117. package/dist/resource-tabs.cjs +10 -5
  118. package/dist/resource-tabs.cjs.map +1 -1
  119. package/dist/resource-tabs.d.ts +1 -1
  120. package/dist/resource-tabs.d.ts.map +1 -1
  121. package/dist/resource-tabs.js +10 -5
  122. package/dist/resource-tabs.js.map +1 -1
  123. package/dist/rules.d.ts +315 -17
  124. package/dist/rules.d.ts.map +1 -1
  125. package/dist/select.cjs +3 -2
  126. package/dist/select.cjs.map +1 -1
  127. package/dist/select.d.ts.map +1 -1
  128. package/dist/select.js +2 -1
  129. package/dist/separator.cjs +8 -9
  130. package/dist/separator.cjs.map +1 -1
  131. package/dist/separator.d.ts.map +1 -1
  132. package/dist/separator.js +7 -8
  133. package/dist/separator.js.map +1 -1
  134. package/dist/session-replay.cjs +2 -2
  135. package/dist/session-replay.js +1 -1
  136. package/dist/spinner.cjs +3 -2
  137. package/dist/spinner.cjs.map +1 -1
  138. package/dist/spinner.d.ts +1 -1
  139. package/dist/spinner.d.ts.map +1 -1
  140. package/dist/spinner.js +2 -1
  141. package/dist/stock-label.cjs +28 -33
  142. package/dist/stock-label.cjs.map +1 -1
  143. package/dist/stock-label.d.ts.map +1 -1
  144. package/dist/stock-label.js +30 -35
  145. package/dist/stock-label.js.map +1 -1
  146. package/llms.txt +3 -2
  147. package/package.json +3 -1
  148. package/dist/chunk-32XB3REY.cjs.map +0 -1
  149. package/dist/chunk-5VMEEKZ5.cjs.map +0 -1
  150. package/dist/chunk-76RSVRGB.cjs.map +0 -1
  151. package/dist/chunk-BXMTQGKV.cjs.map +0 -1
  152. package/dist/chunk-IO77MGHW.js +0 -220
  153. package/dist/chunk-IO77MGHW.js.map +0 -1
  154. package/dist/chunk-JXW4XE2P.cjs.map +0 -1
  155. package/dist/chunk-KVHOMD4T.js.map +0 -1
  156. package/dist/chunk-MOC5N2N4.js.map +0 -1
  157. package/dist/chunk-MXSQYVCK.js.map +0 -1
  158. package/dist/chunk-QKWXJM4Q.cjs +0 -220
  159. package/dist/chunk-QKWXJM4Q.cjs.map +0 -1
  160. package/dist/chunk-RCUKSVMF.js.map +0 -1
  161. package/dist/chunk-RGJT67PN.js.map +0 -1
  162. package/dist/chunk-TFP5RICB.cjs.map +0 -1
  163. package/dist/chunk-UB7HYAAC.js.map +0 -1
  164. package/dist/chunk-UMNTUD2P.js.map +0 -1
  165. package/dist/chunk-Z57QBVUC.cjs.map +0 -1
  166. /package/dist/{chunk-WFMP6TZN.js.map → chunk-I6LMIYVF.js.map} +0 -0
  167. /package/dist/{chunk-C6K7FZNX.js.map → chunk-TEDFINZP.js.map} +0 -0
@@ -1 +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 functionalUpdate,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n useReactTable,\n type ColumnDef as TanstackColumnDef,\n type SortingState,\n type RowSelectionState,\n type Updater,\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 { alignClass, ariaSortOf, bodyViewOf, csvField } from \"./rules\";\nimport { Checkbox } from \"./checkbox\";\nimport { Button } from \"./button\";\nimport { ListPagination } from \"./pagination\";\nimport { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from \"./select\";\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 /** Current page size. Required when pageSizeOptions is set. */\n pageSize?: number;\n /** Available page size options. Renders a dropdown when provided. */\n pageSizeOptions?: number[];\n /** Called when the user selects a different page size. */\n onPageSizeChange?: (size: 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\nfunction getAlignCn(meta: unknown): string {\n return alignClass((meta as { align?: string } | undefined)?.align ?? \"left\");\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) => columns.map((col) => csvField(String(row[col.key] ?? \"\"))));\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 const canSort = header.column.getCanSort();\n const sorted = header.column.getIsSorted();\n // aria-sort announces sort state to screen readers; only meaningful on\n // sortable columns. Sorting is triggered by a real <button> so it is\n // keyboard-focusable and Enter/Space-activatable (a click handler on the\n // <th> alone is mouse-only).\n // ariaSortOf gives only \"ascending\", \"descending\", \"none\" or undefined.\n const ariaSort = ariaSortOf(canSort, sorted === false ? \"unsorted\" : sorted) as\n React.AriaAttributes[\"aria-sort\"] | undefined;\n\n return (\n <TableHead className={getAlignCn(header.column.columnDef.meta)} aria-sort={ariaSort}>\n {canSort ? (\n <button\n type=\"button\"\n onClick={header.column.getToggleSortingHandler()}\n className=\"inline-flex cursor-pointer select-none items-center gap-1\"\n >\n {flexRender(header.column.columnDef.header, header.getContext())}\n <SortIndicator direction={sorted} />\n </button>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n {flexRender(header.column.columnDef.header, header.getContext())}\n </span>\n )}\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 const view = bodyViewOf(loading, rows.length);\n if (view === \"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 (view === \"empty\") {\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 PageSizeSelector({\n pageSize,\n options,\n onChange,\n}: {\n pageSize: number;\n options: number[];\n onChange: (size: number) => void;\n}) {\n return (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground text-sm\">Rows per page</span>\n <Select value={String(pageSize)} onValueChange={(v) => onChange(Number(v))}>\n <SelectTrigger className=\"h-8 w-[70px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((size) => (\n <SelectItem key={size} value={String(size)}>\n {size}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n\nfunction PaginationFooter({ pagination }: { pagination?: DataTablePagination }) {\n if (!pagination) return null;\n const showPageSize =\n pagination.pageSizeOptions && pagination.pageSize !== undefined && pagination.onPageSizeChange;\n const showPages = pagination.totalPages > 1;\n if (!showPageSize && !showPages) return null;\n return (\n <div className=\"flex items-center justify-between\">\n {showPageSize ? (\n <PageSizeSelector\n pageSize={pagination.pageSize!}\n options={pagination.pageSizeOptions!}\n onChange={pagination.onPageSizeChange!}\n />\n ) : (\n <div />\n )}\n {showPages && (\n <ListPagination\n page={pagination.page}\n totalPages={pagination.totalPages}\n getHref={pagination.getHref}\n onPageChange={pagination.onPageChange}\n />\n )}\n </div>\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\n/**\n * The change handler TanStack calls: the caller's (with TanStack's Updater<S> =\n * S | ((old: S) => S) resolved against the current value via functionalUpdate, so a plain\n * value from resetRowSelection()/setSorting(v) is not called as a function), or the internal\n * state setter when the state is uncontrolled.\n */\nfunction changeHandler<S>(\n onChange: ((state: S) => void) | undefined,\n current: S,\n setInternal: React.Dispatch<React.SetStateAction<S>>,\n): (updater: Updater<S>) => void {\n if (!onChange) return setInternal;\n return (updater) => onChange(functionalUpdate(updater, current));\n}\n\n/** TanStack's client-side models, left out when the caller sorts or filters on the server. */\nfunction clientRowModels(isManualSorting: boolean, isManualFiltering: boolean) {\n return {\n getSortedRowModel: isManualSorting ? undefined : getSortedRowModel(),\n getFilteredRowModel: isManualFiltering ? undefined : getFilteredRowModel(),\n };\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 = changeHandler(opts.onSortingChange, sorting, setInternalSorting);\n\n const handleGlobalFilterChange = opts.onGlobalFilterChange ?? setInternalGlobalFilter;\n\n const handleRowSelectionChange = changeHandler(\n opts.onRowSelectionChange,\n rowSelectionState,\n setInternalRowSelection,\n );\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 ...clientRowModels(isManualSorting, isManualFiltering),\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\n/** The toolbar shows when it has something to show: search, custom content, column toggle, CSV export, or a selection. */\nfunction toolbarVisible(\n searchable: boolean,\n toolbar: React.ReactNode,\n columnToggle: boolean,\n exportCsv: boolean,\n selectedRows: readonly unknown[],\n): boolean {\n return Boolean(searchable || toolbar || columnToggle || exportCsv || selectedRows.length > 0);\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 = toolbarVisible(searchable, toolbar, columnToggle, exportCsv, selectedRows);\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":[]}
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 functionalUpdate,\n getCoreRowModel,\n getSortedRowModel,\n getFilteredRowModel,\n useReactTable,\n type ColumnDef as TanstackColumnDef,\n type SortingState,\n type RowSelectionState,\n type Updater,\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 {\n alignClassOf,\n ariaSortOf,\n bodySlots,\n bulkBarShown,\n csvField,\n csvText,\n enableHiding,\n enableSorting,\n slotsWhen,\n sortSlotsOf,\n toolbarShown,\n type SortDir,\n} from \"./rules\";\nimport { Checkbox } from \"./checkbox\";\nimport { Button } from \"./button\";\nimport { ListPagination } from \"./pagination\";\nimport { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from \"./select\";\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 /** Current page size. Required when pageSizeOptions is set. */\n pageSize?: number;\n /** Available page size options. Renders a dropdown when provided. */\n pageSizeOptions?: number[];\n /** Called when the user selects a different page size. */\n onPageSizeChange?: (size: 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\nfunction getAlignCn(meta: unknown): string {\n return alignClassOf((meta as { align?: string } | undefined)?.align);\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: enableSorting(col.sortable),\n enableHiding: enableHiding(col.hideable),\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) => csvField(c.header));\n const rows = data.map((row) => columns.map((col) => csvField(String(row[col.key] ?? \"\"))));\n const csv = csvText(headers, rows);\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: SortDir }) {\n const slots = sortSlotsOf(direction);\n return (\n <>\n {slots.up.map((slot) => (\n <LuArrowUp key={slot} className=\"h-4 w-4\" />\n ))}\n {slots.down.map((slot) => (\n <LuArrowDown key={slot} className=\"h-4 w-4\" />\n ))}\n {slots.both.map((slot) => (\n <LuArrowUpDown key={slot} className=\"text-muted-foreground/50 h-4 w-4\" />\n ))}\n </>\n );\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 const canSort = header.column.getCanSort();\n const sorted = header.column.getIsSorted();\n // aria-sort announces sort state to screen readers; only meaningful on\n // sortable columns. Sorting is triggered by a real <button> so it is\n // keyboard-focusable and Enter/Space-activatable (a click handler on the\n // <th> alone is mouse-only).\n // ariaSortOf gives only \"ascending\", \"descending\", \"none\" or undefined.\n const direction: SortDir = sorted === false ? \"unsorted\" : sorted;\n const ariaSort = ariaSortOf(canSort, direction) as React.AriaAttributes[\"aria-sort\"] | undefined;\n\n return (\n <TableHead className={getAlignCn(header.column.columnDef.meta)} aria-sort={ariaSort}>\n {canSort ? (\n <button\n type=\"button\"\n onClick={header.column.getToggleSortingHandler()}\n className=\"inline-flex cursor-pointer select-none items-center gap-1\"\n >\n {flexRender(header.column.columnDef.header, header.getContext())}\n <SortIndicator direction={direction} />\n </button>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n {flexRender(header.column.columnDef.header, header.getContext())}\n </span>\n )}\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 return (\n <div className=\"flex items-center gap-4\">\n {slotsWhen(searchable).map((slot) => (\n <Input\n key={slot}\n placeholder={searchPlaceholder}\n value={globalFilter}\n onChange={(e) => onGlobalFilterChange(e.target.value)}\n className=\"max-w-sm\"\n />\n ))}\n {slotsWhen(bulkBarShown(selectedRows.length, Boolean(bulkActions))).map((slot) => (\n <BulkActionsBar\n key={slot}\n selectedRows={selectedRows}\n bulkActions={bulkActions as (rows: T[]) => React.ReactNode}\n />\n ))}\n <div className=\"ml-auto flex items-center gap-2\">\n {toolbar}\n {slotsWhen(exportCsv).map((slot) => (\n <ExportButton key={slot} columns={columns} data={data} csvFilename={csvFilename} />\n ))}\n {slotsWhen(columnToggle).map((slot) => (\n <ColumnToggle key={slot} table={table} />\n ))}\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 const slots = bodySlots(loading, rows.length);\n return (\n <>\n {slots.loading.map((slot) => (\n <TableRow key={slot}>\n <TableCell colSpan={colSpan} className=\"h-24 text-center\">\n <Spinner size=\"lg\" className=\"mx-auto\" />\n </TableCell>\n </TableRow>\n ))}\n {slots.empty.map((slot) => (\n <TableRow key={slot}>\n <TableCell colSpan={colSpan} className=\"h-24 text-center\">\n {emptyMessage}\n </TableCell>\n </TableRow>\n ))}\n {slots.rows.map((slot) => (\n <React.Fragment key={slot}>\n {rows.map((row) => (\n <DataRow key={row.id} row={row} onRowClick={onRowClick} />\n ))}\n </React.Fragment>\n ))}\n </>\n );\n}\n\nfunction PageSizeSelector({\n pageSize,\n options,\n onChange,\n}: {\n pageSize: number;\n options: number[];\n onChange: (size: number) => void;\n}) {\n return (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground text-sm\">Rows per page</span>\n <Select value={String(pageSize)} onValueChange={(v) => onChange(Number(v))}>\n <SelectTrigger className=\"h-8 w-[70px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {options.map((size) => (\n <SelectItem key={size} value={String(size)}>\n {size}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n\nfunction PaginationFooter({ pagination }: { pagination?: DataTablePagination }) {\n if (!pagination) return null;\n const showPageSize =\n pagination.pageSizeOptions && pagination.pageSize !== undefined && pagination.onPageSizeChange;\n const showPages = pagination.totalPages > 1;\n if (!showPageSize && !showPages) return null;\n return (\n <div className=\"flex items-center justify-between\">\n {showPageSize ? (\n <PageSizeSelector\n pageSize={pagination.pageSize!}\n options={pagination.pageSizeOptions!}\n onChange={pagination.onPageSizeChange!}\n />\n ) : (\n <div />\n )}\n {showPages && (\n <ListPagination\n page={pagination.page}\n totalPages={pagination.totalPages}\n getHref={pagination.getHref}\n onPageChange={pagination.onPageChange}\n />\n )}\n </div>\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\n/**\n * The change handler TanStack calls: the caller's (with TanStack's Updater<S> =\n * S | ((old: S) => S) resolved against the current value via functionalUpdate, so a plain\n * value from resetRowSelection()/setSorting(v) is not called as a function), or the internal\n * state setter when the state is uncontrolled.\n */\nfunction changeHandler<S>(\n onChange: ((state: S) => void) | undefined,\n current: S,\n setInternal: React.Dispatch<React.SetStateAction<S>>,\n): (updater: Updater<S>) => void {\n if (!onChange) return setInternal;\n return (updater) => onChange(functionalUpdate(updater, current));\n}\n\n/** TanStack's client-side models, left out when the caller sorts or filters on the server. */\nfunction clientRowModels(isManualSorting: boolean, isManualFiltering: boolean) {\n return {\n getSortedRowModel: isManualSorting ? undefined : getSortedRowModel(),\n getFilteredRowModel: isManualFiltering ? undefined : getFilteredRowModel(),\n };\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 = changeHandler(opts.onSortingChange, sorting, setInternalSorting);\n\n const handleGlobalFilterChange = opts.onGlobalFilterChange ?? setInternalGlobalFilter;\n\n const handleRowSelectionChange = changeHandler(\n opts.onRowSelectionChange,\n rowSelectionState,\n setInternalRowSelection,\n );\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 ...clientRowModels(isManualSorting, isManualFiltering),\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 = toolbarShown(\n searchable,\n Boolean(toolbar),\n columnToggle,\n exportCsv,\n selectedRows.length,\n );\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":["Fragment"]}
@@ -2,21 +2,26 @@
2
2
  "use client";
3
3
 
4
4
 
5
- var _chunkZ57QBVUCcjs = require('./chunk-Z57QBVUC.cjs');
5
+ var _chunkVXVOYZBNcjs = require('./chunk-VXVOYZBN.cjs');
6
6
  require('./chunk-5ZITWMV4.cjs');
7
7
 
8
8
 
9
- var _chunk32XB3REYcjs = require('./chunk-32XB3REY.cjs');
9
+ var _chunk6SSR5Z6Xcjs = require('./chunk-6SSR5Z6X.cjs');
10
10
 
11
11
 
12
- var _chunkWJUDOEVBcjs = require('./chunk-WJUDOEVB.cjs');
13
- require('./chunk-BXMTQGKV.cjs');
14
- require('./chunk-76RSVRGB.cjs');
12
+ var _chunk7RDA26CLcjs = require('./chunk-7RDA26CL.cjs');
13
+ require('./chunk-OJS2B6U2.cjs');
14
+ require('./chunk-KVJQIZSM.cjs');
15
15
  require('./chunk-7EXXNELX.cjs');
16
16
  require('./chunk-MAENILZP.cjs');
17
17
 
18
18
 
19
- var _chunk5VMEEKZ5cjs = require('./chunk-5VMEEKZ5.cjs');
19
+ var _chunkSJGSBMMNcjs = require('./chunk-SJGSBMMN.cjs');
20
+
21
+
22
+
23
+
24
+ var _chunkNBXA42AWcjs = require('./chunk-NBXA42AW.cjs');
20
25
 
21
26
 
22
27
  var _chunkVL6L4GDAcjs = require('./chunk-VL6L4GDA.cjs');
@@ -25,59 +30,65 @@ var _chunkVL6L4GDAcjs = require('./chunk-VL6L4GDA.cjs');
25
30
  var _react = require('react'); var React = _interopRequireWildcard(_react);
26
31
  var _web = require('@simpleapps-com/augur-utils/web');
27
32
  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
- }) {
33
+ function useDetailPanel(data, onSave, onCancel, defaultEditing) {
38
34
  const [editing, setEditing] = React.useState(defaultEditing);
39
35
  const [draft, setDraft] = React.useState(data);
40
36
  React.useEffect(() => {
41
37
  setDraft(data);
42
38
  }, [data]);
43
- const handleEdit = () => {
39
+ const edit = () => {
44
40
  setDraft(data);
45
41
  setEditing(true);
46
42
  };
47
- const handleCancel = () => {
43
+ const cancel = () => {
48
44
  setDraft(data);
49
45
  setEditing(false);
50
46
  _optionalChain([onCancel, 'optionalCall', _ => _()]);
51
47
  };
52
- const handleSave = () => {
48
+ const save = () => {
53
49
  onSave(draft);
54
50
  };
55
51
  const updateField = (key, value) => {
56
52
  setDraft((prev) => ({ ...prev, [key]: value }));
57
53
  };
54
+ return { editing, draft, edit, cancel, save, updateField };
55
+ }
56
+ function DetailPanel({
57
+ fields,
58
+ data,
59
+ onSave,
60
+ onCancel,
61
+ loading,
62
+ title,
63
+ className,
64
+ defaultEditing
65
+ }) {
66
+ const panel = useDetailPanel(data, onSave, onCancel, defaultEditing === true);
67
+ const slots = _chunkNBXA42AWcjs.panelSlotsOf.call(void 0, title, panel.editing);
58
68
  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,
69
+ slots.header.map((slot) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-between", children: [
70
+ slots.title.map((text) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h3", { className: "text-lg font-semibold", children: text }, text)),
71
+ slots.editButton.map((button) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVL6L4GDAcjs.Button, { variant: "outline", size: "sm", onClick: panel.edit, children: "Edit" }, button))
72
+ ] }, slot)),
73
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "grid gap-4 sm:grid-cols-2", children: fields.map((field) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, React.Fragment, { children: [
74
+ slots.edit.map((slot) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
65
75
  EditField,
66
76
  {
67
77
  field,
68
- value: draft[field.key],
69
- onChange: updateField
78
+ value: panel.draft[field.key],
79
+ onChange: panel.updateField
70
80
  },
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" }),
81
+ slot
82
+ )),
83
+ slots.read.map((slot) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ReadField, { field, value: data[field.key] }, slot))
84
+ ] }, field.key)) }),
85
+ slots.actions.map((slot) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex gap-2", children: [
86
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkVL6L4GDAcjs.Button, { onClick: panel.save, disabled: loading === true, children: [
87
+ _chunkNBXA42AWcjs.slotsWhen.call(void 0, loading === true).map((spin) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkSJGSBMMNcjs.Spinner, { size: "sm", className: "mr-2" }, spin)),
77
88
  "Save"
78
89
  ] }),
79
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVL6L4GDAcjs.Button, { variant: "outline", onClick: handleCancel, disabled: loading, children: "Cancel" })
80
- ] })
90
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkVL6L4GDAcjs.Button, { variant: "outline", onClick: panel.cancel, disabled: loading === true, children: "Cancel" })
91
+ ] }, slot))
81
92
  ] });
82
93
  }
83
94
  DetailPanel.displayName = "DetailPanel";
@@ -94,15 +105,11 @@ function EditField({
94
105
  onChange
95
106
  }) {
96
107
  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,
108
+ const slots = _chunkNBXA42AWcjs.fieldSlotsOf.call(void 0, field.type);
109
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
110
+ slots.readonly.map((slot) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ReadField, { field, value }, slot)),
111
+ slots.select.map((slot) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
112
+ _chunk7RDA26CLcjs.FormSelect,
106
113
  {
107
114
  label: field.label,
108
115
  name: field.key,
@@ -110,31 +117,32 @@ function EditField({
110
117
  options: _nullishCoalesce(field.options, () => ( [])),
111
118
  value: strValue,
112
119
  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,
120
+ },
121
+ slot
122
+ )),
123
+ slots.textarea.map((slot) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
124
+ _chunkVXVOYZBNcjs.FormTextarea,
119
125
  {
120
126
  label: field.label,
121
127
  name: field.key,
122
128
  required: field.required,
123
129
  value: strValue,
124
130
  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
- );
131
+ },
132
+ slot
133
+ )),
134
+ slots.input.map((slot) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
135
+ _chunk6SSR5Z6Xcjs.FormInput,
136
+ {
137
+ label: field.label,
138
+ name: field.key,
139
+ required: field.required,
140
+ value: strValue,
141
+ onChange: (e) => onChange(field.key, e.target.value)
142
+ },
143
+ slot
144
+ ))
145
+ ] });
138
146
  }
139
147
 
140
148
 
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/stuartjackson/Projects/simpleapps/atlas/repo/packages/augur-web/dist/detail-panel.cjs","../src/detail-panel.tsx"],"names":[],"mappings":"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","file":"/Users/stuartjackson/Projects/simpleapps/atlas/repo/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"]}
1
+ {"version":3,"sources":["/Users/stuartjackson/Projects/simpleapps/atlas/repo/packages/augur-web/dist/detail-panel.cjs","../src/detail-panel.tsx"],"names":["Fragment"],"mappings":"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","file":"/Users/stuartjackson/Projects/simpleapps/atlas/repo/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\";\nimport { fieldSlotsOf, panelSlotsOf, slotsWhen } from \"./rules\";\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 * The panel's state and handlers: the draft, edit mode and what each button does. Its decisions\n * are the proved `panelSlotsOf`; this only holds React state.\n */\nfunction useDetailPanel<T extends object>(\n data: T,\n onSave: (data: T) => void,\n onCancel: (() => void) | undefined,\n defaultEditing: boolean,\n) {\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 edit = () => {\n setDraft(data);\n setEditing(true);\n };\n\n const cancel = () => {\n setDraft(data);\n setEditing(false);\n onCancel?.();\n };\n\n const save = () => {\n onSave(draft);\n };\n\n const updateField = (key: keyof T & string, value: unknown) => {\n setDraft((prev) => ({ ...prev, [key]: value }));\n };\n\n return { editing, draft, edit, cancel, save, updateField };\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,\n title,\n className,\n defaultEditing,\n}: DetailPanelProps<T>) {\n const panel = useDetailPanel(data, onSave, onCancel, defaultEditing === true);\n const slots = panelSlotsOf(title, panel.editing);\n\n return (\n <div className={cn(\"space-y-6\", className)}>\n {slots.header.map((slot) => (\n <div key={slot} className=\"flex items-center justify-between\">\n {slots.title.map((text) => (\n <h3 key={text} className=\"text-lg font-semibold\">\n {text}\n </h3>\n ))}\n {slots.editButton.map((button) => (\n <Button key={button} variant=\"outline\" size=\"sm\" onClick={panel.edit}>\n Edit\n </Button>\n ))}\n </div>\n ))}\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {fields.map((field) => (\n <React.Fragment key={field.key}>\n {slots.edit.map((slot) => (\n <EditField\n key={slot}\n field={field}\n value={panel.draft[field.key]}\n onChange={panel.updateField}\n />\n ))}\n {slots.read.map((slot) => (\n <ReadField key={slot} field={field} value={data[field.key]} />\n ))}\n </React.Fragment>\n ))}\n </div>\n {slots.actions.map((slot) => (\n <div key={slot} className=\"flex gap-2\">\n <Button onClick={panel.save} disabled={loading === true}>\n {slotsWhen(loading === true).map((spin) => (\n <Spinner key={spin} size=\"sm\" className=\"mr-2\" />\n ))}\n Save\n </Button>\n <Button variant=\"outline\" onClick={panel.cancel} disabled={loading === true}>\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 const slots = fieldSlotsOf(field.type);\n\n return (\n <>\n {slots.readonly.map((slot) => (\n <ReadField key={slot} field={field} value={value as T[keyof T]} />\n ))}\n {slots.select.map((slot) => (\n <FormSelect\n key={slot}\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 {slots.textarea.map((slot) => (\n <FormTextarea\n key={slot}\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 {slots.input.map((slot) => (\n <FormInput\n key={slot}\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}\n\nexport { DetailPanel };\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"detail-panel.d.ts","sourceRoot":"","sources":["../src/detail-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;IACtB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;IAClD,iCAAiC;IACjC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;CACjD;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM;IAChD,+CAA+C;IAC/C,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACtB,kCAAkC;IAClC,IAAI,EAAE,CAAC,CAAC;IACR,0DAA0D;IAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;GAcG;AACH,iBAAS,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,EACrC,MAAM,EACN,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,OAAe,EACf,KAAK,EACL,SAAS,EACT,cAAsB,GACvB,EAAE,gBAAgB,CAAC,CAAC,CAAC,qBAkErB;kBA3EQ,WAAW;;;AAgJpB,OAAO,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"detail-panel.d.ts","sourceRoot":"","sources":["../src/detail-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;IACtB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;IAClD,iCAAiC;IACjC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;CACjD;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM;IAChD,+CAA+C;IAC/C,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACtB,kCAAkC;IAClC,IAAI,EAAE,CAAC,CAAC;IACR,0DAA0D;IAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAyCD;;;;;;;;;;;;;;GAcG;AACH,iBAAS,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,EACrC,MAAM,EACN,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,OAAO,EACP,KAAK,EACL,SAAS,EACT,cAAc,GACf,EAAE,gBAAgB,CAAC,CAAC,CAAC,qBAoDrB;kBA7DQ,WAAW;;;AA8HpB,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -2,21 +2,26 @@
2
2
  "use client";
3
3
  import {
4
4
  FormTextarea
5
- } from "./chunk-RGJT67PN.js";
5
+ } from "./chunk-IDCDOE2L.js";
6
6
  import "./chunk-L3BT67EO.js";
7
7
  import {
8
8
  FormInput
9
- } from "./chunk-UB7HYAAC.js";
9
+ } from "./chunk-EBCKH6JE.js";
10
10
  import {
11
11
  FormSelect
12
- } from "./chunk-WFMP6TZN.js";
13
- import "./chunk-MXSQYVCK.js";
14
- import "./chunk-RCUKSVMF.js";
12
+ } from "./chunk-I6LMIYVF.js";
13
+ import "./chunk-IXQFZJSN.js";
14
+ import "./chunk-6USFUHWW.js";
15
15
  import "./chunk-HXQF6XTL.js";
16
16
  import "./chunk-VD3H4QZM.js";
17
17
  import {
18
18
  Spinner
19
- } from "./chunk-UMNTUD2P.js";
19
+ } from "./chunk-AC3OCGDU.js";
20
+ import {
21
+ fieldSlotsOf,
22
+ panelSlotsOf,
23
+ slotsWhen
24
+ } from "./chunk-NSMOT7GD.js";
20
25
  import {
21
26
  Button
22
27
  } from "./chunk-QICSGVX3.js";
@@ -24,60 +29,66 @@ import {
24
29
  // src/detail-panel.tsx
25
30
  import * as React from "react";
26
31
  import { cn } from "@simpleapps-com/augur-utils/web";
27
- import { jsx, jsxs } from "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
- }) {
32
+ import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
33
+ function useDetailPanel(data, onSave, onCancel, defaultEditing) {
38
34
  const [editing, setEditing] = React.useState(defaultEditing);
39
35
  const [draft, setDraft] = React.useState(data);
40
36
  React.useEffect(() => {
41
37
  setDraft(data);
42
38
  }, [data]);
43
- const handleEdit = () => {
39
+ const edit = () => {
44
40
  setDraft(data);
45
41
  setEditing(true);
46
42
  };
47
- const handleCancel = () => {
43
+ const cancel = () => {
48
44
  setDraft(data);
49
45
  setEditing(false);
50
46
  onCancel?.();
51
47
  };
52
- const handleSave = () => {
48
+ const save = () => {
53
49
  onSave(draft);
54
50
  };
55
51
  const updateField = (key, value) => {
56
52
  setDraft((prev) => ({ ...prev, [key]: value }));
57
53
  };
54
+ return { editing, draft, edit, cancel, save, updateField };
55
+ }
56
+ function DetailPanel({
57
+ fields,
58
+ data,
59
+ onSave,
60
+ onCancel,
61
+ loading,
62
+ title,
63
+ className,
64
+ defaultEditing
65
+ }) {
66
+ const panel = useDetailPanel(data, onSave, onCancel, defaultEditing === true);
67
+ const slots = panelSlotsOf(title, panel.editing);
58
68
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
59
- (title || !editing) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
60
- title && /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: title }),
61
- !editing && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleEdit, children: "Edit" })
62
- ] }),
63
- /* @__PURE__ */ jsx("div", { className: "grid gap-4 sm:grid-cols-2", children: fields.map(
64
- (field) => editing ? /* @__PURE__ */ jsx(
69
+ slots.header.map((slot) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
70
+ slots.title.map((text) => /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: text }, text)),
71
+ slots.editButton.map((button) => /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: panel.edit, children: "Edit" }, button))
72
+ ] }, slot)),
73
+ /* @__PURE__ */ jsx("div", { className: "grid gap-4 sm:grid-cols-2", children: fields.map((field) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
74
+ slots.edit.map((slot) => /* @__PURE__ */ jsx(
65
75
  EditField,
66
76
  {
67
77
  field,
68
- value: draft[field.key],
69
- onChange: updateField
78
+ value: panel.draft[field.key],
79
+ onChange: panel.updateField
70
80
  },
71
- field.key
72
- ) : /* @__PURE__ */ jsx(ReadField, { field, value: data[field.key] }, field.key)
73
- ) }),
74
- editing && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
75
- /* @__PURE__ */ jsxs(Button, { onClick: handleSave, disabled: loading, children: [
76
- loading && /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "mr-2" }),
81
+ slot
82
+ )),
83
+ slots.read.map((slot) => /* @__PURE__ */ jsx(ReadField, { field, value: data[field.key] }, slot))
84
+ ] }, field.key)) }),
85
+ slots.actions.map((slot) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
86
+ /* @__PURE__ */ jsxs(Button, { onClick: panel.save, disabled: loading === true, children: [
87
+ slotsWhen(loading === true).map((spin) => /* @__PURE__ */ jsx(Spinner, { size: "sm", className: "mr-2" }, spin)),
77
88
  "Save"
78
89
  ] }),
79
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleCancel, disabled: loading, children: "Cancel" })
80
- ] })
90
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: panel.cancel, disabled: loading === true, children: "Cancel" })
91
+ ] }, slot))
81
92
  ] });
82
93
  }
83
94
  DetailPanel.displayName = "DetailPanel";
@@ -94,14 +105,10 @@ function EditField({
94
105
  onChange
95
106
  }) {
96
107
  const strValue = String(value ?? "");
97
- if (field.type === "readonly") {
98
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
99
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium", children: field.label }),
100
- /* @__PURE__ */ jsx("p", { className: "text-sm", children: field.render ? field.render(value) : strValue })
101
- ] });
102
- }
103
- if (field.type === "select") {
104
- return /* @__PURE__ */ jsx(
108
+ const slots = fieldSlotsOf(field.type);
109
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
110
+ slots.readonly.map((slot) => /* @__PURE__ */ jsx(ReadField, { field, value }, slot)),
111
+ slots.select.map((slot) => /* @__PURE__ */ jsx(
105
112
  FormSelect,
106
113
  {
107
114
  label: field.label,
@@ -110,11 +117,10 @@ function EditField({
110
117
  options: field.options ?? [],
111
118
  value: strValue,
112
119
  onValueChange: (v) => onChange(field.key, v)
113
- }
114
- );
115
- }
116
- if (field.type === "textarea") {
117
- return /* @__PURE__ */ jsx(
120
+ },
121
+ slot
122
+ )),
123
+ slots.textarea.map((slot) => /* @__PURE__ */ jsx(
118
124
  FormTextarea,
119
125
  {
120
126
  label: field.label,
@@ -122,19 +128,21 @@ function EditField({
122
128
  required: field.required,
123
129
  value: strValue,
124
130
  onChange: (e) => onChange(field.key, e.target.value)
125
- }
126
- );
127
- }
128
- return /* @__PURE__ */ jsx(
129
- 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
- );
131
+ },
132
+ slot
133
+ )),
134
+ slots.input.map((slot) => /* @__PURE__ */ jsx(
135
+ FormInput,
136
+ {
137
+ label: field.label,
138
+ name: field.key,
139
+ required: field.required,
140
+ value: strValue,
141
+ onChange: (e) => onChange(field.key, e.target.value)
142
+ },
143
+ slot
144
+ ))
145
+ ] });
138
146
  }
139
147
  export {
140
148
  DetailPanel