@jmruthers/pace-core 0.6.1 → 0.6.2

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 (502) hide show
  1. package/CHANGELOG.md +43 -10
  2. package/cursor-rules/00-pace-core-compliance.mdc +18 -91
  3. package/cursor-rules/01-standards-compliance.mdc +16 -47
  4. package/cursor-rules/02-project-structure.mdc +4 -4
  5. package/cursor-rules/03-solid-principles.mdc +45 -164
  6. package/cursor-rules/04-testing-standards.mdc +22 -69
  7. package/cursor-rules/05-bug-reports-and-features.mdc +2 -2
  8. package/cursor-rules/06-code-quality.mdc +42 -125
  9. package/cursor-rules/07-tech-stack-compliance.mdc +33 -128
  10. package/cursor-rules/08-markup-quality.mdc +452 -0
  11. package/cursor-rules/CHANGELOG.md +18 -0
  12. package/cursor-rules/README.md +2 -1
  13. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  14. package/dist/{DataTable-CH1U5Tpy.d.ts → DataTable-BMRU8a1j.d.ts} +33 -1
  15. package/dist/{DataTable-DQ7RSOHE.js → DataTable-TPTKCX4D.js} +10 -9
  16. package/dist/{PublicPageProvider-ce4xlHYA.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +356 -111
  17. package/dist/{UnifiedAuthProvider-ATAP5UTR.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  18. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  19. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  20. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  21. package/dist/chunk-24UVZUZG.js.map +1 -0
  22. package/dist/{chunk-4N5C5XZU.js → chunk-2UOI2FG5.js} +4 -4
  23. package/dist/chunk-2UOI2FG5.js.map +1 -0
  24. package/dist/{chunk-T33XF5ZC.js → chunk-3XC4CPTD.js} +4317 -3963
  25. package/dist/chunk-3XC4CPTD.js.map +1 -0
  26. package/dist/{chunk-4ZC4GX36.js → chunk-6J4GEEJR.js} +172 -45
  27. package/dist/chunk-6J4GEEJR.js.map +1 -0
  28. package/dist/{chunk-3QRJFVBR.js → chunk-6SOIHG6Z.js} +1 -1
  29. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  30. package/dist/{chunk-BYFSK72L.js → chunk-EHMR7VYL.js} +4 -4
  31. package/dist/chunk-EHMR7VYL.js.map +1 -0
  32. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  33. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  34. package/dist/{chunk-LXQLPRQ2.js → chunk-FFQEQTNW.js} +6 -8
  35. package/dist/chunk-FFQEQTNW.js.map +1 -0
  36. package/dist/chunk-FMUCXFII.js +76 -0
  37. package/dist/chunk-FMUCXFII.js.map +1 -0
  38. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  39. package/dist/chunk-L4OXEN46.js.map +1 -0
  40. package/dist/{chunk-R77UEZ4E.js → chunk-M43Y4SSO.js} +1 -1
  41. package/dist/chunk-M43Y4SSO.js.map +1 -0
  42. package/dist/{chunk-3XTALGJF.js → chunk-MMZ7JXPU.js} +60 -223
  43. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  44. package/dist/{chunk-GLK6VM3F.js → chunk-NECFR5MM.js} +254 -170
  45. package/dist/chunk-NECFR5MM.js.map +1 -0
  46. package/dist/{chunk-JBKQ3SAO.js → chunk-SFZUDBL5.js} +40 -4
  47. package/dist/chunk-SFZUDBL5.js.map +1 -0
  48. package/dist/{chunk-XM25TVIE.js → chunk-XWQCNGTQ.js} +724 -363
  49. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  50. package/dist/components.d.ts +5 -5
  51. package/dist/components.js +14 -11
  52. package/dist/components.js.map +1 -1
  53. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  54. package/dist/hooks.d.ts +55 -122
  55. package/dist/hooks.js +8 -12
  56. package/dist/hooks.js.map +1 -1
  57. package/dist/index.d.ts +60 -13
  58. package/dist/index.js +19 -19
  59. package/dist/index.js.map +1 -1
  60. package/dist/providers.d.ts +21 -3
  61. package/dist/providers.js +2 -2
  62. package/dist/rbac/index.d.ts +145 -114
  63. package/dist/rbac/index.js +8 -11
  64. package/dist/theming/runtime.d.ts +1 -13
  65. package/dist/theming/runtime.js +1 -1
  66. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  67. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  68. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  69. package/dist/types.d.ts +2 -2
  70. package/dist/{usePublicRouteParams-BJAlWfuJ.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +31 -1
  71. package/dist/utils.d.ts +4 -5
  72. package/dist/utils.js +14 -14
  73. package/dist/utils.js.map +1 -1
  74. package/docs/api/README.md +7 -1
  75. package/docs/api/classes/ColumnFactory.md +8 -8
  76. package/docs/api/classes/InvalidScopeError.md +4 -4
  77. package/docs/api/classes/Logger.md +1 -1
  78. package/docs/api/classes/MissingUserContextError.md +4 -4
  79. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  80. package/docs/api/classes/PermissionDeniedError.md +4 -4
  81. package/docs/api/classes/RBACAuditManager.md +1 -1
  82. package/docs/api/classes/RBACCache.md +1 -1
  83. package/docs/api/classes/RBACEngine.md +1 -1
  84. package/docs/api/classes/RBACError.md +4 -4
  85. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  86. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  87. package/docs/api/classes/StorageUtils.md +1 -1
  88. package/docs/api/enums/FileCategory.md +1 -1
  89. package/docs/api/enums/LogLevel.md +1 -1
  90. package/docs/api/enums/RBACErrorCode.md +1 -1
  91. package/docs/api/enums/RPCFunction.md +1 -1
  92. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  93. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  94. package/docs/api/interfaces/AggregateConfig.md +4 -4
  95. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  96. package/docs/api/interfaces/AvatarProps.md +1 -1
  97. package/docs/api/interfaces/BadgeProps.md +9 -2
  98. package/docs/api/interfaces/ButtonProps.md +7 -4
  99. package/docs/api/interfaces/CalendarProps.md +8 -5
  100. package/docs/api/interfaces/CardProps.md +8 -5
  101. package/docs/api/interfaces/ColorPalette.md +1 -1
  102. package/docs/api/interfaces/ColorShade.md +1 -1
  103. package/docs/api/interfaces/ComplianceResult.md +1 -1
  104. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  105. package/docs/api/interfaces/DataRecord.md +1 -1
  106. package/docs/api/interfaces/DataTableAction.md +24 -21
  107. package/docs/api/interfaces/DataTableColumn.md +31 -31
  108. package/docs/api/interfaces/DataTableProps.md +1 -1
  109. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  110. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  111. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  112. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  113. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  114. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  115. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  116. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  117. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  118. package/docs/api/interfaces/ExportColumn.md +1 -1
  119. package/docs/api/interfaces/ExportOptions.md +8 -8
  120. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  121. package/docs/api/interfaces/FileMetadata.md +1 -1
  122. package/docs/api/interfaces/FileReference.md +1 -1
  123. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  124. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  125. package/docs/api/interfaces/FileUploadProps.md +26 -23
  126. package/docs/api/interfaces/FooterProps.md +10 -8
  127. package/docs/api/interfaces/FormFieldProps.md +10 -10
  128. package/docs/api/interfaces/FormProps.md +1 -1
  129. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  130. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  131. package/docs/api/interfaces/InputProps.md +7 -4
  132. package/docs/api/interfaces/LabelProps.md +1 -1
  133. package/docs/api/interfaces/LoggerConfig.md +1 -1
  134. package/docs/api/interfaces/LoginFormProps.md +14 -11
  135. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  136. package/docs/api/interfaces/NavigationContextType.md +1 -1
  137. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  138. package/docs/api/interfaces/NavigationItem.md +11 -11
  139. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  140. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  141. package/docs/api/interfaces/Organisation.md +1 -1
  142. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  143. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  144. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  145. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  146. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  147. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  148. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  149. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  150. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  151. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  152. package/docs/api/interfaces/PaletteData.md +1 -1
  153. package/docs/api/interfaces/ParsedAddress.md +1 -1
  154. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  155. package/docs/api/interfaces/ProgressProps.md +1 -1
  156. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  157. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  158. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  159. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  160. package/docs/api/interfaces/QuickFix.md +1 -1
  161. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  162. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  163. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  164. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  165. package/docs/api/interfaces/RBACConfig.md +1 -1
  166. package/docs/api/interfaces/RBACContext.md +1 -1
  167. package/docs/api/interfaces/RBACLogger.md +1 -1
  168. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  169. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  170. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  171. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  172. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  173. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  174. package/docs/api/interfaces/RBACResult.md +1 -1
  175. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  176. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  177. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  178. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  181. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  182. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  183. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  184. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  185. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  186. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  187. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  188. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  189. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  190. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  191. package/docs/api/interfaces/RouteConfig.md +1 -1
  192. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  193. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  194. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  195. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  196. package/docs/api/interfaces/SetupIssue.md +1 -1
  197. package/docs/api/interfaces/StorageConfig.md +1 -1
  198. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  199. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  200. package/docs/api/interfaces/StorageListOptions.md +1 -1
  201. package/docs/api/interfaces/StorageListResult.md +1 -1
  202. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  203. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  204. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  205. package/docs/api/interfaces/StyleImport.md +1 -1
  206. package/docs/api/interfaces/SwitchProps.md +1 -1
  207. package/docs/api/interfaces/TabsContentProps.md +1 -1
  208. package/docs/api/interfaces/TabsListProps.md +1 -1
  209. package/docs/api/interfaces/TabsProps.md +1 -1
  210. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  211. package/docs/api/interfaces/TextareaProps.md +1 -1
  212. package/docs/api/interfaces/ToastActionElement.md +4 -1
  213. package/docs/api/interfaces/ToastProps.md +1 -1
  214. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  215. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  216. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  217. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  218. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  219. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  220. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  221. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  222. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  223. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  224. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  225. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  226. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  227. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  228. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  229. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  230. package/docs/api/interfaces/UserEventAccess.md +14 -11
  231. package/docs/api/interfaces/UserMenuProps.md +8 -6
  232. package/docs/api/interfaces/UserProfile.md +1 -1
  233. package/docs/api/modules.md +575 -634
  234. package/docs/architecture/database-schema-requirements.md +161 -0
  235. package/docs/core-concepts/rbac-system.md +3 -3
  236. package/docs/documentation-index.md +2 -4
  237. package/docs/getting-started/cursor-rules.md +2 -1
  238. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  239. package/docs/migration/MIGRATION_GUIDE.md +2 -24
  240. package/docs/migration/README.md +52 -6
  241. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  242. package/docs/migration/database-changes-december-2025.md +3 -3
  243. package/docs/rbac/event-based-apps.md +1 -1
  244. package/docs/rbac/getting-started.md +1 -1
  245. package/docs/rbac/quick-start.md +1 -1
  246. package/docs/standards/README.md +1 -0
  247. package/package.json +2 -1
  248. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  249. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  250. package/scripts/audit/core/checks/bundle.cjs +142 -0
  251. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +714 -687
  252. package/scripts/audit/core/checks/config.cjs +54 -0
  253. package/scripts/audit/core/checks/coverage.cjs +84 -0
  254. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  255. package/scripts/audit/core/checks/documentation.cjs +203 -0
  256. package/scripts/audit/core/checks/environment.cjs +128 -0
  257. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  258. package/scripts/audit/core/checks/forms.cjs +172 -0
  259. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  260. package/scripts/audit/core/checks/hooks.cjs +334 -0
  261. package/scripts/audit/core/checks/imports.cjs +244 -0
  262. package/scripts/audit/core/checks/performance.cjs +325 -0
  263. package/scripts/audit/core/checks/routes.cjs +117 -0
  264. package/scripts/audit/core/checks/state.cjs +130 -0
  265. package/scripts/audit/core/checks/structure.cjs +65 -0
  266. package/scripts/audit/core/checks/style.cjs +584 -0
  267. package/scripts/audit/core/checks/testing.cjs +122 -0
  268. package/scripts/audit/core/checks/typescript.cjs +61 -0
  269. package/scripts/audit/core/scanner.cjs +199 -0
  270. package/scripts/audit/core/utils.cjs +137 -0
  271. package/scripts/audit/index.cjs +223 -0
  272. package/scripts/audit/reporters/console.cjs +151 -0
  273. package/scripts/audit/reporters/json.cjs +54 -0
  274. package/scripts/audit/reporters/markdown.cjs +124 -0
  275. package/scripts/audit-consuming-app.cjs +61 -936
  276. package/scripts/build-docs/build-decision.js +240 -0
  277. package/scripts/build-docs/cache-utils.js +105 -0
  278. package/scripts/build-docs/content-normalization.js +150 -0
  279. package/scripts/build-docs/file-utils.js +105 -0
  280. package/scripts/build-docs/git-utils.js +86 -0
  281. package/scripts/build-docs/hash-utils.js +116 -0
  282. package/scripts/build-docs/typedoc-runner.js +220 -0
  283. package/scripts/build-docs-incremental.js +77 -913
  284. package/scripts/utils/command-runner.js +16 -11
  285. package/scripts/validate-formats.js +61 -56
  286. package/scripts/validate-master.js +74 -69
  287. package/scripts/validate-pre-publish.js +70 -65
  288. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  289. package/src/components/Alert/Alert.test.tsx +12 -18
  290. package/src/components/Alert/Alert.tsx +5 -7
  291. package/src/components/Avatar/Avatar.test.tsx +4 -4
  292. package/src/components/Badge/Badge.tsx +14 -0
  293. package/src/components/Button/Button.tsx +22 -0
  294. package/src/components/Calendar/Calendar.tsx +8 -2
  295. package/src/components/Card/Card.tsx +4 -0
  296. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  297. package/src/components/Checkbox/Checkbox.tsx +2 -2
  298. package/src/components/DataTable/DataTable.tsx +38 -4
  299. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  300. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +18 -4
  301. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  302. package/src/components/DataTable/components/AccessDeniedPage.tsx +16 -25
  303. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  304. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
  305. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  306. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  307. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  308. package/src/components/DataTable/components/DataTableCore.tsx +196 -554
  309. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  310. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  311. package/src/components/DataTable/components/DataTableModals.tsx +8 -0
  312. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  313. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  314. package/src/components/DataTable/components/EditFields.tsx +307 -0
  315. package/src/components/DataTable/components/EditableRow.tsx +8 -0
  316. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  317. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  318. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  319. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  320. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  321. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  322. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  323. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  324. package/src/components/DataTable/components/UnifiedTableBody.tsx +61 -849
  325. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  326. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  327. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  328. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  329. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  330. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  331. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  332. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  333. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  334. package/src/components/DataTable/hooks/useColumnReordering.ts +12 -0
  335. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  336. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  337. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  338. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  339. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  340. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  341. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  342. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  343. package/src/components/DataTable/styles.ts +6 -6
  344. package/src/components/DataTable/types.ts +6 -10
  345. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  346. package/src/components/DataTable/utils/debugTools.ts +18 -113
  347. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  348. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  349. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  350. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  351. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  352. package/src/components/Dialog/Dialog.tsx +2 -2
  353. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  354. package/src/components/ErrorBoundary/ErrorBoundary.tsx +45 -5
  355. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  356. package/src/components/ErrorBoundary/index.ts +27 -2
  357. package/src/components/EventSelector/EventSelector.tsx +3 -0
  358. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  359. package/src/components/FileUpload/FileUpload.tsx +22 -2
  360. package/src/components/Footer/Footer.test.tsx +16 -16
  361. package/src/components/Footer/Footer.tsx +14 -11
  362. package/src/components/Form/Form.tsx +1 -0
  363. package/src/components/Header/Header.tsx +21 -10
  364. package/src/components/Input/Input.test.tsx +2 -2
  365. package/src/components/Input/Input.tsx +8 -4
  366. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  367. package/src/components/LoginForm/LoginForm.tsx +4 -0
  368. package/src/components/NavigationMenu/NavigationMenu.tsx +14 -513
  369. package/src/components/NavigationMenu/types.ts +56 -0
  370. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  371. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  372. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -2
  373. package/src/components/PaceAppLayout/PaceAppLayout.tsx +32 -11
  374. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  375. package/src/components/PaceLoginPage/PaceLoginPage.tsx +3 -0
  376. package/src/components/PasswordChange/PasswordChangeForm.tsx +9 -0
  377. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  378. package/src/components/PublicLayout/PublicPageLayout.tsx +2 -5
  379. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  380. package/src/components/Select/Select.tsx +80 -434
  381. package/src/components/Select/context.ts +23 -0
  382. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  383. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  384. package/src/components/Select/hooks/useSelectState.ts +104 -0
  385. package/src/components/Select/index.ts +9 -1
  386. package/src/components/Select/types.ts +123 -0
  387. package/src/components/Select/utils/text.ts +26 -0
  388. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +4 -5
  389. package/src/components/Switch/Switch.tsx +4 -4
  390. package/src/components/Tabs/Tabs.tsx +1 -1
  391. package/src/components/Toast/Toast.tsx +4 -0
  392. package/src/components/Tooltip/Tooltip.tsx +2 -2
  393. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  394. package/src/components/UserMenu/UserMenu.tsx +21 -18
  395. package/src/components/index.ts +2 -2
  396. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  397. package/src/hooks/index.ts +1 -2
  398. package/src/hooks/public/usePublicEvent.ts +4 -0
  399. package/src/hooks/public/usePublicEventLogo.ts +4 -0
  400. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  401. package/src/hooks/public/usePublicRouteParams.ts +4 -0
  402. package/src/hooks/services/useAuth.ts +32 -0
  403. package/src/hooks/services/useCurrentEvent.ts +6 -0
  404. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  405. package/src/hooks/useDebounce.ts +9 -0
  406. package/src/hooks/useEventTheme.ts +6 -0
  407. package/src/hooks/useFileDisplay.ts +4 -0
  408. package/src/hooks/useFileReference.ts +25 -7
  409. package/src/hooks/useFileUrl.ts +11 -1
  410. package/src/hooks/useFocusManagement.ts +14 -0
  411. package/src/hooks/useFocusTrap.ts +3 -0
  412. package/src/hooks/useInactivityTracker.ts +3 -0
  413. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  414. package/src/hooks/useOrganisationPermissions.ts +4 -0
  415. package/src/hooks/useOrganisationSecurity.ts +4 -0
  416. package/src/hooks/usePerformanceMonitor.ts +4 -0
  417. package/src/hooks/usePermissionCache.ts +7 -0
  418. package/src/hooks/useQueryCache.ts +12 -1
  419. package/src/hooks/useSessionRestoration.ts +4 -0
  420. package/src/hooks/useStorage.ts +4 -0
  421. package/src/hooks/useToast.ts +1 -1
  422. package/src/index.ts +2 -1
  423. package/src/providers/__tests__/OrganisationProvider.test.tsx +92 -70
  424. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  425. package/src/providers/services/EventServiceProvider.tsx +18 -0
  426. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  427. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  428. package/src/providers/services/UnifiedAuthProvider.tsx +36 -0
  429. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +29 -13
  430. package/src/rbac/README.md +1 -1
  431. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +2 -2
  432. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  433. package/src/rbac/adapters.tsx +14 -5
  434. package/src/rbac/api.ts +100 -67
  435. package/src/rbac/components/NavigationProvider.tsx +4 -1
  436. package/src/rbac/components/PagePermissionGuard.tsx +157 -17
  437. package/src/rbac/components/RoleBasedRouter.tsx +5 -1
  438. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  439. package/src/rbac/components/SecureDataProvider.tsx +20 -5
  440. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  441. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  442. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  443. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  444. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  445. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  446. package/src/rbac/engine.ts +38 -14
  447. package/src/rbac/hooks/permissions/index.ts +7 -0
  448. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  449. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  450. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  451. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  452. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  453. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  454. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  455. package/src/rbac/hooks/useCan.test.ts +71 -64
  456. package/src/rbac/hooks/usePermissions.ts +14 -995
  457. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  458. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  459. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  460. package/src/rbac/permissions.ts +0 -30
  461. package/src/rbac/secureClient.ts +200 -61
  462. package/src/rbac/types.ts +8 -0
  463. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  464. package/src/theming/parseEventColours.ts +5 -19
  465. package/src/types/vitest-globals.d.ts +51 -26
  466. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  467. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  468. package/src/utils/__tests__/index.unit.test.ts +2 -2
  469. package/src/utils/audit/audit.ts +0 -3
  470. package/src/utils/core/cn.ts +1 -1
  471. package/src/utils/file-reference/index.ts +53 -1
  472. package/src/utils/formatting/formatting.ts +8 -18
  473. package/src/utils/index.ts +0 -1
  474. package/dist/chunk-3QRJFVBR.js.map +0 -1
  475. package/dist/chunk-3XTALGJF.js.map +0 -1
  476. package/dist/chunk-4N5C5XZU.js.map +0 -1
  477. package/dist/chunk-4ZC4GX36.js.map +0 -1
  478. package/dist/chunk-BYFSK72L.js.map +0 -1
  479. package/dist/chunk-EXUD6RNJ.js +0 -451
  480. package/dist/chunk-EXUD6RNJ.js.map +0 -1
  481. package/dist/chunk-GLK6VM3F.js.map +0 -1
  482. package/dist/chunk-I7PSE6JW.js.map +0 -1
  483. package/dist/chunk-JBKQ3SAO.js.map +0 -1
  484. package/dist/chunk-KNC55RTG.js.map +0 -1
  485. package/dist/chunk-LXQLPRQ2.js.map +0 -1
  486. package/dist/chunk-R77UEZ4E.js.map +0 -1
  487. package/dist/chunk-SQGMNID3.js.map +0 -1
  488. package/dist/chunk-T33XF5ZC.js.map +0 -1
  489. package/dist/chunk-XM25TVIE.js.map +0 -1
  490. package/docs/api/classes/ErrorBoundary.md +0 -144
  491. package/docs/migration/quick-migration-guide.md +0 -356
  492. package/docs/migration/service-architecture.md +0 -281
  493. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  494. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  495. package/src/hooks/useSecureDataAccess.ts +0 -681
  496. /package/dist/{DataTable-DQ7RSOHE.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  497. /package/dist/{UnifiedAuthProvider-ATAP5UTR.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  498. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  499. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  500. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  501. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  502. /package/docs/migration/{REACT_19_MIGRATION.md → V0.6.0_REACT_19_MIGRATION.md} +0 -0
@@ -14,429 +14,35 @@
14
14
 
15
15
  import * as React from "react";
16
16
  import { Search, X, ChevronDown, Check } from "lucide-react";
17
- import { Button, type ButtonProps } from "../Button/Button";
17
+ import { Button } from "../Button/Button";
18
18
  import { cn } from "../../utils/core/cn";
19
-
20
- // ============================================================================
21
- // TYPES AND INTERFACES
22
- // ============================================================================
23
-
24
- export interface SelectState {
25
- value: string;
26
- selectedText: string;
27
- open: boolean;
28
- disabled: boolean;
29
- }
30
-
31
- export interface SelectActions {
32
- setValue: (value: string, text: string) => void;
33
- setOpen: (open: boolean) => void;
34
- setSelectedText: (text: string) => void;
35
- }
36
-
37
- export interface SelectEventHandlers {
38
- onValueChange?: (value: string) => void;
39
- onOpenChange?: (open: boolean) => void;
40
- }
41
-
42
- export interface UseSelectStateProps {
43
- value?: string;
44
- defaultValue?: string;
45
- selectedText?: string;
46
- open?: boolean;
47
- defaultOpen?: boolean;
48
- disabled?: boolean;
49
- onValueChange?: (value: string) => void;
50
- onOpenChange?: (open: boolean) => void;
51
- }
52
-
53
- export interface UseSelectEventsProps {
54
- state: SelectState;
55
- actions: SelectActions;
56
- selectRef: React.RefObject<HTMLElement | null>;
57
- }
58
-
59
- export interface UseSelectSearchProps {
60
- children: React.ReactNode;
61
- searchable?: boolean;
62
- searchTerm?: string;
63
- onSearchChange?: (term: string) => void;
64
- }
65
-
66
- export interface SelectContextValue extends SelectState {
67
- actions: SelectActions;
68
- registerItem?: (value: string, text: string) => void;
69
- unregisterItem?: (value: string) => void;
70
- direction?: 'up' | 'down';
71
- }
72
-
73
- export interface SelectProps extends Omit<React.HTMLAttributes<HTMLFormElement>, 'onChange' | 'onKeyDown' | 'onFocus' | 'onBlur'> {
74
- children: React.ReactNode;
75
- className?: string;
76
- direction?: 'up' | 'down';
77
- // State props are in UseSelectStateProps (via & intersection)
78
- }
79
-
80
- export interface SelectTriggerProps extends Omit<ButtonProps, 'onClick' | 'onKeyDown'> {
81
- children: React.ReactNode;
82
- asChild?: boolean;
83
- }
84
-
85
- export interface SelectValueProps {
86
- placeholder?: string;
87
- children?: React.ReactNode;
88
- }
89
-
90
- export interface SelectContentProps {
91
- children: React.ReactNode;
92
- className?: string;
93
- searchable?: boolean;
94
- searchPlaceholder?: string;
95
- maxHeight?: string;
96
- style?: React.CSSProperties;
97
- }
98
-
99
- export interface SelectItemProps {
100
- value: string;
101
- children: React.ReactNode;
102
- disabled?: boolean;
103
- className?: string;
104
- onClick?: (e: React.MouseEvent) => void;
105
- }
106
-
107
- // ============================================================================
108
- // HOOKS
109
- // ============================================================================
110
-
111
- // State Management Hook
112
- export const useSelectState = ({
113
- value: controlledValue,
114
- defaultValue = '',
115
- selectedText: controlledSelectedText,
116
- open: controlledOpen,
117
- defaultOpen = false,
118
- disabled = false,
119
- onValueChange,
120
- onOpenChange,
121
- }: UseSelectStateProps) => {
122
- // Internal state for uncontrolled mode
123
- const [internalValue, setInternalValue] = React.useState(defaultValue);
124
- const [internalSelectedText, setInternalSelectedText] = React.useState('');
125
- const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
126
-
127
- // Computed values (controlled vs uncontrolled)
128
- const value = controlledValue !== undefined ? controlledValue : internalValue;
129
- const selectedText = controlledSelectedText !== undefined ? controlledSelectedText : internalSelectedText;
130
- const open = controlledOpen !== undefined ? controlledOpen : internalOpen;
131
-
132
- // Actions
133
- const setValue = React.useCallback((newValue: string, newText: string) => {
134
- if (controlledValue === undefined) {
135
- setInternalValue(newValue);
136
- setInternalSelectedText(newText);
137
- }
138
- onValueChange?.(newValue);
139
-
140
- // Close dropdown after selection (for uncontrolled mode)
141
- if (controlledOpen === undefined) {
142
- setInternalOpen(false);
143
- }
144
- onOpenChange?.(false);
145
- }, [controlledValue, onValueChange, controlledOpen, onOpenChange]);
146
-
147
- const setOpen = React.useCallback((newOpen: boolean) => {
148
- if (disabled) {
149
- return;
150
- }
151
-
152
- // Close all other select dropdowns when opening this one
153
- if (newOpen) {
154
- const allTriggers = document.querySelectorAll('[data-testid="select-trigger"]');
155
- allTriggers.forEach(trigger => {
156
- if (trigger.getAttribute('aria-expanded') === 'true') {
157
- const selectRoot = trigger.closest('[data-testid="select-root"]');
158
- if (selectRoot) {
159
- const closeEvent = new CustomEvent('closeSelect');
160
- selectRoot.dispatchEvent(closeEvent);
161
- }
162
- }
163
- });
164
- }
165
-
166
- if (controlledOpen === undefined) {
167
- setInternalOpen(newOpen);
168
- }
169
- onOpenChange?.(newOpen);
170
- }, [controlledOpen, onOpenChange, disabled]);
171
-
172
- const setSelectedText = React.useCallback((newText: string) => {
173
- if (controlledSelectedText === undefined) {
174
- setInternalSelectedText(newText);
175
- }
176
- }, [controlledSelectedText]);
177
-
178
- const state: SelectState = {
179
- value,
180
- selectedText,
181
- open,
182
- disabled,
183
- };
184
-
185
- const actions: SelectActions = {
186
- setValue,
187
- setOpen,
188
- setSelectedText,
189
- };
190
-
191
- return { state, actions };
192
- };
193
-
194
- // Event Handling Hook
195
- export const useSelectEvents = ({ state, actions, selectRef }: UseSelectEventsProps) => {
196
- const [isSelecting, setIsSelecting] = React.useState(false);
197
-
198
- // Listen for close events from other selects
199
- React.useEffect(() => {
200
- const handleCloseSelect = () => {
201
- actions.setOpen(false);
202
- };
203
-
204
- const selectElement = selectRef.current as HTMLElement;
205
- if (selectElement) {
206
- selectElement.addEventListener('closeSelect', handleCloseSelect);
207
- return () => {
208
- selectElement.removeEventListener('closeSelect', handleCloseSelect);
209
- };
210
- }
211
- }, [actions, selectRef]);
212
-
213
- // Handle click outside to close dropdown
214
- React.useEffect(() => {
215
- if (!state.open) return;
216
-
217
- const handleClickOutside = (event: MouseEvent) => {
218
- const selectElement = selectRef.current;
219
- if (!selectElement) return;
220
-
221
- const target = event.target as Node;
222
-
223
- // Close if clicking outside the select element
224
- if (!selectElement.contains(target) && !isSelecting) {
225
- actions.setOpen(false);
226
- }
227
- };
228
-
229
- // Add a small delay to avoid closing immediately when opening
230
- // The delay ensures the click event that opened the dropdown has fully processed
231
- const timeoutId = setTimeout(() => {
232
- document.addEventListener('click', handleClickOutside, true); // Use capture phase
233
- }, 100); // Small delay to let the opening click complete
234
-
235
- return () => {
236
- clearTimeout(timeoutId);
237
- document.removeEventListener('click', handleClickOutside, true);
238
- };
239
- }, [state.open, actions, selectRef, isSelecting]);
240
-
241
- // Handle SelectItem mousedown events
242
- React.useEffect(() => {
243
- let timeoutId: NodeJS.Timeout | null = null;
244
- let isMounted = true;
245
-
246
- const handleSelectItemMouseDown = () => {
247
- if (!isMounted) return;
248
-
249
- setIsSelecting(true);
250
- timeoutId = setTimeout(() => {
251
- if (isMounted) {
252
- setIsSelecting(false);
253
- }
254
- }, 150);
255
- };
256
-
257
- document.addEventListener('selectItemMouseDown', handleSelectItemMouseDown as EventListener);
258
- return () => {
259
- isMounted = false;
260
- document.removeEventListener('selectItemMouseDown', handleSelectItemMouseDown as EventListener);
261
- if (timeoutId) {
262
- clearTimeout(timeoutId);
263
- }
264
- };
265
- }, []);
266
-
267
- return { isSelecting };
268
- };
269
-
270
- // Search Functionality Hook
271
- export const useSelectSearch = ({
272
- children,
273
- searchable = false,
274
- searchTerm: controlledSearchTerm,
275
- onSearchChange
276
- }: UseSelectSearchProps) => {
277
- const [internalSearchTerm, setInternalSearchTerm] = React.useState('');
278
- const [filteredChildren, setFilteredChildren] = React.useState<React.ReactNode>(children);
279
- const searchInputRef = React.useRef<HTMLInputElement>(null);
280
-
281
- const searchTerm = controlledSearchTerm !== undefined ? controlledSearchTerm : internalSearchTerm;
282
-
283
- const setSearchTerm = React.useCallback((term: string) => {
284
- if (controlledSearchTerm === undefined) {
285
- setInternalSearchTerm(term);
286
- }
287
- onSearchChange?.(term);
288
- }, [controlledSearchTerm, onSearchChange]);
289
-
290
- // Filter children based on search term
291
- React.useEffect(() => {
292
- if (!searchable || !searchTerm) {
293
- setFilteredChildren(children);
294
- return;
295
- }
296
-
297
- const filterChildren = (nodes: React.ReactNode): React.ReactNode => {
298
- return React.Children.map(nodes, (child) => {
299
- if (!React.isValidElement(child)) return child;
300
-
301
- // Check if this is a SelectItem by looking for the data-testid or value prop
302
- const childProps = child.props as { 'data-testid'?: string; value?: unknown; children?: React.ReactNode; [key: string]: unknown };
303
- const isSelectItem = childProps &&
304
- (childProps['data-testid'] === 'select-item' ||
305
- childProps.value !== undefined);
306
-
307
- if (isSelectItem) {
308
- const childText = React.Children.toArray(childProps.children).join(' ').toLowerCase();
309
- const searchLower = searchTerm.toLowerCase();
310
-
311
- if (childText.includes(searchLower)) {
312
- return child;
313
- }
314
- return null;
315
- }
316
-
317
- if (childProps.children) {
318
- const filteredChildChildren = filterChildren(childProps.children);
319
- if (React.Children.count(filteredChildChildren) > 0) {
320
- return React.cloneElement(child, {}, filteredChildChildren);
321
- }
322
- return null;
323
- }
324
-
325
- return child;
326
- });
327
- };
328
-
329
- const filtered = filterChildren(children);
330
- setFilteredChildren(filtered);
331
- }, [children, searchTerm, searchable]);
332
-
333
- return {
334
- searchTerm,
335
- setSearchTerm,
336
- filteredChildren,
337
- searchInputRef,
338
- };
339
- };
340
-
341
- // ============================================================================
342
- // CONTEXT
343
- // ============================================================================
344
-
345
- const SelectContext = React.createContext<SelectContextValue | null>(null);
346
-
347
- const useSelectContext = () => {
348
- const context = React.useContext(SelectContext);
349
- if (!context) {
350
- throw new Error('Select components must be used within a Select');
351
- }
352
- return context;
353
- };
19
+ import { SelectContext, useSelectContext } from "./context";
20
+ import { useSelectEvents } from "./hooks/useSelectEvents";
21
+ import { useSelectSearch } from "./hooks/useSelectSearch";
22
+ import { useSelectState } from "./hooks/useSelectState";
23
+ import type {
24
+ SelectContentProps,
25
+ SelectContextValue,
26
+ SelectItemProps,
27
+ SelectProps,
28
+ SelectTriggerProps,
29
+ SelectValueProps,
30
+ UseSelectStateProps,
31
+ } from "./types";
32
+ import { getTextContent } from "./utils/text";
354
33
 
355
34
  // ============================================================================
356
35
  // ROOT COMPONENT
357
36
  // ============================================================================
358
37
 
359
- // Helper function to extract text from React children
360
- const getTextContent = (children: React.ReactNode): string => {
361
- if (typeof children === 'string') return children;
362
- if (typeof children === 'number') return children.toString();
363
- if (React.isValidElement(children)) {
364
- const props = children.props as { children?: React.ReactNode; [key: string]: unknown };
365
- if (props.children) {
366
- return getTextContent(props.children);
367
- }
368
- }
369
- if (Array.isArray(children)) {
370
- return children.map(getTextContent).join('');
371
- }
372
- return '';
373
- };
374
-
375
- // Helper function to find SelectItem by value in React children tree
376
- const findSelectItemText = (children: React.ReactNode, targetValue: string): string | null => {
377
- if (!children) return null;
378
-
379
- // Handle arrays directly (common when using .map())
380
- if (Array.isArray(children)) {
381
- for (const child of children) {
382
- if (!child) continue;
383
- const found = findSelectItemText(child, targetValue);
384
- if (found) return found;
385
- }
386
- return null;
387
- }
388
-
389
- // Handle React elements directly if it's a single element
390
- if (React.isValidElement(children)) {
391
- const props = (children as React.ReactElement<{ value?: string; children?: React.ReactNode }>).props;
392
-
393
- // Check if this is a SelectItem
394
- if (props && props.value !== undefined && typeof props.value === 'string' && props.value === targetValue) {
395
- const text = getTextContent(props.children);
396
- if (text && text.trim()) {
397
- return text.trim();
398
- }
399
- }
400
-
401
- // Recurse into children
402
- if (props && props.children !== undefined && props.children !== null) {
403
- const found = findSelectItemText(props.children, targetValue);
404
- if (found) return found;
405
- }
406
- return null;
407
- }
408
-
409
- // Handle multiple children using React.Children utilities
410
- const childrenArray = React.Children.toArray(children);
411
-
412
- for (const child of childrenArray) {
413
- if (!React.isValidElement(child)) continue;
414
-
415
- const props = (child as React.ReactElement<{ value?: string; children?: React.ReactNode }>).props;
416
-
417
- // Check if this is a SelectItem by checking for value prop
418
- // SelectItem components always have a value prop
419
- if (props && props.value !== undefined && typeof props.value === 'string' && props.value === targetValue) {
420
- const text = getTextContent(props.children);
421
- if (text && text.trim()) {
422
- return text.trim();
423
- }
424
- }
425
-
426
- // Recursively search in children
427
- // Need to check both props.children and handle null/undefined
428
- if (props) {
429
- const childChildren = props.children;
430
- if (childChildren !== undefined && childChildren !== null) {
431
- const found = findSelectItemText(childChildren, targetValue);
432
- if (found) return found;
433
- }
434
- }
435
- }
436
-
437
- return null;
438
- };
439
-
38
+ /**
39
+ * Select component root.
40
+ * Provides select dropdown functionality with search, keyboard navigation, and accessibility.
41
+ *
42
+ * @param props - Select configuration
43
+ * @param ref - Forwarded ref to the form element
44
+ * @returns The rendered select component
45
+ */
440
46
  export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectStateProps>(
441
47
  ({
442
48
  children,
@@ -618,6 +224,14 @@ Select.displayName = "Select";
618
224
  // TRIGGER COMPONENT
619
225
  // ============================================================================
620
226
 
227
+ /**
228
+ * Select trigger button component.
229
+ * Opens/closes the select dropdown and displays the selected value.
230
+ *
231
+ * @param props - Select trigger configuration
232
+ * @param ref - Forwarded ref to the button element
233
+ * @returns The rendered select trigger
234
+ */
621
235
  export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerProps>(
622
236
  ({ children, className, variant = "outline", size = "default", asChild = false, ...props }, ref) => {
623
237
  const { open, disabled, value, actions, direction = 'down' } = useSelectContext();
@@ -784,6 +398,14 @@ SelectTrigger.displayName = "SelectTrigger";
784
398
  // VALUE COMPONENT
785
399
  // ============================================================================
786
400
 
401
+ /**
402
+ * Select value display component.
403
+ * Shows the selected value or placeholder text.
404
+ *
405
+ * @param props - Select value configuration
406
+ * @param ref - Forwarded ref to the span element
407
+ * @returns The rendered select value display
408
+ */
787
409
  export const SelectValue = React.forwardRef<HTMLSpanElement, SelectValueProps>(
788
410
  ({ placeholder = "Select an option...", children }, ref) => {
789
411
  const { selectedText } = useSelectContext();
@@ -806,6 +428,14 @@ SelectValue.displayName = "SelectValue";
806
428
  // CONTENT COMPONENT
807
429
  // ============================================================================
808
430
 
431
+ /**
432
+ * Select content/dropdown component.
433
+ * Contains the list of selectable options.
434
+ *
435
+ * @param props - Select content configuration
436
+ * @param ref - Forwarded ref to the list element
437
+ * @returns The rendered select content
438
+ */
809
439
  export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentProps>(
810
440
  ({
811
441
  children,
@@ -914,27 +544,19 @@ SelectContent.displayName = "SelectContent";
914
544
  // ITEM COMPONENT
915
545
  // ============================================================================
916
546
 
547
+ /**
548
+ * Select item component.
549
+ * Represents a single selectable option in the dropdown.
550
+ *
551
+ * @param props - Select item configuration
552
+ * @param ref - Forwarded ref to the list item element
553
+ * @returns The rendered select item
554
+ */
917
555
  export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
918
556
  ({ value, children, disabled = false, className, onClick }, ref) => {
919
557
  const { value: selectedValue, actions, registerItem, unregisterItem } = useSelectContext();
920
558
  const isSelected = selectedValue === value;
921
-
922
- // Extract text content from children for display
923
- const getTextContent = (children: React.ReactNode): string => {
924
- if (typeof children === 'string') return children;
925
- if (typeof children === 'number') return children.toString();
926
- if (React.isValidElement(children)) {
927
- const props = children.props as { children?: React.ReactNode; [key: string]: unknown };
928
- if (props.children) {
929
- return getTextContent(props.children);
930
- }
931
- }
932
- if (Array.isArray(children)) {
933
- return children.map(getTextContent).join('');
934
- }
935
- return '';
936
- };
937
-
559
+
938
560
  const itemText = getTextContent(children);
939
561
 
940
562
  // Register this item when it mounts or text changes
@@ -1025,6 +647,14 @@ SelectItem.displayName = "SelectItem";
1025
647
  // ADDITIONAL COMPONENTS (for backward compatibility)
1026
648
  // ============================================================================
1027
649
 
650
+ /**
651
+ * Select group component.
652
+ * Groups related select items together.
653
+ *
654
+ * @param props - Select group configuration
655
+ * @param ref - Forwarded ref to the div element
656
+ * @returns The rendered select group
657
+ */
1028
658
  export const SelectGroup = React.forwardRef<HTMLDivElement, { children: React.ReactNode; className?: string }>(
1029
659
  ({ children, className }, ref) => {
1030
660
  return (
@@ -1036,6 +666,14 @@ export const SelectGroup = React.forwardRef<HTMLDivElement, { children: React.Re
1036
666
  );
1037
667
  SelectGroup.displayName = "SelectGroup";
1038
668
 
669
+ /**
670
+ * Select label component.
671
+ * Provides a label for a group of select items.
672
+ *
673
+ * @param props - Select label configuration
674
+ * @param ref - Forwarded ref to the div element
675
+ * @returns The rendered select label
676
+ */
1039
677
  export const SelectLabel = React.forwardRef<HTMLDivElement, { children: React.ReactNode; className?: string }>(
1040
678
  ({ children, className }, ref) => {
1041
679
  return (
@@ -1047,6 +685,14 @@ export const SelectLabel = React.forwardRef<HTMLDivElement, { children: React.Re
1047
685
  );
1048
686
  SelectLabel.displayName = "SelectLabel";
1049
687
 
688
+ /**
689
+ * Select separator component.
690
+ * Provides visual separation between groups of select items.
691
+ *
692
+ * @param props - Select separator configuration
693
+ * @param ref - Forwarded ref to the div element
694
+ * @returns The rendered select separator
695
+ */
1050
696
  export const SelectSeparator = React.forwardRef<HTMLDivElement, { className?: string }>(
1051
697
  ({ className }, ref) => {
1052
698
  return (
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+ import type { SelectContextValue } from "./types";
3
+
4
+ /**
5
+ * Select context for sharing state between Select components.
6
+ */
7
+ export const SelectContext =
8
+ React.createContext<SelectContextValue | null>(null);
9
+
10
+ /**
11
+ * Hook to access Select context.
12
+ * Must be used within a Select component.
13
+ *
14
+ * @returns Select context value
15
+ * @throws Error if used outside Select component
16
+ */
17
+ export const useSelectContext = () => {
18
+ const context = React.useContext(SelectContext);
19
+ if (!context) {
20
+ throw new Error("Select components must be used within a Select");
21
+ }
22
+ return context;
23
+ };
@@ -0,0 +1,87 @@
1
+ import * as React from "react";
2
+ import type { UseSelectEventsProps } from "../types";
3
+
4
+ /**
5
+ * Hook for managing Select event handlers.
6
+ * Handles click outside, keyboard navigation, and focus management.
7
+ *
8
+ * @param props - Select events configuration
9
+ */
10
+ export const useSelectEvents = ({
11
+ state,
12
+ actions,
13
+ selectRef,
14
+ }: UseSelectEventsProps) => {
15
+ const [isSelecting, setIsSelecting] = React.useState(false);
16
+
17
+ React.useEffect(() => {
18
+ const handleCloseSelect = () => {
19
+ actions.setOpen(false);
20
+ };
21
+
22
+ const selectElement = selectRef.current as HTMLElement;
23
+ if (selectElement) {
24
+ selectElement.addEventListener("closeSelect", handleCloseSelect);
25
+ return () => {
26
+ selectElement.removeEventListener("closeSelect", handleCloseSelect);
27
+ };
28
+ }
29
+ }, [actions, selectRef]);
30
+
31
+ React.useEffect(() => {
32
+ if (!state.open) return;
33
+
34
+ const handleClickOutside = (event: MouseEvent) => {
35
+ const selectElement = selectRef.current;
36
+ if (!selectElement) return;
37
+
38
+ const target = event.target as Node;
39
+
40
+ if (!selectElement.contains(target) && !isSelecting) {
41
+ actions.setOpen(false);
42
+ }
43
+ };
44
+
45
+ const timeoutId = setTimeout(() => {
46
+ document.addEventListener("click", handleClickOutside, true);
47
+ }, 100);
48
+
49
+ return () => {
50
+ clearTimeout(timeoutId);
51
+ document.removeEventListener("click", handleClickOutside, true);
52
+ };
53
+ }, [state.open, actions, selectRef, isSelecting]);
54
+
55
+ React.useEffect(() => {
56
+ let timeoutId: NodeJS.Timeout | null = null;
57
+ let isMounted = true;
58
+
59
+ const handleSelectItemMouseDown = () => {
60
+ if (!isMounted) return;
61
+
62
+ setIsSelecting(true);
63
+ timeoutId = setTimeout(() => {
64
+ if (isMounted) {
65
+ setIsSelecting(false);
66
+ }
67
+ }, 150);
68
+ };
69
+
70
+ document.addEventListener(
71
+ "selectItemMouseDown",
72
+ handleSelectItemMouseDown as EventListener
73
+ );
74
+ return () => {
75
+ isMounted = false;
76
+ document.removeEventListener(
77
+ "selectItemMouseDown",
78
+ handleSelectItemMouseDown as EventListener
79
+ );
80
+ if (timeoutId) {
81
+ clearTimeout(timeoutId);
82
+ }
83
+ };
84
+ }, []);
85
+
86
+ return { isSelecting };
87
+ };