@firecms/core 3.0.0-beta.12 → 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 +12 -5
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
- package/dist/components/EntityJsonPreview.d.ts +3 -0
- package/dist/core/DefaultAppBar.d.ts +8 -2
- package/dist/core/EntityEditView.d.ts +8 -1
- package/dist/core/FireCMS.d.ts +2 -2
- package/dist/form/EntityForm.d.ts +5 -1
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/index.es.js +8630 -8578
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +8663 -8612
- 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/types/collections.d.ts +6 -0
- package/dist/types/fields.d.ts +4 -1
- package/dist/types/firecms.d.ts +1 -1
- package/dist/types/navigation.d.ts +4 -0
- package/dist/types/plugins.d.ts +1 -0
- package/dist/types/properties.d.ts +22 -1
- package/dist/util/createFormexStub.d.ts +2 -0
- package/dist/util/navigation_utils.d.ts +2 -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 +11 -6
- package/package.json +6 -5
- package/src/app/Scaffold.tsx +13 -3
- package/src/components/ArrayContainer.tsx +40 -23
- package/src/components/DeleteEntityDialog.tsx +4 -2
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +8 -6
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +5 -2
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +22 -6
- package/src/components/EntityJsonPreview.tsx +66 -0
- package/src/components/EntityPreview.tsx +10 -3
- package/src/components/EntityView.tsx +4 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -1
- package/src/components/VirtualTable/VirtualTable.tsx +29 -1
- package/src/components/common/default_entity_actions.tsx +15 -4
- package/src/core/DefaultAppBar.tsx +17 -5
- package/src/core/EntityEditView.tsx +111 -35
- package/src/core/FireCMS.tsx +13 -12
- package/src/form/EntityForm.tsx +31 -23
- package/src/form/PropertyFieldBinding.tsx +8 -5
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +4 -1
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +1 -0
- package/src/form/field_bindings/BlockFieldBinding.tsx +1 -1
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +1 -1
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +6 -2
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +2 -2
- package/src/form/field_bindings/RepeatFieldBinding.tsx +8 -2
- package/src/form/field_bindings/SelectFieldBinding.tsx +1 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +5 -2
- package/src/hooks/data/save.ts +24 -29
- package/src/hooks/useBuildNavigationController.tsx +12 -1
- package/src/hooks/useFireCMSContext.tsx +0 -30
- package/src/internal/useBuildDataSource.ts +9 -5
- package/src/internal/useBuildSideEntityController.tsx +26 -20
- package/src/preview/PropertyPreview.tsx +4 -2
- 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/routes/FireCMSRoute.tsx +15 -4
- package/src/types/collections.ts +7 -0
- package/src/types/fields.tsx +5 -1
- package/src/types/firecms.tsx +1 -1
- package/src/types/navigation.ts +5 -0
- package/src/types/plugins.tsx +1 -0
- package/src/types/properties.ts +27 -1
- package/src/types/side_entity_controller.tsx +5 -0
- package/src/util/createFormexStub.tsx +62 -0
- package/src/util/join_collections.ts +3 -1
- package/src/util/navigation_from_path.ts +5 -1
- package/src/util/navigation_utils.ts +64 -15
- package/src/util/objects.ts +5 -1
- package/src/util/property_utils.tsx +7 -3
- package/src/util/references.ts +8 -6
- package/src/util/resolutions.ts +17 -9
- package/src/util/useStorageUploadController.tsx +21 -2
|
@@ -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) {
|
|
@@ -13,6 +13,7 @@ import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
|
|
|
13
13
|
import { toArray } from "../util/arrays";
|
|
14
14
|
import { EntityCollectionView, NotFoundPage } from "../components";
|
|
15
15
|
import { UnsavedChangesDialog } from "../components/UnsavedChangesDialog";
|
|
16
|
+
import { EntityCollection } from "../types";
|
|
16
17
|
|
|
17
18
|
export function FireCMSRoute() {
|
|
18
19
|
|
|
@@ -68,7 +69,10 @@ export function FireCMSRoute() {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
if (navigationEntries.length === 1 && navigationEntries[0].type === "collection") {
|
|
71
|
-
|
|
72
|
+
let collection: EntityCollection<any> | undefined;
|
|
73
|
+
collection = navigation.getCollectionById(navigationEntries[0].id);
|
|
74
|
+
if (!collection)
|
|
75
|
+
collection = navigation.getCollection(navigationEntries[0].path);
|
|
72
76
|
if (!collection)
|
|
73
77
|
return null;
|
|
74
78
|
return <EntityCollectionView
|
|
@@ -84,11 +88,16 @@ export function FireCMSRoute() {
|
|
|
84
88
|
if (isSidePanel) {
|
|
85
89
|
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
86
90
|
if (lastCollectionEntry) {
|
|
87
|
-
|
|
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);
|
|
88
96
|
if (!collection)
|
|
89
97
|
return null;
|
|
90
98
|
return <EntityCollectionView
|
|
91
99
|
key={`collection_view_${collection.id ?? collection.path}`}
|
|
100
|
+
fullIdPath={collection.id}
|
|
92
101
|
isSubCollection={false}
|
|
93
102
|
parentCollectionIds={[]}
|
|
94
103
|
fullPath={collection.id}
|
|
@@ -175,7 +184,7 @@ function EntityFullScreenRoute({
|
|
|
175
184
|
return blocked.current;
|
|
176
185
|
});
|
|
177
186
|
} catch (e) {
|
|
178
|
-
console.warn("Blocker not available, navigation will not be blocked");
|
|
187
|
+
// console.warn("Blocker not available, navigation will not be blocked");
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
const lastCollectionEntry = navigationEntries.findLast((entry) => entry.type === "collection");
|
|
@@ -189,12 +198,14 @@ function EntityFullScreenRoute({
|
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
const collection = isNew ? lastCollectionEntry!.collection : lastEntityEntry!.parentCollection;
|
|
192
|
-
const
|
|
201
|
+
const fullIdPath = isNew ? lastCollectionEntry!.path : lastEntityEntry!.path;
|
|
202
|
+
const collectionPath = navigation.resolveIdsFrom(fullIdPath);
|
|
193
203
|
|
|
194
204
|
return <>
|
|
195
205
|
<EntityEditView
|
|
196
206
|
key={collection.id + "_" + (isNew ? "new" : (isCopy ? entityId + "_copy" : entityId))}
|
|
197
207
|
entityId={isNew ? undefined : entityId}
|
|
208
|
+
fullIdPath={fullIdPath}
|
|
198
209
|
collection={collection}
|
|
199
210
|
layout={"full_screen"}
|
|
200
211
|
path={collectionPath}
|
package/src/types/collections.ts
CHANGED
|
@@ -215,6 +215,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
215
215
|
* Force a filter in this view. If applied, the rest of the filters will
|
|
216
216
|
* be disabled. Filters applied with this prop cannot be changed.
|
|
217
217
|
* e.g. `forceFilter: { age: [">=", 18] }`
|
|
218
|
+
* e.g. `forceFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
218
219
|
*/
|
|
219
220
|
forceFilter?: FilterValues<Extract<keyof M, string>>;
|
|
220
221
|
|
|
@@ -222,6 +223,7 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
222
223
|
* Initial filters applied to the collection this collection is related to.
|
|
223
224
|
* Defaults to none. Filters applied with this prop can be changed.
|
|
224
225
|
* e.g. `initialFilter: { age: [">=", 18] }`
|
|
226
|
+
* e.g. `initialFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
225
227
|
*/
|
|
226
228
|
initialFilter?: FilterValues<Extract<keyof M, string>>; // setting FilterValues<M> can break defining collections by code
|
|
227
229
|
|
|
@@ -330,6 +332,11 @@ export interface EntityCollection<M extends Record<string, any> = any, USER exte
|
|
|
330
332
|
* Defaults to false.
|
|
331
333
|
*/
|
|
332
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;
|
|
333
340
|
}
|
|
334
341
|
|
|
335
342
|
/**
|
package/src/types/fields.tsx
CHANGED
|
@@ -171,9 +171,13 @@ export interface FormContext<M extends Record<string, any> = any> {
|
|
|
171
171
|
openEntityMode: "side_panel" | "full_screen";
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
|
-
* This is the underlying formex controller that powers the form
|
|
174
|
+
* This is the underlying formex controller that powers the form.
|
|
175
|
+
* If you are in a red only mode, the formex controller is there, but you can't
|
|
176
|
+
* operate with it
|
|
175
177
|
*/
|
|
176
178
|
formex: FormexController<M>;
|
|
179
|
+
|
|
180
|
+
disabled: boolean;
|
|
177
181
|
}
|
|
178
182
|
|
|
179
183
|
/**
|
package/src/types/firecms.tsx
CHANGED
|
@@ -47,7 +47,7 @@ export type CMSViewsBuilder = (params: {
|
|
|
47
47
|
/**
|
|
48
48
|
* @group Models
|
|
49
49
|
*/
|
|
50
|
-
export type FireCMSProps<USER extends User
|
|
50
|
+
export type FireCMSProps<USER extends User> = {
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Use this function to return the components you want to render under
|
package/src/types/navigation.ts
CHANGED
|
@@ -59,6 +59,11 @@ export type NavigationController<EC extends EntityCollection = EntityCollection<
|
|
|
59
59
|
*/
|
|
60
60
|
getCollection: (pathOrId: string, includeUserOverride?: boolean) => EC | undefined;
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Get the top level collection configuration for a given id
|
|
64
|
+
*/
|
|
65
|
+
getCollectionById: (id: string) => EC | undefined;
|
|
66
|
+
|
|
62
67
|
/**
|
|
63
68
|
* Get the collection configuration from its parent ids.
|
|
64
69
|
*/
|
package/src/types/plugins.tsx
CHANGED
|
@@ -192,6 +192,7 @@ export interface PluginFormActionProps<USER extends User = User, EC extends Enti
|
|
|
192
192
|
path: string;
|
|
193
193
|
status: EntityStatus;
|
|
194
194
|
collection: EC;
|
|
195
|
+
disabled: boolean;
|
|
195
196
|
formContext?: FormContext<any>;
|
|
196
197
|
context: FireCMSContext<USER>;
|
|
197
198
|
currentEntityId?: string;
|
package/src/types/properties.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { EntityReference, EntityValues, GeoPoint, Vector } from "./entities";
|
|
|
5
5
|
import { ResolvedArrayProperty, ResolvedStringProperty } from "./resolved_entities";
|
|
6
6
|
import { FilterValues } from "./collections";
|
|
7
7
|
import { ChipColorKey, ChipColorScheme } from "@firecms/ui";
|
|
8
|
+
import { AuthController } from "./auth";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @group Entity properties
|
|
@@ -270,6 +271,11 @@ export type PropertyBuilderProps<M extends Record<string, any> = any> =
|
|
|
270
271
|
* Entity ID
|
|
271
272
|
*/
|
|
272
273
|
entityId?: string;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Controller to manage authentication
|
|
277
|
+
*/
|
|
278
|
+
authController: AuthController;
|
|
273
279
|
};
|
|
274
280
|
|
|
275
281
|
/**
|
|
@@ -285,7 +291,8 @@ export type PropertyBuilder<T extends CMSType = any, M extends Record<string, an
|
|
|
285
291
|
propertyValue,
|
|
286
292
|
index,
|
|
287
293
|
path,
|
|
288
|
-
entityId
|
|
294
|
+
entityId,
|
|
295
|
+
authController
|
|
289
296
|
}: PropertyBuilderProps<M>) => Property<T> | null;
|
|
290
297
|
|
|
291
298
|
/**
|
|
@@ -481,6 +488,18 @@ export interface ArrayProperty<T extends ArrayT[] = any[], ArrayT extends CMSTyp
|
|
|
481
488
|
*/
|
|
482
489
|
minimalistView?: boolean;
|
|
483
490
|
|
|
491
|
+
/**
|
|
492
|
+
* Can the elements in this array be reordered. Defaults to `true`.
|
|
493
|
+
* This prop has no effect if `disabled` is set to true.
|
|
494
|
+
*/
|
|
495
|
+
sortable?: boolean;
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Can the elements in this array be added. Defaults to `true`
|
|
499
|
+
* This prop has no effect if `disabled` is set to true.
|
|
500
|
+
*/
|
|
501
|
+
canAddElements?: boolean;
|
|
502
|
+
|
|
484
503
|
}
|
|
485
504
|
|
|
486
505
|
/**
|
|
@@ -796,6 +815,13 @@ export type StorageConfig = {
|
|
|
796
815
|
*/
|
|
797
816
|
maxSize?: number,
|
|
798
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Use this callback to process the file before uploading it to the storage.
|
|
820
|
+
* If nothing is returned, the file is uploaded as it is.
|
|
821
|
+
* @param file
|
|
822
|
+
*/
|
|
823
|
+
processFile?: (file: File) => Promise<File> | undefined;
|
|
824
|
+
|
|
799
825
|
/**
|
|
800
826
|
* Postprocess the saved value (storage path or URL)
|
|
801
827
|
* after it has been resolved.
|
|
@@ -20,6 +20,11 @@ export interface EntitySidePanelProps<M extends Record<string, any> = any> {
|
|
|
20
20
|
*/
|
|
21
21
|
entityId?: string;
|
|
22
22
|
|
|
23
|
+
// /**
|
|
24
|
+
// * Navigation path with ids of the entity
|
|
25
|
+
// */
|
|
26
|
+
// fullIdPath: string;
|
|
27
|
+
|
|
23
28
|
/**
|
|
24
29
|
* Set this flag to true if you want to make a copy of an existing entity
|
|
25
30
|
*/
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { FormexController } from "@firecms/formex";
|
|
2
|
+
|
|
3
|
+
export function createFormexStub<T extends object>(values: T): FormexController<T> {
|
|
4
|
+
const errorMessage = "You are in a read-only context. You cannot modify the formex controller.";
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
values,
|
|
8
|
+
initialValues: values,
|
|
9
|
+
touched: {} as Record<string, boolean>,
|
|
10
|
+
dirty: false,
|
|
11
|
+
errors: {} as Record<string, string>,
|
|
12
|
+
submitCount: 0,
|
|
13
|
+
isSubmitting: false,
|
|
14
|
+
isValidating: false,
|
|
15
|
+
version: 0,
|
|
16
|
+
canUndo: false,
|
|
17
|
+
canRedo: false,
|
|
18
|
+
|
|
19
|
+
setValues: () => {
|
|
20
|
+
throw new Error(errorMessage);
|
|
21
|
+
},
|
|
22
|
+
setFieldValue: () => {
|
|
23
|
+
throw new Error(errorMessage);
|
|
24
|
+
},
|
|
25
|
+
setFieldTouched: () => {
|
|
26
|
+
throw new Error(errorMessage);
|
|
27
|
+
},
|
|
28
|
+
setDirty: () => {
|
|
29
|
+
throw new Error(errorMessage);
|
|
30
|
+
},
|
|
31
|
+
setSubmitCount: () => {
|
|
32
|
+
throw new Error(errorMessage);
|
|
33
|
+
},
|
|
34
|
+
setFieldError: () => {
|
|
35
|
+
throw new Error(errorMessage);
|
|
36
|
+
},
|
|
37
|
+
handleChange: () => {
|
|
38
|
+
throw new Error(errorMessage);
|
|
39
|
+
},
|
|
40
|
+
handleBlur: () => {
|
|
41
|
+
throw new Error(errorMessage);
|
|
42
|
+
},
|
|
43
|
+
handleSubmit: () => {
|
|
44
|
+
throw new Error(errorMessage);
|
|
45
|
+
},
|
|
46
|
+
validate: () => {
|
|
47
|
+
throw new Error(errorMessage);
|
|
48
|
+
},
|
|
49
|
+
resetForm: () => {
|
|
50
|
+
throw new Error(errorMessage);
|
|
51
|
+
},
|
|
52
|
+
setSubmitting: () => {
|
|
53
|
+
throw new Error(errorMessage);
|
|
54
|
+
},
|
|
55
|
+
undo: () => {
|
|
56
|
+
throw new Error(errorMessage);
|
|
57
|
+
},
|
|
58
|
+
redo: () => {
|
|
59
|
+
throw new Error(errorMessage);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -78,6 +78,7 @@ export function mergeCollection(target: EntityCollection,
|
|
|
78
78
|
modifyCollection?: (props: ModifyCollectionProps) => EntityCollection | void
|
|
79
79
|
): EntityCollection {
|
|
80
80
|
|
|
81
|
+
|
|
81
82
|
const subcollectionsMerged = joinCollectionLists(
|
|
82
83
|
target?.subcollections ?? [],
|
|
83
84
|
source?.subcollections ?? [],
|
|
@@ -94,7 +95,7 @@ export function mergeCollection(target: EntityCollection,
|
|
|
94
95
|
propertiesMerged[key] = source.properties[key] as PropertyOrBuilder;
|
|
95
96
|
});
|
|
96
97
|
|
|
97
|
-
const mergedCollection = mergeDeep(target, source);
|
|
98
|
+
const mergedCollection = mergeDeep(target, source, true);
|
|
98
99
|
const targetPropertiesOrder = getCollectionKeys(target);
|
|
99
100
|
const sourcePropertiesOrder = getCollectionKeys(source);
|
|
100
101
|
const mergedPropertiesOrder = [...new Set([...sourcePropertiesOrder, ...targetPropertiesOrder])];
|
|
@@ -118,6 +119,7 @@ export function mergeCollection(target: EntityCollection,
|
|
|
118
119
|
|
|
119
120
|
// @ts-ignore
|
|
120
121
|
resultCollection["merged"] = true;
|
|
122
|
+
|
|
121
123
|
return resultCollection
|
|
122
124
|
}
|
|
123
125
|
|
|
@@ -51,7 +51,11 @@ export function getNavigationEntriesFromPath(props: {
|
|
|
51
51
|
for (let i = 0; i < subpathCombinations.length; i++) {
|
|
52
52
|
const subpathCombination = subpathCombinations[i];
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
let collection: EntityCollection<any> | undefined;
|
|
55
|
+
collection = collections && collections.find((entry) => entry.id === subpathCombination);
|
|
56
|
+
if (!collection) {
|
|
57
|
+
collection = collections && collections.find((entry) => entry.path === subpathCombination);
|
|
58
|
+
}
|
|
55
59
|
|
|
56
60
|
if (collection) {
|
|
57
61
|
const pathOrAlias = collection.id ?? collection.path;
|
|
@@ -32,29 +32,75 @@ export function getLastSegment(path: string) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function resolveCollectionPathIds(path: string, allCollections: EntityCollection[]): string {
|
|
35
|
-
|
|
36
35
|
const cleanPath = removeInitialAndTrailingSlashes(path);
|
|
37
36
|
const subpaths = cleanPath.split("/");
|
|
37
|
+
|
|
38
38
|
if (subpaths.length % 2 === 0) {
|
|
39
|
-
throw Error(`
|
|
39
|
+
throw Error(`resolveCollectionPathIds: Collection paths must have an odd number of segments: ${path}`);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
42
|
+
// Check if the path exactly matches a collection path
|
|
43
|
+
const exactMatch = allCollections.find(col => col.path === cleanPath);
|
|
44
|
+
if (exactMatch) {
|
|
45
|
+
return exactMatch.path;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
if (subpaths.length
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if (subpaths.length === 1) {
|
|
49
|
+
// Find collection by ID and return its path
|
|
50
|
+
const aliasedCollection = allCollections.find((col) => col.id === subpaths[0]);
|
|
51
|
+
return aliasedCollection?.path ?? subpaths[0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Try to match a multi-segment collection path
|
|
55
|
+
let matchingCollection: EntityCollection | undefined;
|
|
56
|
+
let entityIndex = 1;
|
|
57
|
+
|
|
58
|
+
// Check if the path starts with a multi-segment collection path
|
|
59
|
+
for (const collection of allCollections) {
|
|
60
|
+
const pathSegments = collection.path.split("/");
|
|
61
|
+
if (pathSegments.length > 1 &&
|
|
62
|
+
subpaths.slice(0, pathSegments.length).join("/") === collection.path) {
|
|
63
|
+
matchingCollection = collection;
|
|
64
|
+
entityIndex = pathSegments.length;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If no multi-segment match, fall back to single segment matching
|
|
70
|
+
if (!matchingCollection) {
|
|
71
|
+
const matchingCollections = allCollections.filter(col =>
|
|
72
|
+
col.id === subpaths[0] || col.path === subpaths[0]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (!matchingCollections.length) {
|
|
51
76
|
return cleanPath;
|
|
52
77
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} else {
|
|
56
|
-
return resolvedAliased ?? cleanPath;
|
|
78
|
+
|
|
79
|
+
matchingCollection = matchingCollections[0];
|
|
57
80
|
}
|
|
81
|
+
|
|
82
|
+
const entityId = subpaths[entityIndex];
|
|
83
|
+
const remainingPath = subpaths.slice(entityIndex + 1);
|
|
84
|
+
|
|
85
|
+
// If we have a subcollection ID, try to resolve it
|
|
86
|
+
if (remainingPath.length > 0) {
|
|
87
|
+
const subcollectionId = remainingPath[0];
|
|
88
|
+
const subcollection = matchingCollection.subcollections?.find(
|
|
89
|
+
subcol => subcol.id === subcollectionId
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (subcollection) {
|
|
93
|
+
return `${matchingCollection.path}/${entityId}/${subcollection.path}`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If there are no remaining path segments, just return the collection path with entity ID
|
|
98
|
+
if (remainingPath.length === 0) {
|
|
99
|
+
return `${matchingCollection.path}/${entityId}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Default case - couldn't match subcollection
|
|
103
|
+
return `${matchingCollection.path}/${entityId}/${remainingPath.join("/")}`;
|
|
58
104
|
}
|
|
59
105
|
|
|
60
106
|
/**
|
|
@@ -116,6 +162,7 @@ export function navigateToEntity({
|
|
|
116
162
|
entityId,
|
|
117
163
|
copy,
|
|
118
164
|
path,
|
|
165
|
+
fullIdPath,
|
|
119
166
|
selectedTab,
|
|
120
167
|
sideEntityController,
|
|
121
168
|
onClose,
|
|
@@ -129,6 +176,7 @@ export function navigateToEntity({
|
|
|
129
176
|
selectedTab?: string;
|
|
130
177
|
copy?: boolean;
|
|
131
178
|
path: string;
|
|
179
|
+
fullIdPath?: string;
|
|
132
180
|
sideEntityController: SideEntityController;
|
|
133
181
|
onClose?: () => void;
|
|
134
182
|
navigation: NavigationController
|
|
@@ -138,7 +186,8 @@ export function navigateToEntity({
|
|
|
138
186
|
|
|
139
187
|
sideEntityController.open({
|
|
140
188
|
entityId,
|
|
141
|
-
path,
|
|
189
|
+
path: fullIdPath ?? path,
|
|
190
|
+
// fullIdPath,
|
|
142
191
|
copy,
|
|
143
192
|
selectedTab,
|
|
144
193
|
collection,
|
|
@@ -147,7 +196,7 @@ export function navigateToEntity({
|
|
|
147
196
|
});
|
|
148
197
|
|
|
149
198
|
} else {
|
|
150
|
-
let to = navigation.buildUrlCollectionPath(entityId ? `${path}/${entityId}` : path);
|
|
199
|
+
let to = navigation.buildUrlCollectionPath(entityId ? `${fullIdPath ?? path}/${entityId}` : fullIdPath ?? path);
|
|
151
200
|
if (entityId && selectedTab) {
|
|
152
201
|
to += `/${selectedTab}`;
|
|
153
202
|
}
|
package/src/util/objects.ts
CHANGED
|
@@ -12,12 +12,16 @@ export function isObject(item: any) {
|
|
|
12
12
|
return item && typeof item === "object" && !Array.isArray(item);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export function mergeDeep<T extends Record<any, any>, U extends Record<any, any>>(target: T, source: U): T & U {
|
|
15
|
+
export function mergeDeep<T extends Record<any, any>, U extends Record<any, any>>(target: T, source: U, ignoreUndefined: boolean = false): T & U {
|
|
16
16
|
const targetIsObject = isObject(target);
|
|
17
17
|
const output = targetIsObject ? { ...target } : target;
|
|
18
18
|
if (targetIsObject && isObject(source)) {
|
|
19
19
|
Object.keys(source).forEach(key => {
|
|
20
20
|
const sourceElement = source[key];
|
|
21
|
+
// Skip undefined values when ignoreUndefined is true
|
|
22
|
+
if (ignoreUndefined && sourceElement === undefined) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
21
25
|
if (sourceElement instanceof Date) {
|
|
22
26
|
// Assign a new Date instance with the same time value
|
|
23
27
|
Object.assign(output, { [key]: new Date(sourceElement.getTime()) });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
AuthController,
|
|
4
5
|
EntityCollection,
|
|
5
6
|
PropertiesOrBuilders,
|
|
6
7
|
PropertyConfig,
|
|
@@ -13,12 +14,15 @@ import { resolveProperty } from "./resolutions";
|
|
|
13
14
|
import { CircleIcon, FunctionsIcon } from "@firecms/ui";
|
|
14
15
|
import { getFieldConfig } from "../core";
|
|
15
16
|
|
|
16
|
-
export function isReferenceProperty(
|
|
17
|
-
|
|
17
|
+
export function isReferenceProperty(
|
|
18
|
+
authController: AuthController,
|
|
19
|
+
propertyOrBuilder: PropertyOrBuilder,
|
|
20
|
+
fields: Record<string, PropertyConfig>) {
|
|
18
21
|
const resolvedProperty = resolveProperty({
|
|
19
22
|
propertyKey: "ignore", // TODO
|
|
20
23
|
propertyOrBuilder,
|
|
21
|
-
propertyConfigs: fields
|
|
24
|
+
propertyConfigs: fields,
|
|
25
|
+
authController
|
|
22
26
|
});
|
|
23
27
|
if (!resolvedProperty) return null;
|
|
24
28
|
if (resolvedProperty.dataType === "reference") {
|
package/src/util/references.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { EntityCollection, PropertyConfig, ResolvedEntityCollection } from "../types";
|
|
1
|
+
import { AuthController, EntityCollection, PropertyConfig, ResolvedEntityCollection } from "../types";
|
|
2
2
|
import { isReferenceProperty } from "./property_utils";
|
|
3
3
|
import { isPropertyBuilder } from "./entities";
|
|
4
4
|
import { getFieldConfig } from "../core";
|
|
5
5
|
|
|
6
|
-
export function getEntityPreviewKeys(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export function getEntityPreviewKeys(
|
|
7
|
+
authController: AuthController,
|
|
8
|
+
targetCollection: EntityCollection<any>,
|
|
9
|
+
fields: Record<string, PropertyConfig>,
|
|
10
|
+
previewProperties?: string[],
|
|
11
|
+
limit = 3) {
|
|
10
12
|
const allProperties = Object.keys(targetCollection.properties);
|
|
11
13
|
let listProperties = previewProperties?.filter(p => allProperties.includes(p as string));
|
|
12
14
|
if (!listProperties && targetCollection.previewProperties) {
|
|
@@ -18,7 +20,7 @@ export function getEntityPreviewKeys(targetCollection: EntityCollection<any>,
|
|
|
18
20
|
listProperties = allProperties;
|
|
19
21
|
return listProperties.filter(key => {
|
|
20
22
|
const propertyOrBuilder = targetCollection.properties[key];
|
|
21
|
-
return propertyOrBuilder && !isPropertyBuilder(propertyOrBuilder) && !isReferenceProperty(propertyOrBuilder, fields);
|
|
23
|
+
return propertyOrBuilder && !isPropertyBuilder(propertyOrBuilder) && !isReferenceProperty(authController, propertyOrBuilder, fields);
|
|
22
24
|
}).slice(0, limit);
|
|
23
25
|
}
|
|
24
26
|
}
|