@jmruthers/pace-core 0.2.4 → 0.2.6

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 (163) hide show
  1. package/dist/{DataTable-BHlzyKZP.d.ts → DataTable-C1AEm9Cx.d.ts} +1 -1
  2. package/dist/{DataTable-GEY5U7OI.js → DataTable-EEUDXPE5.js} +2 -8
  3. package/dist/{api-GZHIDA4X.js → api-ETQ6YJ3C.js} +2 -2
  4. package/dist/{chunk-DY5E3AT7.js → chunk-BEZRLNK3.js} +13 -3
  5. package/dist/chunk-BEZRLNK3.js.map +1 -0
  6. package/dist/{chunk-6ZQVSHKL.js → chunk-C5G2A4PO.js} +7 -3
  7. package/dist/chunk-C5G2A4PO.js.map +1 -0
  8. package/dist/{chunk-WYB6MBZA.js → chunk-EWKPTNPO.js} +579 -973
  9. package/dist/chunk-EWKPTNPO.js.map +1 -0
  10. package/dist/{chunk-TMRLB2LA.js → chunk-HEMJ4SUJ.js} +2 -2
  11. package/dist/{chunk-OKXMUYIB.js → chunk-HNDFPXUU.js} +5 -5
  12. package/dist/{chunk-7JL3T7BO.js → chunk-RRUYHORU.js} +161 -74
  13. package/dist/chunk-RRUYHORU.js.map +1 -0
  14. package/dist/{chunk-PFRRIDYA.js → chunk-TIVL4UQ7.js} +2 -2
  15. package/dist/{chunk-2MKP6IYD.js → chunk-VYG4AXYW.js} +2 -2
  16. package/dist/components.d.ts +2 -2
  17. package/dist/components.js +15 -15
  18. package/dist/hooks.d.ts +1 -1
  19. package/dist/hooks.js +4 -4
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +16 -16
  22. package/dist/providers.js +2 -2
  23. package/dist/rbac/index.d.ts +2 -0
  24. package/dist/rbac/index.js +22 -10
  25. package/dist/rbac/index.js.map +1 -1
  26. package/dist/{types-CInEi-ng.d.ts → types-DiRQsGJs.d.ts} +0 -2
  27. package/dist/utils.d.ts +2 -2
  28. package/dist/utils.js +1 -1
  29. package/docs/api/classes/ErrorBoundary.md +1 -1
  30. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  31. package/docs/api/interfaces/AggregateConfig.md +1 -1
  32. package/docs/api/interfaces/ButtonProps.md +1 -1
  33. package/docs/api/interfaces/CardProps.md +1 -1
  34. package/docs/api/interfaces/ColorPalette.md +1 -1
  35. package/docs/api/interfaces/ColorShade.md +1 -1
  36. package/docs/api/interfaces/DataTableAction.md +1 -1
  37. package/docs/api/interfaces/DataTableColumn.md +1 -1
  38. package/docs/api/interfaces/DataTableProps.md +33 -33
  39. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  40. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  41. package/docs/api/interfaces/EventContextType.md +1 -1
  42. package/docs/api/interfaces/EventLogoProps.md +1 -1
  43. package/docs/api/interfaces/EventProviderProps.md +1 -1
  44. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  45. package/docs/api/interfaces/FileUploadProps.md +1 -1
  46. package/docs/api/interfaces/FooterProps.md +1 -1
  47. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  48. package/docs/api/interfaces/InputProps.md +1 -1
  49. package/docs/api/interfaces/LabelProps.md +1 -1
  50. package/docs/api/interfaces/LoginFormProps.md +1 -1
  51. package/docs/api/interfaces/NavigationItem.md +1 -1
  52. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  53. package/docs/api/interfaces/Organisation.md +1 -1
  54. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  55. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  56. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  57. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  58. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  59. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  60. package/docs/api/interfaces/PaletteData.md +1 -1
  61. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  62. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  63. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  64. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  65. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  66. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  67. package/docs/api/interfaces/StorageConfig.md +1 -1
  68. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  69. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  70. package/docs/api/interfaces/StorageListOptions.md +1 -1
  71. package/docs/api/interfaces/StorageListResult.md +1 -1
  72. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  73. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  74. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  75. package/docs/api/interfaces/StyleImport.md +1 -1
  76. package/docs/api/interfaces/ToastActionElement.md +1 -1
  77. package/docs/api/interfaces/ToastProps.md +1 -1
  78. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  79. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  80. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  81. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  82. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  83. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  84. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  85. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  86. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  87. package/docs/api/interfaces/UserEventAccess.md +1 -1
  88. package/docs/api/interfaces/UserMenuProps.md +1 -1
  89. package/docs/api/interfaces/UserProfile.md +1 -1
  90. package/docs/api/modules.md +10 -10
  91. package/docs/architecture/README.md +1 -1
  92. package/package.json +1 -1
  93. package/src/__tests__/shared/testUtils.optimized.tsx +65 -7
  94. package/src/components/DataTable/DataTable.tsx +1 -3
  95. package/src/components/DataTable/__tests__/DataTable.errorHandling.test.tsx +0 -8
  96. package/src/components/DataTable/__tests__/DataTable.hierarchical.test.tsx +17 -12
  97. package/src/components/DataTable/__tests__/DataTable.infinite-loop.test.tsx +0 -1
  98. package/src/components/DataTable/__tests__/DataTable.integration.test.tsx +4 -12
  99. package/src/components/DataTable/__tests__/DataTable.performance.test.tsx +0 -8
  100. package/src/components/DataTable/__tests__/DataTable.permissions.test.tsx +21 -11
  101. package/src/components/DataTable/__tests__/DataTable.sorting.test.tsx +321 -0
  102. package/src/components/DataTable/__tests__/DataTable.userWorkflows.test.tsx +21 -11
  103. package/src/components/DataTable/__tests__/DataTable.workflowValidation.test.tsx +94 -0
  104. package/src/components/DataTable/__tests__/DataTable.workflows.test.tsx +25 -15
  105. package/src/components/DataTable/__tests__/README.md +11 -2
  106. package/src/components/DataTable/__tests__/performance-regression.test.tsx +0 -11
  107. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -1
  108. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +2 -2
  109. package/src/components/DataTable/components/DataTableBody.tsx +34 -35
  110. package/src/components/DataTable/components/DataTableCore.tsx +205 -133
  111. package/src/components/DataTable/components/DataTableToolbar.tsx +9 -10
  112. package/src/components/DataTable/components/DraggableColumnHeader.tsx +3 -7
  113. package/src/components/DataTable/components/EditableRow.tsx +6 -7
  114. package/src/components/DataTable/components/FilterRow.tsx +0 -1
  115. package/src/components/DataTable/components/GroupingDropdown.tsx +2 -2
  116. package/src/components/DataTable/components/UnifiedTableBody.tsx +83 -281
  117. package/src/components/DataTable/components/VirtualizedDataTable.tsx +9 -89
  118. package/src/components/DataTable/components/__tests__/DataTable.accessibility.test.tsx +111 -5
  119. package/src/components/DataTable/components/__tests__/DataTable.integration.test.tsx +82 -13
  120. package/src/components/DataTable/components/__tests__/DataTable.performance.test.tsx +0 -1
  121. package/src/components/DataTable/components/__tests__/DataTable.real.test.tsx +2 -2
  122. package/src/components/DataTable/components/__tests__/DataTable.security.test.tsx +0 -1
  123. package/src/components/DataTable/components/__tests__/DataTable.unit.test.tsx +2 -2
  124. package/src/components/DataTable/components/__tests__/FilteringToggle.unit.test.tsx +3 -0
  125. package/src/components/DataTable/components/index.ts +0 -1
  126. package/src/components/DataTable/core/DataTableContext.tsx +0 -1
  127. package/src/components/DataTable/index.ts +0 -2
  128. package/src/components/DataTable/types.ts +0 -2
  129. package/src/components/Input/Input.tsx +2 -2
  130. package/src/components/Input/__tests__/Input.unit.test.tsx +4 -4
  131. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +6 -2
  132. package/src/components/RBAC/PagePermissionGuard.tsx +13 -0
  133. package/src/components/RBAC/__tests__/PagePermissionGuard.unit.test.tsx +10 -1
  134. package/src/components/Select/Select.tsx +7 -1
  135. package/src/components/__tests__/EdgeCaseTesting.enhanced.test.tsx +2 -1
  136. package/src/hooks/__tests__/useRBAC.unit.test.ts +32 -24
  137. package/src/providers/RBACProvider.tsx +14 -2
  138. package/src/providers/__tests__/UnifiedAuthProvider.unit.test.tsx +11 -3
  139. package/src/rbac/__tests__/cache-invalidation.test.ts +2 -2
  140. package/src/rbac/__tests__/cache.test.ts +3 -3
  141. package/src/rbac/api.ts +2 -0
  142. package/src/rbac/cache.ts +2 -0
  143. package/src/rbac/hooks.ts +15 -0
  144. package/src/rbac/types.ts +2 -0
  145. package/src/utils/__tests__/lazyLoad.unit.test.tsx +13 -18
  146. package/src/utils/storage/__tests__/helpers.unit.test.ts +9 -7
  147. package/dist/chunk-6ZQVSHKL.js.map +0 -1
  148. package/dist/chunk-7JL3T7BO.js.map +0 -1
  149. package/dist/chunk-DY5E3AT7.js.map +0 -1
  150. package/dist/chunk-WYB6MBZA.js.map +0 -1
  151. package/src/components/DataTable/__tests__/DataTable.autoSizing.test.tsx +0 -526
  152. package/src/components/DataTable/components/DataTableHeader.tsx +0 -31
  153. package/src/components/DataTable/components/__tests__/DataTableHeader.unit.test.tsx +0 -143
  154. package/src/components/DataTable/examples/AutoSizingExample.tsx +0 -180
  155. package/src/components/DataTable/examples/ColumnSizingComparison.tsx +0 -235
  156. package/src/components/DataTable/utils/__tests__/columnSizing.test.ts +0 -237
  157. package/src/components/DataTable/utils/columnSizing.ts +0 -125
  158. /package/dist/{DataTable-GEY5U7OI.js.map → DataTable-EEUDXPE5.js.map} +0 -0
  159. /package/dist/{api-GZHIDA4X.js.map → api-ETQ6YJ3C.js.map} +0 -0
  160. /package/dist/{chunk-TMRLB2LA.js.map → chunk-HEMJ4SUJ.js.map} +0 -0
  161. /package/dist/{chunk-OKXMUYIB.js.map → chunk-HNDFPXUU.js.map} +0 -0
  162. /package/dist/{chunk-PFRRIDYA.js.map → chunk-TIVL4UQ7.js.map} +0 -0
  163. /package/dist/{chunk-2MKP6IYD.js.map → chunk-VYG4AXYW.js.map} +0 -0
