@firecms/core 3.0.0-beta.11 → 3.0.0-beta.13
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/dist/app/Scaffold.d.ts +4 -0
- package/dist/components/ArrayContainer.d.ts +14 -3
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
- package/dist/components/EntityCollectionTable/index.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +8 -0
- package/dist/components/EntityJsonPreview.d.ts +3 -0
- package/dist/components/PropertyIdCopyTooltip.d.ts +1 -1
- package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
- package/dist/components/SelectableTable/filters/ReferenceFilterField.d.ts +1 -1
- package/dist/components/UnsavedChangesDialog.d.ts +8 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
- package/dist/components/common/default_entity_actions.d.ts +0 -2
- package/dist/components/common/index.d.ts +1 -1
- package/dist/components/common/useColumnsIds.d.ts +1 -0
- package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
- package/dist/components/common/useDebouncedCallback.d.ts +1 -0
- package/dist/components/common/useScrollRestoration.d.ts +14 -0
- package/dist/contexts/BreacrumbsContext.d.ts +8 -0
- package/dist/core/DefaultAppBar.d.ts +8 -2
- package/dist/core/EntityEditView.d.ts +36 -21
- package/dist/core/EntityEditViewFormActions.d.ts +2 -0
- package/dist/core/FireCMS.d.ts +2 -2
- package/dist/core/FireCMSRouter.d.ts +4 -0
- package/dist/core/NavigationRoutes.d.ts +0 -1
- package/dist/core/SideDialogs.d.ts +4 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/form/EntityForm.d.ts +49 -0
- package/dist/form/EntityFormActions.d.ts +17 -0
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/form/components/FormEntry.d.ts +6 -0
- package/dist/form/components/FormLayout.d.ts +5 -0
- package/dist/form/components/index.d.ts +2 -0
- 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/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/RepeatFieldBinding.d.ts +1 -1
- package/dist/form/index.d.ts +16 -16
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/data/useEntityFetch.d.ts +2 -1
- package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
- package/dist/hooks/useBuildNavigationController.d.ts +4 -1
- package/dist/hooks/useModeController.d.ts +1 -2
- package/dist/index.es.js +15842 -14396
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +15833 -14389
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +3 -2
- package/dist/internal/useBuildSideEntityController.d.ts +3 -3
- package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
- 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/collections.d.ts +36 -10
- package/dist/types/datasource.d.ts +2 -2
- package/dist/types/dialogs_controller.d.ts +7 -3
- package/dist/types/entities.d.ts +1 -1
- package/dist/types/entity_actions.d.ts +10 -0
- package/dist/types/fields.d.ts +13 -3
- package/dist/types/firecms.d.ts +1 -1
- package/dist/types/navigation.d.ts +33 -12
- package/dist/types/plugins.d.ts +2 -0
- package/dist/types/properties.d.ts +40 -4
- package/dist/types/side_entity_controller.d.ts +6 -1
- package/dist/util/builders.d.ts +1 -1
- package/dist/util/createFormexStub.d.ts +2 -0
- package/dist/util/entity_actions.d.ts +2 -0
- package/dist/util/entity_cache.d.ts +23 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_from_path.d.ts +6 -1
- package/dist/util/navigation_utils.d.ts +13 -1
- package/dist/util/objects.d.ts +1 -1
- package/dist/util/property_utils.d.ts +2 -2
- package/dist/util/references.d.ts +2 -2
- package/dist/util/resolutions.d.ts +12 -2
- package/package.json +20 -21
- package/src/app/Scaffold.tsx +13 -3
- package/src/components/ArrayContainer.tsx +60 -24
- package/src/components/CircularProgressCenter.tsx +1 -1
- package/src/components/DeleteEntityDialog.tsx +5 -3
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +46 -23
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
- package/src/components/EntityCollectionTable/index.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +10 -13
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +105 -47
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -2
- package/src/components/EntityJsonPreview.tsx +66 -0
- package/src/components/EntityPreview.tsx +10 -3
- package/src/components/EntityView.tsx +4 -1
- package/src/components/NotFoundPage.tsx +2 -2
- package/src/components/PropertyIdCopyTooltip.tsx +2 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +14 -7
- package/src/components/SelectableTable/SelectableTable.tsx +18 -3
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +7 -7
- package/src/components/UnsavedChangesDialog.tsx +42 -0
- package/src/components/VirtualTable/VirtualTable.tsx +48 -3
- package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
- package/src/components/common/default_entity_actions.tsx +37 -37
- package/src/components/common/index.ts +1 -1
- package/src/components/common/useColumnsIds.tsx +1 -1
- 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/contexts/BreacrumbsContext.tsx +38 -0
- package/src/contexts/DialogsProvider.tsx +3 -2
- package/src/contexts/ModeController.tsx +1 -3
- package/src/core/DefaultAppBar.tsx +59 -17
- package/src/core/DefaultDrawer.tsx +1 -1
- package/src/core/EntityEditView.tsx +354 -1000
- package/src/core/EntityEditViewFormActions.tsx +199 -0
- package/src/core/EntitySidePanel.tsx +85 -15
- package/src/core/FireCMS.tsx +20 -16
- package/src/core/FireCMSRouter.tsx +17 -0
- package/src/core/NavigationRoutes.tsx +23 -32
- package/src/core/SideDialogs.tsx +20 -11
- package/src/core/index.tsx +4 -2
- package/src/form/EntityForm.tsx +782 -0
- package/src/form/EntityFormActions.tsx +169 -0
- package/src/form/PropertyFieldBinding.tsx +21 -16
- package/src/form/components/FormEntry.tsx +22 -0
- package/src/form/components/FormLayout.tsx +16 -0
- package/src/form/components/LabelWithIcon.tsx +6 -3
- package/src/form/components/StorageUploadProgress.tsx +1 -1
- package/src/form/components/index.tsx +2 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +8 -5
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +5 -2
- package/src/form/field_bindings/BlockFieldBinding.tsx +5 -3
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +1 -0
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MapFieldBinding.tsx +14 -16
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +15 -8
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -2
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
- package/src/form/field_bindings/RepeatFieldBinding.tsx +13 -4
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -2
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +11 -6
- package/src/form/field_bindings/SwitchFieldBinding.tsx +1 -0
- package/src/form/field_bindings/TextFieldBinding.tsx +1 -0
- package/src/form/index.tsx +16 -32
- package/src/form/validation.ts +12 -6
- package/src/hooks/data/save.ts +25 -30
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useEntityFetch.tsx +5 -1
- package/src/hooks/useBreadcrumbsController.tsx +31 -0
- package/src/hooks/useBuildModeController.tsx +15 -28
- package/src/hooks/useBuildNavigationController.tsx +81 -24
- package/src/hooks/useFireCMSContext.tsx +0 -30
- package/src/hooks/useLargeLayout.tsx +0 -35
- package/src/hooks/useModeController.tsx +1 -2
- package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
- package/src/internal/useBuildDataSource.ts +9 -5
- package/src/internal/useBuildSideDialogsController.tsx +3 -2
- package/src/internal/useBuildSideEntityController.tsx +135 -88
- package/src/internal/useUnsavedChangesDialog.tsx +126 -92
- package/src/preview/PropertyPreview.tsx +12 -10
- package/src/preview/components/UrlComponentPreview.tsx +17 -18
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +4 -2
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +4 -3
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +4 -2
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +4 -2
- package/src/preview/property_previews/MapPropertyPreview.tsx +3 -2
- package/src/routes/CustomCMSRoute.tsx +21 -0
- package/src/routes/FireCMSRoute.tsx +246 -0
- package/src/routes/HomePageRoute.tsx +17 -0
- package/src/types/collections.ts +45 -13
- package/src/types/datasource.ts +2 -1
- package/src/types/dialogs_controller.tsx +7 -3
- package/src/types/entities.ts +1 -1
- package/src/types/entity_actions.tsx +12 -0
- package/src/types/fields.tsx +18 -3
- package/src/types/firecms.tsx +1 -1
- package/src/types/navigation.ts +41 -16
- package/src/types/plugins.tsx +2 -0
- package/src/types/properties.ts +48 -4
- package/src/types/side_dialogs_controller.tsx +2 -0
- package/src/types/side_entity_controller.tsx +11 -1
- package/src/util/builders.ts +5 -3
- package/src/util/createFormexStub.tsx +62 -0
- package/src/util/entity_actions.ts +28 -0
- package/src/util/entity_cache.ts +204 -0
- package/src/util/index.ts +1 -0
- package/src/util/join_collections.ts +6 -1
- package/src/util/navigation_from_path.ts +18 -7
- package/src/util/navigation_utils.ts +115 -14
- package/src/util/objects.ts +9 -2
- package/src/util/parent_references_from_path.ts +3 -3
- package/src/util/property_utils.tsx +7 -3
- package/src/util/references.ts +8 -6
- package/src/util/resolutions.ts +41 -12
- package/src/util/useStorageUploadController.tsx +60 -23
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +0 -236
- /package/src/util/{common.tsx → common.ts} +0 -0
|
@@ -55,24 +55,23 @@ export function UrlComponentPreview({
|
|
|
55
55
|
return <VideoPreview size={size} src={url} interactive={interactive}/>;
|
|
56
56
|
} else {
|
|
57
57
|
return (
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</a>
|
|
58
|
+
<Tooltip title={hint}>
|
|
59
|
+
<a
|
|
60
|
+
href={url}
|
|
61
|
+
rel="noopener noreferrer"
|
|
62
|
+
target="_blank"
|
|
63
|
+
onClick={(e) => e.stopPropagation()}
|
|
64
|
+
className="flex flex-col items-center justify-center"
|
|
65
|
+
style={{
|
|
66
|
+
width: getThumbnailMeasure(size),
|
|
67
|
+
height: getThumbnailMeasure(size)
|
|
68
|
+
}}>
|
|
69
|
+
<DescriptionIcon className="text-surface-700 dark:text-surface-300"/>
|
|
70
|
+
{hint && <Typography
|
|
71
|
+
className="max-w-full truncate rtl text-left"
|
|
72
|
+
variant={"caption"}>{hint}</Typography>}
|
|
73
|
+
</a>
|
|
74
|
+
</Tooltip>
|
|
76
75
|
);
|
|
77
76
|
}
|
|
78
77
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { ErrorBoundary } from "../../components";
|
|
3
|
-
import { useCustomizationController } from "../../hooks";
|
|
3
|
+
import { useAuthController, useCustomizationController } from "../../hooks";
|
|
4
4
|
import { PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
5
5
|
import { PropertyPreview } from "../PropertyPreview";
|
|
6
6
|
import { resolveArrayProperty } from "../../util";
|
|
@@ -15,12 +15,13 @@ export function ArrayOfMapsPreview({
|
|
|
15
15
|
size,
|
|
16
16
|
// entity
|
|
17
17
|
}: PropertyPreviewProps<Record<string, any>[]>) {
|
|
18
|
-
|
|
18
|
+
const authController = useAuthController();
|
|
19
19
|
const customizationController = useCustomizationController();
|
|
20
20
|
const property = resolveArrayProperty({
|
|
21
21
|
propertyKey,
|
|
22
22
|
property: inputProperty,
|
|
23
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
23
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
24
|
+
authController
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
if (Array.isArray(property?.of)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ResolvedReferenceProperty } from "../../types";
|
|
2
2
|
import { resolveArrayProperty } from "../../util";
|
|
3
|
-
import { useCustomizationController } from "../../hooks";
|
|
3
|
+
import { useAuthController, useCustomizationController } from "../../hooks";
|
|
4
4
|
import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
5
5
|
import { ReferencePreview } from "../components/ReferencePreview";
|
|
6
6
|
|
|
@@ -13,12 +13,13 @@ export function ArrayOfReferencesPreview({
|
|
|
13
13
|
property: inputProperty,
|
|
14
14
|
size
|
|
15
15
|
}: PropertyPreviewProps<any[]>) {
|
|
16
|
-
|
|
16
|
+
const authController = useAuthController();
|
|
17
17
|
const customizationController = useCustomizationController();
|
|
18
18
|
const property = resolveArrayProperty({
|
|
19
19
|
propertyKey,
|
|
20
20
|
property: inputProperty,
|
|
21
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
21
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
22
|
+
authController
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
if (Array.isArray(property?.of)) {
|
|
@@ -3,7 +3,7 @@ import React from "react";
|
|
|
3
3
|
import { resolveArrayProperty } from "../../util";
|
|
4
4
|
import { ResolvedProperty } from "../../types";
|
|
5
5
|
|
|
6
|
-
import { useCustomizationController } from "../../hooks";
|
|
6
|
+
import { useAuthController, useCustomizationController } from "../../hooks";
|
|
7
7
|
import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
8
8
|
import { PropertyPreview } from "../PropertyPreview";
|
|
9
9
|
import { ErrorBoundary } from "../../components";
|
|
@@ -19,11 +19,13 @@ export function ArrayOfStorageComponentsPreview({
|
|
|
19
19
|
size
|
|
20
20
|
}: PropertyPreviewProps<any[]>) {
|
|
21
21
|
|
|
22
|
+
const authController = useAuthController();
|
|
22
23
|
const customizationController = useCustomizationController();
|
|
23
24
|
const property = resolveArrayProperty({
|
|
24
25
|
propertyKey,
|
|
25
26
|
property: inputProperty,
|
|
26
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
27
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
28
|
+
authController
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
if (Array.isArray(property.of)) {
|
|
@@ -3,7 +3,7 @@ import { ResolvedStringProperty } from "../../types";
|
|
|
3
3
|
|
|
4
4
|
import { resolveArrayProperty } from "../../util";
|
|
5
5
|
import { PropertyPreviewProps, StringPropertyPreview } from "../../preview";
|
|
6
|
-
import { useCustomizationController } from "../../hooks";
|
|
6
|
+
import { useAuthController, useCustomizationController } from "../../hooks";
|
|
7
7
|
import { ErrorBoundary } from "../../components";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -16,12 +16,13 @@ export function ArrayOfStringsPreview({
|
|
|
16
16
|
// entity,
|
|
17
17
|
size
|
|
18
18
|
}: PropertyPreviewProps<string[]>) {
|
|
19
|
-
|
|
19
|
+
const authController = useAuthController();
|
|
20
20
|
const customizationController = useCustomizationController();
|
|
21
21
|
const property = resolveArrayProperty({
|
|
22
22
|
propertyKey,
|
|
23
23
|
property: inputProperty,
|
|
24
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
24
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
25
|
+
authController
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
if (Array.isArray(property.of)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { resolveArrayProperty } from "../../util";
|
|
3
3
|
import { ResolvedProperty } from "../../types";
|
|
4
|
-
import { useCustomizationController } from "../../hooks";
|
|
4
|
+
import { useAuthController, useCustomizationController } from "../../hooks";
|
|
5
5
|
import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
6
6
|
import { PropertyPreview } from "../PropertyPreview";
|
|
7
7
|
import { cls, defaultBorderMixin } from "@firecms/ui";
|
|
@@ -19,11 +19,13 @@ export function ArrayOneOfPreview({
|
|
|
19
19
|
// entity
|
|
20
20
|
}: PropertyPreviewProps<any[]>) {
|
|
21
21
|
|
|
22
|
+
const authController = useAuthController();
|
|
22
23
|
const customizationController = useCustomizationController();
|
|
23
24
|
const property = resolveArrayProperty({
|
|
24
25
|
propertyKey,
|
|
25
26
|
property: inputProperty,
|
|
26
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
27
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
28
|
+
authController
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
if (property?.dataType !== "array")
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
|
|
3
3
|
import { resolveArrayProperty } from "../../util";
|
|
4
4
|
import { ResolvedProperty } from "../../types";
|
|
5
|
-
import { useCustomizationController } from "../../hooks";
|
|
5
|
+
import { useAuthController, useCustomizationController } from "../../hooks";
|
|
6
6
|
import { PreviewSize, PropertyPreviewProps } from "../PropertyPreviewProps";
|
|
7
7
|
import { PropertyPreview } from "../PropertyPreview";
|
|
8
8
|
import { cls, defaultBorderMixin } from "@firecms/ui";
|
|
@@ -19,11 +19,13 @@ export function ArrayPropertyPreview({
|
|
|
19
19
|
size
|
|
20
20
|
}: PropertyPreviewProps<any[]>) {
|
|
21
21
|
|
|
22
|
+
const authController = useAuthController();
|
|
22
23
|
const customizationController = useCustomizationController();
|
|
23
24
|
const property = resolveArrayProperty({
|
|
24
25
|
propertyKey,
|
|
25
26
|
property: inputProperty,
|
|
26
|
-
propertyConfigs: customizationController.propertyConfigs
|
|
27
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
28
|
+
authController
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
if (!property.of) {
|
|
@@ -58,6 +58,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
|
|
|
58
58
|
{mapPropertyKeys &&
|
|
59
59
|
mapPropertyKeys.map((key, index) => {
|
|
60
60
|
const childProperty = mapProperty.properties![key];
|
|
61
|
+
const isArrayOrMap = childProperty.dataType === "map" || childProperty === "array";
|
|
61
62
|
return (
|
|
62
63
|
<div
|
|
63
64
|
key={`map_preview_table_${key}}`}
|
|
@@ -75,7 +76,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
|
|
|
75
76
|
<div
|
|
76
77
|
className="flex-grow max-w-[75%]">
|
|
77
78
|
<ErrorBoundary>
|
|
78
|
-
{!
|
|
79
|
+
{!isArrayOrMap &&
|
|
79
80
|
<PropertyPreview
|
|
80
81
|
propertyKey={key}
|
|
81
82
|
value={(value)[key]}
|
|
@@ -86,7 +87,7 @@ export function MapPropertyPreview<T extends Record<string, any> = Record<string
|
|
|
86
87
|
</div>
|
|
87
88
|
</div>
|
|
88
89
|
|
|
89
|
-
{
|
|
90
|
+
{isArrayOrMap &&
|
|
90
91
|
<div className={cls(defaultBorderMixin, "border-l pl-4 ml-2 my-2")}>
|
|
91
92
|
<PropertyPreview
|
|
92
93
|
propertyKey={key}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CMSView } from "../types";
|
|
2
|
+
import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
|
|
5
|
+
export function CustomCMSRoute({ cmsView }: {
|
|
6
|
+
cmsView: CMSView
|
|
7
|
+
}) {
|
|
8
|
+
|
|
9
|
+
const breadcrumbs = useBreadcrumbsController();
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
breadcrumbs.set({
|
|
13
|
+
breadcrumbs: [{
|
|
14
|
+
title: cmsView.name,
|
|
15
|
+
url: cmsView.path
|
|
16
|
+
}]
|
|
17
|
+
});
|
|
18
|
+
}, [cmsView.path]);
|
|
19
|
+
|
|
20
|
+
return cmsView.view;
|
|
21
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { Blocker, useBlocker, useLocation } from "react-router";
|
|
2
|
+
import { EntityEditView } from "../core/EntityEditView";
|
|
3
|
+
import { useNavigationController } from "../hooks";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { useNavigate } from "react-router-dom";
|
|
6
|
+
import {
|
|
7
|
+
getNavigationEntriesFromPath,
|
|
8
|
+
NavigationViewCollectionInternal,
|
|
9
|
+
NavigationViewEntityCustomInternal,
|
|
10
|
+
NavigationViewInternal
|
|
11
|
+
} from "../util/navigation_from_path";
|
|
12
|
+
import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
|
|
13
|
+
import { toArray } from "../util/arrays";
|
|
14
|
+
import { EntityCollectionView, NotFoundPage } from "../components";
|
|
15
|
+
import { UnsavedChangesDialog } from "../components/UnsavedChangesDialog";
|
|
16
|
+
import { EntityCollection } from "../types";
|
|
17
|
+
|
|
18
|
+
export function FireCMSRoute() {
|
|
19
|
+
|
|
20
|
+
const location = useLocation();
|
|
21
|
+
const navigation = useNavigationController();
|
|
22
|
+
const breadcrumbs = useBreadcrumbsController();
|
|
23
|
+
|
|
24
|
+
const hash = location.hash;
|
|
25
|
+
const isSidePanel = hash.includes("#side");
|
|
26
|
+
const isNew = hash.includes("#new") || hash.includes("#new_side");
|
|
27
|
+
const isCopy = hash.includes("#copy");
|
|
28
|
+
|
|
29
|
+
const pathname = location.pathname;
|
|
30
|
+
const navigationPath = navigation.urlPathToDataPath(pathname);
|
|
31
|
+
|
|
32
|
+
const navigationEntries = getNavigationEntriesFromPath({
|
|
33
|
+
path: navigationPath,
|
|
34
|
+
collections: navigation.collections ?? []
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
breadcrumbs.set({
|
|
39
|
+
breadcrumbs: navigationEntries.map(entry => {
|
|
40
|
+
if (entry.type === "entity") {
|
|
41
|
+
return ({
|
|
42
|
+
title: entry.entityId,
|
|
43
|
+
url: navigation.buildUrlCollectionPath(entry.fullPath)
|
|
44
|
+
});
|
|
45
|
+
} else if (entry.type === "custom_view") {
|
|
46
|
+
return ({
|
|
47
|
+
title: entry.view.name,
|
|
48
|
+
url: navigation.buildUrlCollectionPath(entry.fullPath)
|
|
49
|
+
});
|
|
50
|
+
} else if (entry.type === "collection") {
|
|
51
|
+
return ({
|
|
52
|
+
title: entry.collection.name,
|
|
53
|
+
url: navigation.buildUrlCollectionPath(entry.fullPath)
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error("Unexpected navigation entry type");
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
}, [navigationEntries.map(entry => entry.path).join(",")]);
|
|
61
|
+
|
|
62
|
+
if (isNew) {
|
|
63
|
+
return <EntityFullScreenRoute
|
|
64
|
+
pathname={pathname}
|
|
65
|
+
navigationEntries={navigationEntries}
|
|
66
|
+
isNew={true}
|
|
67
|
+
isCopy={false}
|
|
68
|
+
/>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (navigationEntries.length === 1 && navigationEntries[0].type === "collection") {
|
|
72
|
+
let collection: EntityCollection<any> | undefined;
|
|
73
|
+
collection = navigation.getCollectionById(navigationEntries[0].id);
|
|
74
|
+
if (!collection)
|
|
75
|
+
collection = navigation.getCollection(navigationEntries[0].path);
|
|
76
|
+
if (!collection)
|
|
77
|
+
return null;
|
|
78
|
+
return <EntityCollectionView
|
|
79
|
+
key={`collection_view_${collection.id ?? collection.path}`}
|
|
80
|
+
isSubCollection={false}
|
|
81
|
+
parentCollectionIds={[]}
|
|
82
|
+
fullPath={collection.id}
|
|
83
|
+
updateUrl={true}
|
|
84
|
+
{...collection}
|
|
85
|
+
Actions={toArray(collection.Actions)}/>
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (isSidePanel) {
|
|
89
|
+
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
90
|
+
if (lastCollectionEntry) {
|
|
91
|
+
let collection: EntityCollection<any> | undefined;
|
|
92
|
+
const firstEntry = navigationEntries[0] as NavigationViewCollectionInternal<any>;
|
|
93
|
+
collection = navigation.getCollectionById(firstEntry.id);
|
|
94
|
+
if (!collection)
|
|
95
|
+
collection = navigation.getCollection(firstEntry.path);
|
|
96
|
+
if (!collection)
|
|
97
|
+
return null;
|
|
98
|
+
return <EntityCollectionView
|
|
99
|
+
key={`collection_view_${collection.id ?? collection.path}`}
|
|
100
|
+
fullIdPath={collection.id}
|
|
101
|
+
isSubCollection={false}
|
|
102
|
+
parentCollectionIds={[]}
|
|
103
|
+
fullPath={collection.id}
|
|
104
|
+
updateUrl={true}
|
|
105
|
+
{...collection}
|
|
106
|
+
Actions={toArray(collection.Actions)}/>;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return <EntityFullScreenRoute
|
|
111
|
+
pathname={pathname}
|
|
112
|
+
navigationEntries={navigationEntries}
|
|
113
|
+
isNew={isNew}
|
|
114
|
+
isCopy={isCopy}
|
|
115
|
+
/>;
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getSelectedTabFromUrl(isNew: boolean, lastCustomView: NavigationViewCollectionInternal<any> | NavigationViewEntityCustomInternal<any> | undefined) {
|
|
120
|
+
if (isNew) {
|
|
121
|
+
return undefined;
|
|
122
|
+
} else if (lastCustomView) {
|
|
123
|
+
if (lastCustomView.type === "custom_view") {
|
|
124
|
+
return lastCustomView.view.key;
|
|
125
|
+
} else if (lastCustomView.type === "collection") {
|
|
126
|
+
return lastCustomView.id ?? lastCustomView.path;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function EntityFullScreenRoute({
|
|
133
|
+
pathname,
|
|
134
|
+
navigationEntries,
|
|
135
|
+
isNew,
|
|
136
|
+
isCopy
|
|
137
|
+
}: {
|
|
138
|
+
pathname: string;
|
|
139
|
+
navigationEntries: NavigationViewInternal[],
|
|
140
|
+
isNew: boolean,
|
|
141
|
+
isCopy: boolean
|
|
142
|
+
}) {
|
|
143
|
+
|
|
144
|
+
const navigation = useNavigationController();
|
|
145
|
+
const navigate = useNavigate();
|
|
146
|
+
|
|
147
|
+
const navigationPath = navigation.urlPathToDataPath(pathname);
|
|
148
|
+
|
|
149
|
+
// is navigating away blocked
|
|
150
|
+
const blocked = useRef(false);
|
|
151
|
+
|
|
152
|
+
const lastEntityEntry = navigationEntries.findLast((entry) => entry.type === "entity");
|
|
153
|
+
const navigationEntriesAfterEntity = lastEntityEntry ? navigationEntries.slice(navigationEntries.indexOf(lastEntityEntry) + 1) : [];
|
|
154
|
+
|
|
155
|
+
const lastCustomView = navigationEntriesAfterEntity.findLast(
|
|
156
|
+
(entry) => entry.type === "custom_view" || entry.type === "collection"
|
|
157
|
+
) as NavigationViewCollectionInternal<any> | NavigationViewEntityCustomInternal<any> | undefined;
|
|
158
|
+
|
|
159
|
+
const entityId = lastEntityEntry?.entityId;
|
|
160
|
+
|
|
161
|
+
const urlTab = getSelectedTabFromUrl(isNew, lastCustomView);
|
|
162
|
+
const [selectedTab, setSelectedTab] = useState<string | undefined>(urlTab);
|
|
163
|
+
|
|
164
|
+
const parentCollectionIds = navigation.getParentCollectionIds(navigationPath);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
if (urlTab !== selectedTab) {
|
|
167
|
+
setSelectedTab(urlTab);
|
|
168
|
+
}
|
|
169
|
+
}, [urlTab]);
|
|
170
|
+
|
|
171
|
+
const basePath = !entityId || isNew
|
|
172
|
+
? pathname
|
|
173
|
+
: pathname.substring(0, pathname.lastIndexOf(`/${entityId}`));
|
|
174
|
+
|
|
175
|
+
const entityPath = basePath + `/${entityId}`;
|
|
176
|
+
|
|
177
|
+
let blocker: Blocker | undefined = undefined;
|
|
178
|
+
try {
|
|
179
|
+
blocker = useBlocker(({
|
|
180
|
+
nextLocation
|
|
181
|
+
}) => {
|
|
182
|
+
if (nextLocation.pathname.startsWith(entityPath))
|
|
183
|
+
return false;
|
|
184
|
+
return blocked.current;
|
|
185
|
+
});
|
|
186
|
+
} catch (e) {
|
|
187
|
+
// console.warn("Blocker not available, navigation will not be blocked");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
191
|
+
|
|
192
|
+
if (isNew && !lastCollectionEntry) {
|
|
193
|
+
throw new Error("INTERNAL: No collection found in the navigation");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!isNew && !lastEntityEntry) {
|
|
197
|
+
return <NotFoundPage/>;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const collection = isNew ? lastCollectionEntry!.collection : lastEntityEntry!.parentCollection;
|
|
201
|
+
const fullIdPath = isNew ? lastCollectionEntry!.path : lastEntityEntry!.path;
|
|
202
|
+
const collectionPath = navigation.resolveIdsFrom(fullIdPath);
|
|
203
|
+
|
|
204
|
+
return <>
|
|
205
|
+
<EntityEditView
|
|
206
|
+
key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
|
|
207
|
+
entityId={isNew ? undefined : entityId}
|
|
208
|
+
fullIdPath={fullIdPath}
|
|
209
|
+
collection={collection}
|
|
210
|
+
layout={"full_screen"}
|
|
211
|
+
path={collectionPath}
|
|
212
|
+
copy={isCopy}
|
|
213
|
+
selectedTab={selectedTab ?? undefined}
|
|
214
|
+
onValuesModified={(modified) => blocked.current = modified}
|
|
215
|
+
onSaved={(params) => {
|
|
216
|
+
const newSelectedTab = params.selectedTab;
|
|
217
|
+
const newEntityId = params.entityId;
|
|
218
|
+
if (newSelectedTab) {
|
|
219
|
+
navigate(`${basePath}/${newEntityId}/${newSelectedTab}`, { replace: true });
|
|
220
|
+
} else {
|
|
221
|
+
navigate(`${basePath}/${newEntityId}`, { replace: true });
|
|
222
|
+
}
|
|
223
|
+
}}
|
|
224
|
+
onTabChange={(params) => {
|
|
225
|
+
setSelectedTab(params.selectedTab);
|
|
226
|
+
if (isNew) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const newSelectedTab = params.selectedTab;
|
|
230
|
+
if (newSelectedTab) {
|
|
231
|
+
navigate(`${basePath}/${entityId}/${newSelectedTab}`, { replace: true });
|
|
232
|
+
} else {
|
|
233
|
+
navigate(`${basePath}/${entityId}`, { replace: true });
|
|
234
|
+
}
|
|
235
|
+
}}
|
|
236
|
+
parentCollectionIds={parentCollectionIds}
|
|
237
|
+
/>
|
|
238
|
+
|
|
239
|
+
<UnsavedChangesDialog
|
|
240
|
+
open={blocker?.state === "blocked"}
|
|
241
|
+
handleOk={() => blocker?.proceed?.()}
|
|
242
|
+
handleCancel={() => blocker?.reset?.()}
|
|
243
|
+
body={"You have unsaved changes in this entity."}/>
|
|
244
|
+
|
|
245
|
+
</>;
|
|
246
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
export function HomePageRoute({ children }: {
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}) {
|
|
7
|
+
|
|
8
|
+
const breadcrumbs = useBreadcrumbsController();
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
breadcrumbs.set({
|
|
12
|
+
breadcrumbs: []
|
|
13
|
+
});
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
return children;
|
|
17
|
+
}
|
package/src/types/collections.ts
CHANGED
|
@@ -84,6 +84,11 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
84
84
|
*/
|
|
85
85
|
properties: PropertiesOrBuilders<M>;
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Default preview properties displayed when this collection is referenced to.
|
|
89
|
+
*/
|
|
90
|
+
previewProperties?: string[];
|
|
91
|
+
|
|
87
92
|
/**
|
|
88
93
|
* Title property of the entity. This is the property that will be used
|
|
89
94
|
* as the title in entity related views and references.
|
|
@@ -92,11 +97,10 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
92
97
|
titleProperty?: keyof M;
|
|
93
98
|
|
|
94
99
|
/**
|
|
95
|
-
*
|
|
96
|
-
* Defaults to `
|
|
97
|
-
* Keep in mind that you can also set this prop to individual properties.
|
|
100
|
+
* When editing an entity, you can choose to open the entity in a side dialog
|
|
101
|
+
* or in a full screen dialog. Defaults to `full_screen`.
|
|
98
102
|
*/
|
|
99
|
-
|
|
103
|
+
openEntityMode?: "side_panel" | "full_screen";
|
|
100
104
|
|
|
101
105
|
/**
|
|
102
106
|
* Order in which the properties are displayed.
|
|
@@ -119,7 +123,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
119
123
|
* `hidden` in the property definition,will be ignored.
|
|
120
124
|
* `propertiesOrder` has precedence over `hidden`.
|
|
121
125
|
*/
|
|
122
|
-
propertiesOrder?: (Extract<keyof M, string> | `subcollection:${string}`)[];
|
|
126
|
+
propertiesOrder?: (Extract<keyof M, string> | string | `subcollection:${string}` | "collectionGroupParent")[];
|
|
123
127
|
|
|
124
128
|
/**
|
|
125
129
|
* If enabled, content is loaded in batches. If `false` all entities in the
|
|
@@ -211,6 +215,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
211
215
|
* Force a filter in this view. If applied, the rest of the filters will
|
|
212
216
|
* be disabled. Filters applied with this prop cannot be changed.
|
|
213
217
|
* e.g. `forceFilter: { age: [">=", 18] }`
|
|
218
|
+
* e.g. `forceFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
214
219
|
*/
|
|
215
220
|
forceFilter?: FilterValues<Extract<keyof M, string>>;
|
|
216
221
|
|
|
@@ -218,6 +223,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
218
223
|
* Initial filters applied to the collection this collection is related to.
|
|
219
224
|
* Defaults to none. Filters applied with this prop can be changed.
|
|
220
225
|
* e.g. `initialFilter: { age: [">=", 18] }`
|
|
226
|
+
* e.g. `initialFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
221
227
|
*/
|
|
222
228
|
initialFilter?: FilterValues<Extract<keyof M, string>>; // setting FilterValues<M> can break defining collections by code
|
|
223
229
|
|
|
@@ -313,9 +319,24 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
313
319
|
sideDialogWidth?: number | string;
|
|
314
320
|
|
|
315
321
|
/**
|
|
316
|
-
*
|
|
322
|
+
* Can this collection configuration be edited by the end user.
|
|
323
|
+
* Defaults to `true`.
|
|
324
|
+
* Keep in mind that you can also set this prop to individual properties.
|
|
325
|
+
* This prop has only effect if you are using the collection editor.
|
|
317
326
|
*/
|
|
318
|
-
|
|
327
|
+
editable?: boolean;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* If set to true, the default values of the properties will be applied
|
|
331
|
+
* to the entity every time the entity is updated (not only when created).
|
|
332
|
+
* Defaults to false.
|
|
333
|
+
*/
|
|
334
|
+
alwaysApplyDefaultValues?: boolean;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* If set to true, a tab including the JSON representation of the entity will be included.
|
|
338
|
+
*/
|
|
339
|
+
includeJsonView?: boolean;
|
|
319
340
|
}
|
|
320
341
|
|
|
321
342
|
/**
|
|
@@ -491,21 +512,26 @@ export type EntityCustomView<M extends Record<string, any> = any> =
|
|
|
491
512
|
/**
|
|
492
513
|
* Key of this custom view.
|
|
493
514
|
*/
|
|
494
|
-
key: string
|
|
515
|
+
key: string;
|
|
516
|
+
|
|
495
517
|
/**
|
|
496
518
|
* Name of this custom view.
|
|
497
519
|
*/
|
|
498
|
-
name: string
|
|
520
|
+
name: string;
|
|
521
|
+
|
|
499
522
|
/**
|
|
500
|
-
* If set to true, the actions of the entity will be
|
|
501
|
-
*
|
|
523
|
+
* If set to true, the actions of the entity (save, discard,delete) will be
|
|
524
|
+
* included in the view. By default the actions are located in the right or bottom,
|
|
525
|
+
* based on the screen size. You can force the actions to be located at the bottom
|
|
526
|
+
* by setting this prop to "bottom".
|
|
502
527
|
*/
|
|
503
|
-
includeActions?: boolean;
|
|
528
|
+
includeActions?: boolean | "bottom";
|
|
529
|
+
|
|
504
530
|
/**
|
|
505
531
|
* Builder for rendering the custom view
|
|
506
532
|
*/
|
|
507
533
|
Builder?: React.ComponentType<EntityCustomViewParams<M>>;
|
|
508
|
-
}
|
|
534
|
+
};
|
|
509
535
|
|
|
510
536
|
/**
|
|
511
537
|
* Parameters passed to the builder in charge of rendering a custom panel for
|
|
@@ -570,6 +596,12 @@ export type EntityTableController<M extends Record<string, any> = any> = {
|
|
|
570
596
|
clearFilter?: () => void;
|
|
571
597
|
itemCount?: number;
|
|
572
598
|
setItemCount?: (itemCount: number) => void;
|
|
599
|
+
initialScroll?: number;
|
|
600
|
+
onScroll?: (props: {
|
|
601
|
+
scrollDirection: "forward" | "backward",
|
|
602
|
+
scrollOffset: number,
|
|
603
|
+
scrollUpdateWasRequested: boolean
|
|
604
|
+
}) => void;
|
|
573
605
|
paginationEnabled?: boolean;
|
|
574
606
|
pageSize?: number;
|
|
575
607
|
checkFilterCombination?: (filterValues: FilterValues<any>,
|
package/src/types/datasource.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { FireCMSContext } from "./firecms_context";
|
|
|
9
9
|
export interface FetchEntityProps<M extends Record<string, any> = any> {
|
|
10
10
|
path: string;
|
|
11
11
|
entityId: string;
|
|
12
|
+
databaseId?: string;
|
|
12
13
|
collection?: EntityCollection<M, any>
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -18,7 +19,6 @@ export interface FetchEntityProps<M extends Record<string, any> = any> {
|
|
|
18
19
|
export type ListenEntityProps<M extends Record<string, any> = any> =
|
|
19
20
|
FetchEntityProps<M>
|
|
20
21
|
& {
|
|
21
|
-
databaseId?: string;
|
|
22
22
|
onUpdate: (entity: Entity<M>) => void,
|
|
23
23
|
onError?: (error: Error) => void,
|
|
24
24
|
}
|
|
@@ -139,6 +139,7 @@ export interface DataSource {
|
|
|
139
139
|
fetchEntity<M extends Record<string, any> = any>({
|
|
140
140
|
path,
|
|
141
141
|
entityId,
|
|
142
|
+
databaseId,
|
|
142
143
|
collection
|
|
143
144
|
}: FetchEntityProps<M>
|
|
144
145
|
): Promise<Entity<M> | undefined>;
|