@jmruthers/pace-core 0.2.4
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.
- package/CHANGELOG.md +202 -0
- package/README.md +299 -0
- package/dist/DataTable-BHlzyKZP.d.ts +116 -0
- package/dist/DataTable-GEY5U7OI.js +100 -0
- package/dist/DataTable-GEY5U7OI.js.map +1 -0
- package/dist/PublicLoadingSpinner-DztrzuJr.d.ts +3430 -0
- package/dist/UnifiedAuthProvider-w66zSCUf.d.ts +160 -0
- package/dist/api-GZHIDA4X.js +41 -0
- package/dist/api-GZHIDA4X.js.map +1 -0
- package/dist/appConfig-BVGyuvI7.d.ts +71 -0
- package/dist/appNameResolver-7GHF5ED2.js +22 -0
- package/dist/appNameResolver-7GHF5ED2.js.map +1 -0
- package/dist/audit-BUW3LMJB.js +16 -0
- package/dist/audit-BUW3LMJB.js.map +1 -0
- package/dist/chunk-22KLBHPS.js +29 -0
- package/dist/chunk-22KLBHPS.js.map +1 -0
- package/dist/chunk-24MKLB7U.js +81 -0
- package/dist/chunk-24MKLB7U.js.map +1 -0
- package/dist/chunk-2MKP6IYD.js +388 -0
- package/dist/chunk-2MKP6IYD.js.map +1 -0
- package/dist/chunk-2V3Y6YBC.js +114 -0
- package/dist/chunk-2V3Y6YBC.js.map +1 -0
- package/dist/chunk-5CDJCTOO.js +190 -0
- package/dist/chunk-5CDJCTOO.js.map +1 -0
- package/dist/chunk-6ZQVSHKL.js +1345 -0
- package/dist/chunk-6ZQVSHKL.js.map +1 -0
- package/dist/chunk-74C6SNEC.js +77 -0
- package/dist/chunk-74C6SNEC.js.map +1 -0
- package/dist/chunk-7BNPOCLL.js +178 -0
- package/dist/chunk-7BNPOCLL.js.map +1 -0
- package/dist/chunk-7JL3T7BO.js +3344 -0
- package/dist/chunk-7JL3T7BO.js.map +1 -0
- package/dist/chunk-CDQ3PX7L.js +18 -0
- package/dist/chunk-CDQ3PX7L.js.map +1 -0
- package/dist/chunk-DY5E3AT7.js +1734 -0
- package/dist/chunk-DY5E3AT7.js.map +1 -0
- package/dist/chunk-ETEJVKYK.js +6032 -0
- package/dist/chunk-ETEJVKYK.js.map +1 -0
- package/dist/chunk-I5Z3QH5X.js +32 -0
- package/dist/chunk-I5Z3QH5X.js.map +1 -0
- package/dist/chunk-MZBUOP4P.js +119 -0
- package/dist/chunk-MZBUOP4P.js.map +1 -0
- package/dist/chunk-N2EUGZRW.js +98 -0
- package/dist/chunk-N2EUGZRW.js.map +1 -0
- package/dist/chunk-NQ4TOOO6.js +20 -0
- package/dist/chunk-NQ4TOOO6.js.map +1 -0
- package/dist/chunk-OHXGNT3K.js +21 -0
- package/dist/chunk-OHXGNT3K.js.map +1 -0
- package/dist/chunk-OKXMUYIB.js +522 -0
- package/dist/chunk-OKXMUYIB.js.map +1 -0
- package/dist/chunk-PFRRIDYA.js +382 -0
- package/dist/chunk-PFRRIDYA.js.map +1 -0
- package/dist/chunk-PLDDJCW6.js +49 -0
- package/dist/chunk-PLDDJCW6.js.map +1 -0
- package/dist/chunk-SS3E6QLB.js +695 -0
- package/dist/chunk-SS3E6QLB.js.map +1 -0
- package/dist/chunk-TMRLB2LA.js +326 -0
- package/dist/chunk-TMRLB2LA.js.map +1 -0
- package/dist/chunk-WYB6MBZA.js +5533 -0
- package/dist/chunk-WYB6MBZA.js.map +1 -0
- package/dist/chunk-YDJW5XTN.js +84 -0
- package/dist/chunk-YDJW5XTN.js.map +1 -0
- package/dist/components.d.ts +1308 -0
- package/dist/components.js +3759 -0
- package/dist/components.js.map +1 -0
- package/dist/database-C3Szpi5J.d.ts +470 -0
- package/dist/hooks.d.ts +449 -0
- package/dist/hooks.js +612 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +385 -0
- package/dist/index.js +569 -0
- package/dist/index.js.map +1 -0
- package/dist/organisation-CO3Sh3_D.d.ts +99 -0
- package/dist/providers.d.ts +45 -0
- package/dist/providers.js +36 -0
- package/dist/providers.js.map +1 -0
- package/dist/rbac/eslint-rules.d.ts +52 -0
- package/dist/rbac/eslint-rules.js +252 -0
- package/dist/rbac/eslint-rules.js.map +1 -0
- package/dist/rbac/index.d.ts +1918 -0
- package/dist/rbac/index.js +2212 -0
- package/dist/rbac/index.js.map +1 -0
- package/dist/styles/core.css +401 -0
- package/dist/styles/fonts/georama-italic.woff2 +0 -0
- package/dist/styles/fonts/georama.woff2 +0 -0
- package/dist/styles/fonts/open-sans-italic.woff2 +0 -0
- package/dist/styles/fonts/open-sans.woff2 +0 -0
- package/dist/styles/fonts/reddit-mono.woff2 +0 -0
- package/dist/styles/index.d.ts +36 -0
- package/dist/styles/index.js +24 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/theming/runtime.d.ts +73 -0
- package/dist/theming/runtime.js +16 -0
- package/dist/theming/runtime.js.map +1 -0
- package/dist/types-CInEi-ng.d.ts +316 -0
- package/dist/types.d.ts +196 -0
- package/dist/types.js +83 -0
- package/dist/types.js.map +1 -0
- package/dist/unified-CM7T0aTK.d.ts +198 -0
- package/dist/useComponentPerformance-DE9l5RkL.d.ts +11 -0
- package/dist/usePublicRouteParams-B6i0KtXW.d.ts +477 -0
- package/dist/utils.d.ts +639 -0
- package/dist/utils.js +1103 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation-PM_iOaTI.d.ts +159 -0
- package/dist/validation.d.ts +138 -0
- package/dist/validation.js +477 -0
- package/dist/validation.js.map +1 -0
- package/docs/INDEX.md +192 -0
- package/docs/README.md +165 -0
- package/docs/api/.nojekyll +1 -0
- package/docs/api/README.md +301 -0
- package/docs/api/classes/ErrorBoundary.md +144 -0
- package/docs/api/classes/PublicErrorBoundary.md +132 -0
- package/docs/api/interfaces/AggregateConfig.md +43 -0
- package/docs/api/interfaces/ButtonProps.md +53 -0
- package/docs/api/interfaces/CardProps.md +40 -0
- package/docs/api/interfaces/ColorPalette.md +7 -0
- package/docs/api/interfaces/ColorShade.md +41 -0
- package/docs/api/interfaces/DataTableAction.md +200 -0
- package/docs/api/interfaces/DataTableColumn.md +300 -0
- package/docs/api/interfaces/DataTableProps.md +517 -0
- package/docs/api/interfaces/DataTableToolbarButton.md +96 -0
- package/docs/api/interfaces/EmptyStateConfig.md +61 -0
- package/docs/api/interfaces/EventContextType.md +96 -0
- package/docs/api/interfaces/EventLogoProps.md +152 -0
- package/docs/api/interfaces/EventProviderProps.md +19 -0
- package/docs/api/interfaces/FileSizeLimits.md +7 -0
- package/docs/api/interfaces/FileUploadProps.md +154 -0
- package/docs/api/interfaces/FooterProps.md +105 -0
- package/docs/api/interfaces/InactivityWarningModalProps.md +115 -0
- package/docs/api/interfaces/InputProps.md +53 -0
- package/docs/api/interfaces/LabelProps.md +107 -0
- package/docs/api/interfaces/LoginFormProps.md +184 -0
- package/docs/api/interfaces/NavigationItem.md +176 -0
- package/docs/api/interfaces/NavigationMenuProps.md +236 -0
- package/docs/api/interfaces/Organisation.md +140 -0
- package/docs/api/interfaces/OrganisationContextType.md +377 -0
- package/docs/api/interfaces/OrganisationMembership.md +140 -0
- package/docs/api/interfaces/OrganisationProviderProps.md +19 -0
- package/docs/api/interfaces/OrganisationSecurityError.md +62 -0
- package/docs/api/interfaces/PaceAppLayoutProps.md +393 -0
- package/docs/api/interfaces/PaceLoginPageProps.md +34 -0
- package/docs/api/interfaces/PaletteData.md +41 -0
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +94 -0
- package/docs/api/interfaces/PublicErrorBoundaryState.md +68 -0
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +86 -0
- package/docs/api/interfaces/PublicPageFooterProps.md +112 -0
- package/docs/api/interfaces/PublicPageHeaderProps.md +138 -0
- package/docs/api/interfaces/PublicPageLayoutProps.md +138 -0
- package/docs/api/interfaces/StorageConfig.md +41 -0
- package/docs/api/interfaces/StorageFileInfo.md +74 -0
- package/docs/api/interfaces/StorageFileMetadata.md +140 -0
- package/docs/api/interfaces/StorageListOptions.md +86 -0
- package/docs/api/interfaces/StorageListResult.md +41 -0
- package/docs/api/interfaces/StorageUploadOptions.md +88 -0
- package/docs/api/interfaces/StorageUploadResult.md +63 -0
- package/docs/api/interfaces/StorageUrlOptions.md +47 -0
- package/docs/api/interfaces/StyleImport.md +19 -0
- package/docs/api/interfaces/ToastActionElement.md +9 -0
- package/docs/api/interfaces/ToastProps.md +9 -0
- package/docs/api/interfaces/UnifiedAuthContextType.md +1108 -0
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +171 -0
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +136 -0
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +123 -0
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +87 -0
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +81 -0
- package/docs/api/interfaces/UsePublicEventOptions.md +34 -0
- package/docs/api/interfaces/UsePublicEventReturn.md +68 -0
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +94 -0
- package/docs/api/interfaces/UserEventAccess.md +118 -0
- package/docs/api/interfaces/UserMenuProps.md +86 -0
- package/docs/api/interfaces/UserProfile.md +63 -0
- package/docs/api/modules.md +4153 -0
- package/docs/api-reference/components.md +1623 -0
- package/docs/api-reference/hooks.md +627 -0
- package/docs/api-reference/providers.md +487 -0
- package/docs/api-reference/types.md +1005 -0
- package/docs/api-reference/utilities.md +1104 -0
- package/docs/app.css.example +53 -0
- package/docs/architecture/README.md +577 -0
- package/docs/best-practices/README.md +400 -0
- package/docs/best-practices/deployment.md +1042 -0
- package/docs/best-practices/performance.md +789 -0
- package/docs/best-practices/security.md +881 -0
- package/docs/best-practices/testing.md +981 -0
- package/docs/consuming-app-example.md +290 -0
- package/docs/consuming-app-vite-config.md +233 -0
- package/docs/core-concepts/authentication.md +98 -0
- package/docs/core-concepts/events.md +756 -0
- package/docs/core-concepts/organisations.md +790 -0
- package/docs/core-concepts/permissions.md +729 -0
- package/docs/core-concepts/rbac-system.md +233 -0
- package/docs/database-schema-requirements.md +172 -0
- package/docs/documentation-style-checklist.md +294 -0
- package/docs/examples/navigation-menu-auth-fix.md +344 -0
- package/docs/getting-started/examples/README.md +106 -0
- package/docs/getting-started/examples/basic-auth-app.md +521 -0
- package/docs/getting-started/examples/full-featured-app.md +616 -0
- package/docs/getting-started/installation.md +269 -0
- package/docs/getting-started/quick-start.md +401 -0
- package/docs/implementation-guides/app-layout.md +983 -0
- package/docs/implementation-guides/data-tables.md +1898 -0
- package/docs/implementation-guides/dynamic-colors.md +195 -0
- package/docs/implementation-guides/forms.md +578 -0
- package/docs/implementation-guides/hierarchical-datatable.md +850 -0
- package/docs/implementation-guides/large-datasets.md +281 -0
- package/docs/implementation-guides/navigation.md +844 -0
- package/docs/implementation-guides/performance.md +403 -0
- package/docs/implementation-guides/permission-enforcement.md +764 -0
- package/docs/implementation-guides/public-pages.md +752 -0
- package/docs/migration/README.md +493 -0
- package/docs/migration/organisation-context-timing-fix.md +217 -0
- package/docs/migration/quick-migration-guide.md +320 -0
- package/docs/migration/rbac-migration.md +571 -0
- package/docs/migration/v0.4.15-tailwind-scanning.md +272 -0
- package/docs/migration/v0.4.16-css-first-approach.md +306 -0
- package/docs/migration/v0.4.17-source-path-fix.md +229 -0
- package/docs/migration-guide.md +168 -0
- package/docs/performance/README.md +551 -0
- package/docs/print-components/README.md +258 -0
- package/docs/print-components/api-reference.md +636 -0
- package/docs/print-components/examples/README.md +204 -0
- package/docs/print-components/examples/basic-report.tsx +92 -0
- package/docs/print-components/examples/card-catalog.tsx +149 -0
- package/docs/print-components/examples/cover-page-report.tsx +163 -0
- package/docs/print-components/quick-start.md +363 -0
- package/docs/quick-reference.md +576 -0
- package/docs/rbac/README.md +265 -0
- package/docs/rbac/advanced-patterns.md +776 -0
- package/docs/rbac/api-reference.md +1033 -0
- package/docs/rbac/examples.md +883 -0
- package/docs/rbac/getting-started.md +679 -0
- package/docs/rbac/quick-start.md +619 -0
- package/docs/rbac/super-admin-guide.md +592 -0
- package/docs/rbac/troubleshooting.md +316 -0
- package/docs/security/README.md +680 -0
- package/docs/security/checklist.md +343 -0
- package/docs/style-guide.md +522 -0
- package/docs/styles/README.md +319 -0
- package/docs/testing/README.md +874 -0
- package/docs/troubleshooting/README.md +497 -0
- package/docs/troubleshooting/common-issues.md +1563 -0
- package/docs/troubleshooting/database-view-compatibility.md +119 -0
- package/docs/troubleshooting/debugging.md +1117 -0
- package/docs/troubleshooting/migration.md +918 -0
- package/docs/troubleshooting/organisation-context-setup.md +277 -0
- package/docs/troubleshooting/react-hooks-issue-analysis.md +166 -0
- package/docs/troubleshooting/styling-issues.md +219 -0
- package/docs/troubleshooting/tailwind-content-scanning.md +213 -0
- package/docs/usage.md +175 -0
- package/docs/visual-testing.md +114 -0
- package/package.json +211 -0
- package/src/__mocks__/lucide-react.ts +181 -0
- package/src/__tests__/README.md +404 -0
- package/src/__tests__/debug-provider.unit.test.tsx +67 -0
- package/src/__tests__/e2e/workflows.test.tsx +373 -0
- package/src/__tests__/hybridPermissions.unit.test.tsx +474 -0
- package/src/__tests__/index.integration.test.ts +491 -0
- package/src/__tests__/mocks/MockAuthProvider-standalone.tsx +47 -0
- package/src/__tests__/mocks/MockAuthProvider.tsx +63 -0
- package/src/__tests__/mocks/enhancedSupabaseMock.ts +252 -0
- package/src/__tests__/mocks/index.test.ts +23 -0
- package/src/__tests__/mocks/index.ts +16 -0
- package/src/__tests__/mocks/mockAuth.ts +155 -0
- package/src/__tests__/mocks/mockSupabase.ts +83 -0
- package/src/__tests__/mocks/mockSupabaseClient.ts +63 -0
- package/src/__tests__/mocks/providers.tsx +22 -0
- package/src/__tests__/patterns/__tests__/testPatterns.test.ts +394 -0
- package/src/__tests__/patterns/testPatterns.ts +124 -0
- package/src/__tests__/performance/componentPerformance.performance.test.ts +27 -0
- package/src/__tests__/performance/index.ts +24 -0
- package/src/__tests__/performance/performanceValidation.performance.test.ts +15 -0
- package/src/__tests__/security/security.unit.test.tsx +7 -0
- package/src/__tests__/security/securityValidation.security.test.tsx +153 -0
- package/src/__tests__/setup.ts +259 -0
- package/src/__tests__/setupTests.d.ts +1 -0
- package/src/__tests__/shared/componentTestUtils.tsx +475 -0
- package/src/__tests__/shared/errorHandlingTestUtils.ts +107 -0
- package/src/__tests__/shared/index.ts +81 -0
- package/src/__tests__/shared/integrationTestUtils.tsx +375 -0
- package/src/__tests__/shared/performanceTestUtils.tsx +476 -0
- package/src/__tests__/shared/testUtils.optimized.tsx +627 -0
- package/src/__tests__/simple.test.tsx +20 -0
- package/src/__tests__/templates/accessibility.test.template.tsx +279 -0
- package/src/__tests__/templates/component.test.template.tsx +122 -0
- package/src/__tests__/templates/integration.test.template.tsx +199 -0
- package/src/__tests__/test-utils/dataFactories.ts +60 -0
- package/src/__tests__/test-utils/index.ts +6 -0
- package/src/__tests__/typeSafety.unit.test.ts +65 -0
- package/src/__tests__/unifiedAuth.unit.test.tsx +151 -0
- package/src/__tests__/utils/accessibilityHelpers.ts +254 -0
- package/src/__tests__/utils/assertions.ts +50 -0
- package/src/__tests__/utils/deterministicHelpers.ts +31 -0
- package/src/__tests__/utils/edgeCaseConfig.test.ts +75 -0
- package/src/__tests__/utils/edgeCaseConfig.ts +98 -0
- package/src/__tests__/utils/mockHelpers.ts +149 -0
- package/src/__tests__/utils/mockLoader.ts +101 -0
- package/src/__tests__/utils/performanceHelpers.ts +55 -0
- package/src/__tests__/utils/performanceTestHelpers.ts +68 -0
- package/src/__tests__/utils/testDataFactories.ts +28 -0
- package/src/__tests__/utils/testIsolation.ts +67 -0
- package/src/__tests__/utils/visualTestHelpers.ts +20 -0
- package/src/__tests__/visual/__snapshots__/componentSnapshots.visual.test.tsx.snap +68 -0
- package/src/__tests__/visual/__snapshots__/componentVisuals.visual.test.tsx.snap +14 -0
- package/src/__tests__/visual/__snapshots__/visualRegression.test.tsx.snap +217 -0
- package/src/__tests__/visual/__snapshots__/visualRegression.visual.test.tsx.snap +24 -0
- package/src/__tests__/visual/componentSnapshots.visual.test.tsx +33 -0
- package/src/__tests__/visual/componentVisuals.visual.test.tsx +12 -0
- package/src/__tests__/visual/visualRegression.visual.test.tsx +20 -0
- package/src/components/Alert/Alert.tsx +134 -0
- package/src/components/Alert/__tests__/Alert.unit.test.tsx +381 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/Avatar/Avatar.tsx +84 -0
- package/src/components/Avatar/__tests__/Avatar.unit.test.tsx +232 -0
- package/src/components/Avatar/index.ts +2 -0
- package/src/components/Button/Button.tsx +270 -0
- package/src/components/Button/__tests__/Button.accessibility.test.tsx +131 -0
- package/src/components/Button/__tests__/Button.comprehensive.test.tsx +721 -0
- package/src/components/Button/__tests__/Button.unit.test.tsx +189 -0
- package/src/components/Button/__tests__/EventSelector.integration.test.tsx +285 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/Card/Card.tsx +271 -0
- package/src/components/Card/__tests__/Card.accessibility.test.tsx +394 -0
- package/src/components/Card/__tests__/Card.comprehensive.test.tsx +599 -0
- package/src/components/Card/__tests__/Card.integration.test.tsx +673 -0
- package/src/components/Card/__tests__/Card.performance.test.tsx +546 -0
- package/src/components/Card/__tests__/Card.unit.test.tsx +330 -0
- package/src/components/Card/__tests__/Card.visual.test.tsx +599 -0
- package/src/components/Card/__tests__/README.md +211 -0
- package/src/components/Card/index.ts +1 -0
- package/src/components/Checkbox/Checkbox.tsx +75 -0
- package/src/components/Checkbox/__mocks__/Checkbox.tsx +2 -0
- package/src/components/Checkbox/__tests__/Checkbox.unit.test.tsx +520 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/DataTable/DataTable.tsx +440 -0
- package/src/components/DataTable/__tests__/DataTable.autoSizing.test.tsx +526 -0
- package/src/components/DataTable/__tests__/DataTable.errorHandling.test.tsx +259 -0
- package/src/components/DataTable/__tests__/DataTable.hierarchical.test.tsx +675 -0
- package/src/components/DataTable/__tests__/DataTable.infinite-loop.test.tsx +324 -0
- package/src/components/DataTable/__tests__/DataTable.integration.test.tsx +724 -0
- package/src/components/DataTable/__tests__/DataTable.performance.test.tsx +597 -0
- package/src/components/DataTable/__tests__/DataTable.permissions.test.tsx +306 -0
- package/src/components/DataTable/__tests__/DataTable.regressionFixes.test.tsx +546 -0
- package/src/components/DataTable/__tests__/DataTable.selection.controlled.test.tsx +386 -0
- package/src/components/DataTable/__tests__/DataTable.selection.test.tsx +338 -0
- package/src/components/DataTable/__tests__/DataTable.userWorkflows.test.tsx +310 -0
- package/src/components/DataTable/__tests__/DataTable.workflowValidation.test.tsx +489 -0
- package/src/components/DataTable/__tests__/DataTable.workflows.test.tsx +701 -0
- package/src/components/DataTable/__tests__/README.md +136 -0
- package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +66 -0
- package/src/components/DataTable/__tests__/performance-regression.test.tsx +788 -0
- package/src/components/DataTable/__tests__/performance.test.tsx +365 -0
- package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +103 -0
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +382 -0
- package/src/components/DataTable/__tests__/test-utils.ts +94 -0
- package/src/components/DataTable/components/ActionButtons.tsx +177 -0
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +160 -0
- package/src/components/DataTable/components/ColumnFilter.tsx +114 -0
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +100 -0
- package/src/components/DataTable/components/DataTableBody.tsx +462 -0
- package/src/components/DataTable/components/DataTableCore.tsx +869 -0
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +214 -0
- package/src/components/DataTable/components/DataTableHeader.tsx +31 -0
- package/src/components/DataTable/components/DataTableModals.tsx +87 -0
- package/src/components/DataTable/components/DataTableToolbar.tsx +251 -0
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +148 -0
- package/src/components/DataTable/components/EditableRow.tsx +160 -0
- package/src/components/DataTable/components/EmptyState.tsx +64 -0
- package/src/components/DataTable/components/ExpandButton.tsx +113 -0
- package/src/components/DataTable/components/FilterRow.tsx +101 -0
- package/src/components/DataTable/components/GroupHeader.tsx +42 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +96 -0
- package/src/components/DataTable/components/ImportModal.tsx +345 -0
- package/src/components/DataTable/components/LoadingState.tsx +12 -0
- package/src/components/DataTable/components/PaginationControls.tsx +332 -0
- package/src/components/DataTable/components/UnifiedTableBody.tsx +911 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +68 -0
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +593 -0
- package/src/components/DataTable/components/__tests__/ActionButtons.unit.test.tsx +150 -0
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +224 -0
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.unit.test.tsx +244 -0
- package/src/components/DataTable/components/__tests__/DataTable.accessibility.test.tsx +523 -0
- package/src/components/DataTable/components/__tests__/DataTable.integration.test.tsx +401 -0
- package/src/components/DataTable/components/__tests__/DataTable.performance.test.tsx +161 -0
- package/src/components/DataTable/components/__tests__/DataTable.real.test.tsx +251 -0
- package/src/components/DataTable/components/__tests__/DataTable.security.test.tsx +172 -0
- package/src/components/DataTable/components/__tests__/DataTable.unit.test.tsx +290 -0
- package/src/components/DataTable/components/__tests__/DataTableBody.unit.test.tsx +147 -0
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.unit.test.tsx +182 -0
- package/src/components/DataTable/components/__tests__/DataTableHeader.unit.test.tsx +143 -0
- package/src/components/DataTable/components/__tests__/DataTableModals.unit.test.tsx +123 -0
- package/src/components/DataTable/components/__tests__/EditableRow.unit.test.tsx +660 -0
- package/src/components/DataTable/components/__tests__/EmptyState.unit.test.tsx +256 -0
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +498 -0
- package/src/components/DataTable/components/__tests__/FilterRow.unit.test.tsx +112 -0
- package/src/components/DataTable/components/__tests__/FilteringToggle.unit.test.tsx +130 -0
- package/src/components/DataTable/components/__tests__/GroupHeader.unit.test.tsx +172 -0
- package/src/components/DataTable/components/__tests__/GroupingDropdown.unit.test.tsx +222 -0
- package/src/components/DataTable/components/__tests__/ImportModal.unit.test.tsx +780 -0
- package/src/components/DataTable/components/__tests__/LoadingState.unit.test.tsx +65 -0
- package/src/components/DataTable/components/__tests__/PaginationControls.unit.test.tsx +634 -0
- package/src/components/DataTable/components/__tests__/StateComponents.unit.test.tsx +48 -0
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.hierarchical.test.tsx +541 -0
- package/src/components/DataTable/components/__tests__/ViewRowModal.unit.test.tsx +228 -0
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.unit.test.tsx +568 -0
- package/src/components/DataTable/components/index.ts +17 -0
- package/src/components/DataTable/context/DataTableContext.tsx +97 -0
- package/src/components/DataTable/core/ActionManager.ts +235 -0
- package/src/components/DataTable/core/ColumnFactory.ts +268 -0
- package/src/components/DataTable/core/ColumnManager.ts +205 -0
- package/src/components/DataTable/core/DataManager.ts +188 -0
- package/src/components/DataTable/core/DataTableContext.tsx +182 -0
- package/src/components/DataTable/core/LocalDataAdapter.ts +264 -0
- package/src/components/DataTable/core/PluginRegistry.ts +229 -0
- package/src/components/DataTable/core/StateManager.ts +311 -0
- package/src/components/DataTable/core/__tests__/ActionManager.unit.test.ts +405 -0
- package/src/components/DataTable/core/__tests__/ArchitectureIntegration.unit.test.tsx +445 -0
- package/src/components/DataTable/core/__tests__/ColumnFactory.unit.test.ts +288 -0
- package/src/components/DataTable/core/__tests__/ColumnManager.unit.test.ts +623 -0
- package/src/components/DataTable/core/__tests__/DataManager.unit.test.ts +431 -0
- package/src/components/DataTable/core/__tests__/DataTableContext.unit.test.tsx +433 -0
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.unit.test.ts +422 -0
- package/src/components/DataTable/core/__tests__/PluginRegistry.unit.test.tsx +207 -0
- package/src/components/DataTable/core/__tests__/StateManager.unit.test.ts +278 -0
- package/src/components/DataTable/core/index.ts +8 -0
- package/src/components/DataTable/core/interfaces.ts +338 -0
- package/src/components/DataTable/examples/AutoSizingExample.tsx +180 -0
- package/src/components/DataTable/examples/ColumnSizingComparison.tsx +235 -0
- package/src/components/DataTable/examples/HierarchicalActionsExample.tsx +418 -0
- package/src/components/DataTable/examples/HierarchicalExample.tsx +472 -0
- package/src/components/DataTable/examples/InitialPageSizeExample.tsx +173 -0
- package/src/components/DataTable/examples/PerformanceExample.tsx +502 -0
- package/src/components/DataTable/examples/__tests__/PerformanceExample.unit.test.tsx +281 -0
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.unit.test.ts +407 -0
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.unit.test.ts +679 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +95 -0
- package/src/components/DataTable/hooks/useColumnReordering.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +325 -0
- package/src/components/DataTable/hooks/useHierarchicalState.ts +174 -0
- package/src/components/DataTable/index.ts +70 -0
- package/src/components/DataTable/styles.ts +171 -0
- package/src/components/DataTable/types.ts +475 -0
- package/src/components/DataTable/utils/__tests__/columnSizing.test.ts +237 -0
- package/src/components/DataTable/utils/__tests__/debugTools.unit.test.ts +267 -0
- package/src/components/DataTable/utils/__tests__/errorHandling.unit.test.ts +467 -0
- package/src/components/DataTable/utils/__tests__/exportUtils.unit.test.ts +380 -0
- package/src/components/DataTable/utils/__tests__/flexibleImport.unit.test.ts +233 -0
- package/src/components/DataTable/utils/__tests__/performanceUtils.unit.test.ts +414 -0
- package/src/components/DataTable/utils/columnSizing.ts +125 -0
- package/src/components/DataTable/utils/debugTools.ts +583 -0
- package/src/components/DataTable/utils/errorHandling.ts +494 -0
- package/src/components/DataTable/utils/exportUtils.ts +126 -0
- package/src/components/DataTable/utils/flexibleImport.ts +510 -0
- package/src/components/DataTable/utils/hierarchicalSorting.ts +151 -0
- package/src/components/DataTable/utils/hierarchicalUtils.ts +218 -0
- package/src/components/DataTable/utils/index.ts +1 -0
- package/src/components/DataTable/utils/performanceUtils.ts +351 -0
- package/src/components/Dialog/Dialog.tsx +782 -0
- package/src/components/Dialog/README.md +804 -0
- package/src/components/Dialog/__tests__/Dialog.accessibility.test.tsx +521 -0
- package/src/components/Dialog/__tests__/Dialog.auto-size.example.tsx +157 -0
- package/src/components/Dialog/__tests__/Dialog.enhanced.test.tsx +538 -0
- package/src/components/Dialog/__tests__/Dialog.unit.test.tsx +1373 -0
- package/src/components/Dialog/examples/BasicHtmlTest.tsx +55 -0
- package/src/components/Dialog/examples/DebugHtmlExample.tsx +68 -0
- package/src/components/Dialog/examples/HtmlDialogExample.tsx +202 -0
- package/src/components/Dialog/examples/SimpleHtmlTest.tsx +61 -0
- package/src/components/Dialog/examples/SmartDialogExample.tsx +322 -0
- package/src/components/Dialog/examples/__tests__/SmartDialogExample.unit.test.tsx +151 -0
- package/src/components/Dialog/index.ts +12 -0
- package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
- package/src/components/Dialog/utils/safeHtml.ts +185 -0
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +312 -0
- package/src/components/ErrorBoundary/__tests__/ErrorBoundary.accessibility.test.tsx +517 -0
- package/src/components/ErrorBoundary/__tests__/ErrorBoundary.integration.test.tsx +572 -0
- package/src/components/ErrorBoundary/__tests__/ErrorBoundary.unit.test.tsx +579 -0
- package/src/components/ErrorBoundary/index.ts +8 -0
- package/src/components/EventSelector/EventSelector.tsx +360 -0
- package/src/components/EventSelector/__tests__/EventSelector.test.tsx +528 -0
- package/src/components/EventSelector/index.ts +3 -0
- package/src/components/EventSelector/types.ts +79 -0
- package/src/components/FileUpload/FileUpload.example.tsx +218 -0
- package/src/components/FileUpload/FileUpload.tsx +237 -0
- package/src/components/FileUpload/__tests__/FileUpload.integration.test.tsx +992 -0
- package/src/components/FileUpload/__tests__/FileUpload.real.test.tsx +927 -0
- package/src/components/FileUpload/__tests__/FileUpload.test.tsx +855 -0
- package/src/components/FileUpload/__tests__/FileUpload.unit.test.tsx +1311 -0
- package/src/components/FileUpload/__tests__/FileUpload.unmocked.test.tsx +937 -0
- package/src/components/FileUpload/index.ts +6 -0
- package/src/components/Footer/Footer.tsx +197 -0
- package/src/components/Footer/__tests__/Footer.accessibility.test.tsx +359 -0
- package/src/components/Footer/__tests__/Footer.integration.test.tsx +353 -0
- package/src/components/Footer/__tests__/Footer.performance.test.tsx +309 -0
- package/src/components/Footer/__tests__/Footer.unit.test.tsx +309 -0
- package/src/components/Footer/__tests__/Footer.visual.test.tsx +335 -0
- package/src/components/Footer/index.ts +17 -0
- package/src/components/Form/Form.tsx +166 -0
- package/src/components/Form/FormErrorSummary.tsx +113 -0
- package/src/components/Form/FormField.tsx +249 -0
- package/src/components/Form/FormFieldset.tsx +127 -0
- package/src/components/Form/FormLiveRegion.tsx +198 -0
- package/src/components/Form/__tests__/Form.accessibility.test.tsx +820 -0
- package/src/components/Form/__tests__/Form.unit.test.tsx +305 -0
- package/src/components/Form/__tests__/FormErrorSummary.unit.test.tsx +285 -0
- package/src/components/Form/__tests__/FormFieldset.unit.test.tsx +241 -0
- package/src/components/Form/index.ts +26 -0
- package/src/components/Header/Header.tsx +301 -0
- package/src/components/Header/__tests__/Header.accessibility.test.tsx +382 -0
- package/src/components/Header/__tests__/Header.comprehensive.test.tsx +509 -0
- package/src/components/Header/__tests__/Header.unit.test.tsx +335 -0
- package/src/components/Header/index.ts +4 -0
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +196 -0
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +164 -0
- package/src/components/InactivityWarningModal/__tests__/InactivityWarningModal.unit.test.tsx +224 -0
- package/src/components/InactivityWarningModal/index.ts +9 -0
- package/src/components/Input/Input.tsx +201 -0
- package/src/components/Input/__mocks__/Input.tsx +2 -0
- package/src/components/Input/__tests__/Input.accessibility.test.tsx +632 -0
- package/src/components/Input/__tests__/Input.unit.test.tsx +1121 -0
- package/src/components/Input/index.ts +9 -0
- package/src/components/Label/Label.tsx +186 -0
- package/src/components/Label/__tests__/Label.accessibility.test.tsx +239 -0
- package/src/components/Label/__tests__/Label.unit.test.tsx +331 -0
- package/src/components/Label/index.ts +2 -0
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +98 -0
- package/src/components/LoadingSpinner/__tests__/LoadingSpinner.accessibility.test.tsx +116 -0
- package/src/components/LoadingSpinner/__tests__/LoadingSpinner.unit.test.tsx +144 -0
- package/src/components/LoadingSpinner/index.ts +3 -0
- package/src/components/LoginForm/LoginForm.tsx +273 -0
- package/src/components/LoginForm/__tests__/LoginForm.accessibility.test.tsx +201 -0
- package/src/components/LoginForm/__tests__/LoginForm.unit.test.tsx +119 -0
- package/src/components/LoginForm/index.ts +3 -0
- package/src/components/NavigationMenu/NavigationMenu.tsx +698 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.accessibility.test.tsx +378 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.enhanced.test.tsx +768 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.integration.test.tsx +576 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.performance.test.tsx +585 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.real.component.test.tsx +783 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.security.enhanced.test.tsx +810 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.security.test.tsx +494 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.unit.test.tsx +331 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.userWorkflows.test.tsx +347 -0
- package/src/components/NavigationMenu/__tests__/NavigationMenu.workflows.test.tsx +584 -0
- package/src/components/NavigationMenu/index.ts +10 -0
- package/src/components/NavigationMenu/types.ts +85 -0
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +304 -0
- package/src/components/OrganisationSelector/__tests__/OrganisationSelector.unit.test.tsx +664 -0
- package/src/components/OrganisationSelector/index.ts +9 -0
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +699 -0
- package/src/components/PaceAppLayout/README.md +278 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +288 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +889 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +629 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +782 -0
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +904 -0
- package/src/components/PaceAppLayout/index.ts +1 -0
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +221 -0
- package/src/components/PaceLoginPage/__tests__/PaceLoginPage.accessibility.test.tsx +463 -0
- package/src/components/PaceLoginPage/__tests__/PaceLoginPage.integration.test.tsx +586 -0
- package/src/components/PaceLoginPage/__tests__/PaceLoginPage.unit.test.tsx +533 -0
- package/src/components/PaceLoginPage/index.ts +1 -0
- package/src/components/PasswordReset/PasswordChangeForm.tsx +186 -0
- package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
- package/src/components/PasswordReset/__tests__/PasswordChangeForm.accessibility.test.tsx +408 -0
- package/src/components/PasswordReset/__tests__/PasswordChangeForm.unit.test.tsx +561 -0
- package/src/components/PasswordReset/__tests__/PasswordReset.integration.test.tsx +304 -0
- package/src/components/PasswordReset/__tests__/PasswordResetForm.accessibility.test.tsx +20 -0
- package/src/components/PasswordReset/__tests__/PasswordResetForm.unit.test.tsx +523 -0
- package/src/components/PasswordReset/__tests__/__mocks__/UnifiedAuthProvider.ts +29 -0
- package/src/components/PasswordReset/index.ts +4 -0
- package/src/components/Print/__tests__/Print.comprehensive.test.tsx +331 -0
- package/src/components/PrintButton/PrintButton.tsx +321 -0
- package/src/components/PrintButton/PrintButtonGroup.tsx +84 -0
- package/src/components/PrintButton/PrintToolbar.tsx +94 -0
- package/src/components/PrintButton/__tests__/PrintButton.unit.test.tsx +429 -0
- package/src/components/PrintButton/__tests__/PrintButtonGroup.unit.test.tsx +277 -0
- package/src/components/PrintButton/__tests__/PrintToolbar.unit.test.tsx +264 -0
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +438 -0
- package/src/components/PrintButton/index.ts +33 -0
- package/src/components/PrintButton/types.ts +173 -0
- package/src/components/PrintCard/PrintCard.tsx +154 -0
- package/src/components/PrintCard/PrintCardContent.tsx +57 -0
- package/src/components/PrintCard/PrintCardFooter.tsx +60 -0
- package/src/components/PrintCard/PrintCardGrid.tsx +91 -0
- package/src/components/PrintCard/PrintCardHeader.tsx +78 -0
- package/src/components/PrintCard/PrintCardImage.tsx +81 -0
- package/src/components/PrintCard/__tests__/PrintCard.unit.test.tsx +233 -0
- package/src/components/PrintCard/__tests__/PrintCardContent.test.tsx +284 -0
- package/src/components/PrintCard/__tests__/PrintCardGrid.unit.test.tsx +214 -0
- package/src/components/PrintCard/__tests__/PrintCardImage.unit.test.tsx +264 -0
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +239 -0
- package/src/components/PrintCard/index.ts +34 -0
- package/src/components/PrintCard/types.ts +171 -0
- package/src/components/PrintDataTable/PrintDataTable.tsx +215 -0
- package/src/components/PrintDataTable/PrintTableGroup.tsx +90 -0
- package/src/components/PrintDataTable/PrintTableRow.tsx +76 -0
- package/src/components/PrintDataTable/__tests__/PrintDataTable.unit.test.tsx +361 -0
- package/src/components/PrintDataTable/__tests__/PrintTableGroup.unit.test.tsx +314 -0
- package/src/components/PrintDataTable/__tests__/PrintTableRow.unit.test.tsx +362 -0
- package/src/components/PrintDataTable/index.ts +25 -0
- package/src/components/PrintDataTable/types.ts +67 -0
- package/src/components/PrintFooter/PrintFooter.tsx +183 -0
- package/src/components/PrintFooter/PrintFooterContent.tsx +71 -0
- package/src/components/PrintFooter/PrintFooterInfo.tsx +86 -0
- package/src/components/PrintFooter/PrintPageNumber.tsx +90 -0
- package/src/components/PrintFooter/__tests__/PrintFooter.unit.test.tsx +500 -0
- package/src/components/PrintFooter/__tests__/PrintFooterContent.unit.test.tsx +321 -0
- package/src/components/PrintFooter/__tests__/PrintFooterInfo.unit.test.tsx +335 -0
- package/src/components/PrintFooter/__tests__/PrintPageNumber.unit.test.tsx +340 -0
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +390 -0
- package/src/components/PrintFooter/index.ts +30 -0
- package/src/components/PrintFooter/types.ts +149 -0
- package/src/components/PrintGrid/PrintGrid.tsx +180 -0
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +109 -0
- package/src/components/PrintGrid/PrintGridContainer.tsx +128 -0
- package/src/components/PrintGrid/PrintGridItem.tsx +220 -0
- package/src/components/PrintGrid/__tests__/PrintGrid.unit.test.tsx +340 -0
- package/src/components/PrintGrid/__tests__/PrintGridBreakpoint.unit.test.tsx +261 -0
- package/src/components/PrintGrid/__tests__/PrintGridContainer.unit.test.tsx +338 -0
- package/src/components/PrintGrid/__tests__/PrintGridItem.unit.test.tsx +338 -0
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +359 -0
- package/src/components/PrintGrid/index.ts +31 -0
- package/src/components/PrintGrid/types.ts +159 -0
- package/src/components/PrintHeader/PrintCoverHeader.tsx +230 -0
- package/src/components/PrintHeader/PrintHeader.tsx +150 -0
- package/src/components/PrintHeader/__tests__/PrintCoverHeader.unit.test.tsx +309 -0
- package/src/components/PrintHeader/__tests__/PrintHeader.unit.test.tsx +202 -0
- package/src/components/PrintHeader/index.ts +17 -0
- package/src/components/PrintHeader/types.ts +42 -0
- package/src/components/PrintLayout/PrintLayout.tsx +122 -0
- package/src/components/PrintLayout/PrintLayoutContext.tsx +66 -0
- package/src/components/PrintLayout/PrintPageBreak.tsx +52 -0
- package/src/components/PrintLayout/__tests__/PrintLayout.unit.test.tsx +238 -0
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +230 -0
- package/src/components/PrintLayout/index.ts +19 -0
- package/src/components/PrintLayout/types.ts +37 -0
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +120 -0
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +90 -0
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +112 -0
- package/src/components/PrintPageBreak/__tests__/PrintPageBreak.unit.test.tsx +263 -0
- package/src/components/PrintPageBreak/__tests__/PrintPageBreakGroup.unit.test.tsx +239 -0
- package/src/components/PrintPageBreak/__tests__/PrintPageBreakIndicator.unit.test.tsx +235 -0
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +279 -0
- package/src/components/PrintPageBreak/index.ts +23 -0
- package/src/components/PrintPageBreak/types.ts +94 -0
- package/src/components/PrintSection/PrintColumn.tsx +104 -0
- package/src/components/PrintSection/PrintDivider.tsx +101 -0
- package/src/components/PrintSection/PrintSection.tsx +129 -0
- package/src/components/PrintSection/PrintSectionContent.tsx +75 -0
- package/src/components/PrintSection/PrintSectionHeader.tsx +97 -0
- package/src/components/PrintSection/__tests__/PrintColumn.unit.test.tsx +385 -0
- package/src/components/PrintSection/__tests__/PrintDivider.unit.test.tsx +373 -0
- package/src/components/PrintSection/__tests__/PrintSection.unit.test.tsx +390 -0
- package/src/components/PrintSection/__tests__/PrintSectionContent.unit.test.tsx +321 -0
- package/src/components/PrintSection/__tests__/PrintSectionHeader.unit.test.tsx +334 -0
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +258 -0
- package/src/components/PrintSection/index.ts +33 -0
- package/src/components/PrintSection/types.ts +155 -0
- package/src/components/PrintText/PrintText.tsx +116 -0
- package/src/components/PrintText/__tests__/PrintText.unit.test.tsx +351 -0
- package/src/components/PrintText/index.ts +16 -0
- package/src/components/PrintText/types.ts +24 -0
- package/src/components/Progress/Progress.tsx +116 -0
- package/src/components/Progress/__tests__/Progress.accessibility.test.tsx +240 -0
- package/src/components/Progress/__tests__/Progress.unit.test.tsx +242 -0
- package/src/components/Progress/index.ts +3 -0
- package/src/components/PublicLayout/EventLogo.tsx +287 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +279 -0
- package/src/components/PublicLayout/PublicLoadingSpinner.tsx +208 -0
- package/src/components/PublicLayout/PublicPageContextChecker.tsx +130 -0
- package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
- package/src/components/PublicLayout/PublicPageFooter.tsx +124 -0
- package/src/components/PublicLayout/PublicPageHeader.tsx +178 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +232 -0
- package/src/components/PublicLayout/PublicPageProvider.tsx +137 -0
- package/src/components/PublicLayout/__tests__/EventLogo.test.tsx +761 -0
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.simplified.test.tsx +228 -0
- package/src/components/PublicLayout/__tests__/PublicErrorBoundary.test.tsx +228 -0
- package/src/components/PublicLayout/__tests__/PublicLoadingSpinner.test.tsx +459 -0
- package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +362 -0
- package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +522 -0
- package/src/components/PublicLayout/__tests__/PublicPageLayout.test.tsx +599 -0
- package/src/components/PublicLayout/__tests__/PublicPageProvider.test.tsx +513 -0
- package/src/components/PublicLayout/index.ts +51 -0
- package/src/components/RBAC/PagePermissionGuard.tsx +274 -0
- package/src/components/RBAC/RBACGuard.tsx +143 -0
- package/src/components/RBAC/RBACProvider.tsx +186 -0
- package/src/components/RBAC/RoleBasedContent.tsx +129 -0
- package/src/components/RBAC/__tests__/PagePermissionGuard.unit.test.tsx +674 -0
- package/src/components/RBAC/__tests__/RBAC.integration.test.tsx +573 -0
- package/src/components/RBAC/__tests__/RBACGuard.unit.test.tsx +467 -0
- package/src/components/RBAC/__tests__/RBACProvider.accessibility.test.tsx +475 -0
- package/src/components/RBAC/__tests__/RBACProvider.advanced.test.tsx +569 -0
- package/src/components/RBAC/__tests__/RBACProvider.integration.test.tsx +352 -0
- package/src/components/RBAC/__tests__/RBACProvider.unit.test.tsx +128 -0
- package/src/components/RBAC/__tests__/RoleBasedContent.unit.test.tsx +657 -0
- package/src/components/RBAC/index.ts +23 -0
- package/src/components/Select/Select.tsx +654 -0
- package/src/components/Select/__tests__/SearchableSelect.unit.test.tsx +437 -0
- package/src/components/Select/__tests__/Select.accessibility.test.tsx +1202 -0
- package/src/components/Select/__tests__/Select.actual.test.tsx +774 -0
- package/src/components/Select/__tests__/Select.comprehensive.test.tsx +837 -0
- package/src/components/Select/__tests__/Select.enhanced.test.tsx +1101 -0
- package/src/components/Select/__tests__/Select.integration.test.tsx +772 -0
- package/src/components/Select/__tests__/Select.performance.test.tsx +695 -0
- package/src/components/Select/__tests__/Select.real-world.test.tsx +1046 -0
- package/src/components/Select/__tests__/Select.search-algorithms.test.tsx +968 -0
- package/src/components/Select/__tests__/Select.unit.test.tsx +647 -0
- package/src/components/Select/__tests__/Select.utils.test.tsx +890 -0
- package/src/components/Select/index.ts +1 -0
- package/src/components/SuperAdminGuard.tsx +116 -0
- package/src/components/Table/Table.tsx +222 -0
- package/src/components/Table/__tests__/Table.accessibility.test.tsx +233 -0
- package/src/components/Table/__tests__/Table.unit.test.tsx +235 -0
- package/src/components/Table/index.ts +11 -0
- package/src/components/Toast/Toast.tsx +339 -0
- package/src/components/Toast/__tests__/Toast.accessibility.test.tsx +238 -0
- package/src/components/Toast/__tests__/Toast.integration.test.tsx +699 -0
- package/src/components/Toast/__tests__/Toast.unit.test.tsx +750 -0
- package/src/components/Toast/index.ts +14 -0
- package/src/components/Tooltip/Tooltip.tsx +167 -0
- package/src/components/Tooltip/__tests__/Tooltip.accessibility.test.tsx +121 -0
- package/src/components/Tooltip/__tests__/Tooltip.unit.test.tsx +185 -0
- package/src/components/Tooltip/index.ts +7 -0
- package/src/components/UserMenu/UserMenu.tsx +243 -0
- package/src/components/UserMenu/__tests__/UserMenu.accessibility.test.tsx +139 -0
- package/src/components/UserMenu/__tests__/UserMenu.integration.test.tsx +188 -0
- package/src/components/UserMenu/__tests__/UserMenu.unit.test.tsx +458 -0
- package/src/components/UserMenu/index.ts +3 -0
- package/src/components/__tests__/EdgeCaseTesting.enhanced.test.tsx +523 -0
- package/src/components/__tests__/ErrorTesting.enhanced.test.tsx +455 -0
- package/src/components/__tests__/SuperAdminGuard.test.tsx +456 -0
- package/src/components/__tests__/SuperAdminGuard.unit.test.tsx +456 -0
- package/src/components/examples/PermissionExample.tsx +150 -0
- package/src/components/examples/__tests__/PermissionExample.unit.test.tsx +360 -0
- package/src/components/index.ts +434 -0
- package/src/components.ts +19 -0
- package/src/constants/performance.ts +14 -0
- package/src/examples/CorrectPublicPageImplementation.tsx +301 -0
- package/src/examples/PublicEventPage.tsx +274 -0
- package/src/examples/PublicPageApp.tsx +308 -0
- package/src/examples/PublicPageUsageExample.tsx +216 -0
- package/src/fonts/georama-italic.woff2 +0 -0
- package/src/fonts/georama.woff2 +0 -0
- package/src/fonts/open-sans-italic.woff2 +0 -0
- package/src/fonts/open-sans.woff2 +0 -0
- package/src/fonts/reddit-mono.woff2 +0 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +575 -0
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +115 -0
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +133 -0
- package/src/hooks/__tests__/useDebounce.unit.test.ts +82 -0
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +293 -0
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +385 -0
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +286 -0
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +838 -0
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +627 -0
- package/src/hooks/__tests__/useRBAC.unit.test.ts +903 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +537 -0
- package/src/hooks/__tests__/useToast.unit.test.tsx +62 -0
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +37 -0
- package/src/hooks/index.ts +56 -0
- package/src/hooks/public/__tests__/usePublicEvent.test.tsx +397 -0
- package/src/hooks/public/__tests__/usePublicEventLogo.test.tsx +690 -0
- package/src/hooks/public/__tests__/usePublicRouteParams.test.tsx +449 -0
- package/src/hooks/public/index.ts +34 -0
- package/src/hooks/public/usePublicEvent.ts +261 -0
- package/src/hooks/public/usePublicEventLogo.ts +285 -0
- package/src/hooks/public/usePublicRouteParams.ts +259 -0
- package/src/hooks/useAppConfig.ts +94 -0
- package/src/hooks/useComponentPerformance.ts +39 -0
- package/src/hooks/useDataTablePerformance.ts +387 -0
- package/src/hooks/useDataTableState.ts +110 -0
- package/src/hooks/useDebounce.ts +18 -0
- package/src/hooks/useFocusManagement.ts +161 -0
- package/src/hooks/useFocusTrap.ts +155 -0
- package/src/hooks/useInactivityTracker.ts +372 -0
- package/src/hooks/useIsMobile.ts +42 -0
- package/src/hooks/useKeyboardShortcuts.ts +237 -0
- package/src/hooks/useOrganisationPermissions.ts +208 -0
- package/src/hooks/useOrganisationSecurity.ts +262 -0
- package/src/hooks/usePerformanceMonitor.ts +128 -0
- package/src/hooks/usePermissionCache.ts +455 -0
- package/src/hooks/useRBAC.ts +262 -0
- package/src/hooks/useSecureDataAccess.ts +586 -0
- package/src/hooks/useStorage.ts +274 -0
- package/src/hooks/useToast.ts +242 -0
- package/src/hooks/useZodForm.ts +28 -0
- package/src/index.ts +200 -0
- package/src/providers/AuthProvider.tsx +369 -0
- package/src/providers/EventProvider.tsx +324 -0
- package/src/providers/InactivityProvider.tsx +238 -0
- package/src/providers/OrganisationProvider.tsx +588 -0
- package/src/providers/RBACProvider.tsx +622 -0
- package/src/providers/UnifiedAuthProvider.tsx +327 -0
- package/src/providers/__tests__/EventProvider.unit.test.tsx +768 -0
- package/src/providers/__tests__/OrganisationProvider.basic.test.tsx +116 -0
- package/src/providers/__tests__/OrganisationProvider.unit.test.tsx +1312 -0
- package/src/providers/__tests__/UnifiedAuthProvider.inactivity.test.tsx +601 -0
- package/src/providers/__tests__/UnifiedAuthProvider.unit.test.tsx +675 -0
- package/src/providers/__tests__/index.unit.test.ts +78 -0
- package/src/providers/index.ts +15 -0
- package/src/rbac/README.md +885 -0
- package/src/rbac/__tests__/PagePermissionGuard.test.tsx +673 -0
- package/src/rbac/__tests__/README.md +170 -0
- package/src/rbac/__tests__/RoleBasedRouter.test.tsx +709 -0
- package/src/rbac/__tests__/TestContext.tsx +72 -0
- package/src/rbac/__tests__/__mocks__/cache.ts +144 -0
- package/src/rbac/__tests__/__mocks__/supabase.ts +152 -0
- package/src/rbac/__tests__/adapters-hooks-comprehensive.test.tsx +782 -0
- package/src/rbac/__tests__/adapters-hooks.test.tsx +561 -0
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +963 -0
- package/src/rbac/__tests__/adapters.test.tsx +444 -0
- package/src/rbac/__tests__/api.test.ts +620 -0
- package/src/rbac/__tests__/audit-observability-comprehensive.test.ts +792 -0
- package/src/rbac/__tests__/audit-observability.test.ts +549 -0
- package/src/rbac/__tests__/audit.test.ts +616 -0
- package/src/rbac/__tests__/build-contract-compliance-simple.test.ts +230 -0
- package/src/rbac/__tests__/cache-invalidation-comprehensive.test.ts +889 -0
- package/src/rbac/__tests__/cache-invalidation.test.ts +457 -0
- package/src/rbac/__tests__/cache.test.ts +458 -0
- package/src/rbac/__tests__/components-navigation-guard.enhanced.test.tsx +859 -0
- package/src/rbac/__tests__/components-navigation-guard.test.tsx +895 -0
- package/src/rbac/__tests__/components-navigation-provider.test.tsx +692 -0
- package/src/rbac/__tests__/components-page-permission-guard.test.tsx +673 -0
- package/src/rbac/__tests__/components-page-permission-provider.test.tsx +614 -0
- package/src/rbac/__tests__/components-permission-enforcer.enhanced.fixed.test.tsx +836 -0
- package/src/rbac/__tests__/components-permission-enforcer.enhanced.test.tsx +837 -0
- package/src/rbac/__tests__/components-permission-enforcer.test.tsx +825 -0
- package/src/rbac/__tests__/components-role-based-router.test.tsx +709 -0
- package/src/rbac/__tests__/components-secure-data-provider.test.tsx +607 -0
- package/src/rbac/__tests__/config.test.ts +583 -0
- package/src/rbac/__tests__/core-logic-unit.test.ts +190 -0
- package/src/rbac/__tests__/core-permission-logic-comprehensive.test.ts +1467 -0
- package/src/rbac/__tests__/core-permission-logic-fixed.test.ts +151 -0
- package/src/rbac/__tests__/core-permission-logic-simple.test.ts +968 -0
- package/src/rbac/__tests__/core-permission-logic.test.ts +966 -0
- package/src/rbac/__tests__/edge-cases-comprehensive.test.ts +988 -0
- package/src/rbac/__tests__/edge-cases.test.ts +654 -0
- package/src/rbac/__tests__/engine.test.ts +361 -0
- package/src/rbac/__tests__/engine.unit.test.ts +361 -0
- package/src/rbac/__tests__/hooks.enhanced.test.tsx +979 -0
- package/src/rbac/__tests__/hooks.fixed.test.tsx +475 -0
- package/src/rbac/__tests__/hooks.test.tsx +385 -0
- package/src/rbac/__tests__/index.test.ts +269 -0
- package/src/rbac/__tests__/integration.enhanced.test.tsx +824 -0
- package/src/rbac/__tests__/page-permission-guard-super-admin.test.tsx +261 -0
- package/src/rbac/__tests__/performance.enhanced.test.tsx +724 -0
- package/src/rbac/__tests__/permissions.test.ts +383 -0
- package/src/rbac/__tests__/requires-event.test.ts +330 -0
- package/src/rbac/__tests__/scope-isolation-comprehensive.test.ts +1349 -0
- package/src/rbac/__tests__/scope-isolation.test.ts +755 -0
- package/src/rbac/__tests__/secure-client-rls-comprehensive.test.ts +592 -0
- package/src/rbac/__tests__/secure-client-rls.test.ts +377 -0
- package/src/rbac/__tests__/security.test.ts +296 -0
- package/src/rbac/__tests__/setup.ts +228 -0
- package/src/rbac/__tests__/test-utils-enhanced.tsx +400 -0
- package/src/rbac/__tests__/types.test.ts +685 -0
- package/src/rbac/adapters.tsx +726 -0
- package/src/rbac/api.ts +337 -0
- package/src/rbac/audit-enhanced.ts +339 -0
- package/src/rbac/audit.ts +338 -0
- package/src/rbac/cache.ts +213 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +294 -0
- package/src/rbac/components/NavigationGuard.tsx +294 -0
- package/src/rbac/components/NavigationProvider.tsx +314 -0
- package/src/rbac/components/PagePermissionGuard.tsx +430 -0
- package/src/rbac/components/PagePermissionProvider.tsx +274 -0
- package/src/rbac/components/PermissionEnforcer.tsx +307 -0
- package/src/rbac/components/RoleBasedRouter.tsx +425 -0
- package/src/rbac/components/SecureDataProvider.tsx +319 -0
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +631 -0
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +667 -0
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +647 -0
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +496 -0
- package/src/rbac/components/index.ts +64 -0
- package/src/rbac/config.ts +133 -0
- package/src/rbac/docs/event-based-apps.md +285 -0
- package/src/rbac/engine.ts +1026 -0
- package/src/rbac/eslint-rules.js +285 -0
- package/src/rbac/examples/CompleteRBACExample.tsx +323 -0
- package/src/rbac/examples/EventBasedApp.tsx +238 -0
- package/src/rbac/hooks.ts +555 -0
- package/src/rbac/index.ts +114 -0
- package/src/rbac/permissions.ts +293 -0
- package/src/rbac/secureClient.ts +244 -0
- package/src/rbac/security.ts +346 -0
- package/src/rbac/testing/__tests__/index.test.tsx +342 -0
- package/src/rbac/testing/index.tsx +340 -0
- package/src/rbac/types.ts +341 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +428 -0
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +428 -0
- package/src/rbac/utils/eventContext.ts +83 -0
- package/src/styles/__tests__/styles.unit.test.ts +164 -0
- package/src/styles/core.css +401 -0
- package/src/styles/index.ts +51 -0
- package/src/test-dom-cleanup.test.tsx +38 -0
- package/src/theming/__tests__/README.md +335 -0
- package/src/theming/__tests__/runtime.accessibility.test.ts +474 -0
- package/src/theming/__tests__/runtime.error.test.ts +616 -0
- package/src/theming/__tests__/runtime.integration.test.ts +376 -0
- package/src/theming/__tests__/runtime.performance.test.ts +411 -0
- package/src/theming/__tests__/runtime.unit.test.ts +470 -0
- package/src/theming/runtime.ts +187 -0
- package/src/types/__tests__/database.unit.test.ts +489 -0
- package/src/types/__tests__/guards.unit.test.ts +146 -0
- package/src/types/__tests__/index.unit.test.ts +77 -0
- package/src/types/__tests__/organisation.unit.test.ts +713 -0
- package/src/types/__tests__/rbac.unit.test.ts +621 -0
- package/src/types/__tests__/security.unit.test.ts +347 -0
- package/src/types/__tests__/supabase.unit.test.ts +658 -0
- package/src/types/__tests__/theme.unit.test.ts +218 -0
- package/src/types/__tests__/unified.unit.test.ts +537 -0
- package/src/types/__tests__/validation.unit.test.ts +616 -0
- package/src/types/database.ts +472 -0
- package/src/types/guards.ts +30 -0
- package/src/types/index.ts +25 -0
- package/src/types/organisation.ts +184 -0
- package/src/types/security.ts +70 -0
- package/src/types/supabase.ts +166 -0
- package/src/types/theme.ts +6 -0
- package/src/types/unified.ts +262 -0
- package/src/types/validation.ts +164 -0
- package/src/types/vitest-globals.d.ts +43 -0
- package/src/utils/__mocks__/supabaseMock.ts +75 -0
- package/src/utils/__mocks__/supabaseMock.tsx +198 -0
- package/src/utils/__tests__/appConfig.unit.test.ts +55 -0
- package/src/utils/__tests__/appNameResolver.unit.test.ts +137 -0
- package/src/utils/__tests__/audit.unit.test.ts +69 -0
- package/src/utils/__tests__/auth-utils.unit.test.ts +70 -0
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +317 -0
- package/src/utils/__tests__/cn.unit.test.ts +34 -0
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +480 -0
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +322 -0
- package/src/utils/__tests__/formatDate.unit.test.ts +109 -0
- package/src/utils/__tests__/formatting.unit.test.ts +66 -0
- package/src/utils/__tests__/index.unit.test.ts +251 -0
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +309 -0
- package/src/utils/__tests__/organisationContext.unit.test.ts +192 -0
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +259 -0
- package/src/utils/__tests__/permissionTypes.unit.test.ts +250 -0
- package/src/utils/__tests__/permissionUtils.unit.test.ts +362 -0
- package/src/utils/__tests__/sanitization.unit.test.ts +346 -0
- package/src/utils/__tests__/schemaUtils.unit.test.ts +441 -0
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +334 -0
- package/src/utils/__tests__/secureErrors.unit.test.ts +377 -0
- package/src/utils/__tests__/secureStorage.unit.test.ts +293 -0
- package/src/utils/__tests__/security.unit.test.ts +127 -0
- package/src/utils/__tests__/securityMonitor.unit.test.ts +280 -0
- package/src/utils/__tests__/sessionTracking.unit.test.ts +370 -0
- package/src/utils/__tests__/validation.unit.test.ts +84 -0
- package/src/utils/__tests__/validationUtils.unit.test.ts +571 -0
- package/src/utils/appConfig.ts +47 -0
- package/src/utils/appIdResolver.ts +130 -0
- package/src/utils/appNameResolver.ts +190 -0
- package/src/utils/audit.ts +127 -0
- package/src/utils/auth-utils.ts +96 -0
- package/src/utils/bundleAnalysis.ts +129 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils/debugLogger.ts +46 -0
- package/src/utils/deviceFingerprint.ts +215 -0
- package/src/utils/dynamicUtils.ts +105 -0
- package/src/utils/formatting.ts +77 -0
- package/src/utils/index.ts +145 -0
- package/src/utils/lazyLoad.tsx +44 -0
- package/src/utils/organisationContext.ts +135 -0
- package/src/utils/performanceBenchmark.ts +64 -0
- package/src/utils/performanceBudgets.ts +111 -0
- package/src/utils/permissionTypes.ts +37 -0
- package/src/utils/permissionUtils.ts +31 -0
- package/src/utils/print/PrintDataProcessor.ts +390 -0
- package/src/utils/print/__tests__/PrintDataProcessor.unit.test.ts +219 -0
- package/src/utils/print/__tests__/usePrintOptimization.unit.test.tsx +353 -0
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +397 -0
- package/src/utils/print/index.ts +29 -0
- package/src/utils/print/types.ts +196 -0
- package/src/utils/print/usePrintOptimization.ts +272 -0
- package/src/utils/sanitization.ts +264 -0
- package/src/utils/schemaUtils.ts +37 -0
- package/src/utils/secureDataAccess.ts +361 -0
- package/src/utils/secureErrors.ts +79 -0
- package/src/utils/secureStorage.ts +244 -0
- package/src/utils/security.ts +156 -0
- package/src/utils/securityMonitor.ts +45 -0
- package/src/utils/sessionTracking.ts +170 -0
- package/src/utils/storage/README.md +348 -0
- package/src/utils/storage/__tests__/config.unit.test.ts +206 -0
- package/src/utils/storage/__tests__/helpers.unit.test.ts +646 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +167 -0
- package/src/utils/storage/__tests__/types.unit.test.ts +441 -0
- package/src/utils/storage/config.ts +100 -0
- package/src/utils/storage/helpers.ts +359 -0
- package/src/utils/storage/index.ts +36 -0
- package/src/utils/storage/types.ts +90 -0
- package/src/utils/validation.ts +111 -0
- package/src/utils/validationUtils.ts +120 -0
- package/src/validation/__tests__/common.unit.test.ts +101 -0
- package/src/validation/__tests__/csrf.unit.test.ts +302 -0
- package/src/validation/__tests__/passwordSchema.unit.test.ts +98 -0
- package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +466 -0
- package/src/validation/common.ts +53 -0
- package/src/validation/csrf.ts +214 -0
- package/src/validation/index.ts +43 -0
- package/src/validation/passwordSchema.ts +125 -0
- package/src/validation/sanitization.ts +96 -0
- package/src/validation/schemaUtils.ts +42 -0
- package/src/validation/sqlInjectionProtection.ts +242 -0
- package/src/validation/user.ts +34 -0
|
@@ -0,0 +1,1898 @@
|
|
|
1
|
+
# Data Tables
|
|
2
|
+
|
|
3
|
+
This guide covers implementing data tables with PACE Core's DataTable component, including basic usage, advanced features, and best practices.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The PACE Core DataTable is an enterprise-grade data table component built on TanStack Table that provides:
|
|
8
|
+
|
|
9
|
+
- **🚀 Performance Optimized** - Virtual scrolling, intelligent chunking, and automatic mode detection
|
|
10
|
+
- **📊 Data Management** - Sorting, filtering, pagination, search, export/import
|
|
11
|
+
- **🌳 Hierarchical Rows** - Parent/child row relationships with expand/collapse all and smart sorting ([Detailed Guide](./hierarchical-datatable.md))
|
|
12
|
+
- **✏️ Inline Editing** - Row editing, creation, and deletion
|
|
13
|
+
- **🎯 Actions** - Custom row actions and toolbar buttons
|
|
14
|
+
- **📈 Grouping** - Data grouping with aggregation functions
|
|
15
|
+
- **📏 Auto Column Sizing** - Automatic column width adjustment based on content
|
|
16
|
+
- **🎨 Customizable** - Column visibility, responsive design, accessibility
|
|
17
|
+
- **🔧 TypeScript** - Full TypeScript support with strict typing
|
|
18
|
+
|
|
19
|
+
## Basic Usage
|
|
20
|
+
|
|
21
|
+
### Simple Data Table
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { DataTable, type DataTableColumn } from '@jmruthers/pace-core';
|
|
25
|
+
|
|
26
|
+
interface User {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
email: string;
|
|
30
|
+
role: string;
|
|
31
|
+
status: 'active' | 'inactive';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const columns: DataTableColumn<User>[] = [
|
|
35
|
+
{
|
|
36
|
+
accessorKey: 'name',
|
|
37
|
+
header: 'Name',
|
|
38
|
+
sortable: true,
|
|
39
|
+
features: { search: true },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
accessorKey: 'email',
|
|
43
|
+
header: 'Email',
|
|
44
|
+
sortable: true,
|
|
45
|
+
features: { search: true },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
accessorKey: 'role',
|
|
49
|
+
header: 'Role',
|
|
50
|
+
sortable: true,
|
|
51
|
+
enableGrouping: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
accessorKey: 'status',
|
|
55
|
+
header: 'Status',
|
|
56
|
+
sortable: true,
|
|
57
|
+
cell: ({ row }) => (
|
|
58
|
+
<span className={`px-2 py-1 rounded text-xs ${
|
|
59
|
+
row.original.status === 'active'
|
|
60
|
+
? 'bg-main-100 text-main-800'
|
|
61
|
+
: 'bg-acc-100 text-acc-800'
|
|
62
|
+
}`}>
|
|
63
|
+
{row.original.status}
|
|
64
|
+
</span>
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const data: User[] = [
|
|
70
|
+
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active' },
|
|
71
|
+
{ id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'active' },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
function UserTable() {
|
|
75
|
+
return (
|
|
76
|
+
<DataTable
|
|
77
|
+
data={data}
|
|
78
|
+
columns={columns}
|
|
79
|
+
title="Users"
|
|
80
|
+
description="Manage your users"
|
|
81
|
+
features={{
|
|
82
|
+
search: true,
|
|
83
|
+
pagination: true,
|
|
84
|
+
sorting: true,
|
|
85
|
+
filtering: true,
|
|
86
|
+
}}
|
|
87
|
+
columnOrder={['name', 'email', 'role', 'status']} // Control column order
|
|
88
|
+
// Performance and other settings can be configured via performance prop
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### With Permission Enforcement
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { DataTable, PagePermissionGuard, PAGE_IDS } from '@jmruthers/pace-core';
|
|
98
|
+
|
|
99
|
+
function UsersPage() {
|
|
100
|
+
return (
|
|
101
|
+
<PagePermissionGuard pageId={PAGE_IDS.USER_MANAGEMENT}>
|
|
102
|
+
{(permissions) => (
|
|
103
|
+
<DataTable
|
|
104
|
+
data={users}
|
|
105
|
+
columns={columns}
|
|
106
|
+
title="User Management"
|
|
107
|
+
description="Manage system users"
|
|
108
|
+
features={{
|
|
109
|
+
search: true,
|
|
110
|
+
pagination: true,
|
|
111
|
+
creation: permissions.canCreate,
|
|
112
|
+
editing: permissions.canUpdate,
|
|
113
|
+
deletion: permissions.canDelete,
|
|
114
|
+
}}
|
|
115
|
+
onEditRow={permissions.canUpdate ? (row) => navigate(`/users/${row.id}/edit`) : undefined}
|
|
116
|
+
onDeleteRow={permissions.canDelete ? handleDeleteUser : undefined}
|
|
117
|
+
// Custom actions can be added via the deprecated actions prop
|
|
118
|
+
actions={
|
|
119
|
+
permissions.canUpdate || permissions.canDelete ? [
|
|
120
|
+
...(permissions.canUpdate ? [{
|
|
121
|
+
label: 'View Details',
|
|
122
|
+
onClick: (row) => navigate(`/users/${row.id}`),
|
|
123
|
+
}] : [])
|
|
124
|
+
] : []
|
|
125
|
+
}
|
|
126
|
+
/>
|
|
127
|
+
)}
|
|
128
|
+
</PagePermissionGuard>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## DataTable Props Interface
|
|
134
|
+
|
|
135
|
+
The DataTable component has a comprehensive prop interface. Here are the key properties:
|
|
136
|
+
|
|
137
|
+
### Core Props
|
|
138
|
+
- `data: TData[]` - Array of data records to display
|
|
139
|
+
- `columns: any[]` - Column definitions (supports TanStack Table format)
|
|
140
|
+
- `features: DataTableFeatureConfig` - **Required** - Feature configuration object
|
|
141
|
+
|
|
142
|
+
### Display Props
|
|
143
|
+
- `title?: string` - Table title
|
|
144
|
+
- `description?: string` - Table description
|
|
145
|
+
- `variant?: 'default' | 'compact' | 'spacious'` - Visual variant
|
|
146
|
+
- `className?: string` - Additional CSS classes
|
|
147
|
+
- `columnOrder?: string[]` - **Column ordering** - Array of column IDs in desired order
|
|
148
|
+
|
|
149
|
+
### Event Handlers
|
|
150
|
+
- `onEditRow?: (row: TData, data: Partial<TData>) => void` - Row edit handler
|
|
151
|
+
- `onDeleteRow?: (row: TData) => void` - Row deletion handler (also used as fallback for bulk delete)
|
|
152
|
+
- `onCreateRow?: (data: Partial<TData>) => void` - Row creation handler
|
|
153
|
+
- `onImport?: (data: TData[]) => void | Promise<void>` - Import handler
|
|
154
|
+
- `onRowSelectionChange?: (selection: Record<string, boolean>) => void` - Row selection change handler
|
|
155
|
+
- `onDeleteSelected?: (selectedRows: Record<string, boolean>) => void` - **Bulk delete handler** - Called when delete selected button is clicked
|
|
156
|
+
|
|
157
|
+
### Performance Props
|
|
158
|
+
- `performance?: PerformanceConfig` - Performance optimization settings
|
|
159
|
+
- `serverSide?: ServerSideConfig<TData>` - Server-side data fetching
|
|
160
|
+
- `virtualHeight?: number` - Virtual scrolling height (default: 600)
|
|
161
|
+
- `showPerformanceMetrics?: boolean` - Show performance metrics
|
|
162
|
+
- `initialPageSize?: number` - Initial page size for pagination (default: 10)
|
|
163
|
+
|
|
164
|
+
### Feature-Specific Configuration
|
|
165
|
+
- `bulkOperationsConfig?: BulkOperationsConfig` - Bulk operations config
|
|
166
|
+
|
|
167
|
+
### Row Actions
|
|
168
|
+
- `actions?: any[]` - Custom row actions for each row
|
|
169
|
+
|
|
170
|
+
## Feature Configuration
|
|
171
|
+
|
|
172
|
+
The DataTable component uses a unified `features` prop to control all table functionality. This provides a consistent and intuitive way to enable or disable features.
|
|
173
|
+
|
|
174
|
+
### DataTableFeatureConfig Properties
|
|
175
|
+
|
|
176
|
+
| Property | Type | Default | Description |
|
|
177
|
+
|----------|------|---------|-------------|
|
|
178
|
+
| **Core Features** |
|
|
179
|
+
| `search` | `boolean` | `false` | Enable global search functionality |
|
|
180
|
+
| `pagination` | `boolean` | `false` | Enable pagination controls |
|
|
181
|
+
| `sorting` | `boolean` | `false` | Enable column sorting |
|
|
182
|
+
| `filtering` | `boolean` | `false` | Enable column filtering |
|
|
183
|
+
| **Data Management** |
|
|
184
|
+
| `import` | `boolean` | `false` | Enable data import functionality |
|
|
185
|
+
| `export` | `boolean` | `false` | Enable data export functionality |
|
|
186
|
+
| **Row Operations** |
|
|
187
|
+
| `selection` | `boolean` | `false` | Enable row selection for bulk operations |
|
|
188
|
+
| `creation` | `boolean` | `false` | Enable row creation |
|
|
189
|
+
| `editing` | `boolean` | `false` | Enable row editing |
|
|
190
|
+
| `deletion` | `boolean` | `false` | Enable row deletion |
|
|
191
|
+
| `deleteSelected` | `boolean` | `false` | Enable bulk deletion of selected rows |
|
|
192
|
+
| **Table Customization** |
|
|
193
|
+
| `grouping` | `boolean` | `false` | Enable data grouping |
|
|
194
|
+
| `columnVisibility` | `boolean` | `false` | Enable column visibility controls |
|
|
195
|
+
| `columnReordering` | `boolean` | `false` | Enable column reordering |
|
|
196
|
+
| `autoColumnSizing` | `boolean` | `false` | Enable automatic column width adjustment based on content |
|
|
197
|
+
|
|
198
|
+
### Basic Feature Configuration
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<DataTable
|
|
202
|
+
data={data}
|
|
203
|
+
columns={columns}
|
|
204
|
+
features={{
|
|
205
|
+
// Core Features
|
|
206
|
+
search: true,
|
|
207
|
+
pagination: true,
|
|
208
|
+
sorting: true,
|
|
209
|
+
filtering: true,
|
|
210
|
+
|
|
211
|
+
// Data Management
|
|
212
|
+
import: true,
|
|
213
|
+
export: true,
|
|
214
|
+
|
|
215
|
+
// Row Operations
|
|
216
|
+
selection: true, // Required for row selection
|
|
217
|
+
creation: true,
|
|
218
|
+
editing: true,
|
|
219
|
+
deletion: true, // Required for delete functionality
|
|
220
|
+
deleteSelected: true, // Required for delete selected button
|
|
221
|
+
|
|
222
|
+
// Table Customization
|
|
223
|
+
grouping: true,
|
|
224
|
+
columnVisibility: true,
|
|
225
|
+
columnReordering: true,
|
|
226
|
+
autoColumnSizing: true, // Enable automatic column width adjustment
|
|
227
|
+
}}
|
|
228
|
+
/>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Minimal Configuration
|
|
232
|
+
|
|
233
|
+
For a simple read-only table:
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
<DataTable
|
|
237
|
+
data={data}
|
|
238
|
+
columns={columns}
|
|
239
|
+
features={{
|
|
240
|
+
search: true,
|
|
241
|
+
pagination: true,
|
|
242
|
+
sorting: true,
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Advanced Configuration
|
|
248
|
+
|
|
249
|
+
For a full-featured data management table:
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
<DataTable
|
|
253
|
+
data={data}
|
|
254
|
+
columns={columns}
|
|
255
|
+
features={{
|
|
256
|
+
search: true,
|
|
257
|
+
pagination: true,
|
|
258
|
+
sorting: true,
|
|
259
|
+
filtering: true,
|
|
260
|
+
selection: true,
|
|
261
|
+
creation: true,
|
|
262
|
+
editing: true,
|
|
263
|
+
deletion: true,
|
|
264
|
+
deleteSelected: true, // Enable delete selected functionality
|
|
265
|
+
export: true,
|
|
266
|
+
import: true,
|
|
267
|
+
grouping: true,
|
|
268
|
+
columnVisibility: true,
|
|
269
|
+
bulkOperations: true,
|
|
270
|
+
}}
|
|
271
|
+
columnOrder={['select', 'name', 'email', 'role', 'status', 'actions']} // Custom column order
|
|
272
|
+
onEditRow={handleEdit}
|
|
273
|
+
onDeleteRow={handleDelete}
|
|
274
|
+
onCreateRow={handleCreate}
|
|
275
|
+
onImport={handleImport}
|
|
276
|
+
onRowSelectionChange={handleSelectionChange}
|
|
277
|
+
onDeleteSelected={handleBulkDelete}
|
|
278
|
+
getRowId={(row) => row.id} // Important: Required for selection and delete operations
|
|
279
|
+
/>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Auto Column Sizing
|
|
283
|
+
|
|
284
|
+
The DataTable component includes automatic column width adjustment based on content, which helps optimize space usage and improve readability.
|
|
285
|
+
|
|
286
|
+
### How Auto-Sizing Works
|
|
287
|
+
|
|
288
|
+
When `autoColumnSizing: true` is enabled:
|
|
289
|
+
|
|
290
|
+
1. **Content Analysis**: Analyzes both header text and data content to determine optimal widths
|
|
291
|
+
2. **Smart Calculation**: Uses character count estimation (8px per character + padding)
|
|
292
|
+
3. **Constraints**: Respects min-width (80px) and max-width (400px) limits
|
|
293
|
+
4. **Performance**: Only samples first 100 rows for large datasets
|
|
294
|
+
5. **Table Layout**: Switches from `tableLayout: 'fixed'` to `tableLayout: 'auto'`
|
|
295
|
+
|
|
296
|
+
### Benefits
|
|
297
|
+
|
|
298
|
+
- **Better Space Utilization**: Columns automatically adjust to content width
|
|
299
|
+
- **Improved Readability**: Long text gets more space, short text takes less space
|
|
300
|
+
- **No Text Wrapping**: Prevents unnecessary text wrapping in narrow columns
|
|
301
|
+
- **Consistent Layout**: Maintains proper spacing and alignment
|
|
302
|
+
|
|
303
|
+
### Enabling Auto-Sizing
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
<DataTable
|
|
307
|
+
data={data}
|
|
308
|
+
columns={columns}
|
|
309
|
+
features={{
|
|
310
|
+
search: true,
|
|
311
|
+
pagination: true,
|
|
312
|
+
sorting: true,
|
|
313
|
+
autoColumnSizing: true, // Enable automatic column width adjustment
|
|
314
|
+
}}
|
|
315
|
+
/>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Auto-Sizing vs Fixed Widths
|
|
319
|
+
|
|
320
|
+
**With Auto-Sizing (Recommended):**
|
|
321
|
+
- Columns automatically adjust to content width
|
|
322
|
+
- Long text gets more space, short text takes less space
|
|
323
|
+
- Better space utilization
|
|
324
|
+
- Improved readability
|
|
325
|
+
|
|
326
|
+
**Without Auto-Sizing:**
|
|
327
|
+
- All columns have equal width
|
|
328
|
+
- Long text may wrap unnecessarily
|
|
329
|
+
- Short columns have wasted space
|
|
330
|
+
- Consistent but potentially inefficient layout
|
|
331
|
+
|
|
332
|
+
### Example: Content-Based Sizing
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
const data = [
|
|
336
|
+
{ id: '1', name: 'John', description: 'A short description' },
|
|
337
|
+
{ id: '2', name: 'Jane', description: 'A very long description that should make this column wider' },
|
|
338
|
+
{ id: '3', name: 'Bob', description: 'Medium length description' }
|
|
339
|
+
];
|
|
340
|
+
|
|
341
|
+
const columns = [
|
|
342
|
+
{ accessorKey: 'name', header: 'Name' },
|
|
343
|
+
{ accessorKey: 'description', header: 'Description' }
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
// With auto-sizing: Description column will be wider than Name column
|
|
347
|
+
<DataTable
|
|
348
|
+
data={data}
|
|
349
|
+
columns={columns}
|
|
350
|
+
features={{ autoColumnSizing: true }}
|
|
351
|
+
/>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Feature-Based Conditional Rendering
|
|
355
|
+
|
|
356
|
+
When features are disabled, the corresponding UI elements are automatically hidden:
|
|
357
|
+
|
|
358
|
+
```tsx
|
|
359
|
+
// This will show only search and pagination controls
|
|
360
|
+
// No edit, delete, or action buttons will appear
|
|
361
|
+
<DataTable
|
|
362
|
+
data={data}
|
|
363
|
+
columns={columns}
|
|
364
|
+
features={{
|
|
365
|
+
search: true,
|
|
366
|
+
pagination: true,
|
|
367
|
+
// All other features disabled by default
|
|
368
|
+
}}
|
|
369
|
+
/>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## Column Configuration
|
|
373
|
+
|
|
374
|
+
### Basic Column Properties
|
|
375
|
+
|
|
376
|
+
```tsx
|
|
377
|
+
const columns: DataTableColumn<User>[] = [
|
|
378
|
+
{
|
|
379
|
+
accessorKey: 'name', // Data field to access
|
|
380
|
+
header: 'Name', // Column header
|
|
381
|
+
sortable: true, // Enable sorting
|
|
382
|
+
features: { search: true }, // Enable search
|
|
383
|
+
enableGrouping: true, // Enable grouping
|
|
384
|
+
size: 200, // Column width
|
|
385
|
+
minSize: 100, // Minimum width
|
|
386
|
+
maxSize: 300, // Maximum width
|
|
387
|
+
},
|
|
388
|
+
];
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Custom Cell Rendering
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
const columns: DataTableColumn<User>[] = [
|
|
395
|
+
{
|
|
396
|
+
accessorKey: 'avatar',
|
|
397
|
+
header: 'Avatar',
|
|
398
|
+
cell: ({ row }) => (
|
|
399
|
+
<Avatar>
|
|
400
|
+
<AvatarImage src={row.original.avatarUrl} />
|
|
401
|
+
<AvatarFallback>
|
|
402
|
+
{row.original.name.charAt(0).toUpperCase()}
|
|
403
|
+
</AvatarFallback>
|
|
404
|
+
</Avatar>
|
|
405
|
+
),
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
accessorKey: 'status',
|
|
409
|
+
header: 'Status',
|
|
410
|
+
cell: ({ row }) => (
|
|
411
|
+
<Badge variant={row.original.status === 'active' ? 'default' : 'secondary'}>
|
|
412
|
+
{row.original.status}
|
|
413
|
+
</Badge>
|
|
414
|
+
),
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
accessorKey: 'lastLogin',
|
|
418
|
+
header: 'Last Login',
|
|
419
|
+
cell: ({ row }) => (
|
|
420
|
+
<span className="text-sm text-muted-foreground">
|
|
421
|
+
{formatDate(row.original.lastLogin)}
|
|
422
|
+
</span>
|
|
423
|
+
),
|
|
424
|
+
},
|
|
425
|
+
];
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Advanced Column Features
|
|
429
|
+
|
|
430
|
+
```tsx
|
|
431
|
+
const columns: DataTableColumn<User>[] = [
|
|
432
|
+
{
|
|
433
|
+
accessorKey: 'name',
|
|
434
|
+
header: 'Name',
|
|
435
|
+
sortable: true,
|
|
436
|
+
features: { search: true },
|
|
437
|
+
enableColumnFilter: true,
|
|
438
|
+
filterFn: 'includesString',
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
accessorKey: 'role',
|
|
442
|
+
header: 'Role',
|
|
443
|
+
enableGrouping: true,
|
|
444
|
+
enableColumnFilter: true,
|
|
445
|
+
filterFn: 'equals',
|
|
446
|
+
cell: ({ row }) => (
|
|
447
|
+
<Select value={row.original.role} onValueChange={(value) => handleRoleChange(row.original.id, value)}>
|
|
448
|
+
<SelectTrigger>
|
|
449
|
+
<SelectValue />
|
|
450
|
+
</SelectTrigger>
|
|
451
|
+
<SelectContent>
|
|
452
|
+
<SelectItem value="admin">Admin</SelectItem>
|
|
453
|
+
<SelectItem value="user">User</SelectItem>
|
|
454
|
+
<SelectItem value="guest">Guest</SelectItem>
|
|
455
|
+
</SelectContent>
|
|
456
|
+
</Select>
|
|
457
|
+
),
|
|
458
|
+
},
|
|
459
|
+
];
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Column Ordering
|
|
463
|
+
|
|
464
|
+
The DataTable component supports custom column ordering through the `columnOrder` prop, allowing you to control the exact position of columns including selection and actions columns.
|
|
465
|
+
|
|
466
|
+
### Basic Column Ordering
|
|
467
|
+
|
|
468
|
+
```tsx
|
|
469
|
+
<DataTable
|
|
470
|
+
data={data}
|
|
471
|
+
columns={columns}
|
|
472
|
+
features={{
|
|
473
|
+
selection: true,
|
|
474
|
+
sorting: true,
|
|
475
|
+
filtering: true,
|
|
476
|
+
}}
|
|
477
|
+
columnOrder={['select', 'name', 'email', 'role', 'status']}
|
|
478
|
+
/>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Column Order Behavior
|
|
482
|
+
|
|
483
|
+
#### When `columnOrder` is provided:
|
|
484
|
+
- **Exact positioning**: Columns appear in the exact order specified
|
|
485
|
+
- **Selection column**: Include `'select'` in the array to position the selection column
|
|
486
|
+
- **Actions column**: Include `'actions'` in the array to position the actions column
|
|
487
|
+
- **Data columns**: Use the column's `accessorKey` or `id` to reference data columns
|
|
488
|
+
|
|
489
|
+
#### When `columnOrder` is not provided:
|
|
490
|
+
- **Default behavior**: Selection column first (if enabled), then data columns, then actions column
|
|
491
|
+
- **Automatic ordering**: Columns appear in the order they're defined in the `columns` array
|
|
492
|
+
|
|
493
|
+
### Selection Column Positioning
|
|
494
|
+
|
|
495
|
+
The selection column can be positioned anywhere in the column order:
|
|
496
|
+
|
|
497
|
+
```tsx
|
|
498
|
+
// Selection column first (default when not in columnOrder)
|
|
499
|
+
<DataTable
|
|
500
|
+
data={data}
|
|
501
|
+
columns={columns}
|
|
502
|
+
features={{ selection: true }}
|
|
503
|
+
columnOrder={['select', 'name', 'email', 'role']}
|
|
504
|
+
/>
|
|
505
|
+
|
|
506
|
+
// Selection column in the middle
|
|
507
|
+
<DataTable
|
|
508
|
+
data={data}
|
|
509
|
+
columns={columns}
|
|
510
|
+
features={{ selection: true }}
|
|
511
|
+
columnOrder={['name', 'select', 'email', 'role']}
|
|
512
|
+
/>
|
|
513
|
+
|
|
514
|
+
// Selection column last
|
|
515
|
+
<DataTable
|
|
516
|
+
data={data}
|
|
517
|
+
columns={columns}
|
|
518
|
+
features={{ selection: true }}
|
|
519
|
+
columnOrder={['name', 'email', 'role', 'select']}
|
|
520
|
+
/>
|
|
521
|
+
|
|
522
|
+
// Selection column not in columnOrder - defaults to first position
|
|
523
|
+
<DataTable
|
|
524
|
+
data={data}
|
|
525
|
+
columns={columns}
|
|
526
|
+
features={{ selection: true }}
|
|
527
|
+
columnOrder={['name', 'email', 'role']} // selection will be first
|
|
528
|
+
/>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Actions Column Positioning
|
|
532
|
+
|
|
533
|
+
The actions column can also be positioned anywhere:
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
// Actions column last (default when not in columnOrder)
|
|
537
|
+
<DataTable
|
|
538
|
+
data={data}
|
|
539
|
+
columns={columns}
|
|
540
|
+
features={{ editing: true, deletion: true }}
|
|
541
|
+
columnOrder={['name', 'email', 'role', 'actions']}
|
|
542
|
+
/>
|
|
543
|
+
|
|
544
|
+
// Actions column first
|
|
545
|
+
<DataTable
|
|
546
|
+
data={data}
|
|
547
|
+
columns={columns}
|
|
548
|
+
features={{ editing: true, deletion: true }}
|
|
549
|
+
columnOrder={['actions', 'name', 'email', 'role']}
|
|
550
|
+
/>
|
|
551
|
+
|
|
552
|
+
// Actions column in the middle
|
|
553
|
+
<DataTable
|
|
554
|
+
data={data}
|
|
555
|
+
columns={columns}
|
|
556
|
+
features={{ editing: true, deletion: true }}
|
|
557
|
+
columnOrder={['name', 'actions', 'email', 'role']}
|
|
558
|
+
/>
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Complete Column Ordering Example
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
interface User {
|
|
565
|
+
id: string;
|
|
566
|
+
name: string;
|
|
567
|
+
email: string;
|
|
568
|
+
role: string;
|
|
569
|
+
status: 'active' | 'inactive';
|
|
570
|
+
createdAt: Date;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const columns: DataTableColumn<User>[] = [
|
|
574
|
+
{
|
|
575
|
+
accessorKey: 'name',
|
|
576
|
+
header: 'Name',
|
|
577
|
+
sortable: true,
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
accessorKey: 'email',
|
|
581
|
+
header: 'Email',
|
|
582
|
+
sortable: true,
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
accessorKey: 'role',
|
|
586
|
+
header: 'Role',
|
|
587
|
+
sortable: true,
|
|
588
|
+
enableGrouping: true,
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
accessorKey: 'status',
|
|
592
|
+
header: 'Status',
|
|
593
|
+
sortable: true,
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
accessorKey: 'createdAt',
|
|
597
|
+
header: 'Created',
|
|
598
|
+
sortable: true,
|
|
599
|
+
},
|
|
600
|
+
];
|
|
601
|
+
|
|
602
|
+
function UserTable() {
|
|
603
|
+
return (
|
|
604
|
+
<DataTable
|
|
605
|
+
data={users}
|
|
606
|
+
columns={columns}
|
|
607
|
+
title="User Management"
|
|
608
|
+
features={{
|
|
609
|
+
selection: true,
|
|
610
|
+
sorting: true,
|
|
611
|
+
filtering: true,
|
|
612
|
+
editing: true,
|
|
613
|
+
deletion: true,
|
|
614
|
+
}}
|
|
615
|
+
// Custom column order: selection first, then name, email, role, status, actions, created last
|
|
616
|
+
columnOrder={['select', 'name', 'email', 'role', 'status', 'actions', 'createdAt']}
|
|
617
|
+
onEditRow={handleEdit}
|
|
618
|
+
onDeleteRow={handleDelete}
|
|
619
|
+
/>
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Column Ordering with Hierarchical Data
|
|
625
|
+
|
|
626
|
+
When using hierarchical data, column ordering works the same way:
|
|
627
|
+
|
|
628
|
+
```tsx
|
|
629
|
+
<DataTable
|
|
630
|
+
data={hierarchicalData}
|
|
631
|
+
columns={columns}
|
|
632
|
+
features={{
|
|
633
|
+
hierarchical: true,
|
|
634
|
+
selection: true,
|
|
635
|
+
editing: true,
|
|
636
|
+
deletion: true,
|
|
637
|
+
}}
|
|
638
|
+
hierarchical={{
|
|
639
|
+
enabled: true,
|
|
640
|
+
defaultExpanded: false,
|
|
641
|
+
}}
|
|
642
|
+
// Custom order for hierarchical table
|
|
643
|
+
columnOrder={['select', 'name', 'ingredient', 'quantity', 'actions']}
|
|
644
|
+
/>
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Dynamic Column Ordering
|
|
648
|
+
|
|
649
|
+
You can dynamically change column order based on user preferences or application state:
|
|
650
|
+
|
|
651
|
+
```tsx
|
|
652
|
+
function DynamicUserTable() {
|
|
653
|
+
const [columnOrder, setColumnOrder] = useState(['select', 'name', 'email', 'role', 'status']);
|
|
654
|
+
|
|
655
|
+
const handleColumnReorder = (newOrder: string[]) => {
|
|
656
|
+
setColumnOrder(newOrder);
|
|
657
|
+
// Save to localStorage or user preferences
|
|
658
|
+
localStorage.setItem('userTableColumnOrder', JSON.stringify(newOrder));
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
return (
|
|
662
|
+
<DataTable
|
|
663
|
+
data={users}
|
|
664
|
+
columns={columns}
|
|
665
|
+
features={{
|
|
666
|
+
selection: true,
|
|
667
|
+
sorting: true,
|
|
668
|
+
columnReordering: true, // Enable drag-and-drop reordering
|
|
669
|
+
}}
|
|
670
|
+
columnOrder={columnOrder}
|
|
671
|
+
onColumnOrderChange={handleColumnReorder}
|
|
672
|
+
/>
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Column Ordering Best Practices
|
|
678
|
+
|
|
679
|
+
#### 1. **Consistent Ordering**
|
|
680
|
+
```tsx
|
|
681
|
+
// ✅ Good - Consistent ordering across similar tables
|
|
682
|
+
const userTableOrder = ['select', 'name', 'email', 'role', 'status', 'actions'];
|
|
683
|
+
const productTableOrder = ['select', 'name', 'sku', 'price', 'status', 'actions'];
|
|
684
|
+
|
|
685
|
+
// ❌ Avoid - Inconsistent ordering
|
|
686
|
+
const userTableOrder = ['select', 'name', 'email', 'role', 'status', 'actions'];
|
|
687
|
+
const productTableOrder = ['name', 'select', 'sku', 'actions', 'price', 'status'];
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
#### 2. **Logical Grouping**
|
|
691
|
+
```tsx
|
|
692
|
+
// ✅ Good - Logical grouping of related columns
|
|
693
|
+
columnOrder={[
|
|
694
|
+
'select', // Selection controls
|
|
695
|
+
'name', 'email', // Identity information
|
|
696
|
+
'role', 'status', // Access and state
|
|
697
|
+
'createdAt', // Metadata
|
|
698
|
+
'actions' // Actions
|
|
699
|
+
]}
|
|
700
|
+
|
|
701
|
+
// ❌ Avoid - Random column order
|
|
702
|
+
columnOrder={['email', 'actions', 'name', 'select', 'status', 'role', 'createdAt']}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
#### 3. **User Experience Considerations**
|
|
706
|
+
```tsx
|
|
707
|
+
// ✅ Good - Most important columns first
|
|
708
|
+
columnOrder={['select', 'name', 'email', 'role', 'status', 'actions']}
|
|
709
|
+
|
|
710
|
+
// ✅ Good - Actions column last (standard pattern)
|
|
711
|
+
columnOrder={['select', 'name', 'email', 'role', 'status', 'actions']}
|
|
712
|
+
|
|
713
|
+
// ❌ Avoid - Actions column in the middle (confusing)
|
|
714
|
+
columnOrder={['select', 'name', 'actions', 'email', 'role', 'status']}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
#### 4. **Responsive Considerations**
|
|
718
|
+
```tsx
|
|
719
|
+
// For mobile-first design, put most important columns first
|
|
720
|
+
const mobileColumnOrder = ['select', 'name', 'status', 'actions'];
|
|
721
|
+
const desktopColumnOrder = ['select', 'name', 'email', 'role', 'status', 'createdAt', 'actions'];
|
|
722
|
+
|
|
723
|
+
<DataTable
|
|
724
|
+
data={users}
|
|
725
|
+
columns={columns}
|
|
726
|
+
features={{ selection: true, sorting: true }}
|
|
727
|
+
columnOrder={isMobile ? mobileColumnOrder : desktopColumnOrder}
|
|
728
|
+
/>
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
## Data Management
|
|
732
|
+
|
|
733
|
+
### Sorting
|
|
734
|
+
|
|
735
|
+
```tsx
|
|
736
|
+
<DataTable
|
|
737
|
+
data={data}
|
|
738
|
+
columns={columns}
|
|
739
|
+
features={{ sorting: true }}
|
|
740
|
+
defaultSorting={[
|
|
741
|
+
{ id: 'name', desc: false },
|
|
742
|
+
{ id: 'createdAt', desc: true }
|
|
743
|
+
]}
|
|
744
|
+
/>
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### Filtering
|
|
748
|
+
|
|
749
|
+
```tsx
|
|
750
|
+
<DataTable
|
|
751
|
+
data={data}
|
|
752
|
+
columns={columns}
|
|
753
|
+
features={{ filtering: true }}
|
|
754
|
+
globalFilterFn="includesString"
|
|
755
|
+
columnFilters={[
|
|
756
|
+
{ id: 'status', value: 'active' },
|
|
757
|
+
{ id: 'role', value: 'admin' }
|
|
758
|
+
]}
|
|
759
|
+
/>
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Pagination
|
|
763
|
+
|
|
764
|
+
```tsx
|
|
765
|
+
<DataTable
|
|
766
|
+
data={data}
|
|
767
|
+
columns={columns}
|
|
768
|
+
features={{ pagination: true }}
|
|
769
|
+
/>
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
#### Custom Initial Page Size
|
|
773
|
+
|
|
774
|
+
```tsx
|
|
775
|
+
<DataTable
|
|
776
|
+
data={data}
|
|
777
|
+
columns={columns}
|
|
778
|
+
features={{ pagination: true }}
|
|
779
|
+
initialPageSize={25} // Start with 25 items per page instead of default 10
|
|
780
|
+
/>
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
#### Page Size Validation
|
|
784
|
+
|
|
785
|
+
The DataTable automatically validates the `initialPageSize` against available page size options:
|
|
786
|
+
|
|
787
|
+
```tsx
|
|
788
|
+
<DataTable
|
|
789
|
+
data={data}
|
|
790
|
+
columns={columns}
|
|
791
|
+
features={{ pagination: true }}
|
|
792
|
+
initialPageSize={15} // Will fallback to closest valid option (10) with console warning
|
|
793
|
+
/>
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
**Available page size options by mode:**
|
|
797
|
+
- **Client mode**: `[10, 25, 50]` or `[10, 25, 50, 100]` (depending on data size)
|
|
798
|
+
- **Hybrid mode**: `[50, 100, 250, 500]`
|
|
799
|
+
- **Server mode**: `[25, 50, 100, 250]`
|
|
800
|
+
|
|
801
|
+
### Search
|
|
802
|
+
|
|
803
|
+
```tsx
|
|
804
|
+
<DataTable
|
|
805
|
+
data={data}
|
|
806
|
+
columns={columns}
|
|
807
|
+
features={{ search: true }}
|
|
808
|
+
searchKey="name" // Default search field
|
|
809
|
+
searchPlaceholder="Search users..."
|
|
810
|
+
searchDebounceMs={300}
|
|
811
|
+
/>
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
## Advanced Features
|
|
815
|
+
|
|
816
|
+
### Row Actions
|
|
817
|
+
|
|
818
|
+
Row actions are handled through the `actions` prop, which provides full flexibility for custom action buttons:
|
|
819
|
+
|
|
820
|
+
```tsx
|
|
821
|
+
<DataTable
|
|
822
|
+
data={data}
|
|
823
|
+
columns={columns}
|
|
824
|
+
features={{
|
|
825
|
+
editing: true,
|
|
826
|
+
deletion: true,
|
|
827
|
+
}}
|
|
828
|
+
onEditRow={handleEdit}
|
|
829
|
+
onDeleteRow={handleDelete}
|
|
830
|
+
// Custom actions using the actions prop
|
|
831
|
+
actions={[
|
|
832
|
+
{
|
|
833
|
+
label: 'View Details',
|
|
834
|
+
onClick: (row) => navigate(`/users/${row.original.id}`),
|
|
835
|
+
icon: EyeIcon,
|
|
836
|
+
variant: 'default',
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
label: 'Edit',
|
|
840
|
+
onClick: (row) => editUser(row.original.id),
|
|
841
|
+
icon: EditIcon,
|
|
842
|
+
variant: 'outline',
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
label: 'Share',
|
|
846
|
+
onClick: (row) => shareUser(row.original.id),
|
|
847
|
+
icon: ShareIcon,
|
|
848
|
+
variant: 'secondary',
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
label: 'More Options',
|
|
852
|
+
onClick: (row) => showMoreOptions(row.original.id),
|
|
853
|
+
icon: MoreHorizontalIcon,
|
|
854
|
+
variant: 'ghost',
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
label: 'Archive',
|
|
858
|
+
onClick: (row) => archiveUser(row.original.id),
|
|
859
|
+
icon: ArchiveIcon,
|
|
860
|
+
variant: 'destructive',
|
|
861
|
+
disabled: (row) => row.original.status === 'archived',
|
|
862
|
+
},
|
|
863
|
+
]}
|
|
864
|
+
/>
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
#### Action Configuration
|
|
868
|
+
|
|
869
|
+
Each action supports the following properties:
|
|
870
|
+
|
|
871
|
+
- `label: string` - Display label for the action
|
|
872
|
+
- `onClick: (row: TData) => void` - Action handler function
|
|
873
|
+
- `icon?: ComponentType` - Optional icon component
|
|
874
|
+
- `variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost'` - Button variant
|
|
875
|
+
- `disabled?: (row: TData) => boolean` - Function to determine if action is disabled
|
|
876
|
+
- `testId?: string` - Test ID for testing
|
|
877
|
+
- `visible?: boolean | (row: TData) => boolean` - Control visibility
|
|
878
|
+
- `showInEditMode?: boolean` - Show in edit mode (default: true)
|
|
879
|
+
- `hideInViewMode?: boolean` - Hide in view mode (default: false)
|
|
880
|
+
|
|
881
|
+
#### Action Variants Guide
|
|
882
|
+
|
|
883
|
+
The DataTable supports 5 action variants, each designed for specific use cases:
|
|
884
|
+
|
|
885
|
+
```tsx
|
|
886
|
+
const actions = [
|
|
887
|
+
// DEFAULT - Primary actions (most important)
|
|
888
|
+
{
|
|
889
|
+
label: 'View Details',
|
|
890
|
+
onClick: (row) => viewDetails(row.original.id),
|
|
891
|
+
icon: EyeIcon,
|
|
892
|
+
variant: 'default', // Solid background, primary color
|
|
893
|
+
},
|
|
894
|
+
|
|
895
|
+
// OUTLINE - Secondary actions (important but not primary)
|
|
896
|
+
{
|
|
897
|
+
label: 'Edit',
|
|
898
|
+
onClick: (row) => editItem(row.original.id),
|
|
899
|
+
icon: EditIcon,
|
|
900
|
+
variant: 'outline', // Bordered, transparent background
|
|
901
|
+
},
|
|
902
|
+
|
|
903
|
+
// SECONDARY - Supporting actions (less prominent)
|
|
904
|
+
{
|
|
905
|
+
label: 'Share',
|
|
906
|
+
onClick: (row) => shareItem(row.original.id),
|
|
907
|
+
icon: ShareIcon,
|
|
908
|
+
variant: 'secondary', // Muted background, subtle styling
|
|
909
|
+
},
|
|
910
|
+
|
|
911
|
+
// GHOST - Subtle actions (minimal visual impact)
|
|
912
|
+
{
|
|
913
|
+
label: 'More Options',
|
|
914
|
+
onClick: (row) => showMoreOptions(row.original.id),
|
|
915
|
+
icon: MoreHorizontalIcon,
|
|
916
|
+
variant: 'ghost', // No background, minimal styling
|
|
917
|
+
},
|
|
918
|
+
|
|
919
|
+
// DESTRUCTIVE - Dangerous actions (require caution)
|
|
920
|
+
{
|
|
921
|
+
label: 'Delete',
|
|
922
|
+
onClick: (row) => deleteItem(row.original.id),
|
|
923
|
+
icon: TrashIcon,
|
|
924
|
+
variant: 'destructive', // Red styling to indicate danger
|
|
925
|
+
disabled: (row) => row.original.status === 'locked',
|
|
926
|
+
},
|
|
927
|
+
];
|
|
928
|
+
```
|
|
929
|
+
|
|
930
|
+
**Variant Usage Guidelines:**
|
|
931
|
+
|
|
932
|
+
- **`default`**: Use for the most important action (e.g., "View Details", "Open")
|
|
933
|
+
- **`outline`**: Use for important secondary actions (e.g., "Edit", "Download")
|
|
934
|
+
- **`secondary`**: Use for supporting actions (e.g., "Share", "Copy", "Export")
|
|
935
|
+
- **`ghost`**: Use for subtle actions (e.g., "More Options", "Settings")
|
|
936
|
+
- **`destructive`**: Use for dangerous actions (e.g., "Delete", "Archive", "Remove")
|
|
937
|
+
|
|
938
|
+
#### Hierarchical Actions
|
|
939
|
+
|
|
940
|
+
When using hierarchical rows, you can define different action icons and labels for parent vs child rows:
|
|
941
|
+
|
|
942
|
+
```tsx
|
|
943
|
+
<DataTable
|
|
944
|
+
data={hierarchicalData}
|
|
945
|
+
columns={columns}
|
|
946
|
+
features={{
|
|
947
|
+
hierarchical: true,
|
|
948
|
+
editing: true,
|
|
949
|
+
deletion: true,
|
|
950
|
+
}}
|
|
951
|
+
hierarchical={{
|
|
952
|
+
enabled: true,
|
|
953
|
+
defaultExpanded: false,
|
|
954
|
+
}}
|
|
955
|
+
actions={[
|
|
956
|
+
{
|
|
957
|
+
label: 'View Details',
|
|
958
|
+
icon: EyeIcon,
|
|
959
|
+
// Different icons for parent vs child rows
|
|
960
|
+
parentIcon: EyeIcon,
|
|
961
|
+
childIcon: InfoIcon,
|
|
962
|
+
// Different labels for parent vs child rows
|
|
963
|
+
parentLabel: 'View Recipe',
|
|
964
|
+
childLabel: 'View Ingredient',
|
|
965
|
+
onClick: (row) => {
|
|
966
|
+
console.log('Viewing:', row.isParent ? 'Recipe' : 'Ingredient', row.name);
|
|
967
|
+
},
|
|
968
|
+
variant: 'default',
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
label: 'Edit',
|
|
972
|
+
icon: PencilIcon,
|
|
973
|
+
parentIcon: SettingsIcon,
|
|
974
|
+
childIcon: PencilIcon,
|
|
975
|
+
parentLabel: 'Edit Recipe',
|
|
976
|
+
childLabel: 'Edit Ingredient',
|
|
977
|
+
onClick: (row) => {
|
|
978
|
+
console.log('Editing:', row.isParent ? 'Recipe' : 'Ingredient', row.name);
|
|
979
|
+
},
|
|
980
|
+
variant: 'default',
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
label: 'Copy Recipe',
|
|
984
|
+
icon: CopyIcon,
|
|
985
|
+
parentIcon: CopyIcon,
|
|
986
|
+
parentLabel: 'Duplicate Recipe',
|
|
987
|
+
onClick: (row) => {
|
|
988
|
+
console.log('Copying recipe:', row.name);
|
|
989
|
+
},
|
|
990
|
+
variant: 'outline',
|
|
991
|
+
// Only show for parent rows
|
|
992
|
+
showForParent: true,
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
label: 'Export',
|
|
996
|
+
icon: DownloadIcon,
|
|
997
|
+
childIcon: DownloadIcon,
|
|
998
|
+
childLabel: 'Export Ingredient',
|
|
999
|
+
onClick: (row) => {
|
|
1000
|
+
console.log('Exporting ingredient:', row.item);
|
|
1001
|
+
},
|
|
1002
|
+
variant: 'ghost',
|
|
1003
|
+
// Only show for child rows
|
|
1004
|
+
showForChild: true,
|
|
1005
|
+
},
|
|
1006
|
+
]}
|
|
1007
|
+
/>
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
##### Hierarchical Action Properties
|
|
1011
|
+
|
|
1012
|
+
When using hierarchical rows, actions support additional properties:
|
|
1013
|
+
|
|
1014
|
+
- `showForParent?: boolean` - Only show this action for parent rows
|
|
1015
|
+
- `showForChild?: boolean` - Only show this action for child rows
|
|
1016
|
+
- `parentIcon?: ComponentType` - Icon to use for parent rows (overrides `icon`)
|
|
1017
|
+
- `childIcon?: ComponentType` - Icon to use for child rows (overrides `icon`)
|
|
1018
|
+
- `parentLabel?: string` - Label to use for parent rows (overrides `label`)
|
|
1019
|
+
- `childLabel?: string` - Label to use for child rows (overrides `label`)
|
|
1020
|
+
|
|
1021
|
+
##### Action Visibility Logic
|
|
1022
|
+
|
|
1023
|
+
The action visibility is determined by the following logic:
|
|
1024
|
+
|
|
1025
|
+
1. **Hierarchical filtering**: If `showForParent` is true, action only shows for parent rows
|
|
1026
|
+
2. **Hierarchical filtering**: If `showForChild` is true, action only shows for child rows
|
|
1027
|
+
3. **Standard filtering**: Uses `visible`, `showInEditMode`, `hideInViewMode` properties
|
|
1028
|
+
4. **Disabled state**: Uses `disabled` function to determine if action is disabled
|
|
1029
|
+
|
|
1030
|
+
### Toolbar Actions
|
|
1031
|
+
|
|
1032
|
+
Toolbar actions are automatically generated based on the enabled features. Custom toolbar buttons are not directly supported in the current interface, but you can implement them outside the DataTable component:
|
|
1033
|
+
|
|
1034
|
+
```tsx
|
|
1035
|
+
// Toolbar actions are handled through feature configuration
|
|
1036
|
+
<div>
|
|
1037
|
+
{/* Custom toolbar */}
|
|
1038
|
+
<div className="flex gap-2 mb-4">
|
|
1039
|
+
<Button onClick={() => navigate('/users/create')}>
|
|
1040
|
+
<PlusIcon className="h-4 w-4 mr-2" />
|
|
1041
|
+
Add User
|
|
1042
|
+
</Button>
|
|
1043
|
+
</div>
|
|
1044
|
+
|
|
1045
|
+
<DataTable
|
|
1046
|
+
data={data}
|
|
1047
|
+
columns={columns}
|
|
1048
|
+
features={{
|
|
1049
|
+
search: true,
|
|
1050
|
+
pagination: true,
|
|
1051
|
+
export: true,
|
|
1052
|
+
import: true,
|
|
1053
|
+
creation: true, // Enables built-in create functionality if onCreateRow is provided
|
|
1054
|
+
}}
|
|
1055
|
+
onCreateRow={handleCreate}
|
|
1056
|
+
onImport={handleImport}
|
|
1057
|
+
exportFilename="users"
|
|
1058
|
+
/>
|
|
1059
|
+
</div>
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
### Hierarchical Parent/Child Rows
|
|
1063
|
+
|
|
1064
|
+
The DataTable supports hierarchical parent/child row relationships with built-in expand/collapse functionality, expand/collapse all controls, and proper column rendering for different row types.
|
|
1065
|
+
|
|
1066
|
+
#### Data Structure
|
|
1067
|
+
|
|
1068
|
+
Your data must conform to the `HierarchicalDataRow` interface:
|
|
1069
|
+
|
|
1070
|
+
```typescript
|
|
1071
|
+
interface HierarchicalDataRow extends DataRecord {
|
|
1072
|
+
/** Whether this row is a parent row */
|
|
1073
|
+
isParent: boolean;
|
|
1074
|
+
/** For child rows, references the parent row ID */
|
|
1075
|
+
parentId?: string;
|
|
1076
|
+
/** Unique identifier for this row */
|
|
1077
|
+
id: string;
|
|
1078
|
+
// ... your actual data properties
|
|
1079
|
+
}
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
#### Configuration
|
|
1083
|
+
|
|
1084
|
+
Enable hierarchical functionality through the `features` prop and configure it with the `hierarchical` prop:
|
|
1085
|
+
|
|
1086
|
+
```tsx
|
|
1087
|
+
<DataTable
|
|
1088
|
+
data={hierarchicalData}
|
|
1089
|
+
columns={columns}
|
|
1090
|
+
features={{
|
|
1091
|
+
// ... other features
|
|
1092
|
+
hierarchical: true, // Enable hierarchical functionality
|
|
1093
|
+
}}
|
|
1094
|
+
hierarchical={{
|
|
1095
|
+
enabled: true,
|
|
1096
|
+
defaultExpanded: false, // true = all expanded, false = all collapsed, array = specific IDs
|
|
1097
|
+
onExpandedChange: (expandedIds) => console.log('Expanded:', expandedIds),
|
|
1098
|
+
expandButton: CustomExpandButton, // Optional custom expand button
|
|
1099
|
+
indentSize: 24, // Indentation for child rows in pixels
|
|
1100
|
+
parentRowClassName: 'bg-main-50 font-medium', // Custom styling for parent rows
|
|
1101
|
+
childRowClassName: 'bg-sec-25', // Custom styling for child rows
|
|
1102
|
+
}}
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
**Default Collapsed State:**
|
|
1106
|
+
To make parent rows collapsed by default (recommended for better UX), set `defaultExpanded: false`. This ensures users see only parent rows initially and must click to expand and view child rows.
|
|
1107
|
+
|
|
1108
|
+
#### Column Configuration
|
|
1109
|
+
|
|
1110
|
+
Configure how columns render for different row types:
|
|
1111
|
+
|
|
1112
|
+
```tsx
|
|
1113
|
+
const columns: DataTableColumn<YourDataType>[] = [
|
|
1114
|
+
{
|
|
1115
|
+
accessorKey: 'code',
|
|
1116
|
+
header: 'Code',
|
|
1117
|
+
// Render differently for parent vs child rows
|
|
1118
|
+
renderForParent: (row) => (
|
|
1119
|
+
<div className="flex items-center gap-2">
|
|
1120
|
+
<strong>{row.code}</strong>
|
|
1121
|
+
</div>
|
|
1122
|
+
),
|
|
1123
|
+
renderForChild: () => '', // Blank for child rows
|
|
1124
|
+
hideForChild: true, // Hide this column for child rows
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
accessorKey: 'name',
|
|
1128
|
+
header: 'Name',
|
|
1129
|
+
renderForParent: (row) => <strong>{row.name}</strong>,
|
|
1130
|
+
renderForChild: () => '', // Blank for child rows
|
|
1131
|
+
hideForChild: true,
|
|
1132
|
+
},
|
|
1133
|
+
{
|
|
1134
|
+
accessorKey: 'ingredient',
|
|
1135
|
+
header: 'Ingredient',
|
|
1136
|
+
renderForParent: () => '', // Blank for parent rows
|
|
1137
|
+
renderForChild: (row) => <span className="ml-4">{row.ingredient}</span>,
|
|
1138
|
+
hideForParent: true, // Hide this column for parent rows
|
|
1139
|
+
},
|
|
1140
|
+
];
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
#### Column Properties
|
|
1144
|
+
|
|
1145
|
+
- `renderForParent?: (row: TData) => React.ReactNode` - Custom renderer for parent rows
|
|
1146
|
+
- `renderForChild?: (row: TData) => React.ReactNode` - Custom renderer for child rows
|
|
1147
|
+
- `hideForParent?: boolean` - Hide this column for parent rows
|
|
1148
|
+
- `hideForChild?: boolean` - Hide this column for child rows
|
|
1149
|
+
|
|
1150
|
+
#### Expand/Collapse All Controls
|
|
1151
|
+
|
|
1152
|
+
The DataTable automatically adds an expand/collapse all button in the header when hierarchical mode is enabled:
|
|
1153
|
+
|
|
1154
|
+
**Features:**
|
|
1155
|
+
- **Expand All Button (▶️)**: Expands all parent rows to show their children
|
|
1156
|
+
- **Collapse All Button (🔽)**: Collapses all parent rows to hide their children
|
|
1157
|
+
- **Smart State Detection**: Button automatically updates based on current expansion state
|
|
1158
|
+
- **Accessibility**: Proper ARIA labels and keyboard navigation support
|
|
1159
|
+
|
|
1160
|
+
**Usage:**
|
|
1161
|
+
```tsx
|
|
1162
|
+
<DataTable
|
|
1163
|
+
data={hierarchicalData}
|
|
1164
|
+
columns={columns}
|
|
1165
|
+
features={{ hierarchical: true }}
|
|
1166
|
+
hierarchical={{
|
|
1167
|
+
enabled: true,
|
|
1168
|
+
defaultExpanded: false, // Start with all collapsed
|
|
1169
|
+
onExpandedChange: (expandedIds) => {
|
|
1170
|
+
console.log('Expanded rows:', expandedIds);
|
|
1171
|
+
},
|
|
1172
|
+
}}
|
|
1173
|
+
/>
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
**Button Behavior:**
|
|
1177
|
+
- Shows ▶️ when some or all parent rows are collapsed
|
|
1178
|
+
- Shows 🔽 when all parent rows are expanded
|
|
1179
|
+
- Only appears when there are parent rows with children
|
|
1180
|
+
- Positioned in the first column of the table header
|
|
1181
|
+
|
|
1182
|
+
#### Hierarchical Sorting
|
|
1183
|
+
|
|
1184
|
+
When hierarchical mode is enabled, sorting behavior is optimized to preserve the parent-child relationship structure:
|
|
1185
|
+
|
|
1186
|
+
**Parent Row Sorting:**
|
|
1187
|
+
- Parent rows maintain their original order and are not affected by sorting
|
|
1188
|
+
- Only child rows within each parent group are sorted
|
|
1189
|
+
- This prevents parent rows from being moved to unexpected positions
|
|
1190
|
+
|
|
1191
|
+
**Child Row Sorting:**
|
|
1192
|
+
- Child rows are sorted within their parent group only
|
|
1193
|
+
- Sorting by a child-specific column (e.g., "Diet", "Ingredient") will sort children within each parent
|
|
1194
|
+
- The parent row remains at the top of its group
|
|
1195
|
+
|
|
1196
|
+
**Example:**
|
|
1197
|
+
```tsx
|
|
1198
|
+
// When sorting by "Diet" column:
|
|
1199
|
+
// ✅ Correct behavior:
|
|
1200
|
+
// Parent: Caesar Salad
|
|
1201
|
+
// ├─ Child: Dairy Free (Sour cream)
|
|
1202
|
+
// ├─ Child: Egg Free (Mayonnaise)
|
|
1203
|
+
// └─ Child: GF & Vegan (Lettuce)
|
|
1204
|
+
// Parent: Beef Stir Fry
|
|
1205
|
+
// ├─ Child: GF & Vegan (Vegetables)
|
|
1206
|
+
// └─ Child: Halal (Beef)
|
|
1207
|
+
|
|
1208
|
+
// ❌ Incorrect behavior (what we prevent):
|
|
1209
|
+
// Child: Dairy Free (Sour cream)
|
|
1210
|
+
// Child: Egg Free (Mayonnaise)
|
|
1211
|
+
// Child: GF & Vegan (Lettuce)
|
|
1212
|
+
// Child: GF & Vegan (Vegetables)
|
|
1213
|
+
// Child: Halal (Beef)
|
|
1214
|
+
// Parent: Caesar Salad (moved to end)
|
|
1215
|
+
// Parent: Beef Stir Fry (moved to end)
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
**Sorting Configuration:**
|
|
1219
|
+
- All existing sorting features work with hierarchical data
|
|
1220
|
+
- Multi-column sorting is supported
|
|
1221
|
+
- Server-side sorting is compatible
|
|
1222
|
+
- Column-specific sorting behavior is preserved
|
|
1223
|
+
|
|
1224
|
+
#### Example Use Case
|
|
1225
|
+
|
|
1226
|
+
```tsx
|
|
1227
|
+
// Dishes and ingredients example
|
|
1228
|
+
const dishData = [
|
|
1229
|
+
// Parent rows (dishes)
|
|
1230
|
+
{ id: 'dish-1', isParent: true, code: 'D001', name: 'Caesar Salad', type: 'Salad' },
|
|
1231
|
+
{ id: 'dish-2', isParent: true, code: 'D002', name: 'Beef Stir Fry', type: 'Main Course' },
|
|
1232
|
+
|
|
1233
|
+
// Child rows (ingredients) for Caesar Salad
|
|
1234
|
+
{ id: 'ing-1-1', isParent: false, parentId: 'dish-1', ingredient: 'Lettuce', quantity: '200g' },
|
|
1235
|
+
{ id: 'ing-1-2', isParent: false, parentId: 'dish-1', ingredient: 'Chicken', quantity: '150g' },
|
|
1236
|
+
|
|
1237
|
+
// Child rows (ingredients) for Beef Stir Fry
|
|
1238
|
+
{ id: 'ing-2-1', isParent: false, parentId: 'dish-2', ingredient: 'Beef Strips', quantity: '250g' },
|
|
1239
|
+
{ id: 'ing-2-2', isParent: false, parentId: 'dish-2', ingredient: 'Mixed Vegetables', quantity: '200g' },
|
|
1240
|
+
];
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
### Row Selection and Bulk Operations
|
|
1244
|
+
|
|
1245
|
+
```tsx
|
|
1246
|
+
function SelectableUserTable() {
|
|
1247
|
+
const [selectedRows, setSelectedRows] = useState<Record<string, boolean>>({});
|
|
1248
|
+
const [data, setData] = useState<User[]>(initialData);
|
|
1249
|
+
|
|
1250
|
+
const handleBulkDelete = (selectedRows: Record<string, boolean>) => {
|
|
1251
|
+
// Get the selected row IDs
|
|
1252
|
+
const selectedRowIds = Object.keys(selectedRows).filter(id => selectedRows[id]);
|
|
1253
|
+
|
|
1254
|
+
// Get the actual data for selected rows
|
|
1255
|
+
const selectedData = data.filter(row => {
|
|
1256
|
+
const rowId = getRowId ? getRowId(row, data.indexOf(row)) : row.id;
|
|
1257
|
+
return selectedRowIds.includes(rowId);
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
// Perform your deletion logic
|
|
1261
|
+
console.log('Deleting selected rows:', selectedData);
|
|
1262
|
+
|
|
1263
|
+
// Update your data state
|
|
1264
|
+
setData(prevData =>
|
|
1265
|
+
prevData.filter(row => {
|
|
1266
|
+
const rowId = getRowId ? getRowId(row, prevData.indexOf(row)) : row.id;
|
|
1267
|
+
return !selectedRowIds.includes(rowId);
|
|
1268
|
+
})
|
|
1269
|
+
);
|
|
1270
|
+
|
|
1271
|
+
// Clear selection after deletion
|
|
1272
|
+
setSelectedRows({});
|
|
1273
|
+
};
|
|
1274
|
+
|
|
1275
|
+
return (
|
|
1276
|
+
<DataTable
|
|
1277
|
+
data={data}
|
|
1278
|
+
columns={columns}
|
|
1279
|
+
features={{
|
|
1280
|
+
selection: true,
|
|
1281
|
+
deletion: true, // Required for delete functionality
|
|
1282
|
+
deleteSelected: true, // Enables the delete selected button
|
|
1283
|
+
bulkOperations: true,
|
|
1284
|
+
}}
|
|
1285
|
+
onRowSelectionChange={setSelectedRows}
|
|
1286
|
+
onDeleteSelected={handleBulkDelete}
|
|
1287
|
+
getRowId={(row) => row.id} // Important: provide getRowId for proper row identification
|
|
1288
|
+
/>
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
#### Alternative: Using Individual onDeleteRow (Fallback)
|
|
1294
|
+
|
|
1295
|
+
If you don't provide `onDeleteSelected`, the DataTable will automatically fall back to calling `onDeleteRow` for each selected row:
|
|
1296
|
+
|
|
1297
|
+
```tsx
|
|
1298
|
+
function SelectableUserTable() {
|
|
1299
|
+
const [data, setData] = useState<User[]>(initialData);
|
|
1300
|
+
|
|
1301
|
+
const handleDeleteRow = (row: User) => {
|
|
1302
|
+
console.log('Deleting row:', row);
|
|
1303
|
+
|
|
1304
|
+
// Update your data state
|
|
1305
|
+
setData(prevData => prevData.filter(item => item.id !== row.id));
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1308
|
+
return (
|
|
1309
|
+
<DataTable
|
|
1310
|
+
data={data}
|
|
1311
|
+
columns={columns}
|
|
1312
|
+
features={{
|
|
1313
|
+
selection: true,
|
|
1314
|
+
deletion: true,
|
|
1315
|
+
deleteSelected: true,
|
|
1316
|
+
}}
|
|
1317
|
+
onDeleteRow={handleDeleteRow} // This will be called for each selected row
|
|
1318
|
+
getRowId={(row) => row.id}
|
|
1319
|
+
/>
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
```
|
|
1323
|
+
|
|
1324
|
+
### Column Visibility
|
|
1325
|
+
|
|
1326
|
+
```tsx
|
|
1327
|
+
<DataTable
|
|
1328
|
+
data={data}
|
|
1329
|
+
columns={columns}
|
|
1330
|
+
features={{ columnVisibility: true }}
|
|
1331
|
+
defaultColumnVisibility={{
|
|
1332
|
+
id: false,
|
|
1333
|
+
createdAt: false,
|
|
1334
|
+
}}
|
|
1335
|
+
/>
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
### Data Grouping
|
|
1339
|
+
|
|
1340
|
+
```tsx
|
|
1341
|
+
<DataTable
|
|
1342
|
+
data={data}
|
|
1343
|
+
columns={columns}
|
|
1344
|
+
features={{ grouping: true }}
|
|
1345
|
+
grouping={['role', 'status']}
|
|
1346
|
+
/>
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
## Performance Optimization
|
|
1350
|
+
|
|
1351
|
+
### Virtual Scrolling
|
|
1352
|
+
|
|
1353
|
+
```tsx
|
|
1354
|
+
<DataTable
|
|
1355
|
+
data={largeDataset}
|
|
1356
|
+
columns={columns}
|
|
1357
|
+
features={{ virtualization: true }}
|
|
1358
|
+
virtualHeight={600} // Height of the virtual scrolling container
|
|
1359
|
+
performance={{
|
|
1360
|
+
virtualScrolling: true,
|
|
1361
|
+
memoryOptimization: true,
|
|
1362
|
+
}}
|
|
1363
|
+
/>
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
### Lazy Loading
|
|
1367
|
+
|
|
1368
|
+
```tsx
|
|
1369
|
+
function LazyLoadingTable() {
|
|
1370
|
+
const [data, setData] = useState<User[]>([]);
|
|
1371
|
+
const [loading, setLoading] = useState(false);
|
|
1372
|
+
const [hasMore, setHasMore] = useState(true);
|
|
1373
|
+
|
|
1374
|
+
const loadMoreData = async () => {
|
|
1375
|
+
setLoading(true);
|
|
1376
|
+
const newData = await fetchUsers(data.length, 50);
|
|
1377
|
+
setData(prev => [...prev, ...newData]);
|
|
1378
|
+
setHasMore(newData.length === 50);
|
|
1379
|
+
setLoading(false);
|
|
1380
|
+
};
|
|
1381
|
+
|
|
1382
|
+
return (
|
|
1383
|
+
<DataTable
|
|
1384
|
+
data={data}
|
|
1385
|
+
columns={columns}
|
|
1386
|
+
loading={loading}
|
|
1387
|
+
onLoadMore={hasMore ? loadMoreData : undefined}
|
|
1388
|
+
hasMore={hasMore}
|
|
1389
|
+
/>
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
### Optimized Rendering
|
|
1395
|
+
|
|
1396
|
+
```tsx
|
|
1397
|
+
<DataTable
|
|
1398
|
+
data={data}
|
|
1399
|
+
columns={columns}
|
|
1400
|
+
features={{
|
|
1401
|
+
virtualization: true,
|
|
1402
|
+
performanceMetrics: true,
|
|
1403
|
+
}}
|
|
1404
|
+
performance={{
|
|
1405
|
+
virtualScrolling: true,
|
|
1406
|
+
memoryOptimization: true,
|
|
1407
|
+
renderOptimization: true,
|
|
1408
|
+
}}
|
|
1409
|
+
showPerformanceMetrics={true}
|
|
1410
|
+
virtualHeight={600}
|
|
1411
|
+
/>
|
|
1412
|
+
```
|
|
1413
|
+
|
|
1414
|
+
## Integration with RBAC
|
|
1415
|
+
|
|
1416
|
+
### Permission-Based Actions
|
|
1417
|
+
|
|
1418
|
+
```tsx
|
|
1419
|
+
function SecureUserTable() {
|
|
1420
|
+
const { checkPermission } = usePermissionCache();
|
|
1421
|
+
|
|
1422
|
+
const [permissions, setPermissions] = useState({
|
|
1423
|
+
canCreate: false,
|
|
1424
|
+
canUpdate: false,
|
|
1425
|
+
canDelete: false,
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
useEffect(() => {
|
|
1429
|
+
const loadPermissions = async () => {
|
|
1430
|
+
const results = await Promise.all([
|
|
1431
|
+
checkPermission('create', 'user-management'),
|
|
1432
|
+
checkPermission('update', 'user-management'),
|
|
1433
|
+
checkPermission('delete', 'user-management'),
|
|
1434
|
+
]);
|
|
1435
|
+
|
|
1436
|
+
setPermissions({
|
|
1437
|
+
canCreate: results[0],
|
|
1438
|
+
canUpdate: results[1],
|
|
1439
|
+
canDelete: results[2],
|
|
1440
|
+
});
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
loadPermissions();
|
|
1444
|
+
}, [checkPermission]);
|
|
1445
|
+
|
|
1446
|
+
return (
|
|
1447
|
+
<DataTable
|
|
1448
|
+
data={users}
|
|
1449
|
+
columns={columns}
|
|
1450
|
+
features={{
|
|
1451
|
+
search: true,
|
|
1452
|
+
pagination: true,
|
|
1453
|
+
creation: permissions.canCreate,
|
|
1454
|
+
editing: permissions.canUpdate,
|
|
1455
|
+
deletion: permissions.canDelete,
|
|
1456
|
+
rowActions: permissions.canUpdate || permissions.canDelete,
|
|
1457
|
+
}}
|
|
1458
|
+
onEditRow={permissions.canUpdate ? (row) => navigate(`/users/${row.original.id}/edit`) : undefined}
|
|
1459
|
+
onDeleteRow={permissions.canDelete ? (row) => handleDelete(row.original.id) : undefined}
|
|
1460
|
+
// Custom actions using deprecated actions prop (still supported)
|
|
1461
|
+
actions={
|
|
1462
|
+
permissions.canUpdate || permissions.canDelete ? [
|
|
1463
|
+
{
|
|
1464
|
+
label: 'View Details',
|
|
1465
|
+
onClick: (row) => navigate(`/users/${row.original.id}`),
|
|
1466
|
+
}
|
|
1467
|
+
] : []
|
|
1468
|
+
}
|
|
1469
|
+
/>
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
```
|
|
1473
|
+
|
|
1474
|
+
## Practical Examples
|
|
1475
|
+
|
|
1476
|
+
### Custom Page Size Configuration
|
|
1477
|
+
|
|
1478
|
+
Here's a complete example showing how to configure a DataTable with custom initial page size:
|
|
1479
|
+
|
|
1480
|
+
```tsx
|
|
1481
|
+
import { DataTable, type DataTableColumn } from '@jmruthers/pace-core';
|
|
1482
|
+
|
|
1483
|
+
interface User {
|
|
1484
|
+
id: string;
|
|
1485
|
+
name: string;
|
|
1486
|
+
email: string;
|
|
1487
|
+
role: string;
|
|
1488
|
+
status: 'active' | 'inactive';
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
const columns: DataTableColumn<User>[] = [
|
|
1492
|
+
{
|
|
1493
|
+
accessorKey: 'name',
|
|
1494
|
+
header: 'Name',
|
|
1495
|
+
sortable: true,
|
|
1496
|
+
searchable: true,
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
accessorKey: 'email',
|
|
1500
|
+
header: 'Email',
|
|
1501
|
+
sortable: true,
|
|
1502
|
+
searchable: true,
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
accessorKey: 'role',
|
|
1506
|
+
header: 'Role',
|
|
1507
|
+
sortable: true,
|
|
1508
|
+
enableGrouping: true,
|
|
1509
|
+
},
|
|
1510
|
+
{
|
|
1511
|
+
accessorKey: 'status',
|
|
1512
|
+
header: 'Status',
|
|
1513
|
+
sortable: true,
|
|
1514
|
+
cell: ({ row }) => (
|
|
1515
|
+
<span className={`px-2 py-1 rounded text-xs ${
|
|
1516
|
+
row.original.status === 'active'
|
|
1517
|
+
? 'bg-main-100 text-main-800'
|
|
1518
|
+
: 'bg-acc-100 text-acc-800'
|
|
1519
|
+
}`}>
|
|
1520
|
+
{row.original.status}
|
|
1521
|
+
</span>
|
|
1522
|
+
),
|
|
1523
|
+
},
|
|
1524
|
+
];
|
|
1525
|
+
|
|
1526
|
+
function UserManagementTable() {
|
|
1527
|
+
const [users, setUsers] = useState<User[]>([]);
|
|
1528
|
+
const [loading, setLoading] = useState(true);
|
|
1529
|
+
|
|
1530
|
+
useEffect(() => {
|
|
1531
|
+
// Load users data
|
|
1532
|
+
loadUsers().then(setUsers).finally(() => setLoading(false));
|
|
1533
|
+
}, []);
|
|
1534
|
+
|
|
1535
|
+
return (
|
|
1536
|
+
<DataTable
|
|
1537
|
+
data={users}
|
|
1538
|
+
columns={columns}
|
|
1539
|
+
title="User Management"
|
|
1540
|
+
description="Manage system users with custom page size"
|
|
1541
|
+
features={{
|
|
1542
|
+
search: true,
|
|
1543
|
+
pagination: true,
|
|
1544
|
+
sorting: true,
|
|
1545
|
+
filtering: true,
|
|
1546
|
+
selection: true,
|
|
1547
|
+
creation: true,
|
|
1548
|
+
editing: true,
|
|
1549
|
+
deletion: true,
|
|
1550
|
+
deleteSelected: true,
|
|
1551
|
+
export: true,
|
|
1552
|
+
import: true,
|
|
1553
|
+
grouping: true,
|
|
1554
|
+
columnVisibility: true,
|
|
1555
|
+
columnReordering: true,
|
|
1556
|
+
autoColumnSizing: true,
|
|
1557
|
+
}}
|
|
1558
|
+
initialPageSize={25} // Start with 25 users per page
|
|
1559
|
+
isLoading={loading}
|
|
1560
|
+
onEditRow={(row, data) => {
|
|
1561
|
+
console.log('Editing user:', row.id, data);
|
|
1562
|
+
// Handle user edit
|
|
1563
|
+
}}
|
|
1564
|
+
onDeleteRow={(row) => {
|
|
1565
|
+
console.log('Deleting user:', row.id);
|
|
1566
|
+
// Handle user deletion
|
|
1567
|
+
}}
|
|
1568
|
+
onCreateRow={(data) => {
|
|
1569
|
+
console.log('Creating user:', data);
|
|
1570
|
+
// Handle user creation
|
|
1571
|
+
}}
|
|
1572
|
+
onImport={(data) => {
|
|
1573
|
+
console.log('Importing users:', data);
|
|
1574
|
+
// Handle user import
|
|
1575
|
+
}}
|
|
1576
|
+
onRowSelectionChange={(selection) => {
|
|
1577
|
+
console.log('Selection changed:', selection);
|
|
1578
|
+
// Handle selection changes
|
|
1579
|
+
}}
|
|
1580
|
+
onDeleteSelected={(selectedRows) => {
|
|
1581
|
+
console.log('Bulk deleting users:', selectedRows);
|
|
1582
|
+
// Handle bulk deletion
|
|
1583
|
+
}}
|
|
1584
|
+
/>
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
```
|
|
1588
|
+
|
|
1589
|
+
### Page Size Validation Example
|
|
1590
|
+
|
|
1591
|
+
```tsx
|
|
1592
|
+
// This will show a console warning and use the closest valid option
|
|
1593
|
+
<DataTable
|
|
1594
|
+
data={largeDataset}
|
|
1595
|
+
columns={columns}
|
|
1596
|
+
features={{ pagination: true }}
|
|
1597
|
+
initialPageSize={15} // Invalid - will fallback to 10 with warning
|
|
1598
|
+
paginationMode="client" // Uses [10, 25, 50] options
|
|
1599
|
+
/>
|
|
1600
|
+
|
|
1601
|
+
// This will work correctly
|
|
1602
|
+
<DataTable
|
|
1603
|
+
data={largeDataset}
|
|
1604
|
+
columns={columns}
|
|
1605
|
+
features={{ pagination: true }}
|
|
1606
|
+
initialPageSize={25} // Valid option
|
|
1607
|
+
paginationMode="client"
|
|
1608
|
+
/>
|
|
1609
|
+
```
|
|
1610
|
+
|
|
1611
|
+
## Best Practices
|
|
1612
|
+
|
|
1613
|
+
### Data Processing Best Practices
|
|
1614
|
+
- **Stable Data References**: Ensure your data processing doesn't create new object references on every render
|
|
1615
|
+
- **Memoization**: Use `useMemo` with proper dependency arrays to prevent unnecessary re-computations
|
|
1616
|
+
- **Avoid Complex Transformations in useMemo**: Move complex data transformations to the backend or data fetching layer when possible
|
|
1617
|
+
- **Data Comparison**: Use shallow comparison to detect actual data changes before updating state
|
|
1618
|
+
|
|
1619
|
+
#### ✅ Good Data Processing Patterns
|
|
1620
|
+
```tsx
|
|
1621
|
+
// Simple, stable data processing
|
|
1622
|
+
const processedData = useMemo(() => {
|
|
1623
|
+
if (!data || data.length === 0) return [];
|
|
1624
|
+
return data; // Direct usage, no transformations
|
|
1625
|
+
}, [data]);
|
|
1626
|
+
|
|
1627
|
+
// Complex processing with stable references
|
|
1628
|
+
const processedData = useMemo(() => {
|
|
1629
|
+
if (!data || data.length === 0) return [];
|
|
1630
|
+
|
|
1631
|
+
// Use stable references and avoid creating new objects unnecessarily
|
|
1632
|
+
return data.map(item => ({
|
|
1633
|
+
...item,
|
|
1634
|
+
// Simple transformations only
|
|
1635
|
+
displayName: item.firstName + ' ' + item.lastName,
|
|
1636
|
+
}));
|
|
1637
|
+
}, [data]); // Single dependency
|
|
1638
|
+
```
|
|
1639
|
+
|
|
1640
|
+
#### ❌ Avoid These Patterns
|
|
1641
|
+
```tsx
|
|
1642
|
+
// This can cause infinite loops
|
|
1643
|
+
const processedData = useMemo(() => {
|
|
1644
|
+
return data.map(item => ({
|
|
1645
|
+
...item,
|
|
1646
|
+
// Complex transformations that create new objects
|
|
1647
|
+
computed_field: relatedData?.find(rel => rel.id === item.relation_id)?.name || 'Unknown',
|
|
1648
|
+
formatted_date: item.date ? format(new Date(item.date), 'MMM dd, yyyy') : 'No date',
|
|
1649
|
+
}));
|
|
1650
|
+
}, [data, relatedData]); // Multiple dependencies with complex processing
|
|
1651
|
+
```
|
|
1652
|
+
|
|
1653
|
+
#### 🔧 Recommended Solutions for Complex Data
|
|
1654
|
+
1. **Backend Processing**: Move complex transformations to your API layer
|
|
1655
|
+
2. **Supabase Relations**: Use database relations instead of client-side lookups
|
|
1656
|
+
3. **Separate Hooks**: Create dedicated hooks for data processing
|
|
1657
|
+
4. **Stable References**: Use `useCallback` and `useMemo` with proper dependencies
|
|
1658
|
+
|
|
1659
|
+
### 1. Use the Features Configuration Properly
|
|
1660
|
+
|
|
1661
|
+
```tsx
|
|
1662
|
+
// ✅ Good - Clear feature configuration
|
|
1663
|
+
<DataTable
|
|
1664
|
+
data={data}
|
|
1665
|
+
columns={columns}
|
|
1666
|
+
features={{
|
|
1667
|
+
search: true,
|
|
1668
|
+
pagination: true,
|
|
1669
|
+
editing: userCanEdit,
|
|
1670
|
+
deletion: userCanDelete,
|
|
1671
|
+
}}
|
|
1672
|
+
onEditRow={handleEdit}
|
|
1673
|
+
onDeleteRow={handleDelete}
|
|
1674
|
+
/>
|
|
1675
|
+
|
|
1676
|
+
// ❌ Avoid - Don't enable features without handlers
|
|
1677
|
+
<DataTable
|
|
1678
|
+
data={data}
|
|
1679
|
+
columns={columns}
|
|
1680
|
+
features={{
|
|
1681
|
+
editing: true, // But no onEditRow handler
|
|
1682
|
+
deletion: true, // But no onDeleteRow handler
|
|
1683
|
+
}}
|
|
1684
|
+
/>
|
|
1685
|
+
|
|
1686
|
+
// ❌ Avoid - Don't use deprecated individual props
|
|
1687
|
+
<DataTable
|
|
1688
|
+
data={data}
|
|
1689
|
+
columns={columns}
|
|
1690
|
+
// These props are deprecated and should not be used
|
|
1691
|
+
// Use the features prop instead
|
|
1692
|
+
/>
|
|
1693
|
+
```
|
|
1694
|
+
|
|
1695
|
+
### 2. Use Proper TypeScript Types
|
|
1696
|
+
|
|
1697
|
+
```tsx
|
|
1698
|
+
// ✅ Good - Strong typing
|
|
1699
|
+
interface User {
|
|
1700
|
+
id: string;
|
|
1701
|
+
name: string;
|
|
1702
|
+
email: string;
|
|
1703
|
+
role: UserRole;
|
|
1704
|
+
status: UserStatus;
|
|
1705
|
+
createdAt: Date;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
const columns: DataTableColumn<User>[] = [
|
|
1709
|
+
{
|
|
1710
|
+
accessorKey: 'name',
|
|
1711
|
+
header: 'Name',
|
|
1712
|
+
sortable: true,
|
|
1713
|
+
},
|
|
1714
|
+
];
|
|
1715
|
+
|
|
1716
|
+
// ❌ Avoid - Weak typing
|
|
1717
|
+
const columns = [
|
|
1718
|
+
{
|
|
1719
|
+
accessorKey: 'name',
|
|
1720
|
+
header: 'Name',
|
|
1721
|
+
},
|
|
1722
|
+
];
|
|
1723
|
+
```
|
|
1724
|
+
|
|
1725
|
+
### 2. Optimize for Performance
|
|
1726
|
+
|
|
1727
|
+
```tsx
|
|
1728
|
+
// ✅ Good - Memoized components with performance optimization
|
|
1729
|
+
const UserTable = React.memo(() => {
|
|
1730
|
+
const [data, setData] = useState<User[]>([]);
|
|
1731
|
+
|
|
1732
|
+
return (
|
|
1733
|
+
<DataTable
|
|
1734
|
+
data={data}
|
|
1735
|
+
columns={columns}
|
|
1736
|
+
features={{ virtualization: true }}
|
|
1737
|
+
performance={{
|
|
1738
|
+
memoryOptimization: true,
|
|
1739
|
+
renderOptimization: true,
|
|
1740
|
+
}}
|
|
1741
|
+
/>
|
|
1742
|
+
);
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1745
|
+
// ❌ Avoid - Unnecessary re-renders
|
|
1746
|
+
function UserTable() {
|
|
1747
|
+
return (
|
|
1748
|
+
<DataTable
|
|
1749
|
+
data={data}
|
|
1750
|
+
columns={columns}
|
|
1751
|
+
/>
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
```
|
|
1755
|
+
|
|
1756
|
+
### 3. Handle Loading States
|
|
1757
|
+
|
|
1758
|
+
```tsx
|
|
1759
|
+
// ✅ Good - Proper loading states
|
|
1760
|
+
function UserTable() {
|
|
1761
|
+
const [loading, setLoading] = useState(true);
|
|
1762
|
+
const [data, setData] = useState<User[]>([]);
|
|
1763
|
+
|
|
1764
|
+
useEffect(() => {
|
|
1765
|
+
loadUsers().then(setData).finally(() => setLoading(false));
|
|
1766
|
+
}, []);
|
|
1767
|
+
|
|
1768
|
+
return (
|
|
1769
|
+
<DataTable
|
|
1770
|
+
data={data}
|
|
1771
|
+
columns={columns}
|
|
1772
|
+
isLoading={loading}
|
|
1773
|
+
// loadingText is not directly supported - use custom loadingComponent if needed
|
|
1774
|
+
/>
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
```
|
|
1778
|
+
|
|
1779
|
+
### 4. Implement Error Handling
|
|
1780
|
+
|
|
1781
|
+
```tsx
|
|
1782
|
+
// ✅ Good - Error handling
|
|
1783
|
+
function UserTable() {
|
|
1784
|
+
const [error, setError] = useState<string | null>(null);
|
|
1785
|
+
|
|
1786
|
+
const handleDelete = async (userId: string) => {
|
|
1787
|
+
try {
|
|
1788
|
+
await deleteUser(userId);
|
|
1789
|
+
// Refresh data
|
|
1790
|
+
} catch (err) {
|
|
1791
|
+
setError('Failed to delete user');
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1795
|
+
return (
|
|
1796
|
+
<div>
|
|
1797
|
+
{error && (
|
|
1798
|
+
<Alert variant="destructive">
|
|
1799
|
+
<AlertDescription>{error}</AlertDescription>
|
|
1800
|
+
</Alert>
|
|
1801
|
+
)}
|
|
1802
|
+
<DataTable
|
|
1803
|
+
data={data}
|
|
1804
|
+
columns={columns}
|
|
1805
|
+
features={{
|
|
1806
|
+
search: true,
|
|
1807
|
+
pagination: true,
|
|
1808
|
+
deletion: true,
|
|
1809
|
+
rowActions: true,
|
|
1810
|
+
}}
|
|
1811
|
+
onDeleteRow={(row) => handleDelete(row.id)}
|
|
1812
|
+
/>
|
|
1813
|
+
</div>
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
```
|
|
1817
|
+
|
|
1818
|
+
### 5. Use Consistent Styling
|
|
1819
|
+
|
|
1820
|
+
```tsx
|
|
1821
|
+
// ✅ Good - Consistent styling
|
|
1822
|
+
const columns: DataTableColumn<User>[] = [
|
|
1823
|
+
{
|
|
1824
|
+
accessorKey: 'status',
|
|
1825
|
+
header: 'Status',
|
|
1826
|
+
cell: ({ row }) => (
|
|
1827
|
+
<Badge
|
|
1828
|
+
variant={row.original.status === 'active' ? 'default' : 'secondary'}
|
|
1829
|
+
className="capitalize"
|
|
1830
|
+
>
|
|
1831
|
+
{row.original.status}
|
|
1832
|
+
</Badge>
|
|
1833
|
+
),
|
|
1834
|
+
},
|
|
1835
|
+
];
|
|
1836
|
+
```
|
|
1837
|
+
|
|
1838
|
+
## Troubleshooting
|
|
1839
|
+
|
|
1840
|
+
### Common Issues
|
|
1841
|
+
|
|
1842
|
+
#### 1. Performance issues with large datasets
|
|
1843
|
+
- Enable virtualization: `features={{ virtualization: true }}`
|
|
1844
|
+
- Use performance optimization: `performance={{ memoryOptimization: true }}`
|
|
1845
|
+
- Set appropriate virtual height: `virtualHeight={600}`
|
|
1846
|
+
- Consider server-side sorting/filtering with `serverSide` prop
|
|
1847
|
+
|
|
1848
|
+
#### 2. TypeScript errors
|
|
1849
|
+
- Ensure proper column typing: `DataTableColumn<YourType>[]`
|
|
1850
|
+
- Use proper accessor keys that match your data structure
|
|
1851
|
+
- Check that custom cell components return valid JSX
|
|
1852
|
+
|
|
1853
|
+
#### 3. Sorting not working
|
|
1854
|
+
- Enable sorting: `features={{ sorting: true }}`
|
|
1855
|
+
- Set `sortable: true` on columns
|
|
1856
|
+
- Ensure data types are consistent for sorting
|
|
1857
|
+
|
|
1858
|
+
#### 4. Search not working
|
|
1859
|
+
- Enable search: `features={{ search: true }}`
|
|
1860
|
+
- Set `searchable: true` on columns
|
|
1861
|
+
- Check `searchKey` matches your data structure
|
|
1862
|
+
|
|
1863
|
+
#### 5. Actions not appearing
|
|
1864
|
+
- Enable row actions: `features={{ rowActions: true }}`
|
|
1865
|
+
- Check permissions if using RBAC
|
|
1866
|
+
- Ensure action arrays are properly structured
|
|
1867
|
+
- Verify onClick handlers are defined
|
|
1868
|
+
|
|
1869
|
+
#### 6. Features not working
|
|
1870
|
+
- Ensure features are enabled in the `features` prop
|
|
1871
|
+
- Check that required event handlers are provided (e.g., `onEditRow` for editing)
|
|
1872
|
+
- Verify that the feature configuration matches your use case
|
|
1873
|
+
|
|
1874
|
+
#### 7. Delete selected rows not working
|
|
1875
|
+
- **Check feature configuration**: Ensure `features={{ selection: true, deletion: true, deleteSelected: true }}`
|
|
1876
|
+
- **Provide onDeleteSelected callback**: Implement the callback to handle bulk deletion
|
|
1877
|
+
- **Verify getRowId function**: Ensure you provide a `getRowId` function that returns unique IDs
|
|
1878
|
+
- **Check button visibility**: The delete button only appears when all three features are enabled and rows are selected
|
|
1879
|
+
- **Debug selection state**: Add console logs to verify `onRowSelectionChange` is being called with correct data
|
|
1880
|
+
- **Fallback option**: If you don't provide `onDeleteSelected`, the table will fall back to calling `onDeleteRow` for each selected row
|
|
1881
|
+
|
|
1882
|
+
### Debug Mode
|
|
1883
|
+
|
|
1884
|
+
Enable debug logging for DataTable issues:
|
|
1885
|
+
|
|
1886
|
+
```tsx
|
|
1887
|
+
<DataTable
|
|
1888
|
+
data={data}
|
|
1889
|
+
columns={columns}
|
|
1890
|
+
features={{ performanceMetrics: true }}
|
|
1891
|
+
showPerformanceMetrics={true}
|
|
1892
|
+
onPerformanceMetrics={(metrics) => {
|
|
1893
|
+
console.log('Performance metrics:', metrics);
|
|
1894
|
+
}}
|
|
1895
|
+
/>
|
|
1896
|
+
```
|
|
1897
|
+
|
|
1898
|
+
This comprehensive DataTable guide provides everything you need to build powerful, performant data interfaces with PACE Core.
|