@jmruthers/pace-core 0.5.121 → 0.5.123

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 (254) hide show
  1. package/dist/{AuthService-D4646R4b.d.ts → AuthService-DYuQPJj6.d.ts} +0 -9
  2. package/dist/{DataTable-DGZDJUYM.js → DataTable-WTS4IRF2.js} +7 -8
  3. package/dist/{PublicLoadingSpinner-DgDWTFqn.d.ts → PublicLoadingSpinner-CaoRbHvJ.d.ts} +30 -4
  4. package/dist/{UnifiedAuthProvider-UACKFATV.js → UnifiedAuthProvider-6C47WIML.js} +3 -4
  5. package/dist/{chunk-D6BOFXYR.js → chunk-35ZDPMBM.js} +3 -3
  6. package/dist/{chunk-CGURJ27Z.js → chunk-4MXVZVNS.js} +2 -2
  7. package/dist/{chunk-ZYJ6O5CA.js → chunk-C43QIDN3.js} +2 -2
  8. package/dist/{chunk-VKOCWWVY.js → chunk-CX5M4ZAG.js} +1 -6
  9. package/dist/{chunk-VKOCWWVY.js 3.map → chunk-CX5M4ZAG.js.map} +1 -1
  10. package/dist/{chunk-HFBOFZ3Z.js → chunk-DHMFMXFV.js} +258 -243
  11. package/dist/chunk-DHMFMXFV.js.map +1 -0
  12. package/dist/{chunk-RIEJGKD3.js → chunk-ESJTIADP.js} +15 -6
  13. package/dist/{chunk-RIEJGKD3.js.map → chunk-ESJTIADP.js.map} +1 -1
  14. package/dist/{chunk-SMJZMKYN.js → chunk-GEVIB2UB.js} +43 -10
  15. package/dist/chunk-GEVIB2UB.js.map +1 -0
  16. package/dist/{chunk-TDNI6ZWL.js → chunk-IJOZZOGT.js} +7 -7
  17. package/dist/chunk-IJOZZOGT.js.map +1 -0
  18. package/dist/{chunk-GZRXOUBE.js → chunk-M6DDYFUD.js} +2 -2
  19. package/dist/chunk-M6DDYFUD.js.map +1 -0
  20. package/dist/{chunk-B4GZ2BXO.js → chunk-NZGLXZGP.js} +3 -3
  21. package/dist/{chunk-NZ32EONV.js → chunk-QWNJCQXZ.js} +2 -2
  22. package/dist/{chunk-FKFHZUGF.js → chunk-XN6GWKMV.js} +43 -56
  23. package/dist/chunk-XN6GWKMV.js.map +1 -0
  24. package/dist/{chunk-BHWIUEYH.js → chunk-ZBLK676C.js} +1 -61
  25. package/dist/chunk-ZBLK676C.js.map +1 -0
  26. package/dist/{chunk-QPI2CCBA.js → chunk-ZPJMYGEP.js} +149 -96
  27. package/dist/chunk-ZPJMYGEP.js.map +1 -0
  28. package/dist/components.d.ts +1 -1
  29. package/dist/components.js +11 -11
  30. package/dist/{formatting-B1jSqgl-.d.ts → formatting-DFcCxUEk.d.ts} +1 -1
  31. package/dist/hooks.d.ts +1 -1
  32. package/dist/hooks.js +9 -8
  33. package/dist/hooks.js.map +1 -1
  34. package/dist/index.d.ts +6 -6
  35. package/dist/index.js +19 -17
  36. package/dist/index.js.map +1 -1
  37. package/dist/providers.d.ts +2 -2
  38. package/dist/providers.js +2 -3
  39. package/dist/rbac/index.js +7 -8
  40. package/dist/styles/index.d.ts +1 -1
  41. package/dist/styles/index.js +5 -3
  42. package/dist/theming/runtime.d.ts +73 -1
  43. package/dist/theming/runtime.js +5 -5
  44. package/dist/{usePublicRouteParams-BdF8bZgs.d.ts → usePublicRouteParams-Dyt1tzI9.d.ts} +60 -8
  45. package/dist/utils.d.ts +1 -1
  46. package/dist/utils.js +5 -5
  47. package/docs/api/classes/ColumnFactory.md +1 -1
  48. package/docs/api/classes/ErrorBoundary.md +1 -1
  49. package/docs/api/classes/InvalidScopeError.md +1 -1
  50. package/docs/api/classes/MissingUserContextError.md +1 -1
  51. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  52. package/docs/api/classes/PermissionDeniedError.md +1 -1
  53. package/docs/api/classes/PublicErrorBoundary.md +6 -6
  54. package/docs/api/classes/RBACAuditManager.md +1 -1
  55. package/docs/api/classes/RBACCache.md +1 -1
  56. package/docs/api/classes/RBACEngine.md +1 -1
  57. package/docs/api/classes/RBACError.md +1 -1
  58. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  59. package/docs/api/classes/SecureSupabaseClient.md +6 -6
  60. package/docs/api/classes/StorageUtils.md +1 -1
  61. package/docs/api/enums/FileCategory.md +1 -1
  62. package/docs/api/interfaces/AggregateConfig.md +1 -1
  63. package/docs/api/interfaces/ButtonProps.md +1 -1
  64. package/docs/api/interfaces/CardProps.md +1 -1
  65. package/docs/api/interfaces/ColorPalette.md +1 -1
  66. package/docs/api/interfaces/ColorShade.md +1 -1
  67. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  68. package/docs/api/interfaces/DataRecord.md +1 -1
  69. package/docs/api/interfaces/DataTableAction.md +1 -1
  70. package/docs/api/interfaces/DataTableColumn.md +1 -1
  71. package/docs/api/interfaces/DataTableProps.md +1 -1
  72. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  73. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  74. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  75. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  76. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  77. package/docs/api/interfaces/FileMetadata.md +1 -1
  78. package/docs/api/interfaces/FileReference.md +1 -1
  79. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  80. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  81. package/docs/api/interfaces/FileUploadProps.md +1 -1
  82. package/docs/api/interfaces/FooterProps.md +1 -1
  83. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  84. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  85. package/docs/api/interfaces/InputProps.md +1 -1
  86. package/docs/api/interfaces/LabelProps.md +1 -1
  87. package/docs/api/interfaces/LoginFormProps.md +1 -1
  88. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  89. package/docs/api/interfaces/NavigationContextType.md +1 -1
  90. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  91. package/docs/api/interfaces/NavigationItem.md +1 -1
  92. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  93. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  94. package/docs/api/interfaces/Organisation.md +1 -1
  95. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  96. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  97. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  98. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  99. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  100. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  101. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  102. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  103. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  104. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  105. package/docs/api/interfaces/PaletteData.md +1 -1
  106. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  107. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  108. package/docs/api/interfaces/PublicErrorBoundaryProps.md +7 -7
  109. package/docs/api/interfaces/PublicErrorBoundaryState.md +5 -5
  110. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +7 -7
  111. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  112. package/docs/api/interfaces/PublicPageHeaderProps.md +51 -12
  113. package/docs/api/interfaces/PublicPageLayoutProps.md +72 -12
  114. package/docs/api/interfaces/RBACConfig.md +1 -1
  115. package/docs/api/interfaces/RBACLogger.md +1 -1
  116. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  117. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  118. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  119. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  120. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  121. package/docs/api/interfaces/RouteConfig.md +1 -1
  122. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  123. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  124. package/docs/api/interfaces/StorageConfig.md +1 -1
  125. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  126. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  127. package/docs/api/interfaces/StorageListOptions.md +1 -1
  128. package/docs/api/interfaces/StorageListResult.md +1 -1
  129. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  130. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  131. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  132. package/docs/api/interfaces/StyleImport.md +1 -1
  133. package/docs/api/interfaces/SwitchProps.md +1 -1
  134. package/docs/api/interfaces/ToastActionElement.md +1 -1
  135. package/docs/api/interfaces/ToastProps.md +1 -1
  136. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  137. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  138. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  139. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  140. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  141. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  142. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  143. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  144. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  145. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  146. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  147. package/docs/api/interfaces/UserEventAccess.md +1 -1
  148. package/docs/api/interfaces/UserMenuProps.md +1 -1
  149. package/docs/api/interfaces/UserProfile.md +1 -1
  150. package/docs/api/modules.md +140 -30
  151. package/docs/best-practices/README.md +1 -1
  152. package/docs/implementation-guides/datatable-filtering.md +313 -0
  153. package/docs/implementation-guides/datatable-rbac-usage.md +317 -0
  154. package/docs/implementation-guides/hierarchical-datatable.md +850 -0
  155. package/docs/implementation-guides/large-datasets.md +281 -0
  156. package/docs/implementation-guides/performance.md +403 -0
  157. package/docs/implementation-guides/public-pages.md +4 -4
  158. package/docs/migration/quick-migration-guide.md +320 -0
  159. package/docs/rbac/quick-start.md +16 -16
  160. package/docs/troubleshooting/README.md +4 -4
  161. package/docs/troubleshooting/cake-page-permission-guard-issue-summary.md +1 -1
  162. package/docs/troubleshooting/debugging.md +1117 -0
  163. package/docs/troubleshooting/migration.md +918 -0
  164. package/examples/public-pages/CorrectPublicPageImplementation.tsx +30 -30
  165. package/examples/public-pages/PublicEventPage.tsx +41 -41
  166. package/examples/public-pages/PublicPageApp.tsx +33 -33
  167. package/examples/public-pages/PublicPageUsageExample.tsx +30 -30
  168. package/package.json +4 -4
  169. package/src/__tests__/hooks/usePermissions.test.ts +265 -0
  170. package/src/components/DataTable/DataTable.test.tsx +9 -38
  171. package/src/components/DataTable/DataTable.tsx +0 -7
  172. package/src/components/DataTable/components/DataTableCore.tsx +66 -136
  173. package/src/components/DataTable/components/DataTableModals.tsx +25 -22
  174. package/src/components/DataTable/components/EditableRow.tsx +118 -42
  175. package/src/components/DataTable/components/UnifiedTableBody.tsx +129 -76
  176. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +33 -14
  177. package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +17 -5
  178. package/src/components/DataTable/utils/exportUtils.ts +3 -2
  179. package/src/components/Dialog/Dialog.tsx +1 -1
  180. package/src/components/Dialog/README.md +24 -24
  181. package/src/components/Dialog/examples/BasicHtmlTest.tsx +2 -2
  182. package/src/components/Dialog/examples/DebugHtmlExample.tsx +6 -6
  183. package/src/components/Dialog/examples/HtmlDialogExample.tsx +2 -2
  184. package/src/components/Dialog/examples/SimpleHtmlTest.tsx +3 -3
  185. package/src/components/Dialog/examples/__tests__/SimpleHtmlTest.test.tsx +4 -4
  186. package/src/components/PaceAppLayout/PaceAppLayout.tsx +12 -1
  187. package/src/components/PublicLayout/EventLogo.tsx +175 -0
  188. package/src/components/PublicLayout/PublicErrorBoundary.tsx +22 -18
  189. package/src/components/PublicLayout/PublicLoadingSpinner.tsx +22 -14
  190. package/src/components/PublicLayout/PublicPageHeader.tsx +133 -40
  191. package/src/components/PublicLayout/PublicPageLayout.tsx +75 -72
  192. package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +1 -1
  193. package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +8 -8
  194. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +23 -16
  195. package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +86 -14
  196. package/src/examples/CorrectPublicPageImplementation.tsx +30 -30
  197. package/src/examples/PublicEventPage.tsx +41 -41
  198. package/src/examples/PublicPageApp.tsx +33 -33
  199. package/src/examples/PublicPageUsageExample.tsx +30 -30
  200. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +583 -0
  201. package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +10 -3
  202. package/src/hooks/index.ts +1 -1
  203. package/src/hooks/public/usePublicEventLogo.ts +285 -0
  204. package/src/hooks/public/usePublicRouteParams.ts +21 -4
  205. package/src/hooks/useEventTheme.test.ts +119 -43
  206. package/src/hooks/useEventTheme.ts +84 -55
  207. package/src/index.ts +3 -1
  208. package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +630 -0
  209. package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
  210. package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
  211. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +496 -0
  212. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
  213. package/src/rbac/secureClient.ts +4 -2
  214. package/src/services/EventService.ts +0 -66
  215. package/src/services/__tests__/EventService.eventColours.test.ts +44 -40
  216. package/src/styles/index.ts +1 -1
  217. package/src/theming/__tests__/parseEventColours.test.ts +209 -0
  218. package/src/theming/parseEventColours.ts +123 -0
  219. package/src/theming/runtime.ts +3 -0
  220. package/src/types/__tests__/file-reference.test.ts +447 -0
  221. package/src/utils/formatDate.test.ts +11 -11
  222. package/src/utils/formatting.ts +3 -2
  223. package/dist/chunk-BDZUMRBD.js 3.map +0 -1
  224. package/dist/chunk-BHWIUEYH.js.map +0 -1
  225. package/dist/chunk-CGURJ27Z.js.map +0 -1
  226. package/dist/chunk-FKFHZUGF.js.map +0 -1
  227. package/dist/chunk-GKHF54DI 2.js +0 -619
  228. package/dist/chunk-GKHF54DI.js 2.map +0 -1
  229. package/dist/chunk-GZRXOUBE.js.map +0 -1
  230. package/dist/chunk-HFBOFZ3Z.js.map +0 -1
  231. package/dist/chunk-NZ32EONV.js.map +0 -1
  232. package/dist/chunk-O3NWNXDY 2.js +0 -76
  233. package/dist/chunk-QPI2CCBA.js.map +0 -1
  234. package/dist/chunk-SMJZMKYN.js.map +0 -1
  235. package/dist/chunk-TDNI6ZWL.js 2.map +0 -1
  236. package/dist/chunk-TDNI6ZWL.js.map +0 -1
  237. package/dist/chunk-VKOCWWVY.js.map +0 -1
  238. package/dist/chunk-WP5I5GLN 2.js +0 -1564
  239. package/dist/index 3.js +0 -856
  240. package/dist/providers 3.js +0 -38
  241. package/dist/providers.js 3.map +0 -1
  242. package/dist/types 3.js +0 -128
  243. package/dist/types.js 3.map +0 -1
  244. package/dist/useInactivityTracker-MRUU55XI.js 3.map +0 -1
  245. package/dist/utils.js 3.map +0 -1
  246. package/dist/validation 3.js +0 -479
  247. package/src/styles/semantic.css +0 -24
  248. /package/dist/{DataTable-DGZDJUYM.js.map → DataTable-WTS4IRF2.js.map} +0 -0
  249. /package/dist/{UnifiedAuthProvider-UACKFATV.js.map → UnifiedAuthProvider-6C47WIML.js.map} +0 -0
  250. /package/dist/{chunk-D6BOFXYR.js.map → chunk-35ZDPMBM.js.map} +0 -0
  251. /package/dist/{chunk-CGURJ27Z.js 2.map → chunk-4MXVZVNS.js.map} +0 -0
  252. /package/dist/{chunk-ZYJ6O5CA.js.map → chunk-C43QIDN3.js.map} +0 -0
  253. /package/dist/{chunk-B4GZ2BXO.js.map → chunk-NZGLXZGP.js.map} +0 -0
  254. /package/dist/{chunk-NZ32EONV.js 2.map → chunk-QWNJCQXZ.js.map} +0 -0
