@firecms/core 3.0.0-canary.27 → 3.0.0-canary.270
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/README.md +3 -3
- package/dist/app/AppBar.d.ts +12 -0
- package/dist/app/Drawer.d.ts +16 -0
- package/dist/app/Scaffold.d.ts +34 -0
- package/dist/app/index.d.ts +4 -0
- package/dist/app/useApp.d.ts +16 -0
- package/dist/components/ArrayContainer.d.ts +31 -12
- package/dist/components/CircularProgressCenter.d.ts +1 -1
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/{DeleteConfirmationDialog.d.ts → ConfirmationDialog.d.ts} +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +14 -13
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +22 -6
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +1 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +1 -2
- package/dist/components/EntityCollectionTable/fields/TableReferenceField.d.ts +3 -1
- package/dist/components/EntityCollectionTable/index.d.ts +1 -1
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/popup_field/PopupFormField.d.ts +7 -4
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +20 -2
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityCollectionView/utils.d.ts +3 -0
- package/dist/components/EntityJsonPreview.d.ts +3 -0
- package/dist/components/EntityPreview.d.ts +10 -7
- package/dist/components/ErrorView.d.ts +1 -1
- package/dist/components/HomePage/DefaultHomePage.d.ts +2 -15
- package/dist/components/HomePage/HomePageDnD.d.ts +76 -0
- package/dist/components/HomePage/NavigationCard.d.ts +3 -1
- package/dist/components/HomePage/NavigationCardBinding.d.ts +4 -3
- package/dist/components/HomePage/NavigationGroup.d.ts +8 -1
- package/dist/components/HomePage/RenameGroupDialog.d.ts +9 -0
- package/dist/components/PropertyConfigBadge.d.ts +2 -1
- package/dist/components/PropertyIdCopyTooltip.d.ts +8 -0
- package/dist/components/ReferenceWidget.d.ts +3 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +14 -4
- package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +2 -1
- package/dist/components/UnsavedChangesDialog.d.ts +8 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +24 -12
- package/dist/components/VirtualTable/types.d.ts +3 -3
- package/dist/components/{EntityCollectionTable/internal → common}/default_entity_actions.d.ts +1 -3
- package/dist/components/common/index.d.ts +2 -1
- package/dist/components/common/table_height.d.ts +5 -0
- package/dist/components/common/types.d.ts +4 -6
- package/dist/components/common/useColumnsIds.d.ts +3 -1
- package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +13 -2
- package/dist/components/common/useDebouncedCallback.d.ts +1 -0
- package/dist/components/common/useScrollRestoration.d.ts +14 -0
- package/dist/components/index.d.ts +5 -2
- package/dist/contexts/BreacrumbsContext.d.ts +8 -0
- package/dist/core/DefaultAppBar.d.ts +29 -0
- package/dist/core/DefaultDrawer.d.ts +19 -0
- package/dist/core/DrawerNavigationItem.d.ts +10 -0
- package/dist/core/EntityEditView.d.ts +43 -11
- package/dist/core/EntityEditViewFormActions.d.ts +2 -0
- package/dist/core/FireCMS.d.ts +3 -3
- package/dist/core/FireCMSRouter.d.ts +4 -0
- package/dist/core/NavigationRoutes.d.ts +2 -3
- package/dist/core/SideDialogs.d.ts +4 -2
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/core/index.d.ts +4 -4
- package/dist/form/EntityForm.d.ts +37 -64
- package/dist/form/EntityFormActions.d.ts +21 -0
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/form/components/FieldHelperText.d.ts +3 -3
- package/dist/form/components/FormEntry.d.ts +6 -0
- package/dist/form/components/FormLayout.d.ts +5 -0
- package/dist/form/components/LabelWithIcon.d.ts +1 -1
- package/dist/form/components/LabelWithIconAndTooltip.d.ts +15 -0
- package/dist/form/components/StorageItemPreview.d.ts +4 -4
- package/dist/form/components/index.d.ts +3 -1
- package/dist/form/field_bindings/ArrayCustomShapedFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ArrayOfReferencesFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/BlockFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +11 -0
- package/dist/form/field_bindings/{MultiSelectBinding.d.ts → MultiSelectFieldBinding.d.ts} +1 -1
- package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +9 -0
- package/dist/form/field_bindings/ReferenceFieldBinding.d.ts +2 -2
- package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +5 -13
- package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -2
- package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
- package/dist/form/index.d.ts +17 -18
- package/dist/form/useClearRestoreValue.d.ts +2 -2
- package/dist/hooks/data/delete.d.ts +4 -4
- package/dist/hooks/data/save.d.ts +4 -5
- package/dist/hooks/data/useCollectionFetch.d.ts +1 -1
- package/dist/hooks/data/useEntityFetch.d.ts +4 -3
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useAuthController.d.ts +1 -1
- package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
- package/dist/hooks/useBuildNavigationController.d.ts +57 -13
- package/dist/hooks/useCollapsedGroups.d.ts +9 -0
- package/dist/hooks/useFireCMSContext.d.ts +1 -1
- package/dist/hooks/useModeController.d.ts +1 -2
- package/dist/hooks/useProjectLog.d.ts +8 -2
- package/dist/hooks/useResolvedNavigationFrom.d.ts +3 -3
- package/dist/hooks/useValidateAuthenticator.d.ts +4 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +23154 -13912
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +25917 -588
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +3 -17
- package/dist/internal/useBuildSideEntityController.d.ts +3 -3
- package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
- package/dist/preview/PropertyPreviewProps.d.ts +6 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +4 -3
- package/dist/preview/components/StorageThumbnail.d.ts +2 -1
- package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
- package/dist/preview/util.d.ts +3 -3
- package/dist/routes/CustomCMSRoute.d.ts +4 -0
- package/dist/routes/FireCMSRoute.d.ts +1 -0
- package/dist/routes/HomePageRoute.d.ts +3 -0
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +8 -10
- package/dist/types/collections.d.ts +110 -25
- package/dist/types/customization_controller.d.ts +8 -0
- package/dist/types/datasource.d.ts +52 -36
- package/dist/types/dialogs_controller.d.ts +7 -3
- package/dist/types/entities.d.ts +7 -2
- package/dist/types/entity_actions.d.ts +72 -8
- package/dist/types/entity_callbacks.d.ts +16 -16
- package/dist/types/entity_overrides.d.ts +2 -2
- package/dist/types/export_import.d.ts +4 -4
- package/dist/types/fields.d.ts +74 -42
- package/dist/types/firecms.d.ts +16 -3
- package/dist/types/firecms_context.d.ts +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/navigation.d.ts +62 -19
- package/dist/types/permissions.d.ts +4 -4
- package/dist/types/plugins.d.ts +56 -13
- package/dist/types/properties.d.ts +81 -25
- package/dist/types/property_config.d.ts +1 -3
- package/dist/types/roles.d.ts +3 -0
- package/dist/types/side_dialogs_controller.d.ts +10 -0
- package/dist/types/side_entity_controller.d.ts +14 -1
- package/dist/types/storage.d.ts +75 -0
- package/dist/types/user.d.ts +1 -0
- package/dist/util/builders.d.ts +3 -3
- package/dist/util/callbacks.d.ts +2 -0
- package/dist/util/createFormexStub.d.ts +2 -0
- package/dist/util/entities.d.ts +3 -3
- package/dist/util/entity_actions.d.ts +2 -0
- package/dist/util/entity_cache.d.ts +23 -0
- package/dist/util/icon_list.d.ts +5 -1
- package/dist/util/icon_synonyms.d.ts +1 -98
- package/dist/util/icons.d.ts +7 -4
- package/dist/util/index.d.ts +3 -0
- package/dist/util/navigation_from_path.d.ts +10 -1
- package/dist/util/navigation_utils.d.ts +15 -3
- package/dist/util/objects.d.ts +2 -1
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/plurals.d.ts +0 -2
- package/dist/util/property_utils.d.ts +4 -4
- package/dist/util/references.d.ts +2 -2
- package/dist/util/resolutions.d.ts +42 -17
- package/dist/util/storage.d.ts +23 -2
- package/dist/util/useStorageUploadController.d.ts +3 -3
- package/package.json +69 -52
- package/src/app/AppBar.tsx +18 -0
- package/src/app/Drawer.tsx +24 -0
- package/src/app/Scaffold.tsx +253 -0
- package/src/app/index.ts +4 -0
- package/src/app/useApp.tsx +32 -0
- package/src/components/ArrayContainer.tsx +447 -229
- package/src/components/CircularProgressCenter.tsx +2 -2
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/{DeleteConfirmationDialog.tsx → ConfirmationDialog.tsx} +12 -11
- package/src/components/DeleteEntityDialog.tsx +13 -20
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +65 -40
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +38 -31
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +30 -9
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +72 -42
- package/src/components/EntityCollectionTable/column_utils.tsx +3 -3
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +30 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +19 -17
- package/src/components/EntityCollectionTable/index.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +34 -39
- package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +49 -36
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +20 -8
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +135 -105
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +9 -9
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +235 -118
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +7 -4
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityCollectionView/useSelectionController.tsx +20 -7
- package/src/components/EntityCollectionView/utils.ts +19 -0
- package/src/components/EntityJsonPreview.tsx +66 -0
- package/src/components/EntityPreview.tsx +83 -62
- package/src/components/EntityView.tsx +13 -10
- package/src/components/ErrorView.tsx +4 -4
- package/src/components/FireCMSLogo.tsx +7 -51
- package/src/components/HomePage/DefaultHomePage.tsx +512 -157
- package/src/components/HomePage/FavouritesView.tsx +9 -14
- package/src/components/HomePage/HomePageDnD.tsx +599 -0
- package/src/components/HomePage/NavigationCard.tsx +48 -39
- package/src/components/HomePage/NavigationCardBinding.tsx +17 -16
- package/src/components/HomePage/NavigationGroup.tsx +144 -30
- package/src/components/HomePage/RenameGroupDialog.tsx +117 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +5 -6
- package/src/components/NotFoundPage.tsx +2 -2
- package/src/components/PropertyConfigBadge.tsx +9 -3
- package/src/components/PropertyIdCopyTooltip.tsx +47 -0
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +23 -13
- package/src/components/ReferenceWidget.tsx +21 -11
- package/src/components/SearchIconsView.tsx +10 -7
- package/src/components/SelectableTable/SelectableTable.tsx +157 -145
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +2 -3
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +25 -8
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -12
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +92 -23
- package/src/components/UnsavedChangesDialog.tsx +46 -0
- package/src/components/VirtualTable/VirtualTable.tsx +105 -51
- package/src/components/VirtualTable/VirtualTableCell.tsx +1 -9
- package/src/components/VirtualTable/VirtualTableHeader.tsx +10 -10
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +2 -2
- package/src/components/VirtualTable/VirtualTableProps.tsx +28 -14
- package/src/components/VirtualTable/VirtualTableRow.tsx +5 -6
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +5 -5
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -2
- package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -1
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +16 -28
- package/src/components/VirtualTable/types.tsx +2 -3
- package/src/components/{EntityCollectionTable/internal → common}/default_entity_actions.tsx +64 -44
- package/src/components/common/index.ts +2 -1
- package/src/components/{VirtualTable/common.tsx → common/table_height.tsx} +5 -2
- package/src/components/common/types.tsx +4 -6
- package/src/components/common/useColumnsIds.tsx +24 -3
- package/src/components/common/useDataSourceTableController.tsx +420 -0
- package/src/components/common/useDebouncedCallback.tsx +20 -0
- package/src/components/common/useScrollRestoration.tsx +68 -0
- package/src/components/common/useTableSearchHelper.ts +53 -12
- package/src/components/index.tsx +6 -2
- package/src/contexts/BreacrumbsContext.tsx +38 -0
- package/src/contexts/DialogsProvider.tsx +5 -4
- package/src/contexts/ModeController.tsx +1 -3
- package/src/contexts/SnackbarProvider.tsx +2 -0
- package/src/core/DefaultAppBar.tsx +219 -0
- package/src/core/DefaultDrawer.tsx +185 -0
- package/src/core/DrawerNavigationItem.tsx +66 -0
- package/src/core/EntityEditView.tsx +435 -470
- package/src/core/EntityEditViewFormActions.tsx +329 -0
- package/src/core/EntitySidePanel.tsx +88 -21
- package/src/core/FireCMS.tsx +74 -58
- package/src/core/FireCMSRouter.tsx +17 -0
- package/src/core/NavigationRoutes.tsx +28 -38
- package/src/core/SideDialogs.tsx +22 -12
- package/src/core/field_configs.tsx +26 -13
- package/src/core/index.tsx +6 -5
- package/src/form/EntityForm.tsx +620 -534
- package/src/form/EntityFormActions.tsx +211 -0
- package/src/form/PropertyFieldBinding.tsx +88 -45
- package/src/form/components/CustomIdField.tsx +9 -3
- package/src/form/components/FieldHelperText.tsx +4 -4
- package/src/form/components/FormEntry.tsx +22 -0
- package/src/form/components/FormLayout.tsx +16 -0
- package/src/form/components/LabelWithIcon.tsx +30 -19
- package/src/form/components/LabelWithIconAndTooltip.tsx +28 -0
- package/src/form/components/StorageItemPreview.tsx +23 -13
- package/src/form/components/StorageUploadProgress.tsx +5 -6
- package/src/form/components/index.tsx +3 -1
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +34 -19
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +50 -36
- package/src/form/field_bindings/BlockFieldBinding.tsx +56 -34
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +18 -14
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +61 -52
- package/src/form/field_bindings/MapFieldBinding.tsx +73 -55
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +159 -0
- package/src/form/field_bindings/{MultiSelectBinding.tsx → MultiSelectFieldBinding.tsx} +26 -21
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +11 -16
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +135 -0
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +42 -31
- package/src/form/field_bindings/RepeatFieldBinding.tsx +62 -35
- package/src/form/field_bindings/SelectFieldBinding.tsx +24 -15
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +257 -199
- package/src/form/field_bindings/SwitchFieldBinding.tsx +29 -24
- package/src/form/field_bindings/TextFieldBinding.tsx +28 -24
- package/src/form/index.tsx +17 -37
- package/src/form/useClearRestoreValue.tsx +2 -2
- package/src/form/validation.ts +13 -23
- package/src/hooks/data/delete.ts +6 -5
- package/src/hooks/data/save.ts +26 -33
- package/src/hooks/data/useCollectionFetch.tsx +3 -3
- package/src/hooks/data/useDataSource.tsx +11 -3
- package/src/hooks/data/useEntityFetch.tsx +10 -6
- package/src/hooks/index.tsx +2 -0
- package/src/hooks/useAuthController.tsx +1 -1
- package/src/hooks/useBreadcrumbsController.tsx +31 -0
- package/src/hooks/useBrowserTitleAndIcon.tsx +1 -1
- package/src/hooks/useBuildLocalConfigurationPersistence.tsx +8 -10
- package/src/hooks/useBuildModeController.tsx +22 -29
- package/src/hooks/useBuildNavigationController.tsx +440 -119
- package/src/hooks/useCollapsedGroups.ts +64 -0
- package/src/hooks/useFireCMSContext.tsx +3 -33
- package/src/hooks/useLargeLayout.tsx +0 -35
- package/src/hooks/useModeController.tsx +1 -2
- package/src/hooks/useProjectLog.tsx +32 -10
- package/src/hooks/useResolvedNavigationFrom.tsx +10 -12
- package/src/hooks/useValidateAuthenticator.tsx +17 -37
- package/src/index.ts +1 -0
- package/src/internal/useBuildDataSource.ts +79 -85
- package/src/internal/useBuildSideDialogsController.tsx +4 -2
- package/src/internal/useBuildSideEntityController.tsx +204 -77
- package/src/internal/useUnsavedChangesDialog.tsx +127 -91
- package/src/preview/PropertyPreview.tsx +34 -25
- package/src/preview/PropertyPreviewProps.tsx +7 -1
- package/src/preview/components/BooleanPreview.tsx +2 -2
- package/src/preview/components/EmptyValue.tsx +1 -1
- package/src/preview/components/EnumValuesChip.tsx +2 -2
- package/src/preview/components/ImagePreview.tsx +26 -37
- package/src/preview/components/ReferencePreview.tsx +26 -36
- package/src/preview/components/StorageThumbnail.tsx +5 -1
- package/src/preview/components/UrlComponentPreview.tsx +60 -28
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +6 -6
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +7 -5
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +5 -4
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -4
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +7 -6
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +7 -6
- package/src/preview/property_previews/MapPropertyPreview.tsx +12 -11
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +13 -13
- package/src/preview/property_previews/StringPropertyPreview.tsx +3 -3
- package/src/preview/util.ts +10 -10
- package/src/routes/CustomCMSRoute.tsx +21 -0
- package/src/routes/FireCMSRoute.tsx +246 -0
- package/src/routes/HomePageRoute.tsx +17 -0
- package/src/types/analytics.ts +3 -0
- package/src/types/auth.tsx +9 -13
- package/src/types/collections.ts +132 -30
- package/src/types/customization_controller.tsx +9 -1
- package/src/types/datasource.ts +61 -43
- package/src/types/dialogs_controller.tsx +7 -3
- package/src/types/entities.ts +12 -2
- package/src/types/entity_actions.tsx +86 -10
- package/src/types/entity_callbacks.ts +18 -18
- package/src/types/entity_overrides.tsx +2 -2
- package/src/types/export_import.ts +4 -4
- package/src/types/fields.tsx +85 -46
- package/src/types/firecms.tsx +18 -4
- package/src/types/firecms_context.tsx +1 -1
- package/src/types/index.ts +0 -1
- package/src/types/navigation.ts +77 -24
- package/src/types/permissions.ts +5 -5
- package/src/types/plugins.tsx +66 -15
- package/src/types/properties.ts +96 -27
- package/src/types/property_config.tsx +1 -2
- package/src/types/roles.ts +3 -0
- package/src/types/side_dialogs_controller.tsx +15 -0
- package/src/types/side_entity_controller.tsx +16 -1
- package/src/types/storage.ts +83 -1
- package/src/types/user.ts +2 -0
- package/src/util/builders.ts +10 -8
- package/src/util/callbacks.ts +119 -0
- package/src/util/createFormexStub.tsx +62 -0
- package/src/util/entities.ts +10 -7
- package/src/util/entity_actions.ts +28 -0
- package/src/util/entity_cache.ts +204 -0
- package/src/util/enums.ts +1 -1
- package/src/util/icon_list.ts +16 -10
- package/src/util/icon_synonyms.ts +3 -100
- package/src/util/icons.tsx +36 -11
- package/src/util/index.ts +3 -0
- package/src/util/join_collections.ts +9 -2
- package/src/util/make_properties_editable.ts +13 -5
- package/src/util/navigation_from_path.ts +33 -12
- package/src/util/navigation_utils.ts +141 -25
- package/src/util/objects.ts +90 -33
- package/src/util/parent_references_from_path.ts +3 -3
- package/src/util/permissions.ts +9 -8
- package/src/util/plurals.ts +0 -2
- package/src/util/property_utils.tsx +17 -6
- package/src/util/references.ts +19 -8
- package/src/util/resolutions.ts +122 -48
- package/src/util/storage.ts +79 -21
- package/src/util/strings.ts +2 -2
- package/src/util/useStorageUploadController.tsx +91 -28
- package/dist/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.d.ts +0 -5
- package/dist/components/FireCMSAppBar.d.ts +0 -26
- package/dist/components/PropertyIdCopyTooltipContent.d.ts +0 -3
- package/dist/components/VirtualTable/common.d.ts +0 -2
- package/dist/core/Drawer.d.ts +0 -23
- package/dist/core/Scaffold.d.ts +0 -55
- package/dist/core/SideEntityView.d.ts +0 -7
- package/dist/form/components/FormikArrayContainer.d.ts +0 -18
- package/dist/form/field_bindings/MarkdownFieldBinding.d.ts +0 -9
- package/dist/internal/useBuildCustomizationController.d.ts +0 -2
- package/dist/internal/useLocaleConfig.d.ts +0 -1
- package/dist/types/appcheck.d.ts +0 -26
- package/src/components/EntityCollectionTable/internal/popup_field/ElementResizeListener.tsx +0 -59
- package/src/components/FireCMSAppBar.tsx +0 -165
- package/src/components/PropertyIdCopyTooltipContent.tsx +0 -28
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -225
- package/src/core/Drawer.tsx +0 -191
- package/src/core/Scaffold.tsx +0 -281
- package/src/core/SideEntityView.tsx +0 -38
- package/src/form/components/FormikArrayContainer.tsx +0 -44
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +0 -695
- package/src/internal/useBuildCustomizationController.tsx +0 -5
- package/src/internal/useLocaleConfig.tsx +0 -18
- package/src/types/appcheck.ts +0 -29
- /package/src/util/{common.tsx → common.ts} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
2
|
import equal from "react-fast-compare"
|
|
3
|
+
import { useBlocker, useNavigate } from "react-router-dom";
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
AuthController,
|
|
@@ -9,10 +10,13 @@ import {
|
|
|
9
10
|
EntityCollection,
|
|
10
11
|
EntityCollectionsBuilder,
|
|
11
12
|
EntityReference,
|
|
13
|
+
FireCMSPlugin,
|
|
14
|
+
NavigationBlocker,
|
|
12
15
|
NavigationController,
|
|
16
|
+
NavigationEntry,
|
|
17
|
+
NavigationGroupMapping,
|
|
18
|
+
NavigationResult,
|
|
13
19
|
PermissionsBuilder,
|
|
14
|
-
TopNavigationEntry,
|
|
15
|
-
TopNavigationResult,
|
|
16
20
|
User,
|
|
17
21
|
UserConfigurationPersistence
|
|
18
22
|
} from "../types";
|
|
@@ -20,6 +24,7 @@ import {
|
|
|
20
24
|
applyPermissionsFunctionIfEmpty,
|
|
21
25
|
getCollectionByPathOrId,
|
|
22
26
|
mergeDeep,
|
|
27
|
+
removeFunctions,
|
|
23
28
|
removeInitialAndTrailingSlashes,
|
|
24
29
|
resolveCollectionPathIds,
|
|
25
30
|
resolvePermissions
|
|
@@ -29,28 +34,76 @@ import { getParentReferencesFromPath } from "../util/parent_references_from_path
|
|
|
29
34
|
const DEFAULT_BASE_PATH = "/";
|
|
30
35
|
const DEFAULT_COLLECTION_PATH = "/c";
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
export const NAVIGATION_DEFAULT_GROUP_NAME = "Views";
|
|
38
|
+
export const NAVIGATION_ADMIN_GROUP_NAME = "Admin";
|
|
39
|
+
|
|
40
|
+
export type BuildNavigationContextProps<EC extends EntityCollection, USER extends User> = {
|
|
41
|
+
/**
|
|
42
|
+
* Base path for the CMS, used to build the all the URLs.
|
|
43
|
+
* Defaults to "/".
|
|
44
|
+
*/
|
|
33
45
|
basePath?: string,
|
|
46
|
+
/**
|
|
47
|
+
* Base path for the collections, used to build the collection URLs.
|
|
48
|
+
* Defaults to "c" (e.g. "/c/products").
|
|
49
|
+
*/
|
|
34
50
|
baseCollectionPath?: string,
|
|
35
|
-
|
|
51
|
+
/**
|
|
52
|
+
* The auth controller used to manage the user authentication and permissions.
|
|
53
|
+
*/
|
|
54
|
+
authController: AuthController<USER>;
|
|
55
|
+
/**
|
|
56
|
+
* The collections to be used in the CMS.
|
|
57
|
+
* This can be a static array of collections or a function that returns a promise
|
|
58
|
+
* resolving to an array of collections.
|
|
59
|
+
*/
|
|
36
60
|
collections?: EC[] | EntityCollectionsBuilder<EC>;
|
|
61
|
+
/**
|
|
62
|
+
* Optional permissions builder to be applied to the collections.
|
|
63
|
+
* If not provided, the permissions will be resolved from the collection configuration.
|
|
64
|
+
*/
|
|
37
65
|
collectionPermissions?: PermissionsBuilder;
|
|
66
|
+
/**
|
|
67
|
+
* Custom views to be added to the CMS, these will be available in the main navigation.
|
|
68
|
+
* This can be a static array of views or a function that returns a promise
|
|
69
|
+
* resolving to an array of views.
|
|
70
|
+
*/
|
|
38
71
|
views?: CMSView[] | CMSViewsBuilder;
|
|
72
|
+
/**
|
|
73
|
+
* Custom views to be added to the CMS admin navigation.
|
|
74
|
+
* This can be a static array of views or a function that returns a promise
|
|
75
|
+
* resolving to an array of views.
|
|
76
|
+
*/
|
|
39
77
|
adminViews?: CMSView[] | CMSViewsBuilder;
|
|
40
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Controller for storing user preferences.
|
|
80
|
+
*/
|
|
41
81
|
userConfigPersistence?: UserConfigurationPersistence;
|
|
82
|
+
/**
|
|
83
|
+
* Delegate for data source operations, used to resolve collections and views.
|
|
84
|
+
*/
|
|
42
85
|
dataSourceDelegate: DataSourceDelegate;
|
|
43
86
|
/**
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*
|
|
87
|
+
* Plugins to be used in the CMS.
|
|
88
|
+
*/
|
|
89
|
+
plugins?: FireCMSPlugin[];
|
|
90
|
+
/**
|
|
91
|
+
* Used to define the name of groups and order of the navigation entries.
|
|
92
|
+
*/
|
|
93
|
+
navigationGroupMappings?: NavigationGroupMapping[];
|
|
94
|
+
/**
|
|
95
|
+
* If true, the navigation logic will not be updated until this flag is false
|
|
49
96
|
*/
|
|
50
|
-
|
|
97
|
+
disabled?: boolean;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @deprecated
|
|
101
|
+
* Use `navigationGroupMappings` instead.
|
|
102
|
+
*/
|
|
103
|
+
viewsOrder?: string[];
|
|
51
104
|
};
|
|
52
105
|
|
|
53
|
-
export function useBuildNavigationController<EC extends EntityCollection,
|
|
106
|
+
export function useBuildNavigationController<EC extends EntityCollection, USER extends User>(props: BuildNavigationContextProps<EC, USER>): NavigationController {
|
|
54
107
|
const {
|
|
55
108
|
basePath = DEFAULT_BASE_PATH,
|
|
56
109
|
baseCollectionPath = DEFAULT_COLLECTION_PATH,
|
|
@@ -60,18 +113,23 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
60
113
|
views: viewsProp,
|
|
61
114
|
adminViews: adminViewsProp,
|
|
62
115
|
viewsOrder,
|
|
116
|
+
plugins,
|
|
63
117
|
userConfigPersistence,
|
|
64
118
|
dataSourceDelegate,
|
|
65
|
-
|
|
119
|
+
disabled,
|
|
120
|
+
navigationGroupMappings
|
|
66
121
|
} = props;
|
|
67
122
|
|
|
123
|
+
const navigate = useNavigate();
|
|
124
|
+
|
|
68
125
|
const collectionsRef = useRef<EntityCollection[] | undefined>();
|
|
69
126
|
const viewsRef = useRef<CMSView[] | undefined>();
|
|
70
127
|
const adminViewsRef = useRef<CMSView[] | undefined>();
|
|
128
|
+
const navigationEntriesOrderRef = useRef<string[] | undefined>();
|
|
71
129
|
|
|
72
130
|
const [initialised, setInitialised] = useState<boolean>(false);
|
|
73
131
|
|
|
74
|
-
const [topLevelNavigation, setTopLevelNavigation] = useState<
|
|
132
|
+
const [topLevelNavigation, setTopLevelNavigation] = useState<NavigationResult | undefined>(undefined);
|
|
75
133
|
const [navigationLoading, setNavigationLoading] = useState<boolean>(true);
|
|
76
134
|
const [navigationLoadingError, setNavigationLoadingError] = useState<Error | undefined>(undefined);
|
|
77
135
|
|
|
@@ -88,118 +146,221 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
88
146
|
const buildUrlCollectionPath = useCallback((path: string): string => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path)}`,
|
|
89
147
|
[baseCollectionPath]);
|
|
90
148
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
149
|
+
const allPluginGroups = plugins?.flatMap(plugin => plugin.homePage?.navigationEntries ? plugin.homePage.navigationEntries.map(e => e.name) : []) ?? [];
|
|
150
|
+
const pluginGroups = [...new Set(allPluginGroups)];
|
|
151
|
+
|
|
152
|
+
const onNavigationEntriesOrderUpdate = useCallback((entries: NavigationGroupMapping[]) => {
|
|
153
|
+
if (!plugins) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// remove all groups that have no entries
|
|
157
|
+
const filteredEntries = entries.filter(entry => entry.entries.length > 0);
|
|
158
|
+
if (plugins.some(plugin => plugin.homePage?.onNavigationEntriesUpdate)) {
|
|
159
|
+
plugins.forEach(plugin => {
|
|
160
|
+
if (plugin.homePage?.onNavigationEntriesUpdate) {
|
|
161
|
+
plugin.homePage.onNavigationEntriesUpdate(filteredEntries);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}, [plugins]);
|
|
167
|
+
|
|
168
|
+
const computeTopNavigation = useCallback((collections: EntityCollection[], views: CMSView[], adminViews: CMSView[], viewsOrder?: string[]): NavigationResult => {
|
|
169
|
+
|
|
170
|
+
const finalNavigationGroupMappings: NavigationGroupMapping[] = computeNavigationGroups({
|
|
171
|
+
navigationGroupMappings: navigationGroupMappings,
|
|
172
|
+
collections,
|
|
173
|
+
views,
|
|
174
|
+
plugins: plugins
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const allPluginNavigationEntries = finalNavigationGroupMappings.map((g) => g.entries).flat() ?? [];
|
|
178
|
+
const navigationEntriesOrder = ([...new Set(allPluginNavigationEntries)]);
|
|
179
|
+
|
|
180
|
+
let navigationEntries: NavigationEntry[] = [
|
|
181
|
+
...(collections ?? []).reduce((acc, collection) => {
|
|
182
|
+
if (collection.hideFromNavigation) return acc;
|
|
183
|
+
|
|
184
|
+
const pathKey = collection.id ?? collection.path;
|
|
185
|
+
let groupName = getGroup(collection); // Initial group
|
|
186
|
+
|
|
187
|
+
if (finalNavigationGroupMappings) {
|
|
188
|
+
for (const pluginGroupDef of finalNavigationGroupMappings) {
|
|
189
|
+
if (pluginGroupDef.entries.includes(pathKey)) {
|
|
190
|
+
groupName = pluginGroupDef.name;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
acc.push({
|
|
197
|
+
id: `collection:${pathKey}`,
|
|
198
|
+
url: buildUrlCollectionPath(pathKey),
|
|
96
199
|
type: "collection",
|
|
97
200
|
name: collection.name.trim(),
|
|
98
|
-
path:
|
|
201
|
+
path: pathKey,
|
|
99
202
|
collection,
|
|
100
203
|
description: collection.description?.trim(),
|
|
101
|
-
group:
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
:
|
|
130
|
-
|
|
204
|
+
group: groupName ?? NAVIGATION_DEFAULT_GROUP_NAME
|
|
205
|
+
});
|
|
206
|
+
return acc;
|
|
207
|
+
}, [] as NavigationEntry[]),
|
|
208
|
+
|
|
209
|
+
...(views ?? []).reduce((acc, view) => {
|
|
210
|
+
if (view.hideFromNavigation) return acc;
|
|
211
|
+
|
|
212
|
+
const pathKey = Array.isArray(view.path) ? view.path[0] : view.path;
|
|
213
|
+
let groupName = getGroup(view); // Initial group
|
|
214
|
+
|
|
215
|
+
if (finalNavigationGroupMappings) {
|
|
216
|
+
for (const pluginGroupDef of finalNavigationGroupMappings) {
|
|
217
|
+
if (pluginGroupDef.entries.includes(pathKey)) {
|
|
218
|
+
groupName = pluginGroupDef.name;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
acc.push({
|
|
225
|
+
id: `view:${pathKey}`,
|
|
226
|
+
url: buildCMSUrlPath(pathKey),
|
|
227
|
+
name: view.name.trim(),
|
|
228
|
+
type: "view",
|
|
229
|
+
path: view.path,
|
|
230
|
+
view,
|
|
231
|
+
description: view.description?.trim(),
|
|
232
|
+
group: groupName ?? NAVIGATION_DEFAULT_GROUP_NAME
|
|
233
|
+
});
|
|
234
|
+
return acc;
|
|
235
|
+
}, [] as NavigationEntry[]),
|
|
236
|
+
|
|
237
|
+
...(adminViews ?? []).reduce((acc, view) => {
|
|
238
|
+
if (view.hideFromNavigation) return acc;
|
|
239
|
+
|
|
240
|
+
const pathKey = Array.isArray(view.path) ? view.path[0] : view.path;
|
|
241
|
+
const groupName = NAVIGATION_ADMIN_GROUP_NAME;
|
|
242
|
+
|
|
243
|
+
acc.push({
|
|
244
|
+
id: `admin:${pathKey}`,
|
|
245
|
+
url: buildCMSUrlPath(pathKey),
|
|
246
|
+
name: view.name.trim(),
|
|
247
|
+
type: "admin",
|
|
248
|
+
path: view.path,
|
|
249
|
+
view,
|
|
250
|
+
description: view.description?.trim(),
|
|
251
|
+
group: groupName
|
|
252
|
+
});
|
|
253
|
+
return acc;
|
|
254
|
+
}, [] as NavigationEntry[])
|
|
131
255
|
];
|
|
132
256
|
|
|
133
|
-
|
|
257
|
+
const groupOrderValue = (groupName?: string): number => {
|
|
258
|
+
if (groupName === NAVIGATION_ADMIN_GROUP_NAME) return 1;
|
|
259
|
+
return 0; // Other groups
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
navigationEntries = navigationEntries.sort((a, b) => {
|
|
263
|
+
return groupOrderValue(a.group) - groupOrderValue(b.group);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const usedViewsOrder = viewsOrder ?? navigationEntriesOrder;
|
|
267
|
+
if (usedViewsOrder) {
|
|
134
268
|
navigationEntries = navigationEntries.sort((a, b) => {
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
return 1;
|
|
142
|
-
}
|
|
143
|
-
if (bIndex === -1) {
|
|
144
|
-
return -1;
|
|
145
|
-
}
|
|
269
|
+
const getSortPath = (navEntry: NavigationEntry) => typeof navEntry.path === "string" ? navEntry.path : navEntry.path[0];
|
|
270
|
+
const aIndex = usedViewsOrder.indexOf(getSortPath(a));
|
|
271
|
+
const bIndex = usedViewsOrder.indexOf(getSortPath(b));
|
|
272
|
+
if (aIndex === -1 && bIndex === -1) return 0;
|
|
273
|
+
if (aIndex === -1) return 1;
|
|
274
|
+
if (bIndex === -1) return -1;
|
|
146
275
|
return aIndex - bIndex;
|
|
147
276
|
});
|
|
148
277
|
}
|
|
149
278
|
|
|
150
|
-
const
|
|
279
|
+
const collectedGroupsFromEntries = navigationEntries
|
|
151
280
|
.map(e => e.group)
|
|
152
|
-
.filter(Boolean)
|
|
153
|
-
|
|
281
|
+
.filter(Boolean) as string[];
|
|
282
|
+
|
|
283
|
+
const allDefinedGroups = [
|
|
284
|
+
...(pluginGroups ?? []),
|
|
285
|
+
...collectedGroupsFromEntries
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
const uniqueGroups = [...new Set(allDefinedGroups)]
|
|
289
|
+
.sort((a, b) => groupOrderValue(a) - groupOrderValue(b));
|
|
154
290
|
|
|
155
291
|
return {
|
|
292
|
+
allowDragAndDrop: plugins?.some(plugin => plugin.homePage?.allowDragAndDrop) ?? false,
|
|
156
293
|
navigationEntries,
|
|
157
|
-
groups
|
|
294
|
+
groups: uniqueGroups,
|
|
295
|
+
onNavigationEntriesUpdate: onNavigationEntriesOrderUpdate,
|
|
158
296
|
};
|
|
159
|
-
}, [buildCMSUrlPath, buildUrlCollectionPath]);
|
|
297
|
+
}, [navigationGroupMappings, buildCMSUrlPath, buildUrlCollectionPath, pluginGroups, onNavigationEntriesOrderUpdate]);
|
|
160
298
|
|
|
161
299
|
const refreshNavigation = useCallback(async () => {
|
|
162
300
|
|
|
163
|
-
if (authController.initialLoading)
|
|
301
|
+
if (disabled || authController.initialLoading)
|
|
164
302
|
return;
|
|
165
303
|
|
|
304
|
+
console.debug("Refreshing navigation");
|
|
305
|
+
|
|
166
306
|
try {
|
|
167
307
|
|
|
168
308
|
const [resolvedCollections = [], resolvedViews, resolvedAdminViews = []] = await Promise.all([
|
|
169
|
-
resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate,
|
|
309
|
+
resolveCollections(collectionsProp, collectionPermissions, authController, dataSourceDelegate, plugins),
|
|
170
310
|
resolveCMSViews(viewsProp, authController, dataSourceDelegate),
|
|
171
311
|
resolveCMSViews(adminViewsProp, authController, dataSourceDelegate)
|
|
172
312
|
]
|
|
173
313
|
);
|
|
174
314
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
!equal(topLevelNavigation, computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder))
|
|
180
|
-
) {
|
|
315
|
+
const computedTopLevelNav = computeTopNavigation(resolvedCollections, resolvedViews, resolvedAdminViews, viewsOrder);
|
|
316
|
+
|
|
317
|
+
let shouldUpdateTopLevelNav = false;
|
|
318
|
+
if (!areCollectionListsEqual(collectionsRef.current ?? [], resolvedCollections)) {
|
|
181
319
|
collectionsRef.current = resolvedCollections;
|
|
320
|
+
console.debug("Collections have changed", resolvedCollections);
|
|
321
|
+
shouldUpdateTopLevelNav = true;
|
|
322
|
+
}
|
|
323
|
+
if (collectionsRef.current === undefined) {
|
|
324
|
+
collectionsRef.current = resolvedCollections;
|
|
325
|
+
shouldUpdateTopLevelNav = true;
|
|
326
|
+
}
|
|
327
|
+
if (!equal(viewsRef.current, resolvedViews)) {
|
|
182
328
|
viewsRef.current = resolvedViews;
|
|
329
|
+
shouldUpdateTopLevelNav = true;
|
|
330
|
+
}
|
|
331
|
+
if (!equal(adminViewsRef.current, resolvedAdminViews)) {
|
|
183
332
|
adminViewsRef.current = resolvedAdminViews;
|
|
184
|
-
|
|
333
|
+
shouldUpdateTopLevelNav = true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const navigationEntriesOrder = computedTopLevelNav.navigationEntries.map(e => e.id);
|
|
337
|
+
if (!equal(navigationEntriesOrderRef.current, navigationEntriesOrder)) {
|
|
338
|
+
navigationEntriesOrderRef.current = navigationEntriesOrder;
|
|
339
|
+
shouldUpdateTopLevelNav = true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (shouldUpdateTopLevelNav && !equal(topLevelNavigation, computedTopLevelNav)) {
|
|
343
|
+
setTopLevelNavigation(computedTopLevelNav);
|
|
185
344
|
}
|
|
186
345
|
} catch (e) {
|
|
187
346
|
console.error(e);
|
|
188
347
|
setNavigationLoadingError(e as any);
|
|
189
348
|
}
|
|
190
349
|
|
|
191
|
-
|
|
192
|
-
|
|
350
|
+
if (navigationLoading)
|
|
351
|
+
setNavigationLoading(false);
|
|
352
|
+
if (!initialised)
|
|
353
|
+
setInitialised(true);
|
|
193
354
|
|
|
194
355
|
}, [
|
|
195
356
|
collectionsProp,
|
|
196
357
|
collectionPermissions,
|
|
197
358
|
authController.user,
|
|
198
359
|
authController.initialLoading,
|
|
360
|
+
disabled,
|
|
199
361
|
viewsProp,
|
|
200
362
|
adminViewsProp,
|
|
201
363
|
computeTopNavigation,
|
|
202
|
-
injectCollections
|
|
203
364
|
]);
|
|
204
365
|
|
|
205
366
|
useEffect(() => {
|
|
@@ -208,7 +369,6 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
208
369
|
|
|
209
370
|
const getCollection = useCallback((
|
|
210
371
|
idOrPath: string,
|
|
211
|
-
entityId?: string,
|
|
212
372
|
includeUserOverride = false
|
|
213
373
|
): EC | undefined => {
|
|
214
374
|
const collections = collectionsRef.current;
|
|
@@ -218,8 +378,7 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
218
378
|
const baseCollection = getCollectionByPathOrId(removeInitialAndTrailingSlashes(idOrPath), collections);
|
|
219
379
|
|
|
220
380
|
const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(idOrPath) : undefined;
|
|
221
|
-
|
|
222
|
-
const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride) : undefined;
|
|
381
|
+
const overriddenCollection = baseCollection ? mergeDeep(baseCollection, userOverride ?? {}) : undefined;
|
|
223
382
|
|
|
224
383
|
let result: Partial<EntityCollection> | undefined = overriddenCollection;
|
|
225
384
|
|
|
@@ -241,12 +400,22 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
241
400
|
|
|
242
401
|
}, [userConfigPersistence]);
|
|
243
402
|
|
|
403
|
+
const getCollectionById = useCallback((id: string): EC | undefined => {
|
|
404
|
+
const collections = collectionsRef.current;
|
|
405
|
+
if (collections === undefined)
|
|
406
|
+
throw Error("getCollectionById: Collections have not been initialised yet");
|
|
407
|
+
const collection: EntityCollection | undefined = collections.find(c => c.id === id);
|
|
408
|
+
if (!collection)
|
|
409
|
+
return undefined;
|
|
410
|
+
return collection as EC;
|
|
411
|
+
}, []);
|
|
412
|
+
|
|
244
413
|
const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
|
|
245
414
|
|
|
246
415
|
const collections = collectionsRef.current;
|
|
416
|
+
if (collections === undefined)
|
|
417
|
+
throw Error("getCollectionFromPaths: Collections have not been initialised yet");
|
|
247
418
|
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
248
|
-
if (!currentCollections)
|
|
249
|
-
throw Error("Collections have not been initialised yet");
|
|
250
419
|
|
|
251
420
|
for (let i = 0; i < pathSegments.length; i++) {
|
|
252
421
|
const pathSegment = pathSegments[i];
|
|
@@ -265,9 +434,9 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
265
434
|
const getCollectionFromIds = useCallback(<EC extends EntityCollection>(ids: string[]): EC | undefined => {
|
|
266
435
|
|
|
267
436
|
const collections = collectionsRef.current;
|
|
437
|
+
if (collections === undefined)
|
|
438
|
+
throw Error("getCollectionFromIds: Collections have not been initialised yet");
|
|
268
439
|
let currentCollections: EntityCollection[] | undefined = [...(collections ?? [])];
|
|
269
|
-
if (!currentCollections)
|
|
270
|
-
throw Error("Collections have not been initialised yet");
|
|
271
440
|
|
|
272
441
|
for (let i = 0; i < ids.length; i++) {
|
|
273
442
|
const id = ids[i];
|
|
@@ -293,19 +462,8 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
293
462
|
throw Error("Expected path starting with " + fullCollectionPath);
|
|
294
463
|
}, [fullCollectionPath]);
|
|
295
464
|
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
}: {
|
|
299
|
-
path: string
|
|
300
|
-
}): string => {
|
|
301
|
-
return `s/edit/${encodePath(path)}`;
|
|
302
|
-
},
|
|
303
|
-
[]);
|
|
304
|
-
|
|
305
|
-
const resolveAliasesFrom = useCallback((path: string): string => {
|
|
306
|
-
const collections = collectionsRef.current;
|
|
307
|
-
if (!collections)
|
|
308
|
-
throw Error("Collections have not been initialised yet");
|
|
465
|
+
const resolveIdsFrom = useCallback((path: string): string => {
|
|
466
|
+
const collections = collectionsRef.current ?? [];
|
|
309
467
|
return resolveCollectionPathIds(path, collections);
|
|
310
468
|
}, []);
|
|
311
469
|
|
|
@@ -359,29 +517,23 @@ export function useBuildNavigationController<EC extends EntityCollection, UserTy
|
|
|
359
517
|
baseCollectionPath,
|
|
360
518
|
initialised,
|
|
361
519
|
getCollection,
|
|
520
|
+
getCollectionById,
|
|
362
521
|
getCollectionFromPaths,
|
|
363
522
|
getCollectionFromIds,
|
|
364
523
|
isUrlCollectionPath,
|
|
365
524
|
urlPathToDataPath,
|
|
366
525
|
buildUrlCollectionPath,
|
|
367
|
-
|
|
368
|
-
buildCMSUrlPath,
|
|
369
|
-
resolveAliasesFrom,
|
|
526
|
+
resolveIdsFrom,
|
|
370
527
|
topLevelNavigation,
|
|
371
528
|
refreshNavigation,
|
|
372
529
|
getParentReferencesFromPath: getAllParentReferencesForPath,
|
|
373
530
|
getParentCollectionIds,
|
|
374
|
-
convertIdsToPaths
|
|
531
|
+
convertIdsToPaths,
|
|
532
|
+
navigate,
|
|
533
|
+
plugins
|
|
375
534
|
};
|
|
376
535
|
}
|
|
377
536
|
|
|
378
|
-
export function getSidePanelKey(path: string, entityId?: string) {
|
|
379
|
-
if (entityId)
|
|
380
|
-
return `${removeInitialAndTrailingSlashes(path)}/${removeInitialAndTrailingSlashes(entityId)}`;
|
|
381
|
-
else
|
|
382
|
-
return removeInitialAndTrailingSlashes(path);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
537
|
function encodePath(input: string) {
|
|
386
538
|
return encodeURIComponent(removeInitialAndTrailingSlashes(input))
|
|
387
539
|
.replaceAll("%2F", "/")
|
|
@@ -390,9 +542,10 @@ function encodePath(input: string) {
|
|
|
390
542
|
|
|
391
543
|
function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[], authController: AuthController<User>): EntityCollection[] {
|
|
392
544
|
return resolvedCollections
|
|
545
|
+
.filter((c) => Boolean(c.path))
|
|
393
546
|
.filter((c) => {
|
|
394
547
|
if (!c.permissions) return true;
|
|
395
|
-
const resolvedPermissions = resolvePermissions(c, authController, c.path, null)
|
|
548
|
+
const resolvedPermissions = resolvePermissions(c, authController, c.path, null);
|
|
396
549
|
return resolvedPermissions?.read !== false;
|
|
397
550
|
})
|
|
398
551
|
.map((c) => {
|
|
@@ -404,11 +557,24 @@ function filterOutNotAllowedCollections(resolvedCollections: EntityCollection[],
|
|
|
404
557
|
});
|
|
405
558
|
}
|
|
406
559
|
|
|
560
|
+
function applyPluginModifyCollection(resolvedCollections: EntityCollection[], modifyCollection: (collection: EntityCollection) => EntityCollection) {
|
|
561
|
+
return resolvedCollections.map((collection: EntityCollection): EntityCollection => {
|
|
562
|
+
const modifiedCollection = modifyCollection(collection);
|
|
563
|
+
if (modifiedCollection.subcollections) {
|
|
564
|
+
return {
|
|
565
|
+
...modifiedCollection,
|
|
566
|
+
subcollections: applyPluginModifyCollection(modifiedCollection.subcollections, modifyCollection)
|
|
567
|
+
} satisfies EntityCollection;
|
|
568
|
+
}
|
|
569
|
+
return modifiedCollection;
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
|
|
407
573
|
async function resolveCollections(collections: undefined | EntityCollection[] | EntityCollectionsBuilder<any>,
|
|
408
574
|
collectionPermissions: PermissionsBuilder | undefined,
|
|
409
575
|
authController: AuthController,
|
|
410
576
|
dataSource: DataSourceDelegate,
|
|
411
|
-
|
|
577
|
+
plugins: FireCMSPlugin[] | undefined): Promise<EntityCollection[]> {
|
|
412
578
|
let resolvedCollections: EntityCollection[] = [];
|
|
413
579
|
if (typeof collections === "function") {
|
|
414
580
|
resolvedCollections = await collections({
|
|
@@ -420,14 +586,20 @@ async function resolveCollections(collections: undefined | EntityCollection[] |
|
|
|
420
586
|
resolvedCollections = collections;
|
|
421
587
|
}
|
|
422
588
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
589
|
+
if (plugins) {
|
|
590
|
+
for (const plugin of plugins) {
|
|
591
|
+
if (plugin.collection?.modifyCollection) {
|
|
592
|
+
resolvedCollections = applyPluginModifyCollection(resolvedCollections, plugin.collection.modifyCollection);
|
|
593
|
+
}
|
|
426
594
|
|
|
427
|
-
|
|
428
|
-
|
|
595
|
+
if (plugin.collection?.injectCollections) {
|
|
596
|
+
resolvedCollections = plugin.collection.injectCollections(resolvedCollections ?? []);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
429
599
|
}
|
|
430
600
|
|
|
601
|
+
resolvedCollections = applyPermissionsFunctionIfEmpty(resolvedCollections, collectionPermissions);
|
|
602
|
+
resolvedCollections = filterOutNotAllowedCollections(resolvedCollections, authController);
|
|
431
603
|
return resolvedCollections;
|
|
432
604
|
}
|
|
433
605
|
|
|
@@ -448,7 +620,156 @@ async function resolveCMSViews(baseViews: CMSView[] | CMSViewsBuilder | undefine
|
|
|
448
620
|
function getGroup(collectionOrView: EntityCollection<any, any> | CMSView) {
|
|
449
621
|
const trimmed = collectionOrView.group?.trim();
|
|
450
622
|
if (!trimmed || trimmed === "") {
|
|
451
|
-
return
|
|
623
|
+
return NAVIGATION_DEFAULT_GROUP_NAME;
|
|
624
|
+
}
|
|
625
|
+
return trimmed ?? NAVIGATION_DEFAULT_GROUP_NAME;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function areCollectionListsEqual(a: EntityCollection[], b: EntityCollection[]) {
|
|
629
|
+
if (a.length !== b.length) {
|
|
630
|
+
return false;
|
|
452
631
|
}
|
|
453
|
-
|
|
632
|
+
const aCopy = [...a];
|
|
633
|
+
const bCopy = [...b];
|
|
634
|
+
const aSorted = aCopy.sort((x, y) => x.id.localeCompare(y.id));
|
|
635
|
+
const bSorted = bCopy.sort((x, y) => x.id.localeCompare(y.id));
|
|
636
|
+
return aSorted.every((value, index) => areCollectionsEqual(value, bSorted[index]));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function areCollectionsEqual(a: EntityCollection, b: EntityCollection) {
|
|
640
|
+
const {
|
|
641
|
+
subcollections: subcollectionsA,
|
|
642
|
+
...restA
|
|
643
|
+
} = a;
|
|
644
|
+
const {
|
|
645
|
+
subcollections: subcollectionsB,
|
|
646
|
+
...restB
|
|
647
|
+
} = b;
|
|
648
|
+
if (!areCollectionListsEqual(subcollectionsA ?? [], subcollectionsB ?? [])) {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
return equal(removeFunctions(restA), removeFunctions(restB));
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function useCustomBlocker(): NavigationBlocker {
|
|
655
|
+
const [blockListeners, setBlockListeners] = useState<Record<string, {
|
|
656
|
+
block: boolean,
|
|
657
|
+
basePath?: string
|
|
658
|
+
}>>({});
|
|
659
|
+
|
|
660
|
+
const shouldBlock = Object.values(blockListeners).some(b => b.block);
|
|
661
|
+
|
|
662
|
+
let blocker: any;
|
|
663
|
+
try {
|
|
664
|
+
blocker = useBlocker(({
|
|
665
|
+
nextLocation
|
|
666
|
+
}) => {
|
|
667
|
+
const allBasePaths = Object.values(blockListeners).map(b => b.basePath).filter(Boolean) as string[];
|
|
668
|
+
if (allBasePaths && allBasePaths.some(path => nextLocation.pathname.startsWith(path)))
|
|
669
|
+
return false;
|
|
670
|
+
return shouldBlock;
|
|
671
|
+
});
|
|
672
|
+
} catch (e) {
|
|
673
|
+
// console.warn("Blocker not available, navigation will not be blocked");
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const updateBlockListener = (path: string, block: boolean, basePath?: string) => {
|
|
677
|
+
setBlockListeners(prev => ({
|
|
678
|
+
...prev,
|
|
679
|
+
[path]: {
|
|
680
|
+
block,
|
|
681
|
+
basePath
|
|
682
|
+
}
|
|
683
|
+
}));
|
|
684
|
+
return () => setBlockListeners(prev => {
|
|
685
|
+
const {
|
|
686
|
+
[path]: removed,
|
|
687
|
+
...rest
|
|
688
|
+
} = prev;
|
|
689
|
+
return rest;
|
|
690
|
+
})
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
const isBlocked = (path: string) => {
|
|
694
|
+
return (blockListeners[path]?.block ?? false) && blocker?.state === "blocked";
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
updateBlockListener,
|
|
699
|
+
isBlocked,
|
|
700
|
+
proceed: blocker?.proceed,
|
|
701
|
+
reset: blocker?.reset
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function computeNavigationGroups({
|
|
706
|
+
navigationGroupMappings,
|
|
707
|
+
collections,
|
|
708
|
+
views,
|
|
709
|
+
plugins
|
|
710
|
+
}: {
|
|
711
|
+
navigationGroupMappings?: NavigationGroupMapping[],
|
|
712
|
+
collections?: EntityCollection[],
|
|
713
|
+
views?: CMSView[],
|
|
714
|
+
plugins?: FireCMSPlugin[]
|
|
715
|
+
}): NavigationGroupMapping[] {
|
|
716
|
+
|
|
717
|
+
let result = navigationGroupMappings;
|
|
718
|
+
|
|
719
|
+
result = plugins ? plugins?.reduce((acc, plugin) => {
|
|
720
|
+
if (plugin.homePage?.navigationEntries) {
|
|
721
|
+
plugin.homePage.navigationEntries.forEach((entry) => {
|
|
722
|
+
const {
|
|
723
|
+
name,
|
|
724
|
+
entries
|
|
725
|
+
} = entry;
|
|
726
|
+
const existingGroup = acc.find(entry => entry.name === name);
|
|
727
|
+
if (existingGroup) {
|
|
728
|
+
existingGroup.entries.push(...entries);
|
|
729
|
+
} else {
|
|
730
|
+
acc.push({
|
|
731
|
+
name,
|
|
732
|
+
entries: [...entries]
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
}
|
|
738
|
+
return acc;
|
|
739
|
+
}, [...(result ?? [])] as NavigationGroupMapping[]) : result;
|
|
740
|
+
|
|
741
|
+
if (!result) {
|
|
742
|
+
// Convert views and collections to navigation group mappings, grouped by their group name
|
|
743
|
+
result = [];
|
|
744
|
+
const groupMap: Record<string, string[]> = {};
|
|
745
|
+
|
|
746
|
+
// Add collections
|
|
747
|
+
(collections ?? []).forEach(collection => {
|
|
748
|
+
const name = getGroup(collection);
|
|
749
|
+
const entry = collection.id ?? collection.path;
|
|
750
|
+
if (!groupMap[name]) groupMap[name] = [];
|
|
751
|
+
groupMap[name].push(entry);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
// Add views
|
|
755
|
+
(views ?? []).forEach(view => {
|
|
756
|
+
const name = getGroup(view);
|
|
757
|
+
const entry = Array.isArray(view.path) ? view.path[0] : view.path;
|
|
758
|
+
if (!groupMap[name]) groupMap[name] = [];
|
|
759
|
+
groupMap[name].push(entry);
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
// Convert groupMap to initialGroupMappings array
|
|
763
|
+
result = Object.entries(groupMap).map(([name, entries]) => ({
|
|
764
|
+
name,
|
|
765
|
+
entries
|
|
766
|
+
}));
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Remove duplicates in entries
|
|
770
|
+
result.forEach(group => {
|
|
771
|
+
group.entries = [...new Set(group.entries)];
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
return result;
|
|
454
775
|
}
|