@jmruthers/pace-core 0.5.193 → 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 (577) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +7 -1
  3. package/cursor-rules/00-pace-core-compliance.mdc +299 -0
  4. package/cursor-rules/01-standards-compliance.mdc +244 -0
  5. package/cursor-rules/02-project-structure.mdc +200 -0
  6. package/cursor-rules/03-solid-principles.mdc +222 -0
  7. package/cursor-rules/04-testing-standards.mdc +268 -0
  8. package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
  9. package/cursor-rules/06-code-quality.mdc +309 -0
  10. package/cursor-rules/07-tech-stack-compliance.mdc +214 -0
  11. package/cursor-rules/08-markup-quality.mdc +452 -0
  12. package/cursor-rules/CHANGELOG.md +119 -0
  13. package/cursor-rules/README.md +192 -0
  14. package/dist/{AuthService-DjnJHDtC.d.ts → AuthService-BPvc3Ka0.d.ts} +54 -0
  15. package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-BMRU8a1j.d.ts} +34 -2
  16. package/dist/{DataTable-5FU7IESH.js → DataTable-TPTKCX4D.js} +10 -9
  17. package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-DC6kCaqf.d.ts} +385 -261
  18. package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-CH6Z342H.js} +3 -3
  19. package/dist/{UnifiedAuthProvider-185Ih4dj.d.ts → UnifiedAuthProvider-CVcTjx-d.d.ts} +29 -0
  20. package/dist/{api-N774RPUA.js → api-MVVQZLJI.js} +2 -2
  21. package/dist/{chunk-KNC55RTG.js → chunk-24UVZUZG.js} +90 -54
  22. package/dist/chunk-24UVZUZG.js.map +1 -0
  23. package/dist/{chunk-HWIIPPNI.js → chunk-2UOI2FG5.js} +20 -20
  24. package/dist/chunk-2UOI2FG5.js.map +1 -0
  25. package/dist/{chunk-E3SPN4VZ 5.js → chunk-3XC4CPTD.js} +4345 -3986
  26. package/dist/chunk-3XC4CPTD.js.map +1 -0
  27. package/dist/{chunk-7EQTDTTJ.js → chunk-6J4GEEJR.js} +172 -45
  28. package/dist/chunk-6J4GEEJR.js.map +1 -0
  29. package/dist/{chunk-6C4YBBJM 5.js → chunk-6SOIHG6Z.js} +1 -1
  30. package/dist/chunk-6SOIHG6Z.js.map +1 -0
  31. package/dist/{chunk-7FLMSG37.js → chunk-EHMR7VYL.js} +25 -25
  32. package/dist/chunk-EHMR7VYL.js.map +1 -0
  33. package/dist/{chunk-I7PSE6JW.js → chunk-F2IMUDXZ.js} +2 -75
  34. package/dist/chunk-F2IMUDXZ.js.map +1 -0
  35. package/dist/{chunk-QWWZ5CAQ.js → chunk-FFQEQTNW.js} +7 -9
  36. package/dist/chunk-FFQEQTNW.js.map +1 -0
  37. package/dist/chunk-FMUCXFII.js +76 -0
  38. package/dist/chunk-FMUCXFII.js.map +1 -0
  39. package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
  40. package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
  41. package/dist/{chunk-SQGMNID3.js → chunk-L4OXEN46.js} +4 -5
  42. package/dist/chunk-L4OXEN46.js.map +1 -0
  43. package/dist/{chunk-R77UEZ4E 3.js → chunk-M43Y4SSO.js} +1 -1
  44. package/dist/chunk-M43Y4SSO.js.map +1 -0
  45. package/dist/{chunk-IIELH4DL.js → chunk-MMZ7JXPU.js} +60 -223
  46. package/dist/chunk-MMZ7JXPU.js.map +1 -0
  47. package/dist/{chunk-NOAYCWCX 5.js → chunk-NECFR5MM.js} +394 -312
  48. package/dist/chunk-NECFR5MM.js.map +1 -0
  49. package/dist/{chunk-BC4IJKSL.js → chunk-SFZUDBL5.js} +40 -4
  50. package/dist/chunk-SFZUDBL5.js.map +1 -0
  51. package/dist/{chunk-XNXXZ43G.js → chunk-XWQCNGTQ.js} +748 -364
  52. package/dist/chunk-XWQCNGTQ.js.map +1 -0
  53. package/dist/components.d.ts +6 -6
  54. package/dist/components.js +15 -12
  55. package/dist/components.js.map +1 -1
  56. package/dist/{functions-D_kgHktt.d.ts → functions-DHebl8-F.d.ts} +1 -1
  57. package/dist/hooks.d.ts +59 -126
  58. package/dist/hooks.js +19 -28
  59. package/dist/hooks.js.map +1 -1
  60. package/dist/index.d.ts +63 -16
  61. package/dist/index.js +23 -24
  62. package/dist/index.js.map +1 -1
  63. package/dist/providers.d.ts +21 -3
  64. package/dist/providers.js +2 -2
  65. package/dist/rbac/index.d.ts +146 -115
  66. package/dist/rbac/index.js +8 -11
  67. package/dist/theming/runtime.d.ts +1 -13
  68. package/dist/theming/runtime.js +1 -1
  69. package/dist/{timezone-_pgH8qrY.d.ts → timezone-CHhWg6b4.d.ts} +3 -10
  70. package/dist/{types-UU913iLA.d.ts → types-BeoeWV5I.d.ts} +8 -0
  71. package/dist/{types-CEpcvwwF.d.ts → types-CkbwOr4Y.d.ts} +6 -0
  72. package/dist/types.d.ts +2 -2
  73. package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-1oMokgLF.d.ts} +34 -4
  74. package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
  75. package/dist/utils.d.ts +4 -5
  76. package/dist/utils.js +15 -15
  77. package/dist/utils.js.map +1 -1
  78. package/docs/api/README.md +7 -1
  79. package/docs/api/classes/ColumnFactory.md +8 -8
  80. package/docs/api/classes/InvalidScopeError.md +4 -4
  81. package/docs/api/classes/Logger.md +1 -1
  82. package/docs/api/classes/MissingUserContextError.md +4 -4
  83. package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
  84. package/docs/api/classes/PermissionDeniedError.md +4 -4
  85. package/docs/api/classes/RBACAuditManager.md +1 -1
  86. package/docs/api/classes/RBACCache.md +1 -1
  87. package/docs/api/classes/RBACEngine.md +1 -1
  88. package/docs/api/classes/RBACError.md +4 -4
  89. package/docs/api/classes/RBACNotInitializedError.md +4 -4
  90. package/docs/api/classes/SecureSupabaseClient.md +18 -15
  91. package/docs/api/classes/StorageUtils.md +1 -1
  92. package/docs/api/enums/FileCategory.md +1 -1
  93. package/docs/api/enums/LogLevel.md +1 -1
  94. package/docs/api/enums/RBACErrorCode.md +1 -1
  95. package/docs/api/enums/RPCFunction.md +1 -1
  96. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  97. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  98. package/docs/api/interfaces/AggregateConfig.md +4 -4
  99. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  100. package/docs/api/interfaces/AvatarProps.md +1 -1
  101. package/docs/api/interfaces/BadgeProps.md +9 -2
  102. package/docs/api/interfaces/ButtonProps.md +7 -4
  103. package/docs/api/interfaces/CalendarProps.md +8 -5
  104. package/docs/api/interfaces/CardProps.md +8 -5
  105. package/docs/api/interfaces/ColorPalette.md +1 -1
  106. package/docs/api/interfaces/ColorShade.md +1 -1
  107. package/docs/api/interfaces/ComplianceResult.md +1 -1
  108. package/docs/api/interfaces/DataAccessRecord.md +9 -9
  109. package/docs/api/interfaces/DataRecord.md +1 -1
  110. package/docs/api/interfaces/DataTableAction.md +24 -21
  111. package/docs/api/interfaces/DataTableColumn.md +31 -31
  112. package/docs/api/interfaces/DataTableProps.md +1 -1
  113. package/docs/api/interfaces/DataTableToolbarButton.md +7 -7
  114. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  115. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  116. package/docs/api/interfaces/EmptyStateConfig.md +5 -5
  117. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  118. package/docs/api/interfaces/ErrorBoundaryProps.md +147 -0
  119. package/docs/api/interfaces/ErrorBoundaryProviderProps.md +36 -0
  120. package/docs/api/interfaces/ErrorBoundaryState.md +75 -0
  121. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  122. package/docs/api/interfaces/ExportColumn.md +1 -1
  123. package/docs/api/interfaces/ExportOptions.md +8 -8
  124. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  125. package/docs/api/interfaces/FileMetadata.md +1 -1
  126. package/docs/api/interfaces/FileReference.md +1 -1
  127. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  128. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  129. package/docs/api/interfaces/FileUploadProps.md +26 -23
  130. package/docs/api/interfaces/FooterProps.md +10 -8
  131. package/docs/api/interfaces/FormFieldProps.md +10 -10
  132. package/docs/api/interfaces/FormProps.md +1 -1
  133. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  134. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  135. package/docs/api/interfaces/InputProps.md +7 -4
  136. package/docs/api/interfaces/LabelProps.md +1 -1
  137. package/docs/api/interfaces/LoggerConfig.md +1 -1
  138. package/docs/api/interfaces/LoginFormProps.md +14 -11
  139. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  140. package/docs/api/interfaces/NavigationContextType.md +1 -1
  141. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  142. package/docs/api/interfaces/NavigationItem.md +11 -11
  143. package/docs/api/interfaces/NavigationMenuProps.md +15 -15
  144. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  145. package/docs/api/interfaces/Organisation.md +1 -1
  146. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  147. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  148. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  149. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  150. package/docs/api/interfaces/PaceAppLayoutProps.md +30 -27
  151. package/docs/api/interfaces/PaceLoginPageProps.md +6 -4
  152. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  153. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  154. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  155. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  156. package/docs/api/interfaces/PaletteData.md +1 -1
  157. package/docs/api/interfaces/ParsedAddress.md +1 -1
  158. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  159. package/docs/api/interfaces/ProgressProps.md +1 -1
  160. package/docs/api/interfaces/ProtectedRouteProps.md +7 -26
  161. package/docs/api/interfaces/PublicPageFooterProps.md +9 -9
  162. package/docs/api/interfaces/PublicPageHeaderProps.md +10 -10
  163. package/docs/api/interfaces/PublicPageLayoutProps.md +7 -20
  164. package/docs/api/interfaces/QuickFix.md +1 -1
  165. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  166. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  167. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  168. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  169. package/docs/api/interfaces/RBACConfig.md +1 -1
  170. package/docs/api/interfaces/RBACContext.md +1 -1
  171. package/docs/api/interfaces/RBACLogger.md +1 -1
  172. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  173. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  174. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  175. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  176. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  177. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  178. package/docs/api/interfaces/RBACResult.md +1 -1
  179. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  180. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  181. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  182. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  183. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  184. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  185. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  186. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  187. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  188. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  189. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  190. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  191. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  192. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  193. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  194. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  195. package/docs/api/interfaces/RouteConfig.md +1 -1
  196. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  197. package/docs/api/interfaces/SecureDataContextType.md +9 -9
  198. package/docs/api/interfaces/SecureDataProviderProps.md +8 -8
  199. package/docs/api/interfaces/SessionRestorationLoaderProps.md +3 -3
  200. package/docs/api/interfaces/SetupIssue.md +1 -1
  201. package/docs/api/interfaces/StorageConfig.md +1 -1
  202. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  203. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  204. package/docs/api/interfaces/StorageListOptions.md +1 -1
  205. package/docs/api/interfaces/StorageListResult.md +1 -1
  206. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  207. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  208. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  209. package/docs/api/interfaces/StyleImport.md +1 -1
  210. package/docs/api/interfaces/SwitchProps.md +1 -1
  211. package/docs/api/interfaces/TabsContentProps.md +1 -1
  212. package/docs/api/interfaces/TabsListProps.md +1 -1
  213. package/docs/api/interfaces/TabsProps.md +1 -1
  214. package/docs/api/interfaces/TabsTriggerProps.md +3 -3
  215. package/docs/api/interfaces/TextareaProps.md +1 -1
  216. package/docs/api/interfaces/ToastActionElement.md +4 -1
  217. package/docs/api/interfaces/ToastProps.md +1 -1
  218. package/docs/api/interfaces/UnifiedAuthContextType.md +58 -55
  219. package/docs/api/interfaces/UnifiedAuthProviderProps.md +15 -13
  220. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  221. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  222. package/docs/api/interfaces/UseInactivityTrackerOptions.md +11 -9
  223. package/docs/api/interfaces/UseInactivityTrackerReturn.md +8 -8
  224. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  225. package/docs/api/interfaces/UsePublicEventLogoReturn.md +9 -6
  226. package/docs/api/interfaces/UsePublicEventOptions.md +3 -3
  227. package/docs/api/interfaces/UsePublicEventReturn.md +8 -5
  228. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +4 -4
  229. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +12 -9
  230. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +10 -7
  231. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  232. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  233. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  234. package/docs/api/interfaces/UserEventAccess.md +14 -11
  235. package/docs/api/interfaces/UserMenuProps.md +8 -6
  236. package/docs/api/interfaces/UserProfile.md +1 -1
  237. package/docs/api/modules.md +575 -634
  238. package/docs/architecture/database-schema-requirements.md +161 -0
  239. package/docs/core-concepts/rbac-system.md +3 -3
  240. package/docs/documentation-index.md +2 -4
  241. package/docs/getting-started/cursor-rules.md +263 -0
  242. package/docs/getting-started/installation-guide.md +6 -1
  243. package/docs/getting-started/quick-start.md +6 -1
  244. package/docs/migration/DOCUMENTATION_STRUCTURE.md +441 -0
  245. package/docs/migration/MIGRATION_GUIDE.md +6 -28
  246. package/docs/migration/README.md +52 -6
  247. package/docs/migration/V0.5.190_TO_V0.6.1_MIGRATION.md +1153 -0
  248. package/docs/migration/V0.6.0_REACT_19_MIGRATION.md +227 -0
  249. package/docs/migration/database-changes-december-2025.md +3 -3
  250. package/docs/rbac/event-based-apps.md +1 -1
  251. package/docs/rbac/getting-started.md +1 -1
  252. package/docs/rbac/quick-start.md +1 -1
  253. package/docs/standards/README.md +40 -0
  254. package/docs/troubleshooting/migration.md +4 -4
  255. package/examples/PublicPages/PublicEventPage.tsx +1 -1
  256. package/package.json +12 -6
  257. package/scripts/audit/core/checks/accessibility.cjs +197 -0
  258. package/scripts/audit/core/checks/api-usage.cjs +191 -0
  259. package/scripts/audit/core/checks/bundle.cjs +142 -0
  260. package/scripts/{check-pace-core-compliance.cjs → audit/core/checks/compliance.cjs} +737 -691
  261. package/scripts/audit/core/checks/config.cjs +54 -0
  262. package/scripts/audit/core/checks/coverage.cjs +84 -0
  263. package/scripts/audit/core/checks/dependencies.cjs +454 -0
  264. package/scripts/audit/core/checks/documentation.cjs +203 -0
  265. package/scripts/audit/core/checks/environment.cjs +128 -0
  266. package/scripts/audit/core/checks/error-handling.cjs +299 -0
  267. package/scripts/audit/core/checks/forms.cjs +172 -0
  268. package/scripts/audit/core/checks/heuristics.cjs +68 -0
  269. package/scripts/audit/core/checks/hooks.cjs +334 -0
  270. package/scripts/audit/core/checks/imports.cjs +244 -0
  271. package/scripts/audit/core/checks/performance.cjs +325 -0
  272. package/scripts/audit/core/checks/routes.cjs +117 -0
  273. package/scripts/audit/core/checks/state.cjs +130 -0
  274. package/scripts/audit/core/checks/structure.cjs +65 -0
  275. package/scripts/audit/core/checks/style.cjs +584 -0
  276. package/scripts/audit/core/checks/testing.cjs +122 -0
  277. package/scripts/audit/core/checks/typescript.cjs +61 -0
  278. package/scripts/audit/core/scanner.cjs +199 -0
  279. package/scripts/audit/core/utils.cjs +137 -0
  280. package/scripts/audit/index.cjs +223 -0
  281. package/scripts/audit/reporters/console.cjs +151 -0
  282. package/scripts/audit/reporters/json.cjs +54 -0
  283. package/scripts/audit/reporters/markdown.cjs +124 -0
  284. package/scripts/audit-consuming-app.cjs +86 -0
  285. package/scripts/build-docs/build-decision.js +240 -0
  286. package/scripts/build-docs/cache-utils.js +105 -0
  287. package/scripts/build-docs/content-normalization.js +150 -0
  288. package/scripts/build-docs/file-utils.js +105 -0
  289. package/scripts/build-docs/git-utils.js +86 -0
  290. package/scripts/build-docs/hash-utils.js +116 -0
  291. package/scripts/build-docs/typedoc-runner.js +220 -0
  292. package/scripts/build-docs-incremental.js +77 -913
  293. package/scripts/install-cursor-rules.cjs +236 -0
  294. package/scripts/utils/command-runner.js +16 -11
  295. package/scripts/validate-formats.js +61 -56
  296. package/scripts/validate-master.js +74 -69
  297. package/scripts/validate-pre-publish.js +70 -65
  298. package/src/__tests__/helpers/test-providers.tsx +1 -1
  299. package/src/__tests__/helpers/test-utils.tsx +1 -1
  300. package/src/__tests__/hooks/usePermissions.test.ts +2 -2
  301. package/src/components/Alert/Alert.test.tsx +12 -18
  302. package/src/components/Alert/Alert.tsx +5 -7
  303. package/src/components/Avatar/Avatar.test.tsx +4 -4
  304. package/src/components/Badge/Badge.tsx +16 -4
  305. package/src/components/Button/Button.tsx +27 -4
  306. package/src/components/Calendar/Calendar.tsx +9 -3
  307. package/src/components/Card/Card.tsx +4 -0
  308. package/src/components/Checkbox/Checkbox.test.tsx +12 -12
  309. package/src/components/Checkbox/Checkbox.tsx +2 -2
  310. package/src/components/DataTable/DataTable.test.tsx +57 -93
  311. package/src/components/DataTable/DataTable.tsx +40 -6
  312. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +5 -6
  313. package/src/components/DataTable/__tests__/pagination.modes.test.tsx +29 -7
  314. package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
  315. package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +2 -3
  316. package/src/components/DataTable/components/AccessDeniedPage.tsx +17 -26
  317. package/src/components/DataTable/components/ActionButtons.tsx +10 -7
  318. package/src/components/DataTable/components/BulkOperationsDropdown.tsx +2 -2
  319. package/src/components/DataTable/components/ColumnFilter.tsx +10 -0
  320. package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +12 -0
  321. package/src/components/DataTable/components/DataTableBody.tsx +8 -0
  322. package/src/components/DataTable/components/DataTableCore.tsx +200 -561
  323. package/src/components/DataTable/components/DataTableErrorBoundary.tsx +11 -0
  324. package/src/components/DataTable/components/DataTableLayout.tsx +559 -0
  325. package/src/components/DataTable/components/DataTableModals.tsx +9 -1
  326. package/src/components/DataTable/components/DataTableToolbar.tsx +8 -0
  327. package/src/components/DataTable/components/DraggableColumnHeader.tsx +12 -0
  328. package/src/components/DataTable/components/EditFields.tsx +307 -0
  329. package/src/components/DataTable/components/EditableRow.tsx +9 -1
  330. package/src/components/DataTable/components/EmptyState.tsx +10 -0
  331. package/src/components/DataTable/components/FilterRow.tsx +12 -0
  332. package/src/components/DataTable/components/GroupHeader.tsx +12 -0
  333. package/src/components/DataTable/components/GroupingDropdown.tsx +12 -0
  334. package/src/components/DataTable/components/ImportModal.tsx +7 -0
  335. package/src/components/DataTable/components/LoadingState.tsx +6 -0
  336. package/src/components/DataTable/components/PaginationControls.tsx +16 -1
  337. package/src/components/DataTable/components/RowComponent.tsx +391 -0
  338. package/src/components/DataTable/components/UnifiedTableBody.tsx +62 -852
  339. package/src/components/DataTable/components/VirtualizedDataTable.tsx +16 -4
  340. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +4 -2
  341. package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
  342. package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
  343. package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
  344. package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
  345. package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
  346. package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
  347. package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
  348. package/src/components/DataTable/components/cellValueUtils.ts +40 -0
  349. package/src/components/DataTable/components/hooks/useImportModalFocus.ts +53 -0
  350. package/src/components/DataTable/components/hooks/usePermissionTracking.ts +126 -0
  351. package/src/components/DataTable/context/DataTableContext.tsx +50 -0
  352. package/src/components/DataTable/core/ColumnFactory.ts +31 -0
  353. package/src/components/DataTable/core/DataTableContext.tsx +32 -1
  354. package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +10 -0
  355. package/src/components/DataTable/hooks/useColumnReordering.ts +14 -2
  356. package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +10 -0
  357. package/src/components/DataTable/hooks/useDataTableDataPipeline.ts +16 -0
  358. package/src/components/DataTable/hooks/useDataTablePermissions.ts +124 -32
  359. package/src/components/DataTable/hooks/useDataTableState.ts +35 -1
  360. package/src/components/DataTable/hooks/useEffectiveColumnOrder.ts +12 -0
  361. package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
  362. package/src/components/DataTable/hooks/useServerSideDataEffect.ts +11 -0
  363. package/src/components/DataTable/hooks/useTableColumns.ts +8 -0
  364. package/src/components/DataTable/hooks/useTableHandlers.ts +14 -0
  365. package/src/components/DataTable/styles.ts +6 -6
  366. package/src/components/DataTable/types.ts +6 -10
  367. package/src/components/DataTable/utils/a11yUtils.ts +7 -0
  368. package/src/components/DataTable/utils/debugTools.ts +18 -113
  369. package/src/components/DataTable/utils/errorHandling.ts +12 -0
  370. package/src/components/DataTable/utils/exportUtils.ts +9 -0
  371. package/src/components/DataTable/utils/flexibleImport.ts +12 -48
  372. package/src/components/DataTable/utils/paginationUtils.ts +8 -0
  373. package/src/components/DataTable/utils/performanceUtils.ts +5 -1
  374. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
  375. package/src/components/Dialog/Dialog.tsx +8 -7
  376. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +180 -1
  377. package/src/components/ErrorBoundary/ErrorBoundary.tsx +46 -6
  378. package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +129 -0
  379. package/src/components/ErrorBoundary/index.ts +27 -2
  380. package/src/components/EventSelector/EventSelector.tsx +4 -1
  381. package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
  382. package/src/components/FileDisplay/FileDisplay.tsx +32 -18
  383. package/src/components/FileUpload/FileUpload.tsx +22 -2
  384. package/src/components/Footer/Footer.test.tsx +16 -16
  385. package/src/components/Footer/Footer.tsx +15 -12
  386. package/src/components/Form/Form.test.tsx +36 -15
  387. package/src/components/Form/Form.tsx +31 -26
  388. package/src/components/Header/Header.tsx +22 -11
  389. package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
  390. package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
  391. package/src/components/Input/Input.test.tsx +2 -2
  392. package/src/components/Input/Input.tsx +36 -34
  393. package/src/components/Label/Label.tsx +1 -1
  394. package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +4 -4
  395. package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
  396. package/src/components/LoginForm/LoginForm.test.tsx +42 -42
  397. package/src/components/LoginForm/LoginForm.tsx +12 -8
  398. package/src/components/NavigationMenu/NavigationMenu.tsx +15 -514
  399. package/src/components/NavigationMenu/types.ts +56 -0
  400. package/src/components/NavigationMenu/useNavigationFiltering.ts +390 -0
  401. package/src/components/OrganisationSelector/OrganisationSelector.tsx +3 -0
  402. package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
  403. package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +54 -52
  404. package/src/components/PaceAppLayout/PaceAppLayout.tsx +33 -12
  405. package/src/components/PaceAppLayout/README.md +1 -1
  406. package/src/components/PaceAppLayout/test-setup.tsx +1 -2
  407. package/src/components/PaceLoginPage/PaceLoginPage.tsx +4 -1
  408. package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
  409. package/src/components/PasswordChange/PasswordChangeForm.tsx +10 -1
  410. package/src/components/Progress/Progress.tsx +1 -1
  411. package/src/components/ProtectedRoute/ProtectedRoute.tsx +3 -9
  412. package/src/components/PublicLayout/PublicPageLayout.tsx +3 -6
  413. package/src/components/PublicLayout/PublicPageProvider.tsx +4 -0
  414. package/src/components/Select/Select.tsx +95 -438
  415. package/src/components/Select/context.ts +23 -0
  416. package/src/components/Select/hooks/useSelectEvents.ts +87 -0
  417. package/src/components/Select/hooks/useSelectSearch.ts +91 -0
  418. package/src/components/Select/hooks/useSelectState.ts +104 -0
  419. package/src/components/Select/index.ts +9 -1
  420. package/src/components/Select/types.ts +123 -0
  421. package/src/components/Select/utils/text.ts +26 -0
  422. package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +5 -6
  423. package/src/components/Switch/Switch.tsx +4 -4
  424. package/src/components/Table/Table.tsx +1 -1
  425. package/src/components/Tabs/Tabs.tsx +1 -1
  426. package/src/components/Textarea/Textarea.tsx +27 -29
  427. package/src/components/Toast/Toast.tsx +5 -1
  428. package/src/components/Tooltip/Tooltip.tsx +3 -3
  429. package/src/components/UserMenu/UserMenu.test.tsx +24 -11
  430. package/src/components/UserMenu/UserMenu.tsx +22 -19
  431. package/src/components/index.ts +2 -2
  432. package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
  433. package/src/hooks/__tests__/index.unit.test.ts +2 -5
  434. package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
  435. package/src/hooks/index.ts +1 -2
  436. package/src/hooks/public/usePublicEvent.ts +5 -1
  437. package/src/hooks/public/usePublicEventLogo.ts +5 -1
  438. package/src/hooks/public/usePublicFileDisplay.ts +4 -0
  439. package/src/hooks/public/usePublicRouteParams.ts +5 -1
  440. package/src/hooks/services/useAuth.ts +32 -0
  441. package/src/hooks/services/useCurrentEvent.ts +6 -0
  442. package/src/hooks/services/useCurrentOrganisation.ts +6 -0
  443. package/src/hooks/useDataTableState.ts +8 -18
  444. package/src/hooks/useDebounce.ts +9 -0
  445. package/src/hooks/useEventTheme.ts +6 -0
  446. package/src/hooks/useFileDisplay.ts +4 -0
  447. package/src/hooks/useFileReference.ts +25 -7
  448. package/src/hooks/useFileUrl.ts +11 -1
  449. package/src/hooks/useFocusManagement.ts +16 -2
  450. package/src/hooks/useFocusTrap.ts +7 -4
  451. package/src/hooks/useFormDialog.ts +8 -7
  452. package/src/hooks/useInactivityTracker.ts +4 -1
  453. package/src/hooks/useKeyboardShortcuts.ts +4 -0
  454. package/src/hooks/useOrganisationPermissions.ts +4 -0
  455. package/src/hooks/useOrganisationSecurity.ts +4 -0
  456. package/src/hooks/usePerformanceMonitor.ts +4 -0
  457. package/src/hooks/usePermissionCache.ts +8 -1
  458. package/src/hooks/useQueryCache.ts +12 -1
  459. package/src/hooks/useSessionRestoration.ts +4 -0
  460. package/src/hooks/useStorage.ts +4 -0
  461. package/src/hooks/useToast.ts +3 -3
  462. package/src/index.ts +2 -1
  463. package/src/providers/__tests__/OrganisationProvider.test.tsx +115 -49
  464. package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
  465. package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
  466. package/src/providers/services/AuthServiceProvider.tsx +18 -0
  467. package/src/providers/services/EventServiceProvider.tsx +18 -0
  468. package/src/providers/services/InactivityServiceProvider.tsx +18 -0
  469. package/src/providers/services/OrganisationServiceProvider.tsx +18 -0
  470. package/src/providers/services/UnifiedAuthProvider.tsx +58 -22
  471. package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +33 -7
  472. package/src/rbac/README.md +1 -1
  473. package/src/rbac/__tests__/adapters.comprehensive.test.tsx +26 -26
  474. package/src/rbac/__tests__/scenarios.user-role.test.tsx +4 -5
  475. package/src/rbac/adapters.tsx +14 -5
  476. package/src/rbac/api.ts +100 -67
  477. package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
  478. package/src/rbac/components/NavigationGuard.tsx +1 -1
  479. package/src/rbac/components/NavigationProvider.tsx +5 -2
  480. package/src/rbac/components/PagePermissionGuard.tsx +158 -18
  481. package/src/rbac/components/PagePermissionProvider.tsx +1 -1
  482. package/src/rbac/components/PermissionEnforcer.tsx +1 -1
  483. package/src/rbac/components/RoleBasedRouter.tsx +6 -2
  484. package/src/rbac/components/SecureDataProvider.test.tsx +84 -49
  485. package/src/rbac/components/SecureDataProvider.tsx +21 -6
  486. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +24 -14
  487. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +7 -0
  488. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +14 -6
  489. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +15 -4
  490. package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +148 -24
  491. package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +81 -15
  492. package/src/rbac/engine.ts +38 -14
  493. package/src/rbac/hooks/permissions/index.ts +7 -0
  494. package/src/rbac/hooks/permissions/useAccessLevel.ts +105 -0
  495. package/src/rbac/hooks/permissions/useCachedPermissions.ts +79 -0
  496. package/src/rbac/hooks/permissions/useCan.ts +347 -0
  497. package/src/rbac/hooks/permissions/useHasAllPermissions.ts +90 -0
  498. package/src/rbac/hooks/permissions/useHasAnyPermission.ts +90 -0
  499. package/src/rbac/hooks/permissions/useMultiplePermissions.ts +93 -0
  500. package/src/rbac/hooks/permissions/usePermissions.ts +253 -0
  501. package/src/rbac/hooks/useCan.test.ts +71 -64
  502. package/src/rbac/hooks/usePermissions.ts +14 -995
  503. package/src/rbac/hooks/useResourcePermissions.test.ts +54 -18
  504. package/src/rbac/hooks/useResourcePermissions.ts +14 -4
  505. package/src/rbac/hooks/useSecureSupabase.ts +33 -13
  506. package/src/rbac/permissions.ts +0 -30
  507. package/src/rbac/secureClient.ts +212 -61
  508. package/src/rbac/types.ts +8 -0
  509. package/src/theming/__tests__/parseEventColours.test.ts +6 -9
  510. package/src/theming/parseEventColours.ts +5 -19
  511. package/src/types/vitest-globals.d.ts +51 -26
  512. package/src/utils/__mocks__/supabaseMock.ts +1 -3
  513. package/src/utils/__tests__/formatting.unit.test.ts +4 -4
  514. package/src/utils/__tests__/index.unit.test.ts +2 -2
  515. package/src/utils/audit/audit.ts +0 -3
  516. package/src/utils/core/cn.ts +1 -1
  517. package/src/utils/file-reference/index.ts +53 -1
  518. package/src/utils/formatting/formatting.ts +8 -18
  519. package/src/utils/index.ts +0 -1
  520. package/src/utils/security/secureDataAccess.test.ts +31 -20
  521. package/src/utils/security/secureDataAccess.ts +4 -3
  522. package/dist/chunk-6C4YBBJM.js +0 -628
  523. package/dist/chunk-6C4YBBJM.js.map +0 -1
  524. package/dist/chunk-7D4SUZUM.js 2.map +0 -1
  525. package/dist/chunk-7EQTDTTJ.js 2.map +0 -1
  526. package/dist/chunk-7EQTDTTJ.js.map +0 -1
  527. package/dist/chunk-7FLMSG37.js 2.map +0 -1
  528. package/dist/chunk-7FLMSG37.js.map +0 -1
  529. package/dist/chunk-BC4IJKSL.js.map +0 -1
  530. package/dist/chunk-E3SPN4VZ.js +0 -12917
  531. package/dist/chunk-E3SPN4VZ.js.map +0 -1
  532. package/dist/chunk-E66EQZE6 5.js +0 -37
  533. package/dist/chunk-E66EQZE6.js 2.map +0 -1
  534. package/dist/chunk-HWIIPPNI.js.map +0 -1
  535. package/dist/chunk-I7PSE6JW 5.js +0 -191
  536. package/dist/chunk-I7PSE6JW.js 2.map +0 -1
  537. package/dist/chunk-I7PSE6JW.js.map +0 -1
  538. package/dist/chunk-IIELH4DL.js.map +0 -1
  539. package/dist/chunk-KNC55RTG.js 5.map +0 -1
  540. package/dist/chunk-KNC55RTG.js.map +0 -1
  541. package/dist/chunk-KQCRWDSA.js 5.map +0 -1
  542. package/dist/chunk-LFNCN2SP.js +0 -412
  543. package/dist/chunk-LFNCN2SP.js 2.map +0 -1
  544. package/dist/chunk-LFNCN2SP.js.map +0 -1
  545. package/dist/chunk-LMC26NLJ 2.js +0 -84
  546. package/dist/chunk-NOAYCWCX.js +0 -4993
  547. package/dist/chunk-NOAYCWCX.js.map +0 -1
  548. package/dist/chunk-QWWZ5CAQ.js 3.map +0 -1
  549. package/dist/chunk-QWWZ5CAQ.js.map +0 -1
  550. package/dist/chunk-QXHPKYJV 3.js +0 -113
  551. package/dist/chunk-R77UEZ4E.js +0 -68
  552. package/dist/chunk-R77UEZ4E.js.map +0 -1
  553. package/dist/chunk-SQGMNID3.js.map +0 -1
  554. package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
  555. package/dist/chunk-XNXXZ43G.js.map +0 -1
  556. package/dist/chunk-ZSAAAMVR 6.js +0 -25
  557. package/dist/components.js 5.map +0 -1
  558. package/dist/styles/index 2.js +0 -12
  559. package/dist/styles/index.js 5.map +0 -1
  560. package/dist/theming/runtime 5.js +0 -19
  561. package/dist/theming/runtime.js 5.map +0 -1
  562. package/docs/api/classes/ErrorBoundary.md +0 -144
  563. package/docs/migration/quick-migration-guide.md +0 -356
  564. package/docs/migration/service-architecture.md +0 -281
  565. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +0 -680
  566. package/src/hooks/useSecureDataAccess.test.ts +0 -559
  567. package/src/hooks/useSecureDataAccess.ts +0 -666
  568. /package/dist/{DataTable-5FU7IESH.js.map → DataTable-TPTKCX4D.js.map} +0 -0
  569. /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-CH6Z342H.js.map} +0 -0
  570. /package/dist/{api-N774RPUA.js.map → api-MVVQZLJI.js.map} +0 -0
  571. /package/docs/migration/{organisation-context-timing-fix.md → V0.3.44_organisation-context-timing-fix.md} +0 -0
  572. /package/docs/migration/{rbac-migration.md → V0.4.0_rbac-migration.md} +0 -0
  573. /package/docs/migration/{person-scoped-profiles-migration-guide.md → V0.5.190_person-scoped-profiles-migration-guide.md} +0 -0
  574. /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
  575. /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
  576. /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
  577. /package/examples/{rbac → RBAC}/index.ts +0 -0