@@ -0,0 +1,850 @@
1
+ # Hierarchical DataTable Implementation Guide
2
+
3
+ This comprehensive guide covers implementing hierarchical parent/child rows in PACE Core's DataTable component.
4
+
5
+ ## Overview
6
+
7
+ The hierarchical DataTable feature allows you to display parent/child relationships in your data with:
8
+ - **Expand/Collapse All**: Single button to expand/collapse all parent rows
9
+ - **Individual Controls**: Click arrows next to each parent row
10
+ - **Different Action Icons**: Context-aware actions for parent vs child rows
11
+ - **Smart Sorting**: Sorting by child fields sorts children within their parents
12
+ - **Column Rendering**: Different content display for parent vs child rows
13
+ - **Visual Distinction**: Clear visual hierarchy with indentation and styling
14
+
15
+ ## Quick Start
16
+
17
+ ### 1. Enable Hierarchical Features
18
+
19
+ ```tsx
20
+ import { DataTable, type HierarchicalDataRow } from '@jmruthers/pace-core';
21
+
22
+ <DataTable
23
+ data={hierarchicalData}
24
+ columns={columns}
25
+ features={{
26
+ hierarchical: true, // Enable hierarchical functionality
27
+ }}
28
+ hierarchical={{
29
+ enabled: true,
30
+ defaultExpanded: false, // Start with all collapsed
31
+ }}
32
+ />
33
+ ```
34
+
35
+ ### 2. Structure Your Data
36
+
37
+ ```typescript
38
+ interface Dish extends HierarchicalDataRow {
39
+ id: string;
40
+ isParent: boolean;
41
+ parentId?: string;
42
+ name: string;
43
+ type: string;
44
+ cost: number;
45
+ // ... other properties
46
+ }
47
+
48
+ const hierarchicalData: Dish[] = [
49
+ // Parent rows (dishes)
50
+ {
51
+ id: 'dish-1',
52
+ isParent: true,
53
+ parentId: null,
54
+ name: 'Caesar Salad',
55
+ type: 'Salad',
56
+ cost: 12.99
57
+ },
58
+ {
59
+ id: 'dish-2',
60
+ isParent: true,
61
+ parentId: null,
62
+ name: 'Beef Stir Fry',
63
+ type: 'Main Course',
64
+ cost: 18.99
65
+ },
66
+
67
+ // Child rows (ingredients) for Caesar Salad
68
+ {
69
+ id: 'ingredient-1',
70
+ isParent: false,
71
+ parentId: 'dish-1',
72
+ name: 'Romaine Lettuce',
73
+ type: 'Vegetable',
74
+ cost: 2.50
75
+ },
76
+ {
77
+ id: 'ingredient-2',
78
+ isParent: false,
79
+ parentId: 'dish-1',
80
+ name: 'Parmesan Cheese',
81
+ type: 'Dairy',
82
+ cost: 4.20
83
+ },
84
+
85
+ // Child rows (ingredients) for Beef Stir Fry
86
+ {
87
+ id: 'ingredient-3',
88
+ isParent: false,
89
+ parentId: 'dish-2',
90
+ name: 'Beef Strips',
91
+ type: 'Protein',
92
+ cost: 8.50
93
+ },
94
+ {
95
+ id: 'ingredient-4',
96
+ isParent: false,
97
+ parentId: 'dish-2',
98
+ name: 'Mixed Vegetables',
99
+ type: 'Vegetable',
100
+ cost: 3.20
101
+ }
102
+ ];
103
+ ```
104
+
105
+ ### 3. Configure Columns for Different Row Types
106
+
107
+ ```tsx
108
+ const columns: DataTableColumn<Dish>[] = [
109
+ {
110
+ id: 'name',
111
+ accessorKey: 'name',
112
+ header: 'Name',
113
+ // Different rendering for parent vs child rows
114
+ renderForParent: (row) => (
115
+ <div className="flex items-center gap-2">
116
+ <span className="font-semibold text-main-800">{row.name}</span>
117
+ <span className="text-xs bg-main-100 text-main-700 px-2 py-1 rounded">
118
+ {row.type}
119
+ </span>
120
+ </div>
121
+ ),
122
+ renderForChild: (row) => (
123
+ <div className="flex items-center gap-2 ml-4">
124
+ <span className="text-sec-700">{row.name}</span>
125
+ <span className="text-xs bg-sec-100 text-sec-600 px-2 py-1 rounded">
126
+ {row.type}
127
+ </span>
128
+ </div>
129
+ ),
130
+ },
131
+ {
132
+ id: 'cost',
133
+ accessorKey: 'cost',
134
+ header: 'Cost',
135
+ renderForParent: (row) => (
136
+ <div className="text-right font-semibold text-main-800">
137
+ ${row.cost?.toFixed(2)}
138
+ </div>
139
+ ),
140
+ renderForChild: (row) => (
141
+ <div className="text-right text-sec-600 ml-4">
142
+ ${row.cost?.toFixed(2)}
143
+ </div>
144
+ ),
145
+ },
146
+ {
147
+ id: 'ingredient-specific',
148
+ accessorKey: 'quantity',
149
+ header: 'Quantity',
150
+ // Only show for child rows
151
+ renderForParent: () => null,
152
+ renderForChild: (row) => (
153
+ <div className="text-sm ml-4">
154
+ <span className="font-medium">{row.quantity}</span>
155
+ {row.unit && <span className="text-sec-500 ml-1">{row.unit}</span>}
156
+ </div>
157
+ ),
158
+ hideForParent: true, // Hide this column for parent rows
159
+ }
160
+ ];
161
+ ```
162
+
163
+ ## Detailed Configuration
164
+
165
+ ### Data Structure Requirements
166
+
167
+ Your hierarchical data must implement the `HierarchicalDataRow` interface:
168
+
169
+ ```typescript
170
+ interface HierarchicalDataRow extends DataRecord {
171
+ /** Whether this row is a parent row */
172
+ isParent: boolean;
173
+ /** For child rows, references the parent row ID */
174
+ parentId?: string;
175
+ /** Unique identifier for this row */
176
+ id: string;
177
+ // ... your actual data properties
178
+ }
179
+ ```
180
+
181
+ **Critical Requirements:**
182
+ - **Parent rows**: Must have `isParent: true` and `parentId: null` or `undefined`
183
+ - **Child rows**: Must have `isParent: false` and `parentId: string` (referencing a parent's `id`)
184
+ - **Unique IDs**: Every row must have a unique `id` value
185
+ - **Valid References**: All `parentId` values must reference an existing parent row's `id`
186
+
187
+ ### Hierarchical Configuration Options
188
+
189
+ ```tsx
190
+ hierarchical={{
191
+ enabled: true, // Enable/disable hierarchical functionality
192
+ defaultExpanded: false, // Initial expansion state
193
+ onExpandedChange: (expandedIds) => {
194
+ console.log('Expanded rows:', expandedIds);
195
+ },
196
+ expandButton: CustomExpandButton, // Optional custom expand button
197
+ indentSize: 24, // Indentation for child rows in pixels
198
+ parentRowClassName: 'bg-main-50 font-medium', // CSS classes for parent rows
199
+ childRowClassName: 'bg-sec-25', // CSS classes for child rows
200
+ }}
201
+ ```
202
+
203
+ **Configuration Details:**
204
+
205
+ | Option | Type | Default | Description |
206
+ |--------|------|---------|-------------|
207
+ | `enabled` | `boolean` | `false` | Enable/disable hierarchical functionality |
208
+ | `defaultExpanded` | `boolean \| string[]` | `false` | Initial expansion state |
209
+ | `onExpandedChange` | `(expandedIds: string[]) => void` | `undefined` | Callback when expansion changes |
210
+ | `expandButton` | `React.ComponentType` | `ExpandButton` | Custom expand/collapse button |
211
+ | `indentSize` | `number` | `24` | Indentation for child rows in pixels |
212
+ | `parentRowClassName` | `string` | `''` | CSS classes for parent rows |
213
+ | `childRowClassName` | `string` | `''` | CSS classes for child rows |
214
+
215
+ ### Column Configuration for Hierarchical Data
216
+
217
+ Configure how columns render for different row types using these properties:
218
+
219
+ ```tsx
220
+ const column: DataTableColumn<YourType> = {
221
+ // ... standard column properties
222
+
223
+ // Hierarchical-specific properties
224
+ renderForParent?: (row: YourType) => React.ReactNode; // Custom renderer for parent rows
225
+ renderForChild?: (row: YourType) => React.ReactNode; // Custom renderer for child rows
226
+ hideForParent?: boolean; // Hide this column for parent rows
227
+ hideForChild?: boolean; // Hide this column for child rows
228
+ };
229
+ ```
230
+
231
+ **Column Rendering Strategies:**
232
+
233
+ 1. **Different Content**: Use `renderForParent` and `renderForChild` for different content
234
+ 2. **Hide Columns**: Use `hideForParent` or `hideForChild` to hide columns for specific row types
235
+ 3. **Conditional Rendering**: Use both approaches together for complex layouts
236
+
237
+ **Example - Mixed Strategy:**
238
+ ```tsx
239
+ {
240
+ id: 'actions',
241
+ header: 'Actions',
242
+ renderForParent: (row) => (
243
+ <div className="flex gap-2">
244
+ <Button size="sm">Edit Recipe</Button>
245
+ <Button size="sm" variant="outline">Add Ingredient</Button>
246
+ </div>
247
+ ),
248
+ renderForChild: (row) => (
249
+ <div className="flex gap-2">
250
+ <Button size="sm" variant="ghost">Edit</Button>
251
+ <Button size="sm" variant="destructive">Remove</Button>
252
+ </div>
253
+ ),
254
+ }
255
+ ```
256
+
257
+ ## Expand/Collapse All Controls
258
+
259
+ The DataTable automatically adds an expand/collapse all button in the header when hierarchical mode is enabled.
260
+
261
+ ### Features
262
+
263
+ - **Expand All Button (▶️)**: Expands all parent rows to show their children
264
+ - **Collapse All Button (🔽)**: Collapses all parent rows to hide their children
265
+ - **Smart State Detection**: Button automatically updates based on current expansion state
266
+ - **Accessibility**: Proper ARIA labels and keyboard navigation support
267
+
268
+ ### Button Behavior
269
+
270
+ - Shows ▶️ when some or all parent rows are collapsed
271
+ - Shows 🔽 when all parent rows are expanded
272
+ - Only appears when there are parent rows with children
273
+ - Positioned in the first column of the table header
274
+
275
+ ### Custom Expand Button
276
+
277
+ You can provide a custom expand button component:
278
+
279
+ ```tsx
280
+ import { ChevronRight, ChevronDown } from 'lucide-react';
281
+
282
+ const CustomExpandButton = ({ isExpanded, onClick, ...props }) => (
283
+ <button
284
+ onClick={onClick}
285
+ className="p-1 hover:bg-sec-100 rounded"
286
+ {...props}
287
+ >
288
+ {isExpanded ? (
289
+ <ChevronDown className="h-4 w-4" />
290
+ ) : (
291
+ <ChevronRight className="h-4 w-4" />
292
+ )}
293
+ </button>
294
+ );
295
+
296
+ // Use in DataTable
297
+ <DataTable
298
+ hierarchical={{
299
+ enabled: true,
300
+ expandButton: CustomExpandButton,
301
+ }}
302
+ />
303
+ ```
304
+
305
+ ## Hierarchical Actions
306
+
307
+ Configure different actions for parent vs child rows using the `DataTableAction` interface:
308
+
309
+ ```tsx
310
+ const hierarchicalActions: DataTableAction<Dish>[] = [
311
+ {
312
+ label: 'View Details',
313
+ icon: Eye,
314
+ onClick: (row) => console.log('Viewing:', row.name),
315
+ variant: 'default',
316
+ },
317
+ {
318
+ label: 'Copy Recipe',
319
+ parentLabel: 'Copy Recipe',
320
+ childLabel: 'Copy Ingredient',
321
+ parentIcon: Copy,
322
+ childIcon: Copy,
323
+ onClick: (row) => console.log('Copying:', row.name),
324
+ variant: 'secondary',
325
+ showForParent: true,
326
+ showForChild: true,
327
+ },
328
+ {
329
+ label: 'Add Ingredient',
330
+ icon: Plus,
331
+ onClick: (row) => console.log('Adding ingredient to:', row.name),
332
+ variant: 'outline',
333
+ showForParent: true, // Only show for parent rows
334
+ },
335
+ {
336
+ label: 'Export',
337
+ icon: Download,
338
+ onClick: (row) => console.log('Exporting:', row.name),
339
+ variant: 'ghost',
340
+ showForChild: true, // Only show for child rows
341
+ }
342
+ ];
343
+ ```
344
+
345
+ **Action Configuration Properties:**
346
+
347
+ | Property | Type | Description |
348
+ |----------|------|-------------|
349
+ | `showForParent` | `boolean` | Show this action only for parent rows |
350
+ | `showForChild` | `boolean` | Show this action only for child rows |
351
+ | `parentIcon` | `React.ComponentType` | Icon for parent rows (overrides `icon`) |
352
+ | `childIcon` | `React.ComponentType` | Icon for child rows (overrides `icon`) |
353
+ | `parentLabel` | `string` | Label for parent rows (overrides `label`) |
354
+ | `childLabel` | `string` | Label for child rows (overrides `label`) |
355
+
356
+ ## Smart Sorting
357
+
358
+ When hierarchical mode is enabled, sorting behavior is optimized to preserve the parent-child relationship structure.
359
+
360
+ ### How It Works
361
+
362
+ **Parent Row Sorting:**
363
+ - Parent rows maintain their original order and are not affected by sorting
364
+ - Only child rows within each parent group are sorted
365
+ - This prevents parent rows from being moved to unexpected positions
366
+
367
+ **Child Row Sorting:**
368
+ - Child rows are sorted within their parent group only
369
+ - Sorting by a child-specific column will sort children within each parent
370
+ - The parent row remains at the top of its group
371
+
372
+ ### Example Sorting Behavior
373
+
374
+ ```tsx
375
+ // When sorting by "Diet" column:
376
+ // ✅ Correct behavior:
377
+ // Parent: Caesar Salad
378
+ // ├─ Child: Dairy Free (Sour cream)
379
+ // ├─ Child: Egg Free (Mayonnaise)
380
+ // └─ Child: GF & Vegan (Lettuce)
381
+ // Parent: Beef Stir Fry
382
+ // ├─ Child: GF & Vegan (Vegetables)
383
+ // └─ Child: Halal (Beef)
384
+
385
+ // ❌ Incorrect behavior (what we prevent):
386
+ // Child: Dairy Free (Sour cream)
387
+ // Child: Egg Free (Mayonnaise)
388
+ // Child: GF & Vegan (Lettuce)
389
+ // Child: GF & Vegan (Vegetables)
390
+ // Child: Halal (Beef)
391
+ // Parent: Caesar Salad (moved to end)
392
+ // Parent: Beef Stir Fry (moved to end)
393
+ ```
394
+
395
+ ### Sorting Configuration
396
+
397
+ - All existing sorting features work with hierarchical data
398
+ - Multi-column sorting is supported
399
+ - Server-side sorting is compatible
400
+ - Column-specific sorting behavior is preserved
401
+
402
+ ## Complete Example
403
+
404
+ Here's a complete example showing all hierarchical features:
405
+
406
+ ```tsx
407
+ import React, { useMemo } from 'react';
408
+ import { DataTable, type HierarchicalDataRow, type DataTableColumn } from '@jmruthers/pace-core';
409
+ import { Eye, Edit, Plus, Download, Copy, Star, Trash2 } from 'lucide-react';
410
+
411
+ interface Dish extends HierarchicalDataRow {
412
+ id: string;
413
+ isParent: boolean;
414
+ parentId?: string;
415
+ name: string;
416
+ type: string;
417
+ description: string;
418
+ cost: number;
419
+ prepTime?: number;
420
+ difficulty?: string;
421
+ quantity?: string;
422
+ unit?: string;
423
+ supplier?: string;
424
+ }
425
+
426
+ function HierarchicalDishTable() {
427
+ const hierarchicalData: Dish[] = useMemo(() => [
428
+ // Parent rows (dishes)
429
+ {
430
+ id: 'dish-1',
431
+ isParent: true,
432
+ parentId: null,
433
+ name: 'Caesar Salad',
434
+ type: 'Salad',
435
+ description: 'Classic Caesar salad with romaine lettuce',
436
+ cost: 12.99,
437
+ prepTime: 15,
438
+ difficulty: 'Easy'
439
+ },
440
+ {
441
+ id: 'dish-2',
442
+ isParent: true,
443
+ parentId: null,
444
+ name: 'Beef Stir Fry',
445
+ type: 'Main Course',
446
+ description: 'Tender beef with mixed vegetables',
447
+ cost: 18.99,
448
+ prepTime: 25,
449
+ difficulty: 'Medium'
450
+ },
451
+
452
+ // Child rows (ingredients) for Caesar Salad
453
+ {
454
+ id: 'ingredient-1',
455
+ isParent: false,
456
+ parentId: 'dish-1',
457
+ name: 'Romaine Lettuce',
458
+ type: 'Vegetable',
459
+ description: 'Fresh romaine lettuce leaves',
460
+ cost: 2.50,
461
+ quantity: '2 heads',
462
+ unit: 'pieces',
463
+ supplier: 'Fresh Farms'
464
+ },
465
+ {
466
+ id: 'ingredient-2',
467
+ isParent: false,
468
+ parentId: 'dish-1',
469
+ name: 'Parmesan Cheese',
470
+ type: 'Dairy',
471
+ description: 'Grated parmesan cheese',
472
+ cost: 4.20,
473
+ quantity: '100g',
474
+ unit: 'grams',
475
+ supplier: 'Italian Imports'
476
+ },
477
+
478
+ // Child rows (ingredients) for Beef Stir Fry
479
+ {
480
+ id: 'ingredient-3',
481
+ isParent: false,
482
+ parentId: 'dish-2',
483
+ name: 'Beef Strips',
484
+ type: 'Protein',
485
+ description: 'Thinly sliced beef strips',
486
+ cost: 8.50,
487
+ quantity: '300g',
488
+ unit: 'grams',
489
+ supplier: 'Premium Meats'
490
+ },
491
+ {
492
+ id: 'ingredient-4',
493
+ isParent: false,
494
+ parentId: 'dish-2',
495
+ name: 'Mixed Vegetables',
496
+ type: 'Vegetable',
497
+ description: 'Bell peppers, broccoli, carrots',
498
+ cost: 3.20,
499
+ quantity: '200g',
500
+ unit: 'grams',
501
+ supplier: 'Garden Fresh'
502
+ }
503
+ ], []);
504
+
505
+ const columns: DataTableColumn<Dish>[] = useMemo(() => [
506
+ {
507
+ id: 'name',
508
+ accessorKey: 'name',
509
+ header: 'Name',
510
+ enableSorting: true,
511
+ size: 200,
512
+ renderForParent: (row) => (
513
+ <div className="flex items-center gap-2">
514
+ <span className="font-semibold text-main-800">{row.name}</span>
515
+ <span className="text-xs bg-main-100 text-main-700 px-2 py-1 rounded">
516
+ {row.type}
517
+ </span>
518
+ </div>
519
+ ),
520
+ renderForChild: (row) => (
521
+ <div className="flex items-center gap-2 ml-4">
522
+ <span className="text-sec-700">{row.name}</span>
523
+ <span className="text-xs bg-sec-100 text-sec-600 px-2 py-1 rounded">
524
+ {row.type}
525
+ </span>
526
+ </div>
527
+ ),
528
+ },
529
+ {
530
+ id: 'description',
531
+ accessorKey: 'description',
532
+ header: 'Description',
533
+ enableSorting: true,
534
+ size: 250,
535
+ renderForParent: (row) => (
536
+ <div className="text-sm text-sec-600">
537
+ {row.description}
538
+ </div>
539
+ ),
540
+ renderForChild: (row) => (
541
+ <div className="text-sm text-sec-500 ml-4">
542
+ {row.description}
543
+ </div>
544
+ ),
545
+ },
546
+ {
547
+ id: 'quantity',
548
+ accessorKey: 'quantity',
549
+ header: 'Quantity',
550
+ enableSorting: true,
551
+ size: 120,
552
+ renderForParent: () => null,
553
+ renderForChild: (row) => (
554
+ <div className="text-sm ml-4">
555
+ <span className="font-medium">{row.quantity}</span>
556
+ {row.unit && <span className="text-sec-500 ml-1">{row.unit}</span>}
557
+ </div>
558
+ ),
559
+ hideForParent: true,
560
+ },
561
+ {
562
+ id: 'cost',
563
+ accessorKey: 'cost',
564
+ header: 'Cost',
565
+ enableSorting: true,
566
+ size: 100,
567
+ renderForParent: (row) => (
568
+ <div className="text-right font-semibold text-main-800">
569
+ ${row.cost?.toFixed(2)}
570
+ </div>
571
+ ),
572
+ renderForChild: (row) => (
573
+ <div className="text-right text-sec-600 ml-4">
574
+ ${row.cost?.toFixed(2)}
575
+ </div>
576
+ ),
577
+ },
578
+ {
579
+ id: 'supplier',
580
+ accessorKey: 'supplier',
581
+ header: 'Supplier',
582
+ enableSorting: true,
583
+ size: 150,
584
+ renderForParent: () => null,
585
+ renderForChild: (row) => (
586
+ <div className="text-sm text-sec-600 ml-4">
587
+ {row.supplier}
588
+ </div>
589
+ ),
590
+ hideForParent: true,
591
+ },
592
+ {
593
+ id: 'prepTime',
594
+ accessorKey: 'prepTime',
595
+ header: 'Prep Time',
596
+ enableSorting: true,
597
+ size: 100,
598
+ renderForParent: (row) => (
599
+ <div className="text-center">
600
+ <span className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-sec-100 text-sec-800">
601
+ {row.prepTime} min
602
+ </span>
603
+ </div>
604
+ ),
605
+ renderForChild: () => null,
606
+ hideForChild: true,
607
+ },
608
+ {
609
+ id: 'difficulty',
610
+ accessorKey: 'difficulty',
611
+ header: 'Difficulty',
612
+ enableSorting: true,
613
+ size: 100,
614
+ renderForParent: (row) => {
615
+ const colors = {
616
+ Easy: 'bg-main-100 text-main-800',
617
+ Medium: 'bg-acc-100 text-acc-800',
618
+ Hard: 'bg-acc-100 text-acc-800'
619
+ };
620
+ return (
621
+ <div className="text-center">
622
+ <span className={`inline-flex items-center px-2 py-1 rounded text-xs font-medium ${colors[row.difficulty as keyof typeof colors] || 'bg-sec-100 text-sec-800'}`}>
623
+ {row.difficulty}
624
+ </span>
625
+ </div>
626
+ );
627
+ },
628
+ renderForChild: () => null,
629
+ hideForChild: true,
630
+ }
631
+ ], []);
632
+
633
+ const hierarchicalActions = useMemo(() => [
634
+ {
635
+ label: 'View Details',
636
+ icon: Eye,
637
+ onClick: (row: Dish) => {
638
+ console.log('Viewing details for:', row.name);
639
+ alert(`Viewing details for: ${row.name}`);
640
+ },
641
+ variant: 'default' as const,
642
+ },
643
+ {
644
+ label: 'Edit',
645
+ icon: Edit,
646
+ onClick: (row: Dish) => {
647
+ console.log('Editing:', row.name);
648
+ alert(`Editing: ${row.name}`);
649
+ },
650
+ variant: 'outline' as const,
651
+ },
652
+ {
653
+ label: 'Copy Recipe',
654
+ parentLabel: 'Copy Recipe',
655
+ childLabel: 'Copy Ingredient',
656
+ parentIcon: Copy,
657
+ childIcon: Copy,
658
+ onClick: (row: Dish) => {
659
+ console.log('Copying:', row.name);
660
+ alert(`Copying: ${row.name}`);
661
+ },
662
+ variant: 'secondary' as const,
663
+ showForParent: true,
664
+ showForChild: true,
665
+ },
666
+ {
667
+ label: 'Add Ingredient',
668
+ icon: Plus,
669
+ onClick: (row: Dish) => {
670
+ console.log('Adding ingredient to:', row.name);
671
+ alert(`Adding ingredient to: ${row.name}`);
672
+ },
673
+ variant: 'outline' as const,
674
+ showForParent: true, // Only show for parent rows (dishes)
675
+ },
676
+ {
677
+ label: 'Export',
678
+ icon: Download,
679
+ onClick: (row: Dish) => {
680
+ console.log('Exporting:', row.name);
681
+ alert(`Exporting: ${row.name}`);
682
+ },
683
+ variant: 'ghost' as const,
684
+ showForChild: true, // Only show for child rows (ingredients)
685
+ },
686
+ {
687
+ label: 'Favorite Recipe',
688
+ parentLabel: 'Favorite Recipe',
689
+ childLabel: 'Favorite Ingredient',
690
+ icon: Star,
691
+ onClick: (row: Dish) => {
692
+ console.log('Favoriting:', row.name);
693
+ alert(`Favoriting: ${row.name}`);
694
+ },
695
+ variant: 'ghost' as const,
696
+ showForParent: true,
697
+ showForChild: true,
698
+ },
699
+ {
700
+ label: 'Delete Recipe',
701
+ parentLabel: 'Delete Recipe',
702
+ childLabel: 'Remove Ingredient',
703
+ icon: Trash2,
704
+ onClick: (row: Dish) => {
705
+ console.log('Deleting:', row.name);
706
+ alert(`Deleting: ${row.name}`);
707
+ },
708
+ variant: 'destructive' as const,
709
+ showForParent: true,
710
+ showForChild: true,
711
+ }
712
+ ], []);
713
+
714
+ return (
715
+ <DataTable
716
+ data={hierarchicalData}
717
+ columns={columns}
718
+ features={{
719
+ search: true,
720
+ pagination: true,
721
+ sorting: true,
722
+ filtering: true,
723
+ hierarchical: true,
724
+ editing: true,
725
+ deletion: true,
726
+ }}
727
+ actions={hierarchicalActions}
728
+ title="Dishes & Ingredients"
729
+ description="Hierarchical data showing dishes (parent rows) with their ingredients (child rows). Notice the expand/collapse all button in the header and different actions for parent vs child rows."
730
+ hierarchical={{
731
+ enabled: true,
732
+ defaultExpanded: false, // Start with all collapsed
733
+ onExpandedChange: (expandedIds) => {
734
+ console.log('Expanded rows:', expandedIds);
735
+ },
736
+ }}
737
+ getRowId={(row: Dish) => row.id}
738
+ />
739
+ );
740
+ }
741
+
742
+ export default HierarchicalDishTable;
743
+ ```
744
+
745
+ ## Best Practices
746
+
747
+ ### 1. Data Structure
748
+ - Always use unique IDs for each row
749
+ - Ensure parent-child relationships are valid
750
+ - Keep data structure consistent across all rows
751
+
752
+ ### 2. User Experience
753
+ - Start with collapsed state (`defaultExpanded: false`)
754
+ - Use clear visual distinction between parent and child rows
755
+ - Provide meaningful action labels for different row types
756
+
757
+ ### 3. Performance
758
+ - Use `useMemo` for column and action definitions
759
+ - Consider pagination for large hierarchical datasets
760
+ - Implement proper `getRowId` function
761
+
762
+ ### 4. Accessibility
763
+ - Ensure proper ARIA labels for expand/collapse buttons
764
+ - Use semantic HTML structure
765
+ - Provide keyboard navigation support
766
+
767
+ ## Troubleshooting
768
+
769
+ ### Common Issues
770
+
771
+ **1. Rows not expanding/collapsing:**
772
+ - Check that `isParent` and `parentId` are set correctly
773
+ - Ensure all `parentId` values reference existing parent row IDs
774
+ - Verify `hierarchical.enabled` is set to `true`
775
+
776
+ **2. Actions not showing for correct row types:**
777
+ - Check `showForParent` and `showForChild` properties
778
+ - Ensure action configuration is correct
779
+
780
+ **3. Sorting not working as expected:**
781
+ - This is expected behavior - child rows sort within their parent groups
782
+ - Parent rows maintain their original order
783
+
784
+ **4. Visual styling issues:**
785
+ - Check `parentRowClassName` and `childRowClassName` configuration
786
+ - Ensure proper CSS classes are applied
787
+ - Verify `indentSize` is appropriate for your design
788
+
789
+ ### Debug Tips
790
+
791
+ 1. **Check console logs** for expansion state changes
792
+ 2. **Verify data structure** matches `HierarchicalDataRow` interface
793
+ 3. **Test with simple data** first before complex hierarchies
794
+ 4. **Use browser dev tools** to inspect rendered HTML structure
795
+
796
+ ## API Reference
797
+
798
+ ### HierarchicalDataRow Interface
799
+
800
+ ```typescript
801
+ interface HierarchicalDataRow extends DataRecord {
802
+ isParent: boolean;
803
+ parentId?: string;
804
+ id: string;
805
+ }
806
+ ```
807
+
808
+ ### HierarchicalConfig Interface
809
+
810
+ ```typescript
811
+ interface HierarchicalConfig {
812
+ enabled: boolean;
813
+ defaultExpanded?: boolean | string[];
814
+ onExpandedChange?: (expandedIds: string[]) => void;
815
+ expandButton?: React.ComponentType<ExpandButtonProps>;
816
+ indentSize?: number;
817
+ parentRowClassName?: string;
818
+ childRowClassName?: string;
819
+ }
820
+ ```
821
+
822
+ ### DataTableColumn Hierarchical Properties
823
+
824
+ ```typescript
825
+ interface DataTableColumn<TData> {
826
+ // ... standard properties
827
+
828
+ // Hierarchical-specific properties
829
+ renderForParent?: (row: TData) => React.ReactNode;
830
+ renderForChild?: (row: TData) => React.ReactNode;
831
+ hideForParent?: boolean;
832
+ hideForChild?: boolean;
833
+ }
834
+ ```
835
+
836
+ ### DataTableAction Hierarchical Properties
837
+
838
+ ```typescript
839
+ interface DataTableAction<TData> {
840
+ // ... standard properties
841
+
842
+ // Hierarchical-specific properties
843
+ showForParent?: boolean;
844
+ showForChild?: boolean;
845
+ parentIcon?: React.ComponentType<{ className?: string }>;
846
+ childIcon?: React.ComponentType<{ className?: string }>;
847
+ parentLabel?: string;
848
+ childLabel?: string;
849
+ }
850
+ ```