@jmruthers/pace-core 0.5.136 → 0.5.139

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 (292) hide show
  1. package/dist/{DataTable-CYOHOX3O.js → DataTable-JXFCA2BJ.js} +10 -9
  2. package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
  3. package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
  4. package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
  5. package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
  6. package/dist/chunk-4C7EXCAR.js.map +1 -0
  7. package/dist/{chunk-NOHEVYVX.js → chunk-5JMOHWDI.js} +417 -319
  8. package/dist/chunk-5JMOHWDI.js.map +1 -0
  9. package/dist/{chunk-FHWWBIHA.js → chunk-6DXZ6V5Q.js} +5 -5
  10. package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
  11. package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
  12. package/dist/chunk-7QCC6MCP.js.map +1 -0
  13. package/dist/chunk-BJPBT3CU.js +21 -0
  14. package/dist/chunk-BJPBT3CU.js.map +1 -0
  15. package/dist/{chunk-L6PGMCMD.js → chunk-BOOI7GK2.js} +38 -12
  16. package/dist/chunk-BOOI7GK2.js.map +1 -0
  17. package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
  18. package/dist/chunk-JISYG63F.js +70 -0
  19. package/dist/chunk-JISYG63F.js.map +1 -0
  20. package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
  21. package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
  22. package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
  23. package/dist/{chunk-HJGGOMQ6.js → chunk-TLT2ZR3L.js} +147 -103
  24. package/dist/chunk-TLT2ZR3L.js.map +1 -0
  25. package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
  26. package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
  27. package/dist/components.d.ts +1 -1
  28. package/dist/components.js +12 -11
  29. package/dist/components.js.map +1 -1
  30. package/dist/hooks.js +8 -9
  31. package/dist/hooks.js.map +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +15 -14
  34. package/dist/index.js.map +1 -1
  35. package/dist/providers.js +3 -4
  36. package/dist/rbac/index.js +8 -9
  37. package/dist/schema-DTDZQe2u.d.ts +28 -0
  38. package/dist/types.d.ts +152 -3
  39. package/dist/types.js +51 -16
  40. package/dist/types.js.map +1 -1
  41. package/dist/utils.d.ts +89 -4
  42. package/dist/utils.js +214 -96
  43. package/dist/utils.js.map +1 -1
  44. package/dist/validation.d.ts +1 -343
  45. package/dist/validation.js +3 -100
  46. package/docs/api/classes/ColumnFactory.md +1 -1
  47. package/docs/api/classes/ErrorBoundary.md +1 -1
  48. package/docs/api/classes/InvalidScopeError.md +1 -1
  49. package/docs/api/classes/MissingUserContextError.md +1 -1
  50. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  51. package/docs/api/classes/PermissionDeniedError.md +1 -1
  52. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  53. package/docs/api/classes/RBACAuditManager.md +1 -1
  54. package/docs/api/classes/RBACCache.md +1 -1
  55. package/docs/api/classes/RBACEngine.md +1 -1
  56. package/docs/api/classes/RBACError.md +1 -1
  57. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  58. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  59. package/docs/api/classes/StorageUtils.md +1 -1
  60. package/docs/api/enums/FileCategory.md +1 -1
  61. package/docs/api/interfaces/AggregateConfig.md +1 -1
  62. package/docs/api/interfaces/BadgeProps.md +27 -0
  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/EventLogoProps.md +1 -1
  77. package/docs/api/interfaces/ExportColumn.md +1 -1
  78. package/docs/api/interfaces/ExportOptions.md +1 -1
  79. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  80. package/docs/api/interfaces/FileMetadata.md +1 -1
  81. package/docs/api/interfaces/FileReference.md +1 -1
  82. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  83. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  84. package/docs/api/interfaces/FileUploadProps.md +1 -1
  85. package/docs/api/interfaces/FooterProps.md +1 -1
  86. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  87. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  88. package/docs/api/interfaces/InputProps.md +1 -1
  89. package/docs/api/interfaces/LabelProps.md +1 -1
  90. package/docs/api/interfaces/LoginFormProps.md +1 -1
  91. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  92. package/docs/api/interfaces/NavigationContextType.md +1 -1
  93. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  94. package/docs/api/interfaces/NavigationItem.md +1 -1
  95. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  97. package/docs/api/interfaces/Organisation.md +1 -1
  98. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  99. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  100. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  101. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  102. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  103. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  104. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  105. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  106. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  107. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  108. package/docs/api/interfaces/PaletteData.md +1 -1
  109. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  110. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  111. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  112. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  113. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  114. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  115. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  116. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  117. package/docs/api/interfaces/RBACConfig.md +1 -1
  118. package/docs/api/interfaces/RBACLogger.md +1 -1
  119. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  120. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  121. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  122. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  123. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  124. package/docs/api/interfaces/RouteConfig.md +1 -1
  125. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  126. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  127. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  128. package/docs/api/interfaces/StorageConfig.md +1 -1
  129. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  130. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  131. package/docs/api/interfaces/StorageListOptions.md +1 -1
  132. package/docs/api/interfaces/StorageListResult.md +1 -1
  133. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  134. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  135. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  136. package/docs/api/interfaces/StyleImport.md +1 -1
  137. package/docs/api/interfaces/SwitchProps.md +1 -1
  138. package/docs/api/interfaces/ToastActionElement.md +1 -1
  139. package/docs/api/interfaces/ToastProps.md +1 -1
  140. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  141. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  142. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  143. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  144. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  145. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  146. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  147. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  148. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  149. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  150. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  151. package/docs/api/interfaces/UserEventAccess.md +1 -1
  152. package/docs/api/interfaces/UserMenuProps.md +1 -1
  153. package/docs/api/interfaces/UserProfile.md +1 -1
  154. package/docs/api/modules.md +84 -15
  155. package/docs/architecture/README.md +0 -1
  156. package/docs/styles/README.md +0 -2
  157. package/examples/RBAC/CompleteRBACExample.tsx +324 -0
  158. package/examples/RBAC/EventBasedApp.tsx +239 -0
  159. package/examples/RBAC/PermissionExample.tsx +151 -0
  160. package/examples/RBAC/index.ts +13 -0
  161. package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
  162. package/examples/public-pages/PublicEventPage.tsx +274 -0
  163. package/examples/public-pages/PublicPageApp.tsx +308 -0
  164. package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
  165. package/examples/public-pages/index.ts +14 -0
  166. package/package.json +1 -10
  167. package/src/__tests__/TEST_STANDARD.md +92 -0
  168. package/src/components/Badge/Badge.test.tsx +314 -0
  169. package/src/components/Badge/Badge.tsx +304 -0
  170. package/src/components/Badge/index.ts +3 -0
  171. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  172. package/src/components/DataTable/__tests__/styles.test.ts +1 -1
  173. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  174. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  175. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  176. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  177. package/src/components/DataTable/components/PaginationControls.tsx +1 -0
  178. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  179. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  180. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  181. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  182. package/src/components/DataTable/core/ActionManager.ts +235 -0
  183. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  184. package/src/components/DataTable/core/DataManager.ts +188 -0
  185. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  186. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  187. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  188. package/src/components/DataTable/core/StateManager.ts +311 -0
  189. package/src/components/DataTable/core/interfaces.ts +338 -0
  190. package/src/components/DataTable/styles.ts +27 -6
  191. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  192. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  193. package/src/components/DataTable/utils/debugTools.ts +609 -0
  194. package/src/components/DataTable/utils/index.ts +1 -0
  195. package/src/components/Dialog/README.md +804 -0
  196. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
  197. package/src/components/Dialog/utils/safeHtml.ts +185 -0
  198. package/src/components/Footer/Footer.test.tsx +1 -1
  199. package/src/components/Form/Form.test.tsx +1 -1
  200. package/src/components/Form/FormErrorSummary.tsx +113 -0
  201. package/src/components/Form/FormFieldset.tsx +127 -0
  202. package/src/components/Form/FormLiveRegion.tsx +198 -0
  203. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  204. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  205. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  206. package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
  207. package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
  208. package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
  209. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
  210. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  211. package/src/components/Select/Select.test.tsx +1 -1
  212. package/src/components/Select/Select.tsx +20 -8
  213. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  214. package/src/components/index.ts +3 -0
  215. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  216. package/src/index.ts +4 -0
  217. package/src/rbac/hooks/useCan.test.ts +24 -0
  218. package/src/rbac/hooks/usePermissions.ts +49 -12
  219. package/src/styles/core.css +3 -0
  220. package/src/utils/appConfig.ts +47 -0
  221. package/src/utils/appIdResolver.test.ts +499 -0
  222. package/src/utils/appIdResolver.ts +130 -0
  223. package/src/utils/appNameResolver.simple.test.ts +212 -0
  224. package/src/utils/appNameResolver.test.ts +121 -0
  225. package/src/utils/appNameResolver.ts +191 -0
  226. package/src/utils/audit.ts +127 -0
  227. package/src/utils/auth-utils.ts +96 -0
  228. package/src/utils/bundleAnalysis.ts +129 -0
  229. package/src/utils/cn.ts +7 -0
  230. package/src/utils/debugLogger.ts +67 -0
  231. package/src/utils/deviceFingerprint.ts +215 -0
  232. package/src/utils/dynamicUtils.ts +105 -0
  233. package/src/utils/file-reference.test.ts +788 -0
  234. package/src/utils/file-reference.ts +519 -0
  235. package/src/utils/formatDate.test.ts +237 -0
  236. package/src/utils/formatting.ts +133 -0
  237. package/src/utils/index.ts +7 -0
  238. package/src/utils/lazyLoad.tsx +44 -0
  239. package/src/utils/logger.ts +179 -0
  240. package/src/utils/organisationContext.test.ts +322 -0
  241. package/src/utils/organisationContext.ts +153 -0
  242. package/src/utils/performanceBenchmark.ts +64 -0
  243. package/src/utils/performanceBudgets.ts +110 -0
  244. package/src/utils/permissionTypes.ts +37 -0
  245. package/src/utils/permissionUtils.test.ts +393 -0
  246. package/src/utils/permissionUtils.ts +34 -0
  247. package/src/utils/sanitization.ts +264 -0
  248. package/src/utils/schemaUtils.ts +37 -0
  249. package/src/utils/secureDataAccess.test.ts +711 -0
  250. package/src/utils/secureDataAccess.ts +377 -0
  251. package/src/utils/secureErrors.ts +79 -0
  252. package/src/utils/secureStorage.ts +244 -0
  253. package/src/utils/security.ts +156 -0
  254. package/src/utils/securityMonitor.ts +45 -0
  255. package/src/utils/sessionTracking.ts +126 -0
  256. package/src/utils/validation.ts +111 -0
  257. package/src/utils/validationUtils.ts +120 -0
  258. package/src/validation/index.ts +2 -2
  259. package/dist/chunk-444EZN6N.js.map +0 -1
  260. package/dist/chunk-APIBCTL2.js +0 -670
  261. package/dist/chunk-APIBCTL2.js.map +0 -1
  262. package/dist/chunk-HJGGOMQ6.js.map +0 -1
  263. package/dist/chunk-K2WWTH7O.js +0 -94
  264. package/dist/chunk-K2WWTH7O.js.map +0 -1
  265. package/dist/chunk-L6PGMCMD.js.map +0 -1
  266. package/dist/chunk-LMC26NLJ.js +0 -84
  267. package/dist/chunk-LMC26NLJ.js.map +0 -1
  268. package/dist/chunk-NOHEVYVX.js.map +0 -1
  269. package/dist/chunk-TVYPTYOY.js.map +0 -1
  270. package/dist/validation-8npbysjg.d.ts +0 -177
  271. /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-JXFCA2BJ.js.map} +0 -0
  272. /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  273. /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
  274. /package/dist/{chunk-FHWWBIHA.js.map → chunk-6DXZ6V5Q.js.map} +0 -0
  275. /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
  276. /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
  277. /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
  278. /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
  279. /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
  280. /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
  281. /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
  282. /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
  283. /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
  284. /package/examples/{components → components 2}/DataTable/index.ts +0 -0
  285. /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
  286. /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
  287. /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
  288. /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
  289. /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
  290. /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
  291. /package/examples/{components → components 2}/Dialog/index.ts +0 -0
  292. /package/examples/{components → components 2}/index.ts +0 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * @file DataTable Context
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/DataTable/Architecture
5
+ * @since 0.3.0
6
+ */
7
+
8
+ import React, { createContext, useContext, useMemo, useCallback, useRef, useEffect } from 'react';
9
+ import { useReactTable, getCoreRowModel, getFilteredRowModel, getSortedRowModel, getPaginationRowModel } from '@tanstack/react-table';
10
+ import { ChevronDown, ChevronUp, ChevronsUpDown, Filter, MoreHorizontal, Plus, Search, Settings, Trash2, Upload, Download, Eye, Edit, Copy } from 'lucide-react';
11
+ import { Button } from '../../Button/Button';
12
+ import { Input } from '../../Input/Input';
13
+ import { Checkbox } from '../../Checkbox/Checkbox';
14
+ // DropdownMenu components have been merged into Select components
15
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '../../Dialog/Dialog';
16
+ import type { DataTableContext as IDataTableContext, DataTableConfig, DataTableUtils } from './interfaces';
17
+ import type { DataRecord } from '../types';
18
+ import { DataManager } from './DataManager';
19
+ import { ColumnManagerImpl } from './ColumnManager';
20
+ import { ActionManagerImpl } from './ActionManager';
21
+ import { StateManagerImpl } from './StateManager';
22
+ import { PluginRegistryImpl } from './PluginRegistry';
23
+ import { LocalDataAdapter } from './LocalDataAdapter';
24
+
25
+ // Create the context
26
+ const DataTableContext = createContext<IDataTableContext<any> | null>(null);
27
+
28
+ /**
29
+ * DataTable provider component
30
+ */
31
+ export interface DataTableProviderProps<TData extends DataRecord> {
32
+ children: React.ReactNode;
33
+ config: DataTableConfig<TData>;
34
+ data?: TData[];
35
+ columns?: any[];
36
+ actions?: any[];
37
+ pageSize?: number;
38
+ }
39
+
40
+ export function DataTableProvider<TData extends DataRecord>({
41
+ children,
42
+ config,
43
+ data = [],
44
+ columns = [],
45
+ actions = [],
46
+ pageSize = 10,
47
+ }: DataTableProviderProps<TData>) {
48
+ // Create managers
49
+ const adapter = useMemo(() => new LocalDataAdapter<TData>(data), [data]);
50
+ const dataManager = useMemo(() => new DataManager<TData>(adapter), [adapter]);
51
+ const columnManager = useMemo(() => new ColumnManagerImpl<TData>(columns), [columns]);
52
+ const actionManager = useMemo(() => new ActionManagerImpl<TData>(actions), [actions]);
53
+ const stateManager = useMemo(() => new StateManagerImpl<TData>({
54
+ ui: {
55
+ globalFilter: '',
56
+ columnFilters: [],
57
+ sorting: [],
58
+ grouping: [],
59
+ expanded: {},
60
+ pagination: {
61
+ pageIndex: 0,
62
+ pageSize: pageSize,
63
+ },
64
+ rowSelection: {},
65
+ editing: {
66
+ rowId: null,
67
+ data: {},
68
+ isCreating: false,
69
+ creationData: {},
70
+ },
71
+ modals: {
72
+ import: false,
73
+ export: false,
74
+ view: false,
75
+ viewData: null,
76
+ },
77
+ }
78
+ }), [pageSize]);
79
+ const pluginRegistry = useMemo(() => new PluginRegistryImpl<TData>(), []);
80
+
81
+ // Create utilities
82
+ const utils: DataTableUtils<TData> = useMemo(() => ({
83
+ getRowId: (row: TData, index: number) => (row as any).id || String(index),
84
+ formatValue: (value: any, column: any) => String(value),
85
+ validateData: (data: Partial<TData>) => null,
86
+ debounce: <T extends (...args: any[]) => any>(func: T, delay: number) => {
87
+ let timeoutId: NodeJS.Timeout;
88
+ return ((...args: any[]) => {
89
+ clearTimeout(timeoutId);
90
+ timeoutId = setTimeout(() => func(...args), delay);
91
+ }) as T;
92
+ },
93
+ }), []);
94
+
95
+ // Create context value
96
+ const contextValue: IDataTableContext<TData> = useMemo(() => ({
97
+ dataManager,
98
+ columnManager,
99
+ actionManager,
100
+ stateManager,
101
+ pluginRegistry,
102
+ adapter,
103
+ observable: stateManager,
104
+ config,
105
+ utils,
106
+ }), [dataManager, columnManager, actionManager, stateManager, pluginRegistry, adapter, config, utils]);
107
+
108
+ // Initialize data
109
+ useEffect(() => {
110
+ // Update the adapter with new data
111
+ adapter.setData(data);
112
+
113
+ if (data.length > 0) {
114
+ stateManager.updateData(data);
115
+ } else {
116
+ // Handle empty data case
117
+ stateManager.updateData([]);
118
+ }
119
+ }, [data, dataManager, stateManager, adapter]);
120
+
121
+ // Initialize columns
122
+ useEffect(() => {
123
+ if (columns.length > 0) {
124
+ columnManager.setColumns(columns);
125
+ stateManager.updateColumns(columns);
126
+ }
127
+ }, [columns, columnManager, stateManager]);
128
+
129
+ // Initialize actions
130
+ useEffect(() => {
131
+ if (actions.length > 0) {
132
+ actionManager.setActions(actions);
133
+ stateManager.updateActions(actions);
134
+ }
135
+ }, [actions, actionManager, stateManager]);
136
+
137
+ return (
138
+ <DataTableContext.Provider value={contextValue}>
139
+ {children}
140
+ </DataTableContext.Provider>
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Hook to use DataTable context
146
+ */
147
+ export function useDataTableContext<TData extends DataRecord = DataRecord>(): IDataTableContext<TData> {
148
+ const context = useContext(DataTableContext);
149
+ if (!context) {
150
+ throw new Error('useDataTableContext must be used within a DataTableProvider');
151
+ }
152
+ return context as IDataTableContext<TData>;
153
+ }
154
+
155
+ /**
156
+ * Hook to use specific managers
157
+ */
158
+ export function useDataManager<TData extends DataRecord = DataRecord>() {
159
+ const context = useDataTableContext<TData>();
160
+ return context.dataManager;
161
+ }
162
+
163
+ export function useColumnManager<TData extends DataRecord = DataRecord>() {
164
+ const context = useDataTableContext<TData>();
165
+ return context.columnManager;
166
+ }
167
+
168
+ export function useActionManager<TData extends DataRecord = DataRecord>() {
169
+ const context = useDataTableContext<TData>();
170
+ return context.actionManager;
171
+ }
172
+
173
+ export function useStateManager<TData extends DataRecord = DataRecord>() {
174
+ const context = useDataTableContext<TData>();
175
+ return context.stateManager;
176
+ }
177
+
178
+ export function usePluginRegistry<TData extends DataRecord = DataRecord>() {
179
+ const context = useDataTableContext<TData>();
180
+ return context.pluginRegistry;
181
+ }
@@ -0,0 +1,273 @@
1
+ /**
2
+ * @file Local Data Adapter
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/DataTable/Architecture/Adapters
5
+ * @since 0.3.0
6
+ */
7
+
8
+ import type { DataAdapter, FetchOptions, ExportFormat, ExportOptions } from './interfaces';
9
+ import type { DataRecord } from '../types';
10
+ import { generateCSVContent } from '../utils/exportUtils';
11
+
12
+ /**
13
+ * Local data adapter for in-memory data operations
14
+ * Implements DataAdapter interface for local data management
15
+ */
16
+ export class LocalDataAdapter<TData extends DataRecord = DataRecord> implements DataAdapter<TData> {
17
+ public name = 'local';
18
+ private data: TData[] = [];
19
+ private error: Error | null = null;
20
+
21
+ constructor(initialData: TData[] = []) {
22
+ this.data = [...initialData];
23
+ }
24
+
25
+ /**
26
+ * Fetch data with filtering, sorting, and pagination
27
+ */
28
+ async fetchData(options: FetchOptions = {}): Promise<TData[]> {
29
+ try {
30
+ this.error = null;
31
+ let result = [...this.data];
32
+
33
+ // Apply search filter
34
+ if (options.search) {
35
+ const searchTerm = options.search.toLowerCase();
36
+ result = result.filter(item =>
37
+ Object.values(item).some(value =>
38
+ String(value).toLowerCase().includes(searchTerm)
39
+ )
40
+ );
41
+ }
42
+
43
+ // Apply column filters
44
+ if (options.filters) {
45
+ result = result.filter(item => {
46
+ return Object.entries(options.filters!).every(([key, value]) => {
47
+ const itemValue = item[key];
48
+ if (value === null || value === undefined) return true;
49
+ if (typeof value === 'string') {
50
+ return String(itemValue).toLowerCase().includes(value.toLowerCase());
51
+ }
52
+ return itemValue === value;
53
+ });
54
+ });
55
+ }
56
+
57
+ // Apply sorting
58
+ if (options.sortBy) {
59
+ result.sort((a, b) => {
60
+ const aValue = a[options.sortBy!];
61
+ const bValue = b[options.sortBy!];
62
+
63
+ // Handle null/undefined values
64
+ if (aValue == null && bValue == null) return 0;
65
+ if (aValue == null) return 1;
66
+ if (bValue == null) return -1;
67
+
68
+ // Compare values - convert to comparable types
69
+ const aComparable = typeof aValue === 'string' ? aValue.toLowerCase() : aValue;
70
+ const bComparable = typeof bValue === 'string' ? bValue.toLowerCase() : bValue;
71
+
72
+ if (aComparable < bComparable) return options.sortDirection === 'desc' ? 1 : -1;
73
+ if (aComparable > bComparable) return options.sortDirection === 'desc' ? -1 : 1;
74
+ return 0;
75
+ });
76
+ }
77
+
78
+ // Apply pagination
79
+ if (options.page !== undefined && options.pageSize) {
80
+ const start = options.page * options.pageSize;
81
+ const end = start + options.pageSize;
82
+ result = result.slice(start, end);
83
+ }
84
+
85
+ return result;
86
+ } catch (error) {
87
+ this.error = error as Error;
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Update a data item
94
+ */
95
+ async updateData(id: string, data: Partial<TData>): Promise<void> {
96
+ try {
97
+ this.error = null;
98
+ const index = this.data.findIndex(item => this.getRowId(item) === id);
99
+
100
+ if (index === -1) {
101
+ throw new Error(`Item with ID "${id}" not found`);
102
+ }
103
+
104
+ this.data[index] = { ...this.data[index], ...data };
105
+ } catch (error) {
106
+ this.error = error as Error;
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Delete a data item
113
+ */
114
+ async deleteData(id: string): Promise<void> {
115
+ try {
116
+ this.error = null;
117
+ const index = this.data.findIndex(item => this.getRowId(item) === id);
118
+
119
+ if (index === -1) {
120
+ throw new Error(`Item with ID "${id}" not found`);
121
+ }
122
+
123
+ this.data.splice(index, 1);
124
+ } catch (error) {
125
+ this.error = error as Error;
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Create a new data item
132
+ */
133
+ async createData(data: Partial<TData>): Promise<TData> {
134
+ try {
135
+ this.error = null;
136
+ const newItem = {
137
+ id: this.generateId(),
138
+ ...data,
139
+ } as unknown as TData;
140
+
141
+ this.data.push(newItem);
142
+ return newItem;
143
+ } catch (error) {
144
+ this.error = error as Error;
145
+ throw error;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Export data in specified format
151
+ */
152
+ async exportData(format: ExportFormat, options: ExportOptions = {}): Promise<string> {
153
+ try {
154
+ this.error = null;
155
+
156
+ switch (format) {
157
+ case 'csv':
158
+ return this.exportToCSV(options);
159
+ case 'json':
160
+ return this.exportToJSON(options);
161
+ case 'xlsx':
162
+ return this.exportToXLSX(options);
163
+ default:
164
+ throw new Error(`Unsupported export format: ${format}`);
165
+ }
166
+ } catch (error) {
167
+ this.error = error as Error;
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Import data
174
+ */
175
+ async importData(data: TData[]): Promise<void> {
176
+ try {
177
+ this.error = null;
178
+ this.data = [...data];
179
+ } catch (error) {
180
+ this.error = error as Error;
181
+ throw error;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Check if adapter is connected
187
+ */
188
+ isConnected(): boolean {
189
+ return true; // Local adapter is always connected
190
+ }
191
+
192
+ /**
193
+ * Get current error
194
+ */
195
+ getError(): Error | null {
196
+ return this.error;
197
+ }
198
+
199
+ /**
200
+ * Set data directly
201
+ */
202
+ setData(data: TData[]): void {
203
+ this.data = [...data];
204
+ }
205
+
206
+ /**
207
+ * Get all data without filtering
208
+ */
209
+ getAllData(): TData[] {
210
+ return [...this.data];
211
+ }
212
+
213
+ /**
214
+ * Get data count
215
+ */
216
+ getDataCount(): number {
217
+ return this.data.length;
218
+ }
219
+
220
+ /**
221
+ * Clear all data
222
+ */
223
+ clearData(): void {
224
+ this.data = [];
225
+ }
226
+
227
+ /**
228
+ * Get row ID from data item
229
+ */
230
+ private getRowId(item: TData): string {
231
+ return (item as any).id || (item as any).key || JSON.stringify(item);
232
+ }
233
+
234
+ /**
235
+ * Generate unique ID
236
+ */
237
+ private generateId(): string {
238
+ return `id-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
239
+ }
240
+
241
+ /**
242
+ * Export to CSV format
243
+ */
244
+ private exportToCSV(options: ExportOptions): string {
245
+ if (this.data.length === 0) return '';
246
+
247
+ // Create column definitions from data keys
248
+ const columns = Object.keys(this.data[0]).map(key => ({
249
+ accessorKey: key,
250
+ header: key
251
+ }));
252
+
253
+ return generateCSVContent(this.data, columns, {
254
+ includeHeaders: options.includeHeaders
255
+ });
256
+ }
257
+
258
+ /**
259
+ * Export to JSON format
260
+ */
261
+ private exportToJSON(options: ExportOptions): string {
262
+ return JSON.stringify(this.data, null, 2);
263
+ }
264
+
265
+ /**
266
+ * Export to XLSX format (simplified - returns JSON for now)
267
+ */
268
+ private exportToXLSX(options: ExportOptions): string {
269
+ // In a real implementation, you would use a library like xlsx
270
+ // For now, return JSON format
271
+ return this.exportToJSON(options);
272
+ }
273
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * @file DataTable Plugin Registry
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/DataTable/Architecture
5
+ * @since 0.3.0
6
+ */
7
+
8
+ import type { PluginRegistry, DataTablePlugin, DataTableContext } from './interfaces';
9
+ import type { DataRecord } from '../types';
10
+
11
+ /**
12
+ * Plugin registry implementation
13
+ * Manages plugin registration, dependency resolution, and lifecycle
14
+ */
15
+ export class PluginRegistryImpl<TData extends DataRecord = DataRecord> implements PluginRegistry<TData> {
16
+ private plugins = new Map<string, DataTablePlugin<TData>>();
17
+ private initializedPlugins = new Set<string>();
18
+
19
+ /**
20
+ * Register a plugin
21
+ */
22
+ register(plugin: DataTablePlugin<TData>): void {
23
+ if (this.plugins.has(plugin.name)) {
24
+ throw new Error(`Plugin "${plugin.name}" is already registered`);
25
+ }
26
+
27
+ this.plugins.set(plugin.name, plugin);
28
+ }
29
+
30
+ /**
31
+ * Unregister a plugin
32
+ */
33
+ unregister(name: string): void {
34
+ const plugin = this.plugins.get(name);
35
+ if (!plugin) {
36
+ throw new Error(`Plugin "${name}" is not registered`);
37
+ }
38
+
39
+ // Cleanup if initialized
40
+ if (this.initializedPlugins.has(name)) {
41
+ plugin.cleanup().catch(error => {
42
+ console.error(`Error cleaning up plugin "${name}":`, error);
43
+ });
44
+ this.initializedPlugins.delete(name);
45
+ }
46
+
47
+ this.plugins.delete(name);
48
+ }
49
+
50
+ /**
51
+ * Get a plugin by name
52
+ */
53
+ getPlugin(name: string): DataTablePlugin<TData> | undefined {
54
+ return this.plugins.get(name);
55
+ }
56
+
57
+ /**
58
+ * Get all enabled plugins
59
+ */
60
+ getEnabledPlugins(): DataTablePlugin<TData>[] {
61
+ return Array.from(this.plugins.values());
62
+ }
63
+
64
+ /**
65
+ * Get plugins in dependency order
66
+ */
67
+ getDependencyOrder(): DataTablePlugin<TData>[] {
68
+ const sorted: DataTablePlugin<TData>[] = [];
69
+ const visited = new Set<string>();
70
+ const visiting = new Set<string>();
71
+
72
+ const visit = (pluginName: string) => {
73
+ if (visiting.has(pluginName)) {
74
+ throw new Error(`Circular dependency detected: ${pluginName}`);
75
+ }
76
+
77
+ if (visited.has(pluginName)) {
78
+ return;
79
+ }
80
+
81
+ const plugin = this.plugins.get(pluginName);
82
+ if (!plugin) {
83
+ throw new Error(`Plugin "${pluginName}" not found`);
84
+ }
85
+
86
+ visiting.add(pluginName);
87
+
88
+ // Visit dependencies first
89
+ if (plugin.dependencies) {
90
+ for (const dependency of plugin.dependencies) {
91
+ visit(dependency);
92
+ }
93
+ }
94
+
95
+ visiting.delete(pluginName);
96
+ visited.add(pluginName);
97
+ sorted.push(plugin);
98
+ };
99
+
100
+ // Visit all plugins
101
+ for (const pluginName of this.plugins.keys()) {
102
+ if (!visited.has(pluginName)) {
103
+ visit(pluginName);
104
+ }
105
+ }
106
+
107
+ return sorted;
108
+ }
109
+
110
+ /**
111
+ * Initialize all plugins
112
+ */
113
+ async initializePlugins(context: DataTableContext<TData>): Promise<void> {
114
+ const plugins = this.getDependencyOrder();
115
+
116
+ for (const plugin of plugins) {
117
+ try {
118
+ await plugin.initialize(context);
119
+ this.initializedPlugins.add(plugin.name);
120
+ } catch (error) {
121
+ console.error(`Failed to initialize plugin "${plugin.name}":`, error);
122
+ throw error;
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Cleanup all plugins
129
+ */
130
+ async cleanupPlugins(): Promise<void> {
131
+ const plugins = this.getDependencyOrder().reverse(); // Reverse for cleanup order
132
+
133
+ for (const plugin of plugins) {
134
+ if (this.initializedPlugins.has(plugin.name)) {
135
+ try {
136
+ await plugin.cleanup();
137
+ this.initializedPlugins.delete(plugin.name);
138
+ } catch (error) {
139
+ console.error(`Error cleaning up plugin "${plugin.name}":`, error);
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Render all plugins
147
+ */
148
+ renderPlugins(): React.ReactNode[] {
149
+ return this.getEnabledPlugins()
150
+ .filter(plugin => this.initializedPlugins.has(plugin.name))
151
+ .map(plugin => plugin.render());
152
+ }
153
+
154
+ /**
155
+ * Check if plugin is initialized
156
+ */
157
+ isPluginInitialized(name: string): boolean {
158
+ return this.initializedPlugins.has(name);
159
+ }
160
+
161
+ /**
162
+ * Get plugin count
163
+ */
164
+ getPluginCount(): number {
165
+ return this.plugins.size;
166
+ }
167
+
168
+ /**
169
+ * Get initialized plugin count
170
+ */
171
+ getInitializedPluginCount(): number {
172
+ return this.initializedPlugins.size;
173
+ }
174
+
175
+ /**
176
+ * Check if plugin has dependencies
177
+ */
178
+ hasDependencies(name: string): boolean {
179
+ const plugin = this.plugins.get(name);
180
+ return plugin ? !!(plugin.dependencies && plugin.dependencies.length > 0) : false;
181
+ }
182
+
183
+ /**
184
+ * Get plugin dependencies
185
+ */
186
+ getPluginDependencies(name: string): string[] {
187
+ const plugin = this.plugins.get(name);
188
+ return plugin?.dependencies || [];
189
+ }
190
+
191
+ /**
192
+ * Check if all dependencies are satisfied
193
+ */
194
+ areDependenciesSatisfied(name: string): boolean {
195
+ const dependencies = this.getPluginDependencies(name);
196
+ return dependencies.every(dep => this.plugins.has(dep));
197
+ }
198
+
199
+ /**
200
+ * Get plugins that depend on a specific plugin
201
+ */
202
+ getDependents(name: string): DataTablePlugin<TData>[] {
203
+ return Array.from(this.plugins.values()).filter(plugin =>
204
+ plugin.dependencies?.includes(name)
205
+ );
206
+ }
207
+
208
+ /**
209
+ * Validate plugin dependencies
210
+ */
211
+ validateDependencies(): { valid: boolean; errors: string[] } {
212
+ const errors: string[] = [];
213
+
214
+ for (const [name, plugin] of this.plugins) {
215
+ if (plugin.dependencies) {
216
+ for (const dependency of plugin.dependencies) {
217
+ if (!this.plugins.has(dependency)) {
218
+ errors.push(`Plugin "${name}" depends on "${dependency}" which is not registered`);
219
+ }
220
+ }
221
+ }
222
+ }
223
+
224
+ return {
225
+ valid: errors.length === 0,
226
+ errors
227
+ };
228
+ }
229
+ }