@@ -14,425 +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>;
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 isSelectItem = child.props &&
303
- (child.props['data-testid'] === 'select-item' ||
304
- child.props.value !== undefined);
305
-
306
- if (isSelectItem) {
307
- const childText = React.Children.toArray(child.props.children).join(' ').toLowerCase();
308
- const searchLower = searchTerm.toLowerCase();
309
-
310
- if (childText.includes(searchLower)) {
311
- return child;
312
- }
313
- return null;
314
- }
315
-
316
- if (child.props.children) {
317
- const filteredChildChildren = filterChildren(child.props.children);
318
- if (React.Children.count(filteredChildChildren) > 0) {
319
- return React.cloneElement(child, {}, filteredChildChildren);
320
- }
321
- return null;
322
- }
323
-
324
- return child;
325
- });
326
- };
327
-
328
- const filtered = filterChildren(children);
329
- setFilteredChildren(filtered);
330
- }, [children, searchTerm, searchable]);
331
-
332
- return {
333
- searchTerm,
334
- setSearchTerm,
335
- filteredChildren,
336
- searchInputRef,
337
- };
338
- };
339
-
340
- // ============================================================================
341
- // CONTEXT
342
- // ============================================================================
343
-
344
- const SelectContext = React.createContext<SelectContextValue | null>(null);
345
-
346
- const useSelectContext = () => {
347
- const context = React.useContext(SelectContext);
348
- if (!context) {
349
- throw new Error('Select components must be used within a Select');
350
- }
351
- return context;
352
- };
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";
353
33
 
