@jmruthers/pace-core 0.6.9 → 0.6.11
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 +21 -0
- package/audit-tool/00-dependencies.cjs +46 -13
- package/audit-tool/audits/01-pace-core-compliance.cjs +96 -21
- package/audit-tool/audits/02-project-structure.cjs +74 -2
- package/audit-tool/audits/03-architecture.cjs +220 -20
- package/audit-tool/audits/04-code-quality.cjs +95 -3
- package/audit-tool/audits/05-styling.cjs +19 -7
- package/audit-tool/audits/06-security-rbac.cjs +214 -25
- package/audit-tool/audits/07-api-tech-stack.cjs +31 -15
- package/audit-tool/audits/08-testing-documentation.cjs +11 -3
- package/audit-tool/audits/09-operations.cjs +19 -7
- package/audit-tool/index.cjs +22 -11
- package/audit-tool/utils/report-utils.cjs +4 -0
- package/cursor-rules/01-pace-core-compliance.mdc +1 -0
- package/cursor-rules/02-project-structure.mdc +3 -26
- package/cursor-rules/03-architecture.mdc +3 -1
- package/cursor-rules/04-code-quality.mdc +1 -0
- package/cursor-rules/05-styling.mdc +120 -8
- package/cursor-rules/06-security-rbac.mdc +126 -2
- package/cursor-rules/07-api-tech-stack.mdc +1 -0
- package/cursor-rules/08-testing-documentation.mdc +1 -0
- package/cursor-rules/09-operations.mdc +1 -0
- package/dist/DataTable-EFYP2QLE.js +16 -0
- package/dist/InactivityServiceProvider-BbxwwDz1.d.ts +308 -0
- package/dist/UnifiedAuthProvider-Bkt_tzdS.d.ts +183 -0
- package/dist/api-BZR2CYXL.js +5 -0
- package/dist/api-result-USV1Czr-.d.ts +51 -0
- package/dist/assets/app-icons/admin_favicon.svg +462 -0
- package/dist/assets/app-icons/base_favicon.svg +85 -0
- package/dist/assets/app-icons/cake_favicon.svg +68 -0
- package/dist/assets/app-icons/core_favicon.svg +256 -0
- package/dist/assets/app-icons/gear_favicon.svg +91 -0
- package/dist/assets/app-icons/medi_favicon.svg +92 -0
- package/dist/assets/app-icons/mint_favicon.svg +83 -0
- package/dist/assets/app-icons/pace_favicon.svg +49 -0
- package/dist/assets/app-icons/pump_favicon.svg +68 -0
- package/dist/assets/app-icons/seed_favicon.svg +91 -0
- package/dist/assets/app-icons/team_favicon.svg +67 -0
- package/dist/assets/app-icons/trac_favicon.svg +112 -0
- package/dist/assets/app-icons/trip_favicon.svg +102 -0
- package/dist/audit-HI2DHUVU.js +4 -0
- package/dist/auth-JvdRVaud.d.ts +49 -0
- package/dist/chunk-2DL2WSOE.js +327 -0
- package/dist/chunk-2OEVOGGR.js +9598 -0
- package/dist/chunk-44CNXN4P.js +15 -0
- package/dist/chunk-4R3T5ENU.js +2943 -0
- package/dist/chunk-7A6IMHH2.js +2321 -0
- package/dist/chunk-BTHN5MKC.js +121 -0
- package/dist/chunk-CU2BU2MQ.js +2 -0
- package/dist/chunk-D6BMFMQZ.js +200 -0
- package/dist/chunk-DDMPHZ3D.js +58 -0
- package/dist/chunk-ENLXB7GP.js +721 -0
- package/dist/chunk-J2KQK6DG.js +2159 -0
- package/dist/chunk-KJXRL3XE.js +6434 -0
- package/dist/chunk-L5LFKKLJ.js +61 -0
- package/dist/chunk-PCSHBLPB.js +811 -0
- package/dist/chunk-QRYSEPHB.js +429 -0
- package/dist/chunk-RMLY6KB5.js +187 -0
- package/dist/chunk-SACF5YSM.js +31 -0
- package/dist/chunk-UZNAFKGW.js +125 -0
- package/dist/chunk-V7FTM2LU.js +1080 -0
- package/dist/chunk-WY6Y7KC3.js +264 -0
- package/dist/chunk-XOJME5T7.js +407 -0
- package/dist/chunk-XPFVT3GN.js +492 -0
- package/dist/chunk-YFTFFJIV.js +529 -0
- package/dist/chunk-YYTWKVHO.js +1334 -0
- package/dist/components.d.ts +12 -89
- package/dist/components.js +23 -55
- package/dist/database.generated-qkdoiVrJ.d.ts +9441 -0
- package/dist/eslint-rules/index.cjs +3 -0
- package/dist/eslint-rules/rules/03-architecture.cjs +74 -0
- package/dist/eslint-rules/rules/05-styling.cjs +507 -0
- package/dist/eslint-rules/rules/06-security-rbac.cjs +84 -0
- package/dist/event-BfCox3N2.d.ts +265 -0
- package/dist/file-reference-DU1hcawx.d.ts +164 -0
- package/dist/functions-DH45k8ec.d.ts +208 -0
- package/dist/hooks.d.ts +28 -14
- package/dist/hooks.js +90 -56
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +392 -155
- package/dist/index.js +337 -347
- package/dist/pagination-BW1mqywp.d.ts +201 -0
- package/dist/papaparseLoader-WG2UXQ22.js +7 -0
- package/dist/providers.d.ts +29 -14
- package/dist/providers.js +7 -5
- package/dist/rbac/eslint-rules.js +2 -2
- package/dist/rbac/index.d.ts +180 -351
- package/dist/rbac/index.js +13 -11
- package/dist/theming/runtime.d.ts +28 -5
- package/dist/theming/runtime.js +2 -2
- package/dist/timezone-BTWWXKVY.d.ts +696 -0
- package/dist/types-BE2sEHKd.d.ts +55 -0
- package/dist/types-CvOPXWWZ.d.ts +111 -0
- package/dist/types-Dr8sNhER.d.ts +50 -0
- package/dist/types.d.ts +20 -13
- package/dist/types.js +1 -0
- package/dist/usePublicPageContext-B91dGYW1.d.ts +4367 -0
- package/dist/usePublicRouteParams-BgV6VhMi.d.ts +946 -0
- package/dist/utils.d.ts +338 -156
- package/dist/utils.js +78 -60
- package/dist/validation-g5n0hDkh.d.ts +177 -0
- package/docs/api/modules.md +1226 -1094
- package/docs/api-reference/components.md +5 -5
- package/docs/api-reference/rpc-functions.md +12 -3
- package/docs/core-concepts/rbac-system.md +8 -0
- package/docs/getting-started/cursor-rules.md +17 -20
- package/docs/getting-started/dependencies.md +1 -1
- package/docs/getting-started/setup.md +235 -0
- package/docs/implementation-guides/authentication.md +27 -0
- package/docs/implementation-guides/data-tables.md +365 -10
- package/docs/migration/ApiResult-migration.md +25 -0
- package/docs/rbac/RBAC_CONTRACT.md +0 -12
- package/docs/rbac/api-reference.md +33 -31
- package/docs/standards/0-standards-overview.md +50 -15
- package/docs/standards/1-pace-core-compliance-standards.md +62 -57
- package/docs/standards/2-project-structure-standards.md +45 -90
- package/docs/standards/3-architecture-standards.md +41 -1
- package/docs/standards/4-code-quality-standards.md +26 -6
- package/docs/standards/5-styling-standards.md +35 -1
- package/docs/standards/6-security-rbac-standards.md +288 -7
- package/docs/standards/7-api-tech-stack-standards.md +116 -17
- package/docs/standards/8-testing-documentation-standards.md +31 -0
- package/docs/standards/9-operations-standards.md +19 -0
- package/docs/standards/README.md +20 -201
- package/docs/testing/README.md +10 -0
- package/docs/testing/test-setup-for-consumers.md +916 -0
- package/docs/troubleshooting/common-issues.md +17 -1
- package/docs/troubleshooting/organisation-context-setup.md +8 -0
- package/docs/troubleshooting/print-event-name-css-variable-analysis.md +217 -0
- package/eslint-config-pace-core.cjs +24 -0
- package/package.json +14 -20
- package/scripts/build-docs.js +180 -0
- package/scripts/setup.cjs +536 -0
- package/scripts/validate.cjs +480 -0
- package/src/__mocks__/lucide-react.ts +0 -2
- package/src/__tests__/helpers/component-test-utils.test.tsx +260 -0
- package/src/__tests__/helpers/optimized-test-setup.test.ts +224 -0
- package/src/__tests__/helpers/supabaseMock.test.ts +273 -0
- package/src/__tests__/helpers/test-providers.test.tsx +99 -0
- package/src/__tests__/helpers/test-providers.tsx +37 -39
- package/src/__tests__/helpers/test-utils.test.tsx +447 -0
- package/src/__tests__/helpers/timer-utils.test.ts +371 -0
- package/src/assets/app-icons/admin_favicon.svg +462 -0
- package/src/assets/app-icons/base_favicon.svg +85 -0
- package/src/assets/app-icons/cake_favicon.svg +68 -0
- package/src/assets/app-icons/core_favicon.svg +256 -0
- package/src/assets/app-icons/gear_favicon.svg +91 -0
- package/src/assets/app-icons/index.test.ts +304 -0
- package/src/assets/app-icons/index.ts +83 -0
- package/src/assets/app-icons/medi_favicon.svg +92 -0
- package/src/assets/app-icons/mint_favicon.svg +83 -0
- package/src/assets/app-icons/pace_favicon.svg +49 -0
- package/src/assets/app-icons/pump_favicon.svg +68 -0
- package/src/assets/app-icons/seed_favicon.svg +91 -0
- package/src/assets/app-icons/team_favicon.svg +67 -0
- package/src/assets/app-icons/trac_favicon.svg +112 -0
- package/src/assets/app-icons/trip_favicon.svg +102 -0
- package/src/components/AddressField/AddressField.test.tsx +379 -4
- package/src/components/AddressField/AddressField.tsx +239 -213
- package/src/components/AddressField/types.ts +2 -2
- package/src/components/Alert/Alert.test.tsx +35 -25
- package/src/components/Alert/Alert.tsx +8 -8
- package/src/components/AppSwitcher/AppSwitcher.test.tsx +1250 -0
- package/src/components/AppSwitcher/AppSwitcher.tsx +315 -0
- package/src/components/Avatar/Avatar.test.tsx +11 -1
- package/src/components/Avatar/Avatar.tsx +3 -2
- package/src/components/Badge/Badge.test.tsx +11 -1
- package/src/components/Button/Button.test.tsx +13 -3
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/Calendar/Calendar.test.tsx +523 -131
- package/src/components/Calendar/Calendar.tsx +107 -488
- package/src/components/Card/Card.test.tsx +384 -258
- package/src/components/Card/Card.tsx +19 -10
- package/src/components/Checkbox/Checkbox.test.tsx +58 -174
- package/src/components/ContextSelector/ContextSelector.internals.tsx +204 -0
- package/src/components/ContextSelector/ContextSelector.test.tsx +360 -0
- package/src/components/ContextSelector/ContextSelector.tsx +66 -280
- package/src/components/ContextSelector/ContextSelector.types.ts +35 -0
- package/src/components/ContextSelector/useContextSelectorState.tsx +195 -0
- package/src/components/DataTable/AUDIT_REPORT.md +59 -44
- package/src/components/DataTable/DataTable.comprehensive.test.tsx +759 -0
- package/src/components/DataTable/DataTable.default-state.test.tsx +524 -0
- package/src/components/DataTable/DataTable.export.test.tsx +705 -0
- package/src/components/DataTable/DataTable.grouping-aggregation.test.tsx +658 -0
- package/src/components/DataTable/DataTable.hooks.test.tsx +192 -0
- package/src/components/DataTable/DataTable.select-label-display.test.tsx +485 -0
- package/src/components/DataTable/DataTable.test.tsx +787 -416
- package/src/components/DataTable/DataTable.tsx +14 -14
- package/src/components/DataTable/DataTableCore.integration.test.tsx +458 -0
- package/src/components/DataTable/DataTableCore.test-setup.ts +221 -0
- package/src/components/DataTable/DataTableCore.test.tsx +970 -0
- package/src/components/DataTable/README.md +155 -0
- package/src/components/DataTable/TESTING.md +101 -0
- package/src/components/DataTable/a11y.basic.test.tsx +788 -0
- package/src/components/DataTable/components/DataTableCore.tsx +126 -894
- package/src/components/DataTable/components/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/components/GroupingDropdown.tsx +2 -3
- package/src/components/DataTable/components/ImportModal.tsx +82 -408
- package/src/components/DataTable/components/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/context/DataTableContext.test.tsx +328 -0
- package/src/components/DataTable/context/DataTableContext.tsx +13 -13
- package/src/components/DataTable/core/ColumnFactory.test.ts +403 -0
- package/src/components/DataTable/core/ColumnFactory.ts +3 -3
- package/src/components/DataTable/hooks/useColumnOrderPersistence.test.ts +516 -0
- package/src/components/DataTable/hooks/useColumnOrderPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.test.ts +256 -0
- package/src/components/DataTable/hooks/useColumnVisibilityPersistence.ts +12 -9
- package/src/components/DataTable/hooks/useDataTableConfiguration.test.ts +297 -0
- package/src/components/DataTable/hooks/useDataTableConfiguration.ts +15 -3
- package/src/components/DataTable/hooks/useDataTableDataPipeline.test.ts +270 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.test.ts +127 -0
- package/src/components/DataTable/hooks/useDataTableDeletionBatching.ts +106 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.test.ts +461 -0
- package/src/components/DataTable/hooks/useDataTableEffectiveActions.ts +238 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.test.ts +296 -0
- package/src/components/DataTable/hooks/useDataTableLayoutHandlers.ts +175 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.test.ts +203 -0
- package/src/components/DataTable/hooks/useDataTablePaginationSync.ts +109 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.test.ts +280 -0
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +81 -260
- package/src/components/DataTable/hooks/useDataTablePipeline.test.tsx +219 -0
- package/src/components/DataTable/hooks/useDataTablePipeline.tsx +239 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.test.tsx +316 -0
- package/src/components/DataTable/hooks/useDataTableRenderGuard.tsx +195 -0
- package/src/components/DataTable/hooks/useDataTableScope.test.ts +110 -0
- package/src/components/DataTable/hooks/useDataTableScope.ts +123 -0
- package/src/components/DataTable/hooks/useDataTableState.test.ts +733 -0
- package/src/components/DataTable/hooks/useDataTableState.ts +161 -114
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.test.ts +277 -0
- package/src/components/DataTable/hooks/useDataTableStateAndPersistence.ts +222 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.test.ts +93 -0
- package/src/components/DataTable/hooks/useDataTableSuperAdmin.ts +86 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.test.ts +185 -0
- package/src/components/DataTable/hooks/useDataTableTableInstance.ts +178 -0
- package/src/components/DataTable/hooks/useEffectiveColumnOrder.test.ts +183 -0
- package/src/components/DataTable/hooks/useHierarchicalState.test.ts +294 -0
- package/src/components/DataTable/hooks/useImportModalFocus.test.ts +184 -0
- package/src/components/DataTable/hooks/useImportModalFocus.ts +53 -0
- package/src/components/DataTable/hooks/useImportModalState.test.ts +390 -0
- package/src/components/DataTable/hooks/useImportModalState.ts +345 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.test.ts +787 -0
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +311 -271
- package/src/components/DataTable/hooks/usePermissionTracking.test.ts +381 -0
- package/src/components/DataTable/hooks/usePermissionTracking.ts +122 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.test.ts +258 -0
- package/src/components/DataTable/hooks/useServerSideDataEffect.ts +27 -4
- package/src/components/DataTable/hooks/useTableColumns.test.ts +499 -0
- package/src/components/DataTable/hooks/useTableColumns.ts +15 -39
- package/src/components/DataTable/hooks/useTableHandlers.test.ts +461 -0
- package/src/components/DataTable/hooks/useTableHandlers.ts +13 -22
- package/src/components/DataTable/index.ts +28 -5
- package/src/components/DataTable/keyboard.test.tsx +734 -0
- package/src/components/DataTable/mocks/MockRBACProvider.tsx +66 -0
- package/src/components/DataTable/pagination.modes.test.tsx +728 -0
- package/src/components/DataTable/ssr.strict-mode.test.tsx +319 -0
- package/src/components/DataTable/styles.test.ts +379 -0
- package/src/components/DataTable/styles.ts +0 -1
- package/src/components/DataTable/test-utils/MockDataTableComponents.tsx +55 -0
- package/src/components/DataTable/test-utils/dataFactories.ts +103 -0
- package/src/components/DataTable/test-utils/featureConfig.ts +10 -0
- package/src/components/DataTable/test-utils/sharedTestUtils.ts +419 -0
- package/src/components/DataTable/test-utils.ts +94 -0
- package/src/components/DataTable/types/actions.ts +71 -0
- package/src/components/DataTable/types/base.ts +39 -0
- package/src/components/DataTable/types/columns.ts +125 -0
- package/src/components/DataTable/types/export.ts +32 -0
- package/src/components/DataTable/types/features.ts +81 -0
- package/src/components/DataTable/types/hierarchical.ts +44 -0
- package/src/components/DataTable/types/index.ts +43 -0
- package/src/components/DataTable/types/pagination.ts +85 -0
- package/src/components/DataTable/types/performance.ts +47 -0
- package/src/components/DataTable/types/props.ts +62 -0
- package/src/components/DataTable/types/rbac.ts +45 -0
- package/src/components/DataTable/ui/layout/DataTableCore.test.tsx +1194 -0
- package/src/components/DataTable/ui/layout/DataTableCore.tsx +345 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.test.tsx +438 -0
- package/src/components/DataTable/ui/layout/DataTableErrorBoundary.tsx +225 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.test.tsx +1352 -0
- package/src/components/DataTable/ui/layout/DataTableLayout.tsx +661 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.test.tsx +91 -0
- package/src/components/DataTable/ui/modals/BulkDeleteConfirmDialog.tsx +43 -0
- package/src/components/DataTable/ui/modals/DataTableModals.test.tsx +749 -0
- package/src/components/DataTable/ui/modals/DataTableModals.tsx +341 -0
- package/src/components/DataTable/ui/modals/ImportModal.test.tsx +1834 -0
- package/src/components/DataTable/ui/modals/ImportModal.tsx +197 -0
- package/src/components/DataTable/ui/modals/ImportModalFailedRowsSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalFileSection.tsx +148 -0
- package/src/components/DataTable/ui/modals/ImportModalPreviewSection.tsx +60 -0
- package/src/components/DataTable/ui/modals/ImportModalSummarySection.tsx +59 -0
- package/src/components/DataTable/ui/modals/importModalPersistence.ts +73 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.test.tsx +245 -0
- package/src/components/DataTable/ui/shared/AccessDeniedPage.tsx +159 -0
- package/src/components/DataTable/ui/shared/ActionButtons.test.tsx +921 -0
- package/src/components/DataTable/ui/shared/ActionButtons.tsx +195 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.test.tsx +497 -0
- package/src/components/DataTable/ui/shared/ColumnFilter.tsx +113 -0
- package/src/components/DataTable/ui/shared/PaginationControls.test.tsx +451 -0
- package/src/components/DataTable/ui/shared/PaginationControls.tsx +291 -0
- package/src/components/DataTable/ui/shared/SortIndicator.test.tsx +135 -0
- package/src/components/DataTable/ui/shared/SortIndicator.tsx +50 -0
- package/src/components/DataTable/ui/table/EditFields.test.tsx +526 -0
- package/src/components/DataTable/ui/table/EditFields.tsx +355 -0
- package/src/components/DataTable/ui/table/EditableRow.test.tsx +1003 -0
- package/src/components/DataTable/ui/table/EditableRow.tsx +444 -0
- package/src/components/DataTable/ui/table/EmptyState.test.tsx +360 -0
- package/src/components/DataTable/ui/table/EmptyState.tsx +74 -0
- package/src/components/DataTable/ui/table/FilterRow.test.tsx +416 -0
- package/src/components/DataTable/ui/table/FilterRow.tsx +148 -0
- package/src/components/DataTable/ui/table/LoadingState.test.tsx +77 -0
- package/src/components/DataTable/ui/table/LoadingState.tsx +17 -0
- package/src/components/DataTable/ui/table/RowComponent.test.tsx +1024 -0
- package/src/components/DataTable/ui/table/RowComponent.tsx +429 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.test.tsx +1273 -0
- package/src/components/DataTable/ui/table/UnifiedTableBody.tsx +440 -0
- package/src/components/DataTable/ui/table/cellValueUtils.test.ts +453 -0
- package/src/components/DataTable/ui/table/cellValueUtils.ts +40 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.test.tsx +551 -0
- package/src/components/DataTable/ui/toolbar/BulkOperationsDropdown.tsx +160 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.test.tsx +751 -0
- package/src/components/DataTable/ui/toolbar/ColumnVisibilityDropdown.tsx +114 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.test.tsx +629 -0
- package/src/components/DataTable/ui/toolbar/DataTableToolbar.tsx +271 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.test.tsx +621 -0
- package/src/components/DataTable/ui/toolbar/GroupingDropdown.tsx +107 -0
- package/src/components/DataTable/utils/a11yUtils.test.ts +548 -0
- package/src/components/DataTable/utils/a11yUtils.ts +1 -1
- package/src/components/DataTable/utils/aggregationUtils.test.ts +288 -0
- package/src/components/DataTable/utils/aggregationUtils.ts +5 -5
- package/src/components/DataTable/utils/columnUtils.test.ts +94 -0
- package/src/components/DataTable/utils/csvParse.test.ts +74 -0
- package/src/components/DataTable/utils/csvParse.ts +65 -0
- package/src/components/DataTable/utils/errorHandling.test.ts +209 -0
- package/src/components/DataTable/utils/errorHandling.ts +3 -1
- package/src/components/DataTable/utils/exportUtils.test.ts +954 -0
- package/src/components/DataTable/utils/exportUtils.ts +1 -1
- package/src/components/DataTable/utils/flexibleImport.test.ts +573 -0
- package/src/components/DataTable/utils/flexibleImport.ts +3 -186
- package/src/components/DataTable/utils/hierarchicalSorting.test.ts +235 -0
- package/src/components/DataTable/utils/hierarchicalSorting.ts +3 -3
- package/src/components/DataTable/utils/hierarchicalUtils.test.ts +586 -0
- package/src/components/DataTable/utils/importDateParser.test.ts +162 -0
- package/src/components/DataTable/utils/importDateParser.ts +114 -0
- package/src/components/DataTable/utils/importValueParser.test.ts +138 -0
- package/src/components/DataTable/utils/importValueParser.ts +91 -0
- package/src/components/DataTable/utils/paginationUtils.test.ts +593 -0
- package/src/components/DataTable/utils/paginationUtils.ts +7 -4
- package/src/components/DataTable/utils/performanceUtils.test.ts +470 -0
- package/src/components/DataTable/utils/performanceUtils.ts +1 -1
- package/src/components/DataTable/utils/rowUtils.test.ts +235 -0
- package/src/components/DataTable/utils/selectFieldUtils.test.ts +271 -0
- package/src/components/DataTable/utils/selectFieldUtils.ts +97 -67
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +18 -25
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +1 -1
- package/src/components/DateTimeField/DateTimeField.test.tsx +3 -16
- package/src/components/DateTimeField/DateTimeField.tsx +1 -1
- package/src/components/Dialog/Dialog.test-utils.ts +49 -0
- package/src/components/Dialog/Dialog.test.tsx +2865 -458
- package/src/components/Dialog/Dialog.tsx +183 -986
- package/src/components/Dialog/dialogLock.test.ts +238 -0
- package/src/components/Dialog/dialogLock.ts +98 -0
- package/src/components/Dialog/index.ts +2 -0
- package/src/components/Dialog/useDialogDimensions.test.ts +163 -0
- package/src/components/Dialog/useDialogDimensions.ts +140 -0
- package/src/components/Dialog/useDialogLifecycle.test.ts +358 -0
- package/src/components/Dialog/useDialogLifecycle.ts +135 -0
- package/src/components/Dialog/useDialogPersistence.test.ts +381 -0
- package/src/components/Dialog/useDialogPersistence.ts +357 -0
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +2 -62
- package/src/components/ErrorBoundary/ErrorBoundaryContext.context.ts +17 -0
- package/src/components/ErrorBoundary/ErrorBoundaryContext.tsx +2 -45
- package/src/components/ErrorBoundary/ErrorBoundaryContext.types.ts +41 -0
- package/src/components/ErrorBoundary/index.ts +3 -4
- package/src/components/ErrorBoundary/useErrorBoundaryContext.ts +20 -0
- package/src/components/FileDisplay/FileDisplay.test.tsx +479 -247
- package/src/components/FileDisplay/FileDisplay.tsx +29 -659
- package/src/components/FileDisplay/FileDisplayContent.test.tsx +395 -0
- package/src/components/FileDisplay/FileDisplayContent.tsx +242 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.test.tsx +74 -0
- package/src/components/FileDisplay/FileDisplayDeleteConfirmDialog.tsx +38 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.test.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayEmptyView.tsx +33 -0
- package/src/components/FileDisplay/FileDisplayErrorView.test.tsx +71 -0
- package/src/components/FileDisplay/FileDisplayErrorView.tsx +50 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.test.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingFallbackView.tsx +22 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.test.tsx +21 -0
- package/src/components/FileDisplay/FileDisplayLoadingView.tsx +23 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.test.tsx +101 -0
- package/src/components/FileDisplay/FileDisplayMultipleFilesView.tsx +109 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.test.tsx +58 -0
- package/src/components/FileDisplay/FileDisplaySingleDocumentLinkView.tsx +48 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.test.tsx +111 -0
- package/src/components/FileDisplay/FileDisplaySingleFileWithActionsView.tsx +270 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.test.tsx +78 -0
- package/src/components/FileDisplay/FileDisplaySingleImageView.tsx +67 -0
- package/src/components/FileDisplay/fallbackUtils.test.ts +50 -0
- package/src/components/FileDisplay/fallbackUtils.ts +44 -0
- package/src/components/FileDisplay/fetchFileDisplayData.ts +24 -0
- package/src/components/FileDisplay/fetchFileDisplayData.unit.test.ts +183 -0
- package/src/components/FileDisplay/fileDisplayUtils.test.ts +58 -0
- package/src/components/FileDisplay/fileDisplayUtils.ts +24 -0
- package/src/components/FileDisplay/index.tsx +1 -1
- package/src/components/FileDisplay/useFileDisplay.test.ts +538 -0
- package/src/components/FileDisplay/useFileDisplay.ts +515 -0
- package/src/components/FileDisplay/useFileDisplay.unit.test.ts +1438 -0
- package/src/components/FileDisplay/useFileDisplayData.ts +126 -0
- package/src/components/FileDisplay/usePublicFileDisplay.test.ts +729 -0
- package/src/components/FileDisplay/usePublicFileDisplay.ts +579 -0
- package/src/components/FileUpload/FileUpload.test.tsx +69 -27
- package/src/components/FileUpload/FileUpload.tsx +112 -527
- package/src/components/FileUpload/FileUploadDropZone.tsx +112 -0
- package/src/components/FileUpload/FileUploadProgressItem.tsx +86 -0
- package/src/components/FileUpload/FileUploadProgressList.tsx +40 -0
- package/src/components/FileUpload/index.tsx +1 -1
- package/src/components/FileUpload/useFileUploadManager.test.ts +308 -0
- package/src/components/FileUpload/useFileUploadManager.ts +454 -0
- package/src/components/FileUpload/useResolvedAppId.test.ts +102 -0
- package/src/components/FileUpload/useResolvedAppId.ts +77 -0
- package/src/components/Footer/Footer.test.tsx +15 -382
- package/src/components/Footer/Footer.tsx +8 -125
- package/src/components/Form/Form.test.tsx +425 -88
- package/src/components/Form/Form.tsx +91 -299
- package/src/components/Form/useFormPersistence.ts +257 -0
- package/src/components/Header/Header.test.tsx +653 -163
- package/src/components/Header/Header.tsx +62 -44
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +35 -76
- package/src/components/Input/Input.test.tsx +34 -120
- package/src/components/Input/Input.tsx +1 -1
- package/src/components/Label/Label.test.tsx +46 -45
- package/src/components/LoadingSpinner/LoadingSpinner.test.tsx +8 -11
- package/src/components/LoginForm/LoginForm.test.tsx +0 -1
- package/src/components/NavigationMenu/HierarchicalNavItem.tsx +104 -0
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +2422 -102
- package/src/components/NavigationMenu/NavigationMenu.tsx +62 -362
- package/src/components/NavigationMenu/index.ts +6 -1
- package/src/components/NavigationMenu/navigationPermissionHelper.ts +188 -0
- package/src/components/NavigationMenu/useNavigationFiltering.test.ts +1949 -0
- package/src/components/NavigationMenu/useNavigationFiltering.ts +199 -308
- package/src/components/NavigationMenu/useNavigationScope.ts +125 -0
- package/src/components/PaceAppLayout/PaceAppLayout.edge-cases.test.tsx +1322 -0
- package/src/components/PaceAppLayout/PaceAppLayout.integration.test.tsx +50 -49
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +81 -38
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +103 -85
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +774 -44
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +282 -764
- package/src/components/PaceAppLayout/README.md +0 -9
- package/src/components/PaceAppLayout/test-setup.tsx +15 -9
- package/src/components/PaceAppLayout/useFilteredNavItems.ts +304 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutConfig.ts +142 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutGate.tsx +150 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutPermissions.ts +162 -0
- package/src/components/PaceAppLayout/usePaceAppLayoutScope.ts +79 -0
- package/src/components/PaceAppLayout/useRoleBasedRouteAccess.ts +157 -0
- package/src/components/PaceAppLayout/useSuperAdminFallback.ts +58 -0
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +782 -20
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +33 -125
- package/src/components/PaceLoginPage/useLoginAppAccess.ts +153 -0
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +1 -1
- package/src/components/Progress/Progress.test.tsx +127 -1
- package/src/components/Progress/Progress.tsx +1 -2
- package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +1196 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +29 -217
- package/src/components/ProtectedRoute/useProtectedRouteState.ts +128 -0
- package/src/components/ProtectedRoute/useVisibilityRedirectGrace.ts +89 -0
- package/src/components/PublicLayout/PublicLayout.test.tsx +1640 -38
- package/src/components/PublicLayout/PublicPageContext.ts +28 -0
- package/src/components/PublicLayout/PublicPageLayout.tsx +134 -75
- package/src/components/PublicLayout/PublicPageProvider.tsx +7 -42
- package/src/components/PublicLayout/usePublicPageContext.ts +36 -0
- package/src/components/Select/Select.test.tsx +45 -8
- package/src/components/Select/Select.tsx +57 -40
- package/src/components/Select/context.test.tsx +56 -0
- package/src/components/Select/text.test.tsx +104 -0
- package/src/components/Select/text.ts +26 -0
- package/src/components/Select/types.ts +3 -0
- package/src/components/Select/useSelectEvents.test.ts +279 -0
- package/src/components/Select/useSelectEvents.ts +87 -0
- package/src/components/Select/useSelectSearch.test.tsx +295 -0
- package/src/components/Select/useSelectSearch.ts +91 -0
- package/src/components/Select/useSelectState.test.ts +268 -0
- package/src/components/Select/useSelectState.ts +104 -0
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.test.tsx +28 -112
- package/src/components/Switch/Switch.test.tsx +57 -153
- package/src/components/Table/Table.test.tsx +395 -317
- package/src/components/Tabs/Tabs.test.tsx +270 -0
- package/src/components/Tabs/Tabs.tsx +4 -4
- package/src/components/Textarea/Textarea.test.tsx +11 -38
- package/src/components/Toast/Toast.test.tsx +425 -496
- package/src/components/Tooltip/Tooltip.test.tsx +4 -21
- package/src/components/UserMenu/UserMenu.test.tsx +1 -21
- package/src/components/UserMenu/UserMenu.tsx +0 -1
- package/src/components/index.test.ts +346 -0
- package/src/components/index.ts +12 -1
- package/src/constants/performance.test.ts +91 -0
- package/src/hooks/ServiceHooks.test.tsx +725 -0
- package/src/hooks/hooks.integration.test.tsx +608 -0
- package/src/hooks/index.ts +18 -3
- package/src/hooks/index.unit.test.ts +220 -0
- package/src/hooks/public/usePublicEvent.test.ts +304 -0
- package/src/hooks/public/usePublicEvent.ts +11 -11
- package/src/hooks/public/usePublicEventLogo.test.ts +655 -120
- package/src/hooks/public/usePublicEventLogo.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.test.ts +595 -0
- package/src/hooks/public/usePublicRouteParams.ts +2 -2
- package/src/hooks/services/useAuth.ts +9 -7
- package/src/hooks/services/useAuthService.ts +1 -1
- package/src/hooks/services/useEventService.ts +1 -1
- package/src/hooks/useAccessibleApps.test.ts +400 -0
- package/src/hooks/useAccessibleApps.ts +264 -0
- package/src/hooks/useAddressAutocomplete.test.ts +170 -47
- package/src/hooks/useAddressAutocomplete.ts +109 -81
- package/src/hooks/useApiFetch.unit.test.ts +111 -0
- package/src/hooks/useAppConfig.ts +13 -3
- package/src/hooks/useAppConfig.unit.test.ts +712 -0
- package/src/hooks/useComponentPerformance.unit.test.tsx +314 -0
- package/src/hooks/useDataTablePerformance.ts +111 -130
- package/src/hooks/useDataTablePerformance.unit.test.ts +720 -0
- package/src/hooks/useDataTableState.test.ts +170 -0
- package/src/hooks/useDataTableState.ts +5 -5
- package/src/hooks/useDebounce.unit.test.ts +157 -0
- package/src/hooks/useEventTheme.test.ts +70 -18
- package/src/hooks/useEventTheme.ts +50 -22
- package/src/hooks/useEvents.ts +49 -2
- package/src/hooks/useEvents.unit.test.ts +227 -0
- package/src/hooks/useFileReference.test.ts +388 -107
- package/src/hooks/useFileReference.ts +184 -179
- package/src/hooks/useFileUrl.ts +1 -1
- package/src/hooks/useFileUrl.unit.test.ts +686 -0
- package/src/hooks/useFileUrlCache.test.ts +319 -0
- package/src/hooks/useFileUrlCache.ts +5 -2
- package/src/hooks/useFocusManagement.unit.test.ts +604 -0
- package/src/hooks/useFocusTrap.unit.test.tsx +613 -0
- package/src/hooks/useFormDialog.test.ts +307 -0
- package/src/hooks/useFormDialog.ts +2 -2
- package/src/hooks/useInactivityTracker.ts +141 -134
- package/src/hooks/useInactivityTracker.unit.test.ts +446 -0
- package/src/hooks/useIsMobile.unit.test.ts +317 -0
- package/src/hooks/useIsPrint.ts +62 -0
- package/src/hooks/useIsPrint.unit.test.ts +545 -0
- package/src/hooks/useKeyboardShortcuts.unit.test.ts +907 -0
- package/src/hooks/useOrganisationPermissions.test.ts +1 -2
- package/src/hooks/useOrganisationPermissions.ts +1 -4
- package/src/hooks/useOrganisationPermissions.unit.test.tsx +293 -0
- package/src/hooks/useOrganisationSecurity.test.ts +4 -33
- package/src/hooks/useOrganisationSecurity.ts +192 -203
- package/src/hooks/useOrganisationSecurity.unit.test.tsx +959 -0
- package/src/hooks/useOrganisations.ts +1 -1
- package/src/hooks/useOrganisations.unit.test.ts +369 -0
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePerformanceMonitor.unit.test.ts +693 -0
- package/src/hooks/usePermissionCache.test.ts +298 -329
- package/src/hooks/usePermissionCache.ts +277 -276
- package/src/hooks/usePreventTabReload.test.ts +307 -0
- package/src/hooks/usePublicEvent.simple.test.ts +794 -0
- package/src/hooks/usePublicEvent.test.ts +670 -0
- package/src/hooks/usePublicEvent.unit.test.ts +638 -0
- package/src/hooks/usePublicFileDisplay.test.ts +948 -0
- package/src/hooks/usePublicRouteParams.unit.test.ts +442 -0
- package/src/hooks/useQueryCache.test.ts +391 -0
- package/src/hooks/useQueryCache.ts +7 -9
- package/src/hooks/useRBAC.unit.test.ts +253 -0
- package/src/hooks/useSessionDraft.test.ts +556 -0
- package/src/hooks/useSessionDraft.ts +14 -11
- package/src/hooks/useSessionRestoration.ts +1 -1
- package/src/hooks/useSessionRestoration.unit.test.tsx +381 -0
- package/src/hooks/useStorage.ts +94 -54
- package/src/hooks/useStorage.unit.test.ts +684 -0
- package/src/hooks/useToast.test.ts +413 -0
- package/src/hooks/useToast.ts +2 -2
- package/src/hooks/useToast.unit.test.tsx +481 -0
- package/src/hooks/useZodForm.ts +3 -3
- package/src/hooks/useZodForm.unit.test.tsx +191 -0
- package/src/icons/index.test.ts +133 -0
- package/src/icons/index.ts +3 -1
- package/src/index.test.ts +528 -0
- package/src/index.ts +56 -9
- package/src/providers/AuthProvider.test.tsx +218 -0
- package/src/providers/EventProvider.test.tsx +487 -0
- package/src/providers/InactivityProvider.test-helper.tsx +40 -0
- package/src/providers/InactivityProvider.test.tsx +421 -0
- package/src/providers/ProviderLifecycle.test.tsx +308 -0
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +7 -12
- package/src/providers/UnifiedAuthProvider.test.tsx +503 -0
- package/src/providers/index.test.ts +138 -0
- package/src/providers/services/AuthServiceContext.ts +27 -0
- package/src/providers/services/AuthServiceProvider.integration.test.tsx +229 -0
- package/src/providers/services/AuthServiceProvider.test.tsx +638 -0
- package/src/providers/services/AuthServiceProvider.tsx +81 -20
- package/src/providers/services/EventServiceContext.ts +25 -0
- package/src/providers/services/EventServiceProvider.test.tsx +839 -0
- package/src/providers/services/EventServiceProvider.tsx +11 -20
- package/src/providers/services/InactivityServiceContext.ts +25 -0
- package/src/providers/services/InactivityServiceProvider.test.tsx +662 -0
- package/src/providers/services/InactivityServiceProvider.tsx +7 -17
- package/src/providers/services/OrganisationServiceContext.ts +25 -0
- package/src/providers/services/OrganisationServiceProvider.test.tsx +440 -0
- package/src/providers/services/OrganisationServiceProvider.tsx +7 -17
- package/src/providers/services/UnifiedAuthContext.ts +102 -0
- package/src/providers/services/UnifiedAuthProvider.advanced.test.tsx +434 -0
- package/src/providers/services/UnifiedAuthProvider.appId.test.tsx +408 -0
- package/src/providers/services/UnifiedAuthProvider.integration.test.tsx +304 -0
- package/src/providers/services/UnifiedAuthProvider.test.tsx +212 -0
- package/src/providers/services/UnifiedAuthProvider.tsx +147 -497
- package/src/providers/services/contexts.test.tsx +281 -0
- package/src/providers/services/useUnifiedAuth.test.tsx +251 -0
- package/src/providers/services/useUnifiedAuth.ts +29 -0
- package/src/providers/services/useUnifiedAuthContextValue.ts +279 -0
- package/src/providers/useInactivity.test-helper.ts +27 -0
- package/src/rbac/README.md +5 -5
- package/src/rbac/adapters.comprehensive.test.tsx +429 -0
- package/src/rbac/adapters.test.tsx +654 -0
- package/src/rbac/adapters.tsx +53 -38
- package/src/rbac/api.test.ts +986 -259
- package/src/rbac/api.ts +260 -216
- package/src/rbac/audit-batched.test.ts +550 -0
- package/src/rbac/audit-batched.ts +5 -4
- package/src/rbac/audit.test.ts +225 -28
- package/src/rbac/audit.ts +26 -18
- package/src/rbac/auth-rbac-security.integration.test.tsx +300 -0
- package/src/rbac/auth-rbac.e2e.test.tsx +510 -0
- package/src/rbac/cache-invalidation.test.ts +715 -0
- package/src/rbac/cache-invalidation.ts +18 -15
- package/src/rbac/cache.test.ts +123 -63
- package/src/rbac/cache.ts +3 -4
- package/src/rbac/components/AccessDenied.test.tsx +324 -0
- package/src/rbac/components/AccessDenied.tsx +20 -18
- package/src/rbac/components/NavigationGuard.test.tsx +1148 -0
- package/src/rbac/components/NavigationGuard.tsx +10 -8
- package/src/rbac/components/PagePermissionGuard.guard.test.tsx +236 -0
- package/src/rbac/components/PagePermissionGuard.performance.test.tsx +252 -0
- package/src/rbac/components/PagePermissionGuard.race-condition.test.tsx +243 -0
- package/src/rbac/components/PagePermissionGuard.test.tsx +1430 -0
- package/src/rbac/components/PagePermissionGuard.tsx +188 -381
- package/src/rbac/components/PagePermissionGuard.verification.test.tsx +185 -0
- package/src/rbac/config.test.ts +131 -48
- package/src/rbac/config.ts +69 -26
- package/src/rbac/docs/event-based-apps.md +26 -13
- package/src/rbac/engine.comprehensive.test.ts +808 -0
- package/src/rbac/engine.test.ts +974 -130
- package/src/rbac/engine.ts +53 -13
- package/src/rbac/errors.test.ts +99 -87
- package/src/rbac/errors.ts +89 -55
- package/src/rbac/eslint-rules.js +2 -2
- package/src/rbac/hooks/permissions/runPermissionCheck.ts +77 -0
- package/src/rbac/hooks/permissions/useAccessLevel.test.ts +622 -0
- package/src/rbac/hooks/permissions/useAccessLevel.ts +23 -14
- package/src/rbac/hooks/permissions/useCan.test.ts +798 -0
- package/src/rbac/hooks/permissions/useCan.ts +173 -253
- package/src/rbac/hooks/permissions/useMultiplePermissions.test.ts +843 -0
- package/src/rbac/hooks/permissions/useMultiplePermissions.ts +63 -10
- package/src/rbac/hooks/permissions/usePermissions.test.ts +543 -0
- package/src/rbac/hooks/permissions/usePermissions.ts +50 -78
- package/src/rbac/hooks/useCan.test.ts +348 -32
- package/src/rbac/hooks/usePageAccessLogging.ts +160 -0
- package/src/rbac/hooks/usePageGuardScope.ts +117 -0
- package/src/rbac/hooks/usePagePermissionCheck.ts +67 -0
- package/src/rbac/hooks/usePermissions.integration.test.ts +427 -0
- package/src/rbac/hooks/usePermissions.stability.test.ts +268 -0
- package/src/rbac/hooks/usePermissions.test.ts +459 -33
- package/src/rbac/hooks/usePermissions.ts +5 -7
- package/src/rbac/hooks/useRBAC.test.ts +1784 -21
- package/src/rbac/hooks/useRBAC.ts +148 -88
- package/src/rbac/hooks/useResolvedScope.test.ts +442 -5
- package/src/rbac/hooks/useResolvedScope.ts +4 -1
- package/src/rbac/hooks/useResourcePermissions.test.ts +561 -24
- package/src/rbac/hooks/useResourcePermissions.ts +76 -140
- package/src/rbac/hooks/useResourcePermissionsSuperAdmin.ts +67 -0
- package/src/rbac/hooks/useRoleManagement.test.ts +634 -61
- package/src/rbac/hooks/useRoleManagement.ts +158 -586
- package/src/rbac/hooks/useSecureSupabase.test.ts +1179 -0
- package/src/rbac/hooks/useSecureSupabase.ts +21 -14
- package/src/rbac/hooks/useSuperAdminCheck.ts +80 -0
- package/src/rbac/index.test.ts +107 -0
- package/src/rbac/index.ts +32 -32
- package/src/rbac/performance.test.ts +451 -0
- package/src/rbac/permissions.test.ts +149 -68
- package/src/rbac/permissions.ts +0 -3
- package/src/rbac/rbac-core.test.tsx +276 -0
- package/src/rbac/rbac-engine-core-logic.test.ts +387 -0
- package/src/rbac/rbac-engine-simplified.test.ts +252 -0
- package/src/rbac/rbac-functions.test.ts +703 -0
- package/src/rbac/rbac-integration.test.ts +523 -0
- package/src/rbac/rbac-role-isolation.test.ts +456 -0
- package/src/rbac/request-deduplication.test.ts +352 -0
- package/src/rbac/request-deduplication.ts +5 -4
- package/src/rbac/scenarios.user-role.test.tsx +271 -0
- package/src/rbac/secureClient.test.ts +499 -115
- package/src/rbac/secureClient.ts +54 -28
- package/src/rbac/security.test.ts +448 -44
- package/src/rbac/security.ts +7 -6
- package/src/rbac/types/roleManagement.ts +66 -0
- package/src/rbac/types.test.ts +236 -0
- package/src/rbac/types.ts +7 -5
- package/src/rbac/utils/clientSecurity.test.ts +192 -0
- package/src/rbac/utils/clientSecurity.ts +6 -4
- package/src/rbac/utils/contextValidator.test.ts +126 -0
- package/src/rbac/utils/contextValidator.ts +6 -3
- package/src/rbac/utils/deep-equal.test.ts +76 -0
- package/src/rbac/utils/eventContext.test.ts +401 -0
- package/src/rbac/utils/eventContext.ts +38 -34
- package/src/rbac/utils/fetchPermissionMap.ts +13 -0
- package/src/rbac/utils/permissionMapHelpers.ts +34 -0
- package/src/rbac/utils/roleManagementRpc.ts +303 -0
- package/src/services/AuthService.edge-cases.test.ts +746 -0
- package/src/services/AuthService.restoreSession.test.ts +59 -0
- package/src/services/AuthService.test.ts +1362 -0
- package/src/services/AuthService.ts +197 -216
- package/src/services/BaseService.edge-cases.test.ts +506 -0
- package/src/services/BaseService.test.ts +363 -0
- package/src/services/EventService.edge-cases.test.ts +636 -0
- package/src/services/EventService.eventColours.test.ts +64 -0
- package/src/services/EventService.test.ts +1250 -0
- package/src/services/EventService.ts +244 -315
- package/src/services/InactivityService.edge-cases.test.ts +492 -0
- package/src/services/InactivityService.lifecycle.test.ts +406 -0
- package/src/services/InactivityService.test.ts +829 -0
- package/src/services/InactivityService.ts +172 -213
- package/src/services/OrganisationService.edge-cases.test.ts +633 -0
- package/src/services/OrganisationService.pagination.test.ts +409 -0
- package/src/services/OrganisationService.test.ts +1579 -0
- package/src/services/OrganisationService.ts +186 -257
- package/src/services/base/BaseService.test.ts +214 -0
- package/src/services/interfaces/IAuthService.test.ts +184 -0
- package/src/services/interfaces/IAuthService.ts +10 -9
- package/src/services/interfaces/IEventService.test.ts +176 -0
- package/src/services/interfaces/IInactivityService.test.ts +183 -0
- package/src/services/interfaces/IOrganisationService.test.ts +207 -0
- package/src/services/interfaces/IOrganisationService.ts +0 -1
- package/src/styles/core.css +244 -12
- package/src/theming/parseEventColours.test.ts +321 -0
- package/src/theming/parseEventColours.ts +18 -9
- package/src/theming/runtime.test.ts +495 -0
- package/src/theming/runtime.ts +72 -7
- package/src/types/api-result.ts +53 -0
- package/src/types/auth.ts +0 -1
- package/src/types/core.test.ts +397 -0
- package/src/types/database-generated.test.ts +78 -0
- package/src/types/database.generated.ts +45 -10
- package/src/types/event.ts +39 -19
- package/src/types/file-reference.test.ts +351 -0
- package/src/types/file-reference.ts +37 -12
- package/src/types/guards.test.ts +246 -0
- package/src/types/index.test.ts +265 -0
- package/src/types/index.ts +3 -0
- package/src/types/organisation.roles.test.ts +55 -0
- package/src/types/organisation.test.ts +1105 -0
- package/src/types/organisation.ts +15 -15
- package/src/types/rpc-responses.ts +33 -0
- package/src/types/supabase.ts +14 -6
- package/src/types/theme.test.ts +830 -0
- package/src/types/type-validation.test.ts +526 -0
- package/src/types/validation.test.ts +729 -0
- package/src/types/vitest-globals.d.ts +1 -1
- package/src/utils/app/appConfig.test.ts +235 -0
- package/src/utils/app/appIdResolver.test.ts +252 -57
- package/src/utils/app/appIdResolver.ts +31 -20
- package/src/utils/app/appNameResolver.test.ts +18 -10
- package/src/utils/app/appNameResolver.ts +11 -9
- package/src/utils/app/appPortMap.test.ts +125 -0
- package/src/utils/app/appPortMap.ts +51 -0
- package/src/utils/app/buildAppUrl.test.ts +273 -0
- package/src/utils/app/buildAppUrl.ts +114 -0
- package/src/utils/appConfig.unit.test.ts +55 -0
- package/src/utils/audit/audit.test.ts +354 -39
- package/src/utils/audit.unit.test.ts +69 -0
- package/src/utils/auth-utils.unit.test.ts +69 -0
- package/src/utils/bundleAnalysis.unit.test.ts +326 -0
- package/src/utils/cn.unit.test.ts +34 -0
- package/src/utils/context/organisationContext.test.ts +115 -95
- package/src/utils/context/organisationContext.ts +32 -43
- package/src/utils/context/sessionTracking.test.ts +354 -0
- package/src/utils/core/cn.test.ts +66 -0
- package/src/utils/core/debugLogger.test.ts +113 -0
- package/src/utils/core/debugLogger.ts +15 -8
- package/src/utils/core/logger.test.ts +217 -0
- package/src/utils/core/logger.ts +20 -16
- package/src/utils/core/mergeRefs.ts +24 -0
- package/src/utils/debugLogger.test.ts +417 -0
- package/src/utils/device/deviceFingerprint.test.ts +8 -5
- package/src/utils/device/deviceFingerprint.ts +3 -3
- package/src/utils/deviceFingerprint.unit.test.ts +818 -0
- package/src/utils/dynamic/createLazyComponent.tsx +46 -0
- package/src/utils/dynamic/dynamicUtils.test.ts +185 -0
- package/src/utils/dynamic/dynamicUtils.ts +6 -6
- package/src/utils/dynamic/lazyLoad.test.tsx +156 -0
- package/src/utils/dynamic/lazyLoad.tsx +8 -36
- package/src/utils/dynamic/papaparseLoader.ts +7 -0
- package/src/utils/dynamicUtils.unit.test.ts +331 -0
- package/src/utils/file-reference/file-reference.test.ts +1238 -0
- package/src/utils/file-reference/index.ts +330 -348
- package/src/utils/formatDate.unit.test.ts +109 -0
- package/src/utils/formatting/formatDate.test.ts +22 -148
- package/src/utils/formatting/formatDateTime.test.ts +41 -119
- package/src/utils/formatting/formatDateTimeTimezone.test.ts +41 -85
- package/src/utils/formatting/formatNumber.test.ts +259 -0
- package/src/utils/formatting/formatTime.test.ts +36 -128
- package/src/utils/formatting/formatting.ts +1 -1
- package/src/utils/formatting.unit.test.ts +99 -0
- package/src/utils/google-places/googlePlacesUtils.test.ts +127 -36
- package/src/utils/google-places/googlePlacesUtils.ts +67 -86
- package/src/utils/google-places/loadGoogleMapsScript.test.ts +68 -8
- package/src/utils/google-places/loadGoogleMapsScript.ts +140 -118
- package/src/utils/index.ts +52 -11
- package/src/utils/index.unit.test.ts +251 -0
- package/src/utils/lazyLoad.unit.test.tsx +319 -0
- package/src/utils/location/location.test.ts +19 -116
- package/src/utils/logger.unit.test.ts +398 -0
- package/src/utils/organisationContext.unit.test.ts +180 -0
- package/src/utils/performance/bundleAnalysis.test.ts +148 -0
- package/src/utils/performance/bundleAnalysis.ts +16 -22
- package/src/utils/performance/performanceBenchmark.test.ts +251 -0
- package/src/utils/performance/performanceBenchmark.ts +12 -4
- package/src/utils/performance/performanceBudgets.test.ts +241 -0
- package/src/utils/performance/performanceBudgets.ts +9 -6
- package/src/utils/performanceBenchmark.test.ts +174 -0
- package/src/utils/performanceBudgets.unit.test.ts +288 -0
- package/src/utils/permissionTypes.unit.test.ts +250 -0
- package/src/utils/permissionUtils.unit.test.ts +362 -0
- package/src/utils/permissions/permissionTypes.test.ts +149 -0
- package/src/utils/permissions/permissionUtils.test.ts +20 -42
- package/src/utils/persistence/keyDerivation.test.ts +306 -0
- package/src/utils/persistence/sensitiveFieldDetection.test.ts +271 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +2 -2
- package/src/utils/request-deduplication.test.ts +349 -0
- package/src/utils/request-deduplication.ts +6 -4
- package/src/utils/sanitization.unit.test.ts +346 -0
- package/src/utils/schemaUtils.unit.test.ts +441 -0
- package/src/utils/secureDataAccess.unit.test.ts +334 -0
- package/src/utils/secureErrors.unit.test.ts +390 -0
- package/src/utils/secureStorage.unit.test.ts +289 -0
- package/src/utils/security/auth-utils.ts +38 -27
- package/src/utils/security/secureDataAccess.test.ts +22 -191
- package/src/utils/security/secureDataAccess.ts +241 -281
- package/src/utils/security/secureErrors.test.ts +163 -0
- package/src/utils/security/secureStorage.test.ts +156 -0
- package/src/utils/security/secureStorage.ts +1 -1
- package/src/utils/security/security.test.ts +212 -0
- package/src/utils/security/security.ts +15 -18
- package/src/utils/security/securityMonitor.test.ts +90 -0
- package/src/utils/security/securityMonitor.ts +1 -1
- package/src/utils/security.unit.test.ts +155 -0
- package/src/utils/securityMonitor.unit.test.ts +276 -0
- package/src/utils/sessionTracking.unit.test.ts +218 -0
- package/src/utils/storage/config.unit.test.ts +239 -0
- package/src/utils/storage/helpers.test.ts +769 -456
- package/src/utils/storage/helpers.ts +174 -253
- package/src/utils/storage/index.unit.test.ts +68 -0
- package/src/utils/storage/storageUtils.ts +32 -0
- package/src/utils/storage/types.ts +9 -2
- package/src/utils/supabase/createBaseClient.test.ts +201 -0
- package/src/utils/supabase/createBaseClient.ts +2 -1
- package/src/utils/timezone/timezone.test.ts +26 -44
- package/src/utils/timezone.test.ts +345 -0
- package/src/utils/validation/common.test.ts +115 -0
- package/src/utils/validation/csrf.test.ts +198 -0
- package/src/utils/validation/csrf.ts +42 -41
- package/src/utils/validation/htmlSanitization.ts +27 -31
- package/src/utils/validation/htmlSanitization.unit.test.ts +618 -0
- package/src/utils/validation/passwordSchema.test.ts +164 -0
- package/src/utils/validation/schema.test.ts +127 -0
- package/src/utils/validation/schema.ts +6 -3
- package/src/utils/validation/sqlInjectionProtection.test.ts +165 -0
- package/src/utils/validation/sqlInjectionProtection.ts +2 -2
- package/src/utils/validation/user.test.ts +173 -0
- package/src/utils/validation/validation.test.ts +197 -0
- package/src/utils/validation/validationUtils.test.ts +294 -0
- package/src/utils/validation.unit.test.ts +307 -0
- package/src/utils/validationUtils.unit.test.ts +558 -0
- package/src/vite-env.d.ts +6 -0
- package/dist/AuthService-DmfO5rGS.d.ts +0 -524
- package/dist/DataTable-DRUIgtUH.d.ts +0 -166
- package/dist/DataTable-SOAFXIWY.js +0 -15
- package/dist/PublicPageProvider-CIGSujI2.d.ts +0 -4147
- package/dist/UnifiedAuthProvider-7SNDOWYD.js +0 -7
- package/dist/UnifiedAuthProvider-CKvHP1MK.d.ts +0 -139
- package/dist/api-7P7DI652.js +0 -4
- package/dist/audit-MYQXYZFU.js +0 -3
- package/dist/auth-BZOJqrdd.d.ts +0 -49
- package/dist/chunk-4DDCYDQ3.js +0 -544
- package/dist/chunk-5HNSDQWH.js +0 -5046
- package/dist/chunk-5W2A3DRC.js +0 -164
- package/dist/chunk-6GLLNA6U.js +0 -31
- package/dist/chunk-7ILTDCL2.js +0 -80
- package/dist/chunk-A3W6LW53.js +0 -70
- package/dist/chunk-AHU7G2R5.js +0 -423
- package/dist/chunk-C7ZQ5O4C.js +0 -481
- package/dist/chunk-EF2UGZWY.js +0 -611
- package/dist/chunk-FEJLJNWA.js +0 -181
- package/dist/chunk-FYHN4DD5.js +0 -415
- package/dist/chunk-GS5672WG.js +0 -2003
- package/dist/chunk-HF6O3O37.js +0 -187
- package/dist/chunk-J2U36LHD.js +0 -8517
- package/dist/chunk-LX6U42O3.js +0 -2177
- package/dist/chunk-MPBLMWVR.js +0 -2161
- package/dist/chunk-OJ4SKRSV.js +0 -105
- package/dist/chunk-S6ZQKDY6.js +0 -62
- package/dist/chunk-S7DKJPLT.js +0 -699
- package/dist/chunk-T5CVK4R3.js +0 -2816
- package/dist/chunk-TTRFSOKR.js +0 -121
- package/dist/chunk-Z2FNRKF3.js +0 -994
- package/dist/database.generated-DT8JTZiP.d.ts +0 -9406
- package/dist/event-CW5YB_2p.d.ts +0 -239
- package/dist/file-reference-BavO2eQj.d.ts +0 -148
- package/dist/functions-lBy5L2ry.d.ts +0 -208
- package/dist/timezone-0AyangqX.d.ts +0 -697
- package/dist/types-BeoeWV5I.d.ts +0 -110
- package/dist/types-DXstZpNI.d.ts +0 -614
- package/dist/types-t9H8qKRw.d.ts +0 -55
- package/dist/usePublicRouteParams-DQLrDqDb.d.ts +0 -876
- package/dist/useToast-AyaT-x7p.d.ts +0 -68
- package/dist/validation-643vUDZW.d.ts +0 -177
- package/scripts/build-docs-incremental.js +0 -179
- package/scripts/eslint-audit.cjs +0 -123
- package/scripts/generate-docs.js +0 -157
- package/scripts/install-cursor-rules.cjs +0 -255
- package/scripts/install-eslint-config.cjs +0 -349
- package/scripts/setup-build-cache.js +0 -73
- package/scripts/validate-pre-publish.js +0 -145
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +0 -260
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +0 -224
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +0 -273
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +0 -99
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +0 -448
- package/src/__tests__/helpers/__tests__/timer-utils.test.ts +0 -371
- package/src/__tests__/hooks/usePermissions.test.ts +0 -268
- package/src/__tests__/integration/UserProfile.test.tsx +0 -124
- package/src/__tests__/public-recipe-view.test.ts +0 -228
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +0 -220
- package/src/__tests__/rls-policies.test.ts +0 -471
- package/src/components/DataTable/__tests__/DataTable.comprehensive.test.tsx +0 -759
- package/src/components/DataTable/__tests__/DataTable.default-state.test.tsx +0 -524
- package/src/components/DataTable/__tests__/DataTable.export.test.tsx +0 -705
- package/src/components/DataTable/__tests__/DataTable.grouping-aggregation.test.tsx +0 -658
- package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +0 -192
- package/src/components/DataTable/__tests__/DataTable.select-label-display.test.tsx +0 -483
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -876
- package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +0 -220
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +0 -1474
- package/src/components/DataTable/__tests__/README.md +0 -145
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +0 -788
- package/src/components/DataTable/__tests__/keyboard.test.tsx +0 -756
- package/src/components/DataTable/__tests__/mocks/MockRBACProvider.tsx +0 -66
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +0 -730
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +0 -325
- package/src/components/DataTable/__tests__/styles.test.ts +0 -382
- package/src/components/DataTable/__tests__/test-utils/dataFactories.ts +0 -103
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +0 -380
- package/src/components/DataTable/__tests__/test-utils.ts +0 -94
- package/src/components/DataTable/components/AccessDeniedPage.tsx +0 -159
- package/src/components/DataTable/components/ActionButtons.tsx +0 -190
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +0 -160
- package/src/components/DataTable/components/ColumnFilter.tsx +0 -118
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +0 -114
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +0 -225
- package/src/components/DataTable/components/DataTableLayout.tsx +0 -573
- package/src/components/DataTable/components/DataTableModals.tsx +0 -245
- package/src/components/DataTable/components/DataTableToolbar.tsx +0 -271
- package/src/components/DataTable/components/EditFields.tsx +0 -327
- package/src/components/DataTable/components/EditableRow.tsx +0 -462
- package/src/components/DataTable/components/EmptyState.tsx +0 -79
- package/src/components/DataTable/components/FilterRow.tsx +0 -141
- package/src/components/DataTable/components/LoadingState.tsx +0 -17
- package/src/components/DataTable/components/PaginationControls.tsx +0 -289
- package/src/components/DataTable/components/RowComponent.tsx +0 -403
- package/src/components/DataTable/components/SortIndicator.tsx +0 -50
- package/src/components/DataTable/components/UnifiedTableBody.tsx +0 -355
- package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +0 -657
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +0 -913
- package/src/components/DataTable/components/__tests__/BulkOperationsDropdown.test.tsx +0 -572
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +0 -612
- package/src/components/DataTable/components/__tests__/ColumnVisibilityDropdown.test.tsx +0 -708
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +0 -479
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +0 -475
- package/src/components/DataTable/components/__tests__/DataTableToolbar.test.tsx +0 -157
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +0 -1061
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +0 -437
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +0 -474
- package/src/components/DataTable/components/__tests__/GroupingDropdown.test.tsx +0 -617
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +0 -1093
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +0 -139
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +0 -519
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +0 -1004
- package/src/components/DataTable/components/cellValueUtils.ts +0 -40
- package/src/components/DataTable/components/hooks/useImportModalFocus.ts +0 -53
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -122
- package/src/components/DataTable/components/index.ts +0 -16
- package/src/components/DataTable/context/__tests__/DataTableContext.test.tsx +0 -342
- package/src/components/DataTable/core/ActionManager.ts +0 -235
- package/src/components/DataTable/core/ColumnManager.ts +0 -205
- package/src/components/DataTable/core/DataManager.ts +0 -188
- package/src/components/DataTable/core/LocalDataAdapter.ts +0 -274
- package/src/components/DataTable/core/PluginRegistry.ts +0 -229
- package/src/components/DataTable/core/StateManager.ts +0 -312
- package/src/components/DataTable/core/__tests__/ActionManager.test.ts +0 -123
- package/src/components/DataTable/core/__tests__/ColumnFactory.test.ts +0 -305
- package/src/components/DataTable/core/__tests__/ColumnManager.test.ts +0 -84
- package/src/components/DataTable/core/__tests__/DataManager.test.ts +0 -115
- package/src/components/DataTable/core/__tests__/LocalDataAdapter.test.ts +0 -100
- package/src/components/DataTable/core/__tests__/PluginRegistry.test.ts +0 -120
- package/src/components/DataTable/core/__tests__/StateManager.test.ts +0 -104
- package/src/components/DataTable/core/index.ts +0 -1
- package/src/components/DataTable/core/interfaces.ts +0 -338
- package/src/components/DataTable/hooks/__tests__/useColumnOrderPersistence.test.ts +0 -521
- package/src/components/DataTable/hooks/__tests__/useColumnVisibilityPersistence.test.ts +0 -167
- package/src/components/DataTable/hooks/__tests__/useDataTableConfiguration.test.ts +0 -124
- package/src/components/DataTable/hooks/__tests__/useDataTableDataPipeline.test.ts +0 -117
- package/src/components/DataTable/hooks/__tests__/useDataTablePermissions.test.ts +0 -102
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +0 -596
- package/src/components/DataTable/hooks/__tests__/useEffectiveColumnOrder.test.ts +0 -53
- package/src/components/DataTable/hooks/__tests__/useHierarchicalState.test.ts +0 -214
- package/src/components/DataTable/hooks/__tests__/useTableColumns.test.ts +0 -448
- package/src/components/DataTable/hooks/index.ts +0 -13
- package/src/components/DataTable/types.ts +0 -761
- package/src/components/DataTable/utils/__tests__/a11yUtils.test.ts +0 -612
- package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +0 -94
- package/src/components/DataTable/utils/__tests__/errorHandling.test.ts +0 -266
- package/src/components/DataTable/utils/__tests__/exportUtils.test.ts +0 -954
- package/src/components/DataTable/utils/__tests__/flexibleImport.test.ts +0 -573
- package/src/components/DataTable/utils/__tests__/hierarchicalSorting.test.ts +0 -247
- package/src/components/DataTable/utils/__tests__/hierarchicalUtils.test.ts +0 -570
- package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +0 -470
- package/src/components/DataTable/utils/__tests__/rowUtils.test.ts +0 -251
- package/src/components/DataTable/utils/__tests__/selectFieldUtils.test.ts +0 -207
- package/src/components/DataTable/utils/index.ts +0 -10
- package/src/components/PublicLayout/index.ts +0 -32
- package/src/components/Select/hooks/useSelectEvents.ts +0 -87
- package/src/components/Select/hooks/useSelectSearch.ts +0 -91
- package/src/components/Select/hooks/useSelectState.ts +0 -104
- package/src/components/Select/utils/text.ts +0 -26
- package/src/hooks/__tests__/ServiceHooks.test.tsx +0 -615
- package/src/hooks/__tests__/hooks.integration.test.tsx +0 -607
- package/src/hooks/__tests__/index.unit.test.ts +0 -220
- package/src/hooks/__tests__/useApiFetch.unit.test.ts +0 -111
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +0 -347
- package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +0 -144
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +0 -776
- package/src/hooks/__tests__/useDataTableState.test.ts +0 -76
- package/src/hooks/__tests__/useDebounce.unit.test.ts +0 -82
- package/src/hooks/__tests__/useEvents.unit.test.ts +0 -252
- package/src/hooks/__tests__/useFileDisplay.unit.test.ts +0 -1112
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +0 -916
- package/src/hooks/__tests__/useFileUrlCache.test.ts +0 -129
- package/src/hooks/__tests__/useFocusManagement.unit.test.ts +0 -230
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +0 -828
- package/src/hooks/__tests__/useFormDialog.test.ts +0 -478
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +0 -446
- package/src/hooks/__tests__/useIsMobile.unit.test.ts +0 -317
- package/src/hooks/__tests__/useKeyboardShortcuts.unit.test.ts +0 -910
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +0 -294
- package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +0 -961
- package/src/hooks/__tests__/useOrganisations.unit.test.ts +0 -369
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +0 -694
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +0 -192
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +0 -741
- package/src/hooks/__tests__/usePreventTabReload.test.ts +0 -88
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +0 -785
- package/src/hooks/__tests__/usePublicEvent.test.ts +0 -678
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -630
- package/src/hooks/__tests__/usePublicFileDisplay.test.ts +0 -951
- package/src/hooks/__tests__/usePublicRouteParams.unit.test.ts +0 -443
- package/src/hooks/__tests__/useQueryCache.test.ts +0 -144
- package/src/hooks/__tests__/useRBAC.unit.test.ts +0 -236
- package/src/hooks/__tests__/useSessionDraft.test.ts +0 -163
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +0 -390
- package/src/hooks/__tests__/useStorage.unit.test.ts +0 -751
- package/src/hooks/__tests__/useToast.unit.test.tsx +0 -481
- package/src/hooks/__tests__/useZodForm.unit.test.tsx +0 -37
- package/src/hooks/public/index.ts +0 -36
- package/src/hooks/public/usePublicFileDisplay.ts +0 -504
- package/src/hooks/useFileDisplay.ts +0 -715
- package/src/providers/OrganisationProvider.tsx +0 -92
- package/src/providers/__tests__/AuthProvider.test.tsx +0 -287
- package/src/providers/__tests__/EventProvider.test.tsx +0 -551
- package/src/providers/__tests__/InactivityProvider.test-helper.tsx +0 -65
- package/src/providers/__tests__/InactivityProvider.test.tsx +0 -572
- package/src/providers/__tests__/OrganisationProvider.test.tsx +0 -617
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +0 -424
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +0 -596
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +0 -263
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +0 -294
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +0 -434
- package/src/rbac/__tests__/auth-rbac-security.integration.test.tsx +0 -313
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +0 -486
- package/src/rbac/__tests__/cache-invalidation.test.ts +0 -399
- package/src/rbac/__tests__/engine.comprehensive.test.ts +0 -813
- package/src/rbac/__tests__/isSuperAdmin.real.test.ts +0 -82
- package/src/rbac/__tests__/rbac-core.test.tsx +0 -276
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +0 -392
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +0 -258
- package/src/rbac/__tests__/rbac-functions.test.ts +0 -647
- package/src/rbac/__tests__/rbac-integration.test.ts +0 -524
- package/src/rbac/__tests__/rbac-role-isolation.test.ts +0 -456
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +0 -282
- package/src/rbac/audit-enhanced.ts +0 -384
- package/src/rbac/compliance/database-validator.ts +0 -165
- package/src/rbac/compliance/index.ts +0 -48
- package/src/rbac/compliance/pattern-detector.ts +0 -553
- package/src/rbac/compliance/quick-fix-suggestions.ts +0 -209
- package/src/rbac/compliance/runtime-compliance.ts +0 -99
- package/src/rbac/compliance/setup-validator.ts +0 -131
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +0 -975
- package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +0 -248
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +0 -242
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +0 -1107
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +0 -184
- package/src/rbac/components/index.ts +0 -26
- package/src/rbac/hooks/__tests__/usePermissions.integration.test.ts +0 -432
- package/src/rbac/hooks/__tests__/useSecureSupabase.test.ts +0 -579
- package/src/rbac/hooks/index.ts +0 -34
- package/src/rbac/hooks/permissions/index.ts +0 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +0 -95
- package/src/rbac/utils/__tests__/contextValidator.test.ts +0 -128
- package/src/rbac/utils/__tests__/deep-equal.test.ts +0 -53
- package/src/rbac/utils/__tests__/eventContext.test.ts +0 -433
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +0 -490
- package/src/services/__tests__/AuthService.restoreSession.test.ts +0 -39
- package/src/services/__tests__/AuthService.test.ts +0 -1332
- package/src/services/__tests__/BaseService.test.ts +0 -314
- package/src/services/__tests__/EventService.eventColours.test.ts +0 -76
- package/src/services/__tests__/EventService.test.ts +0 -1025
- package/src/services/__tests__/InactivityService.lifecycle.test.ts +0 -411
- package/src/services/__tests__/InactivityService.test.ts +0 -654
- package/src/services/__tests__/OrganisationService.pagination.test.ts +0 -409
- package/src/services/__tests__/OrganisationService.test.ts +0 -1176
- package/src/theming/__tests__/parseEventColours.test.ts +0 -321
- package/src/theming/__tests__/runtime.test.ts +0 -569
- package/src/types/__tests__/file-reference.test.ts +0 -447
- package/src/types/__tests__/guards.test.ts +0 -246
- package/src/types/__tests__/organisation.roles.test.ts +0 -55
- package/src/types/__tests__/organisation.test.ts +0 -1133
- package/src/types/__tests__/theme.test.ts +0 -830
- package/src/types/__tests__/type-validation.test.ts +0 -526
- package/src/types/__tests__/validation.test.ts +0 -731
- package/src/utils/__tests__/appConfig.unit.test.ts +0 -55
- package/src/utils/__tests__/audit.unit.test.ts +0 -69
- package/src/utils/__tests__/auth-utils.unit.test.ts +0 -70
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +0 -339
- package/src/utils/__tests__/cn.unit.test.ts +0 -34
- package/src/utils/__tests__/debugLogger.test.ts +0 -417
- package/src/utils/__tests__/deviceFingerprint.unit.test.ts +0 -818
- package/src/utils/__tests__/dynamicUtils.unit.test.ts +0 -318
- package/src/utils/__tests__/formatDate.unit.test.ts +0 -109
- package/src/utils/__tests__/formatting.unit.test.ts +0 -99
- package/src/utils/__tests__/index.unit.test.ts +0 -251
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +0 -321
- package/src/utils/__tests__/logger.unit.test.ts +0 -398
- package/src/utils/__tests__/organisationContext.unit.test.ts +0 -191
- package/src/utils/__tests__/performanceBenchmark.test.ts +0 -175
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +0 -253
- package/src/utils/__tests__/permissionTypes.unit.test.ts +0 -250
- package/src/utils/__tests__/permissionUtils.unit.test.ts +0 -362
- package/src/utils/__tests__/sanitization.unit.test.ts +0 -346
- package/src/utils/__tests__/schemaUtils.unit.test.ts +0 -441
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +0 -335
- package/src/utils/__tests__/secureErrors.unit.test.ts +0 -390
- package/src/utils/__tests__/secureStorage.unit.test.ts +0 -289
- package/src/utils/__tests__/security.unit.test.ts +0 -149
- package/src/utils/__tests__/securityMonitor.unit.test.ts +0 -276
- package/src/utils/__tests__/sessionTracking.unit.test.ts +0 -218
- package/src/utils/__tests__/timezone.test.ts +0 -345
- package/src/utils/__tests__/validation.unit.test.ts +0 -308
- package/src/utils/__tests__/validationUtils.unit.test.ts +0 -555
- package/src/utils/app/appNameResolver.simple.test.ts +0 -212
- package/src/utils/file-reference/__tests__/file-reference.test.ts +0 -875
- package/src/utils/google-places/index.ts +0 -26
- package/src/utils/location/index.ts +0 -16
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +0 -135
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +0 -123
- package/src/utils/storage/__tests__/helpers.unit.test.ts +0 -332
- package/src/utils/storage/__tests__/index.unit.test.ts +0 -16
- package/src/utils/storage/index.ts +0 -67
- package/src/utils/timezone/index.ts +0 -17
- package/src/utils/validation/__tests__/csrf.test.ts +0 -105
- package/src/utils/validation/__tests__/htmlSanitization.unit.test.ts +0 -598
- package/src/utils/validation/__tests__/sqlInjectionProtection.test.ts +0 -92
- package/src/utils/validation/__tests__/validationUtils.test.ts +0 -72
- package/src/utils/validation/index.ts +0 -73
- /package/src/components/DataTable/{components/__tests__ → ui}/COVERAGE_NOTE.md +0 -0
- /package/src/components/DataTable/utils/{__tests__/COVERAGE_NOTE.md → COVERAGE_NOTE.md} +0 -0
- /package/src/providers/{__tests__/README.md → README.md} +0 -0
- /package/src/types/{__tests__/README.md → README.md} +0 -0
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
* <DialogBody>
|
|
43
43
|
* <section className="space-y-4">
|
|
44
44
|
* <fieldset className="grid grid-cols-4 items-center gap-4">
|
|
45
|
-
* <Label htmlFor="name"
|
|
45
|
+
* <Label htmlFor="name">Name</Label>
|
|
46
46
|
* <Input id="name" defaultValue="John Doe" className="col-span-3" />
|
|
47
47
|
* </fieldset>
|
|
48
48
|
* </section>
|
|
@@ -89,44 +89,20 @@
|
|
|
89
89
|
|
|
90
90
|
import * as React from 'react';
|
|
91
91
|
import { createPortal } from 'react-dom';
|
|
92
|
-
import { X } from 'lucide-react';
|
|
93
92
|
import { useLocation } from 'react-router-dom';
|
|
93
|
+
import { Button, IconButton } from '../Button';
|
|
94
|
+
import { X } from '../../icons';
|
|
94
95
|
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
95
96
|
import { cn } from '../../utils/core/cn';
|
|
97
|
+
import { mergeRefs } from '../../utils/core/mergeRefs';
|
|
96
98
|
import { renderSafeHtml } from '../../utils/validation/htmlSanitization';
|
|
97
99
|
import { useState, useEffect, useRef, useCallback, useId, useMemo } from 'react';
|
|
98
100
|
import { useFocusTrap } from '../../hooks/useFocusTrap';
|
|
99
|
-
import { useSessionDraft } from '../../hooks/useSessionDraft';
|
|
100
101
|
import { deriveDialogKey } from '../../utils/persistence/keyDerivation';
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*/
|
|
106
|
-
function debounce<T extends (...args: any[]) => void>(
|
|
107
|
-
func: T,
|
|
108
|
-
wait: number
|
|
109
|
-
): T & { cancel: () => void } {
|
|
110
|
-
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
111
|
-
|
|
112
|
-
const debounced = ((...args: Parameters<T>) => {
|
|
113
|
-
if (timeoutId !== null) {
|
|
114
|
-
clearTimeout(timeoutId);
|
|
115
|
-
}
|
|
116
|
-
timeoutId = setTimeout(() => {
|
|
117
|
-
func(...args);
|
|
118
|
-
}, wait);
|
|
119
|
-
}) as T & { cancel: () => void };
|
|
120
|
-
|
|
121
|
-
debounced.cancel = () => {
|
|
122
|
-
if (timeoutId !== null) {
|
|
123
|
-
clearTimeout(timeoutId);
|
|
124
|
-
timeoutId = null;
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
return debounced;
|
|
129
|
-
}
|
|
102
|
+
import { createLogger } from '../../utils/core/logger';
|
|
103
|
+
import { useDialogPersistence } from './useDialogPersistence';
|
|
104
|
+
import { useDialogDimensions } from './useDialogDimensions';
|
|
105
|
+
import { useDialogLifecycle } from './useDialogLifecycle';
|
|
130
106
|
|
|
131
107
|
/**
|
|
132
108
|
* Dialog size variants
|
|
@@ -143,7 +119,6 @@ interface DialogContextValue {
|
|
|
143
119
|
dialogRef: React.RefObject<HTMLDialogElement | null>;
|
|
144
120
|
titleId: string;
|
|
145
121
|
descriptionId: string;
|
|
146
|
-
dialogTitle?: string; // For persistence key derivation
|
|
147
122
|
markClosedByUser?: () => void; // Callback to mark dialog as closed by user (for Cancel buttons, etc.)
|
|
148
123
|
}
|
|
149
124
|
|
|
@@ -218,12 +193,6 @@ export interface DialogContentProps extends React.HTMLAttributes<HTMLDialogEleme
|
|
|
218
193
|
persistOpenState?: boolean;
|
|
219
194
|
}
|
|
220
195
|
|
|
221
|
-
/**
|
|
222
|
-
* Props for the DialogOverlay component
|
|
223
|
-
* @public
|
|
224
|
-
*/
|
|
225
|
-
export interface DialogOverlayProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
226
|
-
|
|
227
196
|
/**
|
|
228
197
|
* Props for the DialogPortal component
|
|
229
198
|
* @public
|
|
@@ -232,12 +201,6 @@ export interface DialogPortalProps {
|
|
|
232
201
|
children: React.ReactNode;
|
|
233
202
|
}
|
|
234
203
|
|
|
235
|
-
/**
|
|
236
|
-
* Props for the DialogClose component
|
|
237
|
-
* @public
|
|
238
|
-
*/
|
|
239
|
-
export interface DialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {}
|
|
240
|
-
|
|
241
204
|
/**
|
|
242
205
|
* Props for the DialogHeader component (semantic header element)
|
|
243
206
|
* @public
|
|
@@ -267,8 +230,6 @@ export interface DialogBodyProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
267
230
|
htmlContent?: string;
|
|
268
231
|
/** Whether to allow HTML content rendering (default: true) */
|
|
269
232
|
allowHtml?: boolean;
|
|
270
|
-
/** Whether to use strict HTML sanitization (default: true) */
|
|
271
|
-
strictSanitization?: boolean;
|
|
272
233
|
/** Whether to log HTML sanitization warnings to console (default: false) */
|
|
273
234
|
logWarnings?: boolean;
|
|
274
235
|
}
|
|
@@ -295,16 +256,6 @@ export interface DialogDescriptionProps extends React.HTMLAttributes<HTMLParagra
|
|
|
295
256
|
allowHtml?: boolean;
|
|
296
257
|
}
|
|
297
258
|
|
|
298
|
-
// Size mapping for dialog variants
|
|
299
|
-
const sizeClasses = {
|
|
300
|
-
sm: 'max-w-sm',
|
|
301
|
-
md: 'max-w-md',
|
|
302
|
-
lg: 'max-w-lg',
|
|
303
|
-
xl: 'max-w-xl',
|
|
304
|
-
full: 'max-w-full size-full',
|
|
305
|
-
auto: 'max-w-none w-auto min-w-0'
|
|
306
|
-
};
|
|
307
|
-
|
|
308
259
|
/**
|
|
309
260
|
* Dialog root component
|
|
310
261
|
* Provides context for dialog state management
|
|
@@ -319,7 +270,6 @@ const Dialog = React.memo<DialogProps>(function Dialog({
|
|
|
319
270
|
const dialogRef = useRef<HTMLDialogElement | null>(null);
|
|
320
271
|
const titleId = useId();
|
|
321
272
|
const descriptionId = useId();
|
|
322
|
-
const dialogTitleRef = useRef<string | undefined>(undefined);
|
|
323
273
|
const [markClosedByUser, setMarkClosedByUserState] = useState<(() => void) | undefined>(undefined);
|
|
324
274
|
|
|
325
275
|
const isControlled = controlledOpen !== undefined;
|
|
@@ -338,15 +288,9 @@ const Dialog = React.memo<DialogProps>(function Dialog({
|
|
|
338
288
|
dialogRef,
|
|
339
289
|
titleId,
|
|
340
290
|
descriptionId,
|
|
341
|
-
dialogTitle: dialogTitleRef.current,
|
|
342
291
|
markClosedByUser, // Set by DialogContent
|
|
343
292
|
}), [open, handleOpenChange, titleId, descriptionId, markClosedByUser]);
|
|
344
293
|
|
|
345
|
-
// Expose function to set dialog title (called by DialogContent)
|
|
346
|
-
const setDialogTitle = useCallback((title: string | undefined) => {
|
|
347
|
-
dialogTitleRef.current = title;
|
|
348
|
-
}, []);
|
|
349
|
-
|
|
350
294
|
// Expose function to set markClosedByUser callback (called by DialogContent)
|
|
351
295
|
const setMarkClosedByUser = useCallback((callback: (() => void) | undefined) => {
|
|
352
296
|
setMarkClosedByUserState(callback);
|
|
@@ -354,22 +298,17 @@ const Dialog = React.memo<DialogProps>(function Dialog({
|
|
|
354
298
|
|
|
355
299
|
return (
|
|
356
300
|
<DialogContext.Provider value={contextValue}>
|
|
357
|
-
<
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
</DialogMarkClosedContext.Provider>
|
|
361
|
-
</DialogTitleContext.Provider>
|
|
301
|
+
<DialogMarkClosedContext.Provider value={setMarkClosedByUser}>
|
|
302
|
+
{children}
|
|
303
|
+
</DialogMarkClosedContext.Provider>
|
|
362
304
|
</DialogContext.Provider>
|
|
363
305
|
);
|
|
364
306
|
});
|
|
365
307
|
|
|
366
|
-
// Context for setting dialog title from DialogContent
|
|
367
|
-
const DialogTitleContext = React.createContext<((title: string | undefined) => void) | null>(null);
|
|
368
|
-
|
|
369
308
|
// Context for setting markClosedByUser callback from DialogContent
|
|
370
309
|
const DialogMarkClosedContext = React.createContext<((callback: (() => void) | undefined) => void) | null>(null);
|
|
371
310
|
|
|
372
|
-
// Context for
|
|
311
|
+
// Context for passing markClosedByUser to DialogClose in the same render (DialogContext.markClosedByUser is set async)
|
|
373
312
|
const DialogCloseContext = React.createContext<(() => void) | null>(null);
|
|
374
313
|
Dialog.displayName = 'Dialog';
|
|
375
314
|
|
|
@@ -377,7 +316,7 @@ Dialog.displayName = 'Dialog';
|
|
|
377
316
|
* DialogTrigger component
|
|
378
317
|
* Opens the dialog when clicked
|
|
379
318
|
*/
|
|
380
|
-
const DialogTrigger = React.forwardRef<
|
|
319
|
+
const DialogTrigger = React.forwardRef<HTMLButtonElement, DialogTriggerProps>(
|
|
381
320
|
({ children, asChild = false, className, onClick, ...props }, ref) => {
|
|
382
321
|
const { onOpenChange } = useDialogContext();
|
|
383
322
|
|
|
@@ -387,24 +326,25 @@ const DialogTrigger = React.forwardRef<HTMLElement, DialogTriggerProps>(
|
|
|
387
326
|
}, [onOpenChange, onClick]);
|
|
388
327
|
|
|
389
328
|
if (asChild && React.isValidElement(children)) {
|
|
390
|
-
|
|
329
|
+
const childElement = children as React.ReactElement<React.HTMLAttributes<HTMLElement>>;
|
|
330
|
+
return React.cloneElement(childElement, {
|
|
391
331
|
ref,
|
|
392
332
|
onClick: handleClick,
|
|
393
|
-
className: cn(className,
|
|
333
|
+
className: cn(className, childElement.props?.className),
|
|
394
334
|
...props,
|
|
395
|
-
});
|
|
335
|
+
} as React.HTMLAttributes<HTMLElement> & React.RefAttributes<HTMLElement>);
|
|
396
336
|
}
|
|
397
337
|
|
|
398
338
|
return (
|
|
399
|
-
<
|
|
400
|
-
ref={ref
|
|
339
|
+
<Button
|
|
340
|
+
ref={ref}
|
|
401
341
|
type="button"
|
|
402
342
|
onClick={handleClick}
|
|
403
343
|
className={className}
|
|
404
344
|
{...props}
|
|
405
345
|
>
|
|
406
346
|
{children}
|
|
407
|
-
</
|
|
347
|
+
</Button>
|
|
408
348
|
);
|
|
409
349
|
}
|
|
410
350
|
);
|
|
@@ -428,222 +368,28 @@ const DialogPortal: React.FC<DialogPortalProps> = ({ children }) => {
|
|
|
428
368
|
};
|
|
429
369
|
DialogPortal.displayName = 'DialogPortal';
|
|
430
370
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
DialogOverlay.displayName = 'DialogOverlay';
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Custom hook for managing smart dialog dimensions
|
|
449
|
-
* Handles responsive sizing and viewport-based constraints
|
|
450
|
-
*/
|
|
451
|
-
const useSmartDimensions = ({
|
|
452
|
-
maxHeightPercent,
|
|
453
|
-
maxWidthPercent,
|
|
454
|
-
maxHeight,
|
|
455
|
-
maxWidth,
|
|
456
|
-
minHeight,
|
|
457
|
-
minWidth,
|
|
458
|
-
enableScrolling = false,
|
|
459
|
-
}: {
|
|
460
|
-
maxHeightPercent?: number;
|
|
461
|
-
maxWidthPercent?: number;
|
|
462
|
-
maxHeight?: string;
|
|
463
|
-
maxWidth?: string;
|
|
464
|
-
minHeight?: string;
|
|
465
|
-
minWidth?: string;
|
|
466
|
-
enableScrolling?: boolean;
|
|
467
|
-
}) => {
|
|
468
|
-
const [dimensions, setDimensions] = useState<React.CSSProperties>({});
|
|
469
|
-
|
|
470
|
-
useEffect(() => {
|
|
471
|
-
if (!maxHeightPercent && !maxWidthPercent && !maxHeight && !maxWidth && !minHeight && !minWidth) {
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const updateDimensions = () => {
|
|
476
|
-
const result: React.CSSProperties = {};
|
|
477
|
-
|
|
478
|
-
// Handle height constraints
|
|
479
|
-
if (maxHeightPercent && typeof maxHeightPercent === 'number') {
|
|
480
|
-
const constrainedHeight = Math.min(maxHeightPercent, 95);
|
|
481
|
-
result.maxHeight = `${constrainedHeight}vh`;
|
|
482
|
-
} else if (maxHeight) {
|
|
483
|
-
result.maxHeight = maxHeight;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Handle width constraints
|
|
487
|
-
if (maxWidthPercent && typeof maxWidthPercent === 'number') {
|
|
488
|
-
const constrainedWidth = Math.min(maxWidthPercent, 95);
|
|
489
|
-
result.maxWidth = `${constrainedWidth}vw`;
|
|
490
|
-
} else if (maxWidth) {
|
|
491
|
-
result.maxWidth = maxWidth;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Include minHeight/minWidth if provided
|
|
495
|
-
if (minHeight) {
|
|
496
|
-
result.minHeight = minHeight;
|
|
497
|
-
}
|
|
498
|
-
if (minWidth) {
|
|
499
|
-
result.minWidth = minWidth;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
setDimensions(result);
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
// Debounced resize handler
|
|
506
|
-
const debouncedUpdate = debounce(updateDimensions, 100);
|
|
507
|
-
|
|
508
|
-
// Initial calculation
|
|
509
|
-
updateDimensions();
|
|
510
|
-
|
|
511
|
-
// Listen for resize events
|
|
512
|
-
window.addEventListener('resize', debouncedUpdate);
|
|
513
|
-
|
|
514
|
-
return () => {
|
|
515
|
-
window.removeEventListener('resize', debouncedUpdate);
|
|
516
|
-
debouncedUpdate.cancel();
|
|
517
|
-
};
|
|
518
|
-
}, [maxHeightPercent, maxWidthPercent, maxHeight, maxWidth, minHeight, minWidth, enableScrolling]);
|
|
519
|
-
|
|
520
|
-
// Return dimensions
|
|
521
|
-
const result: React.CSSProperties = {};
|
|
522
|
-
|
|
523
|
-
// Handle height constraints
|
|
524
|
-
if (maxHeightPercent && typeof maxHeightPercent === 'number') {
|
|
525
|
-
const constrainedHeight = Math.min(maxHeightPercent, 95);
|
|
526
|
-
result.maxHeight = `${constrainedHeight}vh`;
|
|
527
|
-
} else if (maxHeight) {
|
|
528
|
-
result.maxHeight = maxHeight;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// Handle width constraints
|
|
532
|
-
if (maxWidthPercent && typeof maxWidthPercent === 'number') {
|
|
533
|
-
const constrainedWidth = Math.min(maxWidthPercent, 95);
|
|
534
|
-
result.maxWidth = `${constrainedWidth}vw`;
|
|
535
|
-
} else if (maxWidth) {
|
|
536
|
-
result.maxWidth = maxWidth;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
// Include minHeight/minWidth if provided
|
|
540
|
-
if (minHeight) {
|
|
541
|
-
result.minHeight = minHeight;
|
|
542
|
-
}
|
|
543
|
-
if (minWidth) {
|
|
544
|
-
result.minWidth = minWidth;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
return result;
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Global lock to ensure only one dialog is open at a time
|
|
552
|
-
* Uses sessionStorage for persistence across page reloads
|
|
553
|
-
*/
|
|
554
|
-
const DIALOG_LOCK_KEY = 'pace-core:dialog:lock';
|
|
555
|
-
|
|
556
|
-
function acquireDialogLock(persistenceKey: string | null): boolean {
|
|
557
|
-
if (!persistenceKey) {
|
|
558
|
-
return true; // Non-persisted dialogs can always open
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
try {
|
|
562
|
-
const lock = sessionStorage.getItem(DIALOG_LOCK_KEY);
|
|
563
|
-
if (lock) {
|
|
564
|
-
const lockData = JSON.parse(lock);
|
|
565
|
-
// If lock is held by this dialog, allow it
|
|
566
|
-
if (lockData.key === persistenceKey) {
|
|
567
|
-
return true;
|
|
568
|
-
}
|
|
569
|
-
// If lock is held by another dialog, check if that dialog is still open
|
|
570
|
-
const lockDialog = document.querySelector(`dialog[data-persistence-key="${lockData.key}"]`) as HTMLDialogElement;
|
|
571
|
-
if (lockDialog && lockDialog.open) {
|
|
572
|
-
return false; // Another dialog is still open
|
|
573
|
-
}
|
|
574
|
-
// Lock is stale, clear it
|
|
575
|
-
sessionStorage.removeItem(DIALOG_LOCK_KEY);
|
|
576
|
-
}
|
|
577
|
-
// Acquire the lock
|
|
578
|
-
sessionStorage.setItem(DIALOG_LOCK_KEY, JSON.stringify({
|
|
579
|
-
key: persistenceKey,
|
|
580
|
-
timestamp: Date.now(),
|
|
581
|
-
}));
|
|
582
|
-
return true;
|
|
583
|
-
} catch {
|
|
584
|
-
// If sessionStorage fails, allow opening (graceful degradation)
|
|
585
|
-
return true;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
function releaseDialogLock(persistenceKey: string | null): void {
|
|
590
|
-
if (!persistenceKey) {
|
|
591
|
-
return;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
try {
|
|
595
|
-
const lock = sessionStorage.getItem(DIALOG_LOCK_KEY);
|
|
596
|
-
if (lock) {
|
|
597
|
-
const lockData = JSON.parse(lock);
|
|
598
|
-
if (lockData.key === persistenceKey) {
|
|
599
|
-
sessionStorage.removeItem(DIALOG_LOCK_KEY);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
} catch {
|
|
603
|
-
// Ignore errors
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* Check if any other dialog (besides the current one) has persisted open state
|
|
609
|
-
* This helps determine if another dialog should be allowed to auto-open
|
|
610
|
-
*/
|
|
611
|
-
function checkOtherDialogsHavePersistedState(currentPersistenceKey: string | null): boolean {
|
|
612
|
-
if (!currentPersistenceKey) {
|
|
613
|
-
return false;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
try {
|
|
617
|
-
const lock = sessionStorage.getItem(DIALOG_LOCK_KEY);
|
|
618
|
-
if (lock) {
|
|
619
|
-
const lockData = JSON.parse(lock);
|
|
620
|
-
if (lockData.key !== currentPersistenceKey) {
|
|
621
|
-
// Another dialog holds the lock
|
|
622
|
-
return true;
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
} catch {
|
|
626
|
-
// Error accessing sessionStorage - assume no other dialogs
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
return false;
|
|
371
|
+
export interface UseDialogContentStateReturn {
|
|
372
|
+
mergedRef: (node: HTMLDialogElement | null) => void;
|
|
373
|
+
canRender: boolean;
|
|
374
|
+
markClosedByUser: () => void;
|
|
375
|
+
sizeClass: string;
|
|
376
|
+
mergedStyle: React.CSSProperties;
|
|
377
|
+
hasHeightConstraint: boolean;
|
|
378
|
+
persistenceKey: string | null;
|
|
379
|
+
persistOpenState: boolean;
|
|
380
|
+
titleId: string;
|
|
381
|
+
descriptionId: string;
|
|
382
|
+
open: boolean;
|
|
383
|
+
title: string | undefined;
|
|
384
|
+
description: string | undefined;
|
|
631
385
|
}
|
|
632
386
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
* @param ref - Forwarded ref to the dialog element
|
|
639
|
-
* @returns JSX.Element - The semantic dialog content with overlay and optional close button
|
|
640
|
-
*/
|
|
641
|
-
const DialogContent = React.forwardRef<HTMLDialogElement, DialogContentProps>(
|
|
642
|
-
({
|
|
643
|
-
className,
|
|
644
|
-
children,
|
|
387
|
+
function useDialogContentState(
|
|
388
|
+
props: DialogContentProps,
|
|
389
|
+
ref: React.ForwardedRef<HTMLDialogElement>
|
|
390
|
+
): UseDialogContentStateReturn {
|
|
391
|
+
const {
|
|
645
392
|
size = 'md',
|
|
646
|
-
showCloseButton = true,
|
|
647
393
|
preventCloseOnEscape = false,
|
|
648
394
|
preventCloseOnOutsideClick = false,
|
|
649
395
|
maxHeightPercent,
|
|
@@ -657,430 +403,47 @@ const DialogContent = React.forwardRef<HTMLDialogElement, DialogContentProps>(
|
|
|
657
403
|
description,
|
|
658
404
|
style,
|
|
659
405
|
persistOpenState = true,
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
// This prevents unscoped persistence that could leak between users
|
|
691
|
-
if (!userId) {
|
|
692
|
-
return null;
|
|
693
|
-
}
|
|
694
|
-
return deriveDialogKey(
|
|
695
|
-
{
|
|
696
|
-
title,
|
|
697
|
-
description,
|
|
698
|
-
},
|
|
699
|
-
location,
|
|
700
|
-
userId
|
|
701
|
-
);
|
|
702
|
-
}, [title, description, location, userId, persistOpenState]);
|
|
703
|
-
|
|
704
|
-
// Use session draft for open state persistence
|
|
705
|
-
// Only enabled when we have a valid persistenceKey (which requires userId)
|
|
706
|
-
const { state: persistedOpen, setState: setPersistedOpen, clearDraft, wasRestored } = useSessionDraft<boolean>(
|
|
707
|
-
persistenceKey ? `${persistenceKey}:open` : 'dialog:no-key:open',
|
|
708
|
-
false,
|
|
709
|
-
{
|
|
710
|
-
enabled: Boolean(persistenceKey && persistOpenState && userId),
|
|
711
|
-
debounceMs: 300,
|
|
712
|
-
}
|
|
713
|
-
);
|
|
714
|
-
|
|
715
|
-
// Track if we've attempted auto-open to prevent multiple attempts
|
|
716
|
-
const hasAutoOpenedRef = useRef(false);
|
|
717
|
-
const hasInitializedRef = useRef(false);
|
|
718
|
-
// Track if dialog was closed by user action (to clear persistence)
|
|
719
|
-
const wasClosedByUserRef = useRef(false);
|
|
720
|
-
// Track if dialog was manually opened (to prevent auto-open from interfering)
|
|
721
|
-
const wasManuallyOpenedRef = useRef(false);
|
|
722
|
-
|
|
723
|
-
// Callback to mark dialog as closed by user (exposed via context for DialogClose and Cancel buttons)
|
|
724
|
-
const markClosedByUser = useCallback(() => {
|
|
725
|
-
if (hasInitializedRef.current) {
|
|
726
|
-
wasClosedByUserRef.current = true;
|
|
727
|
-
}
|
|
728
|
-
}, []);
|
|
729
|
-
|
|
730
|
-
// Register markClosedByUser with parent Dialog component so it's available via DialogContext
|
|
731
|
-
useEffect(() => {
|
|
732
|
-
if (setMarkClosedByUser) {
|
|
733
|
-
setMarkClosedByUser(markClosedByUser);
|
|
734
|
-
}
|
|
735
|
-
return () => {
|
|
736
|
-
// Cleanup: unregister when DialogContent unmounts
|
|
737
|
-
if (setMarkClosedByUser) {
|
|
738
|
-
setMarkClosedByUser(undefined);
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
}, [setMarkClosedByUser, markClosedByUser]);
|
|
742
|
-
// Track if we've cleaned up other dialog states (to prevent multiple cleanup runs)
|
|
743
|
-
const hasCleanedUpOtherDialogsRef = useRef(false);
|
|
744
|
-
|
|
745
|
-
// Auto-open on mount if dialog was open when tab closed
|
|
746
|
-
useEffect(() => {
|
|
747
|
-
if (!persistenceKey || !persistOpenState) {
|
|
748
|
-
hasInitializedRef.current = true;
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// Only attempt auto-open once
|
|
753
|
-
// This prevents auto-open from running after manual opens
|
|
754
|
-
if (hasAutoOpenedRef.current) {
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// CRITICAL: Don't auto-open if dialog was manually opened
|
|
759
|
-
// This prevents auto-open from interfering with manual opens
|
|
760
|
-
if (wasManuallyOpenedRef.current) {
|
|
761
|
-
console.log('[Dialog Persistence] ⏭️ Skipping auto-open - dialog was manually opened', {
|
|
762
|
-
persistenceKey,
|
|
763
|
-
currentOpen: open,
|
|
764
|
-
});
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
// Mark as initialized after first check
|
|
769
|
-
if (!hasInitializedRef.current) {
|
|
770
|
-
hasInitializedRef.current = true;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
// Auto-open check (logging removed for performance)
|
|
774
|
-
|
|
775
|
-
// CRITICAL: Don't auto-open if dialog is already open (user-initiated)
|
|
776
|
-
if (open === true) {
|
|
777
|
-
hasAutoOpenedRef.current = true;
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// CRITICAL: Don't auto-open if userId is not available (prevents unscoped persistence from being restored)
|
|
782
|
-
if (!userId) {
|
|
783
|
-
hasAutoOpenedRef.current = true;
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// Only auto-open if conditions are met
|
|
788
|
-
if (persistedOpen === true && open === false && wasRestored && !hasAutoOpenedRef.current && !wasManuallyOpenedRef.current) {
|
|
789
|
-
const AUTO_OPEN_LOCK_KEY = 'pace-core:dialog:auto-open-lock';
|
|
790
|
-
const lockTimestamp = sessionStorage.getItem(AUTO_OPEN_LOCK_KEY);
|
|
791
|
-
const now = Date.now();
|
|
792
|
-
|
|
793
|
-
// Check if another dialog is already auto-opening (lock exists and is recent)
|
|
794
|
-
if (lockTimestamp) {
|
|
795
|
-
const lockAge = now - parseInt(lockTimestamp, 10);
|
|
796
|
-
if (lockAge < 1000) {
|
|
797
|
-
// Lock is recent - another dialog is auto-opening
|
|
798
|
-
// Check if there's already an open dialog with this persistence key
|
|
799
|
-
const existingOpenDialog = document.querySelector(`dialog[data-persistence-key="${persistenceKey}"][open]`);
|
|
800
|
-
if (existingOpenDialog) {
|
|
801
|
-
// Another instance is already open - skip
|
|
802
|
-
hasAutoOpenedRef.current = true;
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
// Check if other dialog has persisted state
|
|
806
|
-
const otherDialogHasPersistedState = checkOtherDialogsHavePersistedState(persistenceKey);
|
|
807
|
-
if (otherDialogHasPersistedState) {
|
|
808
|
-
// Another dialog with persisted state is auto-opening - skip this one
|
|
809
|
-
clearDraft();
|
|
810
|
-
hasAutoOpenedRef.current = true;
|
|
811
|
-
return;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
// Clear stale locks
|
|
815
|
-
if (lockAge > 2000) {
|
|
816
|
-
sessionStorage.removeItem(AUTO_OPEN_LOCK_KEY);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
// Check if dialog with same key is already open in DOM (synchronous check)
|
|
821
|
-
const existingDialog = document.querySelector(`dialog[data-persistence-key="${persistenceKey}"][open]`);
|
|
822
|
-
if (existingDialog && existingDialog !== dialogRef.current && existingDialog !== internalRef.current) {
|
|
823
|
-
hasAutoOpenedRef.current = true;
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// Set lock and mark as auto-opened BEFORE calling onOpenChange
|
|
828
|
-
sessionStorage.setItem(AUTO_OPEN_LOCK_KEY, String(now));
|
|
829
|
-
hasAutoOpenedRef.current = true;
|
|
830
|
-
wasManuallyOpenedRef.current = false;
|
|
831
|
-
|
|
832
|
-
console.log('[Dialog] 🔄 AUTO-OPEN', { persistenceKey });
|
|
833
|
-
|
|
834
|
-
// Use small delay to prevent visual flash
|
|
835
|
-
const timeoutId = setTimeout(() => {
|
|
836
|
-
// Double-check: if dialog is still closed and no other instance opened it
|
|
837
|
-
const stillClosed = !open;
|
|
838
|
-
const noOtherInstance = !document.querySelector(`dialog[data-persistence-key="${persistenceKey}"][open]`);
|
|
839
|
-
|
|
840
|
-
if (stillClosed && noOtherInstance) {
|
|
841
|
-
sessionStorage.removeItem(AUTO_OPEN_LOCK_KEY);
|
|
842
|
-
onOpenChange(true);
|
|
843
|
-
} else {
|
|
844
|
-
sessionStorage.removeItem(AUTO_OPEN_LOCK_KEY);
|
|
845
|
-
}
|
|
846
|
-
}, 75);
|
|
847
|
-
|
|
848
|
-
return () => {
|
|
849
|
-
clearTimeout(timeoutId);
|
|
850
|
-
sessionStorage.removeItem(AUTO_OPEN_LOCK_KEY);
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
}, [persistenceKey, persistOpenState, persistedOpen, open, onOpenChange, wasRestored, clearDraft]);
|
|
854
|
-
|
|
855
|
-
// When this dialog auto-opens, clear persisted state of all other dialogs
|
|
856
|
-
// This prevents multiple dialogs from being restored simultaneously
|
|
857
|
-
useEffect(() => {
|
|
858
|
-
if (!persistenceKey || !persistOpenState || !open || !hasAutoOpenedRef.current) {
|
|
859
|
-
return;
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
// Only run once when dialog first auto-opens
|
|
863
|
-
if (hasCleanedUpOtherDialogsRef.current) {
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
// Clear all other dialog persisted states from sessionStorage
|
|
868
|
-
// AND close any other dialogs that are currently open in the DOM
|
|
869
|
-
// This ensures only the first auto-opened dialog remains open
|
|
870
|
-
try {
|
|
871
|
-
const keysToRemove: string[] = [];
|
|
872
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
873
|
-
const key = sessionStorage.key(i);
|
|
874
|
-
if (key && key.startsWith('pace-core:draft:dialog:') && key.endsWith(':open')) {
|
|
875
|
-
// Don't clear this dialog's own state
|
|
876
|
-
if (key !== `pace-core:draft:${persistenceKey}:open`) {
|
|
877
|
-
keysToRemove.push(key);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
if (keysToRemove.length > 0) {
|
|
883
|
-
console.log('[Dialog Persistence] Clearing other dialog persisted states:', keysToRemove);
|
|
884
|
-
keysToRemove.forEach(key => sessionStorage.removeItem(key));
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// Also close any other dialogs that are currently open in the DOM AND have persisted state
|
|
888
|
-
// This prevents dialogs with persisted state from being hidden behind this one
|
|
889
|
-
// We only close dialogs that have persisted state, not dialogs opened by app code
|
|
890
|
-
// We identify dialogs with persistence using a data attribute
|
|
891
|
-
const timeoutId = setTimeout(() => {
|
|
892
|
-
// Guard against test environment teardown
|
|
893
|
-
if (typeof document === 'undefined' || typeof sessionStorage === 'undefined') {
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
const otherOpenDialogs = document.querySelectorAll('dialog[open][role="dialog"]');
|
|
898
|
-
const currentDialog = dialogRef.current || internalRef.current;
|
|
899
|
-
if (otherOpenDialogs.length > 0 && currentDialog) {
|
|
900
|
-
let closedCount = 0;
|
|
901
|
-
otherOpenDialogs.forEach((dialog) => {
|
|
902
|
-
// Don't close this dialog
|
|
903
|
-
const dialogElement = dialog as HTMLDialogElement;
|
|
904
|
-
if (dialogElement !== currentDialog) {
|
|
905
|
-
// Check if this dialog has a data-persistence-key attribute
|
|
906
|
-
// This indicates it was auto-opened from persistence
|
|
907
|
-
const dialogPersistenceKey = dialogElement.getAttribute('data-persistence-key');
|
|
908
|
-
if (dialogPersistenceKey && dialogPersistenceKey !== persistenceKey) {
|
|
909
|
-
// Check if this dialog's persisted state is true
|
|
910
|
-
let hasPersistedState = false;
|
|
911
|
-
try {
|
|
912
|
-
const key = `pace-core:draft:${dialogPersistenceKey}:open`;
|
|
913
|
-
const stored = sessionStorage.getItem(key);
|
|
914
|
-
if (stored) {
|
|
915
|
-
const parsed = JSON.parse(stored);
|
|
916
|
-
if (parsed && parsed.data === true) {
|
|
917
|
-
hasPersistedState = true;
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
} catch {
|
|
921
|
-
// Invalid data - skip
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// Only close if this dialog has persisted state (was auto-opened)
|
|
925
|
-
if (hasPersistedState) {
|
|
926
|
-
console.log('[Dialog Persistence] Closing other dialog with persisted state:', dialogPersistenceKey);
|
|
927
|
-
dialogElement.close();
|
|
928
|
-
closedCount++;
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
// If dialog doesn't have data-persistence-key, it was opened by app code - don't close it
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
if (closedCount > 0) {
|
|
935
|
-
console.log('[Dialog Persistence] Closed', closedCount, 'other dialog(s) with persisted state');
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
}, 100);
|
|
939
|
-
|
|
940
|
-
hasCleanedUpOtherDialogsRef.current = true;
|
|
941
|
-
|
|
942
|
-
// Also clear the auto-open lock
|
|
943
|
-
if (typeof sessionStorage !== 'undefined') {
|
|
944
|
-
sessionStorage.removeItem('pace-core:dialog:auto-open-lock');
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// Cleanup timeout on unmount or dependency change
|
|
948
|
-
return () => {
|
|
949
|
-
clearTimeout(timeoutId);
|
|
950
|
-
};
|
|
951
|
-
} catch (error) {
|
|
952
|
-
console.warn('[Dialog Persistence] Failed to clear other dialog states:', error);
|
|
953
|
-
}
|
|
954
|
-
}, [open, persistenceKey, persistOpenState]);
|
|
955
|
-
|
|
956
|
-
// When dialog closes (user action), immediately clear persisted state
|
|
957
|
-
// This prevents the dialog from auto-opening again after user explicitly closed it
|
|
958
|
-
useEffect(() => {
|
|
959
|
-
if (!persistenceKey || !persistOpenState) {
|
|
960
|
-
return;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
if (!hasInitializedRef.current) {
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// If dialog is closed and user closed it, clear persisted state immediately
|
|
968
|
-
if (!open && wasClosedByUserRef.current) {
|
|
969
|
-
clearDraft();
|
|
970
|
-
wasClosedByUserRef.current = false;
|
|
971
|
-
}
|
|
972
|
-
}, [open, persistenceKey, persistOpenState, clearDraft]);
|
|
973
|
-
|
|
974
|
-
// Check lock BEFORE allowing dialog to open (synchronous check)
|
|
975
|
-
// This prevents React from even trying to open if another dialog is open
|
|
976
|
-
useEffect(() => {
|
|
977
|
-
if (!open) {
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// Synchronously check if we can acquire the lock
|
|
982
|
-
const lockAcquired = acquireDialogLock(persistenceKey);
|
|
983
|
-
if (!lockAcquired) {
|
|
984
|
-
// Another dialog is open - prevent this one from opening
|
|
985
|
-
console.warn('[Dialog] ⚠️ Cannot open - another dialog holds the lock', {
|
|
986
|
-
persistenceKey,
|
|
987
|
-
});
|
|
988
|
-
// Immediately close this dialog's React state
|
|
989
|
-
onOpenChange?.(false);
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
// Lock acquired successfully - dialog can proceed to open
|
|
994
|
-
}, [open, persistenceKey, onOpenChange]);
|
|
995
|
-
|
|
996
|
-
// Track when dialog closes via onOpenChange to mark as closed by user
|
|
997
|
-
// This handles Cancel buttons and other programmatic closes
|
|
998
|
-
const previousOpenRef = useRef(open);
|
|
999
|
-
useEffect(() => {
|
|
1000
|
-
// If dialog was open and is now closed, and it wasn't auto-opened, mark as closed by user
|
|
1001
|
-
if (previousOpenRef.current === true && open === false && hasInitializedRef.current) {
|
|
1002
|
-
// Only mark as closed by user if it wasn't an auto-open scenario
|
|
1003
|
-
// Auto-open sets hasAutoOpenedRef before calling onOpenChange, so we can detect it
|
|
1004
|
-
if (!hasAutoOpenedRef.current || wasManuallyOpenedRef.current) {
|
|
1005
|
-
// Dialog was manually opened and then closed - mark as closed by user
|
|
1006
|
-
wasClosedByUserRef.current = true;
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
previousOpenRef.current = open;
|
|
1010
|
-
}, [open]);
|
|
1011
|
-
|
|
1012
|
-
// Persist open state changes
|
|
1013
|
-
useEffect(() => {
|
|
1014
|
-
if (!persistenceKey || !persistOpenState) {
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// Only persist after initial mount check is complete
|
|
1019
|
-
// This prevents overwriting the persisted state before auto-open can read it
|
|
1020
|
-
if (!hasInitializedRef.current) {
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
406
|
+
} = props;
|
|
407
|
+
|
|
408
|
+
const { open, onOpenChange, dialogRef, titleId, descriptionId } = useDialogContext();
|
|
409
|
+
const setMarkClosedByUser = React.useContext(DialogMarkClosedContext);
|
|
410
|
+
|
|
411
|
+
const location = useLocation();
|
|
412
|
+
const auth = useUnifiedAuth();
|
|
413
|
+
const userId = auth.user?.id || null;
|
|
414
|
+
|
|
415
|
+
const logger = createLogger('Dialog');
|
|
416
|
+
|
|
417
|
+
const persistenceKey = useMemo(() => {
|
|
418
|
+
if (!persistOpenState) return null;
|
|
419
|
+
if (!userId) return null;
|
|
420
|
+
return deriveDialogKey({ title, description }, location, userId);
|
|
421
|
+
}, [title, description, location, userId, persistOpenState]);
|
|
422
|
+
|
|
423
|
+
const internalRef = useRef<HTMLDialogElement>(null);
|
|
424
|
+
|
|
425
|
+
const { lockAcquired, markClosedByUser, clearDraft } = useDialogPersistence({
|
|
426
|
+
open,
|
|
427
|
+
onOpenChange,
|
|
428
|
+
persistenceKey,
|
|
429
|
+
persistOpenState,
|
|
430
|
+
userId,
|
|
431
|
+
dialogRef,
|
|
432
|
+
internalRef,
|
|
433
|
+
setMarkClosedByUser,
|
|
434
|
+
logger,
|
|
435
|
+
});
|
|
1023
436
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
// Only persist when dialog is open
|
|
1029
|
-
if (open) {
|
|
1030
|
-
// Reset the flag when opening
|
|
1031
|
-
wasClosedByUserRef.current = false;
|
|
1032
|
-
// If dialog is manually opened (not via auto-open), mark it so auto-open doesn't interfere
|
|
1033
|
-
// This prevents auto-open from trying to open an already-open dialog
|
|
1034
|
-
// We check if hasAutoOpenedRef is false to determine if this is a manual open
|
|
1035
|
-
// (auto-open sets hasAutoOpenedRef to true before calling onOpenChange)
|
|
1036
|
-
if (!hasAutoOpenedRef.current) {
|
|
1037
|
-
// Mark as manually opened to prevent auto-open from interfering
|
|
1038
|
-
wasManuallyOpenedRef.current = true;
|
|
1039
|
-
// Also mark as "opened" to prevent auto-open from trying to open it again
|
|
1040
|
-
hasAutoOpenedRef.current = true;
|
|
1041
|
-
}
|
|
1042
|
-
setPersistedOpen(true);
|
|
1043
|
-
} else {
|
|
1044
|
-
// Only clear draft if user explicitly closed (not if it was never opened or auto-opened then closed)
|
|
1045
|
-
if (wasClosedByUserRef.current) {
|
|
1046
|
-
clearDraft();
|
|
1047
|
-
wasClosedByUserRef.current = false;
|
|
1048
|
-
// Reset manual open flag when dialog is closed
|
|
1049
|
-
wasManuallyOpenedRef.current = false;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
// Cleanup timeout on unmount or dependency change
|
|
1054
|
-
return () => {
|
|
1055
|
-
if (logTimeoutId) {
|
|
1056
|
-
clearTimeout(logTimeoutId);
|
|
1057
|
-
}
|
|
1058
|
-
};
|
|
1059
|
-
}, [open, persistenceKey, persistOpenState, setPersistedOpen, clearDraft]);
|
|
1060
|
-
|
|
1061
|
-
// Note: We do NOT automatically clear the draft when dialog closes
|
|
1062
|
-
// The draft should only be cleared on explicit user actions (e.g., form submit success)
|
|
1063
|
-
// This allows the dialog to restore its state after tab switches
|
|
1064
|
-
const internalRef = useRef<HTMLDialogElement>(null);
|
|
1065
|
-
|
|
1066
|
-
// Use the dialogRef from context, or fall back to internal ref or forwarded ref
|
|
1067
|
-
const actualDialogRef = dialogRef.current ? dialogRef : (ref ? (ref as React.RefObject<HTMLDialogElement>) : internalRef);
|
|
1068
|
-
|
|
1069
|
-
// Default to 80% viewport height if no height constraint is provided
|
|
1070
|
-
// This allows the dialog to grow to 80% before enabling scrolling
|
|
1071
|
-
const effectiveMaxHeightPercent = maxHeightPercent ?? (maxHeight ? undefined : 80);
|
|
1072
|
-
|
|
1073
|
-
// Determine if we have a height constraint that requires flex layout
|
|
1074
|
-
const hasHeightConstraint = Boolean(effectiveMaxHeightPercent || maxHeight);
|
|
1075
|
-
|
|
1076
|
-
const smartDimensions = useSmartDimensions({
|
|
1077
|
-
maxHeightPercent: effectiveMaxHeightPercent,
|
|
437
|
+
const { sizeClass, mergedStyle, hasHeightConstraint } = useDialogDimensions({
|
|
438
|
+
size,
|
|
439
|
+
maxHeightPercent,
|
|
1078
440
|
maxWidthPercent,
|
|
1079
|
-
maxHeight,
|
|
441
|
+
maxHeight,
|
|
1080
442
|
maxWidth,
|
|
1081
443
|
minHeight,
|
|
1082
444
|
minWidth,
|
|
1083
|
-
enableScrolling
|
|
445
|
+
enableScrolling,
|
|
446
|
+
style,
|
|
1084
447
|
});
|
|
1085
448
|
|
|
1086
449
|
// Focus trap
|
|
@@ -1091,260 +454,114 @@ const DialogContent = React.forwardRef<HTMLDialogElement, DialogContentProps>(
|
|
|
1091
454
|
onEscape: preventCloseOnEscape ? undefined : () => onOpenChange(false),
|
|
1092
455
|
});
|
|
1093
456
|
|
|
1094
|
-
// Merge refs
|
|
1095
|
-
const mergedRef =
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
}
|
|
1100
|
-
// Set internal ref
|
|
1101
|
-
if (internalRef && 'current' in internalRef) {
|
|
1102
|
-
internalRef.current = node;
|
|
1103
|
-
}
|
|
1104
|
-
// Set focus trap container ref
|
|
1105
|
-
if (containerRef && 'current' in containerRef) {
|
|
1106
|
-
(containerRef as React.MutableRefObject<HTMLElement | null>).current = node;
|
|
1107
|
-
}
|
|
1108
|
-
// Handle forwarded ref
|
|
1109
|
-
if (typeof ref === 'function') {
|
|
1110
|
-
ref(node);
|
|
1111
|
-
} else if (ref && 'current' in ref) {
|
|
1112
|
-
(ref as React.MutableRefObject<HTMLDialogElement | null>).current = node;
|
|
1113
|
-
}
|
|
1114
|
-
}, [dialogRef, containerRef, ref]);
|
|
1115
|
-
|
|
1116
|
-
// Handle dialog open/close
|
|
1117
|
-
useEffect(() => {
|
|
1118
|
-
const dialog = dialogRef.current || internalRef.current;
|
|
1119
|
-
if (!dialog) return;
|
|
1120
|
-
|
|
1121
|
-
if (open) {
|
|
1122
|
-
// Log all dialogs in DOM before opening
|
|
1123
|
-
const allDialogsBefore = document.querySelectorAll('dialog[role="dialog"]');
|
|
1124
|
-
const dialogsBefore = Array.from(allDialogsBefore).map((d) => {
|
|
1125
|
-
const dialogEl = d as HTMLDialogElement;
|
|
1126
|
-
return {
|
|
1127
|
-
persistenceKey: dialogEl.getAttribute('data-persistence-key') || 'NO-KEY',
|
|
1128
|
-
open: dialogEl.open,
|
|
1129
|
-
isCurrent: d === dialog,
|
|
1130
|
-
};
|
|
1131
|
-
});
|
|
1132
|
-
console.log('[Dialog] 🟢 OPENING', {
|
|
1133
|
-
persistenceKey,
|
|
1134
|
-
dialogsInDOM: dialogsBefore,
|
|
1135
|
-
totalDialogs: allDialogsBefore.length,
|
|
1136
|
-
});
|
|
1137
|
-
|
|
1138
|
-
// Use requestAnimationFrame to ensure DOM is ready
|
|
1139
|
-
// Lock was already checked in the earlier useEffect, so we can proceed
|
|
1140
|
-
requestAnimationFrame(() => {
|
|
1141
|
-
if (dialog && open) {
|
|
1142
|
-
// Before opening, close any other open dialogs (safety check)
|
|
1143
|
-
const allDialogs = document.querySelectorAll('dialog[role="dialog"]');
|
|
1144
|
-
allDialogs.forEach((d) => {
|
|
1145
|
-
const dialogEl = d as HTMLDialogElement;
|
|
1146
|
-
if (dialogEl !== dialog && dialogEl.open) {
|
|
1147
|
-
dialogEl.setAttribute('data-duplicate-cleanup', 'true');
|
|
1148
|
-
dialogEl.close();
|
|
1149
|
-
}
|
|
1150
|
-
});
|
|
1151
|
-
|
|
1152
|
-
console.log('[Dialog] ✅ showModal() called', { persistenceKey });
|
|
1153
|
-
dialog.showModal();
|
|
1154
|
-
}
|
|
1155
|
-
});
|
|
1156
|
-
} else {
|
|
1157
|
-
// Close dialog before it's removed from DOM
|
|
1158
|
-
if (dialog.open) {
|
|
1159
|
-
console.log('[Dialog] 🔴 CLOSING', { persistenceKey });
|
|
1160
|
-
dialog.close();
|
|
1161
|
-
// Release the lock
|
|
1162
|
-
releaseDialogLock(persistenceKey);
|
|
1163
|
-
|
|
1164
|
-
// After closing, check if any other dialogs with persistence are trying to open
|
|
1165
|
-
// Only close dialogs that have persistence (data-persistence-key attribute)
|
|
1166
|
-
// Non-persistent dialogs should be left alone
|
|
1167
|
-
setTimeout(() => {
|
|
1168
|
-
const allDialogs = document.querySelectorAll('dialog[role="dialog"]');
|
|
1169
|
-
allDialogs.forEach((d) => {
|
|
1170
|
-
const dialogEl = d as HTMLDialogElement;
|
|
1171
|
-
if (dialogEl !== dialog && dialogEl.open) {
|
|
1172
|
-
const otherPersistenceKey = dialogEl.getAttribute('data-persistence-key');
|
|
1173
|
-
// Only close dialogs that have persistence (they might auto-open)
|
|
1174
|
-
// Non-persistent dialogs are user-controlled and shouldn't be closed
|
|
1175
|
-
if (otherPersistenceKey) {
|
|
1176
|
-
console.warn('[Dialog] 🗑️ Closing other persisted dialog after lock release:', {
|
|
1177
|
-
persistenceKey,
|
|
1178
|
-
otherPersistenceKey,
|
|
1179
|
-
});
|
|
1180
|
-
dialogEl.setAttribute('data-duplicate-cleanup', 'true');
|
|
1181
|
-
dialogEl.close();
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
});
|
|
1185
|
-
}, 50);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}, [open, persistenceKey, dialogRef]);
|
|
1189
|
-
|
|
1190
|
-
// Handle close event - sync state when dialog is closed externally
|
|
1191
|
-
// Also track when dialog is closed by user action (for persistence clearing)
|
|
1192
|
-
useEffect(() => {
|
|
1193
|
-
const dialog = dialogRef.current || internalRef.current;
|
|
1194
|
-
if (!dialog) return;
|
|
1195
|
-
|
|
1196
|
-
const handleClose = () => {
|
|
1197
|
-
// Check if this close was initiated by the user (via close button)
|
|
1198
|
-
const wasUserClosed = dialog.hasAttribute('data-user-closed');
|
|
1199
|
-
if (wasUserClosed) {
|
|
1200
|
-
dialog.removeAttribute('data-user-closed');
|
|
1201
|
-
if (hasInitializedRef.current) {
|
|
1202
|
-
wasClosedByUserRef.current = true;
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// Ignore duplicate cleanup closes
|
|
1207
|
-
const isDuplicateCleanup = dialog.hasAttribute('data-duplicate-cleanup');
|
|
1208
|
-
if (isDuplicateCleanup) {
|
|
1209
|
-
dialog.removeAttribute('data-duplicate-cleanup');
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
if (!dialog.open && open) {
|
|
1214
|
-
// Mark as closed by user if this wasn't an auto-open scenario
|
|
1215
|
-
if (hasInitializedRef.current && !wasUserClosed) {
|
|
1216
|
-
wasClosedByUserRef.current = true;
|
|
1217
|
-
}
|
|
1218
|
-
onOpenChange(false);
|
|
1219
|
-
} else if (!dialog.open && !open && hasInitializedRef.current && wasUserClosed) {
|
|
1220
|
-
wasClosedByUserRef.current = true;
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
|
|
1224
|
-
dialog.addEventListener('close', handleClose);
|
|
1225
|
-
return () => {
|
|
1226
|
-
dialog.removeEventListener('close', handleClose);
|
|
1227
|
-
};
|
|
1228
|
-
}, [open, onOpenChange, dialogRef]);
|
|
1229
|
-
|
|
1230
|
-
// Handle cancel event (Escape or backdrop click)
|
|
1231
|
-
useEffect(() => {
|
|
1232
|
-
const dialog = dialogRef.current || internalRef.current;
|
|
1233
|
-
if (!dialog) return;
|
|
1234
|
-
|
|
1235
|
-
const handleCancel = (e: Event) => {
|
|
1236
|
-
if (preventCloseOnEscape || preventCloseOnOutsideClick) {
|
|
1237
|
-
e.preventDefault();
|
|
1238
|
-
return;
|
|
1239
|
-
}
|
|
1240
|
-
// Mark as closed by user and clear persisted state
|
|
1241
|
-
wasClosedByUserRef.current = true;
|
|
1242
|
-
if (persistenceKey && persistOpenState && clearDraft) {
|
|
1243
|
-
clearDraft();
|
|
1244
|
-
}
|
|
1245
|
-
onOpenChange(false);
|
|
1246
|
-
};
|
|
1247
|
-
|
|
1248
|
-
dialog.addEventListener('cancel', handleCancel);
|
|
1249
|
-
return () => {
|
|
1250
|
-
dialog.removeEventListener('cancel', handleCancel);
|
|
1251
|
-
};
|
|
1252
|
-
}, [preventCloseOnEscape, preventCloseOnOutsideClick, onOpenChange, dialogRef, persistenceKey, persistOpenState, clearDraft]);
|
|
1253
|
-
|
|
1254
|
-
// Merge smart dimensions with provided style
|
|
1255
|
-
const mergedStyle = React.useMemo(() => {
|
|
1256
|
-
if (Object.keys(smartDimensions).length === 0) {
|
|
1257
|
-
return style;
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
const finalStyle: React.CSSProperties = { ...smartDimensions, ...style };
|
|
1261
|
-
|
|
1262
|
-
if (!maxWidth && !maxWidthPercent) {
|
|
1263
|
-
const { maxWidth: _, ...styleWithoutMaxWidth } = finalStyle;
|
|
1264
|
-
return styleWithoutMaxWidth;
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
return finalStyle;
|
|
1268
|
-
}, [smartDimensions, style, maxWidth, maxWidthPercent]);
|
|
1269
|
-
|
|
1270
|
-
// Track if lock has been acquired (set by useEffect when open becomes true)
|
|
1271
|
-
const [lockAcquired, setLockAcquired] = React.useState(false);
|
|
1272
|
-
|
|
1273
|
-
// Check lock BEFORE allowing dialog to open (synchronous check)
|
|
1274
|
-
// This prevents React from even trying to open if another dialog is open
|
|
1275
|
-
useEffect(() => {
|
|
1276
|
-
if (!open) {
|
|
1277
|
-
setLockAcquired(false);
|
|
1278
|
-
return;
|
|
1279
|
-
}
|
|
457
|
+
// Merge refs (avoids type assertions; mergeRefs handles RefObject and callback refs)
|
|
458
|
+
const mergedRef = useMemo(
|
|
459
|
+
() => mergeRefs(dialogRef, internalRef, containerRef, ref),
|
|
460
|
+
[dialogRef, containerRef, ref]
|
|
461
|
+
);
|
|
1280
462
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
// Lock acquired successfully - dialog can proceed to open
|
|
1296
|
-
}, [open, persistenceKey, onOpenChange]);
|
|
463
|
+
useDialogLifecycle({
|
|
464
|
+
dialogRef,
|
|
465
|
+
internalRef,
|
|
466
|
+
open,
|
|
467
|
+
persistenceKey,
|
|
468
|
+
logger,
|
|
469
|
+
onOpenChange,
|
|
470
|
+
markClosedByUser,
|
|
471
|
+
preventCloseOnEscape,
|
|
472
|
+
preventCloseOnOutsideClick,
|
|
473
|
+
persistOpenState,
|
|
474
|
+
clearDraft,
|
|
475
|
+
});
|
|
1297
476
|
|
|
1298
|
-
// Synchronously check if we can render (must hold lock if open)
|
|
1299
477
|
const canRender = React.useMemo(() => {
|
|
1300
|
-
if (!open)
|
|
1301
|
-
|
|
1302
|
-
}
|
|
1303
|
-
if (!persistenceKey) {
|
|
1304
|
-
return true; // Non-persisted dialogs can always render
|
|
1305
|
-
}
|
|
1306
|
-
// Use the lockAcquired state which is set by the effect
|
|
478
|
+
if (!open) return true;
|
|
479
|
+
if (!persistenceKey) return true;
|
|
1307
480
|
return lockAcquired;
|
|
1308
481
|
}, [open, persistenceKey, lockAcquired]);
|
|
1309
482
|
|
|
483
|
+
return {
|
|
484
|
+
mergedRef,
|
|
485
|
+
canRender,
|
|
486
|
+
markClosedByUser,
|
|
487
|
+
sizeClass,
|
|
488
|
+
mergedStyle: mergedStyle ?? {},
|
|
489
|
+
hasHeightConstraint,
|
|
490
|
+
persistenceKey,
|
|
491
|
+
persistOpenState,
|
|
492
|
+
titleId,
|
|
493
|
+
descriptionId,
|
|
494
|
+
open,
|
|
495
|
+
title,
|
|
496
|
+
description,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* DialogContent component
|
|
502
|
+
* The main content container using semantic HTML <dialog> element with enhanced features.
|
|
503
|
+
* Inline style exception (5-styling): viewport-based max/min dimensions from props are applied
|
|
504
|
+
* via the style prop; they cannot be expressed with static Tailwind and are merged with optional consumer style.
|
|
505
|
+
*
|
|
506
|
+
* @param props - Content configuration and styling
|
|
507
|
+
* @param ref - Forwarded ref to the dialog element
|
|
508
|
+
* @returns JSX.Element - The semantic dialog content with overlay and optional close button
|
|
509
|
+
*/
|
|
510
|
+
const DialogContent = (React.forwardRef<HTMLDialogElement, DialogContentProps>)(
|
|
511
|
+
function DialogContentInner(props, ref) {
|
|
512
|
+
const {
|
|
513
|
+
className,
|
|
514
|
+
children,
|
|
515
|
+
size = 'md',
|
|
516
|
+
showCloseButton = true,
|
|
517
|
+
maxHeightPercent,
|
|
518
|
+
maxWidthPercent,
|
|
519
|
+
enableScrolling = false,
|
|
520
|
+
maxHeight,
|
|
521
|
+
maxWidth,
|
|
522
|
+
minHeight,
|
|
523
|
+
minWidth,
|
|
524
|
+
style: _styleProp,
|
|
525
|
+
title: _titleProp,
|
|
526
|
+
description: _descriptionProp,
|
|
527
|
+
persistOpenState: _persistOpenStateProp,
|
|
528
|
+
preventCloseOnEscape: _preventCloseOnEscape,
|
|
529
|
+
preventCloseOnOutsideClick: _preventCloseOnOutsideClick,
|
|
530
|
+
...restProps
|
|
531
|
+
} = props;
|
|
532
|
+
|
|
533
|
+
const state = useDialogContentState(props, ref);
|
|
534
|
+
|
|
1310
535
|
return (
|
|
1311
536
|
<DialogPortal>
|
|
1312
|
-
{open && canRender && (
|
|
537
|
+
{state.open && state.canRender && (
|
|
1313
538
|
<dialog
|
|
1314
|
-
ref={mergedRef}
|
|
539
|
+
ref={state.mergedRef}
|
|
1315
540
|
className={cn(
|
|
1316
541
|
'fixed left-[50%] top-[50%] z-[51] w-full translate-x-[-50%] translate-y-[-50%] border bg-background shadow-lg duration-200',
|
|
1317
542
|
'animate-in fade-in-0 zoom-in-95 slide-in-from-left-1/2 slide-in-from-top-[48%]',
|
|
1318
543
|
'sm:rounded-lg',
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
// Apply our custom styling
|
|
544
|
+
'm-0 p-0 max-w-none max-h-none border-0 bg-transparent outline-none',
|
|
545
|
+
(maxWidth || maxWidthPercent) ? 'w-full' : 'w-auto h-auto',
|
|
1322
546
|
'border bg-background shadow-lg',
|
|
1323
|
-
|
|
1324
|
-
// Only apply size classes if not using smart width
|
|
1325
|
-
!maxWidth && !maxWidthPercent && sizeClasses[size],
|
|
1326
|
-
// Auto size gets special handling
|
|
547
|
+
!maxWidth && !maxWidthPercent && state.sizeClass,
|
|
1327
548
|
size === 'auto' && 'w-fit max-w-[90vw] sm:max-w-[80vw]',
|
|
1328
|
-
|
|
1329
|
-
// Flex layout is needed for proper scrolling when height is constrained
|
|
1330
|
-
(enableScrolling || hasHeightConstraint) ? 'flex flex-col px-6' : 'grid gap-4 p-6',
|
|
1331
|
-
// Full screen handling
|
|
549
|
+
(enableScrolling || state.hasHeightConstraint) ? 'flex flex-col p-6' : 'grid p-6',
|
|
1332
550
|
size === 'full' && 'sm:left-[50%] sm:top-[50%] sm:translate-x-[-50%] sm:translate-y-[-50%] left-0 top-0 translate-x-0 translate-y-0 h-full rounded-none sm:h-auto sm:rounded-lg',
|
|
1333
|
-
|
|
1334
|
-
(enableScrolling || hasHeightConstraint) && 'overflow-hidden',
|
|
551
|
+
(enableScrolling || state.hasHeightConstraint) && 'overflow-hidden',
|
|
1335
552
|
className
|
|
1336
553
|
)}
|
|
1337
|
-
style={mergedStyle}
|
|
554
|
+
style={state.mergedStyle}
|
|
1338
555
|
role="dialog"
|
|
1339
556
|
aria-modal="true"
|
|
1340
|
-
aria-labelledby={titleId}
|
|
1341
|
-
aria-describedby={descriptionId}
|
|
1342
|
-
title={title}
|
|
1343
|
-
aria-description={description}
|
|
1344
|
-
data-persistence-key={persistenceKey && persistOpenState ? persistenceKey : undefined}
|
|
1345
|
-
{...
|
|
557
|
+
aria-labelledby={state.titleId}
|
|
558
|
+
aria-describedby={state.descriptionId}
|
|
559
|
+
title={state.title}
|
|
560
|
+
aria-description={state.description}
|
|
561
|
+
data-persistence-key={state.persistenceKey && state.persistOpenState ? state.persistenceKey : undefined}
|
|
562
|
+
{...restProps}
|
|
1346
563
|
>
|
|
1347
|
-
<DialogCloseContext.Provider value={markClosedByUser}>
|
|
564
|
+
<DialogCloseContext.Provider value={state.markClosedByUser}>
|
|
1348
565
|
{children}
|
|
1349
566
|
{showCloseButton && (
|
|
1350
567
|
<DialogClose />
|
|
@@ -1373,47 +590,38 @@ export interface DialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonE
|
|
|
1373
590
|
*/
|
|
1374
591
|
const DialogClose = React.forwardRef<HTMLButtonElement, DialogCloseProps>(
|
|
1375
592
|
({ className, asChild = false, children, onClick, ...props }, ref) => {
|
|
1376
|
-
// Call all hooks unconditionally at the top level
|
|
1377
|
-
// Hooks must be called in the same order on every render
|
|
1378
593
|
const { onOpenChange, markClosedByUser: contextMarkClosedByUser } = useDialogContext();
|
|
1379
|
-
// Prefer DialogContext markClosedByUser (available to all components), fallback to DialogCloseContext (for backwards compatibility)
|
|
1380
594
|
const dialogCloseContextValue = React.useContext(DialogCloseContext);
|
|
1381
|
-
const markClosedByUser = contextMarkClosedByUser
|
|
1382
|
-
|
|
1383
|
-
const handleClick = useCallback((e: React.MouseEvent<
|
|
1384
|
-
// Mark dialog as closed by user before calling onOpenChange
|
|
1385
|
-
// This ensures the persisted state is cleared when user clicks close button
|
|
595
|
+
const markClosedByUser = contextMarkClosedByUser ?? dialogCloseContextValue;
|
|
596
|
+
|
|
597
|
+
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
|
|
1386
598
|
if (markClosedByUser) {
|
|
1387
599
|
markClosedByUser();
|
|
1388
600
|
}
|
|
1389
|
-
|
|
1390
|
-
onClick?.(e as React.MouseEvent<HTMLButtonElement>);
|
|
601
|
+
onClick?.(e);
|
|
1391
602
|
onOpenChange(false);
|
|
1392
603
|
}, [onOpenChange, markClosedByUser, onClick]);
|
|
1393
604
|
|
|
1394
605
|
if (asChild && React.isValidElement(children)) {
|
|
1395
|
-
|
|
606
|
+
const childElement = children as React.ReactElement<React.HTMLAttributes<HTMLButtonElement>>;
|
|
607
|
+
return React.cloneElement(childElement, {
|
|
1396
608
|
ref,
|
|
1397
609
|
onClick: handleClick,
|
|
1398
|
-
className: cn(className,
|
|
610
|
+
className: cn(className, childElement.props?.className),
|
|
1399
611
|
...props,
|
|
1400
|
-
});
|
|
612
|
+
} as React.HTMLAttributes<HTMLButtonElement> & React.RefAttributes<HTMLButtonElement>);
|
|
1401
613
|
}
|
|
1402
614
|
|
|
1403
615
|
return (
|
|
1404
|
-
<
|
|
616
|
+
<IconButton
|
|
1405
617
|
ref={ref}
|
|
618
|
+
icon={<X className="size-4" />}
|
|
619
|
+
aria-label="Close"
|
|
1406
620
|
type="button"
|
|
1407
621
|
onClick={handleClick}
|
|
1408
|
-
className={cn(
|
|
1409
|
-
'absolute right-4 top-4 z-10 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none',
|
|
1410
|
-
className
|
|
1411
|
-
)}
|
|
622
|
+
className={cn('absolute right-4 top-4 z-10', className)}
|
|
1412
623
|
{...props}
|
|
1413
|
-
|
|
1414
|
-
<X className="size-4" />
|
|
1415
|
-
<span className="sr-only">Close</span>
|
|
1416
|
-
</button>
|
|
624
|
+
/>
|
|
1417
625
|
);
|
|
1418
626
|
}
|
|
1419
627
|
);
|
|
@@ -1429,11 +637,7 @@ const DialogHeader = ({
|
|
|
1429
637
|
...props
|
|
1430
638
|
}: DialogHeaderProps) => (
|
|
1431
639
|
<header
|
|
1432
|
-
className={cn(
|
|
1433
|
-
'flex flex-col space-y-1.5 text-center sm:text-left',
|
|
1434
|
-
sticky ? 'sticky top-0 z-10 bg-background pt-6 pb-4 border-b' : 'py-2',
|
|
1435
|
-
className
|
|
1436
|
-
)}
|
|
640
|
+
className={cn('dialog-header', sticky && 'sticky top-0 z-10 bg-background', className)}
|
|
1437
641
|
{...props}
|
|
1438
642
|
/>
|
|
1439
643
|
);
|
|
@@ -1449,11 +653,11 @@ const DialogBody = ({
|
|
|
1449
653
|
style,
|
|
1450
654
|
htmlContent,
|
|
1451
655
|
allowHtml = true,
|
|
1452
|
-
strictSanitization = true,
|
|
1453
656
|
logWarnings = false,
|
|
1454
657
|
children,
|
|
1455
658
|
...props
|
|
1456
659
|
}: DialogBodyProps) => {
|
|
660
|
+
// Inline style exception (5-styling): maxHeight and style from props; no fixed token set.
|
|
1457
661
|
const mergedStyle = React.useMemo(() => {
|
|
1458
662
|
return {
|
|
1459
663
|
...(maxHeight && { maxHeight }),
|
|
@@ -1467,12 +671,11 @@ const DialogBody = ({
|
|
|
1467
671
|
}
|
|
1468
672
|
|
|
1469
673
|
const result = renderSafeHtml(htmlContent, {
|
|
1470
|
-
strict: strictSanitization,
|
|
1471
674
|
logWarnings
|
|
1472
675
|
});
|
|
1473
676
|
|
|
1474
677
|
return result.html;
|
|
1475
|
-
}, [htmlContent, allowHtml,
|
|
678
|
+
}, [htmlContent, allowHtml, logWarnings]);
|
|
1476
679
|
|
|
1477
680
|
const hasHtmlContent = Boolean(htmlContent && allowHtml);
|
|
1478
681
|
|
|
@@ -1510,7 +713,7 @@ const DialogBody = ({
|
|
|
1510
713
|
return (
|
|
1511
714
|
<main
|
|
1512
715
|
className={cn(
|
|
1513
|
-
'overflow-y-auto
|
|
716
|
+
'overflow-y-auto',
|
|
1514
717
|
// When in a flex container with height constraint, use flex properties
|
|
1515
718
|
// so DialogBody takes up available space and only scrolls when content exceeds it
|
|
1516
719
|
isInFlexContainer && 'flex-1 min-h-0',
|
|
@@ -1522,14 +725,14 @@ const DialogBody = ({
|
|
|
1522
725
|
{...props}
|
|
1523
726
|
>
|
|
1524
727
|
{processedHtmlContent ? (
|
|
1525
|
-
<p
|
|
728
|
+
<p
|
|
1526
729
|
dangerouslySetInnerHTML={{ __html: processedHtmlContent }}
|
|
1527
|
-
className="
|
|
730
|
+
className="dialog-body-html-content"
|
|
1528
731
|
/>
|
|
1529
732
|
) : (
|
|
1530
733
|
<>
|
|
1531
734
|
{hasHtmlContent && !processedHtmlContent && (
|
|
1532
|
-
<p className="
|
|
735
|
+
<p className="dialog-body-fallback">
|
|
1533
736
|
No HTML content processed. Showing children instead.
|
|
1534
737
|
</p>
|
|
1535
738
|
)}
|
|
@@ -1551,11 +754,7 @@ const DialogFooter = ({
|
|
|
1551
754
|
...props
|
|
1552
755
|
}: DialogFooterProps) => (
|
|
1553
756
|
<footer
|
|
1554
|
-
className={cn(
|
|
1555
|
-
!className && 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
|
|
1556
|
-
!className && (sticky ? 'sticky bottom-0 z-10 bg-background pt-4 pb-6 border-t' : 'py-2'),
|
|
1557
|
-
className
|
|
1558
|
-
)}
|
|
757
|
+
className={cn('dialog-footer', sticky && 'sticky bottom-0 z-10 bg-background', className)}
|
|
1559
758
|
{...props}
|
|
1560
759
|
/>
|
|
1561
760
|
);
|
|
@@ -1575,7 +774,6 @@ const DialogTitle = React.forwardRef<HTMLHeadingElement, DialogTitleProps>(
|
|
|
1575
774
|
}
|
|
1576
775
|
|
|
1577
776
|
const result = renderSafeHtml(htmlContent, {
|
|
1578
|
-
strict: true,
|
|
1579
777
|
logWarnings: false
|
|
1580
778
|
});
|
|
1581
779
|
|
|
@@ -1614,7 +812,6 @@ const DialogDescription = React.forwardRef<HTMLParagraphElement, DialogDescripti
|
|
|
1614
812
|
}
|
|
1615
813
|
|
|
1616
814
|
const result = renderSafeHtml(htmlContent, {
|
|
1617
|
-
strict: true,
|
|
1618
815
|
logWarnings: false
|
|
1619
816
|
});
|
|
1620
817
|
|