@@ -58,9 +58,8 @@
58
58
 
59
59
  import React from 'react';
60
60
  import { type Table, flexRender } from '@tanstack/react-table';
61
- import { TableBody, TableHead, TableHeader, TableRow, TableCell } from '../../Table/Table';
62
61
  import { Button } from '../../Button/Button';
63
- import { ChevronUp, ChevronDown, ChevronRight } from 'lucide-react';
62
+ import { ChevronUp, ChevronDown, ChevronRight, ChevronsUpDown } from 'lucide-react';
64
63
  import { EmptyState } from './EmptyState';
65
64
  import { Input } from '../../Input/Input';
66
65
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../Select/Select';
@@ -228,12 +227,12 @@ export function DataTableBody<TData extends Record<string, any>>({
228
227
  return (
229
228
  <>
230
229
  {/* Table Header */}
231
- <TableHeader>
230
+ <thead>
232
231
  {table.getHeaderGroups().map((headerGroup) => (
233
- <TableRow key={headerGroup.id}>
232
+ <tr key={headerGroup.id}>
234
233
  {/* Expand/Collapse All Button - Only show for hierarchical tables */}
235
234
  {hierarchical?.enabled && hierarchical?.hasAnyChildren && (
236
- <TableHead className="w-12">
235
+ <th className="w-12">
237
236
  <Button
238
237
  variant="ghost"
239
238
  size="sm"
@@ -248,7 +247,7 @@ export function DataTableBody<TData extends Record<string, any>>({
248
247
  <ChevronRight className="h-4 w-4" />
249
248
  )}
250
249
  </Button>
251
- </TableHead>
250
+ </th>
252
251
  )}
253
252
 
254
253
  {headerGroup.headers.map((header) => {
@@ -261,7 +260,7 @@ export function DataTableBody<TData extends Record<string, any>>({
261
260
  : 'none')
262
261
  : undefined;
263
262
  return (
264
- <TableHead
263
+ <th
265
264
  key={header.id}
266
265
  {...(isSortable ? { 'aria-sort': ariaSort } : {})}
267
266
  >
@@ -283,7 +282,7 @@ export function DataTableBody<TData extends Record<string, any>>({
283
282
  ) : header.column.getIsSorted() === 'desc' ? (
284
283
  <ChevronDown className="h-4 w-4" />
285
284
  ) : (
286
- <div className="h-4 w-4" />
285
+ <ChevronsUpDown className="h-4 w-4" />
287
286
  )}
288
287
  </div>
289
288
  </Button>
@@ -295,20 +294,20 @@ export function DataTableBody<TData extends Record<string, any>>({
295
294
  </div>
296
295
  )
297
296
  )}
298
- </TableHead>
297
+ </th>
299
298
  );
300
299
  })}
301
- </TableRow>
300
+ </tr>
302
301
  ))}
303
- </TableHeader>
302
+ </thead>
304
303
 
305
304
  {/* Table Body */}
306
- <TableBody>
305
+ <tbody>
307
306
  {/* Creation Row */}
308
307
  {isCreating && (
309
- <TableRow>
308
+ <tr>
310
309
  {table.getVisibleFlatColumns().map((column) => (
311
- <TableCell key={column.id}>
310
+ <td key={column.id}>
312
311
  {renderEditField(column, creationData[column.id], (value) => {
313
312
  if (typeof value === 'object' && value !== null) {
314
313
  // Handle editAccessorKey case
@@ -318,9 +317,9 @@ export function DataTableBody<TData extends Record<string, any>>({
318
317
  onCreationDataChange({ ...creationData, [column.id]: value });
319
318
  }
320
319
  }, creationData)}
321
- </TableCell>
320
+ </td>
322
321
  ))}
323
- <TableCell className="flex gap-1">
322
+ <td className="flex gap-1">
324
323
  <>
325
324
  <button
326
325
  onClick={onSaveCreation}
@@ -341,14 +340,14 @@ export function DataTableBody<TData extends Record<string, any>>({
341
340
  </svg>
342
341
  </button>
343
342
  </>
344
- </TableCell>
345
- </TableRow>
343
+ </td>
344
+ </tr>
346
345
  )}
347
346
 
348
347
  {/* Table Rows */}
349
348
  {table.getRowModel().rows.length === 0 && (
350
- <TableRow>
351
- <TableCell colSpan={table.getVisibleFlatColumns().length} role="status">
349
+ <tr>
350
+ <td colSpan={table.getVisibleFlatColumns().length} role="status">
352
351
  <EmptyState
353
352
  title={emptyState?.title}
354
353
  description={emptyState?.description}
@@ -357,8 +356,8 @@ export function DataTableBody<TData extends Record<string, any>>({
357
356
  isFiltered={isFiltered}
358
357
  onClearFilters={onClearFilters}
359
358
  />
360
- </TableCell>
361
- </TableRow>
359
+ </td>
360
+ </tr>
362
361
  )}
363
362
 
364
363
  {table.getRowModel().rows.map((row) => {
@@ -374,8 +373,8 @@ export function DataTableBody<TData extends Record<string, any>>({
374
373
  return (
375
374
  <React.Fragment key={row.id}>
376
375
  {/* Group Header Row */}
377
- <TableRow className="bg-app-sec-50 hover:bg-app-sec-100">
378
- <TableCell className="flex items-center py-2 font-medium" colSpan={table.getAllColumns().length}>
376
+ <tr className="bg-app-sec-50 hover:bg-app-sec-100">
377
+ <td className="flex items-center py-2 font-medium" colSpan={table.getAllColumns().length}>
379
378
  <>
380
379
  <Button
381
380
  variant="ghost"
@@ -393,8 +392,8 @@ export function DataTableBody<TData extends Record<string, any>>({
393
392
  {String(groupValue)} ({subRowsCount} items)
394
393
  </span>
395
394
  </>
396
- </TableCell>
397
- </TableRow>
395
+ </td>
396
+ </tr>
398
397
 
399
398
  {/* Render sub-rows if expanded */}
400
399
  {isExpanded && row.subRows?.map((subRow) => {
@@ -402,9 +401,9 @@ export function DataTableBody<TData extends Record<string, any>>({
402
401
  const isSubRowEditing = editingRowId === subRowId;
403
402
 
404
403
  return (
405
- <TableRow key={subRow.id} className="border-l-2 border-l-blue-200">
404
+ <tr key={subRow.id} className="border-l-2 border-l-blue-200">
406
405
  {subRow.getVisibleCells().map((cell) => (
407
- <TableCell key={cell.id} className="pl-8">
406
+ <td key={cell.id} className="pl-8">
408
407
  {isSubRowEditing && cell.column.id !== 'actions' ? (
409
408
  renderEditField(cell.column, editingData[cell.column.id], (value) => {
410
409
  if (typeof value === 'object' && value !== null) {
@@ -418,9 +417,9 @@ export function DataTableBody<TData extends Record<string, any>>({
418
417
  ) : (
419
418
  flexRender(cell.column.columnDef.cell, cell.getContext())
420
419
  )}
421
- </TableCell>
420
+ </td>
422
421
  ))}
423
- </TableRow>
422
+ </tr>
424
423
  );
425
424
  })}
426
425
  </React.Fragment>
@@ -435,9 +434,9 @@ export function DataTableBody<TData extends Record<string, any>>({
435
434
 
436
435
  // Regular row (non-grouped or when grouping is disabled)
437
436
  return (
438
- <TableRow key={row.id}>
437
+ <tr key={row.id}>
439
438
  {row.getVisibleCells().map((cell) => (
440
- <TableCell key={cell.id}>
439
+ <td key={cell.id}>
441
440
  {isEditing && cell.column.id !== 'actions' ? (
442
441
  renderEditField(cell.column, editingData[cell.column.id], (value) => {
443
442
  if (typeof value === 'object' && value !== null) {
@@ -451,12 +450,12 @@ export function DataTableBody<TData extends Record<string, any>>({
451
450
  ) : (
452
451
  flexRender(cell.column.columnDef.cell, cell.getContext())
453
452
  )}
454
- </TableCell>
453
+ </td>
455
454
  ))}
456
- </TableRow>
455
+ </tr>
457
456
  );
458
457
  })}
459
- </TableBody>
458
+ </tbody>
460
459
  </>
461
460
  );
462
461
  }
@@ -17,6 +17,7 @@ import {
17
17
  getPaginationRowModel,
18
18
  getGroupedRowModel,
19
19
  getExpandedRowModel,
20
+ flexRender,
20
21
  type SortingState,
21
22
  type ColumnDef,
22
23
  type ColumnFiltersState,
@@ -27,12 +28,12 @@ import {
27
28
  type HeaderContext,
28
29
  type CellContext,
29
30
  } from '@tanstack/react-table';
30
- import { Edit, Trash } from 'lucide-react';
31
+ import { Edit, Trash, ChevronUp, ChevronDown, ChevronsUpDown } from 'lucide-react';
31
32
  import { cn } from '../../../utils/cn';
32
33
  import { Checkbox } from '../../Checkbox/Checkbox';
34
+ import { Button } from '../../Button/Button';
33
35
  import { getTableClasses, getMainContainerClasses } from '../styles';
34
36
  import { useDataTablePerformance } from '../../../hooks/useDataTablePerformance';
35
- import { DataTableHeader } from './DataTableHeader';
36
37
  import { DataTableToolbar } from './DataTableToolbar';
37
38
  import { UnifiedTableBody } from './UnifiedTableBody';
38
39
  import { PaginationControls, EnhancedPaginationControls } from './PaginationControls';
@@ -44,7 +45,6 @@ import { useHierarchicalState } from '../hooks/useHierarchicalState';
44
45
  import { useDataTableState } from '../hooks/useDataTableState';
45
46
  import { validateHierarchicalData, sortHierarchicalData } from '../utils/hierarchicalUtils';
46
47
  import { sortHierarchicalData as sortHierarchicalDataWithSorting } from '../utils/hierarchicalSorting';
47
- import { calculateColumnWidths, getColumnSizingConfig } from '../utils/columnSizing';
48
48
  import { ColumnFactory } from '../core/ColumnFactory';
49
49
 
50
50
  import type {
@@ -551,17 +551,6 @@ function DataTableInternal<TData extends DataRecord>({
551
551
  // COLUMN WIDTHS
552
552
  // ============================================================================
553
553
 
554
- const columnWidths = useMemo(() => {
555
- if (features.autoColumnSizing) {
556
- return calculateColumnWidths(data, enhancedColumns, {
557
- minWidth: 80,
558
- maxWidth: 400,
559
- padding: 32,
560
- sampleSize: Math.min(100, data.length)
561
- });
562
- }
563
- return undefined;
564
- }, [data, enhancedColumns, features.autoColumnSizing]);
565
554
 
566
555
  // ============================================================================
567
556
  // TABLE CONFIGURATION
@@ -610,7 +599,6 @@ function DataTableInternal<TData extends DataRecord>({
610
599
  manualFiltering: finalPaginationMode === 'server',
611
600
  manualPagination: finalPaginationMode === 'server',
612
601
  pageCount: finalPaginationMode === 'server' ? Math.ceil(dataCount / pagination.pageSize) : undefined,
613
- ...getColumnSizingConfig(features.autoColumnSizing || false, columnWidths),
614
602
  }), [
615
603
  finalTableData,
616
604
  enhancedColumns,
@@ -629,8 +617,6 @@ function DataTableInternal<TData extends DataRecord>({
629
617
  onRowSelectionChange,
630
618
  finalPaginationMode,
631
619
  features.pagination,
632
- features.autoColumnSizing,
633
- columnWidths,
634
620
  dataCount,
635
621
  pagination.pageSize
636
622
  ]);
@@ -707,135 +693,221 @@ function DataTableInternal<TData extends DataRecord>({
707
693
  ? EnhancedPaginationControls
708
694
  : PaginationControls;
709
695
 
696
+ // Calculate column counts for colgroup
697
+ const visibleColumns = table?.getVisibleFlatColumns() || [];
698
+ const dataColumns = visibleColumns.filter(col =>
699
+ col.id !== 'select' && col.id !== 'actions'
700
+ ).length;
701
+ const hasSelectColumn = visibleColumns.some(col => col.id === 'select');
702
+ const hasActionsColumn = visibleColumns.some(col => col.id === 'actions');
703
+
710
704
  return (
711
705
  <>
712
- {/* Header */}
713
- {(title || description) && (
714
- <DataTableHeader
715
- title={title}
716
- description={description}
717
- />
718
- )}
719
-
720
- {/* Toolbar */}
721
- <DataTableToolbar
722
- features={features}
723
- globalFilter={searchQuery}
724
- onGlobalFilterChange={handleSearch}
725
- columns={columns}
726
- grouping={grouping}
727
- onGroupByChange={(columnId) => {
728
- setGrouping(columnId ? [columnId] : []);
729
- }}
730
- tableColumns={table?.getAllColumns() || []}
731
- onColumnVisibilityChange={(columnId, visible) => {
732
- setColumnVisibility(prev => ({ ...prev, [columnId]: visible }));
733
- }}
734
- onCreateRow={features.creation && onCreateRow ? () => tableActions.setCreating(true) : undefined}
735
- onImportClick={() => setShowImportModal(true)}
736
- onExport={() => {
737
- // Export logic here
738
- }}
739
- rowSelection={rowSelection}
740
- onDeleteSelected={onDeleteSelected}
741
- onToggleFilterRow={() => setShowFilterRow(!showFilterRow)}
742
- showFilterRow={showFilterRow}
743
- />
744
-
745
- {/* Table */}
706
+ {/* Table with semantic HTML structure */}
746
707
  <table className={getTableClasses({
747
- isFixed: !features.autoColumnSizing,
708
+ isFixed: true,
748
709
  variant,
749
710
  className: cn('border-collapse relative w-full', className)
750
711
  })} style={{
751
- tableLayout: features.autoColumnSizing ? 'auto' : 'fixed',
712
+ tableLayout: 'fixed',
752
713
  width: '100%'
753
714
  }}>
715
+ {/* Caption with title, description, and toolbar */}
716
+ <caption className="text-left pb-2">
717
+ {(title || description) && (
718
+ <>
719
+ {title && <h2 >{title}</h2>}
720
+ {description && <p>{description}</p>}
721
+ </>
722
+ )}
723
+ <>
724
+ <DataTableToolbar
725
+ features={features}
726
+ globalFilter={searchQuery}
727
+ onGlobalFilterChange={handleSearch}
728
+ columns={columns}
729
+ grouping={grouping}
730
+ onGroupByChange={(columnId) => {
731
+ setGrouping(columnId ? [columnId] : []);
732
+ }}
733
+ tableColumns={table?.getAllColumns() || []}
734
+ onColumnVisibilityChange={(columnId, visible) => {
735
+ setColumnVisibility(prev => ({ ...prev, [columnId]: visible }));
736
+ }}
737
+ onCreateRow={features.creation && onCreateRow ? () => tableActions.setCreating(true) : undefined}
738
+ onImportClick={() => setShowImportModal(true)}
739
+ onExport={() => {
740
+ // Export logic here
741
+ }}
742
+ rowSelection={rowSelection}
743
+ onDeleteSelected={onDeleteSelected}
744
+ onToggleFilterRow={() => setShowFilterRow(!showFilterRow)}
745
+ showFilterRow={showFilterRow}
746
+ />
747
+ </>
748
+ </caption>
749
+
750
+ {/* Column groups */}
751
+ <colgroup>
752
+ {hasSelectColumn && <col span={1} data-col-type="select" />}
753
+ <col span={dataColumns} data-col-type="data" />
754
+ {hasActionsColumn && <col span={1} data-col-type="actions" />}
755
+ </colgroup>
756
+
757
+ {/* Table header */}
758
+ <thead>
759
+ {table?.getHeaderGroups().map((headerGroup) => (
760
+ <tr key={headerGroup.id}>
761
+ {headerGroup.headers
762
+ .filter(header => {
763
+ return typeof header.column.getIsVisible === 'function'
764
+ ? header.column.getIsVisible()
765
+ : true;
766
+ })
767
+ .map((header) => {
768
+ const isSortable = header.column.getCanSort();
769
+ const ariaSort = isSortable
770
+ ? (header.column.getIsSorted() === 'asc'
771
+ ? 'ascending'
772
+ : header.column.getIsSorted() === 'desc'
773
+ ? 'descending'
774
+ : 'none')
775
+ : undefined;
776
+ return (
777
+ <th
778
+ key={header.id}
779
+ {...(isSortable ? { 'aria-sort': ariaSort } : {})}
780
+ >
781
+ {header.isPlaceholder ? null : (
782
+ isSortable ? (
783
+ <Button
784
+ variant="ghost"
785
+ className="h-auto p-0 font-medium hover:bg-transparent"
786
+ onClick={header.column.getToggleSortingHandler()}
787
+ aria-label={`Sort by ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : 'column'}`}
788
+ tabIndex={0}
789
+ >
790
+ <div className="flex items-center gap-1">
791
+ {typeof header.column.columnDef.header === 'function'
792
+ ? header.column.columnDef.header(header.getContext())
793
+ : header.column.columnDef.header}
794
+ {header.column.getIsSorted() === 'asc' ? (
795
+ <ChevronUp className="h-4 w-4" />
796
+ ) : header.column.getIsSorted() === 'desc' ? (
797
+ <ChevronDown className="h-4 w-4" />
798
+ ) : (
799
+ <ChevronsUpDown className="h-4 w-4" />
800
+ )}
801
+ </div>
802
+ </Button>
803
+ ) : (
804
+ <div>
805
+ {typeof header.column.columnDef.header === 'function'
806
+ ? header.column.columnDef.header(header.getContext())
807
+ : header.column.columnDef.header}
808
+ </div>
809
+ )
810
+ )}
811
+ </th>
812
+ );
813
+ })}
814
+ </tr>
815
+ ))}
816
+ </thead>
817
+
818
+ {/* Table body */}
754
819
  <UnifiedTableBody
755
- table={table}
756
- isCreating={isCreating}
757
- creationData={creationData}
758
- onCreationDataChange={tableActions.setCreationData}
759
- onSaveCreation={() => {
760
- if (onCreateRow) {
761
- onCreateRow(creationData as Partial<TData>);
820
+ table={table}
821
+ isCreating={isCreating}
822
+ creationData={creationData}
823
+ onCreationDataChange={tableActions.setCreationData}
824
+ onSaveCreation={() => {
825
+ if (onCreateRow) {
826
+ onCreateRow(creationData as Partial<TData>);
827
+ tableActions.clearCreationData();
828
+ tableActions.setCreating(false);
829
+ }
830
+ }}
831
+ onCancelCreation={() => {
762
832
  tableActions.clearCreationData();
763
833
  tableActions.setCreating(false);
764
- }
765
- }}
766
- onCancelCreation={() => {
767
- tableActions.clearCreationData();
768
- tableActions.setCreating(false);
769
- }}
770
- editingRowId={editingRowId}
771
- editingData={editingData}
772
- onEditingDataChange={(data) => {
773
- // Update the editing data in the centralized state
774
- if (editingRowId) {
775
- tableActions.setEditingRow(editingRowId, data);
776
- }
777
- }}
778
- onSaveEditing={() => {
779
- if (onEditRow && editingRowId) {
780
- // Find the original row data
781
- const originalRow = data.find(row => {
782
- const rowId = getRowId ? getRowId(row, 0) : (row as any).id || String(0);
783
- return rowId === editingRowId;
784
- });
785
- if (originalRow) {
786
- onEditRow(originalRow, editingData as Partial<TData>);
834
+ }}
835
+ editingRowId={editingRowId}
836
+ editingData={editingData}
837
+ onEditingDataChange={(data) => {
838
+ // Update the editing data in the centralized state
839
+ if (editingRowId) {
840
+ tableActions.setEditingRow(editingRowId, data);
787
841
  }
788
- }
789
- tableActions.clearEditing();
790
- }}
791
- onCancelEditing={() => {
792
- tableActions.clearEditing();
793
- }}
794
- grouping={grouping}
795
- aggregates={aggregates}
796
- getRowId={getRowId}
797
- emptyState={React.isValidElement(emptyState) ? undefined : emptyState as any}
798
- isFiltered={searchQuery !== '' || columnFilters.length > 0}
799
- onClearFilters={() => {
800
- setSearchQuery('');
801
- setColumnFilters([]);
802
- }}
803
- enableFiltering={features.filtering}
804
- showFilterRow={showFilterRow}
805
- enableColumnReordering={features.columnReordering}
806
- onColumnOrderChange={setColumnOrder}
807
- onColumnDrop={(draggedColumnId, targetColumnId) => {
808
- // Handle column drop
809
- }}
810
- savedColumnOrder={savedColumnOrder}
811
- enablePersistence={features.columnReordering}
812
- tableId={title ? `datatable-${title.toLowerCase().replace(/\s+/g, '-')}` : undefined}
813
- dataLength={finalTableData?.length || 0}
814
- virtualHeight={virtualHeight}
815
- forceVirtualization={false}
816
- hierarchical={features.hierarchical && hierarchical?.enabled ? {
817
- ...hierarchical,
818
- state: hierarchicalState,
819
- expandAll: hierarchicalState.expandAll,
820
- collapseAll: hierarchicalState.collapseAll,
821
- isAllExpanded: hierarchicalState.getExpandedIds().length > 0 &&
822
- hierarchicalState.getExpandedIds().length === (finalTableData as any[]).filter(row => row.isParent).length,
823
- hasAnyChildren: (finalTableData as any[]).some(row => row.isParent),
824
- } : undefined}
825
- actions={effectiveActions}
826
- />
827
- </table>
842
+ }}
843
+ onSaveEditing={() => {
844
+ if (onEditRow && editingRowId) {
845
+ // Find the original row data
846
+ const originalRow = data.find(row => {
847
+ const rowId = getRowId ? getRowId(row, 0) : (row as any).id || String(0);
848
+ return rowId === editingRowId;
849
+ });
850
+ if (originalRow) {
851
+ onEditRow(originalRow, editingData as Partial<TData>);
852
+ }
853
+ }
854
+ tableActions.clearEditing();
855
+ }}
856
+ onCancelEditing={() => {
857
+ tableActions.clearEditing();
858
+ }}
859
+ grouping={grouping}
860
+ aggregates={aggregates}
861
+ getRowId={getRowId}
862
+ emptyState={React.isValidElement(emptyState) ? undefined : emptyState as any}
863
+ isFiltered={searchQuery !== '' || columnFilters.length > 0}
864
+ onClearFilters={() => {
865
+ setSearchQuery('');
866
+ setColumnFilters([]);
867
+ }}
868
+ enableFiltering={features.filtering}
869
+ showFilterRow={showFilterRow}
870
+ enableColumnReordering={features.columnReordering}
871
+ onColumnOrderChange={setColumnOrder}
872
+ onColumnDrop={(draggedColumnId, targetColumnId) => {
873
+ // Handle column drop
874
+ }}
875
+ savedColumnOrder={savedColumnOrder}
876
+ enablePersistence={features.columnReordering}
877
+ tableId={title ? `datatable-${title.toLowerCase().replace(/\s+/g, '-')}` : undefined}
878
+ dataLength={finalTableData?.length || 0}
879
+ virtualHeight={virtualHeight}
880
+ forceVirtualization={false}
881
+ hierarchical={features.hierarchical && hierarchical?.enabled ? {
882
+ ...hierarchical,
883
+ state: hierarchicalState,
884
+ expandAll: hierarchicalState.expandAll,
885
+ collapseAll: hierarchicalState.collapseAll,
886
+ isAllExpanded: hierarchicalState.getExpandedIds().length > 0 &&
887
+ hierarchicalState.getExpandedIds().length === (finalTableData as any[]).filter(row => row.isParent).length,
888
+ hasAnyChildren: (finalTableData as any[]).some(row => row.isParent),
889
+ } : undefined}
890
+ actions={effectiveActions}
891
+ />
828
892
 
829
- {/* Pagination */}
830
- {features.pagination && (
831
- <PaginationComponent
832
- table={table}
833
- pageSizeOptions={finalPageSizeOptions}
834
- paginationMode={finalPaginationMode}
835
- totalCount={finalDataCount}
836
- isLoading={isLoading}
837
- />
838
- )}
893
+ {/* Table footer with pagination */}
894
+ {features.pagination && (
895
+ <tfoot>
896
+ <tr>
897
+ <td colSpan={visibleColumns.length}>
898
+ <PaginationComponent
899
+ table={table}
900
+ pageSizeOptions={finalPageSizeOptions}
901
+ paginationMode={finalPaginationMode}
902
+ totalCount={finalDataCount}
903
+ isLoading={isLoading}
904
+ />
905
+ </td>
906
+ </tr>
907
+ </tfoot>
908
+ )}
909
+
910
+ </table>
839
911
 
840
912
  {/* Modal Dialogs */}
841
913
  <DataTableModals
@@ -150,11 +150,10 @@ export function DataTableToolbar<TData extends DataRecord>({
150
150
  const currentGroupBy = grouping.length > 0 ? grouping[0] : null;
151
151
 
152
152
  return (
153
- <nav className="flex flex-col sm:flex-row items-stretch sm:items-center gap-4 p-4 border-b bg-sec-50">
154
- {/* Left side - Search only */}
153
+
154
+ <nav className="flex justify-end flex-wrap gap-2">
155
155
  {features.search && (
156
- <div className="flex items-center gap-2 w-full sm:min-w-[200px] sm:max-w-[300px]">
157
- <Search className="h-4 w-4 text-sec-500 flex-shrink-0" />
156
+
158
157
  <Input
159
158
  id="table-search"
160
159
  placeholder="Search..."
@@ -162,13 +161,13 @@ export function DataTableToolbar<TData extends DataRecord>({
162
161
  onChange={(e) => onGlobalFilterChange(e.target.value)}
163
162
  aria-label="Search table"
164
163
  tabIndex={0}
165
- className="flex-1"
164
+ className="justify-self-start w-50 flex-1"
166
165
  />
167
- </div>
166
+
168
167
  )}
169
168
 
170
- {/* Right side - All Buttons in specified order */}
171
- <div className="flex items-center gap-2 flex-wrap justify-end sm:justify-end min-w-0">
169
+
170
+
172
171
  {/* Grouping */}
173
172
  {features.grouping && (
174
173
  <GroupingDropdown
@@ -245,7 +244,7 @@ export function DataTableToolbar<TData extends DataRecord>({
245
244
  </Button>
246
245
  )}
247
246
 
248
- </div>
249
- </nav>
247
+ </nav>
248
+
250
249
  );
251
250
  }