354
34
  // ============================================================================
355
35
  // ROOT COMPONENT
356
36
  // ============================================================================
357
37
 
358
- // Helper function to extract text from React children
359
- const getTextContent = (children: React.ReactNode): string => {
360
- if (typeof children === 'string') return children;
361
- if (typeof children === 'number') return children.toString();
362
- if (React.isValidElement(children) && children.props.children) {
363
- return getTextContent(children.props.children);
364
- }
365
- if (Array.isArray(children)) {
366
- return children.map(getTextContent).join('');
367
- }
368
- return '';
369
- };
370
-
371
- // Helper function to find SelectItem by value in React children tree
372
- const findSelectItemText = (children: React.ReactNode, targetValue: string): string | null => {
373
- if (!children) return null;
374
-
375
- // Handle arrays directly (common when using .map())
376
- if (Array.isArray(children)) {
377
- for (const child of children) {
378
- if (!child) continue;
379
- const found = findSelectItemText(child, targetValue);
380
- if (found) return found;
381
- }
382
- return null;
383
- }
384
-
385
- // Handle React elements directly if it's a single element
386
- if (React.isValidElement(children)) {
387
- const props = (children as React.ReactElement<{ value?: string; children?: React.ReactNode }>).props;
388
-
389
- // Check if this is a SelectItem
390
- if (props && props.value !== undefined && typeof props.value === 'string' && props.value === targetValue) {
391
- const text = getTextContent(props.children);
392
- if (text && text.trim()) {
393
- return text.trim();
394
- }
395
- }
396
-
397
- // Recurse into children
398
- if (props && props.children !== undefined && props.children !== null) {
399
- const found = findSelectItemText(props.children, targetValue);
400
- if (found) return found;
401
- }
402
- return null;
403
- }
404
-
405
- // Handle multiple children using React.Children utilities
406
- const childrenArray = React.Children.toArray(children);
407
-
408
- for (const child of childrenArray) {
409
- if (!React.isValidElement(child)) continue;
410
-
411
- const props = (child as React.ReactElement<{ value?: string; children?: React.ReactNode }>).props;
412
-
413
- // Check if this is a SelectItem by checking for value prop
414
- // SelectItem components always have a value prop
415
- if (props && props.value !== undefined && typeof props.value === 'string' && props.value === targetValue) {
416
- const text = getTextContent(props.children);
417
- if (text && text.trim()) {
418
- return text.trim();
419
- }
420
- }
421
-
422
- // Recursively search in children
423
- // Need to check both props.children and handle null/undefined
424
- if (props) {
425
- const childChildren = props.children;
426
- if (childChildren !== undefined && childChildren !== null) {
427
- const found = findSelectItemText(childChildren, targetValue);
428
- if (found) return found;
429
- }
430
- }
431
- }
432
-
433
- return null;
434
- };
435
-
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
+ */
436
46
  export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectStateProps>(
437
47
  ({
438
48
  children,
@@ -443,7 +53,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
443
53
  const internalRef = React.useRef<HTMLFormElement>(null);
444
54
  const selectRef = React.useMemo(() => {
445
55
  if (ref && typeof ref === 'object' && 'current' in ref) {
446
- return ref as React.RefObject<HTMLFormElement>;
56
+ return ref as React.RefObject<HTMLFormElement | null>;
447
57
  }
448
58
  return internalRef;
449
59
  }, [ref]);
@@ -614,13 +224,21 @@ Select.displayName = "Select";
614
224
  // TRIGGER COMPONENT
615
225
  // ============================================================================
616
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
+ */
617
235
  export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerProps>(
618
236
  ({ children, className, variant = "outline", size = "default", asChild = false, ...props }, ref) => {
619
237
  const { open, disabled, value, actions, direction = 'down' } = useSelectContext();
620
238
  const opensUpward = direction === 'up';
621
239
 
622
240
  // Use ref to store the latest handleClick to avoid re-creating the effect
623
- const handleClickRef = React.useRef<(e: React.MouseEvent) => void>();
241
+ const handleClickRef = React.useRef<(e: React.MouseEvent) => void>(undefined);
624
242
 
625
243
  const handleClick = React.useCallback((e: React.MouseEvent) => {
626
244
  if (disabled) {
@@ -687,22 +305,26 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
687
305
  };
688
306
 
689
307
  if (asChild) {
690
- const childChildren = React.Children.toArray((children as React.ReactElement).props.children);
691
- const hasChevron = childChildren.some(child =>
692
- React.isValidElement(child) &&
693
- (child.type === ChevronDown ||
694
- (child.type === 'svg' && child.props['data-testid'] === 'chevron-down') ||
695
- (typeof child === 'object' && 'type' in child && typeof child.type === 'function' && child.type.name === 'ChevronDown'))
696
- );
308
+ const childElement = children as React.ReactElement<{ children?: React.ReactNode; [key: string]: unknown }>;
309
+ const childChildren = React.Children.toArray(childElement.props.children);
310
+ const hasChevron = childChildren.some(child => {
311
+ if (React.isValidElement(child)) {
312
+ const childProps = child.props as { 'data-testid'?: string; [key: string]: unknown };
313
+ return child.type === ChevronDown ||
314
+ (child.type === 'svg' && childProps['data-testid'] === 'chevron-down') ||
315
+ (typeof child === 'object' && 'type' in child && typeof child.type === 'function' && child.type.name === 'ChevronDown');
316
+ }
317
+ return false;
318
+ });
697
319
 
698
320
  // Merge child's className with triggerProps className
699
- const childClassName = (children as React.ReactElement).props.className;
321
+ const childClassName = (children as React.ReactElement<any>).props.className;
700
322
  const mergedClassName = cn(
701
323
  triggerProps.className,
702
324
  childClassName
703
325
  );
704
326
 
705
- return React.cloneElement(children as React.ReactElement, {
327
+ return React.cloneElement(children as React.ReactElement<any>, {
706
328
  ...triggerProps,
707
329
  className: mergedClassName,
708
330
  children: hasChevron ? childChildren : [
@@ -776,6 +398,14 @@ SelectTrigger.displayName = "SelectTrigger";
776
398
  // VALUE COMPONENT
777
399
  // ============================================================================
778
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
+ */
779
409
  export const SelectValue = React.forwardRef<HTMLSpanElement, SelectValueProps>(
780
410
  ({ placeholder = "Select an option...", children }, ref) => {
781
411
  const { selectedText } = useSelectContext();
@@ -798,6 +428,14 @@ SelectValue.displayName = "SelectValue";
798
428
  // CONTENT COMPONENT
799
429
  // ============================================================================
800
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
+ */
801
439
  export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentProps>(
802
440
  ({
803
441
  children,
@@ -906,24 +544,19 @@ SelectContent.displayName = "SelectContent";
906
544
  // ITEM COMPONENT
907
545
  // ============================================================================
908
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
+ */
909
555
  export const SelectItem = React.forwardRef<HTMLLIElement, SelectItemProps>(
910
556
  ({ value, children, disabled = false, className, onClick }, ref) => {
911
557
  const { value: selectedValue, actions, registerItem, unregisterItem } = useSelectContext();
912
558
  const isSelected = selectedValue === value;
913
-
914
- // Extract text content from children for display
915
- const getTextContent = (children: React.ReactNode): string => {
916
- if (typeof children === 'string') return children;
917
- if (typeof children === 'number') return children.toString();
918
- if (React.isValidElement(children) && children.props.children) {
919
- return getTextContent(children.props.children);
920
- }
921
- if (Array.isArray(children)) {
922
- return children.map(getTextContent).join('');
923
- }
924
- return '';
925
- };
926
-
559
+
927
560
  const itemText = getTextContent(children);
928
561
 
929
562
  // Register this item when it mounts or text changes
@@ -1014,6 +647,14 @@ SelectItem.displayName = "SelectItem";
1014
647
  // ADDITIONAL COMPONENTS (for backward compatibility)
1015
648
  // ============================================================================
1016
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
+ */
1017
658
  export const SelectGroup = React.forwardRef<HTMLDivElement, { children: React.ReactNode; className?: string }>(
1018
659
  ({ children, className }, ref) => {
1019
660
  return (
@@ -1025,6 +666,14 @@ export const SelectGroup = React.forwardRef<HTMLDivElement, { children: React.Re
1025
666
  );
1026
667
  SelectGroup.displayName = "SelectGroup";
1027
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
+ */
1028
677
  export const SelectLabel = React.forwardRef<HTMLDivElement, { children: React.ReactNode; className?: string }>(
1029
678
  ({ children, className }, ref) => {
1030
679
  return (
@@ -1036,6 +685,14 @@ export const SelectLabel = React.forwardRef<HTMLDivElement, { children: React.Re
1036
685
  );
1037
686
  SelectLabel.displayName = "SelectLabel";
1038
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
+ */
1039
696
  export const SelectSeparator = React.forwardRef<HTMLDivElement, { className?: string }>(
1040
697
  ({ className }, ref) => {
1041
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
+ };